-
Notifications
You must be signed in to change notification settings - Fork 1
[FEAT] 캘린더 기능을 추가합니다. #281
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
- Calendar -> SemesterPeriod
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 23
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
eeos/src/main/java/com/blackcompany/eeos/program/presentation/dto/UpdateSemesterPeriodRequest.java (1)
11-14: NPE 위험: @AssertTrue 메서드에서 null 접근 가능Bean Validation에서 필드 검증 순서가 보장되지 않아
startDate/endDate가 null이면 NPE가 날 수 있습니다. null-세이프하게 수정해 주세요.@AssertTrue(message = "종료일은 시작일 이후여야 합니다.") public boolean isEndDateAfterStartDate() { - return endDate.getTime() > startDate.getTime(); + return startDate != null + && endDate != null + && endDate.after(startDate); }eeos/src/main/java/com/blackcompany/eeos/program/application/model/SemesterPeriodModel.java (1)
19-37: 1–2월 기본 학기 계산 오류: 현재 월이 1–2월일 때 직전 해 9/1~당해 2월 말이어야 함현 구현은 1–2월에도 3/1~8/31로 설정되어 잘못된 기본 학기 범위를 반환합니다. 아래처럼 분기(9–12월 / 3–8월 / 1–2월)를 명확히 나눠 주세요.
public SemesterPeriodModel() { LocalDate now = LocalDate.now(); - // 9월 이후이면 9월 1일부터 다음 해 2월 말까지 - if (now.getMonthValue() >= Month.SEPTEMBER.getValue()) { + // 9–12월: 해당 해 9/1 ~ 다음 해 2월 말 + if (now.getMonthValue() >= Month.SEPTEMBER.getValue()) { startDate = Timestamp.valueOf(LocalDate.of(now.getYear(), Month.SEPTEMBER, 1).atStartOfDay()); endDate = Timestamp.valueOf( LocalDate.of( now.getYear() + 1, Month.FEBRUARY, Month.FEBRUARY.length(Year.isLeap(now.getYear() + 1))) .atTime(23, 59, 59)); - } else { - // 3월 이후이면 3월 1일부터 8월 말까지 + // 3–8월: 해당 해 3/1 ~ 8/31 + } else if (now.getMonthValue() >= Month.MARCH.getValue()) { startDate = Timestamp.valueOf(LocalDate.of(now.getYear(), Month.MARCH, 1).atStartOfDay()); endDate = Timestamp.valueOf(LocalDate.of(now.getYear(), Month.AUGUST, 31).atTime(23, 59, 59)); + // 1–2월: 직전 해 9/1 ~ 해당 해 2월 말 + } else { + startDate = Timestamp.valueOf(LocalDate.of(now.getYear() - 1, Month.SEPTEMBER, 1).atStartOfDay()); + endDate = + Timestamp.valueOf( + LocalDate.of( + now.getYear(), + Month.FEBRUARY, + Month.FEBRUARY.length(Year.isLeap(now.getYear()))) + .atTime(23, 59, 59)); } }추가 제안:
- 서버 타임존 의존 최소화를 위해
Clock주입을 고려하면 테스트가 쉬워집니다.- 종료 시각을 진짜 EOD로 다루려면
atStartOfDay().plusDays(1).minusNanos(1)패턴을 고려해 보세요. (DB 정밀도에 따라 선택)eeos/src/main/java/com/blackcompany/eeos/target/application/service/AttendService.java (1)
280-284: Null-safe 정렬 파라미터 처리
sortType.equals("asc")는sortType이 null이면 NPE를 유발합니다. 기본값과 대소문자 무시 처리를 권장합니다.- if (sortType.equals("asc")) { + if ("asc".equalsIgnoreCase(sortType)) { order = Sort.Order.asc("totalScore"); } else { order = Sort.Order.desc("totalScore"); }
🧹 Nitpick comments (38)
eeos/src/main/java/com/blackcompany/eeos/member/application/repository/MemberRepository.java (1)
29-30: findNameById 계약에 예외 명시를 추가해 주세요구현체에서
NotFoundMemberException을 던지므로, 인터페이스에도 이를 문서화하면 호출 측에서 의도를 명확히 파악할 수 있습니다. (대안:Optional<String>반환)아래처럼 Javadoc을 추가하는 것을 제안합니다.
- String findNameById(Long memberId); + /** + * @throws NotFoundMemberException 존재하지 않는 멤버 ID인 경우 + */ + String findNameById(Long memberId);eeos/src/main/java/com/blackcompany/eeos/calendar/application/usecase/DeleteCalendarUsecase.java (1)
3-6: 삭제 유스케이스: 예외/권한 실패 케이스를 계약에 문서화삭제 시 대상 없음/권한 없음 등 도메인 에러가 발생할 수 있습니다. 인터페이스 수준에서 계약을 명시하면 컨트롤러·서비스 계층에서의 처리 일관성이 높아집니다.
public interface DeleteCalendarUsecase { - - void delete(Long calendarId); + /** + * @throws com.blackcompany.eeos.common.exception.BusinessException 도메인 예외(대상 없음/권한 없음 등) + */ + void delete(Long calendarId); }eeos/src/main/java/com/blackcompany/eeos/calendar/application/dto/CalendarQuery.java (1)
3-3: CalendarQuery 필드 의미/범위 주석화
date(null=월 전체?),duration(단위/최솟값)의 의미와 경계값(월=112, 일=131 등)을 Javadoc으로 명시하면 사용 측 혼동을 줄일 수 있습니다. 컨트롤러 레벨에서의 기본값 정책도 함께 기록해 주세요.-public record CalendarQuery(Integer year, Integer month, Integer date, Integer duration) {} +/** + * year, month는 KST 기준. + * date가 null이면 월 단위 조회로 간주(예: 1일~말일), duration은 일 단위이며 null이면 1로 처리(예시). + * 유효 범위: month=1..12, date=1..31, duration>=1. + */ +public record CalendarQuery(Integer year, Integer month, Integer date, Integer duration) {}eeos/src/main/java/com/blackcompany/eeos/common/utils/DateConverter.java (1)
56-58: Null-safe 정렬 및 일관성다른 메서드들은 null-safe인데
toMillis만 NPE 가능성이 있습니다. 동일한 패턴으로 null 처리해 주세요. (이전 러닝: toEpochSecond 반환 타입 혼동 이슈가 있었으므로, 본 메서드에서 null 처리로 호출부 혼선을 줄이는 것이 좋습니다.)- public static Long toMillis(LocalDateTime localDateTime) { - return localDateTime.atZone(ZoneId.of(KST)).toInstant().toEpochMilli(); - } + public static Long toMillis(LocalDateTime localDateTime) { + if (localDateTime == null) { + return null; + } + return localDateTime.atZone(ZoneId.of(KST)).toInstant().toEpochMilli(); + }추가로, 미세 최적화로 아래처럼
ZoneId캐싱을 고려해 주세요. (선택)private static final ZoneId KST_ZONE = ZoneId.of(KST); // 사용처: ...atZone(KST_ZONE)...eeos/src/main/java/com/blackcompany/eeos/calendar/application/exception/NotFoundCalendarTypeException.java (1)
4-8: 불필요한 @Getter 제거 제안필드가 없어 Lombok @Getter는 효과가 없습니다. 불필요한 의존을 줄여 주세요.
-import lombok.Getter; @@ -@Getter public class NotFoundCalendarTypeException extends BusinessException {eeos/src/main/java/com/blackcompany/eeos/member/persistence/MemberRepositoryImpl.java (1)
71-74: 단건 이름 조회 구현은 적절합니다. 다만 대량 조회 시 N+1 주의
CalendarQueryService등에서 리스트 항목별로 반복 호출되면 N+1이 발생할 수 있습니다. 필요 시 ID 목록 기반 벌크 조회(findNamesByIds)를 추가하거나, 상위 계층에서 작성자 ID를 집계해 한 번에 가져오는 방식을 고려해 주세요.eeos/src/main/java/com/blackcompany/eeos/calendar/application/dto/CalendarUpdateCommand.java (1)
3-6: 요청 DTO에 Bean Validation 추가 제안기본 필드 제약을 명시하면 컨트롤러 단에서 빠르게 부적합 입력을 차단할 수 있습니다.
다음과 같이 최소 제약을 권장합니다.
package com.blackcompany.eeos.calendar.application.dto; import com.blackcompany.eeos.common.support.dto.AbstractRequestDto; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; -public record CalendarUpdateCommand(String title, String url, String type, Long startAt, Long endAt) - implements AbstractRequestDto {} +public record CalendarUpdateCommand( + @NotBlank String title, + String url, + @NotBlank String type, + @NotNull Long startAt, + @NotNull Long endAt +) implements AbstractRequestDto {}eeos/src/main/java/com/blackcompany/eeos/calendar/application/dto/CalendarResponses.java (1)
6-6: 응답 리스트 불변성 확보 고려record 자체는 불변이지만 List 내용은 가변입니다. 서비스/컨트롤러에서
List.copyOf(...)로 감싸거나, 팩토리 메서드를 두어 방어적 복사를 권장합니다.eeos/src/main/java/com/blackcompany/eeos/calendar/application/exception/InvalidDateException.java (1)
14-17: 오류 메시지에 상세 맥락 포함 고려가능하면 시작/종료 시각(또는 yyyy-MM-dd 포맷)을 포함하면 디버깅이 수월합니다. 국제화(MessageSource) 적용 시 메시지 키와 파라미터로 분리하는 것도 좋습니다.
eeos/src/main/java/com/blackcompany/eeos/calendar/application/exception/DeniedCalendarTypeException.java (1)
19-19: 한글 표기 통일(칼렌더 → 캘린더)프로젝트 전반에 “캘린더” 표기를 사용 중이라면 메시지도 통일하는 것이 좋습니다.
- return String.format("%s 부서는 해당 타입의 칼렌더를 생성할 수 없습니다.", department.name()); + return String.format("%s 부서는 해당 타입의 캘린더를 생성할 수 없습니다.", department.name());eeos/src/main/java/com/blackcompany/eeos/calendar/application/usecase/GetCalendarUsecase.java (1)
11-11: 파라미터 네이밍 컨벤션 정리 (DDay→dDay)자바 로컬 변수/파라미터는 lowerCamelCase가 관례입니다.
- List<CalendarResponse> getCalendarForDDay(int DDay); + List<CalendarResponse> getCalendarForDDay(int dDay);eeos/src/main/java/com/blackcompany/eeos/calendar/application/repository/CalendarRepository.java (3)
3-5: findById의 부재 케이스 표현을 명확히 해주세요 (Optional 권장).조회 실패 시 예외/NULL/Optional 중 어떤 계약인지 불명확합니다. API 경계를 명확히 하기 위해 Optional 반환을 권장합니다.
아래와 같이 시그니처와 import를 조정해 주세요:
import com.blackcompany.eeos.calendar.application.model.CalendarModel; import java.time.LocalDateTime; import java.util.List; +import java.util.Optional; - CalendarModel findById(Long id); + Optional<CalendarModel> findById(Long id);Also applies to: 11-11
13-13: 메서드 네이밍을 의미와 일치시키세요 (JPA 구현과도 정합).현재 findByBetweenDate는 “구간에 겹치는 일정”을 반환합니다. 내부 JPA는 findBetween을 사용하므로 인터페이스도 동일한 이름을 쓰는 편이 이해/정렬에 좋습니다.
- List<CalendarModel> findByBetweenDate(LocalDateTime startAt, LocalDateTime endAt); + List<CalendarModel> findBetween(LocalDateTime startAt, LocalDateTime endAt);
13-15: 구간 포함 범위/시간대(타임존) 계약을 명시해 주세요.현재 조건은 “양끝 포함” 겹침입니다. 프런트 요구사항(예: 끝=시작 접점 포함 여부)과 서버 내부 기준 시간대(UTC/Asia/Seoul) 합의를 문서/자바독에 명시하는 것을 권장합니다.
eeos/src/main/java/com/blackcompany/eeos/calendar/application/dto/CalendarResponse.java (2)
11-18: 에포크 변환의 시간대 기준과 NPE 방지 확인 필요.DateConverter.toMillis의 기준 시간대(UTC vs KST)를 명시/고정하고, startAt/endAt가 null일 경우 NPE가 발생합니다. 최소한의 널 가드 추가를 권장합니다.
import com.blackcompany.eeos.common.utils.DateConverter; +import java.util.Objects; public static CalendarResponse toResponse(CalendarModel model, String writerName) { - Long startAt = DateConverter.toMillis(model.getStartAt()); - Long endAt = DateConverter.toMillis(model.getEndAt()); + Long startAt = DateConverter.toMillis(Objects.requireNonNull(model.getStartAt(), "startAt must not be null")); + Long endAt = DateConverter.toMillis(Objects.requireNonNull(model.getEndAt(), "endAt must not be null")); String type = model.getType().name();
7-9: 필드 명칭 정합성(“writer” → “writerName” 여부) 검토.응답의 writer 타입이 String(이름)인데, 도메인에서는 writer가 Long(작성자 ID)입니다. 혼동 방지를 위해 “writerName” 등 구체 명명으로의 변경을 검토해 주세요. API 호환성 영향이 크면 문서화라도 권장합니다.
eeos/src/main/java/com/blackcompany/eeos/calendar/persistence/CalendarJpaRepository.java (2)
11-20: 대규모 주석 블록 정리 제안.과거 시도 쿼리 주석은 PR 병합 전 정리(제거)하는 편이 가독성과 유지보수에 유리합니다. 변경 이력은 Git에 남습니다.
21-26: 겹침 검색의 성능/용어 정합성 확인.
- 쿼리는 “겹침(overlap)” 조건입니다. 상위 레이어 메서드 네이밍/문서도 이에 맞추세요(예: findBetween/Overlapping).
- 대량 데이터에서 성능 이슈가 있으면, DB가 PostgreSQL인 경우 tsrange + GIST 인덱스(‘&&’ 연산자)로 전환을 검토하세요. JPA에는 네이티브 쿼리가 필요할 수 있습니다.
eeos/src/main/java/com/blackcompany/eeos/calendar/persistence/CalendarEntity.java (1)
51-56: 열 길이 제한 추가(특히 URL).URL은 255자를 넘는 경우가 많습니다. DB 제약을 명시해 두는 편이 안전합니다. 타이틀도 최대 길이 정책을 명확히 하세요.
- @Column(name = ENTITY_PREFIX + "_title", nullable = false) + @Column(name = ENTITY_PREFIX + "_title", nullable = false, length = 200) private String title; - @Column(name = ENTITY_PREFIX + "_url", nullable = false) + @Column(name = ENTITY_PREFIX + "_url", nullable = false, length = 2048) private String url;eeos/src/main/java/com/blackcompany/eeos/calendar/application/validator/CalendarValidator.java (2)
35-40: 널 입력 방지로 유효성 검사를 견고하게.startAt/endAt가 null이면 isAfter에서 NPE가 납니다. 간단한 널 가드를 추가해 주세요.
public void durationValidate(CalendarModel calendar) { LocalDateTime startAt = calendar.getStartAt(); LocalDateTime endAt = calendar.getEndAt(); - if (startAt.isAfter(endAt)) throw new InvalidDateException(); + if (startAt == null || endAt == null || startAt.isAfter(endAt)) { + throw new InvalidDateException(); + } }
17-33: EnumMap/불변 컬렉션으로 초기화 안정성/성능 미세 개선.HashMap 대신 EnumMap을 쓰고 put 이후 불변으로 고정하면 실수로의 변형을 막을 수 있습니다. 영향도는 낮으므로 취향에 따라 적용하세요.
eeos/src/main/java/com/blackcompany/eeos/calendar/persistence/CalendarRepositoryImpl.java (2)
7-7: 빈 메시지의 orElseThrow 개선: 원인 추적 가능한 예외로 변환현재 NoSuchElementException이 메시지 없이 발생합니다. 식별자 포함 메시지로 치환해 디버깅/관찰성 향상하세요.
+import java.util.NoSuchElementException; @@ - return entity.map(CalendarEntity::toModel).orElseThrow(); + return entity + .map(CalendarEntity::toModel) + .orElseThrow(() -> new NoSuchElementException("Calendar not found. id=" + id));Also applies to: 23-28
36-40: 메서드 네이밍 일관성도메인 레포는 findByBetweenDate, JPA 레포는 findBetween으로 서로 다릅니다. 한쪽으로 맞추어 가독성과 검색성을 높이는 것을 권장합니다.
eeos/src/main/java/com/blackcompany/eeos/calendar/application/service/CalendarCommandService.java (2)
41-42: @slf4j 사용: 주요 상태 변경 로깅 추가 또는 애노테이션 제거현재 로그가 없어 @slf4j가 무용합니다. 생성/수정/삭제에 최소한의 감사 로그를 남기거나 @slf4j 제거를 제안합니다.
- return repository.save(calendar); + Long savedId = repository.save(calendar); + log.info("Calendar created. id={}, writer={}", savedId, memberId); + return savedId; @@ - repository.save(model); + repository.save(model); + log.info("Calendar updated. id={}, writer={}", model.getId(), memberId); @@ - repository.delete(calendarId); + repository.delete(calendarId); + log.info("Calendar deleted. id={}, writer={}", calendarId, memberId);Also applies to: 52-55, 65-66
34-36: RequestScope.getMemberId() 존재 보장 검증미설정 시 NPE/권한우회 이슈가 될 수 있습니다. 필터에서 항상 세팅되는지 확인하거나 null 가드(예: requireNonNull) 추가를 권장합니다.
필요하시면 전역 인증 필터에서 RequestScope 설정 여부를 점검하는 테스트/헬스체크 코드를 제안드릴게요.
Also applies to: 46-47, 59-60
eeos/src/main/java/com/blackcompany/eeos/calendar/application/service/CalendarQueryService.java (4)
17-17: 조회 메서드에 readOnly 트랜잭션 부여읽기 전용 트랜잭션은 플러시 방지/성능/일관성에 유리합니다.
+import org.springframework.transaction.annotation.Transactional; @@ @Override +@Transactional(readOnly = true) public List<CalendarResponse> getCalendar(CalendarQuery query) { @@ @Override +@Transactional(readOnly = true) public List<CalendarResponse> getCalendarForDDay(int DDay) {Also applies to: 26-41, 43-50
68-76: 불필요 변수 및 분기 정리
result는 사용되지 않으며 if-else는 블록으로 감싸 가독성을 높일 수 있습니다.- LocalDateTime result; LocalDate localDate; - if (Objects.isNull(date)) localDate = LocalDate.of(year, month, 1); - else localDate = LocalDate.of(year, month, date); + if (Objects.isNull(date)) { + localDate = LocalDate.of(year, month, 1); + } else { + localDate = LocalDate.of(year, month, date); + } return LocalDateTime.of(localDate, LocalTime.MIDNIGHT);
56-66: 작성자 이름 조회 N+1 최소화캘린더 항목마다
findNameById호출로 N+1 발생 가능성이 큽니다. ID 집합을 모아 배치 조회(findNamesByIds) 후 매핑하거나, JPA 조인/프로젝션으로 한 번에 가져오기를 권장합니다.
27-33: 입력 유효성 보강 제안year/month/date/duration에 대한 범위 검증이 보이지 않습니다(다른 레이어에서 하면 OK). 최소한 month(1
12), date(131), duration(>=0) 체크를 권장합니다.eeos/src/main/java/com/blackcompany/eeos/calendar/presentation/controller/CalendarController.java (1)
64-69: DELETE는 204 No Content 권장 (본문 없이)삭제 성공 시 본문 없는 204가 일반적인 REST 컨벤션입니다. 현재 래퍼로 본문을 생성한다면 API 일관성을 위해 컨벤션을 재검토해 주세요.
대안 A(컨벤션 변경):
@DeleteMapping("/{calendarId}") -public ApiResponse<SuccessBody<Void>> deleteCalendar( - @PathVariable("calendarId") Long calendarId) { - deleteUsecase.delete(calendarId); - return ApiResponseGenerator.success(HttpStatus.OK, MessageCode.DELETE); -} +@ResponseStatus(HttpStatus.NO_CONTENT) +public void deleteCalendar(@PathVariable("calendarId") Long calendarId) { + deleteUsecase.delete(calendarId); +}대안 B(현 래퍼 유지): 프로젝트 전반에서 DELETE 응답을 200 + 표준 래퍼로 통일.
eeos/src/main/java/com/blackcompany/eeos/config/security/SecurityFilterChainConfig.java (1)
89-91:/api/admin/semester-periods/**에 관리자 권한 매처 추가
컨트롤러에서@PutMapping("/admin/semester-periods")가 정의되어 있으므로(/api접두사 기준) 아래와 같이 보안 설정에 명시해 주세요:httpSecurity.authorizeHttpRequests(requests -> { requests.requestMatchers("/api/admin/**").hasAnyRole(ADMIN); + requests.requestMatchers("/api/admin/semester-periods/**").hasAnyRole(ADMIN); requests.requestMatchers(HttpMethod.POST, "/api/programs").hasAnyRole(ADMIN); // … });eeos/src/main/java/com/blackcompany/eeos/program/presentation/dto/UpdateSemesterPeriodRequest.java (1)
7-10: 타입 선택 제안:Timestamp대신LocalDateTime/Instant고려API 경계에서는
java.time타입 사용이 직관적이고 직렬화/타임존 이슈 대응이 수월합니다. DTO를LocalDateTime(또는Instant)로 전환하는 방안을 검토해 주세요. 서비스/리포지토리 단에서 변환 유틸을 일관 적용하면 됩니다.eeos/src/main/java/com/blackcompany/eeos/program/persistence/JpaSemesterPeriodRepository.java (1)
6-8: 최신 행 조회 전략 보완: 인덱스/제약 고려
findTopByOrderByCreatedDateDesc()성능/정합성 강화를 위해:
createdDate에 인덱스를 추가하거나(엔티티 @Index)- 테이블 단일행 보장을 원하면 유니크 제약/체크 제약으로 중복 생성을 차단해 주세요.
eeos/src/main/java/com/blackcompany/eeos/program/application/repository/SemesterPeriodRepository.java (1)
6-10: 리포지토리 계약은 명확함. 메서드 네이밍 소폭 개선 제안JPA 관례와 맞추려면
updateSemesterPeriod를saveSemesterPeriod로 변경하는 것도 고려해 주세요(신규 생성/업데이트 모두 포괄).eeos/src/main/java/com/blackcompany/eeos/target/application/service/AttendService.java (3)
288-296: 기간 기본값 처리 중복/가독성 개선같은 메서드 블록에서 start/end를 각각 Provider로부터 별도로 가져옵니다. 상단에서 가져온
period를 재사용해 중복 호출을 제거하세요.- Timestamp startTimestamp = - startDate == null - ? semesterPeriodProvider.getSemesterPeriod().getStartDate() - : new Timestamp(startDate); - Timestamp endTimestamp = - endDate == null - ? semesterPeriodProvider.getSemesterPeriod().getEndDate() - : new Timestamp(endDate); + var period = semesterPeriodProvider.getSemesterPeriod(); + Timestamp startTimestamp = (startDate == null) ? period.getStartDate() : new Timestamp(startDate); + Timestamp endTimestamp = (endDate == null) ? period.getEndDate() : new Timestamp(endDate);
338-340: Penalty 랭킹에서도 동일한 기간 기본값 처리 패턴 적용여기도 Provider 중복 호출 및 NPE 위험이 있습니다. 한 번만 받아 재사용하세요.
- Timestamp startDate = semesterPeriodProvider.getSemesterPeriod().getStartDate(); - Timestamp endDate = semesterPeriodProvider.getSemesterPeriod().getEndDate(); + var period = semesterPeriodProvider.getSemesterPeriod(); + Timestamp startDate = period.getStartDate(); + Timestamp endDate = period.getEndDate();
314-328: N+1/중복 계산 제거로 쿼리 수 절감이미
pages에 [memberId, totalScore]가 들어왔다면, 각 멤버에 대해 다시findTotalPenaltyScoreByMemberId를 호출할 필요가 없습니다. pages 결과를 Map으로 만들어 재사용하고, 랭킹 계산도 윈도우 함수(가능 시) 또는 배치 질의로 대체를 고려하세요.원하시면 JPA/SQL 레벨에서 집계+랭킹을 한 번에 가져오는 쿼리 스케치 드리겠습니다.
eeos/src/main/java/com/blackcompany/eeos/program/persistence/SemesterPeriodEntity.java (1)
34-38: DB 레벨 무결성(종료 ≥ 시작) 보강 권장엔티티/DB에 체크 제약을 추가하면 애플리케이션 버그나 동시성 이슈에도 데이터 무결성을 지킬 수 있습니다.
예: Hibernate 사용 시
@Check(constraints = "semester_period_end_date >= semester_period_start_date")또는 마이그레이션에서
CHECK제약 추가(Flyway/Liquibase).원하시면 마이그레이션 스크립트 초안을 제공하겠습니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (51)
eeos/src/main/java/com/blackcompany/eeos/calendar/application/dto/CalendarCreateCommand.java(1 hunks)eeos/src/main/java/com/blackcompany/eeos/calendar/application/dto/CalendarQuery.java(1 hunks)eeos/src/main/java/com/blackcompany/eeos/calendar/application/dto/CalendarResponse.java(1 hunks)eeos/src/main/java/com/blackcompany/eeos/calendar/application/dto/CalendarResponses.java(1 hunks)eeos/src/main/java/com/blackcompany/eeos/calendar/application/dto/CalendarUpdateCommand.java(1 hunks)eeos/src/main/java/com/blackcompany/eeos/calendar/application/exception/DeniedCalendarTypeException.java(1 hunks)eeos/src/main/java/com/blackcompany/eeos/calendar/application/exception/DeniedCalendarUpdateException.java(1 hunks)eeos/src/main/java/com/blackcompany/eeos/calendar/application/exception/InvalidDateException.java(1 hunks)eeos/src/main/java/com/blackcompany/eeos/calendar/application/exception/NotFoundCalendarTypeException.java(1 hunks)eeos/src/main/java/com/blackcompany/eeos/calendar/application/model/CalendarModel.java(1 hunks)eeos/src/main/java/com/blackcompany/eeos/calendar/application/model/CalendarType.java(1 hunks)eeos/src/main/java/com/blackcompany/eeos/calendar/application/repository/CalendarRepository.java(1 hunks)eeos/src/main/java/com/blackcompany/eeos/calendar/application/service/CalendarCommandService.java(1 hunks)eeos/src/main/java/com/blackcompany/eeos/calendar/application/service/CalendarQueryService.java(1 hunks)eeos/src/main/java/com/blackcompany/eeos/calendar/application/usecase/CreateCalendarUsecase.java(1 hunks)eeos/src/main/java/com/blackcompany/eeos/calendar/application/usecase/DeleteCalendarUsecase.java(1 hunks)eeos/src/main/java/com/blackcompany/eeos/calendar/application/usecase/GetCalendarUsecase.java(1 hunks)eeos/src/main/java/com/blackcompany/eeos/calendar/application/usecase/UpdateCalendarUsecase.java(1 hunks)eeos/src/main/java/com/blackcompany/eeos/calendar/application/validator/CalendarValidator.java(1 hunks)eeos/src/main/java/com/blackcompany/eeos/calendar/persistence/CalendarEntity.java(1 hunks)eeos/src/main/java/com/blackcompany/eeos/calendar/persistence/CalendarJpaRepository.java(1 hunks)eeos/src/main/java/com/blackcompany/eeos/calendar/persistence/CalendarRepositoryImpl.java(1 hunks)eeos/src/main/java/com/blackcompany/eeos/calendar/presentation/controller/CalendarController.java(1 hunks)eeos/src/main/java/com/blackcompany/eeos/common/utils/DateConverter.java(2 hunks)eeos/src/main/java/com/blackcompany/eeos/common/utils/DateUtil.java(1 hunks)eeos/src/main/java/com/blackcompany/eeos/config/security/SecurityFilterChainConfig.java(1 hunks)eeos/src/main/java/com/blackcompany/eeos/member/application/repository/MemberRepository.java(1 hunks)eeos/src/main/java/com/blackcompany/eeos/member/persistence/JpaMemberRepository.java(2 hunks)eeos/src/main/java/com/blackcompany/eeos/member/persistence/MemberRepositoryImpl.java(1 hunks)eeos/src/main/java/com/blackcompany/eeos/program/application/dto/request/SemesterPeriodApplicationCommand.java(1 hunks)eeos/src/main/java/com/blackcompany/eeos/program/application/dto/response/SemesterPeriodApplicationQuery.java(1 hunks)eeos/src/main/java/com/blackcompany/eeos/program/application/model/SemesterPeriodModel.java(1 hunks)eeos/src/main/java/com/blackcompany/eeos/program/application/repository/CalendarRepository.java(0 hunks)eeos/src/main/java/com/blackcompany/eeos/program/application/repository/SemesterPeriodRepository.java(1 hunks)eeos/src/main/java/com/blackcompany/eeos/program/application/service/CalendarService.java(0 hunks)eeos/src/main/java/com/blackcompany/eeos/program/application/service/SemesterPeriodService.java(1 hunks)eeos/src/main/java/com/blackcompany/eeos/program/application/support/CalendarProvider.java(0 hunks)eeos/src/main/java/com/blackcompany/eeos/program/application/support/SemesterPeriodProvider.java(1 hunks)eeos/src/main/java/com/blackcompany/eeos/program/application/usecase/GetCalendarUsecase.java(0 hunks)eeos/src/main/java/com/blackcompany/eeos/program/application/usecase/GetSemesterPeriodUsecase.java(1 hunks)eeos/src/main/java/com/blackcompany/eeos/program/application/usecase/UpdateCalendarUsecase.java(0 hunks)eeos/src/main/java/com/blackcompany/eeos/program/application/usecase/UpdateSemesterPeriodUsecase.java(1 hunks)eeos/src/main/java/com/blackcompany/eeos/program/persistence/CalendarRepositoryImpl.java(0 hunks)eeos/src/main/java/com/blackcompany/eeos/program/persistence/JpaCalendarRepository.java(0 hunks)eeos/src/main/java/com/blackcompany/eeos/program/persistence/JpaSemesterPeriodRepository.java(1 hunks)eeos/src/main/java/com/blackcompany/eeos/program/persistence/SemesterPeriodEntity.java(1 hunks)eeos/src/main/java/com/blackcompany/eeos/program/persistence/SemesterPeriodRepositoryImpl.java(1 hunks)eeos/src/main/java/com/blackcompany/eeos/program/presentation/controller/CalendarController.java(0 hunks)eeos/src/main/java/com/blackcompany/eeos/program/presentation/controller/SemesterPeriodController.java(1 hunks)eeos/src/main/java/com/blackcompany/eeos/program/presentation/dto/UpdateSemesterPeriodRequest.java(1 hunks)eeos/src/main/java/com/blackcompany/eeos/target/application/service/AttendService.java(5 hunks)
💤 Files with no reviewable changes (8)
- eeos/src/main/java/com/blackcompany/eeos/program/persistence/JpaCalendarRepository.java
- eeos/src/main/java/com/blackcompany/eeos/program/application/support/CalendarProvider.java
- eeos/src/main/java/com/blackcompany/eeos/program/persistence/CalendarRepositoryImpl.java
- eeos/src/main/java/com/blackcompany/eeos/program/application/repository/CalendarRepository.java
- eeos/src/main/java/com/blackcompany/eeos/program/application/usecase/GetCalendarUsecase.java
- eeos/src/main/java/com/blackcompany/eeos/program/application/service/CalendarService.java
- eeos/src/main/java/com/blackcompany/eeos/program/application/usecase/UpdateCalendarUsecase.java
- eeos/src/main/java/com/blackcompany/eeos/program/presentation/controller/CalendarController.java
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-03-30T10:51:03.804Z
Learnt from: rlajm1203
PR: JNU-econovation/EEOS-BE#250
File: eeos/src/main/java/com/blackcompany/eeos/program/application/service/ProgramQuitService.java:47-55
Timestamp: 2025-03-30T10:51:03.804Z
Learning: DateConverter.toEpochSecond(LocalDate) 메소드는 long 값이 아닌 Timestamp 객체를 반환하므로, epoch 시간을 밀리초 단위의 long 값으로 변환하려면 .getTime()을 호출해야 합니다.
Applied to files:
eeos/src/main/java/com/blackcompany/eeos/common/utils/DateConverter.java
🔇 Additional comments (17)
eeos/src/main/java/com/blackcompany/eeos/common/utils/DateUtil.java (1)
17-19: LGTM: YearMonth.lengthOfMonth() 적절히 활용간결하고 표준 API를 사용한 구현입니다.
eeos/src/main/java/com/blackcompany/eeos/member/persistence/JpaMemberRepository.java (1)
26-27: LGTM: 필요한 필드만 선택하는 JPQL로 과다 로딩 방지이름만 조회하여 성능/메모리 측면에서 효율적입니다.
eeos/src/main/java/com/blackcompany/eeos/calendar/application/usecase/CreateCalendarUsecase.java (1)
5-8: 인터페이스 설계 적절간결한 계약(입력 DTO + id 반환)으로 용도에 부합합니다.
eeos/src/main/java/com/blackcompany/eeos/calendar/application/usecase/UpdateCalendarUsecase.java (1)
5-8: 계약 명확하고 적절id와 요청 DTO를 받아 id를 반환하는 패턴이 일관적입니다.
eeos/src/main/java/com/blackcompany/eeos/calendar/application/model/CalendarType.java (1)
13-18: null/blank 입력 방어 코드 추가.null이 들어오면 equalsIgnoreCase에서 NPE가 납니다. 명시적으로 예외를 던져주세요.
public static CalendarType findByName(String name) { - return Arrays.stream(CalendarType.values()) + if (name == null || name.isBlank()) { + throw new NotFoundCalendarTypeException(); + } + return Arrays.stream(CalendarType.values()) .filter(calendarType -> calendarType.name().equalsIgnoreCase(name)) .findFirst() .orElseThrow(NotFoundCalendarTypeException::new); }Likely an incorrect or invalid review comment.
eeos/src/main/java/com/blackcompany/eeos/calendar/persistence/CalendarRepositoryImpl.java (1)
17-21: 엔티티-모델 매핑 및 저장 흐름 LGTM모델→엔티티 변환 후 저장하고 식별자를 반환하는 흐름이 명확합니다.
eeos/src/main/java/com/blackcompany/eeos/calendar/application/service/CalendarQueryService.java (1)
46-50: D-Day 포함 범위 정의 확인
now(자정)~now.plusDays(DDay)에서 종료 포함/제외 기준을 명확히 해주세요. UX 상 DDay=0,1에서 기대치가 다를 수 있습니다(예: 오늘 포함 여부). 필요 시plusDays(DDay + 1)또는 종료 시각을 다음날 자정으로 조정하세요.eeos/src/main/java/com/blackcompany/eeos/calendar/presentation/controller/CalendarController.java (2)
12-15: ApiResponseGenerator.success가 반환하는 ApiResponse는 ResponseEntity를 상속해 생성자에서 전달된 HttpStatus를 super로 호출하므로, 컨트롤러가 반환한 ApiResponse에 의도한 HTTP 상태 코드(201/200 등)가 정상적으로 설정됩니다.
29-33: 메서드 보안 활성화 어노테이션 누락 확인 필요
코드베이스 내에@EnableMethodSecurity또는@EnableGlobalMethodSecurity어노테이션이 검색되지 않습니다. 메서드 레벨@PreAuthorize가 정상 동작하려면 Spring Security 설정 클래스에 해당 어노테이션을 적용했는지 검토해 주세요.eeos/src/main/java/com/blackcompany/eeos/program/application/dto/response/SemesterPeriodApplicationQuery.java (1)
12-15: DTO 구성 일관성 양호엔티티/모델과 필드명이 일치하고, 응답 DTO로 충분합니다.
eeos/src/main/java/com/blackcompany/eeos/program/application/usecase/GetSemesterPeriodUsecase.java (1)
1-7: Usecase 정의 적절단일 책임에 맞는 깔끔한 계약입니다.
eeos/src/main/java/com/blackcompany/eeos/program/application/usecase/UpdateSemesterPeriodUsecase.java (1)
6-8: Usecase 계약 적절명령-쿼리 DTO를 통해 의도 명확합니다.
eeos/src/main/java/com/blackcompany/eeos/target/application/service/AttendService.java (1)
221-223: 메서드 시그니처 확인 완료: @query로 startDate와 endDate를 모두 처리하도록 정의되어 있어 해당 호출은 컴파일 오류 없이 정상 동작합니다.eeos/src/main/java/com/blackcompany/eeos/program/application/service/SemesterPeriodService.java (1)
22-26: 기간 미설정 케이스의 API 응답 정책 명확화Provider가 예외를 던지도록 변경할 경우(권장), 컨트롤러에서 404/204 등 일관된 응답을 주도록 합의가 필요합니다. 현재는 빈/nullable 응답 위험이 있습니다.
보유한 글로벌 예외 핸들러에서
NotFoundSemesterPeriodException→ 404 매핑이 있는지 확인해 주세요.eeos/src/main/java/com/blackcompany/eeos/program/persistence/SemesterPeriodRepositoryImpl.java (1)
26-38: 매핑 단순명료, 역할 적합 — LGTM엔티티↔모델 매핑과 최신 레코드 조회/저장 흐름이 명확합니다. 추가 변경 없이 승인합니다.
eeos/src/main/java/com/blackcompany/eeos/program/presentation/controller/SemesterPeriodController.java (1)
33-38: 기간 미설정 시 응답 정의기간이 없다면 404 또는 204를 반환하도록 예외 매핑/로직을 정리하세요. 현재는 null 필드 포함 응답 가능성이 있습니다(Provider/Service 코멘트와 연계).
eeos/src/main/java/com/blackcompany/eeos/program/persistence/SemesterPeriodEntity.java (1)
21-25: 내림차순 인덱스 호환성 검증 요청
@Index(columnList = "createdDate DESC, semester_period_id DESC")의DESC구문이 실제 사용하는 DB/Dialect 및 Hibernate 설정에서 DDL에 반영되는지 확인하세요. 지원되지 않거나 오류가 발생하면DESC를 제거한 복합 인덱스로 대체해야 합니다.
📌 관련 이슈
closes #280
✒️ 작업 내용
기존에 Calendar 라는 클래스가 존재했지만,
해당 클래스는 '동아리 한 학기 일정' 이라는 의미를 나타내는 클래스이므로 용도에 맞게 기존 클래스의 이름을 변경하였습니다.
캘린더 기능을 구현합니다.
CalendarModel / CalendarEntity: 모델/엔티티CalendarType: 일정 타입 (행사부, 기타)CalendarValidator: 일정 유효성 검증DateConverter.toMillisDateConverter.toLocalDateTime스크린샷 🏞️ (선택)
💬 REVIEWER에게 요구사항 💬
이 PR은, 기존 기능의 클래스명 변경 및 관련된 클래스명 변경으로 인해, 수정된 파일의 수가 많습니다. (PR을 나눠서 날려야 한다는 것을 이제야 알았네요..)그렇기 때문에, 클래스 수정과 관련된 작업만 보고 싶으시다면
이 브랜치는 회원의 부서 관리 기능에 의존하고 있으므로,
BM/feat/#277/department브랜치에서 생성하여,BM/feat/#277/department향해 PR을 열었습니다.Summary by CodeRabbit