From 4760b5a284783ab19af5014233ec80543e23d5c9 Mon Sep 17 00:00:00 2001 From: joungmin Date: Sun, 28 Jun 2026 16:15:53 +0900 Subject: [PATCH] =?UTF-8?q?[Developer]=20#532=20=EC=A7=80=EC=97=AD=20?= =?UTF-8?q?=ED=91=9C=EC=8B=9C=20=EA=B3=84=EC=B8=B5=ED=99=94=20formatRegion?= =?UTF-8?q?=20(=ED=95=9C=EA=B5=AD|=EC=84=9C=EC=9A=B8=20=E2=86=92=20?= =?UTF-8?q?=ED=95=9C=EA=B5=AD=20=E2=80=BA=20=EC=84=9C=EC=9A=B8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - lib/region.ts formatRegion() 신설: 파이프 분리 + 빈토큰/null/나라 더미 제거 + ' › ' 조인 - RestaurantDetail/RestaurantList 지역 표기에 적용 - buildSearchQuery(검색용 공백조인) 유지 - 설계서 docs/design/532-region-display/README.md Refs #532 Co-Authored-By: Claude Opus 4.8 (1M context) --- CHANGELOG.md | 9 ++++ docs/design/532-region-display/README.md | 43 ++++++++++++++++++++ frontend/src/components/RestaurantDetail.tsx | 3 +- frontend/src/components/RestaurantList.tsx | 3 +- frontend/src/lib/region.ts | 22 ++++++++++ 5 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 docs/design/532-region-display/README.md create mode 100644 frontend/src/lib/region.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c44e9a..a682b5a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,15 @@ --- +## 2026-06-28 + +### 🐛 지역 표시 계층화 — "한국|서울" → "한국 › 서울" (#532) +- 문제: 식당 상세/리스트가 region 원본(`한국|서울|강남구`, 파이프 노출)을 그대로 표시 +- 해결: 공용 유틸 `lib/region.ts` `formatRegion()` 신설 — 빈토큰/`null`/더미(`나라`) 제거 후 ` › ` 계층 조인 +- 적용: `RestaurantDetail.tsx`(지역 표기), `RestaurantList.tsx`(카드 지역) +- 검색 쿼리용 `buildSearchQuery`(공백 조인)는 유지 — 외부 지도 검색 호환 +- 설계서: `docs/design/532-region-display/README.md` + ## 2026-06-17 ### 🎯 클러스터 expansion 후 마커 클릭 시 재묶임 방지 (v0.1.66) diff --git a/docs/design/532-region-display/README.md b/docs/design/532-region-display/README.md new file mode 100644 index 0000000..3888270 --- /dev/null +++ b/docs/design/532-region-display/README.md @@ -0,0 +1,43 @@ +# #532 — 지역 표시 계층화 (formatRegion) + +## 배경 / 문제 +지역(`region`)은 `나라|시도|구군` 파이프 구분 문자열(예: `한국|서울|강남구`). +사용자 화면에 **원본이 그대로 노출**되어 `한국|서울|강남구`(파이프) 또는 평문 `한국 서울`로 보임. +기대: **`한국 › 서울 › 구군`** 계층(breadcrumb) 표시. + +## 범위 +- 식당 상세 시트의 "지역" 표기 (`RestaurantDetail.tsx`) +- 식당 리스트 카드의 지역 표기 (`RestaurantList.tsx`) +- 공용 유틸 `formatRegion()` 신설 (`lib/region.ts`) + +비범위: 지역 *필터* 동작/정렬(정상), 외부 지도 검색 쿼리(`buildSearchQuery` — 공백 조인 유지), 데이터 정합성(별도 이슈). + +## 함수 설계 + +### `formatRegion(region: string | null | undefined): string` +- **입력**: 파이프 구분 지역 문자열 (예: `"한국|서울|강남구"`, `"일본|null"`, `"한국||관악구"`, `""`) +- **처리**: + 1. falsy → `""` 반환 + 2. `split("|")` → 각 토큰 `trim()` + 3. 빈 토큰, `"null"`(대소문자 무시), 더미 `"나라"` 제거 + 4. 남은 토큰을 `" › "`(U+203A)로 조인 +- **출력 예**: + | 입력 | 출력 | + |---|---| + | `한국\|서울\|강남구` | `한국 › 서울 › 강남구` | + | `일본\|null` | `일본` | + | `한국\|\|관악구` | `한국 › 관악구` | + | `""` / `null` | `""` (호출부에서 미표시) | +- **순수 함수**(I/O 없음) → 단위 테스트 용이. + +### 적용 +- `RestaurantDetail.tsx:132`: `{restaurant.region}` → `{formatRegion(restaurant.region)}` +- `RestaurantList.tsx:54`: `{r.region}` → `{formatRegion(r.region)}` + +## 엣지케이스 +- 모든 토큰이 더미/빈 값(`"한국|"`, `"|"`) → 유효 토큰만 남김(`한국`) 또는 `""`. 호출부는 `region &&` 가드가 있어 `""`이면 미표시. +- 단일 토큰(`"한국"`) → `한국` 그대로. + +## 검증 +- 단위 테스트(가능 시) 또는 dev 화면에서 상세/리스트 지역 표기가 `›` 계층으로 보이는지. +- `npm run build`(또는 tsc/lint) 통과. diff --git a/frontend/src/components/RestaurantDetail.tsx b/frontend/src/components/RestaurantDetail.tsx index 790df49..22f1d92 100644 --- a/frontend/src/components/RestaurantDetail.tsx +++ b/frontend/src/components/RestaurantDetail.tsx @@ -7,6 +7,7 @@ import ReviewSection from "@/components/ReviewSection"; import MemoSection from "@/components/MemoSection"; import { RestaurantDetailSkeleton } from "@/components/Skeleton"; import Icon from "@/components/Icon"; +import { formatRegion } from "@/lib/region"; interface RestaurantDetailProps { restaurant: Restaurant; @@ -129,7 +130,7 @@ export default function RestaurantDetail({ )} {restaurant.region && (

- 지역 {restaurant.region} + 지역 {formatRegion(restaurant.region)}

)} {restaurant.price_range && ( diff --git a/frontend/src/components/RestaurantList.tsx b/frontend/src/components/RestaurantList.tsx index a9aca30..5431891 100644 --- a/frontend/src/components/RestaurantList.tsx +++ b/frontend/src/components/RestaurantList.tsx @@ -3,6 +3,7 @@ import type { Restaurant } from "@/lib/api"; import { getCuisineIcon } from "@/lib/cuisine-icons"; import Icon from "@/components/Icon"; +import { formatRegion } from "@/lib/region"; import { RestaurantListSkeleton } from "@/components/Skeleton"; interface RestaurantListProps { @@ -51,7 +52,7 @@ export default function RestaurantList({ {r.name} {r.region && ( - {r.region} + {formatRegion(r.region)} )} {r.rating && ( ★ {r.rating} diff --git a/frontend/src/lib/region.ts b/frontend/src/lib/region.ts new file mode 100644 index 0000000..7924478 --- /dev/null +++ b/frontend/src/lib/region.ts @@ -0,0 +1,22 @@ +// 지역 표시 유틸 — #532 +// region 은 "나라|시도|구군" 파이프 구분 문자열. 사용자에게는 계층(breadcrumb)으로 보여준다. + +/** 표시에서 제외할 더미/무의미 토큰. */ +const DUMMY_TOKENS = new Set(["", "null", "나라"]); + +/** + * 파이프 구분 region 을 "나라 › 시도 › 구군" 계층 문자열로 변환한다. + * 빈 토큰·"null"·더미("나라")는 제거한다. 유효 토큰이 없으면 "". + * + * 예) "한국|서울|강남구" → "한국 › 서울 › 강남구" + * "일본|null" → "일본" + * "한국||관악구" → "한국 › 관악구" + */ +export function formatRegion(region: string | null | undefined): string { + if (!region) return ""; + return region + .split("|") + .map((t) => t.trim()) + .filter((t) => !DUMMY_TOKENS.has(t.toLowerCase())) + .join(" › "); +}