[Compose] BottomSheet에 네이버 지도 적용하기

h2on ·2023년 6월 15일
6

Android

목록 보기
2/4
post-thumbnail

사용자가 설정한 장소에 도착하면 알아서 체크해주는 투두리스트 두두
스토어 출시하면 많은 관심 부탁드립니다! 🐥

🐥 Compose도 드디어 지도를!

과거 Compose를 사용할 때면, 구글맵, 네이버 맵 등 지도 API가 Compose를 지원하지 않아 AndroidView를 통해 개발하거나, 개발시 제약사항이 매우 많았다. 하지만 최근에는 대부분의 지도 API가 Compose를 공식적으로 지원하면서 개발이 조금 더 수월해졌다. 두두에서는 BottomSheet에 네이버 지도를 사용하게 되었는데, 어떤 문제가 있었고, 어떻게 해결했는지 알아보자.

시작하기 전에

Naver Map ComposeModalBottomSheetLayout(Compose Material Design에 포함 된 항목)을 사용하여 개발했다.


Dependency

implementation "io.github.fornewid:naver-map-compose:<version>"

Compose 버전마다 맞는 Naver Map Compose 버전이 있으니 참고하자.

클라이언트 키 발급

네이버 클라우드 플랫폼에서 클라이언트 키를 발급받아야 한다. (설명대로 따라하면 금방 발급받음)

클라이언트 키 지정

발급받은 클라이언트 키를 앱에 지정해줘야 한다. 그러나 앱에 그냥 클라이언트 키를 넣어서 사용하면 큰일난다. 반드시 숨겨야 우리 모두가 행복해진다. 😙 아래의 방법을 통해서 클라이언트 키를 숨겨보자.

1. local.properties에 클라이언트 키 생성

local.properties는 직접 프로젝트를 열어서 보지 않는 이상 확인할 수 없다. (Github에는 .gitignore에 이미 포함되어 있음)

sdk.dir=SDK 경로
naver_api_key=발급받은 클라이언트 ID

2. Build.gradle (Module: app)

local.properties에 클라이언트 키를 넣었다면, build.gradle에서 설정이 필요하다.

Properties properties = new Properties()
properties.load(project.rootProject.file('local.properties').newDataInputStream())
def naver_client_id = properties.getProperty('naver_client_id')

defaultConfig {
	...
    //Manifest에서 사용할 수 있도록 함
    manifestPlaceholders = [NAVER_CLIENT_ID: naver_client_id]
}

3. Manifest

Build.gradle 설정까지 마쳤다면, 마지막으로 Manifest에 지정하면 끝이다.

<application>
  ...
  <meta-data
  	android:name="com.naver.maps.map.CLIENT_ID"
    android:value="${NAVER_CLIENT_ID}"/>
</application>

ModalBottomSheetLayout

이번에도 Compose BottmSheet에 대한 자료가 진짜 없다. (항상 없네? 그래도 공식적으로 바텀싯을 제공해줬으니 감사하다..)

ModalBottomSheetLayout 살펴보기

sheetContent : BottomSheet에 보여지는 UI (바텀싯을 구성하는 콘텐츠)
sheetState : ModalBottomSheetLayout의 상태
sheetGesture : BottomSheet가 제스처로 상호작용이 가능한지의 여부

개인적으로 중요한 내용은 이 세 가지라고 생각한다. (나머지는 UI관련이기 때문에)

Composable 개발

ModalBottomSheetLayoutNaver Map을 이용하여 개발해보자.

@Composable
fun MapBottomSheet(
	modifier: Modifier = Modifier
) {
	val coroutineScope = rememberCoroutineScope()
    val sheetState = rememberModalBottomSheetState(
        initialValue = ModalBottomSheetValue.Hidden,
        skipHalfExpanded = true
    )
    
    // BottomSheet가 보이는 상태에서 BackPressed시 BottomSheet를 닫음
    BackHandler(sheetState.isVisible) {
        coroutineScope.launch { sheetState.hide() }
    }
    
    ModalBottomSheetLayout(
        sheetContent = { HomeTodoLocationBottomSheet(modifier = Modifier.padding(20.dp)) },
        sheetState = sheetState,
        sheetBackgroundColor = gray09,
        sheetShape = RoundedCornerShape(topStart = 20.dp, topEnd = 20.dp),
    ) {
    	Box(modifier = modifier) {
        	NaverMap(modifier = Modifier.fillMaxSize())
            ...
        }
    }
}

sheetState

  • initialValue 를 Hidden으로 설정해 처음에 BottomSheet가 표시되지 않음
  • skipHalfExpanded를 true로 설정하여 HalfExpanded 상태를 무시함

🤯 끝인줄 알았는데..?

BottomSheet가 터치 이벤트를 다 가져가서 지도를 움직일 수 없다.... 🫠🫠🫠

NaverMap 터치 이벤트가 BottomSheet 의 터치 이벤트에 막혀 지도 이동이 불가능하다는 사실을 잊어버리고 있었다. 그래서 어떻게 해결할까 고민하다가, cameraPositionState.isMoving == true 일 때 BottomSheet 를 비활성화 시켜 터치 이벤트를 온전히 지도가 가져가는 방법을 시도하려 했으나, 해당 BottomSheet에는 enabled 관련 설정을 할 수가 없어서 구현하지 못했다.

유레카 🥳

엄청난 삽질을 하다가 Modifier 속성 중 pointerInput 을 찾게 되었다. Compose 에서 제스처를 감지하기 위해 사용한다. PointerInputScope.detectDragGestures 를 통해 사용자의 드래그 입력을 감지하고 처리할 수 있게 도와준다.

NaverMap(
	modifier = Modifier
    	.fillMaxSize()
        .pointerInput(Unit) {
        	detectDragGestures { _, dragAmount ->
            	cameraPositionState.move(CameraUpdate.scrollBy(
                	PointF(dragAmount.x, dragAmount.y)
                ))
            }
        },
        cameraPositionState = cameraPositionState
) {
...
}

cameraPositionState.move() 에 사용자가 드래그중인 x, y값을 가져와 설정해준다.

마치며

Compose로 두두를 개발 하면 할 수록 머리카락이 다 빠질 것만 같다. 지도를 굳이 바텀시트에 올려서 사용할 사람은 많지 않을 것이라 생각하지만, 그래도 쓰는 사람이 있다면 이 글을 통해 나와 같은 이슈를 삽질하지 않고 편하게 해결하길 바란다. 다음에는 어떤 기능이 말썽을 부릴지 모르지만 도움이 될 수 있는 내용을 들고 찾아오겠다.

다음 삽질도 정말 기대된다. 스토어 랭킹에 두두가 보이는 그날까지 💪

profile
돈 버는 백수가 꿈

2개의 댓글

comment-user-thumbnail
2023년 9월 30일
    manifestPlaceholders = [NAVER_CLIENT_ID: naver_client_id]

이거 집어넣으면 아래 에러 발생하는데 어떻게 해결하나요?

A build operation failed.
Could not create task ':app:createDebugVariantModel'.
Could not create task ':app:createDebugVariantModel'.

1개의 답글