[BE] SISC1-223 [FIX] 출석 세션 시간 변수 수정#142
Hidden character warning
Conversation
개요해당 변경사항은 출석 관리 시스템의 DTO 필드명 재정의(roundDate → date, allowedMinutes → availableMinutes)와 시간 처리 방식 전환(LocalDateTime → LocalTime)을 수행합니다. AttendanceSession의 시간 계산 메서드 제거, 저장소 쿼리 메서드 제거, 출석 검증 로직의 라운드 기반 전환 등이 포함됩니다. 변경사항
시퀀스 다이어그램sequenceDiagram
participant Client
participant Controller as AttendanceRoundController
participant Service as AttendanceRoundService
participant Repo as AttendanceRoundRepository
participant DB as Database
Client->>Controller: POST /attendance-round<br/>(date, availableMinutes)
Controller->>Service: createRound(request)
Note over Service: request.getDate() 추출<br/>(null일 시 서버 날짜)
Service->>Repo: save(round)
Repo->>DB: INSERT
DB-->>Repo: roundId
Repo-->>Service: AttendanceRound
Service-->>Controller: AttendanceRoundResponse<br/>(date, availableMinutes)
Controller-->>Client: 200 OK
sequenceDiagram
participant Client
participant Service as AttendanceService
participant RoundRepo as AttendanceRoundRepository
participant AttendanceRepo as AttendanceRepository
participant DB as Database
Client->>Service: checkIn(sessionId, userId)
Service->>RoundRepo: findActiveRound(sessionId)
Note over Service: 활성 라운드 검색
RoundRepo-->>Service: AttendanceRound
Service->>Service: roundStartDateTime =<br/>round.getRoundDate() +<br/>round.getStartTime()
Service->>Service: 5분 윈도우 내<br/>checkedAt 검증
Service->>AttendanceRepo: save(attendance)<br/>(attendanceRound, checkedAt)
AttendanceRepo->>DB: INSERT
DB-->>AttendanceRepo: ok
AttendanceRepo-->>Service: Attendance
Service-->>Client: 201 Created
코드 리뷰 난이도 추정🎯 3 (중간) | ⏱️ ~25분 추가 검토 필요 영역:
관련 가능 PR
제안 검토자
시
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (10)
backend/src/main/java/org/sejongisc/backend/attendance/service/AttendanceSessionService.java (2)
84-91:getAllSessions와getPublicSessions의 구현이 동일합니다.두 메서드 모두
findAll()을 호출하고 동일한 정렬 로직을 사용합니다.getPublicSessions는 공개 세션만 반환해야 하는데, 현재는 모든 세션을 반환하고 있습니다. 의도된 동작인지 확인이 필요합니다.또한, 데이터가 많아질 경우
findAll()후 인메모리 정렬은 성능 이슈가 발생할 수 있습니다. Repository 레벨에서findAllByOrderByCreatedDateDesc()같은 메서드 사용을 권장합니다.@Transactional(readOnly = true) public List<AttendanceSessionResponse> getAllSessions() { - List<AttendanceSession> sessions = attendanceSessionRepository.findAll(); - - return sessions.stream() - .sorted((a, b) -> b.getCreatedDate().compareTo(a.getCreatedDate())) - .map(this::convertToResponse) - .collect(Collectors.toList()); + return attendanceSessionRepository.findAllByOrderByCreatedDateDesc().stream() + .map(this::convertToResponse) + .collect(Collectors.toList()); }
114-122: 활성 세션 필터링 로직이 적절합니다.
OPEN상태 필터링 및createdDate역순 정렬이 올바르게 구현되어 있습니다. 다만, 세션 수가 많아지면findAll()후 필터링보다 Repository 쿼리에서 직접 필터링하는 것이 효율적입니다.-List<AttendanceSession> allSessions = attendanceSessionRepository.findAll(); - -return allSessions.stream() - .filter(session -> SessionStatus.OPEN.equals(session.getStatus())) - .sorted((a, b) -> b.getCreatedDate().compareTo(a.getCreatedDate())) - .map(this::convertToResponse) - .collect(Collectors.toList()); +return attendanceSessionRepository.findByStatusOrderByCreatedDateDesc(SessionStatus.OPEN).stream() + .map(this::convertToResponse) + .collect(Collectors.toList());backend/src/main/java/org/sejongisc/backend/attendance/entity/AttendanceSession.java (2)
30-32: Entity에@JsonFormat어노테이션 사용에 대한 검토가 필요합니다.Entity 클래스에 직접
@JsonFormat을 적용하는 것은 영속성 레이어와 표현 레이어의 결합을 유발합니다. 일반적으로 DTO에서 직렬화 형식을 관리하는 것이 권장됩니다.현재 구조에서 이 어노테이션이 필요한 경우라면 유지해도 무방하지만, 향후 API 응답 형식 변경 시 Entity 수정이 필요해질 수 있습니다.
64-66:getStatus()메서드가 Lombok@Getter와 중복됩니다.클래스에 이미
@Getter가 적용되어 있어status필드에 대한 getter가 자동 생성됩니다. 이 명시적 메서드는 Javadoc 문서화 목적이 아니라면 불필요합니다.backend/src/main/java/org/sejongisc/backend/attendance/entity/Attendance.java (1)
71-74:attendanceRound가 null일 때 false 반환은 데이터 무결성 문제를 숨길 수 있습니다.출석 기록에 라운드 정보가 없는 것은 비정상적인 상태일 수 있습니다. 로깅을 추가하거나, 호출 측에서 이 케이스를 명시적으로 처리하는 것을 권장합니다.
backend/src/main/java/org/sejongisc/backend/attendance/controller/AttendanceRoundController.java (1)
247-252: 예외 처리에서 익명 클래스 대신 Map.of() 사용을 권장합니다.현재 익명 HashMap 초기화 블록을 사용하고 있는데, Java 9+ 환경이라면
Map.of()가 더 간결합니다.} catch (Exception e) { log.error("라운드별 출석 명단 조회 실패: {}", e.getMessage()); - return ResponseEntity.status(400).body(new java.util.HashMap<String, String>() {{ - put("error", "라운드를 찾을 수 없습니다"); - }}); + return ResponseEntity.status(HttpStatus.BAD_REQUEST) + .body(Map.of("error", "라운드를 찾을 수 없습니다")); }backend/src/main/java/org/sejongisc/backend/attendance/service/AttendanceRoundService.java (1)
38-40: 라운드 생성 시 date/availableMinutes 기본값 처리
request.getDate()가null일 때 서버의 현재 날짜를roundDate로 사용하는 로직은 클라이언트/서버 시계 차이를 완화하는 용도로 합리적으로 보입니다.
다만AttendanceRoundRequest.availableMinutes는 DTO에서@NotNull로 강제하고 있는데, 서비스에서는null일 경우 30분을 기본값으로 사용하는 분기까지 두고 있어 약간 설계가 중복됩니다. 실제로 기본값 30분을 허용하려는 의도라면 DTO 쪽 제약을 완화하거나, 항상 명시 입력을 요구한다면 여기의?: 30분기문을 제거해 두면 규칙이 더 명확해질 것 같습니다.Also applies to: 45-47, 49-50, 54-57
backend/src/test/java/org/sejongisc/backend/attendance/service/AttendanceRoundServiceTest.java (1)
52-56: 서비스 테스트가 date/availableMinutes 변경 사항을 잘 검증하고 있습니다
createRound,getRound,getRoundsBySession,updateRound,getRoundByDate테스트 모두가AttendanceRoundRequest의.date(...),.availableMinutes(...)와AttendanceRoundResponse의getDate(),getAvailableMinutes()를 통해 새 필드명을 일관되게 검증하고 있어 DTO–서비스–엔티티 매핑이 잘 커버되고 있습니다.추가로,
createRound에서request.getDate()가null인 경우LocalDate.now()로 fallback 되는 경로에 대한 단위 테스트를 하나 정도 더 두면, 서버 기준 날짜 기본값 동작까지 명확히 문서화/보장할 수 있을 것 같습니다.Also applies to: 82-85, 97-100, 134-136, 187-189, 200-204, 228-231, 287-288
backend/src/main/java/org/sejongisc/backend/attendance/service/AttendanceService.java (1)
78-88: 세션 기반 checkIn에서 활성 라운드 탐색 로직 및 비즈니스 룰 확인 제안현재 시간
now가roundStart ≤ now < roundEnd인 라운드를 스트림에서 찾아 사용하는 구조는 시간 윈도우 관점에서 자연스럽고, 이후 5분 지각 기준(roundStart.plusMinutes(5)) 계산도 직관적입니다.다만 두 가지는 한 번 확인해 보시면 좋겠습니다.
- 이 메서드는 여전히
existsByAttendanceSessionAndUser(session, user)로 세션 단위 중복 출석을 막고 있어서, 하나의 세션에 여러 라운드가 있는 경우에도 해당 세션에서는 한 번만checkIn이 가능해집니다. 여러 라운드를 가진 세션에서 라운드별로 별도의 출석을 허용할지, 아니면 세션당 1회가 맞는지 기획 의도와 맞는지 한 번 점검해 보시면 좋겠습니다.- 라운드 시간 검증 로직이
AttendanceRound.isCheckInAvailable()와 상당 부분 겹칠 가능성이 있어, 장기적으로는 엔티티 쪽 헬퍼(예:isCheckInAvailableAt(LocalDateTime now))를 재사용하는 형태로 리팩터링하면 규칙 변경 시 한 곳만 수정해도 되어 유지보수가 더 수월할 것 같습니다.Also applies to: 90-93
backend/src/test/java/org/sejongisc/backend/attendance/controller/AttendanceRoundControllerTest.java (1)
72-76: 컨트롤러 테스트에서 roundDate → date, allowedMinutes → availableMinutes 전환이 잘 반영되었습니다요청/응답 DTO 변경에 맞춰 컨트롤러 테스트가 모두
date,availableMinutes필드를 사용하도록 정리된 점이 좋습니다. 특히 생성/수정 API에서 JSON 응답을$.date,$.availableMinutes로 검증해 주고 있어, 프런트엔드와의 계약 변경이 회귀 없이 유지될 가능성이 높아졌습니다.추가로,
getRound_success,getRoundsBySession_success,getRoundByDate_success등 일부 조회 테스트에서도 상태뿐 아니라date필드까지 함께 검증해 두면, 향후 매핑 변경 시 더 빨리 이상을 감지하는 데 도움이 될 것 같습니다.Also applies to: 80-83, 95-99, 106-110, 129-133, 153-168, 188-193, 195-200, 210-213, 323-327
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (15)
backend/src/main/java/org/sejongisc/backend/attendance/controller/AttendanceRoundController.java(1 hunks)backend/src/main/java/org/sejongisc/backend/attendance/controller/AttendanceSessionController.java(3 hunks)backend/src/main/java/org/sejongisc/backend/attendance/dto/AttendanceRoundRequest.java(2 hunks)backend/src/main/java/org/sejongisc/backend/attendance/dto/AttendanceRoundResponse.java(2 hunks)backend/src/main/java/org/sejongisc/backend/attendance/dto/AttendanceSessionRequest.java(2 hunks)backend/src/main/java/org/sejongisc/backend/attendance/entity/Attendance.java(1 hunks)backend/src/main/java/org/sejongisc/backend/attendance/entity/AttendanceSession.java(3 hunks)backend/src/main/java/org/sejongisc/backend/attendance/repository/AttendanceSessionRepository.java(0 hunks)backend/src/main/java/org/sejongisc/backend/attendance/service/AttendanceRoundService.java(2 hunks)backend/src/main/java/org/sejongisc/backend/attendance/service/AttendanceService.java(2 hunks)backend/src/main/java/org/sejongisc/backend/attendance/service/AttendanceSessionService.java(7 hunks)backend/src/test/java/org/sejongisc/backend/attendance/controller/AttendanceRoundControllerTest.java(8 hunks)backend/src/test/java/org/sejongisc/backend/attendance/service/AttendanceRoundServiceTest.java(8 hunks)backend/src/test/java/org/sejongisc/backend/attendance/service/AttendanceServiceTest.java(0 hunks)backend/src/test/java/org/sejongisc/backend/attendance/service/SessionLocationUpdateTest.java(0 hunks)
💤 Files with no reviewable changes (3)
- backend/src/test/java/org/sejongisc/backend/attendance/service/AttendanceServiceTest.java
- backend/src/test/java/org/sejongisc/backend/attendance/service/SessionLocationUpdateTest.java
- backend/src/main/java/org/sejongisc/backend/attendance/repository/AttendanceSessionRepository.java
🧰 Additional context used
🧬 Code graph analysis (3)
backend/src/main/java/org/sejongisc/backend/attendance/service/AttendanceRoundService.java (1)
frontend/src/utils/TimeUtils.js (1)
now(2-2)
backend/src/main/java/org/sejongisc/backend/attendance/service/AttendanceSessionService.java (2)
frontend/src/components/attendancemanage/SessionModifyModal.jsx (1)
defaultAvailableMinutes(29-31)frontend/src/contexts/AttendanceContext.jsx (1)
sessions(52-52)
backend/src/main/java/org/sejongisc/backend/attendance/dto/AttendanceSessionRequest.java (2)
frontend/src/utils/axios.js (1)
message(43-46)frontend/src/components/attendancemanage/SessionModifyModal.jsx (1)
defaultAvailableMinutes(29-31)
🔇 Additional comments (13)
backend/src/main/java/org/sejongisc/backend/attendance/dto/AttendanceSessionRequest.java (1)
3-8: 임포트 변경사항 확인 완료필드 타입 변경에 맞춰
LocalDateTime에서LocalTime으로, 그리고@JsonFormat어노테이션을 위한 임포트가 올바르게 추가되었습니다.backend/src/main/java/org/sejongisc/backend/attendance/service/AttendanceSessionService.java (3)
35-36: 세션 생성 로직이 새 필드명으로 올바르게 업데이트되었습니다.
defaultStartTime과defaultAvailableMinutes필드가 request에서 entity로 적절히 매핑되고 있습니다.Also applies to: 49-57
146-152: 세션 수정 시 새 필드가 올바르게 반영됩니다.
defaultStartTime과defaultAvailableMinutes가 업데이트 로직에 정확히 적용되었습니다.
287-295: DTO 변환 로직이 새 필드명으로 올바르게 업데이트되었습니다.
convertToResponse메서드가defaultStartTime과defaultAvailableMinutes를 정확히 매핑합니다. 프론트엔드의SessionModifyModal.jsx에서defaultAvailableMinutes를 사용하는 것과 일관성 있습니다.backend/src/main/java/org/sejongisc/backend/attendance/controller/AttendanceSessionController.java (3)
37-44: API 문서가 새로운 필드명을 반영하여 올바르게 업데이트되었습니다."시간 윈도우 설정" 대신 "기본 시작 시간 및 출석 인정 시간 설정"으로 변경되어 서비스 레이어의 변경사항과 일관성을 유지합니다.
114-123: 활성 세션 조회 API 문서가 새로운 로직을 정확히 설명합니다.OPEN 상태 기반 필터링과 라운드의 시간 상태로 체크인 가능 여부를 판단한다는 점이 명확하게 문서화되었습니다.
133-142: 세션 수정 API 문서가 새 필드명으로 업데이트되었습니다."기본 시작 시간, 출석 인정 시간" 용어가 entity 및 서비스 레이어와 일관성 있게 사용됩니다.
backend/src/main/java/org/sejongisc/backend/attendance/entity/AttendanceSession.java (1)
34-35: 필드명 변경이 적절합니다.
windowSeconds에서defaultAvailableMinutes로 변경하여 분 단위로 직관적인 값을 사용합니다. DB 컬럼명도default_available_minutes로 적절히 매핑되었습니다.backend/src/main/java/org/sejongisc/backend/attendance/controller/AttendanceRoundController.java (1)
56-67: 로깅 문이 새 필드명으로 올바르게 업데이트되었습니다.
roundDate→date,allowedMinutes→availableMinutes로 변경되어 DTO의 필드명 변경과 일관성을 유지합니다. 디버깅에 유용한 상세 정보(타입, 시간 상세)도 잘 유지되어 있습니다.backend/src/main/java/org/sejongisc/backend/attendance/dto/AttendanceRoundResponse.java (1)
38-38: DTO 날짜 필드명 변경 및 매핑이 잘 정리되어 있습니다엔티티의
roundDate를 DTO의date로 매핑하는 부분이 일관되고,@JsonFormat및 Swagger 스키마 설명과도 잘 맞습니다.availableMinutes에allowedMinutes를 노출하는 것도 도메인 보존 + API 명확성 측면에서 자연스러운 구성으로 보입니다.Also applies to: 72-75
backend/src/main/java/org/sejongisc/backend/attendance/service/AttendanceRoundService.java (1)
114-118: updateRound에서 date/availableMinutes 사용으로 변경된 부분 일관성 확인
updateRound에서updateRoundInfo에request.getDate()와request.getAvailableMinutes()를 넘기도록 수정한 부분이 DTO/엔티티 매핑 및 테스트(AttendanceRoundServiceTest)와 일관되게 잘 맞습니다. 이 변경 자체로는 특별한 리스크는 없어 보입니다.backend/src/main/java/org/sejongisc/backend/attendance/dto/AttendanceRoundRequest.java (1)
42-42: 요청 DTO 필드명(date, availableMinutes) 변경이 응답/서비스와 일관적입니다요청 DTO에서
roundDate→date,allowedMinutes→availableMinutes로 이름을 정리한 부분이AttendanceRoundResponse및AttendanceRoundService에서 사용하는 필드들과 잘 맞고, Swagger 스키마 설명과 타입도 그대로 유지되어 API 가독성이 좋아졌습니다.Also applies to: 63-63
backend/src/main/java/org/sejongisc/backend/attendance/service/AttendanceService.java (1)
95-105: Attendance 엔티티에 activeRound를 연결한 변경으로 응답 정합성 향상
Attendance생성 시attendanceRound(activeRound)를 설정하고,checkedAt,awardedPoints,note,checkInLocation,deviceInfo를 함께 기록하는 흐름이convertToResponse에서 사용하는attendanceRoundId, 위치/메모/디바이스 정보 필드와 잘 맞습니다. 기존 세션 기반 체크인에서도 어떤 라운드에 속한 출석인지 식별할 수 있게 되어, 이후 라운드별 조회(getAttendancesByRound)나 분석에 도움이 될 것 같습니다.
| @Schema( | ||
| description = "세션의 제목/이름", | ||
| example = "2024년 10월 동아리 정기 모임", | ||
| example = "금융 IT팀 세션", | ||
| maxLength = 100 | ||
| ) | ||
| @NotBlank(message = "제목은 필수입니다") | ||
| @Size(max = 100, message = "제목은 100자 이하여야 합니다") | ||
| private String title; | ||
|
|
||
| @Schema( | ||
| description = "세션 시작 시간 (ISO 8601 형식). 현재 시간 이후여야 합니다.", | ||
| example = "2024-11-15T14:00:00", | ||
| description = "세션의 기본 시작 시간 (HH:MM:SS 형식). " + | ||
| "모든 라운드는 이 시간을 기본값으로 사용합니다.", | ||
| example = "18:30:00", | ||
| type = "string", | ||
| format = "date-time" | ||
| format = "time" | ||
| ) | ||
| @NotNull(message = "시작 시간은 필수입니다") | ||
| @Future(message = "시작 시간은 현재 시간 이후여야 합니다") | ||
| private LocalDateTime startsAt; | ||
| @JsonFormat(pattern = "HH:mm:ss") | ||
| private LocalTime defaultStartTime; |
There was a problem hiding this comment.
🧩 Analysis chain
@Future 검증 제거 및 날짜 정보 손실 확인 필요
LocalDateTime startsAt에서 LocalTime defaultStartTime으로 변경하면서 다음 사항들을 확인해주세요:
-
검증 로직 손실: 기존
@Future어노테이션이 제거되어 과거 시간에 대한 검증이 불가능합니다. 날짜 정보 없이 시간만으로는 미래 여부를 판단할 수 없으므로, 서비스 레이어에서 날짜와 함께 검증하는 로직이 추가되었는지 확인이 필요합니다. -
의미론적 변경: 절대 날짜시간에서 시간대(time-of-day)로의 변경은 세션이 "언제" 시작하는지가 아닌 "몇 시에" 시작하는지만 나타냅니다. 이것이 의도된 설계 변경인지 확인해주세요.
-
데이터베이스 마이그레이션: 기존
LocalDateTime데이터가LocalTime으로 올바르게 마이그레이션되었는지 확인이 필요합니다.
다음 스크립트로 관련 엔티티와 서비스 레이어의 검증 로직을 확인해주세요:
🏁 Script executed:
#!/bin/bash
# AttendanceSession 엔티티에서 defaultStartTime 필드와 검증 로직 확인
ast-grep --pattern 'class AttendanceSession {
$$$
defaultStartTime
$$$
}'
# 서비스 레이어에서 시간 검증 로직 검색
rg -n -C5 --type=java "defaultStartTime.*valid|validate.*defaultStartTime|Future.*defaultStartTime"
# 데이터베이스 마이그레이션 파일 확인
fd -e sql -e xml "migration|flyway|liquibase" --exec rg -l "defaultStartTime|startsAt"Length of output: 0
🏁 Script executed:
#!/bin/bash
# Find AttendanceSession entity file
echo "=== Finding AttendanceSession entity ==="
fd -e java --exec rg -l "class AttendanceSession" {} \;
# Find all files in attendance directory
echo -e "\n=== Attendance module structure ==="
fd "attendance" -type d
# Search for defaultStartTime definition
echo -e "\n=== Searching for defaultStartTime field ==="
rg -n "defaultStartTime" --type=java -B2 -A2
# Search for LocalTime usage in attendance module
echo -e "\n=== LocalTime usage in attendance ==="
rg -n "LocalTime" --type=java -B1 -A1 | head -50
# Check for validation-related annotations in attendance DTO/entity files
echo -e "\n=== Validation annotations in attendance ==="
rg -n "@Future|@Valid|@Validated" --type=java backend/src/main/java/org/sejongisc/backend/attendanceLength of output: 11423
🏁 Script executed:
#!/bin/bash
# Look for migration files
echo "=== Database migration files ==="
fd -e sql -e xml
# Search for any validation logic on defaultStartTime in service
echo -e "\n=== Validation logic on defaultStartTime ==="
rg -n "defaultStartTime" --type=java -B5 -A5 backend/src/main/java/org/sejongisc/backend/attendance/service/
# Search for LocalTime validation/temporal logic
echo -e "\n=== LocalTime validation/temporal checks ==="
rg -n "LocalTime.*isAfter|LocalTime.*isBefore|LocalTime.*now\(\)|LocalTime.*compareTo" --type=java backend/src/main/java/org/sejongisc/backend/attendance/
# Check entire AttendanceSession entity for all fields and validations
echo -e "\n=== Full AttendanceSession entity ==="
cat backend/src/main/java/org/sejongisc/backend/attendance/entity/AttendanceSession.java | head -80Length of output: 7586
제거된 @Future 검증 대체 로직 미발견
검증 결과 다음 사항이 확인되었습니다:
-
검증 손실 확인:
AttendanceSession엔티티(line 32)와AttendanceSessionRequestDTO(line 39) 모두에서@Future검증이 제거되었으며, 서비스 레이어에서도 대체 검증 로직이 없습니다. -
시간만 저장:
LocalTime사용으로 인해 시간대(time-of-day)만 저장되고 날짜 정보가 제거되었습니다. 엔티티 주석도 "시간만"이라고 명시합니다. -
검증 공백: 현재는
@NotNull만 있어서 null 값만 방지하고, 과거 시간 설정을 막을 방법이 없습니다. 다른 레이어에서도defaultStartTime에 대한 시간 유효성 검증이 발견되지 않습니다. -
데이터베이스: 마이그레이션 파일이 없어서 기존
LocalDateTime데이터의 마이그레이션 상태를 확인할 수 없습니다.
필수 검토 항목:
- 서비스 레이어에서
defaultStartTime이 유효한(미래의) 시간인지 검증하는 로직 추가 필요 - 데이터베이스 스키마 및 기존 데이터 마이그레이션 전략 확인 필요
🤖 Prompt for AI Agents
In
backend/src/main/java/org/sejongisc/backend/attendance/dto/AttendanceSessionRequest.java
around lines 21-39, the @Future validation was removed and defaultStartTime is
now a LocalTime (time-of-day only), leaving no check that the configured start
is a future occurrence and no DB migration strategy; add service-layer
validation in the session create/update service to enforce a future occurrence
by either (A) changing the DTO/entity to LocalDateTime if a specific date+time
is required and validate @Future, or (B) if preserving LocalTime, implement
logic to resolve the next occurrence (combine LocalTime with LocalDate.now(), if
that datetime is before now use next day) and reject or adjust inputs
accordingly, and add unit tests; additionally, review database schema and
provide a migration plan to convert any existing LocalDateTime values to the new
shape or to store both date+time as needed, documenting the chosen approach.
| @Schema( | ||
| description = "출석 체크인이 가능한 시간 윈도우 (초 단위). " + | ||
| "범위: 300초(5분) ~ 14400초(4시간)", | ||
| example = "1800", | ||
| minimum = "300", | ||
| maximum = "14400" | ||
| description = "출석 인정 시간 (분 단위). " + | ||
| "범위: 1분 ~ 240분(4시간)", | ||
| example = "30", | ||
| minimum = "1", | ||
| maximum = "240" | ||
| ) | ||
| @Min(value = 300, message = "최소 5분 이상이어야 합니다") | ||
| @Max(value = 14400, message = "최대 4시간 설정 가능합니다") | ||
| private Integer windowSeconds; | ||
| @Min(value = 1, message = "최소 1분 이상이어야 합니다") | ||
| @Max(value = 240, message = "최대 240분(4시간) 설정 가능합니다") | ||
| private Integer defaultAvailableMinutes; |
There was a problem hiding this comment.
🧩 Analysis chain
최소 시간 제한 변경 검증 필요
출석 인정 시간의 단위가 초에서 분으로 변경되면서, 최소값이 300초(5분)에서 1분으로 감소했습니다:
- 기존: 300초 ~ 14,400초 (5분 ~ 4시간)
- 변경: 1분 ~ 240분 (1분 ~ 4시간)
1분의 출석 인정 시간은 다음과 같은 문제를 야기할 수 있습니다:
- 앱 로딩 시간, 네트워크 지연으로 인한 체크인 실패
- 사용자 경험 저하
최소 5분 정책이 비즈니스 요구사항이었다면, @Min(value = 5)로 유지하는 것을 권장합니다.
비즈니스 로직 검증을 위해 다음을 확인해주세요:
🏁 Script executed:
#!/bin/bash
# 기존 windowSeconds 사용처에서 최소값 검증 로직 확인
rg -n -C3 --type=java "windowSeconds|availableMinutes" | rg -C3 "300|5.*minute"
# 테스트 코드에서 최소값 테스트 케이스 확인
rg -n --type=java "availableMinutes.*[1-5]" backend/src/test/Length of output: 2041
Need to check if there are references to the old 5-minute constraint in the actual business logic.
Based on my verification, I need to gather more direct information from the codebase to understand if this change was intentional.
Based on the verification, here's the final rewritten review comment:
---
**출석 인정 시간 최소값 5분 → 1분 변경에 대한 검증 필요**
단위 변경(초 → 분)과 함께 최소값이 300초(5분)에서 1분으로 감소했습니다:
- **기존**: 300초 ~ 14,400초 (5분 ~ 4시간)
- **변경**: 1분 ~ 240분
**문제점:**
1. **테스트 커버리지 부재** - 최소값(1분) 또는 최대값(240분)에 대한 경계값 테스트 케이스가 없음
현재 테스트는 30분, 45분만 사용 중
2. **비현실적인 최소값** - 1분 출석 창은 다음과 같은 실무 문제 야기:
- 앱 로딩 시간 (보통 2-3초+)
- 네트워크 지연
- 사용자 체크인 실패 가능성 증가
3. **비즈니스 의도 불명확** - 최소값 변경 사유나 관련 주석 없음
**권장사항:**
- 1분 최소값이 의도된 것인지 확인 필요
- 비즈니스 요구사항이면 문서화 필수
- 그렇지 않으면 `@Min(value = 5)`로 유지 권장
- 최소/최대 경계값 테스트 케이스 추가 필수
---
🏁 Script executed:
```shell
#!/bin/bash
# Search for service layer usage of availableMinutes
rg -n -B5 -A5 "availableMinutes" backend/src/main/java/org/sejongisc/backend/attendance/ --type=java | grep -v "test" | head -100
# Look for any migration or change notes
find backend -name "*.java" -o -name "*.md" | xargs rg -l "windowSeconds|availableMinutes|migration|change" 2>/dev/null | head -20
Length of output: 1679
Let me search with the correct directory structure:
🏁 Script executed:
#!/bin/bash
# Search for availableMinutes usage in service/controller/dto
rg -n -B3 -A3 "availableMinutes" git/backend/src/main/java/org/sejongisc/backend/attendance/ --type=java
# Look for any business logic with duration/time calculations
rg -n -B2 -A2 "availableMinutes|windowSeconds" git/backend/src/main/java/org/sejongisc/backend/attendance/service/ --type=javaLength of output: 207
Let me try a different approach to inspect the files directly:
🏁 Script executed:
#!/bin/bash
# Find and display the AttendanceRoundRequest.java file
find . -name "AttendanceRoundRequest.java" -type f
# Find and display the AttendanceRoundService.java file
find . -name "AttendanceRoundService.java" -type f
# Find dto files related to attendance
find . -path "*/attendance/dto/*" -name "*.java" -type fLength of output: 990
| /** | ||
| * 지각 여부 판단 | ||
| * - 라운드의 시작 시간 이후에 체크인했으면 지각 | ||
| */ | ||
| public boolean isLate() { | ||
| if (checkedAt == null || attendanceSession.getStartsAt() == null) { | ||
| if (checkedAt == null || attendanceRound == null) { | ||
| return false; | ||
| } | ||
| return checkedAt.isAfter(attendanceSession.getStartsAt()); | ||
| // 라운드의 시작 시간(LocalTime)과 체크인 시간(LocalDateTime)을 비교 | ||
| LocalDateTime roundStartDateTime = attendanceRound.getRoundDate() | ||
| .atTime(attendanceRound.getStartTime()); | ||
| return checkedAt.isAfter(roundStartDateTime); | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
isLate() 로직이 잘못되었을 가능성이 있습니다.
현재 구현은 체크인 시간이 라운드 시작 시간 이후면 지각으로 판단합니다. 그러나 일반적으로 "지각"은 출석 인정 시간(availableMinutes) 이후에 체크인한 경우를 의미합니다.
예: 시작 시간 18:30, 출석 인정 30분이면 18:30~19:00 사이는 정상 출석, 19:00 이후가 지각이어야 합니다.
public boolean isLate() {
if (checkedAt == null || attendanceRound == null) {
return false;
}
- // 라운드의 시작 시간(LocalTime)과 체크인 시간(LocalDateTime)을 비교
LocalDateTime roundStartDateTime = attendanceRound.getRoundDate()
.atTime(attendanceRound.getStartTime());
- return checkedAt.isAfter(roundStartDateTime);
+ // 출석 인정 시간 이후에 체크인했으면 지각
+ LocalDateTime lateThreshold = roundStartDateTime
+ .plusMinutes(attendanceRound.getAvailableMinutes());
+ return checkedAt.isAfter(lateThreshold);
}🏁 Script executed:
#!/bin/bash
# Description: AttendanceRound 엔티티에서 availableMinutes 필드와 getter 확인
# availableMinutes 필드 확인
ast-grep --pattern $'class AttendanceRound {
$$$
availableMinutes
$$$
}'
# getAvailableMinutes 메서드 또는 필드 검색
rg -n "availableMinutes" --type javaLength of output: 2812
🏁 Script executed:
#!/bin/bash
# Find and examine the AttendanceRound entity class definition
# Look for the AttendanceRound entity class
fd -t f "AttendanceRound.java" --exclude="*Test*" --exclude="*Request*" --exclude="*Response*"
# Search for the entity file path
rg -n "class AttendanceRound" --type java -A 30 --exclude="*Test*" --exclude="*Request*" --exclude="*Response*" | head -100Length of output: 494
🏁 Script executed:
#!/bin/bash
# Get content of AttendanceRound entity to see all methods
cat backend/src/main/java/org/sejongisc/backend/attendance/entity/AttendanceRound.javaLength of output: 3429
isLate() 로직이 잘못되었고, 제안된 수정 사항의 메서드명이 맞지 않습니다.
현재 구현은 체크인 시간이 라운드 시작 시간 이후면 지각으로 판단합니다. 일반적으로 "지각"은 출석 인정 시간(allowedMinutes) 이후에 체크인한 경우를 의미하므로, 라운드 시작 시간 + allowedMinutes 이후인지 확인해야 합니다.
예: 시작 시간 18:30, 출석 인정 30분이면 18:30~19:00 사이는 정상 출석, 19:00 이후가 지각입니다.
단, 제안된 수정에서 사용된 getAvailableMinutes() 메서드는 존재하지 않습니다. AttendanceRound 엔티티의 실제 메서드명은 getAllowedMinutes() 입니다.
public boolean isLate() {
if (checkedAt == null || attendanceRound == null) {
return false;
}
- // 라운드의 시작 시간(LocalTime)과 체크인 시간(LocalDateTime)을 비교
LocalDateTime roundStartDateTime = attendanceRound.getRoundDate()
.atTime(attendanceRound.getStartTime());
- return checkedAt.isAfter(roundStartDateTime);
+ // 출석 인정 시간 이후에 체크인했으면 지각
+ LocalDateTime lateThreshold = roundStartDateTime
+ .plusMinutes(attendanceRound.getAllowedMinutes());
+ return checkedAt.isAfter(lateThreshold);
}Committable suggestion skipped: line range outside the PR's diff.
Summary by CodeRabbit
릴리스 노트
문서 개선
개선사항
✏️ Tip: You can customize this high-level summary in your review settings.