범위: US-13 ~ US-20. 세션 생성 → 첫 질문 → 텍스트 답변 → 꼬리질문 → 세션 종료까지의 골든 패스 완성. 피드백 리포트(US-24)는 Sprint 3로 분리.
작성일: 2026-05-29 / 기준 브랜치: main (Sprint 1 #30까지 머지 완료)
- 열린 PR 없음. Sprint 1 마지막 PR(#30, Sprint1 백엔드 및 AI서버 API 연동)이 5/28에 머지됨.
- 머지 완료: auth(GitHub OAuth + JWT), resume 업로드/분석, GitHub repo 등록/분석, document 메타·요약·임베딩 upsert, SSE stream-token + 분석 상태 푸시, RabbitMQ DLX·DLQ + 재시도 정책(US-28),
.env자동 sync 워크플로. - Sprint 2 진입 준비 완료 상태.
| 레이어 | 상태 |
|---|---|
| DB | 7개 테이블(interview_sessions, interview_messages, session_contexts, message_voice_analyses, session_feedbacks 등) + 모든 ENUM 이미 마이그레이션됨 |
Backend session/ |
InterviewSession.java, InterviewMessage.java, Repository, Enum 까지만. application/ · presentation/ · infrastructure/ 패키지 부재 |
| AI 서버 | analyze.resume / .repository / .web 컨슈머만 본 구현. generate.questions · generate.followup 큐는 정의만, 코드 없음 (ai/CLAUDE.md §16) |
| Frontend | features/interview/index.ts, pages/Interview/index.ts, features/feedback/index.ts, pages/History/index.ts 모두 1줄 빈 export 스캐폴딩 |
| RealTime | SSE + RabbitMQ realtime.session.* bridge 동작 중. Core 가 publish 만 하면 그대로 fan-out |
| 메시징 스펙 | docs/messaging.md §5.6~5.9, envelope · 큐 · DLQ 모두 확정 |
데이터·흐름·SSE 이벤트 카탈로그 모두 사양화 완료 (docs/data-flow.md §3, docs/event-stream.md §3.3~3.4) — 사양은 다 있고 코드만 채우면 됨.
세션 생성부터 종료까지의 트랜잭션 · 메시지 발행 · SSE push 전부.
session/application/SessionServicecreate(cmd)—interview_sessionsINSERT(status=READY) +session_contextsINSERTstart(id)—READY → IN_PROGRESS,started_at세팅submitAnswer(id, content)— INTERVIEWEE 메시지 INSERT +generate.followup발행end(id, reason)—IN_PROGRESS → COMPLETED,ended_atget(id),list(userId, pageable),getMessages(id)
session/application/dto/—SessionCreateCommand,SessionResult,MessageSubmitCommand,MessageResultsession/presentation/SessionControllerPOST /api/sessionsPOST /api/sessions/{id}/startPOST /api/sessions/{id}/messagesPOST /api/sessions/{id}/endGET /api/sessions/{id}GET /api/sessionsGET /api/sessions/{id}/messages
- 세션 생성 commit 후
generate.questions발행 (@TransactionalEventListener(AFTER_COMMIT),docs/messaging.md §3) - 답변 INSERT commit 후
generate.followup발행
core.callback.questions 구독, payload.kind로 분기.
POOL— 질문 풀 저장 + 첫 질문interview_messagesINSERT + SSEsession.messagepush + (옵션)realtime.session.notify발행FOLLOWUP— INTERVIEWER 메시지 INSERT(parent_message_id매핑) + SSE push +total_question_count >= max_questions시 자동 종료 트리거
- 백엔드 컷:
total_question_count >= max_questions또는 timeout interview_sessions.status=COMPLETED,ended_at, SSEsession.statepush
A-5. ArchUnit 룰 위반 점검 (backend/CLAUDE.md §16)
SessionServiceTest(Mockito)SessionControllerTest(@WebMvcTest)- 컨슈머 통합 테스트 (Testcontainer RabbitMQ + PG)
ai/CLAUDE.md §14 절차 따라.
model/messages/generate.py—GenerateQuestionsPayload,GenerateFollowupPayload,CallbackQuestionsPayload
messaging/consumers/questions_consumer.pymessaging/consumers/followup_consumer.py- 멱등(messageId) 패턴은 기존 컨슈머 동일
- 입력:
interviewType,jobCategory,documentIds,maxQuestions - RAG: Core 내부 API
POST /api/internal/embeddings/search로 컨텍스트 chunk fetch → 프롬프트 주입 - LLM: Pro 모델 (Gemini 3.1 Pro), Pydantic 출력 파서로
[{ category, question }]강제 - 프롬프트:
chain/prompts/question_pool.py신규
- 입력: 직전 질문 / 답변, sessionId
- Flash 모델 + 답변 평가 스키마(
specificity,logic,structure) - SLA < 3 초 — 응답 토큰 cap, streaming 미사용
- 운영/개발:
GeminiEmbeddingProvider사용 (이미 구현되어 있음). 설정에서 default 로 스위치 - 테스트:
MockEmbeddingProvider유지 (단위/통합 테스트 결정론 확보) ai/CLAUDE.md §16의 "MockEmbeddingProvider default" 기술은 본 스프린트에서 갱신
callback.questions(kind=POOL/FOLLOWUP)
- Core API 또는 자체 publish 로 input/output 토큰 · latency 기록
FakeListLLMmock 으로 chain 단위- Testcontainer 로 컨슈머 통합
frontend/CLAUDE.md §5 라우트 가이드 기준.
/sessions/new,/sessions/:id,/history,/history/:idRequireAuthwrap
api/interview.ts— create / start / submitAnswer / end / get / list / getMessagesmodel/types.ts—Session,InterviewMessage, ENUM 미러 (docs/glossary.md)model/useSession.ts,useSessionMessages.ts,useSubmitAnswer.tsui/SessionConfigForm.tsx— mode / type / jobCategory / maxQuestions / maxDuration +analyzed_documents멀티셀렉트(컨텍스트 문서)ui/MessageList.tsx,ui/AnswerInput.tsx,ui/SessionControls.tsx(start, end, 진행률)ui/QuestionPendingState.tsx— AI 풀 생성 대기용 4-state pending
- 기존
useEventStream.ts에session.message·session.state핸들러 추가 GET /api/stream/sessions/{sessionId}— Sprint 1 의 stream-token API 재사용
NewSessionPage(config form 만)InterviewPage(:id, AsyncBoundary 로 감싸고 진행 중 / 종료 분기)
- 세션 리스트(상태 뱃지) · 세션 상세(메시지 시퀀스)
- Vitest 로 hook
- Playwright 로 "세션 생성 → 답변 3회 → 종료" 골든 패스
현재 q.realtime.session.notify 컨슈머가 SSE 로 fan-out 됨. Core 가 publish 만 시작하면 별도 작업 없음. 다만 확인할 것:
- Core 가
q.realtime.session.notify로 publish vs 자기 인메모리 SSE 둘 중 어디로 보낼지 정책 통일 (docs/event-stream.md §6) — Phase 1 은 Core 인메모리로도 충분. RealTime 경유로 가면 멀티 인스턴스 대비 가능. Sprint 2 에선 인메모리 + 별도 publish 둘 다 가능 — 결정 필요 - session 권한 검증(
/api/stream/sessions/:id본인 세션 검사) 이 이미 들어가 있는지 확인
| # | 항목 | 결정 | 근거 |
|---|---|---|---|
| 1 | 임베딩 provider | 운영 / 개발은 GeminiEmbeddingProvider 사용 (이미 구현됨), 테스트는 MockEmbeddingProvider 유지 |
Mock 만 쓰면 RAG 검색이 noise, 신규 구현체 도입 불필요 |
| 2 | 첫 질문 push 방식 | Core 가 callback.questions (POOL) 수신 즉시 첫 질문을 interview_messages INSERT + SSE session.message push |
round-trip · race 회피, SSE 인프라 재사용 |
| 3 | SSE 전송 경로 | Phase 1 은 Core 인메모리 단일화 | RT2 (분석 상태) 와 경로 통일, 디버깅 비용 ↓. RealTime 서버는 멀티 인스턴스 / 음성 단계에서 본격 활용 |
| 4 | 답변 멱등키 | POST /api/sessions/{id}/messages 한정으로 Idempotency-Key 헤더 도입 (Frontend UUID 발급 → Core 가 processed_messages 테이블에 24h 캐시) |
(session_id, sequence_number) UNIQUE 깨짐 방지, SSE 재연결 + 자동 재시도 시 중복 INSERT 차단 |
| # | 항목 | 책임 | 메모 |
|---|---|---|---|
| 5 | 세션 종료 후 피드백 (US-24) Sprint 3 로 미루는지 확정 | PO | 본 계획은 분리를 가정. Sprint 2 종료 시점에 /sessions/:id/feedback placeholder 만 두는 안 검토 |
| 6 | Flyway 추가 마이그레이션 필요 여부 | Core | 핵심 결정: 질문 풀 저장 위치 — interview_sessions.question_pool JSONB 컬럼 추가 vs 별도 테이블 vs Core 인메모리. 컬럼 추가 시 마이그레이션 1 개 필요 |
- 미결정 사항 5·6 회의로 픽스 (10 분)
- 메시지 스키마 정합성 PR — envelope 모델만 Core / AI 양쪽에 추가 → 트랙 A · B 의 unblock
- 트랙 A · B · C 병렬 진행 (각각 PR 1~2 개)
- 통합 시나리오 테스트 — 세션 생성 → 첫 질문 → 답변 → 꼬리질문 → 종료 골든 패스 E2E
- 트랙 D 정책 통일 + 멀티 인스턴스 대비 (선택)
Sprint 1 PR 들이 평균 12 개 도메인씩 끊어 머지된 패턴을 보면, Sprint 2 도 트랙별 PR 23 개씩(총 8~10 PR)으로 끊는 게 자연스러움.
-
/sessions/new에서 컨텍스트 문서를 골라 세션 생성 가능 - 세션 생성 후 첫 질문이 SSE 로 도착
- 답변 제출 → < 3 초 내 꼬리질문 push
-
maxQuestions도달 시 자동 종료,/history에 COMPLETED 표시 -
/history/:id에서 전체 메시지 트리 조회 가능 - DLQ 격리 정책 (
docs/messaging.md §6) 가 generate.* 큐에도 적용 확인 -
ai_request_logs에 질문 풀 / 꼬리질문 호출 기록 - 골든 패스 E2E 테스트 통과