💡 URECA/🗒️ 스터디 노트

[URECA] Day52 React Router

코딩하세현 2025. 4. 11. 17:28
728x90

📘 React Router 개념

React에서 라우팅을 구현하기 위한 표준 라이브러리

웹 애플리케이션에서 여러 페이지를 쉽게 관리하고 내비게이션을 구현할 수 있게 해준다.

  • 라우팅의 개념예를 들어:
    • example.com/home → 서버의 home.html 파일 요청
    • example.com/about → 서버의 about.html 파일 요청
    React와 같은 단일 페이지 애플리케이션(SPA)에서는 클라이언트 사이드 라우팅이 사용됩니다.
    1. 웹 애플리케이션이 처음 로드될 때 필요한 모든 JavaScript를 다운로드
    2. URL이 변경되면 새로운 페이지를 서버에서 받아오는 대신, JavaScript가 브라우저의 히스토리 API를 사용하여 URL을 변경하고 화면에 표시되는 컴포넌트를 변경
    3. 페이지 전체를 다시 로드하지 않기 때문에 더 빠른 사용자 경험을 제공

특징

  • 라우팅이란 웹 애플리케이션에서 URL에 따라 다른 콘텐츠를 사용자에게 보여주는 메커니즘

전통적인 웹 사이트와의 차이점

  • 전통적인 웹사이트에서는 각 URL이 서버에 있는 다른 HTML 파일을 요청하는 방식으로 작동

1. **프로토콜(Protocol)**: `https://`
    ◦ HTTPS(Hypertext Transfer Protocol Secure)는 웹 브라우저와 웹 서버 간의 암호화된 통신을 제공하는 프로토콜
    ◦ 일반 HTTP와 달리 데이터가 암호화되어 전송되므로 보안이 강화된다.
2. **도메인(Domain)**: `heart-heart.org`
    ◦ 웹사이트의 주소를 나타내는 고유 식별자
    ◦ `.org`는 주로 비영리 단체나 기관을 위한 최상위 도메인(TLD)
    ◦ `heart-heart`는 도메인 이름으로, 해당 조직의 이름을 나타낸다.
3. **경로(Path)**: `/sponsor/regular`
    ◦ 웹사이트 내에서 특정 페이지나 리소스의 위치를 나타낸다.
    ◦ `/sponsor`는 후원 관련 페이지의 디렉토리를 나타낸다.
    ◦ `/regular`는 그 안의 '정기 후원' 관련 페이지를 나타낸다.
4. **쿼리 스트링(Query String)**:
    ◦ 제공된 URL에는 쿼리 스트링이 포함되어 있지 않지만, 일반적으로 `?` 다음에 오는 부분이다.
    ◦ 만약 쿼리 스트링이 있다면: `https://heart-heart.org/sponsor/regular?param1=value1¶m2=value2` 형태로 나타 나타낸다.
    ◦ 쿼리 스트링은 서버에 추가 정보를 전달하는 데 사용되며, 주로 검색 조건, 필터링, 페이지네이션 등에 활용된다.
5. **프래그먼트(Fragment)**:
    ◦  `#` 다음에 오는 부분
    ◦ 예: `https://heart-heart.org/sponsor/regular#section1`
    ◦ 페이지 내 특정 섹션으로 직접 이동할 때 사용
만약 이 URL에 쿼리 스트링이 있다면 (예: `https://heart-heart.org/sponsor/regular?type=monthly&amount=10000`), 이는 정기 후원 페이지에서 월간 후원 타입과 10,000원의 후원 금액을 지정하는 파라미터일 수 있다.


📘 React Router 연결 순서

📁 새로운 리액트 파일을 만들기 위해서는 npm create vite@latest를 입력한다. 

그 뒤 npm i를 입력해 추가 확장자들을 설치해준다.

⬇️ ⬇️ ⬇️

router를 이용하기 위해서 npm i react-router-dom axios json-server 를 입력해준다. 

⬇️ ⬇️ ⬇️

routes폴더 - pages 폴더-AboutPage.jsx

🧠 리액트 함수 컴포넌트 단축어 rsc 또는 rafce 기억하자

import React from 'react';

const AboutPage = () => {
  return <div>AboutPage</div>;
};

export default AboutPage;

 

페이지를 만들었으면 뭘 하냐? 연결! 연결해야한다. 

⬇️ ⬇️ ⬇️

📁 App.jsx에서 연결

import React from 'react';
import AboutPage from './routes/pages/AboutPage';

const App = () => {
  return (
    <div>
      <AboutPage />
    </div>
  );
};

export default App;

⬇️ ⬇️ ⬇️

 

📁 형제관계인 components폴더 생성 후 Header.jsx파일 만든다. 

import React from 'react';

const Header = () => {
  return (
    <header>
      <h1>로고</h1>
    </header>
  );
};

export default Header;

⬇️ ⬇️ ⬇️

 

📁  기존 css 파일 내용물 초기화 후 index.css

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

a {
  text-decoration: none;
  color: inherit;
}

 

color: inherit란 무엇인가?

부모 요소로부터 해당 속성의 계산값을 받아 사용

inherit - CSS: Cascading Style Sheets | MDN

 

inherit - CSS: Cascading Style Sheets | MDN

CSS inherit 키워드를 사용한 속성은 부모 요소로부터 해당 속성의 계산값을 받아 사용합니다. CSS all 단축 속성을 포함한 모든 속성에 사용할 수 있습니다.

developer.mozilla.org

⬇️ ⬇️ ⬇️

 

📁  Footer역시 만들어서 App.jsx에 연결해준다. 

import React from 'react';

const Footer = () => {
  return <footer>Footer</footer>;
};

export default Footer;

⬇️ ⬇️ ⬇️

import React from 'react';
import AboutPage from './routes/pages/AboutPage';
import Header from './components/Header';
import Footer from './components/Footer';

const App = () => {
  return (
    <div>
      <Header />
      <AboutPage />
      <Footer />
    </div>
  );
};

export default App;

app.jsx에 각각 컴포넌트를 불러왔으면 import하는걸 잊지말자! 🙂

 

🤔 Router에 의해서 화면이 처리되는 방법은 어떻게 할까?

 

📁 router폴더에 index.jsx 파일 생성

import { RouterProvider } from 'react-router';
import { createBrowserRouter } from 'react-router-dom';

const router = createBrowserRouter([여기에는 보통 배열이 들어간다. ]);

// 함수를 하나 만들어서 내보내야된다.
export default function Router() {
    return <RouterProvider router={router} />;
}

createBrowserRouter란?

모든 ReactRouter 웹 프로젝트에 권장되는 라우터이다. 

 

createBrowserRouter v6.30.0 | 반응 라우터

 

createBrowserRouter v6.30.0 | React Router

 

reactrouter.com

 

import { createBrowserRouter, RouterProvider } from 'react-router-dom';
import App from './App';

const router = createBrowserRouter([
  {
    path: '/',
    element: <App />,
  },
]);

// 함수 만들어서 내보내야한다.
export default function Router() {
  return <RouterProvider router={router} />;
}

 

createBrowserRouter([...])

라우터 정보를 배열로 정의해서 라우터를 객체 기반으로 구성하고 있다.

 

배열 내부의 객체 [{path:'/', element:<App/>}]

path: '/' 어떤 주소에서  사용자가 브라우저 주소창에 /로 접속했을 때
element: <App /> 어떤 컴포넌트를 보여줄지 <App /> 컴포넌트를 화면에 렌더링해줌

⬇️⬇️⬇️

main.jsx

import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import './index.css';
import Router from './router/index.jsx';

createRoot(document.getElementById('root')).render(
  <StrictMode>
    <Router />
  </StrictMode>
);

app.jsx에서 일정한 레이아웃을 만드는게 필요하다. 

⬇️⬇️⬇️

📁 일단 router폴더- layout폴더 - Default.jsx파일 생성

app.jsx에 있던 

      <Header />
      <AboutPage />
      <Footer />

을 Default.jsx에 옮긴다. 

import React from 'react';
import { Outlet } from 'react-router-dom';

const Default = () => {
  return (
    <div>
      <Header />
      {/* Outlet은 라우터에 의해서 보여질 컴포넌트 */}
      <Outlet />
      <Footer />
    </div>
  );
};

export default Default;

<Outlet/>이 적혀있던 자리에 <App/>이 있었다. <App/>이 있을때는 화면이 렌더링이 안됭ㅆ다.

왜 그런것일까? <Outlet/>의 역할은 무엇일까?

 

📘 Outlet이 하는 역할

<Outlet/>은 중첩 라우트에서 자식 컴포넌트를 보여주는 자리

 

ex: App이 Outlet으로 이동했을 때

const router = createBrowserRouter([
  {
    path: '/',
    element: <Default />, // 여기에서 Outlet을 사용
    children: [
      {
        index: true,
        element: <App /> // 이게 Outlet 안에 들어가서 보여짐
      }
    ]
  }
]);

👉 이렇게 하면 /에 접속했을 때 Default 컴포넌트가 렌더링되고, 그 안에서 <Outlet /> 자리에 <App />이 나오는 것

 

그렇다면 왜 App을 바로 넣으면 안보이는걸가?

이전 코드에서는 

{
  path: '/',
  element: <App /> // 이건 Default 없이 바로 App만 보여주는 구조
}

지금은

{
  path: '/',
  element: <Default />,
  children: [
    { element: <App /> }
  ]
}

이제는 App이 자식 라우트여서 Default에서 <Outlet/>이 없으면 렌더링 위치가 없다! 그래서 화면에 안나오는 것이다.

 

App이 자식이면 반드시 Outlet이 필요하다! Outlet이 없으면 아무리 자식 설정을 해도 화면에 나오지 않는다. 

⬇️⬇️⬇️ 

그 뒤 App.jsx 폴더 삭제 후 index.jsx 파일에

import { RouterProvider } from 'react-router';
import { createBrowserRouter } from 'react-router-dom';
import Default from './layout/Default';

const router = createBrowserRouter([{ path: '/', element: <Default /> }]);

// 함수를 하나 만들어서 내보내야된다.
export default function Router() {
  return <RouterProvider router={router} />;
}

router를 새팅할때 대괄호를 만들어 path정보와 element정보를 만든다. 

 

그 하위 옵션으로 path를 넣는 이유는? routes 폴더 안에 pages가 있어서 그러나? (위에서 설명함)

import { RouterProvider } from 'react-router';
import { createBrowserRouter } from 'react-router-dom';
import Default from './layout/Default';

const router = createBrowserRouter([{  element: <Default />, children:[{}] }]);

// 함수를 하나 만들어서 내보내야된다.
export default function Router() {
  return <RouterProvider router={router} />;
}

 

import React from 'react';
import { Link } from 'react-router-dom';

const Header = () => {
  return (
    <header>
      <h1>
        <Link to={'/'}>로고</Link>
      </h1>
      <nav>
        <Link to={'/about'}>회사소개</Link>
        <Link to={'/shop'}>쇼핑</Link>
        <Link to={'/'}>링크</Link>
      </nav>
    </header>
  );
};

export default Header;
import React from 'react';
import { NavLink } from 'react-router-dom';

const Header = () => {
  return (
    <header>
      <h1>
        <NavLink to={'/'}>로고</NavLink>
      </h1>
      <nav>
        <NavLink to={'/about'}>회사소개</NavLink>
        <NavLink to={'/shop'}>쇼핑</NavLink>
        <NavLink to={'/'}>링크</NavLink>
      </nav>
    </header>
  );
};

export default Header;

 

Link랑 LinkNav의 차이점

 

import { RouterProvider } from 'react-router-dom';
import { createBrowserRouter } from 'react-router-dom';
import Default from './layout/Default';
import MainPage from './pages/MainPage';
import AboutPage from './pages/AboutPage';
import ShopPage from './pages/ShopPage';
import BlogPage from './pages/BlogPage';

// createBrowserRouter: 경로 설정할 수 있음
const router = createBrowserRouter([
  {
    element: <Default />,
    children: [
      {
        path: '/',
        element: <MainPage />,
      },
      {
        path: '/about',
        element: <AboutPage />,
      },
      {
        path: '/shop',
        element: <ShopPage />,
      },
      {
        path: '/blog',
        element: <BlogPage />,
      },
    ],
  },
  // 404에러 페이지
  { path: '*', element: <MainPage /> },
]);

// 함수를 하나 만들어서 내보내야된다.
export default function Router() {
  return <RouterProvider router={router} />;
}

NavLink는 Link와 비슷하다. 

현재 경로와 Link에서 사용하는 경로가 일치하는 경우 특정 스타일 혹은 CSS클래스를 적용할 수 있는 컴포넌트이다.

NavLink에서 링크가 활성화되었을 때의 스타일을 적용할 때는 activeStyle 값을, CSS 클래스를 적용할 때는 activeClassName 값을 props로 넣어 주면 된다!

 

리액트를 다루는 기술 [개정판]: 13.6.4 NavLink

 

리액트를 다루는 기술 [개정판]: 13.6.4 NavLink

더북(TheBook): (주)도서출판 길벗에서 제공하는 IT 도서 열람 서비스입니다.

thebook.io

⬇️⬇️⬇️ 

📁 404페이지도 만들어 준다.

import React from 'react';
import { Link } from 'react-router-dom';

const NotFound = () => {
  return (
    <div>
      NotFound
      <h2>페이지 없요</h2>
      <Link to={'/'}> 홈으로</Link>
    </div>
  );
};

export default NotFound;

⬇️⬇️⬇️ 

중첩된 라우터 구조

근데 또 하위 페이지를 설정할 수 있다.

import { RouterProvider } from 'react-router-dom';
import { createBrowserRouter } from 'react-router-dom';
import Default from './layout/Default';
import MainPage from './pages/MainPage';
import AboutPage from './pages/AboutPage';
import ShopPage from './pages/ShopPage';
import BlogPage from './pages/BlogPage';
import MainSub1Page from './pages/MainSub1Page';

// createBrowserRouter: 경로 설정할 수 있음
const router = createBrowserRouter([
  {
    element: <Default />,
    children: [
      {
        path: '/',
        element: <MainPage />,
        // children안에 무언가를 넣으려면 컴포너느가 필요하낟.
        children: [{ path: '', element: <MainSub1Page /> }],
      },
      {
        path: '/about',
        element: <AboutPage />,
      },
      {
        path: '/shop',
        element: <ShopPage />,
      },
      {
        path: '/blog',
        element: <BlogPage />,
      },
    ],
  },
  // 404에러 페이지
  { path: '*', element: <MainPage /> },
]);

// 함수를 하나 만들어서 내보내야된다.
export default function Router() {
  return <RouterProvider router={router} />;
}
import React from 'react';

const MainSub1Page = () => {
  return (
    <div>
      <div>MainSub1Page</div>
      <div>MainSub1Page</div>
      <div>MainSub1Page</div>
      <div>MainSub1Page</div>
      <div>MainSub1Page</div>
      <div>MainSub1Page</div>
    </div>
  );
};

export default MainSub1Page;
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
import Default from './layout/default';
import MainPage from './pages/MainPage';
import AboutPage from './pages/AboutPage';
import ShopPage from './pages/ShopPage';
import BlogPage from './pages/BlogPage';
import MainSub1Page from './pages/MainSub1Page';
import MainSub2Page from './pages/MainSub2Page';

const router = createBrowserRouter([
  {
    element: <Default />,
    children: [
      {
        path: '/',
        element: <MainPage />,
        children: [
          { path: '/sub1', element: <MainSub1Page /> },
          { path: '/sub2', element: <MainSub2Page /> },
        ],
      },
      { path: '/about', element: <AboutPage /> },
      { path: '/shop', element: <ShopPage /> },
      { path: '/blog', element: <BlogPage /> },
    ],
  },
  { path: '*', element: <MainPage /> },
]);

// 함수 만들어서 내보내야한다.
export default function Router() {
  return <RouterProvider router={router} />;
}

 

 

이렇게 화면에 있는 걸 누르면 화면 전환이 된다. 

728x90