feat: 매칭 현황 페이지 리팩터링 및 그룹 매칭 결과 뷰 수정, 페이지별 배경 색 변경 등#405
feat: 매칭 현황 페이지 리팩터링 및 그룹 매칭 결과 뷰 수정, 페이지별 배경 색 변경 등#405
Conversation
Made-with: Cursor
Made-with: Cursor
Made-with: Cursor
Made-with: Cursor
Walkthrough다크 테마 UI로 전환하고, 매칭 탭 상태 관리를 URL 기반으로 리팩토링했으며, 그룹 매칭 생성 후 채팅방 입장 플로우를 추가했습니다. 타입스크립트 설정 및 빌드 옵션도 최적화했습니다. Changes
Sequence DiagramsequenceDiagram
actor User
participant GroupMatchingCreatedView
participant React Query
participant API
participant External Chat Service
User->>GroupMatchingCreatedView: 페이지 접속 (matchId)
GroupMatchingCreatedView->>GroupMatchingCreatedView: matchId 파싱 & 검증
GroupMatchingCreatedView->>React Query: GROUP_MATCH_RESULT 조회
React Query->>API: 그룹 매칭 결과 요청
API-->>React Query: 그룹 데이터 반환
React Query-->>GroupMatchingCreatedView: groupData 업데이트
GroupMatchingCreatedView->>React Query: OPEN_CHAT_URL 조회
React Query->>API: 채팅방 URL 요청
API-->>React Query: 외부 채팅 URL 반환
React Query-->>GroupMatchingCreatedView: chatUrl 업데이트
GroupMatchingCreatedView->>GroupMatchingCreatedView: 매칭 완료 UI 렌더링
User->>GroupMatchingCreatedView: "채팅방 입장" 클릭
GroupMatchingCreatedView->>GroupMatchingCreatedView: gaEvent 기록
GroupMatchingCreatedView->>External Chat Service: 채팅 URL 오픈
External Chat Service-->>User: 채팅방 표시
GroupMatchingCreatedView->>GroupMatchingCreatedView: 쿨다운 시작 (재클릭 방지)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 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 |
Deploying mateball-client with
|
| Latest commit: |
c4874c5
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://b049fc50.mateball-client.pages.dev |
| Branch Preview URL: | https://feat--404-matching-now.mateball-client.pages.dev |
|
MATEBALL-STORYBOOK |
896b230 to
c4874c5
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (4)
src/shared/components/ui/empty-view.tsx (1)
16-16: 공용 컴포넌트로서 제목 색상 제어 가능성을 제안합니다.
EmptyView는 여러 화면에서 재사용되는 공용 UI 컴포넌트입니다. 현재 제목이text-gray-white로 고정되어 있는데, 향후 다양한 배경에 대응할 수 있도록 색상을 prop으로 분리하면 컴포넌트의 유연성이 높아집니다.♻️ 리팩토링 제안
interface EmptyViewProps { iconName: string; text: string; subText?: string; className?: string; + titleClassName?: string; } -const EmptyView = ({ iconName, text, subText, className }: EmptyViewProps) => { +const EmptyView = ({ iconName, text, subText, className, titleClassName }: EmptyViewProps) => { return ( <div className={cn('flex-col-center gap-[2.4rem]', className)}> <Icon name={iconName} size={8.4} /> <div className="flex-col-center gap-[0.8rem] text-center"> - <h3 className="head_20_sb text-gray-white">{text}</h3> + <h3 className={cn('head_20_sb text-gray-white', titleClassName)}>{text}</h3> <p className="cap_14_m text-gray-500">{subText}</p> </div> </div> ); };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/shared/components/ui/empty-view.tsx` at line 16, EmptyView currently hardcodes the header color via className "text-gray-white"; make the title color configurable by adding a prop (e.g., titleColor or titleClassName) to the EmptyView component's props interface with a default value of "text-gray-white", then replace the fixed class on the <h3> in EmptyView with a composition that includes this prop so callers can override the header color while preserving the default.src/pages/match/match.tsx (1)
38-38: 탭 전환 시 리렌더링 동작 확인
key={activeTab}을 사용하면 탭 전환 시MatchTabPanel이 완전히 언마운트/리마운트됩니다. 현재MatchTabPanel내부에서 두 쿼리 모두 항상 실행되므로, 탭 전환마다 불필요한 쿼리 재실행이 발생할 수 있습니다.
key를 제거하고isCreatedTabprop 변경만으로 필터링을 처리하면 React Query 캐시를 재활용할 수 있습니다.♻️ 선택적 개선안
- <MatchTabPanel key={activeTab} isCreatedTab={isCreatedTab} onCardClick={handleCardClick} /> + <MatchTabPanel isCreatedTab={isCreatedTab} onCardClick={handleCardClick} />🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/pages/match/match.tsx` at line 38, Using key={activeTab} forces MatchTabPanel to unmount/remount on tab change which causes both internal queries to refetch every switch; remove the key prop from the MatchTabPanel usage so it stays mounted and rely on the isCreatedTab prop to control behavior, then update the queries inside MatchTabPanel (the two React Query hooks) to use the enabled option tied to isCreatedTab (e.g., enabled: isCreatedTab for the "created" query and enabled: !isCreatedTab for the other) so only the active tab's query runs and React Query cache is reused; keep handleCardClick unchanged and consider memoizing any derived values if needed.src/pages/result/components/group-matching-created-view.tsx (1)
84-99: 로딩/에러 상태에 대한 사용자 피드백 부재
groupData가 없을 때(로딩 중이거나 에러 발생 시) 카드 영역이 비어 보입니다. 스켈레톤 로더나 에러 메시지를 추가하면 UX가 개선될 수 있습니다.현재 동작이 의도된 것이라면 무시하셔도 됩니다.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/pages/result/components/group-matching-created-view.tsx` around lines 84 - 99, When groupData is falsy the Card area is left blank; add explicit loading and error fallbacks so users get feedback. In the component that renders groupData (look for groupData and the Card invocation in GroupMatchingCreatedView / src/pages/result/components/group-matching-created-view.tsx) add conditional rendering: if a loading flag (e.g., isLoading or pending state) render a skeleton loader component or placeholder, if an error flag (e.g., isError or fetch failure) render a concise error message and retry action, otherwise render the existing Card(...) as-is when groupData is present. Ensure you reuse existing props (id, nickname, etc.) only when groupData exists and keep the fallback UI styled to match the Card container.src/pages/match/hooks/useMatchTabState.ts (1)
8-19: URL 변경 시 로컬 상태 동기화 검토
useSearchParams는 브라우저 뒤로가기/앞으로가기 시 자동으로 업데이트되지만,useState(initialTab)은 마운트 시점에만 초기값을 적용합니다. 따라서 URL이 변경되어도activeTab상태는 이전 값을 유지하여 URL과 로컬 상태 간 불일치가 발생할 수 있습니다.URL 동기화가 필요하다면
useEffect를 통해tabParam변경 시activeTab을 업데이트하는 것을 권장합니다:useEffect(() => { setActiveTab(isMatchTabType(tabParam) ? tabParam : MATCH_TAB_TYPES.CREATED); }, [tabParam]);현재 동작이 의도된 것이라면 이 제안을 무시해도 됩니다.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/pages/match/hooks/useMatchTabState.ts` around lines 8 - 19, The hook useMatchTabState currently initializes local activeTab from tabParam only on mount, so URL changes (via useSearchParams/back/forward) can desync activeTab; add an effect in useMatchTabState that watches tabParam and calls setActiveTab(isMatchTabType(tabParam) ? tabParam : MATCH_TAB_TYPES.CREATED) to keep local state in sync, leaving handleTabChange (which updates both activeTab and setSearchParams) unchanged; reference symbols: useMatchTabState, activeTab, tabParam, setActiveTab, isMatchTabType, MATCH_TAB_TYPES, useSearchParams, handleTabChange.
🤖 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/match/constants/matching.ts`:
- Around line 49-53: Two message constants have inconsistent spacing:
MATCHING_GUIDE_MESSAGE_DESCRIPTION uses "매칭 현황에서" while
GROUP_MATCHING_CREATED_DESCRIPTION uses "매칭현황에서"; update
GROUP_MATCHING_CREATED_DESCRIPTION to use the same spacing as
MATCHING_GUIDE_MESSAGE_DESCRIPTION ("매칭 현황에서") so both strings are consistent,
by editing the GROUP_MATCHING_CREATED_DESCRIPTION string value accordingly.
In `@src/pages/result/components/matching-success-view.tsx`:
- Around line 41-47: MatchingSuccessView contains unreachable group-matching
branches: the useQuery call for matchQueries.COUNTED_MEMBER (with enabled:
isGroupMatching && isValidMatchId) and the conditional render {isGroupMatching
&& <MatchCurrentCard count={matchedCount} />} are always false because
MatchingSuccessView is only rendered with isGroupMatching={false}; remove these
unreachable lines or, if you plan to support group matching later, add a clear
comment above the useQuery and the conditional render explaining they are
intentionally inactive and when they should be re-enabled (reference:
MatchingSuccessView, useQuery(matchQueries.COUNTED_MEMBER), isGroupMatching,
matchedCount, MatchCurrentCard).
---
Nitpick comments:
In `@src/pages/match/hooks/useMatchTabState.ts`:
- Around line 8-19: The hook useMatchTabState currently initializes local
activeTab from tabParam only on mount, so URL changes (via
useSearchParams/back/forward) can desync activeTab; add an effect in
useMatchTabState that watches tabParam and calls
setActiveTab(isMatchTabType(tabParam) ? tabParam : MATCH_TAB_TYPES.CREATED) to
keep local state in sync, leaving handleTabChange (which updates both activeTab
and setSearchParams) unchanged; reference symbols: useMatchTabState, activeTab,
tabParam, setActiveTab, isMatchTabType, MATCH_TAB_TYPES, useSearchParams,
handleTabChange.
In `@src/pages/match/match.tsx`:
- Line 38: Using key={activeTab} forces MatchTabPanel to unmount/remount on tab
change which causes both internal queries to refetch every switch; remove the
key prop from the MatchTabPanel usage so it stays mounted and rely on the
isCreatedTab prop to control behavior, then update the queries inside
MatchTabPanel (the two React Query hooks) to use the enabled option tied to
isCreatedTab (e.g., enabled: isCreatedTab for the "created" query and enabled:
!isCreatedTab for the other) so only the active tab's query runs and React Query
cache is reused; keep handleCardClick unchanged and consider memoizing any
derived values if needed.
In `@src/pages/result/components/group-matching-created-view.tsx`:
- Around line 84-99: When groupData is falsy the Card area is left blank; add
explicit loading and error fallbacks so users get feedback. In the component
that renders groupData (look for groupData and the Card invocation in
GroupMatchingCreatedView /
src/pages/result/components/group-matching-created-view.tsx) add conditional
rendering: if a loading flag (e.g., isLoading or pending state) render a
skeleton loader component or placeholder, if an error flag (e.g., isError or
fetch failure) render a concise error message and retry action, otherwise render
the existing Card(...) as-is when groupData is present. Ensure you reuse
existing props (id, nickname, etc.) only when groupData exists and keep the
fallback UI styled to match the Card container.
In `@src/shared/components/ui/empty-view.tsx`:
- Line 16: EmptyView currently hardcodes the header color via className
"text-gray-white"; make the title color configurable by adding a prop (e.g.,
titleColor or titleClassName) to the EmptyView component's props interface with
a default value of "text-gray-white", then replace the fixed class on the <h3>
in EmptyView with a composition that includes this prop so callers can override
the header color while preserving the default.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: aba7eb48-9871-42da-bcf3-71d7c52d9220
⛔ Files ignored due to path filters (1)
src/shared/assets/svgs/graphic_empty_2.svgis excluded by!**/*.svg
📒 Files selected for processing (20)
.vscode/settings.jsonsrc/pages/chat/chat-list.tsxsrc/pages/home/components/match-list-section.tsxsrc/pages/home/home.tsxsrc/pages/match/components/match-tab-pannel.tsxsrc/pages/match/constants/matching.tssrc/pages/match/hooks/useMatchTabState.tssrc/pages/match/match.tsxsrc/pages/result/components/group-matching-created-view.tsxsrc/pages/result/components/matching-fail-view.tsxsrc/pages/result/components/matching-success-view.tsxsrc/pages/result/components/sent-view.tsxsrc/pages/result/result.tsxsrc/shared/components/bottom-navigation/bottom-navigation.tsxsrc/shared/components/footer/footer.tsxsrc/shared/components/header/header.tsxsrc/shared/components/header/utils/get-header.tsxsrc/shared/components/tab/tab/styles/tab-style.tssrc/shared/components/ui/empty-view.tsxtsconfig.app.json
| export const MATCHING_GUIDE_MESSAGE_DESCRIPTION = | ||
| '딱! 맞는 메이트의 요청이 도착하면\n' + "'매칭 현황'에서 확인할 수 있어요."; | ||
| '새 요청이 도착하면\n매칭 현황에서 확인할 수 있어요.'; | ||
|
|
||
| export const GROUP_MATCHING_CREATED_DESCRIPTION = | ||
| '매칭현황에서 요청을 확인하고\n채팅방에서 메이트와 만날 수 있어요.'; |
There was a problem hiding this comment.
띄어쓰기 불일치
Line 50의 "매칭 현황에서"와 Line 53의 "매칭현황에서" 사이에 띄어쓰기가 일관되지 않습니다. UI 일관성을 위해 통일해주세요.
제안된 수정
export const GROUP_MATCHING_CREATED_DESCRIPTION =
- '매칭현황에서 요청을 확인하고\n채팅방에서 메이트와 만날 수 있어요.';
+ '매칭 현황에서 요청을 확인하고\n채팅방에서 메이트와 만날 수 있어요.';📝 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.
| export const MATCHING_GUIDE_MESSAGE_DESCRIPTION = | |
| '딱! 맞는 메이트의 요청이 도착하면\n' + "'매칭 현황'에서 확인할 수 있어요."; | |
| '새 요청이 도착하면\n매칭 현황에서 확인할 수 있어요.'; | |
| export const GROUP_MATCHING_CREATED_DESCRIPTION = | |
| '매칭현황에서 요청을 확인하고\n채팅방에서 메이트와 만날 수 있어요.'; | |
| export const MATCHING_GUIDE_MESSAGE_DESCRIPTION = | |
| '새 요청이 도착하면\n매칭 현황에서 확인할 수 있어요.'; | |
| export const GROUP_MATCHING_CREATED_DESCRIPTION = | |
| '매칭 현황에서 요청을 확인하고\n채팅방에서 메이트와 만날 수 있어요.'; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/pages/match/constants/matching.ts` around lines 49 - 53, Two message
constants have inconsistent spacing: MATCHING_GUIDE_MESSAGE_DESCRIPTION uses "매칭
현황에서" while GROUP_MATCHING_CREATED_DESCRIPTION uses "매칭현황에서"; update
GROUP_MATCHING_CREATED_DESCRIPTION to use the same spacing as
MATCHING_GUIDE_MESSAGE_DESCRIPTION ("매칭 현황에서") so both strings are consistent,
by editing the GROUP_MATCHING_CREATED_DESCRIPTION string value accordingly.
| const { data: countedData } = useQuery({ | ||
| ...matchQueries.COUNTED_MEMBER(matchId), | ||
| enabled: isGroupMatching && isValidMatchId, | ||
| }); | ||
|
|
||
| const openChatUrl = typeof data?.chattingUrl === 'string' ? data.chattingUrl.trim() : ''; | ||
| const matchedCount = countedData?.count; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Verify that MatchingSuccessView is only called with isGroupMatching={false}
# Check all usages of MatchingSuccessView
rg -n "MatchingSuccessView" --type=tsx -A 3 -B 1
# Also check if GroupMatchingCreatedView handles group success completely
rg -n "GroupMatchingCreatedView" --type=tsx -A 3 -B 1Repository: MATEBALL/MATEBALL-CLIENT
Length of output: 123
🏁 Script executed:
#!/bin/bash
# Search for MatchingSuccessView usage without problematic -t flag
rg "MatchingSuccessView" -A 3 -B 1
# Also search for GroupMatchingCreatedView
rg "GroupMatchingCreatedView" -A 3 -B 1Repository: MATEBALL/MATEBALL-CLIENT
Length of output: 4388
🏁 Script executed:
#!/bin/bash
# Read the file to verify lines 41-47 and around line 94
wc -l src/pages/result/components/matching-success-view.tsxRepository: MATEBALL/MATEBALL-CLIENT
Length of output: 125
🏁 Script executed:
#!/bin/bash
# Read lines 41-47 and around line 94
sed -n '35,100p' src/pages/result/components/matching-success-view.tsx | cat -nRepository: MATEBALL/MATEBALL-CLIENT
Length of output: 2806
도달 불가능한 코드 제거 또는 주석 추가 필요
result.tsx에서 MatchingSuccessView는 항상 isGroupMatching={false}로만 렌더링되며, 그룹 매칭 성공 시에는 GroupMatchingCreatedView를 사용합니다.
따라서 다음 코드는 실행되지 않습니다:
- Lines 41-44:
COUNTED_MEMBER쿼리의enabled: isGroupMatching && isValidMatchId조건이 항상false - Line 60:
{isGroupMatching && <MatchCurrentCard count={matchedCount} />}조건이 항상false
이 코드는 도달 불가능합니다. 향후 사용 계획이 있다면 주석으로 명시하고, 그렇지 않다면 제거해 주세요.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/pages/result/components/matching-success-view.tsx` around lines 41 - 47,
MatchingSuccessView contains unreachable group-matching branches: the useQuery
call for matchQueries.COUNTED_MEMBER (with enabled: isGroupMatching &&
isValidMatchId) and the conditional render {isGroupMatching && <MatchCurrentCard
count={matchedCount} />} are always false because MatchingSuccessView is only
rendered with isGroupMatching={false}; remove these unreachable lines or, if you
plan to support group matching later, add a clear comment above the useQuery and
the conditional render explaining they are intentionally inactive and when they
should be re-enabled (reference: MatchingSuccessView,
useQuery(matchQueries.COUNTED_MEMBER), isGroupMatching, matchedCount,
MatchCurrentCard).
#️⃣ Related Issue
Closes #404
☀️ New-insight
useSearchParams의setSearchParams를 활용하면useNavigate없이도 URL 쿼리 동기화가 가능해서 훅이 더 간결해짐useMemo로 single/group 데이터를 합친 뒤isCreated로 필터링하면 탭별 API 분리 없이 한 번에 처리 가능BottomNavigation처럼 경로 기반으로 분기하면 페이지별 테마 전환을 깔끔하게 처리할 수 있음💎 PR Point
1:1/그룹+전체/진행중/...필터 조합에서생성한 매칭/요청한 매칭단일 탭 구조로 리팩터링.useMatchTabState훅을 정리하고 불필요한handleFilterChange,statusToCategory등 제거sent-view,matching-fail-view,matching-success-view,group-matching-created-view에서 이전 탭 시스템의 stale 쿼리 파라미터(?tab=그룹&filter=전체) 사용하고 있던 부분을ROUTES.MATCH로 정리GroupMatchingCreatedView신규 추가.result.tsx에서type=success && mode=group일 때 기존MatchingSuccessView대신 그룹 전용 뷰로 분기📸 Screenshot
등 .....
(그룹 카드 수정 필요함)
Summary by CodeRabbit
릴리스 노트
새로운 기능
UI 개선
성능 개선