-
Notifications
You must be signed in to change notification settings - Fork 0
[Feat] 온보딩 노출 정책 변경, Build Flavor #261
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
Conversation
서비스 온보딩 시작 버튼 클릭 -> 서비스 온보딩 첫 페이지 시작으로 시점 변경
- 서비스 소개 -> 지역인증 -> 취향 탐색 - 모든 단계는 최초 1회 노출 후, 여부 상관없이 재접속 후에는 노출 X
Walkthrough앱/모듈 재구성, 광고 API 제거 및 광고 구현 이동, 온보딩 네비게이션·뷰모델 확장(뒤로가기 포함), sign-in 반환 타입을 ExternalUUID로 변경, 프리사인드 URL 인증 API 분리 및 업로드 병렬화, 테스트 컨벤션 플러그인 추가 및 테스트 의존성 정리, AppUpdateHandler를 nullable로 처리. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor U as 사용자
participant SV as SignInViewModel
participant UR as UserRepository
participant OR as OnboardingRepository
participant Nav as Navigation
U->>SV: 로그인 시도
SV->>UR: signIn()
UR-->>SV: Result<ExternalUUID>
SV->>OR: getOnboardingPreferences()
OR-->>SV: OnboardingPreferences(shouldShowIntroduce, shouldVerifyArea, shouldChooseDislikes)
alt shouldShowIntroduce
SV->>Nav: Navigate to Introduce
else shouldVerifyArea
SV->>Nav: Navigate to AreaVerification
else shouldChooseDislikes
SV->>Nav: Navigate to ChooseDislikes
else
SV->>Nav: Navigate to SpotList
end
sequenceDiagram
autonumber
participant Repo as AconAppRepositoryImpl
participant API as AconAppApi (Auth)
participant DS as AconAppRemoteDataSource
note over Repo: withContext(IO) + async 병렬화 (presigned URL + RequestBody)
Repo->>API: POST /api/v1/images/presigned-url
API-->>Repo: PresignedUrlResponse
Repo-->>Repo: Uri -> RequestBody (read bytes, mime)
Repo->>DS: uploadFile(url, fields, body)
DS-->>Repo: Result<String> (업로드된 파일 URL)
sequenceDiagram
autonumber
participant UI as IntroduceScreen
participant VM as IntroduceViewModel
participant UR as UserRepository
participant OR as OnboardingRepository
participant Nav as Navigation
UI->>VM: 시작 버튼
VM->>OR: updateShouldShowIntroduce(false)
VM->>UR: getSignInStatus()
alt USER
VM->>OR: getOnboardingPreferences()
OR-->>VM: prefs
alt shouldVerifyArea
VM->>Nav: NavigateToAreaVerification
else shouldChooseDislikes
VM->>Nav: NavigateToChooseDislikes
else
VM->>Nav: NavigateToHomeScreen
end
else
VM->>Nav: NavigateToHomeScreen
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests
Comment |
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.
Actionable comments posted: 8
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
acon/src/main/java/com/acon/acon/update/AppUpdateHandler.kt (1)
56-75: AppUpdateManager가 null이면 getUpdateState가 영원히 반환되지 않습니다.Test 플레이버에서
appUpdateManager를 null 주입하면appUpdateInfo가 어떤 값도 emit하지 못하고firstNotNull()이 계속 대기하게 됩니다. 이 때문에getUpdateState()전체가 suspend 상태로 멈춰 버려 강제/선택 업데이트 검사 로직이 완전히 막힙니다.appUpdateManager가 null인 경우에는 즉시UpdateState.NONE을 반환하도록 방어 로직을 추가해야 합니다.override suspend fun getUpdateState() : UpdateState { + if (appUpdateManager == null) { + return UpdateState.NONE + } val shouldUpdateAppDeferred = scope.async {feature/onboarding/src/main/java/com/acon/feature/onboarding/area/composable/AreaVerificationScreenContainer.kt (1)
50-83: 시스템 뒤로가기를 ViewModel 경로로 연결해 주세요.UI 내부 뒤로가기 버튼은
viewModel::onBackClicked를 타면서NavigateBack사이드이펙트를 통해onNavigateBack콜백이 실행되지만, 기기 뒤로가기는 여전히navController.popBackStack()만 직접 호출하고 있습니다. 그 결과 새로 추가된 온보딩 노출 정책(예: 완료 플래그 갱신)이 시스템 뒤로가기에서는 반영되지 않습니다. BackHandler에서도 동일하게 ViewModel을 거치도록 바꿔주세요.- val navController = LocalNavController.current - BackHandler { - if (!skippable) - navController.popBackStack() - } + BackHandler { + if (!skippable) { + viewModel.onBackClicked() + } + }feature/spot/src/main/java/com/acon/acon/feature/spot/screen/spotlist/composable/SpotListSuccessView.kt (1)
284-289: LaunchedEffect 키 부적절: spot 변경 시 색상 업데이트 누락 가능키가 Unit이라 페이지/이미지 변경에 반응하지 않습니다. spot(혹은 spot.image)에 키를 걸어 갱신 누락을 방지하세요. 광고(null) 페이지로 전환 시 초기화도 권장합니다.
다음과 같이 수정 제안:
- LaunchedEffect(Unit) { - if (spot != null) { - spotFogColor = if (spot.image.isBlank()) Color(0xFFE17651) else spot.image.getOverlayColor(context) - } - } + LaunchedEffect(spot?.image) { + if (spot != null) { + spotFogColor = if (spot.image.isBlank()) Color(0xFFE17651) else spot.image.getOverlayColor(context) + } else { + spotFogColor = Color.Transparent + } + }
🧹 Nitpick comments (10)
core/ads/build.gradle.kts (1)
2-21: 불필요한 import 제거 제안
Line 2의import kotlin.apply는 기본 패키지에 포함된 확장이므로 별도 import 없이도 사용 가능합니다. 유지하지 않아도 컴파일 결과가 동일하니 정리해 두면 좋겠습니다.적용 제안:
-import kotlin.applyfeature/spot/src/main/java/com/acon/acon/feature/spot/screen/spotlist/composable/SpotListScreen.kt (2)
85-87: remember 키 누락: 화면 크기 변경 시 itemHeightPx가 갱신되지 않음Density/윈도우 사이즈 변화 시 재계산을 보장하려면 키를 추가하세요.
다음과 같이 수정 제안:
- val itemHeightPx by remember { + val itemHeightPx by remember(screenHeightPx) { mutableFloatStateOf(screenHeightPx * .64f) }
139-141: 불필요한 완전수식 타입 참조 정리 제안동일 파일 상단에 SignInStatus를 import하고 있으므로, 완전수식 참조를 통일하면 가독성이 좋아집니다.
- if (userType == com.acon.acon.core.model.type.SignInStatus.GUEST) + if (userType == SignInStatus.GUEST)- if (userType == com.acon.acon.core.model.type.SignInStatus.GUEST) { + if (userType == SignInStatus.GUEST) {Also applies to: 290-292
feature/spot/src/main/java/com/acon/acon/feature/spot/screen/spotlist/composable/SpotListSuccessView.kt (2)
79-90: 광고 삽입 인덱스 의도 확인 필요 (5/11 위치 정의 모호)현재는 11 → 5 순으로 삽입하여 최종 리스트에서 광고가 5, 12 인덱스에 위치합니다. 만약 “최종” 인덱스를 5, 11로 고정하려는 의도라면, 원본 크기를 기준으로 5 → 11 순으로 삽입해야 합니다.
의도가 “최종 5, 11 위치”라면 다음 수정 제안:
- val adInsertedSpot = remember(state.spotList) { - val list: MutableList<Spot?> = state.spotList.toMutableList() - if (list.size >= 11) { - list.add(11, null) - } - if (list.size >= 5) { - list.add(5, null) - } - list - } + val adInsertedSpot = remember(state.spotList) { + val original = state.spotList.size + val list: MutableList<Spot?> = state.spotList.toMutableList() + if (original >= 5) list.add(5, null) + if (original >= 11) list.add(11, null) + list + }의도가 “원본 5, 11번째 뒤에 광고(즉 최종 6, 12번째)”라면 현재 구현이 맞는지 확인 부탁드립니다.
62-63: 중복 상수 정리 제안: MAX_GUEST_AVAILABLE_COUNT동일 상수가 SpotEmptyView/SpotListSuccessView에 중복 정의되어 있습니다. 공용 파일로 추출해 단일 출처로 관리하세요.
feature/spot/src/main/java/com/acon/acon/feature/spot/screen/spotlist/composable/SpotEmptyView.kt (2)
126-127: 신규 스팟 등록 클릭 처리 OK텍스트 CTA에서 콜백 호출로 전환한 점 적절합니다. 접근성 측면에서 버튼 역할 부여 고려 여지는 있습니다.
52-54: remember 키 누락: 화면 크기 변경 시 아이템 높이 재계산 필요Density/윈도우 변경 반영을 위해 키 추가 권장.
- val itemHeightPx by remember { + val itemHeightPx by remember(screenHeightPx) { mutableFloatStateOf(screenHeightPx * .3f) }feature/spot/src/main/java/com/acon/acon/feature/spot/screen/spotlist/composable/SpotListScreenContainer.kt (2)
83-89: 게스트 가드 + 신규 스팟 등록 트리거 OK — 분석 이벤트 라벨 보강 제안onSignInRequired("")의 공백 라벨은 추적 어려움. 구분 가능한 라벨 사용을 권장합니다.
- if (userType == SignInStatus.GUEST) - onSignInRequired("") + if (userType == SignInStatus.GUEST) + onSignInRequired("click_register_new_spot_guest?") else viewModel.onRegisterNewSpot()
93-96: 권한/상태 구독 호출은 LaunchedEffect로 이동 권장Composable 재구성마다 호출될 수 있습니다. 일회성/구독 시작은 효과 블록으로 묶는 게 안전합니다.
- viewModel.requestLocationPermission() - viewModel.useSignInStatus() - viewModel.useLiveLocation() + LaunchedEffect(Unit) { + viewModel.requestLocationPermission() + viewModel.useSignInStatus() + viewModel.useLiveLocation() + }feature/onboarding/src/main/java/com/acon/feature/onboarding/area/composable/AreaVerificationScreen.kt (1)
105-111: 백 버튼 터치 영역을 최소 48dp로 키워주세요.Line 108에서
Modifier.padding(...).noRippleClickable순서 때문에 실제 터치 영역이 아이콘 크기(약 24dp)에 머뭅니다. 시스템/디자인 가이드라인에 맞게 최소 48dp 터치 타겟을 확보하도록 래핑 레이아웃을 추가해 주세요.size확장을 쓰면import androidx.compose.foundation.layout.size도 필요합니다.- Icon( - imageVector = ImageVector.vectorResource(R.drawable.ic_topbar_arrow_left), - contentDescription = stringResource(R.string.back), - modifier = Modifier.padding(start = 16.dp, top = 32.dp).noRippleClickable { - onBack() - }, tint = Color.Unspecified - ) + Box( + modifier = Modifier + .padding(start = 16.dp, top = 32.dp) + .size(48.dp) + .noRippleClickable { onBack() }, + contentAlignment = Alignment.Center + ) { + Icon( + imageVector = ImageVector.vectorResource(R.drawable.ic_topbar_arrow_left), + contentDescription = stringResource(R.string.back), + tint = Color.Unspecified + ) + }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
acon/src/main/ic_launcher-playstore.pngis excluded by!**/*.png
📒 Files selected for processing (67)
acon/build.gradle.kts(2 hunks)acon/proguard-rules.pro(1 hunks)acon/src/main/java/com/acon/acon/MainActivity.kt(7 hunks)acon/src/main/java/com/acon/acon/navigation/nested/AreaVerificationNavigation.kt(1 hunks)acon/src/main/java/com/acon/acon/navigation/nested/OnboardingNavigation.kt(2 hunks)acon/src/main/java/com/acon/acon/navigation/nested/SpotNavigation.kt(1 hunks)acon/src/main/java/com/acon/acon/update/AppUpdateHandler.kt(3 hunks)app/src/androidTest/java/com/acon/acon/ExampleInstrumentedTest.kt(0 hunks)build-logic/convention/build.gradle.kts(1 hunks)build-logic/convention/src/main/kotlin/AndroidApplicationComposeConventionPlugin.kt(1 hunks)build-logic/convention/src/main/kotlin/test/CommonUnitTestConventionPlugin.kt(1 hunks)build-logic/convention/src/main/kotlin/test/FeatureTestConventionPlugin.kt(1 hunks)build-logic/convention/src/main/kotlin/utils/DependencyExtensions.kt(1 hunks)core/ads-api/build.gradle.kts(0 hunks)core/ads-api/src/main/AndroidManifest.xml(0 hunks)core/ads-api/src/main/java/com/acon/acon/core/ads_api/AdProvider.kt(0 hunks)core/ads/build.gradle.kts(2 hunks)core/ads/src/main/AndroidManifest.xml(0 hunks)core/ads/src/main/java/com/acon/core/ads/SpotListAdProvider.kt(6 hunks)core/analytics/build.gradle.kts(0 hunks)core/common/build.gradle.kts(1 hunks)core/data/build.gradle.kts(1 hunks)core/data/src/androidTest/java/com/acon/core/data/ExampleInstrumentedTest.kt(0 hunks)core/data/src/main/kotlin/com/acon/core/data/api/remote/auth/AconAppApi.kt(1 hunks)core/data/src/main/kotlin/com/acon/core/data/api/remote/noauth/AconAppNoAuthApi.kt(0 hunks)core/data/src/main/kotlin/com/acon/core/data/datasource/local/OnboardingLocalDataSource.kt(2 hunks)core/data/src/main/kotlin/com/acon/core/data/datasource/remote/AconAppRemoteDataSource.kt(2 hunks)core/data/src/main/kotlin/com/acon/core/data/di/ApiModule.kt(2 hunks)core/data/src/main/kotlin/com/acon/core/data/dto/response/SignInResponse.kt(1 hunks)core/data/src/main/kotlin/com/acon/core/data/repository/AconAppRepositoryImpl.kt(2 hunks)core/data/src/main/kotlin/com/acon/core/data/repository/OnboardingRepositoryImpl.kt(4 hunks)core/data/src/main/kotlin/com/acon/core/data/repository/UserRepositoryImpl.kt(5 hunks)core/data/src/main/kotlin/com/acon/core/data/serializer/OnboardingPreferencesSerializer.kt(1 hunks)core/data/src/main/proto/onboarding_preferences_entity.proto(1 hunks)core/data/src/test/java/com/acon/core/data/repository/ProfileRepositoryTest.kt(3 hunks)core/designsystem/src/main/res/values/strings.xml(1 hunks)core/model/src/main/java/com/acon/acon/core/model/model/OnboardingPreferences.kt(1 hunks)core/model/src/main/java/com/acon/acon/core/model/model/user/ExternalUUID.kt(1 hunks)core/model/src/main/java/com/acon/acon/core/model/model/user/VerificationStatus.kt(0 hunks)core/social/build.gradle.kts(1 hunks)core/social/src/androidTest/java/com/acon/core/social/ExampleInstrumentedTest.kt(0 hunks)domain/build.gradle.kts(1 hunks)domain/src/main/java/com/acon/acon/domain/repository/AconAppRepository.kt(1 hunks)domain/src/main/java/com/acon/acon/domain/repository/OnboardingRepository.kt(1 hunks)domain/src/main/java/com/acon/acon/domain/repository/UserRepository.kt(1 hunks)feature/onboarding/src/main/java/com/acon/feature/onboarding/area/composable/AreaVerificationScreen.kt(3 hunks)feature/onboarding/src/main/java/com/acon/feature/onboarding/area/composable/AreaVerificationScreenContainer.kt(3 hunks)feature/onboarding/src/main/java/com/acon/feature/onboarding/area/composable/VerifyInMapScreenContainer.kt(0 hunks)feature/onboarding/src/main/java/com/acon/feature/onboarding/area/viewmodel/AreaVerificationViewModel.kt(3 hunks)feature/onboarding/src/main/java/com/acon/feature/onboarding/area/viewmodel/VerifyInMapViewModel.kt(0 hunks)feature/onboarding/src/main/java/com/acon/feature/onboarding/dislikes/viewmodel/ChooseDislikesViewModel.kt(1 hunks)feature/onboarding/src/main/java/com/acon/feature/onboarding/introduce/composable/IntroduceScreenContainer.kt(2 hunks)feature/onboarding/src/main/java/com/acon/feature/onboarding/introduce/viewmodel/IntroduceViewModel.kt(3 hunks)feature/profile/build.gradle.kts(1 hunks)feature/signin/src/main/java/com/acon/acon/feature/signin/screen/SignInViewModel.kt(3 hunks)feature/signin/src/main/java/com/acon/acon/feature/signin/screen/component/SignInTopBar.kt(1 hunks)feature/spot/build.gradle.kts(1 hunks)feature/spot/src/main/java/com/acon/acon/feature/spot/screen/spotdetail/composable/SpotDetailViewModel.kt(2 hunks)feature/spot/src/main/java/com/acon/acon/feature/spot/screen/spotlist/SpotListViewModel.kt(3 hunks)feature/spot/src/main/java/com/acon/acon/feature/spot/screen/spotlist/composable/SpotEmptyView.kt(6 hunks)feature/spot/src/main/java/com/acon/acon/feature/spot/screen/spotlist/composable/SpotListScreen.kt(3 hunks)feature/spot/src/main/java/com/acon/acon/feature/spot/screen/spotlist/composable/SpotListScreenContainer.kt(4 hunks)feature/spot/src/main/java/com/acon/acon/feature/spot/screen/spotlist/composable/SpotListSuccessView.kt(5 hunks)gradle/libs.versions.toml(2 hunks)provider/ads-impl/.gitignore(0 hunks)provider/ads-impl/proguard-rules.pro(0 hunks)settings.gradle.kts(2 hunks)
💤 Files with no reviewable changes (14)
- core/ads/src/main/AndroidManifest.xml
- feature/onboarding/src/main/java/com/acon/feature/onboarding/area/composable/VerifyInMapScreenContainer.kt
- core/analytics/build.gradle.kts
- app/src/androidTest/java/com/acon/acon/ExampleInstrumentedTest.kt
- core/data/src/androidTest/java/com/acon/core/data/ExampleInstrumentedTest.kt
- core/data/src/main/kotlin/com/acon/core/data/api/remote/noauth/AconAppNoAuthApi.kt
- core/model/src/main/java/com/acon/acon/core/model/model/user/VerificationStatus.kt
- core/ads-api/src/main/AndroidManifest.xml
- provider/ads-impl/proguard-rules.pro
- feature/onboarding/src/main/java/com/acon/feature/onboarding/area/viewmodel/VerifyInMapViewModel.kt
- provider/ads-impl/.gitignore
- core/ads-api/src/main/java/com/acon/acon/core/ads_api/AdProvider.kt
- core/social/src/androidTest/java/com/acon/core/social/ExampleInstrumentedTest.kt
- core/ads-api/build.gradle.kts
👮 Files not reviewed due to content moderation or server errors (2)
- core/data/build.gradle.kts
- core/data/src/main/kotlin/com/acon/core/data/di/ApiModule.kt
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: ThirFir
PR: AconInc/ACON-Android#255
File: core/data/src/main/kotlin/com/acon/core/data/datasource/remote/AconAppRemoteDataSource.kt:12-14
Timestamp: 2025-09-19T07:16:52.945Z
Learning: ThirFir prefers pragmatic engineering decisions over strict architectural separation when the practical benefits don't justify the implementation complexity, such as keeping FileUploadApi with the same client configuration rather than creating separate upload-specific clients.
Learnt from: ThirFir
PR: AconInc/ACON-Android#256
File: feature/profile/src/main/java/com/acon/feature/profile/update/composable/ProfileUpdateScreen.kt:86-99
Timestamp: 2025-09-20T06:53:43.571Z
Learning: ThirFir prefers practical engineering decisions over theoretical best practices when the actual risk is minimal, such as questioning the need for remember keys when the captured values are demonstrably stable.
Learnt from: ThirFir
PR: AconInc/ACON-Android#255
File: feature/profile/src/main/java/com/acon/feature/profile/update/viewmodel/ProfileUpdateViewModel.kt:38-57
Timestamp: 2025-09-19T07:40:37.778Z
Learning: ThirFir prefers pragmatic engineering solutions that balance user experience with implementation effort, such as choosing snackbar + retry button over more complex custom retry UI when design resources are limited.
📚 Learning: 2025-09-19T04:52:02.675Z
Learnt from: ThirFir
PR: AconInc/ACON-Android#254
File: app/src/main/java/com/acon/acon/navigation/nested/ProfileNavigationLegacy.kt:38-40
Timestamp: 2025-09-19T04:52:02.675Z
Learning: In ProfileNavigationLegacy.kt, the onNavigateToProfileUpdate callback is intentionally left as a placeholder (just returning navController) and will be implemented in a future PR according to ThirFir.
Applied to files:
acon/src/main/java/com/acon/acon/navigation/nested/AreaVerificationNavigation.ktacon/src/main/java/com/acon/acon/navigation/nested/SpotNavigation.ktfeature/spot/src/main/java/com/acon/acon/feature/spot/screen/spotlist/composable/SpotListScreenContainer.ktacon/src/main/java/com/acon/acon/navigation/nested/OnboardingNavigation.ktfeature/onboarding/src/main/java/com/acon/feature/onboarding/area/composable/AreaVerificationScreenContainer.kt
📚 Learning: 2025-09-19T04:57:25.197Z
Learnt from: ThirFir
PR: AconInc/ACON-Android#254
File: core/ui/src/main/java/com/acon/acon/core/ui/base/BaseContainerHost.kt:50-57
Timestamp: 2025-09-19T04:57:25.197Z
Learning: BaseContainerHost에서 useSignInStatus() 메서드의 패턴: 로컬 변수 signInStatus (LocalSignInStatus.current에서 가져옴)와 필드 _signInStatus (MutableStateFlow)는 서로 다른 스코프에 있어서, _signInStatus.value를 설정해도 로컬 signInStatus 변수에 영향을 주지 않는다. 이는 Composition Local과 ViewModel 상태 동기화의 표준 패턴이다.
Applied to files:
feature/onboarding/src/main/java/com/acon/feature/onboarding/introduce/viewmodel/IntroduceViewModel.kt
📚 Learning: 2025-09-19T09:06:29.860Z
Learnt from: ThirFir
PR: AconInc/ACON-Android#255
File: core/data/src/main/kotlin/com/acon/core/data/repository/ProfileRepositoryImpl.kt:64-77
Timestamp: 2025-09-19T09:06:29.860Z
Learning: ThirFir moved image upload logic from ProfileRepository to AconAppRepository for better separation of concerns, demonstrating preference for proper architectural boundaries over keeping all profile-related operations in one repository.
Applied to files:
core/data/src/test/java/com/acon/core/data/repository/ProfileRepositoryTest.ktcore/data/src/main/kotlin/com/acon/core/data/repository/AconAppRepositoryImpl.kt
🧬 Code graph analysis (7)
feature/spot/build.gradle.kts (1)
build-logic/convention/src/main/kotlin/utils/DependencyExtensions.kt (1)
implementation(9-11)
build-logic/convention/src/main/kotlin/test/CommonUnitTestConventionPlugin.kt (1)
build-logic/convention/src/main/kotlin/utils/DependencyExtensions.kt (2)
testImplementation(29-31)testRuntimeOnly(33-35)
feature/spot/src/main/java/com/acon/acon/feature/spot/screen/spotlist/composable/SpotListSuccessView.kt (2)
feature/spot/src/main/java/com/acon/acon/feature/spot/screen/spotlist/composable/SpotEmptyView.kt (1)
SpotEmptyView(38-132)core/ads/src/main/java/com/acon/core/ads/SpotListAdProvider.kt (1)
SpotListNativeAd(53-200)
feature/onboarding/src/main/java/com/acon/feature/onboarding/introduce/composable/IntroduceScreenContainer.kt (1)
feature/settings/src/main/java/com/acon/acon/feature/verification/screen/UserVerifiedAreasViewModel.kt (1)
onNavigateToAreaVerification(115-117)
core/data/src/main/kotlin/com/acon/core/data/repository/OnboardingRepositoryImpl.kt (1)
core/data/src/main/kotlin/com/acon/core/data/error/ErrorUtils.kt (2)
runCatchingWith(7-24)runCatchingWith(26-40)
build-logic/convention/src/main/kotlin/test/FeatureTestConventionPlugin.kt (1)
build-logic/convention/src/main/kotlin/utils/DependencyExtensions.kt (3)
androidTestImplementation(25-27)testImplementation(29-31)testRuntimeOnly(33-35)
acon/build.gradle.kts (4)
acon/src/main/java/com/acon/acon/startup/BranchInitializer.kt (3)
create(7-16)create(8-11)dependencies(13-15)acon/src/main/java/com/acon/acon/startup/AconAmplitudeInitializer.kt (3)
create(7-15)create(8-10)dependencies(12-14)acon/src/main/java/com/acon/acon/startup/MobileAdsInitializer.kt (3)
create(7-15)create(8-10)dependencies(12-14)acon/src/main/java/com/acon/acon/startup/TimberInitializer.kt (3)
create(8-19)create(9-13)dependencies(15-17)
🔇 Additional comments (54)
build-logic/convention/src/main/kotlin/AndroidApplicationComposeConventionPlugin.kt (1)
21-24: BuildConfig 생성 활성화 반영 👍테스트/프로덕션 플래버를 구분하려면 BuildConfig 필드가 필요하니 이 설정 추가가 타당합니다. 다른 부작용도 없어 보입니다.
core/designsystem/src/main/res/values/strings.xml (1)
171-171: 문구 변경 문제 없음온보딩 정책 변경 의도와 일치하는 자연스러운 문구로 보입니다.
acon/proguard-rules.pro (1)
141-143: androidx 인터페이스와 온보딩 엔티티 보존 규칙 OK새 노출 정책에서 리플렉션·시리얼라이제이션 경로가 넓어졌는데, 이번 추가로 누락 위험 없이 동작하겠습니다.
feature/spot/src/main/java/com/acon/acon/feature/spot/screen/spotlist/SpotListViewModel.kt (3)
78-95: 온보딩 노출 조건 전환 확인 완료
shouldVerifyArea플래그를 직접 확인하도록 바뀌어 온보딩이 한 번 노출되면 재노출되지 않도록 하는 정책 의도를 정확히 반영하고 있습니다. 추가 조치는 필요 없어 보입니다.
354-356: 업로드 화면 네비게이션 사이드이펙트 연동 확인
onRegisterNewSpot()에서 새 사이드이펙트만 발행하는 간결한 형태로 잘 연결되었습니다. UI 계층에서 해당 사이드이펙트를 처리하기만 하면 되므로 명확합니다.
406-406: 사이드이펙트 선언 추가 적합
NavigateToUploadPlace데이터 오브젝트 추가로 뷰모델과 UI 간 계약이 명확해졌습니다. 네비게이션 목적에 맞는 확장입니다.acon/src/main/java/com/acon/acon/navigation/nested/AreaVerificationNavigation.kt (1)
38-40: 뒤로 가기 핸들러 연결 적절합니다
navigateUp을 그대로 넘겨서 스택 정리가 깔끔해지고, 호출 측에서 별도 분기 없이 재사용할 수 있게 된 점 좋아요.acon/src/main/java/com/acon/acon/navigation/nested/OnboardingNavigation.kt (1)
54-60: 에어리어 인증 플로우 진입선이 자연스럽습니다
navigateAndClear(AreaVerificationRoute.Graph)로 온보딩 스택 중복 없이 분기시켜 새 정책과도 정합성이 맞는 것 확인했습니다.acon/src/main/java/com/acon/acon/navigation/nested/SpotNavigation.kt (1)
60-62: 업로드 장소 진입 경로 추가 확인 완료Spot 리스트에서 곧장
UploadRoute.Place로 이동할 수 있어 탐색 동선이 한층 직관적입니다.domain/src/main/java/com/acon/acon/domain/repository/AconAppRepository.kt (1)
7-16: 도메인 업로드 API 추가 확인 완료
간결한 KDoc 덕분에 새 계약의 의도와 파라미터가 명확하게 드러나며, 도메인 계층에서 필요한 추상화가 잘 정리되었습니다.build-logic/convention/src/main/kotlin/utils/DependencyExtensions.kt (1)
29-34: testRuntimeOnly 확장 추가 👍
테스트 러ntime 의존성도 헬퍼로 정리되어 새 테스트 컨벤션 플러그인들과 잘 맞춰질 것 같습니다.feature/spot/build.gradle.kts (1)
17-19: 의존성 교체 확인 완료
Spot 모듈이 새core.ads모듈로 정렬되어 ads-api 제거 흐름과 일관됩니다.settings.gradle.kts (1)
24-48: 모듈 include 조정 잘 반영되었습니다
루트 프로젝트명과 include 목록이 새 구조(:acon,:core:ads)에 맞춰 정리되어 빌드 구성이 명확해졌습니다.build-logic/convention/build.gradle.kts (1)
64-71: 새 테스트 컨벤션 플러그인 등록 좋습니다
Feature/공통 테스트 세팅을 전용 플러그인으로 분리한 덕에 모듈별 스크립트가 한결 가벼워질 것 같습니다.domain/src/main/java/com/acon/acon/domain/repository/UserRepository.kt (1)
5-10: signIn 반환 타입 변경 확인
ExternalUUID로 반환을 단순화한 덕분에 이후 흐름이 명확해집니다. 관련 호출부도 동일 타입으로 정리되었는지만 한 번만 더 확인해 주세요.core/common/build.gradle.kts (1)
3-9: 공통 유닛 테스트 플러그인 적용 👍
플러그인 한 줄로 테스트 의존성을 주입할 수 있게 되어 스크립트 유지보수가 쉬워질 것 같습니다.domain/build.gradle.kts (1)
3-3: 공통 테스트 플러그인 적용 👍모듈별 중복 설정을 걷어내고 플러그인으로 통합한 덕분에 테스트 환경 유지보수가 훨씬 간결해졌습니다.
core/data/src/main/kotlin/com/acon/core/data/dto/response/SignInResponse.kt (1)
7-13: ExternalUUID 전환 반영 확인했습니다.응답 모델이 불필요한 도메인 의존성을 끊고 DTO 본연의 역할에 집중하게 되어 방향성에 동의합니다.
feature/onboarding/src/main/java/com/acon/feature/onboarding/dislikes/viewmodel/ChooseDislikesViewModel.kt (1)
21-22: 첫 진입 시 노출 플래그 초기화 확인했습니다.화면이 한 번이라도 뜨면 즉시 재노출을 막도록 플래그를 false로 내리는 접근이 요구사항과 잘 맞습니다.
core/data/src/main/kotlin/com/acon/core/data/serializer/OnboardingPreferencesSerializer.kt (1)
14-18: 기본값 갱신 확인했습니다.새 정책에 맞춰 기본값을 “노출 필요”로 두어 초기 온보딩이 보장되는 흐름이 명확합니다. 위에서 언급한 마이그레이션만 보완되면 완성될 것 같습니다.
core/data/src/main/kotlin/com/acon/core/data/datasource/local/OnboardingLocalDataSource.kt (1)
31-44: 로컬 업데이트 API 네이밍 정비 👍새 의미에 맞춰 메서드와 필드 명을 정리해둔 덕분에 이후 코드 리딩이 훨씬 수월하겠습니다.
core/model/src/main/java/com/acon/acon/core/model/model/user/ExternalUUID.kt (1)
1-6: ExternalUUID 래퍼 추가 확인했습니다.식별자를 타입으로 감싸 null·빈 문자열 체크를 강제할 수 있어 도메인 모델 안정성이 좋아졌습니다.
core/social/build.gradle.kts (1)
7-7: 공통 테스트 플러그인 적용 👍사회 모듈도 동일한 테스트 컨벤션을 따르게 되어 프로젝트 전반의 일관성이 올라갔습니다.
feature/spot/src/main/java/com/acon/acon/feature/spot/screen/spotlist/composable/SpotListScreen.kt (2)
77-79: 콜백 추가 OK — API 표면 확장 적절AreaVerification 네비게이션 및 신규 스팟 등록 콜백 추가는 명확하고 기본값도 안전합니다. 상위 컨테이너와의 연결만 유지되면 문제 없습니다.
200-202: 하위 컴포저블로의 콜백 전파 OKSpotListSuccessView로 onChooseNavigationAppModalDismiss, onRegisterNewSpotClick 전달 정상입니다. SpotEmptyView까지 전파되는 흐름과 일치합니다.
Also applies to: 232-234
feature/spot/src/main/java/com/acon/acon/feature/spot/screen/spotlist/composable/SpotListSuccessView.kt (4)
69-77: API 표면 변경 OK — 새 콜백(onRegisterNewSpotClick) 추가 합리적SignInStatus/Spot 단축 import와 콜백 추가 모두 일관성 있고 호출부와 호환됩니다.
95-106: BIKING 모드에서 리스트 대신 EmptyView 노출 — 기획 확인 필요자전거 모드에서 항상 SpotEmptyView를 노출하는 게 의도인지 확인 바랍니다. 조건부(결과 없음일 때만)로 노출해야 한다면 분기 보완이 필요합니다.
243-270: 게스트 노출 제한 분기 OK페이지 기준으로 게스트 제한을 적용하는 로직 일관적이며, SpotGuestItem/SpotItem 분기도 적절합니다.
272-281: 광고 컴포넌트 교체 OK — callToAction 터치 영역 위임 해결SpotListNativeAd로 교체하여 CTA 비응답 이슈를 해결하는 구성으로 보입니다. 스타일 래핑도 안전합니다.
feature/spot/src/main/java/com/acon/acon/feature/spot/screen/spotlist/composable/SpotEmptyView.kt (2)
40-45: API 표면 확장 OK — 신규 스팟 등록 콜백 추가 적절URI 기반 처리 제거하고 콜백으로 위임하는 방향이 더 유연합니다.
89-97: 게스트 제한 분기 OK인덱스/게스트 조건 일치하며, 잠금 항목 클릭 시 로그인 요청으로 연결도 적절합니다.
feature/spot/src/main/java/com/acon/acon/feature/spot/screen/spotlist/composable/SpotListScreenContainer.kt (3)
35-36: 업로드 네비게이션 콜백 추가 OK외부 네비게이션 지점(onNavigateToUploadPlace) 노출 및 기본값 안전합니다.
67-68: onSpotClick 위임 단순화 OK게스트 가드 로직을 뷰모델로 위임하여 화면단 복잡도 감소. 일관성 좋습니다.
110-112: 사이드이펙트 처리 OKNavigateToUploadPlace → 외부 콜백 호출 정상 동작합니다.
acon/src/main/java/com/acon/acon/MainActivity.kt (7)
51-51: 온보딩 경로 임포트가 적절하게 추가되었습니다.새로운 온보딩 플로우 구현을 위한 필수 임포트가 정확히 추가되었습니다.
61-61: LocalSnackbarHostState 임포트가 올바르게 추가되었습니다.스낵바 상태 관리를 위한 임포트가 적절히 추가되었습니다.
63-63: OnboardingRepository 의존성 주입이 올바르게 추가되었습니다.온보딩 기본 설정 관리를 위한 의존성이 적절하게 주입되었습니다.
127-130: QA 빌드에서 앱 업데이트 매니저 비활성화 로직이 적절합니다.테스트 환경에서는 인앱 업데이트를 비활성화하여 테스트 플로우를 방해하지 않도록 하는 합리적인 접근입니다. null 안전성도 적절히 고려되었습니다.
158-158: 앱 업데이트 매니저의 null 안전성 처리가 잘 구현되었습니다.QA 빌드에서 appUpdateManager가 null일 수 있는 상황을 모든 사용 지점에서 안전하게 처리하고 있습니다. 특히 listener 등록/해제 및 업데이트 완료 호출에서 안전 연산자를 적절히 사용했습니다.
Also applies to: 165-165, 173-175, 486-486, 488-488
351-372: 온보딩 플로우 네비게이션 로직이 잘 구현되었습니다.사인인 성공 후 온보딩 상태에 따른 조건부 네비게이션이 명확하게 구현되었으며, 실패 시 기본값으로 SpotList로 이동하는 방어적 코드도 포함되어 있습니다. 각 온보딩 단계에 대한 분기 처리가 논리적으로 구성되었습니다.
358-358: 사용자 ID 설정 로직이 올바르게 변경되었습니다.
verificationStatus.externalUUID에서externalUUID.value로 변경된 것이 타입 변경사항과 일치합니다.core/data/src/test/java/com/acon/core/data/repository/ProfileRepositoryTest.kt (1)
51-68: 의존성 주입 정렬 확인 완료테스트가 실제 구현과 동일하게
AconAppRepository를 주입하도록 정리된 덕분에, 업로드 경로 관련 회귀 위험이 줄었습니다.acon/build.gradle.kts (1)
16-55: QA/Production 플래버 분리 적용 잘 봤습니다
productionDebug비활성화까지 포함된 구성이 목적에 맞게 동작할 것으로 보입니다.core/data/src/main/kotlin/com/acon/core/data/datasource/remote/AconAppRemoteDataSource.kt (1)
3-26: 프리사인드 URL을 인증 채널로 돌린 판단 👍Presigned URL 요청이 이제 인증 Retrofit 경로를 타게 되어 접근 제어가 분명해졌습니다.
feature/profile/build.gradle.kts (1)
11-39: 공통 테스트 플러그인 적용 확인모듈마다 중복 선언하던 테스트 의존성을 플러그인으로 통합해 유지보수가 쉬워졌네요.
core/model/src/main/java/com/acon/acon/core/model/model/OnboardingPreferences.kt (1)
7-9: 기존 저장 데이터 역매핑 확인 부탁드립니다
hasTastePreference/hasVerifiedArea가shouldChooseDislikes/shouldVerifyArea로 의미가 뒤집힌 상태라, 동일한 필드 번호를 그대로 사용한다면 예전에true로 저장됐던 사용자는 “다시 진행해야 한다”로 해석되어 온보딩이 재노출됩니다. Serializer나 마이그레이션 단계에서 값을 반전하거나, 한 번이라도 노출된 사용자에 대해 false로 초기화해 주셨는지 확인 부탁드려요.build-logic/convention/src/main/kotlin/test/CommonUnitTestConventionPlugin.kt (1)
17-30: 공통 JUnit5 설정 플러그인 👍테스트 번들 캡슐화와
useJUnitPlatform()기본 적용으로 모듈별 설정을 단순화할 수 있게 됐네요.core/data/src/main/kotlin/com/acon/core/data/api/remote/auth/AconAppApi.kt (1)
8-13: Auth API 정의 추가 확인프리사인드 URL 엔드포인트가 전용 인터페이스로 분리돼 DI 구성이 명확해졌습니다.
gradle/libs.versions.toml (1)
260-281: 새 테스트 번들 등록 확인
acon-feature-test/acon-common-unit-test플러그인용 번들이 잘 추가돼서 컨벤션 플러그인이 기대대로 동작할 환경이 마련됐습니다.feature/onboarding/src/main/java/com/acon/feature/onboarding/introduce/composable/IntroduceScreenContainer.kt (1)
52-57: 사이드이펙트-콜백 매핑이 명확합니다.세 가지 내비게이션 분기를 개별 콜백으로 끊어 준 덕분에 컨테이너가 훨씬 읽기 쉬워졌습니다.
feature/onboarding/src/main/java/com/acon/feature/onboarding/area/viewmodel/AreaVerificationViewModel.kt (1)
20-26: 초기화 시 노출 플래그를 바로 갱신한 결정이 인상적입니다.화면 진입 순간 updateShouldVerifyArea(false)를 호출해 “한 번 본 뒤에는 다시 노출하지 않는다”는 정책이 확실히 적용되도록 한 점이 요구사항과 잘 맞습니다.
build-logic/convention/src/main/kotlin/test/FeatureTestConventionPlugin.kt (1)
31-42: 테스트 번들 구성 깔끔합니다
Feature 모듈에서 자주 쓰는 테스트 의존성을 번들로 묶어 플러그인 한 곳에서 주입하는 접근 덕분에 새 모듈 온보딩이 훨씬 수월해지겠네요. 👍domain/src/main/java/com/acon/acon/domain/repository/OnboardingRepository.kt (1)
17-18: 도메인 표현 정합성이 좋아졌어요
should접두어로 바꾸면서 실제로 UI가 따라야 할 플래그 의미가 훨씬 명확해졌네요.core/data/src/main/kotlin/com/acon/core/data/repository/AconAppRepositoryImpl.kt (1)
44-61: IO 디스패처 분리 덕분에 업로드 경로가 안정적이네요
프리사인드 URL 요청과 파일 바디 준비를 IO 컨텍스트에서 동시에 처리하게 바꿔서 업로드 경로 지연이 줄어들겠어요. 이런 정리가 있었으면 좋겠다고 생각하고 있었는데 바로 반영해주셔서 감사합니다. 🙌
core/data/src/main/kotlin/com/acon/core/data/repository/OnboardingRepositoryImpl.kt
Show resolved
Hide resolved
core/data/src/main/kotlin/com/acon/core/data/repository/UserRepositoryImpl.kt
Show resolved
Hide resolved
...boarding/src/main/java/com/acon/feature/onboarding/introduce/viewmodel/IntroduceViewModel.kt
Show resolved
Hide resolved
feature/signin/src/main/java/com/acon/acon/feature/signin/screen/component/SignInTopBar.kt
Show resolved
Hide resolved
feature/signin/src/main/java/com/acon/acon/feature/signin/screen/SignInViewModel.kt
Show resolved
Hide resolved
...src/main/java/com/acon/acon/feature/spot/screen/spotdetail/composable/SpotDetailViewModel.kt
Show resolved
Hide resolved
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.
Actionable comments posted: 0
🧹 Nitpick comments (3)
feature/spot/src/main/java/com/acon/acon/feature/spot/screen/spotdetail/composable/SpotDetailScreenContainer.kt (3)
29-30: 옵션 A 채택 시 불필요한 CompositionLocal 참조 제거화면에서만 게이트하도록 단일화한다면, 이 로컬 값들은 사용되지 않으므로 삭제하세요.
- val signInStatus = LocalSignInStatus.current - val onRequestSignIn = LocalRequestSignIn.current
10-16: 옵션 A 채택 시 관련 import 정리컨테이너에서 게이트 제거 시 아래 import들도 불필요합니다.
-import com.acon.acon.core.model.type.SignInStatus -import com.acon.acon.core.ui.compose.LocalRequestSignIn -import com.acon.acon.core.ui.compose.LocalSignInStatus
38-44: 게스트 게이팅 중복 제거
SpotDetailScreen에서 이미 LocalSignInStatus와 LocalRequestSignIn을 사용해 게스트 분기를 처리하고 있으므로, SpotDetailScreenContainer의 onClickBookmark 내부 if-else를 제거하고onClickBookmark = viewModel::toggleBookmark로 단순화하세요. [SpotDetailScreenContainer.kt Lines 38–44]
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
feature/spot/src/main/java/com/acon/acon/feature/spot/screen/spotdetail/composable/SpotDetailScreenContainer.kt(2 hunks)
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: ThirFir
PR: AconInc/ACON-Android#261
File: core/ads/src/main/java/com/acon/core/ads/SpotListAdProvider.kt:118-120
Timestamp: 2025-09-25T05:00:33.876Z
Learning: ThirFir validates implementations through actual testing and prefers test-proven solutions over theoretical concerns, especially when the theoretical issue doesn't manifest in real-world usage.
Learnt from: ThirFir
PR: AconInc/ACON-Android#255
File: core/data/src/main/kotlin/com/acon/core/data/datasource/remote/AconAppRemoteDataSource.kt:12-14
Timestamp: 2025-09-19T07:16:52.945Z
Learning: ThirFir prefers pragmatic engineering decisions over strict architectural separation when the practical benefits don't justify the implementation complexity, such as keeping FileUploadApi with the same client configuration rather than creating separate upload-specific clients.
Learnt from: ThirFir
PR: AconInc/ACON-Android#256
File: feature/profile/src/main/java/com/acon/feature/profile/update/composable/ProfileUpdateScreen.kt:86-99
Timestamp: 2025-09-20T06:53:43.571Z
Learning: ThirFir prefers practical engineering decisions over theoretical best practices when the actual risk is minimal, such as questioning the need for remember keys when the captured values are demonstrably stable.
Learnt from: ThirFir
PR: AconInc/ACON-Android#261
File: feature/signin/src/main/java/com/acon/acon/feature/signin/screen/component/SignInTopBar.kt:34-34
Timestamp: 2025-09-25T04:33:13.690Z
Learning: ThirFir makes intentional design decisions to de-emphasize UI elements (like making sign-in topbar text less visible with Gray500 instead of White) for user experience reasons, preferring design intent over strict accessibility compliance when there's clear reasoning behind the choice.
Learnt from: ThirFir
PR: AconInc/ACON-Android#261
File: core/data/src/main/kotlin/com/acon/core/data/repository/OnboardingRepositoryImpl.kt:65-69
Timestamp: 2025-09-25T04:29:03.128Z
Learning: ThirFir considers user impact when making technical decisions - when there are no existing users affected, they prefer to skip complex migration logic rather than over-engineering for theoretical scenarios.
Learnt from: ThirFir
PR: AconInc/ACON-Android#255
File: feature/profile/src/main/java/com/acon/feature/profile/update/viewmodel/ProfileUpdateViewModel.kt:38-57
Timestamp: 2025-09-19T07:40:37.778Z
Learning: ThirFir prefers pragmatic engineering solutions that balance user experience with implementation effort, such as choosing snackbar + retry button over more complex custom retry UI when design resources are limited.
📚 Learning: 2025-09-19T04:57:25.197Z
Learnt from: ThirFir
PR: AconInc/ACON-Android#254
File: core/ui/src/main/java/com/acon/acon/core/ui/base/BaseContainerHost.kt:50-57
Timestamp: 2025-09-19T04:57:25.197Z
Learning: BaseContainerHost에서 useSignInStatus() 메서드의 패턴: 로컬 변수 signInStatus (LocalSignInStatus.current에서 가져옴)와 필드 _signInStatus (MutableStateFlow)는 서로 다른 스코프에 있어서, _signInStatus.value를 설정해도 로컬 signInStatus 변수에 영향을 주지 않는다. 이는 Composition Local과 ViewModel 상태 동기화의 표준 패턴이다.
Applied to files:
feature/spot/src/main/java/com/acon/acon/feature/spot/screen/spotdetail/composable/SpotDetailScreenContainer.kt
📚 Learning: 2025-09-19T05:01:52.778Z
Learnt from: ThirFir
PR: AconInc/ACON-Android#254
File: feature/spot/src/main/java/com/acon/acon/feature/spot/screen/spotlist/composable/SpotListScreenContainer.kt:91-94
Timestamp: 2025-09-19T05:01:52.778Z
Learning: ViewModel의 useSignInStatus(), requestLocationPermission(), useLiveLocation() 등과 같은 메서드들은 Composable 함수로서, LaunchedEffect 내부에서 호출할 수 없으며 Composable 스코프에서 직접 호출해야 한다. 이들은 내부적으로 이미 적절한 생명주기 관리를 하고 있다.
Applied to files:
feature/spot/src/main/java/com/acon/acon/feature/spot/screen/spotdetail/composable/SpotDetailScreenContainer.kt
📚 Learning: 2025-09-19T04:58:10.590Z
Learnt from: ThirFir
PR: AconInc/ACON-Android#254
File: feature/profile/src/main/java/com/acon/feature/profile/info/composable/ProfileInfoScreenContainer.kt:25-27
Timestamp: 2025-09-19T04:58:10.590Z
Learning: initOnRequestSignIn()과 같은 Composable 함수는 LaunchedEffect 내부에서 호출할 수 없으며, Composable 스코프에서 직접 호출해야 한다.
Applied to files:
feature/spot/src/main/java/com/acon/acon/feature/spot/screen/spotdetail/composable/SpotDetailScreenContainer.kt
🧬 Code graph analysis (1)
feature/spot/src/main/java/com/acon/acon/feature/spot/screen/spotdetail/composable/SpotDetailScreenContainer.kt (2)
feature/spot/src/main/java/com/acon/acon/feature/spot/screen/spotdetail/composable/SpotDetailScreen.kt (1)
SpotDetailScreen(70-496)feature/profile/src/main/java/com/acon/feature/profile/info/viewmodel/ProfileInfoViewModel.kt (1)
onRequestSignIn(115-117)
fix: 장소 등록 presignedUrlAPI 변경 사항 적용
💻 Work Description
Summary by CodeRabbit
신규 기능
변경 사항
잡무(Chores)