Skip to content

[feat] #80 스플래시~이용약관 프로세스 구성#91

Merged
Ojongseok merged 54 commits into
developfrom
feat/#80-login-onboarding
Feb 8, 2026
Merged

[feat] #80 스플래시~이용약관 프로세스 구성#91
Ojongseok merged 54 commits into
developfrom
feat/#80-login-onboarding

Conversation

@Ojongseok
Copy link
Copy Markdown
Member

@Ojongseok Ojongseok commented Feb 8, 2026

🔗 관련 이슈

📙 작업 설명

  • 스플래시 / 온보딩 / 로그인 / 이용약관 흐름의 프로세스 및 UI 구성
  • 위 과정은 모두 feature:auth 모듈 내에서 진행됩니다.
  • Auth 관련 내비게이션 로직 작성
  • Title 28 폰트 시스템 추가

📸 스크린샷 또는 시연 영상 (선택)

기능 미리보기 기능 미리보기
온보딩
KakaoTalk_Video_2026-02-08-15-51-45.mp4
이용약관
KakaoTalk_Video_2026-02-08-15-52-27.mp4
로그아웃
KakaoTalk_Video_2026-02-08-15-58-58.mp4
회원탈퇴
KakaoTalk_Video_2026-02-08-15-58-30.mp4

💬 추가 설명 or 리뷰 포인트 (선택)

  • 스플래시~이용약관 : "Auth", 로그인&이용약관 : "Login~~"의 네이밍으로 사용됩니다.
  • 스플래시~약관까지의 화면 전환을 관리할 AuthNavigationState, AuthNavigator, AuthScreen를 정의하고, AuthScreen 내 별도 NavDisplay()를 이용했습니다.
  • 로그인과 약관은 LoginViewModel을 공유하고, Splash는 스플래시 이후의 화면 전환과 이후 딥링크 처리를 위해 별도 SplashViewModel을 사용합니다.
  • ViewModel을 공유하는 경우 RootNavKey 이동 시 기존에 작성된 HiltSharedViewModelStoreNavEntryDecorator 만으로 ViewModel이 clear() 되지 않고, 남아있는 현상이 있습니다.
    ex. 로그인/약관 동의 후 메인화면에 진입했을 때 LoginViewModel이 살아있는 현상이 있습니다. 반대로 메인화면에서 로그아웃/탈퇴 후 스플래시 화면으로 이동했을 때 탑레벨의 뷰모델이 모두 살아있는 현상이 있구요.
    -> 이부분은 해당 pr에서 수정하면 변경사항이 너무 많을 것 같아 별도 이슈로 등록하여 수정하겠습니다!

Q. 현재 메인화면에서의 화면 전환에 사용되는 Navigator/NavigatorImpl을 MainNavigator/MainNavigatorImpl로 네이밍을 변경할까요?

Summary by CodeRabbit

  • 새로운 기능

    • 인증 흐름 통합: 스플래시 → 3단계 온보딩 → 카카오 로그인 → 약관 화면 추가 및 연동
    • 온보딩 완료 상태 저장/해제 기능 추가
  • 디자인 개선

    • 다수 신규 벡터 아이콘(온보딩 일러스트, 로고, 카카오 등) 추가
    • 타이포그래피에 28pt 제목 스타일 추가
    • 그래디언트 배경 및 로그인 하단 카카오 버튼 UI 도입
  • 기타

    • 탈퇴 시 온보딩 상태 초기화로 재가입 유도 및 인증 흐름 안정화

@Ojongseok Ojongseok force-pushed the feat/#80-login-onboarding branch from 556bc51 to 90f7f1b Compare February 8, 2026 11:16
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
core/common/src/main/java/com/neki/android/core/common/kakao/KakaoAuthHelper.kt (1)

17-17: ⚠️ Potential issue | 🟠 Major

token.idToken!! 강제 언래핑은 NPE 위험이 있습니다.

Kakao SDK의 OAuthToken.idToken은 nullable 타입입니다. OIDC 설정이 누락되었거나 토큰에 idToken이 포함되지 않은 경우 런타임에 NullPointerException이 발생합니다. Line 25도 동일한 문제가 있습니다.

🛡️ null 처리 제안
-                    onSuccess(token.idToken!!)
+                    val idToken = token.idToken
+                    if (idToken != null) {
+                        onSuccess(idToken)
+                    } else {
+                        onFailure("ID 토큰을 가져올 수 없습니다.")
+                    }

Line 25에도 동일하게 적용해 주세요.

🤖 Fix all issues with AI agents
In
`@feature/auth/impl/src/main/kotlin/com/neki/android/feature/auth/impl/login/LoginScreen.kt`:
- Line 27: The collected but unused uiState in LoginScreen (val uiState by
viewModel.store.uiState.collectAsStateWithLifecycle()) should be removed or
actually used: either delete that line to avoid unnecessary state collection and
extra recompositions, or pass uiState into the composable UI/child components or
reference it inside LoginScreen (e.g., use uiState.isLoading / uiState.error) so
the collected flow is consumed; locate the declaration in LoginScreen and update
accordingly.
🧹 Nitpick comments (2)
core/common/src/main/java/com/neki/android/core/common/kakao/KakaoAuthHelper.kt (1)

12-28: KakaoTalk/KakaoAccount 로그인 콜백 로직이 중복됩니다.

두 분기의 콜백 처리가 동일하므로 헬퍼 함수로 추출하면 유지보수성이 향상됩니다.

♻️ 중복 제거 제안
+    private fun handleLoginResult(
+        token: com.kakao.sdk.auth.model.OAuthToken?,
+        error: Throwable?,
+        onSuccess: (String) -> Unit,
+        onFailure: (String) -> Unit,
+    ) {
+        if (error != null) {
+            onFailure(error.message ?: "카카오 로그인에 실패했습니다.")
+        } else if (token != null) {
+            val idToken = token.idToken
+            if (idToken != null) {
+                onSuccess(idToken)
+            } else {
+                onFailure("ID 토큰을 가져올 수 없습니다.")
+            }
+        }
+    }
+
     fun login(
         context: Context,
         onSuccess: (String) -> Unit,
         onFailure: (String) -> Unit,
     ) {
         if (UserApiClient.instance.isKakaoTalkLoginAvailable(context)) {
-            UserApiClient.instance.loginWithKakaoTalk(context) { token, error ->
-                if (error != null) {
-                    onFailure(error.message ?: "카카오 로그인에 실패했습니다.")
-                } else if (token != null) {
-                    onSuccess(token.idToken!!)
-                }
-            }
+            UserApiClient.instance.loginWithKakaoTalk(context) { token, error ->
+                handleLoginResult(token, error, onSuccess, onFailure)
+            }
         } else {
-            UserApiClient.instance.loginWithKakaoAccount(context) { token, error ->
-                if (error != null) {
-                    onFailure(error.message ?: "카카오 로그인에 실패했습니다.")
-                } else if (token != null) {
-                    onSuccess(token.idToken!!)
-                }
-            }
+            UserApiClient.instance.loginWithKakaoAccount(context) { token, error ->
+                handleLoginResult(token, error, onSuccess, onFailure)
+            }
         }
     }
feature/auth/impl/src/main/kotlin/com/neki/android/feature/auth/impl/login/LoginScreen.kt (1)

48-49: else -> {} 분기에서 일부 side effect가 무시될 수 있습니다.

LoginViewModelLoginSideEffect.NavigateBack, LoginSideEffect.NavigateUrl 등의 side effect도 발행할 수 있습니다. 공유 ViewModel 특성상 Term 화면에서 처리될 수 있지만, else 분기로 무시하면 디버깅이 어렵습니다. 의도적으로 무시하는 것이라면 주석을 남기거나, 명시적으로 나열하는 것을 권장합니다.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
feature/auth/impl/src/main/kotlin/com/neki/android/feature/auth/impl/login/LoginScreen.kt (1)

35-37: ⚠️ Potential issue | 🟠 Major

카카오 로그인 실패 시 LoginIntent.FailLogin이 디스패치되지 않습니다.

onFailure 콜백에서 Timber.d로 로깅만 하고 FailLogin 인텐트를 전달하지 않습니다. ViewModel에서 FailLogin은 사용자에게 토스트 메시지를 표시하도록 구현되어 있으므로, 현재 로그인 실패 시 사용자에게 아무런 피드백이 제공되지 않습니다.

🐛 수정 제안
                     onFailure = { message ->
                         Timber.d("로그인 실패 $message")
+                        viewModel.store.onIntent(LoginIntent.FailLogin)
                     },
🤖 Fix all issues with AI agents
In
`@core/designsystem/src/main/java/com/neki/android/core/designsystem/logo/NekiAppLogo.kt`:
- Around line 47-53: The WhiteNekiAppLogoPreview renders a white logo on a white
NekiTheme background so it's invisible; update the preview function
WhiteNekiAppLogoPreview to wrap the call to WhiteNekiAppLogo with a dark
background preview container (e.g., use the already-imported ComponentPreview or
wrap in a Box with a non-white background) while keeping NekiTheme so the rest
of theming remains applied; ensure the preview composable still uses `@Preview`
and the function name WhiteNekiAppLogoPreview to keep tooling recognition.
🧹 Nitpick comments (5)
feature/auth/impl/src/main/kotlin/com/neki/android/feature/auth/impl/splash/component/SplashBackground.kt (1)

37-38: 소수점 dp 값이 지나치게 정밀합니다.

73.89.dp, 80.41.dp처럼 소수점 두 자리까지 지정하는 것은 실제 렌더링에서 의미 있는 차이를 만들지 않습니다. 디자인 스펙 원본 값을 반올림하여 74.dp x 80.dp 등으로 정리하면 가독성이 높아집니다.

feature/auth/impl/src/main/kotlin/com/neki/android/feature/auth/impl/login/LoginScreen.kt (1)

44-45: else -> {}가 처리되지 않은 사이드 이펙트를 무시합니다.

LoginViewModelNavigateBack, NavigateUrl 등의 사이드 이펙트도 발행할 수 있습니다. 현재 else -> {}는 이들을 조용히 무시하므로, 디버깅 시 문제 추적이 어려울 수 있습니다. 공유 ViewModel 구조에서 예기치 않은 사이드 이펙트가 여기로 전달될 가능성이 있으므로, 최소한 로깅을 추가하는 것을 권장합니다.

♻️ 리팩터 제안
-            else -> {}
+            else -> Timber.d("LoginRoute에서 처리되지 않은 sideEffect: $sideEffect")
core/designsystem/src/main/java/com/neki/android/core/designsystem/logo/NekiAppLogo.kt (1)

14-25: contentDescriptionnull로 하드코딩되어 있어 접근성 지원이 불가합니다.

앱 로고는 장식 요소가 아닌 브랜딩 의미를 전달하므로, 스크린 리더 사용자를 위해 콘텐츠 설명이 필요합니다. 또한 공개 API(WhiteNekiAppLogo, PrimaryNekiAppLogo)에서도 contentDescription 파라미터를 노출하지 않아 호출부에서 재정의할 수 없습니다.

♻️ 개선 제안
 `@Composable`
 private fun NekiAppLogo(
     color: Color,
     modifier: Modifier = Modifier,
+    contentDescription: String? = null,
 ) {
     Icon(
         imageVector = ImageVector.vectorResource(R.drawable.icon_neki_logo_white),
-        contentDescription = null,
+        contentDescription = contentDescription,
         tint = color,
         modifier = modifier,
     )
 }

공개 composable에도 contentDescription 파라미터를 전달할 수 있도록 확장하면 좋습니다.

feature/auth/impl/src/main/kotlin/com/neki/android/feature/auth/impl/login/LoginViewModel.kt (2)

85-105: onSuccess 블록 내 예외 발생 시 isLoadingtrue로 고정될 수 있습니다.

tokenRepository.saveTokens() 또는 authRepository.setCompletedOnboarding()에서 예외가 발생하면, Line 104의 reduce { copy(isLoading = false) }에 도달하지 못합니다. try-finally 또는 invokeOnCompletion으로 isLoading 해제를 보장하는 것이 안전합니다.

♻️ 수정 제안
     private fun loginWithKakao(
         idToken: String,
         reduce: (LoginState.() -> LoginState) -> Unit,
         postSideEffect: (LoginSideEffect) -> Unit,
     ) = viewModelScope.launch {
         reduce { copy(isLoading = true) }
-        authRepository.loginWithKakao(idToken)
-            .onSuccess {
-                tokenRepository.saveTokens(
-                    accessToken = it.accessToken,
-                    refreshToken = it.refreshToken,
-                )
-                authRepository.setCompletedOnboarding(true)
-                postSideEffect(LoginSideEffect.NavigateToMain)
-            }
-            .onFailure { exception ->
-                Timber.e(exception)
-                postSideEffect(LoginSideEffect.ShowToastMessage("로그인에 실패했습니다. 다시 시도해주세요."))
-            }
-        reduce { copy(isLoading = false) }
+        try {
+            authRepository.loginWithKakao(idToken)
+                .onSuccess {
+                    tokenRepository.saveTokens(
+                        accessToken = it.accessToken,
+                        refreshToken = it.refreshToken,
+                    )
+                    authRepository.setCompletedOnboarding(true)
+                    postSideEffect(LoginSideEffect.NavigateToMain)
+                }
+                .onFailure { exception ->
+                    Timber.e(exception)
+                    postSideEffect(LoginSideEffect.ShowToastMessage("로그인에 실패했습니다. 다시 시도해주세요."))
+                }
+        } finally {
+            reduce { copy(isLoading = false) }
+        }
     }

43-49: ClickAgreeAll이 필수 약관만 토글합니다 — 의도된 동작인지 확인 필요.

ClickAgreeAll 처리에서 isRequired 항목만 agreedTerms에 추가하고, 해제 시에는 전체(persistentSetOf())를 비웁니다. 선택 약관이 존재하는 경우 "전체 동의" 해제 시 개별적으로 동의했던 선택 약관도 함께 초기화됩니다. 의도된 UX라면 문제없지만, 확인이 필요합니다.

Copy link
Copy Markdown
Member Author

@Ojongseok Ojongseok left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Q. 현재 메인화면에서의 화면 전환에 사용되는 Navigator/NavigatorImpl을 MainNavigator/MainNavigatorImpl로 네이밍을 변경할까요?
A. 기존 NavigationState 와 동일한 Level 의 AuthNavigationState 가 생겼으니,
MainNavigationState로 네이밍은 구별하는 건 어떨까요

MainNavigator/MainNavigatorImpl에 더해 NavigationState 또한 MainNavigationState로 변경하자는 말씀이신거 맞나요?


HiltSharedViewModelStoreNavEntryDecorator와 관련해 확인해주셔 감사합니다!
onPop{}을 제어할 방법을 못찾았었는데 onPop{}은 리컴포지션 시 호출되는 블럭이었군요..!
로그로 확인해보니 ViewModel 정상적으로 제거되었고, 메인 <-> 로그인 이동할 때 작성해주신 방법으로 메인쪽의 ViewModel도 onCleaed되는 것 확인했습니다!

해당 PR에서 같이 반영되면 좋을 것 같아 수정하였습니다. e4feb77

Copy link
Copy Markdown
Contributor

@ikseong00 ikseong00 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

리코멘트 남겼습니다!
Image 는 이후 PR에서 답변온 뒤에 반영하실 예정인가요??

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
feature/auth/impl/src/main/kotlin/com/neki/android/feature/auth/impl/login/LoginScreen.kt (1)

38-40: ⚠️ Potential issue | 🟠 Major

카카오 로그인 실패 시 LoginIntent.FailLogin이 디스패치되지 않습니다.

onFailure 콜백에서 Timber 로그만 남기고 viewModel.store.onIntent(LoginIntent.FailLogin)을 호출하지 않고 있습니다. LoginViewModelFailLogin 인텐트는 ShowToastMessage("카카오 로그인에 실패했습니다.")를 발행하도록 구현되어 있으므로, 현재 상태에서는 로그인 실패 시 사용자에게 아무런 피드백이 제공되지 않습니다.

🐛 수정 제안
                 onFailure = { message ->
                     Timber.d("로그인 실패 $message")
+                    viewModel.store.onIntent(LoginIntent.FailLogin)
                 },
🧹 Nitpick comments (5)
feature/mypage/impl/src/main/java/com/neki/android/feature/mypage/impl/main/MyPageViewModel.kt (1)

191-196: onSuccess 블록 내 suspend 호출의 예외 처리 부재

clearTokens()setCompletedOnboarding(false) 모두 suspend 함수로, DataStore I/O 예외가 발생할 수 있습니다. onSuccess 블록 내에서 예외가 발생하면 코루틴이 조용히 실패하여 UI가 로딩 상태에 머물 수 있습니다.

clearTokens()의 경우 기존 패턴이므로 이 PR 범위를 벗어나지만, 후속 작업으로 이 블록 전체를 try-catch로 감싸거나, 각 호출의 실패를 독립적으로 처리하는 것을 권장합니다.

♻️ 제안: 예외 처리 추가
         .onSuccess {
-            tokenRepository.clearTokens()
-            authRepository.setCompletedOnboarding(false)
-            reduce { copy(isLoading = false) }
-            postSideEffect(MyPageEffect.UnlinkWithKakao)
+            try {
+                tokenRepository.clearTokens()
+                authRepository.setCompletedOnboarding(false)
+            } catch (e: Exception) {
+                Timber.e(e)
+            }
+            reduce { copy(isLoading = false) }
+            postSideEffect(MyPageEffect.UnlinkWithKakao)
         }
feature/auth/impl/src/main/kotlin/com/neki/android/feature/auth/impl/login/component/LoginBackground.kt (2)

44-48: title24Bold 스타일에 fontSize = 32.sp를 오버라이드하고 있습니다.

디자인 시스템의 title24Bold 타이포그래피를 사용하면서 fontSize를 32sp로 덮어쓰면, line-height·letter-spacing 등 나머지 속성이 24sp 기준 값으로 남아 의도치 않은 레이아웃이 될 수 있습니다. PR 설명에 Title 28 폰트가 추가되었다고 언급되어 있으니, 32sp에 맞는 타이포그래피 스타일(title32Bold 등)을 디자인 시스템에 추가하거나, 이미 존재한다면 해당 스타일을 직접 사용하는 것이 좋겠습니다.

♻️ 제안
             Text(
                 text = "네컷의 순간이 \n이어지는 곳",
-                style = NekiTheme.typography.title24Bold,
-                fontSize = 32.sp,
+                style = NekiTheme.typography.title32Bold, // 디자인 시스템에 정의 필요
                 color = NekiTheme.colorScheme.white,
             )

44-55: 하드코딩된 한국어 문자열은 string resource로 분리하는 것을 권장합니다.

"네컷의 순간이 \n이어지는 곳", "위치, 포즈, 아카이빙까지" 등 UI 텍스트가 코드에 직접 작성되어 있습니다. 향후 다국어 지원이나 텍스트 변경 관리를 위해 strings.xml로 추출하는 것이 좋습니다.

core/navigation/src/main/java/com/neki/android/core/navigation/NavigationState.kt (1)

56-58: toMutableStateList()가 매 리컴포지션마다 새 리스트를 생성합니다.

flatMap + toMutableStateList()는 리컴포지션 시마다 새 SnapshotStateList 인스턴스를 할당합니다. 현재 동작에 문제는 없지만, 호출 측에서 불필요한 리컴포지션을 유발할 수 있습니다. 필요 시 remember/derivedStateOf로 감싸는 것을 고려해 볼 수 있습니다.

feature/auth/impl/src/main/kotlin/com/neki/android/feature/auth/impl/login/LoginScreen.kt (1)

47-48: else -> {}가 처리되지 않은 사이드 이펙트를 조용히 무시합니다.

LoginViewModel이 Login과 Term 화면 간에 공유되므로, Term 전용 사이드 이펙트(NavigateBack, NavigateUrl)가 여기서 무시될 수 있습니다. 현재는 문제가 없지만, 향후 새 사이드 이펙트 추가 시 처리 누락을 발견하기 어려울 수 있습니다. 로그를 남기거나, exhaustive when을 사용하여 명시적으로 각 케이스를 나열하는 것을 고려해 주세요.

@Ojongseok
Copy link
Copy Markdown
Member Author

리코멘트 남겼습니다! Image 는 이후 PR에서 답변온 뒤에 반영하실 예정인가요??

약관과 관련해 API 추가로 연결해야 할 부분이 생겨 이후 PR에서 함께 반영하겠습니다!

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In
`@feature/auth/impl/src/main/kotlin/com/neki/android/feature/auth/impl/login/LoginScreen.kt`:
- Line 25: The code stores a NekiToast with remember { NekiToast(context) }
which can capture a stale Context across configuration changes; update the
remember call to use context as a key (e.g., remember(context) {
NekiToast(context) }) so a new NekiToast is created when context changes,
targeting the nekiToast variable in LoginScreen.kt.
🧹 Nitpick comments (4)
core/data/src/main/java/com/neki/android/core/data/repository/impl/TokenRepositoryImpl.kt (2)

20-23: companion object의 키를 private으로 제한하세요.

ACCESS_TOKENREFRESH_TOKEN은 이 리포지토리의 내부 구현 세부사항입니다. 현재 val로 선언되어 companion object 외부에서 접근 가능합니다. 불필요한 노출을 방지하기 위해 private으로 변경하는 것이 좋습니다.

♻️ 수정 제안
     companion object {
-        val ACCESS_TOKEN = stringPreferencesKey("access_token")
-        val REFRESH_TOKEN = stringPreferencesKey("refresh_token")
+        private val ACCESS_TOKEN = stringPreferencesKey("access_token")
+        private val REFRESH_TOKEN = stringPreferencesKey("refresh_token")
     }

36-43: hasTokens()에서 불필요한 복호화 수행.

토큰 존재 여부만 확인하면 되는데 매번 CryptoManager.decrypt()를 호출하고 있습니다. 암호화된 값 자체가 null이 아니고 blank가 아닌지만 확인하면 충분합니다. 복호화는 비용이 있는 연산이므로, 단순 존재 확인에서는 생략할 수 있습니다.

♻️ 수정 제안
     override fun hasTokens(): Flow<Boolean> {
         return dataStore.data.map { preferences ->
-            val accessToken = preferences[ACCESS_TOKEN]?.let { CryptoManager.decrypt(it) }
-            val refreshToken = preferences[REFRESH_TOKEN]?.let { CryptoManager.decrypt(it) }
-
-            !accessToken.isNullOrBlank() && !refreshToken.isNullOrBlank()
+            !preferences[ACCESS_TOKEN].isNullOrBlank() && !preferences[REFRESH_TOKEN].isNullOrBlank()
         }
     }
feature/auth/impl/src/main/kotlin/com/neki/android/feature/auth/impl/login/LoginScreen.kt (2)

30-40: 로그인 실패 시 사용자에게 피드백 없음

onFailure에서 Timber.d로 로그만 남기고 있어, 사용자는 로그인 실패 원인을 알 수 없습니다. Toast나 에러 UI를 통해 실패를 알리는 것이 좋습니다.

🛠️ 예시
                     onFailure = { message ->
                         Timber.d("로그인 실패 $message")
+                        nekiToast.showToast(text = message ?: "로그인에 실패했습니다.")
                     },

46-47: else -> {} 공백 분기를 제거하고 모든 LoginSideEffect 케이스를 명시적으로 처리

LoginSideEffect는 sealed interface이므로 else 분기 없이 when을 사용하면 컴파일 타임에 모든 케이스를 강제로 처리하게 됩니다. 현재 코드는 NavigateToMain, NavigateBack, NavigateUrl 케이스를 처리하지 않으면서 공백 else 분기로 무시하고 있습니다. 향후 새로운 side effect가 추가되더라도 조용히 무시될 수 있으므로, 모든 케이스를 명시적으로 처리하거나 불필요한 케이스에 대해 의도적인 처리를 추가해야 합니다.


val context = LocalContext.current
val kakaoAuthHelper = remember { KakaoAuthHelper(context) }
val nekiToast = remember { NekiToast(context) }
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

remembercontext 키가 누락되어 stale context 참조 위험

remember { NekiToast(context) }context를 키로 사용하지 않으므로, Configuration 변경(예: 화면 회전) 시 이전 context를 캡처한 NekiToast 인스턴스가 재사용됩니다. 이는 메모리 릭 또는 잘못된 Toast 표시로 이어질 수 있습니다.

🛠️ 수정 제안
-    val nekiToast = remember { NekiToast(context) }
+    val nekiToast = remember(context) { NekiToast(context) }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
val nekiToast = remember { NekiToast(context) }
val nekiToast = remember(context) { NekiToast(context) }
🤖 Prompt for AI Agents
In
`@feature/auth/impl/src/main/kotlin/com/neki/android/feature/auth/impl/login/LoginScreen.kt`
at line 25, The code stores a NekiToast with remember { NekiToast(context) }
which can capture a stale Context across configuration changes; update the
remember call to use context as a key (e.g., remember(context) {
NekiToast(context) }) so a new NekiToast is created when context changes,
targeting the nekiToast variable in LoginScreen.kt.

@Ojongseok Ojongseok merged commit b5586ab into develop Feb 8, 2026
3 checks passed
@Ojongseok Ojongseok deleted the feat/#80-login-onboarding branch February 9, 2026 22:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[feat] 로그인, 온보딩 UI 구성

2 participants