-
Notifications
You must be signed in to change notification settings - Fork 3
[Feat] 카카오 소셜 로그인 #76
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
c56e4fe
95ca8c8
e80c3a5
a4c5c28
48014b0
e00e585
cdff7a0
9a67f61
56144a0
e78a120
9833a07
5b5b7e3
9b4c045
d846745
fe5f3bb
7befae5
f39127a
5a5c765
d8310aa
fafe4d2
8638197
c5ec891
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -27,6 +27,10 @@ android { | |
| testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" | ||
|
|
||
| buildConfigField("String", "BASE_URL", "\"${properties["BASE_URL"]}\"") | ||
| buildConfigField("String", "NATIVE_APP_KEY", "\"${properties["NATIVE_APP_KEY"]}\"") | ||
| manifestPlaceholders += mapOf( | ||
| "NATIVE_APP_KEY" to properties["NATIVE_APP_KEY"] as String | ||
| ) | ||
| } | ||
|
|
||
| buildTypes { | ||
|
|
@@ -68,6 +72,7 @@ dependencies { | |
| implementation(libs.kotlinx.serialization.json) | ||
| implementation(libs.coil.compose) | ||
| implementation(libs.foundation) | ||
| implementation(libs.androidx.lifecycle.runtime.compose) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chain알파 채널 의존성 사용 주의: lifecycle-runtime-compose는 안정 버전으로 정렬 권장 여기서 사용하는 위 파일 코멘트에 남긴 웹 검색으로 최신 안정 버전을 확인해 주세요. 먼저 🏁 Script executed: #!/bin/bash
# 버전 카탈로그 파일 경로 찾기
catalog=$(fd --hidden --ignore-case "libs.versions.toml" | head -n1)
if [[ -n "$catalog" ]]; then
echo "== $catalog 에서 lifecycleRuntimeCompose 버전 추출 =="
rg "lifecycleRuntimeCompose" -n "$catalog"
else
echo "Error: libs.versions.toml 파일을 찾을 수 없습니다."
fiLength of output: 525 🌐 Web query: 💡 Result: The latest stable version is 2.9.2 (released July 16, 2025). (developer.android.com, mvnrepository.com) Gradle (Groovy): Gradle (Kotlin DSL): This artifact is published on Google’s Maven repository, so ensure you have google() in your repositories. (mvnrepository.com, developer.android.com) Would you like the Maven XML snippet or the corresponding lifecycle/viewmodel-compose version info as well? Citations:
안정 버전 사용 권장: lifecycle-runtime-compose 2.9.2로 변경 아래와 같이 버전 카탈로그를 안정 버전으로 롤백하고, • gradle/libs.versions.toml (28행) - lifecycleRuntimeCompose = "2.10.0-alpha01"
+ lifecycleRuntimeCompose = "2.9.2"• app/build.gradle.kts (72행) implementation(libs.androidx.lifecycle.runtime.compose)🤖 Prompt for AI Agents |
||
| implementation(libs.androidx.datastore.preferences) | ||
| testImplementation(libs.junit) | ||
| androidTestImplementation(libs.androidx.junit) | ||
|
|
@@ -91,6 +96,12 @@ dependencies { | |
| // OkHttp | ||
| implementation(libs.okhttp) | ||
| implementation(libs.logging.interceptor) | ||
|
|
||
| // 카카오 로그인 SDK | ||
| implementation("com.kakao.sdk:v2-all:2.21.6") | ||
|
|
||
| // 토큰 저장을 위한 DataStore | ||
| implementation ("androidx.datastore:datastore-preferences:1.1.1") | ||
| } | ||
|
|
||
| kapt { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,9 @@ | ||
| <?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" /> | ||
|
|
||
| <application | ||
| android:name=".ThipApplication" | ||
| android:allowBackup="true" | ||
|
|
@@ -14,6 +16,25 @@ | |
| android:supportsRtl="true" | ||
| android:theme="@style/Theme.Thip" | ||
| tools:targetApi="31"> | ||
|
|
||
| <meta-data | ||
| android:name="com.kakao.sdk.AppKey" | ||
| android:value="${NATIVE_APP_KEY}" /> | ||
|
Comment on lines
+20
to
+22
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chainManifest placeholder 주입 경로 확인 필요 (${NATIVE_APP_KEY}) Manifest의 meta-data 값에 다음 스크립트로 설정 여부를 점검해 주세요: 🏁 Script executed: #!/bin/bash
# manifestPlaceholders에 NATIVE_APP_KEY 정의 여부 확인
fd build.gradle.kts -a | while read -r f; do
echo ">>> $f"
rg -n "manifestPlaceholders" "$f" -A 5 -n || true
rg -n "NATIVE_APP_KEY" "$f" -A 2 -n || true
doneLength of output: 663 Manifest placeholder 설정 누락: NATIVE_APP_KEY
• 수정 대상
• 예시 스니펫 (Kotlin DSL) android {
defaultConfig {
// 기존 buildConfigField는 그대로 유지
buildConfigField("String", "NATIVE_APP_KEY", "\"${properties["NATIVE_APP_KEY"]}\"")
// manifestPlaceholders 설정 추가
manifestPlaceholders += mapOf(
"NATIVE_APP_KEY" to properties["NATIVE_APP_KEY"] as String
)
}
}위와 같이 설정하면 <meta-data
android:name="com.kakao.sdk.AppKey"
android:value="${NATIVE_APP_KEY}" />가 실제 앱 키로 정상 치환됩니다. 🤖 Prompt for AI Agents |
||
|
|
||
| <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> | ||
|
|
||
| <activity | ||
| android:name=".MainActivity" | ||
| android:exported="true" | ||
|
|
@@ -27,5 +48,4 @@ | |
| </intent-filter> | ||
| </activity> | ||
| </application> | ||
|
|
||
| </manifest> | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,21 @@ | ||
| package com.texthip.thip | ||
|
|
||
| import com.kakao.sdk.common.KakaoSdk | ||
| import android.app.Application | ||
| import dagger.hilt.android.HiltAndroidApp | ||
| import com.texthip.thip.BuildConfig | ||
|
|
||
|
|
||
| @HiltAndroidApp | ||
| class ThipApplication : Application() | ||
| class ThipApplication : Application(){ | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chainAndroidManifest에 Application 등록 및 Kakao 리디렉션 구성 확인 권장 @HiltAndroidApp를 사용하는 Application은 매니페스트 application android:name에 반드시 등록되어야 합니다. 또한 카카오 로그인 리디렉션을 위해 com.kakao.sdk.AppKey 메타데이터와 AuthCodeHandlerActivity 등록이 필요합니다. 로컬 환경/브랜치에 따라 누락되지 않았는지 한번 점검 부탁드립니다. 다음 스크립트로 매니페스트/빌드 설정을 빠르게 확인할 수 있습니다. 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "1) Application 클래스 등록 확인"
rg -n -C2 -P '<application[^>]*android:name\s*=\s*"(?:\.|com\.texthip\.thip\.)ThipApplication"' -g 'app/**/AndroidManifest.xml' || echo "⚠️ AndroidManifest에 ThipApplication 등록이 보이지 않습니다."
echo
echo "2) Kakao AppKey 메타데이터 확인"
rg -n -C2 -P '<meta-data[^>]*name="com\.kakao\.sdk\.AppKey"[^>]*/>' -g 'app/**/AndroidManifest.xml' || echo "⚠️ com.kakao.sdk.AppKey 메타데이터가 보이지 않습니다."
echo
echo "3) Kakao AuthCodeHandlerActivity 등록 확인"
rg -n -C2 -P 'com\.kakao\.sdk\.auth\.AuthCodeHandlerActivity' -g 'app/**/AndroidManifest.xml' || echo "⚠️ AuthCodeHandlerActivity 등록이 보이지 않습니다."
echo
echo "4) BuildConfig NATIVE_APP_KEY 정의 확인"
rg -n -C2 -P 'buildConfigField\([^)]*"String"\s*,\s*"NATIVE_APP_KEY"' -g 'app/**/build.gradle*' || echo "⚠️ build.gradle에 NATIVE_APP_KEY buildConfigField가 보이지 않습니다."Length of output: 1692 AndroidManifest에 ThipApplication 및 Kakao AppKey 메타데이터 등록 필요 확인 결과, 아래 항목이 누락되어 있어 카카오 로그인 리디렉션이 정상 동작하지 않습니다. 적용 부탁드립니다. • app/src/main/AndroidManifest.xml
위 두 가지를 추가한 뒤 카카오 SDK 리디렉션이 정상 작동하는지 재확인 부탁드립니다. 🤖 Prompt for AI Agents |
||
| override fun onCreate() { | ||
| super.onCreate() | ||
|
|
||
| // 카카오 SDK 초기화 | ||
| try { | ||
| KakaoSdk.init(this, BuildConfig.NATIVE_APP_KEY) | ||
| }catch (e: Exception){ | ||
| e.printStackTrace() | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -7,18 +7,44 @@ import androidx.datastore.preferences.core.edit | |||||||||||||||||||||||
| import androidx.datastore.preferences.core.stringPreferencesKey | ||||||||||||||||||||||||
| import androidx.datastore.preferences.preferencesDataStore | ||||||||||||||||||||||||
| import dagger.hilt.android.qualifiers.ApplicationContext | ||||||||||||||||||||||||
| import kotlinx.coroutines.flow.Flow | ||||||||||||||||||||||||
| import kotlinx.coroutines.flow.first | ||||||||||||||||||||||||
| import kotlinx.coroutines.flow.map | ||||||||||||||||||||||||
| import javax.inject.Inject | ||||||||||||||||||||||||
| import javax.inject.Singleton | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "thip_tokens") | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| @Singleton | ||||||||||||||||||||||||
| class TokenManager @Inject constructor( | ||||||||||||||||||||||||
| @ApplicationContext private val context: Context | ||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||
| private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "thip_auth_tokens") | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
|
Comment on lines
+22
to
+23
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Context.dataStore를 클래스 내부에서 다시 정의하면 DataStore 인스턴스가 이중으로 생성될 수 있습니다. 하나로 통일하세요. 동일 파일에
가 동시에 존재합니다. 호출 위치에 따라 서로 다른 파일명으로 분리된 두 개의 DataStore를 사용할 위험이 있습니다. 토큰 저장/조회가 서로 다른 저장소를 바라보면 데이터 유실·불일치가 발생합니다. 권장: 클래스 내부 정의를 제거하고, top-level 하나만 남기되 파일명을 원하는 최종값(예: "thip_auth_tokens")으로 맞추세요. 적용 diff(선택 범위 내 제거): - private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "thip_auth_tokens")선택 범위 밖(참고): top-level 정의를 다음과 같이 통일하세요. // line 16 제안
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "thip_auth_tokens")🤖 Prompt for AI Agents |
||||||||||||||||||||||||
| companion object { | ||||||||||||||||||||||||
| //토큰저장에 사용되는 키 | ||||||||||||||||||||||||
| private val APP_TOKEN_KEY = stringPreferencesKey("app_token") | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
Comment on lines
+24
to
+27
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. companion object가 중복 선언되어 컴파일 오류가 납니다. 키 정의를 하나의 companion으로 통합하세요. 현재 클래스 아래쪽(라인 49-53)에 또 다른 한 곳으로 합치고, 아래쪽의 중복 companion은 제거하세요. 적용 diff(상단 companion에 모든 키 통합, 하단 중복 companion 제거는 다음 코멘트 설명 참고): - companion object {
- //토큰저장에 사용되는 키
- private val APP_TOKEN_KEY = stringPreferencesKey("app_token")
- }
+ companion object {
+ // Preferences Keys
+ private val APP_TOKEN_KEY = stringPreferencesKey("app_token")
+ private val TEMP_TOKEN_KEY = stringPreferencesKey("temp_token")
+ private val ACCESS_TOKEN_KEY = stringPreferencesKey("access_token")
+ private val REFRESH_TOKEN_KEY = stringPreferencesKey("refresh_token")
+ }선택 범위 밖(설명): 라인 49-53의 하단 companion object 블록은 통째로 삭제하세요. 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| //토큰 저장 | ||||||||||||||||||||||||
| suspend fun saveToken(token: String) { | ||||||||||||||||||||||||
| context.dataStore.edit { prefs -> | ||||||||||||||||||||||||
| prefs[APP_TOKEN_KEY] = token | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| //저장된 토큰을 Flow 형태로 불러옴 | ||||||||||||||||||||||||
| fun getToken(): Flow<String?> { | ||||||||||||||||||||||||
| return context.dataStore.data.map { prefs -> | ||||||||||||||||||||||||
| prefs[APP_TOKEN_KEY] | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
Comment on lines
+36
to
+41
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion DataStore 스트림 예외 대비 로직을 추가하세요.
적용 diff(선택된 범위 내 변경): - fun getToken(): Flow<String?> {
- return context.dataStore.data.map { prefs ->
- prefs[APP_TOKEN_KEY]
- }
- }
+ fun getToken(): Flow<String?> {
+ return context.dataStore.data
+ .catch { emit(emptyPreferences()) }
+ .map { prefs ->
+ prefs[APP_TOKEN_KEY]
+ }
+ }선택 범위 외 필요한 import: import kotlinx.coroutines.flow.catch
import androidx.datastore.preferences.core.emptyPreferences🤖 Prompt for AI Agents |
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| //저장된 토큰 삭제 (로그아웃 시?) | ||||||||||||||||||||||||
| suspend fun deleteToken() { | ||||||||||||||||||||||||
| context.dataStore.edit { prefs -> | ||||||||||||||||||||||||
| prefs.remove(APP_TOKEN_KEY) | ||||||||||||||||||||||||
|
Comment on lines
+43
to
+46
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 중괄호 누락으로 컴파일 오류 발생 —
적용 diff: suspend fun deleteToken() {
context.dataStore.edit { prefs ->
prefs.remove(APP_TOKEN_KEY)
+ }
+ }추가 제안: 본 메서드는 APP_TOKEN만 지우고, 아래의 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| // 저장할 데이터의 Key 정의 | ||||||||||||||||||||||||
| companion object { | ||||||||||||||||||||||||
| private val TEMP_TOKEN_KEY = stringPreferencesKey("temp_token") | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| package com.texthip.thip.data.model.auth.request | ||
| import kotlinx.serialization.SerialName | ||
| import kotlinx.serialization.Serializable | ||
|
|
||
| @Serializable | ||
| data class AuthRequest( | ||
| @SerialName("oauth2Id") val oauth2Id: String | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| package com.texthip.thip.data.model.auth.response | ||
|
|
||
| import kotlinx.serialization.SerialName | ||
| import kotlinx.serialization.Serializable | ||
|
|
||
| @Serializable | ||
| data class AuthResponse( | ||
| @SerialName("token") val token: String, | ||
| @SerialName("isNewUser") val isNewUser: Boolean | ||
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
local.properties 미설정 시 “null” 문자열이 주입될 수 있습니다. 강제 검증 로직으로 빌드를 fail-fast 하세요.
현재
properties["NATIVE_APP_KEY"]가 없으면 "null" 문자열이 들어갈 수 있습니다. 빌드 시점에 존재 여부를 검증하고 없으면 명시적으로 실패시키는 것이 안전합니다.선택 범위 내 변경(diff):
선택 범위 밖에 필요한 보조 코드(상단 프로퍼티 로드 직후):
🤖 Prompt for AI Agents