Skip to content

[BE] SISC1-229 [FIX] 라운드 출석상태 수정#157

Merged
Kosw6 merged 14 commits intomainfrom
SISC1-229-BE-라운드-출석상태-수정
Dec 1, 2025

Hidden character warning

The head ref may contain hidden characters: "SISC1-229-BE-\ub77c\uc6b4\ub4dc-\ucd9c\uc11d\uc0c1\ud0dc-\uc218\uc815"
Merged

[BE] SISC1-229 [FIX] 라운드 출석상태 수정#157
Kosw6 merged 14 commits intomainfrom
SISC1-229-BE-라운드-출석상태-수정

Conversation

@ochanhyeok
Copy link
Contributor

@ochanhyeok ochanhyeok commented Dec 1, 2025

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능

    • 출석 상태에 "미정" 상태 추가
    • 새 출석 라운드 생성 시 자동으로 모든 참여자의 출석 기록을 "미정" 상태로 생성
    • 세션에 사용자 추가 시 향후 라운드의 출석 기록을 "미정" 상태로 자동 생성
  • 개선 사항

    • 출석 체크인 시 중복 체크 로직 개선
    • 사용자 정보 처리 시 안정성 강화

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

@coderabbitai
Copy link

coderabbitai bot commented Dec 1, 2025

Walkthrough

출석 도메인에 PENDING 상태를 추가하고, 라운드 생성 시 또는 세션에 사용자 추가 시 모든 세션 사용자에 대해 PENDING 상태의 Attendance 레코드를 자동으로 미리 생성하는 기능을 구현합니다.

Changes

Cohort / File(s) Change Summary
출석 상태 및 엔티티
backend/src/main/java/org/sejongisc/backend/attendance/entity/AttendanceStatus.java, backend/src/main/java/org/sejongisc/backend/attendance/entity/Attendance.java
PENDING("미정") 열거형 상수 추가 및 Attendance 엔티티에 @Setter 애노테이션 추가
저장소 계층
backend/src/main/java/org/sejongisc/backend/attendance/repository/AttendanceRoundRepository.java
지정된 날짜 이후의 모든 출석 라운드를 조회하는 findBySession_SessionIdAndRoundDateAfterOrEqual() 쿼리 메서드 추가
서비스 계층 - 라운드 관리
backend/src/main/java/org/sejongisc/backend/attendance/service/AttendanceRoundService.java
SessionUserRepository, AttendanceRepository 의존성 추가 및 라운드 생성 시 세션의 모든 사용자에 대해 PENDING 상태의 Attendance 레코드 자동 생성
서비스 계층 - 세션 사용자 관리
backend/src/main/java/org/sejongisc/backend/attendance/service/SessionUserService.java
사용자 추가 시 향후 라운드에 대해 PENDING 상태의 Attendance 레코드 자동 생성
서비스 계층 - 출석 관리
backend/src/main/java/org/sejongisc/backend/attendance/service/AttendanceService.java
checkInByRound에서 PENDING 상태 처리 개선 및 convertToResponse에서 사용자 필드 null-safe 처리 추가

Sequence Diagram(s)

sequenceDiagram
    actor Admin as 관리자
    participant ARS as AttendanceRoundService
    participant SUR as SessionUserRepository
    participant AR as AttendanceRepository
    participant DB as Database
    
    Admin->>ARS: 출석 라운드 생성 요청
    activate ARS
    ARS->>DB: 라운드 저장
    ARS->>SUR: 세션의 모든 사용자 조회
    activate SUR
    SUR->>DB: SessionUser 쿼리
    SUR-->>ARS: 사용자 목록 반환
    deactivate SUR
    
    loop 각 세션 사용자별
        ARS->>AR: PENDING 상태의 Attendance 레코드 생성
        activate AR
        AR->>DB: 레코드 저장
        AR-->>ARS: 저장 완료
        deactivate AR
        ARS->>ARS: per-user 생성 로그
    end
    
    ARS->>ARS: 최종 요약 로그 (생성 개수)
    ARS-->>Admin: 라운드 및 PENDING 레코드 생성 완료
    deactivate ARS
Loading
sequenceDiagram
    actor Admin as 관리자
    participant SUS as SessionUserService
    participant ARR as AttendanceRoundRepository
    participant AR as AttendanceRepository
    participant DB as Database
    
    Admin->>SUS: 세션에 사용자 추가 요청
    activate SUS
    SUS->>DB: 과거 라운드에 대해 ABSENT 상태로 처리
    
    SUS->>ARR: 오늘 이후의 미래 라운드 조회
    activate ARR
    ARR->>DB: roundDate >= today 쿼리
    ARR-->>SUS: 미래 라운드 목록 반환
    deactivate ARR
    
    loop 각 미래 라운드별
        SUS->>DB: 기존 Attendance 확인
        alt 기존 레코드 없음
            SUS->>AR: PENDING 상태의 Attendance 생성
            activate AR
            AR->>DB: 레코드 저장
            AR-->>SUS: 저장 완료
            deactivate AR
            SUS->>SUS: per-record 생성 로그
        end
    end
    
    SUS->>DB: 세션-사용자 추가
    SUS-->>Admin: 사용자 추가 및 PENDING 레코드 생성 완료
    deactivate SUS
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

추가 검토 필요 영역:

  • AttendanceRoundService.java - 라운드 생성 시 모든 세션 사용자에 대한 PENDING 레코드 자동 생성 로직이 올바르게 구현되었는지, 대량 생성 시 성능 영향이 없는지 확인 필요
  • SessionUserService.java - 사용자 추가 시 미래 라운드에 대한 PENDING 레코드 생성 시 기존 레코드 중복 확인 로직의 정확성
  • AttendanceService.java - PENDING 상태 처리 변경이 기존 체크인 로직과의 상호작용에 미치는 영향 확인
  • 트랜잭션 일관성 - 여러 저장소에 걸친 레코드 생성 시 트랜잭션 경계와 롤백 처리 확인

Possibly related PRs

Suggested reviewers

  • discipline24

🐰 조용한 밤, 출석 라운드 시작되면
PENDING 상태로 모두 준비해두고
사용자들이 도착할 때까지 살짝 기다려,
미리 마련된 기록에 체크인만 하면 돼
덕분에 번거로움 한 뿐 줄었다네! ✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목은 라운드 출석상태 수정이라는 주요 변경사항을 명확하게 설명하고 있으며, 실제 코드 변경사항(PENDING 상태 추가, 출석 기록 자동 생성 등)과 일치합니다.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ 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-229-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: 1

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

106-136: 성능 최적화 권장: 배치 저장 및 N+1 쿼리 개선

현재 구현은 각 라운드마다 개별적으로 findByAttendanceRound_RoundIdAndUser를 호출하고 save를 수행합니다. 라운드 수가 많아지면 성능 저하가 발생할 수 있습니다.

-        if (!futureRounds.isEmpty()) {
-            log.info("📅 미래 라운드 PENDING 처리: 미래 라운드 수={}", futureRounds.size());
-
-            for (AttendanceRound round : futureRounds) {
-                // 이미 해당 라운드에 출석 기록이 있는지 확인
-                boolean alreadyExists = attendanceRepository.findByAttendanceRound_RoundIdAndUser(round.getRoundId(), user)
-                        .isPresent();
-
-                if (!alreadyExists) {
-                    // 새로운 Attendance 레코드 생성 (PENDING 상태)
-                    Attendance pendingRecord = Attendance.builder()
-                            .user(user)
-                            .attendanceSession(session)
-                            .attendanceRound(round)
-                            .attendanceStatus(AttendanceStatus.PENDING)
-                            .build();
-
-                    attendanceRepository.save(pendingRecord);
-                    log.info("  - PENDING 기록 생성: roundId={}, date={}, userName={}",
-                            round.getRoundId(), round.getRoundDate(), user.getName());
-                }
-            }
-
-            log.info("✅ 미래 라운드 PENDING 처리 완료: 처리된 라운드 수={}", futureRounds.size());
-        }
+        if (!futureRounds.isEmpty()) {
+            log.info("📅 미래 라운드 PENDING 처리: 미래 라운드 수={}", futureRounds.size());
+
+            List<UUID> futureRoundIds = futureRounds.stream()
+                    .map(AttendanceRound::getRoundId)
+                    .collect(Collectors.toList());
+            
+            // 기존 출석 기록이 있는 라운드 ID 조회 (한 번의 쿼리로 처리)
+            Set<UUID> existingRoundIds = attendanceRepository
+                    .findExistingRoundIdsByUserAndRoundIds(user.getUserId(), futureRoundIds);
+
+            List<Attendance> pendingRecords = futureRounds.stream()
+                    .filter(round -> !existingRoundIds.contains(round.getRoundId()))
+                    .map(round -> Attendance.builder()
+                            .user(user)
+                            .attendanceSession(session)
+                            .attendanceRound(round)
+                            .attendanceStatus(AttendanceStatus.PENDING)
+                            .build())
+                    .collect(Collectors.toList());
+
+            attendanceRepository.saveAll(pendingRecords);
+            log.info("✅ 미래 라운드 PENDING 처리 완료: 생성된 레코드 수={}", pendingRecords.size());
+        }

AttendanceRepository에 다음 메서드 추가가 필요합니다:

@Query("SELECT a.attendanceRound.roundId FROM Attendance a WHERE a.user.userId = :userId AND a.attendanceRound.roundId IN :roundIds")
Set<UUID> findExistingRoundIdsByUserAndRoundIds(@Param("userId") UUID userId, @Param("roundIds") List<UUID> roundIds);

72-104: 동일한 성능 최적화 패턴을 과거 라운드 처리에도 적용 권장

미래 라운드 처리(lines 106-136)와 동일한 N+1 쿼리 패턴이 존재합니다. 배치 조회/저장 패턴을 적용하면 두 로직 모두 성능이 개선됩니다.

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

80-98: 배치 저장으로 최적화 권장

라운드 생성 시 각 사용자에 대해 개별 save 호출이 발생합니다. saveAll을 사용하면 더 효율적입니다.

-            List<SessionUser> sessionUsers = sessionUserRepository.findBySessionId(sessionId);
-            for (SessionUser sessionUser : sessionUsers) {
-                Attendance pendingAttendance = Attendance.builder()
-                        .user(sessionUser.getUser())
-                        .attendanceSession(session)
-                        .attendanceRound(saved)
-                        .attendanceStatus(AttendanceStatus.PENDING)
-                        .build();
-                attendanceRepository.save(pendingAttendance);
-                log.info("  ✓ PENDING 출석 기록 생성: userId={}, userName={}, roundId={}",
-                        sessionUser.getUser().getUserId(), sessionUser.getUser().getName(), saved.getRoundId());
-            }
+            List<SessionUser> sessionUsers = sessionUserRepository.findBySessionId(sessionId);
+            List<Attendance> pendingAttendances = sessionUsers.stream()
+                    .map(sessionUser -> Attendance.builder()
+                            .user(sessionUser.getUser())
+                            .attendanceSession(session)
+                            .attendanceRound(saved)
+                            .attendanceStatus(AttendanceStatus.PENDING)
+                            .build())
+                    .collect(Collectors.toList());
+            
+            attendanceRepository.saveAll(pendingAttendances);
+            log.debug("PENDING 출석 기록 생성 완료: 사용자 수={}", pendingAttendances.size());

추가로, 개별 레코드 생성 로그는 log.debug로 변경하거나 제거하는 것을 권장합니다. 프로덕션에서 사용자가 많을 경우 로그가 과도하게 생성될 수 있습니다.


7-8: 와일드카드 import 사용 지양 권장

org.sejongisc.backend.attendance.entity.* 대신 명시적 import를 사용하면 코드 가독성과 유지보수성이 향상됩니다.

-import org.sejongisc.backend.attendance.entity.*;
+import org.sejongisc.backend.attendance.entity.Attendance;
+import org.sejongisc.backend.attendance.entity.AttendanceRound;
+import org.sejongisc.backend.attendance.entity.AttendanceSession;
+import org.sejongisc.backend.attendance.entity.AttendanceStatus;
+import org.sejongisc.backend.attendance.entity.RoundStatus;
+import org.sejongisc.backend.attendance.entity.SessionUser;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 059902b and 075acb9.

📒 Files selected for processing (9)
  • backend/src/main/java/org/sejongisc/backend/attendance/controller/AttendanceController.java (2 hunks)
  • backend/src/main/java/org/sejongisc/backend/attendance/dto/AttendanceCheckInRequest.java (2 hunks)
  • backend/src/main/java/org/sejongisc/backend/attendance/entity/AttendanceStatus.java (1 hunks)
  • backend/src/main/java/org/sejongisc/backend/attendance/entity/SessionUser.java (1 hunks)
  • backend/src/main/java/org/sejongisc/backend/attendance/repository/AttendanceRoundRepository.java (1 hunks)
  • backend/src/main/java/org/sejongisc/backend/attendance/repository/SessionUserRepository.java (1 hunks)
  • backend/src/main/java/org/sejongisc/backend/attendance/service/AttendanceRoundService.java (3 hunks)
  • backend/src/main/java/org/sejongisc/backend/attendance/service/AttendanceService.java (5 hunks)
  • backend/src/main/java/org/sejongisc/backend/attendance/service/SessionUserService.java (1 hunks)
🔇 Additional comments (9)
backend/src/main/java/org/sejongisc/backend/attendance/repository/SessionUserRepository.java (1)

39-40: LGTM!

쿼리 단순화가 적절합니다. COUNT(su) > 0은 JPQL에서 유효한 boolean 표현식이며, 이전의 CASE WHEN 구문보다 간결합니다.

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

35-53: LGTM!

위도/경도 필드에서 @NotNull 제거 후에도 범위 검증(@DecimalMin, @DecimalMax)이 유지되어 값이 제공될 경우 유효성을 보장합니다. 조건부 필수 필드로의 변경이 적절합니다.

backend/src/main/java/org/sejongisc/backend/attendance/repository/AttendanceRoundRepository.java (1)

60-71: LGTM!

미래 라운드 조회를 위한 새 쿼리 메서드가 적절하게 추가되었습니다. >= 연산자로 오늘 날짜를 포함하는 것이 PENDING 처리 로직과 일치합니다.

backend/src/main/java/org/sejongisc/backend/attendance/entity/AttendanceStatus.java (1)

3-8: PENDING 상태 추가는 안전합니다

AttendanceStatus 필드는 @Enumerated(EnumType.STRING) (Attendance.java 라인 42)로 설정되어 있어, enum 값이 이름으로 저장됩니다. 따라서 enum 순서 변경이 기존 데이터에 영향을 주지 않습니다. PENDING 상태 추가는 데이터 호환성 문제가 없습니다.

Likely an incorrect or invalid review comment.

backend/src/main/java/org/sejongisc/backend/attendance/entity/SessionUser.java (1)

22-23: No action needed. The application uses Spring Data JPA with generate-ddl: true in application.yml, which automatically manages schema creation from entity definitions. Hibernate will create the attendance_session_user table when the application starts without requiring explicit SQL migration scripts. Additionally, no references to the old table name session_user exist in the codebase, indicating this is likely a new entity or the table naming follows the current standard.

Likely an incorrect or invalid review comment.

backend/src/main/java/org/sejongisc/backend/attendance/service/AttendanceService.java (2)

52-57: 날짜/시간 검증 및 remainingSeconds 계산은 일관성 있게 보입니다

라운드 날짜 동일성 체크 후 startTime <= now < endTime 윈도우 검증, 그리고 Duration.between(checkTime, endTime) 기반 남은 시간 계산 흐름이 서로 잘 맞습니다. 다만 LocalDate.now()/LocalTime.now() 가 서버 기본 타임존에 의존하므로, 운영 환경 타임존(KST 등)과 라운드 날짜/시간이 정의된 타임존이 항상 일치하는지 한 번만 점검해두면 좋겠습니다.

Also applies to: 60-68, 70-72, 82-83, 156-159


85-95: Verify the PENDING status pre-creation flow and reconcile with duplicate attendance logic

The review raises a critical architectural concern: if AttendanceRoundService and SessionUserService pre-create attendance records in PENDING status (as mentioned in the PR description), the current duplicate check at lines 85-95 will reject all subsequent check-in attempts, making PENDING→PRESENT/LATE transitions impossible.

The core issue is that findByAttendanceRound_RoundIdAndUser() returns any existing record regardless of status, treating PENDING records identically to completed check-ins. To align with the pre-creation strategy, the logic should:

  1. Allow PENDING records to be updated: Check existing.getAttendanceStatus() != AttendanceStatus.PENDING before blocking
  2. Update existing PENDING records instead of creating duplicates: Reuse the found record and update its status, timestamp, location, and points
  3. Only create new Attendance when no pre-existing record exists

This pattern should also apply to lines 134-142 and 152-155 for consistency. Before proceeding, confirm:

  • Do AttendanceRoundService and SessionUserService actually pre-create PENDING records?
  • Does AttendanceStatus enum include PENDING status?
  • Are similar duplicate checks present at the other mentioned lines?
backend/src/main/java/org/sejongisc/backend/attendance/controller/AttendanceController.java (2)

32-34: 세션별 조회 엔드포인트 deprecate 처리 적절함

Javadoc, Swagger @Operation description, 자바 @Deprecated(since = "2.0", forRemoval = true) 까지 일관되게 표시되어 있어, 클라이언트/문서 양쪽 모두에 라운드 기반 조회로의 마이그레이션 경로가 잘 안내됩니다. 시그니처나 보안 설정은 그대로 유지되어 있어 기존 소비자에게도 안전해 보입니다.

Also applies to: 39-44, 47-48


79-80: 세션 기반 출석 상태 수정 엔드포인트 deprecate 처리도 괜찮습니다

상단 주석과 Swagger description, @Deprecated 설정 모두 라운드 기반 수정 엔드포인트(PUT /api/attendance/rounds/{roundId}/attendances/{userId})로의 전환을 명확히 안내하고 있습니다. 런타임 로직 변경 없이 문서 수준에서만 경고를 주고 있어, 단계적 제거 전략으로 적절해 보입니다.

Also applies to: 85-91, 94-95

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: 1

♻️ Duplicate comments (1)
backend/src/main/java/org/sejongisc/backend/attendance/service/AttendanceService.java (1)

332-353: NPE 방지를 위한 null-safe 처리 완료

이전 리뷰에서 지적된 attendance.getUser() null 가능성에 대한 처리가 완료되었습니다. userIduserName 모두 null-safe하게 처리되고 있습니다.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 075acb9 and 737247b.

📒 Files selected for processing (1)
  • backend/src/main/java/org/sejongisc/backend/attendance/service/AttendanceService.java (5 hunks)
🔇 Additional comments (4)
backend/src/main/java/org/sejongisc/backend/attendance/service/AttendanceService.java (4)

17-18: 시간 검증 로직 개선 확인

LocalDate, LocalTime을 사용한 중앙집중식 시간 검증 로직이 명확하게 구현되었습니다. lateThresholdstartTime.plusMinutes(5)로 설정하여 지각 기준이 일관되게 적용됩니다.

Also applies to: 52-57


60-68: 날짜 검증 로직 추가 확인

라운드 날짜와 현재 날짜가 일치하는지 검증하는 로직이 추가되었습니다. 날짜 불일치 시 명확한 실패 사유와 함께 로그를 남기고 있어 디버깅에 유용합니다.


70-83: 시간 범위 검증: 자정 경계 케이스 고려

startTime <= now < endTime 검증은 일반적인 경우에 올바르게 동작합니다. 다만, 자정을 넘는 라운드(예: 23:00~01:00)가 있다면 현재 로직으로는 처리되지 않습니다.

비즈니스 요구사항에 자정을 넘는 라운드가 없다면 무시해도 됩니다. 만약 있다면 별도 처리가 필요합니다.


126-131: 지각 판별 로직 명확화

lateThreshold 변수를 사용하여 지각 기준이 명확해졌습니다. 로그에도 지각 기준 시간이 포함되어 디버깅이 용이합니다.

@ochanhyeok ochanhyeok force-pushed the SISC1-229-BE-라운드-출석상태-수정 branch from 737247b to 83dc1f8 Compare December 1, 2025 05:11
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: 0

♻️ Duplicate comments (1)
backend/src/main/java/org/sejongisc/backend/attendance/service/AttendanceService.java (1)

85-96: PENDING 레코드를 재사용하지 않고 새 Attendance를 생성해서 중복/예외가 발생할 수 있습니다

existingAttendancePENDING인 경우에도 134~143라인에서 항상 새 Attendance를 빌더로 생성하고 있습니다. 라운드/유저별로 PENDING 레코드를 미리 만들어두는 구조라면, 체크인 시에는 이 PENDING 레코드를 PRESENT/LATE로 업데이트하는 것이 의도에 더 가깝고, (roundId, user) 쌍당 하나의 Attendance만 유지할 수 있습니다.

현재 구현처럼 새 레코드를 추가로 만들면 같은 라운드/유저에 대해 PENDING + PRESENT/LATE 두 건이 생길 수 있고, 이 경우

  • DB에 (round_id, user_id) 유니크 제약이 있다면 제약 위반,
  • findByAttendanceRound_RoundIdAndUser처럼 단일 결과를 기대하는 조회에서 여러 건이 조회되어 예외가 날 위험
    이 있어 보입니다. 이전 리뷰 코멘트에서 제안되었던 “기존 PENDING 레코드 업데이트” 부분이 아직 반영되지 않은 상태로 보입니다.

기존 PENDING이 있을 때는 그 엔티티를 재사용해서 필드만 갱신하도록 아래와 같이 바꾸는 쪽을 권장합니다.

-        // 5. 출석 기록 저장
-        Attendance attendance = Attendance.builder()
-                .user(user)
-                .attendanceSession(session)
-                .attendanceRound(round)
-                .attendanceStatus(status)
-                .checkedAt(java.time.LocalDateTime.now())
-                .awardedPoints(session.getRewardPoints())
-                .checkInLocation(userLocation)
-                .build();
+        // 5. 출석 기록 저장 (기존 PENDING 레코드가 있으면 해당 엔티티를 업데이트)
+        Attendance attendance = existingAttendance != null
+                ? existingAttendance
+                : Attendance.builder()
+                        .user(user)
+                        .attendanceSession(session)
+                        .attendanceRound(round)
+                        .build();
+
+        attendance.setAttendanceStatus(status);
+        attendance.setCheckedAt(java.time.LocalDateTime.now());
+        attendance.setAwardedPoints(session.getRewardPoints());
+        attendance.setCheckInLocation(userLocation);

이렇게 하면 PENDING 선생성 전략과도 자연스럽게 맞고, 이후 관리자용 API(updateAttendanceStatusByRound)에서 단일 Attendance를 전제로 조회하는 흐름과도 일관성이 맞습니다.

Also applies to: 134-149

🧹 Nitpick comments (3)
backend/src/main/java/org/sejongisc/backend/attendance/service/AttendanceService.java (1)

333-353: convertToResponse의 user null-safe 처리 적절합니다 (추가 아이디어 한 가지)

attendance.getUser()가 null인 경우를 삼항 연산자로 처리해서 기존에 지적되었던 NPE 가능성이 사라진 점 좋습니다. 현재는 user가 null이면 무조건 "익명"으로 내려가는데, 도메인에 따라 Attendance.anonymousUserName 필드를 우선 사용하고, 없을 때만 "익명"으로 fallback 하는 형태도 고려해 볼 수 있습니다. 그런 요구가 없다면 지금 구현만으로도 충분해 보입니다.

backend/src/main/java/org/sejongisc/backend/attendance/entity/Attendance.java (1)

5-19: Attendance 엔티티 전체에 @Setter 부여 시 변경 범위 한 번 점검해 보시는 것을 권장합니다

클래스 레벨에 @Setter가 추가되면서 attendanceId, 연관 엔티티, 타임스탬프까지 모든 필드에 세터가 열려 있습니다. PENDING 선생성 후 상태/시간을 갱신하는 용도에는 편리하지만, 외부 코드에서 ID나 연관관계, 시간 정보 등을 임의로 변경하기 쉬워져 도메인 제약이 느슨해질 수 있습니다.

실제로 세터가 필요한 필드만 선택적으로 @Setter를 붙이거나, ID/연관관계/생성일·업데이트일 같은 필드는 @Setter(AccessLevel.NONE) 등으로 막아 두는 방향도 한 번 고려해 보시면 좋겠습니다.

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

7-9: 라운드 생성 시 PENDING 출석 미리 생성 로직은 의도와 잘 맞습니다 (성능/중복 관점에서 확인 포인트)

createRound에서 세션의 모든 SessionUser를 조회해 PENDING Attendance를 선생성하는 흐름은 이후 라운드 기반 체크인 및 관리자 상태 변경 플로우와 잘 맞아 보입니다. 로그도 단계별로 잘 남겨서 운영 시 추적성이 좋을 것 같습니다.

다만 세션 인원이 많은 경우 개별 attendanceRepository.save(...) 호출이 상당히 늘어날 수 있으니, 필요 시 saveAll(...) 같은 배치 저장이나 별도 헬퍼 메서드로 분리하는 것도 고려해 볼 수 있겠습니다. 또, 세션에 유저를 추가할 때 미래 라운드에 대해 PENDING를 생성하는 로직(SessionUserService)과 조합될 때 동일 (round, user) 쌍에 대해 중복 Attendance가 생기지 않는지(특히 동시성 상황에서) 한 번만 확인해 두시면 좋겠습니다.

Also applies to: 11-11, 33-35, 80-98

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 737247b and 83dc1f8.

📒 Files selected for processing (6)
  • backend/src/main/java/org/sejongisc/backend/attendance/entity/Attendance.java (2 hunks)
  • backend/src/main/java/org/sejongisc/backend/attendance/entity/AttendanceStatus.java (1 hunks)
  • backend/src/main/java/org/sejongisc/backend/attendance/repository/AttendanceRoundRepository.java (1 hunks)
  • backend/src/main/java/org/sejongisc/backend/attendance/service/AttendanceRoundService.java (3 hunks)
  • backend/src/main/java/org/sejongisc/backend/attendance/service/AttendanceService.java (2 hunks)
  • backend/src/main/java/org/sejongisc/backend/attendance/service/SessionUserService.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • backend/src/main/java/org/sejongisc/backend/attendance/entity/AttendanceStatus.java
  • backend/src/main/java/org/sejongisc/backend/attendance/service/SessionUserService.java
🔇 Additional comments (1)
backend/src/main/java/org/sejongisc/backend/attendance/repository/AttendanceRoundRepository.java (1)

61-71: 미래 라운드 조회용 쿼리 조건과 메소드명이 의도와 잘 맞습니다

JPQL이 roundDate >= :date 조건을 사용하고 메소드명이 findBySession_SessionIdAndRoundDateAfterOrEqual이라서, “기준 날짜(예: 오늘) 포함 이후 라운드”를 조회하려는 용도와 일관성이 있습니다. 세션에 유저 추가 시 미래 라운드에 PENDING 출석을 미리 생성하는 시나리오에도 적절해 보입니다.

@Kosw6 Kosw6 merged commit 5bbdced into main Dec 1, 2025
1 check passed
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.

2 participants