# Android CameraX 2026 완벽 가이드: 사진 및 동영상 촬영과 면접 질문 > CameraX 1.6을 활용한 사진 촬영과 동영상 녹화 구현 방법을 설명합니다. Jetpack Compose 통합, 권한 처리, Android 개발자 면접에서 자주 출제되는 CameraX 질문과 답변을 다룹니다. - Published: 2026-09-11 - Updated: 2026-09-11 - Author: Anthony Fillion-Maillet - Reading time: 5 min --- Android CameraX는 라이프사이클 인식 API를 통해 하드웨어의 복잡성을 추상화하여 카메라 개발을 크게 간소화합니다. 버전 1.6.2가 안정 버전으로 출시되고 CameraPipe로의 마이그레이션이 완료된 현재, CameraX는 Pixel 카메라 앱과 동일한 고성능 카메라 스택을 제공합니다. > **CameraX 1.6 주요 변경사항** > > CameraX 1.6에서는 CameraPipe로의 마이그레이션, VideoCapture의 Media3 Muxer 기본 적용, SessionConfig API 안정화가 이루어졌습니다. Kotlin DSL 빌더(`preview { }`, `imageCapture { }`, `videoCapture { }`)를 통해 보일러플레이트 코드가 40% 감소합니다. ## CameraX 아키텍처와 유스케이스 모델 CameraX는 동시에 실행 가능한 4가지 주요 유스케이스를 제공합니다: Preview, ImageCapture, ImageAnalysis, VideoCapture. 각 유스케이스는 [LifecycleOwner](https://developer.android.com/topic/libraries/architecture/lifecycle)에 바인딩되므로 카메라는 Activity나 Fragment와 연동하여 자동으로 시작하고 중지됩니다. CameraProvider는 이러한 유스케이스를 인스턴스화하고 카메라에 바인딩하는 팩토리 역할을 합니다. 이러한 느슨한 결합을 통해 벤더별 문제를 걱정하지 않고 다양한 OEM 디바이스에서 동일한 코드가 동작합니다. ```kotlin // CameraSetup.kt class CameraSetup( private val context: Context, private val lifecycleOwner: LifecycleOwner ) { private lateinit var cameraProvider: ProcessCameraProvider private var imageCapture: ImageCapture? = null private var videoCapture: VideoCapture? = null suspend fun initialize() { // ProcessCameraProvider binds use cases to lifecycle cameraProvider = ProcessCameraProvider.getInstance(context).await() } fun bindUseCases(previewView: PreviewView) { // Unbind existing use cases before rebinding cameraProvider.unbindAll() // Preview use case displays camera feed val preview = Preview.Builder() .build() .apply { setSurfaceProvider(previewView.surfaceProvider) } // ImageCapture for photos imageCapture = ImageCapture.Builder() .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY) .build() // VideoCapture with Recorder val recorder = Recorder.Builder() .setQualitySelector(QualitySelector.from(Quality.FHD)) .build() videoCapture = VideoCapture.withOutput(recorder) // Bind all use cases to lifecycle cameraProvider.bindToLifecycle( lifecycleOwner, CameraSelector.DEFAULT_BACK_CAMERA, preview, imageCapture, videoCapture ) } } ``` `bindToLifecycle` 호출은 유스케이스를 카메라 하드웨어와 라이프사이클 모두에 연결합니다. LifecycleOwner가 STARTED 상태가 되면 카메라가 열리고, STOPPED 상태가 되면 자동으로 닫힙니다. ## ImageCapture를 통한 사진 촬영 구현 ImageCapture는 두 가지 캡처 모드를 제공합니다: 빠른 셔터 응답을 위한 `CAPTURE_MODE_MINIMIZE_LATENCY`와 처리 부하가 높은 촬영을 위한 `CAPTURE_MODE_MAXIMIZE_QUALITY`. 선택은 앱이 속도(SNS 앱)를 우선시하는지 품질(문서 스캐너)을 우선시하는지에 따라 결정됩니다. ```kotlin // PhotoCaptureManager.kt class PhotoCaptureManager( private val context: Context, private val imageCapture: ImageCapture ) { private val mainExecutor = ContextCompat.getMainExecutor(context) fun capturePhoto(onResult: (Uri?) -> Unit) { // Generate unique filename with timestamp val filename = "IMG_${System.currentTimeMillis()}.jpg" val contentValues = ContentValues().apply { put(MediaStore.MediaColumns.DISPLAY_NAME, filename) put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg") // Store in Pictures/CameraX folder on Android 10+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/CameraX") } } val outputOptions = ImageCapture.OutputFileOptions.Builder( context.contentResolver, MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues ).build() imageCapture.takePicture( outputOptions, mainExecutor, object : ImageCapture.OnImageSavedCallback { override fun onImageSaved(results: ImageCapture.OutputFileResults) { // results.savedUri contains the MediaStore URI onResult(results.savedUri) } override fun onError(exception: ImageCaptureException) { Log.e("PhotoCapture", "Capture failed: ${exception.message}") onResult(null) } } ) } } ``` CameraX 1.5에서는 DNG(RAW) 캡처 지원이 추가되어 처리되지 않은 센서 데이터가 필요한 앱에 대응합니다. RAW 출력을 활성화하기 전에 `ImageCapture.getImageCaptureCapabilities()`로 디바이스의 기능을 확인해야 합니다. ## VideoCapture와 Recorder를 통한 동영상 녹화 VideoCapture는 [Recorder](https://developer.android.com/reference/androidx/camera/video/Recorder) 객체를 사용하여 인코딩과 멀티플렉싱을 처리합니다. Recorder API는 오디오 동기화를 자동으로 처리하는 prepare-start-stop 패턴을 따릅니다. ```kotlin // VideoRecordingManager.kt class VideoRecordingManager( private val context: Context, private val videoCapture: VideoCapture ) { private var activeRecording: Recording? = null fun startRecording(onEvent: (VideoRecordEvent) -> Unit): Boolean { // Check if already recording if (activeRecording != null) return false val filename = "VID_${System.currentTimeMillis()}.mp4" val contentValues = ContentValues().apply { put(MediaStore.MediaColumns.DISPLAY_NAME, filename) put(MediaStore.MediaColumns.MIME_TYPE, "video/mp4") if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { put(MediaStore.Video.Media.RELATIVE_PATH, "Movies/CameraX") } } val mediaStoreOutput = MediaStoreOutputOptions.Builder( context.contentResolver, MediaStore.Video.Media.EXTERNAL_CONTENT_URI ) .setContentValues(contentValues) .build() // Prepare recording with audio if permission granted val pendingRecording = videoCapture.output .prepareRecording(context, mediaStoreOutput) if (hasAudioPermission()) { pendingRecording.withAudioEnabled() } // Start recording and store reference activeRecording = pendingRecording.start( ContextCompat.getMainExecutor(context) ) { event -> onEvent(event) // Clear reference when recording completes if (event is VideoRecordEvent.Finalize) { activeRecording = null } } return true } fun stopRecording() { activeRecording?.stop() } fun pauseRecording() { activeRecording?.pause() } fun resumeRecording() { activeRecording?.resume() } private fun hasAudioPermission(): Boolean { return ContextCompat.checkSelfPermission( context, Manifest.permission.RECORD_AUDIO ) == PackageManager.PERMISSION_GRANTED } } ``` CameraX 1.5에서는 슬로우모션 동영상 지원이 추가되었습니다. 120fps 또는 240fps 녹화를 활성화하기 전에 `Recorder.getHighSpeedVideoCapabilities()`로 기능을 확인해야 합니다. ## Jetpack Compose에서의 CameraX 통합 CameraX 1.5에서는 `camera-compose` 아티팩트와 `CameraXViewfinder` 컴포저블이 도입되었습니다. View 기반 PreviewView를 사용하는 앱에서는 AndroidView가 상호 운용을 제공합니다. ```kotlin // CameraPreviewComposable.kt @Composable fun CameraPreview( modifier: Modifier = Modifier, onPreviewViewCreated: (PreviewView) -> Unit ) { AndroidView( modifier = modifier.fillMaxSize(), factory = { context -> PreviewView(context).apply { implementationMode = PreviewView.ImplementationMode.COMPATIBLE scaleType = PreviewView.ScaleType.FILL_CENTER onPreviewViewCreated(this) } } ) } @Composable fun CameraScreen(viewModel: CameraViewModel = viewModel()) { val context = LocalContext.current val lifecycleOwner = LocalLifecycleOwner.current LaunchedEffect(Unit) { viewModel.initializeCamera(context, lifecycleOwner) } Box(modifier = Modifier.fillMaxSize()) { CameraPreview( onPreviewViewCreated = { previewView -> viewModel.bindPreview(previewView) } ) // Capture buttons overlay Row( modifier = Modifier .align(Alignment.BottomCenter) .padding(32.dp), horizontalArrangement = Arrangement.spacedBy(24.dp) ) { IconButton( onClick = { viewModel.capturePhoto() } ) { Icon(Icons.Default.Camera, "Take photo") } IconButton( onClick = { viewModel.toggleRecording() } ) { Icon( if (viewModel.isRecording) Icons.Default.Stop else Icons.Default.Videocam, "Record video" ) } } } } ``` Compose 우선 구현에 대해서는 [Jetpack Compose 면접 질문 모음](/technologies/android/interview-questions/android-compose)에서 카메라 상태 관리에 직접 적용할 수 있는 ViewModel 통합 패턴을 설명합니다. ## 카메라 권한 처리 Android에서는 CAMERA 권한의 런타임 획득이 필요합니다. RECORD_AUDIO는 오디오가 포함된 동영상 녹화 시에만 필요합니다. 권한 플로우에서는 요청 전에 접근이 필요한 이유를 설명해야 합니다. ```kotlin // PermissionHandler.kt class CameraPermissionHandler(private val activity: ComponentActivity) { private val requiredPermissions = arrayOf( Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO ) private val permissionLauncher = activity.registerForActivityResult( ActivityResultContracts.RequestMultiplePermissions() ) { permissions -> val cameraGranted = permissions[Manifest.permission.CAMERA] == true val audioGranted = permissions[Manifest.permission.RECORD_AUDIO] == true if (cameraGranted) { onCameraPermissionGranted(audioGranted) } else { onCameraPermissionDenied() } } fun checkAndRequestPermissions() { when { hasAllPermissions() -> onCameraPermissionGranted(hasAudioPermission()) shouldShowRationale() -> showPermissionRationale() else -> permissionLauncher.launch(requiredPermissions) } } private fun hasAllPermissions(): Boolean { return requiredPermissions.all { permission -> ContextCompat.checkSelfPermission( activity, permission ) == PackageManager.PERMISSION_GRANTED } } private fun hasAudioPermission(): Boolean { return ContextCompat.checkSelfPermission( activity, Manifest.permission.RECORD_AUDIO ) == PackageManager.PERMISSION_GRANTED } private fun shouldShowRationale(): Boolean { return requiredPermissions.any { permission -> ActivityCompat.shouldShowRequestPermissionRationale(activity, permission) } } } ``` ## CameraX 면접 질문 CameraX는 카메라 통합, 라이프사이클 관리, 하드웨어 추상화에 대해 논의할 때 Android 면접에서 출제됩니다. 이러한 질문들은 아키텍처와 실제 구현 세부사항에 대한 이해도를 테스트합니다. ### Camera2 대신 CameraX를 사용하는 이유 CameraX는 Camera2가 직접 노출하는 디바이스별 문제를 추상화합니다. Camera2 구현에서는 수백 가지의 디바이스별 해결책을 처리해야 합니다. CameraX는 이러한 수정 사항을 라이브러리에 통합하고 150개 이상의 디바이스 모델을 커버하는 [CameraX Test Lab](https://android-developers.googleblog.com/2025/11/introducing-camerax-15-powerful-video.html)에서 테스트됩니다. 라이프사이클 인식 바인딩을 통해 수동 리소스 관리가 불필요해집니다. Camera2에서는 모든 라이프사이클 콜백에서 올바르게 처리하지 않으면 누수를 일으키는 명시적인 open/close 호출이 필요합니다. ### CameraProvider는 어떻게 유스케이스를 라이프사이클에 바인딩하는가 CameraProvider는 내부적으로 LifecycleObserver 패턴을 사용합니다. `bindToLifecycle()`을 호출하면 제공된 LifecycleOwner에 옵저버를 등록합니다. 이 옵저버는 ON_START와 ON_STOP 이벤트를 수신하여 카메라를 열고 닫습니다. 중요한 포인트: CameraX는 바인딩 시 즉시 카메라를 시작하지 않습니다. 라이프사이클이 STARTED 상태에 도달할 때까지 대기하며, 이는 Activity.onStart() 타이밍과 일치합니다. ### 호환되지 않는 유스케이스를 바인딩하면 어떻게 되는가 모든 유스케이스 조합이 모든 디바이스에서 동작하는 것은 아닙니다. Preview + VideoCapture + ImageAnalysis + ImageCapture는 구형 하드웨어에서 실패할 수 있습니다. CameraX는 런타임이 아닌 바인딩 시점에 IllegalArgumentException을 던집니다. 해결책은 복잡한 조합을 바인딩하기 전에 `CameraProvider.hasCamera()`와 디바이스 기능을 확인하는 것입니다. 중요한 앱에서는 저사양 디바이스에서 명시적으로 테스트해야 합니다. ### CAPTURE_MODE_MINIMIZE_LATENCY와 CAPTURE_MODE_MAXIMIZE_QUALITY의 차이 MINIMIZE_LATENCY는 더 빠른 처리 파이프라인을 사용하고 일부 후처리를 건너뛰어 셔터 지연을 줄입니다. MAXIMIZE_QUALITY는 지원되는 디바이스에서 완전한 HDR+ 처리, 노이즈 감소, 멀티프레임 캡처를 적용합니다. 트레이드오프: 레이턴시 모드는 100-200ms에 캡처하고, 퀄리티 모드는 조명 조건에 따라 500ms-1s가 걸릴 수 있습니다. SNS 앱은 보통 레이턴시를 선택하고, 문서 스캔 앱은 퀄리티를 선택합니다. ### CameraX는 디바이스 회전을 어떻게 처리하는가 CameraX는 타겟 회전 설정을 통해 회전을 자동으로 처리합니다. 기본적으로 디스플레이 회전을 사용하지만 앱은 `setTargetRotation()`으로 오버라이드할 수 있습니다. 출력 이미지의 EXIF 데이터에는 올바른 방향 태그가 포함됩니다. PreviewView의 경우 scaleType이 종횡비 차이를 처리합니다. FILL_CENTER는 채우기 위해 크롭하고, FIT_CENTER는 레터박스를 표시합니다. 선택은 앱이 풀스크린 프리뷰를 우선시하는지 크롭을 피하는 것을 우선시하는지에 따라 결정됩니다. ## CameraX 1.6 의존성 및 설정 CameraX 라이브러리는 여러 아티팩트로 분리되어 있습니다. APK 크기를 최소화하기 위해 앱이 사용하는 것만 포함해야 합니다. ```kotlin // build.gradle.kts (Module: app) dependencies { val cameraxVersion = "1.6.2" // Core library required for all use cases implementation("androidx.camera:camera-core:$cameraxVersion") // Camera2 implementation (required) implementation("androidx.camera:camera-camera2:$cameraxVersion") // Lifecycle integration implementation("androidx.camera:camera-lifecycle:$cameraxVersion") // VideoCapture use case implementation("androidx.camera:camera-video:$cameraxVersion") // PreviewView and camera UI components implementation("androidx.camera:camera-view:$cameraxVersion") // Optional: Extensions (Night mode, HDR, etc.) implementation("androidx.camera:camera-extensions:$cameraxVersion") // Optional: Real-time effects implementation("androidx.camera:camera-effects:$cameraxVersion") } android { // CameraX requires Java 11 bytecode compileOptions { sourceCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11 } } ``` 매니페스트에는 기능 선언과 권한이 필요합니다: ```xml ``` CameraX의 ListenableFuture 호출을 suspend 함수로 래핑할 때는 [Kotlin Coroutines](/blog/android/mastering-kotlin-coroutines)의 관련 패턴이 적용됩니다. ## CameraX 1.6이 프로덕션 앱에 미치는 영향 CameraX 1.6은 기존 구현에 영향을 미치는 세 가지 변경사항을 가져왔습니다: 1. **CameraPipe 백엔드**: 내부 카메라 스택이 Pixel 카메라와 동일한 아키텍처인 CameraPipe를 사용하게 되었습니다. 이를 통해 Pixel 디바이스에서의 성능이 향상되고 고급 기능을 위한 더 깔끔한 기반이 제공됩니다. 기존 코드에 변경은 필요 없습니다. 2. **Media3 Muxer 기본화**: VideoCapture가 MediaMuxer 대신 Media3 Muxer를 사용하게 되었습니다. 이를 통해 크래시 내성이 향상되어 녹화 중 앱이 종료되어도 동영상 파일은 재생 가능한 상태로 유지됩니다. API 서피스에 변경은 없습니다. 3. **SessionConfig API 안정화**: 앱은 experimental 어노테이션 없이 SessionConfig를 통해 고급 카메라 설정을 구성할 수 있게 되었습니다. 이를 통해 지원 디바이스에서의 전면/후면 카메라 동시 사용 등의 기능이 가능해집니다. **마이그레이션 경로**: 의존성 버전을 업데이트합니다. 이전에 문제가 있었던 디바이스에서 테스트합니다. [CameraX 릴리스 노트](https://developer.android.com/jetpack/androidx/releases/camera)에 버전 간 동작 변경사항이 기록되어 있습니다. - CameraX는 라이프사이클 인식 바인딩 사용: `bindToLifecycle()`은 유스케이스를 카메라와 Activity 라이프사이클 모두에 연결 - ImageCapture 모드는 레이턴시와 품질의 트레이드오프: 디바이스 기능이 아닌 유스케이스에 기반하여 선택 - VideoCapture는 바인딩 전 Recorder 설정 필요: 시작 전에 출력 옵션 준비 - 권한 처리는 요청 전에 목적 설명: 거부율 감소 - CameraX 1.6은 내부적으로 CameraPipe로 마이그레이션: API 변경 불필요하지만 타겟 디바이스에서 테스트 필요 - 면접 질문은 라이프사이클 관리, 유스케이스 조합, Camera2 비교에 집중 --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/ko/blog/android/camerax-photo-video-capture-interview-questions