diff --git a/src/main/java/com/back/domain/study/plan/dto/StudyPlanRequest.java b/src/main/java/com/back/domain/study/plan/dto/StudyPlanRequest.java index 53935bb8..c7e309ee 100644 --- a/src/main/java/com/back/domain/study/plan/dto/StudyPlanRequest.java +++ b/src/main/java/com/back/domain/study/plan/dto/StudyPlanRequest.java @@ -10,26 +10,41 @@ import lombok.Setter; import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.List; @Getter @Setter @NoArgsConstructor -@AllArgsConstructor public class StudyPlanRequest { private String subject; - @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss") private LocalDateTime startDate; - @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss") private LocalDateTime endDate; private Color color; // RepeatRule 중첩 객체 private RepeatRuleRequest repeatRule; + // LocalDateTime을 분 단위로 자르기 위한 setter + public void setStartDate(LocalDateTime startDate) { + this.startDate = startDate != null ? startDate.truncatedTo(ChronoUnit.MINUTES) : null; + } + public void setEndDate(LocalDateTime endDate) { + this.endDate = endDate != null ? endDate.truncatedTo(ChronoUnit.MINUTES) : null; + } + + // 분 단위로 자른 값을 생성자에서도 설정 + public StudyPlanRequest(String subject, LocalDateTime startDate, LocalDateTime endDate, + Color color, RepeatRuleRequest repeatRule) { + this.subject = subject; + this.startDate = startDate != null ? startDate.truncatedTo(ChronoUnit.MINUTES) : null; + this.endDate = endDate != null ? endDate.truncatedTo(ChronoUnit.MINUTES) : null; + this.color = color; + this.repeatRule = repeatRule; + } @Getter @Setter diff --git a/src/main/java/com/back/global/exception/GlobalExceptionHandler.java b/src/main/java/com/back/global/exception/GlobalExceptionHandler.java index c5f7e94f..3d23fa6e 100644 --- a/src/main/java/com/back/global/exception/GlobalExceptionHandler.java +++ b/src/main/java/com/back/global/exception/GlobalExceptionHandler.java @@ -34,8 +34,8 @@ public ResponseEntity> handleValidationException(MethodArgumentNotV .body(RsData.fail(ErrorCode.BAD_REQUEST)); } - // PATH VARIABLE, REQUEST PARAMETER 타입 미스매치 예외 처리 - // 클라이언트의 데이터 형식이 서버 인자 타입과 안 맞는 경우 예외 (형식 불일치) + // PATH VARIABLE, REQUEST PARAMETER의 validation 예외 처리 + // 클라이언트의 데이터 형식이 서버 인자 형식과 안 맞는 경우 예외 (형식 불일치) @ExceptionHandler(MethodArgumentTypeMismatchException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) public ResponseEntity> handleMethodArgumentTypeMismatch(MethodArgumentTypeMismatchException ex) { diff --git a/src/test/java/com/back/domain/study/plan/controller/StudyPlanControllerTest.java b/src/test/java/com/back/domain/study/plan/controller/StudyPlanControllerTest.java index e0b577c1..36a4ebd2 100644 --- a/src/test/java/com/back/domain/study/plan/controller/StudyPlanControllerTest.java +++ b/src/test/java/com/back/domain/study/plan/controller/StudyPlanControllerTest.java @@ -135,7 +135,7 @@ private StudyPlan createDailyPlan() { } @Test - @DisplayName("단발성 계획 생성") + @DisplayName("단발성 계획 생성 - 하나에만 밀리초가 들어가도 되는가?!") void t1() throws Exception { ResultActions resultActions = mvc.perform(post("/api/plans") @@ -144,8 +144,8 @@ void t1() throws Exception { .content(""" { "subject": "단발성 계획", - "startDate": "2025-09-26T10:46:00", - "endDate": "2025-09-26T11:46:00", + "startDate": "2025-10-03T17:00:00", + "endDate": "2025-10-03T18:30:00.000", "color": "RED" } """)) @@ -162,8 +162,37 @@ void t1() throws Exception { .andExpect(jsonPath("$.message").value("학습 계획이 성공적으로 생성되었습니다.")) .andExpect(jsonPath("$.data.subject").value("단발성 계획")) .andExpect(jsonPath("$.data.color").value("RED")) - .andExpect(jsonPath("$.data.startDate").value("2025-09-26T10:46:00")) - .andExpect(jsonPath("$.data.endDate").value("2025-09-26T11:46:00")) + .andExpect(jsonPath("$.data.startDate").value("2025-10-03T17:00:00")) + .andExpect(jsonPath("$.data.endDate").value("2025-10-03T18:30:00")) + .andExpect(jsonPath("$.data.repeatRule").doesNotExist()); + + } + + @Test + @DisplayName("단발성 계획 생성 - 밀리초까지도 둘 다 전송받는 경우") + void t1_1() throws Exception { + + ResultActions resultActions = mvc.perform(post("/api/plans") + .header("Authorization", "Bearer faketoken") + .contentType(MediaType.APPLICATION_JSON) + .content(""" + { + "subject": "단발성 계획 - 밀리초 포함", + "startDate": "2025-09-21T05:00:00.000", + "endDate": "2025-09-21T07:00:00.000", + "color": "RED" + } + """)) + .andDo(print()); + + resultActions + .andExpect(status().isOk()) // 200 OK인지 확인 + .andExpect(handler().handlerType(StudyPlanController.class)) + .andExpect(handler().methodName("createStudyPlan")) + .andExpect(jsonPath("$.success").value(true)) + .andExpect(jsonPath("$.message").value("학습 계획이 성공적으로 생성되었습니다.")) + .andExpect(jsonPath("$.data.startDate").value("2025-09-21T05:00:00")) + .andExpect(jsonPath("$.data.endDate").value("2025-09-21T07:00:00")) .andExpect(jsonPath("$.data.repeatRule").doesNotExist()); }