02. React 진입점과 라우팅 설정
요약
Gemini CLI로 구현하기 — 진입점 & 라우팅 설정
npx gemini-
1React 19 + Vite 프로젝트에서 main.jsx와 App.jsx를 작성해줘. main.jsx에는 BrowserRouter로 앱을 감싸고, App.jsx에는 Routes를 구성해줘. 페이지 컴포넌트는 Home, MovieDetail, Category, ErrorPage이고, 경로는 각각 /, /movie/:id, /category/:type, * 야. [파일 경로: src/]
- 사용 가이드:
[파일 경로]를 실제 src 폴더 경로로 바꾼다. 생성된 파일을src/폴더에 저장한다.
1. React 앱은 어디서 시작될까?
브라우저가 웹사이트를 열면, 가장 먼저 index.html 파일을 읽는다. 이 파일 안에 React가 연결되는 출발점이 있다.
1<!doctype html>2<html lang="ko">3 <head>4 <meta charset="UTF-8" />5 <link rel="icon" type="image/svg+xml" href="/vite.svg" />6 <meta name="viewport" content="width=device-width, initial-scale=1.0" />7 <title>frontend</title>8 </head>9 <body>10 <div id="root"></div>11 <script type="module" src="/src/main.jsx"></script>12 </body>13</html>| 줄 | 설명 |
|---|---|
| 10 | 핵심! React가 이 div 안에 모든 화면을 그린다. id="root"가 React의 시작점이다. |
| 11 | main.jsx 파일을 불러온다. 이 파일이 React 앱을 실제로 실행시킨다. |
정보
React는 빈 div 하나에 JavaScript로 화면을 그리는 방식이다. 이것을 SPA(Single Page Application, 싱글 페이지 애플리케이션)라고 부른다.
2. main.jsx 작성 — React의 출발점
src/main.jsx 파일을 열고 아래 코드를 작성한다.
1import { StrictMode } from "react";2import { createRoot } from "react-dom/client";3import "./index.css";4
5import App from "./App.jsx";6import { Home } from "./components/Home.jsx";7import { MovieDetail } from "./components/MovieDetail.jsx";8import { ErrorPage } from "./components/ErrorPage.jsx";9import { Category } from "./components/Category.jsx";10
11import { createBrowserRouter, RouterProvider } from "react-router";12
13const router = createBrowserRouter([14 {15 path: "/",16 element: <App />,17 errorElement: <ErrorPage />,18 children: [19 { index: true, element: <Home /> },20 { path: "movie/:id", element: <MovieDetail /> },21 { path: "category/:type", element: <Category /> },22 ],23 },24]);25
26createRoot(document.getElementById("root")).render(27 <StrictMode>28 <RouterProvider router={router} />29 </StrictMode>,30);코드가 세 부분으로 나뉜다. 탭을 눌러 각 역할을 확인해 보자.
- ① import 영역
- ② 라우터 설정
- ③ 렌더링
1import { StrictMode } from "react";2import { createRoot } from "react-dom/client";3import "./index.css";4
5import App from "./App.jsx";6import { Home } from "./components/Home.jsx";7import { MovieDetail } from "./components/MovieDetail.jsx";8import { ErrorPage } from "./components/ErrorPage.jsx";9import { Category } from "./components/Category.jsx";10
11import { createBrowserRouter, RouterProvider } from "react-router";| 줄 | 설명 |
|---|---|
| 1 | StrictMode(스트릭트모드) — 개발 중 잠재 문제를 경고한다. |
| 2 | createRoot — React 앱을 DOM에 연결하는 함수이다. |
| 3 | Tailwind CSS + 폰트 설정 파일을 불러온다. |
| 5 | 전체 레이아웃 역할의 App 컴포넌트이다. |
| 6-10 | 각 페이지 컴포넌트를 가져온다. 아직 만들지 않았으므로 나중에 하나씩 만든다. |
| 11 | URL 경로와 컴포넌트를 연결하는 라우팅 도구이다. |
1const router = createBrowserRouter([2 {3 path: "/",4 element: <App />,5 errorElement: <ErrorPage />,6 children: [7 { index: true, element: <Home /> },8 { path: "movie/:id", element: <MovieDetail /> },9 { path: "category/:type", element: <Category /> },10 ],11 },12]);| 줄 | 설명 |
|---|---|
| 3 | path: "/"는 메인 페이지(홈)의 경로이다. |
| 4 | 메인 경로에서 App 컴포넌트를 렌더링한다. |
| 5 | 존재하지 않는 URL에 접속하면 ErrorPage를 보여준다. |
| 7 | index: true — "/"에 접속하면 기본으로 Home을 표시한다. |
| 8 | movie/:id — :id는 동적 파라미터. movie/550처럼 숫자가 바뀐다. |
| 9 | category/:type — :type은 popular, now_playing, top_rated 등이 온다. |
1createRoot(document.getElementById("root")).render(2 <StrictMode>3 <RouterProvider router={router} />4 </StrictMode>,5);| 줄 | 설명 |
|---|---|
| 1 | index.html의 div#root를 찾아 React 앱을 연결한다. |
| 2-4 | StrictMode로 앱 전체를 감싸고, RouterProvider에 라우터를 전달한다. |
3. 라우팅의 동작 원리
| URL | 표시되는 컴포넌트 |
|---|---|
http://localhost:5173/ | App 안에 Home 표시 |
http://localhost:5173/movie/550 | App 안에 MovieDetail 표시 |
http://localhost:5173/search?q=어벤져스 | App 안에 Search 표시 |
http://localhost:5173/category/popular | App 안에 Category 표시 |
| 존재하지 않는 URL | ErrorPage 표시 |
App 컴포넌트는 항상 화면에 있고(헤더+푸터), URL이 바뀌면 App 안쪽 내용만 교체된다. 이 구조를 중첩 라우팅(Nested Routing, 네스티드 라우팅)이라 한다.
요약
넷플릭스를 떠올려 보자. 상단의 로고와 메뉴는 항상 그대로인데, 메인 콘텐츠 영역만 바뀐다. 이것이 바로 중첩 라우팅이다.
4. 동적 라우트 파라미터 이해하기
이 프로젝트에는 두 종류의 동적 파라미터가 있다.
| 경로 | 파라미터 | 예시 URL | 값 |
|---|---|---|---|
movie/:id | :id | /movie/550 | "550" |
category/:type | :type | /category/popular | "popular" |
나중에 각 컴포넌트에서 useParams() 훅으로 이 값을 꺼내 API에 요청을 보낸다.
5. errorElement란?
1errorElement: <ErrorPage />,사용자가 존재하지 않는 URL(예: /abcdef)에 접속하면 React Router가 자동으로 ErrorPage 컴포넌트를 보여준다.
6. 아직 없는 컴포넌트 임시 생성
지금 main.jsx가 여러 컴포넌트를 import 하고 있지만, 아직 해당 파일들이 존재하지 않는다. 이 상태로 개발 서버를 실행하면 에러가 발생한다. src/components/ 폴더 안에 아래 4개 파일을 각각 생성하고, 코드를 한 줄씩 입력한다.
- Home
- MovieDetail
- ErrorPage
- Category
1export function Home() {2 return <div>Home 페이지 (준비중)</div>;3}1export function MovieDetail() {2 return <div>MovieDetail 페이지 (준비중)</div>;3}1export function ErrorPage() {2 return <div>404 에러 페이지 (준비중)</div>;3}1export function Category() {2 return <div>Category 페이지 (준비중)</div>;3}이 프로젝트에서는 App.jsx만 export default를 사용하고, 나머지 컴포넌트는 export function(named export)을 사용한다.
| 방식 | 코드 | 가져올 때 |
|---|---|---|
export default | export default function App() | import App from "./App" |
| named export | export function Home() | import { Home } from "./Home" |
중괄호 { } 유무에 주의한다.
7. App.jsx 임시 생성
src/App.jsx 파일을 열고 기존 내용을 모두 지운 뒤, 아래 코드로 교체한다. Outlet(아울렛)은 URL이 바뀔 때 하위 페이지가 표시되는 빈자리라고 생각하면 된다. 액자의 틀은 그대로인데 안에 들어가는 사진만 바뀌는 것과 같다.
1import { Outlet } from "react-router";2
3export default function App() {4 return (5 <div>6 <h1>GOFLEX</h1>7 <Outlet />8 </div>9 );10}8. 동작 확인
1npm run dev| 확인 항목 | 기대 결과 |
|---|---|
http://localhost:5173/ | ”GOFLEX”와 “Home 페이지 (준비중)” 텍스트 표시 |
http://localhost:5173/movie/550 | ”GOFLEX”와 “MovieDetail 페이지 (준비중)” 텍스트 표시 |
http://localhost:5173/abcdef | ”404 에러 페이지 (준비중)” 텍스트 표시 |
9. 전체 코드
- src/main.jsx
- src/App.jsx (임시)
1import { StrictMode } from "react";2import { createRoot } from "react-dom/client";3import "./index.css";4
5import App from "./App.jsx";6import { Home } from "./components/Home.jsx";7import { MovieDetail } from "./components/MovieDetail.jsx";8import { ErrorPage } from "./components/ErrorPage.jsx";9import { Category } from "./components/Category.jsx";10
11import { createBrowserRouter, RouterProvider } from "react-router";12
13const router = createBrowserRouter([14 {15 path: "/",16 element: <App />,17 errorElement: <ErrorPage />,18 children: [19 { index: true, element: <Home /> },20 { path: "movie/:id", element: <MovieDetail /> },21 { path: "category/:type", element: <Category /> },22 ],23 },24]);25
26createRoot(document.getElementById("root")).render(27 <StrictMode>28 <RouterProvider router={router} />29 </StrictMode>,30);1import { Outlet } from "react-router";2
3export default function App() {4 return (5 <div>6 <h1>GOFLEX</h1>7 <Outlet />8 </div>9 );10}