Today's

길을 나서지 않으면 그 길에서 만날 수 있는 사람을 만날 수 없다

모바일 앱(안드로이드)

외국인 관광객을 위한 앱 만들기 : Jetpack Compose + ARCore + Google Maps로 위치 기반 AR 구현하기

Billcorea 2025. 6. 3. 15:15
반응형

 

 

📍 Jetpack Compose + ARCore + Google Maps로 위치 기반 AR 구현하기

가이드앱 초안 이미지

 

ARCore의 Geospatial API는 GPS 좌표와 같은 실제 세계의 위치를 기준으로 가상 객체를 배치할 수 있는 기능을 제공합니다. 본 게시물에서는 Jetpack ComposeGoogle Maps Compose를 활용해 지도와 AR 콘텐츠를 동시에 표시하는 방법을 소개합니다.

💡 이 예제는 관광지 안내, 실외 AR 내비게이션, 위치 기반 게임 등에 활용할 수 있습니다.

1️⃣ 프로젝트 구성 및 의존성 추가

build.gradle.kts에 다음 라이브러리들을 추가합니다.

dependencies {
    implementation("com.google.maps.android:maps-compose:4.1.1")
    implementation("com.google.android.gms:play-services-maps:18.2.0")
    implementation("com.google.ar:core:1.43.0")
}

추가적으로 ARCore 사용을 위한 권한 및 카메라 기능도 AndroidManifest에 설정해야 합니다.


2️⃣ ARCore 렌더러 구현 (ArCoreRenderer)

ARCore 프레임을 받아서 SurfaceView 위에 AR 객체를 렌더링하는 ArCoreRenderer 클래스를 작성합니다. Geospatial API를 통해 현재 위치의 위도, 경도, heading 값을 받아 지도 위치와 연동할 수 있습니다.

if (earth?.trackingState == TrackingState.TRACKING) {
    val pose = earth.cameraGeospatialPose
    Log.d("Geo", "Lat: ${pose.latitude}, Lng: ${pose.longitude}, Heading: ${pose.heading}")
}

지도의 좌표를 클릭했을 때 해당 위치에 Anchor를 생성해 AR 콘텐츠를 띄울 수 있습니다.

fun onMapClick(latLng: LatLng) {
    val earth = session?.earth ?: return
    if (earth.trackingState != TrackingState.TRACKING) return

    earthAnchor?.detach()
    earthAnchor = earth.createAnchor(
        latLng.latitude, latLng.longitude, 0.0,
        0f, 0f, 0f, 1f // 회전값 (Quaternion)
    )
}

3️⃣ AR SurfaceView를 Compose에 포함시키기

Jetpack Compose에서는 AndroidView를 통해 SurfaceView를 삽입합니다.
렌더링 준비가 완료되면 SampleRender를 실행해 OpenGL 렌더링 루프를 시작합니다.

@Composable
fun ArSurfaceView(
    modifier: Modifier = Modifier,
    onSurfaceReady: (SurfaceView) -> Unit
) {
        AndroidView(
            factory = { context ->
                val renderer = HelloArRenderer(
                    context = context,
                    sessionProvider = sessionProvider,
                    latLng = currentLocation?.let {
                        LatLng(
                            it.latitude,
                            it.longitude
                        )
                    } ?: LatLng(37.5665, 126.9780),
                    tapQueue = tapQueue // 🔥 renderer로 전달
                ) // 예시
                sessionProvider.setOnRendererEventCallback { event ->
                    rendererEvent = event
                }
                sessionProvider.setOnTrackingMessageCallBack { message ->
                    trackingMessage = message
                }
                SampleRender(glSurfaceView, renderer, context.assets)
                glSurfaceView.renderMode = GLSurfaceView.RENDERMODE_CONTINUOUSLY
                glSurfaceView
            },
            modifier = Modifier.fillMaxSize()
                .pointerInteropFilter { motionEvent ->
                    if (motionEvent.action == MotionEvent.ACTION_UP) {
                        tapQueue.offer(motionEvent) // 🔥 터치 이벤트 큐에 전달
                    }
                    true
                }
        )
}

4️⃣ 지도와 AR 통합 Compose 화면 만들기

GoogleMap과 AR SurfaceView를 하나의 Compose 화면에 겹쳐서 배치합니다. 지도를 클릭하면 해당 좌표에 Anchor를 생성하고 AR로 렌더링합니다.

@Composable
fun ArWithMapScreen(activity: MainComposeActivity) {
    val lifecycleOwner = LocalLifecycleOwner.current
    val arRenderer = remember { ArCoreRenderer(activity) }

    DisposableEffect(Unit) {
        lifecycleOwner.lifecycle.addObserver(arRenderer)
        onDispose { lifecycleOwner.lifecycle.removeObserver(arRenderer) }
    }

    Box(modifier = Modifier.fillMaxSize()) {
        var clickedLatLng by remember { mutableStateOf<LatLng?>(null) }

        GoogleMap(
            modifier = Modifier.fillMaxSize(),
            onMapClick = { latLng ->
                clickedLatLng = latLng
                arRenderer.onMapClick(latLng)
            }
        )

        ArSurfaceView(
            modifier = Modifier
                .fillMaxSize()
                .zIndex(1f),
            onSurfaceReady = { surfaceView ->
                SampleRender(surfaceView, arRenderer, activity.assets)
            }
        )
    }
}

5️⃣ MainActivity 설정

Jetpack Compose와 ARCore를 통합한 MainActivity입니다. ArCoreSessionHelper는 세션 초기화 및 생명주기 처리를 돕습니다.

@AndroidEntryPoint
class MainComposeActivity : ComponentActivity() {
    lateinit var arCoreSessionHelper: ArCoreSessionHelper

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        arCoreSessionHelper = ArCoreSessionHelper(this)
        arCoreSessionHelper.initialize()

        setContent {
            ArWithMapScreen(this)
        }
    }

    override fun onResume() {
        super.onResume()
        arCoreSessionHelper.session?.resume()
    }

    override fun onPause() {
        super.onPause()
        arCoreSessionHelper.session?.pause()
    }
}

✅ 마무리

Jetpack Compose로 Google Map과 ARCore를 동시에 사용할 수 있다는 점은 매우 큰 장점입니다. UI를 선언적으로 작성하면서도 실시간 위치 기반 AR 콘텐츠를 렌더링할 수 있어, 차세대 사용자 경험을 만드는 데 적합한 아키텍처입니다.

  • 지도 기반 AR 관광 가이드
  • 위치 기반 보물 찾기 게임
  • 실외 AR 내비게이션
🚀 다음으로는 AR 오브젝트가 지도 위에 정확하게 위치하고, 거리/방향을 시각화하는 기능도 확장해보세요!
반응형