Conversation
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
📝 WalkthroughWalkthrough인증 관련 세션 만료/데스크톱 로그인 플로우가 새로 구성되고, interceptor와 middleware가 토큰 기반 처리로 변경되었다. Dialog 컴포넌트가 size 컨텍스트와 Alert/Confirm sugar API로 개편되어 다수 화면에 적용되었다. 가챠 카드팩/부화 애니메이션(CardPackGame, GachaHatchGame)이 신규 도입되어 OnePet/TenPet과 기존 카드 게임(TenCardFlipGame, FanDrawingGame)을 대체했다. ChangesDialog 시스템 개편 및 마이그레이션
Estimated code review effort: 4 (Complex) | ~60 minutes 인증/세션 만료 및 데스크톱 로그인 플로우
Estimated code review effort: 3 (Moderate) | ~40 minutes 가챠 카드팩/부화 게임 신규 구현
Estimated code review effort: 4 (Complex) | ~55 minutes Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 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: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
apps/web/src/app/[locale]/shop/_petGotcha/TenPet.tsx (1)
61-68: 🔒 Security & Privacy | 🔴 Critical | ⚡ Quick winSlack 오류 메시지에서 access token을 제거하세요.
Line 67은 세션 access token을 외부 채널에 그대로 노출합니다. 또한
JSON.stringify(error)에도 요청 헤더가 포함될 수 있으므로 허용된 오류 필드만 전송해야 합니다.🔒 제안 수정
-Error Message: ${JSON.stringify(error)} +Error Message: ${error instanceof Error ? error.message : 'Unknown error'} \`\`\` User: ${data?.user.name} -Token: ${data?.user.accessToken}🤖 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/`[locale]/shop/_petGotcha/TenPet.tsx around lines 61 - 68, Update the error notification in the pet-draw failure handler around sendMessageToErrorChannel to remove data?.user.accessToken entirely and avoid serializing the full error object; include only explicitly allowed, non-sensitive error fields in the Slack message.packages/ui/tailwind/src/components/dialog/Dialog.tsx (1)
72-95: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
scrollable이screen/hero의 풀뷰포트 높이를 덮어쓰지 않도록 분기하세요.Line 94에서
scrollable을 사용하면h-screen뒤에h-[calc(100vh-40px)]가 병합되어, 여백 없는screen/hero계약이 깨집니다.수정 예시
-const dialogScrollableStyle = 'h-[calc(100vh-40px)] overflow-hidden [&_.dialog-title]:shrink-0'; +const dialogScrollableStyle = 'overflow-hidden [&_.dialog-title]:shrink-0'; ... className={cn( dialogContentCva({ size: resolvedSize }), scrollable && dialogScrollableStyle, + scrollable && + resolvedSize !== 'screen' && + resolvedSize !== 'hero' && + 'h-[calc(100vh-40px)]', className, )}🤖 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 `@packages/ui/tailwind/src/components/dialog/Dialog.tsx` around lines 72 - 95, Update the scrollable styling in Content so dialog sizes with the screen/hero full-viewport contract do not receive dialogScrollableStyle’s h-[calc(100vh-40px)] override. Apply the scrollable height rule only to applicable sizes while preserving overflow behavior and existing class merging for other dialog sizes.
🧹 Nitpick comments (2)
apps/web/src/app/[locale]/game/quiz/solve/_components/solving/SolvingQuizSection.tsx (1)
6-6: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
@gitanimals/ui-panda로 UI 컴포넌트 import를 통일하세요.
apps/web/src/app/[locale]/game/quiz/solve/_components/solving/SolvingQuizSection.tsxapps/web/src/app/[locale]/mypage/my-pet/(evolution)/EvolutionPersona.tsxapps/web/src/app/[locale]/mypage/my-pet/(merge)/MergePersona.tsx
Button과Dialog는@gitanimals/ui-tailwind대신@gitanimals/ui-panda에서 가져오면 됩니다.🤖 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/`[locale]/game/quiz/solve/_components/solving/SolvingQuizSection.tsx at line 6, Unify UI component imports by changing Button and Dialog imports from `@gitanimals/ui-tailwind` to `@gitanimals/ui-panda` in SolvingQuizSection.tsx (line 6), EvolutionPersona.tsx (line 7), and MergePersona.tsx (line 7); leave other imports unchanged.Source: Coding guidelines
apps/web/src/app/[locale]/game/quiz/_components/CreateOrSolve/SelectQuizType.tsx (1)
34-37: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
onConfirm내 불필요한close()호출 제거신규 도입된
Dialog.Confirm컴포넌트는 내부적으로onConfirm비동기/동기 작업이 완료된 직후 자동으로onOpenChange(false)를 호출하여 다이얼로그를 닫습니다. 따라서onConfirm콜백 내에서 수동으로close()를 호출할 필요가 없으며, 중복된 상태 업데이트를 방지하기 위해 제거를 권장합니다.
apps/web/src/app/[locale]/game/quiz/_components/CreateOrSolve/SelectQuizType.tsx#L34-L37:onConfirm내의close()호출을 제거하고onConfirm={handleCheckLanguage}형태로 간소화하세요.apps/web/src/app/[locale]/game/quiz/_components/CreateOrSolve/SelectQuizType.tsx#L51-L54:onConfirm내의close()호출을 제거하고onConfirm={() => router.push(ROUTE.GAME.QUIZ.SOLVE())}형태로 수정하세요.apps/web/src/app/[locale]/game/quiz/create/_components/QuizCreateForm.tsx#L53-L56:onConfirm내의close()호출을 제거하고onConfirm={toggleLocale}형태로 간소화하세요.🤖 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/`[locale]/game/quiz/_components/CreateOrSolve/SelectQuizType.tsx around lines 34 - 37, Remove the redundant close() calls from all Dialog.Confirm onConfirm handlers: in apps/web/src/app/[locale]/game/quiz/_components/CreateOrSolve/SelectQuizType.tsx lines 34-37, replace the wrapper with onConfirm={handleCheckLanguage}; at lines 51-54, use onConfirm={() => router.push(ROUTE.GAME.QUIZ.SOLVE())}; and in apps/web/src/app/[locale]/game/quiz/create/_components/QuizCreateForm.tsx lines 53-56, use onConfirm={toggleLocale}.
🤖 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/`[locale]/game/quiz/_components/CreateOrSolve/SelectQuizType.tsx:
- Line 4: Update imports in SelectQuizType.tsx (line 4), QuizCreateForm.tsx
(line 6), and useDialog.tsx (line 5) so Dialog and Button are sourced from
`@gitanimals/ui-panda` instead of `@gitanimals/ui-tailwind`; no other behavior
changes are needed.
In `@apps/web/src/app/`[locale]/shop/_petGotcha/GachaHatchGame.tsx:
- Around line 166-169: Update the failure branch after `await onDraw()` in
`GachaHatchGame` to stop calling `onClose()` when `res` is undefined, since
`OnePet.onDraw` already handles the error and closure; retain only the early
return.
In `@apps/web/src/components/Global/LoginCallbackWatcher.tsx`:
- Around line 3-31: Wrap the useSearchParams-dependent logic in
LoginCallbackWatcher with a React Suspense boundary, separating the existing
watcher implementation from the wrapper component as needed. Preserve the
callbackUrl capture, validation, storage, and URL cleanup behavior while
ensuring the globally inserted utility provides its own nearest Suspense
boundary.
In `@apps/web/src/components/Global/SessionExpiredDialog.tsx`:
- Line 4: Update the Button and Dialog import in SessionExpiredDialog to use the
`@gitanimals/ui-panda` package instead of `@gitanimals/ui-tailwind`, leaving the
component usage unchanged.
---
Outside diff comments:
In `@apps/web/src/app/`[locale]/shop/_petGotcha/TenPet.tsx:
- Around line 61-68: Update the error notification in the pet-draw failure
handler around sendMessageToErrorChannel to remove data?.user.accessToken
entirely and avoid serializing the full error object; include only explicitly
allowed, non-sensitive error fields in the Slack message.
In `@packages/ui/tailwind/src/components/dialog/Dialog.tsx`:
- Around line 72-95: Update the scrollable styling in Content so dialog sizes
with the screen/hero full-viewport contract do not receive
dialogScrollableStyle’s h-[calc(100vh-40px)] override. Apply the scrollable
height rule only to applicable sizes while preserving overflow behavior and
existing class merging for other dialog sizes.
---
Nitpick comments:
In
`@apps/web/src/app/`[locale]/game/quiz/_components/CreateOrSolve/SelectQuizType.tsx:
- Around line 34-37: Remove the redundant close() calls from all Dialog.Confirm
onConfirm handlers: in
apps/web/src/app/[locale]/game/quiz/_components/CreateOrSolve/SelectQuizType.tsx
lines 34-37, replace the wrapper with onConfirm={handleCheckLanguage}; at lines
51-54, use onConfirm={() => router.push(ROUTE.GAME.QUIZ.SOLVE())}; and in
apps/web/src/app/[locale]/game/quiz/create/_components/QuizCreateForm.tsx lines
53-56, use onConfirm={toggleLocale}.
In
`@apps/web/src/app/`[locale]/game/quiz/solve/_components/solving/SolvingQuizSection.tsx:
- Line 6: Unify UI component imports by changing Button and Dialog imports from
`@gitanimals/ui-tailwind` to `@gitanimals/ui-panda` in SolvingQuizSection.tsx (line
6), EvolutionPersona.tsx (line 7), and MergePersona.tsx (line 7); leave other
imports unchanged.
🪄 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: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: ff57aec2-b84b-4a36-b6d0-9f74758f5b90
⛔ Files ignored due to path filters (2)
apps/web/public/shop/egg-hatch.pngis excluded by!**/*.pngpnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (44)
apps/web/messages/en-US.jsonapps/web/messages/ko-KR.jsonapps/web/package.jsonapps/web/src/apis/interceptor.tsapps/web/src/app/[locale]/auth/desktop/page.tsxapps/web/src/app/[locale]/auth/error/page.tsxapps/web/src/app/[locale]/game/quiz/_components/CreateOrSolve/SelectQuizType.tsxapps/web/src/app/[locale]/game/quiz/create/_components/QuizCreateForm.tsxapps/web/src/app/[locale]/game/quiz/solve/_components/done/CompleteAlertDialog.tsxapps/web/src/app/[locale]/game/quiz/solve/_components/fail/FailAlertDialog.tsxapps/web/src/app/[locale]/game/quiz/solve/_components/solving/SolvingQuizSection.tsxapps/web/src/app/[locale]/game/quiz/solve/_components/success/CorrectConfirmDialog.tsxapps/web/src/app/[locale]/laboratory/_components/AlertDialog.tsxapps/web/src/app/[locale]/mypage/(github-custom)/FarmPersonaSelect.tsxapps/web/src/app/[locale]/mypage/(github-custom)/LinePersonaSelect.tsxapps/web/src/app/[locale]/mypage/my-pet/(evolution)/EvolutionPersona.tsxapps/web/src/app/[locale]/mypage/my-pet/(merge)/MergePersona.tsxapps/web/src/app/[locale]/mypage/my-pet/SelectedPetTable.tsxapps/web/src/app/[locale]/shop/_auction/PersonaSearch.tsxapps/web/src/app/[locale]/shop/_auction/SellSection/EditModal.tsxapps/web/src/app/[locale]/shop/_petGotcha/CardPackGame.tsxapps/web/src/app/[locale]/shop/_petGotcha/GachaHatchGame.tsxapps/web/src/app/[locale]/shop/_petGotcha/OnePet.tsxapps/web/src/app/[locale]/shop/_petGotcha/TenCardFlipGame.tsxapps/web/src/app/[locale]/shop/_petGotcha/TenPet.tsxapps/web/src/components/AuthButton.tsxapps/web/src/components/CardGame/FanDrawingGame/CardMotion.tsxapps/web/src/components/CardGame/FanDrawingGame/FanDrawingGame.tsxapps/web/src/components/ConfirmDialog/ConfirmDialog.tsxapps/web/src/components/ConfirmDialog/index.tsapps/web/src/components/Global/GlobalComponent.tsxapps/web/src/components/Global/LoginCallbackWatcher.tsxapps/web/src/components/Global/SessionExpiredDialog.tsxapps/web/src/components/Global/useDialog.tsxapps/web/src/components/RouteModal.tsxapps/web/src/middleware.tsapps/web/src/utils/sessionExpired.tspackage.jsonpackages/ui/tailwind/src/components/dialog/Alert.tsxpackages/ui/tailwind/src/components/dialog/CommonDialog.tsxpackages/ui/tailwind/src/components/dialog/Confirm.tsxpackages/ui/tailwind/src/components/dialog/Dialog.stories.tsxpackages/ui/tailwind/src/components/dialog/Dialog.tsxpackages/ui/tailwind/src/components/dialog/index.ts
💤 Files with no reviewable changes (10)
- apps/web/src/app/[locale]/game/quiz/solve/_components/success/CorrectConfirmDialog.tsx
- packages/ui/tailwind/src/components/dialog/CommonDialog.tsx
- apps/web/src/app/[locale]/shop/_petGotcha/TenCardFlipGame.tsx
- apps/web/src/app/[locale]/game/quiz/solve/_components/done/CompleteAlertDialog.tsx
- apps/web/src/app/[locale]/game/quiz/solve/_components/fail/FailAlertDialog.tsx
- apps/web/src/components/ConfirmDialog/ConfirmDialog.tsx
- apps/web/src/components/CardGame/FanDrawingGame/CardMotion.tsx
- apps/web/src/components/CardGame/FanDrawingGame/FanDrawingGame.tsx
- apps/web/src/app/[locale]/laboratory/_components/AlertDialog.tsx
- apps/web/src/components/ConfirmDialog/index.ts
| 'use client'; | ||
|
|
||
| import { useTranslations } from 'next-intl'; | ||
| import { Dialog } from '@gitanimals/ui-tailwind'; |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# ui-panda 패키지 내 Dialog 및 Button 컴포넌트 존재 여부 확인
fd -i -t f "dialog|button" packages/ui/pandaRepository: git-goods/git-animal-client
Length of output: 440
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect the relevant files and the ui-panda package entrypoints/exports.
printf '\n== file list ==\n'
git ls-files \
'apps/web/src/app/[locale]/game/quiz/_components/CreateOrSolve/SelectQuizType.tsx' \
'apps/web/src/app/[locale]/game/quiz/create/_components/QuizCreateForm.tsx' \
'apps/web/src/components/Global/useDialog.tsx' \
'packages/ui/panda/src/components/Dialog/Dialog.tsx' \
'packages/ui/panda/src/components/Button/Button.tsx' \
'packages/ui/panda/src/index.ts' \
'packages/ui/panda/src/components/index.ts' \
'packages/ui/panda/package.json' || true
printf '\n== package.json ==\n'
sed -n '1,220p' packages/ui/panda/package.json
printf '\n== index files ==\n'
for f in packages/ui/panda/src/index.ts packages/ui/panda/src/components/index.ts; do
if [ -f "$f" ]; then
echo "--- $f ---"
sed -n '1,220p' "$f"
fi
done
printf '\n== Dialog/Button implementations (heads) ==\n'
for f in packages/ui/panda/src/components/Dialog/Dialog.tsx packages/ui/panda/src/components/Button/Button.tsx; do
echo "--- $f ---"
sed -n '1,220p' "$f"
done
printf '\n== relevant app files (selected ranges) ==\n'
for f in \
apps/web/src/app/[locale]/game/quiz/_components/CreateOrSolve/SelectQuizType.tsx \
apps/web/src/app/[locale]/game/quiz/create/_components/QuizCreateForm.tsx \
apps/web/src/components/Global/useDialog.tsx
do
echo "--- $f ---"
nl -ba "$f" | sed -n '1,220p'
doneRepository: git-goods/git-animal-client
Length of output: 7342
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Map the ui-panda component export surface to see whether Dialog/Button are re-exported.
for f in packages/ui/panda/src/index.ts packages/ui/panda/src/components/index.ts; do
[ -f "$f" ] || continue
echo "--- outline $f ---"
ast-grep outline "$f" --view expanded || true
doneRepository: git-goods/git-animal-client
Length of output: 950
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Check whether the app code imports from ui-tailwind and whether ui-panda is already used nearby.
rg -n "`@gitanimals/ui-tailwind`|`@gitanimals/ui-panda`" \
apps/web/src/app/[locale]/game/quiz/_components/CreateOrSolve/SelectQuizType.tsx \
apps/web/src/app/[locale]/game/quiz/create/_components/QuizCreateForm.tsx \
apps/web/src/components/Global/useDialog.tsxRepository: git-goods/git-animal-client
Length of output: 529
UI 컴포넌트 임포트를 @gitanimals/ui-panda로 바꾸세요
apps/web/src/app/[locale]/game/quiz/_components/CreateOrSolve/SelectQuizType.tsxapps/web/src/app/[locale]/game/quiz/create/_components/QuizCreateForm.tsxapps/web/src/components/Global/useDialog.tsx
Dialog와 Button를 @gitanimals/ui-tailwind가 아니라 @gitanimals/ui-panda에서 가져오도록 맞추세요.
📍 Affects 3 files
apps/web/src/app/[locale]/game/quiz/_components/CreateOrSolve/SelectQuizType.tsx#L4-L4(this comment)apps/web/src/app/[locale]/game/quiz/create/_components/QuizCreateForm.tsx#L6-L6apps/web/src/components/Global/useDialog.tsx#L5-L5
🤖 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/`[locale]/game/quiz/_components/CreateOrSolve/SelectQuizType.tsx
at line 4, Update imports in SelectQuizType.tsx (line 4), QuizCreateForm.tsx
(line 6), and useDialog.tsx (line 5) so Dialog and Button are sourced from
`@gitanimals/ui-panda` instead of `@gitanimals/ui-tailwind`; no other behavior
changes are needed.
Source: Coding guidelines
| const res = await onDraw(); | ||
| if (!res) { | ||
| onClose(); // onDraw가 에러 토스트/닫기를 처리함 | ||
| return; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
실패 시 onClose()가 두 번 호출됩니다.
OnePet.onDraw가 이미 오류 처리와 onClose()를 수행한 뒤 undefined를 반환하므로, 여기서 다시 닫으면 router.back()이 두 번 실행되어 이전 페이지까지 이탈할 수 있습니다.
🐛 제안 수정
const res = await onDraw();
if (!res) {
- onClose(); // onDraw가 에러 토스트/닫기를 처리함
return;
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const res = await onDraw(); | |
| if (!res) { | |
| onClose(); // onDraw가 에러 토스트/닫기를 처리함 | |
| return; | |
| const res = await onDraw(); | |
| if (!res) { | |
| return; |
🤖 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/`[locale]/shop/_petGotcha/GachaHatchGame.tsx around lines
166 - 169, Update the failure branch after `await onDraw()` in `GachaHatchGame`
to stop calling `onClose()` when `res` is undefined, since `OnePet.onDraw`
already handles the error and closure; retain only the early return.
| import { useEffect } from 'react'; | ||
| import { useSearchParams } from 'next/navigation'; | ||
|
|
||
| import { LOCAL_STORAGE_KEY } from '@/constants/storage'; | ||
|
|
||
| // 보호 라우트에서 미들웨어가 홈으로 되돌릴 때 실어 보낸 `?callbackUrl` 을 캡처해 | ||
| // localStorage 에 저장한다. 사용자가 로그인하면 login()/LoginButton 이 이 값을 읽어 | ||
| // 원래 목적지로 자동 복귀시킨다. (useSearchParams 구독으로 클라 네비게이션 유입도 감지) | ||
| export function LoginCallbackWatcher() { | ||
| const searchParams = useSearchParams(); | ||
|
|
||
| useEffect(() => { | ||
| const callbackUrl = searchParams.get('callbackUrl'); | ||
| if (!callbackUrl) return; | ||
|
|
||
| // open-redirect 방지: 외부 절대 URL(`//`, `https://…`)은 무시하고 앱 내부 경로만 허용. | ||
| const isInternalPath = callbackUrl.startsWith('/') && !callbackUrl.startsWith('//'); | ||
| if (isInternalPath) { | ||
| localStorage.setItem(LOCAL_STORAGE_KEY.callbackUrl, callbackUrl); | ||
| } | ||
|
|
||
| const params = new URLSearchParams(searchParams.toString()); | ||
| params.delete('callbackUrl'); | ||
| const query = params.toString(); | ||
| window.history.replaceState(null, '', window.location.pathname + (query ? `?${query}` : '')); | ||
| }, [searchParams]); | ||
|
|
||
| return null; | ||
| } |
There was a problem hiding this comment.
🚀 Performance & Scalability | 🟠 Major | ⚡ Quick win
useSearchParams 사용 컴포넌트에 Suspense 바운더리 적용
Next.js 환경에서 클라이언트 컴포넌트가 useSearchParams를 사용할 때 가장 가까운 상위 Suspense 바운더리가 없으면, 빌드 시 해당 라우트가 정적 생성에서 제외되거나 강제 동적 렌더링(De-opt)이 발생할 수 있습니다. 전역으로 삽입되는 유틸리티 컴포넌트이므로 내부에서 Suspense로 감싸는 것을 권장합니다.
🛠️ 수정 제안
-import { useEffect } from 'react';
+import { useEffect, Suspense } from 'react';
import { useSearchParams } from 'next/navigation';
import { LOCAL_STORAGE_KEY } from '`@/constants/storage`';
// 보호 라우트에서 미들웨어가 홈으로 되돌릴 때 실어 보낸 `?callbackUrl` 을 캡처해
// localStorage 에 저장한다. 사용자가 로그인하면 login()/LoginButton 이 이 값을 읽어
// 원래 목적지로 자동 복귀시킨다. (useSearchParams 구독으로 클라 네비게이션 유입도 감지)
-export function LoginCallbackWatcher() {
+function LoginCallbackWatcherContent() {
const searchParams = useSearchParams();
useEffect(() => {
const callbackUrl = searchParams.get('callbackUrl');
if (!callbackUrl) return;
// open-redirect 방지: 외부 절대 URL(`//`, `https://…`)은 무시하고 앱 내부 경로만 허용.
const isInternalPath = callbackUrl.startsWith('/') && !callbackUrl.startsWith('//');
if (isInternalPath) {
localStorage.setItem(LOCAL_STORAGE_KEY.callbackUrl, callbackUrl);
}
const params = new URLSearchParams(searchParams.toString());
params.delete('callbackUrl');
const query = params.toString();
window.history.replaceState(null, '', window.location.pathname + (query ? `?${query}` : ''));
}, [searchParams]);
return null;
}
+
+export function LoginCallbackWatcher() {
+ return (
+ <Suspense fallback={null}>
+ <LoginCallbackWatcherContent />
+ </Suspense>
+ );
+}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| import { useEffect } from 'react'; | |
| import { useSearchParams } from 'next/navigation'; | |
| import { LOCAL_STORAGE_KEY } from '@/constants/storage'; | |
| // 보호 라우트에서 미들웨어가 홈으로 되돌릴 때 실어 보낸 `?callbackUrl` 을 캡처해 | |
| // localStorage 에 저장한다. 사용자가 로그인하면 login()/LoginButton 이 이 값을 읽어 | |
| // 원래 목적지로 자동 복귀시킨다. (useSearchParams 구독으로 클라 네비게이션 유입도 감지) | |
| export function LoginCallbackWatcher() { | |
| const searchParams = useSearchParams(); | |
| useEffect(() => { | |
| const callbackUrl = searchParams.get('callbackUrl'); | |
| if (!callbackUrl) return; | |
| // open-redirect 방지: 외부 절대 URL(`//`, `https://…`)은 무시하고 앱 내부 경로만 허용. | |
| const isInternalPath = callbackUrl.startsWith('/') && !callbackUrl.startsWith('//'); | |
| if (isInternalPath) { | |
| localStorage.setItem(LOCAL_STORAGE_KEY.callbackUrl, callbackUrl); | |
| } | |
| const params = new URLSearchParams(searchParams.toString()); | |
| params.delete('callbackUrl'); | |
| const query = params.toString(); | |
| window.history.replaceState(null, '', window.location.pathname + (query ? `?${query}` : '')); | |
| }, [searchParams]); | |
| return null; | |
| } | |
| import { useEffect, Suspense } from 'react'; | |
| import { useSearchParams } from 'next/navigation'; | |
| import { LOCAL_STORAGE_KEY } from '`@/constants/storage`'; | |
| // 보호 라우트에서 미들웨어가 홈으로 되돌릴 때 실어 보낸 `?callbackUrl` 을 캡처해 | |
| // localStorage 에 저장한다. 사용자가 로그인하면 login()/LoginButton 이 이 값을 읽어 | |
| // 원래 목적지로 자동 복귀시킨다. (useSearchParams 구독으로 클라 네비게이션 유입도 감지) | |
| function LoginCallbackWatcherContent() { | |
| const searchParams = useSearchParams(); | |
| useEffect(() => { | |
| const callbackUrl = searchParams.get('callbackUrl'); | |
| if (!callbackUrl) return; | |
| // open-redirect 방지: 외부 절대 URL(`//`, `https://…`)은 무시하고 앱 내부 경로만 허용. | |
| const isInternalPath = callbackUrl.startsWith('/') && !callbackUrl.startsWith('//'); | |
| if (isInternalPath) { | |
| localStorage.setItem(LOCAL_STORAGE_KEY.callbackUrl, callbackUrl); | |
| } | |
| const params = new URLSearchParams(searchParams.toString()); | |
| params.delete('callbackUrl'); | |
| const query = params.toString(); | |
| window.history.replaceState(null, '', window.location.pathname + (query ? `?${query}` : '')); | |
| }, [searchParams]); | |
| return null; | |
| } | |
| export function LoginCallbackWatcher() { | |
| return ( | |
| <Suspense fallback={null}> | |
| <LoginCallbackWatcherContent /> | |
| </Suspense> | |
| ); | |
| } |
🤖 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/components/Global/LoginCallbackWatcher.tsx` around lines 3 - 31,
Wrap the useSearchParams-dependent logic in LoginCallbackWatcher with a React
Suspense boundary, separating the existing watcher implementation from the
wrapper component as needed. Preserve the callbackUrl capture, validation,
storage, and URL cleanup behavior while ensuring the globally inserted utility
provides its own nearest Suspense boundary.
| 'use client'; | ||
|
|
||
| import { useTranslations } from 'next-intl'; | ||
| import { Button, Dialog } from '@gitanimals/ui-tailwind'; |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
코딩 가이드라인 준수: UI 컴포넌트 임포트 경로 변경
코딩 가이드라인에 따라 UI 컴포넌트는 @gitanimals/ui-tailwind가 아닌 @gitanimals/ui-panda 패키지에서 가져와야 합니다.
♻️ 수정 제안
-import { Button, Dialog } from '`@gitanimals/ui-tailwind`';
+import { Button, Dialog } from '`@gitanimals/ui-panda`';📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| import { Button, Dialog } from '@gitanimals/ui-tailwind'; | |
| import { Button, Dialog } from '`@gitanimals/ui-panda`'; |
🤖 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/components/Global/SessionExpiredDialog.tsx` at line 4, Update
the Button and Dialog import in SessionExpiredDialog to use the
`@gitanimals/ui-panda` package instead of `@gitanimals/ui-tailwind`, leaving the
component usage unchanged.
Source: Coding guidelines
개요
dev 브랜치에 머지된 4개 작업을 main으로 배포합니다.
주요 변경사항
🥚 상점 펫 뽑기 UX 개편
GachaHatchGame)CardPackGame), 기존TenCardFlipGame·FanDrawingGame제거💬 Dialog 시스템 재설계 (#398)
Alert/Confirmsugar API 도입, 기존CommonDialog·ConfirmDialog정리🔐 로그인 복구 UX 개선 (#387)
SessionExpiredDialog,LoginCallbackWatcher)middleware.ts세션 만료 처리 보강,sessionExpired유틸 추가변경 규모
46 files changed, +1738 / -1101
Summary by CodeRabbit
새 기능
개선