diff --git a/src/App.tsx b/src/App.tsx index f702fa08..b09fa42b 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,7 +1,22 @@ +import { Outlet, useLocation } from 'react-router-dom'; + import './App.css'; +import { Gnb, Layout } from './components/layout'; +import { DEFAULT_TAB, PATH_TO_TAB } from './constants/navigation'; function App() { - return <>; + const location = useLocation(); + const activeTab = PATH_TO_TAB[location.pathname] ?? DEFAULT_TAB; + + return ( + 로고} + headerCenter={} + headerRight={
로그인
} + > + +
+ ); } export default App; diff --git a/src/components/layout/GNB.tsx b/src/components/layout/Gnb.tsx similarity index 53% rename from src/components/layout/GNB.tsx rename to src/components/layout/Gnb.tsx index 144f59b7..2db7fff9 100644 --- a/src/components/layout/GNB.tsx +++ b/src/components/layout/Gnb.tsx @@ -1,32 +1,32 @@ +import { Link } from 'react-router-dom'; + import clsx from 'clsx'; -import { TABS, type Tab } from '../../constants/navigation'; +import { DEFAULT_TAB, TABS, type Tab } from '../../constants/navigation'; -interface GNBProps { +interface GnbProps { activeTab?: Tab; - onTabChange?: (tab: Tab) => void; } -export function GNB({ activeTab = 'slide', onTabChange }: GNBProps) { +export function Gnb({ activeTab = DEFAULT_TAB }: GnbProps) { return ( diff --git a/src/components/layout/Header.tsx b/src/components/layout/Header.tsx index 9dd91d49..caf158d9 100644 --- a/src/components/layout/Header.tsx +++ b/src/components/layout/Header.tsx @@ -1,13 +1,17 @@ import type { ReactNode } from 'react'; interface HeaderProps { - children?: ReactNode; + left?: ReactNode; + center?: ReactNode; + right?: ReactNode; } -export function Header({ children }: HeaderProps) { +export function Header({ left, center, right }: HeaderProps) { return ( -
- {children} +
+
{left}
+
{center}
+
{right}
); } diff --git a/src/components/layout/Layout.tsx b/src/components/layout/Layout.tsx index 1517a961..ff060363 100644 --- a/src/components/layout/Layout.tsx +++ b/src/components/layout/Layout.tsx @@ -3,14 +3,16 @@ import type { ReactNode } from 'react'; import { Header } from './Header'; interface LayoutProps { - header: ReactNode; + headerLeft?: ReactNode; + headerCenter?: ReactNode; + headerRight?: ReactNode; children?: ReactNode; } -export function Layout({ children, header }: LayoutProps) { +export function Layout({ headerLeft, headerCenter, headerRight, children }: LayoutProps) { return (
-
{header}
+
{children}
); diff --git a/src/components/layout/index.ts b/src/components/layout/index.ts index 985c4f27..e5fd9468 100644 --- a/src/components/layout/index.ts +++ b/src/components/layout/index.ts @@ -1,3 +1,3 @@ -export { GNB } from './GNB'; +export { Gnb } from './Gnb'; export { Header } from './Header'; export { Layout } from './Layout'; diff --git a/src/constants/navigation.ts b/src/constants/navigation.ts index 9224f64e..a547c29a 100644 --- a/src/constants/navigation.ts +++ b/src/constants/navigation.ts @@ -1,7 +1,14 @@ export const TABS = [ - { key: 'slide', label: '슬라이드' }, - { key: 'video', label: '영상' }, - { key: 'insight', label: '인사이트' }, + { key: 'slide', label: '슬라이드', path: '/slide' }, + { key: 'video', label: '영상', path: '/video' }, + { key: 'insight', label: '인사이트', path: '/insight' }, ] as const; export type Tab = (typeof TABS)[number]['key']; + +export const DEFAULT_TAB: Tab = 'slide'; + +export const PATH_TO_TAB: Record = { + '/': DEFAULT_TAB, + ...Object.fromEntries(TABS.map((tab) => [tab.path, tab.key])), +}; diff --git a/src/main.tsx b/src/main.tsx index 66b0f0df..406b8e1e 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,11 +1,28 @@ import { StrictMode } from 'react'; import { createRoot } from 'react-dom/client'; +import { RouterProvider, createBrowserRouter } from 'react-router-dom'; -import App from './App.tsx'; +import App from './App'; +import InsightPage from './pages/InsightPage'; +import SlidePage from './pages/SlidePage'; +import VideoPage from './pages/VideoPage'; import './styles/index.css'; +const router = createBrowserRouter([ + { + path: '/', + element: , + children: [ + { index: true, element: }, // DEFAULT_TAB 변경 시 동기화 필요 + { path: 'slide', element: }, + { path: 'video', element: }, + { path: 'insight', element: }, + ], + }, +]); + createRoot(document.querySelector('#root')!).render( - + , ); diff --git a/src/pages/InsightPage.tsx b/src/pages/InsightPage.tsx new file mode 100644 index 00000000..38d9ac0e --- /dev/null +++ b/src/pages/InsightPage.tsx @@ -0,0 +1,7 @@ +export default function InsightPage() { + return ( +
+

인사이트

+
+ ); +} diff --git a/src/pages/SlidePage.tsx b/src/pages/SlidePage.tsx new file mode 100644 index 00000000..7e3229fb --- /dev/null +++ b/src/pages/SlidePage.tsx @@ -0,0 +1,7 @@ +export default function SlidePage() { + return ( +
+

슬라이드

+
+ ); +} diff --git a/src/pages/VideoPage.tsx b/src/pages/VideoPage.tsx new file mode 100644 index 00000000..c6b10470 --- /dev/null +++ b/src/pages/VideoPage.tsx @@ -0,0 +1,7 @@ +export default function VideoPage() { + return ( +
+

영상

+
+ ); +} diff --git a/src/styles/index.css b/src/styles/index.css index 1da34e2c..d029f3af 100644 --- a/src/styles/index.css +++ b/src/styles/index.css @@ -23,4 +23,8 @@ --font-size-caption: var(--font-size-caption); --font-size-body-s: var(--font-size-body-s); --font-size-body-m: var(--font-size-body-m); + + --spacing-15: 3.75rem; + --spacing-18: 4.5rem; + --spacing-25: 6.25rem; } diff --git a/src/styles/theme.css b/src/styles/theme.css index a00eaff8..fb7cfecf 100644 --- a/src/styles/theme.css +++ b/src/styles/theme.css @@ -25,9 +25,9 @@ --line-height: 1.5; /* 폰트 크기 */ - --font-size-caption: 12px; - --font-size-body-s: 14px; - --font-size-body-m: 16px; + --font-size-caption: 0.75rem; + --font-size-body-s: 0.875rem; + --font-size-body-m: 1rem; /* 폰트 굵기 */ --font-weight-regular: 400;