Skip to content

[BE] SISC1-223 [FIX] 출석 세션 시간 변수 수정#142

Closed
ochanhyeok wants to merge 5 commits intomainfrom
SISC1-223-BE-출석-세션-시간-변수-수정

Hidden character warning

The head ref may contain hidden characters: "SISC1-223-BE-\ucd9c\uc11d-\uc138\uc158-\uc2dc\uac04-\ubcc0\uc218-\uc218\uc815"
Closed

[BE] SISC1-223 [FIX] 출석 세션 시간 변수 수정#142
ochanhyeok wants to merge 5 commits intomainfrom
SISC1-223-BE-출석-세션-시간-변수-수정

Conversation

@ochanhyeok
Copy link
Contributor

@ochanhyeok ochanhyeok commented Nov 25, 2025

Summary by CodeRabbit

릴리스 노트

  • 문서 개선

    • 세션 및 라운드 관리에 대한 API 문서 설명을 보다 명확하게 개선했습니다.
  • 개선사항

    • 출석 시스템의 시간 관리 로직을 세션 기준에서 라운드 기준으로 개선했습니다.
    • API 필드명을 더 직관적으로 정리하여 시스템 이해도를 높였습니다.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Nov 25, 2025

개요

해당 변경사항은 출석 관리 시스템의 DTO 필드명 재정의(roundDate → date, allowedMinutes → availableMinutes)와 시간 처리 방식 전환(LocalDateTime → LocalTime)을 수행합니다. AttendanceSession의 시간 계산 메서드 제거, 저장소 쿼리 메서드 제거, 출석 검증 로직의 라운드 기반 전환 등이 포함됩니다.

변경사항

응집군 / 파일 변경 요약
DTO 필드 재정의
backend/src/main/java/org/sejongisc/backend/attendance/dto/AttendanceRoundRequest.java, backend/src/main/java/org/sejongisc/backend/attendance/dto/AttendanceRoundResponse.java
roundDatedate 필드명 변경, allowedMinutesavailableMinutes 필드명 변경으로 공개 API 업데이트
시간 타입 전환
backend/src/main/java/org/sejongisc/backend/attendance/entity/AttendanceSession.java, backend/src/main/java/org/sejongisc/backend/attendance/dto/AttendanceSessionRequest.java
startsAt(LocalDateTime) → defaultStartTime(LocalTime), windowSecondsdefaultAvailableMinutes 필드 전환, JsonFormat 어노테이션 추가
시간 계산 로직 제거
backend/src/main/java/org/sejongisc/backend/attendance/entity/AttendanceSession.java
calculateCurrentStatus(), isCheckInAvailable(), getEndsAt(), getRemainingSeconds() 메서드 제거, getStatus() 메서드로 단순화
저장소 메서드 제거
backend/src/main/java/org/sejongisc/backend/attendance/repository/AttendanceSessionRepository.java
findByCode(), findByStatus(), findAllByOrderByStartsAtDesc() 메서드 제거, existsByCode() 메서드만 유지
서비스 로직 업데이트
backend/src/main/java/org/sejongisc/backend/attendance/service/AttendanceRoundService.java, backend/src/main/java/org/sejongisc/backend/attendance/service/AttendanceSessionService.java, backend/src/main/java/org/sejongisc/backend/attendance/service/AttendanceService.java
필드명 변경에 따른 빌더/게터 업데이트, 라운드 기반 시간 검증 적용, 세션 상태 필터링 로직 변경
컨트롤러 로깅 업데이트
backend/src/main/java/org/sejongisc/backend/attendance/controller/AttendanceRoundController.java, backend/src/main/java/org/sejongisc/backend/attendance/controller/AttendanceSessionController.java
로깅 메시지의 필드명 업데이트(roundDate → date, allowedMinutes → availableMinutes), API 문서 설명 갱신
출석 엔티티 로직 변경
backend/src/main/java/org/sejongisc/backend/attendance/entity/Attendance.java
isLate() 메서드가 attendanceSession 대신 attendanceRound를 사용하도록 변경
테스트 파일 업데이트
backend/src/test/java/org/sejongisc/backend/attendance/controller/AttendanceRoundControllerTest.java, backend/src/test/java/org/sejongisc/backend/attendance/service/AttendanceRoundServiceTest.java, backend/src/test/java/org/sejongisc/backend/attendance/service/AttendanceServiceTest.java, backend/src/test/java/org/sejongisc/backend/attendance/service/SessionLocationUpdateTest.java
빌더 메서드 호출명 변경, 예상 응답 필드명 업데이트, 시간 설정 제거

시퀀스 다이어그램

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
Loading
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
Loading

코드 리뷰 난이도 추정

🎯 3 (중간) | ⏱️ ~25분

추가 검토 필요 영역:

  • AttendanceSession.java: 4개의 공개 메서드 제거(calculateCurrentStatus(), isCheckInAvailable(), getEndsAt(), getRemainingSeconds()) — 의존성 및 호출 지점 확인 필수
  • AttendanceService.checkIn(): 라운드 기반 시간 검증 로직으로의 전환 — 체크인 가능 여부 판정 로직이 정확한지 검증 필요
  • AttendanceSessionRepository: 3개 메서드 제거(findByCode(), findByStatus(), findAllByOrderByStartsAtDesc()) — 다른 모듈에서의 사용 여부 확인
  • 테스트 파일 수정: 시간 설정 제거 시 기본값 동작 확인 필요

관련 가능 PR

제안 검토자

  • discipline24

🐰 라운드 기반의 새로운 시간 계산,
필드명도 깔끔하게 정리하여,
세션의 복잡한 계산 로직은 빠져나가고,
활성 라운드로 출석을 확인하네!
타입도 LocalTime으로 변신 완료! ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목은 출석 세션 시간 변수 수정이라는 주요 변경 사항을 명확하게 요약하고 있으며, 변경 집합의 핵심 목표와 일치합니다.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch SISC1-223-BE-출석-세션-시간-변수-수정

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (10)
backend/src/main/java/org/sejongisc/backend/attendance/service/AttendanceSessionService.java (2)

84-91: getAllSessionsgetPublicSessions의 구현이 동일합니다.

두 메서드 모두 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(...)AttendanceRoundResponsegetDate(), 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에서 활성 라운드 탐색 로직 및 비즈니스 룰 확인 제안

현재 시간 nowroundStart ≤ 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

📥 Commits

Reviewing files that changed from the base of the PR and between 8e861a1 and a05230d.

📒 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: 세션 생성 로직이 새 필드명으로 올바르게 업데이트되었습니다.

defaultStartTimedefaultAvailableMinutes 필드가 request에서 entity로 적절히 매핑되고 있습니다.

Also applies to: 49-57


146-152: 세션 수정 시 새 필드가 올바르게 반영됩니다.

defaultStartTimedefaultAvailableMinutes가 업데이트 로직에 정확히 적용되었습니다.


287-295: DTO 변환 로직이 새 필드명으로 올바르게 업데이트되었습니다.

convertToResponse 메서드가 defaultStartTimedefaultAvailableMinutes를 정확히 매핑합니다. 프론트엔드의 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: 로깅 문이 새 필드명으로 올바르게 업데이트되었습니다.

roundDatedate, allowedMinutesavailableMinutes로 변경되어 DTO의 필드명 변경과 일관성을 유지합니다. 디버깅에 유용한 상세 정보(타입, 시간 상세)도 잘 유지되어 있습니다.

backend/src/main/java/org/sejongisc/backend/attendance/dto/AttendanceRoundResponse.java (1)

38-38: DTO 날짜 필드명 변경 및 매핑이 잘 정리되어 있습니다

엔티티의 roundDate를 DTO의 date로 매핑하는 부분이 일관되고, @JsonFormat 및 Swagger 스키마 설명과도 잘 맞습니다. availableMinutesallowedMinutes를 노출하는 것도 도메인 보존 + API 명확성 측면에서 자연스러운 구성으로 보입니다.

Also applies to: 72-75

backend/src/main/java/org/sejongisc/backend/attendance/service/AttendanceRoundService.java (1)

114-118: updateRound에서 date/availableMinutes 사용으로 변경된 부분 일관성 확인

updateRound에서 updateRoundInforequest.getDate()request.getAvailableMinutes()를 넘기도록 수정한 부분이 DTO/엔티티 매핑 및 테스트(AttendanceRoundServiceTest)와 일관되게 잘 맞습니다. 이 변경 자체로는 특별한 리스크는 없어 보입니다.

backend/src/main/java/org/sejongisc/backend/attendance/dto/AttendanceRoundRequest.java (1)

42-42: 요청 DTO 필드명(date, availableMinutes) 변경이 응답/서비스와 일관적입니다

요청 DTO에서 roundDatedate, allowedMinutesavailableMinutes로 이름을 정리한 부분이 AttendanceRoundResponseAttendanceRoundService에서 사용하는 필드들과 잘 맞고, 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)나 분석에 도움이 될 것 같습니다.

Comment on lines 21 to +39
@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;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

@Future 검증 제거 및 날짜 정보 손실 확인 필요

LocalDateTime startsAt에서 LocalTime defaultStartTime으로 변경하면서 다음 사항들을 확인해주세요:

  1. 검증 로직 손실: 기존 @Future 어노테이션이 제거되어 과거 시간에 대한 검증이 불가능합니다. 날짜 정보 없이 시간만으로는 미래 여부를 판단할 수 없으므로, 서비스 레이어에서 날짜와 함께 검증하는 로직이 추가되었는지 확인이 필요합니다.

  2. 의미론적 변경: 절대 날짜시간에서 시간대(time-of-day)로의 변경은 세션이 "언제" 시작하는지가 아닌 "몇 시에" 시작하는지만 나타냅니다. 이것이 의도된 설계 변경인지 확인해주세요.

  3. 데이터베이스 마이그레이션: 기존 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/attendance

Length 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 -80

Length of output: 7586


제거된 @Future 검증 대체 로직 미발견

검증 결과 다음 사항이 확인되었습니다:

  1. 검증 손실 확인: AttendanceSession 엔티티(line 32)와 AttendanceSessionRequest DTO(line 39) 모두에서 @Future 검증이 제거되었으며, 서비스 레이어에서도 대체 검증 로직이 없습니다.

  2. 시간만 저장: LocalTime 사용으로 인해 시간대(time-of-day)만 저장되고 날짜 정보가 제거되었습니다. 엔티티 주석도 "시간만"이라고 명시합니다.

  3. 검증 공백: 현재는 @NotNull 만 있어서 null 값만 방지하고, 과거 시간 설정을 막을 방법이 없습니다. 다른 레이어에서도 defaultStartTime에 대한 시간 유효성 검증이 발견되지 않습니다.

  4. 데이터베이스: 마이그레이션 파일이 없어서 기존 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.

Comment on lines 41 to +50
@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;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 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=java

Length 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 f

Length of output: 990


Comment on lines +67 to 79
/**
* 지각 여부 판단
* - 라운드의 시작 시간 이후에 체크인했으면 지각
*/
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);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 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 java

Length 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 -100

Length 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.java

Length 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.

@ochanhyeok ochanhyeok closed this Nov 25, 2025
@ochanhyeok ochanhyeok deleted the SISC1-223-BE-출석-세션-시간-변수-수정 branch November 25, 2025 10:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant