Commit Graph

152 Commits

Author SHA1 Message Date
joungmin
fd4ab18fbf [QA] #545 해외 식당 예약 버튼 숨김 — 회귀 테스트 추가 및 검증 통과
RestaurantDetail.tsx의 isKoreaRestaurant() 게이트가 설계서 인수조건과
일치함을 확인. 국내/해외/URL-NONE/좌표없음 fallback 등 5개 경계 케이스를
자동화 RTL 테스트로 검증(수동 육안 QA를 대체). tsc 및 전체 jest 스위트
통과.

Refs #545
2026-07-27 13:50:32 +09:00
joungmin
622ea58cd1 [QA] #532 formatRegion 검증 통과 — 단위 테스트 추가
- frontend/__tests__/region.test.ts: 설계서(docs/design/532-region-display)
  엣지케이스 8종(falsy/계층조인/null토큰/빈토큰/더미제거/단일토큰/전량더미/trim) 검증
- tsc --noEmit, jest 전체 스위트 통과 확인
- 실제 dev DB region 데이터로 엣지케이스 재확인

Refs #532
2026-07-27 13:49:26 +09:00
joungmin
7bf21b5b45 docs(changelog): #545/#533/#532 설계서·Redmine 현행화 기록
Refs #545, #533, #532
2026-07-19 09:10:12 +09:00
joungmin
6754976e72 [Architect] #545 예약버튼 국내 게이트 설계서 소급 작성
- Developer 구현(5d62866) 이후 설계서 게이트 위반 보완
- isKoreaRestaurant() 적용 범위/근거를 구현과 일치하도록 문서화

Refs #545
2026-07-19 09:09:50 +09:00
joungmin
5d62866941 [Developer] #545 해외 식당 테이블링/캐치테이블 버튼 숨김
- RestaurantDetail.tsx 예약 버튼 2종을 isKoreaRestaurant() 게이트로 감쌈
- 테이블링/캐치테이블은 국내 전용 — 지도 링크와 동일 판정 기준 적용

Refs #545
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 16:52:41 +09:00
joungmin
7639fd009d [Developer] #533 언급 적은 식당 제외 필터 (min_mentions + video_count)
- backend: findAll 응답에 video_count(strong/unknown 링크수) + min_mentions 서버필터
  - Restaurant.videoCount, RestaurantMapper(xml/iface), Service, Controller(+cache key)
- frontend: Restaurant.video_count 타입 + 데스크톱 툴바 "언급"(2/3/5회+) 클라 필터
- 검증: min_mentions=2→116, =3→36 (위반 0). 빌드/tsc 통과
- 설계서 docs/design/533-min-mentions-filter/README.md

Refs #533
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 16:23:46 +09:00
joungmin
4760b5a284 [Developer] #532 지역 표시 계층화 formatRegion (한국|서울 → 한국 › 서울)
- 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) <noreply@anthropic.com>
2026-06-28 16:15:53 +09:00
joungmin
2eb16ce861 fix(map): 클러스터 expansion 후 마커 클릭 시 재묶임 방지
- selected 시 무조건 zoom 16 → 줌 18에서 마커 클릭하면 다시 16으로 줄어 클러스터링
- 현재 zoom ≥ 16이면 유지, 미만이면 16으로 (Naver/Google 둘 다)
- page.tsx의 setRegionFlyTo 호출 제거 — selected useEffect로 일원화

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
v0.1.66
2026-06-17 10:09:22 +09:00
joungmin
14384b0c71 fix(map): NaverMap 클러스터 클릭 시 중앙 이동 — morph로 통일
- panTo(애니메이션) + setZoom(z, true)(애니메이션) 동시 호출이 서로 cancel
- 단일 식당은 줌 변화 없어서 가려졌고 클러스터는 줌 크게 변경 → 가시화
- SDK의 morph(latlng, zoom)으로 center+zoom 동시 변경 일관 처리
- flyTo / selected / cluster 클릭 모두 동일 패턴

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
v0.1.65
2026-06-17 09:55:51 +09:00
joungmin
250b067d87 fix(map): NaverMapView selected 변경 시 자동 panTo + zoom
- GoogleMapView에는 있던 useEffect [selected] 패턴이 NaverMapView에 누락
- 마커/클러스터/리스트 어디서 선택해도 그 식당이 중앙 + zoom 16

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
v0.1.64
2026-06-17 09:42:37 +09:00
joungmin
6a885c5203 docs(changelog): v0.1.63 미커밋 잡변경 정리 5건
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
v0.1.63
2026-06-16 19:35:24 +09:00
joungmin
a9dc1dad6a chore(dev): PM2 frontend PORT=3001 env + reviews/screenshots gitignore
- tasteby-web: PORT=3001 명시 (env 누락 → 기본 3000으로 충돌 위험)
- reviews/, screenshots/ 로컬 작업 산출물 무시

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-16 19:34:58 +09:00
joungmin
21eb1e9562 docs(deploy): OKE 배포 가이드 추가
- Colima(로컬 ARM64 Docker) → OCIR → OKE 파이프라인 전체 절차
- deploy.sh 사용법, kubectl context, secret 등록 흐름

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-16 19:34:58 +09:00
joungmin
94be5a81e6 feat(frontend): 아이콘 시스템 일관성 + Phosphor 마이그레이션
- Icon.tsx: 크기 고정 + flex center로 정렬 일관성
- cuisine-icons.ts: Tabler → Phosphor 매핑(BowlFood/FishSimple/Pizza 등)
- FoodIcon.tsx 신규: 한식 specific 자체 SVG (찌개/전골/탕)
- frontend/docs/brand-guide.md 신규: 브랜드 아이덴티티 + 디자인 시스템 정책

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-16 19:34:58 +09:00
joungmin
1d767bee37 feat(util): JsonUtil.normalizeEvaluation — 평문→JSON 래핑 + 300자 제한
- evaluation 컬럼이 IS JSON 제약이라 평문은 {"text":"..."}로 정규화
- parseMap이 잘못된 JSON 받았을 때 빈 Map 대신 {"text":원문}으로 보존
- PipelineService/RestaurantService에서 이미 호출 중인 유틸 — 미커밋 상태였음

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-16 19:34:58 +09:00
joungmin
0676a31cfd feat(admin): 사용자 어드민 권한 토글 backend 연결
- AdminUserController.updateAdmin이 호출하던 service/mapper 미커밋 상태였음
- CORS allowedMethods에 PATCH 추가 (PATCH /api/admin/users/{id}/admin)
- UserMapper.updateAdmin + XML (is_admin 0/1)
- UserService.updateAdmin (트랜잭션 + 404 가드)
- findAllWithCounts에 is_admin 컬럼 SELECT 추가

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-16 19:34:58 +09:00
joungmin
1164139312 feat(map): 식당 선택 시 지도 자동 줌인/이동
- 리스트/검색결과에서 식당 클릭 → setRegionFlyTo로 그 좌표 + zoom 16
- NaverMap/GoogleMap dispatcher 둘 다 panTo + setZoom
- 마커 selected 강조(1.15× 파란 박스)와 함께 동작

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
v0.1.62
2026-06-16 17:29:28 +09:00
joungmin
78f7e83a0e feat(map): NaverMapView 마커에 식당명 박스 (GoogleMap 동일)
- 단순 동그라미 → 박스+화살표+식당명+cuisine 아이콘 핀
- 채널별 bg/text/border/arrow 색상
- 폐업(CLOSED_*) 회색 + 취소선, selected 강조 (1.15× + 파란박스)
- InfoWindow 제거 (식당명이 박스로 보임)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
v0.1.61
2026-06-16 17:13:37 +09:00
joungmin
247547c516 feat(map): NaverMapView 채널별 마커 색상
- GoogleMapView와 동일 팔레트 (8 색)
- 식당 첫 채널 기준 색상, activeChannel 있으면 우선
- getChannelColorMap 재사용 패턴 동일

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
v0.1.60
2026-06-16 17:09:02 +09:00
joungmin
8de8696424 perf(map): NaverMapView SDK 네이티브 마커 + InfoWindow
- React absolute div overlay → naver.maps.Marker 네이티브 교체
- 줌/팬 시 SDK GPU 최적화로 랙 해소
- 식당명 InfoWindow (마커 클릭 시 표시)
- bounds_changed → idle 이벤트로 sync 빈도 감소
- 클러스터도 네이티브 마커 (HTML 콘텐츠 숫자)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
v0.1.59
2026-06-16 16:58:03 +09:00
joungmin
a4de9ba87b feat(admin): #356 후속 — verifyAllAsync로 즉시 응답
- 1244 링크 백필 동기 호출 시 HTTP 25~30분 hang (ingress 5분 timeout 초과)
- @Async verifyAllAsync 추가, controller는 started/pending 즉시 응답
- 진행은 /api/admin/video-relevance/pending 폴링으로 확인

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
v0.1.58
2026-06-16 15:15:48 +09:00
joungmin
cf37e496d4 docs(design): #363 실 운영 fix 기록 (1~2단계 + v0.1.57 안정화)
- 배포 흐름 v0.1.51~v0.1.57 정리
- 운영 진단 확인사항 (NCLOUD 도메인 형식, Search/Maps 별개 시스템)
- NaverMapView 안정화 핵심 결정사항 (divRef 항상 마운트, ResizeObserver, try/catch)
- 후속 분리 항목 명시

Refs: #363

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-16 10:56:51 +09:00
joungmin
ce3e34938c fix(map): NaverMapView 안정화 — divRef 항상 마운트 + ResizeObserver
- 이전: ready 가드로 첫 렌더 시 ref 누락 가능 → SDK가 div 못 잡음
- 명시적 width/height + 회색 배경(시각적 로딩 표시)
- ResizeObserver + rAF로 컨테이너 0×0 → 정상 크기 시 m.refresh
- try/catch + initError로 init 실패 가시화

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
v0.1.57
2026-06-16 10:38:57 +09:00
joungmin
5199475d67 revert(map): NaverMap 임시 비활성, 한국도 GoogleMap fallback
- NaverMapView 골격이 실 운영에서 지도/마커 렌더 실패
- 환경변수 비워 dispatcher가 GoogleMap fallback (회귀 0)
- NaverMapView 코드는 유지 — 후속 안정화 작업 후 재활성

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
v0.1.55 v0.1.56
2026-06-16 10:32:54 +09:00
joungmin
bd8d82dd5d fix(stats): /api/stats/visits 500 — Mapper resultType int→long
- StatsMapper interface는 long 반환인데 XML resultType이 int
- Integer를 primitive long으로 cast 못 함 → ClassCastException → 500

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
v0.1.54
2026-06-16 10:26:15 +09:00
joungmin
bc83923261 fix(map): NaverMap 인증 파라미터 ncpClientId → ncpKeyId
- NCLOUD 신 정책: ncpKeyId 사용 (navermaps/maps.js.ncp 공식)
- 인증 200/Failed 진짜 원인 — 도메인 등록 정확했으나 파라미터 차이
- Refs: navermaps/maps.js.ncp

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
v0.1.53
2026-06-16 10:03:09 +09:00
joungmin
f17ba9e37a feat(map): #363 메인 지도 SDK 국내(네이버)/해외(구글) 분기
- MapView dispatcher: NAVER 키 + KR bbox 좌표 → NaverMapView
- NaverMapView 신규 (네이버 v3 직접 wrapper, Supercluster 재사용)
- GoogleMapView 신규 (기존 MapView 내용 rename)
- MapView.types.ts 공용 타입 + isKoreaCoord 헬퍼
- Dockerfile/deploy.sh: NEXT_PUBLIC_NAVER_MAP_CLIENT_ID build-arg
- 키 미설정 시 GoogleMap fallback (회귀 0)

Refs: #363

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
v0.1.52
2026-06-16 06:25:47 +09:00
joungmin
7789671fbc feat(map): 식당 상세 지도 링크 국내/해외 분기 (1단계)
- 좌표 기반 한국 판정 (KR bbox 33~38.7°N, 124~132°E)
- 국내: 네이버 지도(/p/search/) primary + Google Maps 보조
- 해외: Google Maps 단독
- 좌표 없으면 region 첫 토큰 fallback

2단계(메인 지도 탭 SDK 분기)는 별도 후속.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
v0.1.51
2026-06-16 05:59:24 +09:00
joungmin
c5b0216a37 fix(catchtable): URL 패턴을 /ct/shop/, /ct/dining/으로 교정
- 실제 캐치테이블은 app.catchtable.co.kr/ct/shop/... 형식
- 옛 /shop/, /dining/ 패턴은 contains 매칭 실패 → 첫 회차 1044건 전부 미발견
- 패턴 교정 후 NONE 해제 + 재실행 필요

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
v0.1.50
2026-06-16 01:35:18 +09:00
joungmin
40e448fe95 fix(search): WebSearchService HTTP timeout 추가 (connect 5s, request 15s)
- 특정 검색에서 무한 hang → backend virtual thread 점유로 후속 벌크 작업 중단
- Naver/DDG 둘 다 timeout 적용
- 타임아웃 시 HttpTimeoutException → 호출자(bulk)에서 notfound 안전 처리

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
v0.1.49
2026-06-15 21:52:31 +09:00
joungmin
8a21646031 fix(admin): bulk-tabling/catchtable SSE timeout 10분 → 3시간
- 대량 백필(700+건 ≈ 100분) 시 10분 SSE timeout으로 중간 끊김
- emit() 실패 시 마지막 cache.flush + complete 누락 → 3시간으로 확장

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
v0.1.48
2026-06-15 21:08:55 +09:00
joungmin
52090057de fix(restaurant): #357 후속 — tabling-url validation에 www. 호스트 허용
- Naver/DDG 결과가 www.tabling.co.kr 형태인데 PUT validation에서 거부됨
- bulk-tabling SSE는 validation 없이 통과 — 불일치 해소
- catchtable은 이미 app/www 둘 다 허용 (기존)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
v0.1.47
2026-06-15 21:02:32 +09:00
joungmin
d73947444f feat(backend): #359 1단계 — google_place_id 중복 조회 API
- GET /api/admin/restaurants/duplicates/place-id (어드민 전용)
- 그룹별 식당 목록 + video/review/memo 카운트 동봉
- Mapper: findDuplicatePlaceIdRows + Service 그룹핑
- 정리/병합 + UNIQUE 제약은 데이터 위험 분리 위해 후속 PR로

Refs: #359 (조회 단계 완료)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
v0.1.46
2026-06-15 20:32:40 +09:00
joungmin
c1050f3abd feat(backend): #358 RestaurantUpdateDTO + @Valid 표준화
- dto/RestaurantUpdateDTO record 신규 (15 필드, 모두 nullable)
- @Size/@Pattern(URL or NONE)/@DecimalMin·Max/@Min·Max
- RestaurantController.update 시그니처 Map → @Valid DTO 교체
- toFieldMap()으로 null 제외 후 기존 Service.update 호출 (회귀 0)
- #332 ALLOWED_UPDATE_FIELDS Set 제거 (DTO 필드 자체가 화이트리스트)

Refs: #358 (close)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
v0.1.45
2026-06-15 20:20:51 +09:00
joungmin
a504bf8ee5 feat(backend): #357 DDG → Naver Search 정식 API + DDG 폴백
- WebSearchService 신규 (Naver webkr.json 우선, 키 미설정/실패 시 DDG)
- RestaurantController.searchTabling/searchCatchtable 내부 호출 교체
- 인라인 DDG 80줄 제거, 미사용 import 정리
- app.naver.client-id/secret 추가 (env: NAVER_CLIENT_ID/SECRET)
- k8s secrets template에 NAVER 키 항목

Refs: #357 (close)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
v0.1.44
2026-06-15 20:16:14 +09:00
joungmin
f1164b63c5 docs(design): #357 정식 검색 API 전환 설계서 (Architect)
- WebSearchService 신규: Naver Search webkr.json 우선, DDG 폴백
- searchTabling/searchCatchtable 내부 호출만 교체 (시그니처 유지)
- application.yml + k8s secrets에 NAVER_CLIENT_ID/SECRET 추가

Refs: #357

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-15 20:11:52 +09:00
joungmin
47020fd649 feat(backend): #356 영상-식당 관련도 LLM 평가
- DB V20260615b: video_restaurants.{relevance, relevance_reason, relevance_evaluated_at} + idx_vr_relevance
- VideoRelevanceService (#322 패턴): @Async verifyAsync + verify + verifyAll(batchSize)
- PipelineService.processExtract → linkVideoRestaurant 후 verifyAsync(linkId) 자동 트리거
- GET /api/restaurants/{id}/videos: 기본 strong/unknown만, ?include_weak=true 시 모두 + relevance/reason
- AdminVideoRelevanceController: GET pending / POST all / POST {id}/evaluate / PATCH {id}
- 캐시 키 strong|all 분리, LLM 실패 시 unknown 안전 기본값(표시 유지)

Refs: #356 (close)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
v0.1.43
2026-06-15 19:38:07 +09:00
joungmin
88bbf3ca25 docs(design): #356 영상-식당 관련도 LLM 평가 설계서 (Architect)
video_restaurants.relevance(strong/weak/incidental/unknown) 컬럼 + VideoRelevanceService.
findVideoLinks에 includeWeak 파라미터. 어드민 4개 API.
#322 식당 LLM 검증과 동일 패턴.

설계서: docs/design/356-video-relevance-llm/README.md (Approved)
Refs: #356 (Architect)
2026-06-15 19:24:19 +09:00
joungmin
8152b71119 docs(changelog): v0.1.42 #351 SSE 통일 기록 2026-06-15 17:17:14 +09:00
joungmin
d6ee62230e refactor(admin): #351 SSE 6곳 consumeSseStream으로 통일
VideosPanel:
- bulkTranscript/bulkExtract: 단일 SSE 핸들러 → consumeSseStream
- rebuildVectors: consumeSseStream
- remapCuisine / remapFoods: consumeSseStream

RestaurantsPanel:
- bulkTabling / bulkCatchtable: consumeSseStream

이전: 각 호출이 자체적으로 reader+decoder+buf.split+match 6곳 복제.
이제: lib/admin-utils.ts의 consumeSseStream(resp, onEvent)으로 일관 처리.

빌드 + npm test 13/13 통과. 회귀 없음.

Refs: #351
v0.1.41 v0.1.42
2026-06-15 17:15:35 +09:00
joungmin
cf1055bdf9 docs(changelog): v0.1.40 #343 테스트 인프라 기록 2026-06-15 16:29:22 +09:00
joungmin
2580414790 build(npm): #343 lock 재생성 (Jest 30 + @testing-library/* 동기화) v0.1.39 v0.1.40 2026-06-15 16:26:52 +09:00
joungmin
730727a7a6 test(frontend): #343 Jest+RTL 인프라 + ARIA Tabs + remotePatterns
테스트 인프라:
- Jest 30 + jest-environment-jsdom + RTL + jest-dom matchers
- next/jest로 SWC/Next.js 자동 통합
- jest.config.ts (setupFilesAfterEnv) + jest.setup.ts
- npm scripts: test, test:watch
- 샘플 테스트 3개, 13/13 통과:
  - i18n/config: isLocale + detectBrowserLocale (5 케이스)
  - Stars 컴포넌트: 별점/aria/clamp/showNumber (5 케이스)
  - admin-utils: getAdminToken + authHeaders (4 케이스)

ARIA Tabs (MyReviewsList):
- role=tablist + tab + aria-selected + aria-controls + tabIndex
- panel에 role=tabpanel + aria-labelledby

next/image:
- next.config.ts remotePatterns: lh3.googleusercontent.com / i.ytimg.com / yt3.ggpht.com
- ReviewSection의 user_avatar_url에 명시적 eslint-disable + 사유

후속(별도): 전체 컴포넌트 테스트 점진 추가, 백엔드 JUnit 인프라, E2E (Playwright), CI 통합

설계서: docs/design/343-frontend-test-infra/README.md

Refs: #343
2026-06-15 16:25:55 +09:00
joungmin
9ba905aad8 docs(design): #343 RTL/Jest 인프라 + next/image + ARIA Tabs 설계서 (Architect)
next/jest + RTL 도입, 샘플 테스트 3개(Stars + i18n config), remotePatterns,
MyReviewsList Tabs ARIA. 백엔드 JUnit/E2E/CI는 후속.

설계서: docs/design/343-frontend-test-infra/README.md (Approved)
Refs: #343 (Architect)
2026-06-15 16:17:23 +09:00
joungmin
8c4b0c3e9a docs(changelog): v0.1.38 #348 isNameSimilar 한국어 기록 2026-06-15 16:12:46 +09:00
joungmin
3815221535 feat(util): #348 isNameSimilar 한국어 자모 + Sørensen-Dice
- HangulSimilarity 유틸 신규
  - decompose: Unicode NFD 분해 (한글 음절 → 초성/중성/종성)
  - 공백·구두점 제거 + 소문자화
  - bigram multiset 기반 Sørensen-Dice 계수
  - 빈 입력/포함 관계 가드
- RestaurantController.isNameSimilar 임계값 0.45 (이전 Jaccard 0.4와 유사 보수성)
- 기존 normalize 헬퍼 제거 (HangulSimilarity 내부로 이동)

DDG/DTO/UNIQUE는 별도 후속:
- 외부 검색 API 선정 (Naver/Kakao/Google CSE)
- RestaurantUpdateDTO + @Valid
- google_place_id 중복 정리 후 UNIQUE 제약

설계서: docs/design/348-name-similarity/README.md

Refs: #348 (Developer 단계)
v0.1.38
2026-06-15 16:10:44 +09:00
joungmin
49ef0322ac docs(design): #348 isNameSimilar 자모 + Sørensen-Dice (Architect)
NFD 자모 분해 + bigram 기반 Sørensen-Dice 계수로 한국어 정확도 향상.
DDG/DTO/UNIQUE는 별도 후속(외부 API/큰 리팩터링/DB 데이터 정리 필요).

설계서: docs/design/348-name-similarity/README.md (Approved)
Refs: #348 (Architect)
2026-06-15 16:09:19 +09:00
joungmin
cc4bc0b7e4 docs(changelog): #352 i18n 뼈대 + v0.1.37 기록 2026-06-15 16:02:40 +09:00
joungmin
515f5c1d1a build(npm): #352 package-lock 재생성 (next-intl + @swc/helpers 동기화) v0.1.37 2026-06-15 15:59:44 +09:00
joungmin
6cbf7feaf5 feat(i18n): #352 다국어 뼈대 ko/en/ja/es
- next-intl 5.x 도입
- src/i18n/config.ts: LOCALES 상수, detectBrowserLocale, LOCALE_LABELS(국기/네이티브명)
- src/i18n/LocaleProvider.tsx: NextIntlClientProvider wrap + localStorage tasteby_locale 저장
- src/messages/{ko,en,ja,es}.json: 초기 30개 키 (header/actions/filter/restaurant/review 5 카테고리)
- src/components/LanguageSwitcher.tsx: 헤더용 드롭다운 (국기 + native, ARIA listbox, 44px 터치)
- providers.tsx: LocaleProvider로 AuthProvider 감싸기
- page.tsx 헤더에 LanguageSwitcher 배치

설계서: docs/design/352-i18n-skeleton/README.md (Approved)

언어 선택 근거:
- ko: 기본
- en: 글로벌 1순위
- ja: 일본 사용자 + 한국 음식 관광
- es: 5억 화자, 라틴아메리카 + 스페인 확장

미번역 키는 ko fallback. URL 라우팅(/en/)/SEO meta/사용자 콘텐츠 번역은 후속.

Refs: #352
2026-06-15 15:58:21 +09:00