Conversation
…into WTH-249-출석-api-연결
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthrough패키지에 QR 코드 스타일링 라이브러리를 추가하고, 출석 관련 페이지와 레이아웃을 서버 기반 비동기 컴포넌트로 전환했습니다. 클럽 ID를 쿠키에서 읽어 서버 API로 대시보드·출석·요약을 조회하고, QR 생성·체크인 훅과 에러/토스트 흐름을 통합했습니다. Changes
Sequence Diagram(s)sequenceDiagram
participant User as 사용자
participant Browser as 브라우저
participant MainLayout as MainLayout(server)
participant HomeAPI as Home API
participant UserHydrator as UserHydrator(client)
participant AttendancePage as AttendancePage(server)
participant AttendanceServer as Attendance Server
participant AttendanceContent as AttendanceContent(client)
User->>Browser: /attendance 접근
Browser->>MainLayout: 요청 (쿠키 포함)
MainLayout->>HomeAPI: getDashboard(clubId)
HomeAPI-->>MainLayout: 대시보드 데이터
MainLayout->>UserHydrator: 사용자/클럽 정보 전달
MainLayout-->>Browser: 페이지 응답
Browser->>AttendancePage: 서버컴포넌트 렌더
AttendancePage->>AttendanceServer: getAttendance(clubId)
AttendanceServer-->>AttendancePage: attendance 데이터
AttendancePage-->>AttendanceContent: { attendance, errorMessage, qrSessionId?, qrCode? }
AttendanceContent->>Browser: 클라이언트 UI 렌더
sequenceDiagram
participant User as 사용자
participant AttendanceContent as AttendanceContent(client)
participant useQRCheckIn as useQRCheckIn(hook)
participant AttendanceAPI as Attendance API
participant Toast as toastError
participant Router as router
User->>AttendanceContent: QR 파라미터 포함 방문
AttendanceContent->>useQRCheckIn: 초기화(qrSessionId, qrCode)
useQRCheckIn->>AttendanceAPI: checkIn(clubId, sessionId, code)
alt 성공
AttendanceAPI-->>useQRCheckIn: 성공
useQRCheckIn-->>AttendanceContent: isChecked=true
else 실패
AttendanceAPI-->>useQRCheckIn: 에러(code?)
useQRCheckIn->>Toast: ATTENDANCE_ERROR_MESSAGE[code]
useQRCheckIn->>Router: replace('/attendance')
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 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 |
🤖 Claude 테스트 제안
변경된 컴포넌트에 대해 Claude가 생성한 테스트 코드입니다. 검토 후 적합한 부분만 사용하세요.
|
PR 테스트 결과✅ Jest: 통과 🎉 모든 테스트를 통과했습니다! |
PR 검증 결과✅ TypeScript: 통과 🎉 모든 검증을 통과했습니다! |
|
구현한 기능 Preview: https://weeth-4pk762nx9-weethsite-4975s-projects.vercel.app |
There was a problem hiding this comment.
Actionable comments posted: 10
🧹 Nitpick comments (1)
src/stores/useUserStore.ts (1)
5-11:role도 공용 타입을 재사용하는 편이 안전합니다.
UserInfo는 공용 타입으로 옮겼는데 Line 11만 문자열 유니온을 다시 적어 두면 다음 역할 추가 때 store와 타입 정의가 또 어긋납니다.Role | null이나UserInfo['role'] | null로 맞추는 쪽이 이번 리팩토링 방향과도 일관됩니다.♻️ 제안된 정리
-import type { UserInfo } from '@/types/user'; +import type { Role, UserInfo } from '@/types/user'; @@ - role: null as 'LEAD' | 'ADMIN' | 'USER' | null, + role: null as Role | null,🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/stores/useUserStore.ts` around lines 5 - 11, The initialState object declares role as a string union; change it to reuse the shared type by replacing the explicit union on role with UserInfo['role'] | null so the store stays in sync with the central UserInfo type; update the role declaration in initialState (in useUserStore.ts) to use UserInfo['role'] | null and ensure any imports for UserInfo remain in place.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/app/`(private)/(main)/attendance/history/page.tsx:
- Around line 9-20: Don't swallow exceptions from
attendanceServerApi.getDetail('YUNJcjFKMO'): capture the error instead of
replacing it with a fake empty summary and surface an explicit error state to
the UI (either rethrow to let the page's error boundary handle it or pass an
error prop to AttendanceHistoryContent). Update the try/catch around
attendanceServerApi.getDetail to set an error variable (e.g., fetchError) or
rethrow the caught error, and change the return to conditionally render an error
UI or pass fetchError alongside summary so real failures are distinguishable
from a genuine empty attendance result.
In `@src/app/`(private)/(main)/attendance/page.tsx:
- Around line 12-18: The attendance fetch currently uses a hardcoded clubId
string in attendanceServerApi.getAttendance('YUNJcjFKMO'), locking all
attendance logic to one club; replace that literal with the real club identifier
the page should trust (e.g., use the route parameter or server-derived value) —
for example, read params.clubId (or a server-validated clubId from
cookies/session or from a prior server lookup) and pass that variable into
attendanceServerApi.getAttendance(clubId). Update any related QR/history flows
in this file that call attendanceServerApi.getAttendance or generate club-scoped
data to use the same dynamic clubId variable (ensure you validate/authorize the
clubId on the server side before use).
In `@src/app/`(private)/(main)/attendance/qr/page.tsx:
- Around line 13-25: Validate that sessionId is a numeric integer before using
it: where sessionId is read and before calling redirect or rendering
AttendanceQRContent, parse and check it (e.g., const id = Number(sessionId);
ensure !isNaN(id) && Number.isInteger(id) and id > 0) and if the check fails
call redirect('/attendance') instead of proceeding to render with
Number(sessionId); update usages (Number(sessionId)) to the validated id so
downstream code does not receive NaN or invalid paths.
In `@src/app/`(private)/(main)/layout.tsx:
- Around line 7-18: MainLayout currently hardcodes clubId by calling
homeServerApi.getDashboard('YUNJcjFKMO') and hydrates UserHydrator with that
static clubId/clubName, which breaks multi-club and routing context; change
MainLayout to derive clubId dynamically (e.g., from route params, server
session/context, or request headers) before calling homeServerApi.getDashboard
or replace getDashboard argument with the dynamic value, then pass the resulting
{ id: clubId, name: clubName } into UserHydrator so each route/user gets the
correct club context; update the code paths that reference MainLayout,
homeServerApi.getDashboard, UserHydrator, clubId and clubName to use the dynamic
source.
In `@src/components/attendance/AttendanceContent.tsx`:
- Around line 63-64: The manual check-in call is using a hardcoded clubId
string; change attendanceApi.checkIn('YUNJcjFKMO', sessionId, Number(code)) to
use the same current clubId source as the QR flow. Locate AttendanceContent
component and retrieve the clubId from the same prop/store selector used by
AttendanceQRContent (e.g., the prop named clubId or the Redux
selector/useClubStore used in AttendanceQRContent) and pass that clubId into
attendanceApi.checkIn(sessionId, Number(code)) (preserve sessionId and code),
ensuring both flows target the identical clubId.
- Around line 59-65: The manual check-in flow (handleAttendanceComplete) only
calls setIsManualChecked(true) but the AttendanceCompleteModal is controlled by
qrCompleteModalOpen, so the manual path never shows the modal; update the logic
so both flows share the same completion state: either set
qrCompleteModalOpen(true) when setting setIsManualChecked(true) inside
handleAttendanceComplete (and the analogous manual-success branch around lines
115-117), or change AttendanceCompleteModal's open prop to use a combined
condition (qrCompleteModalOpen || isManualChecked) so the modal appears for both
QR and manual check-ins; adjust only the state updates or the modal open
expression accordingly.
In `@src/components/attendance/AttendanceTodayCard.tsx`:
- Around line 20-24: In AttendanceTodayCard, guard against null/undefined
sessionId in the QR admin flow: update the QR button click handler and the
button's disabled logic (where it currently only checks disabled) to also check
sessionId == null, and prevent building/navigating to
`/attendance/qr?sessionId=${sessionId}` when sessionId is null (return early or
show noop); reference the sessionId prop and the QR button handler in
AttendanceTodayCard to locate and fix the code.
In `@src/hooks/useQRCheckIn.ts`:
- Around line 25-26: The checkIn call in useQRCheckIn currently uses a hardcoded
clubId ('YUNJcjFKMO'); update useQRCheckIn to accept a clubId parameter (or
internally call the same source hook used by AttendanceQRContent.tsx, e.g.,
useClubId()) and pass that dynamic clubId into attendanceApi.checkIn along with
Number(qrSessionId) and Number(qrCode) so the issued QR and the verification use
the same club context; ensure the function signature and all call sites are
updated to provide the real clubId.
- Around line 17-21: The current effect flips hasCheckedIn.current to true
immediately, preventing retries for new qrSessionId/qrCode combos or after
failures; update the logic in useQRCheckIn's useEffect so you either (a) track
processed combos with a Set/Map keyed by `${qrSessionId}:${qrCode}` (check
membership before sending and add the key only after a successful check-in) or
(b) keep a boolean but only set hasCheckedIn.current = true after the request
completes successfully; reference hasCheckedIn, qrSessionId, qrCode and the
effect that initiates the request to locate and modify the code.
---
Nitpick comments:
In `@src/stores/useUserStore.ts`:
- Around line 5-11: The initialState object declares role as a string union;
change it to reuse the shared type by replacing the explicit union on role with
UserInfo['role'] | null so the store stays in sync with the central UserInfo
type; update the role declaration in initialState (in useUserStore.ts) to use
UserInfo['role'] | null and ensure any imports for UserInfo remain in place.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 94086f27-989b-40b3-98c6-e26eb78a4a49
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (30)
package.jsonsrc/app/(private)/(main)/attendance/history/page.tsxsrc/app/(private)/(main)/attendance/page.tsxsrc/app/(private)/(main)/attendance/qr/page.tsxsrc/app/(private)/(main)/layout.tsxsrc/components/attendance/AttendanceCompleteModal.tsxsrc/components/attendance/AttendanceContent.tsxsrc/components/attendance/AttendanceHistoryContent.tsxsrc/components/attendance/AttendanceQRContent.tsxsrc/components/attendance/AttendanceTodayCard.tsxsrc/components/home/HomePageSections.tsxsrc/components/ui/card.tsxsrc/constants/attendance/attendance.tssrc/constants/attendance/error.tssrc/constants/attendance/index.tssrc/hooks/useQRCheckIn.tssrc/hooks/useQRCode.tssrc/lib/apis/attendance.server.tssrc/lib/apis/attendance.tssrc/lib/apis/home.server.tssrc/providers/user-hydrator.tsxsrc/proxy.tssrc/stores/index.tssrc/stores/useClubStore.tssrc/stores/useUserStore.tssrc/types/attendance.tssrc/types/club.tssrc/types/home.tssrc/types/index.tssrc/types/user.ts
PR 테스트 결과✅ Jest: 통과 🎉 모든 테스트를 통과했습니다! |
🤖 Claude 테스트 제안
변경된 컴포넌트에 대해 Claude가 생성한 테스트 코드입니다. 검토 후 적합한 부분만 사용하세요.
|
PR 검증 결과✅ TypeScript: 통과 🎉 모든 검증을 통과했습니다! |
|
구현한 기능 Preview: https://weeth-g7h6gszpl-weethsite-4975s-projects.vercel.app |
…into WTH-249-출석-api-연결
JIN921
left a comment
There was a problem hiding this comment.
수고하셧습니다,, 워낙 꼼꼼하게 해주셔서 큰 수정사항은 없는 거 같아요!!
src/lib/apis/home.server.ts
Outdated
| export const homeServerApi = { | ||
| getDashboard: (clubId: string) => | ||
| apiServer.get<HomeDashboardResponse>(`/clubs/${clubId}/dashboard/home`, { | ||
| cache: 'no-store', |
There was a problem hiding this comment.
이거 캐시 설정을 no-store로 해 놓으면 메인 폴더 하위 페이지에서 매 네비게이션마다,, 호출 될 거 같습니당 revalidate 설정하고 클럽 아이디를 태그로 달면 좋을 거 같애요...
src/hooks/useQRCheckIn.ts
Outdated
| function useQRCheckIn({ qrSessionId, qrCode }: UseQRCheckInParams) { | ||
| const router = useRouter(); | ||
| const [isChecked, setIsChecked] = useState(false); | ||
| const [completeModalOpen, setCompleteModalOpen] = useState(false); |
There was a problem hiding this comment.
현재는 useQRCheckIn에서 성공 모달 UI 상태까지 책임지는 구조가 되어서,,,, 이거 onSuccess 콜백으로 제어하면 좋을 거 같습니다! MutationCallbacks 타입 잇으니까 onSuccess 콜백 받고 { isChecked }만 반환하도록 하면 어떨까요? 모달 열기 같은 UI 처리는 호출 측에서 onSuccess 콜백으로 넣어주면 훅이 체크인 로직에만 집중할 수 잇을 거 같습니당
src/providers/user-hydrator.tsx
Outdated
| const { setUser } = useUserActions(); | ||
| const { setClub } = useClubActions(); | ||
|
|
||
| useEffect(() => { |
There was a problem hiding this comment.
이거 첫 렌더 시점에서 null이 반환되면 깜빡거려 보일 수도 잇는데,, useRef 가드로 렌더 중에 동기적으로 store를 세팅하믄 어떨까요,,,
b3c6660 to
e6c4577
Compare
🤖 Claude 테스트 제안
변경된 컴포넌트에 대해 Claude가 생성한 테스트 코드입니다. 검토 후 적합한 부분만 사용하세요.
|
PR 테스트 결과✅ Jest: 통과 🎉 모든 테스트를 통과했습니다! |
PR 검증 결과✅ TypeScript: 통과 🎉 모든 검증을 통과했습니다! |
|
구현한 기능 Preview: https://weeth-6alqlmso4-weethsite-4975s-projects.vercel.app |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (4)
src/components/attendance/AttendanceContent.tsx (1)
69-71: 에러 타입 단언 개선 고려
error as { response?: ... }타입 단언이 안전하지 않을 수 있습니다. Axios 에러 타입을 사용하면 더 타입 안전합니다.💡 타입 개선 제안
+ import { isAxiosError } from 'axios'; } catch (error) { - const errorCode = (error as { response?: { data?: { code?: number } } }).response?.data?.code; + const errorCode = isAxiosError(error) ? error.response?.data?.code : undefined; toastError(errorCode ? ATTENDANCE_ERROR_MESSAGE[errorCode] : undefined); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/attendance/AttendanceContent.tsx` around lines 69 - 71, The catch block in AttendanceContent.tsx uses an unsafe assertion "error as { response?: { data?: { code?: number } } }"; replace it with the Axios error type to be type-safe: import AxiosError from 'axios' (or use AxiosError generic) and cast the caught error to AxiosError<{ code?: number }>, then read error.response?.data?.code and pass ATTENDANCE_ERROR_MESSAGE[errorCode] (or undefined) to toastError; update the catch signature and ensure the import for AxiosError is added and toastError/ATTENDANCE_ERROR_MESSAGE usage remains unchanged.src/app/(private)/(main)/layout.tsx (1)
14-15:clubId쿠키 누락 시 빈 화면 대신 리다이렉트 고려
clubId가 없을 때null을 반환하면 사용자에게 빈 화면이 표시됩니다. 로그인 페이지나 클럽 선택 페이지로 리다이렉트하는 것이 UX에 더 적합할 수 있습니다.💡 리다이렉트 제안
+ import { redirect } from 'next/navigation'; const clubId = (await cookies()).get(CLUB_ID_KEY)?.value; - if (!clubId) return null; + if (!clubId) redirect('/login');🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/app/`(private)/(main)/layout.tsx around lines 14 - 15, The current layout.tsx returns null when clubId (from cookies().get(CLUB_ID_KEY)?.value) is missing, causing a blank page; replace this behavior by performing a redirect to the appropriate route (e.g., login or club selection) instead of returning null. In layout.tsx, locate the clubId extraction and the early return (the lines using cookies(), CLUB_ID_KEY and clubId) and call the Next.js redirect helper (or your app's routing util) to send the user to the desired page (e.g., '/login' or '/select-club') when clubId is falsy; ensure you add the needed import for redirect at the top of the file.src/components/attendance/AttendanceQRContent.tsx (1)
59-79: 만료 상태에서 사용자에게 추가 안내 고려
isExpired일 때 "마감"만 표시되고 새 QR 코드가 자동 생성되는 동안 사용자가 혼란스러울 수 있습니다. 새로고침 중임을 나타내는 로딩 인디케이터나 안내 문구를 추가하면 UX가 개선될 수 있습니다.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/attendance/AttendanceQRContent.tsx` around lines 59 - 79, When isExpired is true the UI only shows "마감", which can confuse users while a new QR is being generated; update the JSX inside AttendanceQRContent (references: isExpired, isLoading, qrRef, qrData) to display a clear regeneration state: when isExpired && isLoading show a loading indicator/message (e.g., "새 QR 코드 생성 중..." and/or spinner) in place of the QR and the timer, and when isExpired && !isLoading show a brief instruction like "새 코드를 곧 표시합니다" or the regenerated QR once qrData exists; ensure you update the conditional that renders the timer ({isExpired ? ...}) and the QR container (div ref={qrRef}) to reflect these states so users see a clear loading/refresh message while a new QR is created.src/hooks/useAttendanceQR.ts (1)
17-37: QR 코드 인스턴스 정리 로직 누락컴포넌트 언마운트 시
qrCodeRef.current가 정리되지 않아 메모리 누수 가능성이 있습니다. 또한qrRef.current가 변경되면 기존 QR 코드가 다시 마운트되지 않습니다.♻️ 정리 로직 추가 제안
useEffect(() => { if (!qrData || !qrRef.current) return; const checkInUrl = `${window.location.origin}/attendance?sessionId=${qrData.sessionId}&code=${qrData.code}`; if (!qrCodeRef.current) { qrCodeRef.current = new QRCodeStyling({ width: 256, height: 256, data: checkInUrl, qrOptions: { errorCorrectionLevel: 'L' }, type: 'svg', dotsOptions: { type: 'dots' }, cornersSquareOptions: { type: 'extra-rounded' }, cornersDotOptions: { type: 'extra-rounded' }, }); qrCodeRef.current.append(qrRef.current); } else { qrCodeRef.current.update({ data: checkInUrl }); } + + return () => { + if (qrRef.current) { + qrRef.current.innerHTML = ''; + } + }; }, [qrData]);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/hooks/useAttendanceQR.ts` around lines 17 - 37, The effect creating the QRCodeStyling instance (in useEffect) lacks cleanup and doesn't handle changes to qrRef, risking memory leaks and stale mounts; update the effect tied to qrData so it also reacts when qrRef.current changes, and add a cleanup that, if qrCodeRef.current exists, removes/unappends it from qrRef.current and calls the QRCodeStyling teardown/clear method (then nulls qrCodeRef.current) to fully release resources; ensure you re-append the existing qrCodeRef.current to a new qrRef.current when qrRef changes instead of creating a duplicate instance.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/components/attendance/AttendanceHistoryContent.tsx`:
- Around line 77-79: The StatBox fields (total, attendanceCount, absenceCount)
are rendering "0회" even when the summary is missing or there was a
fetch/permission failure; update AttendanceHistoryContent to detect when summary
(or the fetch error/state) is absent and avoid treating that as a real
zero—render a clear placeholder (e.g., '-' or '조회불가') or a dedicated "failed to
load" UI for the statistics instead, and ensure the statistics block is rendered
separately from the attendance list when in error/unknown state; adjust the
logic that currently uses `${total ?? 0}회`, `${attendanceCount ?? 0}회`,
`${absenceCount ?? 0}회` to check summary/fetch state first and only format
numeric values when summary is present.
In `@src/hooks/useAttendanceQR.ts`:
- Around line 7-15: The hook useAttendanceQR currently calls useRemainingTime
with an empty string which causes immediate expiry when clubId is null; update
the call in useAttendanceQR to pass qrData?.expiredAt ?? undefined (or null)
instead of '' and, if needed, adjust useRemainingTime to accept undefined/null
as “no expiry” so it short-circuits (returning not-expired/zero timers) — this
keeps hooks stable (useQRCode and useRemainingTime still called) and prevents
unnecessary expiry logic and refetch checks involving qrData, referring to
useAttendanceQR, useQRCode, useRemainingTime, qrData, isExpired, refetch and
getRemainingSeconds.
In `@src/stores/useClubStore.ts`:
- Line 5: Remove the unused import ClubIdentifier from the top of
useClubStore.ts; locate the import statement that reads "import type {
ClubIdentifier }" and delete it so the file no longer imports an unused type and
the lint warning is resolved. If you plan to use ClubIdentifier later, instead
keep it commented with a TODO or add a usage; otherwise simply remove the
import.
---
Nitpick comments:
In `@src/app/`(private)/(main)/layout.tsx:
- Around line 14-15: The current layout.tsx returns null when clubId (from
cookies().get(CLUB_ID_KEY)?.value) is missing, causing a blank page; replace
this behavior by performing a redirect to the appropriate route (e.g., login or
club selection) instead of returning null. In layout.tsx, locate the clubId
extraction and the early return (the lines using cookies(), CLUB_ID_KEY and
clubId) and call the Next.js redirect helper (or your app's routing util) to
send the user to the desired page (e.g., '/login' or '/select-club') when clubId
is falsy; ensure you add the needed import for redirect at the top of the file.
In `@src/components/attendance/AttendanceContent.tsx`:
- Around line 69-71: The catch block in AttendanceContent.tsx uses an unsafe
assertion "error as { response?: { data?: { code?: number } } }"; replace it
with the Axios error type to be type-safe: import AxiosError from 'axios' (or
use AxiosError generic) and cast the caught error to AxiosError<{ code?: number
}>, then read error.response?.data?.code and pass
ATTENDANCE_ERROR_MESSAGE[errorCode] (or undefined) to toastError; update the
catch signature and ensure the import for AxiosError is added and
toastError/ATTENDANCE_ERROR_MESSAGE usage remains unchanged.
In `@src/components/attendance/AttendanceQRContent.tsx`:
- Around line 59-79: When isExpired is true the UI only shows "마감", which can
confuse users while a new QR is being generated; update the JSX inside
AttendanceQRContent (references: isExpired, isLoading, qrRef, qrData) to display
a clear regeneration state: when isExpired && isLoading show a loading
indicator/message (e.g., "새 QR 코드 생성 중..." and/or spinner) in place of the QR
and the timer, and when isExpired && !isLoading show a brief instruction like "새
코드를 곧 표시합니다" or the regenerated QR once qrData exists; ensure you update the
conditional that renders the timer ({isExpired ? ...}) and the QR container (div
ref={qrRef}) to reflect these states so users see a clear loading/refresh
message while a new QR is created.
In `@src/hooks/useAttendanceQR.ts`:
- Around line 17-37: The effect creating the QRCodeStyling instance (in
useEffect) lacks cleanup and doesn't handle changes to qrRef, risking memory
leaks and stale mounts; update the effect tied to qrData so it also reacts when
qrRef.current changes, and add a cleanup that, if qrCodeRef.current exists,
removes/unappends it from qrRef.current and calls the QRCodeStyling
teardown/clear method (then nulls qrCodeRef.current) to fully release resources;
ensure you re-append the existing qrCodeRef.current to a new qrRef.current when
qrRef changes instead of creating a duplicate instance.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: cadf95d3-b785-430e-95a3-a54186225ce1
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (30)
package.jsonsrc/app/(private)/(main)/attendance/history/page.tsxsrc/app/(private)/(main)/attendance/page.tsxsrc/app/(private)/(main)/attendance/qr/page.tsxsrc/app/(private)/(main)/layout.tsxsrc/components/attendance/AttendanceCompleteModal.tsxsrc/components/attendance/AttendanceContent.tsxsrc/components/attendance/AttendanceHistoryContent.tsxsrc/components/attendance/AttendanceQRContent.tsxsrc/components/attendance/AttendanceTodayCard.tsxsrc/components/home/HomePageSections.tsxsrc/components/ui/card.tsxsrc/constants/attendance/attendance.tssrc/constants/attendance/error.tssrc/constants/attendance/index.tssrc/hooks/useAttendanceQR.tssrc/hooks/useQRCheckIn.tssrc/hooks/useQRCode.tssrc/lib/apis/attendance.server.tssrc/lib/apis/attendance.tssrc/lib/apis/home.server.tssrc/providers/user-hydrator.tsxsrc/proxy.tssrc/stores/useClubStore.tssrc/stores/useUserStore.tssrc/types/attendance.tssrc/types/club.tssrc/types/home.tssrc/types/index.tssrc/types/user.ts
✅ Files skipped from review due to trivial changes (7)
- package.json
- src/constants/attendance/index.ts
- src/types/club.ts
- src/types/attendance.ts
- src/constants/attendance/error.ts
- src/lib/apis/attendance.server.ts
- src/types/user.ts
🚧 Files skipped from review as they are similar to previous changes (12)
- src/proxy.ts
- src/components/home/HomePageSections.tsx
- src/types/index.ts
- src/components/attendance/AttendanceTodayCard.tsx
- src/stores/useUserStore.ts
- src/providers/user-hydrator.tsx
- src/lib/apis/attendance.ts
- src/hooks/useQRCheckIn.ts
- src/hooks/useQRCode.ts
- src/app/(private)/(main)/attendance/history/page.tsx
- src/types/home.ts
- src/components/ui/card.tsx
🤖 Claude 테스트 제안
변경된 컴포넌트에 대해 Claude가 생성한 테스트 코드입니다. 검토 후 적합한 부분만 사용하세요.
|
PR 테스트 결과✅ Jest: 통과 🎉 모든 테스트를 통과했습니다! |
PR 검증 결과✅ TypeScript: 통과 |
🤖 Claude 테스트 제안
변경된 컴포넌트에 대해 Claude가 생성한 테스트 코드입니다. 검토 후 적합한 부분만 사용하세요.
|
PR 테스트 결과✅ Jest: 통과 🎉 모든 테스트를 통과했습니다! |
PR 검증 결과✅ TypeScript: 통과 🎉 모든 검증을 통과했습니다! |
|
구현한 기능 Preview: https://weeth-d63rawlmi-weethsite-4975s-projects.vercel.app |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/hooks/useRemainingTime.ts`:
- Around line 23-24: In useRemainingTime, when endTime is falsy the hook
currently returns early but leaves the remaining state unchanged; update the
early-return branch (the if (!endTime) block) to reset remaining to 0 by calling
setRemaining(0) and also ensure any active interval/timeout is cleared
(clearInterval/clearTimeout on the stored timer ID) before returning so the UI
shows expired state consistently.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: d44a78fa-e68a-48fa-9fc5-bbb7d7a6a456
⛔ Files ignored due to path filters (1)
src/assets/icons/attendance/ic_attendance_qr.svgis excluded by!**/*.svg
📒 Files selected for processing (3)
src/assets/icons/index.tssrc/components/attendance/AttendanceHistoryContent.tsxsrc/hooks/useRemainingTime.ts
💤 Files with no reviewable changes (1)
- src/assets/icons/index.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- src/components/attendance/AttendanceHistoryContent.tsx
🤖 Claude 테스트 제안
변경된 컴포넌트에 대해 Claude가 생성한 테스트 코드입니다. 검토 후 적합한 부분만 사용하세요.
|
PR 테스트 결과✅ Jest: 통과 🎉 모든 테스트를 통과했습니다! |
PR 검증 결과✅ TypeScript: 통과 🎉 모든 검증을 통과했습니다! |
|
구현한 기능 Preview: https://weeth-bpfjw2l40-weethsite-4975s-projects.vercel.app |
✅ PR 유형
어떤 변경 사항이 있었나요?
📌 관련 이슈번호
✅ Key Changes
출석 api
user/club 정보를 (main) 하위 페이지 접근 가능하게 수정
types/club.ts로 이동하여 중복 제거
QR을 통한 출석 플로우
요렇게 되게 구현해두었습니다!
다만 배포 이후에 잘 되는지 테스트가 필요한,,,
📸 스크린샷 or 실행영상
/attendance에서 확인 가능합니다!
qr 코드는 아래처럼 생성됩니당
🎸 기타 사항 or 추가 코멘트
슬랙으로 말씀드렷던 문제 상황 중 1번만 반영햇습니다!!
2번 clubId 관련도..빠르게 수정해보겟습니다 ㅠ ㅠ
Summary by CodeRabbit
새로운 기능
버그 수정
개선 사항