관리 메뉴

막내의 막무가내 프로그래밍 & 일상

[안드로이드] 카카오링크 API 구현 방법 (feat. 카카오톡 공유하기 기능, 피드형 메시지) 본문

안드로이드/코틀린 & 아키텍처 & Recent

[안드로이드] 카카오링크 API 구현 방법 (feat. 카카오톡 공유하기 기능, 피드형 메시지)

막무가내막내 2021. 4. 8. 17:14
728x90

 

 

 

게시글에 아무 사진이나 올려서 농촌봉사활동과 전혀 상관없는 치킨이 등장했네요 ㅈㅅ..

안녕하세요 카카오링크를 구현해보고 싶어 기존 프로젝트에 기능을 구현해보았습니다.

이에 대해 포스팅을 해보겠습니다. ㅎㅎ

 

 

[카카오링크란?]

카카오링크는 사용자가 카카오톡 메시지로 친구에게 메시지에 담긴 정보를 공유하는 기능입니다. 사용자는 카카오링크가 적용된 웹 페이지 또는 특정 정보를 자신의 카카오톡 친구에게 공유할 수 있습니다.

 


[어플리케이션 등록]

카카오 링크를 구현하기전에 애플리케이션 등록을 진행해주어야 합니다. 밑 링크들을 참고해주세요.

카카오링크 관련만 집중해서 포스팅하고 기본 세팅같은건 공식문서 URL로 남기도록 하겠습니다. !

 

developers.kakao.com/docs/latest/ko/getting-started/app

 

Kakao Developers

카카오 API를 활용하여 다양한 어플리케이션을 개발해보세요. 카카오 로그인, 메시지 보내기, 친구 API, 인공지능 API 등을 제공합니다.

developers.kakao.com

youngest-programming.tistory.com/93

 

[안드로이드] 카카오톡 로그인

구글로그인과 페이스북로그인에 이어서 카카오톡 로그인을 해본 걸 정리하는 포스팅을 갖도록 해보겠습니다. [2020-06-21 업데이트] 참고로 저는 구글 파이어베이스의 OAuth 토큰을 사용하기 위해

youngest-programming.tistory.com

 

 

 


[시작하기전 기본 모듈 세팅]

카카오 기본 모듈들 설치도 필요니다. 밑 링크를 꼭 참고해서 세팅해주세요 :) 제가 적은거 말고도 키해시등록 등이 필요해요 !

developers.kakao.com/docs/latest/ko/getting-started/sdk-android

 

Kakao Developers

카카오 API를 활용하여 다양한 어플리케이션을 개발해보세요. 카카오 로그인, 메시지 보내기, 친구 API, 인공지능 API 등을 제공합니다.

developers.kakao.com

[ 프로젝트 수준 builde.gradle]

프로젝트의 Gradle 설정을 통해 Android SDK를 간편하게 연동할 수 있습니다. Android SDK를 적용할 프로젝트의 build.gradle(Project) 파일에 다음과 같이 Android SDK 레파지토리(Repository)를 설정합니다.

maven { url 'https://devrepo.kakao.com/nexus/content/groups/public/' }

 

[ 앱 수준 bulilde.gradle]

build.gradle(Module) 파일에 필요한 모듈을 설정합니다. 저는 RxKotlin을 활용했습니다.

dependencies {
  implementation "com.kakao.sdk:v2-user:2.4.2" // 카카오 로그인
  implementation "com.kakao.sdk:v2-talk:2.4.2" // 친구, 메시지(카카오톡)
  implementation "com.kakao.sdk:v2-story:2.4.2" // 카카오스토리
  implementation "com.kakao.sdk:v2-link:2.4.2" // 메시지(카카오링크)
  implementation "com.kakao.sdk:v2-navi:2.4.2" // 카카오내비
}

//RxKotlin
dependencies {
  implementation "com.kakao.sdk:v2-user-rx:2.4.2" // 카카오 로그인
  implementation "com.kakao.sdk:v2-talk-rx:2.4.2" // 친구, 메시지(카카오톡) 
  implementation "com.kakao.sdk:v2-story-rx:2.4.2" // 카카오스토리
  implementation "com.kakao.sdk:v2-link-rx:2.4.2" // 메시지(카카오링크)
  implementation "com.kakao.sdk:v2-navi:2.4.2" // 카카오내비
}

 

[manifest 권한]

 <!-- 인터넷 사용 권한 설정-->
    <uses-permission android:name="android.permission.INTERNET" />

 

[Java8 사용설정 필요]

// Java 8 사용을 위한 build.gradle 설정
android {

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

    kotlinOptions {
        jvmTarget = "1.8"
    }
}

 

 

[Application 클래스 생성 및 KakaoSdk 초기화]

class GlobalApplication : Application() {
  override fun onCreate() {
    super.onCreate()
    // 다른 초기화 코드들

    // Kakao SDK 초기화
    KakaoSdk.init(this, "{NATIVE_APP_KEY}")
  }
}
<application
    <!-- android:name 설정 -->
    android:name=".GlobalApplication"
    ...
>

 

[나의 구현] 

카카오로그인과 Koin도 함께 있어서 위 코드처럼 카카오링크에 필요한 부분만 추가해주면 됩니다.

package com.mtjin.nomoneytrip.di

import android.app.Application
import android.content.res.Resources
import com.kakao.auth.*
import com.kakao.sdk.common.KakaoSdk
import com.mtjin.nomoneytrip.BuildConfig
import com.mtjin.nomoneytrip.module.*
import org.koin.android.ext.koin.androidContext
import org.koin.android.ext.koin.androidLogger
import org.koin.core.context.startKoin
import org.koin.core.logger.Level


class KoinApplication : Application() {

    override fun onCreate() {
        super.onCreate()
        instance = this
        // Kakao Sdk 초기화
        KakaoSDK.init(KakaoSDKAdapter())
        //카카오링크
        KakaoSdk.init(this, "발급받은 해시키")

        startKoin {
            if (BuildConfig.DEBUG) {
                androidLogger()
            } else {
                androidLogger(Level.NONE)
            }
            androidContext(this@KoinApplication)
            modules(
                repositoryModule,
                localDataModule,
                remoteDataModule,
                viewModelModule,
                apiModule,
                firebaseModule
            )
        }
    }

    override fun onTerminate() {
        super.onTerminate()
        instance = null
    }

    class KakaoSDKAdapter : KakaoAdapter() {
        override fun getSessionConfig(): ISessionConfig {
            return object : ISessionConfig {
                override fun getAuthTypes(): Array<AuthType> {
                    return arrayOf(AuthType.KAKAO_LOGIN_ALL)
                }

                override fun isUsingWebviewTimer(): Boolean {
                    return false
                }

                override fun isSecureMode(): Boolean {
                    return false
                }

                override fun getApprovalType(): ApprovalType? {
                    return ApprovalType.INDIVIDUAL
                }

                override fun isSaveFormData(): Boolean {
                    return true
                }
            }
        }

        // Application이 가지고 있는 정보를 얻기 위한 인터페이스
        override fun getApplicationConfig(): IApplicationConfig {
            return IApplicationConfig { getGlobalApplicationContext() }
        }
    }

    companion object {
        private var instance: KoinApplication? = null

        fun getGlobalApplicationContext(): KoinApplication? {
            checkNotNull(instance) { "This Application does not inherit com.kakao.GlobalApplication" }
            return instance
        }
    }

}

 


 

[구현 방법]

 

이제 카카오링크 구현방법에 대해 살펴보겠습니다.

저는 밑 공식문서를 참고하여 구현했으며 문서도 꼭 읽어보시길 추천드립니다. 매우 잘되어있습니다. ㅎㅎ

developers.kakao.com/docs/latest/ko/message/android-link

 

Kakao Developers

카카오 API를 활용하여 다양한 어플리케이션을 개발해보세요. 카카오 로그인, 메시지 보내기, 친구 API, 인공지능 API 등을 제공합니다.

developers.kakao.com

 

 

먼저 카카오링크 API는 다음과 같은 로직으로 구성으로 되어있습니다.

https://developers.kakao.com/docs/latest/ko/message/common

 

 

 

 

카카오링크 구현을 앞서 세팅말고도 카카오링크 관련 추가 세팅이 필요합니다.

 

1. 패키지 쿼리 및 상호작용 설정해야합니다.

Android SDK 2.4.0 이상을 사용할 경우, 해당 설정은 필요하지 않습니다.

Android 11에서는 카카오톡 앱을 통해 메시지를 보내기 위해 패키지 쿼리 및 상호작용 설정이 필요합니다.

앱이 Android 11을 타겟팅하는 경우, AndroidManifest.xml에 아래와 같이 queries 요소를 추가해야 합니다. 자세한 내용은 Android 11의 패키지 공개 상태를 참고합니다.

<manifest package="com.example.sample">
    <!--queries에 카카오톡 패키지 추가-->
    <queries>
        <package android:name="com.kakao.talk" />
    </queries>
    ...
</manifest>

 

 

2. 커스텀 스킴(Custom scheme) 설정하기

사용자가 카카오톡 메시지의 버튼을 통해 앱을 실행할 수 있도록 하려면 AndroidManifest.xml 파일에 앱 실행 스킴 설정을 해야 합니다.

[문서]

<activity android:name=".{YOUR_ACTIVITY_NAME}">
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />

        <!-- "kakao{YOUR_NATIVE_APP_KEY}://kakaolink" 형식의 앱 실행 스킴을 설정하는데 사용 -->
        <data android:host="kakaolink"
                android:scheme="kakao{YOUR_NATIVE_APP_KEY}" />
    </intent-filter>
</activity>

 

[나의 구현]

가장 먼저 시작하는 화면인 스플래쉬 액티비티에 추가했습니다.

<activity android:name=".views.splash.SplashActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />

                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
                <!-- "kakao{YOUR_NATIVE_APP_KEY}://kakaolink" 형식의 앱 실행 스킴을 설정하는데 사용 -->
                <data
                    android:host="@string/kakao_link"
                    android:scheme="@string/kakao_scheme" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

@string/ 은 다음과 같이 발급받은 키를 등록해주면 됩니다.  kakao_scheme는 카카오 애플리케이션 키에서 앞에 "kakao"만 덧붙여 주면 됩니다.

//    values/strings.xml
<string name="kakao_app_key">541c8caff279뒤에보안상생략</string>
<string name="kakao_scheme">kakao541c8caff279뒤에보안상생략</string>
<string name="kakao_link">kakaolink.com</string>

 

추가로 카카오톡 메시지의 앱 실행 버튼에는 androidExecutionParamsiosExecutionParams로 앱 실행 시 앱에 전달할 파라미터를 설정할 수 있습니다. 이 파라미터는 앱 실행 스킴에 다음과 같은 형식으로 추가됩니다.

kakao{YOUR_NATIVE_APP_KEY}://kakaolink${androidExecutionParams}
kakao{YOUR_NATIVE_APP_KEY}://kakaolink${iosExecutionParams}

 

 

 

 

3. 카카오링크 구현(메시지만들기 및 전송)

 

이제 설정은 끝났고 구현을 해야합니다.

카카오링크의 핵심 구현은 크게 메시지만들기 -> 메시지전송(공유)로 되어 있습니다.

순서대로 살펴보겠습니다. :)

 

3-1. 메시지만들기

카카오링크와 카카오톡 메시지 API는 카카오톡 메시지를 구성할 때 com.kakao.sdk.template에 정의된 클래스들을 공통적으로 사용합니다. 메시지 템플릿 > 기본 메시지 템플릿 구성 요소를 참고하여 원하는 메시지 템플릿에서 지원하는 객체들을 포함하여 메시지를 구성합니다.

 

https://developers.kakao.com/docs/latest/ko/getting-started/sdk-android

카카오에서 Feed, List, Location, Commerece, Text 형과 같이 각각의 목적에 맞는 기본 메시지 템플릿들을 제공합니다. 이것들을 활용하면 아주 쉽게 메시지를 만들 수 있습니다.  (각각의 메시지유형에 대한 설명은 메시지 템플릿 > 기본 메시지 템플릿 구성 요소 에서 자세히 보실 수 있습니다.) 

저같은 경우는 Feed가 적절하겠다고 느껴 Feed 형으로 구현했습니다.

 

 

[공식문서 Feed 형 예시]

val defaultFeed = FeedTemplate(
    content = Content(
        title = "딸기 치즈 케익",
        description = "#케익 #딸기 #삼평동 #카페 #분위기 #소개팅",
        imageUrl = "http://mud-kage.kakao.co.kr/dn/Q2iNx/btqgeRgV54P/VLdBs9cvyn8BJXB3o7N8UK/kakaolink40_original.png",
        link = Link(
            webUrl = "https://developers.kakao.com",
            mobileWebUrl = "https://developers.kakao.com"
        )
    ),
    social = Social(
        likeCount = 286,
        commentCount = 45,
        sharedCount = 845
    ),
    buttons = listOf(
        Button(
            "웹으로 보기",
            Link(
                webUrl = "https://developers.kakao.com",
                mobileWebUrl = "https://developers.kakao.com"
            )
        ),
        Button(
            "앱으로 보기",
            Link(
                androidExecParams = mapOf("key1" to "value1", "key2" to "value2"),
                iosExecParams = mapOf("key1" to "value1", "key2" to "value2")
            )
        )
    )
)

 

[나의 구현]

공유하기 버튼 클릭 시 실행되는 함수를 만들었습니다.

url은 모바일 url만 적용했으며 앱 다운로드로 가게끔 설정했습니다.

더 고차원적으로 개발하려면 노티피케이션 pendingIntent 처리하듯이 앱이 다운되어있는 상태면 앱이 켜지게 스키마를 설정하고 Link 객체의 androidExecParams 파라미터를 활용하여 해당 게시글 상세화면으로 가게하는게 좋을겁니다. (저는 실제 상용화된 앱도 아니여서 여기까지만 구현했습니다. ㅎㅎ )

 

그리고 위의 기본예시에서 필요없는 것들은 제거했습니다.

private fun sendKakaoLink(userReview: UserReview) {
        // 메시지 템플릿 만들기 (피드형)
        val defaultFeed = FeedTemplate(
            content = Content(
                title = userReview.product.title,
                description = userReview.review.content,
                imageUrl = userReview.review.image,
                link = Link(
                    mobileWebUrl = "https://play.google.com/store/apps/details?id=com.mtjin.nomoneytrip"
                )
            ), social = Social(
                likeCount = userReview.review.recommendList.size
            ),
            buttons = listOf(
                Button(
                    "앱으로 보기",
                    Link(
                        androidExecParams = mapOf(
                            "key1" to "value1",
                            "key2" to "value2"
                        ) // 내 앱에서 파라미터보낼건 필요없음 (앱 다운로드만 유도할것이다)
                    )
                )
            )
        )
        
        //메시지 보내기 (뒤에서 살펴볼것)

 

 

 

3-2. 기본 템플릿으로 메시지 보내기

다양한 메시지 보내기 종류 중 저는 기본 제공되는 피드를 사용했고 추가적인 커스텀한 기능이 필요 없어서 기본 템플릿으로 메시지 보내기를 선택하였습니다. 

 

 

[공식문서 구현 예시] 

코틀린만 사용시

// 피드 메시지 보내기
LinkClient.instance.defaultTemplate(context, defaultFeed) { linkResult, error ->
    if (error != null) {
        Log.e(TAG, "카카오링크 보내기 실패", error)
    }
    else if (linkResult != null) {
        Log.d(TAG, "카카오링크 보내기 성공 ${linkResult.intent}")
        startActivity(linkResult.intent)

        // 카카오링크 보내기에 성공했지만 아래 경고 메시지가 존재할 경우 일부 컨텐츠가 정상 동작하지 않을 수 있습니다.
        Log.w(TAG, "Warning Msg: ${linkResult.warningMsg}")
        Log.w(TAG, "Argument Msg: ${linkResult.argumentMsg}")
    }
}

Rx 사용시

var disposables = CompositeDisposable()

// 피드 메시지 보내기
LinkClient.rx.defaultTemplate(context, defaultFeed)
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe({ linkResult ->
        Log.d(TAG, "카카오링크 보내기 성공 ${linkResult.intent}")
        startActivity(linkResult.intent)

        // 카카오링크 보내기에 성공했지만 아래 경고 메시지가 존재할 경우 일부 컨텐츠가 정상 동작하지 않을 수 있습니다.
        Log.w(TAG, "Warning Msg: ${linkResult.warningMsg}")
        Log.w(TAG, "Argument Msg: ${linkResult.argumentMsg}")
    }, { error ->
        Log.e(TAG, "카카오링크 보내기 실패 ", error)
    })
    .addTo(disposables)

 

[나의 구현 - Rx사용, //피드메시지 보내기 주석 밑 코드]

private fun sendKakaoLink(userReview: UserReview) {
        // 메시지 템플릿 만들기 (피드형)
        val defaultFeed = FeedTemplate(
            content = Content(
                title = userReview.product.title,
                description = userReview.review.content,
                imageUrl = userReview.review.image,
                link = Link(
                    mobileWebUrl = "https://play.google.com/store/apps/details?id=com.mtjin.nomoneytrip"
                )
            ), social = Social(
                likeCount = userReview.review.recommendList.size
            ),
            buttons = listOf(
                Button(
                    "앱으로 보기",
                    Link(
                        androidExecParams = mapOf(
                            "key1" to "value1",
                            "key2" to "value2"
                        ) // 내 앱에서 파라미터보낼건 필요없음 (앱 다운로드만 유도할것이다)
                    )
                )
            )
        )
        // 피드 메시지 보내기
        LinkClient.rx.defaultTemplate(thisContext, defaultFeed)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe({ linkResult ->
                Log.d(TAG, "카카오링크 보내기 성공 ${linkResult.intent}")
                startActivity(linkResult.intent)

                // 카카오링크 보내기에 성공했지만 아래 경고 메시지가 존재할 경우 일부 컨텐츠가 정상 동작하지 않을 수 있습니다.
                Log.w(TAG, "Warning Msg: ${linkResult.warningMsg}")
                Log.w(TAG, "Argument Msg: ${linkResult.argumentMsg}")
            }, { error ->
                Log.e(TAG, "카카오링크 보내기 실패 ", error)
            })
            .addTo(compositeDisposable)
    }

 

 

 

[구현한 전체 코드]

참고로 compositDisaposable 은 BaseFragment에 선언해놨습니다. 

package com.mtjin.nomoneytrip.views.community

import android.graphics.Color
import android.util.Log
import android.view.View
import android.widget.AdapterView
import android.widget.TextView
import androidx.lifecycle.Observer
import androidx.navigation.fragment.findNavController
import com.kakao.sdk.link.LinkClient
import com.kakao.sdk.link.rx
import com.kakao.sdk.template.model.*
import com.mtjin.nomoneytrip.R
import com.mtjin.nomoneytrip.base.BaseFragment
import com.mtjin.nomoneytrip.data.community.UserReview
import com.mtjin.nomoneytrip.databinding.FragmentCommunityBinding
import com.mtjin.nomoneytrip.utils.TAG
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.rxkotlin.addTo
import io.reactivex.schedulers.Schedulers
import org.koin.androidx.viewmodel.ext.android.viewModel

class CommunityFragment : BaseFragment<FragmentCommunityBinding>(R.layout.fragment_community) {
    private val viewModel: CommunityViewModel by viewModel()

    override fun init() {
        binding.vm = viewModel
        initViewModelCallback()
        initAdapter()
        initView()
    }


    private fun initView() {
        binding.spCities.setBackgroundColor(Color.TRANSPARENT)
        binding.spCities.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
            override fun onItemSelected(
                parent: AdapterView<*>,
                view: View?,
                position: Int,
                id: Long
            ) {
                view?.let {
                    viewModel.requestReviews((parent.getChildAt(0) as TextView).text.toString())
                }
            }

            override fun onNothingSelected(parent: AdapterView<*>) {}
        }
    }

    private fun initAdapter() {
        binding.rvReviews.adapter = CommunityAdapter(context = thisContext, recommendClick = {
            viewModel.updateReviewRecommend(it)
        }, productClick = {
            findNavController().navigate(
                CommunityFragmentDirections.actionBottomNav2ToLodgmentDetailFragment(
                    it.product
                )
            )
        }, shareClick = {
            sendKakaoLink(it)
        })
    }

    private fun initViewModelCallback() {
        with(viewModel) {
            goTourHistory.observe(this@CommunityFragment, Observer {
                findNavController().navigate(
                    CommunityFragmentDirections.actionBottomNav2ToTourHistoryFragment(it.toTypedArray())
                )
            })

            goTourNoHistory.observe(this@CommunityFragment, Observer {
                findNavController().navigate(CommunityFragmentDirections.actionBottomNav2ToTourNoHistoryFragment())
            })
        }
    }

    private fun sendKakaoLink(userReview: UserReview) {
        // 메시지 템플릿 만들기 (피드형)
        val defaultFeed = FeedTemplate(
            content = Content(
                title = userReview.product.title,
                description = userReview.review.content,
                imageUrl = userReview.review.image,
                link = Link(
                    mobileWebUrl = "https://play.google.com/store/apps/details?id=com.mtjin.nomoneytrip"
                )
            ), social = Social(
                likeCount = userReview.review.recommendList.size
            ),
            buttons = listOf(
                Button(
                    "앱으로 보기",
                    Link(
                        androidExecParams = mapOf(
                            "key1" to "value1",
                            "key2" to "value2"
                        ) // 내 앱에서 파라미터보낼건 필요없음 (앱 다운로드만 유도할것이다)
                    )
                )
            )
        )
        // 피드 메시지 보내기
        LinkClient.rx.defaultTemplate(thisContext, defaultFeed)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe({ linkResult ->
                Log.d(TAG, "카카오링크 보내기 성공 ${linkResult.intent}")
                startActivity(linkResult.intent)

                // 카카오링크 보내기에 성공했지만 아래 경고 메시지가 존재할 경우 일부 컨텐츠가 정상 동작하지 않을 수 있습니다.
                Log.w(TAG, "Warning Msg: ${linkResult.warningMsg}")
                Log.w(TAG, "Argument Msg: ${linkResult.argumentMsg}")
            }, { error ->
                Log.e(TAG, "카카오링크 보내기 실패 ", error)
            })
            .addTo(compositeDisposable)
    }


}

 

[구현 결과]

 

 

 

 

이상 카카오링크 구현방법에 대해 알아봤습니다.

 

댓글과 공감은 큰 힘이 됩니다. 감사합니다. !!

 

 

 

728x90
Comments