20260220 fe #214 about attendace page#243
Conversation
Walkthrough출석 QR 관리 기능을 추가하기 위해 새로운 라우트, 컴포넌트, 유틸리티를 도입했습니다. 기존 출석 관리 UI를 리팩토링하고 QR 코드 생성 및 체크인 인프라를 구축했으며, AttendanceContext를 개선하고 API 엔드포인트를 업데이트했습니다. Changes
Sequence DiagramsequenceDiagram
participant User as 사용자
participant QRP as QrRenderPage
participant API as 백엔드 QR Stream<br/>(EventSource)
participant UI as QR Code Display
User->>QRP: 라운드 QR 페이지 접속<br/>(roundId 포함)
QRP->>API: connectRoundQrStream(roundId)<br/>SSE 연결
activate API
Note over API: QR 토큰 생성 및 스트림
API->>QRP: qrToken 이벤트 수신
deactivate API
QRP->>QRP: qrData 상태 업데이트
QRP->>UI: QRCodeSVG 렌더링<br/>(qrToken 값)
UI->>User: QR 코드 표시
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45분 Possibly related issues
Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 8
🧹 Nitpick comments (1)
frontend/src/App.jsx (1)
68-69:<Layout />외부로 분리 고려
/attendance/admin/qr는 새 탭에서 QR을 전시하는 화면이므로,<Layout />의 내비게이션 헤더가 함께 렌더링됩니다. 출석 확인용 QR 전시 화면에는 불필요한 UI이며,/attendance/check-in역시 단순 처리 화면임을 감안하면 두 라우트 모두 Layout 밖에 두는 것이 적합합니다.♻️ 라우트 분리 제안
<Route element={<ProtectedRoute />}> <Route element={<Layout />}> ... - <Route path="/attendance/admin/qr" element={<QrRenderPage />} /> - <Route path="/attendance/check-in" element={<CheckInPage />} /> </Route> + <Route path="/attendance/admin/qr" element={<QrRenderPage />} /> + <Route path="/attendance/check-in" element={<CheckInPage />} /> </Route>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/App.jsx` around lines 68 - 69, 두 라우트는 내비게이션 헤더가 필요하지 않은 간단한 화면이므로 <Layout /> 안에 렌더링되지 않도록 분리해야 합니다; App.jsx에서 현재 <Route path="/attendance/admin/qr" element={<QrRenderPage />} /> 및 <Route path="/attendance/check-in" element={<CheckInPage />} /> 를 찾아서 기존 <Layout>...</Layout> 라우트 그룹 바깥으로 옮기고, 필요하다면 각각 별도의 최상위 Route로 등록해 레이아웃이 적용되지 않도록 변경하세요 (참고 컴포넌트: QrRenderPage, CheckInPage, Layout).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@frontend/src/components/attendancemanage/qrmanagement/CheckInPage.jsx`:
- Around line 1-3: CheckInPage.jsx currently calls api.post but the api import
is commented out, causing a ReferenceError; restore or add the correct import
for the shared axios helper (e.g., import api from '../../../utils/axios' or the
named export used in your project) so that api.post is defined, or replace the
api.post call with the project QR utility function (the existing QR helper
method used elsewhere) and update references in the CheckInPage component
accordingly; ensure you update the top-level imports and keep the function name
api and the api.post usage in the component consistent.
- Around line 11-17: The CheckInPage useEffect currently checks
localStorage.getItem('accessToken') which is incompatible with the cookie-based
auth used elsewhere (see axios.js withCredentials); update the logic in the
CheckInPage component (the useEffect block) to perform an authenticated API call
(e.g., call your current-user or /me endpoint via the existing axios instance)
to verify the session and only navigate to /login if that call fails or returns
401/unauthenticated, rather than reading localStorage; ensure you use the same
axios instance that sends cookies (withCredentials) and handle async state and
cleanup in the useEffect.
In `@frontend/src/components/attendancemanage/qrmanagement/QrRenderPage.jsx`:
- Around line 25-26: QrRenderPage.jsx currently passes qrData.qrToken directly
into <QRCodeSVG value={...}> which can be null/undefined/empty and will crash;
update the render logic in the QrRenderPage component to validate and normalize
qrData.qrToken (e.g., check qrData && typeof qrData.qrToken === 'string' &&
qrData.qrToken.trim() !== '') and only render <QRCodeSVG value={qrData.qrToken}>
when that check passes, otherwise render a safe fallback (placeholder UI or a
default non-null string) so the value prop always receives a valid string.
- Around line 11-19: The component's useEffect calls
connectRoundQrStream(roundId, (data) => setQrData(data)) but omits the onError
handler so SSE errors leave qrData null and UI stuck; update the effect to pass
a third argument to connectRoundQrStream (an onError callback) that sets
component error state or sets qrData to an error sentinel and triggers any
loading flags to false (use the existing setQrData or a new setError/setLoading
state), and ensure the returned cleanup still calls es.close() so streams are
closed on unmount; reference connectRoundQrStream, setQrData, qrData, and
useEffect when making the change.
In `@frontend/src/components/attendancemanage/RoundDayPicker.jsx`:
- Around line 32-69: handleComplete allows end times before start times and
won't catch errors if AttendanceContext.handleAddRounds swallows them; add a
pre-submit validation in handleComplete after building startAt/closeAt that
compares Date(startAt) and Date(closeAt) and alerts/returns if closeAt <=
startAt, and ensure you don't submit when invalid. Also update
AttendanceContext.handleAddRounds to propagate errors (remove swallowing
try/catch or rethrow the caught error) so the try/catch in handleComplete can
handle failures; keep existing console.log/closeAddRoundsModal behavior inside
the try block.
In `@frontend/src/components/attendancemanage/SessionManagementCard.jsx`:
- Around line 125-155: The table body in SessionManagementCard.jsx renders five
<td> cells (date, start time, duration, round number, and a QR button) via
currentDisplayedRounds.map, but the table header still has only four columns,
causing misalignment; update the table header JSX in the SessionManagementCard
component to include a fifth <th> (e.g., "QR" or the localized label) aligned
with the QR button column so the header count matches the body cells and table
columns stay aligned.
In `@frontend/src/utils/axios.js`:
- Around line 3-5: api 인스턴스가 baseURL을 빈 문자열로 고정해 환경 설정을 무시하므로, export된 const
api의 설정에서 baseURL을 빈 문자열('') 대신 프로젝트에서 정의한 BASE_URL (예:
import.meta.env.VITE_API_URL || '')로 바꿔 일관된 오리진을 사용하도록 수정하세요; 즉 axios.create 호출의
baseURL 옵션을 BASE_URL로 교체하고, BASE_URL이 파일에 정의되어 있지 않다면 같은 파일 상단에 const BASE_URL =
import.meta.env.VITE_API_URL || ''를 추가해 참조 오류가 없도록 해주세요.
In `@frontend/src/utils/qrManage.js`:
- Around line 18-21: The SSE listener for 'qrToken' should guard against invalid
or empty JSON to avoid crashing the stream: in the event handler attached via
eventSource.addEventListener('qrToken', ...) wrap JSON.parse(event.data) in a
try/catch, check for falsy/empty event.data before parsing, call
onError?.(error) when parsing fails (or when event.data is empty) and return
early so the stream continues, otherwise call onMessage?.(parsed) with the
parsed payload.
---
Nitpick comments:
In `@frontend/src/App.jsx`:
- Around line 68-69: 두 라우트는 내비게이션 헤더가 필요하지 않은 간단한 화면이므로 <Layout /> 안에 렌더링되지 않도록
분리해야 합니다; App.jsx에서 현재 <Route path="/attendance/admin/qr" element={<QrRenderPage
/>} /> 및 <Route path="/attendance/check-in" element={<CheckInPage />} /> 를 찾아서
기존 <Layout>...</Layout> 라우트 그룹 바깥으로 옮기고, 필요하다면 각각 별도의 최상위 Route로 등록해 레이아웃이 적용되지
않도록 변경하세요 (참고 컴포넌트: QrRenderPage, CheckInPage, Layout).
ℹ️ Review info
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (11)
frontend/src/App.jsxfrontend/src/components/attendancemanage/RoundDayPicker.jsxfrontend/src/components/attendancemanage/SessionManagementCard.jsxfrontend/src/components/attendancemanage/SessionSettingCard.jsxfrontend/src/components/attendancemanage/qrmanagement/CheckInPage.jsxfrontend/src/components/attendancemanage/qrmanagement/QrRenderPage.jsxfrontend/src/contexts/AttendanceContext.jsxfrontend/src/utils/attendanceManage.jsfrontend/src/utils/axios.jsfrontend/src/utils/qrManage.jspackage.json
frontend/src/components/attendancemanage/qrmanagement/CheckInPage.jsx
Outdated
Show resolved
Hide resolved
frontend/src/components/attendancemanage/qrmanagement/CheckInPage.jsx
Outdated
Show resolved
Hide resolved
frontend/src/components/attendancemanage/qrmanagement/QrRenderPage.jsx
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 2
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)
11-17:⚠️ Potential issue | 🟡 Minor
formatDate함수에서 날짜 전용 ISO 문자열 파싱 시 타임존 오프셋 문제
roundDate는 백엔드에서LocalDate타입으로 직렬화되어 프론트엔드에"2026-02-21"형식의 날짜 전용 문자열로 전달됩니다. JavaScript의new Date()스펙에 따르면 시간 정보가 없는 ISO 문자열은 UTC 자정으로 파싱되므로, UTC 음수 오프셋 지역(예: EST, PST)에서는 표시되는 날짜가 하루 전으로 어긋날 수 있습니다.현재 KST(UTC+9) 환경에서는 문제가 없으나, 국제 배포 시 대응하기 위해 아래와 같이 수정하는 것이 안전합니다.
제안 수정안
const formatDate = (dateStr) => { if (!dateStr) return ''; - const date = new Date(dateStr); - const month = String(date.getMonth() + 1).padStart(2, '0'); - const day = String(date.getDate()).padStart(2, '0'); + const [year, month, day] = dateStr.split('T')[0].split('-'); return `${month}/${day}`; };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/components/attendancemanage/SessionManagementCard.jsx` around lines 11 - 17, formatDate currently calls new Date(dateStr) which treats date-only ISO strings as UTC midnight and can shift the day in negative-offset timezones; update formatDate to parse the "YYYY-MM-DD" roundDate string manually (split the string into year, month, day and construct a local Date via new Date(year, monthIndex, day) or format directly from the parts) so the date is interpreted in local calendar terms and always returns the same MM/DD string; ensure you still handle falsy input and non-matching formats as a fallback.
♻️ Duplicate comments (1)
frontend/src/components/attendancemanage/SessionManagementCard.jsx (1)
115-122: 이전 리뷰 지적 사항 반영 확인Line 121에
<th>QR 코드</th>컬럼 헤더가 추가되어 테이블 바디(5열)와 헤더(5열)가 정렬됩니다.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/components/attendancemanage/SessionManagementCard.jsx` around lines 115 - 122, The table header in SessionManagementCard.jsx was missing the QR code column which caused column misalignment with the table body; update the <thead> in SessionManagementCard.jsx to include the missing <th>QR 코드</th> so the header columns (일자, 시간, 가능(분), 회차, QR 코드) match the five columns rendered in the tbody (date, time, availableMinutes, sessionNumber, qrCode) and verify the column order aligns with the data rendering logic.
🧹 Nitpick comments (1)
frontend/src/components/attendancemanage/SessionManagementCard.jsx (1)
4-8: 사용되지 않는 import 3개 제거 필요
useContext(line 4),addRound(line 7),RoundDayPicker(line 8)가 모두 컴포넌트 내에서 참조되지 않습니다.🧹 제안 수정안
-import { useContext, useEffect, useState } from 'react'; +import { useEffect, useState } from 'react'; import { toast } from 'react-toastify'; import { useAttendance } from '../../contexts/AttendanceContext'; -import { getRounds, addRound } from '../../utils/attendanceManage'; -import RoundDayPicker from './RoundDayPicker'; +import { getRounds } from '../../utils/attendanceManage';🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/components/attendancemanage/SessionManagementCard.jsx` around lines 4 - 8, Remove the three unused imports to clean up the module: delete useContext from the React import, remove addRound from the import of '../../utils/attendanceManage', and remove the RoundDayPicker import; leave the remaining used imports (e.g., useEffect, useState, toast, useAttendance, getRounds) intact so SessionManagementCard.jsx only imports symbols that are actually referenced.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@frontend/src/components/attendancemanage/SessionManagementCard.jsx`:
- Around line 55-68: The function handleAddRound is dead code—it's defined but
never used; either wire it into the intended consumer (pass handleAddRound as
the callback prop to RoundDayPicker or to whatever component opened by
openAddRoundsModal) so the RoundDayPicker or modal calls handleAddRound
(ensuring it uses selectedSessionId and handleAddRounds), or remove
handleAddRound entirely to clean up unused code; update any parent call-sites
that should receive the callback (e.g., where openAddRoundsModal is invoked) to
accept and forward handleAddRound if you choose to keep it.
- Around line 127-141: The rendering uses new Date(round.startAt) and new
Date(round.closeAt) without validation, which can produce "Invalid Date" or NaN;
update the SessionManagementCard.jsx row rendering to validate dates before
formatting: create Date objects for round.startAt/round.closeAt, test validity
with isFinite(date.getTime()) or !isNaN(date.getTime()), only call
toLocaleTimeString and compute minutes when both dates are valid, and otherwise
render a safe fallback (e.g., '-' or 'N/A') in the time and minutes cells;
ensure you reference the existing variables startTime, closeTime and the minutes
calculation so the change is localized to that table row.
---
Outside diff comments:
In `@frontend/src/components/attendancemanage/SessionManagementCard.jsx`:
- Around line 11-17: formatDate currently calls new Date(dateStr) which treats
date-only ISO strings as UTC midnight and can shift the day in negative-offset
timezones; update formatDate to parse the "YYYY-MM-DD" roundDate string manually
(split the string into year, month, day and construct a local Date via new
Date(year, monthIndex, day) or format directly from the parts) so the date is
interpreted in local calendar terms and always returns the same MM/DD string;
ensure you still handle falsy input and non-matching formats as a fallback.
---
Duplicate comments:
In `@frontend/src/components/attendancemanage/SessionManagementCard.jsx`:
- Around line 115-122: The table header in SessionManagementCard.jsx was missing
the QR code column which caused column misalignment with the table body; update
the <thead> in SessionManagementCard.jsx to include the missing <th>QR 코드</th>
so the header columns (일자, 시간, 가능(분), 회차, QR 코드) match the five columns rendered
in the tbody (date, time, availableMinutes, sessionNumber, qrCode) and verify
the column order aligns with the data rendering logic.
---
Nitpick comments:
In `@frontend/src/components/attendancemanage/SessionManagementCard.jsx`:
- Around line 4-8: Remove the three unused imports to clean up the module:
delete useContext from the React import, remove addRound from the import of
'../../utils/attendanceManage', and remove the RoundDayPicker import; leave the
remaining used imports (e.g., useEffect, useState, toast, useAttendance,
getRounds) intact so SessionManagementCard.jsx only imports symbols that are
actually referenced.
ℹ️ Review info
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
frontend/src/components/attendancemanage/SessionManagementCard.jsxfrontend/src/components/attendancemanage/qrmanagement/CheckInPage.jsxfrontend/src/components/attendancemanage/qrmanagement/QrRenderPage.jsx
🚧 Files skipped from review as they are similar to previous changes (2)
- frontend/src/components/attendancemanage/qrmanagement/QrRenderPage.jsx
- frontend/src/components/attendancemanage/qrmanagement/CheckInPage.jsx
1) #214
2) 변경 요약 (What & Why)
출석 관리 페이지
QR 렌더링 페이지 추가
기타
3) 스크린샷 / 동영상 (UI 변경 시)
4) 상세 변경사항
1. 세션 관련 코드 수정
roundId,startAt,closeAt기준으로 데이터 매핑 변경startTime,allowedMinutes제거roundId로 수정2. 라운드 관련 코드 수정
(
roundName,roundDate,startAt,closeAt,locationName)allowedMinutes제거 →closeAt - startAt계산 방식으로 변경roundStatus) 확장 가능하도록 구조 정리3. QR 렌더링 페이지 신규 구현
/api/attendance/rounds/{roundId}/qr-stream연결event: qrToken커스텀 이벤트 수신 처리qrToken을QRCodeSVG로 렌더링5) 참고사항
Summary by CodeRabbit
릴리스 노트