반응형

다음(카카오) 지도를 안드로이드에서 사용하기 위해 필요한 작업과  지도에 현재 위치를 표시하는 예제를 포함하고 있습니다.

.

최초 작성 2019. 3. 6

최종 작성 2019. 9. 26




준비 작업부터 앱 실행까지 설명합니다.  틀린 점이 있으면 댓글로 남겨주세요.

 

1. 카카오 개발자 사이트에 접속하여 카카오 계정으로 로그인합니다.

https://developers.kakao.com

 




2. 개발자 등록을 합니다.

 




3. 앱 만들기를 합니다. 이름에 실제 앱 이름을 입력하지 않아도 됩니다. 

 




4. 생성된 키를 보여줍니다. 따로 복사해둡니다. 

 




5. 왼쪽에 보이는 항목에서 일반을 선택합니다.

 




6. 플랫폼 추가를 선택합니다.

 




7. Android를 체크하고 패키지명을 입력하고 빈 공간을 클릭하면 마켓 URL은 자동으로 입력됩니다. 

 




8. OpenSSL을 설치합니다.

 https://slproweb.com/products/Win32OpenSSL.html 에서 Win64 OpenSSL  항목에 있는 것을 다운로드 받으세요.  

 

C:\Program Files\OpenSSL-Win64\bin를 시스템 변수 path에 추가하여 명령 프롬프트에서 openssl을 실행할 수 있도록 합니다. 

 




9. 명령 프롬프트에서 다음 명령으로  디버그용 해시키를 얻습니다. 따로 복사해두세요

 

keytool -exportcert -alias androiddebugkey -keystore "%USERPROFILE%\.android\debug.keystore" -storepass android -keypass android | openssl sha1 -binary | openssl base64

 




10. 릴리즈용 해시키를 얻으려면 추가 작업이 필요합니다.  

앞에서 지정한 패키지 이름으로 프로젝트를 만들고  빌드해서 앱을 스마트폰에 업로드를 한 후 진행해야합니다. 

안드로이드 스튜디오의 메뉴에서 Build > Generate Signed Bundle / APK를 선택합니다. 




11. 앱스토어에 올릴게 아니므로 APK를 선택했습니다. Android App Bundle은 추후 테스트해볼 예정입니다. 

 




12. 새로운 키 스토어를 생성하기 위해 Create new 버튼을 선택합니다. 

 




13. key store path에 경로와 파일 이름을 지정해줍니다.

이미 생성된 확장자가 jsk인 파일을 지정해주는게 아니라 새로 확장자 jsk인 파일을 만드는 겁니다. 

 

아래쪽에 보이는 Password 항목에 비밀번호를 지정해줍니다.

Alias에는 키에 대한 적당한 별칭을 적어줍니다. 그 아래에 보이는 Password 항목에 비밀번호를 지정해줍니다.

Certificate에서 항목 하나만 기입하면 됩니다

 




14. 다음처럼 항목이 자동으로 채워집니다.

 




15. Build Variants에서 debug와 release 모두 선택 한 후, V2를 체크해주었습니다. 

 




16. 에러가 날 수 있는데 다시 해보면 됩니다.




17. 이제 릴리즈용 해시키를 가져옵니다. <release_key_alias>와 <release_keystore_path>는 앞에서 지정해주었던 별칭과 경로를 적어주면 됩니다. 

앞에서 지정했던 암호를 입력해주어야 해시키를 얻을 수 있습니다. 따로 복사해두세요. . 

 

keytool -exportcert -alias <release_key_alias> -keystore <release_keystore_path> | openssl sha1 -binary | openssl base64

 

keytool -exportcert -alias webnautes -keystore "%USERPROFILE%\.android\debug.keystore" | openssl sha1 -binary | openssl base64






18. 키 해시에 릴리즈용, 디버그용을 입력하고 저장을 선택합니다. 

현재 페이지에서 네이티브 앱 키와 패키지 명, 키해시는 따로 저장해두어야 합니다. 

 




19.  http://apis.map.daum.net/android/guide/ 에서 API SDK를 다운로드 받습니다. 

글 작성 시점에서 최신 버전은  2019년 7월 22일에 배포된 1.4.0.0입니다. 

 




20. 압축을 풀어 libs 폴더에 있는 폴더와 파일을 복사하여

 



앱 프로젝트의 app\src\main경로에  jniLibs 폴더를 생성한 후 붙여넣기 해줍니다.

 





21. AndroidManifest.xml 매니페스트 파일에 다음 권한을 추가합니다. 인터넷 접근 권한과 위치 정보 접근 권한입니다. 

 

<?xml version="1.0" encoding="utf-8"?>

    package="com.tistory.webnautes.kakao_map_test">

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

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"





22.  <application> 태그 하위요소로 <meta-data> 태그를 사용하여 복사해둔 네이티브 앱 키를 입력해줍니다.

 

 

            android:allowBackup="true"        android:icon="@mipmap/ic_launcher"        android:label="@string/app_name"        android:roundIcon="@mipmap/ic_launcher_round"        android:supportsRtl="true"        android:theme="@style/AppTheme">        <meta-data
            android:name="com.kakao.sdk.AppKey"
            android:value="네이티브 앱 키"/>
               





23. 메뉴에서 File > Project Structure를 선택합니다. 

 

왼쪽 항목에서 Dependencies를 선택합니다.

 





app을 선택하고 빨간색 사각형으로 표시한  + 아이콘을 클릭합니다. 

 





24. 메뉴에서 Jar dependency를 선택합니다.

 




25. 복사해두었던 libDaumMapAndroid.jar를 추가로 입력하고 OK 버튼을 클릭합니다.

 




26. 이제 jar 파일이 프로젝트에 추가되었습니다. 

 




27. 이제 http://apis.map.daum.net/android/sample/ 에서 제공하는 다음 지도 샘플을 사용해볼 수 있습니다.

 

다음은 샘플에서 가져온 지도에 현재 위치 보여주는 예제입니다.  

전체 코드는 LocationDemoActivity.java 파일을 참고하세요. 



실행시켜보면 위치 정보에 대한 런타임 퍼미션을 허용할지 물어봅니다. 

허용시 지도에 현재 위치와 바라보고 있는 방향을 보여줍니다.

 

 



다음 지도에서 제공하는 모드 테스트시 다음 한줄을 변경하면 됩니다. 코드에 두 군데 존재합니다.

가능한 모드는 샘플 앱에 있는  LocationDemoActivity.java 파일에 있습니다.

 

mMapView.setCurrentLocationTrackingMode(MapView.CurrentLocationTrackingMode.TrackingModeOnWithHeading);




다음은 테스트에 사용한 전체 소스 코드입니다. 

 

activity_main.xml

 

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

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

</RelativeLayout>





MainActivity.java

 

package com.tistory.webnautes.kakao_map_test;


import android.Manifest;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.location.LocationManager;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.appcompat.app.AppCompatActivity;
import android.util.Log;
import android.widget.Toast;


import net.daum.mf.map.api.MapPoint;
import net.daum.mf.map.api.MapReverseGeoCoder;
import net.daum.mf.map.api.MapView;

public class MainActivity extends AppCompatActivity implements MapView.CurrentLocationEventListener, MapReverseGeoCoder.ReverseGeoCodingResultListener {

    private static final String LOG_TAG = "MainActivity";

    private MapView mMapView;


    private static final int GPS_ENABLE_REQUEST_CODE = 2001;
    private static final int PERMISSIONS_REQUEST_CODE = 100;
    String[] REQUIRED_PERMISSIONS  = {Manifest.permission.ACCESS_FINE_LOCATION};




    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        mMapView = (MapView) findViewById(R.id.map_view);
        //mMapView.setDaumMapApiKey(MapApiConst.DAUM_MAPS_ANDROID_APP_API_KEY);
        mMapView.setCurrentLocationEventListener(this);

        if (!checkLocationServicesStatus()) {

            showDialogForLocationServiceSetting();
        }else {

            checkRunTimePermission();
        }

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mMapView.setCurrentLocationTrackingMode(MapView.CurrentLocationTrackingMode.TrackingModeOff);
        mMapView.setShowCurrentLocationMarker(false);
    }

    @Override
    public void onCurrentLocationUpdate(MapView mapView, MapPoint currentLocation, float accuracyInMeters) {
        MapPoint.GeoCoordinate mapPointGeo = currentLocation.getMapPointGeoCoord();
        Log.i(LOG_TAG, String.format("MapView onCurrentLocationUpdate (%f,%f) accuracy (%f)", mapPointGeo.latitude, mapPointGeo.longitude, accuracyInMeters));
    }


    @Override
    public void onCurrentLocationDeviceHeadingUpdate(MapView mapView, float v) {

    }

    @Override
    public void onCurrentLocationUpdateFailed(MapView mapView) {

    }

    @Override
    public void onCurrentLocationUpdateCancelled(MapView mapView) {

    }

    @Override
    public void onReverseGeoCoderFoundAddress(MapReverseGeoCoder mapReverseGeoCoder, String s) {
        mapReverseGeoCoder.toString();
        onFinishReverseGeoCoding(s);
    }

    @Override
    public void onReverseGeoCoderFailedToFindAddress(MapReverseGeoCoder mapReverseGeoCoder) {
        onFinishReverseGeoCoding("Fail");
    }

    private void onFinishReverseGeoCoding(String result) {
//        Toast.makeText(LocationDemoActivity.this, "Reverse Geo-coding : " + result, Toast.LENGTH_SHORT).show();
    }




    /*
    * ActivityCompat.requestPermissions를 사용한 퍼미션 요청의 결과를 리턴받는 메소드입니다.
    */
    @Override
    public void onRequestPermissionsResult(int permsRequestCode,
                                          @NonNull String[] permissions,
                                          @NonNull int[] grandResults) {

        if ( permsRequestCode == PERMISSIONS_REQUEST_CODE && grandResults.length == REQUIRED_PERMISSIONS.length) {

            // 요청 코드가 PERMISSIONS_REQUEST_CODE 이고, 요청한 퍼미션 개수만큼 수신되었다면

            boolean check_result = true;


            // 모든 퍼미션을 허용했는지 체크합니다.

            for (int result : grandResults) {
                if (result != PackageManager.PERMISSION_GRANTED) {
                    check_result = false;
                    break;
                }
            }


            if ( check_result ) {
                Log.d("@@@", "start");
                //위치 값을 가져올 수 있음
                mMapView.setCurrentLocationTrackingMode(MapView.CurrentLocationTrackingMode.TrackingModeOnWithHeading);
            }
            else {
                // 거부한 퍼미션이 있다면 앱을 사용할 수 없는 이유를 설명해주고 앱을 종료합니다.2 가지 경우가 있습니다.

                if (ActivityCompat.shouldShowRequestPermissionRationale(this, REQUIRED_PERMISSIONS[0])) {

                    Toast.makeText(MainActivity.this, "퍼미션이 거부되었습니다. 앱을 다시 실행하여 퍼미션을 허용해주세요.", Toast.LENGTH_LONG).show();
                    finish();


                }else {

                    Toast.makeText(MainActivity.this, "퍼미션이 거부되었습니다. 설정(앱 정보)에서 퍼미션을 허용해야 합니다. ", Toast.LENGTH_LONG).show();

                }
            }

        }
    }

    void checkRunTimePermission(){

        //런타임 퍼미션 처리
        // 1. 위치 퍼미션을 가지고 있는지 체크합니다.
        int hasFineLocationPermission = ContextCompat.checkSelfPermission(MainActivity.this,
                Manifest.permission.ACCESS_FINE_LOCATION);


        if (hasFineLocationPermission == PackageManager.PERMISSION_GRANTED ) {

            // 2. 이미 퍼미션을 가지고 있다면
            // ( 안드로이드 6.0 이하 버전은 런타임 퍼미션이 필요없기 때문에 이미 허용된 걸로 인식합니다.)


            // 3.  위치 값을 가져올 수 있음
            mMapView.setCurrentLocationTrackingMode(MapView.CurrentLocationTrackingMode.TrackingModeOnWithHeading);


        } else//2. 퍼미션 요청을 허용한 적이 없다면 퍼미션 요청이 필요합니다. 2가지 경우(3-1, 4-1)가 있습니다.

            // 3-1. 사용자가 퍼미션 거부를 한 적이 있는 경우에는
            if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this, REQUIRED_PERMISSIONS[0])) {

                // 3-2. 요청을 진행하기 전에 사용자가에게 퍼미션이 필요한 이유를 설명해줄 필요가 있습니다.
                Toast.makeText(MainActivity.this, "이 앱을 실행하려면 위치 접근 권한이 필요합니다.", Toast.LENGTH_LONG).show();
                // 3-3. 사용자게에 퍼미션 요청을 합니다. 요청 결과는 onRequestPermissionResult에서 수신됩니다.
                ActivityCompat.requestPermissions(MainActivity.this, REQUIRED_PERMISSIONS,
                        PERMISSIONS_REQUEST_CODE);


            } else {
                // 4-1. 사용자가 퍼미션 거부를 한 적이 없는 경우에는 퍼미션 요청을 바로 합니다.
                // 요청 결과는 onRequestPermissionResult에서 수신됩니다.
                ActivityCompat.requestPermissions(MainActivity.this, REQUIRED_PERMISSIONS,
                        PERMISSIONS_REQUEST_CODE);
            }

        }

    }



    //여기부터는 GPS 활성화를 위한 메소드들
    private void showDialogForLocationServiceSetting() {

        AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
        builder.setTitle("위치 서비스 비활성화");
        builder.setMessage("앱을 사용하기 위해서는 위치 서비스가 필요합니다.\n"
                + "위치 설정을 수정하실래요?");
        builder.setCancelable(true);
        builder.setPositiveButton("설정", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int id) {
                Intent callGPSSettingIntent
                        = new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS);
                startActivityForResult(callGPSSettingIntent, GPS_ENABLE_REQUEST_CODE);
            }
        });
        builder.setNegativeButton("취소", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int id) {
                dialog.cancel();
            }
        });
        builder.create().show();
    }


    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        switch (requestCode) {

            case GPS_ENABLE_REQUEST_CODE:

                //사용자가 GPS 활성 시켰는지 검사
                if (checkLocationServicesStatus()) {
                    if (checkLocationServicesStatus()) {

                        Log.d("@@@", "onActivityResult : GPS 활성화 되있음");
                        checkRunTimePermission();
                        return;
                    }
                }

                break;
        }
    }

    public boolean checkLocationServicesStatus() {
        LocationManager locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);

        return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)
                || locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
    }
}





참고

 

http://apis.map.daum.net/android/guide/






문제 발생시 지나치지 마시고 댓글 남겨주시면 가능한 빨리 답장드립니다.


제가 쓴 책도 한번 검토해보세요 ^^

+ Recent posts