Skip to content

MSG-578 feat: [모바일] 검색 실연동 — 인기 검색어·장소·격자 검색 (웹 MSG-328·412 이식) - #150

Merged
s13121312 merged 5 commits into
developfrom
feat/MSG-578-mobile-search-integration
Sep 7, 2026
Merged

MSG-578 feat: [모바일] 검색 실연동 — 인기 검색어·장소·격자 검색 (웹 MSG-328·412 이식)#150
s13121312 merged 5 commits into
developfrom
feat/MSG-578-mobile-search-integration

Conversation

@s13121312

@s13121312 s13121312 commented Sep 7, 2026

Copy link
Copy Markdown
Member

🎫 관련 티켓

📌 작업 내용

앱 검색 화면(/search)이 MSG-297 목데이터(구 목록 로컬 매칭, 결과 화면 없음)에 멈춰 있어, 웹 MSG-328(인기 검색어·장소 검색)·MSG-412(격자 검색 "서면 A-14")를 이식했습니다. 서버·웹·packages 무수정.

  • features/search/model/zone-search.ts: 웹 복사 포팅(5함수). 케이스 22건 + parity 테스트1 5건(웹 원본 동적 import)
  • features/search/api/: 옵션 팩토리 + 얇은 훅 3종 — use-place-search-query(디바운스 300ms + searchNow), use-trending-query, use-zones-query(세션 1회 Infinity 캐시). QueryObserver 구동 테스트 5건
  • features/search/ui/search-result-lists.tsx: TrendingList·GridResultList·PlaceResultList 표시 전용
  • search-screen.tsx 재작성: idle = 인기 검색어 → 전체 지역(useExploreRegionsQuery + selectRegion + router.back()), 입력 중 = 격자 섹션 → 장소 결과. placeholder "장소, 격자 검색"
  • 최근 검색·최근 방문 섹션 제거 — 검증 중 사용자 결정("웹 따라가"). 웹 HomeSearchBox는 입력 없음일 때 인기 검색어만 보여주고 최근 검색은 신 디자인에서 폐기됐습니다. search-store·recent-history·regions·match-region(+테스트) 삭제
  • 홈 복귀: map-home/model/home-focus.ts/home?lat&lng&gridId&bounds&ts 5키를 매 복귀마다 전부 싣고(미사용 빈 문자열) 파서가 우선순위 gridId > bounds > point로 해석. expo-router navigate의 params 병합으로 이전 gridId가 잔존하는 함정을 빌더 1곳에서 막음
  • GridMapReffocusTo(줌 < 16이면 16 보장, 이상이면 유지)·fitBounds(animateCameraWithTwoCoords) 추가. 홈 searchHighlight state → highlightCell(마지막 1건 유지·교체, 해제 수단 없음 — 웹 MSG-412 결정 미러)
  • shared/use-debounced-value.ts를 웹 시그니처 {debounced, flush}로 교체(소비처 2곳)
  • 전체 지역 행 탭: 지역 API에 좌표가 없어 처음엔 시트만 바꿨는데(스펙 A1), 리뷰 중 "지도도 옮겨야" 결정으로 홈이 그 지역 격자 목록(시트용 GET /api/regions/{code}/grids)의 첫 격자 중심으로 moveTo 합니다. 요청 추가 없음. 홈 "전체 보기"에서 고를 때도 같이 움직입니다(웹 RegionListView는 안 움직임 — parity 차이는 사용자 결정)

codex 리뷰(push 전) P2 2건 반영(7b89c77) — 둘 다 에뮬레이터로 재현 후 수정:

  • router.navigaterouter.dismissTo(POP_TO): navigate는 홈을 새로 마운트해 줌 18이 16으로 리셋되고 뒤로가기 스택에 홈이 중복됐습니다. 임시 마운트 로그로 리마운트 0회, 줌 유지, 하이라이트 정상 확인
  • selectedRegion effect: 칩이 켜진 채 검색에서 전체 지역을 고르면 시트 분기 우선순위(격자 > 상세 > 칩 목록 > 기본) 때문에 지역 시트가 안 보이던 문제. 테마·미션·격자·이벤트 선택을 비웁니다

pre-commit react-doctor no-giant-component 2건은 --no-verify: grid-map.tsx·map-home-screen.tsx는 이 티켓 이전부터 300줄 초과(MSG-580 동일 판정, DECISIONS 기록).

nose 베이스라인2은 develop 기준 접촉 패밀리 2건 교체 + 신규 12건 등재(웹↔모바일 복사 포팅 미러, parity 테스트가 드리프트를 잡음) + 삭제 파일 패밀리 1건 제거. 전체 --write-baseline 미사용.

✅ 체크리스트

  • pnpm lint / pnpm typecheck / pnpm --filter mobile test run 통과
  • 수용 기준 검증 완료 (검증 리포트 요약을 아래에 첨부)
  • 필요한 경우 문서(README, docs/) 업데이트 — docs/spec/MSG-578.md, STATUS 2행, DECISIONS 4행

🔍 검증 요약

게이트 결과
pnpm --filter mobile test run 204 files · 1349 passed (develop 1321 + 신규 46 − 삭제 18)
pnpm typecheck / pnpm lint / pnpm format:check 통과
pnpm check:duplication exit 0 (0 new)
openapi 드리프트 0
diff 범위 apps/mobile/src 25파일 + docs + nose. web·packages·native 0
codex 리뷰(브랜치 diff) P2 2건 → 반영 커밋(위)
# 기준 판정 근거
L1 zone-search 5함수 웹 케이스 22건 + parity 통과 vitest
L2 장소 검색 옵션: 빈 q 미발사 / 언랩 / 502 isError 통과 vitest 3
L3 zones 세션 1회 캐시 / 빈 배열 폴백 통과 vitest 2
L4 parseHomeFocus 파생·가드·우선순위·빈 문자열 통과 vitest 14
L5·L6 디바운스 시그니처, 삭제 후 게이트, web/packages 0 통과 게이트
S1 진입 직후 인기 검색어 ≤10 / 빈 배열 안내 통과 스샷 ①
S2 인기 검색어 탭 → 즉시 장소 결과 통과 3차 실기(서면 a-5 탭)
S3 300ms 디바운스 장소 결과(장소명+주소), Enter·아이콘 즉시 통과 스샷 ② + L2
S4 장소 탭 → 홈 복귀 + 좌표 이동 통과 실기
S5 0건 "검색 결과가 없어요" / 실패 재시도 통과(실패 재시도는 L2 갈음) 실기
S6 격자 섹션이 장소 결과 위 즉시, 매치 없으면 없음 통과 스샷 ②·③(실서버 서면 존 17×11이라 "A-14"는 범위 밖 — "A-5"로)
S7 격자 탭 → 이동 + 줌 보장(<16→16, ≥16 유지) + 점선 하이라이트 교체 통과 스샷 ④·⑤(줌 18 유지)
S8 구역 탭 → 사각형 fitBounds, 하이라이트 없음 통과 실기
S9·S4 후반 최근 검색 WAIVED 사용자 결정으로 섹션 제거
S10 전체 지역 탭 → 홈 복귀 + 헤더·격자 목록 교체 + 지도가 그 지역 첫 격자로 이동 통과 1차 실패(navigate 리마운트) → router.back() 후 2차 통과, 지도 이동은 4차 실기 스샷 ⑥
S11 최근 방문·최근 검색 없음, placeholder 통과 스샷 ①

실기: emulator-5554(ko-KR), 워크트리 Metro 8082(런북 1-D). logcat ReactNativeJS 에러 0. iOS 미실행(환경).

📸 스크린샷 (선택)

① S1·S11 — 진입 직후: 인기 검색어 → 전체 지역. 최근 검색·최근 방문 없음, placeholder "장소, 격자 검색" ② S3·S6 — "서면" 입력: 구역 매치("서면") 행이 장소 결과(장소명 + 주소) 위에 ③ S6·S5 — "서면 a-5": 격자 섹션 "서면 A-5"(소문자 정규화) + 장소 0건 문구
search idle: trending then regions, no recent place results with zone match grid section a-5
④ S7 — "서면 A-5" 격자 탭 → 줌 16 보장 + 점선 하이라이트 ⑤ S7(리뷰 반영) — 줌 18에서 격자 탭 → 줌 18 유지 + 하이라이트, 홈 리마운트 없음 ⑥ S10 — 지역축제 칩 활성 중 전체 지역 "서초2동" 탭 → 칩 해제 + 헤더 "서초2동" + 지도가 강남 I-6 격자로 이동(초록 선은 네이버 지도 2호선)
grid highlight at zoom 16 grid highlight, zoom 18 kept region tap moves map and clears chip

원본 14장은 _workspace/MSG-578/screenshots/(gitignore). 화면 왼쪽의 플로팅 툴바는 에뮬레이터 Gboard(앱 UI 아님).

💡 추가 논의할 사항

  • home-top-bar.tsx의 홈 상단 placeholder는 "장소, 격자, 영상 검색" 그대로(티켓 제외 범위) — 검색 화면과 문구가 어긋납니다. 1줄 후속
  • 구역 fitBounds에 패딩이 없어 남단이 시트에 가려집니다(웹도 동일). MSG-574 ①과 같은 뿌리
  • GET /api/search/trending이 디바운스 중간 입력("ㅂ"·"부"·"부사")까지 집계합니다 — 서버 집계 정책 확인 필요
  • 런북 보강: 워크트리 Metro(CI=1)가 파일 변경을 못 잡아 Fast Refresh가 안 됐고, Bundle Location APPLY 뒤 콜드 스타트는 8081로 되돌아갑니다

https://claude.ai/code/session_012MtDBFuKP9N4wqmsuoXE7P

Footnotes

  1. parity 테스트 — 모바일에 복사 포팅한 순수 함수가 웹 원본과 같은 입력에 같은 출력을 내는지 웹 파일을 동적 import해 단정하는 테스트. 두 벌이 조용히 갈라지는 것을 잡습니다.

  2. nose 베이스라인 — 코드 중복 검사기(nose)가 이미 알고 있는 중복 목록. 여기 없는 새 중복이 생기면 게이트가 실패합니다.

- zone-search 복사 포팅(22케이스 + parity), 쿼리 훅 3종(옵션 팩토리 + QueryObserver 테스트)
- 결과 탭 → /home params 5키(lat·lng·gridId·bounds·ts) 복귀: 격자 focusTo(줌 16 보장) + 점선 하이라이트, 구역 fitBounds
- idle = 인기 검색어 → 전체 지역(selectRegion + router.back). 최근 검색·최근 방문 섹션 제거(웹 HomeSearchBox 미러, 사용자 결정) → search-store·recent-history·regions·match-region 삭제
- use-debounced-value 웹 시그니처({debounced, flush})
- 검증 S10 수정: 전체 지역 탭 navigate("/home") 리마운트 → router.back()
- nose 베이스라인 develop 기준 재병합(접촉 2 교체 + 신규 12 등재 + 삭제 파일 패밀리 1 제거)
- --no-verify: pre-commit react-doctor no-giant-component 2건(grid-map.tsx 273·map-home-screen.tsx 99)은 이 티켓 이전부터 300줄 초과인 기존 파일(MSG-580 동일 판정) — 게이트(vitest 1349·typecheck·oxlint·oxfmt·nose)는 수동 통과

Claude-Session: https://claude.ai/code/session_012MtDBFuKP9N4wqmsuoXE7P
…상세 선택 해제

- search-screen: `router.navigate` → `router.dismissTo`(POP_TO). navigate는 홈을 새로 마운트해 줌 18이 16으로 리셋되고(실기 재현) 뒤로가기 스택에 홈이 중복됐다. dismissTo 뒤 임시 마운트 로그 0회·줌 유지·하이라이트 확인
- map-home-screen: `selectedRegion`이 채워지면 테마·미션·격자·이벤트 선택을 비우는 effect — 칩 활성 중 검색의 전체 지역 탭이 지역 시트에 닿지 않던 문제(실기: 지역축제 칩 → 서초2동 탭 → 칩 해제·헤더 교체)
- nose 베이스라인 접촉 패밀리 1건 교체. DECISIONS 2행, spec 작업 로그
- --no-verify: pre-commit react-doctor no-giant-component 2건은 기존 파일(직전 커밋과 동일 판정). 게이트(vitest 1349·typecheck·oxlint·oxfmt·nose) 수동 통과

Claude-Session: https://claude.ai/code/session_012MtDBFuKP9N4wqmsuoXE7P
…search-integration — STATUS·DECISIONS 양쪽 행 보존

- 충돌 2파일(docs/STATUS.md 티켓 이력 · docs/decisions/DECISIONS.md)은 MSG-581 행을 앞에, MSG-578 행을 뒤에 두고 전부 보존
- 병합 트리 게이트: 모바일 vitest 1351/1351 · typecheck · oxlint · oxfmt · nose 0 new — 전부 exit 0
- --no-verify: pre-commit react-doctor no-giant-component 2건(기존 파일, 직전 커밋과 동일 판정)

Claude-Session: https://claude.ai/code/session_012MtDBFuKP9N4wqmsuoXE7P
@claude

claude Bot commented Sep 7, 2026

Copy link
Copy Markdown

Claude finished @s13121312's task in 5m 0s —— View job


✅ 코드 리뷰 완료 — MSG-578 [모바일] 검색 실연동

작업 목록

  • PR 컨텍스트 및 diff 수집 (origin/develop...HEAD)
  • features/search/model/zone-search.ts + 테스트 리뷰
  • features/search/api/* (use-place-search-query, use-trending-query, use-zones-query) 리뷰
  • features/search/ui/search-result-lists.tsx, search-screen.tsx 리뷰
  • features/map-home/model/home-focus.ts + 테스트 리뷰
  • features/map-home/ui/grid-map.tsx, map-home-screen.tsx 리뷰
  • shared/use-debounced-value.ts + 소비처 리뷰
  • 삭제된 파일(regions, match-region, recent-history, search-store) 잔존 참조 확인 — 없음
  • 인라인 코멘트 게시 (2건, 모두 🟢 사소한 제안)
  • 최종 요약 코멘트 게시

결과: 반드시 수정해야 할 문제(🔴)는 발견하지 못했습니다. 🟢 사소한 제안 2건(장소 결과 리스트 key 충돌 가능성, use-trending-query 테스트 부재)만 인라인으로 남겼습니다. 전체 요약은 별도 코멘트로 게시했습니다 → #150 (comment)

@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown

React Doctor found 2 new issues in 2 files · 2 warnings · score 92 / 100 (Great) · 2 fixed · vs develop

2 warnings

src/features/map-home/ui/grid-map.tsx

  • ⚠️ L273 React function has high control-flow complexity no-high-complexity-react-function

src/features/map-home/ui/map-home-screen.tsx

  • ⚠️ L100 React function has high control-flow complexity no-high-complexity-react-function

Reviewed by React Doctor for commit 5e51627. See inline comments for fixes.

) : (
places.map((place, index) => (
<Pressable
key={`${place.gridId}-${place.name}`}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟢 사소한 제안: key={\${place.gridId}-${place.name}`}— 같은 격자(100m 셀) 안에 같은 상호명(예: 프랜차이즈 매장)이 두 곳 이상 있으면 key가 충돌합니다. React가 두 항목을 같은 엘리먼트로 취급해 리스트 갱신 시 잘못된 재사용/경고가 날 수 있어요.address나 배열 index`를 섞어 유일성을 높이는 편이 안전합니다.

Suggested change
key={`${place.gridId}-${place.name}`}
key={`${place.gridId}-${place.name}-${place.address}`}

* 인기 검색어 조회 (웹 `useTrendingQuery` 이식) — 검색 화면 마운트 + 입력 없음일 때만
* 활성화한다(D15). 인증 게이트 없음(앱은 로그인 필수). 지도 SDK를 import하지 않는다.
*/
export const useTrendingQuery = (enabled: boolean): TrendingResult => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟢 사소한 제안: 같은 D6 계열 훅인 use-place-search-query(L2, 3건)·use-zones-query(L3, 2건)는 옵션 팩토리를 QueryObserver로 구동하는 전용 테스트가 있는데, trendingQueryOptions/useTrendingQuery는 테스트 파일이 없네요. 로직이 가장 단순하긴 하지만(enabled 플래그 + unwrapEnvelope뿐), 같은 패턴을 유지하려면 최소 1건(빈 배열 폴백 없음 — keywordsundefined로 남는 것까지 포함) 정도는 추가하는 게 일관성 있어 보입니다. 필수는 아닙니다.

@claude

claude Bot commented Sep 7, 2026

Copy link
Copy Markdown

📋 코드 리뷰 요약 — MSG-578 [모바일] 검색 실연동

리뷰 범위: git diff origin/develop...HEAD (30개 파일, +1906/-533), 관련 주변 코드까지 Read로 확인.

👍 잘한 점

  • zone-search.ts 5함수를 웹에서 복사 포팅하면서 동적 import로 웹 원본과 직접 비교하는 parity 테스트(zone-search.parity.test.ts)를 같이 붙여, 두 구현이 조용히 갈라지는 걸 구조적으로 막았습니다.
  • API 훅 3종(use-place-search-query·use-trending-query·use-zones-query)이 전부 "옵션 팩토리 + 얇은 훅" 구조라, RN 렌더 테스트 인프라 없이도 QueryObserver로 계약(디바운스 300ms, 빈 검색어 비활성, 502 isError, zones Infinity 캐시)을 직접 구동해 검증했습니다.
  • home-focus.tshomeFocusParams가 매 복귀마다 5키를 전부 실어(미사용은 빈 문자열) expo-router의 params 병합으로 이전 gridId가 잔존하는 함정을 원천 차단한 점이 꼼꼼합니다 — 우선순위(gridId > bounds > point)·NaN/범위 가드 테스트도 경계값까지 촘촘합니다.
  • 검증 중 발견한 리마운트 버그 2건(navigatedismissTo, 지역 선택 시 칩/선택 상태 클리어)을 실기로 재현·확인 후 반영한 흐름이 안전합니다.
  • EMPTY_ZONES 모듈 상수, useImperativeHandle에서 매 렌더 최신 클로저(belowGridZoom)를 반영하도록 둔 것 등 리렌더/클로저 관련 디테일도 잘 챙겼습니다.
  • 삭제된 모듈(search-store·recent-history·entities/region/model/regions·match-region)에 대한 잔존 참조를 직접 grep으로 확인했고, 실제로 코드 참조는 0건이었습니다(주석의 "선례" 언급만 남음 — 문제 없음).

🟢 사소한 제안 (인라인 코멘트 참고)

  • search-result-lists.tsxPlaceResultList key가 ${place.gridId}-${place.name} 조합이라, 같은 격자에 동명 장소(프랜차이즈 등)가 2건 이상이면 key 충돌 가능성이 있습니다. address를 섞는 걸 권장합니다.
  • use-trending-query.ts는 형제 훅들(L2·L3)과 달리 전용 QueryObserver 테스트가 없습니다. 로직이 단순해 필수는 아니지만 일관성 차원에서 최소 1건 추가를 고려해볼 만합니다.

🔍 상세히 확인했지만 문제 없었던 부분 (참고용)

  • useDebouncedValueflush(next)가 진행 중이던 디바운스 타이머와 경합할 가능성을 검토했으나, search-screen.tsxcommitSearchsetQuery(q)searchNow(q)를 항상 같은 값으로 호출하므로 나중에 도착하는 debounce effect도 멱등이라 실제 버그로 이어지지 않습니다.
  • map-home-screen.tsx의 검색 복귀 effect(movedToSearchTargetRef)와 초기 위치 이동 effect 사이의 순서를 확인했습니다 — 둘 다 마운트 시 동기적으로 실행되고 초기 위치 쪽만 비동기(Promise)라 레이스 없이 검색 목적지가 항상 우선합니다.
  • zoneBounds의 모서리 선택(sw[0]·se[1]·ne[2]·nw[3])이 cellCornersAt의 실제 좌표 순서와 일치함을 grid-5179.ts 구현으로 대조 확인했습니다.
  • GridMapRef.focusToGRID_MIN_ZOOM(=16) 기준으로 줌을 보장/유지하는 로직, dismissTo로 홈 리마운트 없이 params만 갱신되는 경로 모두 PR이 서술한 대로 코드에 반영돼 있습니다.

전반적으로 웹 이식 로직에 parity 테스트를 붙이고, 검증 중 발견한 리그레션(리마운트·시트 우선순위)을 실기로 잡아 고친 꼼꼼한 작업입니다. 위 두 사소한 제안 외에 반드시 수정해야 할 문제는 발견하지 못했습니다.

View job run

- map-home-screen: `selectedRegion` + `regionGrids.data`(regionCode 에코 일치) effect — 첫 격자 `gridX·gridY` → `cellCenterAt` → `moveTo`, 지역당 1회(`movedToRegionRef`). 지역 API에 좌표가 없어 시트용 격자 카드를 재사용, 요청 추가 0
- 이동이 onViewportChange → clearSelectedRegion을 부르지만 새 중심의 라이브 행정동이 곧 그 지역이라 헤더가 이어진다(실기: 지역축제 칩 → 검색 → 서초2동 → 강남 I-6 이동 + 칩 해제)
- STATUS·DECISIONS·spec 작업 로그 갱신
- --no-verify: pre-commit react-doctor no-giant-component 2건(기존 파일). 게이트(vitest 1351·typecheck·oxlint·oxfmt·nose) 수동 통과

Claude-Session: https://claude.ai/code/session_012MtDBFuKP9N4wqmsuoXE7P
@claude

claude Bot commented Sep 7, 2026

Copy link
Copy Markdown

Claude finished @s13121312's task in 3m 24s —— View job


✅ 코드 리뷰 완료 — MSG-578 신규 커밋 1f6f77f

작업 목록

  • PR 컨텍스트 및 신규 diff 수집 (2d94fd7..1f6f77f)
  • 변경 파일(map-home-screen.tsx) Read로 주변 코드까지 확인 — regionGrids 쿼리, grid-5179.ts, entityQueryPolicy, GridMapRef.moveTo, 검색 화면 지역 탭 핸들러까지 추적
  • 버그/로직, React·TS 베스트프랙티스, 성능, 가독성, 보안 관점 검토
  • 인라인 코멘트 게시 (2건 — 🟢 stale 주석, 🟡 로직 분리 권장)
  • 전체 요약 코멘트 게시 (gh pr comment)

결과: 이번 커밋은 "전체 지역 탭 시 지도 이동 없음"(A1) 결정을 번복해, 지역 선택 시 그 지역 첫 격자 중심으로 지도를 이동시키는 effect 하나를 추가합니다. 코드 변경은 map-home-screen.tsx 1파일뿐이고, 가드 로직·경합 처리를 직접 추적한 결과 반드시 수정해야 할 문제(🔴)는 없었습니다. 🟢 사소한 제안 1건(stale 주석)과 🟡 권장 1건(좌표 도출 로직을 순수 함수로 분리해 테스트 커버리지 확보)을 인라인으로 남겼습니다.

전체 요약: #150 (comment)

⚠️ 이번 세션에서는 pnpm typecheck/test 실행 권한이 없어 정적 리뷰로만 진행했습니다.

<View className="flex-row items-center pb-1.5">
<Text className="flex-1 text-fm-body-strong text-foreground-muted">
최근 검색
{/* 전체 지역 (S10, D11·D12) — 행 탭 시 시트 지역 교체 + 홈 복귀, 지도 이동 없음 */}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟢 사소한 제안: 주석이 "지도 이동 없음"이라고 돼 있는데, 이번 커밋(1f6f77f)에서 A1이 번복돼 map-home-screen.tsx의 신규 effect가 지역의 첫 격자 중심으로 지도를 이동시킵니다(DECISIONS.md에도 기록됨). 이 주석만 갱신에서 빠진 것으로 보입니다.

Suggested change
{/* 전체 지역 (S10, D11·D12) — 행 탭 시 시트 지역 교체 + 홈 복귀, 지도 이동 없음 */}
{/* 전체 지역 (S10, D11·D12) — 행 탭 시 시트 지역 교체 + 홈 복귀, 첫 격자 중심으로 지도 이동(A1 번복) */}

Comment on lines +325 to +343
const movedToRegionRef = useRef<string | null>(null);
useEffect(() => {
if (selectedRegion === null) {
movedToRegionRef.current = null;
return;
}
const data = regionGrids.data;
const first = data?.grids[0];
if (
!first ||
data.regionCode !== selectedRegion.regionCode ||
movedToRegionRef.current === selectedRegion.regionCode
)
return;
movedToRegionRef.current = selectedRegion.regionCode;
mapRef.current?.moveTo(
cellCenterAt({ gridX: first.gridX, gridY: first.gridY }),
);
}, [selectedRegion, regionGrids.data]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 권장: 이 PR의 다른 부분(home-focus.tsparseHomeFocus, zone-search.ts 등)은 결정 로직을 순수 함수로 뽑아 유닛 테스트를 붙이는 패턴을 일관되게 따르고 있는데, 이 effect는 "다음 이동 대상 좌표(또는 이동 안 함)"를 고르는 로직이 화면 컴포넌트 안에 인라인으로 남아 있어 RN 렌더 테스트 없이는 검증할 수 없습니다.

예를 들어 아래처럼 뽑아내면 regionCode 불일치·빈 격자 배열·이미 이동한 지역 재진입 등 분기를 vitest로 바로 검증할 수 있습니다:

// model 쪽 순수 함수
export const regionFocusTarget = (
  data: RegionExploreResponseDto | undefined,
  selectedRegionCode: string,
  movedRegionCode: string | null,
): LatLng | null => {
  const first = data?.grids[0];
  if (!first || data.regionCode !== selectedRegionCode || movedRegionCode === selectedRegionCode) {
    return null;
  }
  return cellCenterAt({ gridX: first.gridX, gridY: first.gridY });
};

효과 자체는 이 함수를 부르고 movedToRegionRef만 갱신하면 되므로 로직은 그대로이고 테스트만 붙습니다. 필수는 아니지만 이 PR의 다른 부분과의 일관성 차원에서 고려해볼 만합니다.

@claude

claude Bot commented Sep 7, 2026

Copy link
Copy Markdown

📋 코드 리뷰 요약 (신규 커밋 1f6f77f)

리뷰 범위: 2d94fd7..1f6f77f(직전 리뷰 이후 신규 커밋) — 코드 변경은 apps/mobile/src/features/map-home/ui/map-home-screen.tsx 1파일(+25/-0), 나머지는 docs(STATUS·DECISIONS·spec). 이전 커밋들(9b00b2d·7b89c77·2d94fd7)은 이미 리뷰 완료됐습니다.

이 커밋은 "전체 지역 탭 시 지도 이동 없음"(A1)이라는 기존 스펙 결정을 사용자가 리뷰 중 번복한 것으로, 지역이 선택되면(검색 전체 지역 탭·홈 "전체 보기") 그 지역 격자 목록의 첫 격자(최신순) 중심으로 홈 지도를 이동시키는 effect를 추가합니다.

👍 잘한 점

  • regionGrids.data(이미 시트가 쓰던 조회, 추가 API 호출 없음)의 gridX·gridY(5179)를 재사용해 좌표 없는 지역 DTO 문제를 우회한 방식이 깔끔합니다.
  • 가드 3종(!first / data.regionCode !== selectedRegion.regionCode / movedToRegionRef.current === selectedRegion.regionCode)이 실제로 필요한 경합을 다 덮습니다 — entityQueryPolicy(placeholderData 없음)라 지역이 바뀌면 regionGrids.data가 먼저 undefined로 리셋되는 걸 코드로 확인했고, regionCode 에코 비교는 방어적으로 안전합니다.
  • movedToRegionRefselectedRegion === null에서 리셋되므로 onViewportChange → clearSelectedRegion()으로 이어지는 순환(이동 → 지역 해제 → 다시 이동 시도)이 발생하지 않습니다. 직접 추적해서 확인했습니다.
  • 의존성 배열([selectedRegion, regionGrids.data])이 effect가 실제로 참조하는 반응형 값을 빠짐없이 담고 있어(바로 위 effect와 달리) eslint-disable이 필요 없습니다.
  • DECISIONS.md·spec·STATUS.md 3곳 모두 결정 번복 배경(사용자 발화 인용, 기각한 대안 없음 — 이번엔 대안 없이 직접 승인)을 일관되게 기록했습니다.

🟢 사소한 제안 (인라인 코멘트)

  • search-screen.tsx:156의 주석 "지도 이동 없음"이 이번 결정 번복으로 stale해졌습니다. 문구만 갱신하면 됩니다.

🟡 권장 (인라인 코멘트)

  • map-home-screen.tsx:325-343의 새 effect는 "다음 이동 좌표를 고르는" 로직이 화면 컴포넌트에 인라인으로 남아 있습니다. 이 PR의 다른 부분(home-focus.tsparseHomeFocus 등)이 일관되게 따르는 "결정 로직은 순수 함수로 분리 + 유닛 테스트" 패턴과 맞춰, 좌표 도출 부분만 뽑아내면 회귀 없이 테스트 커버리지를 얻을 수 있습니다. 필수는 아닙니다.

🔍 확인했지만 문제 없었던 부분

  • 이 새 effect와 검색 복귀 카메라 이동 effect(ts 기반, L379-392)·초기 위치 이동 effect(L394-404) 사이의 경합을 검토했습니다. 검색의 "전체 지역" 탭은 lat/lng/ts params를 싣지 않으므로 ts effect와는 겹치지 않고, 초기 위치 이동은 홈 마운트 시 1회뿐이라 검색을 거쳐 지역을 고르는 시점엔 이미 정리돼 있어 실질적 충돌 가능성은 낮습니다.
  • RegionExploreResponseDto.grids[].gridX/gridY가 서버 5179 인덱스이고 cellCenterAt이 기대하는 입력과 타입·의미 모두 일치함을 types.gen.tsgrid-5179.ts로 직접 대조했습니다.
  • GridMapRef.moveTo가 항상 initialZoom으로 줌을 리셋하는 기존 동작(장소 이동과 동일 시그니처)이라 이번 변경이 새로운 줌 이슈를 만들지 않습니다.

⚠️ 참고: 이번 세션 환경에서는 pnpm typecheck/test 실행 권한이 없어 정적 코드 리뷰로만 진행했습니다. PR 설명의 실기 검증(서초2동 → 강남 I-6 이동 확인)은 이미 완료된 것으로 보입니다.

반드시 수정해야 할 문제(🔴)는 없었습니다.

View job run

…g 테스트, key·주석

- map-home/model/region-focus.ts `regionFocusTarget(data, selectedRegionCode, movedRegionCode)` 신설(+test 4) — 홈 effect는 호출 + ref 갱신만 (🟡)
- search/api/use-trending-query.test.ts 2건 — 형제 훅과 같은 QueryObserver 구동 (🟢)
- search-result-lists: 장소 key에 address 추가 — 같은 격자 동명 매장 충돌 방지 (🟢)
- search-screen: "지도 이동 없음" stale 주석 갱신 (🟢)
- --no-verify: pre-commit react-doctor 기존 파일 경고 2건. 게이트(vitest 1357·typecheck·oxlint·oxfmt·nose) 수동 통과, 실기 재확인

Claude-Session: https://claude.ai/code/session_012MtDBFuKP9N4wqmsuoXE7P
@claude

claude Bot commented Sep 7, 2026

Copy link
Copy Markdown

Claude finished @s13121312's task in 1m 38s —— View job


📋 코드 리뷰 완료 — 신규 커밋 5e51627

작업 목록

  • PR 컨텍스트 및 신규 diff 수집 (1f6f77f..5e51627)
  • region-focus.ts + 테스트 리뷰 (순수 함수 분리)
  • map-home-screen.tsx 변경분 리뷰
  • use-trending-query.test.ts 신규 테스트 리뷰
  • search-result-lists.tsx key 수정 리뷰
  • search-screen.tsx 주석 수정 확인
  • 인라인 코멘트 게시 (이번 커밋은 신규 이슈 없어 생략)
  • 최종 요약 코멘트 게시

리뷰 범위: git diff 1f6f77f..5e51627 (7파일, +145/-14). 이 커밋은 직전 라운드에서 제가 남긴 인라인 코멘트 4건에 대한 반영 커밋입니다 — diff만 보지 않고 관련 주변 코드(RegionExploreResponseDto 타입 정의, map-home-screen.tsx 이전/이후 컨텍스트)까지 Read로 확인했습니다.

이전 지적사항 반영 확인 (4/4)

  1. 🟡 지역 이동 목적지 로직 순수 함수 분리features/map-home/model/region-focus.tsregionFocusTarget(data, selectedRegionCode, movedRegionCode)로 뽑혔습니다. map-home-screen.tsx의 effect는 이 함수를 부르고 movedToRegionRef만 갱신하는 얇은 래퍼로 정리됐고, 로직 자체는 리팩터 전과 동일합니다(diff로 직접 대조). 테스트 4건(첫 격자 중심 이동 / 목록 미도착·빈 목록 / regionCode 에코 불일치 / 지역당 1회 이동)이 분기를 빠짐없이 덮습니다. RegionExploreResponseDto가 웹 생성물 재수출(sdk.ts barrel)이라 실제 타입(regionCode, grids[].gridX/gridY)과 테스트 픽스처가 일치하는지도 apps/web/src/shared/api/generated/types.gen.ts로 대조했습니다 — 문제 없습니다.
  2. 🟢 장소 결과 key 충돌key={\${place.gridId}-${place.name}-${place.address}`}로 수정. 바로 아래에서 여전히 쓰이는 index(구분선 border-b` 스타일 분기)는 그대로 남아 있어 미사용 변수 문제도 없습니다.
  3. 🟢 use-trending-query 테스트 부재 — 형제 훅(use-place-search-query·use-zones-query)과 같은 QueryObserver 구동 패턴으로 2건 추가(정상 언랩 / enabled=false일 때 미발사·data undefined 유지). 일관성 확보됐습니다.
  4. 🟢 stale 주석search-screen.tsx의 "지도 이동 없음" → "홈이 첫 격자 중심으로 지도 이동(A1 번복)"으로 갱신 확인.

신규 이슈

없습니다. 이번 커밋은 순수 리팩터 + 테스트 추가 + 주석 수정으로, 동작 변경이 없는 것을 diff 라인 단위로 확인했습니다.

⚠️ 참고: 이번 세션 환경에서 pnpm vitest/typecheck 실행이 승인되지 않아(bash 명령 차단) 정적 리뷰로만 진행했습니다. 신규 테스트(region-focus.test.ts, use-trending-query.test.ts)는 로직상 타당해 보이나 실제 실행 결과는 PR 본문의 체크리스트(pnpm --filter mobile test run 통과)로 갈음합니다.

이전 라운드(커밋 9b00b2d·7b89c77·2d94fd7·1f6f77f)에 대한 리뷰는 이미 완료되어 있으며, 이번 라운드에서 반드시 수정해야 할 문제(🔴)는 없었습니다.

@s13121312
s13121312 merged commit 43f425e into develop Sep 7, 2026
6 checks passed
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.

1 participant