[FE] [FEAT] 출석관리(관리자) 페이지 세션 정보&회차 정보 수정 기능, 세션&회차 삭제 기능, 회차 추가 기능 구현#112
[FE] [FEAT] 출석관리(관리자) 페이지 세션 정보&회차 정보 수정 기능, 세션&회차 삭제 기능, 회차 추가 기능 구현#112
Conversation
WalkthroughAttendance 관리가 prop 기반에서 컨텍스트 기반으로 리팩토링되었습니다. 새로운 AttendanceContext와 훅(useAttendance)을 도입하고, 다중 날짜 선택 RoundDayPicker, RoundModifyModal(이전 RoundSettingModal), SessionModifyModal, ConfirmationToast 등의 컴포넌트가 추가되었으며 CSS 및 의존성(react-day-picker, react-toastify, date-fns 등)이 갱신되었습니다. Changes
Sequence Diagram(s)sequenceDiagram
participant Page as AttendanceManage 페이지
participant Provider as AttendanceProvider (컨텍스트)
participant SessionCard as SessionManagementCard
participant RoundCard as AttendanceManagementCard
participant Modals as Modal 컴포넌트들
Page->>Provider: AttendanceProvider로 래핑
Provider->>Provider: 초기 세션/라운드 데이터 보유
SessionCard->>Provider: useAttendance()로 데이터/액션 요청
Provider-->>SessionCard: sessions, 선택/모달 제어 함수
RoundCard->>Provider: useAttendance()로 selectedRound, participants 요청
Provider-->>RoundCard: selectedRound, handleAttendanceChange
rect rgba(150,255,200,0.2)
Note over SessionCard,Modals: 라운드 추가 흐름
SessionCard->>Modals: RoundDayPicker 오픈
Modals->>Provider: handleAddRounds(newRounds)
Provider->>Provider: 해당 세션에 라운드 추가 후 정렬
Provider-->>RoundCard: 상태 업데이트(리렌더)
end
rect rgba(255,220,220,0.2)
Note over RoundCard,Provider: 출석 변경 흐름
RoundCard->>Provider: handleAttendanceChange(memberId, value)
Provider->>Provider: 현재 선택 라운드의 참가자 출석 상태 업데이트
Provider-->>RoundCard: 변경 반영
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Tip 📝 Customizable high-level summaries are now available in beta!You can now customize how CodeRabbit generates the high-level summary in your pull requests — including its content, structure, tone, and formatting.
Example:
Note: This feature is currently in beta for Pro-tier users, and pricing will be announced later. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
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/SessionSettingCard.jsx (1)
38-53: 검증에 사용한 trim 결과를 실제 생성에도 사용하면 데이터가 더 일관적입니다
handleCreateClick에서title = sessionTitle.trim()으로 공백을 제거해 검증하지만, 실제 생성에는 다시 원본sessionTitle을 전달하고 있어 앞뒤 공백이 그대로 저장될 수 있습니다. 아래처럼title을 넘기면 UI와 저장 값이 일치해집니다.- handleAddSession(sessionTitle, { + handleAddSession(title, { hh: hh.padStart(2, '0'), mm: mm.padStart(2, '0'), ss: ss.padStart(2, '0'), availableTimeMm: availableMinute, });또한 현재 숫자 검증이
parseInt기반이라"10aa"같은 입력도 통과해"10aa:..."형태로 저장될 여지가 있습니다. 나중에 필요하다면 숫자만 허용하는 정규식 검사나input타입을number로 제한하는 것도 고려해 주세요.
🧹 Nitpick comments (11)
frontend/src/components/VerificationModal.module.css (2)
100-121: 중복된 색상 선언 제거
.button클래스에서color속성이 111번 줄과 114번 줄에 중복으로 선언되어 있습니다. 하나로 통합하면 코드의 일관성이 향상됩니다..button { display: inline-flex; padding: 16px 24px; justify-content: center; align-items: center; gap: 10px; border-radius: 8px; width: 100px; height: 42px; color: white; border: none; - - color: #fff; font-family: Pretendard; font-size: 16px; font-style: normal; font-weight: 500; line-height: 100%; /* 16px */ letter-spacing: -0.48px; }
108-109: 버튼 크기 축소로 인한 터치 타겟 크기 확인 필요버튼 크기가 132x48px에서 100x42px로 축소되었습니다. WCAG 접근성 기준상 권장되는 최소 터치 타겟은 44x44px이므로, 현재 42px 높이가 이 기준 이하입니다. 모바일 또는 터치 기기에서의 사용성을 고려하여 크기를 검토해주시기 바랍니다.
frontend/src/components/attendancemanage/SessionModifyModal.jsx (2)
29-51: 유효성 검사 로직이 다른 컴포넌트와 중복됩니다.
SessionSettingCard,RoundModifyModal,SessionModifyModal에 거의 동일한 시간/분/초/분 유효성 검사 로직이 반복되고 있습니다. 나중에 규칙 변경이나 메시지 수정 시 누락 위험이 있으니, 공용 유틸 함수나 훅으로 분리해서 재사용하는 것을 고려해 주세요.
93-139:id중복으로 인한 경미한 접근성 이슈 가능성
id="sessionStartTime"/id="sessionAvailableTime"조합이 다른 컴포넌트(예:SessionSettingCard,RoundModifyModal)에도 동일하게 존재하는데, 페이지/모달가 동시에 열리면 DOM 상에서id가 중복될 수 있습니다. 큰 문제는 아니지만 라벨 연결 및 스크린 리더 측면에서 혼동 여지가 있어, 모달 쪽은sessionStartTime-modal같은 고유한id로 분리하는 것을 추천드립니다.frontend/src/pages/AttendanceManage.jsx (1)
39-57: 모달 렌더링 시editingRound/editingSession에 대한 가드를 추가하면 더 안전합니다.현재는
isRoundModifyModalOpen또는isSessionModifyModalOpen만으로 모달을 렌더링합니다. 컨텍스트 사용 패턴상editingRound/editingSession이 항상 세팅된 상태에서만 열리긴 하지만, 미래에 로직이 바뀌면round.startTime/session.defaultStartTime접근에서 런타임 에러가 날 수 있습니다.아래처럼 편의상 한 줄만 바꿔 두면 방어 코드로 도움이 됩니다.
- {isRoundModifyModalOpen && ( + {isRoundModifyModalOpen && editingRound && ( <RoundModifyModal styles={styles} onClose={closeRoundModifyModal} round={editingRound} onSave={handleRoundChange} onDelete={handleDeleteRound} /> )} - {isSessionModifyModalOpen && ( + {isSessionModifyModalOpen && editingSession && ( <SessionModifyModal styles={styles} onClose={closeSessionModifyModal} session={editingSession} onSave={handleSessionChange} onDelete={handleDeleteSession} /> )}frontend/src/components/attendancemanage/RoundDayPicker.jsx (1)
1-86: 회차 다중 생성 플로우와 날짜 포맷 처리 전반적으로 잘 구성되어 있습니다선택된 세션 여부와 선택된 날짜 배열을 먼저 검증하고, 타임존 오프셋을 보정한 뒤
YYYY-MM-DD문자열로 저장하는 흐름이 명확하고 안정적입니다.handleAddRounds(selectedSessionId, newRounds)로 컨텍스트에 위임하는 구조도 자연스럽습니다.다만 한 세션에서 같은 날짜/시간 조합의 회차를 중복 생성하지 않으려면,
newRounds를 만들 때 기존session.rounds와 중복 여부를 한 번 필터링하는 것도 추후 요구사항에 따라 고려해 볼 수 있습니다.frontend/src/components/attendancemanage/RoundModifyModal.jsx (1)
68-73: 삭제 플로우는 명확하지만 상위 onDelete 구현과의 연동만 한 번 확인해 주세요
handleDeleteClick에서window.confirm으로 사용자 확인 후onDelete(round.id)를 호출하고 모달을 닫는 흐름이 직관적입니다. 상위에서는 이 콜백에서 컨텍스트의handleDeleteRound를 호출할 것으로 보이는데, 비동기 API 연동 시에는 다음을 같이 고려해 볼 수 있습니다.
- 삭제 요청 중 중복 클릭을 막기 위해 삭제 버튼 비활성화 또는 로딩 상태 표시
- 삭제 실패 시 모달을 닫지 않고 오류 안내 후 상태 롤백
또, 이 모달이 렌더링될 때 항상
round와onDelete가 정의되어 있다는 전제가 있으므로, 상위에서editingRound가 설정된 경우에만 모달을 띄우는지 한 번만 점검해 주세요.Also applies to: 135-148
frontend/src/components/attendancemanage/AttendanceManagementCard.jsx (1)
3-7: 컨텍스트 기반 출석 변경 흐름이 단순하고 일관성 있습니다
selectedRound,participants,handleAttendanceChange를 컨텍스트에서 바로 가져와 카드 자체는 UI 렌더링에만 집중하도록 한 구조가 좋습니다. 회차 미선택 시와 참가자 없음 시 메시지도 명확합니다.향후 출석 상태 종류(예: 지각, 조퇴 등)가 늘어날 가능성이 있다면,
<option>리스트를 상수로 분리하거나 컨텍스트/상위에서 내려받도록 해 두면 상태 종류가 바뀌었을 때 여러 컴포넌트를 동시에 수정하지 않아도 되어 유지보수가 더 쉬워질 것 같습니다.Also applies to: 41-44
frontend/src/contexts/AttendanceContext.jsx (2)
143-175: 세션/회차 삭제 후 선택 상태는 리셋되지만 편집 상태까지 초기화할지 결정 필요
handleDeleteSession에서selectedSessionId,selectedRound를null로 리셋하고,handleDeleteRound에서도selectedRound를 초기화하는 부분은 일관성이 좋습니다.다만 모달이 열린 상태에서 삭제가 일어날 수 있다면(예: 세션 수정 모달이 열려 있는데 세션 삭제 버튼을 누르는 경우),
editingSession,editingRound같은 편집용 상태도 함께 초기화해 두면 이후 재사용 시 헷갈릴 여지가 더 줄어듭니다. 현재 호출 플로우 상에서 항상 모달을 먼저 닫고 삭제를 호출한다면 필수는 아니지만, 방어적으로 초기화해 두는 것도 한 가지 선택지입니다.
224-230: useAttendance에서 에러 메시지를 명시하면 디버깅에 더 유리합니다컨텍스트가 설정되지 않은 경우
throw new Error();로만 던지면 스택 트레이스에는 원인이 드러나지 않아 디버깅이 어렵습니다. 예를 들어 다음처럼 메시지를 추가해 두면 어디서 문제인지 바로 파악하기 좋습니다.if (context === null) { throw new Error('useAttendance는 AttendanceProvider 내에서만 사용할 수 있습니다.'); }개발자 경험 측면에서 작은 차이지만 도움이 됩니다.
frontend/src/components/attendancemanage/SessionManagementCard.jsx (1)
57-70: LGTM! 접근성 개선 제안세션 수정 버튼의 검증 로직과 상태 업데이트 순서가 올바릅니다.
접근성을 위해
aria-label추가를 고려하세요:<button type="button" className={styles.menuButton} + aria-label="세션 수정" onClick={() => {
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (2)
frontend/package-lock.jsonis excluded by!**/package-lock.jsonfrontend/src/assets/calendar-add-icon.svgis excluded by!**/*.svg
📒 Files selected for processing (12)
frontend/package.json(1 hunks)frontend/src/components/VerificationModal.module.css(5 hunks)frontend/src/components/attendancemanage/AttendanceManagementCard.jsx(2 hunks)frontend/src/components/attendancemanage/RoundDayPicker.jsx(1 hunks)frontend/src/components/attendancemanage/RoundModifyModal.jsx(5 hunks)frontend/src/components/attendancemanage/SessionManagementCard.jsx(4 hunks)frontend/src/components/attendancemanage/SessionManagementCard.module.css(2 hunks)frontend/src/components/attendancemanage/SessionModifyModal.jsx(1 hunks)frontend/src/components/attendancemanage/SessionSettingCard.jsx(2 hunks)frontend/src/contexts/AttendanceContext.jsx(1 hunks)frontend/src/pages/AttendanceManage.jsx(1 hunks)frontend/src/pages/AttendanceManage.module.css(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (8)
frontend/src/components/attendancemanage/SessionModifyModal.jsx (2)
frontend/src/components/attendancemanage/RoundModifyModal.jsx (6)
hh(22-22)mm(23-23)ss(24-24)isFormValid(29-47)handleModifyClick(49-67)handleDeleteClick(68-73)frontend/src/components/attendancemanage/SessionSettingCard.jsx (4)
hh(9-9)mm(10-10)ss(11-11)isFormValid(14-36)
frontend/src/pages/AttendanceManage.jsx (7)
frontend/src/components/attendancemanage/AttendanceManagementCard.jsx (2)
useAttendance(6-7)AttendanceManagementCard(5-66)frontend/src/components/attendancemanage/RoundDayPicker.jsx (2)
useAttendance(10-11)RoundDayPicker(9-84)frontend/src/components/attendancemanage/SessionManagementCard.jsx (2)
useAttendance(6-17)SessionManagementCard(5-122)frontend/src/components/attendancemanage/SessionSettingCard.jsx (2)
useAttendance(6-6)SessionSettingCard(5-129)frontend/src/contexts/AttendanceContext.jsx (15)
useAttendance(225-231)useAttendance(225-231)isRoundModifyModalOpen(56-56)closeRoundModifyModal(101-101)editingRound(55-55)handleRoundChange(77-87)handleDeleteRound(157-175)isSessionModifyModalOpen(58-58)closeSessionModifyModal(104-104)editingSession(57-57)handleSessionChange(89-98)handleDeleteSession(143-156)isAddRoundsModalOpen(60-60)AttendanceProvider(51-222)AttendanceProvider(51-222)frontend/src/components/attendancemanage/RoundModifyModal.jsx (1)
RoundModifyModal(4-155)frontend/src/components/attendancemanage/SessionModifyModal.jsx (1)
SessionModifyModal(4-159)
frontend/src/components/attendancemanage/SessionSettingCard.jsx (1)
frontend/src/contexts/AttendanceContext.jsx (3)
useAttendance(225-231)useAttendance(225-231)handleAddSession(110-132)
frontend/src/components/attendancemanage/SessionManagementCard.jsx (2)
frontend/src/contexts/AttendanceContext.jsx (6)
useAttendance(225-231)useAttendance(225-231)openAddRoundsModal(106-106)openSessionModifyModal(103-103)selectedRound(54-54)openRoundModifyModal(100-100)frontend/src/pages/AttendanceManage.jsx (1)
useAttendance(15-27)
frontend/src/components/attendancemanage/RoundDayPicker.jsx (1)
frontend/src/contexts/AttendanceContext.jsx (6)
useAttendance(225-231)useAttendance(225-231)sessions(52-52)selectedSessionId(53-53)handleAddRounds(133-141)closeAddRoundsModal(108-108)
frontend/src/contexts/AttendanceContext.jsx (6)
frontend/src/pages/OAuthSuccess.jsx (1)
p(5-5)frontend/src/components/attendancemanage/SessionSettingCard.jsx (2)
sessionTitle(8-8)useAttendance(6-6)frontend/src/components/attendancemanage/AttendanceManagementCard.jsx (1)
useAttendance(6-7)frontend/src/components/attendancemanage/RoundDayPicker.jsx (1)
useAttendance(10-11)frontend/src/components/attendancemanage/SessionManagementCard.jsx (1)
useAttendance(6-17)frontend/src/pages/AttendanceManage.jsx (1)
useAttendance(15-27)
frontend/src/components/attendancemanage/AttendanceManagementCard.jsx (1)
frontend/src/contexts/AttendanceContext.jsx (3)
useAttendance(225-231)useAttendance(225-231)handleAttendanceChange(62-75)
frontend/src/components/attendancemanage/RoundModifyModal.jsx (1)
frontend/src/components/attendancemanage/SessionModifyModal.jsx (1)
handleModifyClick(53-71)
🔇 Additional comments (11)
frontend/src/components/VerificationModal.module.css (3)
137-139: 삭제/초기화 작업을 위한 색상 변경 - 승인
resetPasswordButton배경색을 파란색(#1d80f4)에서 빨간색(#ff2c4c)으로 변경한 것은 의미론적으로 적절합니다. 빨간색은 파괴적인 작업(삭제, 초기화)을 시각적으로 강조하는 UI 관례에 부합합니다.
179-183: 새로운 버튼 그룹 유틸리티 클래스 추가 - 승인
.modifyButtonGroup클래스가 새로 추가되어 여러 버튼을 일관되게 배치할 수 있게 되었습니다. Flex 레이아웃과 적절한 여백 설정으로 회차 수정/삭제 모달의 버튼 그룹에 사용되는 것으로 보입니다.여러 관련 모달 컴포넌트(RoundModifyModal, SessionModifyModal 등)에서 이 클래스가 일관되게 사용되고 있는지 확인해주시기 바랍니다.
14-20: 모달 레이아웃 및 간격 조정 - 승인모달 패딩을 56px에서 30px 50px로, 헤더 하단 마진을 52px에서 30px로 조정하여 새로운 디자인 시스템에 맞춘 여백 설정입니다. 이는 세션/회차 관리 UI 전체의 일관된 디자인 개선에 기여합니다.
frontend/src/components/attendancemanage/SessionManagementCard.module.css (1)
8-11: 세션 선택 영역 레이아웃 및 선택 행 스타일이 일관성 있게 개선되었습니다.
.selectGroup으로 세션 셀렉트와 메뉴 버튼을 가로 정렬하고,.selectedRound에cursor: pointer를 추가해 클릭 가능한 행이라는 점이 명확해졌습니다.SessionManagementCard.jsx에서의 사용과도 잘 맞습니다.Also applies to: 88-90
frontend/src/pages/AttendanceManage.module.css (1)
60-77: 공통 헤더 및 아이콘 버튼 스타일이 잘 추출되었습니다.
.header와.iconButton을 페이지 레벨에서 공통으로 정의해서SessionSettingCard,SessionManagementCard,AttendanceManagementCard의 헤더 레이아웃이 일관되게 맞춰진 점이 좋습니다. 아이콘 크기를 20px로 고정한 것도 레이아웃 안정성에 도움이 됩니다.frontend/src/pages/AttendanceManage.jsx (1)
1-27: 페이지 레벨에서 컨텍스트로 상태를 집약한 구조가 깔끔합니다.
AttendanceProvider로 페이지 전체를 감싸고, 실제 뷰는AttendanceContent에서useAttendance로만 상태를 읽도록 분리한 구조가 명확합니다. 세션 생성/관리/출석 카드가 모두 컨텍스트 하나를 공유하므로, 이후 API 연동 시에도 데이터 흐름을 따라가기가 쉬워질 것 같습니다.Also applies to: 62-71
frontend/src/components/attendancemanage/SessionSettingCard.jsx (1)
3-7: AttendanceContext 연동으로 props 의존성이 줄어든 점은 좋습니다이제 세션 추가 로직을 컨텍스트에서 직접 가져와 상위 컴포넌트가 훨씬 단순해집니다. 이 컴포넌트가 항상
AttendanceProvider내부에서만 렌더링되도록 페이지 구성만 한 번 확인해 주세요.frontend/src/components/attendancemanage/RoundModifyModal.jsx (1)
4-21: 시간 파싱을 객체 기반으로 바꾼 점이 가독성과 안정성에 도움이 됩니다
parseTime에서 항상{ h, m, s }형태의 객체를 반환하고,round.startTime이 falsy여도'00:00:00'으로 안전하게 떨어지도록 한 구조가 좋습니다. 디폴트 값까지 구조 분해 할당으로 명시해 둬서 초기 렌더 시 에러 가능성도 줄었습니다.frontend/src/components/attendancemanage/SessionManagementCard.jsx (3)
1-3: LGTM!컨텍스트 기반 아키텍처로의 전환을 위한 임포트가 적절하게 구성되어 있습니다.
5-17: LGTM!컨텍스트 기반 상태 관리로의 전환이 잘 이루어졌습니다. 모든 필요한 상태와 액션이 적절하게 구조 분해되어 있습니다.
83-116: LGTM!회차 테이블의 조건부 렌더링과 선택 로직이 잘 구현되어 있습니다. 데이터가 없을 때의 폴백 메시지도 적절합니다.
frontend/package.json
Outdated
| "date-fns": "^4.1.0", | ||
| "immer": "^10.2.0", | ||
| "pretendard": "^1.3.9", | ||
| "react": "^19.1.1", | ||
| "react-day-picker": "^9.11.1", |
There was a problem hiding this comment.
🧩 Analysis chain
새로 추가된 의존성 구성이 사용처와 잘 맞습니다.
react-day-picker는 RoundDayPicker에서 바로 사용되고 있고, date-fns도 날짜 처리 유틸로 무난한 선택이라 방향은 좋아 보입니다. 실제로 쓰이지 않는 의존성이 없는지와 버전 호환성만 한 번 점검해 두면 좋겠습니다.
다음 스크립트로 사용처/중복 여부를 빠르게 확인해 볼 수 있습니다:
🏁 Script executed:
#!/bin/bash
# date-fns / react-day-picker 실제 사용 위치 확인
rg "date-fns" frontend/src -n || echo "date-fns 사용처 없음"
rg "react-day-picker" frontend/src -n || echo "react-day-picker 사용처 없음"Length of output: 222
더 광범위하게 확인하겠습니다. date-fns가 테스트나 다른 디렉토리에서 사용되는지, 그리고 react-day-picker 내부 의존성으로 필요한지 검증하겠습니다.
이 출력을 기반으로 최종 검증을 마치겠습니다.
🏁 Script executed:
#!/bin/bash
# 전체 repository에서 date-fns 검색 (frontend 폴더만)
echo "=== frontend 전체에서 date-fns 검색 ==="
rg "date-fns" frontend -n --type json || echo "JSON 파일에서도 없음"
# react-day-picker의 실제 사용 위치 다시 확인
echo -e "\n=== react-day-picker 상세 검색 ==="
rg "react-day-picker" frontend -n -B2 -A2
# package.json에서 react-day-picker 의존성 확인
echo -e "\n=== package.json에서 의존성 확인 ==="
cat frontend/package.json | grep -A 50 '"dependencies"' | grep -E '(date-fns|react-day-picker)'Length of output: 262
이전 스크립트에 `` 태그가 있어서 저장소가 복제되지 않았습니다. 다시 올바른 설정으로 검증하겠습니다.
🏁 Script executed:
#!/bin/bash
# 저장소 경로 확인
echo "=== 현재 디렉토리 ==="
pwd
ls -la | head -20
# 전체 repository에서 date-fns 검색
echo -e "\n=== date-fns 사용 위치 전체 검색 ==="
rg "date-fns" . -n --type=js --type=jsx --type=ts --type=tsx 2>/dev/null || echo "date-fns 사용처 없음"
# react-day-picker 사용 위치 확인
echo -e "\n=== react-day-picker 사용 위치 ==="
rg "react-day-picker" . -n --type=js --type=jsx --type=ts --type=tsx -B1 -A1
# RoundDayPicker.jsx에서 date-fns 관련 코드 확인
echo -e "\n=== RoundDayPicker.jsx 전체 내용 확인 ==="
find . -name "RoundDayPicker.jsx" -exec cat {} \;Length of output: 3125
react-day-picker이 date-fns에 의존하는지 확인하겠습니다.
🏁 Script executed:
#!/bin/bash
# react-day-picker package.json에서 dependencies 확인
echo "=== react-day-picker 의존성 확인 ==="
find . -path "*/node_modules/react-day-picker/package.json" -exec cat {} \; 2>/dev/null | grep -A 20 '"dependencies"' || echo "node_modules에서 react-day-picker 찾을 수 없음"
# package.json 파일 찾기
echo -e "\n=== package.json 찾기 ==="
find . -name "package.json" -type f -not -path "*/node_modules/*" | head -5Length of output: 120
react-day-picker의 의존성을 확인하겠습니다.
🌐 Web query:
react-day-picker 9.11.1 dependencies date-fns required
💡 Result:
Yes — in v9 (including 9.11.1) react-day-picker includes date-fns as a direct dependency (not a peer dependency), so date-fns is required by the package. The v9 changelog states date-fns was moved into dependencies, and package listings for 9.11.1 show date-fns (plus @date-fns/tz and date-fns-jalali) as dependencies. [1][2][3]
Sources:
[1] react-day-picker changelog (v9: "Moved date-fns from peer dependencies to dependencies").
[2] jsDocs/npm package listing for react-day-picker 9.11.1 (dependencies include date-fns, @date-fns/tz, date-fns-jalali).
[3] Maven/NPM metadata for react-day-picker 9.11.1 (compile dependencies include date-fns and related packages).
date-fns는 react-day-picker의 내부 의존성이므로 직접 추가할 필요가 없습니다.
react-day-picker v9.11.1은 date-fns를 직접 의존성으로 포함하고 있습니다. 코드 검증 결과 date-fns가 어디서도 직접 import되거나 사용되지 않음을 확인했습니다. RoundDayPicker.jsx는 native JavaScript Date API를 사용하고 있습니다.
제거 대상: frontend/package.json의 "date-fns": "^4.1.0" (14번 줄)
react-day-picker가 자동으로 관리하는 의존성이므로 직접 추가하면 불필요한 중복 발생react-day-picker와의 버전 불일치 가능성
🤖 Prompt for AI Agents
In frontend/package.json around lines 14 to 18, remove the direct dependency
entry for "date-fns": "^4.1.0" because react-day-picker already bundles/depends
on it and there are no direct imports; after removing the line, run your package
manager (npm or yarn/pnpm) to update node_modules and the lockfile
(package-lock.json/yarn.lock/pnpm-lock.yaml) and verify the app builds/tests
pass to ensure no missing imports remain.
gxuoo
left a comment
There was a problem hiding this comment.
기능 위주로 리뷰를 드렸습니다. 그리고 이전에도 말씀드린 것 같긴 한데, 현재 출석관리(관리자) 페이지 내부에서 왼쪽 공백과 오른쪽 공백의 비율이 불균형하다고 생각이 들어서, 피그마 디자인을 그대로 따라하는 것도 좋지만 양쪽 공백은 어느 정도 맞춰서 페이지 내부를 최대한 활용하는 방식으로 디자인을 조금 고쳐보는 건 어떤지 한 번 생각해보시는 것도 좋을 것 같습니다.
고생하셨습니다! 👍👍
| }; | ||
| }); | ||
|
|
||
| handleAddRounds(selectedSessionId, newRounds); |
There was a problem hiding this comment.
현재는 먼저 선택된 날짜 순서로 세션이 등록되고 있어서 세션이 추가 됐을 때, 날짜 순서로 정렬이 된 후에 추가되면 좋을 거 같아요.
There was a problem hiding this comment.
회차를 추가하기 전에 정렬하면 추가한 뒤에는 다른 회차들까지 봤을 때 정렬되지 않을 수 있으니까 회차 정보가 수정되거나 회차가 추가되었을때 정렬되도록 구현했습니다!
| if (window.confirm('이 회차를 정말로 삭제하시겠습니까?')) { | ||
| onDelete(round.id); | ||
| onClose(); | ||
| } |
There was a problem hiding this comment.
react-toastify 라이브러리에 대해 검색해보시는 걸 추천드립니다! UX적으로 alert나 confirm은 그렇게 좋지 않다고 개인적으로 생각하고 있거든요.
There was a problem hiding this comment.
alert는 react-toastify 사용하고 confirm은 react-confirm-alert 사용하는 게 좋아보입니다!
8aaf706 to
dd09635
Compare
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (2)
frontend/src/contexts/AttendanceContext.jsx (2)
8-49: 세션location필드 스키마를 새로 생성되는 세션과 일관되게 맞추는 게 좋겠습니다현재 목데이터의
sessionData에는location필드가 있지만,handleAddSession에서 생성하는 새 세션에는location이 없습니다. 지금은location을 어디서도 사용하지 않아 기능상 문제는 없지만, 이후 위치 기반 기능을 붙이면 새 세션만location이undefined인 상태가 되어 예외 처리가 필요해질 수 있습니다.
location이 불필요하다면 목데이터에서 제거하거나- 모든 세션에 위치가 있어야 한다면
handleAddSession에서 기본값(null혹은 기본 좌표)까지 포함해 생성하는 쪽을 추천합니다.이 부분은 이전 리뷰에서도 한 번 언급되었던 내용이라, 이번에 정리해 두면 좋겠습니다.
Also applies to: 118-136
77-95:handleRoundChange/handleDeleteRound의selectedSessionId클로저 의존성은 한 번 더 점검이 필요해 보입니다두 핸들러 모두 클로저에 캡처된
selectedSessionId를 기준으로 세션을 찾고 있습니다. Round/Session 수정/삭제 모달이 열린 상태에서 세션 선택을 바꿀 수 있는 UI라면:
- 수정: 모달은 A 세션의 회차를 보여주고 있는데,
selectedSessionId가 B로 바뀌면 B 세션의 회차를 수정하거나, 해당 ID가 없으면 아무 것도 안 하는 상황이 생길 수 있고- 삭제: 마찬가지로 의도와 다른 세션을 대상으로 동작하거나, 조용히 실패할 수 있습니다.
UI 쪽에서 모달이 열려 있는 동안 세션 변경을 막고 있다면 현재 구조도 괜찮지만, 그렇지 않다면 아래 중 하나를 고려해 볼 만합니다.
handleRoundChange(sessionId, updateRoundData)/handleDeleteRound(sessionId, roundId)처럼 세션 ID를 인자로 받도록 변경하고, RoundModifyModal 쪽에서 모달을 열 때의 세션 ID를 함께 넘기거나- 세션 선택 핸들러에서
isRoundModifyModalOpen/isSessionModifyModalOpen이 true면 early-return 하는 가드를 넣어, 모달이 열린 상태에서 세션이 바뀌지 않게 하기이 이슈도 이전 리뷰에서 이미 한 차례 언급된 부분이라, 현재 UI 플로우 기준으로 실제로 문제가 없는지 한 번만 다시 확인해 보시면 좋겠습니다.
Also applies to: 171-185
🧹 Nitpick comments (15)
frontend/src/components/VerificationModal.module.css (2)
184-185: 비어있는 스타일 규칙 제거.
.modifyButtonGroup button:disabled규칙이 정의되지 않아 기능하지 않습니다. 이 규칙이 필요하지 않다면 제거하거나, 스타일이 필요한 경우 구현하세요.-.modifyButtonGroup button:disabled { -}
111-114:.button클래스에서 중복된 color 속성 제거.
color: white;속성이 줄 111과 114에서 중복 정의되어 있습니다. 다음과 같이 정리하세요:.button { display: inline-flex; padding: 16px 24px; justify-content: center; align-items: center; gap: 10px; border-radius: 8px; width: 100px; height: 42px; color: white; border: none; - color: #fff; font-family: Pretendard;frontend/src/contexts/AttendanceContext.jsx (3)
118-140: 새 세션 생성 시 입력값 사용을 조금 더 일관되게 다듬을 수 있습니다
SessionSettingCard에서 이미title,availableMinute로 trim/parse를 한 뒤, 여기에서는 원본sessionTitle과availableTimeMm를 다시 받아 사용하는 형태입니다. 큰 문제는 아니지만:
handleAddSession호출 시sessionTitle대신 미리 trim된title을 넘기면, 앞뒤 공백이 포함되지 않아 더 일관적이고availableTimeMm는 현재 숫자가 들어오는데parseInt를 한 번 더 하는 구조라, 인터페이스를 "분 단위 숫자"로 고정하고 여기서는 그냥 그대로 대입해도 됩니다 (defaultAvailableMinutes: availableMinute형태).또, 주석 처리된 기본 회차 템플릿은 실제로 사용하지 않을 계획이라면 정리해 두는 편이 코드 가독성에 도움이 될 것 같습니다.
157-170: 세션 삭제 후 selection 초기화 조건을 조금 더 좁히는 것도 고려해 볼 만합니다현재 구현은 어떤 세션을 삭제하더라도 항상
selectedSessionId와selectedRound를null로 초기화합니다. 보통은 "선택된 세션을 삭제"하는 플로우일 것 같지만, 추후 UI에서 선택과 무관하게 다른 세션을 바로 삭제할 수 있게 되면, 사용자가 보고 있던 세션이 아닌데도 selection이 풀리는 UX가 나올 수 있습니다.예를 들어 아래처럼 삭제 대상이 현재 선택된 세션일 때만 selection을 초기화하는 식으로 좁히는 것도 한 가지 방법입니다.
if (selectedSessionId === sessionId) { setSelectedSessionId(null); setSelectedRound(null); }현재 플로우가 어떤지에 따라 선택적으로 고려해 보셔도 좋겠습니다.
239-244:useAttendance에서 에러 메시지를 명시하면 디버깅에 더 도움이 됩니다지금은
context === null일 때new Error()만 던지고 있어서, 실제 런타임 에러 로그만 보고는 원인을 파악하기가 어렵습니다. 예를 들어 다음처럼 메시지를 넣어두면, Provider로 감싸지 않고 훅을 썼을 때 바로 원인을 알 수 있어 좋습니다.if (context === null) { throw new Error('AttendanceProvider로 감싸지 않은 컴포넌트에서 useAttendance를 호출했습니다.'); }frontend/src/components/attendancemanage/ConfirmationToast.module.css (1)
1-46: CSS 구조는 전반적으로 깔끔하고, flex 컨테이너의justify-items만 정리하면 좋겠습니다토스트 레이아웃과 버튼 스타일이 단순하고 읽기 좋아서 유지보수하기 편해 보입니다. 한 가지 정도만 정리하면 좋을 것 같은 부분은:
.toastContainer가display: flex인데,justify-items는 CSS Grid용 속성이라 flex 컨테이너에서는 효과가 없습니다. 필요 없다면 제거하거나, 의도하신 게 메인 축 정렬이라면justify-content로 바꾸는 쪽을 추천드립니다.그 외 버튼/hover 색상 정의는 일관되고 명확합니다.
frontend/src/components/attendancemanage/ConfirmationToast.jsx (1)
3-25: 확인 토스트 컴포넌트 구조가 명확하고, 비동기 처리까지 고려한 소소한 개선 여지가 있습니다현재 구현은 단순·명료하고,
ConfirmationToast를 다른 삭제/확인 플로우에도 쉽게 재사용할 수 있을 것 같아 좋습니다.만약
onConfirm가 비동기 함수일 가능성이 있다면, 아래처럼Promise완료 이후에 토스트를 닫도록 하면 로딩/에러 처리 시에도 동작이 더 예측 가능해집니다.- onClick={() => { - onConfirm(); - closeToast(); - }} + onClick={async () => { + await Promise.resolve(onConfirm()); + closeToast(); + }}동기 콜백만 쓴다면 지금 구현 그대로도 충분합니다.
frontend/src/pages/AttendanceManage.jsx (2)
16-60: 컨텍스트 기반으로 카드/모달을 묶어낸 구조가 이해하기 쉽습니다
AttendanceContent에서 컨텍스트 값들을 한 번에 꺼내서 카드 3개와 모달들을 모두 연결한 구조라, 출석 관리 화면의 데이터 플로우를 따라가기가 훨씬 수월해졌습니다.한 가지 정도만 체크해 보면 좋을 부분은, 모달 렌더링이
isRoundModifyModalOpen/isSessionModifyModalOpen플래그만 보고 있어서editingRound나editingSession이 우연히null인 상태로 모달이 열릴 수 있는지 여부입니다. 그런 경로가 있다면:
- 플래그를 켜기 전에 항상 editing 객체를 먼저 세팅하거나
- 모달 쪽에서
round/session이 없으면 바로onClose()를 호출하는 식의 방어 로직을 추가하는 것도 고려해 볼 만합니다.
64-80: 페이지 레벨의ToastContainer위치는 적절해 보이며, 중복 여부만 한 번 확인해 주세요
AttendanceManage내부에ToastContainer를 두어서 이 페이지에서 발생하는 토스트를 자체적으로 관리하는 구조는 직관적입니다. 다만 앱 루트(App 등)에 이미 다른ToastContainer가 있다면 토스트가 중복 렌더링될 수 있으니:
- 전역에 하나만 두고 재사용할지,
- 특정 페이지 전용으로 둘지
정책을 한 번 정리해 두시면 좋겠습니다.
frontend/src/components/attendancemanage/SessionSettingCard.jsx (1)
38-53: 세션 생성 시 이미 정제해 둔 값들을 재사용하면 인터페이스가 더 일관될 것 같습니다
handleCreateClick에서:
title = sessionTitle.trim()과availableMinute = parseInt(availableTimeMm, 10)으로 검증용 값을 따로 만들고 있는데, 실제
handleAddSession호출 시에는 다시 원본sessionTitle과availableTimeMm를 넘기고 있습니다.크게 문제 되진 않지만, 아래처럼 정제된 값을 그대로 넘기면 조금 더 예측 가능한 인터페이스가 됩니다.
// 예시 if (!isFormValid(title, hour, minute, second, availableMinute)) return; handleAddSession(title, { hh: hh.padStart(2, '0'), mm: mm.padStart(2, '0'), ss: ss.padStart(2, '0'), // 숫자를 넘기고 싶다면 이름도 맞춰주는 것도 한 방법입니다. availableTimeMm: availableMinute, });혹은 context 쪽에서 아예
"분 단위 숫자"를 받는 형태(defaultAvailableMinutes: availableMinute)로 계약을 명확히 해 두는 것도 좋습니다.frontend/src/components/attendancemanage/SessionModifyModal.jsx (2)
53-91: 세션 시간/인정 시간 검증 및 저장 로직 잘 정리되어 있습니다
parseInt로 숫자 변환 후isFormValid에서 범위 체크하고,onSave에defaultAvailableMinutes: availableMinute처럼 숫자 값을 넘기도록 바꾼 부분이 이전 타입 불일치 가능성을 잘 해소하고 있습니다.
다만parseTime/isFormValid/HH:MM:SS포맷팅 로직이RoundModifyModal과 거의 동일하니, 향후에는 공통 유틸/훅으로 분리하면 유지보수성이 더 좋아질 것 같습니다.
33-51: ESC + 토스트 상태 관리 흐름이 일관적이며, UX 측면에서 약간의 여지가 있습니다
activeToastId로 토스트 활성 상태를 추적하면서 ESC 입력 시 토스트 → 모달 순으로 닫고, 토스트가 떠 있는 동안 상단 닫기/삭제/완료 버튼을 비활성화한 구조가 잘 짜여 있습니다.
추가로, 토스트가 없을 때 오버레이 영역 클릭 시onClose를 호출하는 패턴(다른 모달들과 통일)까지 들어가면 사용자가 모달을 닫는 경로가 더 자연스러울 것 같습니다.Also applies to: 96-116, 118-195
frontend/src/components/attendancemanage/RoundDayPicker.jsx (1)
16-29: ESC 처리와 버튼 UX는 기본은 갖췄고, 버튼 비활성화도 고려해볼 수 있습니다ESC 키로 모달을 닫도록
useEffect에서 전역 키다운 리스너를 등록/해제하는 부분이 깔끔합니다.
다만 현재는 날짜를 하나도 선택하지 않아도 "추가" 버튼이 활성이고, 클릭 시에야 alert가 뜨므로,selectedRounds.length === 0일 때 버튼을 비활성화하는 정도의 UX 개선을 고려해볼 수 있습니다.Also applies to: 78-95
frontend/src/components/attendancemanage/RoundModifyModal.jsx (1)
33-51: 회차 삭제 플로우와 ESC/토스트 제어가 잘 연결되어 있습니다삭제 시
ConfirmationToast를 띄우고, 확인 시onDelete(round.id)후onClose()를 호출하며,activeToastId를 통해 ESC 입력 시 토스트 → 모달 순으로 닫는 흐름이 자연스럽습니다. 토스트가 떠 있는 동안 닫기/삭제/완료 버튼을 비활성화한 것도 이중 액션을 막는 데 도움이 됩니다.
이 컴포넌트 역시parseTime/검증/토스트 + ESC 패턴이SessionModifyModal과 거의 동일하므로, 장기적으로는 공통 훅/헬퍼로 추출하는 것을 고려해볼 만합니다.Also applies to: 92-112, 119-191
frontend/src/components/attendancemanage/AttendanceManagementCard.jsx (1)
3-8: 컨텍스트 기반 출석 변경 플로우가 단순하고 명확합니다
useAttendance에서selectedRound,participants,handleAttendanceChange를 받아와 사용하고, 선택된 회차가 없을 때/참가자가 없을 때 각각 다른 안내 문구를 보여주는 분기 구조가 직관적입니다. 셀렉트에서 바로handleAttendanceChange(memberId, e.target.value)를 호출하는 부분도 컨텍스트 구현(handleAttendanceChange)과 잘 맞습니다.
한 가지 전제만 공유하자면,participants가 항상 배열(기본값[])로 초기화되어 있어야participants.length접근에서 런타임 에러가 나지 않습니다. 컨텍스트 쪽에서 이미 그렇게 처리되어 있다면 현재 구현으로 충분해 보입니다.Also applies to: 25-61
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (2)
frontend/package-lock.jsonis excluded by!**/package-lock.jsonfrontend/src/assets/calendar-add-icon.svgis excluded by!**/*.svg
📒 Files selected for processing (14)
frontend/package.json(1 hunks)frontend/src/components/VerificationModal.module.css(5 hunks)frontend/src/components/attendancemanage/AttendanceManagementCard.jsx(2 hunks)frontend/src/components/attendancemanage/ConfirmationToast.jsx(1 hunks)frontend/src/components/attendancemanage/ConfirmationToast.module.css(1 hunks)frontend/src/components/attendancemanage/RoundDayPicker.jsx(1 hunks)frontend/src/components/attendancemanage/RoundModifyModal.jsx(7 hunks)frontend/src/components/attendancemanage/SessionManagementCard.jsx(4 hunks)frontend/src/components/attendancemanage/SessionManagementCard.module.css(2 hunks)frontend/src/components/attendancemanage/SessionModifyModal.jsx(1 hunks)frontend/src/components/attendancemanage/SessionSettingCard.jsx(2 hunks)frontend/src/contexts/AttendanceContext.jsx(1 hunks)frontend/src/pages/AttendanceManage.jsx(1 hunks)frontend/src/pages/AttendanceManage.module.css(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- frontend/src/pages/AttendanceManage.module.css
- frontend/src/components/attendancemanage/SessionManagementCard.module.css
- frontend/package.json
🧰 Additional context used
🧬 Code graph analysis (8)
frontend/src/components/attendancemanage/AttendanceManagementCard.jsx (1)
frontend/src/contexts/AttendanceContext.jsx (3)
useAttendance(239-245)useAttendance(239-245)handleAttendanceChange(62-75)
frontend/src/components/attendancemanage/RoundModifyModal.jsx (2)
frontend/src/components/attendancemanage/SessionModifyModal.jsx (5)
activeToastId(13-13)parseTime(16-23)parseTime(24-24)handleDeleteClick(96-116)handleModifyClick(77-94)frontend/src/components/attendancemanage/ConfirmationToast.jsx (1)
ConfirmationToast(3-26)
frontend/src/pages/AttendanceManage.jsx (7)
frontend/src/components/attendancemanage/RoundDayPicker.jsx (2)
useAttendance(10-11)RoundDayPicker(9-99)frontend/src/contexts/AttendanceContext.jsx (15)
useAttendance(239-245)useAttendance(239-245)isRoundModifyModalOpen(56-56)closeRoundModifyModal(109-109)editingRound(55-55)handleRoundChange(77-95)handleDeleteRound(171-189)isSessionModifyModalOpen(58-58)closeSessionModifyModal(112-112)editingSession(57-57)handleSessionChange(97-106)handleDeleteSession(157-170)isAddRoundsModalOpen(60-60)AttendanceProvider(51-236)AttendanceProvider(51-236)frontend/src/components/attendancemanage/SessionManagementCard.jsx (2)
useAttendance(6-17)SessionManagementCard(5-126)frontend/src/components/attendancemanage/AttendanceManagementCard.jsx (2)
useAttendance(6-7)AttendanceManagementCard(5-66)frontend/src/components/attendancemanage/SessionSettingCard.jsx (2)
useAttendance(6-6)SessionSettingCard(5-129)frontend/src/components/attendancemanage/RoundModifyModal.jsx (1)
RoundModifyModal(6-197)frontend/src/components/attendancemanage/SessionModifyModal.jsx (1)
SessionModifyModal(6-201)
frontend/src/contexts/AttendanceContext.jsx (5)
frontend/src/components/attendancemanage/SessionSettingCard.jsx (2)
sessionTitle(8-8)useAttendance(6-6)frontend/src/pages/AttendanceManage.jsx (1)
useAttendance(17-29)frontend/src/components/attendancemanage/RoundDayPicker.jsx (1)
useAttendance(10-11)frontend/src/components/attendancemanage/SessionManagementCard.jsx (1)
useAttendance(6-17)frontend/src/components/attendancemanage/AttendanceManagementCard.jsx (1)
useAttendance(6-7)
frontend/src/components/attendancemanage/SessionManagementCard.jsx (2)
frontend/src/pages/AttendanceManage.jsx (1)
useAttendance(17-29)frontend/src/contexts/AttendanceContext.jsx (6)
useAttendance(239-245)useAttendance(239-245)openAddRoundsModal(114-114)openSessionModifyModal(111-111)selectedRound(54-54)openRoundModifyModal(108-108)
frontend/src/components/attendancemanage/SessionModifyModal.jsx (2)
frontend/src/components/attendancemanage/RoundModifyModal.jsx (9)
activeToastId(13-13)parseTime(16-23)parseTime(24-24)hh(26-26)mm(27-27)ss(28-28)isFormValid(53-71)handleModifyClick(73-90)handleDeleteClick(92-112)frontend/src/components/attendancemanage/ConfirmationToast.jsx (1)
ConfirmationToast(3-26)
frontend/src/components/attendancemanage/SessionSettingCard.jsx (1)
frontend/src/contexts/AttendanceContext.jsx (3)
useAttendance(239-245)useAttendance(239-245)handleAddSession(118-140)
frontend/src/components/attendancemanage/RoundDayPicker.jsx (1)
frontend/src/contexts/AttendanceContext.jsx (6)
useAttendance(239-245)useAttendance(239-245)closeAddRoundsModal(116-116)sessions(52-52)selectedSessionId(53-53)handleAddRounds(141-155)
🔇 Additional comments (10)
frontend/src/components/VerificationModal.module.css (3)
108-109: 버튼 크기 감소의 UI 일관성 확인.버튼 크기가
132x48에서100x42로 감소했습니다. 전체 애플리케이션에서 모달 버튼의 일관성과 사용성이 유지되는지 확인하세요. 특히 텍스트 길이가 긴 버튼 라벨이 잘리지 않는지 검증하세요.
138-138: ResetPasswordButton 색상 변경 - 승인.파괴적 작업(비밀번호 재설정)에 적합한 빨간색(
#ff2c4c)으로 변경되었습니다. 비활성화 상태의 색상(#ff9ead)도 적절하게 유지되었습니다.Also applies to: 141-141
179-183: 새로운.modifyButtonGroup클래스 - 승인.유연한 레이아웃과 버튼 간 일관된 간격을 제공하는 효과적인 추가입니다. 재사용 가능한 컴포넌트 구조에 적합합니다.
frontend/src/contexts/AttendanceContext.jsx (3)
62-75: 출석 상태 업데이트 로직은 방어 코드까지 포함되어 잘 작성되어 있습니다
selectedSessionId와selectedRound로 세션/회차를 찾고, 없으면 조용히 빠져나가는 구조라 NPE 없이 안전하게 동작할 것 같습니다.useImmer기반으로 참가자 배열을 직접 수정하는 패턴도 이 use-case에 잘 맞습니다.
97-106: 세션 기본 시간/출석 가능 시간 수정 로직은 단순·명확합니다
updateSessionData.id로 세션을 찾고 필요한 필드만 갱신하는 구조라 부작용 범위가 명확하고, 나중에 다른 메타데이터를 추가하기에도 확장성이 좋아 보입니다.
141-155: 여러 회차 추가 후 정렬까지 한 번에 처리하는 구조가 좋습니다
handleAddRounds에서 새 라운드를 한 번에push한 뒤, 날짜/시간 기준으로 정렬을 다시 해 주는 방식이라, UI에서 항상 시간 순으로 정렬된 목록을 기대하기에 적합해 보입니다.date와startTime을 ISO 형태로 조합해Date로 비교하는 것도 명확합니다.frontend/src/components/attendancemanage/SessionSettingCard.jsx (1)
3-7: 세션 생성 핸들러를 컨텍스트에서 가져오는 방향이 API를 단순하게 만들어 줍니다기존에 상위에서
onAddSession을 props로 내려보내던 구조보다,useAttendance에서handleAddSession만 끌어다 쓰는 쪽이 이 카드 컴포넌트를 재사용하기에도 더 깔끔해 보입니다. 페이지 레벨에서 세션 추가 로직을 한 곳에서 관리할 수 있다는 점도 좋습니다.frontend/src/components/attendancemanage/RoundDayPicker.jsx (1)
31-60: 회차 생성 로직(세션 검증 + 로컬 날짜 처리)이 안정적으로 구현되었습니다
currentSession미존재와 선택된 날짜 0건을 사전에 막고,getTimezoneOffset을 이용해 로컬 기준YYYY-MM-DD문자열을 만드는 부분이 실제 운영 시 헷갈리기 쉬운 타임존 문제를 잘 피하고 있습니다.
startTime/availableMinutes를 세션의 디폴트 값에서 가져와handleAddRounds로 넘기는 것도 컨텍스트 쪽 정렬/관리 로직과 잘 맞습니다.frontend/src/components/attendancemanage/SessionManagementCard.jsx (2)
5-17: 세션 선택/검증과 회차 추가 모달 오픈 순서가 적절합니다컨텍스트에서
sessions/selectedSessionId/setSelectedSessionId/openAddRoundsModal등을 받아와 사용하고,currentSession이 없을 때 회차 추가 버튼 클릭 시 경고를 띄우고 리턴하는 로직으로 이전 리뷰에서 언급됐던 "세션 미선택 상태에서 회차 추가" 문제를 잘 예방하고 있습니다.
세션 셀렉트 변경 시setSelectedRound(null)로 회차 선택 상태를 초기화하는 부분도 다른 카드(출석 관리)와의 연동을 고려한 합리적인 처리입니다.Also applies to: 19-40
61-75: 세션/회차 수정 모달을 열기 전 편집 대상 설정 순서가 올바르게 정리되었습니다세션 수정 버튼에서
currentSession존재 여부를 먼저 확인한 뒤setEditingSession(currentSession) → openSessionModifyModal()순으로 호출하고, 회차 행의 메뉴 버튼에서도setEditingRound(round)를 호출한 후openRoundModifyModal()을 실행하는 순서가 잘 맞습니다.e.stopPropagation()으로 행 클릭과 메뉴 클릭 동작을 분리한 것도 의도대로 동작할 것으로 보입니다.
현재 구조라면 모달이 렌더링될 때 항상 올바른 편집 대상이 세팅되어 있어, 이전에 지적되던 편집 대상 race condition 문제는 없어 보입니다.Also applies to: 87-112
| }; | ||
| }); | ||
|
|
||
| handleAddRounds(selectedSessionId, newRounds); |
There was a problem hiding this comment.
회차를 추가하기 전에 정렬하면 추가한 뒤에는 다른 회차들까지 봤을 때 정렬되지 않을 수 있으니까 회차 정보가 수정되거나 회차가 추가되었을때 정렬되도록 구현했습니다!
DongEun02
left a comment
There was a problem hiding this comment.
react-confirm-alert 한 번 찾아보시는 걸 추천드립니다!
작업한 이슈번호
변경 요약 (What & Why)
무엇을 변경했는지:
왜 변경했는지(문제/목표):
전/후 비교, 반응형(모바일/데스크톱) 캡쳐
Before:
After:
라우팅/페이지:
컴포넌트: AttendanceManagementCard.jsx, RoundDayPicker.jsx, RoundModifyModal.jsx, SessionManagementCard.jsx, SessionModifyModal.jsx, SessionSettingCard.jsx, AttendanceManage.jsx
상태관리:
API 호출:
스타일:
기타:
라이브러리:
Summary by CodeRabbit
릴리스 노트
New Features
UI Improvements