Feature/#76 landing - #78
Conversation
- Android SplashScreen API(core-splashscreen) 추가, startDestination 결정 전까지 Splash 유지 - App.kt startDestination을 nullable state로 변경하여 NavHost 조건부 렌더링 - Token refresh API 호출 (POST api/auth/refresh) 및 응답 모델 추가 - CheckAuthStateUseCase 구현: 토큰 존재 확인 → refresh → 에러 타입별 분기 처리 - ApiException 시 토큰 삭제, 네트워크 에러 시 토큰 유지 정책 적용 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Landing 화면 MVI 구현 (Contract, ViewModel, Screen) - 카카오 로그인 연동 및 애플 로그인 준비 중 토스트 (iOS only) - CheckAuthStateUseCase 기반 startDestination 동적 결정 - Landing → Home 네비게이션 (popUpTo inclusive) - WebViewGuideScreen 로그인 버튼 제거 - Refresh API 엔드포인트 경로 수정 (api/ 중복 제거) - HttpClient request body pretty-print 로깅 추가 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
CchBasicButton을 아이콘 포함 SocialLoginButton으로 교체하고, 카카오(노란 배경 + 말풍선 로고) / 애플(검정 배경 + 애플 로고) 표준 디자인 가이드라인에 맞게 구현 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Walkthrough랜딩 화면의 소셜 로그인 UI와 관련 뷰모델/네비게이션을 추가하고, 앱 시작 시 스플래시 화면을 유지하며 인증 상태(리프레시 토큰을 이용한 갱신)를 확인해 시작 목적지(startDestination)를 결정하는 인증·네비게이션 흐름과 토큰 새로고침 API를 도입했습니다. Changes
Sequence Diagram(s)sequenceDiagram
participant App as App Composable
participant CheckAuth as CheckAuthStateUseCase
participant AuthRepo as AuthRepository
participant LocalAuth as LocalAuthDataSource
participant RemoteAuth as RemoteAuthDataSource
App->>CheckAuth: invoke()
CheckAuth->>LocalAuth: getRefreshToken()
alt token exists
LocalAuth-->>CheckAuth: refreshTokenValue
CheckAuth->>AuthRepo: refreshToken()
AuthRepo->>LocalAuth: getRefreshToken()
LocalAuth-->>AuthRepo: token
AuthRepo->>RemoteAuth: refreshToken(token)
alt success
RemoteAuth-->>AuthRepo: RefreshTokenResult
AuthRepo->>LocalAuth: saveTokens(access, refresh)
AuthRepo-->>CheckAuth: success
CheckAuth-->>App: Result.success(true)
else api error
RemoteAuth-->>AuthRepo: ApiException
AuthRepo->>LocalAuth: clearTokens()
AuthRepo-->>CheckAuth: error
CheckAuth-->>App: Result.success(false)
end
else no token
LocalAuth-->>CheckAuth: null
CheckAuth-->>App: Result.success(false)
end
App->>App: setStartDestination()
App->>App: onReady()
sequenceDiagram
participant Landing as LandingRoute (Composable)
participant VM as LandingViewModel
participant Store as MviStore
participant Kakao as KakaoLoginUseCase
participant Nav as MainNavigator
Landing->>VM: onKakaoLogin()
VM->>Store: setLoading(true)
VM->>Kakao: invoke()
Kakao-->>VM: Result
alt success
VM->>Store: emit(NavigateHome)
Store-->>Landing: NavigateHome effect
Landing->>Nav: navigateFromLandingToHome()
else failure
VM->>Store: emit(HandleException)
Store-->>Landing: HandleException effect
Landing->>Landing: handleException()
end
VM->>Store: setLoading(false)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ❌ 3❌ Failed checks (2 warnings, 1 inconclusive)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
📝 Coding Plan
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment Tip CodeRabbit can generate a title for your PR based on the changes with custom instructions.Set the |
There was a problem hiding this comment.
Actionable comments posted: 11
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.mcp.json:
- Around line 1-8: 파일 `.mcp.json`에 하드코딩된 개발용 MCP 서버 설정(mcpServers ->
"figma-desktop" -> "url": "http://127.0.0.1:3845/mcp")이 커밋되어 있어 팀별 로컬 설정 차이를 유발할
수 있으니, 이 파일을 버전관리에서 제외하거나 설정을 템플릿화하세요; 결정 방식으로는 1) 로컬 전용이라면 `.mcp.json`을
`.gitignore`에 추가하고 이미 커밋된 경우 원격에서 제거(예: git rm --cached) 후 커밋, 2) 공용 기본값이 필요하면
`.mcp.json.example` 같은 템플릿 파일로 교체하고 실제 비포맷 파일은 gitignore에 추가, 또는 3) 환경변수/프로필을
사용하도록 코드(참조 대상: mcpServers / "figma-desktop" / "url")를 변경하여 파일을 안전하게 처리하고
README에 사용 방법을 문서화하세요.
In `@composeApp/src/androidMain/kotlin/com/chukchukhaksa/mobile/MainActivity.kt`:
- Around line 16-26: Add a hard timeout when waiting for auth to avoid the
splash screen staying forever: in App.kt where CheckAuthStateUseCase is invoked
(inside the LaunchedEffect that sets startDestination), wrap the call with
withTimeoutOrNull (e.g., 5000L) and treat a null result as false so you always
set startDestination and call onReady; also ensure the Activity's splash logic
(installSplashScreen / isReady) is set to true when App signals readiness so the
splash is dismissed even on timeout.
In `@composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/App.kt`:
- Around line 55-56: koinInject을 Composable 본문에서 직접 호출하면 리컴포지션마다 새 인스턴스가 생성될 수
있으니, CheckAuthStateUseCase 인스턴스 생성 위치를 변경하세요: Composable 내부에서 사용할 경우 val
checkAuthStateUseCase by remember {
mutableStateOf(koinInject<CheckAuthStateUseCase>()) }처럼 remember로 한 번만 생성하거나 더
권장되는 방식으로 MainViewModel에 CheckAuthStateUseCase를 주입하고 Composable에서는 viewModel()을
통해 MainViewModel의 공개된 메서드/프로퍼티를 사용해 LaunchedEffect(Unit)에서 인증 상태를 확인하도록 수정하세요;
관련 심볼: koinInject, CheckAuthStateUseCase, LaunchedEffect(Unit), MainViewModel,
startDestination.
- Around line 64-73: Replace the two LaunchedEffect blocks with a single
LaunchedEffect that calls checkAuthStateUseCase(), assigns startDestination
(using TimetableRoute.route or LandingRoute.route), and then invokes onReady()
if startDestination is non-null; locate the code around LaunchedEffect,
checkAuthStateUseCase(), startDestination, and onReady() and perform the merge
so the auth check and the onReady() call happen sequentially in one effect.
In
`@composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/data/auth/repository/AuthRepositoryImpl.kt`:
- Around line 17-23: The refreshToken() implementation duplicates token-persist
logic — replace the two calls
localAuthDataSource.saveAccessToken(result.accessToken) and
localAuthDataSource.saveRefreshToken(result.refreshToken) with a single call to
the existing unified method localAuthDataSource.saveTokens(result.accessToken,
result.refreshToken) (or saveTokens(result) if that API exists), updating
AuthRepositoryImpl.refreshToken to pass the access and refresh token to
saveTokens so all token persistence goes through the single saveTokens path.
In
`@composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/domain/auth/usecase/CheckAuthStateUseCase.kt`:
- Around line 11-26: The inner try-catch inside CheckAuthStateUseCase.invoke
duplicates CancellationException handling already provided by
runCatchingIgnoreCancelled; remove the explicit catch (e: CancellationException)
branch and simplify the inner block to only handle ApiException and other
Exceptions (or replace the inner try-catch with a runCatching that maps
ApiException to clearing tokens and false, and other exceptions to false),
ensuring you still call authRepository.refreshToken() and on ApiException call
authRepository.clearTokens() before returning false; update the method body in
CheckAuthStateUseCase.invoke accordingly.
In
`@composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/landing/LandingScreen.kt`:
- Around line 163-204: Add a Compose preview for the new SocialLoginButton so
designers and reviewers can visually validate it under preview/designsystem;
create a `@Preview` composable (in preview/designsystem package under androidMain)
that calls SocialLoginButton with representative parameters (sample text, a
drawable icon resource, containerColor/contentColor, and enabled true —
optionally add a second preview with enabled=false) and apply the app theme
(CchTheme) so typography and colors match; reference the SocialLoginButton
function from LandingScreen.kt when implementing the preview.
- Around line 90-119: The five repeated Image(...) calls should be replaced by
iterating over a list of drawable resource IDs to avoid duplication; create a
list like listOf(Res.drawable.img_landing_1, ..., Res.drawable.img_landing_5)
and forEach { id -> Image(painter = painterResource(id), contentDescription =
null, modifier = imageModifier, contentScale = ContentScale.FillWidth) } so the
repeated use of Image, painterResource, imageModifier and ContentScale.FillWidth
is consolidated and adding/removing images only requires editing the list.
In
`@composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/landing/LandingViewModel.kt`:
- Around line 16-31: The isLoading guard in onKakaoLogin is not atomic: the
current check (mviStore.uiState.value.isLoading) and subsequent setState are
separated, allowing races and duplicate launches; fix by performing an atomic
check-and-set before launching the coroutine — e.g., add a single-operation
helper on mviStore such as trySetLoading(desired: Boolean): Boolean (or use a
Mutex around the check+set) and call if (!mviStore.trySetLoading(true)) return,
then launch viewModelScope.launch and ensure you set trySetLoading(false) (or
call setState) in finally; reference symbols: onKakaoLogin, mviStore, isLoading,
viewModelScope.launch.
In
`@composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/landing/navigation/LandingNavigation.kt`:
- Around line 6-10: The identifier LandingRoute is duplicated between the import
(com.chukchukhaksa.mobile.presentation.landing.LandingRoute), the local object
LandingRoute, and the Composable call; rename the navigation object to
LandingNavRoute (or use an import alias) and update all references — change the
local object name from LandingRoute to LandingNavRoute and replace occurrences
in the Composable invocation and any imports that refer to the local symbol
(e.g., update usages that currently reference LandingRoute to LandingNavRoute or
alias the imported LandingRoute as ImportedLandingRoute) so the external route
type and the local nav object are unambiguous.
In `@gradle/libs.versions.toml`:
- Line 40: Update the androidx-splashscreen entry in libs.versions.toml from
"1.0.1" to "1.2.0": locate the dependency key androidx-splashscreen and change
its value to "1.2.0", then sync the Gradle project (re-import or run a build) to
ensure the updated dependency resolves and tests/build succeed.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: a413c457-79df-44cc-9f58-bf85b901f01a
📒 Files selected for processing (29)
.mcp.jsoncomposeApp/build.gradle.ktscomposeApp/src/androidMain/kotlin/com/chukchukhaksa/mobile/MainActivity.ktcomposeApp/src/commonMain/composeResources/drawable/ic_apple_logo.xmlcomposeApp/src/commonMain/composeResources/drawable/ic_kakao_logo.xmlcomposeApp/src/commonMain/composeResources/drawable/img_landing_1.webpcomposeApp/src/commonMain/composeResources/drawable/img_landing_2.webpcomposeApp/src/commonMain/composeResources/drawable/img_landing_3.webpcomposeApp/src/commonMain/composeResources/drawable/img_landing_4.webpcomposeApp/src/commonMain/composeResources/drawable/img_landing_5.webpcomposeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/App.ktcomposeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/MainNavigator.ktcomposeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/data/auth/datasource/RemoteAuthDataSource.ktcomposeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/data/auth/repository/AuthRepositoryImpl.ktcomposeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/di/DomainModules.ktcomposeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/di/PresentationModules.ktcomposeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/domain/auth/model/RefreshTokenResult.ktcomposeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/domain/auth/repository/AuthRepository.ktcomposeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/domain/auth/usecase/CheckAuthStateUseCase.ktcomposeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/landing/LandingContract.ktcomposeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/landing/LandingScreen.ktcomposeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/landing/LandingViewModel.ktcomposeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/landing/navigation/LandingNavigation.ktcomposeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetable/component/WebViewGuideScreen.ktcomposeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/remote/auth/RemoteAuthDataSourceImpl.ktcomposeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/remote/auth/model/RefreshRequest.ktcomposeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/remote/auth/model/RefreshResponse.ktcomposeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/remote/di/HttpClientModule.ktgradle/libs.versions.toml
💤 Files with no reviewable changes (1)
- composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetable/component/WebViewGuideScreen.kt
| val checkAuthStateUseCase: CheckAuthStateUseCase = koinInject() | ||
| var startDestination by remember { mutableStateOf<String?>(null) } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
koinInject를 Composable 본문에서 직접 호출하면 리컴포지션마다 새 인스턴스가 생성될 수 있습니다.
CheckAuthStateUseCase는 LaunchedEffect(Unit)에서 한 번만 사용되지만, koinInject가 리컴포지션될 때마다 호출됩니다. remember로 감싸거나 MainViewModel에 의존성을 주입하는 것이 더 적절합니다.
♻️ 권장 수정안
- val checkAuthStateUseCase: CheckAuthStateUseCase = koinInject()
+ val checkAuthStateUseCase: CheckAuthStateUseCase = remember { koinInject() }
var startDestination by remember { mutableStateOf<String?>(null) }또는 MainViewModel에 CheckAuthStateUseCase를 주입하고 ViewModel에서 인증 상태를 확인하는 것이 더 좋은 아키텍처입니다.
📝 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.
| val checkAuthStateUseCase: CheckAuthStateUseCase = koinInject() | |
| var startDestination by remember { mutableStateOf<String?>(null) } | |
| val checkAuthStateUseCase: CheckAuthStateUseCase = remember { koinInject() } | |
| var startDestination by remember { mutableStateOf<String?>(null) } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/App.kt` around
lines 55 - 56, koinInject을 Composable 본문에서 직접 호출하면 리컴포지션마다 새 인스턴스가 생성될 수 있으니,
CheckAuthStateUseCase 인스턴스 생성 위치를 변경하세요: Composable 내부에서 사용할 경우 val
checkAuthStateUseCase by remember {
mutableStateOf(koinInject<CheckAuthStateUseCase>()) }처럼 remember로 한 번만 생성하거나 더
권장되는 방식으로 MainViewModel에 CheckAuthStateUseCase를 주입하고 Composable에서는 viewModel()을
통해 MainViewModel의 공개된 메서드/프로퍼티를 사용해 LaunchedEffect(Unit)에서 인증 상태를 확인하도록 수정하세요;
관련 심볼: koinInject, CheckAuthStateUseCase, LaunchedEffect(Unit), MainViewModel,
startDestination.
| private fun SocialLoginButton( | ||
| modifier: Modifier = Modifier, | ||
| text: String, | ||
| iconRes: DrawableResource, | ||
| containerColor: Color, | ||
| contentColor: Color, | ||
| enabled: Boolean, | ||
| onClick: () -> Unit, | ||
| ) { | ||
| val clickableModifier = if (enabled) { | ||
| Modifier.cchClickable { onClick() } | ||
| } else { | ||
| Modifier | ||
| } | ||
|
|
||
| Box( | ||
| modifier = modifier | ||
| .wrapContentHeight() | ||
| .clip(RoundedCornerShape(10.dp)) | ||
| .then(clickableModifier) | ||
| .background(containerColor) | ||
| .padding(18.dp), | ||
| ) { | ||
| Row( | ||
| modifier = Modifier.fillMaxWidth(), | ||
| horizontalArrangement = Arrangement.Center, | ||
| verticalAlignment = Alignment.CenterVertically, | ||
| ) { | ||
| Image( | ||
| painter = painterResource(iconRes), | ||
| contentDescription = null, | ||
| modifier = Modifier.size(32.dp), | ||
| ) | ||
| Spacer(modifier = Modifier.width(8.dp)) | ||
| Text( | ||
| text = text, | ||
| color = contentColor, | ||
| style = CchTheme.typography.bodyMdStrong, | ||
| ) | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
신규 SocialLoginButton 컴포넌트 프리뷰를 추가해 주세요.
재사용 컴포넌트가 추가되어 디자인/상태 확인을 위해 preview/designsystem 프리뷰가 있으면 회귀 확인이 쉬워집니다.
Based on learnings: "Applies to composeApp/src/androidMain/kotlin/com/chukchukhaksa/mobile/preview/designsystem/**/*.kt : Preview components in preview/designsystem/ package when creating new UI components".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/landing/LandingScreen.kt`
around lines 163 - 204, Add a Compose preview for the new SocialLoginButton so
designers and reviewers can visually validate it under preview/designsystem;
create a `@Preview` composable (in preview/designsystem package under androidMain)
that calls SocialLoginButton with representative parameters (sample text, a
drawable icon resource, containerColor/contentColor, and enabled true —
optionally add a second preview with enabled=false) and apply the app theme
(CchTheme) so typography and colors match; reference the SocialLoginButton
function from LandingScreen.kt when implementing the preview.
| fun onKakaoLogin(context: Any? = null) { | ||
| if (mviStore.uiState.value.isLoading) return | ||
|
|
||
| viewModelScope.launch { | ||
| mviStore.setState { copy(isLoading = true) } | ||
|
|
||
| kakaoLoginUseCase(context) | ||
| .onSuccess { | ||
| mviStore.postSideEffect(LandingSideEffect.NavigateHome) | ||
| } | ||
| .onFailure { throwable -> | ||
| mviStore.postSideEffect(LandingSideEffect.HandleException(throwable)) | ||
| } | ||
|
|
||
| mviStore.setState { copy(isLoading = false) } | ||
| } |
There was a problem hiding this comment.
isLoading 가드가 원자적이지 않아 중복 로그인 요청이 발생할 수 있습니다.
Line 17에서 체크하고 Line 20에서 나중에 상태를 바꾸기 때문에, 빠른 연속 탭 시 코루틴이 여러 개 실행될 수 있습니다.
🐛 제안 diff
fun onKakaoLogin(context: Any? = null) {
if (mviStore.uiState.value.isLoading) return
+ mviStore.setState { copy(isLoading = true) }
viewModelScope.launch {
- mviStore.setState { copy(isLoading = true) }
-
- kakaoLoginUseCase(context)
- .onSuccess {
- mviStore.postSideEffect(LandingSideEffect.NavigateHome)
- }
- .onFailure { throwable ->
- mviStore.postSideEffect(LandingSideEffect.HandleException(throwable))
- }
-
- mviStore.setState { copy(isLoading = false) }
+ try {
+ kakaoLoginUseCase(context)
+ .onSuccess {
+ mviStore.postSideEffect(LandingSideEffect.NavigateHome)
+ }
+ .onFailure { throwable ->
+ mviStore.postSideEffect(LandingSideEffect.HandleException(throwable))
+ }
+ } finally {
+ mviStore.setState { copy(isLoading = false) }
+ }
}
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/landing/LandingViewModel.kt`
around lines 16 - 31, The isLoading guard in onKakaoLogin is not atomic: the
current check (mviStore.uiState.value.isLoading) and subsequent setState are
separated, allowing races and duplicate launches; fix by performing an atomic
check-and-set before launching the coroutine — e.g., add a single-operation
helper on mviStore such as trySetLoading(desired: Boolean): Boolean (or use a
Mutex around the check+set) and call if (!mviStore.trySetLoading(true)) return,
then launch viewModelScope.launch and ensure you set trySetLoading(false) (or
call setState) in finally; reference symbols: onKakaoLogin, mviStore, isLoading,
viewModelScope.launch.
- App.kt: 인증 확인 후 onReady() 호출하는 LaunchedEffect 통합 - AuthRepositoryImpl: refreshToken()에서 saveTokens() 재사용으로 중복 제거 - LandingScreen: 반복되는 Image 호출을 forEach 루프로 축약 - LandingViewModel: isLoading 상태를 launch 전에 설정하고 try-finally로 안전하게 해제 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/landing/LandingScreen.kt`:
- Around line 156-168: 현재 LandingScreen에서 enabled가 false일 때 클릭만 비활성화되고 시각적 피드백이
없어 사용자가 상태를 인지하지 못합니다; LandingScreen.kt의 clickableModifier/enabled 정의와 Box 호출부를
수정해 비활성화 시 배경이나 전체 컴포저블에 투명도 또는 색상 변화를 적용하도록 변경하세요 (예: background에
containerColor.copy(alpha = if (enabled) 1f else 0.5f) 또는 Box에 .alpha(if
(enabled) 1f else 0.5f)을 추가), clickableModifier와 충돌하지 않도록
.then(clickableModifier) 순서는 유지하세요.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 17dbe659-350e-422a-9e94-0f72fbf4761a
📒 Files selected for processing (4)
composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/App.ktcomposeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/data/auth/repository/AuthRepositoryImpl.ktcomposeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/landing/LandingScreen.ktcomposeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/landing/LandingViewModel.kt
📌 PR 요약
🌱 작업한 내용
🌱 PR 포인트
📸 스크린샷
📮 관련 이슈
RCA 룰을 사용하여 코드 리뷰를 해주세요
R (Request Changes): 적극적으로 반영을 고려해주세요C (Comment): 웬만하면 반영해주세요A (Approve): 반영해도 좋고, 넘어가도 좋습니다. 사소한 의견입니다.Summary by CodeRabbit
릴리스 노트
새로운 기능
개선사항
변경