Skip to content

Feature/sprint1 leftovers us02 03 04 08#34

Merged
i3months merged 9 commits into
devfrom
feature/sprint1-leftovers-us02-03-04-08
May 29, 2026
Merged

Feature/sprint1 leftovers us02 03 04 08#34
i3months merged 9 commits into
devfrom
feature/sprint1-leftovers-us02-03-04-08

Conversation

@i3months

Copy link
Copy Markdown
Member

Sprint 1 잔여 4건 + Sprint 2 백엔드/AI 본 구현. 프론트엔드는 별도 PR.

Sprint 1 잔여

  • US-02 UserStatsService: 세션 통계 + 평균 점수
  • US-03 UserConsent: 개인정보 동의 기록·철회
  • US-04 회원 탈퇴: 이벤트 기반 refresh token revoke
  • US-08 GitHub 레포 메타 재동기화

Sprint 2

  • B1 (US-13~17) session/application + presentation: create/list/get/start/end/interrupt/cancel + 컨텍스트 문서 N:M 링크
  • B2 (US-18) AI generate.questions consumer + 질문 풀 생성 (Gemini Pro)
  • B3 (US-20) callback.questions(POOL) 수신 → 첫 질문 INSERT + SSE push
  • B5 (US-19) AI generate.followup consumer (Flash, 답변 평가) → callback.questions(FOLLOWUP) + maxQuestions 자동 종료
  • B7 (US-30) ai_request_logs: LangChain AsyncCallbackHandler → Core /api/internal/ai-logs

부가

Sprint 3 분리

i3months added 9 commits May 29, 2026 11:29
…repo sync)

## US-02 — GET /api/users/me/stats
- 총/완료 세션 수 + 평균 점수(overall/technical/logic/communication) + 최근 10건 점수 추이
- session 슬라이스에 controller 위치 (user → session 의존 회피, URL 만 /api/users/me/stats)
- 신규 사용자(데이터 없음)는 카운트 0 + 평균 null + recent 빈 배열 안전 반환
- InterviewSessionRepository: countByUser_Id 외 status별 count 추가
- SessionFeedbackRepository: 평균/최근 N건 JPQL 추가

## US-03 — /api/users/me/consents
- POST (제출, append-only), GET (이력 최신순), DELETE /{type} (최신 활성 row revoke)
- UserConsent.agree() 정적 팩토리 + revoke() 도메인 메서드
- IP 는 X-Forwarded-For 우선 (Nginx 프록시 대응)
- 동의 ConsentType: TOS|PRIVACY|MARKETING

## US-04 — DELETE /api/users/me
- User.markDeleted() + UserDeletedEvent 발행
- auth 슬라이스 UserDeletionRevokeListener 가 받아 모든 refresh token revoke
- user → auth 직접 의존 회피 (event 패턴, auth → user 단방향)
- RefreshTokenRepository.revokeAllByUserId @Modifying 쿼리
- 이미 탈퇴한 사용자는 USER_ALREADY_DELETED (410)
- GitHub access token 무효화는 사용자 본인이 GitHub Settings 에서 별도 수행 안내

## US-08 — POST /api/repositories/{id}/sync
- 등록된 레포의 GitHub 메타(name/full_name/url/default_branch) 재fetch → 갱신
- GithubRepository.updateMetadata() 도메인 메서드 + last_synced_at 갱신
- 분석 재트리거는 분리(/reanalyze 후속 PR)
- 404/403/502 분기 기존 등록 API 와 동일

## 빌드
- ArchUnit 도메인 순환 없음 (user ↔ auth 이벤트 분리 / session 슬라이스 위치)
- UserServiceTest 갱신: deleteAccount 케이스 추가
- StackupApplicationTests: 신규 repository 8개 @MockitoBean 추가
- InterviewSession 도메인 메서드: create/start/end/interrupt/cancel/incrementQuestionCount/updateTitleAndMemo
- SessionContext 정적 팩토리 link()
- SessionContextRepository: 단건→List 로 변경 (N:M)
- InterviewSessionRepository: deleted=false 필터 + 페이지 + 통계 메서드
- SessionService: create(context 연결 포함)/list/get/start/end/interrupt/cancel/update/delete
- SessionController: POST /api/sessions, GET (목록/단건), PATCH (메타/start/end/interrupt/cancel), DELETE
- 상태 전환 위반은 SESSION_INVALID_STATE (422)
- 분석 미완료 문서를 context 로 지정 시 DOC_NOT_ANALYZED (422)
SPRINT2_PLAN 정합 — generate.questions 는 세션 CREATE commit 이후 자동 발행
(start 가 아니라 create, plan §A-2).

## AI 측
- ai/src/ai_server/model/messages/questions.py
  - DocumentContext / GenerateQuestionsRequest / GeneratedQuestion / QuestionPoolCallbackPayload
  - QuestionCategory: CS_FUNDAMENTAL | PROJECT_DEEP_DIVE | TECH_CHOICE | BEHAVIORAL
- ai/src/ai_server/chain/prompts/question_generation.py
  - 직군·면접유형별 카테고리 분배 가이드 포함
- ai/src/ai_server/chain/question_generation_chain.py
  - GeneratedQuestionPool (Pydantic 출력 파서) + LlmQuestionGenerator + Pro 모델
- ai/src/ai_server/messaging/consumers/questions_consumer.py
  - parse → idempotency check → LLM 호출 → callback.questions(kind=POOL) publish
- runner.py: questions consumer 등록, settings 에 ai_queue_questions/ai_callback_routing_questions
- tests/test_questions_consumer.py — 4 케이스 (publish, idempotency skip, context build x2) — pass

## Core 측
- session.application.event.SessionCreatedEvent
- SessionService.create() 가 commit 직전 publishEvent
- SessionQuestionsRequester (application)
  - @TransactionalEventListener(AFTER_COMMIT) + Propagation.NOT_SUPPORTED
  - documentIds → AnalyzedDocument fetch → MinIO 에서 MD 본문 read (≤200KB cap) → envelope 구성
  - generate.questions 발행 (envelope.context.sessionId 포함)
- application/dto/GenerateQuestionsPayload — DocumentContext 포함

(RAG 정교화는 후속 — 현재는 doc summary + MD 전체를 컨텍스트로 주입)
(US-24 피드백은 SPRINT2_PLAN §3 에 따라 Sprint 3 분리)
…-20)

## 도메인
- InterviewMessage: interviewer/followup/interviewee 정적 팩토리 + markStatus
- InterviewMessageRepository: findFirst...DESC, countBySession_Id

## callback.questions 처리 (Core)
- QuestionsCallbackPayload (POOL/FOLLOWUP 양쪽 필드)
- QuestionsCallbackEnvelope (구체 타입, JacksonJsonMessageConverter 호환)
- QuestionsCallbackHandler @RabbitListener
- QuestionsCallbackService
  - POOL: 첫 질문을 sequenceNumber=1 INSERT + totalQuestionCount++ + SSE session.message
    + user 채널에도 알림(SessionMessageNotice) — 프론트가 session 단위 사전 인지 없이 수신 가능
  - FOLLOWUP: B5 진행 시 활성 (지금은 로깅만)
  - 멱등: processed_messages

## 답변 제출 + 꼬리질문 트리거
- InterviewMessageService.submitAnswer
  - 직전 메시지가 INTERVIEWER 인지 검증, IN_PROGRESS 검증
  - INTERVIEWEE 메시지 INSERT
  - AnswerSubmittedEvent 발행 (B5 가 받아 generate.followup publish)
- GET /api/sessions/{id}/messages, POST /api/sessions/{id}/messages
B5 (US-19 꼬리질문):
- AI: generate.followup consumer + LangChain chain (Flash 모델)
  - 답변 평가 (specificity 0~5, logic 0~5, structure: FULL_STAR|PARTIAL_STAR|NONE)
  - FollowupCallbackPayload 발행 (kind=FOLLOWUP, parent_message_id)
- Core: AnswerSubmittedEvent → SessionFollowupRequester @TransactionalEventListener(AFTER_COMMIT)
  로 generate.followup 발행
- callback.questions FOLLOWUP 처리 활성화 — INSERT followup 메시지 + SSE push
- maxQuestions 도달 시 자동 세션 종료 (SESSION_STATE: MAX_QUESTIONS_REACHED)

B7 (US-30 AI 호출 로깅):
- AI: CoreAiLogCallback (LangChain AsyncCallbackHandler) — on_llm_start/end/error
  에서 토큰/latency 추출, fire-and-forget POST /api/internal/ai-logs
- 3개 chain builder (document_analysis, question_generation, followup_generation)에
  core_client 주입 + 콜백 부착
- Core: InternalAiLogController + AiRequestLogService.record() — userId/sessionId
  로딩 후 ai_request_logs INSERT (202 Accepted)
- Settings: llm_flash_model/temperature/max_tokens 추가

테스트: 백엔드 contextLoads 통과 (@MockitoBean AiRequestLogRepository 추가),
AI 129 테스트 통과 (followup consumer 포함)
- ai/.env.example, docs/environment.md: LLM_FLASH_MODEL/TEMPERATURE/MAX_TOKENS 추가
- ai/CLAUDE.md §5/§16: generate.questions·generate.followup·llm 호출 로깅 본 구현 반영
- backend/CLAUDE.md §19: 현재 상태 갱신 (면접 도메인, ai_request_logs, ArchUnit, Flyway 본 구현)
- SPRINT2_PLAN decision #4: POST /api/sessions/{id}/messages 에 Idempotency-Key
  헤더 처리 (interview_messages.idempotency_key 컬럼 + Flyway V5 partial unique
  index). SSE 재연결 자동 재시도 시 동일 키면 기존 답변 반환.
- 단위 테스트 (Mockito):
  - SessionServiceTest: create/start/end + analyzed 컨텍스트 링크 + non-analyzed 거부
  - InterviewMessageServiceTest: submitAnswer + 멱등키 중복 차단 + 직전 메시지 검증
  - QuestionsCallbackServiceTest: POOL 첫 질문 INSERT/SSE, FOLLOWUP 자동종료, 중복 messageId skip
  - AiRequestLogServiceTest: record + invalid status fallback
deploy-app.yml 의 .env append 로직이 루트 .env.example 만 읽기 때문에
루트 .env.example 동기화 누락 시 stack-up.shop 의 ai 컨테이너에 환경변수
주입 누락 발생. docker-compose 의 ai 서비스 env 매핑도 함께 추가.
origin/dev 동료(서현) PR #31 (feat(session): 면접 세션 도메인 + Core/AI 메시지 DTO) 흡수.

충돌 해결 원칙: 내 풀스택 동작 코드 유지, 동료의 좋은 부분만 흡수.

- DTO 4개 (GenerateQuestionsPayload, GenerateFollowupPayload,
  QuestionsCallbackPayload, QuestionsCallbackEnvelope): 동료 것은 미사용
  데드코드 (Service 없음). 내 풀스택 동작 코드 채택.
- InterviewSession: 내 `start/end/interrupt/cancel/updateTitleAndMemo` 유지,
  동료의 `isMaxReached()` 흡수.
- InterviewMessage: 내 `interviewer(no parent) / followup(with parent) /
  interviewee(with parent + idempotencyKey)` 시그니처 유지, 동료의
  null/blank/sequence 검증 로직을 단일 생성자에 흡수.
- InterviewMessageRepository: 내 메서드 + 동료의 `findMaxSequenceBySessionId` 흡수.
- InterviewSessionRepository, SessionContextRepository: 내 메서드 유지
  (동료 메서드는 미사용 + 필드명 부정확).
- 동료의 도메인 테스트 3개 (InterviewSessionTest, InterviewMessageTest,
  QuestionsCallbackPayloadTest): 시그니처·스키마가 내 코드와 호환 불가,
  내 SessionServiceTest/InterviewMessageServiceTest/QuestionsCallbackServiceTest
  가 같은 영역을 서비스 레이어에서 커버하므로 제거.
@vercel

vercel Bot commented May 29, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
stackup Ready Ready Preview, Comment May 29, 2026 12:17pm

@i3months i3months merged commit 7142a3e into dev May 29, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant