Skip to content

[Feat] WTH-324 : 출석 SSE 구독을 통한 출석 가능 시간 실시간 동기화#71

Merged
nabbang6 merged 7 commits intodevelopfrom
WTH-324-출석-SSE-구독-api-반영
Apr 27, 2026

Hidden character warning

The head ref may contain hidden characters: "WTH-324-\ucd9c\uc11d-SSE-\uad6c\ub3c5-api-\ubc18\uc601"
Merged

[Feat] WTH-324 : 출석 SSE 구독을 통한 출석 가능 시간 실시간 동기화#71
nabbang6 merged 7 commits intodevelopfrom
WTH-324-출석-SSE-구독-api-반영

Conversation

@nabbang6
Copy link
Copy Markdown
Collaborator

@nabbang6 nabbang6 commented Apr 25, 2026

✅ PR 유형

어떤 변경 사항이 있었나요?

  • 새로운 기능 추가
  • 버그 수정
  • 코드에 영향을 주지 않는 변경사항(오타 수정, 탭 사이즈 변경, 변수명 변경)
  • 코드 리팩토링
  • 주석 추가 및 수정
  • 문서 수정
  • 빌드 부분 혹은 패키지 매니저 수정
  • 파일 혹은 폴더명 수정
  • 파일 혹은 폴더 삭제

📌 관련 이슈번호

  • Closed #324

✅ Key Changes

SSE 구독 기반 실시간 expiredAt 반영

  • useAttendanceSSE 훅을 useSyncExternalStore + 모듈 레벨 싱글턴으로 리팩터링하여,
    여러 컴포넌트에서 호출해도 EventSource 연결이 1개만 유지되도록 개선
  • SSE 연결 에러 시 지수 백오프(최대 30초)로 자동 재연결
  • AttendanceQRContent, AttendanceCodeModal에서 SSE 구독을 통해 출석 가능 시간(expiredAt)을 실시간으로
    수신

출석 체크인 로직 통합

  • useCheckIn 훅에 sessionId, onSuccess 옵션을 추가하여 AttendanceContent의 중복 체크인 로직 제거
  • AttendanceContent에서 불필요한 의존성 제거

관리자 전용 QR API 호출 제한

  • useQRCode 호출 시 isAdmin 조건을 추가하여 일반 사용자의 불필요한 admin API 호출(403) 방지

QR 훅 위치 정리

  • useAttendanceQR, useQRCode를 hooks/ 루트에서 hooks/attendance/로 이동 및 barrel export 추가

SSE 프록시 개선

  • Accept 헤더 검사를 includes로 완화
  • response.ok 조건 추가로 업스트림 에러 시 즉시 에러 응답 반환

📸 스크린샷 or 실행영상

image

테스트할 때는 잘 됏엇는데 오늘 출석을 이미 해버려서......
내일 영상 추가해둘게용!


🎸 기타 사항 or 추가 코멘트

  • 스웨거 응답에는 timeout만 표시되어 잇는데, 실제 응답은 아래 이미지처럼 { data: { expiredAt: "..." } } 형태로 내려오고 있어 해당 구조 기준으로 파싱하게 해두었습니다!
image

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능

    • 서버 전송 이벤트(SSE)를 통한 실시간 출석 코드 만료 시간 업데이트
    • QR 코드 만료 시간이 이제 서버에서 실시간으로 동기화됨
  • 개선사항

    • 출석 체크인 워크플로우 개선으로 더욱 안정적인 상태 관리
    • 출석 코드 모달에서 직렬화된 시간 전달 방식 제거

여러 컴포넌트에서 useAttendanceSSE를 호출해도 EventSource 연결이
1개만 유지되도록 모듈 레벨 싱글턴 패턴 적용. 에러 시 지수 백오프
재연결 로직 추가.
- AttendanceCodeModal, AttendanceQRContent에서 SSE 구독으로 실시간
  expiredAt 수신
- AttendanceData 타입에 expiredAt 필드 추가
- useCheckIn에 sessionId/onSuccess 옵션 추가하여 AttendanceContent의
  중복 체크인 로직 제거
- useQRCode 호출을 관리자 전용으로 제한 (비관리자 403 방지)
useAttendanceQR, useQRCode를 hooks/ 루트에서 hooks/attendance/로
이동하고 barrel export 추가.
- strict equality 대신 includes로 Accept 헤더 검사
- response.ok 조건 추가로 업스트림 에러 시 즉시 에러 응답 반환
- 불필요한 connection: keep-alive 헤더 제거
@nabbang6 nabbang6 requested review from JIN921, dalzzy and woneeeee April 25, 2026 10:39
@nabbang6 nabbang6 self-assigned this Apr 25, 2026
@nabbang6 nabbang6 added 📬 API 서버 API 통신 ✨ Feature 기능 개발 labels Apr 25, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 25, 2026

Warning

Rate limit exceeded

@nabbang6 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 2 minutes and 21 seconds before requesting another review.

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 584740c2-be3b-4846-aedb-0ab2cf5fd22d

📥 Commits

Reviewing files that changed from the base of the PR and between b6a1aa1 and bbe60a4.

📒 Files selected for processing (8)
  • src/app/(private)/[clubId]/(main)/attendance/history/loading.tsx
  • src/app/(private)/[clubId]/(main)/attendance/loading.tsx
  • src/app/(private)/[clubId]/(main)/attendance/qr/loading.tsx
  • src/components/attendance/AttendanceCodeModal.tsx
  • src/components/attendance/AttendanceCompleteModal.tsx
  • src/components/attendance/AttendanceQRContent.tsx
  • src/hooks/attendance/useAttendanceSSE.ts
  • src/hooks/attendance/useCheckIn.ts
📝 Walkthrough

Walkthrough

출석 기능에서 endTime prop 기반의 만료 시스템을 Server-Sent Events(SSE) 기반의 실시간 업데이트 시스템으로 변경했습니다. 프록시 라우트에 SSE 처리 로직을 추가했으며, 새로운 useAttendanceSSE 훅을 통해 서버로부터 만료 시간을 스트리밍 방식으로 수신하도록 수정했습니다.

Changes

Cohort / File(s) Summary
API Proxy SSE 처리
src/app/api/proxy/[...path]/route.ts
클라이언트가 text/event-stream을 요청할 때 업스트림 응답을 스트리밍으로 반환하고, content-typetext/event-stream으로 설정하며 캐싱을 비활성화합니다.
SSE 훅 추가
src/hooks/attendance/useAttendanceSSE.ts
clubId별 EventSource 연결을 관리하고 캐시하는 새로운 훅으로, 구독자 등록/해제 시 연결을 열고 닫으며 메시지 수신 시 expiredAt을 업데이트하고 지수 백오프를 통해 자동 재연결합니다.
출석 컴포넌트에서 endTime 제거
src/components/attendance/AttendanceCodeModal.tsx, src/components/attendance/AttendanceTodayCard.tsx, src/components/home/TodayScheduleBox.tsx
endTime prop을 제거하고 SSE를 통해 만료 상태를 파생시킵니다.
출석 내용 컴포넌트 리팩토링
src/components/attendance/AttendanceContent.tsx, src/components/attendance/AttendanceQRContent.tsx
useCheckIn 훅 사용으로 체크인 로직 간소화, SSE 기반 만료 시간 표시로 변경합니다.
출석 훅 업데이트
src/hooks/attendance/useAttendanceQR.ts, src/hooks/attendance/useCheckIn.ts, src/hooks/attendance/useQRCode.ts
useAttendanceQR에서 타이밍 로직 제거, useCheckIn에 옵션 추가, useQRCode에 sessionId 매개변수 필수화합니다.
훅 인덱스 확장
src/hooks/attendance/index.ts
useAttendanceQR, useAttendanceSSE, useQRCode 재내보내기를 추가합니다.
타입 확장
src/types/attendance.ts
AttendanceData 인터페이스에 expiredAt 필드를 추가합니다.

Sequence Diagram(s)

sequenceDiagram
    participant Client as 클라이언트
    participant Component as 컴포넌트
    participant Hook as useAttendanceSSE
    participant EventSource as EventSource
    participant Server as 서버

    Client->>Component: 컴포넌트 마운트 (clubId 전달)
    Component->>Hook: useAttendanceSSE(clubId) 호출
    Hook->>EventSource: EventSource 연결<br/>('/api/proxy/clubs/{clubId}/attendances/stream')
    EventSource->>Server: SSE 연결 요청
    Server-->>EventSource: 스트림 시작 (text/event-stream)
    
    loop 메시지 수신
        Server->>EventSource: 메시지 전송 (expiredAt 포함)
        EventSource->>Hook: 메시지 파싱 및 처리
        Hook->>Hook: expiredAt 상태 업데이트<br/>& 리스너에 알림
        Hook-->>Component: 새로운 expiredAt 반환
        Component->>Component: UI 리렌더링
    end
    
    Note over EventSource: 재연결 로직<br/>(지수 백오프)
    
    Component->>Hook: 언마운트 또는 구독 해제
    Hook->>EventSource: 연결 종료
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

🔨 Refactor, ✨ Feature

Suggested reviewers

  • JIN921
  • dalzzy
  • woneeeee

Poem

🐰✨ SSE의 마법으로
실시간 업데이트 흐르고
서버와 클라이언트가
따뜻한 스트림으로 만나
새로운 출석 경험이 피어난다! 🌸

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 15.38% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목이 주요 변경사항(SSE 구독을 통한 출석 가능 시간 실시간 동기화)을 명확하게 요약하고 있어 변경 내용을 잘 설명합니다.
Description check ✅ Passed PR 설명이 템플릿의 필수 섹션들을 충실히 포함하고 있으며, 주요 변경사항, 관련 이슈, 스크린샷, 추가 코멘트가 모두 제시되어 있습니다.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch WTH-324-출석-SSE-구독-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.

@github-actions
Copy link
Copy Markdown

🤖 Claude 테스트 제안

모델: claude-sonnet-4-6 | 토큰: 0 입력 / 0 출력

변경된 컴포넌트에 대해 Claude가 생성한 테스트 코드입니다. 검토 후 적합한 부분만 사용하세요.

src/app/api/proxy/[...path]/route.ts

오류: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.


src/components/attendance/AttendanceCodeModal.tsx

오류: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.


src/components/attendance/AttendanceContent.tsx

오류: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.


src/components/attendance/AttendanceQRContent.tsx

오류: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.


src/components/attendance/AttendanceTodayCard.tsx

오류: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.


이 코멘트는 Claude API를 통해 자동 생성되었습니다. 반드시 검토 후 사용하세요.

@github-actions
Copy link
Copy Markdown

PR 테스트 결과

Jest: 통과

🎉 모든 테스트를 통과했습니다!

@github-actions
Copy link
Copy Markdown

구현한 기능 Preview: https://weeth-gstot5pur-weethsite-4975s-projects.vercel.app

@github-actions
Copy link
Copy Markdown

PR 검증 결과

TypeScript: 통과
ESLint: 통과
Prettier: 통과
Build: 통과

🎉 모든 검증을 통과했습니다!

Copy link
Copy Markdown

@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: 4

🧹 Nitpick comments (1)
src/app/api/proxy/[...path]/route.ts (1)

45-54: 역방향 프록시(Nginx 등)/배포 환경에서 SSE 응답이 버퍼링되지 않도록 X-Accel-Buffering: no 헤더 추가를 검토해 주세요.

일부 프록시(특히 Nginx)나 호스팅 플랫폼은 응답을 기본적으로 버퍼링하여 SSE 이벤트가 클라이언트에 즉시 전달되지 않을 수 있습니다. 개발 환경(스크린샷의 localhost:8080)에서는 확인이 어려울 수 있으니, 운영 환경에서 이벤트가 실시간으로 도착하는지 확인하시고 필요 시 다음 헤더를 추가해 주세요.

♻️ 제안
   if (isSSE) {
     responseHeaders.set('content-type', 'text/event-stream');
     responseHeaders.set('cache-control', 'no-cache');
+    responseHeaders.set('x-accel-buffering', 'no');
+    responseHeaders.set('connection', 'keep-alive');

     return new NextResponse(response.body, {
       status: response.status,
       statusText: response.statusText,
       headers: responseHeaders,
     });
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/api/proxy/`[...path]/route.ts around lines 45 - 54, When handling SSE
in the isSSE branch, add the reverse-proxy buffering header so responses are not
buffered by Nginx/hosting layers: set responseHeaders.set('X-Accel-Buffering',
'no') before returning the NextResponse in the isSSE block (where
responseHeaders, isSSE, and NextResponse are used) so the SSE stream is
forwarded immediately to clients in production.
🤖 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/api/proxy/`[...path]/route.ts:
- Around line 42-43: The SSE retry logic can loop forever on permanent errors
(401/403); update useAttendanceSSE to enforce a max retry limit (e.g.,
MAX_RETRY_ATTEMPTS constant) and stop retrying when exceeded, and add explicit
handling to treat 401 and 403 as terminal errors (set connection state to failed
and do not schedule further reconnection). Locate and change the code that
manipulates conn.retryCount (resetting only on success) to increment and compare
against MAX_RETRY_ATTEMPTS, and add a branch that checks response.status (or the
error code path) to immediately abort retries for 401/403 instead of backing
off; keep exponential backoff/reset behavior for transient errors only.

In `@src/components/attendance/AttendanceCodeModal.tsx`:
- Around line 38-39: AttendanceCodeModal currently treats sseExpiredAt === null
as an empty string and lets useRemainingTime mark isExpired true before the
first SSE arrives, disabling the submit button; update the component to
distinguish "not-yet-received" from real expiry by checking sseExpiredAt ===
null and, while null, render a placeholder/skeleton for the timer and force
isExpired to false (or introduce a new flag like isExpiredLoading) so the "출석 가능
시간이 만료되었습니다" branch is suppressed and disabled={!isComplete || isExpired} only
uses the real expiry value from useRemainingTime once sseExpiredAt is populated;
alternatively adapt useRemainingTime to accept null/undefined and return an
explicit loading boolean along with minutes, seconds, and isExpired, then wire
that loading flag into the UI and button disabled logic.

In `@src/components/attendance/AttendanceQRContent.tsx`:
- Around line 26-27: The timer shows "expired" briefly because
useAttendanceSSE's expiredAt is null initially and an empty string passed to
useRemainingTime yields isExpired=true; update AttendanceQRContent to avoid
rendering or calling useRemainingTime until sseExpiredAt is explicitly set: gate
the timer rendering (the block that uses minutes/seconds/isExpired and the call
to useRemainingTime) behind a check for sseExpiredAt (or pass undefined/null
through) and preserve existing isLoading behavior so that useRemainingTime is
only invoked with a valid timestamp (reference useAttendanceSSE, sseExpiredAt,
useRemainingTime, isLoading, and isExpired).

In `@src/hooks/attendance/useCheckIn.ts`:
- Around line 22-26: The isChecked computation incorrectly treats null === null
as true when both checkedSessionId and sessionId are null; update the isChecked
logic in useCheckIn to only compare checkedSessionId to sessionId when sessionId
is non-null (i.e., require sessionId != null/undefined before evaluating
checkedSessionId === sessionId) so that isChecked is false when there is no
active session, preserving the existing data?.status === 'ATTEND' check; modify
the expression that sets isChecked (referencing checkedSessionId, sessionId, and
isChecked) accordingly.

---

Nitpick comments:
In `@src/app/api/proxy/`[...path]/route.ts:
- Around line 45-54: When handling SSE in the isSSE branch, add the
reverse-proxy buffering header so responses are not buffered by Nginx/hosting
layers: set responseHeaders.set('X-Accel-Buffering', 'no') before returning the
NextResponse in the isSSE block (where responseHeaders, isSSE, and NextResponse
are used) so the SSE stream is forwarded immediately to clients in production.
🪄 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: f20cb3c0-00a5-459a-a8ab-76cf58f8c445

📥 Commits

Reviewing files that changed from the base of the PR and between ebf6da0 and b6a1aa1.

📒 Files selected for processing (12)
  • src/app/api/proxy/[...path]/route.ts
  • src/components/attendance/AttendanceCodeModal.tsx
  • src/components/attendance/AttendanceContent.tsx
  • src/components/attendance/AttendanceQRContent.tsx
  • src/components/attendance/AttendanceTodayCard.tsx
  • src/components/home/TodayScheduleBox.tsx
  • src/hooks/attendance/index.ts
  • src/hooks/attendance/useAttendanceQR.ts
  • src/hooks/attendance/useAttendanceSSE.ts
  • src/hooks/attendance/useCheckIn.ts
  • src/hooks/attendance/useQRCode.ts
  • src/types/attendance.ts
💤 Files with no reviewable changes (2)
  • src/components/home/TodayScheduleBox.tsx
  • src/components/attendance/AttendanceTodayCard.tsx

Comment on lines +42 to +43
const isSSE =
request.headers.get('accept')?.includes('text/event-stream') && response.ok && response.body;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
fd -e ts useAttendanceSSE --exec cat {}

Repository: Team-Weeth/weeth-client

Length of output: 2832


🏁 Script executed:

cat -n src/app/api/proxy/[...path]/route.ts | head -80

Repository: Team-Weeth/weeth-client

Length of output: 2663


비-2xx SSE 요청에서 응답 본문이 arrayBuffer로 소비되며, 재시도 횟수 제한이 없습니다.

upstream이 401/5xx로 응답하면 response.okfalse가 되어 line 56의 arrayBuffer() 분기로 진입하고, 에러 응답 본문이 그대로 반환됩니다. 브라우저 EventSource는 비-2xx 응답을 받으면 onerror를 호출하고 내장 재연결을 시도하며, useAttendanceSSE의 지수 백오프 로직이 이에 응답합니다.

문제는 useAttendanceSSE에 최대 재시도 횟수 제한이 없다는 점입니다. retryCount는 성공한 메시지 수신 시에만 리셋되며(conn.retryCount = 0), 지연시간은 MAX_RETRY_DELAY(30초)로 상한이 있지만 재시도 자체는 계속됩니다. 인증 만료(401) 같은 영구적 에러에서 무한 재시도가 발생할 수 있으므로, 재시도 횟수 상한 추가 또는 특정 상태 코드(예: 401, 403)에 대한 별도 처리 로직이 필요합니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/api/proxy/`[...path]/route.ts around lines 42 - 43, The SSE retry
logic can loop forever on permanent errors (401/403); update useAttendanceSSE to
enforce a max retry limit (e.g., MAX_RETRY_ATTEMPTS constant) and stop retrying
when exceeded, and add explicit handling to treat 401 and 403 as terminal errors
(set connection state to failed and do not schedule further reconnection).
Locate and change the code that manipulates conn.retryCount (resetting only on
success) to increment and compare against MAX_RETRY_ATTEMPTS, and add a branch
that checks response.status (or the error code path) to immediately abort
retries for 401/403 instead of backing off; keep exponential backoff/reset
behavior for transient errors only.

Comment thread src/components/attendance/AttendanceCodeModal.tsx
Comment thread src/components/attendance/AttendanceQRContent.tsx
Comment thread src/hooks/attendance/useCheckIn.ts Outdated
Copy link
Copy Markdown
Collaborator

@JIN921 JIN921 left a comment

Choose a reason for hiding this comment

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

오 SSE는 처음 보는데 이렇게 하나 배워갑니당!! 깔끔하게 구현하셔서 수정할 부분은 없어 보여용 수고하셧어요!

const [code, setCode] = useState('');
const { minutes, seconds, isExpired } = useRemainingTime(endTime);
const { expiredAt: sseExpiredAt } = useAttendanceSSE();
const { minutes, seconds, isExpired } = useRemainingTime(sseExpiredAt ?? '');
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

sseExpiredAt이 null 상태일 때 처리가 useRemainingTime에 잘 되어 잇나용? 아니라면 null일 때 카운트 다운 렌더가 안 되게 처리해도 좋을 거 같습니다!

Copy link
Copy Markdown
Member

@dalzzy dalzzy left a comment

Choose a reason for hiding this comment

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

수고하셨습니다~!! sse는 해본적이 없어서 신기하구만요,, 크게 수정할 부분 없는 것 같습니당 👍🏻

Copy link
Copy Markdown
Member

@woneeeee woneeeee left a comment

Choose a reason for hiding this comment

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

우왓 확인했습니당!! SSE도 잘 됐으면 좋겠네용...👍🏻

@github-actions
Copy link
Copy Markdown

🤖 Claude 테스트 제안

모델: claude-sonnet-4-6 | 토큰: 0 입력 / 0 출력

변경된 컴포넌트에 대해 Claude가 생성한 테스트 코드입니다. 검토 후 적합한 부분만 사용하세요.

src/app/api/proxy/[...path]/route.ts

오류: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.


src/components/attendance/AttendanceCodeModal.tsx

오류: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.


src/components/attendance/AttendanceContent.tsx

오류: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.


src/components/attendance/AttendanceQRContent.tsx

오류: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.


src/components/attendance/AttendanceTodayCard.tsx

오류: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.


이 코멘트는 Claude API를 통해 자동 생성되었습니다. 반드시 검토 후 사용하세요.

@github-actions
Copy link
Copy Markdown

PR 테스트 결과

Jest: 통과

🎉 모든 테스트를 통과했습니다!

@github-actions
Copy link
Copy Markdown

구현한 기능 Preview: https://weeth-f1k7br4sw-weethsite-4975s-projects.vercel.app

@github-actions
Copy link
Copy Markdown

PR 검증 결과

TypeScript: 통과
ESLint: 통과
Prettier: 통과
Build: 통과

🎉 모든 검증을 통과했습니다!

@github-actions
Copy link
Copy Markdown

🤖 Claude 테스트 제안

모델: claude-sonnet-4-6 | 토큰: 0 입력 / 0 출력

변경된 컴포넌트에 대해 Claude가 생성한 테스트 코드입니다. 검토 후 적합한 부분만 사용하세요.

src/app/(private)/[clubId]/(main)/attendance/history/loading.tsx

오류: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.


src/app/(private)/[clubId]/(main)/attendance/loading.tsx

오류: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.


src/app/(private)/[clubId]/(main)/attendance/qr/loading.tsx

오류: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.


src/app/api/proxy/[...path]/route.ts

오류: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.


src/components/attendance/AttendanceCodeModal.tsx

오류: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.


이 코멘트는 Claude API를 통해 자동 생성되었습니다. 반드시 검토 후 사용하세요.

@github-actions
Copy link
Copy Markdown

PR 테스트 결과

Jest: 통과

🎉 모든 테스트를 통과했습니다!

@github-actions
Copy link
Copy Markdown

PR 검증 결과

TypeScript: 통과
ESLint: 통과
Prettier: 통과
Build: 통과

🎉 모든 검증을 통과했습니다!

@github-actions
Copy link
Copy Markdown

구현한 기능 Preview: https://weeth-ducpdcob1-weethsite-4975s-projects.vercel.app

@nabbang6 nabbang6 merged commit d3ad61c into develop Apr 27, 2026
5 checks passed
@nabbang6 nabbang6 deleted the WTH-324-출석-SSE-구독-api-반영 branch April 29, 2026 07:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

📬 API 서버 API 통신 ✨ Feature 기능 개발

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants