0Pricing
Android Academy · 강의

카메라 미리보기 표시

실시간 미리보기를 연결합니다.

카메라 미리보기 표시은(는) CoddyKit의 무료 Android Academy 강의입니다. 이것은 4개 중 2번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Android Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Android Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

목표: 실시간 미리보기

거의 모든 카메라 앱에 가장 먼저 필요한 것은 실시간 미리보기입니다. 실시간 미리보기는 카메라가 보는 장면을 실시간으로 보여 줍니다. 이 강의에서는 카메라 영상을 전체 화면으로 표시하는 Compose 화면을 만듭니다.

PreviewView, Preview 사용 사례, 바인딩된 ProcessCameraProvider라는 세 가지 요소를 결합합니다.

카메라 권한 요청

미리보기를 표시하기 전에 사용자의 권한을 받아야 합니다. Compose에서는 Accompanist(또는 공식 권한 API)의 rememberPermissionState를 사용하면 쉽게 처리할 수 있습니다.

권한이 granted된 후에만 카메라를 시작해야 합니다.

@OptIn(ExperimentalPermissionsApi::class)
@Composable
fun CameraScreen() {
    val permission = rememberPermissionState(Manifest.permission.CAMERA)

    LaunchedEffect(Unit) { permission.launchPermissionRequest() }

    if (permission.status.isGranted) {
        CameraPreview()
    } else {
        Text("Camera permission is required.")
    }
}

Compose에 PreviewView 포함하기

PreviewView는 기존 Android View이므로 AndroidView를 사용해 Compose 내부에서 호스팅합니다. factory 람다가 뷰를 한 번 생성하고, 설정에 사용할 참조를 제공합니다.

생성된 뷰를 저장해 두어야 카메라 표면을 뷰에 연결할 수 있습니다.

@Composable
fun CameraPreview(modifier: Modifier = Modifier) {
    AndroidView(
        modifier = modifier.fillMaxSize(),
        factory = { context ->
            PreviewView(context).apply {
                scaleType = PreviewView.ScaleType.FILL_CENTER
            }
        }
    )
}

LifecycleOwner 가져오기

카메라를 바인딩하려면 LifecycleOwner가 필요합니다. Compose에서는 LocalLifecycleOwner.current로 현재 수명 주기 소유자를 읽고, LocalContext.current로 컨텍스트를 읽습니다.

카메라 프로바이더를 설정하고 사용 사례를 바인딩하려면 둘 다 필요합니다.

val context = LocalContext.current
val lifecycleOwner = LocalLifecycleOwner.current

Preview 사용 사례 만들기

Preview 사용 사례는 프레임을 표면으로 스트리밍합니다. 이를 만든 후 뷰의 표면 프로바이더를 사용해 setSurfaceProvider를 호출하면 PreviewView에 연결할 수 있습니다.

이 연결이 있어야 카메라 영상이 실제로 화면에 표시됩니다.

val preview = Preview.Builder().build()
preview.setSurfaceProvider(previewView.surfaceProvider)

코루틴 내부에서 바인딩하기

프로바이더를 가져오는 작업은 비동기이므로 코루틴에서 처리해야 합니다. 뷰에 연결된 LaunchedEffect를 사용하면 한 번만 실행됩니다. 내부에서 프로바이더를 await하고, 이전에 연결된 항목을 해제한 다음 미리보기를 바인딩합니다.

LaunchedEffect(previewView) {
    val cameraProvider = ProcessCameraProvider.getInstance(context).await()
    val preview = Preview.Builder().build().also {
        it.setSurfaceProvider(previewView.surfaceProvider)
    }
    cameraProvider.unbindAll()
    cameraProvider.bindToLifecycle(
        lifecycleOwner,
        CameraSelector.DEFAULT_BACK_CAMERA,
        preview
    )
}

await() 확장 함수

ProcessCameraProvider.getInstance()는 ListenableFuture를 반환합니다. kotlinx-coroutines-guava의 await() 확장 함수는 결과가 준비될 때까지 일시 중지하여 콜백 코드를 깔끔한 순차 코드로 바꿔 줍니다.

사용하려면 org.jetbrains.kotlinx:kotlinx-coroutines-guava 종속 항목을 추가합니다.

import kotlinx.coroutines.guava.await

// Without await you'd use a listener:
// future.addListener({ val provider = future.get() }, executor)
// await() makes it a single suspend call instead.

하나로 연결하기

다음은 완성된 미리보기 컴포저블입니다. factory에서 PreviewView를 만들고 이를 가져온 다음 LaunchedEffect 내부에서 바인딩하는 흐름에 주목하세요.

@Composable
fun CameraPreview(modifier: Modifier = Modifier) {
    val context = LocalContext.current
    val lifecycleOwner = LocalLifecycleOwner.current
    val previewView = remember { PreviewView(context) }

    LaunchedEffect(previewView) {
        val provider = ProcessCameraProvider.getInstance(context).await()
        val preview = Preview.Builder().build().also {
            it.setSurfaceProvider(previewView.surfaceProvider)
        }
        provider.unbindAll()
        provider.bindToLifecycle(
            lifecycleOwner,
            CameraSelector.DEFAULT_BACK_CAMERA,
            preview
        )
    }

    AndroidView(
        factory = { previewView },
        modifier = modifier.fillMaxSize()
    )
}

크기 조정 방식

카메라 센서의 화면 비율은 사용 중인 화면의 비율과 거의 일치하지 않습니다. PreviewView.ScaleType은 영상이 뷰에 맞춰지는 방식을 제어합니다.

  • FILL_CENTER — 뷰를 가득 채우며 가장자리를 잘라 냅니다(가장 일반적인 방식).
  • FIT_CENTER — 레터박스를 추가해 전체 프레임을 표시합니다.

몰입감 있는 전체 화면 카메라를 만들려면 FILL_CENTER를 선택하세요.

previewView.scaleType = PreviewView.ScaleType.FILL_CENTER
// or
previewView.scaleType = PreviewView.ScaleType.FIT_CENTER

카메라 전환하기

전면 카메라와 후면 카메라 사이를 전환하려면 선택기를 상태로 저장하고 선택기가 변경될 때 다시 바인딩합니다. LaunchedEffect는 키가 변경되면 다시 실행되므로 선택기를 키로 포함하기만 하면 됩니다.

var selector by remember { mutableStateOf(CameraSelector.DEFAULT_BACK_CAMERA) }

LaunchedEffect(selector) {
    val provider = ProcessCameraProvider.getInstance(context).await()
    provider.unbindAll()
    provider.bindToLifecycle(lifecycleOwner, selector, preview)
}

// Flip button toggles 'selector' between front and back.

흔히 발생하는 문제

다음 두 가지 실수로 미리보기가 검은색으로 표시됩니다.

  • setSurfaceProvider를 호출하지 않음 — 프레임을 전달할 곳이 없습니다.
  • 다시 바인딩하기 전에 unbindAll()을 호출하지 않음 — 사용 사례를 두 번 바인딩하면 CameraX에서 오류가 발생합니다.

또한 권한이 실제로 허용될 때까지 미리보기가 시작되지 않는다는 점을 기억하세요.

빠른 확인

미리보기를 연결하는 방법을 얼마나 이해했는지 확인해 보세요.

복습

Compose에서 실시간 카메라 미리보기를 만들었습니다.

  • 시작하기 전에 CAMERA 권한을 요청합니다.
  • AndroidView를 사용해 PreviewView를 호스팅합니다.
  • 코루틴에서 ProcessCameraProvider를 await합니다.
  • Preview를 만들고, setSurfaceProvider와 unbindAll()을 호출한 다음 bindToLifecycle합니다.

다음으로 ImageCapture 사용 사례를 추가하고 실제 사진을 촬영해 보겠습니다.

자주 묻는 질문

“카메라 미리보기 표시” 강의는 무료인가요?

네 — “카메라 미리보기 표시” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Android Academy 강의 전체를 잠금 해제할 수 있습니다. Android Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“카메라 미리보기 표시”에서 뭘 배우나요?

실시간 미리보기를 연결합니다. 브라우저에서 직접 실행하는 실습 코드로 Android Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

Android Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 Android Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 2번째 강의입니다.

“카메라 미리보기 표시” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 Android Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 Android Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. CameraX 개요
  2. 카메라 미리보기 표시
  3. 사진 촬영
  4. 동영상 녹화
← Android Academy(으)로 돌아가기