Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
732 changes: 667 additions & 65 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@
"preview": "vite preview"
},
"dependencies": {
"@tailwindcss/vite": "^4.1.17",
"axios": "^1.13.2",
"react": "^19.1.1",
"react-dom": "^19.1.1",
"react-router-dom": "^7.9.5",
"socket.io-client": "^4.8.1"
},
"devDependencies": {
Expand Down
10 changes: 9 additions & 1 deletion public/vite.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Empty file removed src/App.css
Empty file.
14 changes: 10 additions & 4 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
const App = () => {
return <div>Snowgent</div>;
};
import { Outlet } from 'react-router-dom';

export default App;
export default function App() {
return (
<div className="mobile-container">
<div className="mobile-content">
<Outlet />
</div>
</div>
);
}
125 changes: 125 additions & 0 deletions src/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
@import 'tailwindcss';

@font-face {
font-family: 'Pretendard';
src: url('https://cdn.jsdelivr.net/gh/projectnoonnu/[email protected]/Pretendard-Thin.woff2')
format('woff2');
font-weight: 100;
font-display: swap;
}

@font-face {
font-family: 'Pretendard';
src: url('https://cdn.jsdelivr.net/gh/projectnoonnu/[email protected]/Pretendard-ExtraLight.woff2')
format('woff2');
font-weight: 200;
font-display: swap;
}

@font-face {
font-family: 'Pretendard';
src: url('https://cdn.jsdelivr.net/gh/projectnoonnu/[email protected]/Pretendard-Light.woff2')
format('woff2');
font-weight: 300;
font-display: swap;
}

@font-face {
font-family: 'Pretendard';
src: url('https://cdn.jsdelivr.net/gh/projectnoonnu/[email protected]/Pretendard-Regular.woff2')
format('woff2');
font-weight: 400;
font-display: swap;
}

@font-face {
font-family: 'Pretendard';
src: url('https://cdn.jsdelivr.net/gh/projectnoonnu/[email protected]/Pretendard-Medium.woff2')
format('woff2');
font-weight: 500;
font-display: swap;
}

@font-face {
font-family: 'Pretendard';
src: url('https://cdn.jsdelivr.net/gh/projectnoonnu/[email protected]/Pretendard-SemiBold.woff2')
format('woff2');
font-weight: 600;
font-display: swap;
}

@font-face {
font-family: 'Pretendard';
src: url('https://cdn.jsdelivr.net/gh/projectnoonnu/[email protected]/Pretendard-Bold.woff2')
format('woff2');
font-weight: 700;
font-display: swap;
}

@font-face {
font-family: 'Pretendard';
src: url('https://cdn.jsdelivr.net/gh/projectnoonnu/[email protected]/Pretendard-ExtraBold.woff2')
format('woff2');
font-weight: 800;
font-display: swap;
}

@font-face {
font-family: 'Pretendard';
src: url('https://cdn.jsdelivr.net/gh/projectnoonnu/[email protected]/Pretendard-Black.woff2')
format('woff2');
font-weight: 900;
font-display: swap;
}

@layer base {
* {
@apply box-border;
}

html {
@apply h-full overflow-x-hidden;
height: 100dvh;
}

body {
@apply m-0 h-full p-0 antialiased;
font-family:
'Pretendard',
-apple-system,
BlinkMacSystemFont,
system-ui,
sans-serif;
background-color: #f9f9f9;
overscroll-behavior: none;
touch-action: pan-y;
}

#root {
@apply h-full;
}
}

@layer components {
.mobile-container {
@apply mx-auto flex h-full w-full justify-center;
max-width: 480px;
}

.mobile-content {
@apply flex h-full w-full flex-col;
background-color: white;
}

.mobile-padding {
@apply px-4 py-4;
}

.safe-top {
padding-top: max(env(safe-area-inset-top), 1rem);
}

.safe-bottom {
padding-bottom: max(env(safe-area-inset-bottom), 1rem);
}
}
30 changes: 30 additions & 0 deletions src/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
import Onboarding from './pages/onboarding/Onboarding';
import Chat from './pages/chat/Chat';
import App from './App';
import HomePage from './pages/HomePage';

const router = createBrowserRouter([
{
path: '/',
element: <App />,
children: [
{
index: true,
element: <HomePage />,
},
{
path: 'onboarding',
element: <Onboarding />,
},
{
path: 'chat',
element: <Chat />,
},
],
},
]);
Comment on lines +7 to +26
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

라우터 구조는 잘 설계되었으나 에러 바운더리가 누락되었습니다.

React Router v7의 createBrowserRouter를 올바르게 사용하고 있으며, 레이아웃 기반 중첩 라우팅 구조도 적절합니다. 하지만 프로덕션 준비를 위해 에러 처리를 추가하는 것을 권장합니다.

에러 바운더리와 404 페이지를 추가하세요:

 const router = createBrowserRouter([
   {
     path: '/',
     element: <App />,
+    errorElement: <ErrorPage />,
     children: [
       {
         index: true,
         element: <HomePage />,
       },
       {
         path: 'onboarding',
         element: <Onboarding />,
       },
       {
         path: 'chat',
         element: <Chat />,
       },
+      {
+        path: '*',
+        element: <NotFoundPage />,
+      },
     ],
   },
 ]);

ErrorPage 컴포넌트 예시:

import { useRouteError, isRouteErrorResponse } from 'react-router-dom';

function ErrorPage() {
  const error = useRouteError();
  
  return (
    <div className="flex h-full flex-col items-center justify-center gap-4">
      <h1 className="text-2xl font-bold">오류가 발생했습니다</h1>
      <p className="text-gray-600">
        {isRouteErrorResponse(error) ? error.statusText : '알 수 없는 오류'}
      </p>
    </div>
  );
}
🤖 Prompt for AI Agents
In src/index.tsx around lines 7 to 26, the router is missing an error boundary
and a catch-all 404 route; create or import an ErrorPage component (using
useRouteError/isRouteErrorResponse to render route errors) and set it as the
root route's errorElement, and add a final route with path '*' (or { path: '*',
element: <NotFound /> } using ErrorPage or a simple 404 component) so both route
errors and unknown paths are handled in production.


export default function Router() {
return <RouterProvider router={router} />;
}
12 changes: 6 additions & 6 deletions src/main.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.tsx'
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import './index.css';
import Router from './index.tsx';

createRoot(document.getElementById('root')!).render(
<StrictMode>
<App />
<Router />
</StrictMode>,
)
);
20 changes: 20 additions & 0 deletions src/pages/HomePage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { useNavigate } from 'react-router-dom';
import logo from '/vite.svg';

const HomePage = () => {
const navigate = useNavigate();
return (
<div className="flex h-full flex-col items-center justify-center gap-8">
<img src={logo} alt="Vite logo" className="h-40" />
<h1 className="text-6xl text-[#0D2D84]">Snowgent</h1>
<button
onClick={() => navigate('/onboarding')}
className="cursor-pointer rounded-lg bg-blue-50 px-10 py-5 text-xl font-semibold text-[#0D2D84] hover:bg-blue-100"
>
시작하기
</button>
</div>
);
};

export default HomePage;
5 changes: 5 additions & 0 deletions src/pages/chat/Chat.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const Chat = () => {
return <div className="flex h-full flex-col">Chat</div>;
};

export default Chat;
5 changes: 5 additions & 0 deletions src/pages/onboarding/Onboarding.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const Onboarding = () => {
return <div>Onboarding</div>;
};

export default Onboarding;
9 changes: 5 additions & 4 deletions vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react-swc'
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react-swc';
import tailwindcss from '@tailwindcss/vite';

// https://vite.dev/config/
export default defineConfig({
plugins: [react()],
})
plugins: [react(), tailwindcss()],
});