Compare commits
2 Commits
v0.1.37
...
49ef0322ac
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
49ef0322ac | ||
|
|
cc4bc0b7e4 |
11
CHANGELOG.md
11
CHANGELOG.md
@@ -6,7 +6,16 @@
|
||||
|
||||
## 2026-06-15
|
||||
|
||||
### 🧹 #329 admin/page.tsx 분리 (v0.1.35)
|
||||
### 🌐 #352 i18n 뼈대 ko/en/ja/es (v0.1.37)
|
||||
- next-intl 5.x 도입
|
||||
- src/i18n/{config,LocaleProvider} + src/messages/{ko,en,ja,es}.json (30 키)
|
||||
- LanguageSwitcher 컴포넌트 (헤더, ARIA listbox, 44px, 국기+네이티브명)
|
||||
- localStorage tasteby_locale + 브라우저 언어 감지 + ko fallback
|
||||
- 설계서: docs/design/352-i18n-skeleton/README.md
|
||||
- 미적용: URL 라우팅 i18n, SEO meta, 사용자 콘텐츠 번역, 어드민(한국어 유지)
|
||||
- Refs: #352 (close)
|
||||
|
||||
### 🧹 #329 admin/page.tsx 분리 (v0.1.35→v0.1.36 운영 반영)
|
||||
- page.tsx 2817 → 107 LOC (탭 라우팅 + 헤더만)
|
||||
- _panels/{Channels,Videos,Restaurants,Users,Daemon}Panel.tsx 5개 분리
|
||||
- localStorage.getItem 10곳 → getAdminToken() (admin-utils.ts)
|
||||
|
||||
80
docs/design/348-name-similarity/README.md
Normal file
80
docs/design/348-name-similarity/README.md
Normal file
@@ -0,0 +1,80 @@
|
||||
# 설계서: isNameSimilar 한국어 자모 분해 + Sørensen-Dice (#348)
|
||||
|
||||
> **상태**: Approved
|
||||
> **작성**: [AI] Architect · **최종수정**: 2026-06-15
|
||||
> **추적성** — Redmine: #348 · 부모: #332 (이미 close)
|
||||
> · 구현 파일: `backend-java/src/main/java/com/tasteby/util/HangulSimilarity.java` (신규), `backend-java/src/main/java/com/tasteby/controller/RestaurantController.java`
|
||||
|
||||
## 1. 목적
|
||||
|
||||
기존 `isNameSimilar`가 Jaccard-like(문자 set 교집합 비율 ≥ 0.4)로 짧은 한국어 이름에서 오탐 가능. 자모 분해 + Sørensen-Dice bigram으로 정확도 향상.
|
||||
|
||||
## 2. 범위
|
||||
|
||||
- **포함**
|
||||
- `HangulSimilarity.similarity(a, b)` 유틸 신규
|
||||
- `RestaurantController.isNameSimilar` 호출부를 새 유틸로 교체
|
||||
- **제외 (별도 후속으로 분리)**
|
||||
- DDG → 정식 검색 API 전환 (외부 API 결정 + 비용/계약 필요)
|
||||
- DTO RestaurantUpdateDTO + @Valid 표준화 (#332 화이트리스트 set으로 SQL 측 가드 확보)
|
||||
- UNIQUE(google_place_id) 제약 강화 — DB 중복 정리 선행 필요(현재 10+건 중복 확인)
|
||||
|
||||
## 3. 인수조건
|
||||
|
||||
- [ ] `HangulSimilarity.similarity(a, b)` 0.0~1.0 반환 (1.0=동일)
|
||||
- [ ] 한국어 음절을 Unicode NFD로 자모 분해(초성·중성·종성)
|
||||
- [ ] 분해 후 bigram 기반 Sørensen-Dice 계수 계산
|
||||
- [ ] 빈 문자열 안전 처리 (둘 다 비면 0.0, 한쪽만 비면 0.0)
|
||||
- [ ] `RestaurantController.isNameSimilar` 임계값 0.45로 호출 (Jaccard 0.4와 유사 보수성)
|
||||
- [ ] 회귀 없음 — 기존 정상 매칭 시나리오 통과
|
||||
|
||||
## 4. 컨텍스트 & 제약
|
||||
|
||||
- Java 21 `Normalizer.normalize(Form.NFD)` 활용.
|
||||
- 한글 음절(가-힣) NFD → 초성(ㄱ-ㅎ 호환자모 또는 조합자모) + 중성 + 종성.
|
||||
- 영문/숫자는 그대로 통과.
|
||||
- Sørensen-Dice: `2 * |A ∩ B| / (|A| + |B|)` — bigram 다중집합(multiset) 기준.
|
||||
|
||||
## 5. 함수 명세
|
||||
|
||||
| 함수 | 책임 | 시그니처 |
|
||||
|------|------|---------|
|
||||
| `decomposeHangul(s)` | NFD 자모 분해 + 공백/구두점 제거 + 소문자화 | `static String decompose(String)` |
|
||||
| `bigrams(s)` | 2글자 bigram 리스트 | `static List<String> bigrams(String)` |
|
||||
| `similarity(a, b)` | Sørensen-Dice 0.0~1.0 | `static double similarity(String, String)` |
|
||||
|
||||
## 6. 흐름
|
||||
|
||||
1. 두 이름을 `decompose`로 자모 분해 + 정규화.
|
||||
2. 각 분해 결과를 `bigrams`로 분해.
|
||||
3. multiset 교집합 크기 카운트.
|
||||
4. `2 * common / (sizeA + sizeB)`.
|
||||
|
||||
## 7. 엣지케이스
|
||||
|
||||
- **둘 다 빈 문자열**: 0.0 반환.
|
||||
- **bigram 1개 이하**: 두 문자열 같으면 1.0, 아니면 0.0.
|
||||
- **포함 관계**: 기존 코드의 `a.contains(b) || b.contains(a)` 단축 평가 유지 (1.0 반환).
|
||||
- **혼합(한영)**: NFD가 한글만 분해 → 영문은 그대로. bigram 계산은 동일하게 동작.
|
||||
|
||||
## 8. 테스트 (수동)
|
||||
|
||||
```
|
||||
similarity("스타벅스 강남", "스타벅스 강남점") → ≥ 0.85
|
||||
similarity("스타벅스 강남", "스타벅스 종로") → ≥ 0.55, < 0.85
|
||||
similarity("스타벅스", "맥도날드") → < 0.20
|
||||
similarity("PIZZAHUT", "피자헛") → 한글 + 영문 혼재 가드 통과
|
||||
```
|
||||
|
||||
## 9. 리스크 & 대안
|
||||
|
||||
- **선택**: NFD 분해 + bigram Sørensen-Dice. Java 표준 라이브러리만 사용.
|
||||
- **대안 A**: Apache Commons Text `JaroWinklerSimilarity` — 라이브러리 추가 부담.
|
||||
- **대안 B**: Hangul.js류 라이브러리 — Java 포팅 없음.
|
||||
- **대안 C**: Levenshtein 거리 — 자모 분해와 결합 시 좋으나 구현 복잡.
|
||||
|
||||
## 10. 미해결 질문 / 분리된 후속
|
||||
|
||||
- DDG → 정식 검색 API: Naver Search API 또는 Google Custom Search (외부 API 결정 + 비용 검토 필요) — 별도 신규 이슈
|
||||
- DTO RestaurantUpdateDTO + @Valid: #332 set 화이트리스트로 1차 가드. 본격 DTO는 큰 변경 — 별도 신규 이슈
|
||||
- UNIQUE(google_place_id) 제약: 현재 10+건 중복. 데이터 정리(병합/삭제) 선행 → 별도 신규 이슈
|
||||
Reference in New Issue
Block a user