# 구현 계획: 채팅 이미지 입력 ## 기술 배경 - React 19 + TypeScript + Zustand 기반 `features/snap` - 기존 SSE 전송기 `streamLLM`과 `/chat/stream` JSON 요청 - Tauri 화면 캡쳐가 만드는 PNG data URL - 브라우저 Clipboard API가 제공하는 PNG/JPEG/WebP Blob - Vitest + Testing Library ## 규칙 확인 - `CLAUDE.md`를 유일한 작업 규칙으로 적용함 - 기존 `Composer`, `useSnapChat`, `snapStream`을 재사용함 - 서버 DTO는 `features/snap/contract/types.ts`의 기존 계약 위치에 둠 - 테스트를 먼저 실패시킨 뒤 최소 구현함 - 새 라이브러리는 추가하지 않음 ## 설계 1. `SnapImageInput`을 `mediaType`과 data URL `data`로 정의하고 `SnapStreamRequest.images`에 연결한다. 2. `Composer`가 캡쳐와 클립보드 이미지를 같은 첨부 목록으로 관리한다. 3. 첨부 시 형식, 개수, 개별 크기, 전체 크기를 검사한다. 4. `onSend`는 텍스트와 이미지 목록을 받아 새 대화와 기존 대화에서 같은 전송 경로를 쓴다. 5. 새 대화는 세션 생성 뒤 route state로 텍스트와 이미지를 넘기고, 세션 화면이 한 번 소비한다. 6. `useSnapChat`은 로컬 user 메시지를 만든 뒤 백엔드 요청에 `images`를 포함한다. 7. 과거 메시지 DTO에는 이미지 정보를 추가하지 않는다. 원본이 저장되지 않는 백엔드 계약을 따른다. ## 변경 파일 - `2_frontend/src/features/snap/contract/types.ts` - `2_frontend/src/features/snap/components/Composer.tsx` - `2_frontend/src/features/snap/components/Composer.test.tsx` - `2_frontend/src/features/snap/hooks/useSnapChat.ts` - `2_frontend/src/features/snap/hooks/useSnapChat.test.tsx` - `2_frontend/src/features/snap/pages/NewChatPage.tsx` - `2_frontend/src/features/snap/pages/SessionChatPage.tsx` - `2_frontend/src/features/snap/api/snap.stream.test.ts` ## 위험과 대응 - base64 크기를 문자열 길이로 잘못 계산할 수 있음 → data URL 본문을 디코딩한 byte 길이로 검사 - 새 대화 이동 중 이미지가 사라질 수 있음 → 텍스트와 이미지 모두 route state 한 묶음으로 전달 - 요청 실패 때 작성 내용이 사라질 수 있음 → 전송이 시작되기 전 검증 실패에는 입력과 첨부를 보존함 ## 검증 - 단위 테스트: 이미지 계약, 캡쳐 수신, 클립보드 첨부, 이미지-only, 제한 차단, 요청 body - 전체: `npm run format`, `npm run lint`, `npm run test`, `npm run build`