From e20a4331bbe2bb68b017a7de0c72f1edd1d41135 Mon Sep 17 00:00:00 2001 From: pswaao88 Date: Mon, 1 Jun 2026 19:44:55 +0900 Subject: [PATCH 1/3] =?UTF-8?q?docs:=20=EB=A9=B4=EC=A0=91=20=EB=AA=A8?= =?UTF-8?q?=EB=93=9C=20=EC=A0=95=EC=B1=85=20=EB=AC=B8=EC=84=9C=20=EC=A0=95?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/CLAUDE.md | 3 +-- docs/coding-conventions.md | 6 +++--- docs/database.md | 7 ++----- docs/design-system.md | 7 +++---- docs/glossary.md | 5 ++--- docs/messaging.md | 2 +- 6 files changed, 12 insertions(+), 18 deletions(-) diff --git a/backend/CLAUDE.md b/backend/CLAUDE.md index f70561d6..70c3aa9b 100644 --- a/backend/CLAUDE.md +++ b/backend/CLAUDE.md @@ -137,9 +137,8 @@ QueryDSL Q-class 생성 위치: `build/generated/sources/annotationProcessor/... public record SessionCreateRequest( String title, @NotNull SessionMode mode, - @NotNull InterviewType interviewType, @NotNull JobCategory jobCategory, - @Min(1) @Max(30) Integer maxQuestions, + @Min(2) @Max(30) Integer maxQuestions, @Min(5) @Max(180) Integer maxDurationMinutes, List contextDocumentIds ) {} diff --git a/docs/coding-conventions.md b/docs/coding-conventions.md index 7fccfffd..c8ca9d01 100644 --- a/docs/coding-conventions.md +++ b/docs/coding-conventions.md @@ -44,7 +44,7 @@ createSession(true, false); // 좋음 - createSession(SessionMode.ONLINE, JobCategory.BACKEND); + createSession(SessionMode.TECHNICAL, JobCategory.BACKEND); ``` --- @@ -109,7 +109,7 @@ analyzeResume_whenS3UploadFails_marksResumeFailed ```java // given var user = aUser().build(); -var request = aSessionCreateRequest().mode(ONLINE).build(); +var request = aSessionCreateRequest().mode(TECHNICAL).build(); // when var session = sessionService.create(user, request); @@ -168,7 +168,7 @@ if (session.questionCount() >= DEFAULT_MAX_QUESTIONS) {...} public Session create(User user, boolean online, boolean technical) {...} // 좋음 -public Session create(User user, SessionMode mode, InterviewType type) {...} +public Session create(User user, SessionMode mode, JobCategory jobCategory) {...} ``` --- diff --git a/docs/database.md b/docs/database.md index c1517b84..a620fa17 100644 --- a/docs/database.md +++ b/docs/database.md @@ -140,9 +140,7 @@ CREATE TABLE interview_sessions ( user_id BIGINT NOT NULL REFERENCES users(id), title VARCHAR(200), memo TEXT, - mode VARCHAR(20) NOT NULL CHECK (mode IN ('ONLINE','OFFLINE')), - interview_type VARCHAR(30) NOT NULL - CHECK (interview_type IN ('PERSONALITY','TECHNICAL','LIVE_CODING','INTEGRATED')), + mode VARCHAR(20) NOT NULL CHECK (mode IN ('TECHNICAL','PERSONALITY','INTEGRATED')), job_category VARCHAR(30) NOT NULL CHECK (job_category IN ('FRONTEND','BACKEND','INFRA','DBA')), max_questions INT NOT NULL DEFAULT 10, @@ -247,8 +245,7 @@ CREATE TABLE ai_request_logs ( > 코드(Enum)와 DB(VARCHAR + CHECK)는 **반드시 1:1 매칭**. Enum 추가 시 Flyway 마이그레이션도 같이 작성. ``` -mode : ONLINE | OFFLINE -interview_type : PERSONALITY | TECHNICAL | LIVE_CODING | INTEGRATED +mode : TECHNICAL | PERSONALITY | INTEGRATED job_category : FRONTEND | BACKEND | INFRA | DBA session_status : READY | IN_PROGRESS | INTERRUPTED | COMPLETED | CANCELLED message_role : INTERVIEWER | INTERVIEWEE | SYSTEM diff --git a/docs/design-system.md b/docs/design-system.md index 152703a3..5284f772 100644 --- a/docs/design-system.md +++ b/docs/design-system.md @@ -98,13 +98,12 @@ StackUp 도메인 한정 컬러. `features/*/ui` 의 도메인 뱃지에서만 | Infra | `#b06c70` (rose) | `bg-job-infra` | | DBA | `#b89c5e` (gold) | `bg-job-dba` | -#### 면접 유형 +#### 면접 모드 -| 유형 | Hex | Tailwind | +| 모드 | Hex | Tailwind | |---|---|---| | Personality | `#6f9978` (mint) | `bg-type-personality` | | Technical | `#6c8294` (slate) | `bg-type-technical` | -| Live Coding | `#a87385` (mauve) | `bg-type-live-coding` | | Integrated | `#8a7896` (violet)| `bg-type-integrated` | ### 2.6 타이포그래피 — 폰트 패밀리 (3-Tier) @@ -270,7 +269,7 @@ Tailwind v4 기본 `--spacing: 0.25rem` (= 4px) 사용. `p-4` = `16px`. ### Domain-aware (`features/*/ui` 에 위치) - `JobCategoryBadge` — 직군별 컬러 (§2.5). -- `InterviewTypeBadge` — 면접 유형별 컬러 (§2.5). +- `InterviewModeBadge` — 면접 모드별 컬러 (§2.5). - `SessionStatusBadge` — 상태 매핑 (§4). - `AnalysisStateIndicator` — `QUEUED / PROCESSING / COMPLETED / FAILED` 4단계 progress. diff --git a/docs/glossary.md b/docs/glossary.md index 55b3dbfb..2c67c563 100644 --- a/docs/glossary.md +++ b/docs/glossary.md @@ -35,12 +35,11 @@ | 한국어 | 영어 | 비고 | |--------|------|------| | 면접 세션 | `interview session` | DB: `interview_sessions` | -| 면접 모드 | `mode` | `ONLINE` / `OFFLINE` | -| 면접 유형 | `interview type` | `PERSONALITY` / `TECHNICAL` / `LIVE_CODING` / `INTEGRATED` | +| 면접 모드 | `mode` | `TECHNICAL` / `PERSONALITY` / `INTEGRATED` | +| 면접 유형 | `interview type` | legacy 용어. MVP에서는 `mode`로 통합 | | 직군 | `job category` | `FRONTEND` / `BACKEND` / `INFRA` / `DBA` | | 인성 면접 | `personality interview` | `PERSONALITY` | | 기술 면접 | `technical interview` | `TECHNICAL` | -| 라이브 코딩 | `live coding` | `LIVE_CODING` | | 종합 면접 | `integrated interview` | `INTEGRATED` | | 질문 풀 | `question pool` | 세션 시작 시 Pro 모델이 생성 | | 꼬리질문 | `follow-up question` | Flash + RAG로 실시간 생성 | diff --git a/docs/messaging.md b/docs/messaging.md index eaf8446c..2bd8cb63 100644 --- a/docs/messaging.md +++ b/docs/messaging.md @@ -220,7 +220,7 @@ "messageType": "generate.questions", "payload": { "sessionId": 99, - "interviewType": "TECHNICAL", + "mode": "TECHNICAL", "jobCategory": "BACKEND", "documentIds": [42, 17], "maxQuestions": 10 From 5489a9f32c6a42e82364a3e6f6dc046ca74b6955 Mon Sep 17 00:00:00 2001 From: pswaao88 Date: Mon, 1 Jun 2026 19:47:17 +0900 Subject: [PATCH 2/3] =?UTF-8?q?feat:=20=EB=A9=B4=EC=A0=91=20=EB=AA=A8?= =?UTF-8?q?=EB=93=9C=EB=A5=BC=20mode=EB=A1=9C=20=ED=86=B5=ED=95=A9?= =?UTF-8?q?=ED=95=B4=20=EA=B4=80=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/SessionFeedbackRequester.java | 2 +- .../application/SessionFollowupRequester.java | 2 +- .../SessionQuestionsRequester.java | 2 +- .../session/application/SessionService.java | 3 +-- .../application/VoiceAnswerUploadService.java | 2 +- .../application/dto/AnalyzeVoicePayload.java | 2 +- .../dto/GenerateFeedbackPayload.java | 2 +- .../dto/GenerateFollowupPayload.java | 4 ++-- .../dto/GenerateQuestionsPayload.java | 4 ++-- .../application/dto/SessionCreateCommand.java | 2 -- .../application/dto/SessionResult.java | 3 --- .../event/SessionCreatedEvent.java | 4 ++-- .../session/domain/InterviewSession.java | 15 +++++---------- .../stackup/session/domain/InterviewType.java | 8 -------- .../stackup/session/domain/SessionMode.java | 5 +++-- .../presentation/SessionController.java | 2 +- .../dto/SessionCreateRequest.java | 9 +++------ .../presentation/dto/SessionResponse.java | 3 --- ...nge_session_mode_to_interview_category.sql | 19 +++++++++++++++++++ .../FeedbackCallbackServiceTest.java | 6 ++---- .../InterviewMessageServiceTest.java | 4 +--- .../QuestionsCallbackServiceTest.java | 10 ++++------ .../SessionFeedbackRequesterTest.java | 4 +--- .../application/SessionServiceTest.java | 10 ++++------ .../application/VoiceCallbackServiceTest.java | 10 ++++------ 25 files changed, 60 insertions(+), 77 deletions(-) delete mode 100644 backend/src/main/java/com/stackup/stackup/session/domain/InterviewType.java create mode 100644 backend/src/main/resources/db/migration/V6__change_session_mode_to_interview_category.sql diff --git a/backend/src/main/java/com/stackup/stackup/session/application/SessionFeedbackRequester.java b/backend/src/main/java/com/stackup/stackup/session/application/SessionFeedbackRequester.java index 00975030..2a3509d3 100644 --- a/backend/src/main/java/com/stackup/stackup/session/application/SessionFeedbackRequester.java +++ b/backend/src/main/java/com/stackup/stackup/session/application/SessionFeedbackRequester.java @@ -61,7 +61,7 @@ public void onSessionEnded(SessionEndedEvent event) { GenerateFeedbackPayload payload = new GenerateFeedbackPayload( session.getId(), - session.getInterviewType().name(), + session.getMode().name(), session.getJobCategory().name(), session.getTotalQuestionCount(), event.reason(), diff --git a/backend/src/main/java/com/stackup/stackup/session/application/SessionFollowupRequester.java b/backend/src/main/java/com/stackup/stackup/session/application/SessionFollowupRequester.java index 09f16f23..e216d152 100644 --- a/backend/src/main/java/com/stackup/stackup/session/application/SessionFollowupRequester.java +++ b/backend/src/main/java/com/stackup/stackup/session/application/SessionFollowupRequester.java @@ -45,7 +45,7 @@ public void onAnswerSubmitted(AnswerSubmittedEvent event) { answer.getId(), parent.getContent(), answer.getContent(), - session.getInterviewType(), + session.getMode(), session.getJobCategory() ); publisher.publishToAi( diff --git a/backend/src/main/java/com/stackup/stackup/session/application/SessionQuestionsRequester.java b/backend/src/main/java/com/stackup/stackup/session/application/SessionQuestionsRequester.java index 1f2bd69a..011b4731 100644 --- a/backend/src/main/java/com/stackup/stackup/session/application/SessionQuestionsRequester.java +++ b/backend/src/main/java/com/stackup/stackup/session/application/SessionQuestionsRequester.java @@ -50,7 +50,7 @@ public void onSessionCreated(SessionCreatedEvent event) { List documents = buildDocumentContexts(event.contextDocumentIds()); GenerateQuestionsPayload payload = new GenerateQuestionsPayload( event.sessionId(), - event.interviewType(), + event.mode(), event.jobCategory(), documents, event.maxQuestions() diff --git a/backend/src/main/java/com/stackup/stackup/session/application/SessionService.java b/backend/src/main/java/com/stackup/stackup/session/application/SessionService.java index b3e78522..7c9c2162 100644 --- a/backend/src/main/java/com/stackup/stackup/session/application/SessionService.java +++ b/backend/src/main/java/com/stackup/stackup/session/application/SessionService.java @@ -44,7 +44,6 @@ public SessionResult create(Long userId, SessionCreateCommand command) { command.title(), command.memo(), command.mode(), - command.interviewType(), command.jobCategory(), command.maxQuestions(), command.maxDurationMinutes() @@ -56,7 +55,7 @@ public SessionResult create(Long userId, SessionCreateCommand command) { events.publishEvent(new SessionCreatedEvent( userId, session.getId(), - session.getInterviewType(), + session.getMode(), session.getJobCategory(), session.getMaxQuestions(), linkedIds diff --git a/backend/src/main/java/com/stackup/stackup/session/application/VoiceAnswerUploadService.java b/backend/src/main/java/com/stackup/stackup/session/application/VoiceAnswerUploadService.java index 24d091a8..a94dccfa 100644 --- a/backend/src/main/java/com/stackup/stackup/session/application/VoiceAnswerUploadService.java +++ b/backend/src/main/java/com/stackup/stackup/session/application/VoiceAnswerUploadService.java @@ -82,7 +82,7 @@ public MessageResult submit(Long userId, Long sessionId, VoiceAnswerUploadComman key, cmd.contentType(), latest.getContent(), - session.getInterviewType().name(), + session.getMode().name(), session.getJobCategory().name() ); publisher.publishToAi( diff --git a/backend/src/main/java/com/stackup/stackup/session/application/dto/AnalyzeVoicePayload.java b/backend/src/main/java/com/stackup/stackup/session/application/dto/AnalyzeVoicePayload.java index 7da809b5..7c33286e 100644 --- a/backend/src/main/java/com/stackup/stackup/session/application/dto/AnalyzeVoicePayload.java +++ b/backend/src/main/java/com/stackup/stackup/session/application/dto/AnalyzeVoicePayload.java @@ -8,7 +8,7 @@ public record AnalyzeVoicePayload( String audioS3Key, String contentType, String previousQuestionText, // STT 정확도 향상용 hint (optional) - String interviewType, + String mode, String jobCategory ) { } diff --git a/backend/src/main/java/com/stackup/stackup/session/application/dto/GenerateFeedbackPayload.java b/backend/src/main/java/com/stackup/stackup/session/application/dto/GenerateFeedbackPayload.java index 590b22fb..5ff90679 100644 --- a/backend/src/main/java/com/stackup/stackup/session/application/dto/GenerateFeedbackPayload.java +++ b/backend/src/main/java/com/stackup/stackup/session/application/dto/GenerateFeedbackPayload.java @@ -6,7 +6,7 @@ // AI 가 세션 메시지/평가/분석문서 컨텍스트 기반으로 종합 피드백을 생성. public record GenerateFeedbackPayload( Long sessionId, - String interviewType, + String mode, String jobCategory, Integer totalQuestionCount, String endReason, // USER_REQUEST | MAX_QUESTIONS_REACHED diff --git a/backend/src/main/java/com/stackup/stackup/session/application/dto/GenerateFollowupPayload.java b/backend/src/main/java/com/stackup/stackup/session/application/dto/GenerateFollowupPayload.java index 8a6f7fa5..0b5b4469 100644 --- a/backend/src/main/java/com/stackup/stackup/session/application/dto/GenerateFollowupPayload.java +++ b/backend/src/main/java/com/stackup/stackup/session/application/dto/GenerateFollowupPayload.java @@ -1,7 +1,7 @@ package com.stackup.stackup.session.application.dto; -import com.stackup.stackup.session.domain.InterviewType; import com.stackup.stackup.session.domain.JobCategory; +import com.stackup.stackup.session.domain.SessionMode; public record GenerateFollowupPayload( Long sessionId, @@ -9,7 +9,7 @@ public record GenerateFollowupPayload( Long answerMessageId, String previousQuestion, String answerText, - InterviewType interviewType, + SessionMode mode, JobCategory jobCategory ) { } diff --git a/backend/src/main/java/com/stackup/stackup/session/application/dto/GenerateQuestionsPayload.java b/backend/src/main/java/com/stackup/stackup/session/application/dto/GenerateQuestionsPayload.java index e1335627..22684a92 100644 --- a/backend/src/main/java/com/stackup/stackup/session/application/dto/GenerateQuestionsPayload.java +++ b/backend/src/main/java/com/stackup/stackup/session/application/dto/GenerateQuestionsPayload.java @@ -1,12 +1,12 @@ package com.stackup.stackup.session.application.dto; -import com.stackup.stackup.session.domain.InterviewType; import com.stackup.stackup.session.domain.JobCategory; +import com.stackup.stackup.session.domain.SessionMode; import java.util.List; public record GenerateQuestionsPayload( Long sessionId, - InterviewType interviewType, + SessionMode mode, JobCategory jobCategory, List documents, Integer maxQuestions diff --git a/backend/src/main/java/com/stackup/stackup/session/application/dto/SessionCreateCommand.java b/backend/src/main/java/com/stackup/stackup/session/application/dto/SessionCreateCommand.java index a009c58f..8b667b8f 100644 --- a/backend/src/main/java/com/stackup/stackup/session/application/dto/SessionCreateCommand.java +++ b/backend/src/main/java/com/stackup/stackup/session/application/dto/SessionCreateCommand.java @@ -1,6 +1,5 @@ package com.stackup.stackup.session.application.dto; -import com.stackup.stackup.session.domain.InterviewType; import com.stackup.stackup.session.domain.JobCategory; import com.stackup.stackup.session.domain.SessionMode; import java.util.List; @@ -9,7 +8,6 @@ public record SessionCreateCommand( String title, String memo, SessionMode mode, - InterviewType interviewType, JobCategory jobCategory, Integer maxQuestions, Integer maxDurationMinutes, diff --git a/backend/src/main/java/com/stackup/stackup/session/application/dto/SessionResult.java b/backend/src/main/java/com/stackup/stackup/session/application/dto/SessionResult.java index 52a2da0e..c4ff6fe0 100644 --- a/backend/src/main/java/com/stackup/stackup/session/application/dto/SessionResult.java +++ b/backend/src/main/java/com/stackup/stackup/session/application/dto/SessionResult.java @@ -1,7 +1,6 @@ package com.stackup.stackup.session.application.dto; import com.stackup.stackup.session.domain.InterviewSession; -import com.stackup.stackup.session.domain.InterviewType; import com.stackup.stackup.session.domain.JobCategory; import com.stackup.stackup.session.domain.SessionMode; import com.stackup.stackup.session.domain.SessionStatus; @@ -13,7 +12,6 @@ public record SessionResult( String title, String memo, SessionMode mode, - InterviewType interviewType, JobCategory jobCategory, Integer maxQuestions, Integer maxDurationMinutes, @@ -31,7 +29,6 @@ public static SessionResult of(InterviewSession session, List documentIds) session.getTitle(), session.getMemo(), session.getMode(), - session.getInterviewType(), session.getJobCategory(), session.getMaxQuestions(), session.getMaxDurationMinutes(), diff --git a/backend/src/main/java/com/stackup/stackup/session/application/event/SessionCreatedEvent.java b/backend/src/main/java/com/stackup/stackup/session/application/event/SessionCreatedEvent.java index 49cc4519..97f656a4 100644 --- a/backend/src/main/java/com/stackup/stackup/session/application/event/SessionCreatedEvent.java +++ b/backend/src/main/java/com/stackup/stackup/session/application/event/SessionCreatedEvent.java @@ -1,14 +1,14 @@ package com.stackup.stackup.session.application.event; -import com.stackup.stackup.session.domain.InterviewType; import com.stackup.stackup.session.domain.JobCategory; +import com.stackup.stackup.session.domain.SessionMode; import java.util.List; // 세션 생성 commit 후 발행. 인프라스트럭처가 받아 generate.questions 메시지를 RabbitMQ 에 publish. public record SessionCreatedEvent( Long userId, Long sessionId, - InterviewType interviewType, + SessionMode mode, JobCategory jobCategory, Integer maxQuestions, List contextDocumentIds diff --git a/backend/src/main/java/com/stackup/stackup/session/domain/InterviewSession.java b/backend/src/main/java/com/stackup/stackup/session/domain/InterviewSession.java index b0689470..998bec4d 100644 --- a/backend/src/main/java/com/stackup/stackup/session/domain/InterviewSession.java +++ b/backend/src/main/java/com/stackup/stackup/session/domain/InterviewSession.java @@ -50,10 +50,6 @@ public class InterviewSession extends BaseSoftDeleteEntity { @Enumerated(EnumType.STRING) private SessionMode mode; - @Column(name = "interview_type", nullable = false, length = 30) - @Enumerated(EnumType.STRING) - private InterviewType interviewType; - @Column(name = "job_category", nullable = false, length = 30) @Enumerated(EnumType.STRING) private JobCategory jobCategory; @@ -78,13 +74,12 @@ public class InterviewSession extends BaseSoftDeleteEntity { private Instant endedAt; private InterviewSession(User user, String title, String memo, SessionMode mode, - InterviewType interviewType, JobCategory jobCategory, + JobCategory jobCategory, Integer maxQuestions, Integer maxDurationMinutes) { this.user = user; this.title = title; this.memo = memo; this.mode = mode; - this.interviewType = interviewType; this.jobCategory = jobCategory; if (maxQuestions != null) { this.maxQuestions = maxQuestions; @@ -95,15 +90,15 @@ private InterviewSession(User user, String title, String memo, SessionMode mode, } public static InterviewSession create(User user, String title, String memo, SessionMode mode, - InterviewType interviewType, JobCategory jobCategory, + JobCategory jobCategory, Integer maxQuestions, Integer maxDurationMinutes) { if (user == null) { throw new IllegalArgumentException("user must not be null"); } - if (mode == null || interviewType == null || jobCategory == null) { - throw new IllegalArgumentException("mode/interviewType/jobCategory must not be null"); + if (mode == null || jobCategory == null) { + throw new IllegalArgumentException("mode/jobCategory must not be null"); } - return new InterviewSession(user, title, memo, mode, interviewType, jobCategory, maxQuestions, maxDurationMinutes); + return new InterviewSession(user, title, memo, mode, jobCategory, maxQuestions, maxDurationMinutes); } public void start() { diff --git a/backend/src/main/java/com/stackup/stackup/session/domain/InterviewType.java b/backend/src/main/java/com/stackup/stackup/session/domain/InterviewType.java deleted file mode 100644 index 5f9eca33..00000000 --- a/backend/src/main/java/com/stackup/stackup/session/domain/InterviewType.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.stackup.stackup.session.domain; - -public enum InterviewType { - PERSONALITY, - TECHNICAL, - LIVE_CODING, - INTEGRATED -} diff --git a/backend/src/main/java/com/stackup/stackup/session/domain/SessionMode.java b/backend/src/main/java/com/stackup/stackup/session/domain/SessionMode.java index 99bae07f..868130fa 100644 --- a/backend/src/main/java/com/stackup/stackup/session/domain/SessionMode.java +++ b/backend/src/main/java/com/stackup/stackup/session/domain/SessionMode.java @@ -1,6 +1,7 @@ package com.stackup.stackup.session.domain; public enum SessionMode { - ONLINE, - OFFLINE + TECHNICAL, + PERSONALITY, + INTEGRATED } diff --git a/backend/src/main/java/com/stackup/stackup/session/presentation/SessionController.java b/backend/src/main/java/com/stackup/stackup/session/presentation/SessionController.java index b2c7814a..859500f6 100644 --- a/backend/src/main/java/com/stackup/stackup/session/presentation/SessionController.java +++ b/backend/src/main/java/com/stackup/stackup/session/presentation/SessionController.java @@ -35,7 +35,7 @@ public class SessionController { @Operation( operationId = "createSession", summary = "세션 생성 (US-13/14)", - description = "mode/interviewType/jobCategory 필수. contextDocumentIds 로 분석 문서 N개 연결 (US-14). 분석 미완료 문서는 422." + description = "mode/jobCategory 필수. contextDocumentIds 로 분석 문서 N개 연결 (US-14). 분석 미완료 문서는 422." ) @ApiResponses({ @ApiResponse(responseCode = "201", description = "세션 생성 완료"), diff --git a/backend/src/main/java/com/stackup/stackup/session/presentation/dto/SessionCreateRequest.java b/backend/src/main/java/com/stackup/stackup/session/presentation/dto/SessionCreateRequest.java index e1f3f844..c59be710 100644 --- a/backend/src/main/java/com/stackup/stackup/session/presentation/dto/SessionCreateRequest.java +++ b/backend/src/main/java/com/stackup/stackup/session/presentation/dto/SessionCreateRequest.java @@ -1,7 +1,6 @@ package com.stackup.stackup.session.presentation.dto; import com.stackup.stackup.session.application.dto.SessionCreateCommand; -import com.stackup.stackup.session.domain.InterviewType; import com.stackup.stackup.session.domain.JobCategory; import com.stackup.stackup.session.domain.SessionMode; import jakarta.validation.constraints.Max; @@ -13,9 +12,8 @@ public record SessionCreateRequest( @Size(max = 200) String title, @Size(max = 4000) String memo, - // mode: 현재 시연 범위에선 ONLINE 만 유효. 미입력 시 default 적용 (Sprint 4 에서 정식 분기). - SessionMode mode, - @NotNull InterviewType interviewType, + // mode: TECHNICAL/PERSONALITY/INTEGRATED 중 하나를 선택한다. + @NotNull SessionMode mode, @NotNull JobCategory jobCategory, // 첫 질문 + 꼬리질문 최소 1쌍 보장을 위해 하한 2. applyFollowup 자동 종료 흐름 결합. @Min(2) @Max(30) Integer maxQuestions, @@ -27,8 +25,7 @@ public SessionCreateCommand toCommand() { return new SessionCreateCommand( title, memo, - mode == null ? SessionMode.ONLINE : mode, - interviewType, + mode, jobCategory, maxQuestions, maxDurationMinutes, diff --git a/backend/src/main/java/com/stackup/stackup/session/presentation/dto/SessionResponse.java b/backend/src/main/java/com/stackup/stackup/session/presentation/dto/SessionResponse.java index a7907d3f..1c4ecf7f 100644 --- a/backend/src/main/java/com/stackup/stackup/session/presentation/dto/SessionResponse.java +++ b/backend/src/main/java/com/stackup/stackup/session/presentation/dto/SessionResponse.java @@ -1,7 +1,6 @@ package com.stackup.stackup.session.presentation.dto; import com.stackup.stackup.session.application.dto.SessionResult; -import com.stackup.stackup.session.domain.InterviewType; import com.stackup.stackup.session.domain.JobCategory; import com.stackup.stackup.session.domain.SessionMode; import com.stackup.stackup.session.domain.SessionStatus; @@ -13,7 +12,6 @@ public record SessionResponse( String title, String memo, SessionMode mode, - InterviewType interviewType, JobCategory jobCategory, Integer maxQuestions, Integer maxDurationMinutes, @@ -31,7 +29,6 @@ public static SessionResponse from(SessionResult r) { r.title(), r.memo(), r.mode(), - r.interviewType(), r.jobCategory(), r.maxQuestions(), r.maxDurationMinutes(), diff --git a/backend/src/main/resources/db/migration/V6__change_session_mode_to_interview_category.sql b/backend/src/main/resources/db/migration/V6__change_session_mode_to_interview_category.sql new file mode 100644 index 00000000..8e1d58da --- /dev/null +++ b/backend/src/main/resources/db/migration/V6__change_session_mode_to_interview_category.sql @@ -0,0 +1,19 @@ +ALTER TABLE interview_sessions + DROP CONSTRAINT IF EXISTS chk_interview_sessions_mode; + +ALTER TABLE interview_sessions + DROP CONSTRAINT IF EXISTS chk_interview_sessions_interview_type; + +UPDATE interview_sessions +SET mode = CASE + WHEN interview_type IN ('TECHNICAL', 'PERSONALITY', 'INTEGRATED') THEN interview_type + ELSE 'INTEGRATED' +END +WHERE mode NOT IN ('TECHNICAL', 'PERSONALITY', 'INTEGRATED'); + +ALTER TABLE interview_sessions + ADD CONSTRAINT chk_interview_sessions_mode + CHECK (mode IN ('TECHNICAL', 'PERSONALITY', 'INTEGRATED')); + +ALTER TABLE interview_sessions + DROP COLUMN IF EXISTS interview_type; diff --git a/backend/src/test/java/com/stackup/stackup/session/application/FeedbackCallbackServiceTest.java b/backend/src/test/java/com/stackup/stackup/session/application/FeedbackCallbackServiceTest.java index 0758fe36..e12baada 100644 --- a/backend/src/test/java/com/stackup/stackup/session/application/FeedbackCallbackServiceTest.java +++ b/backend/src/test/java/com/stackup/stackup/session/application/FeedbackCallbackServiceTest.java @@ -14,7 +14,6 @@ import com.stackup.stackup.session.application.dto.FeedbackCallbackPayload; import com.stackup.stackup.session.domain.InterviewSession; import com.stackup.stackup.session.domain.InterviewSessionRepository; -import com.stackup.stackup.session.domain.InterviewType; import com.stackup.stackup.session.domain.JobCategory; import com.stackup.stackup.session.domain.SessionFeedback; import com.stackup.stackup.session.domain.SessionFeedbackRepository; @@ -44,7 +43,7 @@ void apply_insertsFeedbackAndPushesSse() { InterviewSession session = sessionFixture(50L); FeedbackCallbackEnvelope env = envelope(50L, "fb-1", new FeedbackCallbackPayload(50L, 85.0, 80.0, 90.0, 75.0, - "강점 요약", "약점 요약", List.of("Spring", "JPA"), null)); + "strength summary", "weakness summary", List.of("Spring", "JPA"), null)); when(processedMessageRepository.existsById("fb-1")).thenReturn(false); when(feedbackRepository.existsBySession_Id(50L)).thenReturn(false); @@ -96,8 +95,7 @@ private FeedbackCallbackEnvelope envelope(Long sessionId, String messageId, Feed private InterviewSession sessionFixture(Long id) { User user = User.createGithubUser(1L, "u", null, null, "t"); ReflectionTestUtils.setField(user, "id", 1L); - InterviewSession s = InterviewSession.create(user, "t", null, SessionMode.ONLINE, - InterviewType.TECHNICAL, JobCategory.BACKEND, 5, 30); + InterviewSession s = InterviewSession.create(user, "t", null, SessionMode.TECHNICAL, JobCategory.BACKEND, 5, 30); ReflectionTestUtils.setField(s, "id", id); return s; } diff --git a/backend/src/test/java/com/stackup/stackup/session/application/InterviewMessageServiceTest.java b/backend/src/test/java/com/stackup/stackup/session/application/InterviewMessageServiceTest.java index 99d93205..ea421192 100644 --- a/backend/src/test/java/com/stackup/stackup/session/application/InterviewMessageServiceTest.java +++ b/backend/src/test/java/com/stackup/stackup/session/application/InterviewMessageServiceTest.java @@ -14,7 +14,6 @@ import com.stackup.stackup.session.domain.InterviewMessageRepository; import com.stackup.stackup.session.domain.InterviewSession; import com.stackup.stackup.session.domain.InterviewSessionRepository; -import com.stackup.stackup.session.domain.InterviewType; import com.stackup.stackup.session.domain.JobCategory; import com.stackup.stackup.session.domain.SessionMode; import com.stackup.stackup.user.domain.User; @@ -89,8 +88,7 @@ private InterviewSession sessionInProgress(Long id) { User user = User.createGithubUser(1L, "u", null, null, "t"); ReflectionTestUtils.setField(user, "id", 1L); InterviewSession s = InterviewSession.create( - user, "t", null, SessionMode.ONLINE, - InterviewType.TECHNICAL, JobCategory.BACKEND, 5, 30 + user, "t", null, SessionMode.TECHNICAL, JobCategory.BACKEND, 5, 30 ); ReflectionTestUtils.setField(s, "id", id); s.start(); diff --git a/backend/src/test/java/com/stackup/stackup/session/application/QuestionsCallbackServiceTest.java b/backend/src/test/java/com/stackup/stackup/session/application/QuestionsCallbackServiceTest.java index acb8af0b..b3ddc779 100644 --- a/backend/src/test/java/com/stackup/stackup/session/application/QuestionsCallbackServiceTest.java +++ b/backend/src/test/java/com/stackup/stackup/session/application/QuestionsCallbackServiceTest.java @@ -18,7 +18,6 @@ import com.stackup.stackup.session.domain.InterviewMessageRepository; import com.stackup.stackup.session.domain.InterviewSession; import com.stackup.stackup.session.domain.InterviewSessionRepository; -import com.stackup.stackup.session.domain.InterviewType; import com.stackup.stackup.session.domain.JobCategory; import com.stackup.stackup.session.domain.SessionMode; import com.stackup.stackup.session.domain.SessionStatus; @@ -46,7 +45,7 @@ class QuestionsCallbackServiceTest { void apply_poolInsertsFirstQuestionAndPushesSse() { InterviewSession session = sessionFixture(11L, SessionStatus.READY); QuestionsCallbackEnvelope env = poolEnvelope(11L, List.of( - new GeneratedQuestion("INTRO", "자기소개"), + new GeneratedQuestion("INTRO", "Introduce yourself"), new GeneratedQuestion("TECH", "JPA?") )); when(processedMessageRepository.existsById("m-1")).thenReturn(false); @@ -68,10 +67,10 @@ void apply_poolInsertsFirstQuestionAndPushesSse() { @Test void apply_followupAutoEndsSessionAtMaxQuestions() { InterviewSession session = sessionFixture(11L, SessionStatus.IN_PROGRESS); - // maxQuestions=5 ; total=4 → followup INSERT 시 5 도달 → 자동 종료 + // maxQuestions=5; total=4, then one follow-up reaches the limit. ReflectionTestUtils.setField(session, "totalQuestionCount", 4); - QuestionsCallbackEnvelope env = followupEnvelope(11L, 200L, "꼬리?"); + QuestionsCallbackEnvelope env = followupEnvelope(11L, 200L, "Follow-up?"); when(processedMessageRepository.existsById("m-2")).thenReturn(false); when(sessionRepository.findById(11L)).thenReturn(Optional.of(session)); when(messageRepository.findById(200L)).thenReturn(Optional.of(parentMessageFixture(session))); @@ -117,8 +116,7 @@ private InterviewSession sessionFixture(Long id, SessionStatus status) { User user = User.createGithubUser(1L, "u", null, null, "t"); ReflectionTestUtils.setField(user, "id", 1L); InterviewSession s = InterviewSession.create( - user, "t", null, SessionMode.ONLINE, - InterviewType.TECHNICAL, JobCategory.BACKEND, 5, 30 + user, "t", null, SessionMode.TECHNICAL, JobCategory.BACKEND, 5, 30 ); ReflectionTestUtils.setField(s, "id", id); if (status == SessionStatus.IN_PROGRESS || status == SessionStatus.COMPLETED) { diff --git a/backend/src/test/java/com/stackup/stackup/session/application/SessionFeedbackRequesterTest.java b/backend/src/test/java/com/stackup/stackup/session/application/SessionFeedbackRequesterTest.java index 28d7d286..414e0b07 100644 --- a/backend/src/test/java/com/stackup/stackup/session/application/SessionFeedbackRequesterTest.java +++ b/backend/src/test/java/com/stackup/stackup/session/application/SessionFeedbackRequesterTest.java @@ -16,7 +16,6 @@ import com.stackup.stackup.session.domain.InterviewMessageRepository; import com.stackup.stackup.session.domain.InterviewSession; import com.stackup.stackup.session.domain.InterviewSessionRepository; -import com.stackup.stackup.session.domain.InterviewType; import com.stackup.stackup.session.domain.JobCategory; import com.stackup.stackup.session.domain.SessionContextRepository; import com.stackup.stackup.session.domain.SessionFeedbackRepository; @@ -75,8 +74,7 @@ void onSessionEnded_skipsWhenFeedbackExists() { private InterviewSession sessionFixture(Long id) { User user = User.createGithubUser(1L, "u", null, null, "t"); ReflectionTestUtils.setField(user, "id", 1L); - InterviewSession s = InterviewSession.create(user, "t", null, SessionMode.ONLINE, - InterviewType.TECHNICAL, JobCategory.BACKEND, 5, 30); + InterviewSession s = InterviewSession.create(user, "t", null, SessionMode.TECHNICAL, JobCategory.BACKEND, 5, 30); ReflectionTestUtils.setField(s, "id", id); return s; } diff --git a/backend/src/test/java/com/stackup/stackup/session/application/SessionServiceTest.java b/backend/src/test/java/com/stackup/stackup/session/application/SessionServiceTest.java index 1da89ad5..33aba768 100644 --- a/backend/src/test/java/com/stackup/stackup/session/application/SessionServiceTest.java +++ b/backend/src/test/java/com/stackup/stackup/session/application/SessionServiceTest.java @@ -16,7 +16,6 @@ import com.stackup.stackup.session.application.event.SessionCreatedEvent; import com.stackup.stackup.session.domain.InterviewSession; import com.stackup.stackup.session.domain.InterviewSessionRepository; -import com.stackup.stackup.session.domain.InterviewType; import com.stackup.stackup.session.domain.JobCategory; import com.stackup.stackup.session.domain.SessionContext; import com.stackup.stackup.session.domain.SessionContextRepository; @@ -55,7 +54,7 @@ void create_savesSessionAndPublishesEvent() { }); SessionResult result = service.create(1L, new SessionCreateCommand( - "title", "memo", SessionMode.ONLINE, InterviewType.TECHNICAL, JobCategory.BACKEND, + "title", "memo", SessionMode.TECHNICAL, JobCategory.BACKEND, 5, 30, List.of() )); @@ -77,7 +76,7 @@ void create_linksAnalyzedContextDocuments() { when(documentRepository.findActiveByIdAndOwner(7L, 1L)).thenReturn(Optional.of(doc)); SessionResult result = service.create(1L, new SessionCreateCommand( - "t", null, SessionMode.ONLINE, InterviewType.TECHNICAL, JobCategory.BACKEND, + "t", null, SessionMode.TECHNICAL, JobCategory.BACKEND, 5, 30, List.of(7L, 7L) )); @@ -94,7 +93,7 @@ void create_rejectsNonAnalyzedDocument() { when(documentRepository.findActiveByIdAndOwner(8L, 1L)).thenReturn(Optional.of(pending)); assertThatThrownBy(() -> service.create(1L, new SessionCreateCommand( - "t", null, SessionMode.ONLINE, InterviewType.TECHNICAL, JobCategory.BACKEND, + "t", null, SessionMode.TECHNICAL, JobCategory.BACKEND, 5, 30, List.of(8L) ))).isInstanceOf(DomainException.class); } @@ -141,8 +140,7 @@ private User userFixture(Long id) { private InterviewSession sessionFixture(Long id) { InterviewSession s = InterviewSession.create( - userFixture(1L), "t", null, SessionMode.ONLINE, - InterviewType.TECHNICAL, JobCategory.BACKEND, 5, 30 + userFixture(1L), "t", null, SessionMode.TECHNICAL, JobCategory.BACKEND, 5, 30 ); ReflectionTestUtils.setField(s, "id", id); return s; diff --git a/backend/src/test/java/com/stackup/stackup/session/application/VoiceCallbackServiceTest.java b/backend/src/test/java/com/stackup/stackup/session/application/VoiceCallbackServiceTest.java index 8e1a02c5..825af1c5 100644 --- a/backend/src/test/java/com/stackup/stackup/session/application/VoiceCallbackServiceTest.java +++ b/backend/src/test/java/com/stackup/stackup/session/application/VoiceCallbackServiceTest.java @@ -16,7 +16,6 @@ import com.stackup.stackup.session.domain.InterviewMessage; import com.stackup.stackup.session.domain.InterviewMessageRepository; import com.stackup.stackup.session.domain.InterviewSession; -import com.stackup.stackup.session.domain.InterviewType; import com.stackup.stackup.session.domain.JobCategory; import com.stackup.stackup.session.domain.MessageStatus; import com.stackup.stackup.session.domain.MessageVoiceAnalysis; @@ -52,8 +51,8 @@ void apply_setsTranscriptAndPublishesAnswerSubmittedEvent() { ReflectionTestUtils.setField(voiceMsg, "id", 200L); VoiceCallbackPayload payload = new VoiceCallbackPayload(50L, 200L, - "원자성 일관성 격리성 영속성", - 120.0, 1.5, Map.of("음", 2), 0.85, null); + "Transactions provide isolation and consistency", + 120.0, 1.5, Map.of("um", 2), 0.85, null); VoiceCallbackEnvelope env = new VoiceCallbackEnvelope("vc-1", "callback.voice", "1", "t", null, "ai", payload, null); @@ -63,7 +62,7 @@ void apply_setsTranscriptAndPublishesAnswerSubmittedEvent() { service.apply(env); - assertThat(voiceMsg.getContent()).isEqualTo("원자성 일관성 격리성 영속성"); + assertThat(voiceMsg.getContent()).isEqualTo("Transactions provide isolation and consistency"); assertThat(voiceMsg.getStatus()).isEqualTo(MessageStatus.COMPLETED); verify(voiceAnalysisRepository).save(any(MessageVoiceAnalysis.class)); verify(events).publishEvent(any(AnswerSubmittedEvent.class)); @@ -107,8 +106,7 @@ void apply_skipsDuplicateMessageId() { private InterviewSession sessionFixture(Long id) { User user = User.createGithubUser(1L, "u", null, null, "t"); ReflectionTestUtils.setField(user, "id", 1L); - InterviewSession s = InterviewSession.create(user, "t", null, SessionMode.ONLINE, - InterviewType.TECHNICAL, JobCategory.BACKEND, 5, 30); + InterviewSession s = InterviewSession.create(user, "t", null, SessionMode.TECHNICAL, JobCategory.BACKEND, 5, 30); ReflectionTestUtils.setField(s, "id", id); return s; } From 63bb00b2f7d543f323d3f78fd0dcf24dc4b76a18 Mon Sep 17 00:00:00 2001 From: pswaao88 Date: Mon, 1 Jun 2026 19:47:41 +0900 Subject: [PATCH 3/3] =?UTF-8?q?feat(ai):=20=EB=A9=B4=EC=A0=91=20=EB=AA=A8?= =?UTF-8?q?=EB=93=9C=EB=A5=BC=20mode=EB=A1=9C=20=ED=86=B5=ED=95=A9?= =?UTF-8?q?=ED=95=B4=20=EA=B4=80=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ai/src/ai_server/chain/feedback_generation_chain.py | 6 +++--- ai/src/ai_server/chain/followup_generation_chain.py | 6 +++--- ai/src/ai_server/chain/prompts/feedback_generation.py | 2 +- ai/src/ai_server/chain/prompts/followup_generation.py | 2 +- ai/src/ai_server/chain/prompts/question_generation.py | 8 ++++---- ai/src/ai_server/chain/question_generation_chain.py | 6 +++--- ai/src/ai_server/messaging/consumers/feedback_consumer.py | 2 +- ai/src/ai_server/messaging/consumers/followup_consumer.py | 2 +- .../ai_server/messaging/consumers/questions_consumer.py | 2 +- ai/src/ai_server/model/messages/feedback.py | 4 +++- ai/src/ai_server/model/messages/followup.py | 4 +++- ai/src/ai_server/model/messages/questions.py | 4 ++-- ai/src/ai_server/model/messages/voice.py | 4 +++- ai/tests/test_feedback_consumer.py | 2 +- ai/tests/test_followup_consumer.py | 2 +- ai/tests/test_questions_consumer.py | 6 +++--- ai/tests/test_voice_consumer.py | 2 +- 17 files changed, 35 insertions(+), 29 deletions(-) diff --git a/ai/src/ai_server/chain/feedback_generation_chain.py b/ai/src/ai_server/chain/feedback_generation_chain.py index fc2ced3f..22c9234c 100644 --- a/ai/src/ai_server/chain/feedback_generation_chain.py +++ b/ai/src/ai_server/chain/feedback_generation_chain.py @@ -28,7 +28,7 @@ async def generate( self, *, job_category: str, - interview_type: str, + mode: str, total_question_count: int | None, end_reason: str | None, transcript: str, @@ -44,7 +44,7 @@ async def generate( self, *, job_category: str, - interview_type: str, + mode: str, total_question_count: int | None, end_reason: str | None, transcript: str, @@ -53,7 +53,7 @@ async def generate( result = await self._chain.ainvoke( { "job_category": job_category, - "interview_type": interview_type, + "mode": mode, "total_question_count": total_question_count or 0, "end_reason": end_reason or "USER_REQUEST", "transcript": transcript, diff --git a/ai/src/ai_server/chain/followup_generation_chain.py b/ai/src/ai_server/chain/followup_generation_chain.py index 2c26ce80..a787b5aa 100644 --- a/ai/src/ai_server/chain/followup_generation_chain.py +++ b/ai/src/ai_server/chain/followup_generation_chain.py @@ -24,7 +24,7 @@ async def generate( self, *, job_category: str, - interview_type: str, + mode: str, previous_question: str, answer_text: str, ) -> FollowupResult: ... @@ -38,14 +38,14 @@ async def generate( self, *, job_category: str, - interview_type: str, + mode: str, previous_question: str, answer_text: str, ) -> FollowupResult: result = await self._chain.ainvoke( { "job_category": job_category, - "interview_type": interview_type, + "mode": mode, "previous_question": previous_question, "answer_text": answer_text, } diff --git a/ai/src/ai_server/chain/prompts/feedback_generation.py b/ai/src/ai_server/chain/prompts/feedback_generation.py index 31a496ef..18a26886 100644 --- a/ai/src/ai_server/chain/prompts/feedback_generation.py +++ b/ai/src/ai_server/chain/prompts/feedback_generation.py @@ -22,7 +22,7 @@ HUMAN_PROMPT = ( "직군: {job_category}\n" - "면접 유형: {interview_type}\n" + "면접 모드: {mode}\n" "총 질문 수: {total_question_count}\n" "종료 사유: {end_reason}\n\n" "=== 메시지 시퀀스 ===\n" diff --git a/ai/src/ai_server/chain/prompts/followup_generation.py b/ai/src/ai_server/chain/prompts/followup_generation.py index 01049b24..e8c10d17 100644 --- a/ai/src/ai_server/chain/prompts/followup_generation.py +++ b/ai/src/ai_server/chain/prompts/followup_generation.py @@ -17,7 +17,7 @@ HUMAN_PROMPT = ( "직군: {job_category}\n" - "면접 유형: {interview_type}\n\n" + "면접 모드: {mode}\n\n" "직전 질문:\n{previous_question}\n\n" "지원자 답변:\n{answer_text}\n\n" "{format_instructions}" diff --git a/ai/src/ai_server/chain/prompts/question_generation.py b/ai/src/ai_server/chain/prompts/question_generation.py index e455403a..eb53718c 100644 --- a/ai/src/ai_server/chain/prompts/question_generation.py +++ b/ai/src/ai_server/chain/prompts/question_generation.py @@ -1,9 +1,9 @@ # 질문 풀 생성 프롬프트 (US-18) -# 분석된 이력서/레포 컨텍스트 + 직군 + 면접 유형 → 질문 N개. +# 분석된 이력서/레포 컨텍스트 + 직군 + 면접 모드 → 질문 N개. SYSTEM_PROMPT = ( "당신은 IT 직군 채용을 진행하는 시니어 면접관입니다. " - "지원자 컨텍스트(이력서, GitHub 레포 분석)와 면접 유형에 맞춰 면접 질문 풀을 " + "지원자 컨텍스트(이력서, GitHub 레포 분석)와 면접 모드에 맞춰 면접 질문 풀을 " "생성하세요.\n" "- 사실에 근거한 질문만. 자료에 없는 내용은 추측하지 않습니다.\n" "- 자료에 명시된 기술 스택과 프로젝트 경험을 적극 인용하세요.\n" @@ -15,7 +15,7 @@ HUMAN_PROMPT = ( "직군: {job_category}\n" - "면접 유형: {interview_type}\n" + "면접 모드: {mode}\n" "최대 질문 수: {max_questions}\n\n" "지원자 컨텍스트 (이력서/레포 분석):\n" "---\n" @@ -24,7 +24,7 @@ "요구 사항:\n" "1. 정확히 {max_questions}개의 질문을 생성합니다.\n" "2. 각 질문에 적절한 category 를 부여합니다.\n" - "3. 직군({job_category})·유형({interview_type}) 에 맞는 비중으로 카테고리 분배:\n" + "3. 직군({job_category})·면접 모드({mode}) 에 맞는 비중으로 카테고리 분배:\n" " - TECHNICAL: CS_FUNDAMENTAL + TECH_CHOICE + PROJECT_DEEP_DIVE 중심.\n" " - PERSONALITY: BEHAVIORAL 중심.\n" " - INTEGRATED: 모두 균형.\n" diff --git a/ai/src/ai_server/chain/question_generation_chain.py b/ai/src/ai_server/chain/question_generation_chain.py index 0b6ecd5f..225ec806 100644 --- a/ai/src/ai_server/chain/question_generation_chain.py +++ b/ai/src/ai_server/chain/question_generation_chain.py @@ -23,7 +23,7 @@ async def generate( self, *, job_category: str, - interview_type: str, + mode: str, max_questions: int, context: str, ) -> GeneratedQuestionPool: ... @@ -37,14 +37,14 @@ async def generate( self, *, job_category: str, - interview_type: str, + mode: str, max_questions: int, context: str, ) -> GeneratedQuestionPool: result = await self._chain.ainvoke( { "job_category": job_category, - "interview_type": interview_type, + "mode": mode, "max_questions": max_questions, "context": context, } diff --git a/ai/src/ai_server/messaging/consumers/feedback_consumer.py b/ai/src/ai_server/messaging/consumers/feedback_consumer.py index f0f4d4df..df78af20 100644 --- a/ai/src/ai_server/messaging/consumers/feedback_consumer.py +++ b/ai/src/ai_server/messaging/consumers/feedback_consumer.py @@ -87,7 +87,7 @@ async def handle(self, message: AbstractIncomingMessage) -> None: result = await self._generator.generate( job_category=req.job_category, - interview_type=req.interview_type, + mode=req.mode, total_question_count=req.total_question_count, end_reason=req.end_reason, transcript=transcript, diff --git a/ai/src/ai_server/messaging/consumers/followup_consumer.py b/ai/src/ai_server/messaging/consumers/followup_consumer.py index fe72079d..dfd40dcd 100644 --- a/ai/src/ai_server/messaging/consumers/followup_consumer.py +++ b/ai/src/ai_server/messaging/consumers/followup_consumer.py @@ -62,7 +62,7 @@ async def handle(self, message: AbstractIncomingMessage) -> None: result = await self._generator.generate( job_category=req.job_category, - interview_type=req.interview_type, + mode=req.mode, previous_question=req.previous_question, answer_text=req.answer_text, ) diff --git a/ai/src/ai_server/messaging/consumers/questions_consumer.py b/ai/src/ai_server/messaging/consumers/questions_consumer.py index 5a415565..72d97053 100644 --- a/ai/src/ai_server/messaging/consumers/questions_consumer.py +++ b/ai/src/ai_server/messaging/consumers/questions_consumer.py @@ -71,7 +71,7 @@ async def handle(self, message: AbstractIncomingMessage) -> None: context_text = _build_context(req.documents) pool = await self._generator.generate( job_category=req.job_category, - interview_type=req.interview_type, + mode=req.mode, max_questions=effective_pool_size, context=context_text, ) diff --git a/ai/src/ai_server/model/messages/feedback.py b/ai/src/ai_server/model/messages/feedback.py index a567899c..c887d3da 100644 --- a/ai/src/ai_server/model/messages/feedback.py +++ b/ai/src/ai_server/model/messages/feedback.py @@ -4,6 +4,8 @@ from ai_server.model._config import camel_config +InterviewMode = Literal["PERSONALITY", "TECHNICAL", "INTEGRATED"] + class FeedbackMessageItem(BaseModel): """세션 시퀀스 한 줄 (Core 가 통째로 동봉).""" @@ -21,7 +23,7 @@ class GenerateFeedbackRequest(BaseModel): model_config = camel_config() session_id: int - interview_type: Literal["PERSONALITY", "TECHNICAL", "LIVE_CODING", "INTEGRATED"] + mode: InterviewMode job_category: Literal["FRONTEND", "BACKEND", "INFRA", "DBA"] total_question_count: int | None = None end_reason: Literal["USER_REQUEST", "MAX_QUESTIONS_REACHED"] | None = None diff --git a/ai/src/ai_server/model/messages/followup.py b/ai/src/ai_server/model/messages/followup.py index 7ce9111f..bfc118b0 100644 --- a/ai/src/ai_server/model/messages/followup.py +++ b/ai/src/ai_server/model/messages/followup.py @@ -4,6 +4,8 @@ from ai_server.model._config import camel_config +InterviewMode = Literal["PERSONALITY", "TECHNICAL", "INTEGRATED"] + class GenerateFollowupRequest(BaseModel): """Core 가 답변 commit 후 발행.""" @@ -14,7 +16,7 @@ class GenerateFollowupRequest(BaseModel): answer_message_id: int # 답변 메시지 ID previous_question: str answer_text: str - interview_type: Literal["PERSONALITY", "TECHNICAL", "LIVE_CODING", "INTEGRATED"] + mode: InterviewMode job_category: Literal["FRONTEND", "BACKEND", "INFRA", "DBA"] diff --git a/ai/src/ai_server/model/messages/questions.py b/ai/src/ai_server/model/messages/questions.py index 718f9c5c..12d83f0e 100644 --- a/ai/src/ai_server/model/messages/questions.py +++ b/ai/src/ai_server/model/messages/questions.py @@ -4,7 +4,7 @@ from ai_server.model._config import camel_config -InterviewType = Literal["PERSONALITY", "TECHNICAL", "LIVE_CODING", "INTEGRATED"] +InterviewMode = Literal["PERSONALITY", "TECHNICAL", "INTEGRATED"] JobCategory = Literal["FRONTEND", "BACKEND", "INFRA", "DBA"] QuestionCategory = Literal[ "CS_FUNDAMENTAL", @@ -30,7 +30,7 @@ class GenerateQuestionsRequest(BaseModel): model_config = camel_config() session_id: int - interview_type: InterviewType + mode: InterviewMode job_category: JobCategory documents: list[DocumentContext] = [] max_questions: int = 10 diff --git a/ai/src/ai_server/model/messages/voice.py b/ai/src/ai_server/model/messages/voice.py index e1842a99..3853c72b 100644 --- a/ai/src/ai_server/model/messages/voice.py +++ b/ai/src/ai_server/model/messages/voice.py @@ -4,6 +4,8 @@ from ai_server.model._config import camel_config +InterviewMode = Literal["PERSONALITY", "TECHNICAL", "INTEGRATED"] + class AnalyzeVoiceRequest(BaseModel): """Core 가 음성 답변 업로드 commit 후 발행.""" @@ -15,7 +17,7 @@ class AnalyzeVoiceRequest(BaseModel): audio_s3_key: str content_type: str previous_question_text: str | None = None - interview_type: Literal["PERSONALITY", "TECHNICAL", "LIVE_CODING", "INTEGRATED"] + mode: InterviewMode job_category: Literal["FRONTEND", "BACKEND", "INFRA", "DBA"] diff --git a/ai/tests/test_feedback_consumer.py b/ai/tests/test_feedback_consumer.py index 57d54596..3870e8e7 100644 --- a/ai/tests/test_feedback_consumer.py +++ b/ai/tests/test_feedback_consumer.py @@ -39,7 +39,7 @@ def _envelope(*, context_documents: list[int] | None = None) -> bytes: "publisher": "core-server", "payload": { "sessionId": 50, - "interviewType": "TECHNICAL", + "mode": "TECHNICAL", "jobCategory": "BACKEND", "totalQuestionCount": 2, "endReason": "MAX_QUESTIONS_REACHED", diff --git a/ai/tests/test_followup_consumer.py b/ai/tests/test_followup_consumer.py index b5e69494..661629c8 100644 --- a/ai/tests/test_followup_consumer.py +++ b/ai/tests/test_followup_consumer.py @@ -45,7 +45,7 @@ def _envelope() -> bytes: "answerMessageId": 502, "previousQuestion": "결제 outbox 어떻게 구현?", "answerText": "RabbitMQ로 보냈습니다.", - "interviewType": "TECHNICAL", + "mode": "TECHNICAL", "jobCategory": "BACKEND", }, "context": {"userId": 42, "sessionId": 99}, diff --git a/ai/tests/test_questions_consumer.py b/ai/tests/test_questions_consumer.py index 27e5cf59..7f495288 100644 --- a/ai/tests/test_questions_consumer.py +++ b/ai/tests/test_questions_consumer.py @@ -73,7 +73,7 @@ async def test_consumer_generates_questions_and_publishes_callback(): body = _envelope( { "sessionId": 99, - "interviewType": "TECHNICAL", + "mode": "TECHNICAL", "jobCategory": "BACKEND", "documents": [ { @@ -92,7 +92,7 @@ async def test_consumer_generates_questions_and_publishes_callback(): generator.generate.assert_awaited_once() call = generator.generate.await_args assert call.kwargs["job_category"] == "BACKEND" - assert call.kwargs["interview_type"] == "TECHNICAL" + assert call.kwargs["mode"] == "TECHNICAL" # envelope.max_questions(=5) 무시하고 initial_pool_size(default 1) 로 강제. Core 가 첫 질문만 사용. assert call.kwargs["max_questions"] == 1 assert "Java" in call.kwargs["context"] @@ -126,7 +126,7 @@ async def test_consumer_skips_when_message_id_already_seen(): body = _envelope( { "sessionId": 99, - "interviewType": "TECHNICAL", + "mode": "TECHNICAL", "jobCategory": "BACKEND", "documents": [], "maxQuestions": 3, diff --git a/ai/tests/test_voice_consumer.py b/ai/tests/test_voice_consumer.py index 51debc84..c9e8dd15 100644 --- a/ai/tests/test_voice_consumer.py +++ b/ai/tests/test_voice_consumer.py @@ -47,7 +47,7 @@ def _envelope() -> bytes: "audioS3Key": "interview/voice/raw/99/501.webm", "contentType": "audio/webm", "previousQuestionText": "ACID 설명해주세요", - "interviewType": "TECHNICAL", + "mode": "TECHNICAL", "jobCategory": "BACKEND", }, "context": {"userId": 42, "sessionId": 99},