반응형
외국인을 위한 한국 여행 가이드 앱 개발 일지 - 위치 권한과 구글맵 화면 구현 (Jetpack Compose + Hilt)

오늘은 Jetpack Compose 기반으로 개발 중인 한국 여행 가이드 앱에서
인트로 화면 이후 위치 권한을 요청하고, 구글 지도를 표시하는 메인 화면을 구현했습니다.
✅ 오늘 구현한 핵심 기능
항목 | 구현 방식 |
---|---|
위치 권한 요청 | Accompanist Permissions |
위치 정보 획득 | FusedLocationProviderClient (Hilt 주입) |
지도 표시 | Google Maps Compose |
기본 위치 fallback | 서울 시청 (37.5665, 126.9780) |
권한 거부 시 안내 | 설정 화면으로 유도 (Intent) |
상단바 겹침 방지 | statusBarsPadding() 적용 |
📦 Hilt로 FusedLocationProviderClient 주입
@Module
@InstallIn(SingletonComponent::class)
object LocationModule {
@Provides
fun provideFusedLocationProviderClient(
@ApplicationContext context: Context
): FusedLocationProviderClient =
LocationServices.getFusedLocationProviderClient(context)
}
🧭 ViewModel에서 현재 위치 가져오기
@HiltViewModel
class MapViewModel @Inject constructor(
private val locationClient: FusedLocationProviderClient
) : ViewModel() {
var currentLocation by mutableStateOf<LatLng?>(null)
private set
fun fetchLocation(context: Context) {
try {
locationClient.lastLocation.addOnSuccessListener { location ->
location?.let {
currentLocation = LatLng(it.latitude, it.longitude)
}
}
} catch (e: SecurityException) {
e.printStackTrace()
}
}
}
🗺️ MapScreen Composable
@Composable
fun MapScreen(viewModel: MapViewModel = hiltViewModel()) {
val context = LocalContext.current
val permissionState = rememberMultiplePermissionsState(
listOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION
)
)
val defaultLocation = LatLng(37.5665, 126.9780)
val currentLocation = viewModel.currentLocation ?: defaultLocation
val cameraPositionState = rememberCameraPositionState {
position = CameraPosition.fromLatLngZoom(currentLocation, 15f)
}
LaunchedEffect(Unit) {
permissionState.launchMultiplePermissionRequest()
}
LaunchedEffect(permissionState.allPermissionsGranted) {
if (permissionState.allPermissionsGranted) {
viewModel.fetchLocation(context)
}
}
Scaffold(
topBar = {
TopAppBar(
title = { Text("한국 여행 가이드") },
modifier = Modifier.statusBarsPadding()
)
},
contentWindowInsets = WindowInsets.systemBars
) { paddingValues ->
Box(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues)
) {
when {
permissionState.allPermissionsGranted -> {
GoogleMap(
modifier = Modifier.fillMaxSize(),
cameraPositionState = cameraPositionState
) {
Marker(
state = MarkerState(position = currentLocation),
title = "현재 위치"
)
}
}
permissionState.shouldShowRationale -> {
PermissionExplanationUI {
permissionState.launchMultiplePermissionRequest()
}
}
else -> {
PermissionDeniedUI()
}
}
}
}
}
👮 권한 거절 대응 UI
@Composable
fun PermissionExplanationUI(onRequest: () -> Unit) {
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text("이 기능을 사용하려면 위치 권한이 필요합니다.")
Button(onClick = onRequest) {
Text("권한 요청하기")
}
}
}
@Composable
fun PermissionDeniedUI() {
val context = LocalContext.current
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text("위치 권한이 영구적으로 거부되었습니다.")
Button(onClick = {
context.startActivity(Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
data = Uri.fromParts("package", context.packageName, null)
})
}) {
Text("설정에서 권한 허용하기")
}
}
}
📌 상단바가 StatusBar와 겹칠 때 해결법
TopAppBar에 Modifier.statusBarsPadding()
을 추가하면 겹침 현상이 해결됩니다.
TopAppBar(
title = { Text("앱 이름") },
modifier = Modifier.statusBarsPadding()
)
또한 Scaffold
에 아래 설정을 함께 추가하는 것이 좋습니다:
contentWindowInsets = WindowInsets.systemBars
📘 다음 목표
- 실시간 위치 추적 추가
- 주변 관광지 마커 표시
- Navigation Compose와 통합
이 앱은 Jetpack Compose, Hilt, Room 등을 활용해 외국인들이 한국 여행을 쉽게 즐길 수 있도록 돕는 것이 목적입니다.
계속해서 개발 과정을 공유해보겠습니다! 👋
반응형
'모바일 앱(안드로이드)' 카테고리의 다른 글
외국인 관광객을 위한 앱 만들기 : Jetpack Compose + ARCore + Google Maps로 위치 기반 AR 구현하기 (1) | 2025.06.03 |
---|---|
외국인 관광객을 위한 앱 만들기 : Jetpack Compose에서 Google Maps로 실시간 위치 추적 및 야간 모드 적용하기 (0) | 2025.06.01 |
외국인관광객을 위한 다국어 환영 앱 첫 화면, Jetpack Compose로 구현하기 (4) | 2025.05.26 |
외국인 관광객을 위한 맞춤형 길찾기 앱의 초기 화면은? (1) | 2025.05.24 |
앱 기획 안) 외국인 관광객을 위한 맞춤형 길찾기 앱 개발 여정: 기획부터 초안까지 (1) | 2025.05.22 |