From b21a8a0eb78edd757f28433ecb221c35f6cd786f Mon Sep 17 00:00:00 2001 From: iOdiO89 <117376841+iOdiO89@users.noreply.github.com> Date: Wed, 15 Jul 2026 23:38:36 +0900 Subject: [PATCH 1/6] =?UTF-8?q?refactor:=20/archive=20=ED=83=AD=20?= =?UTF-8?q?=EC=BF=BC=EB=A6=AC=20=ED=8C=8C=EB=9D=BC=EB=AF=B8=ED=84=B0?= =?UTF-8?q?=EB=A5=BC=20/archive/wish,=20/archive/tournament=20=EA=B2=BD?= =?UTF-8?q?=EB=A1=9C=EB=A1=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/app/app/+native-intent.ts | 2 +- apps/app/components/ShareBottomSheet.tsx | 2 +- apps/app/hooks/useShareIntent.ts | 18 ++---- .../src/app/archive/_components/WishTab.tsx | 57 ------------------- .../archive/_components/WishlistLayout.tsx | 14 ++--- .../tournament-history-content/client.tsx | 18 ------ apps/web/src/app/archive/_types/wish.ts | 2 - apps/web/src/app/archive/page.tsx | 27 --------- .../_components/ArchiveTournamentClient.tsx | 39 +++++++++++++ .../_components/TournamentHistoryContent.tsx} | 11 ++-- .../_components/TournamentHistoryList.tsx | 9 ++- .../_components/TournamentStatusTab.tsx | 40 +++++++++++++ apps/web/src/app/archive/tournament/page.tsx | 7 +++ .../archive/wish/[id]/_hooks/useDeleteWish.ts | 2 +- .../archive/wish/[id]/_hooks/usePatchWish.ts | 2 +- apps/web/src/app/archive/wish/[id]/layout.tsx | 4 +- apps/web/src/app/archive/wish/page.tsx | 12 ++++ .../_utils/getNotificationRoute.ts | 2 +- .../src/components/bottom-tab-bar/index.tsx | 5 +- .../get-item-dialog/ByLinkDialog.tsx | 2 +- .../tournament-error-dialog/index.tsx | 2 +- apps/web/src/consts/route.ts | 6 +- apps/web/src/hooks/useNotificationSSE.ts | 2 +- apps/web/src/hooks/usePostWishLink.ts | 2 +- apps/web/src/hooks/usePostWishOCR.ts | 2 +- apps/web/src/utils/getRouteType.ts | 3 +- apps/web/src/utils/pushNotificationRoute.ts | 2 +- 27 files changed, 142 insertions(+), 152 deletions(-) delete mode 100644 apps/web/src/app/archive/_components/WishTab.tsx delete mode 100644 apps/web/src/app/archive/_components/tournament-history-content/client.tsx delete mode 100644 apps/web/src/app/archive/page.tsx create mode 100644 apps/web/src/app/archive/tournament/_components/ArchiveTournamentClient.tsx rename apps/web/src/app/archive/{_components/tournament-history-content/index.tsx => tournament/_components/TournamentHistoryContent.tsx} (57%) rename apps/web/src/app/archive/{ => tournament}/_components/TournamentHistoryList.tsx (83%) create mode 100644 apps/web/src/app/archive/tournament/_components/TournamentStatusTab.tsx create mode 100644 apps/web/src/app/archive/tournament/page.tsx create mode 100644 apps/web/src/app/archive/wish/page.tsx diff --git a/apps/app/app/+native-intent.ts b/apps/app/app/+native-intent.ts index dc7ee350..18dcc41a 100644 --- a/apps/app/app/+native-intent.ts +++ b/apps/app/app/+native-intent.ts @@ -20,7 +20,7 @@ export function redirectSystemPath({ path }: { path: string; initial: boolean }) if (webFromUrl) return `/?web=${encodeURIComponent(webFromUrl)}`; try { - /** share extension → openHostApp(`/?web=${encodeURIComponent('/archive?tab=wish')}`) */ + /** share extension → openHostApp(`/?web=${encodeURIComponent('/archive/wish')}`) */ const url = new URL(path, 'piki://app'); const web = url.searchParams.get('web'); diff --git a/apps/app/components/ShareBottomSheet.tsx b/apps/app/components/ShareBottomSheet.tsx index 6050edcb..f95033f0 100644 --- a/apps/app/components/ShareBottomSheet.tsx +++ b/apps/app/components/ShareBottomSheet.tsx @@ -19,7 +19,7 @@ function ShareBottomSheetContent({ url, text }: ShareExtensionProps) { const handleOpenWishlist = () => { /** openHostApp path 규칙: `/{path}?{query}` — `web=...`만 넘기면 `/web=...` 라우트로 해석됨 */ - openHostApp(`/?web=${encodeURIComponent('/archive?tab=wish')}`); + openHostApp(`/?web=${encodeURIComponent('/archive/wish')}`); }; useEffect(() => { diff --git a/apps/app/hooks/useShareIntent.ts b/apps/app/hooks/useShareIntent.ts index be8cbb9e..f563c61d 100644 --- a/apps/app/hooks/useShareIntent.ts +++ b/apps/app/hooks/useShareIntent.ts @@ -5,15 +5,9 @@ import { useCallback, useEffect, useRef } from 'react'; import { toShareIntentPayload } from '@/utils/serializeShareIntent'; import { WebBridge } from '@/utils/webBridge'; -const ARCHIVE_PATH = '/archive'; -const ARCHIVE_WISH_TAB = 'wish'; +const WISHLIST_PATH = '/archive/wish'; -const isArchiveWishTab = (uri: URL) => { - if (uri.pathname !== ARCHIVE_PATH) return false; - - const tab = uri.searchParams.get('tab'); - return tab === null || tab === ARCHIVE_WISH_TAB; -}; +const isWishlistPath = (uri: URL) => uri.pathname === WISHLIST_PATH; type Props = { onChangeWebviewUri: (uri: string) => void; @@ -47,14 +41,14 @@ export const useShareIntent = ({ onChangeWebviewUri, webviewUri }: Props) => { const nextUri = new URL(webviewUri); /** 이미 아카이브 위시 탭: 즉시 전송 */ - if (isArchiveWishTab(nextUri)) { + if (isWishlistPath(nextUri)) { sendShareIntent(); return; } - /** 다른 페이지: payload 보관 후 아카이브 위시 탭으로 이동 → WEB_REQ_READY 수신 시 전송 */ - nextUri.pathname = ARCHIVE_PATH; - nextUri.search = `?tab=${ARCHIVE_WISH_TAB}`; + /** 다른 페이지: payload 보관 후 아카이브 위시로 이동 → WEB_REQ_READY 수신 시 전송 */ + nextUri.pathname = WISHLIST_PATH; + nextUri.search = ''; nextUri.hash = ''; onChangeWebviewUri(nextUri.toString()); } catch { diff --git a/apps/web/src/app/archive/_components/WishTab.tsx b/apps/web/src/app/archive/_components/WishTab.tsx deleted file mode 100644 index b3b3311f..00000000 --- a/apps/web/src/app/archive/_components/WishTab.tsx +++ /dev/null @@ -1,57 +0,0 @@ -'use client'; - -import { useRouter, useSearchParams } from 'next/navigation'; - -import { ROUTES } from '@/consts/route'; -import type { ItemTypeT } from '@/types/item'; -import { cn } from '@/utils/cn'; - -import type { WishTabT } from '../_types/wish'; - -const TABS: WishTabT[] = ['저장한 위시템', '토너먼트 기록']; - -const TAB_TYPE: Record = { - '저장한 위시템': 'wish', - '토너먼트 기록': 'tournament', -}; - -const TYPE_TAB: Record = { - wish: '저장한 위시템', - tournament: '토너먼트 기록', -}; - -function WishTab() { - const router = useRouter(); - const searchParams = useSearchParams(); - const tabParam = searchParams.get('tab'); - const activeTab: WishTabT = - tabParam === 'wish' || tabParam === 'tournament' ? TYPE_TAB[tabParam] : '저장한 위시템'; - - const handleTabChange = (tab: WishTabT) => { - router.replace(ROUTES.ARCHIVE(TAB_TYPE[tab])); - }; - - return ( -
-
- {TABS.map(tab => ( - - ))} -
-
- ); -} - -export default WishTab; diff --git a/apps/web/src/app/archive/_components/WishlistLayout.tsx b/apps/web/src/app/archive/_components/WishlistLayout.tsx index 66d5e3e7..a4ee9df3 100644 --- a/apps/web/src/app/archive/_components/WishlistLayout.tsx +++ b/apps/web/src/app/archive/_components/WishlistLayout.tsx @@ -1,14 +1,11 @@ -import { Suspense } from 'react'; - import { Header, HeaderIcon } from '@/components/header'; -import WishTab from './WishTab'; - -type WishlistLayoutProps = { +type Props = { + title: string; children: React.ReactNode; }; -function WishlistLayout({ children }: WishlistLayoutProps) { +function WishlistLayout({ title, children }: Props) { return (
@@ -22,12 +19,9 @@ function WishlistLayout({ children }: WishlistLayoutProps) { } />

- 내 보관함 + {title}

- - -
{children} diff --git a/apps/web/src/app/archive/_components/tournament-history-content/client.tsx b/apps/web/src/app/archive/_components/tournament-history-content/client.tsx deleted file mode 100644 index 10d8ff32..00000000 --- a/apps/web/src/app/archive/_components/tournament-history-content/client.tsx +++ /dev/null @@ -1,18 +0,0 @@ -'use client'; - -import BottomTabBar from '@/components/bottom-tab-bar'; - -import TournamentHistoryList from '../TournamentHistoryList'; - -function TournamentHistoryContentClient() { - return ( - <> - -
- -
- - ); -} - -export default TournamentHistoryContentClient; diff --git a/apps/web/src/app/archive/_types/wish.ts b/apps/web/src/app/archive/_types/wish.ts index 47673ab0..3a5e3aed 100644 --- a/apps/web/src/app/archive/_types/wish.ts +++ b/apps/web/src/app/archive/_types/wish.ts @@ -1,8 +1,6 @@ import type { ItemStatusT, ItemT } from '@/types/item'; import type { WishT } from '@/types/wish'; -export type WishTabT = '저장한 위시템' | '토너먼트 기록'; - export type WishlistEntryT = { wish: WishT; item: ItemT; diff --git a/apps/web/src/app/archive/page.tsx b/apps/web/src/app/archive/page.tsx deleted file mode 100644 index 04734902..00000000 --- a/apps/web/src/app/archive/page.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import WishlistLayout from './_components/WishlistLayout'; -import TournamentHistoryContent from './_components/tournament-history-content'; -import WishContent from './_components/wish-content'; -import type { WishTabT } from './_types/wish'; - -type ArchivePageProps = { - searchParams: Promise<{ tab?: string }>; -}; - -const getActiveWishTab = (tab?: string): WishTabT => { - if (tab === 'tournament') return '토너먼트 기록'; - return '저장한 위시템'; -}; - -async function ArchivePage({ searchParams }: ArchivePageProps) { - const { tab } = await searchParams; - const activeTab = getActiveWishTab(tab); - - return ( - - {activeTab === '저장한 위시템' && } - {activeTab === '토너먼트 기록' && } - - ); -} - -export default ArchivePage; diff --git a/apps/web/src/app/archive/tournament/_components/ArchiveTournamentClient.tsx b/apps/web/src/app/archive/tournament/_components/ArchiveTournamentClient.tsx new file mode 100644 index 00000000..5e5a5e1b --- /dev/null +++ b/apps/web/src/app/archive/tournament/_components/ArchiveTournamentClient.tsx @@ -0,0 +1,39 @@ +'use client'; + +import { Suspense, useState } from 'react'; + +import BottomTabBar from '@/components/bottom-tab-bar'; +import type { TournamentStatusT } from '@/types/tournament'; + +import WishlistLayout from '../../_components/WishlistLayout'; +import TournamentHistoryList from './TournamentHistoryList'; +import TournamentStatusTab, { type TournamentStatusTabT } from './TournamentStatusTab'; + +const STATUS_BY_TAB: Record = { + 'in-progress': ['PENDING', 'IN_PROGRESS'], + completed: ['COMPLETED'], +}; + +function ArchiveTournamentClient() { + const [activeTab, setActiveTab] = useState('in-progress'); + + return ( + + + +

토너먼트를 불러오는 중이에요

+ + } + > + +
+
+ +
+
+ ); +} + +export default ArchiveTournamentClient; diff --git a/apps/web/src/app/archive/_components/tournament-history-content/index.tsx b/apps/web/src/app/archive/tournament/_components/TournamentHistoryContent.tsx similarity index 57% rename from apps/web/src/app/archive/_components/tournament-history-content/index.tsx rename to apps/web/src/app/archive/tournament/_components/TournamentHistoryContent.tsx index 2390d23d..ec562773 100644 --- a/apps/web/src/app/archive/_components/tournament-history-content/index.tsx +++ b/apps/web/src/app/archive/tournament/_components/TournamentHistoryContent.tsx @@ -1,21 +1,24 @@ import { HydrationBoundary, dehydrate } from '@tanstack/react-query'; import { getTournamentList } from '@/apis/getTournamentList'; +import type { TournamentStatusT } from '@/types/tournament'; import { getQueryClient } from '@/utils/queryClient'; -import TournamentHistoryContentClient from './client'; +import ArchiveTournamentClient from './ArchiveTournamentClient'; + +const DEFAULT_STATUSES: TournamentStatusT[] = ['PENDING', 'IN_PROGRESS']; async function TournamentHistoryContent() { const queryClient = getQueryClient(); await queryClient.prefetchQuery({ - queryKey: ['tournamentList', []], - queryFn: () => getTournamentList(), + queryKey: ['tournamentList', DEFAULT_STATUSES], + queryFn: () => getTournamentList(DEFAULT_STATUSES), }); return ( - + ); } diff --git a/apps/web/src/app/archive/_components/TournamentHistoryList.tsx b/apps/web/src/app/archive/tournament/_components/TournamentHistoryList.tsx similarity index 83% rename from apps/web/src/app/archive/_components/TournamentHistoryList.tsx rename to apps/web/src/app/archive/tournament/_components/TournamentHistoryList.tsx index 4d5dc206..d807557f 100644 --- a/apps/web/src/app/archive/_components/TournamentHistoryList.tsx +++ b/apps/web/src/app/archive/tournament/_components/TournamentHistoryList.tsx @@ -3,9 +3,14 @@ import { TrophyIconFill } from '@/assets/icons'; import TournamentCard from '@/components/tournament-card'; import { useGetTournamentList } from '@/hooks/useGetTournamentList'; +import type { TournamentStatusT } from '@/types/tournament'; -function TournamentHistoryList() { - const { tournamentListData } = useGetTournamentList(); +type Props = { + statuses: TournamentStatusT[]; +}; + +function TournamentHistoryList({ statuses }: Props) { + const { tournamentListData } = useGetTournamentList(statuses); if (tournamentListData.length === 0) return ( diff --git a/apps/web/src/app/archive/tournament/_components/TournamentStatusTab.tsx b/apps/web/src/app/archive/tournament/_components/TournamentStatusTab.tsx new file mode 100644 index 00000000..7577c49f --- /dev/null +++ b/apps/web/src/app/archive/tournament/_components/TournamentStatusTab.tsx @@ -0,0 +1,40 @@ +'use client'; + +import { cn } from '@/utils/cn'; + +export type TournamentStatusTabT = 'in-progress' | 'completed'; + +type Props = { + activeTab: TournamentStatusTabT; + onTabChange: (tab: TournamentStatusTabT) => void; +}; + +const TABS: { value: TournamentStatusTabT; label: string }[] = [ + { value: 'in-progress', label: '진행 중' }, + { value: 'completed', label: '완료' }, +]; + +function TournamentStatusTab({ activeTab, onTabChange }: Props) { + return ( +
+ {TABS.map(tab => ( + + ))} +
+ ); +} + +export default TournamentStatusTab; diff --git a/apps/web/src/app/archive/tournament/page.tsx b/apps/web/src/app/archive/tournament/page.tsx new file mode 100644 index 00000000..70d1383b --- /dev/null +++ b/apps/web/src/app/archive/tournament/page.tsx @@ -0,0 +1,7 @@ +import TournamentHistoryContent from './_components/TournamentHistoryContent'; + +async function ArchiveTournamentPage() { + return ; +} + +export default ArchiveTournamentPage; diff --git a/apps/web/src/app/archive/wish/[id]/_hooks/useDeleteWish.ts b/apps/web/src/app/archive/wish/[id]/_hooks/useDeleteWish.ts index 32467efa..5ffb1432 100644 --- a/apps/web/src/app/archive/wish/[id]/_hooks/useDeleteWish.ts +++ b/apps/web/src/app/archive/wish/[id]/_hooks/useDeleteWish.ts @@ -17,7 +17,7 @@ export const useDeleteWish = (wishId: number) => { onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['wishlists'] }); toast.success('위시 상품이 삭제되었습니다.'); - router.replace(ROUTES.ARCHIVE()); + router.replace(ROUTES.WISHLIST); }, onError: error => { if (!isAxiosError(error) || !error.response) return; diff --git a/apps/web/src/app/archive/wish/[id]/_hooks/usePatchWish.ts b/apps/web/src/app/archive/wish/[id]/_hooks/usePatchWish.ts index 20ee4d4f..9b869b7d 100644 --- a/apps/web/src/app/archive/wish/[id]/_hooks/usePatchWish.ts +++ b/apps/web/src/app/archive/wish/[id]/_hooks/usePatchWish.ts @@ -41,7 +41,7 @@ export const usePatchWish = (wishId: number) => { toast.error(clientErrorMessage); } else if (status === 403 || status === 404 || status === 409) { toast.error(clientErrorMessage); - router.replace(ROUTES.ARCHIVE()); + router.replace(ROUTES.WISHLIST); } }, }); diff --git a/apps/web/src/app/archive/wish/[id]/layout.tsx b/apps/web/src/app/archive/wish/[id]/layout.tsx index 57363041..93c5270f 100644 --- a/apps/web/src/app/archive/wish/[id]/layout.tsx +++ b/apps/web/src/app/archive/wish/[id]/layout.tsx @@ -28,13 +28,13 @@ async function WishEditLayout({ children, params }: WishEditLayoutProps) { /** 아직 PROCESSING 상태인 경우에는 접근 불가 */ if (wishData.item.status === 'PROCESSING' || wishData.item.status === 'PENDING') - redirect(ROUTES.ARCHIVE()); + redirect(ROUTES.WISHLIST); } catch (error) { if (!isAxiosError(error)) throw error; /** 위시가 존재하지 않는 경우 */ if (error.response?.status === 404) - redirect(ROUTES.ARCHIVE('wish')); + redirect(ROUTES.WISHLIST); throw error; } diff --git a/apps/web/src/app/archive/wish/page.tsx b/apps/web/src/app/archive/wish/page.tsx new file mode 100644 index 00000000..4d8bad8b --- /dev/null +++ b/apps/web/src/app/archive/wish/page.tsx @@ -0,0 +1,12 @@ +import WishlistLayout from '../_components/WishlistLayout'; +import WishContent from '../_components/wish-content'; + +async function ArchiveWishPage() { + return ( + + + + ); +} + +export default ArchiveWishPage; diff --git a/apps/web/src/app/notification/_utils/getNotificationRoute.ts b/apps/web/src/app/notification/_utils/getNotificationRoute.ts index 41d5da4f..efe9b5b7 100644 --- a/apps/web/src/app/notification/_utils/getNotificationRoute.ts +++ b/apps/web/src/app/notification/_utils/getNotificationRoute.ts @@ -22,7 +22,7 @@ export const getNotificationRoute = ( if (extra?.kind === 'TOURNAMENT' && extra.tournamentId) { return ROUTES.TOURNAMENT_CREATE(extra.tournamentId); } - return ROUTES.ARCHIVE(); + return ROUTES.WISHLIST; case 'ANNOUNCEMENT': return null; } diff --git a/apps/web/src/components/bottom-tab-bar/index.tsx b/apps/web/src/components/bottom-tab-bar/index.tsx index ff0a2603..e4f14c3a 100644 --- a/apps/web/src/components/bottom-tab-bar/index.tsx +++ b/apps/web/src/components/bottom-tab-bar/index.tsx @@ -10,7 +10,7 @@ import { ROUTES } from '@/consts/route'; const TABS = [ { label: '홈', icon: HomeIconFill, href: ROUTES.HOME }, - { label: '보관', icon: HeartIconFill, href: ROUTES.ARCHIVE() }, + { label: '보관', icon: HeartIconFill, href: ROUTES.WISHLIST }, ] as const; function BottomTabBar() { @@ -43,7 +43,8 @@ function BottomTabBar() { {TABS.map(({ label, icon: Icon, href }) => { const isActive = label === '보관' - ? pathname.startsWith(ROUTES.ARCHIVE_BASE) + ? pathname.startsWith(ROUTES.WISHLIST) || + pathname.startsWith(ROUTES.TOURNAMENT_HISTORY) : pathname === href || pathname.startsWith(`${href}/`); return (