Skip to content

Commit 4059c91

Browse files
authored
Merge pull request #10 from TTORANG/feat/routing#9
feat/라우팅 설정
2 parents 9914cf9 + 841d6a8 commit 4059c91

12 files changed

Lines changed: 98 additions & 28 deletions

File tree

src/App.tsx

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,22 @@
1+
import { Outlet, useLocation } from 'react-router-dom';
2+
13
import './App.css';
4+
import { Gnb, Layout } from './components/layout';
5+
import { DEFAULT_TAB, PATH_TO_TAB } from './constants/navigation';
26

37
function App() {
4-
return <></>;
8+
const location = useLocation();
9+
const activeTab = PATH_TO_TAB[location.pathname] ?? DEFAULT_TAB;
10+
11+
return (
12+
<Layout
13+
headerLeft={<div>로고</div>}
14+
headerCenter={<Gnb activeTab={activeTab} />}
15+
headerRight={<div>로그인</div>}
16+
>
17+
<Outlet />
18+
</Layout>
19+
);
520
}
621

722
export default App;
Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,40 @@
1+
import { Link } from 'react-router-dom';
2+
13
import clsx from 'clsx';
24

3-
import { TABS, type Tab } from '../../constants/navigation';
5+
import { DEFAULT_TAB, TABS, type Tab } from '../../constants/navigation';
46

5-
interface GNBProps {
7+
interface GnbProps {
68
activeTab?: Tab;
7-
onTabChange?: (tab: Tab) => void;
89
}
910

10-
export function GNB({ activeTab = 'slide', onTabChange }: GNBProps) {
11+
export function Gnb({ activeTab = DEFAULT_TAB }: GnbProps) {
1112
return (
1213
<nav
13-
className="absolute left-1/2 flex h-15 -translate-x-1/2 items-center justify-center"
14+
className="flex h-15 items-center justify-center"
1415
role="tablist"
1516
aria-label="네비게이션 메뉴"
1617
>
17-
{TABS.map(({ key, label }) => {
18+
{TABS.map(({ key, label, path }) => {
1819
const isActive = activeTab === key;
1920
return (
20-
<button
21+
<Link
2122
key={key}
22-
type="button"
23+
to={path}
2324
role="tab"
2425
id={`tab-${key}`}
2526
aria-selected={isActive}
2627
aria-controls={`tabpanel-${key}`}
27-
onClick={() => onTabChange?.(key)}
2828
className={clsx(
29-
`flex h-full w-25 items-end justify-center px-2.5 pb-4 pt-4 text-body-m-bold border-b-2`,
29+
'flex h-full w-25 items-end justify-center px-2.5 pb-4 pt-4 text-body-m-bold border-b-2',
3030
{
3131
'border-main text-main': isActive,
3232
'border-transparent text-gray-600': !isActive,
3333
},
3434
)}
3535
>
3636
{label}
37-
</button>
37+
</Link>
3838
);
3939
})}
4040
</nav>

src/components/layout/Header.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
import type { ReactNode } from 'react';
22

33
interface HeaderProps {
4-
children?: ReactNode;
4+
left?: ReactNode;
5+
center?: ReactNode;
6+
right?: ReactNode;
57
}
68

7-
export function Header({ children }: HeaderProps) {
9+
export function Header({ left, center, right }: HeaderProps) {
810
return (
9-
<header className="fixed top-0 left-0 right-0 z-50 flex h-15 items-center justify-center border-b border-gray-200 bg-white px-18">
10-
{children}
11+
<header className="fixed top-0 left-0 right-0 z-50 flex h-15 items-center justify-between border-b border-gray-200 bg-white px-18">
12+
<div className="flex items-center">{left}</div>
13+
<div className="absolute left-1/2 -translate-x-1/2">{center}</div>
14+
<div className="flex items-center">{right}</div>
1115
</header>
1216
);
1317
}

src/components/layout/Layout.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,16 @@ import type { ReactNode } from 'react';
33
import { Header } from './Header';
44

55
interface LayoutProps {
6-
header: ReactNode;
6+
headerLeft?: ReactNode;
7+
headerCenter?: ReactNode;
8+
headerRight?: ReactNode;
79
children?: ReactNode;
810
}
911

10-
export function Layout({ children, header }: LayoutProps) {
12+
export function Layout({ headerLeft, headerCenter, headerRight, children }: LayoutProps) {
1113
return (
1214
<div className="min-h-screen bg-gray-100">
13-
<Header>{header}</Header>
15+
<Header left={headerLeft} center={headerCenter} right={headerRight} />
1416
<main className="pt-15">{children}</main>
1517
</div>
1618
);

src/components/layout/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
export { GNB } from './GNB';
1+
export { Gnb } from './Gnb';
22
export { Header } from './Header';
33
export { Layout } from './Layout';

src/constants/navigation.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
export const TABS = [
2-
{ key: 'slide', label: '슬라이드' },
3-
{ key: 'video', label: '영상' },
4-
{ key: 'insight', label: '인사이트' },
2+
{ key: 'slide', label: '슬라이드', path: '/slide' },
3+
{ key: 'video', label: '영상', path: '/video' },
4+
{ key: 'insight', label: '인사이트', path: '/insight' },
55
] as const;
66

77
export type Tab = (typeof TABS)[number]['key'];
8+
9+
export const DEFAULT_TAB: Tab = 'slide';
10+
11+
export const PATH_TO_TAB: Record<string, Tab> = {
12+
'/': DEFAULT_TAB,
13+
...Object.fromEntries(TABS.map((tab) => [tab.path, tab.key])),
14+
};

src/main.tsx

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,28 @@
11
import { StrictMode } from 'react';
22
import { createRoot } from 'react-dom/client';
3+
import { RouterProvider, createBrowserRouter } from 'react-router-dom';
34

4-
import App from './App.tsx';
5+
import App from './App';
6+
import InsightPage from './pages/InsightPage';
7+
import SlidePage from './pages/SlidePage';
8+
import VideoPage from './pages/VideoPage';
59
import './styles/index.css';
610

11+
const router = createBrowserRouter([
12+
{
13+
path: '/',
14+
element: <App />,
15+
children: [
16+
{ index: true, element: <SlidePage /> }, // DEFAULT_TAB 변경 시 동기화 필요
17+
{ path: 'slide', element: <SlidePage /> },
18+
{ path: 'video', element: <VideoPage /> },
19+
{ path: 'insight', element: <InsightPage /> },
20+
],
21+
},
22+
]);
23+
724
createRoot(document.querySelector('#root')!).render(
825
<StrictMode>
9-
<App />
26+
<RouterProvider router={router} />
1027
</StrictMode>,
1128
);

src/pages/InsightPage.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export default function InsightPage() {
2+
return (
3+
<div role="tabpanel" id="tabpanel-insight" aria-labelledby="tab-insight" className="p-8">
4+
<h1 className="text-body-m-bold">인사이트</h1>
5+
</div>
6+
);
7+
}

src/pages/SlidePage.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export default function SlidePage() {
2+
return (
3+
<div role="tabpanel" id="tabpanel-slide" aria-labelledby="tab-slide" className="p-8">
4+
<h1 className="text-body-m-bold">슬라이드</h1>
5+
</div>
6+
);
7+
}

src/pages/VideoPage.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export default function VideoPage() {
2+
return (
3+
<div role="tabpanel" id="tabpanel-video" aria-labelledby="tab-video" className="p-8">
4+
<h1 className="text-body-m-bold">영상</h1>
5+
</div>
6+
);
7+
}

0 commit comments

Comments
 (0)