Skip to content

[refactor] #51 네컷지도 기능 및 UI 고도화#53

Merged
Ojongseok merged 37 commits into
developfrom
refactor/#51-map
Jan 25, 2026
Merged

[refactor] #51 네컷지도 기능 및 UI 고도화#53
Ojongseok merged 37 commits into
developfrom
refactor/#51-map

Conversation

@Ojongseok
Copy link
Copy Markdown
Member

@Ojongseok Ojongseok commented Jan 24, 2026

🔗 관련 이슈

📙 작업 설명

  • 네컷지도 하단 패널 드래그 레벨별 높이 조절 로직 개선
  • '현 위치에서 탐색' 버튼 컴포넌트 추가 및 UI 로직 구성
  • 위치 권한 확인 로직 추가 (화면 진입 시, 현위치 버튼 클릭 시, 길찾기 버튼 클릭 시)
  • 권한 관련 공통 로직 작성용 PermissionManager.kt 정의
  • DataStore Key 관리 경로 변경 및 Boolean 타입 저장 인터페이스 추가
  • 현위치 기반 길찾기 앱(네이버, 카카오, 구글) 이동 로직 개선 -> 근데 아직 구글이 조금 이상

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

기능 미리보기 기능 미리보기
하단 패널
KakaoTalk_Video_2026-01-25-02-17-59.mp4
위치 권한
KakaoTalk_Video_2026-01-25-02-23-21.mp4
카카오,네이버 길찾기
KakaoTalk_Video_2026-01-25-02-20-02.mp4
구글 길찾기
KakaoTalk_Video_2026-01-25-02-23-41.mp4

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

  • PermissionManager를 정의해 권한 관련 공통 로직을 분리했습니다.
  • 위치 권한 요청 시 최초 요청 / 1회 거부 / 2회 이상 거부(영구 거부) 상태를 구분하여 처리합니다.
    • 최초 / 1회 거부: 시스템 권한 요청 팝업 노출
    • 2회 이상 거부: 앱 설정 이동 다이얼로그 표시
  • 네컷지도 하단 패널의 드래그 상태별 높이, 높이 계산 로직을 단순화했습니다.
    • 피그마에 명시된 96.dp, 218.dp로 DragLevel.FIRST, DragLevel.SECOND 상태의 높이 변경(THIRD는 화면의 90%)
  • DataStoreKey를 core:data-api 모듈로 이동시켰습니다.

Summary by CodeRabbit

  • 새로운 기능

    • 지도: 위치 권한 흐름, 권한 안내 다이얼로그 및 앱 설정 이동 지원
    • 지도 UI: 현위치 탐색 칩, 닫기 버튼, 회전 아이콘, PhotoBooth 관련 컴포넌트 추가/명칭 변경
    • 저장소/토큰: TokenRepository 도입 및 토큰 암호화 저장 구현, DataStore 제공자 분리
  • 버그 수정

    • 인증 토큰 저장·갱신 흐름 안정화 및 리프레시 처리 개선
  • 스타일

    • 하단 내비게이션 바 세로 여백 축소 및 탭 라벨 폰트 조정; 그림자 스타일(pinShadow) 추가
  • 리팩터

    • 지도 상태/이벤트 정비(DragLevel 도입), 내부 API 정리 및 컴포넌트 구조 개선

✏️ Tip: You can customize this high-level summary in your review settings.

@Ojongseok Ojongseok requested a review from ikseong00 January 24, 2026 17:13
@Ojongseok Ojongseok self-assigned this Jan 24, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jan 24, 2026

Walkthrough

지도 화면을 DragLevel 기반으로 재구성하고 위치 권한 요청/다이얼로그/앱 설정 흐름과 현위치 탐색 칩(MapRefreshChip)을 추가. 토큰 전용 DataStore/리포지토리(TokenRepository/TokenDataStore)를 분리하고 기존 DataStoreRepository는 boolean API로 축소. Brand 명칭을 PhotoBooth로 리네임 및 UI 컴포넌트 보완.

Changes

Cohort / File(s) 변경 요약
Map 상태·UI·플로우
feature/map/impl/src/main/java/.../MapContract.kt, feature/map/impl/src/main/java/.../MapViewModel.kt, feature/map/impl/src/main/java/.../MapScreen.kt
MapState/Intent/Effect에 dragLevel 및 위치권한 관련 이벤트·효과·현위치 갱신·새로고침 추가. ViewModel에 권한 흐름·핸들러 분리. Screen에서 권한 런처, 다이얼로그, 앱설정 네비게이션 및 현위치 기반 액션 처리 추가.
드래그 패널·상수
feature/map/impl/src/main/java/.../component/AnchoredDraggablePanel.kt, feature/map/impl/src/main/java/.../const/MapConst.kt
DragValue → DragLevel 전환, 패널 레벨별 높이 상수(PANEL_DRAG_*) 추가, BOTTOM_NAVIGATION_BAR_HEIGHT 72→52, AnchoredDraggablePanel 시그니처·내부 로직·앙커·애니메이션 재설계.
UI 컴포넌트·리소스
feature/map/impl/src/main/java/.../component/MapRefreshChip.kt, feature/map/impl/src/main/java/.../component/ToMapChip.kt, feature/map/impl/src/main/java/.../component/CloseButton.kt, feature/map/impl/src/main/java/.../component/PhotoBoothMarker.kt, feature/map/impl/src/main/java/.../component/PhotoBoothCard.kt, feature/map/impl/src/main/java/.../component/PhotoBoothDetailCard.kt, core/designsystem/src/main/res/drawable/icon_rotation.xml, app/src/main/java/.../BottomNavigationBar.kt
현위치 탐색 칩(MapRefreshChip)·CloseButton 추가, ToMapChip에 섀도우 적용, Brand→PhotoBooth 컴포넌트 및 프리뷰명 리네이밍, 회전 아이콘 리소스 추가, BottomNavigationBar의 패딩·라벨 텍스트 스타일 소폭 조정.
토큰·DataStore 분리 및 리포지토리
core/data-api/src/main/java/.../repository/TokenRepository.kt, core/data/src/main/java/.../repository/impl/TokenRepositoryImpl.kt, core/data/src/main/java/.../local/di/DataStoreModule.kt, core/data/src/main/java/.../local/di/DataStoreQualifier.kt, core/data/src/main/java/.../repository/di/RepositoryModule.kt, core/data/src/main/java/.../repository/impl/DataStoreRepositoryImpl.kt, core/data-api/src/main/java/.../datastore/DataStoreKey.kt, core/data-api/src/main/java/.../repository/DataStoreRepository.kt
TokenRepository 인터페이스·구현(TokenRepositoryImpl) 추가로 토큰 전용 DataStore(@TokenDataStore) 분리. 기존 DataStoreRepositoryImpl은 boolean API(setBoolean/getBoolean)로 축소. DataStoreKey 추가 및 DI 바인딩 추가.
네트워크·인증 사용점
core/data/src/main/java/.../remote/di/NetworkModule.kt, feature/auth/impl/src/main/kotlin/.../LoginViewModel.kt
네트워크와 로그인에서 DataStoreRepository → TokenRepository 교체. 토큰 조회·저장 및 provideHttpClient 파라미터 타입 변경.
권한 유틸리티·앱설정 네비게이션 및 빌드 설정
core/common/build.gradle.kts, core/common/src/main/java/.../permission/LocationPermissionManager.kt, core/common/src/main/java/.../permission/PermissionUtils.kt
androidx.core.ktx 의존성 추가. LocationPermissionManager(권한 상수·확인·rationale) 및 앱설정 이동 유틸(navigateToAppSettings) 추가.
경량 UI 조정
app/src/main/java/com/neki/android/app/ui/BottomNavigationBar.kt
BottomNavigationBar의 수직 패딩(8.dp→4.dp) 및 레이블 텍스트 스타일(caption11SemiBold→caption12SemiBold) 소폭 조정.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant MapScreen
    participant MapViewModel
    participant DataStore
    participant LocationPermissionManager
    participant System as "시스템(권한/설정)"

    User->>MapScreen: 현 위치 기반 액션 요청 (탐색/길찾기)
    MapScreen->>LocationPermissionManager: 권한 보유 여부 확인
    alt 권한 없음
        LocationPermissionManager-->>MapScreen: false
        MapScreen->>MapViewModel: RequestLocationPermission 인텐트
        MapViewModel->>DataStore: getBoolean(DataStoreKey.IS_FIRST_LOCATION_PERMISSION)
        DataStore-->>MapViewModel: Boolean(초회 여부)
        alt 초회 요청
            MapViewModel-->>MapScreen: ShowLocationPermissionDialog 효과
            MapScreen->>User: 권한 설명 다이얼로그 표시
            User->>MapScreen: 동의/거부 선택
            MapScreen->>MapViewModel: ConfirmLocationPermissionDialog / DismissLocationPermissionDialog
        else 재요청(이미 거부)
            MapViewModel-->>MapScreen: RequestLocationPermission 효과
        end
        MapScreen->>System: 권한 런처 실행
        System-->>MapScreen: 권한 결과 콜백
        MapScreen->>MapViewModel: 권한 결과 전달
        MapViewModel->>DataStore: setBoolean(DataStoreKey.IS_FIRST_LOCATION_PERMISSION, false)
    else 권한 있음
        LocationPermissionManager-->>MapScreen: true
        MapScreen->>MapViewModel: UpdateCurrentLocation or ClickDirection
    end
    MapViewModel-->>MapScreen: UI 업데이트 / MoveCamera / Navigate 효과
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 지도가 폴짝, 레벨로 춤추네
권한 묻고 길을 찬찬히 찾아주며
토큰은 따로 숨겨 안전히 보관하고
칩을 눌러 현위치로 훌쩍 출발해요 🎉

🚥 Pre-merge checks | ✅ 3 | ❌ 2
❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning 일부 변경사항이 #51 이슈 범위를 벗어났습니다: DataStore 구조 변경, TokenRepository 새 인터페이스/구현 추가, 하단 네비게이션 높이 변경(72→52), UI 스타일 미세 조정이 주요 목표와 직접적인 관련이 제한적입니다. DataStore 모듈 분리, TokenRepository 도입, 네비게이션 높이 변경 등 부수적 변경사항들을 별도 이슈로 분리하거나 명확한 연관성을 문서화하시기 바랍니다.
Docstring Coverage ⚠️ Warning Docstring coverage is 3.70% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목이 주요 변경사항인 네컷지도 기능 및 UI 고도화를 명확하게 요약하고 있으며, 전체 변경사항을 잘 대표하고 있습니다.
Linked Issues check ✅ Passed PR이 #51 이슈의 모든 코딩 관련 요구사항을 충족했습니다: 하단 시트 높이 수정, 현 위치 탐색 기능, 길찾기 기능 개선, 위치 권한 플로우 구현이 모두 적용되었습니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

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.

❤️ Share

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

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🤖 Fix all issues with AI agents
In
`@core/common/src/main/java/com/neki/android/core/common/permission/PermissionManager.kt`:
- Around line 33-37: The navigateToAppSettings function can crash when called
with a non-Activity Context because startActivity requires
FLAG_ACTIVITY_NEW_TASK outside an Activity; modify the Intent created in
navigateToAppSettings to include Intent.FLAG_ACTIVITY_NEW_TASK (or conditionally
add the flag when context is not an Activity) before calling
context.startActivity(intent) so the settings screen launches safely from any
Context.

In
`@core/data/src/main/java/com/neki/android/core/data/repository/impl/DataStoreRepositoryImpl.kt`:
- Around line 48-50: The clearTokens() implementation currently calls
dataStore.edit { it.clear() } which wipes all stored prefs (including
IS_FIRST_LOCATION_PERMISSION); change it to only remove the auth token entries
instead. Inside DataStoreRepositoryImpl.clearTokens(), call dataStore.edit {
prefs -> prefs.remove(ACCESS_TOKEN_KEY); prefs.remove(REFRESH_TOKEN_KEY) } (use
the actual token preference keys/constants defined in your class or file) and do
not call prefs.clear(), so flags like IS_FIRST_LOCATION_PERMISSION remain
intact.

In
`@feature/map/impl/src/main/java/com/neki/android/feature/map/impl/component/AnchoredDraggablePanel.kt`:
- Around line 92-111: The AnchoredDraggableState and its anchors are captured in
a keyless remember block so they won't update when screenHeightPx or panel
heights change (e.g., on rotation); change the remember to use explicit keys so
the state and anchors are recomputed when inputs change (e.g.,
remember(screenHeightPx, bottomPanelHeightPx, centerPanelHeightPx, density) {
... }). Recompute the DraggableAnchors and AnchoredDraggableState inside that
keyed remember so values used by DragLevel.FIRST/SECOND/THIRD/INVISIBLE and
positional/velocity/decay calculations are refreshed when configuration or
density changes.

In
`@feature/map/impl/src/main/java/com/neki/android/feature/map/impl/MapScreen.kt`:
- Around line 61-62: The code force-unwraps LocalActivity.current (val activity
= LocalActivity.current!!) which crashes in previews or non-Activity contexts;
change to a nullable activity (val activity = LocalActivity.current) and update
all uses (e.g., permission rationale checks and functions referenced around
MapScreen, calls at the spots previously at lines ~174-175, 229-233, 263-268,
272-276) to null-safe operations: guard early (if (activity == null) { /* no-op
preview-safe path or fallback UI */ }) or use activity?.let { ... } and compute
rationale/permission logic only when activity is non-null, providing fallback
behavior for preview rendering to prevent NPEs.

In
`@feature/map/impl/src/main/java/com/neki/android/feature/map/impl/MapViewModel.kt`:
- Around line 99-116: handleClickDirectionItem uses state.currentLocation
directly and can send (0.0,0.0) as the start point; add a validity check before
posting MapEffect.MoveDirectionApp: after reduce {
copy(isShowDirectionBottomSheet = false) } verify state.currentLocation is a
valid lat/lng (e.g., not Pair(0.0, 0.0) or use an isValidLocation helper) and
only post MoveDirectionApp when valid, otherwise post a side effect to request a
location update or show a location-required message (e.g.,
postSideEffect(MapEffect.RequestLocationUpdate) or
MapEffect.ShowLocationRequired) and return early; keep existing symbols
(handleClickDirectionItem, MapState.currentLocation, MapEffect.MoveDirectionApp,
intent.app, state.selectedBrandInfo) to locate the change.
🧹 Nitpick comments (4)
core/data-api/src/main/java/com/neki/android/core/dataapi/repository/DataStoreRepository.kt (1)

16-17: Preferences.Key를 인터페이스에 노출하면 추상화가 약해집니다.

Preferences.Key<Boolean>을 직접 파라미터로 받으면 소비자 모듈이 DataStore 구현 세부사항에 의존하게 됩니다. 향후 저장소 구현을 교체하기 어려워질 수 있습니다.

대안으로 문자열 키나 enum을 사용하고, 키 생성은 내부에서 처리하는 방식을 고려해보세요:

♻️ 추상화 개선 제안
-    suspend fun setBoolean(key: Preferences.Key<Boolean>, value: Boolean)
-    fun getBoolean(key: Preferences.Key<Boolean>): Flow<Boolean>
+    suspend fun setBoolean(key: String, value: Boolean)
+    fun getBoolean(key: String): Flow<Boolean>

또는 미리 정의된 키를 사용하는 전용 메서드:

suspend fun setFirstLocationPermissionShown(value: Boolean)
fun isFirstLocationPermissionShown(): Flow<Boolean>
feature/map/impl/src/main/java/com/neki/android/feature/map/impl/component/MapRefreshChip.kt (2)

35-38: background()shape 파라미터가 불필요합니다.

Line 33에서 이미 clip(CircleShape)을 적용했으므로, background()에서 다시 shape을 지정할 필요가 없습니다.

♻️ 중복 제거
             .background(
-                shape = CircleShape,
                 color = NekiTheme.colorScheme.white,
             )

54-58: 하드코딩된 문자열을 리소스로 분리하는 것을 고려해보세요.

"현 위치에서 탐색" 문자열이 코드에 직접 작성되어 있습니다. 다국어 지원이나 문자열 관리를 위해 strings.xml 리소스로 분리하는 것이 좋습니다.

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

48-55: 빈 토큰에 대한 명시적 검증을 고려해보세요.

getRefreshToken()이 토큰이 없을 때 빈 문자열("")을 반환합니다. 현재 코드는 빈 토큰을 updateAccessToken()에 전달하고 API 실패에 의존하여 에러를 처리합니다.

명시적인 검증을 추가하면 의도가 더 명확해지고 불필요한 네트워크 호출을 방지할 수 있습니다:

♻️ 명시적 검증 추가 제안
             authRepository.updateAccessToken(
-                refreshToken = dataStoreRepository.getRefreshToken().first(),
-            ).onSuccess {
+                refreshToken = dataStoreRepository.getRefreshToken().first()
+                    .takeIf { it.isNotBlank() }
+                    ?: run {
+                        authEventManager.emitTokenExpired()
+                        return@launch
+                    },
+            ).onSuccess {
                 postSideEffect(LoginSideEffect.NavigateToHome)

Copy link
Copy Markdown
Contributor

@ikseong00 ikseong00 left a comment

Choose a reason for hiding this comment

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

고생하셨습니다!

가까운 네컷 사진 브랜드 info icon 팝업 쪽은 아직 디자인이 나오지 않아 다이얼로그로 뜨는 거죠??

Comment thread app/src/main/java/com/neki/android/app/ui/BottomNavigationBar.kt
Comment thread feature/map/impl/src/main/java/com/neki/android/feature/map/impl/MapScreen.kt Outdated
Comment thread feature/map/impl/src/main/java/com/neki/android/feature/map/impl/MapScreen.kt Outdated
Comment thread feature/map/impl/src/main/java/com/neki/android/feature/map/impl/MapScreen.kt Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In
`@core/common/src/main/java/com/neki/android/core/common/permission/PermissionUtils.kt`:
- Around line 8-12: The navigateToAppSettings(Context) function can be called
with non-Activity contexts (Application, Service) and will crash if
startActivity is invoked without FLAG_ACTIVITY_NEW_TASK; update the function
(navigateToAppSettings) to set Intent.FLAG_ACTIVITY_NEW_TASK on the constructed
Intent before calling context.startActivity(intent) so the settings Activity can
be launched safely from any Context.

In
`@feature/map/impl/src/main/java/com/neki/android/feature/map/impl/component/CloseButton.kt`:
- Around line 26-41: The close button's clickable area and accessibility are
insufficient: the Box/Modifier chain in CloseButton.kt (modifier,
.buttonShadow(), .background(..., CircleShape), .clickable(onClick = onClick),
.padding(8.dp)) yields a touch target under 48dp and the Icon has
contentDescription = null; fix by adding a minimum touch target with
Modifier.sizeIn(minWidth = 48.dp, minHeight = 48.dp) in the Box modifier chain
(before padding or clickable as appropriate), and add semantics to expose an
accessibility label (e.g., .semantics { contentDescription = "Close" } or use
role = Role.Button if available) so the Icon can remain decorative while the Box
provides the screen-reader label; keep CircleShape and visual styling intact.

In
`@feature/map/impl/src/main/java/com/neki/android/feature/map/impl/MapScreen.kt`:
- Around line 86-88: MapEffect.RefreshPhotoBooth in MapScreen.kt is left as a
TODO so the "현 위치에서 탐색" button does nothing; implement the refresh flow by
resolving the current location and triggering the photo-booth reload: inside the
MapEffect.RefreshPhotoBooth branch call the existing location
provider/getCurrentLocation helper (or viewModel.getCurrentLocation()) and then
invoke the viewModel method that queries nearby booths (e.g.,
viewModel.refreshPhotoBooths(currentLocation) or dispatch
MapIntent.LoadNearbyPhotoBooths with the location). If those helpers don't exist
yet, either implement them or change the effect handling to disable/hide the "현
위치에서 탐색" action until the refresh flow (getCurrentLocation + refreshPhotoBooths)
is implemented.
♻️ Duplicate comments (1)
feature/map/impl/src/main/java/com/neki/android/feature/map/impl/MapScreen.kt (1)

60-60: LocalActivity.current!!는 프리뷰/비-Activity 컨텍스트에서 크래시 위험

nullable 처리로 NPE를 피하고, rationale 계산/권한 요청은 null-safe하게 처리하는 게 안전합니다.

🐛 수정 제안
-    val activity = LocalActivity.current!!
+    val activity = LocalActivity.current
+    val shouldShowRationale =
+        activity?.let(LocationPermissionManager::shouldShowLocationRationale) ?: false
...
-            viewModel.store.onIntent(MapIntent.RequestLocationPermission(LocationPermissionManager.shouldShowLocationRationale(activity)))
+            viewModel.store.onIntent(MapIntent.RequestLocationPermission(shouldShowRationale))
-    val activity = LocalActivity.current!!
+    val activity = LocalActivity.current
+    val shouldShowRationale =
+        activity?.let(LocationPermissionManager::shouldShowLocationRationale) ?: false
...
-                    onIntent(MapIntent.RequestLocationPermission(LocationPermissionManager.shouldShowLocationRationale(activity)))
+                    onIntent(MapIntent.RequestLocationPermission(shouldShowRationale))
...
-                        onIntent(MapIntent.RequestLocationPermission(LocationPermissionManager.shouldShowLocationRationale(activity)))
+                        onIntent(MapIntent.RequestLocationPermission(shouldShowRationale))
...
-                        onIntent(MapIntent.RequestLocationPermission(LocationPermissionManager.shouldShowLocationRationale(activity)))
+                        onIntent(MapIntent.RequestLocationPermission(shouldShowRationale))

Also applies to: 173-175, 229-233, 262-268, 270-276

@ikseong00 ikseong00 requested review from Copilot and removed request for Copilot January 25, 2026 05:56
@ikseong00
Copy link
Copy Markdown
Contributor

고생하셨습니다!

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In
`@feature/map/impl/src/main/java/com/neki/android/feature/map/impl/MapScreen.kt`:
- Around line 77-84: 현재 영구 거부 감지 조건 `!currentShouldShowRationale &&
!previousShouldShowRationale`가 이전 상태에 따라 영구 거부를 놓치므로, 거부 시 현재 상태만 검사하도록 변경하세요:
대신 `if (!currentShouldShowRationale) {
viewModel.store.onIntent(MapIntent.ShowLocationPermissionDialog) }`로 바꿔
`currentShouldShowRationale`가 false인 경우(영구 거부 포함)에 항상 다이얼로그를 표시하도록 수정하고, 관련 식별자
locationTrackingMode, LocationTrackingMode.Follow, currentShouldShowRationale,
previousShouldShowRationale,
viewModel.store.onIntent(MapIntent.ShowLocationPermissionDialog)를 사용해 코드를 찾아
적용하세요.
♻️ Duplicate comments (2)
feature/map/impl/src/main/java/com/neki/android/feature/map/impl/MapScreen.kt (2)

61-61: LocalActivity의 강제 언래핑은 Preview에서 크래시 유발

LocalActivity.current!! 사용 시 Preview 또는 Activity 없는 컨텍스트에서 NullPointerException이 발생합니다. nullable로 처리해 주세요.


96-98: '현 위치에서 탐색' 기능 미구현 상태

MapEffect.RefreshPhotoBooth가 TODO로 비어 있어 버튼 클릭 시 실제 동작이 없습니다. 구현 전이라면 버튼 비활성화 또는 숨김 처리를 고려해 주세요.

🧹 Nitpick comments (1)
feature/map/impl/src/main/java/com/neki/android/feature/map/impl/MapScreen.kt (1)

52-52: 사용되지 않는 Timber import

Timber가 import되어 있지만 파일 내에서 사용되지 않습니다. 제거해 주세요.

♻️ 수정 제안
-import timber.log.Timber

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In
`@feature/map/impl/src/main/java/com/neki/android/feature/map/impl/MapScreen.kt`:
- Around line 143-150: The call to getPlaceName from MapScreen (used to build
NAVER map URL) can block the main thread on Android <13 because getPlaceName
performs a synchronous geocoder.getFromLocation; move that blocking work to
Dispatchers.IO by making getPlaceName a suspend function that wraps the
synchronous geocoding in withContext(Dispatchers.IO) (or, if you prefer not to
change its signature, call it inside withContext(Dispatchers.IO) where MapScreen
invokes it). Update the MapScreen invocation (the code that computes
startName/destName before DirectionHelper.moveAppOrStore) to run inside a
coroutine and obtain names via withContext(Dispatchers.IO) to avoid main-thread
blocking. Ensure the rest of the call to DirectionHelper.moveAppOrStore remains
on the main thread after names are resolved.
♻️ Duplicate comments (3)
feature/map/impl/src/main/java/com/neki/android/feature/map/impl/MapScreen.kt (3)

60-61: LocalActivity의 강제 언래핑 이슈가 아직 해결되지 않았습니다.

LocalActivity.current!! 사용은 프리뷰 또는 비Activity 컨텍스트에서 NullPointerException을 유발합니다. 이전 리뷰에서 제안된 대로 nullable 처리가 필요합니다.


76-84: 영구 거부 감지 로직이 여전히 일부 시나리오를 놓칠 수 있습니다.

이전 리뷰에서 지적된 대로, 첫 번째 거부 후(prev=false → current=true) 두 번째 요청에서 영구 거부(prev=true → current=false)하는 케이스를 현재 조건이 감지하지 못합니다. !currentShouldShowRationale만 확인하는 방식으로 변경 검토가 필요합니다.


95-97: '현 위치에서 탐색' 동작이 여전히 구현되지 않았습니다.

MapEffect.RefreshPhotoBooth가 TODO로 비어 있어 버튼 클릭 시 실제 동작이 없습니다. 이전 리뷰에서 언급된 대로 구현이 필요합니다.

Comment on lines 143 to 150
DirectionApp.NAVER_MAP -> {
val startName = context.getPlaceName(37.5270539, 126.8862648, "출발지")
val destName = context.getPlaceName(37.5256372, 126.8861924, "도착지")
val startName = context.getPlaceName(startLatitude, startLongitude, "출발지")
val destName = context.getPlaceName(endLatitude, endLongitude, "도착지")
DirectionHelper.moveAppOrStore(
context = context,
url = "nmap://route/walk?slat=37.5270539&slng=126.8862648&sname=$startName&dlat=37.5256372&dlng=126.8861924&dname=$destName",
url = "nmap://route/walk?slat=$startLatitude&slng=$startLongitude&sname=$startName&dlat=$endLatitude&dlng=$endLongitude&dname=$destName",
packageName = sideEffect.app.packageName,
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Find getPlaceName function definition
ast-grep --pattern 'fun Context.getPlaceName($$$) {
  $$$
}'

Repository: YAPP-Github/27th-App-Team-2-Android

Length of output: 61


🏁 Script executed:

# Search for getPlaceName function definition using rg
rg "fun.*getPlaceName" -A 15

Repository: YAPP-Github/27th-App-Team-2-Android

Length of output: 1954


🏁 Script executed:

# Check the context of getPlaceName calls in MapScreen.kt
cat -n feature/map/impl/src/main/java/com/neki/android/feature/map/impl/MapScreen.kt | sed -n '120,160p'

Repository: YAPP-Github/27th-App-Team-2-Android

Length of output: 2393


🏁 Script executed:

# Check broader context to see the function/lambda scope
cat -n feature/map/impl/src/main/java/com/neki/android/feature/map/impl/MapScreen.kt | sed -n '80,145p'

Repository: YAPP-Github/27th-App-Team-2-Android

Length of output: 3385


🏁 Script executed:

# Verify the complete Extension.kt implementation to confirm async pattern
cat -n feature/map/impl/src/main/java/com/neki/android/feature/map/impl/util/Extension.kt | sed -n '1,50p'

Repository: YAPP-Github/27th-App-Team-2-Android

Length of output: 1558


Android 13(TIRAMISU) 이전 버전에서 메인 스레드 블로킹 문제 발생

getPlaceName 구현에서 Android 13 미만 기기에서는 geocoder.getFromLocation() 동기 호출(Extension.kt 28줄)이 메인 스레드를 차단합니다. 역지오코딩 네트워크 I/O가 완료될 때까지 UI가 멈출 수 있으므로, 해당 호출을 Dispatchers.IO로 옮겨야 합니다.

Android 13 이상에서는 콜백 기반 비동기 패턴이 올바르게 사용되고 있습니다.

🤖 Prompt for AI Agents
In
`@feature/map/impl/src/main/java/com/neki/android/feature/map/impl/MapScreen.kt`
around lines 143 - 150, The call to getPlaceName from MapScreen (used to build
NAVER map URL) can block the main thread on Android <13 because getPlaceName
performs a synchronous geocoder.getFromLocation; move that blocking work to
Dispatchers.IO by making getPlaceName a suspend function that wraps the
synchronous geocoding in withContext(Dispatchers.IO) (or, if you prefer not to
change its signature, call it inside withContext(Dispatchers.IO) where MapScreen
invokes it). Update the MapScreen invocation (the code that computes
startName/destName before DirectionHelper.moveAppOrStore) to run inside a
coroutine and obtain names via withContext(Dispatchers.IO) to avoid main-thread
blocking. Ensure the rest of the call to DirectionHelper.moveAppOrStore remains
on the main thread after names are resolved.

@Ojongseok Ojongseok merged commit 948df58 into develop Jan 25, 2026
2 checks passed
@Ojongseok Ojongseok deleted the refactor/#51-map branch January 27, 2026 16:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[refactor] 네컷지도 기능 고도화

2 participants