fix: 회원가입 design, gaEvent 수정 및 매칭 현황 내 중복 데이터 오류 해결#386
Conversation
|
Caution Review failedThe pull request is closed. WalkthroughMatchTabPanel이 외부 카드 대신 Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User
participant MatchPage as Match 페이지
participant Panel as MatchTabPanel
participant API as matchQueries
participant Mapper as mapSingle/Group
participant Mut as patchStageMutation
participant Router as Router
User->>MatchPage: 탭/상태 선택 (activeType, statusParam)
MatchPage->>Panel: mount(props)
alt activeType == '1:1'
Panel->>API: useQuery(SINGLE_MATCH_STATUS, statusParam)
else activeType == '그룹'
Panel->>API: useQuery(GROUP_MATCH_STATUS, statusParam)
end
API-->>Panel: 응답 데이터
Panel->>Mapper: map*MatchData(data)
Mapper-->>Panel: cards[]
User->>Panel: 카드 클릭
alt 카드가 단계 진행 가능
Panel->>Mut: mutate(patch stage)
Mut-->>Panel: success
end
Panel->>Router: navigate(result route)
sequenceDiagram
autonumber
actor User
participant Sheet as GameMatchBottomSheet
participant GA as gaEvent
participant API as CREATE_MATCH
participant Cache as QueryClient
participant Router as Router
User->>Sheet: 제출 클릭
Sheet->>GA: gaEvent('match_create_click', { match_type, role: 'creator' })
Sheet->>API: mutate(createMatch)
alt 성공
Sheet->>Cache: invalidateQueries(match lists)
Sheet->>Router: navigate(next)
else 실패
Sheet-->>User: 에러 처리
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Assessment against linked issues
Assessment against linked issues: Out-of-scope changes
Possibly related PRs
Suggested reviewers
Poem
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
✨ Finishing Touches
🧪 Generate unit tests
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 |
Deploying mateball-client with
|
| Latest commit: |
af5fb3a
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://8093963d.mateball-client.pages.dev |
| Branch Preview URL: | https://fix--383-signup-design.mateball-client.pages.dev |
Dubabbi
left a comment
There was a problem hiding this comment.
덕분에 TanStack Query에 대해 많이 알아가는 것 같슴다... MatchTabPanel 내부에서 쿼리를 실행하게 하는 거 너무 좋네요!! 고생하셨어용
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (6)
src/shared/components/bottom-sheet/game-match/game-match-bottom-sheet.tsx (1)
64-68: 클릭 이벤트 로깅에 컨텍스트 파라미터 추가 제안분석 정밀도를 위해 소스/매치/날짜 정보를 함께 기록하면 퍼널 분석에 유용합니다.
다음과 같이 payload 보강을 제안합니다:
- gaEvent('match_create_click', { - match_type: gaMatchType, - role: 'creator', - }); + gaEvent('match_create_click', { + match_type: gaMatchType, + role: 'creator', + game_id: selectedGame.id, + game_date: date, + source: fromOnboarding ? 'onboarding' : 'match_page', + });src/pages/match/match.tsx (1)
41-41: 타입 단언(as) 제거 가능성 검토
activeType as '1:1' | '그룹'대신useMatchTabState()나TabType정의를 좁혀 콜 사이트 단언을 없애는 편이 안전합니다.가능하다면
TabType이 곧바로'1:1' | '그룹'이 되도록 타입 정리를 고려해 주세요.src/pages/match/components/match-tab-pannel.tsx (4)
25-31: Props 타입 구체화 제안 (statusParam/activeType 공용화)statusParam이 특정 상태 문자열 집합으로 한정된다면 리터럴 유니온으로 제한해 오타와 키 충돌을 방지하는 편이 좋습니다. activeType 리터럴도 별칭 타입으로 노출해 전역 재사용을 권장합니다.
가능한 최소 변경 예시:
- interface MatchTabPanelProps { - activeType: '1:1' | '그룹'; - statusParam: string; + type MatchActiveType = '1:1' | '그룹'; + // 예: 서버 상태 키가 정해져 있다면 유니온으로 한정 + // type MatchStatusParam = '승인 대기' | '승인 완료' | '매칭 완료' | '실패'; + interface MatchTabPanelProps { + activeType: MatchActiveType; + statusParam: string /* MatchStatusParam */;
35-43: 로딩 중 EmptyView 깜빡임(flash) 방지현재 기본값 매핑(빈 배열) 때문에 최초 로딩에 빈 화면이 잠깐 노출될 수 있습니다. isPending 기준으로 EmptyView 렌더를 지연시키면 UX가 안정됩니다.
- const { data: singleData } = useQuery({ + const { data: singleData, isPending: isSinglePending } = useQuery({ ...matchQueries.SINGLE_MATCH_STATUS(statusParam), enabled: isSingle, }); - const { data: groupData } = useQuery({ + const { data: groupData, isPending: isGroupPending } = useQuery({ ...matchQueries.GROUP_MATCH_STATUS(statusParam), enabled: !isSingle, }); + const isPending = isSingle ? isSinglePending : isGroupPending; - {filteredCards.length === 0 ? ( + {isPending ? null : filteredCards.length === 0 ? ( <EmptyView ... /> ) : ( ... )}Also applies to: 76-86
52-53: 중복 클릭/중복 stage 전환 방지 및 오류 토스트 노출stage 패치 중 재클릭을 차단하고, 실패 시 사용자에게 토스트로 안내하는 것이 좋습니다.
- const patchStageMutation = useMutation(matchMutations.MATCH_STAGE()); + const patchStageMutation = useMutation(matchMutations.MATCH_STAGE()); const handleCardClick = async (card: MatchableCardProps) => { + if (patchStageMutation.isPending) return; // 중복 클릭 방지 onCardClick(card); const toastMsg = getPendingToast(card.status, card.type); if (toastMsg) { showErrorToast(toastMsg, '8.9rem', false); return; } const query = CLICKABLE_STATUS_MAP[card.status ?? '']; if (!query) return; try { if (card.status === '승인 완료') { await patchStageMutation.mutateAsync(card.id); } navigate(`${ROUTES.RESULT(card.id.toString())}?type=${query}&cardtype=${card.type}`); } catch (error) { - console.error('매칭 상태 전환 실패', error); + showErrorToast('매칭 상태 전환에 실패했어요. 잠시 후 다시 시도해 주세요.', '8.9rem', false); } };추가로, 성공 시 관련 STATUS 쿼리 무효화(invalidate) 필요성이 있다면 onSuccess에서 invalidateQueries를 붙이는 것도 고려해 주세요.
Also applies to: 63-74
91-93: aria-disabled 사용이 실제 동작과 불일치aria-disabled가 true여도 클릭 시 토스트가 뜨는 동작이라 보조공학 사용자에겐 혼란입니다. 실제로 비활성화하지 않을 거라면 aria-disabled를 제거하거나, 정말 비활성화한다면 disabled 속성으로 이벤트를 차단하는 쪽이 접근성에 더 적절합니다. 탭 포커스 제어가 필요하면 tabIndex도 함께 제어하세요.
- aria-disabled={!isClickable(card.status)} + // aria-disabled 제거 또는 disabled로 실제 비활성화 + // disabled={!isClickable(card.status)}
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
src/pages/match/components/match-tab-pannel.tsx(2 hunks)src/pages/match/match.tsx(1 hunks)src/pages/onboarding/utils/onboarding-button.ts(0 hunks)src/pages/sign-up/components/signup-step.tsx(1 hunks)src/shared/components/bottom-sheet/game-match/game-match-bottom-sheet.tsx(2 hunks)
💤 Files with no reviewable changes (1)
- src/pages/onboarding/utils/onboarding-button.ts
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: heesunee
PR: MATEBALL/MATEBALL-CLIENT#78
File: src/pages/sign-up/utils/age-calculate.ts:1-4
Timestamp: 2025-07-09T09:08:46.371Z
Learning: heesunee는 나이 계산 시 한국식 나이 시스템을 사용하므로, 정확한 생일 기반 계산보다는 출생연도 기준의 간단한 계산 방식을 선호합니다.
📚 Learning: 2025-07-09T18:07:41.693Z
Learnt from: heesunee
PR: MATEBALL/MATEBALL-CLIENT#95
File: src/pages/sign-up/components/nickname-step.tsx:28-30
Timestamp: 2025-07-09T18:07:41.693Z
Learning: heesunee는 src/pages/sign-up/components/nickname-step.tsx의 onSubmit 핸들러 API 호출을 쿼리와 함께 통합해서 처리할 예정이므로, 이 부분에 대해 다시 언급하지 않아야 합니다.
Applied to files:
src/pages/sign-up/components/signup-step.tsx
🧬 Code graph analysis (2)
src/shared/components/bottom-sheet/game-match/game-match-bottom-sheet.tsx (2)
src/shared/components/tab/tab/constants/tab-type.ts (1)
TAB_TYPES(1-4)src/shared/libs/analytics.ts (1)
gaEvent(53-56)
src/pages/match/components/match-tab-pannel.tsx (4)
src/shared/components/card/match-card/types/card.ts (2)
SingleCardProps(28-39)GroupCardProps(41-47)src/shared/apis/match/match-queries.ts (1)
matchQueries(17-119)src/pages/match/hooks/mapMatchData.ts (2)
mapSingleMatchData(5-20)mapGroupMatchData(22-28)src/pages/match/utils/match-status.ts (2)
statusToCategory(7-12)isClickable(40-40)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Cloudflare Pages
🔇 Additional comments (5)
src/shared/components/bottom-sheet/game-match/game-match-bottom-sheet.tsx (1)
9-9: gaEvent import LGTM분석 이벤트 주입을 이 컴포넌트로 일원화한 방향이 합리적입니다.
src/pages/match/match.tsx (1)
40-41: key로 탭/필터 변경 시 강제 리마운트 처리 적절캐시/상태 간섭 방지에 효과적입니다. 현재 접근 유지에 동의합니다.
src/pages/match/components/match-tab-pannel.tsx (3)
3-20: 탭 내부로 쿼리/매핑을 이동한 방향성 좋습니다React Query 쿼리/매핑을 패널 내부로 한정하고 공용 매퍼(mapSingleMatchData/mapGroupMatchData)를 사용한 점이 캐시 간섭을 줄이는 데 유효합니다. import 구성도 적절합니다.
86-101: 리스트 렌더링 키와 카드 전달 구조 적절key에 고유 id 사용, 카드 속성 스프레드와 상태 기반 색상 파생도 명확합니다.
35-47: singleData?.results 사용은 스키마와 일치합니다
getSingleMatchStatusResponse 인터페이스에results: singleMatchMate[]가 정의되어 있어singleData?.results사용이 올바릅니다.Likely an incorrect or invalid review comment.
| @@ -1,38 +1,56 @@ | |||
| // src/pages/match/components/match-tab-pannel.tsx | |||
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
파일명 오탈자: pannel → panel
경로와 컴포넌트명이 혼용될 수 있으니 match-tab-pannel.tsx → match-tab-panel.tsx로 정리하는 것을 권장합니다. 참조부 일괄 치환 영향 확인이 필요합니다.
검증 스크립트:
🏁 Script executed:
#!/bin/bash
# pannel 철자 사용 위치 점검
rg -nP 'match-tab-pannel' -C1Length of output: 484
매치 탭 파일 및 import 경로 “pannel” → “panel”로 수정
src/pages/match/components/match-tab-pannel.tsx 파일명과 내부 컴포넌트명, 그리고 src/pages/match/match.tsx의 import 경로(@pages/match/components/match-tab-pannel)에서 “pannel”을 “panel”로 일괄 변경하세요.
🤖 Prompt for AI Agents
In src/pages/match/components/match-tab-pannel.tsx around line 1, the filename
and identifier use the misspelled "pannel"; rename the file to
match-tab-panel.tsx, update the default/exported component name(s) inside the
file from *Pannel* to *Panel* (e.g., MatchTabPanel), and update all import sites
— notably src/pages/match/match.tsx import path
@pages/match/components/match-tab-pannel →
@pages/match/components/match-tab-panel — to use the corrected name; ensure any
TypeScript/exports and relative paths reflect the new filename and run tests/TS
check to fix any resulting import errors.
| onCardClick={handleCardClick} | ||
| /> |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
onCardClick 파라미터 타입 호환성 점검 필요
요약에 따르면 MatchTabPanel은 onCardClick: (card: MatchableCardProps) => void를 기대하는 반면, 이 파일의 핸들러는 MatchCardData를 인자로 받습니다. 구조가 우연히 호환되더라도 타입 상 불일치 가능성을 확인해 주세요.
아래 스크립트로 두 타입과 Prop 시그니처를 확인할 수 있습니다:
필요 시 최소 요구 형태로 핸들러 시그니처를 완화하는 방법:
- import type { MatchCardData } from './create/types/match-data-type';
+ type MatchAnalyticsPayload = { id: number | string; status: string };
- const handleCardClick = (card: MatchCardData) => {
+ const handleCardClick = (card: MatchAnalyticsPayload) => {
gaEvent('match_card_click', {
match_id: card.id,
match_type: activeType === '1:1' ? 'one_to_one' : 'group',
match_status: card.status,
});
};🏁 Script executed:
#!/bin/bash
rg -nC3 'type\s+MatchCardData|interface\s+MatchCardData'
rg -nC3 'MatchableCardProps'
rg -nC3 'MatchTabPanelProps|onCardClick' src/pages/match/components/match-tab-pannel.tsxLength of output: 4204
#!/bin/bash
rg -nC3 'const handleCardClick' src/pages/match/match.tsx🏁 Script executed:
#!/bin/bash
rg -nC3 'handleCardClick' src/pages/match/match.tsxLength of output: 493
🏁 Script executed:
#!/bin/bash
rg -nC3 'export (interface|type) .*CardProps' -g 'src/components/card/**/types/card.*'Length of output: 148
match-card/types 디렉터리 내 파일 목록을 확인해서 SingleCardProps와 GroupCardProps 정의 파일을 찾습니다:
#!/bin/bash
find src/components/card/match-card/types -maxdepth 1 -type f🏁 Script executed:
#!/bin/bash
find src/components/card/match-card/types -maxdepth 1 -type fLength of output: 71
onCardClick 파라미터 타입 통일 필요
MatchTabPanelProps.onCardClick은 MatchableCardProps를, handleCardClick은 MatchCardData를 인자로 기대해 타입 불일치가 발생합니다. onCardClick 시그니처를 (card: MatchCardData) => void로 변경하거나 handleCardClick을 MatchableCardProps로 조정해 일치시켜야 합니다.
🤖 Prompt for AI Agents
src/pages/match/match.tsx around lines 44-45: 현재 MatchTabPanelProps.onCardClick이
MatchableCardProps를 기대하는 반면 handleCardClick은 MatchCardData를 받도록 구현되어 타입 불일치가
발생합니다; onCardClick 시그니처를 `(card: MatchCardData) => void`로 변경하고 관련 타입
정의(인터페이스/props 사용처)들을 그에 맞춰 업데이트하거나, 대신 handleCardClick을 MatchableCardProps를 인자로
받도록 수정해 두 시그니처가 일치하도록 만드세요; 모든 호출 지점과 컴포넌트에 전달되는 핸들러 타입을 일괄적으로 고쳐 타입 에러를 제거하세요.
| <form | ||
| onSubmit={handleSubmit(onSubmit)} | ||
| className="h-full w-full flex-col justify-between gap-[4rem] px-[1.6rem] pt-[4rem] pb-[1.6rem]" | ||
| className="h-svh w-full flex-col justify-between gap-[4rem] px-[1.6rem] pt-[4rem] pb-[1.6rem]" |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
h-svh 적용은 모바일 주소창 이슈 완화에 유리하나, Tailwind 지원/폴백 확인 필요
팀의 Tailwind 설정에 h-svh 유틸리티가 없으면 빌드 시 무시될 수 있습니다. 미지원 환경(구형 iOS/Android) 대비 폴백도 고려해 주세요.
대안 예시 1(유틸 미지원 대비, 동일 동작):
- className="h-svh w-full flex-col justify-between gap-[4rem] px-[1.6rem] pt-[4rem] pb-[1.6rem]"
+ className="h-[100svh] w-full flex-col justify-between gap-[4rem] px-[1.6rem] pt-[4rem] pb-[1.6rem]"대안 예시 2(내용 증가 시 잘림 방지):
- className="h-svh w-full flex-col justify-between gap-[4rem] px-[1.6rem] pt-[4rem] pb-[1.6rem]"
+ className="min-h-[100svh] w-full flex-col justify-between gap-[4rem] px-[1.6rem] pt-[4rem] pb-[1.6rem] overflow-y-auto"검증 스크립트:
🏁 Script executed:
#!/bin/bash
# Tailwind에서 svh 지원/사용 여부 점검
fd -a 'tailwind.config.*'
rg -n 'svh' tailwind.config.* || true
rg -n 'h-svh|100svh' -g '!**/dist/**' -SLength of output: 206
다음 스크립트로 tailwind.config.* 파일 위치를 찾고 svh 유틸리티 지원 여부를 재확인해 주세요.
🏁 Script executed:
#!/bin/bash
# 1. tailwind.config 파일 검색
echo "==> tailwind.config 파일 목록"
fd --type f 'tailwind.config.*' || echo "파일 없음"
# 2. 각 config 파일에서 svh 키워드 검색
for cfg in $(fd --type f 'tailwind.config.*'); do
echo "==> $cfg에서 svh 검색"
rg -n 'svh' "$cfg" || echo "svh 미발견"
done
# 3. 코드 전반에서 h-svh / 100svh 클래스 사용 여부 확인
echo "==> 전체 소스에서 h-svh|100svh 검색"
rg -n 'h-svh|100svh' -g '!**/dist/**' -C2 .Length of output: 53
Tailwind 기본 설정에 h-svh 유틸리티가 없어 브래킷 표기법 및 Fallback 적용 필요
기본 Tailwind 구성에는 h-svh가 없어 해당 클래스가 무시됩니다.
h-[100svh]로 교체- 콘텐츠 길이 증가 대비
min-h-[100svh] overflow-y-auto적용
🤖 Prompt for AI Agents
In src/pages/sign-up/components/signup-step.tsx around line 121, the Tailwind
class h-svh is not in the default config so it gets ignored; replace it with
h-[100svh] and add min-h-[100svh] overflow-y-auto to ensure full-viewport
fallback and allow scrolling when content grows — update the className to use
h-[100svh] min-h-[100svh] overflow-y-auto along with the existing layout
classes.
#️⃣ Related Issue
Closes #383
☀️ New-insight
💎 PR Point
📸 Screenshot
이전

수정 후

Summary by CodeRabbit