카메라 미리보기 표시
실시간 미리보기를 연결합니다.
카메라 미리보기 표시은(는) 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.currentPreview 사용 사례 만들기
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 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.
이 강의의 모든 강의
- CameraX 개요
- 카메라 미리보기 표시
- 사진 촬영
- 동영상 녹화