Displaying Maps - Picplz/picplz-aos GitHub Wiki

์นด์นด์˜ค ๋งต api ํ˜ธ์ถœํ•ด์„œ ์ง€๋„ ๊ตฌํ˜„

์นด์นด์˜ค ๋งต ๊ณต์‹ ๊ฐ€์ด๋“œ๋Š” Java ๊ธฐ๋ฐ˜์œผ๋กœ ์•ˆ๋‚ด๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.
์ด๋ฅผ kotlin ๋ฐ jetpack compose๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๊ตฌํ˜„ํ•˜์˜€์Šต๋‹ˆ๋‹ค.



์นด์นด์˜ค SDK ์ด์šฉํ•˜๊ธฐ



์›๊ฒฉ ์ €์žฅ์†Œ ์„ค์ •

settings.gradle.kts์— ์นด์นด์˜ค ์ง€๋„ SDK ์ €์žฅ์†Œ๋ฅผ ์›๊ฒฉ ์ €์žฅ์†Œ๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

...
dependencyResolutionManagement {
    ...
    repositories {
        ...
        maven { url = uri("https://devrepo.kakao.com/nexus/repository/kakaomap-releases/") }
    }
}

...

Maven ์ €์žฅ์†Œ

๋ฐฐํฌ๋œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ €์žฅํ•˜๊ณ , ์ด์šฉํ• ์ˆ˜ ์žˆ๋Š” ์›๊ฒฉ ์ €์žฅ์†Œ

dependencyResolutionManagement์˜ repositories์— ์นด์นด์˜ค ์ง€๋„ sdk์˜ ์›๊ฒฉ ์ €์žฅ์†Œ๋ฅผ ์„ค์ •ํ•ด์„œ ์ด๋ฅผ ํ”„๋กœ์ ํŠธ์—์„œ ๋‹ค์šด๋กœ๋“œํ•ด์„œ ์ด์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค.



๋นŒ๋“œ ์˜์กด์„ฑ ์„ค์ •

build.gradle

...

dependencies {
    ...
    // kakao map
    implementation(libs.kakao.maps)
}

...

libs.versions.toml

[versions]
...
kakaoMaps = "2.12.8"

[libraries]
...
kakao-maps = { module = "com.kakao.maps.open:android", version.ref = "kakaoMaps" }

๋ฒ„์ „ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ toml์— ์ •์˜ํ•œ ํ›„ ์ด๋ฅผ build.gradle์—์„œ ์˜์กด์„ฑ ์„ค์ • ์ถ”๊ฐ€ํ•ด์ค๋‹ˆ๋‹ค.



์นด์นด์˜ค ๊ฐœ๋ฐœ์ž ์‚ฌ์ดํŠธ์— ์•ฑ ๋“ฑ๋ก

image
์นด์นด์˜ค ๊ฐœ๋ฐœ์ž ์‚ฌ์ดํŠธ์—์„œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜๋ฅผ ์ถ”๊ฐ€ํ•ด์ค๋‹ˆ๋‹ค.

image.

๋‚ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ > ์•ฑ ์„ค์ • > ํ”Œ๋žซํผ์—์„œ Android ํ”Œ๋žซํผ ๋“ฑ๋กํ•ด์ค๋‹ˆ๋‹ค.

image.

ํ‚ค ํ•ด์‹œ ํ™•์ธ๋ฒ•

ํ‚ค ํ•ด์‹œ๋ž€ ์•ฑ์˜ ์‹ ์›์„ ์ฆ๋ช…ํ•˜๋Š” ๊ณ ์œ ํ•œ ๋””์ง€ํ„ธ ์„œ๋ช…. ์ธ์ฆ๋œ ์ด์šฉ์ž๋งŒ ์„œ๋น„์Šค๋ฅผ ์ด์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
๊ฐœ๋ฐœ ํ™˜๊ฒฝ๋ณ„๋กœ ํ‚ค ํ•ด์‹œ๊ฐ€ ์กด์žฌ

  1. ์นด์นด์˜ค SDK ์‚ฌ์šฉ

Utility์—์„œ ์ œ๊ณตํ•˜๋Š” ํŽธ์˜ ๊ธฐ๋Šฅ ์ค‘ getKeyHash()ํ•จ์ˆ˜ ์‚ฌ์šฉํ•ด์„œ ๊ตฌํ•˜๊ธฐ

import com.kakao.sdk.common.util.Utility

var keyHash = Utility.getKeyHash(this)
  1. ํ„ฐ๋ฏธ๋„์— keytool
keytool -exportcert -alias androiddebugkey -keystore ~/.android/debug.keystore -storepass android -keypass android | openssl sha1 -binary | openssl base64

ํ„ฐ๋ฏธ๋„์— ํ•ด๋‹น ์ฝ”๋“œ๋ฅผ ์ž…๋ ฅํ•˜๋ฉด ํ„ฐ๋ฏธ๋„์—์„œ ํ‚ค ํ•ด์‹œ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  1. ํŒจํ‚ค์ง€ ๋งค๋‹ˆ์ € ์‚ฌ์šฉ
try {
    val info = packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES)
    for (signature in info.signatures) {
        val md = MessageDigest.getInstance("SHA")
        md.update(signature.toByteArray())
        val key = Base64.encodeToString(md.digest(), Base64.DEFAULT)
        Log.d("KeyHash", "key hash: $key")
    }
} catch (e: Exception) {
    Log.e("KeyHash", "ํ‚ค ํ•ด์‹œ ๊ฐ€์ ธ์˜ค๊ธฐ ์‹คํŒจ", e)
}

๋ฅผ onCreate์— ์ž…๋ ฅํ›„ ์•ฑ์„ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. ์ด ๋•Œ ๋กœ๊ทธ๋กœ ํ•ด์‹œ ๊ฐ’์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค..

์•ฑ ํ‚ค ํ™•์ธ

๋‚ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ > ์•ฑ ์„ค์ • > ์•ฑ ํ‚ค image

๋„ค์ดํ‹ฐ๋ธŒ ์•ฑ ํ‚ค, REST API ํ‚ค, JavaScript ํ‚ค, Admin ํ‚ค ๊ฐ๊ฐ ํ™•์ธ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

์•ฑํ‚ค๋ฅผ local.properties์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.



AndroidManifest.xml์— ๊ถŒํ•œ ์„ ์–ธ

์•ฑ์—์„œ ์ง€๋„ ์ด์šฉ๊ณผ ๊ด€๋ จํ•œ ๊ถŒํ•œ์„ ์„ ์–ธํ•ด์ค๋‹ˆ๋‹ค

ACCESS_FINE_LOCATION

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

GPS๋ฅผ ํ†ตํ•œ ์ •ํ™•ํ•œ ์œ„์น˜ ์ •๋ณด ๊ถŒํ•œ ๋Ÿฐํƒ€์ž„์— ์‚ฌ์šฉ์ž ๋™์˜ ํ•„์š”

ACCESS_COARSE_LOCATION

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

๋„คํŠธ์›Œํฌ๋‚˜ ๊ธฐ์ง€๊ตญ ๊ธฐ๋ฐ˜์˜ ์œ„์น˜ ์ •๋ณด ๊ถŒํ•œ์œผ๋กœ ๋Œ€๋žต์ ์ธ ์œ„์น˜๋ฅผ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค ๋Ÿฐํƒ€์ž„์— ์‚ฌ์šฉ์ž ๋™์˜ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

INTERNET

<uses-permission android:name="android.permission.INTERNET" />

api ํ†ต์‹ ์„ ์œ„ํ•œ ์ธํ„ฐ๋„ท ์ ‘๊ทผ ๊ถŒํ•œ ๋Ÿฐํƒ€์ž„์— ์‚ฌ์šฉ์ž ๋™์˜ ๋ถˆํ•„์š”

metadata๋กœ ๋„ค์ดํ‹ฐ๋ธŒ ์•ฑ ํ‚ค ๋“ฑ๋ก

        <meta-data
            android:name="com.kakao.vectormap.APP_KEY"
            android:value="${KAKAO_NATIVE_APP_KEY}"/>

๋„ค์ดํ‹ฐ๋ธŒ ์•ฑ ํ‚ค๋ฅผ ํ†ตํ•ด ์„œ๋น„์Šค ๊ถŒํ•œ์„ ํ™•์ธํ•˜๊ณ  api ์‚ฌ์šฉ๋Ÿ‰์„ ์ธก์ •ํ•ฉ๋‹ˆ๋‹ค

local properties ์‚ฌ์šฉ

local.properties ํŒŒ์ผ์— ํ‚ค๋ฅผ ์ €์žฅํ•˜๊ณ  ์ด๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ์‹ ๋ณด์•ˆ ์ƒ ์ค‘์š”ํ•œ ์ •๋ณด๋ฅผ git์— ์˜ฌ๋ฆฌ์ง€ ์•Š๊ณ  ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

import java.util.Properties

...

val localProperties = Properties().apply {
    val localPropertiesFile = rootProject.file("local.properties")
    if (localPropertiesFile.exists()) {
        load(localPropertiesFile.inputStream())
    }
}

android {
    ...

    defaultConfig {
        ...
        buildConfigField("String", "KAKAO_NATIVE_APP_KEY", "${localProperties["kakao_native_app_key"]}")
        manifestPlaceholders["KAKAO_NATIVE_APP_KEY"] = localProperties["kakao_native_app_key"] ?: ""
    }
    ...
}
BuildConfig

Properties ๊ฐ์ฒด์— local.properties ํŒŒ์ผ์—์„œ ๋ถˆ๋Ÿฌ์˜จ ์ •๋ณด๋“ค์„ ํ‚ค-๊ฐ’ ํ˜•ํƒœ๋กœ ์ €์žฅ BuildConfig ํ•„๋“œ๋ฅผ ์ƒ์„ฑํ•ด์„œ ํ•ด๋‹น ๊ฐ์ฒด์˜ ๊ฐ’์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

์ด ๊ฒฝ์šฐ BuildConfig.KAKAO_NATIVE_APP_KEY๋กœ ํ˜ธ์ถœ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

Manifest ํ”Œ๋ ˆ์ด์Šค ํ™€๋”

AndroidManifest.xml์˜ ๊ฐ’์„ ๋Œ€์ฒดํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ๊ธฐ๋Šฅ ${KAKAO_NATIVE_APP_KEY}๋กœ ์„ค์ •์‹œ ๋™์ ์œผ๋กœ local properties ๊ฐ’ ์ ์šฉ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

๋ฉ”ํƒ€ ๋ฐ์ดํ„ฐ

์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ > ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ์— ์•ฑํ‚ค๋ฅผ ๋“ฑ๋กํ•ด์ค๋‹ˆ๋‹ค

<meta-data
    android:name="com.kakao.vectormap.APP_KEY"
    android:value="${KAKAO_NATIVE_APP_KEY}"/>   

์ตœ์ข… AndroidManifext.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

    <application
        ...
        <meta-data
            android:name="com.kakao.vectormap.APP_KEY"
            android:value="${KAKAO_NATIVE_APP_KEY}"/>
        ...
    </application>

</manifest>



Application ์‹คํ–‰์‹œ KakaoMAP SDK init ์„ค์ •

์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์‹คํ–‰๋˜๋Š” ์‹œ์ ์— ์นด์นด์˜ค๋งต SDK๋ฅผ ์ดˆ๊ธฐํ™”ํ•ฉ๋‹ˆ๋‹ค. KakaoMapSdk.init(context, appKey){...}์— ๋„ค์ดํ‹ฐ๋ธŒ ์•ฑ ํ‚ค๋ฅผ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ์ž…๋ ฅํ•ด์„œ KakaoMapSdk๋ฅผ ์ดˆ๊ธฐํ™”ํ•ด์ค๋‹ˆ๋‹ค.

@HiltAndroidApp
class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        try {
            KakaoMapSdk.init(this, BuildConfig.KAKAO_NATIVE_APP_KEY)
        } catch (e: Exception) {
            Log.e("KakaoMapSdk", "์นด์นด์˜ค๋งต SDK init ์‹คํŒจ", e)
        }
    }
}



Mapview๋ฅผ ์ด์šฉํ•ด์„œ ์ง€๋„ ์ถœ๋ ฅ

AndroidView

Jetpack Compose์—์„œ ๊ธฐ์กด์˜ Android View ์‹œ์Šคํ…œ์˜ ๋ทฐ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” Composable์ž…๋‹ˆ๋‹ค. ์นด์นด์˜ค๋งต SDK๋Š” ๊ธฐ์กด์˜ android view ์‹œ์Šคํ…œ์œผ๋กœ ์„ค๊ณ„๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— jetpack compose๋กœ ์ด๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด AndroidView ์ปดํผ์„œ๋ธ”์„ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

factory

context๋ฅผ ์ด์šฉํ•ด View ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค

Update

์ƒํƒœ๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค view์˜ ์†์„ฑ์ด๋‚˜ ๋ฐ์ดํ„ฐ๋ฅผ ์—…๋ฐ์ดํŠธ ํ•ฉ๋‹ˆ๋‹ค

KakaoMap ๊ฐ์ฒด ๊ฐ€์ ธ์™€์„œ ์„ค์ •ํ•˜๊ธฐ

Java๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์ž‘์„ฑ๋œ ์นด์นด์˜ค ๋งต ๊ณต์‹ ๊ฐ€์ด๋“œ์˜ ์ฝ”๋“œ๋ฅผ Kotlin์œผ๋กœ ์ „ํ™˜ํ•˜๊ณ ์ž ํ–ˆ์Šต๋‹ˆ๋‹ค




mapView ์ธ์Šคํ„ด์Šค ๊ด€๋ฆฌ

๋™์ผํ•œ ์ธ์Šคํ„ด์Šค๋กœ ์ƒ๋ช… ์ฃผ๊ธฐ ๊ด€๋ฆฌ์™€ ๋ทฐ ์ƒ์„ฑ์„ ํ•˜๊ธฐ ์œ„ํ•ด mutatableStateOf๋ฅผ ํ†ตํ•ด mapView ์ƒํƒœ๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค. mutableStateOf๋Š” ๋ณ€๊ฒฝ ๊ฐ€๋Šฅํ•œ ์ƒํƒœ ๊ด€๋ฆฌ๋ฅผ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์žฌ๊ตฌ์„ฑ๋˜์–ด๋„ ๋‹ค์‹œ ์ธ์Šคํ„ด์Šค๋ฅผ ๋งŒ๋“ค์ง€ ์•Š๊ธฐ ์œ„ํ•ด remember๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

var mapView by remember { mutableStateOf<MapView?>(null) }

AndroidView(
    modifier = modifier
        .fillMaxSize(),
    factory = { context ->
        MapView(context).also { mapView = it }.apply {
            layoutParams = ViewGroup.LayoutParams(
                ...
            )
            start(
                ...
            )
        }
    }
)

.apply

์ฝ”ํ‹€๋ฆฐ์˜ ๋ฒ”์œ„ ํ•จ์ˆ˜ ์ค‘ ํ•˜๋‚˜๋กœ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์ฆ‰์‹œ ๊ทธ ๊ฐ์ฒด์˜ ์†์„ฑ์„ ์ •์˜ํ•  ๋•Œ ์œ ์šฉ ์—ฌ๋Ÿฌ๊ฐœ์˜ ์†์„ฑ์„ ์—ฐ์†ํ•ด์„œ ์ •์˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ธฐ๋ณธ ํ˜•ํƒœ

val mapView = MapView(context)
mapView.layoutParams = ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)
mapView.start(callback1, callback2)
return mapView

.apply๋ฅผ ์‚ฌ์šฉํ•œ ์ฝ”๋“œ

MapView(context).apply {
    layoutParams = ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)
    start(callback1, callback2)
}
  • this๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค
  • ๊ฐ์ฒด์˜ context ๋‚ด์—์„œ ์ฝ”๋“œ ๋ธ”๋ก์ด ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค
  • ์ฒด์ด๋‹ ํ˜•์‹์œผ๋กœ ๊ตฌ์„ฑ๋ฉ๋‹ˆ๋‹ค

.also

.also๋Š” ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉด์„œ ๋ถ€๊ฐ€์ ์ธ ์ž‘์—…์„ ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค๋‹ˆ๋‹ค

MapView(context).also { mapView = it }

ํ•ด๋‹น ์ฝ”๋“œ๋Š” ๊ธฐ์กด์˜ ์ƒํƒœ๋ฅผ ๋ฐ˜ํ™˜ํ•ด์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๋งŒ๋“ค์–ด์ค๋‹ˆ๋‹ค.

mapView.layoutParams

mapView์˜ ๋ฉ”์†Œ๋“œ๋กœ ๋ ˆ์ด์•„์›ƒ์„ ์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋„ˆ๋น„์™€ ๋†’์ด๋ฅผ parameter๋กœ ๋ฐ›์Šต๋‹ˆ๋‹ค.

์˜ต์…˜

  • WRAP_CONTENT : ๋‚ด์šฉ๋ฌผ ํฌ๊ธฐ๋กœ ์„ค์ •
  • MATCH_PARENT : ๋ถ€๋ชจ ์ปจํ…Œ์ด๋„ˆ์˜ ํฌ๊ธฐ๋กœ ์„ค์ •
layoutParams = ViewGroup.LayoutParams(
    ViewGroup.LayoutParams.MATCH_PARENT,
    ViewGroup.LayoutParams.MATCH_PARENT,
)

๋„ˆ๋น„์™€ ๋†’์ด๋ฅผ ๋ชจ๋‘ ๋ถ€๋ชจ ์ปจํ…Œ์ด๋„ˆ์˜ ํฌ๊ธฐ๋กœ ์„ค์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.

mapView.start

์ง€๋„๋ฅผ ์ดˆ๊ธฐํ™”ํ•˜์—ฌ ํ™”๋ฉด์— ๋กœ๋“œํ•˜๋Š” ๋ฉ”์†Œ๋“œ์ž…๋‹ˆ๋‹ค.

mapView.start(
    object : MapLifeCycleCallback() {
        ...
    },

    object : KakaoMapReadyCallback() {
        ...
    }
)

MapLifeCycleCallback()์™€ KakaoMapReadyCallback() ๋‘๊ฐ€์ง€ ์ฝœ๋ฐฑํ•จ์ˆ˜๋ฅผ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๋ฐ›์Šต๋‹ˆ๋‹ค.

์ฝ”ํ‹€๋ฆฐ์˜ ๊ฐ์ฒด ์ƒ์„ฑ

์ž๋ฐ”(๊ณต์‹ ๊ฐ€์ด๋“œ)์—์„œ์˜ ์ƒ์„ฑ ๋ฐฉ์‹์€

new MapLifeCycleCallback() {
    ....
}

๋ผ๋ฉด ์ฝ”ํ‹€๋ฆฐ์€

object : MapLifeCycleCallback() { 
    ...
}

MapLifeCycleCallback()

์ง€๋„์˜ ์ƒ์„ฑ ์ฃผ๊ธฐ์™€ ๊ด€๋ จ๋œ ๋™์ž‘์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋Š” ์ถ”์ƒ ํ•จ์ˆ˜ onMapDestroy()์™€ onMapError(error: Exception)์„ overrideํ•ด์„œ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

onMapDestroy()

์ง€๋„ API๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ์ข…๋ฃŒ๋  ๋•Œ ํ˜ธ์ถœ๋œ๋‹ค

onMapError(error: Exception)

์ธ์ฆ ์‹คํŒจ ๋ฐ ์ง€๋„ ์‚ฌ์šฉ ์ค‘ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•  ๋•Œ ํ˜ธ์ถœ๋จ

start(
    object : MapLifeCycleCallback() {
        override fun onMapDestroy() {
            // ์ง€๋„ api๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ์ข…๋ฃŒ๋  ๋•Œ ํ˜ธ์ถœ
        }

        override fun onMapError(error: Exception) {
            // ์ธ์ฆ ์‹คํŒจ ๋ฐ ์ง€๋„ ์‚ฌ์šฉ ์ค‘ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•  ๋•Œ ํ˜ธ์ถœ๋จ
         
        }
    },
    ...
    }
)

KakaoMapReadyCallback()

์ง€๋„์˜ ์ค€๋น„ ์ƒํƒœ์™€ ๊ด€๋ จ๋œ ๋™์ž‘์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋Š” ์ฝœ๋ฐฑ ํ•จ์ˆ˜

onMapReady()์™€ ๊ธฐํƒ€ ์ง€๋„ ๊ด€๋ จํ•œ ์ถ”์ƒ ํ•จ์ˆ˜ getPosition(), getZoomLevel(), getMapViewInfo(), getViewName(), isVisible(), getTag() overrideํ•ด์„œ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค

start(
    ...
    object : KakaoMapReadyCallback() {
        override fun onMapReady(kakaoMap: KakaoMap) {
            // ์ธ์ฆ ํ›„ API๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ์‹คํ–‰๋  ๋•Œ ํ˜ธ์ถœ๋จ
        }
        override fun getPosition(): LatLng {
            // ์ง€๋„ ์‹œ์ž‘ ์‹œ ์œ„์น˜ ์ขŒํ‘œ๋ฅผ ์„ค์ •
            return LatLng.from(37.406960, 127.115587)
        }
        override fun getZoomLevel(): Int {
             // ์ง€๋„ ์‹œ์ž‘ ์‹œ ํ™•๋Œ€/์ถ•์†Œ ์คŒ ๋ ˆ๋ฒจ ์„ค์ •
            return 15
        }
    }
)

onMapReady()

์ธ์ฆ ํ›„ api๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ์‹คํ–‰๋  ๋•Œ ํ˜ธ์ถœ๋จ

๊ธฐํƒ€ ํ˜ธ์ถœ

getZoomLevel() : ์ง€๋„ ์‹œ์ž‘ ์‹œ ํ™•๋Œ€/์ถ•์†Œ ์คŒ ๋ ˆ๋ฒจ ์„ค์ • getMapViewInfo() : ์ง€๋„ ์‹œ์ž‘ ์‹œ App ๋ฐ MapType ์„ค์ • getViewName() : KakaoMap์˜ ๊ณ ์œ ํ•œ ์ด๋ฆ„์„ ์„ค์ • isVisible() : ์ง€๋„ ์‹œ์ž‘ ์‹œ visible ์—ฌ๋ถ€๋ฅผ ์„ค์ • getTag() : KakaoMap์˜ tag๋ฅผ ์„ค์ •



mapView.resume(), mapView.pause()

Activity๋‚˜ Fragment์˜ ์ƒ๋ช…์ฃผ๊ธฐ์— ๋”ฐ๋ผ MapView๋„ .resume()๊ณผ .pause()๋ฅผ ๊ผญ ํ˜ธ์ถœํ•ด์ค˜์•ผ ํ•œ๋‹ค.

DisposableEffect() { onDispose{} }

์ปดํฌ์ €๋ธ”์ด ํ™œ์„ฑํ™”๋  ๋•Œ ์ž‘๋™ dispose๋  ๋•Œ clean up๊ณผ ๊ฐ™์€ ๋™์ž‘์ด ์ž‘๋™๋  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” Compose์˜ ์ดํŽ™ํŠธ

DisposableEffect(key1, key2, ...) {  // ํ‚ค๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค ์žฌ์‹คํ–‰
    // ์„ค์ • ์ฝ”๋“œ (effect setup)
    
    onDispose {
        // ์ •๋ฆฌ ์ฝ”๋“œ (cleanup)
    }
}

์‚ฌ์šฉ๋˜๋Š” ๊ฒฝ์šฐ

  • ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒฝ์šฐ
  • ์ƒ๋ช…์ฃผ๊ธฐ์— ๋”ฐ๋ฅธ ์˜ต์ €๋ฒ„
  • ์‹œ์Šคํ…œ ๋ธŒ๋กœ๋“œ์บ์ŠคํŠธ ๋ฆฌ์‹œ๋ฒ„

์‹คํ–‰์‹œ์ 

  • ์ตœ์ดˆ ์ปดํฌํ‹ฐ์…˜
  • ํ‚ค ๋ณ€๊ฒฝ์‹œ
  • ์ปดํฌ์ €๋ธ” ์ œ๊ฑฐ ์‹œ - onDispose()

.addObserver{}, .removeObserver(observer)

Android์˜ ์ƒ๋ช…์ฃผ๊ธฐ ์ด๋ฒคํŠธ๋ฅผ ๊ฐ์ง€ํ•˜๊ธฐ ์œ„ํ•œ ์˜ต์ €๋ฒ„๋ฅผ ๋“ฑ๋กํ•˜๊ณ  ์ œ๊ฑฐํ•˜๋Š” ๋งค์†Œ๋“œ

// 1. LifecycleOwner์—์„œ lifecycle ๊ฐ€์ ธ์˜ค๊ธฐ
val lifecycleOwner = LocalLifecycleOwner.current  // Compose์—์„œ ํ˜„์žฌ LifecycleOwner ๊ฐ€์ ธ์˜ค๊ธฐ
val lifecycle = lifecycleOwner.lifecycle          // Lifecycle ๊ฐ์ฒด ์ ‘๊ทผ

// 2. Observer ์ƒ์„ฑ
val observer = object : DefaultLifecycleObserver {
    override fun onCreate(owner: LifecycleOwner) {}      // ์ƒ์„ฑ๋  ๋•Œ
    override fun onStart(owner: LifecycleOwner) {}       // ์‹œ์ž‘๋  ๋•Œ
    override fun onResume(owner: LifecycleOwner) {}      // ํ™”๋ฉด์— ๋ณด์ผ ๋•Œ
    override fun onPause(owner: LifecycleOwner) {}       // ํ™”๋ฉด์—์„œ ์‚ฌ๋ผ์งˆ ๋•Œ
    override fun onStop(owner: LifecycleOwner) {}        // ์ •์ง€๋  ๋•Œ
    override fun onDestroy(owner: LifecycleOwner) {}     // ํŒŒ๊ดด๋  ๋•Œ
}

// 3. Observer ๋“ฑ๋ก
lifecycle.addObserver(observer)

// 4. Observer ์ œ๊ฑฐ
lifecycle.removeObserver(observer)

onResume๊ณผ onPause ๊ด€๋ จ ์˜ต์ €๋ฒ„๋ฅผ ์ƒ์„ฑ ํ›„ addObserver๋กœ ์„ค์ •ํ•˜๊ณ  onDispose์‹œ removeObeserver๋กœ ์ œ๊ฑฐ

    val lifecycleOwner = LocalLifecycleOwner.current

    DisposableEffect(lifecycleOwner) {
        val observer = object : DefaultLifecycleObserver {
            override fun onResume(owner: LifecycleOwner) {
                mapView?.resume()
            }

            override fun onPause(owner: LifecycleOwner) {
                mapView?.pause()
            }
        }

        lifecycleOwner.lifecycle.addObserver(observer)

        onDispose {
            lifecycleOwner.lifecycle.removeObserver(observer)
        }
    }

์ตœ์ข… KakaoMapView

package com.hm.picplz.ui.screen.search_photographer

import android.util.Log
import android.view.ViewGroup
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.viewinterop.AndroidView
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import com.kakao.vectormap.*


@Composable
fun KakaoMapView(
    modifier: Modifier = Modifier,
    onMapReady: (KakaoMap) -> Unit = {}
) {
    var mapView by remember { mutableStateOf<MapView?>(null) }

    AndroidView(
        modifier = modifier
            .fillMaxSize(),
        factory = { context ->
            MapView(context).also { mapView = it }.apply {
                layoutParams = ViewGroup.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT,
                    ViewGroup.LayoutParams.MATCH_PARENT,
                )
                start(
                    object : MapLifeCycleCallback() {
                        override fun onMapDestroy() {
                            // ์ง€๋„ api๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ์ข…๋ฃŒ๋  ๋•Œ ํ˜ธ์ถœ
                        }

                        override fun onMapError(error: Exception) {
                            // ์ธ์ฆ ์‹คํŒจ ๋ฐ ์ง€๋„ ์‚ฌ์šฉ ์ค‘ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•  ๋•Œ ํ˜ธ์ถœ๋จ
                            Log.e("KakaoMapView", "Map Error: ${error.message}")
                            error.printStackTrace()
                            if (error is MapAuthException) {
                                when (error.errorCode) {
                                    -1 -> Log.e("KakaoMapView", "์ธ์ฆ ๊ณผ์ • ์ค‘ ์›์ธ์„ ์•Œ ์ˆ˜ ์—†๋Š” ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ ์ƒํƒœ")
                                    -2 -> Log.e("KakaoMapView", "ํ†ต์‹  ์—ฐ๊ฒฐ ์‹œ๋„ ์ค‘ ๋ฐœ์ƒํ•˜๋Š” ์—๋Ÿฌ")
                                    -3 -> Log.e(
                                        "KakaoMapView",
                                        "ํ†ต์‹  ์—ฐ๊ฒฐ ์ค‘ SocketTimeoutException ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ ๊ฒฝ์šฐ"
                                    )

                                    -4 -> Log.e(
                                        "KakaoMapView",
                                        "ํ†ต์‹  ์‹œ๋„ ์ค‘ ConnectTimeoutException ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ ๊ฒฝ์šฐ"
                                    )

                                    400 -> Log.e(
                                        "KakaoMapView",
                                        "์ผ๋ฐ˜์ ์ธ ์˜ค๋ฅ˜. ์ฃผ๋กœ API์— ํ•„์š”ํ•œ ํ•„์ˆ˜ ํŒŒ๋ผ๋ฏธํ„ฐ์™€ ๊ด€๋ จํ•˜์—ฌ ์„œ๋ฒ„๊ฐ€ ํด๋ผ์ด์–ธํŠธ ์˜ค๋ฅ˜๋ฅผ ๊ฐ์ง€ํ•ด ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜์ง€ ๋ชปํ•œ ์ƒํƒœ์ž…๋‹ˆ๋‹ค."
                                    )

                                    401 -> Log.e(
                                        "KakaoMapView",
                                        "์ธ์ฆ ์˜ค๋ฅ˜. ํ•ด๋‹น ๋ฆฌ์†Œ์Šค์— ์œ ํšจํ•œ ์ธ์ฆ ์ž๊ฒฉ ์ฆ๋ช…์ด ์—†์–ด ์š”์ฒญ์— ์‹คํŒจํ•œ ์ƒํƒœ"
                                    )

                                    403 -> Log.e(
                                        "KakaoMapView",
                                        "๊ถŒํ•œ ์˜ค๋ฅ˜. ์„œ๋ฒ„์— ์š”์ฒญ์ด ์ „๋‹ฌ๋˜์—ˆ์ง€๋งŒ, ๊ถŒํ•œ ๋•Œ๋ฌธ์— ๊ฑฐ์ ˆ๋œ ์ƒํƒœ"
                                    )

                                    429 -> Log.e(
                                        "KakaoMapView",
                                        "์ฟผํ„ฐ ์ดˆ๊ณผ. ์ •ํ•ด์ง„ ์‚ฌ์šฉ๋Ÿ‰์ด๋‚˜ ์ดˆ๋‹น ์š”์ฒญ ํ•œ๋„๋ฅผ ์ดˆ๊ณผํ•œ ๊ฒฝ์šฐ"
                                    )

                                    499 -> Log.e(
                                        "KakaoMapView",
                                        "ํ†ต์‹  ์‹คํŒจ ์˜ค๋ฅ˜. ์ธํ„ฐ๋„ท ์—ฐ๊ฒฐ ์ƒํƒœ ํ™•์ธ์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ"
                                    )
                                }
                            }
                        }
                    },
                    object : KakaoMapReadyCallback() {
                        override fun onMapReady(kakaoMap: KakaoMap) {
                            // ์ธ์ฆ ํ›„ API๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ์‹คํ–‰๋  ๋•Œ ํ˜ธ์ถœ๋จ
                            onMapReady(kakaoMap)
                            Log.e("KakaoMapView", "์นด์นด์˜ค ๋งต ์ค€๋น„ ์™„๋ฃŒ")

                        }

                        override fun getPosition(): LatLng {
                            // ์ง€๋„ ์‹œ์ž‘ ์‹œ ์œ„์น˜ ์ขŒํ‘œ๋ฅผ ์„ค์ •
                            return LatLng.from(37.406960, 127.115587)
                        }

                        override fun getZoomLevel(): Int {
                            // ์ง€๋„ ์‹œ์ž‘ ์‹œ ํ™•๋Œ€/์ถ•์†Œ ์คŒ ๋ ˆ๋ฒจ ์„ค์ •
                            return 15
                        }
                    }
                )
            }
        }
    )

    val lifecycleOwner = LocalLifecycleOwner.current

    DisposableEffect(lifecycleOwner) {
        val observer = object : DefaultLifecycleObserver {
            override fun onResume(owner: LifecycleOwner) {
                mapView?.resume()
            }

            override fun onPause(owner: LifecycleOwner) {
                mapView?.pause()
            }
        }

        lifecycleOwner.lifecycle.addObserver(observer)

        onDispose {
            lifecycleOwner.lifecycle.removeObserver(observer)
        }
    }
}
โš ๏ธ **GitHub.com Fallback** โš ๏ธ