Skip to content

[FE] [FEAT] : 출석하기(담당자) 페이지 디자인 수정 및 기능 api 연결#165

Merged
Kosw6 merged 4 commits intomainfrom
attendance-manage-api
Dec 23, 2025
Merged

[FE] [FEAT] : 출석하기(담당자) 페이지 디자인 수정 및 기능 api 연결#165
Kosw6 merged 4 commits intomainfrom
attendance-manage-api

Conversation

@GamjaIsMine02
Copy link
Contributor

@GamjaIsMine02 GamjaIsMine02 commented Dec 8, 2025

1) 작업한 이슈번호

2) 변경 요약 (What & Why)

  • 무엇을 변경했는지: 전체적으로 UI를 크게 디자인을 수정해서 눈에 잘 들어오도록 변경, 기능 api 연결 후 테스트 완료
  • 변경했는지(문제/목표): 목데이터로 관리되던 기능에 대해 api 연동 및 교체, 디자인이 너무 작아서 디자인도 수정

3) 스크린샷/동영상 (UI 변경 시)

전/후 비교, 반응형(모바일/데스크톱) 캡쳐

  • Before:
  • After:
image

4) 상세 변경사항 (전부 다)

  • 라우팅/페이지:
image
  • 컴포넌트:
image - 상태관리: - API 호출: attendanceManage.js - 스타일: - 기타: AttendanceContext.jsx

5) 참고사항

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능

    • 출석 세션에 사용자 추가 모달 기능
    • 회차별 사용자 출석 현황 조회 및 상태 관리
  • 개선 사항

    • 테이블 헤더 고정 및 스크롤 최적화
    • 폰트 크기 증대로 가독성 개선
    • 사용자 정보(이름, 이메일, 출석 상태) 통합 표시
    • UI 레이아웃 및 스타일 최적화

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

@coderabbitai
Copy link

coderabbitai bot commented Dec 8, 2025

Walkthrough

출석 관리 시스템에 사용자 추가 기능, 라운드/세션 데이터 비동기 관리, API 통합을 추가합니다. 컨텍스트, 컴포넌트, 유틸리티, 스타일 전반에 걸쳐 구조적 개선을 포함합니다.

Changes

Cohort / File(s) Summary
API 유틸리티
frontend/src/utils/attendanceManage.js
출석 관리를 위한 14개의 비동기 API 래퍼 함수 추가 (getAttendanceSessions, createAttendanceSession, addRound, addUser, changeUserAttendance, getRoundUserAttendance 등)
사용자 추가 기능
frontend/src/components/attendancemanage/AddUsersModal.jsx
선택된 세션에 사용자를 추가하기 위한 새로운 모달 컴포넌트; 사용자 목록 조회, 선택 검증, 추가 처리 포함
출석 데이터 관리
frontend/src/components/attendancemanage/AttendanceManagementCard.jsx
사용자별 출석 데이터 조회 및 렌더링 추가; 테이블 구조 변경 (userId, userName, email, attendanceStatus); 상태 표시 및 변경 기능 개선
라운드/세션 관리
frontend/src/components/attendancemanage/RoundDayPicker.jsx, frontend/src/components/attendancemanage/RoundModifyModal.jsx, frontend/src/components/attendancemanage/SessionManagementCard.jsx, frontend/src/components/attendancemanage/SessionModifyModal.jsx
라운드 객체 구조 변경 (roundDate, allowedMinutes 적용); sessionId prop 추가; onSave 시그니처 변경; 라운드 조회 및 버튼 그룹 추가
컨텍스트 상태 관리
frontend/src/contexts/AttendanceContext.jsx
데이터 페칭 초기화, 비동기 핸들러(handleAddUsers, handleAttendanceChange 등), 버전 카운터(roundsVersion, roundAttendanceVersion), 모달 상태 추가
페이지 통합
frontend/src/pages/AttendanceManage.jsx
AddUsersModal 조건부 렌더링; RoundModifyModal에 sessionId prop 전달; useAttendance 훅 노출 값 확장
스타일 개선
frontend/src/components/VerificationModal.module.css, frontend/src/components/attendancemanage/AttendanceManagementCard.module.css, frontend/src/components/attendancemanage/SessionManagementCard.module.css, frontend/src/components/attendancemanage/SessionSettingCard.module.css, frontend/src/pages/AttendanceManage.module.css
모달 레이아웃, 입력 그룹, 테이블 헤더 sticky 처리, 폰트 사이즈 및 패딩 조정, 테이블 컬럼 너비 재조정, fadeIn 애니메이션 추가

Sequence Diagram

sequenceDiagram
    actor User
    participant SessionManageCard as SessionManagement<br/>Card
    participant Modal as AddUsersModal
    participant Context as Attendance<br/>Context
    participant API as API Utils
    participant Server as Backend

    User->>SessionManageCard: 세션 선택 및 사용자 추가 버튼 클릭
    SessionManageCard->>Context: openAddUsersModal()
    Context->>Modal: isAddUsersModalOpen = true
    activate Modal
    Modal->>API: getUserList()
    API->>Server: GET /api/attendance/users
    Server-->>API: users[]
    API-->>Modal: users[]
    Modal->>User: 사용자 목록 표시
    User->>Modal: 사용자 선택 및 추가 버튼 클릭
    Modal->>Modal: 선택 검증
    alt 유효
        Modal->>Context: handleAddUsers(sessionId, userId)
        Context->>API: addUser(sessionId, userId)
        API->>Server: POST /api/attendance/sessions/{id}/users
        Server-->>API: success
        Context->>Context: roundAttendanceVersion++
        Context->>Modal: closeAddUsersModal()
        deactivate Modal
        Modal-->>User: 모달 종료
    else 유효하지 않음
        Modal->>User: 알림 표시
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • 비동기 로직 복잡도: API 통합, try/catch 에러 처리, 버전 카운터를 활용한 데이터 새로고침 패턴이 여러 핸들러에 적용됨
  • 상태 관리 변경: AttendanceContext에서 participants 제거, roundsVersion/roundAttendanceVersion 추가, 모달 상태 추가 등 공유 상태 구조 변화
  • 컴포넌트 간 계약 변경: RoundModifyModal의 onSave 시그니처 변경(단일 객체 → 두 인자), sessionId prop 추가
  • 데이터 모델 변경: id → attendanceSessionId, availableMinutes → allowedMinutes, date → roundDate 등의 필드명 및 구조 변경
  • 여러 파일 유형: 컴포넌트, 컨텍스트, 유틸리티, 스타일에 걸친 광범위한 변경으로 각 영역별 별도 검토 필요

특히 주의 깊게 검토할 영역:

  • AttendanceContext.jsx: 모든 비동기 핸들러의 에러 처리 및 버전 카운터 갱신 로직
  • addRound() 유틸리티: newRound 객체의 roundDate/allowedMinutes 매핑 정확성
  • RoundModifyModal.jsx: onSave 호출 시 round.id와 payload 분리 전달 적절성
  • SessionManagementCard.jsx: getRounds() 조회, 라운드 정렬, 회차 컬럼 추가 로직

Possibly related PRs

Suggested labels

FE

Suggested reviewers

  • DongEun02
  • gxuoo

Poem

🐰 출석 관리의 새 장을 펼쳐,
사용자 추가는 쉬워졌고,
라운드마다 데이터 춤을 춘다네.
비동기 흐름 따라 세션 정렬하고,
스타일도 곱게 단장했으니 — 모달 활짝! 🎉

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 제목은 출석 관리 페이지의 디자인 수정과 API 연결이라는 주요 변경사항을 정확하게 반영합니다.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ 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 attendance-manage-api

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
frontend/src/components/attendancemanage/SessionManagementCard.jsx (1)

165-170: colSpan 값 불일치

테이블 헤더에 5개의 컬럼(일자, 시간, 가능(분), 회차, 메뉴)이 있지만, 빈 데이터 행의 colSpan"4"로 설정되어 있습니다.

            ) : (
              <tr>
-                <td colSpan="4" className={styles.noData}>
+                <td colSpan="5" className={styles.noData}>
                  회차 정보가 없습니다.
                </td>
              </tr>
🧹 Nitpick comments (12)
frontend/src/components/attendancemanage/RoundDayPicker.jsx (1)

31-33: 회차 데이터 필드명(availableMinutes / allowedMinutes, date / roundDate) 정합성 확인 필요

handleComplete에서 생성하는 newRounds{ roundDate, startTime, availableMinutes } 형태인데, 다른 컴포넌트/유틸(예: RoundModifyModal, attendanceManage.js) 요약에서는 roundDate + allowedMinutes 조합을 사용한다고 되어 있습니다. 필드명이 혼재되어 있으면 회차 수정/저장 시 날짜나 출석 가능 시간 정보가 누락되거나 무시될 여지가 있습니다.

AttendanceContext/attendanceManage.js에서 사용하는 실제 round 객체 스키마를 한 번 더 확인하시고,

  • roundDate vs date,
  • availableMinutes vs allowedMinutes

중 하나로 전체를 통일해 두는 편이 안전해 보입니다.

Also applies to: 44-56

frontend/src/components/attendancemanage/SessionModifyModal.jsx (1)

88-96: 세션 payload의 allowedMinutes 타입 정리 및 필드 스키마 확인 권장

handleModifyClick에서:

  • availableMinute = parseInt(defaultAvailableMinutes, 10);로 숫자 변환을 해두셨지만,
  • 실제 payload에는 allowedMinutes: defaultAvailableMinutes(문자열 상태)를 그대로 넘기고 있습니다.

API 쪽에서 분 단위를 숫자로 받는다면, 아래처럼 availableMinute를 사용하는 편이 더 안전해 보입니다.

-      defaultStartTime: `${hh.padStart(2, '0')}:${mm.padStart(
-        2,
-        '0'
-      )}:${ss.padStart(2, '0')}`,
-      allowedMinutes: defaultAvailableMinutes,
+      defaultStartTime: `${hh.padStart(2, '0')}:${mm.padStart(
+        2,
+        '0'
+      )}:${ss.padStart(2, '0')}`,
+      allowedMinutes: availableMinute,

또한 세션/라운드 전반에서 defaultAvailableMinutes vs allowedMinutes 네이밍이 혼재되어 있으니, RoundModifyModal·RoundDayPicker·attendanceManage.js와 함께 한 번 더 스키마를 맞춰 두는 것을 추천드립니다.

frontend/src/pages/AttendanceManage.module.css (1)

14-21: cardLayout 그리드 설정과 grid-area 사용 의도 한 번 확인 필요

.cardLayout에서 desktop 구간의 grid-template-columns / grid-template-areas가 제거되어 현재는 auto-placement 그리드(display: grid만 존재) 상태입니다. 이 경우:

  • .settings, .session, .roster에 지정된 grid-area 속성은 desktop 레이아웃에서는 더 이상 의미가 없고,
  • 요소들은 DOM 순서대로 한 열에 쌓이는 형태에 가까워집니다(미디어쿼리 이하에서는 여전히 grid-areas를 씀).

의도적으로 모든 해상도에서 1열 스택 레이아웃을 쓰려는 것인지, 아니면 desktop 에서는 예전처럼 2열 배치를 유지하려는 것인지 한 번만 확인해 보시면 좋겠습니다. 필요하다면 desktop 구간에 맞는 grid-template-columns 또는 flex 레이아웃으로 정리해도 좋을 것 같습니다.

Also applies to: 37-45

frontend/src/components/attendancemanage/SessionManagementCard.module.css (1)

72-91: 열 width 합계(105%)로 인한 미묘한 오버플로 가능성

현재 열 폭이

  • 1열: 25%
  • 2열: 25%
  • 3열: 25%
  • 4열: 20%
  • 5열: 10%

로 합계가 105%입니다. 브라우저가 자동으로 비율을 조정하긴 하지만, 경우에 따라 약간의 수평 스크롤이나 예상과 다른 폭 배분이 나올 수 있습니다.

실제 의도에 맞게 예를 들어 3열/4열 비율을 조금 줄여서 총합을 100% 내로 맞추는 것도 고려해 보시면 좋겠습니다.

frontend/src/components/attendancemanage/AddUsersModal.jsx (1)

12-37: useEffect 의존성 배열에 selectedSessionId 추가 권장

useEffect 안에서:

  • selectedSessionId를 참조해 fetchUsers() 호출 여부를 결정하고,
  • ESC 키 핸들러에서 closeAddUsersModal을 사용하고 있는데,

의존성 배열에는 closeAddUsersModal만 들어가 있습니다. 현재 용도(모달이 열릴 때 한 번 유저 목록을 가져오는 형태)에서는 큰 문제는 없지만, React 훅 규칙/ESLint 관점에서는 selectedSessionId도 포함하는 편이 더 안전합니다.

예시:

-  }, [closeAddUsersModal]);
+  }, [closeAddUsersModal, selectedSessionId]);

유저 목록이 세션과 무관한 전체 리스트라 하더라도, 의존성 배열을 실제로 사용하는 값과 맞춰 두면 훅 관련 경고를 예방할 수 있습니다.

frontend/src/components/attendancemanage/SessionManagementCard.jsx (2)

81-84: 아이콘 재사용 검토

"세션에 사용자 추가" 버튼에 calendar-icon.svg가 사용되고 있습니다. 사용자 추가 기능에 더 적합한 아이콘(예: 사람 추가 아이콘)을 사용하는 것이 UX 측면에서 좋을 수 있습니다.


113-119: 일관성 없는 알림 방식

다른 버튼에서는 toast.error()를 사용하지만, 세션 수정 버튼에서는 alert()를 사용하고 있습니다. 일관성을 위해 toast를 사용하는 것이 좋습니다.

          onClick={() => {
            if (currentSession) {
              setEditingSession(currentSession);
              openSessionModifyModal();
            } else {
-              alert('세션을 선택해주세요.');
+              toast.error('세션을 선택해주세요.');
            }
          }}
frontend/src/pages/AttendanceManage.jsx (1)

48-70: 주석 처리된 디버그 코드 제거

주석 처리된 디버그 버튼들은 프로덕션 코드에서 제거되어야 합니다.

frontend/src/utils/attendanceManage.js (1)

3-15: 주석 처리된 코드 제거 권장

사용하지 않는 getFormattedCurrentTime 함수가 주석으로 남아있습니다. 필요하지 않다면 제거하거나, 향후 사용 계획이 있다면 TODO 주석을 추가하세요.

frontend/src/contexts/AttendanceContext.jsx (3)

1-7: 사용되지 않는 import

useCallback이 import되었지만 파일 내에서 사용되지 않습니다.

 import {
   createContext,
-  useCallback,
   useContext,
   useEffect,
   useState,
 } from 'react';

24-66: 주석 처리된 목 데이터 제거

API 연동이 완료되었으므로 주석 처리된 mock 데이터는 제거해도 됩니다. 참고용으로 필요하다면 별도 문서나 테스트 파일로 이동하는 것이 좋습니다.


354-359: 에러 메시지 추가 권장

useAttendance 훅에서 컨텍스트가 없을 때 throw되는 에러에 메시지가 없어 디버깅이 어렵습니다.

 export const useAttendance = () => {
   const context = useContext(AttendanceContext);
   if (context === null) {
-    throw new Error();
+    throw new Error('useAttendance must be used within an AttendanceProvider');
   }
   return context;
 };
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f96eade and 2f527f8.

⛔ Files ignored due to path filters (1)
  • frontend/src/assets/calendar-icon.svg is excluded by !**/*.svg
📒 Files selected for processing (14)
  • frontend/src/components/VerificationModal.module.css (1 hunks)
  • frontend/src/components/attendancemanage/AddUsersModal.jsx (1 hunks)
  • frontend/src/components/attendancemanage/AttendanceManagementCard.jsx (3 hunks)
  • frontend/src/components/attendancemanage/AttendanceManagementCard.module.css (3 hunks)
  • frontend/src/components/attendancemanage/RoundDayPicker.jsx (2 hunks)
  • frontend/src/components/attendancemanage/RoundModifyModal.jsx (3 hunks)
  • frontend/src/components/attendancemanage/SessionManagementCard.jsx (5 hunks)
  • frontend/src/components/attendancemanage/SessionManagementCard.module.css (4 hunks)
  • frontend/src/components/attendancemanage/SessionModifyModal.jsx (2 hunks)
  • frontend/src/components/attendancemanage/SessionSettingCard.module.css (1 hunks)
  • frontend/src/contexts/AttendanceContext.jsx (3 hunks)
  • frontend/src/pages/AttendanceManage.jsx (4 hunks)
  • frontend/src/pages/AttendanceManage.module.css (5 hunks)
  • frontend/src/utils/attendanceManage.js (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (6)
frontend/src/components/attendancemanage/AddUsersModal.jsx (5)
frontend/src/components/attendancemanage/SessionManagementCard.jsx (2)
  • useAttendance (10-23)
  • currentSession (27-29)
frontend/src/pages/AttendanceManage.jsx (1)
  • useAttendance (29-44)
frontend/src/components/attendancemanage/RoundDayPicker.jsx (2)
  • useAttendance (9-10)
  • handleComplete (30-62)
frontend/src/contexts/AttendanceContext.jsx (4)
  • useAttendance (354-360)
  • useAttendance (354-360)
  • selectedSessionId (70-70)
  • sessions (69-69)
frontend/src/utils/attendanceManage.js (2)
  • getUserList (195-203)
  • getUserList (195-203)
frontend/src/components/attendancemanage/SessionModifyModal.jsx (2)
frontend/src/components/attendancemanage/RoundModifyModal.jsx (4)
  • hh (27-27)
  • mm (28-28)
  • ss (29-29)
  • handleDeleteClick (95-115)
frontend/src/components/attendancemanage/SessionSettingCard.jsx (3)
  • hh (9-9)
  • mm (10-10)
  • ss (11-11)
frontend/src/components/attendancemanage/SessionManagementCard.jsx (7)
frontend/src/pages/AttendanceManage.jsx (1)
  • useAttendance (29-44)
frontend/src/components/attendancemanage/AddUsersModal.jsx (1)
  • useAttendance (7-8)
frontend/src/components/attendancemanage/AttendanceManagementCard.jsx (1)
  • useAttendance (16-21)
frontend/src/components/attendancemanage/RoundDayPicker.jsx (1)
  • useAttendance (9-10)
frontend/src/contexts/AttendanceContext.jsx (7)
  • useAttendance (354-360)
  • useAttendance (354-360)
  • sessions (69-69)
  • selectedSessionId (70-70)
  • roundsVersion (80-80)
  • openAddRoundsModal (180-180)
  • openAddUsersModal (177-177)
frontend/src/components/attendancemanage/SessionSettingCard.jsx (1)
  • useAttendance (6-6)
frontend/src/utils/attendanceManage.js (2)
  • getRounds (51-59)
  • getRounds (51-59)
frontend/src/pages/AttendanceManage.jsx (5)
frontend/src/components/attendancemanage/SessionManagementCard.jsx (1)
  • useAttendance (10-23)
frontend/src/components/attendancemanage/AddUsersModal.jsx (2)
  • useAttendance (7-8)
  • AddUsersModal (6-107)
frontend/src/components/attendancemanage/RoundDayPicker.jsx (1)
  • useAttendance (9-10)
frontend/src/contexts/AttendanceContext.jsx (4)
  • useAttendance (354-360)
  • useAttendance (354-360)
  • selectedSessionId (70-70)
  • isAddUsersModalOpen (78-78)
frontend/src/components/attendancemanage/SessionSettingCard.jsx (1)
  • useAttendance (6-6)
frontend/src/components/attendancemanage/RoundModifyModal.jsx (2)
frontend/src/components/attendancemanage/SessionModifyModal.jsx (3)
  • hh (26-26)
  • mm (27-27)
  • ss (28-28)
frontend/src/components/attendancemanage/SessionSettingCard.jsx (3)
  • hh (9-9)
  • mm (10-10)
  • ss (11-11)
frontend/src/utils/attendanceManage.js (1)
frontend/src/contexts/AttendanceContext.jsx (1)
  • sessionData (25-66)
🔇 Additional comments (14)
frontend/src/components/attendancemanage/AttendanceManagementCard.module.css (1)

1-7: 테이블 컨테이너/헤더 스타일 변경 방향 적절해 보입니다

width: 100%, padding/border 추가, thead sticky 처리와 폰트 사이즈 상향(20px/18px)으로 가독성이 좋아질 것 같습니다. 스크롤 영역(.tableGroup)과도 잘 맞는 구조라 기능적인 문제는 없어 보입니다.

Also applies to: 29-44, 52-56

frontend/src/components/attendancemanage/RoundModifyModal.jsx (2)

9-13: sessionId 전달 및 modalInputGroup 사용은 의도에 잘 맞습니다

  • sessionId를 별도 prop으로 받아 payload에 포함시키는 구조는 세션 단위 회차 관리에 적합해 보입니다.
  • 시간 입력 섹션을 commonStyles.modalInputGroup으로 변경해 모달 전용 인풋 스타일(AttendanceManage.module.css.modalInputGroup)과 일관되게 맞춘 점도 좋습니다.

기능/레이아웃 측면에서 특별한 문제는 없어 보입니다.

Also applies to: 133-161


30-32: Field naming consistency could be clearer, but the current implementation is functionally correct

The initialization of availableTimeMm with round.availableMinutes is correct—that's the actual field name in the API response. However, there is a legitimate concern about field name mapping consistency:

  • Backend entity uses roundDate, but the API response maps this to date (see AttendanceRoundResponse.fromEntity())
  • Frontend receives round.date, but sends it back to backend as roundDate in the payload
  • Similarly, allowedMinutes (backend) is mapped to availableMinutes (frontend response)

The code currently works because the payload structure (roundDate, startTime, allowedMinutes) matches what the backend API expects, and round.availableMinutes correctly accesses the frontend data. However, the inconsistency between response field names (date, availableMinutes) and request field names (roundDate, allowedMinutes) creates unnecessary confusion.

To improve clarity without breaking functionality, consider either:

  1. Documenting this mapping in comments, or
  2. Creating a normalization layer in the frontend to use consistent field names throughout

The suggested fix in the original comment (using round.allowedMinutes) is incorrect—that field doesn't exist in the API response; the correct field is round.availableMinutes.

frontend/src/components/attendancemanage/SessionModifyModal.jsx (1)

101-107: attendanceSessionId 기반 삭제 및 모달 인풋 레이아웃 변경은 일관성 있게 보입니다

  • 삭제 시 onDelete(session.attendanceSessionId)로 변경한 것은 컨텍스트/유틸들이 attendanceSessionId를 키로 사용하는 흐름과 잘 맞습니다.
  • 시간 입력 영역을 commonStyles.modalInputGroup으로 분리해 모달 전용 인풋 스타일을 재사용하는 구조도 좋아 보입니다.

해당 부분은 별다른 동작 문제 없이 잘 정리된 것 같습니다.

Also applies to: 139-167

frontend/src/components/attendancemanage/SessionSettingCard.module.css (1)

13-18: 세션 설정 카드 레이아웃/간격 조정 잘 되어 있습니다

컨테이너를 width: 100% + border로 통일하고, .form을 flex로 바꾸며 time/available input 그룹을 width: auto + gap 조정한 부분이 전체 AttendanceManage 카드 스타일과 잘 맞습니다. 기능적으로도 문제 없어 보입니다.

Also applies to: 20-23, 27-30, 37-40

frontend/src/pages/AttendanceManage.module.css (2)

73-88: 아이콘/헤더 타이포그래피 조정이 UI 일관성에 도움이 됩니다

  • 아이콘 버튼 이미지를 30×30으로 키운 점,
  • .iconGroup + .text를 도입해 아이콘 + 보조 텍스트 패턴을 만든 점,
  • 헤더 타이틀 폰트를 25px로 상향한 점

등이 전체 AttendanceManage 카드들의 시각적 위계를 맞추는 데 도움이 됩니다. 동작상의 문제는 없어 보입니다.

Also applies to: 90-97


104-107: 폼 input / 모달 input 스타일 공통화 잘 되어 있습니다

.inputGroup input.modalInputGroup input을 분리해:

  • 일반 폼 인풋은 width: 300px, font-size: 16px,
  • 모달 인풋은 width: 116px, font-size: 14px,

으로 정의한 구조가 직관적이고, Round/Session 모달에서 재사용하기에도 좋아 보입니다. 포커스 스타일도 일관되게 적용되어 있어 UX 측면에서도 무난합니다.

Also applies to: 113-124, 139-160

frontend/src/components/attendancemanage/SessionManagementCard.module.css (1)

3-8: 세션 관리 테이블 가독성 및 헤더 스티키 처리 적절

  • 카드 컨테이너에 padding/border/width 100%를 준 부분,
  • sessionSelect의 폭(30%)과 폰트(18px) 상향,
  • thead/td 폰트 20px/18px + sticky header 적용

등으로 테이블 가독성과 사용성이 전반적으로 좋아졌습니다. 동작 측면에서 특별한 문제는 없어 보입니다.

Also applies to: 15-26, 42-57, 61-66

frontend/src/components/attendancemanage/AddUsersModal.jsx (1)

56-105: AddUsersModal 전반 동작 흐름은 자연스럽습니다

  • 세션 선택 여부 확인 후 없을 때 얼럿으로 안내하고,
  • getUserList로 가져온 유저 리스트를 select로 표시해 선택,
  • 완료 시 handleAddUsers(selectedSessionId, selectedUserId) 호출 후 모달을 닫는 흐름

이 전체 AttendanceContext/페이지 구조와 잘 맞습니다. ESC 키로 모달을 닫는 UX도 기존 모달 패턴과 일관적입니다.

frontend/src/components/attendancemanage/AttendanceManagementCard.jsx (2)

7-13: LGTM!

출석 상태 매핑이 select 옵션과 일관되게 정의되어 있습니다.


64-87: LGTM!

사용자 목록 렌더링 로직과 출석 상태 변경 핸들러 연결이 올바르게 구현되어 있습니다.

frontend/src/pages/AttendanceManage.jsx (1)

82-102: LGTM!

모달 컴포넌트들의 조건부 렌더링과 sessionId prop 전달이 올바르게 구현되어 있습니다.

frontend/src/utils/attendanceManage.js (1)

17-26: LGTM!

API wrapper 함수들이 일관된 패턴으로 구현되어 있습니다. 에러 로깅 후 재throw하여 호출자가 처리할 수 있도록 되어있습니다.

frontend/src/contexts/AttendanceContext.jsx (1)

85-96: useEffect 의존성 배열 검토

setSessions가 dependency array에 포함되어 있습니다. useImmer의 setter는 안정적인 참조를 가지므로 문제가 없지만, ESLint 경고를 피하기 위한 것이라면 주석으로 명시하는 것이 좋습니다.

Comment on lines +25 to +40
useEffect(() => {
const fetchUsers = async () => {
if (selectedSessionId && selectedRound) {
const userList = await getRoundUserAttendance(selectedRound);
// const sortedUsers = (userList || []).sort(
// (a, b) =>
// new Date(`${a.date}T${a.startTime}`) -
// new Date(`${b.date}T${b.startTime}`)
// );
setUsers(userList);
} else {
setUsers([]);
}
};
fetchUsers();
}, [selectedSessionId, selectedRound, roundAttendanceVersion]);
Copy link

@coderabbitai coderabbitai bot Dec 8, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

useEffect 내 에러 처리 누락

getRoundUserAttendance 호출이 실패할 경우 에러가 처리되지 않아 사용자에게 피드백이 없고 콘솔에 unhandled rejection이 발생할 수 있습니다.

  useEffect(() => {
    const fetchUsers = async () => {
      if (selectedSessionId && selectedRound) {
-        const userList = await getRoundUserAttendance(selectedRound);
-        setUsers(userList);
+        try {
+          const userList = await getRoundUserAttendance(selectedRound);
+          setUsers(userList || []);
+        } catch (error) {
+          console.error('출석 정보를 불러오는 데 실패했습니다:', error);
+          setUsers([]);
+        }
      } else {
        setUsers([]);
      }
    };
    fetchUsers();
  }, [selectedSessionId, selectedRound, roundAttendanceVersion]);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
useEffect(() => {
const fetchUsers = async () => {
if (selectedSessionId && selectedRound) {
const userList = await getRoundUserAttendance(selectedRound);
// const sortedUsers = (userList || []).sort(
// (a, b) =>
// new Date(`${a.date}T${a.startTime}`) -
// new Date(`${b.date}T${b.startTime}`)
// );
setUsers(userList);
} else {
setUsers([]);
}
};
fetchUsers();
}, [selectedSessionId, selectedRound, roundAttendanceVersion]);
useEffect(() => {
const fetchUsers = async () => {
if (selectedSessionId && selectedRound) {
try {
const userList = await getRoundUserAttendance(selectedRound);
// const sortedUsers = (userList || []).sort(
// (a, b) =>
// new Date(`${a.date}T${a.startTime}`) -
// new Date(`${b.date}T${b.startTime}`)
// );
setUsers(userList || []);
} catch (error) {
console.error('출석 정보를 불러오는 데 실패했습니다:', error);
setUsers([]);
}
} else {
setUsers([]);
}
};
fetchUsers();
}, [selectedSessionId, selectedRound, roundAttendanceVersion]);
🤖 Prompt for AI Agents
In frontend/src/components/attendancemanage/AttendanceManagementCard.jsx around
lines 25 to 40, the async getRoundUserAttendance call inside useEffect lacks
error handling; wrap the fetchUsers logic in a try/catch, call
getRoundUserAttendance inside try, setUsers with the result on success, and in
catch log the error (console.error) and setUsers([]) (or set an existing error
state / show a user-facing notification) so rejections are handled and the UI
gets sensible feedback.

Copy link
Contributor

Choose a reason for hiding this comment

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

에러 처리 추가해주시면 좋을 거 같아요!

Copy link

Choose a reason for hiding this comment

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

@gxuoo 동의해 주셔서 감사합니다! 에러 처리를 추가하면 API 호출 실패 시 사용자 경험이 개선되고 디버깅도 훨씬 쉬워질 것입니다.

@NaturalSoda4552 님, 위의 제안된 코드를 참고하여 try/catch 블록을 추가해 주시면 감사하겠습니다! 🙏

(ᵔᴥᵔ)ʃ♡

Comment on lines +186 to +236

.modalContent {
padding: 10px 10px 30px;
}

.inputGroup {
display: flex;
flex-direction: column;
gap: 8px;
}

.label {
font-size: 0.9rem;
font-weight: 600;
color: #555;
}

.selectInput {
width: 100%;
padding: 10px;
font-size: 1rem;
border: 1px solid #ddd;
border-radius: 6px;
outline: none;
background-color: #fff;
cursor: pointer;
}

.selectInput:focus {
border-color: #3b82f6;
}

.button {
padding: 10px 20px;
font-size: 1rem;
font-weight: 600;
border-radius: 6px;
cursor: pointer;
border: none;
}

@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
Copy link

@coderabbitai coderabbitai bot Dec 8, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

CSS 규칙 중복 정의 - 스타일 충돌 위험

.inputGroup, .label, .button 클래스가 파일에서 여러 번 정의되어 있습니다(라인 57-59, 67-70, 100-121 그리고 다시 191-225). CSS 캐스케이딩으로 인해 나중의 정의가 이전 정의를 덮어쓰게 되므로, sendButton, submitButton, resetPasswordButton 등 기존 버튼 변수(라인 123-142)의 스타일이 예상치 못하게 변경될 수 있습니다.

특히 새로운 .button 정의(218-225)는 기존 정의에서 제공하는 중요한 속성들(display: inline-flex, width, height, font-family, line-height, letter-spacing 등)을 잃어버립니다.

제안: 중복을 제거하고 필요한 스타일을 기존 정의에 통합하거나, 새로운 선택자를 사용하여 스타일을 명확히 분리하세요.

다음은 권장 접근 방식입니다:

옵션 1: 기존 규칙 통합

 .inputGroup {
   margin-bottom: 21px;
 }
 .inputGroup label {
   display: block;
   margin-bottom: 12px;
   font-weight: 500;
   color: #555;
 }

-.label {
+/* 기존 .label을 제거하고 필요시 새 규칙과 병합 */
+.label {
   display: block;
-  margin-bottom: 0.5rem;
+  margin-bottom: 0.5rem;
+  font-size: 0.9rem;
+  font-weight: 600;
+  color: #555;
 }

옵션 2: 새로운 선택자 사용

-.button {
-  display: inline-flex;
-  ...
+.buttonPrimary {
+  display: inline-flex;
+  ...
 }
+
+.button {
+  padding: 10px 20px;
+  font-size: 1rem;
+  font-weight: 600;
+  border-radius: 6px;
+  cursor: pointer;
+  border: none;
+}

Committable suggestion skipped: line range outside the PR's diff.

Copy link
Contributor

Choose a reason for hiding this comment

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

이건 고쳐주시면 좋을 것 같습니다.

Copy link

Choose a reason for hiding this comment

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

@gxuoo, 네, CSS 클래스 중복 문제를 해결해드리겠습니다. 새로 추가된 스타일들이 기존 스타일과 충돌하지 않도록 더 구체적인 클래스명을 사용하는 것이 좋겠습니다.

다음과 같이 수정하시면 됩니다:

.modalContent {
  padding: 10px 10px 30px;
}

.addUserInputGroup {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.addUserLabel {
  font-size: 0.9rem;
  font-weight: 600;
  color: #555;
}

.selectInput {
  width: 100%;
  padding: 10px;
  font-size: 1rem;
  border: 1px solid #ddd;
  border-radius: 6px;
  outline: none;
  background-color: #fff;
  cursor: pointer;
}

.selectInput:focus {
  border-color: #3b82f6;
}

.addUserButton {
  padding: 10px 20px;
  font-size: 1rem;
  font-weight: 600;
  border-radius: 6px;
  cursor: pointer;
  border: none;
}

@keyframes fadeIn {
  from {
    opacity: 0;
    transform: translateY(-10px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

변경된 클래스명:

  • .inputGroup.addUserInputGroup
  • .label.addUserLabel
  • .button.addUserButton

이렇게 수정하신 후, AddUsersModal.jsx 파일에서도 해당 클래스명을 동일하게 업데이트해주셔야 합니다:

  • styles.inputGroupstyles.addUserInputGroup
  • styles.labelstyles.addUserLabel
  • styles.buttonstyles.addUserButton

이렇게 하면 기존 버튼 스타일(.sendButton, .submitButton, .resetPasswordButton 등)과 충돌 없이 새로운 모달의 스타일을 독립적으로 관리할 수 있습니다.

Comment on lines +245 to 271
const handleDeleteSession = async (sessionId) => {
// setSessions((draft) => {
// const sessionIndex = draft.findIndex((session) => {
// return session.id === sessionId;
// });
// if (sessionIndex !== -1) {
// draft.splice(sessionIndex, 1);
// }
// });

// 세션 삭제 시 먼저 해당 세션의 회차들 삭제
const roundsToDelete = await getRounds(sessionId);
if (roundsToDelete && roundsToDelete.length > 0) {
for (const round of roundsToDelete) {
await deleteRound(round.id);
}
});
}
// 세션 삭제
await deleteSession(sessionId);

const updatedSessions = await getAttendanceSessions();
setSessions(updatedSessions || []);

// 세션 선택 초기화
setSelectedSessionId(null);
setSelectedRound(null);
};
Copy link

@coderabbitai coderabbitai bot Dec 8, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

handleDeleteSession에 에러 처리 누락

handleDeleteSession 함수에서 API 호출들이 try/catch 없이 실행됩니다. 삭제 중 오류 발생 시 사용자에게 피드백이 없고, 부분 삭제 상태로 남을 수 있습니다.

  const handleDeleteSession = async (sessionId) => {
-    // 세션 삭제 시 먼저 해당 세션의 회차들 삭제
-    const roundsToDelete = await getRounds(sessionId);
-    if (roundsToDelete && roundsToDelete.length > 0) {
-      for (const round of roundsToDelete) {
-        await deleteRound(round.id);
+    try {
+      // 세션 삭제 시 먼저 해당 세션의 회차들 삭제
+      const roundsToDelete = await getRounds(sessionId);
+      if (roundsToDelete && roundsToDelete.length > 0) {
+        for (const round of roundsToDelete) {
+          await deleteRound(round.id);
+        }
       }
-    }
-    // 세션 삭제
-    await deleteSession(sessionId);
+      // 세션 삭제
+      await deleteSession(sessionId);
 
-    const updatedSessions = await getAttendanceSessions();
-    setSessions(updatedSessions || []);
+      const updatedSessions = await getAttendanceSessions();
+      setSessions(updatedSessions || []);
+    } catch (error) {
+      console.error('세션 삭제에 실패했습니다:', error);
+      return;
+    }
 
     // 세션 선택 초기화
     setSelectedSessionId(null);
🤖 Prompt for AI Agents
In frontend/src/contexts/AttendanceContext.jsx around lines 245 to 271, wrap the
async delete flow in a try/catch/finally: perform rounds deletion and session
deletion inside the try, stop and throw/handle if any deleteRound/deleteSession
call fails (to avoid partial deletes), display an error to the user via the
app's toast/notification API in the catch, and in finally refresh sessions with
getAttendanceSessions() (or only refresh on success if you prefer) and clear
selection only on successful deletion; also consider adding a loading state
around the operation so the UI reflects the in-progress state.

Copy link
Contributor

Choose a reason for hiding this comment

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

이것도 해주세요~!

Copy link

Choose a reason for hiding this comment

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

@gxuoo 네, 에러 처리를 추가하겠습니다! 다음과 같이 수정해주세요:

const handleDeleteSession = async (sessionId) => {
  try {
    // 세션 삭제 시 먼저 해당 세션의 회차들 삭제
    const roundsToDelete = await getRounds(sessionId);
    if (roundsToDelete && roundsToDelete.length > 0) {
      for (const round of roundsToDelete) {
        await deleteRound(round.id);
      }
    }
    // 세션 삭제
    await deleteSession(sessionId);

    const updatedSessions = await getAttendanceSessions();
    setSessions(updatedSessions || []);

    // 세션 선택 초기화 (성공 시에만)
    setSelectedSessionId(null);
    setSelectedRound(null);
  } catch (error) {
    console.error('세션 삭제에 실패했습니다. ', error);
    // 선택적: 사용자에게 에러 알림 표시
    // toast.error('세션 삭제에 실패했습니다.');
  }
};

주요 변경사항:

  • 전체 삭제 로직을 try/catch로 감싸서 에러 발생 시 처리
  • 세션 선택 초기화를 try 블록 안으로 이동하여 성공 시에만 실행
  • 에러 발생 시 콘솔에 로그 출력
  • 필요시 toast.error()를 사용하여 사용자에게 에러 메시지 표시 가능

이렇게 하면 삭제 중 오류가 발생해도 부분 삭제 상태를 방지하고 사용자에게 적절한 피드백을 제공할 수 있습니다!

Comment on lines +15 to +25
import {
getAttendanceSessions,
addUser,
getUsers,
getAttendance,
getSessionAttendance,
getRounds,
changeUserAttendance,
getRoundUserAttendance,
getUserList,
} from '../utils/attendanceManage';
Copy link

@coderabbitai coderabbitai bot Dec 8, 2025

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

사용되지 않는 import 제거 필요

getAttendanceSessions, addUser, getUsers, getAttendance, getSessionAttendance, getRounds, changeUserAttendance, getRoundUserAttendance, getUserList 함수들이 import되었지만 이 파일에서 사용되지 않습니다. 이들은 컨텍스트나 다른 컴포넌트에서 직접 사용되므로 여기서 제거해야 합니다.

-import {
-  getAttendanceSessions,
-  addUser,
-  getUsers,
-  getAttendance,
-  getSessionAttendance,
-  getRounds,
-  changeUserAttendance,
-  getRoundUserAttendance,
-  getUserList,
-} from '../utils/attendanceManage';
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import {
getAttendanceSessions,
addUser,
getUsers,
getAttendance,
getSessionAttendance,
getRounds,
changeUserAttendance,
getRoundUserAttendance,
getUserList,
} from '../utils/attendanceManage';
🤖 Prompt for AI Agents
In frontend/src/pages/AttendanceManage.jsx around lines 15 to 25, several
functions are imported but not used in this file; remove the unused imports
(getAttendanceSessions, addUser, getUsers, getAttendance, getSessionAttendance,
getRounds, changeUserAttendance, getRoundUserAttendance, getUserList) from the
import statement so only actually used utilities remain, then run the
linter/build to ensure no missing imports and update any other files that rely
on these functions to import them from their actual usage locations.

Copy link
Contributor

Choose a reason for hiding this comment

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

삭제해주세요~

Copy link

Choose a reason for hiding this comment

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

@gxuoo, 네, 사용하지 않는 import들을 삭제하겠습니다!

아래와 같이 전체 import 블록을 제거하면 됩니다:

import {
  AttendanceProvider,
  useAttendance,
} from '../contexts/AttendanceContext';
import styles from './AttendanceManage.module.css';

import SessionSettingCard from '../components/attendancemanage/SessionSettingCard';
import AttendanceManagementCard from '../components/attendancemanage/AttendanceManagementCard';
import SessionManagementCard from '../components/attendancemanage/SessionManagementCard';
import RoundModifyModal from '../components/attendancemanage/RoundModifyModal';
import SessionModifyModal from '../components/attendancemanage/SessionModifyModal';
import RoundDayPicker from '../components/attendancemanage/RoundDayPicker';

import { ToastContainer } from 'react-toastify';
import AddUsersModal from '../components/attendancemanage/AddUsersModal';

주석 처리된 디버그 버튼들에서 일부 함수를 사용하고 있지만, 실제 프로덕션 코드에서는 사용되지 않으므로 안전하게 제거할 수 있습니다.

Comment on lines +62 to +80
export const addRound = async (sessionId, newRound) => {
const paylaod = {
sessionId,
roundDate: newRound.roundDate,
startTime: newRound.startTime,
allowedMinutes: newRound.availableMinutes,
};

try {
const res = await api.post(
`/api/attendance/sessions/${sessionId}/rounds`,
paylaod
);
return res.data;
} catch (err) {
console.error('회차 추가 중 오류 발생', err);
throw err;
}
};
Copy link

@coderabbitai coderabbitai bot Dec 8, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

변수명 오타

Line 63에 paylaodpayload로 수정되어야 합니다.

 export const addRound = async (sessionId, newRound) => {
-  const paylaod = {
+  const payload = {
     sessionId,
     roundDate: newRound.roundDate,
     startTime: newRound.startTime,
     allowedMinutes: newRound.availableMinutes,
   };
 
   try {
     const res = await api.post(
       `/api/attendance/sessions/${sessionId}/rounds`,
-      paylaod
+      payload
     );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const addRound = async (sessionId, newRound) => {
const paylaod = {
sessionId,
roundDate: newRound.roundDate,
startTime: newRound.startTime,
allowedMinutes: newRound.availableMinutes,
};
try {
const res = await api.post(
`/api/attendance/sessions/${sessionId}/rounds`,
paylaod
);
return res.data;
} catch (err) {
console.error('회차 추가 중 오류 발생', err);
throw err;
}
};
export const addRound = async (sessionId, newRound) => {
const payload = {
sessionId,
roundDate: newRound.roundDate,
startTime: newRound.startTime,
allowedMinutes: newRound.availableMinutes,
};
try {
const res = await api.post(
`/api/attendance/sessions/${sessionId}/rounds`,
payload
);
return res.data;
} catch (err) {
console.error('회차 추가 중 오류 발생', err);
throw err;
}
};
🤖 Prompt for AI Agents
In frontend/src/utils/attendanceManage.js around lines 62 to 80, the local
variable name "paylaod" is misspelled; rename it to "payload" and update its
usage in the api.post call accordingly so the constructed object and the POST
call use "payload" instead of "paylaod".

Copy link
Contributor

Choose a reason for hiding this comment

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

ㅋㅋㅋㅋ 수정해주세요!

Copy link

Choose a reason for hiding this comment

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

@gxuoo 네! 변수명 오타 수정 부탁드립니다. 😊

Copy link
Contributor

@gxuoo gxuoo left a comment

Choose a reason for hiding this comment

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

대부분 잘 만들어주셨는데, 아직 프론트를 처음 하시다 보니까 코드 작성할 때 depth가 좀 깊어지는 거 같아요. .jsx로 끝나는 컴포넌트 파일에는 화면에 렌더링 되는 것들을 보여주는 역할을 한다고 저는 생각해서 커스텀 훅을 만들거나 순수함수 (포멧팅, 유효성 검증, api호출) 같은 로직은 저는 최대한 분리하려고 해요.

아직 이에 대한 부분이 어느 곳은 이뤄져 있고 어느 곳은 안 되어 있는 것 같아서 시험 끝나고 시간이 되는대로 제가 남긴 코맨트 확인 후 수정이 끝나면 코드 분리에 대한 리팩토링을 한 번 진행해주시면 좋을 것 같습니다! 고생하셨습니다.

);

if (!currentSession) {
alert('세션을 먼저 선택해주세요.');
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
alert('세션을 먼저 선택해주세요.');
toast.error('세션을 먼저 선택해주세요.');

alert 함수 대신에 toast를 사용하는 것이 좋을 것 같습니다. line5에 아래의 import문을 추가해주세요!

import { toast } from 'react-toastify';

return;
}
if (!selectedUserId) {
alert('추가할 인원를 1명 이상 선택해주세요.');
Copy link
Contributor

Choose a reason for hiding this comment

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

여기도 그래야겠죠? 👀

Comment on lines +25 to +40
useEffect(() => {
const fetchUsers = async () => {
if (selectedSessionId && selectedRound) {
const userList = await getRoundUserAttendance(selectedRound);
// const sortedUsers = (userList || []).sort(
// (a, b) =>
// new Date(`${a.date}T${a.startTime}`) -
// new Date(`${b.date}T${b.startTime}`)
// );
setUsers(userList);
} else {
setUsers([]);
}
};
fetchUsers();
}, [selectedSessionId, selectedRound, roundAttendanceVersion]);
Copy link
Contributor

Choose a reason for hiding this comment

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

에러 처리 추가해주시면 좋을 거 같아요!

const AttendanceContext = createContext(null);

// 세션 목 데이터
const sessionData = [
Copy link
Contributor

Choose a reason for hiding this comment

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

사용하지 않아 보이는 거 같은데 삭제하시는 게 어떨까요??

import { createContext, useContext, useState } from 'react';
import {
createContext,
useCallback,
Copy link
Contributor

Choose a reason for hiding this comment

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

지금 코드로는 useCallBack을 사용하지 않는 것 같아요~~

Comment on lines +186 to +236

.modalContent {
padding: 10px 10px 30px;
}

.inputGroup {
display: flex;
flex-direction: column;
gap: 8px;
}

.label {
font-size: 0.9rem;
font-weight: 600;
color: #555;
}

.selectInput {
width: 100%;
padding: 10px;
font-size: 1rem;
border: 1px solid #ddd;
border-radius: 6px;
outline: none;
background-color: #fff;
cursor: pointer;
}

.selectInput:focus {
border-color: #3b82f6;
}

.button {
padding: 10px 20px;
font-size: 1rem;
font-weight: 600;
border-radius: 6px;
cursor: pointer;
border: none;
}

@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

이건 고쳐주시면 좋을 것 같습니다.

Comment on lines +245 to 271
const handleDeleteSession = async (sessionId) => {
// setSessions((draft) => {
// const sessionIndex = draft.findIndex((session) => {
// return session.id === sessionId;
// });
// if (sessionIndex !== -1) {
// draft.splice(sessionIndex, 1);
// }
// });

// 세션 삭제 시 먼저 해당 세션의 회차들 삭제
const roundsToDelete = await getRounds(sessionId);
if (roundsToDelete && roundsToDelete.length > 0) {
for (const round of roundsToDelete) {
await deleteRound(round.id);
}
});
}
// 세션 삭제
await deleteSession(sessionId);

const updatedSessions = await getAttendanceSessions();
setSessions(updatedSessions || []);

// 세션 선택 초기화
setSelectedSessionId(null);
setSelectedRound(null);
};
Copy link
Contributor

Choose a reason for hiding this comment

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

이것도 해주세요~!

handleAddUsers,
};

return (
Copy link
Contributor

Choose a reason for hiding this comment

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

혹시 Context API를 사용하신 이유를 들을 수 있을까요??

Comment on lines +15 to +25
import {
getAttendanceSessions,
addUser,
getUsers,
getAttendance,
getSessionAttendance,
getRounds,
changeUserAttendance,
getRoundUserAttendance,
getUserList,
} from '../utils/attendanceManage';
Copy link
Contributor

Choose a reason for hiding this comment

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

삭제해주세요~

Comment on lines +62 to +80
export const addRound = async (sessionId, newRound) => {
const paylaod = {
sessionId,
roundDate: newRound.roundDate,
startTime: newRound.startTime,
allowedMinutes: newRound.availableMinutes,
};

try {
const res = await api.post(
`/api/attendance/sessions/${sessionId}/rounds`,
paylaod
);
return res.data;
} catch (err) {
console.error('회차 추가 중 오류 발생', err);
throw err;
}
};
Copy link
Contributor

Choose a reason for hiding this comment

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

ㅋㅋㅋㅋ 수정해주세요!

@Kosw6 Kosw6 merged commit bfb04d6 into main Dec 23, 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.

3 participants