From 655aa4aeaf0ea6c13ffc9453f5e27c9b0163692b Mon Sep 17 00:00:00 2001 From: wooh Date: Tue, 26 May 2026 17:12:04 +0900 Subject: [PATCH 1/2] =?UTF-8?q?[Feat]=20=EC=84=A0=ED=83=9D=20=EB=AC=B8?= =?UTF-8?q?=ED=95=AD=20=EC=9D=91=EB=8B=B5=EC=97=90=20=EC=A7=81=EC=A0=91=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20=EC=97=AC=EB=B6=80=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - QuestionResponse에 custom 필드 추가 - 선택 문항 조회 응답에서 기본 문항과 직접 추가 문항을 구분하도록 수정 - 선택 문항 저장 및 답변 저장 응답에도 custom 값이 포함되도록 반영 - 선택 문항 조회 시 custom 값 반환 테스트 추가 --- .../dto/response/QuestionResponse.java | 10 ++++++-- .../analysis/service/QuestionService.java | 19 ++++++++++----- .../analysis/service/QuestionServiceTest.java | 24 +++++++++++++++++-- 3 files changed, 43 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/jobdri/jobdri_api/domain/analysis/dto/response/QuestionResponse.java b/src/main/java/com/jobdri/jobdri_api/domain/analysis/dto/response/QuestionResponse.java index 2a12d0f..95365f0 100644 --- a/src/main/java/com/jobdri/jobdri_api/domain/analysis/dto/response/QuestionResponse.java +++ b/src/main/java/com/jobdri/jobdri_api/domain/analysis/dto/response/QuestionResponse.java @@ -6,14 +6,20 @@ public record QuestionResponse( Long questionId, String content, int charLimit, - String answer + String answer, + boolean custom ) { public static QuestionResponse from(Question question) { + return from(question, false); + } + + public static QuestionResponse from(Question question, boolean custom) { return new QuestionResponse( question.getId(), question.getContent(), question.getLimit(), - question.getAnswer() + question.getAnswer(), + custom ); } } diff --git a/src/main/java/com/jobdri/jobdri_api/domain/analysis/service/QuestionService.java b/src/main/java/com/jobdri/jobdri_api/domain/analysis/service/QuestionService.java index 0bf0cb2..c355f83 100644 --- a/src/main/java/com/jobdri/jobdri_api/domain/analysis/service/QuestionService.java +++ b/src/main/java/com/jobdri/jobdri_api/domain/analysis/service/QuestionService.java @@ -136,7 +136,7 @@ private CustomQuestionCandidate saveCustomCandidate( public QuestionSelectionResponse getSelectedQuestions(User user, Long mockApplyId) { MockApply mockApply = getOwnedMockApply(user, mockApplyId); List questions = questionRepository.findAllByMockApplyIdOrderByIdAsc(mockApply.getId()).stream() - .map(QuestionResponse::from) + .map(this::toQuestionResponse) .toList(); return new QuestionSelectionResponse(mockApply.getId(), mockApply.getStatus(), questions); @@ -169,7 +169,7 @@ public QuestionSelectionResponse saveSelectedQuestions( return new QuestionSelectionResponse( mockApply.getId(), mockApply.getStatus(), - savedQuestions.stream().map(QuestionResponse::from).toList() + savedQuestions.stream().map(this::toQuestionResponse).toList() ); } @@ -200,10 +200,14 @@ public QuestionAnswerResponse saveAnswers( mockApply.getId(), mockApply.getStatus(), mockApplyRepository.calculateSequence(mockApply), - questions.stream().map(QuestionResponse::from).toList() + questions.stream().map(this::toQuestionResponse).toList() ); } + private QuestionResponse toQuestionResponse(Question question) { + return QuestionResponse.from(question, isCustomQuestion(question.getContent())); + } + private MockApply getOwnedMockApply(User user, Long mockApplyId) { MockApply mockApply = mockApplyRepository.findById(mockApplyId) .orElseThrow(() -> new GeneralException( @@ -234,10 +238,13 @@ private int resolveCharLimit(Integer charLimit) { return charLimit; } + private boolean isCustomQuestion(String content) { + return DEFAULT_CANDIDATES.stream() + .noneMatch(candidate -> candidate.content().equals(content)); + } + private void validateCustomCandidate(String content) { - boolean existsInDefault = DEFAULT_CANDIDATES.stream() - .anyMatch(candidate -> candidate.content().equals(content)); - if (existsInDefault) { + if (!isCustomQuestion(content)) { throw new GeneralException(GeneralErrorCode.INVALID_PARAMETER, "이미 기본 후보에 존재하는 문항입니다."); } } diff --git a/src/test/java/com/jobdri/jobdri_api/domain/analysis/service/QuestionServiceTest.java b/src/test/java/com/jobdri/jobdri_api/domain/analysis/service/QuestionServiceTest.java index 155ed99..944eca2 100644 --- a/src/test/java/com/jobdri/jobdri_api/domain/analysis/service/QuestionServiceTest.java +++ b/src/test/java/com/jobdri/jobdri_api/domain/analysis/service/QuestionServiceTest.java @@ -73,7 +73,7 @@ void saveSelectedQuestions() { User user = saveUser("question-save@example.com"); MockApply mockApply = saveMockApply(user); QuestionSelectionSaveRequest request = new QuestionSelectionSaveRequest(List.of( - new QuestionSelectionSaveRequest.QuestionItem("지원 동기를 작성해주세요.", 700, false), + new QuestionSelectionSaveRequest.QuestionItem("지원 동기와 입사 후 목표를 작성해주세요.", 700, false), new QuestionSelectionSaveRequest.QuestionItem("직접 추가한 문항입니다.", null, true) )); @@ -82,10 +82,12 @@ void saveSelectedQuestions() { assertThat(response.mockApplyId()).isEqualTo(mockApply.getId()); assertThat(response.status()).isEqualTo(MockApplyStatus.ANSWER_WRITE); assertThat(response.questions()).hasSize(2); - assertThat(response.questions().get(0).content()).isEqualTo("지원 동기를 작성해주세요."); + assertThat(response.questions().get(0).content()).isEqualTo("지원 동기와 입사 후 목표를 작성해주세요."); assertThat(response.questions().get(0).charLimit()).isEqualTo(700); + assertThat(response.questions().get(0).custom()).isFalse(); assertThat(response.questions().get(1).content()).isEqualTo("직접 추가한 문항입니다."); assertThat(response.questions().get(1).charLimit()).isEqualTo(1000); + assertThat(response.questions().get(1).custom()).isTrue(); assertThat(mockApply.getStatus()).isEqualTo(MockApplyStatus.ANSWER_WRITE); assertThat(questionRepository.findAllByMockApplyId(mockApply.getId())).hasSize(2); } @@ -106,11 +108,29 @@ void saveSelectedQuestionsReplacesExistingQuestions() { assertThat(response.questions()).hasSize(1); assertThat(response.questions().get(0).content()).isEqualTo("새 문항"); + assertThat(response.questions().get(0).custom()).isTrue(); assertThat(questionRepository.findAllByMockApplyId(mockApply.getId())) .extracting(Question::getContent) .containsExactly("새 문항"); } + @Test + @DisplayName("선택 문항 조회 시 직접 추가 여부를 함께 반환한다") + void getSelectedQuestionsReturnsCustomFlag() { + User user = saveUser("question-selected-custom-flag@example.com"); + MockApply mockApply = saveMockApply(user); + questionService.saveSelectedQuestions(user, mockApply.getId(), new QuestionSelectionSaveRequest(List.of( + new QuestionSelectionSaveRequest.QuestionItem("지원 동기와 입사 후 목표를 작성해주세요.", 700, false), + new QuestionSelectionSaveRequest.QuestionItem("직접 추가한 문항입니다.", 1000, true) + ))); + + QuestionSelectionResponse response = questionService.getSelectedQuestions(user, mockApply.getId()); + + assertThat(response.questions()).hasSize(2); + assertThat(response.questions().get(0).custom()).isFalse(); + assertThat(response.questions().get(1).custom()).isTrue(); + } + @Test @DisplayName("직접 추가 문항은 선택 문항으로 저장하지 않고 후보 목록에 추가한다") void addCustomQuestionCandidate() { From 569d72ebe3998425c8fc8554ba3d63cce08b4eff Mon Sep 17 00:00:00 2001 From: wooh Date: Tue, 26 May 2026 17:21:58 +0900 Subject: [PATCH 2/2] =?UTF-8?q?[Refactor]=20=EC=84=A0=ED=83=9D=20=EB=AC=B8?= =?UTF-8?q?=ED=95=AD=20custom=20=EC=9D=91=EB=8B=B5=20=EC=83=9D=EC=84=B1=20?= =?UTF-8?q?=EB=B0=A9=EC=8B=9D=20=EB=AA=85=EC=8B=9C=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - QuestionResponse의 단일 인자 from 메서드 제거 - QuestionResponse 생성 시 custom 값을 명시적으로 전달하도록 정리 - 선택 문항 조회 테스트를 문항 내용과 custom 값 쌍으로 검증하도록 수정 - 리스트 순서에 의존하지 않도록 테스트 assertion 개선 --- .../domain/analysis/dto/response/QuestionResponse.java | 4 ---- .../domain/analysis/service/QuestionServiceTest.java | 9 +++++++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/jobdri/jobdri_api/domain/analysis/dto/response/QuestionResponse.java b/src/main/java/com/jobdri/jobdri_api/domain/analysis/dto/response/QuestionResponse.java index 95365f0..1ba2dee 100644 --- a/src/main/java/com/jobdri/jobdri_api/domain/analysis/dto/response/QuestionResponse.java +++ b/src/main/java/com/jobdri/jobdri_api/domain/analysis/dto/response/QuestionResponse.java @@ -9,10 +9,6 @@ public record QuestionResponse( String answer, boolean custom ) { - public static QuestionResponse from(Question question) { - return from(question, false); - } - public static QuestionResponse from(Question question, boolean custom) { return new QuestionResponse( question.getId(), diff --git a/src/test/java/com/jobdri/jobdri_api/domain/analysis/service/QuestionServiceTest.java b/src/test/java/com/jobdri/jobdri_api/domain/analysis/service/QuestionServiceTest.java index 944eca2..6b75283 100644 --- a/src/test/java/com/jobdri/jobdri_api/domain/analysis/service/QuestionServiceTest.java +++ b/src/test/java/com/jobdri/jobdri_api/domain/analysis/service/QuestionServiceTest.java @@ -37,6 +37,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.assertj.core.api.Assertions.tuple; @SpringBootTest @ActiveProfiles("test") @@ -127,8 +128,12 @@ void getSelectedQuestionsReturnsCustomFlag() { QuestionSelectionResponse response = questionService.getSelectedQuestions(user, mockApply.getId()); assertThat(response.questions()).hasSize(2); - assertThat(response.questions().get(0).custom()).isFalse(); - assertThat(response.questions().get(1).custom()).isTrue(); + assertThat(response.questions()) + .extracting("content", "custom") + .containsExactlyInAnyOrder( + tuple("지원 동기와 입사 후 목표를 작성해주세요.", false), + tuple("직접 추가한 문항입니다.", true) + ); } @Test