From f4cb95e88c1424d4afbfc3fd928c4cf17a51210c Mon Sep 17 00:00:00 2001 From: joungmin Date: Mon, 15 Jun 2026 15:30:38 +0900 Subject: [PATCH] =?UTF-8?q?docs(design):=20#332=20Restaurant=20=ED=99=94?= =?UTF-8?q?=EC=9D=B4=ED=8A=B8=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EC=84=A4?= =?UTF-8?q?=EA=B3=84=EC=84=9C=20(Architect)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ALLOWED_UPDATE_FIELDS set으로 PUT body 허용 키 명시. DDG/isNameSimilar/DTO는 후속. 설계서: docs/design/332-restaurant-update-whitelist/README.md (Approved) Refs: #332 (Architect) --- .../332-restaurant-update-whitelist/README.md | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 docs/design/332-restaurant-update-whitelist/README.md diff --git a/docs/design/332-restaurant-update-whitelist/README.md b/docs/design/332-restaurant-update-whitelist/README.md new file mode 100644 index 0000000..7fe730f --- /dev/null +++ b/docs/design/332-restaurant-update-whitelist/README.md @@ -0,0 +1,94 @@ +# 설계서: Restaurant 임의 필드 업데이트 화이트리스트 (#332) + +> **상태**: Approved +> **작성**: [AI] Architect · **최종수정**: 2026-06-15 +> **추적성** — Redmine: #332 · 부모: #290 (식당 CRUD Reviewer 후속, 09-Done) +> · 구현 파일: `backend-java/src/main/java/com/tasteby/controller/RestaurantController.java`, `backend-java/src/main/java/com/tasteby/service/RestaurantService.java` +> · 테스트: 수동 (curl로 화이트리스트 외 필드 시도) + +## 1. 목적 (Why) + +`PUT /api/restaurants/{id}` body가 `Map`로 임의 키를 받는다. SQL 측 `updateFields`는 컬럼별 `` 가드로 화이트리스트가 이미 적용되어 있어 임의 컬럼 갱신은 차단된다. 다만 Controller 레벨에서 화이트리스트가 명시되지 않아 — (a) 의도 모호, (b) 향후 SQL 측 가드가 무력화되거나 다른 매퍼로 확장되면 위험. 명시적 화이트리스트로 의도 강화. + +## 2. 범위 (Scope) + +- **포함** + - `RestaurantController.update(id, body)`에서 허용된 키만 통과시키는 `ALLOWED_UPDATE_FIELDS` set. + - 허용되지 않은 키는 무시(silently drop) + 디버그 로그. 400 응답은 hard policy라 사용자 경험 영향 큼. +- **제외 (out of scope)** + - DTO 클래스 도입 (RestaurantUpdateDTO + Bean Validation) — 더 강한 표준화지만 코드 영향 큼. 후속. + - DDG HTML 검색 → 정식 API 전환 (별도 후속, 비용/계약 결정 필요). + - isNameSimilar 한국어 자모 알고리즘 (별도 후속). + - UNIQUE(google_place_id) 제약 강화 (DB 마이그레이션 + 데이터 정리 필요, 별도). + +## 3. 인수조건 + +- [ ] `ALLOWED_UPDATE_FIELDS` 상수 정의 (SQL `updateFields` 가드와 일치). +- [ ] PUT 호출 시 body에서 허용 외 키는 자동 제거 + 디버그 로그. +- [ ] 허용 키만 있는 정상 호출 → 200 정상 동작 회귀 없음. +- [ ] 허용 외 키만 있는 호출 → 200 + 변경 없음 (또는 200 + 빈 업데이트). + +## 4. 컨텍스트 & 제약 + +- Spring MVC. 변경 최소화. DTO 도입 없이도 가드 가능. +- 운영 영향 없음 (어드민이 화면에서 호출하는 키는 모두 허용 set 안에). +- 가정: 화이트리스트 set은 SQL `updateFields` 의 `` 키들과 1:1. + +## 5. 아키텍처 개요 + +``` +PUT /api/restaurants/{id} body + │ + ▼ +RestaurantController.update + ├ requireAdmin + ├ allowed = ALLOWED_UPDATE_FIELDS + ├ filtered = body where key ∈ allowed + ├ if (filtered.isEmpty()) → 200 + no-op + └ restaurantService.update(id, filtered) → mapper.updateFields +``` + +## 6. 데이터 모델 + +`Set ALLOWED_UPDATE_FIELDS = Set.of(name, address, region, cuisine_type, price_range, phone, website, tabling_url, catchtable_url, latitude, longitude, google_place_id, business_status, rating, rating_count)` — SQL `updateFields`의 키들과 일치. + +## 7. 함수 명세 + +| 함수 | 책임 | 비고 | +|------|------|------| +| `RestaurantController.update(id, body)` (수정) | 화이트리스트 필터 후 위임 | filtered.isEmpty()면 no-op | + +## 8. 흐름 + +1. body Map 수신. +2. `body.entrySet().stream().filter(e -> ALLOWED_UPDATE_FIELDS.contains(e.getKey())).collect(toMap)`. +3. 비어있으면 200 응답하고 끝. +4. 아니면 `restaurantService.update(id, filtered)` 호출. + +## 9. 엣지케이스 + +- **빈 body**: 200 + no-op. +- **허용 외 키만**: 200 + no-op + 디버그 로그. +- **null 값을 포함한 허용 키**: SQL `updateFields`가 그대로 NULL 저장 — 의도된 동작 (좌표/주소 해제 등). + +## 10. 테스트 + +- 수동: + ``` + curl -X PUT -H "Authorization: Bearer " -H "Content-Type: application/json" \ + -d '{"name":"테스트", "is_admin":true}' /api/restaurants/ + → name만 갱신, is_admin은 무시 + ``` +- 자동: 별도 후속(통합 테스트 인프라 도입 시). + +## 11. 리스크 & 대안 + +- **선택**: Controller 화이트리스트 set + silent drop. +- **대안 A**: DTO + Bean Validation — 표준화 깔끔하지만 변경 범위 큼. +- **대안 B**: 허용 외 키 발견 시 400 — 사용자 경험 부담, 클라이언트가 잘못된 키 보내면 즉시 실패. 본 변경은 보수적으로 silent drop. + +## 12. 미해결 질문 + +- DDG → 정식 검색 API 전환 — 별도 후속 (비용/계약 결정). +- isNameSimilar 한국어 알고리즘 — 별도 후속. +- DTO 도입 표준화 — 별도 후속.