#114(S15P11A705-323, 이미지 기반 장소·맥락 제안)를 머지 후 살펴보다 발견한 후속 항목입니다. 기능 자체는 잘 동작하고, 아래는 전부 "붙여야 할 마감" 성격입니다. 우선순위 순으로 적었습니다.
기능 전체를 @ghkim1632 님이 맡고 계셔서 배정합니다. 3번은 백엔드 확인이 필요할 수 있습니다.
1. /dev/place-record 프리뷰 라우트가 프로덕션 번들에 그대로 실린다 🔴
src/app/router.tsx의 placeRecordPreviewRoute에 import.meta.env.DEV 가드도, 다른 라우트가 쓰는 beforeLoad 인증 가드도 없습니다. 배포된 앱에서 URL만 알면 누구나 열립니다.
문제는 라우트 존재 자체보다 그 안의 버튼입니다. PlaceRecordPreviewPage.tsx의 "테스트 세션 발급"이 이걸 호출합니다.
await fetch('/api/core/v1/dev/auth/login', {
method: 'GET',
credentials: 'include',
});
credentials: 'include'로 세션 쿠키를 받고 memberId를 화면에 띄웁니다. 운영 백엔드가 /dev/auth/login을 노출하고 있다면 로그인 없이 세션을 발급받는 경로가 열려 있는 것입니다. 프론트만으로는 판단할 수 없으니 확인이 필요합니다.
2. 서버가 보낸 warnings·kakaoSearch.status를 아무도 읽지 않는다 🟡
suggestPlacesFromImage.ts가 Zod로 파싱은 하는데, 소비하는 코드가 한 줄도 없습니다(grep으로 확인).
kakaoSearch: { status: 'SUCCESS' | 'NO_RESULTS' | 'FAILED', query, items }
warnings: [{ code, message, candidateId }]
PlaceRecordSheet는 candidate.kakaoSearch.items만 꺼내 쓰고 status로 분기하지 않습니다. 서버가 "이 후보는 카카오 검색이 실패했다"(FAILED)고 알려줘도 UI는 items: []와 구분하지 못하고, 사용자에게는 그냥 후보가 비어 보입니다. warnings도 통째로 버려집니다.
keywords: []를 오류로 처리하지 말라는 규칙의 반대편 실수입니다 — 정상이 아닌 신호를 정상처럼 넘기고 있습니다.
3. POST /places/suggestions 계약이 어느 문서에도 없다 🟡
docs/api-contract.md에도, 원본 docs/reference/08_API_명세.md에도 이 엔드포인트가 없습니다(grep으로 확인). 현재 근거는 #109 이슈뿐입니다.
AGENTS.md가 "문서에 없는 정책·엔드포인트를 추측해서 구현하지 않는다"를 절대 금지로 두고 있어서, 지금 상태로는 다른 사람이 이 코드를 만졌을 때 기댈 근거가 없습니다. 이미 머지됐으니 사후에 계약을 문서로 고정하는 쪽이 맞겠습니다.
전례가 있습니다 — 표지 생성 API(/image/api/*)는 원본 명세 §7.7이 front#99 가이드로 위임하고, api-contract.md가 "이 절만 근거가 원본 docs가 아니라 front#99"라고 명시하는 형태입니다. 같은 패턴으로 절을 하나 만들면 됩니다.
스키마는 코드에서 읽어낼 수 있으니 제가 초안을 쓸 수 있습니다. 다만 아래는 추측하면 안 되는 항목이라 확인이 필요합니다.
4. httpClient multipart 관용구가 문서에 없다 🟢
httpClient가 기본 헤더로 Content-Type: application/json을 박고 있어서, multipart를 보내려면 이렇게 지워야 브라우저가 boundary를 붙입니다.
httpClient.post('/places/suggestions', formData, {
headers: { 'Content-Type': undefined },
});
이 레포에서 처음 등장한 패턴이고, 모르면 boundary 누락으로 400이 납니다. 다음에 파일 업로드를 붙이는 사람이 같은 데서 막힐 자리라 한 줄 남길 가치가 있습니다.
확인했지만 문제 없었던 것
공개 범위는 위반이 없습니다. extracted.evidence(대화 캡처에서 뽑은 근거 문장)는 읽기 전용 표시에만 쓰이고 저장되지 않습니다. contextSuggestion만 Context 본문 초안으로 들어가는데 Context는 본인 것이고 타인 응답에서는 null이라 기존 규칙 그대로입니다.
다만 한 가지는 docs/privacy-rules.md에 남길 만합니다 — Context 원문이 이제 사용자가 직접 쓴 메모가 아니라 AI가 사적 대화 캡처에서 추출한 문장일 수 있습니다. "Context 원문 비공개" 규칙의 무게가 그만큼 커졌습니다. 이건 제 문서 쪽이라 제가 반영하겠습니다.
VITE_KAKAO_REST_KEY가 브라우저에 노출되는 건 docs/frontend-image-runtime-contract.md에 이미 "공개 입력"으로 명시돼 있어 새로 할 게 없습니다.
제가 맡을 것
docs/architecture.md 2장 폴더 목록이 실제와 어긋난 것(places·map·layout 누락, follow→follows)과 위 privacy-rules 한 줄은 제 문서라 제가 반영하겠습니다. 4번도 원하시면 제가 쓰겠습니다.
#114(S15P11A705-323, 이미지 기반 장소·맥락 제안)를 머지 후 살펴보다 발견한 후속 항목입니다. 기능 자체는 잘 동작하고, 아래는 전부 "붙여야 할 마감" 성격입니다. 우선순위 순으로 적었습니다.
기능 전체를 @ghkim1632 님이 맡고 계셔서 배정합니다. 3번은 백엔드 확인이 필요할 수 있습니다.
1.
/dev/place-record프리뷰 라우트가 프로덕션 번들에 그대로 실린다 🔴src/app/router.tsx의placeRecordPreviewRoute에import.meta.env.DEV가드도, 다른 라우트가 쓰는beforeLoad인증 가드도 없습니다. 배포된 앱에서 URL만 알면 누구나 열립니다.문제는 라우트 존재 자체보다 그 안의 버튼입니다.
PlaceRecordPreviewPage.tsx의 "테스트 세션 발급"이 이걸 호출합니다.credentials: 'include'로 세션 쿠키를 받고memberId를 화면에 띄웁니다. 운영 백엔드가/dev/auth/login을 노출하고 있다면 로그인 없이 세션을 발급받는 경로가 열려 있는 것입니다. 프론트만으로는 판단할 수 없으니 확인이 필요합니다.GET /api/core/v1/dev/auth/login이 살아 있는지 확인 (살아 있으면 백엔드 이슈로 승격)import.meta.env.DEV로 감싸거나 프로덕션 빌드에서 제외docs/conventions.md에 개발 전용 라우트를 다루는 규칙 한 줄 추가 (규칙이 없어서 재발하기 쉬운 자리입니다)2. 서버가 보낸
warnings·kakaoSearch.status를 아무도 읽지 않는다 🟡suggestPlacesFromImage.ts가 Zod로 파싱은 하는데, 소비하는 코드가 한 줄도 없습니다(grep으로 확인).PlaceRecordSheet는candidate.kakaoSearch.items만 꺼내 쓰고status로 분기하지 않습니다. 서버가 "이 후보는 카카오 검색이 실패했다"(FAILED)고 알려줘도 UI는items: []와 구분하지 못하고, 사용자에게는 그냥 후보가 비어 보입니다.warnings도 통째로 버려집니다.keywords: []를 오류로 처리하지 말라는 규칙의 반대편 실수입니다 — 정상이 아닌 신호를 정상처럼 넘기고 있습니다.NO_RESULTS와FAILED를 구분해 안내 (전자는 "검색 결과가 없어요", 후자는 재시도 여지가 있는 오류)warnings를 노출하거나, 노출하지 않기로 정했다면 스키마 주석에 그 이유를 남기기3.
POST /places/suggestions계약이 어느 문서에도 없다 🟡docs/api-contract.md에도, 원본docs/reference/08_API_명세.md에도 이 엔드포인트가 없습니다(grep으로 확인). 현재 근거는 #109 이슈뿐입니다.AGENTS.md가 "문서에 없는 정책·엔드포인트를 추측해서 구현하지 않는다"를 절대 금지로 두고 있어서, 지금 상태로는 다른 사람이 이 코드를 만졌을 때 기댈 근거가 없습니다. 이미 머지됐으니 사후에 계약을 문서로 고정하는 쪽이 맞겠습니다.전례가 있습니다 — 표지 생성 API(
/image/api/*)는 원본 명세 §7.7이 front#99 가이드로 위임하고,api-contract.md가 "이 절만 근거가 원본 docs가 아니라 front#99"라고 명시하는 형태입니다. 같은 패턴으로 절을 하나 만들면 됩니다.스키마는 코드에서 읽어낼 수 있으니 제가 초안을 쓸 수 있습니다. 다만 아래는 추측하면 안 되는 항목이라 확인이 필요합니다.
kakaoSearch.status의NO_RESULTS/FAILED가 오류인지 정상 응답인지 (2번 처리 방향이 여기서 갈립니다)warnings[].code의 값 목록과 의미api-contract.md"협의 필요"에 §3.2로 올라가 있습니다PlaceRecordSheet.tsx#L28-L29) 서버 기준과 같은지 불명입니다docs/api-contract.md에 절 추가 (미확정 항목은 "협의 필요"로)4.
httpClientmultipart 관용구가 문서에 없다 🟢httpClient가 기본 헤더로Content-Type: application/json을 박고 있어서, multipart를 보내려면 이렇게 지워야 브라우저가 boundary를 붙입니다.이 레포에서 처음 등장한 패턴이고, 모르면 boundary 누락으로 400이 납니다. 다음에 파일 업로드를 붙이는 사람이 같은 데서 막힐 자리라 한 줄 남길 가치가 있습니다.
docs/conventions.md또는docs/api-contract.md에 한 줄 추가확인했지만 문제 없었던 것
공개 범위는 위반이 없습니다.
extracted.evidence(대화 캡처에서 뽑은 근거 문장)는 읽기 전용 표시에만 쓰이고 저장되지 않습니다.contextSuggestion만 Context 본문 초안으로 들어가는데 Context는 본인 것이고 타인 응답에서는null이라 기존 규칙 그대로입니다.다만 한 가지는
docs/privacy-rules.md에 남길 만합니다 — Context 원문이 이제 사용자가 직접 쓴 메모가 아니라 AI가 사적 대화 캡처에서 추출한 문장일 수 있습니다. "Context 원문 비공개" 규칙의 무게가 그만큼 커졌습니다. 이건 제 문서 쪽이라 제가 반영하겠습니다.VITE_KAKAO_REST_KEY가 브라우저에 노출되는 건docs/frontend-image-runtime-contract.md에 이미 "공개 입력"으로 명시돼 있어 새로 할 게 없습니다.제가 맡을 것
docs/architecture.md2장 폴더 목록이 실제와 어긋난 것(places·map·layout누락,follow→follows)과 위 privacy-rules 한 줄은 제 문서라 제가 반영하겠습니다. 4번도 원하시면 제가 쓰겠습니다.