Skip to content

Conversation

@ThirFir
Copy link
Collaborator

@ThirFir ThirFir commented Sep 25, 2025

💻 Work Description

  • app -> acon 모듈명 변경
  • Test / Production 분리 Build Flavor 추가
    • Test 빌드에서 인앱업데이트 API 에러 처리
  • 온보딩 노출 정책 변경 - 1회 노출 시에는 반드시 이후에는 다시 보이지 않음
  • 광고 callToAction 무반응 수정
  • 기타 QA 수정

Summary by CodeRabbit

  • 신규 기능

    • 온보딩 흐름 확장: 소개 → 지역 인증 → 취향 선택의 세분화된 이동 지원 및 뒤로가기 추가
    • 스팟 리스트에 “장소 직접 등록하기” 진입 경로 및 업로드 화면 이동 추가
    • 이미지 업로드: 프리사인드 URL 기반 업로드 통합
  • 변경 사항

    • 로그인 이후 이동 로직을 온보딩 상태(ExternalUUID 기준)로 정교화
    • 스팟 리스트의 게스트/사용자 분기 및 빈 상태 동작 개선
    • 광고 UI·권한 및 상단바 색상(White → Gray500) 조정
    • 문자열 변경: “장소 등록 신청하기” → “장소 직접 등록하기”
  • 잡무(Chores)

    • 테스트 설정/종속성 정리 및 테스트 플러그인 도입
    • 프로젝트 모듈 및 네임스페이스 정리, 일부 불필요한 테스트 제거

ThirFir and others added 22 commits September 20, 2025 18:32
서비스 온보딩 시작 버튼 클릭 -> 서비스 온보딩 첫 페이지 시작으로 시점 변경
- 서비스 소개 -> 지역인증 -> 취향 탐색
- 모든 단계는 최초 1회 노출 후, 여부 상관없이 재접속 후에는 노출 X
@ThirFir ThirFir self-assigned this Sep 25, 2025
@coderabbitai
Copy link

coderabbitai bot commented Sep 25, 2025

Walkthrough

앱/모듈 재구성, 광고 API 제거 및 광고 구현 이동, 온보딩 네비게이션·뷰모델 확장(뒤로가기 포함), sign-in 반환 타입을 ExternalUUID로 변경, 프리사인드 URL 인증 API 분리 및 업로드 병렬화, 테스트 컨벤션 플러그인 추가 및 테스트 의존성 정리, AppUpdateHandler를 nullable로 처리.

Changes

Cohort / File(s) Summary
프로젝트/모듈 구성 변경
settings.gradle.kts, acon/build.gradle.kts, core/ads-api/*, core/ads/*, provider/ads-impl/*, feature/spot/build.gradle.kts
루트명/모듈 include 변경(:app:acon, :core:ads-api:core:ads, provider 모듈 제외), distributionChannel flavor 추가, 네임스페이스·의존성 정리(projects.core.adsApiprojects.core.ads), .gitignore/proguard 파일 정리.
광고 API 제거/대체
core/ads-api/src/main/java/.../AdProvider.kt, core/ads/src/main/java/com/acon/core/ads/SpotListAdProvider.kt, feature/spot/.../SpotListSuccessView.kt, core/ads/src/main/AndroidManifest.xml
공용 AdProvider 인터페이스 및 CompositionLocal 삭제, 광고 구현 패키지·네임스페이스 이동, CTA 트리거 뷰 추가, INTERNET 권한 제거.
온보딩 네비게이션 확장
acon/src/main/java/com/acon/acon/MainActivity.kt, acon/.../navigation/nested/OnboardingNavigation.kt, acon/.../navigation/nested/AreaVerificationNavigation.kt
사인인 후 온보딩 기반 분기(Introduce/ChooseDislikes/AreaVerification/SpotList) 도입 및 AreaVerification에 뒤로가기 콜백 추가.
온보딩 화면·뷰모델 변경
feature/onboarding/.../Introduce*, feature/onboarding/.../area/*, feature/onboarding/.../dislikes/*
IntroduceViewModel에 UserRepository 주입 및 네비게이션 재구성, AreaVerification에 onBack/side-effect NavigateBack 추가, ChooseDislikes 초기화에서 shouldChooseDislikes(false) 호출 등 흐름 변경.
모델·도메인 API 변경
core/model/.../OnboardingPreferences.kt, core/model/.../user/ExternalUUID.kt, core/model/.../user/VerificationStatus.kt(삭제), domain/.../OnboardingRepository.kt, domain/.../UserRepository.kt, domain/.../AconAppRepository.kt
온보딩 필드명 변경(has→should), VerificationStatus 제거, ExternalUUID 값타입 추가, OnboardingRepository API명 변경, UserRepository.signIn 반환 타입이 ExternalUUID로 변경, AconAppRepository에 uploadImage 추가.
프리사인드 URL · 업로드 리팩터링
core/data/.../api/remote/auth/AconAppApi.kt, core/data/.../noauth/AconAppNoAuthApi.kt, core/data/.../AconAppRemoteDataSource.kt, core/data/.../di/ApiModule.kt, core/data/.../repository/AconAppRepositoryImpl.kt
Presigned URL API를 Auth 전용으로 분리·추가, DI에 Auth/NoAuth 제공자 추가, Repository에 IO 디스패처 주입 및 presigned URL 획득과 RequestBody 생성의 async 병렬화로 업로드 수행.
사용자/프로필 리포지토리 변경
core/data/.../UserRepositoryImpl.kt, core/data/.../OnboardingRepositoryImpl.kt
signIn 반환 타입/처리 변경(ExternalUUID), 온보딩 상태 업데이트 호출명 변경(updateShould*), 비동기 업데이트 흐름 재구성 및 clearCache 호출 추가.
스팟·화면 연결 변경
feature/spot/.../SpotList*, SpotDetail*, SpotEmptyView.kt, SpotListSuccessView.kt, SpotListScreenContainer.kt, SpotNavigation.kt
온보딩 조건을 shouldVerifyArea로 전환, 신규 장소 등록(onRegisterNewSpot) 콜백·사이드이펙트 및 업로드 네비게이션(onNavigateToUploadPlace) 연동, SpotDetail 북마크에 sign-in guard 추가.
앱 업데이트 핸들러
acon/src/main/java/.../update/AppUpdateHandler.kt, acon/src/main/java/com/acon/acon/MainActivity.kt
AppUpdateManager를 nullable로 변경하고 안전 호출로 리스너/플로우 제어.
빌드로직 · 테스트 컨벤션
build-logic/convention/build.gradle.kts, build-logic/convention/src/main/kotlin/test/*, build-logic/convention/src/main/kotlin/utils/DependencyExtensions.kt, gradle/libs.versions.toml, 여러 모듈의 build.gradle.kts
featureTest·commonUnitTest convention 플러그인 등록 및 구현 추가, testRuntimeOnly 확장 추가, 라이브러리/번들 항목 추가, 모듈 테스트 의존성 제거·컨벤션 적용.
프로가드·리소스·테스트 정리
acon/proguard-rules.pro, core/designsystem/src/main/res/values/strings.xml, **/androidTest/**/ExampleInstrumentedTest.kt(삭제), provider/ads-impl/proguard-rules.pro
androidx keep 규칙 조정 및 OnboardingPreferencesEntity 명시 keep 추가, 문자열 리소스 변경, 예제 인스트루먼트 테스트 파일 삭제, 불필요 ProGuard 파일 제거.
CI/workflow 변경
.github/workflows/debug_build_ci.yml
google-services.json 비밀 파일의 대상 경로를 ./acon/google-services.json으로 변경.

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
Loading
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)
Loading
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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

Suggested reviewers

  • 1971123-seongmin

Poem

새 길 앞에서 귀가 쫑긋, hop-hop 나는 토끼
소개→인증→취향, 갈래길 지도를 펼치지
외부 UUID 품에 안고, 광고 숲도 정리했지
프리사인드 바람 타고, 이미지가 훨씬 빨리지
QA 굴·PROD 굴로 폴짝—머지 초콜릿 하나! 🥕✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Title Check ✅ Passed 제목 “[Feat] 온보딩 노출 정책 변경, Build Flavor”은 PR의 주요 변경 사항인 온보딩 노출 정책 변경과 테스트·프로덕션 빌드 플래버 추가를 명확하게 요약하고 있어 간결하며 관련성이 높습니다.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/relax-guest-policy

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@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: 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.apply
feature/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

📥 Commits

Reviewing files that changed from the base of the PR and between 8cd1b43 and 9168fcf.

⛔ Files ignored due to path filters (1)
  • acon/src/main/ic_launcher-playstore.png is 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.kt
  • acon/src/main/java/com/acon/acon/navigation/nested/SpotNavigation.kt
  • feature/spot/src/main/java/com/acon/acon/feature/spot/screen/spotlist/composable/SpotListScreenContainer.kt
  • acon/src/main/java/com/acon/acon/navigation/nested/OnboardingNavigation.kt
  • feature/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.kt
  • core/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: 하위 컴포저블로의 콜백 전파 OK

SpotListSuccessView로 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: 사이드이펙트 처리 OK

NavigateToUploadPlace → 외부 콜백 호출 정상 동작합니다.

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/hasVerifiedAreashouldChooseDislikes/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 컨텍스트에서 동시에 처리하게 바꿔서 업로드 경로 지연이 줄어들겠어요. 이런 정리가 있었으면 좋겠다고 생각하고 있었는데 바로 반영해주셔서 감사합니다. 🙌

Copy link

@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: 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

📥 Commits

Reviewing files that changed from the base of the PR and between 9168fcf and 46e80f8.

📒 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)

@ThirFir ThirFir merged commit fd2706a into develop Oct 1, 2025
2 checks passed
@ThirFir ThirFir deleted the feat/relax-guest-policy branch October 1, 2025 01:20
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.

3 participants