refactor: invite 진입 분기 로직을 클라이언트에서 RSC로 이관 - #412
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Discord 스레드 연동용 메타데이터입니다. discord-pr-bot 워크플로가 자동 생성하며, 수정·삭제하면 PR 과 Discord 알림 연동이 끊깁니다. |
|
Warning Review limit reached
Next review available in: 58 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (7)
📝 WalkthroughWalkthrough초대 페이지가 클라이언트 검증에서 서버 미리보기와 리디렉션 방식으로 변경되었습니다. 무효·만료 링크 UI가 분리되었습니다. 회원 사용자는 참여 페이지에서 자동 참여하며, 기존 참여자는 토너먼트 화면으로 이동합니다. Changes초대 진입 및 참여 흐름
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant InvitePage as 초대 페이지
participant PreviewApi as getInvitePreviewByCode
participant ServerApi as serverApi
participant JoinPreview as JoinPreviewClient
participant TournamentApi as 참여 mutation
InvitePage->>PreviewApi: 초대 코드 미리보기 요청
PreviewApi->>ServerApi: 서버 API 호출
ServerApi-->>PreviewApi: 초대 미리보기 반환
PreviewApi-->>InvitePage: 토너먼트 및 참여 상태 전달
InvitePage->>JoinPreview: 미참여 사용자를 참여 페이지로 리디렉션
JoinPreview->>TournamentApi: 회원 자동 참여 요청
TournamentApi-->>JoinPreview: 참여 성공 또는 오류 반환
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ 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 |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (1)
apps/web/src/app/invite/[id]/_components/InviteInvalid.tsx (1)
38-43: 📐 Maintainability & Code Quality | 🔵 TrivialTODO: 단일 오류 타입 처리.
주석은 409가 만료·이미 참여·이미 시작 등 여러 경우를 포함하지만 서버가 세부 오류 코드를 내려주지 않아
LINK_EXPIRED하나로 처리한다고 명시합니다. 백엔드에서 세부 오류 코드를 제공하도록 후속 작업을 진행할 계획이 있다면 알려주세요. 필요하면 타입별 분기 처리 초안 작성을 도와드리겠습니다.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/src/app/invite/`[id]/_components/InviteInvalid.tsx around lines 38 - 43, 현재 서버가 세부 오류 코드를 제공하지 않으므로 InviteInvalid의 TournamentErrorDialog는 LINK_EXPIRED 단일 타입 처리를 유지하세요. 백엔드에서 만료·참여 중·시작됨을 구분하는 오류 코드를 제공할 계획이 있다면 해당 후속 작업을 확인한 뒤 타입별 분기를 추가하세요.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/web/src/app/invite/`[id]/page.tsx:
- Line 46: Update the redirect URL construction to encode the code query value
before interpolation, using encodeURIComponent(code) in the redirect call while
preserving the existing tournament join route and parameter structure.
- Around line 30-46: Update the invite flow around getInvitePreviewByCode and
the redirect to ROUTES.TOURNAMENT_JOIN_BY_LINK so the fetched preview is stored
through the shared query cache, such as queryClient.setQueryData(), under the
same query key and response shape consumed by JoinPreviewClient. Preserve the
existing invalid-invite, tournament mismatch, and joined-user behavior while
allowing the join page to reuse tournamentName, itemCount, participantCount, and
joined without requesting /tournaments/${id}/invite-preview again.
In `@apps/web/src/app/tournament/join/`[id]/_components/JoinPreviewClient.tsx:
- Around line 97-136: Track non-409 failures from joinTournament in the member
auto-join flow using component state, and render a retry or exit guidance view
instead of the perpetual spinner when that state is set. Update the
joinTournament error handling and the isMember rendering branch around
hasAutoJoinRunRef so failed attempts expose an actionable recovery path while
preserving the existing success and already-joined navigation behavior.
---
Nitpick comments:
In `@apps/web/src/app/invite/`[id]/_components/InviteInvalid.tsx:
- Around line 38-43: 현재 서버가 세부 오류 코드를 제공하지 않으므로 InviteInvalid의
TournamentErrorDialog는 LINK_EXPIRED 단일 타입 처리를 유지하세요. 백엔드에서 만료·참여 중·시작됨을 구분하는 오류
코드를 제공할 계획이 있다면 해당 후속 작업을 확인한 뒤 타입별 분기를 추가하세요.
🪄 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: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 345fe46d-f594-489b-bcd9-ec0236cb60df
📒 Files selected for processing (5)
apps/web/src/app/invite/[id]/_components/InviteClient.tsxapps/web/src/app/invite/[id]/_components/InviteInvalid.tsxapps/web/src/app/invite/[id]/page.tsxapps/web/src/app/tournament/join/[id]/_components/JoinPreviewClient.tsxapps/web/src/app/tournament/join/_apis/getInvitePreviewByCode.ts
💤 Files with no reviewable changes (1)
- apps/web/src/app/invite/[id]/_components/InviteClient.tsx
| let preview; | ||
| try { | ||
| preview = await getInvitePreviewByCode(code); | ||
| } catch (error) { | ||
| /** 409(만료·비활성 초대)는 만료 다이얼로그 노출, 그 외(400 코드 불일치 등)는 안내 화면만 */ | ||
| const isExpired = isAxiosError<ApiErrorResponseT>(error) && error.response?.status === 409; | ||
| return <InviteInvalid showExpiredDialog={isExpired} />; | ||
| } | ||
|
|
||
| /** 코드의 토너먼트가 URL path 와 다르면 잘못된 링크 */ | ||
| if (preview.tournamentId !== tournamentId) return <InviteInvalid />; | ||
|
|
||
| /** 이미 참여한 유저(회원·게스트 공통) → join 건너뛰고 토너먼트로 바로 진입 */ | ||
| if (preview.joined) redirect(ROUTES.TOURNAMENT_CREATE(tournamentId)); | ||
|
|
||
| /** 미참여 → 참여 방식(회원 자동 / 게스트 닉네임 입력)은 join 페이지가 소유 */ | ||
| redirect(`${ROUTES.TOURNAMENT_JOIN_BY_LINK(tournamentId)}?code=${code}`); |
There was a problem hiding this comment.
🚀 Performance & Scalability | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Confirm whether the invite preview data fetched by code and the join-page preview fetched by tournamentId hit the same backend resource.
set -euo pipefail
fd -e ts -e tsx useGetInvitePreview apps/web/src | xargs -I{} sh -c 'echo "--- {} ---"; cat -n {}'
rg -n 'TOURNAMENT_INVITE_PREVIEW' apps/web/src -C2Repository: TeamPiKi/client
Length of output: 3341
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "--- locate relevant files ---"
fd -e ts -e tsx 'page\.tsx|getInvitePreview(ByCode)?|JoinPreviewClient\.tsx|useGetInvitePreview|api\.ts' apps/web/src | sort | sed -n '1,120p'
echo "--- invite page ---"
cat -n apps/web/src/app/invite/[id]/page.tsx
echo "--- join client preview usage ---"
cat -n apps/web/src/app/tournament/join/_components/JoinPreviewClient.tsx
cat -n apps/web/src/app/tournament/join/_apis/getInvitePreview.ts
cat -n apps/web/src/app/tournament/join/_apis/getInvitePreviewByCode.ts
echo "--- route references ---"
rg -n 'InviteInvalid|showExpiredDialog|TOURNAMENT_JOIN_BY_LINK|TournamentJoin|JoinPreviewClient|useGetInvite|getInvitePreview' apps/web/src/app -C 3
echo "--- constants/types mentions ---"
grep -Rn 'GetInvitePreviewResponseT|invite-preview|invite-preview-by-code|TOURNAMENT_INVITE_PREVIEW' apps/web/src -C 2Repository: TeamPiKi/client
Length of output: 3657
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "--- join related files ---"
for f in \
apps/web/src/app/tournament/join/[id]/page.tsx \
apps/web/src/app/tournament/join/_apis/getInvitePreview.ts \
apps/web/src/app/tournament/join/_apis/getInvitePreviewByCode.ts \
apps/web/src/app/tournament/join/_hooks/useGetInvitePreview.ts \
apps/web/src/consts/api.ts \
apps/web/src/types/api.ts; do
if [ -f "$f" ]; then
echo "### $f"
cat -n "$f"
fi
done
echo "--- preview type and endpoint references ---"
rg -n 'GetInvitePreview|toInvitePreview|TOURNAMENT_INVITE_PREVIEW|INVITE_PREVIEW|preview' apps/web/src -C 3
echo "--- API endpoint definitions/usages outside web src if tracked in repo ---"
git ls-files | rg '\.(ts|tsx)$' | xargs rg -n 'TOURNAMENT_INVITE_PREVIEW|by-invite-code|invite-preview|invitePreview' -C 2 2>/dev/null || trueRepository: TeamPiKi/client
Length of output: 30509
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "--- invite client component ---"
cat -n apps/web/src/app/invite/[id]/_components/InviteInvalid.tsx
echo "--- join client component relevant sections ---"
cat -n apps/web/src/app/tournament/join/[id]/_components/JoinPreviewClient.tsx | sed -n '1,180p'
echo "--- join types ---"
cat -n apps/web/src/app/tournament/join/_types/join.tsRepository: TeamPiKi/client
Length of output: 9725
초대 미리보기 조회 결과를 참여 페이지까지 전달하세요.
apps/web/src/app/invite/[id]/page.tsx에서 getInvitePreviewByCode(code)로 tournamentName, itemCount, participantCount, joined를 취득한 뒤 리다이렉트되면 이 값은 버려집니다. 이후 tournament/join/[id]_components/JoinPreviewClient.tsx가 같은 응답 구조의 미리보기를 /tournaments/${id}/invite-preview로 다시 요청합니다. queryClient.setQueryData() 같은 공유 방식에 미리보기 값을 녹여 중복 서버 요청과 브라우저 API 왕복을 줄이세요.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@apps/web/src/app/invite/`[id]/page.tsx around lines 30 - 46, Update the
invite flow around getInvitePreviewByCode and the redirect to
ROUTES.TOURNAMENT_JOIN_BY_LINK so the fetched preview is stored through the
shared query cache, such as queryClient.setQueryData(), under the same query key
and response shape consumed by JoinPreviewClient. Preserve the existing
invalid-invite, tournament mismatch, and joined-user behavior while allowing the
join page to reuse tournamentName, itemCount, participantCount, and joined
without requesting /tournaments/${id}/invite-preview again.
두 개 이상 top-level 라우트(home, invite)에서 쓰이므로 라우트 전용 _apis 가 아닌 공용 src/apis 로 옮긴다. 응답 타입도 src/types/tournament.ts 로 함께 이동.
- 참여 성공 시 router.push -> replace 로 변경해 뒤로가기 재참여(409) 차단 - isAutoJoinFailed boolean 을 joining/retryable/blocked 상태로 통합 - 409 는 재시도가 무의미하므로 홈으로 가기 종료 화면 노출
* refactor: invite 진입 분기를 클라이언트에서 RSC redirect 로 이관 * refactor: 회원 자동 join 을 invite 에서 join 페이지로 이관 * fix: 리다이렉트 code 인코딩 및 자동 참여 실패 시 재시도 UI 추가 * style: prettier 포맷 적용 * refactor: getInvitePreviewByCode 를 src/apis 로 이동 두 개 이상 top-level 라우트(home, invite)에서 쓰이므로 라우트 전용 _apis 가 아닌 공용 src/apis 로 옮긴다. 응답 타입도 src/types/tournament.ts 로 함께 이동. * fix: 회원 자동 참여 409 시 무한 스피너 갇힘 해소 - 참여 성공 시 router.push -> replace 로 변경해 뒤로가기 재참여(409) 차단 - isAutoJoinFailed boolean 을 joining/retryable/blocked 상태로 통합 - 409 는 재시도가 무의미하므로 홈으로 가기 종료 화면 노출
충돌 해결: - InviteClient.tsx: dev 의 삭제 수용 (#412 로 RSC 이관) - JoinPreviewClient.tsx: dev 의 회원 자동 참여 구조 + 에러 처리는 usePostJoin 훅 레벨로 (mutate 레벨 onError 는 전역 fallback 과 토스트가 겹치고 문구도 하드코딩이었음) - usePostWishLink / usePostTournamentItemLink: 카탈로그 기반 문구 + dev 의 showErrorToast 옵션 결합 - error.tsx: dev 의 시안 디자인 유지, 문구는 서버 code 있으면 카탈로그로 대체 - types/tournament.ts: 양쪽 타입 추가분 합침
작업 요약
작업 세부 내용
/invite/[id]의 초대 코드 검증 → 참여 여부 판별 → 목적지 결정을 InviteClient 의 useEffect 에서 RSC(page.tsx)로 올렸습니다. (#390 작업 상세 4개 항목 전부 반영)invite/[id]/page.tsx — 판단은 서버가
InviteInvalid렌더,joined면 토너먼트로, 미참여면 join 페이지로redirect()redirect()는 NEXT_REDIRECT throw 방식이라 try 밖에서 호출 (catch 가 삼키지 않도록 preview 조회만 감쌈)showExpiredDialogprop 으로 기존 만료 다이얼로그 UX 유지InviteClient → InviteInvalid
getInvitePreviewByCode
environmentManager.isServer()분기 추가 — RSC 에서 serverApi(쿠키 포함)로 호출 (getInvitePreview.ts패턴)회원 자동 join → join 페이지로 이관
JoinPreviewClient가 참여 방식을 소유: 회원 = 자동 join(스피너) / 게스트 = 닉네임 입력joined가드로 409 방지)useCallback으로 재호출 방어효과
테스트
연관 이슈
closes #390
Summary by CodeRabbit
새 기능
버그 수정