chaekmate 개발일지 — Next.js + FastAPI 마이그레이션 & 프로덕션 배포
chaekmate 개발일지 (2026-04-19)
오늘 한 일 요약
Streamlit MVP로 완성된 chaekmate를 FastAPI + Next.js 스택으로 마이그레이션하고, Fly.io(백엔드) + Vercel(프론트) 프로덕션 환경까지 배포 완료했다. 쿠키 기반 인증 관련 버그를 두 건 잡으며 로그인 플로우가 실제 브라우저에서 동작하는 것을 확인했다.
커밋 로그
| 시각(UTC) | SHA | 내용 |
|---|---|---|
| 01:37 | fceeb90 |
fix(auth): OAuth state 쿠키와 JWT 세션 쿠키 이름 충돌 해결 |
| 01:46 | ada4c59 |
feat(frontend): Next.js App Router — 랜딩·온보딩·SSE 채팅 페이지 |
| 02:54 | b43b4ce |
Merge PR #3: FastAPI 백엔드 + Google OAuth + 스트리밍 채팅 |
| 03:13 | 23c946b |
refactor: Streamlit 앱 → scripts/ 로 이동 (유지보수 모드) |
| 03:17 | f4afc4c |
feat(deploy): Fly.io 백엔드 설정 (SQLite 볼륨, Tokyo 리전) |
| 04:34 | 4a8d470 |
chore: Claude Code 로컬 MCP·설정 파일 gitignore 추가 |
| 07:58 | a78b173 |
Merge PR #4: cleanup & deploy 브랜치 병합 |
| 08:29 | 15829ba |
fix(frontend): Next.js rewrite로 same-origin 쿠키 문제 해결 |
| 09:01 | e3cda84 |
fix(auth): 로그아웃 시 Set-Cookie 헤더 누락 버그 수정 |
주요 작업 상세
Phase 1 완료 — FastAPI 백엔드 마이그레이션
기존 Streamlit의 비즈니스 로직(src/ 디렉토리)을 그대로 import해 FastAPI 라우터로 래핑했다. 재작성 없이 이식하는 방식을 택했기 때문에 추천 엔진·임베딩·DB 로직에는 변경이 없다.
구현된 엔드포인트 - GET /api/onboarding/books — 온보딩 그리드용 도서 18권 - POST /api/onboarding — 평점 제출 → 추천 후보 풀 생성 - POST /api/chat — Claude API SSE 스트리밍 - GET /api/auth/google → GET /api/auth/google/callback — Google OAuth 2.0 - GET /api/auth/me / POST /api/auth/logout
Phase 2 완료 — Next.js 프론트엔드
Next.js 16 + React 19 + TypeScript + Tailwind 조합으로 세 페이지를 구현했다.
- 랜딩
/— Google 로그인 버튼, 로그인 상태면/chat자동 리다이렉트 - 온보딩
/onboarding— 18권 그리드 + ❤️ 👎 ✨ ⏭️ 4단계 토글 평점 (MVP 결정사항 유지) - 채팅
/chat—fetch+ReadableStream으로 SSE 수신, 타이핑 효과 렌더링, 추천 카드에 피드백 버튼
EventSource는 POST body를 지원하지 않아 fetch + ReadableStream으로 직접 스트림을 처리했다.
프로덕션 배포
| 서비스 | 플랫폼 | 비고 |
|---|---|---|
| FastAPI 백엔드 | Fly.io (Tokyo nrt) |
1GB SQLite 볼륨, 유휴 시 자동 중지 |
| Next.js 프론트엔드 | Vercel | - |
| DB | SQLite (볼륨 마운트) | 첫 부팅 시 seed DB 복사 후 보존 |
버그 수정 2건
1. OAuth state 쿠키 vs JWT 세션 쿠키 이름 충돌
Starlette SessionMiddleware와 JWT 쿠키가 모두 session이라는 이름을 사용해, OAuth 콜백 이후 미들웨어가 빈 세션을 덮어쓰면서 로그인이 풀리는 현상. 미들웨어 쿠키를 oauth_state로 개명하고 SameSite/Secure 플래그를 환경 변수로 통일했다.
2. 크로스 사이트 쿠키 차단 (Chrome 서드파티 정책)
백엔드(chaekmate-api.fly.dev)가 프론트(chaekmate.vercel.app)와 다른 도메인이라 Chrome이 세션 쿠키를 차단. Next.js rewrites를 추가해 /api/* 요청이 프론트와 같은 출처로 보이도록 프록시 처리했다.
3. 로그아웃 Set-Cookie 헤더 누락
response 파라미터를 변경하고 새로운 Response(status_code=204)를 반환해 Set-Cookie 삭제 헤더가 드랍되던 문제. 먼저 Response 객체를 생성하고 거기에 쿠키를 적용한 뒤 동일 인스턴스를 반환하도록 수정했다.
레거시 처리
Streamlit app.py는 scripts/streamlit_app.py로 이동해 유지보수 모드로 전환했다. src/ import 경로는 sys.path.insert로 유지해 기존 스크립트가 깨지지 않도록 했다.
내일 할 일 (v2 잔여)
repo: sigolyori/chaekmate