모두 소셜 로그인 .. 마음 속에 품고 계시죠?
소셜 로그인이라는게 서비스 자체 로그인과 같은 귀찮은 플로우를 줄여준다 등의 메리트로 요즘 많이 사용이 되는 것 같습니다.
소셜 로그인에는 구글, 애플, 카카오, 네이버 등 각종 다양한 것들이 있는데 이번 아티클에서는 카카오 로그인에 대하여 말해볼까 합니다.
카카오 로그인은 개인적으로 개발자 입장에서는 다른 로그인에 비해 구현 난이도가 낮고, 사용자의 입장에서는 다른 로그인 중에서 가장 접근성이 좋다고 생각합니다.
그래서 이번 프로젝트에서도 저희는 구글, 카카오 로그인을 선택하였고, 앱잼 내에서 그 중 카카오 구현을 진행하였습니다.
그럼 이제 차근차근 앞에서부터 순서대로 진행해볼게요.
카카오 디벨로퍼 세팅

우선 카카오 디벨로퍼에 들어가서 앱을 만들어줍니다.
설정

그리고 설정에서 카카오 로그인, 동의 항복, 리다이렉트 URI를 설정해줘요
리다이렉트 URI

리다이렉트 URI란 앱 내에서 카카오 로그인을 실행한 후 다시 돌아갈 곳을 의미해요
그래서 카카오 로그인을 요청한 API를 등록해주시면 됩니다. 다시 그곳으로 돌아가기 위해서!
앱 키

그 다음은 앱키입니다.
사실 이 부분에서 저희가 알아야할 키는

네이티브 앱 키뿐이에요. 잘 복사해뒀다가 이따가 써먹을 겁니다.
플랫폼

다음은 플랫폼 설정입니다.
그 안에는 안드로이드, iOS, 웹이 있지만

우리는 안드로이드 개발자들이니 안드로이드 설정만 하시면 되겠죠??
패키지명
우선 패키지명에는 정말 저희 프로젝트 패키지명을 작성하시면 됩니다.

헷갈리신다면 이 안드로이드 스튜디오를 키자마자 보이는 이 부분. 에서 org.sopt.at입니다.
마켓 URL
그 다음 마켓 URL은 플레이스토어에 출시한 이후에 등록하시면 됩니다. 이건 생략할게요. 출시하지 않는다면 굳이 넣어줄 필요가 없습니다.
키 해시
다음 키 해시 등록에는 이제 각자의 로컬에서 디버그 키 해시를 뽑은 다음에 등록하는 방법이 있고, 키스토어를 활용하는 방법이 있는데요.
우선 각자의 로컬에서 디버그 키 해시를 뽑는 방법은
var keyHash = Utility.getKeyHash(this)
Timber.tag("ㅋㅋㅋ").d("$keyHash")
이걸 MainActivity에 작성하신 다음에 실행 후 로그에서 keyHash를 뽑아주시면 됩니다.
그렇게 뽑은 값을 위의 카카오 디벨로퍼 안드로이드 부분의 키 해시에 등록해주시면 되는데요, 이 방법을 선택할 경우 같은 프로젝트 개발을 진행하는 모든 개발자들의 키 해시를 각각 등록해줘야 하기 때문에 굉장히 번거롭습니다.
때문에 이 방법은 출시 예정이 없는, 가벼운 팀프로젝트와 같은 프로젝트에서만 추천을 드립니다.
그럼 다른 방법인 키스토어 활용 방법은 무엇이냐?
signingConfigs {
getByName("debug") {
keyAlias = "androiddebugkey"
keyPassword = "android"
storeFile = file("$rootDir/keystore/debug.keystore")
storePassword = "android"
}
}
buildTypes {
debug {
signingConfig = signingConfigs.getByName("debug")
}
우선 gradle 파일에 이런 식으로 debug.keystore를 저장해줍니다.
사실은 출시예정이라면 keyAlias, keyPassword, storeFile, storePassword까지 모두 local properties에서 관리하며 보안에 신경쓰는게 좋습니다.
그건 우리 모두 알고있는 base url 감추는 방법을 쓰시면 됩니다.
이런 다음에는 안드로이드 스튜디오 터미널을 열고
cp ~/.android/debug.keystore keystore/debug.keystore
이것만 작성해주시면 알아서 내 컴퓨터 홈폴더에 자동생성된 디버그 keystore를 현재 프로젝트의 keystore 폴더에 복사됩니다.
이렇게 해준 후??
위의 각자 로컬에서 키해시 뽑는 방법을 소개해드렸죠?? 그 방법을 통해 keystore를 등록한 개발자의 키 해시를 추출해 카카오 디벨로퍼에 해당 키 해시만 등록해주면 됩니다.(나머지 다른 개발자의 키 해시는 필요하지 않음. 또 apk 추출 후 돌려볼 때 카카오 로그인이 정상적으로 동작하게 되어용. 때문에 이 방법을 정말정말 추천드립니다.)
프로젝트 세팅
이제 위에서 세팅한 카카오 디벨로퍼를 바탕으로 실제 우리의 프로젝트를 세팅해보도록 할게요.
의존성
일단 기본적으로 의존성을 추가해줍니다.
# Kakao SDK
kakao = "2.21.0"
# --- Kakao SDK ---
kakao-user = { group = "com.kakao.sdk", name = "v2-user", version.ref = "kakao" }
libs.versions.toml
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
maven { url = java.net.URI("https://devrepo.kakao.com/nexus/content/groups/public/") }
}
settings.gradle.kts
네이티브 앱 키
우리 위 카카오 디벨로퍼 세팅 때 네이티브 앱 키를 저장하라고 했었죠? 그 저장해둔 값을 이제 활용해보겠습니다.
일단 local properties에
certi.base.url = ""
kakao.native.app.key= 어쩌고저쩌고
이렇게 네이티브 앱 키 값을 넣어주세요.
그 후에는
val kakaoNativeAppKey = properties["kakao.native.app.key"].toString()
buildConfigField("String", "KAKAO_NATIVE_APP_KEY", "\"$kakaoNativeAppKey\"")
manifestPlaceholders["KAKAO_NATIVE_APP_KEY"] = kakaoNativeAppKey
gradle 파일
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application android:name=".CERTIApp"
~~
<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${KAKAO_NATIVE_APP_KEY}" />
</intent-filter>
</activity>
manifest 파일
class CERTIApp : Application() {
override fun onCreate() {
super.onCreate()
initKakaoSdk()
}
private fun initKakaoSdk() {
KakaoSdk.init(this, BuildConfig.KAKAO_NATIVE_APP_KEY)
}
}
App 파일
이렇게 해주시면 세팅 끝 ~~ 입니다.
여기서 트슛⚽️ 하나가 있습니다.
사실 트러블까지는 아니지만 리팩토링 관련 내용입니다.
이제 실제로 구현을 진행해야하는데,
저는 원래 뷰모델 내에서 카카오 로그인을 진행하는 과정을 넣어뒀었어요.
그런데 startActivity 등 context가 필요한 기능은 화면(screen)에서 처리하는 것이 안드로이드 아키텍처에 맞는 설계라는 피드백을 받았습니다.
실제로 별도 클래스로 분리하고 나니,
- ViewModel은 UI 상태 관리에만 집중하고
- 화면에서는 context가 필요한 부분만 책임져서
코드가 훨씬 읽기 쉽고 테스트, 유지보수, 추후 구글/애플 등 다른 소셜 로그인 추가 시 재활용성까지 훨씬 좋아졌어요.
처음엔 “굳이?” 싶었는데,
실제 리팩토링하고 나니 구조도 명확해지고, 메모리릭 걱정도 줄었습니다.
추천드립니다!
class KakaoLoginManager @Inject constructor() {
fun login(
context: Context,
onResult: (Result<OAuthToken>) -> Unit
) {
if (UserApiClient.instance.isKakaoTalkLoginAvailable(context)) {
UserApiClient.instance.loginWithKakaoTalk(context) { token, error ->
if (error is ClientError && error.reason == ClientErrorCause.Cancelled) return@loginWithKakaoTalk
when {
token != null -> onResult(Result.success(token))
error != null -> UserApiClient.instance.loginWithKakaoAccount(context) { token2, error2 ->
if (token2 != null) {
onResult(Result.success(token2))
} else {
onResult(Result.failure(error2 ?: Exception("Unknown error")))
}
}
}
}
} else {
UserApiClient.instance.loginWithKakaoAccount(context) { token, error ->
if (token != null) {
onResult(Result.success(token))
} else {
onResult(Result.failure(error ?: Exception("Unknown error")))
}
}
}
}
}
실제로 저는 이렇게 뽑기는 했는데 .. 사실 이렇게 하는게 정석적인 방법인지는 모르겠습니다. 제가 구현해서 실행했을 때는 문제가 없었긴 합니다. 그래도 혹시 모르니 참고만 해주시길 !
'🤖안드로이드🤖' 카테고리의 다른 글
| [안드로이드] 프로젝트 구조 뜯어보기 - Android SDK에 대하여 (5) | 2025.08.19 |
|---|---|
| [안드로이드] WindowInsets 조정하기 (2) | 2025.08.10 |
| [안드로이드] CMP/KMP 톺아보기🔎 (1) | 2025.06.07 |
| [안드로이드] ktlint와 GitHub Actions로 Android 코드 스타일 자동화하기 (0) | 2025.05.23 |
| [안드로이드] 빈혈 도메인 모델과 쓸모없는 유스케이스, 그리고 비대한 뷰모델 (0) | 2025.05.11 |