Skip to content

Commit a6b39d0

Browse files
committed
fix : byDay 자동 입력 수정에도 적용 + refect: byDay 타입 변경 및 일부 중복 제거
1 parent 4b33ece commit a6b39d0

File tree

7 files changed

+85
-86
lines changed

7 files changed

+85
-86
lines changed

src/main/java/com/back/domain/study/plan/dto/StudyPlanRequest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.back.domain.study.plan.dto;
22

33
import com.back.domain.study.plan.entity.Color;
4+
import com.back.domain.study.plan.entity.DayOfWeek;
45
import com.back.domain.study.plan.entity.Frequency;
56
import com.fasterxml.jackson.annotation.JsonFormat;
67
import lombok.AllArgsConstructor;
@@ -37,7 +38,7 @@ public class StudyPlanRequest {
3738
public static class RepeatRuleRequest {
3839
private Frequency frequency;
3940
private Integer intervalValue;
40-
private List<String> byDay = new ArrayList<>(); // 문자열 리스트
41+
private List<DayOfWeek> byDay = new ArrayList<>();
4142

4243
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
4344
private String untilDate; // "2025-12-31" 형태

src/main/java/com/back/domain/study/plan/dto/StudyPlanResponse.java

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public static class RepeatRuleResponse {
4444
private Frequency frequency;
4545
private Integer repeatInterval;
4646
// byDay 필드는 이미 List<String>으로 선언되어 있음.
47-
private List<String> byDay = new ArrayList<>(); // "MON" 형태의 문자열 리스트
47+
private List<DayOfWeek> byDay = new ArrayList<>(); // "MON" 형태의 문자열 리스트
4848

4949
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
5050
private LocalDate untilDate;
@@ -60,16 +60,6 @@ public RepeatRuleResponse(com.back.domain.study.plan.entity.RepeatRule repeatRul
6060
}
6161
}
6262

63-
public List<DayOfWeek> getByDaysList() {
64-
if (byDay == null || byDay.isEmpty()) {
65-
return List.of();
66-
}
67-
68-
// List<String>의 각 요소를 DayOfWeek enum으로 변환하여 반환
69-
return byDay.stream()
70-
.map(com.back.domain.study.plan.entity.DayOfWeek::valueOf)
71-
.collect(Collectors.toList());
72-
}
7363
}
7464
//엔티티를 DTO로 변환하는 생성자
7565
public StudyPlanResponse(StudyPlan studyPlan) {

src/main/java/com/back/domain/study/plan/entity/RepeatRule.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public class RepeatRule extends BaseEntity {
2929

3030
//요일은 응답에 들어있는 요일을 그대로 저장 (예: "WED")
3131
@Column(name = "by_day")
32-
private List<String> byDay = new ArrayList<>();
32+
private List<DayOfWeek> byDay = new ArrayList<>();
3333

3434
private LocalDate untilDate;
3535
}

src/main/java/com/back/domain/study/plan/entity/RepeatRuleEmbeddable.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,6 @@ public class RepeatRuleEmbeddable {
2222
private Frequency frequency;
2323

2424
private Integer intervalValue;
25-
private List<String> byDay = new ArrayList<>();
25+
private List<DayOfWeek> byDay = new ArrayList<>();
2626
private LocalDate untilDate; // LocalDateTime → LocalDate 변경
2727
}

src/main/java/com/back/domain/study/plan/entity/RepeatType.java

Lines changed: 0 additions & 6 deletions
This file was deleted.

src/main/java/com/back/domain/study/plan/service/StudyPlanService.java

Lines changed: 60 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package com.back.domain.study.plan.service;
22

3-
import com.back.domain.study.plan.dto.StudyPlanDeleteRequest;
43
import com.back.domain.study.plan.dto.StudyPlanRequest;
54
import com.back.domain.study.plan.dto.StudyPlanResponse;
65
import com.back.domain.study.plan.entity.*;
@@ -69,19 +68,9 @@ private RepeatRule createRepeatRule(StudyPlanRequest.RepeatRuleRequest request,
6968
repeatRule.setStudyPlan(studyPlan);
7069
repeatRule.setFrequency(request.getFrequency());
7170
repeatRule.setRepeatInterval(request.getIntervalValue() != null ? request.getIntervalValue() : 1);
71+
// byDay 설정 (WEEKLY인 경우에만 의미 있음)
72+
getByDayInWeekly(request, studyPlan, repeatRule);
7273

73-
// byDay 설정 (WEEKLY 인 경우에만)
74-
if (request.getFrequency() == Frequency.WEEKLY) {
75-
// 1. byDay가 없으면 시작일 요일을 자동으로 설정 (현재 구현 의도 반영)
76-
if(request.getByDay() == null || request.getByDay().isEmpty()) {
77-
String startDayOfWeek = studyPlan.getStartDate().getDayOfWeek().name().substring(0, 3);
78-
// *가정: RepeatRule.byDay는 List<String> 타입으로 가정
79-
repeatRule.setByDay(List.of(startDayOfWeek));
80-
} else {
81-
// 2. byDay가 있다면 요청 값을 사용 (List<String> to List<String> 매핑 확인)
82-
repeatRule.setByDay(request.getByDay());
83-
}
84-
}
8574
// untilDate 설정 및 검증
8675
LocalDate untilDate;
8776

@@ -335,6 +324,10 @@ public StudyPlanResponse updateStudyPlan(Long userId, Long planId, StudyPlanRequ
335324

336325
switch (updateType) {
337326
case ORIGINAL_PLAN_UPDATE:
327+
// 요청에 반복 규칙이 있으면 반복 규칙 수정 후 원본 계획 수정
328+
if (request.getRepeatRule() != null) {
329+
updateRepeatRule(originalPlan.getRepeatRule(), request.getRepeatRule(), originalPlan);
330+
}
338331
return updateOriginalPlan(originalPlan, request);
339332

340333
case REPEAT_INSTANCE_CREATE:
@@ -390,11 +383,6 @@ private StudyPlanResponse updateOriginalPlan(StudyPlan originalPlan, StudyPlanRe
390383
if (request.getEndDate() != null) originalPlan.setEndDate(request.getEndDate());
391384
if (request.getColor() != null) originalPlan.setColor(request.getColor());
392385

393-
// 요청에 반복 규칙이 있고 원본 반복성 계획인 경우에만 반복 규칙 수정
394-
if (request.getRepeatRule() != null && originalPlan.getRepeatRule() != null) {
395-
updateRepeatRule(originalPlan.getRepeatRule(), request.getRepeatRule());
396-
}
397-
398386
StudyPlan savedPlan = studyPlanRepository.save(originalPlan);
399387
return new StudyPlanResponse(savedPlan);
400388
}
@@ -412,7 +400,7 @@ private StudyPlanResponse createRepeatException(StudyPlan originalPlan, StudyPla
412400
exception.setStudyPlan(originalPlan);
413401
exception.setExceptionDate(exceptionDate);
414402
exception.setExceptionType(StudyPlanException.ExceptionType.MODIFIED);
415-
exception.setApplyScope(applyScope); // 파라미터로 받은 applyScope
403+
exception.setApplyScope(applyScope);
416404

417405
// 수정된 내용 설정
418406
if (request.getSubject() != null) exception.setModifiedSubject(request.getSubject());
@@ -422,24 +410,10 @@ private StudyPlanResponse createRepeatException(StudyPlan originalPlan, StudyPla
422410

423411
// 반복 규칙 수정. 요청에 반복 규칙이 있으면 설정
424412
if (request.getRepeatRule() != null) {
425-
RepeatRuleEmbeddable embeddable = new RepeatRuleEmbeddable();
426-
embeddable.setFrequency(request.getRepeatRule().getFrequency());
427-
embeddable.setIntervalValue(request.getRepeatRule().getIntervalValue());
428-
embeddable.setByDay(request.getRepeatRule().getByDay());
429-
430-
if (request.getRepeatRule().getUntilDate() != null && !request.getRepeatRule().getUntilDate().isEmpty()) {
431-
try {
432-
LocalDate untilDate = LocalDate.parse(request.getRepeatRule().getUntilDate());
433-
embeddable.setUntilDate(untilDate);
434-
} catch (Exception e) {
435-
throw new CustomException(ErrorCode.INVALID_DATE_FORMAT);
436-
}
437-
}
438-
413+
RepeatRuleEmbeddable embeddable = createRepeatRuleEmbeddable(request.getRepeatRule(), request.getStartDate());
439414
exception.setModifiedRepeatRule(embeddable);
440415
}
441416

442-
443417
studyPlanExceptionRepository.save(exception);
444418
return createVirtualPlanForDate(originalPlan, exceptionDate);
445419
}
@@ -458,38 +432,26 @@ private StudyPlanResponse updateExistingException(StudyPlan originalPlan, StudyP
458432
if (request.getEndDate() != null) existingException.setModifiedEndDate(request.getEndDate());
459433
if (request.getColor() != null) existingException.setModifiedColor(request.getColor());
460434

461-
// ApplyScope도 업데이트 (사용자가 범위를 변경할 수 있음)
435+
// ApplyScope도 업데이트
462436
existingException.setApplyScope(applyScope);
463437

464438
// 반복 규칙 수정사항 있으면 예외 안에 추가 (embeddable)
465439
if (request.getRepeatRule() != null) {
466-
RepeatRuleEmbeddable embeddable = new RepeatRuleEmbeddable();
467-
embeddable.setFrequency(request.getRepeatRule().getFrequency());
468-
embeddable.setIntervalValue(request.getRepeatRule().getIntervalValue());
469-
embeddable.setByDay(request.getRepeatRule().getByDay());
470-
471-
if (request.getRepeatRule().getUntilDate() != null && !request.getRepeatRule().getUntilDate().isEmpty()) {
472-
try {
473-
LocalDate untilDate = LocalDate.parse(request.getRepeatRule().getUntilDate());
474-
embeddable.setUntilDate(untilDate);
475-
} catch (Exception e) {
476-
throw new CustomException(ErrorCode.INVALID_DATE_FORMAT);
477-
}
478-
}
479-
440+
RepeatRuleEmbeddable embeddable = createRepeatRuleEmbeddable(request.getRepeatRule(), request.getStartDate());
480441
existingException.setModifiedRepeatRule(embeddable);
481442
}
482443

483444
studyPlanExceptionRepository.save(existingException);
484445
return createVirtualPlanForDate(originalPlan, exceptionDate);
485446
}
486447

487-
488448
// 원본의 반복 룰 수정 (엔티티)
489-
private void updateRepeatRule(RepeatRule repeatRule, StudyPlanRequest.RepeatRuleRequest request) {
449+
private void updateRepeatRule(RepeatRule repeatRule, StudyPlanRequest.RepeatRuleRequest request, StudyPlan studyPlan) {
490450
if (request.getFrequency() != null) repeatRule.setFrequency(request.getFrequency());
491451
if (request.getIntervalValue() != null) repeatRule.setRepeatInterval(request.getIntervalValue());
492-
if (request.getByDay() != null) repeatRule.setByDay(request.getByDay());
452+
453+
// byDay 자동 설정 (기존 메서드 재사용)
454+
getByDayInWeekly(request, studyPlan, repeatRule);
493455

494456
if (request.getUntilDate() != null && !request.getUntilDate().isEmpty()) {
495457
try {
@@ -501,6 +463,27 @@ private void updateRepeatRule(RepeatRule repeatRule, StudyPlanRequest.RepeatRule
501463
}
502464
}
503465

466+
// RepeatRuleEmbeddable 생성 헬퍼 메서드 (중복 코드 제거)
467+
private RepeatRuleEmbeddable createRepeatRuleEmbeddable(StudyPlanRequest.RepeatRuleRequest request, LocalDateTime startDate) {
468+
RepeatRuleEmbeddable embeddable = new RepeatRuleEmbeddable();
469+
embeddable.setFrequency(request.getFrequency());
470+
embeddable.setIntervalValue(request.getIntervalValue());
471+
472+
// byDay 자동 설정 (오버로딩된 메서드 사용)
473+
getByDayInWeekly(request, startDate, embeddable);
474+
475+
if (request.getUntilDate() != null && !request.getUntilDate().isEmpty()) {
476+
try {
477+
LocalDate untilDate = LocalDate.parse(request.getUntilDate());
478+
embeddable.setUntilDate(untilDate);
479+
} catch (Exception e) {
480+
throw new CustomException(ErrorCode.INVALID_DATE_FORMAT);
481+
}
482+
}
483+
484+
return embeddable;
485+
}
486+
504487
// ==================== 삭제 ===================
505488
@Transactional
506489
public void deleteStudyPlan(Long userId, Long planId, LocalDate selectedDate, ApplyScope applyScope) {
@@ -617,5 +600,30 @@ private void validateRepeatRuleDate(StudyPlan studyPlan, LocalDate untilDate) {
617600
throw new CustomException(ErrorCode.REPEAT_INVALID_UNTIL_DATE);
618601
}
619602
}
603+
// WEEKLY인 경우 빈 byDay 처리 메서드 (RepeatRule용)
604+
private void getByDayInWeekly(StudyPlanRequest.RepeatRuleRequest request, StudyPlan studyPlan, RepeatRule repeatRule) {
605+
// byDay 설정 (WEEKLY 인 경우에만)
606+
if (request.getFrequency() == Frequency.WEEKLY) {
607+
// 1. byDay가 없으면 시작일 요일을 자동으로 설정
608+
if(request.getByDay() == null || request.getByDay().isEmpty()) {
609+
DayOfWeek startDay = DayOfWeek.valueOf(studyPlan.getStartDate().getDayOfWeek().name().substring(0,3));
610+
repeatRule.setByDay(List.of(startDay));
611+
} else {
612+
// 2. byDay가 있다면 요청 값을 사용
613+
repeatRule.setByDay(request.getByDay());
614+
}
615+
}
616+
}
617+
// WEEKLY인 경우 빈 byDay 처리 메서드 (RepeatRuleEmbeddable용 - 오버로딩)
618+
private void getByDayInWeekly(StudyPlanRequest.RepeatRuleRequest request, LocalDateTime startDate, RepeatRuleEmbeddable embeddable) {
619+
if (request.getFrequency() == Frequency.WEEKLY) {
620+
if (request.getByDay() == null || request.getByDay().isEmpty()) {
621+
DayOfWeek startDay = DayOfWeek.valueOf(startDate.getDayOfWeek().name().substring(0, 3));
622+
embeddable.setByDay(List.of(startDay));
623+
} else {
624+
embeddable.setByDay(request.getByDay());
625+
}
626+
}
627+
}
620628

621629
}

src/test/java/com/back/domain/study/plan/controller/StudyPlanControllerTest.java

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@
3636
import java.util.List;
3737

3838
import static org.assertj.core.api.Assertions.assertThat;
39+
import static org.hamcrest.Matchers.containsInAnyOrder;
40+
import static org.hamcrest.Matchers.hasSize;
3941
import static org.junit.jupiter.api.Assertions.*;
4042
import static org.mockito.ArgumentMatchers.anyString;
4143
import static org.mockito.BDDMockito.given;
@@ -200,7 +202,7 @@ void t2() throws Exception {
200202
.andExpect(jsonPath("$.data.endDate").value("2025-09-26T11:46:00"))
201203
.andExpect(jsonPath("$.data.repeatRule.frequency").value("DAILY"))
202204
.andExpect(jsonPath("$.data.repeatRule.repeatInterval").value(1))
203-
.andExpect(jsonPath("$.data.repeatRule.byDay", Matchers.hasSize(0)))
205+
.andExpect(jsonPath("$.data.repeatRule.byDay", hasSize(0)))
204206
.andExpect(jsonPath("$.data.repeatRule.untilDate").value("2025-12-31"));
205207

206208
}
@@ -295,7 +297,7 @@ void t5() throws Exception {
295297
.andExpect(jsonPath("$.message").value("해당 날짜의 계획을 조회했습니다."))
296298
.andExpect(jsonPath("$.data.date").value("2025-09-29"))
297299
.andExpect(jsonPath("$.data.totalCount").value(1))
298-
.andExpect(jsonPath("$.data.plans", Matchers.hasSize(1)))
300+
.andExpect(jsonPath("$.data.plans", hasSize(1)))
299301
.andExpect(jsonPath("$.data.plans[0].subject").value("Java 공부"))
300302
.andExpect(jsonPath("$.data.plans[0].color").value("BLUE"))
301303
.andExpect(jsonPath("$.data.plans[0].startDate").value("2025-09-29T09:00:00"))
@@ -318,7 +320,7 @@ void t6() throws Exception {
318320
.andExpect(jsonPath("$.message").value("해당 날짜의 계획을 조회했습니다."))
319321
.andExpect(jsonPath("$.data.date").value("2025-09-01"))
320322
.andExpect(jsonPath("$.data.totalCount").value(0))
321-
.andExpect(jsonPath("$.data.plans", Matchers.hasSize(0)));
323+
.andExpect(jsonPath("$.data.plans", hasSize(0)));
322324
}
323325
@Test
324326
@DisplayName("계획 조회 - 기간별 조회")
@@ -357,7 +359,7 @@ void t7() throws Exception {
357359
.andExpect(handler().methodName("getStudyPlansForPeriod"))
358360
.andExpect(jsonPath("$.success").value(true))
359361
.andExpect(jsonPath("$.message").value("기간별 계획을 조회했습니다."))
360-
.andExpect(jsonPath("$.data", Matchers.hasSize(7)));
362+
.andExpect(jsonPath("$.data", hasSize(7)));
361363
}
362364

363365
@Test
@@ -403,12 +405,13 @@ void t9() throws Exception {
403405
.andExpect(jsonPath("$.data.subject").value("수정된 최종 계획 (PUT)"))
404406
.andExpect(jsonPath("$.data.color").value("RED"))
405407
.andExpect(jsonPath("$.data.startDate").value("2025-10-10T14:00:00"))
406-
.andExpect(jsonPath("$.data.endDate").value("2025-10-10T16:00:00"));
408+
.andExpect(jsonPath("$.data.endDate").value("2025-10-10T16:00:00"))
409+
.andExpect(jsonPath("$.data.repeatRule").doesNotExist());
407410

408411
}
409412

410413
@Test
411-
@DisplayName("계획 수정 - 단발성 (반복 규칙 추가 시도)")
414+
@DisplayName("계획 수정 - 단발성 (반복 규칙 추가 시도 + WEEKLY 인 경우 byDay 자동 적용)")
412415
void t9_1() throws Exception {
413416
StudyPlan originalPlan = createSinglePlan();
414417
Long planId = originalPlan.getId();
@@ -423,7 +426,7 @@ void t9_1() throws Exception {
423426
"endDate": "2025-10-01T12:00:00",
424427
"color": "RED",
425428
"repeatRule": {
426-
"frequency": "DAILY",
429+
"frequency": "WEEKLY",
427430
"repeatInterval": 1,
428431
"untilDate": "2025-12-31"
429432
}
@@ -434,11 +437,14 @@ void t9_1() throws Exception {
434437
resultActions
435438
.andExpect(status().isOk()) // 400 Bad Request
436439
.andExpect(handler().handlerType(StudyPlanController.class))
437-
.andExpect(jsonPath("$.success").value(true));
440+
.andExpect(jsonPath("$.success").value(true))
441+
.andExpect(jsonPath("$.data.subject").value("수정된 최종 계획 (PUT)"))
442+
.andExpect(jsonPath("$.data.repeatRule.byDay", hasSize(1)))
443+
.andExpect(jsonPath("$.data.repeatRule.byDay[0]").value("WED"));
438444
}
439445

440446
@Test
441-
@DisplayName("계획 수정 - 반복성 단일 수정")
447+
@DisplayName("계획 수정 - 반복성 가상 계획 단일 수정")
442448
void t10() throws Exception {
443449
StudyPlan originalPlan = createDailyPlan();
444450
Long planId = originalPlan.getId();
@@ -467,13 +473,12 @@ void t10() throws Exception {
467473
.andExpect(jsonPath("$.data.color").value("BLUE"))
468474
.andExpect(jsonPath("$.data.startDate").value("2025-10-10T14:00:00"))
469475
.andExpect(jsonPath("$.data.endDate").value("2025-10-10T16:00:00"));
470-
471-
//조회가 잘 되는지도 검증
472-
mvc.perform(get("/api/plans/date/2025-10-10")
476+
//원본은 변경사항 없이 조회가 잘 되는지도 검증
477+
mvc.perform(get("/api/plans/date/2025-10-01")
473478
.header("Authorization", "Bearer faketoken")
474479
.contentType(MediaType.APPLICATION_JSON))
475480
.andDo(print())
476-
.andExpect(jsonPath("$.data.plans[0].subject").value("수정된 반복 계획 (PUT)"));
481+
.andExpect(jsonPath("$.data.plans[0].subject").value("매일 반복 계획"));
477482
}
478483

479484
@Test
@@ -509,6 +514,7 @@ void t11() throws Exception {
509514
.andExpect(jsonPath("$.data.plans[0].subject").value("매일 반복 계획"));
510515
}
511516

517+
512518
@Test
513519
@DisplayName("계획 삭제 - 단발성 단독 삭제")
514520
void t12() throws Exception {
@@ -617,7 +623,7 @@ void t15() throws Exception {
617623
.contentType(MediaType.APPLICATION_JSON))
618624
.andDo(print())
619625
//검색 결과가 없다면 빈 배열
620-
.andExpect(jsonPath("$.data", Matchers.hasSize(0)));
626+
.andExpect(jsonPath("$.data", hasSize(0)));
621627
}
622628

623629

0 commit comments

Comments
 (0)