외국인 관광객을 위한 앱 만들기 : Jetpack Compose에서 Google Maps로 실시간 위치 추적 및 야간 모드 적용하기
dark mode google map
Jetpack Compose에서 Google Map을 전면에 표시하고, 실시간으로 위치를 추적하며 지도 카메라를 이동시키고, 야간 모드까지 적용하는 방법을 단계별로 정리합니다.
1. Hilt ViewModel에서 위치 실시간 추적
FusedLocationProviderClient를 활용하여 위치를 계속 추적합니다:
@HiltViewModel class LocationViewModel @Inject constructor( @ApplicationContext private val context: Context ) : ViewModel() {
private val fusedLocationClient = LocationServices.getFusedLocationProviderClient(context) private val _locationFlow = MutableStateFlow<LatLng?>(null) val locationFlow: StateFlow<LatLng?> = _locationFlow
init { val request = LocationRequest.Builder(Priority.PRIORITY_HIGH_ACCURACY, 3000).build() if (ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { fusedLocationClient.requestLocationUpdates(request, locationCallback, Looper.getMainLooper()) } }
override fun onCleared() { fusedLocationClient.removeLocationUpdates(locationCallback) } }
2. Compose에서 지도와 위치 UI 표시
ViewModel의 위치를 수신하여 Google Map 카메라를 이동시킵니다:
@Composable fun RealTimeLocationMapScreen(viewModel: LocationViewModel = hiltViewModel()) { val cameraPositionState = rememberCameraPositionState() val currentLocation by viewModel.locationFlow.collectAsState()
@Composable fun getMapStyleByUiMode(context: Context): MapStyleOptions { return if (isSystemInDarkTheme()) { MapStyleOptions.loadRawResourceStyle(context, R.raw.google_night_style) } else { MapStyleOptions.loadRawResourceStyle(context, R.raw.google_day_style) } }
마무리
Jetpack Compose를 활용해 Google Maps에 실시간 위치를 반영하고, 다양한 사용자 경험을 위해 UI 옵션 및 스타일을 적용하는 방법을 정리했습니다.
야간 모드 스타일링이나 위치 추적 성능 최적화는 향후 앱 완성도에 매우 중요하므로 잘 적용해 보시기 바랍니다.
눈을 뜬 순간—
세상은 이상할 정도로 조용했다.
햇살은 부드럽고,
창문 밖으론 고양이 울음소리와 나뭇잎 흔들리는 소리만 들렸다.
휴대폰도, 회사 메신저도, 회의 알림도 없었다.
모든 게 멈춰 있었다.
아니, 아니었다. 모든 게… 돌아가 있었다.
그는 침대에서 몸을 일으켰다.
하지만 이건 지금 그의 방이 아니었다.
묘하게 낯익고, 오래된 구조의 작은 방.
벽엔 그가 어릴 적 좋아하던 로봇 포스터가 붙어 있었고,
서랍 속엔 세월이 덮은 색연필과 딱지들이 가지런히 들어 있었다.
거울을 보았다.
자신은 여전히 지금의 모습이었다.
하지만 세상은—1996년의 어느 봄날로 돌아가 있었다.
그는 마당으로 나갔다.
햇빛이 따뜻하게 뺨을 쓸고 지나갔다.
그리운 흙냄새.
옆집 아주머니가 부르는 개 짖는 소리.
멀리서 들리는, 아이들의 웃음소리.
그리고 그 소리.
딱, 그 소리.
“…그 아이?”
그는 발길을 따라 골목길로 향했다.
그리고 거기— 시간 속에 묻혀 있던 그 장면이 다시 펼쳐졌다.
철문 앞에 서 있는 작은 아이.
긴 머리, 수줍은 눈빛.
그는 너무 놀라서 말을 잊었다.
“...안녕.”
그 아이가 말했다.
그 순간, 그는 어린 자신이 아니라 지금의 시선으로 그 아이를 바라보고 있었다.
기억 속 그 목소리, 그 표정.
정확히, 그대로였다.
그는 웃음과 눈물이 섞인 얼굴로 인사했다.
“…안녕.”
그 하루는 마치 기적 같았다.
함께 숨바꼭질을 하고,
아이스크림을 나눠 먹고,
작은 비밀기지를 만들어 웃던 하루.
그는 처음엔 어색했지만,
점점 이 시간이 기억이 아니라 현실이라는 것을 받아들이기 시작했다.
이 하루는 너무 완벽했다. 다시 살아도 좋을 만큼, 완벽하게 아름다웠다.
그는 속으로 중얼거렸다.
‘이 하루를 다시 산다면,
잃게 될 것이 있어도…
후회하지 않을 수 있을까?’
해가 뉘엿뉘엿 기울 무렵,
그 아이가 작은 목소리로 말했다.
“내일도 볼 수 있어?”
그는 대답하지 못했다.
목이 멨고, 마음이 흔들렸다.
왜냐면— 내일은 없다는 걸 그는 알고 있었으니까.
해가 완전히 지자,
세상은 다시 조용해졌다.
그리고 어두운 가게 안,
그는 눈을 떴다.
모래시계가 멈춰 있었다.
그는 여전히 그대로였다.
하지만 뭔가— 무언가가 사라졌다는 느낌이 들었다.
주인은 말없이 서 있었다.
눈빛은 조용히 그를 바라보고 있었다.
“…무엇을 잃었죠?”
그가 물었다.
주인은 천천히 말했다.
“그건 당신만이 알게 될 겁니다.
곧, 아주 가까운 시간 안에.”
아무튼 새로운 제약 사항이 생겼습니다. 시행이 6월 1일 이라니 준비를 해야 할 것 같습니다.
아래 글은 adsens 지원팀의 게시글 입니다.
티스토리 광고 설정 정책에 관해 중요한 안내사항이 있어 전달드립니다.
티스토리 커뮤니티에 공지된 바와 같이, 티스토리의 자체 광고 운영 정책 변경 결정에 따라 티스토리에서 “앵커 광고와 오퍼월 광고”는 설정이 불가능해집니다. 애드센스 자동광고 설정에서 앵커 광고를 켜는 것과 직접 html 스크립트를 통해 구현하는 것을 포함 모든 방식의 앵커 광고와 오퍼월 광고는 허용되지 않습니다.
자동 광고에서 앵커 광고 끄는 방법: 애드센스 로그인 왼쪽 패널에서 “광고” 클릭 해당 사이트에 “연필” 아이콘 클릭 “광고 설정”에서 “오버레이 형식” 클릭 “형식”에서 “앵커 광고” 설정 OFF 하기
오퍼월 광고 끄는 방법: 애드센스 로그인 왼쪽 패널에서 “개인 정보 보호 및 메시지” 클릭 Offerwall “관리” 클릭 게시 버튼을 끄거나 “⋮” 버튼을 클릭하여 오퍼월 메시지를 삭제.
6월 1일 (월)부터 설정 불가 규제가 시작되니, 이 점 참고하셔서 적절한 조치를 취해주시면 감사하겠습니다.
Gradle 프로젝트를 설정할 때, buildSrc 디렉토리에서 id("com.google.devtools.ksp") 플러그인을 찾을 수 없다는 오류가 발생하는 경우가 있습니다. 이는 Gradle의 빌드 스크립트 컨텍스트와 플러그인 정의 위치 사이의 차이로 인해 발생합니다.
이번 글에서는 이 문제를 해결하는 방법에 대해 단계별로 알아보겠습니다.
1. buildSrc 프로젝트의 build.gradle.kts 수정
buildSrc는 Gradle의 독립적인 빌드 환경으로 동작하기 때문에, 사용하려는 플러그인을 명시적으로 추가해야 합니다. buildSrc/build.gradle.kts 파일을 열고 다음을 추가하세요:
위 단계를 따르면 buildSrc 디렉토리에서도 com.google.devtools.ksp 플러그인을 정상적으로 사용할 수 있습니다. 문제가 계속 발생한다면, module.kotlin.gradle.kts 파일이나 관련 Gradle 설정 파일의 내용을 검토하여 추가적인 설정이 필요한지 확인하세요.
이 글이 Gradle 프로젝트 설정에 도움이 되길 바랍니다. 질문이나 피드백이 있다면 댓글로 남겨주세요!
val grantedPermissions = healthConnectClient.permissionController.getGrantedPermissions()
if (grantedPermissions.contains(HealthPermission.READ_STEPS)) {
println("걸음 수 읽기 권한이 이미 허용되었습니다.")
}
거부된 권한 처리:
권한이 거부된 경우 사용자에게 알림을 표시하거나 재요청을 유도합니다.
Toast.makeText(context, "Health Connect 권한이 필요합니다.", Toast.LENGTH_SHORT).show()
📌 5. 추가 참고 사항
APK 설치:
Android 13 이하에서는 Health Connect APK를 Google Play에서 설치해야 합니다. Health Connect APK
Android 14 이상:
Android 14 이상에서는 Health Connect가 기본적으로 기기에 내장되어 있습니다.