Conversation
둘러보기온보딩 매치 생성 플로우를 재구성하여 선택한 게임 정보(SelectedGame)와 날짜를 상태로 관리하도록 변경했고, 완료 단계에서 React Query 뮤테이션으로 매치를 생성한 뒤 홈으로 네비게이션하면서 토스트를 표시하도록 흐름과 프롭 시그니처를 업데이트했습니다. 변경 사항
시퀀스 다이어그램sequenceDiagram
participant User as 사용자
participant Onboarding as 온보딩 페이지
participant DateSelect as 날짜 선택 컴포넌트
participant GameMatch as GameMatch BottomSheet
participant API as 서버 / React Query
participant Home as 홈 페이지
participant Toast as 토스트
User->>Onboarding: 온보딩 진입
Onboarding->>DateSelect: DATE_SELECT 단계 렌더링
User->>DateSelect: 게임 및 날짜 선택
DateSelect->>Onboarding: onComplete({game, date})
Onboarding->>Onboarding: pendingMatch 상태 저장
User->>Onboarding: COMPLETE에서 "만들기" 클릭
Onboarding->>API: CREATE_MATCH 뮤테이션 요청 (gameId, matchType)
API-->>Onboarding: 생성 성공
Onboarding->>Home: navigate(..., state: { shouldShowMatchCreatedToast: true })
Home->>Home: useEffect에서 상태 감지
Home->>Toast: showErrorToast(CREATE_MATCH_TOAST_MESSAGE)
Toast->>User: 토스트 표시
예상 코드 리뷰 노력🎯 4 (Complex) | ⏱️ ~50 minutes 관련 가능성 있는 PR
제안 리뷰어
시
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 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 |
|
MATEBALL-STORYBOOK |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (5)
src/shared/constants/error-toast.ts (1)
25-25: 문자열 끝부분 공백 확인메시지 끝에 불필요한 공백이 포함되어 있습니다 (
'...확인해 보세요! 🎉 '). 의도적인 것이 아니라면 제거를 권장합니다.🔧 수정 제안
-export const CREATE_MATCH_TOAST_MESSAGE = '방금 만든 카드를 매칭 현황에서 확인해 보세요! 🎉 '; +export const CREATE_MATCH_TOAST_MESSAGE = '방금 만든 카드를 매칭 현황에서 확인해 보세요! 🎉';🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/shared/constants/error-toast.ts` at line 25, The constant CREATE_MATCH_TOAST_MESSAGE contains an unintended trailing space at the end of the string; remove the trailing whitespace from the string value assigned to CREATE_MATCH_TOAST_MESSAGE so it ends with the emoji and no extra space, then run a quick search for usages of CREATE_MATCH_TOAST_MESSAGE to ensure no code relied on that trailing space.src/pages/onboarding/components/complete.tsx (1)
15-21:enabled: true제거 및 로딩 상태 고려
enabled: true는 기본값이므로 제거해도 됩니다.useQuery사용 시 데이터 로딩 중undefined상태가 발생합니다. 현재 fallback 값으로 처리되지만, 부모 컴포넌트의 Suspense 경계와 일관성을 유지하려면useSuspenseQuery사용을 고려해 보세요.♻️ enabled 옵션 제거
const { data } = useQuery({ ...userQueries.USER_INFO(), - enabled: true, });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/pages/onboarding/components/complete.tsx` around lines 15 - 21, Remove the redundant enabled: true option from the useQuery call and replace useQuery with the Suspense-aware hook (useSuspenseQuery) that corresponds to userQueries.USER_INFO() so the parent Suspense boundary handles loading; update the component to read data via useSuspenseQuery instead of useQuery and keep data?.nickname and data?.imgUrl handling as before (nickname and imgUrl variables) so you no longer need to guard for an intermediate undefined loading state inside this component.src/pages/home/home.tsx (1)
74-87: useEffect 의존성 배열 최적화 권장
location객체 전체를 의존성으로 사용하면 경로 변경 시마다 effect가 재실행됩니다. 토스트 표시 후navigate()로 상태를 초기화하므로 무한 루프는 발생하지 않지만,location.state만 의존성으로 사용하는 것이 더 명확합니다.♻️ 의존성 배열 개선
useEffect(() => { - if (location.state?.shouldShowMatchCreatedToast) { + const shouldShow = location.state?.shouldShowMatchCreatedToast; + if (shouldShow) { showErrorToast(CREATE_MATCH_TOAST_MESSAGE, { offset: '8.8rem', icon: false, className: 'bg-sub-900 text-gray-black cap_14_sb', }); navigate(location.pathname, { replace: true, state: {}, }); } - }, [location, navigate]); + }, [location.state, location.pathname, navigate]);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/pages/home/home.tsx` around lines 74 - 87, The effect currently depends on the entire location and navigate which causes unnecessary re-runs; change the dependency array of the useEffect that checks location.state?.shouldShowMatchCreatedToast (the block that calls showErrorToast and then navigate(..., { state: {} })) to depend only on location.state?.shouldShowMatchCreatedToast (or that boolean expression) instead of the full location object so the toast logic only runs when the relevant state flag changes.src/shared/utils/show-error-toast.tsx (1)
21-27: 템플릿 리터럴 공백 및 className 동작 확인
- 멀티라인 템플릿 리터럴은 불필요한 공백/개행을 포함할 수 있어 CSS 클래스 파싱에 영향을 줄 수 있습니다.
- 커스텀
className제공 시 기본 스타일(cap_14_m text-gray-white bg-gray-700)이 완전히 대체되므로, 호출부에서 필요한 모든 스타일을 명시해야 합니다.♻️ 공백 최소화 및 명확한 클래스 조합
- className: ` - !min-h-[4.5rem] - max-w-[calc(43rem-3.2rem)] - w-[calc(100%-3.2rem)] - rounded-[12px] - ${className || 'cap_14_m text-gray-white bg-gray-700'} - `, + className: [ + '!min-h-[4.5rem]', + 'max-w-[calc(43rem-3.2rem)]', + 'w-[calc(100%-3.2rem)]', + 'rounded-[12px]', + className || 'cap_14_m text-gray-white bg-gray-700', + ].join(' '),🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/shared/utils/show-error-toast.tsx` around lines 21 - 27, The template literal for the className in show-error-toast (the className property construction) contains multiline whitespace and currently replaces default styles when a custom className is passed; change it to build a single space-joined class string (or use a utility like clsx) that trims newlines and always merges defaults with any provided className (e.g., join an array of the fixed classes and className if present) so there are no stray whitespace/newline characters and custom className augments instead of completely replacing the defaults.src/pages/onboarding/components/date-select.tsx (1)
14-24:SelectedGame타입은 공용 위치로 분리해 주세요.이 타입이
date-select.tsx에 있으면src/shared/components/bottom-sheet/game-match/game-match-bottom-sheet.tsx같은 shared 레이어가 onboarding 페이지 파일을 역참조하게 됩니다. 지금처럼 바텀시트와 COMPLETE 단계에서 함께 쓰는 타입이면shared/types나 onboarding 전용types.ts로 옮겨 의존성 방향을 바로잡는 편이 안전합니다.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/pages/onboarding/components/date-select.tsx` around lines 14 - 24, Move the SelectedGame interface out of date-select.tsx into a shared types module (e.g., export it from a central shared/types or an onboarding-specific types.ts) so it can be reused without reverse-importing onboarding pages; update date-select.tsx to import { SelectedGame } from the new module and keep DateSelectProps unchanged (using the imported SelectedGame), then update any other consumers like game-match-bottom-sheet to import the same shared type to eliminate the onboarding -> shared reverse dependency and prevent circular imports.
🤖 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/pages/onboarding/components/complete-button-section.tsx`:
- Around line 21-23: The CREATE_MATCH mutation can be triggered multiple times
if the CTA is clicked rapidly; add an isPending guard and disable the button
while the mutation is in-flight: in the handler that calls
createMatchMutation.mutate or CREATE_MATCH (refer to createMatchMutation and
handleCreate / handleGoToMate) check createMatchMutation.isLoading (or an
explicit isPending state) and return early if true, and also bind that same flag
to the CTA's disabled prop so the button is inert during the request; ensure any
additional callers in this file (lines around handleGoToMate and the other
handlers) use the same guard to prevent duplicate CREATE_MATCH invocations.
In `@src/shared/styles/global.css`:
- Around line 49-52: 삭제할 전역 CSS 규칙: .toast-text가 프로젝트에서 사용되지 않으므로 global.css에서
해당 규칙을 제거하세요; 관련 컴포넌트는 show-error-toast.tsx에서 className으로 Tailwind를 직접 적용하므로
.toast-text 선택자는 불필요하며 파일에서 안전하게 삭제하면 됩니다.
---
Nitpick comments:
In `@src/pages/home/home.tsx`:
- Around line 74-87: The effect currently depends on the entire location and
navigate which causes unnecessary re-runs; change the dependency array of the
useEffect that checks location.state?.shouldShowMatchCreatedToast (the block
that calls showErrorToast and then navigate(..., { state: {} })) to depend only
on location.state?.shouldShowMatchCreatedToast (or that boolean expression)
instead of the full location object so the toast logic only runs when the
relevant state flag changes.
In `@src/pages/onboarding/components/complete.tsx`:
- Around line 15-21: Remove the redundant enabled: true option from the useQuery
call and replace useQuery with the Suspense-aware hook (useSuspenseQuery) that
corresponds to userQueries.USER_INFO() so the parent Suspense boundary handles
loading; update the component to read data via useSuspenseQuery instead of
useQuery and keep data?.nickname and data?.imgUrl handling as before (nickname
and imgUrl variables) so you no longer need to guard for an intermediate
undefined loading state inside this component.
In `@src/pages/onboarding/components/date-select.tsx`:
- Around line 14-24: Move the SelectedGame interface out of date-select.tsx into
a shared types module (e.g., export it from a central shared/types or an
onboarding-specific types.ts) so it can be reused without reverse-importing
onboarding pages; update date-select.tsx to import { SelectedGame } from the new
module and keep DateSelectProps unchanged (using the imported SelectedGame),
then update any other consumers like game-match-bottom-sheet to import the same
shared type to eliminate the onboarding -> shared reverse dependency and prevent
circular imports.
In `@src/shared/constants/error-toast.ts`:
- Line 25: The constant CREATE_MATCH_TOAST_MESSAGE contains an unintended
trailing space at the end of the string; remove the trailing whitespace from the
string value assigned to CREATE_MATCH_TOAST_MESSAGE so it ends with the emoji
and no extra space, then run a quick search for usages of
CREATE_MATCH_TOAST_MESSAGE to ensure no code relied on that trailing space.
In `@src/shared/utils/show-error-toast.tsx`:
- Around line 21-27: The template literal for the className in show-error-toast
(the className property construction) contains multiline whitespace and
currently replaces default styles when a custom className is passed; change it
to build a single space-joined class string (or use a utility like clsx) that
trims newlines and always merges defaults with any provided className (e.g.,
join an array of the fixed classes and className if present) so there are no
stray whitespace/newline characters and custom className augments instead of
completely replacing the defaults.
🪄 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
Run ID: debd5ef5-236f-4f04-9f05-10eb4eaa6365
📒 Files selected for processing (12)
src/pages/game/game.tsxsrc/pages/home/home.tsxsrc/pages/onboarding/components/complete-button-section.tsxsrc/pages/onboarding/components/complete-group-card.tsxsrc/pages/onboarding/components/complete-single-card.tsxsrc/pages/onboarding/components/complete.tsxsrc/pages/onboarding/components/date-select.tsxsrc/pages/onboarding/onboarding.tsxsrc/shared/components/bottom-sheet/game-match/game-match-bottom-sheet.tsxsrc/shared/constants/error-toast.tssrc/shared/styles/global.csssrc/shared/utils/show-error-toast.tsx
Deploying mateball-client with
|
| Latest commit: |
6e29539
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://f523568b.mateball-client.pages.dev |
| Branch Preview URL: | https://fix--423-fix-onboarding.mateball-client.pages.dev |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/pages/onboarding/components/complete-button-section.tsx (1)
30-31: 타입별 매핑은 한 곳으로 모아두는 편이 안전합니다.
pendingMatch.type에서 파생되는 값이 분석 이벤트, API payload, 캐시 무효화 대상으로 흩어져 있어서 이후 타입 추가/변경 시 한 군데만 수정되고 나머지가 어긋나기 쉽습니다.리팩터링 예시
- const matchType = pendingMatch.type === 'single' ? 'DIRECT' : 'GROUP'; - const gaMatchType = pendingMatch.type === 'single' ? 'one_to_one' : 'group'; + const matchConfig = + pendingMatch.type === 'single' + ? { + matchType: 'DIRECT' as const, + gaMatchType: 'one_to_one' as const, + queryKey: matchQueries.SINGLE_MATCH_LIST(pendingMatch.date).queryKey, + } + : { + matchType: 'GROUP' as const, + gaMatchType: 'group' as const, + queryKey: matchQueries.GROUP_MATCH_LIST(pendingMatch.date).queryKey, + }; gaEvent('match_create_click', { - match_type: gaMatchType, + match_type: matchConfig.gaMatchType, role: 'creator', }); createMatchMutation.mutate( { gameId: pendingMatch.game.id, - matchType, + matchType: matchConfig.matchType, }, { onSuccess: () => { - if (matchType === 'DIRECT') { - queryClient.invalidateQueries({ - queryKey: matchQueries.SINGLE_MATCH_LIST(pendingMatch.date).queryKey, - }); - } else { - queryClient.invalidateQueries({ - queryKey: matchQueries.GROUP_MATCH_LIST(pendingMatch.date).queryKey, - }); - } + queryClient.invalidateQueries({ + queryKey: matchConfig.queryKey, + }); navigate(ROUTES.HOME, { state: { shouldShowMatchCreatedToast: true,Also applies to: 45-52
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/pages/onboarding/components/complete-button-section.tsx` around lines 30 - 31, The code derives multiple related values (matchType, gaMatchType, and other mappings noted around lines 45-52) from pendingMatch.type in multiple places, which risks inconsistent updates; consolidate these into a single source of truth by creating a utility mapping or function (e.g., getMatchTypeMappings or MATCH_TYPE_MAP) that takes pendingMatch.type and returns an object with all derived fields (matchType, gaMatchType, and any API/cache/event keys), then replace the scattered constants (matchType, gaMatchType, and the other derived values) to read from that single mapping function/object so future type additions/changes need only one edit.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@src/pages/onboarding/components/complete-button-section.tsx`:
- Around line 30-31: The code derives multiple related values (matchType,
gaMatchType, and other mappings noted around lines 45-52) from pendingMatch.type
in multiple places, which risks inconsistent updates; consolidate these into a
single source of truth by creating a utility mapping or function (e.g.,
getMatchTypeMappings or MATCH_TYPE_MAP) that takes pendingMatch.type and returns
an object with all derived fields (matchType, gaMatchType, and any
API/cache/event keys), then replace the scattered constants (matchType,
gaMatchType, and the other derived values) to read from that single mapping
function/object so future type additions/changes need only one edit.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 4f84f3df-d919-43f6-bb60-1ef74c8887ea
📒 Files selected for processing (1)
src/pages/onboarding/components/complete-button-section.tsx
#️⃣ Related Issue
Closes #423
☀️ New-insight
navigate state를 활용하면 특정 플로우에서 이동했을 때만 1회성 토스트를 노출할 수 있어, 전역 상태 없이도 자연스러운 후처리가 가능하다.💎 PR Point
➊ 온보딩 매칭 생성 시점 변경
기존에는
DATE_SELECT > GameMatchBottomSheet > 다음으로클릭 시 즉시 매칭 생성 API가 호출되었으나,만들기버튼 클릭 시 실제 매칭 생성 API 호출하도록 플로우를 변경했습니다.
➋ COMPLETE 단계 카드 미리보기 구현
기존 matchId를 받아서 경기 정보를 렌더링하던 카드를 사용하였으나, 플로우 변경 후 매칭이 생성되지 않은 상태에서 카드를 렌더링해야 했습니다. 따라서 선택한 경기 정보와 유저의 프로필 정보를 모두 사용하여 매칭 생성 전에도 COMPLETE 단계에서 실제 생성될 카드와 동일한 UI를 확인할 수 있도록 수정했습니다.
➌ 생성 완료 후 홈 진입 토스트 처리
온보딩에서 매칭 생성 후 홈으로 이동하는 경우에만 토스트가 노출되도록 처리했습니다.
navigate(..., state)방식으로 1회성 플래그 전달➍ 토스트 스타일 확장
홈 전용 토스트 디자인 적용을 위해 기존 공통 토스트 유틸을 확장했습니다.
📸 Screenshot
Summary by CodeRabbit
New Features
Style
Chores