[Android] kakao map ์‚ฌ์šฉํ•˜๊ธฐ

2 ๋ถ„ ์†Œ์š”

๐Ÿ™‹โ€โ™€๏ธ kakao sdk ์‚ฌ์šฉ ์ดˆ๊ธฐ ์„ค์ •

์šฐ์„  ๊ธฐ๋ณธ์ ์œผ๋กœ kakao sdk ์‚ฌ์šฉ์„ ์œ„ํ•ด ํ”„๋กœ์ ํŠธ๋ฅผ ๋“ฑ๋กํ•˜๊ณ  app key๋ฅผ ๋ฐ›์•„์˜ค๋Š” ๊ฒƒ๊นŒ์ง€ ๋งˆ๋ฌด๋ฆฌํ–ˆ๋‹ค๋Š” ๊ฐ€์ •ํ•˜์— ๊ทธ ๋‹ค์Œ ๋‹จ๊ณ„๋ถ€ํ„ฐ ์ฐจ๊ทผ์ฐจ๊ทผ ์ •๋ฆฌํ•ด๋ณด๊ณ ์ž ํ•œ๋‹ค.

๐Ÿ™‹โ€โ™€๏ธ kakao map sdk ๋‹ค์šด

์šฐ์„  kakao map์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ํ•„์š”ํ•œ sdk๋ฅผ ๋‹ค์šด๋ฐ›์•„์•ผ ํ•œ๋‹ค. ๋‹ค์Œ ๊ณต์‹ ๋ฌธ์„œ์—์„œ sdk๋ฅผ ๋‹ค์šด๋ฐ›์•„ ์••์ถ•์„ ํ’€์–ด์ฃผ๋ฉด ๋‹ค์Œ์˜ ํดํ„ฐ, ํŒŒ์ผ์„ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค.

  • libDaumMapAndroid.jar
  • ์•„ํ‚คํ…์ณ๋ณ„๋กœ libMapEngineApi.so ์„ธ ๊ฐœ์˜ ๋ฐ”์ด๋„ˆ๋ฆฌ ํŒŒ์ผ

์šฐ์„  libDaumMapAndroid.jar ํŒŒ์ผ์€ /app/libs/์— ๋ณต์‚ฌ๋ฅผ ํ•˜๊ณ , libMapEngineApi.so ํŒŒ์ผ์€ /app/source/main/jniLibs ํด๋”๋ฅผ ๋งŒ๋“ค์–ด ๊ทธ ์•ˆ์— ๋ณต์‚ฌํ•ด์ฃผ๋ฉด ๋œ๋‹ค.

์ด์ œ ๋“ฑ๋กํ•œ jar ํŒŒ์ผ์˜ dependency๋ฅผ ์ถ”๊ฐ€ํ•ด์ค˜์•ผ ํ•œ๋‹ค! ์šฐ์„  File -> Project Structure๋กœ ๋“ค์–ด๊ฐ€ ์™ผ์ชฝ ๋ฉ”๋‰ด์˜ Dependencies๋ฅผ ํด๋ฆฐํ•œ๋‹ค. Modules ๋ถ€๋ถ„์€ app์„ ์„ ํƒํ•˜๊ณ  Declared Dependencies์˜ +๋ฅผ ๋ˆŒ๋Ÿฌ jar dependency๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค.

์—ฌ๊ธฐ์„œ step1์—๋Š” src\main\jniLibs๋ฅผ ์ž…๋ ฅํ•˜๊ณ , step2์—๋Š” implementation์„ ์„ ํƒํ•œ ํ›„ Apply, Ok ๋ฒ„ํŠผ์„ ํด๋ฆญํ•ด ์ ์šฉ์„ ํ•œ๋‹ค.

์—ฌ๊ธฐ๊นŒ์ง€ ์ง„ํ–‰ํ–ˆ๋‹ค๋ฉด build ํŒŒ์ผ์— ๋‹ค์Œ์˜ dependency๊ฐ€ ์ถ”๊ฐ€๋˜์–ด ์žˆ๋Š”์ง€ ํ™•์ธํ•˜์ž! ๊ทธ๋ฆฌ๊ณ  ๋งŒ์•ฝ ์ถ”๊ฐ€๋˜์–ด ์žˆ์ง€ ์•Š๋‹ค๋ฉด ์•„๋ž˜์˜ ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•ด๋ผ!

    //kakao map
    implementation files('libs/libDaumMapAndroid.jar')

์ด์ œ ๋งˆ์ง€๋ง‰์œผ๋กœ manifest์—์„œ permission ๋ฐ app key๋ฅผ ์ถ”๊ฐ€ํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค.

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

    // internet ๋ฐ location ๊ถŒํ•œโญ๏ธ
    <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
        android:usesCleartextTraffic="true // ์•”ํ˜ธํ™” ๋˜์ง€ ์•Š์€ ์„œ๋ฒ„์™€ ํ†ต์‹ ์ด ํ•„์š”ํ•จโญ๏ธ
        android:name=".presentation.base.GlobalApplication"
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@drawable/ic_sopar_logo"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/Theme.Sopar.Splash"
        tools:targetApi="31">
        <activity
            android:name="com.kakao.sdk.auth.AuthCodeHandlerActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
                <data android:host="oauth"
                    android:scheme="kakao${NATIVE_APP_KEY}" />
            </intent-filter>
        </activity>
...

        // app key ์ถ”๊ฐ€โญ๏ธ
        <meta-data android:name="com.kakao.sdk.AppKey" android:value="${NATIVE_APP_KEY}"/>

    </application>
</manifest>
  • ์นด์นด์˜ค ๋งต api ํ˜ธ์ถœ์—์„œ๋Š” http๊ฐ€ ์‚ฌ์šฉ๋˜๋ฏ€๋กœ ์œ„์˜ usesCleartextTraffic๋ฅผ true๋กœ ์„ค์ •ํ•ด์•ผ ํ•œ๋‹ค.
  • ๊ณต์‹๋ฌธ์„œ์—์„œ๋Š” ACCESS_FINE_LOCATION ๊ถŒํ•œ๋งŒ์„ ์„ค์ •ํ•˜์ง€๋งŒ ํ•ด๋‹น ๊ถŒํ•œ๋งŒ์„ ์„ค์ •ํ•˜๋ฉด ๋‹ค์Œ์˜ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๊ฒŒ ๋œ๋‹ค.
    • If you need access to FINE location, you must request both ACCESS_FINE_LOCATION and ACCESS_COARSE_LOCATION
    • ๋”ฐ๋ผ์„œ wifi๋‚˜ ๊ธฐ์ง€๊ตญ ์‹ ํ˜ธ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์œ„์น˜๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ACCESS_COARSE_LOCATION ๊ถŒํ•œ๋„ ํ•จ๊ป˜ ์„ค์ •ํ•ด์ค€๋‹ค.

๊ทธ๋ ‡๋‹ค๋ฉด ์ด์ œ ์นด์นด์˜ค ๋งต์„ ํ™”๋ฉด์— ๋„์–ด๋ณด์ž!

๐Ÿ™‹โ€โ™€๏ธ MapView ์‚ฌ์šฉํ•˜๊ธฐ

โœ“ ์นด์นด์˜ค๋งต ํ™”๋ฉด์— ๋„์šฐ๊ธฐ

ํ™”๋ฉด์— ์ง€๋„๋ฅผ ๋„์šฐ๊ธฐ ์œ„ํ•ด์„œ๋Š” net.daum.mf.map.api.MapView๋ฅผ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".presentation.map.MapFragment">

    <net.daum.mf.map.api.MapView
        android:id="@+id/map_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>

MapView๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ๋Š” net.daum.android.map.MapView์™€ ํ—ท๊ฐˆ๋ฆฌ์ง€ ์•Š๋„๋ก ํ•œ๋‹ค!

์—ฌ๊ธฐ๊นŒ์ง€ ์ง„ํ–‰ํ•˜๋ฉด ์•„๋ž˜ ์‚ฌ์ง„๊ฐ™์ด ๋งต์ด ์ž˜ ์ถœ๋ ฅ๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ์ด๋‹ค.

๋ณธ๊ฒฉ์ ์œผ๋กœ ๋งต์„ ์‚ฌ์šฉํ•˜๊ธฐ ์ „์— ํ•„์š”ํ•œ ๊ถŒํ•œ ์Šน์ธ ์ž‘์—…์ด ํ•„์š”ํ•˜๋‹ค!

โœ“ ๊ถŒํ•œ ์Šน์ธ ์ง„ํ–‰ํ•˜๊ธฐ

๊ถŒํ•œ ์Šน์ธ์€ registerForActivityResult์™€ ActivityResultContracts๋ฅผ ์‚ฌ์šฉํ•ด ์ง„ํ–‰ํ•˜๊ณ ์ž ํ•œ๋‹ค.

์šฐ์„  ๊ถŒํ•œ์ด ์Šน์ธ๋˜์—ˆ๋Š”์ง€๋ฅผ ๋จผ์ € ContextCompat.checkSelfPermission๋ฅผ ํ†ตํ•ด ํ™•์ธํ•ด์ค€๋‹ค.

    private fun isAllPermissionsGranted(): Boolean = REQUIRED_PERMISSIONS.all { permission ->
        ContextCompat.checkSelfPermission(requireContext(), permission) ==
                PackageManager.PERMISSION_GRANTED
    }

๋งŒ์•ฝ ์Šน์ธ์ด ํ•„์š”ํ•˜๋‹ค๋ฉด ๊ถŒํ•œ์— ๋Œ€ํ•œ ์Šน์ธ ์š”์ฒญ์„ ์ง„ํ–‰ํ•˜๋ฉด ๋œ๋‹ค. ์šฐ์„  ๊ทธ ๊ณผ์ •์—์„œ ์‚ฌ์šฉํ•  requestPermissionLauncher๋ฅผ ์ •์˜ํ•ด์ค€๋‹ค.

    private val requestPermissionLauncher =
        registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions ->
            permissions.entries.forEach { permission ->
                when {
                    //๊ถŒํ•œ์„ ์Šน์ธํ•˜์˜€์„ ๋–„
                    permission.value -> {
                        Toast.makeText(requireContext(), "ํ•„์š”ํ•œ ๊ถŒํ•œ์ด ์Šน์ธ๋˜์—ˆ์Šต๋‹ˆ๋‹ค!", Toast.LENGTH_SHORT).show()
                    }
                    //๊ถŒํ•œ์„ ์™„์ „ํžˆ ๊ฑฐ๋ถ€ํ–ˆ์„ ๊ฒฝ์šฐ
                    shouldShowRequestPermissionRationale(permission.key) -> {
                        Toast.makeText(requireContext(), "์„ค์ •์—์„œ ๊ถŒํ•œ์„ ์Šน์ธํ•ด์ฃผ์„ธ์š”!", Toast.LENGTH_SHORT).show()
                    }
                    //๊ถŒํ•œ์„ ์Šน์ธํ•˜์ง€ ์•Š์€ ๊ฒฝ์šฐ
                    else -> {
                        Toast.makeText(requireContext(), "์„ค์ •์—์„œ ๊ถŒํ•œ์„ ์Šน์ธํ•ด์ฃผ์„ธ์š”!", Toast.LENGTH_SHORT).show()
                    }
                }
            }
        }

์ด์ œ ์ด๋ฅผ ์ด์šฉํ•ด ํ•„์š”ํ•œ ๊ถŒํ•œ์„ ์š”์ฒญํ•œ๋‹ค.

        if (!isAllPermissionsGranted()) {
          //ํ•„์š”ํ•œ ๊ถŒํ•œ์ด ์Šน์ธ๋˜์–ด ์žˆ์ง€ ์•Š์€ ๊ฒฝ์šฐ
            requestPermissionLauncher.launch(REQUIRED_PERMISSIONS)
        }

์—ฌ๊ธฐ์„œ ํ•„์š”ํ•œ ๊ถŒํ•œ์€ companion object ์† ๋ฐฐ์—ด ํ˜•ํƒœ๋กœ ๋ฏธ๋ฆฌ ์ •์˜ํ•ด์ฃผ์—ˆ๋‹ค.

    companion object {
        private val REQUIRED_PERMISSIONS = arrayOf(
            android.Manifest.permission.ACCESS_COARSE_LOCATION,
            android.Manifest.permission.ACCESS_FINE_LOCATION,
            android.Manifest.permission.INTERNET
        )
    }

์ด๋ ‡๊ฒŒ ํ•œ๋‹ค๋ฉด ์ด์ œ ํ•„์š”ํ•œ ๊ถŒํ•œ์ด ๋ชจ๋‘ ์Šน์ธ๋˜์—ˆ๋‹ค.

์ด์ œ ์นด์นด์˜ค๋งต์„ ํ™œ์šฉํ•˜๋Š” ๋‹จ๊ณ„๋งŒ ๋‚จ์•˜๋‹ค! ํ•ด๋‹น ๋‚ด์šฉ์— ๋Œ€ํ•ด์„œ๋Š” ์ถ”ํ›„ ๊ฒŒ์‹œ๊ธ€์„ ํ†ตํ•ด ์ •๋ฆฌํ•ด๋ณด๊ฒ ๋‹ค!



(2023.05.12) ์ถ”๊ฐ€ ์ •๋ฆฌ ๋ถ€๋ถ„

hilt์™€ ํ•จ๊ป˜ kakao map์„ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ, xml์—์„œ ๋ฐ”๋กœ MapView๋ฅผ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ

Caused by: android.view.InflateException: Binary XML file line #12 in org.sopar:layout/fragment_map: Error inflating class net.daum.mf.map.api.MapView

์œ„์˜ ์˜ค๋ž˜๊ฐ€ ๋ฐœ์ƒํ•˜๊ฒŒ ๋œ๋‹ค. ๋”ฐ๋ผ์„œ addView๋ฅผ ํ†ตํ•ด MapView๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ์‹์„ ์ ์šฉํ•ด์•ผ ํ•œ๋‹ค.

   <RelativeLayout
        android:id="@+id/map_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        mapView = MapView(requireActivity())
        binding.mapView.addView(mapView)
        ...


๐Ÿ™‡โ€โ™€๏ธ ๋ถ€์กฑํ•œ ๋ถ€๋ถ„์ด ์žˆ๋‹ค๋ฉด ๋ง์”€ํ•ด์ฃผ์„ธ์š”! ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค!


๐Ÿ“ƒ์ฐธ๊ณ 

ํƒœ๊ทธ:

์นดํ…Œ๊ณ ๋ฆฌ:

์—…๋ฐ์ดํŠธ:

๋Œ“๊ธ€๋‚จ๊ธฐ๊ธฐ