매일 업데이트
2023-09-26 08:40 18 min

React-Router-dom 사용 가이드

React 애플리케이션을 여러 페이지로 나누는 것은 중요하며, 이를 위해 React 라우팅은 필수적인 요소입니다.

처음 접하는 사람에게는 다소 어려울 수 있지만, 이 가이드에서는 필요한 기본 사항들을 빠르게 습득할 수 있도록 돕고, 실제 라우팅을 사용하는 앱을 함께 만들어볼 것입니다.

React 라우팅 (클라이언트 측 라우팅)이란?

React 라우팅은 React 앱에서 클라이언트 측 라우팅을 구현하는 한 방법입니다. 클라이언트 측 라우팅은 서버 측 라우팅의 대안적인 접근 방식입니다. 서버 측 라우팅에서는 사용자가 다른 페이지로 이동할 때마다 브라우저가 웹 서버에 GET 요청을 보내지만, 클라이언트 측 라우팅은 이러한 요청을 JavaScript로 처리합니다.

만약 웹 애플리케이션이 페이지 간의 빈번한 이동을 필요로 한다면, 서버 측 라우팅의 대기 시간은 사용자 경험을 저해할 수 있습니다. 클라이언트 측 라우팅은 이러한 문제에 대한 효과적인 해결책입니다. 브라우저에 HTML을 직접 제공하는 대신, 앱은 JavaScript를 활용하여 다양한 페이지에 대한 HTML을 동적으로 생성합니다.

앱의 초기 실행을 위해서는 index.html 파일이 진입점 역할을 합니다. 이 진입점은 JavaScript 코드를 로드하고, JavaScript 번들은 DOM 조작, 라우팅 처리, 그리고 앱의 핵심 기능을 구현하여 페이지를 렌더링합니다.

서버는 index.html 파일 하나만 렌더링하기 때문에, 이러한 방식의 애플리케이션을 싱글 페이지 애플리케이션(SPA)이라고 부릅니다.

클라이언트 측 라우팅의 이점

  • 향상된 사용자 경험: 페이지 탐색 속도가 빨라지고 앱의 반응성이 높아져 사용자 경험이 개선됩니다. 서버 측 라우팅에서는 모든 탐색이 네트워크 요청을 필요로 하여 대기 시간이 발생하지만, 클라이언트 측 라우팅은 이러한 지연을 최소화합니다.
  • 오프라인 기능 지원: 앱을 실행하는 데 필요한 모든 코드를 로컬에 캐싱할 수 있어 오프라인 애플리케이션 개발이 가능합니다. 이를 통해 사용자는 인터넷 연결 없이도 앱의 기능을 일부 또는 전부 이용할 수 있습니다.
  • 데이터 사용량 감소: 모든 코드가 한 번 전송되고 일부 파일이 로컬에 저장되면 네트워크 요청 수가 현저히 줄어듭니다. 이는 데이터 사용량 감소로 이어져 사용자에게 유리합니다.
  • 서버 부하 감소: 서버는 애플리케이션을 단 한 번만 렌더링하면 되므로, 서버 측 렌더링 방식에 비해 서버 부하를 줄일 수 있습니다. 서버 측 렌더링은 매번 페이지 요청 시 서버가 애플리케이션을 새로 렌더링해야 합니다.

이제 React 라우팅을 실제로 구현하는 방법을 살펴보겠습니다.

React 라우팅 구현 방법

이 튜토리얼에서는 간단한 메모 작성 앱을 만들어보면서 라우팅을 구현해보겠습니다. 이 앱은 여러 페이지로 구성될 것이며, 사용자가 페이지를 이동할 수 있도록 React Router DOM 라이브러리를 사용할 것입니다. 앱의 모든 기능을 구현하지는 않고, 라우팅에 초점을 맞출 것입니다.

필수 조건

이 튜토리얼을 따라 하려면 HTML, JavaScript, 그리고 React에 대한 기본적인 이해가 필요합니다. 또한 Node.js와 NPM(Node Package Manager)이 설치되어 있어야 합니다. Node.js는 공식 웹사이트에서 다운로드 및 설치할 수 있습니다. 또는 관련 YouTube 비디오 가이드를 참고하세요.

만들 앱의 모습

이 앱은 여러 페이지로 구성되어 있으며, React 라우팅을 통해 사용자는 다른 페이지로 이동할 수 있습니다. 아래에서 각 페이지의 디자인을 확인할 수 있습니다.

메인 페이지는 '/' 경로에서 렌더링됩니다.

정보 페이지는 '/about' 경로에서 렌더링됩니다.

노트 페이지는 '/notes' 경로에서 볼 수 있습니다.

새 노트 페이지는 '/notes/new' 경로에 있습니다.

각 노트의 상세 내용은 노트 페이지에서 확인할 수 있으며, 이는 '/notes/<id>' 경로에서 렌더링됩니다. 여기서 <id>는 확인하고자 하는 노트의 ID를 나타냅니다.

시작하기

먼저, 새로운 React 프로젝트를 생성합니다. 여기서는 Vite를 사용하므로, 프로젝트 초기화를 위한 명령어는 다음과 같습니다.

npm create vite@latest scribbble --template react

프로젝트 이름은 'scribbble'로 설정하고, React 템플릿을 사용하도록 지정했습니다. 다음으로, VS Code를 열기 위해 아래의 명령어를 사용합니다.

cd scribbble
code .

VS Code가 열리면, 터미널 창에서 react-router-dom을 설치합니다. 이 패키지를 통해 애플리케이션에서 React 라우팅을 쉽게 구현할 수 있습니다.

npm install react-router-dom

이제 메모를 저장할 파일을 만들겠습니다. src/notes.js 파일을 생성하고 다음 코드를 추가합니다.

const notes = [
      {
        id: 1,
        title: "Note 1",
        body: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
      },
      {
        id: 2,
        title: "Note 2",
        body: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
      },
      {
        id: 3,
        title: "Note 3",
        body: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
      },
    ];
    
    export default notes;
    

다음으로, src/App.css 파일을 삭제합니다. 이 프로젝트에서는 사용하지 않을 것입니다. 또한, App.jsx 파일에서 App.css 파일 임포트 구문을 제거했는지 확인하세요.

이제 index.css 파일의 모든 내용을 다음으로 대체합니다.

:root{font-family:Inter,system-ui,Avenir,Helvetica,Arial,sans-serif;font-weight:400;color:#404040}*{margin:0;padding:0}.nav-container{display:flex;justify-content:space-between;padding:15px 30px}.home-buttons,.nav{display:flex;gap:10px}a{text-decoration:none;color:inherit;font-weight:600}h1{font-size:63px;margin:20px 0}input,textarea{border:1px solid #f1f1f1;background-color:#fafafa;outline:0;padding:10px;width:100%}textarea{resize:none;font-family:inherit}.container{padding:15px}.primary{background-color:#8a2be2;color:#fff}.secondary{background-color:#eee}.button{padding:15px 30px;font-size:16px;border:none;font-weight:700;border-radius:7px;cursor:pointer}.home-container{height:300px;display:flex;flex-direction:column;align-items:center;justify-content:center}.new-note-container{padding:20px}.new-note-form{display:flex;flex-direction:column;align-items:center;width:500px;gap:20px;margin:auto;border-radius:7px;padding:20px 30px}.notes-list{display:grid;grid-template-columns:1fr 1fr 1fr;gap:30px;padding:0 60px}.note{border:1px solid #d3d3d3;padding:15px;border-radius:7px}.note h2{font-size:1rem;margin-bottom:10px}.note p{color:#585858;font-size:.9rem;cursor:pointer}.note-container{display:flex;align-items:center;justify-content:center;padding:50px}.note-content{width:500px}

이제, 만들고자 하는 페이지들에 대한 파일을 생성합니다.

  • src/pages/Home.jsx
  • src/pages/About.jsx
  • src/pages/Note.jsx
  • src/pages/NewNote.jsx
  • src/pages/Notes.jsx

다음으로, 탐색 모음 컴포넌트 파일을 생성합니다. 이 파일은 src/components/NavBar.jsx에 위치합니다.

React 라우팅 설정

앱의 기본 설정이 완료되면, 애플리케이션에 라우팅을 구성합니다.

App.jsx 파일을 열고 내부의 모든 내용을 삭제합니다. 그런 다음, 파일 상단에 다음 import 문을 추가합니다.

import { BrowserRouter, Routes, Route } from "react-router-dom";
import { NavBar } from "./components/NavBar";
import { Home } from "./pages/Home";
import { About } from "./pages/About";
import { Notes } from "./pages/Notes";
import { Note } from "./pages/Note";
import { NewNote } from "./pages/NewNote";

react-router-dom에서 BrowserRouter, Routes, 그리고 Route 컴포넌트를 가져옵니다. 이 컴포넌트들은 라우터를 설정하는 데 사용됩니다. 또한, 컴포넌트 디렉토리에서 NavBar를 가져오고, 페이지 파일에서 여러 페이지를 가져옵니다. 아직 페이지를 구현하지 않았지만, 곧 진행할 예정입니다.

다음으로, App 컴포넌트를 설정합니다.

export default function App () {
    
    }

이제 return 문에 다음 마크업을 추가합니다.

return (
      <BrowserRouter>
        
      </BrowserRouter>
    )

BrowserRouterreact-router-dom 컴포넌트에서 제공하는 React 컴포넌트이며, 브라우저 내에서 라우팅을 관리합니다. 이 태그 안에 앱 전체 내용이 포함됩니다.

다음으로, 탐색 모음을 추가하고 Routes 컴포넌트를 생성합니다.

return (
      <BrowserRouter>
        <NavBar />
        <div className="container">
          <Routes>
            
          </Routes>
        </div>
      </BrowserRouter>
    );
    

BrowserRouter 요소 내부에 NavBar를 추가했습니다. 이 요소는 나중에 정의하겠지만, 각 페이지 상단에 링크를 생성합니다. 각 페이지마다 탐색 모음을 개별적으로 작성하는 대신, 하나의 NavBar를 만들어서 재사용할 것입니다.

그런 다음, container 요소를 만들었습니다. 이 요소는 라우팅에 필수적인 요소는 아니지만, 스타일링을 적용하기 위해 추가했습니다.

container 내부에 Routes 컴포넌트를 추가했습니다. 이 컴포넌트는 브라우저 경로에 따라 다른 페이지를 렌더링하는 역할을 합니다. Routes 컴포넌트 내부의 내용은 경로가 변경될 때마다 다시 렌더링됩니다.

마지막으로, 다양한 페이지에 대한 경로를 추가합니다.

return (
      <BrowserRouter>
        <NavBar />
        <div className="container">
          <Routes>
            <Route path="/" Component={Home} />
            <Route path="about" Component={About} />
            <Route path="notes" Component={Notes}>
              <Route path="new" Component={NewNote} />
              <Route path=":id" Component={Note} />
            </Route>
          </Routes>
        </div>
      </BrowserRouter>
    );
    

Home 컴포넌트는 경로가 '/'일 때 렌더링되고, About 컴포넌트는 '/about' 경로에서 렌더링됩니다. Notes 컴포넌트는 '/notes' 경로에서 렌더링되며, '/notes/new' 경로와 '/notes/:id' 경로는 중첩된 경로로 정의됩니다.

중첩된 경로 설명

경로는 내부 경로를 포함할 수 있습니다. 이것을 중첩된 경로라고 합니다. 이러한 중첩된 경로의 경로는 상위 경로에 결합되어 전체 경로를 형성합니다. 예를 들어, 'notes'와 'new' 경로는 '/notes/new'로 결합됩니다.

상위 경로로 이동할 때 해당 상위 컴포넌트만 렌더링됩니다. 하지만, 상위 컴포넌트와 중첩된 컴포넌트는 중첩 경로를 탐색할 때 함께 렌더링됩니다.

두 컴포넌트를 함께 렌더링하려면, Notes 컴포넌트는 Note 컴포넌트가 표시될 위치를 지정하는 Outlet 컴포넌트를 렌더링해야 합니다. 나중에 페이지 생성을 시작할 때 이 내용을 확인하게 될 것입니다.

동적 경로

지금까지는 일치시키고자 하는 문자 그대로의 경로를 지정해왔습니다. 예를 들어, '/' 와 'about' 경로입니다. 하지만, react-router-dom을 사용하면 동적 경로를 지정할 수 있습니다. 동적 경로는 쿼리 매개변수와 일치할 수 있는 부분을 포함합니다. 경로가 일치하면 쿼리 매개변수가 페이지에 전달됩니다.

예를 들어, 'posts' 상위 경로 내부에는 ':id'로 지정된 동적 부분을 포함하는 중첩된 경로가 있습니다. 이 경로는 ':id' 대신 어떠한 텍스트든 허용하며, 해당 텍스트는 Note 컴포넌트 내에서 id로 사용할 수 있습니다.

탐색 모음 만들기

react-router-dom을 사용하여 탐색을 구현하려면, 일반 앵커 태그 대신 Link 컴포넌트를 사용해야 합니다. 따라서 탐색 모음은 다음과 같아야 합니다.

import { Link } from "react-router-dom";

export function NavBar() {
  return (
    <div className="nav-container">
      <Link to="/">Scribbble</Link>
      <nav className="nav">
        <Link to="/about">About</Link>
        <Link to="/notes">Notes</Link>
        <Link to="/notes/new">New Note</Link>
      </nav>
    </div>
  );
}

위 코드를 src/components/NavBar.jsx에 추가합니다.

페이지 구현

다음으로 페이지들을 구현합니다. 메인 페이지의 경우, src/pages/Home.jsx에 다음 코드를 추가합니다.

import { useNavigate } from "react-router-dom";

export function Home() {
  const navigate = useNavigate();

  return (
    <div className="home-container">
      <h1>Notes for professionals</h1>
      <div className="home-buttons">
        <button
          onClick={() => {
            navigate("/notes/new");
          }}
          className="button primary"
        >
          Start Scribbling
        </button>
        <button
          onClick={() => {
            navigate("/notes");
          }}
          className="button secondary"
        >
          View Notes
        </button>
      </div>
    </div>
  );
}

HomePage에서는 버튼을 사용하여 탐색하고 싶습니다. 따라서 useNavigate 훅을 사용하여 프로그래밍 방식으로 탐색합니다. 이 훅을 가져온 다음 Home 컴포넌트 내에서 호출했습니다. 훅을 호출한 후 반환되는 값은 탐색하는 데 사용할 수 있는 함수입니다.

이제 정보 페이지를 정의합니다. src/pages/About.jsx 파일에 다음 코드를 추가합니다.

export function About() {
      return (
        <div>
          <h1>About</h1>
          <p>Simple Notes is the best note-taking application for professionals</p>
        </div>
      );
    }
    

다음으로, 노트 페이지를 정의합니다.

이 컴포넌트는 중첩된 경로를 렌더링하는 데 사용될 Outlet 컴포넌트도 포함해야 합니다. 이러한 이유로, src/pages/Notes.jsx 페이지는 다음과 같습니다.

import { Outlet, useNavigate } from "react-router-dom";
import notes from "../notes";

export function Notes() {
  const navigate = useNavigate();
  return (
    <div>
      <Outlet />
      <div className="notes-list">
        {notes.map((note) => {
          return (
            <div
              className="note"
              key={note.id}
              onClick={() => {
                navigate("/notes/" + note.id);
              }}
            >
              <h2>{note.title}</h2>
              <p>{note.body.slice(0, 100)}</p>
            </div>
          );
        })}
      </div>
    </div>
  );
}

이제, 노트 세부 페이지를 정의합니다.

이는 개별 노트에 대한 내용을 렌더링합니다. 어떤 노트를 렌더링할지 결정하기 위해, 노트 페이지는 경로의 동적 부분에서 지정된 ID에 접근합니다. 이를 위해 useParams 훅을 사용합니다. 따라서, src/pages/Note.jsx 파일 내부의 코드는 다음과 같아야 합니다.

import { useParams } from "react-router-dom";
import notes from "../notes";

export function Note() {
  const params = useParams();
  const note = notes.find((note) => note.id == params.id);
  return (
    <div className="note-container">
      <div className="note-content">
        <h2>{note.title}</h2>
        <p>{note.body}</p>
      </div>
    </div>
  );
}

마지막으로, 다음 코드를 사용하여 src/pages/NewNote.jsx 내부에 NewNote 컴포넌트를 생성합니다.

export function NewNote() {
  return (
    <div class="new-note-container">
      <form class="new-note-form">
        <h2>New Note</h2>
        <input type="text" name="title" placeholder="Note title" />
        <textarea rows="10" placeholder="Note text" />
        <button class="button primary">Save Note</button>
      </form>
    </div>
  );
}

이제 애플리케이션에 필요한 모든 코드가 작성되었습니다. npm run dev 명령어를 사용하여 앱을 실행할 수 있습니다. 다른 페이지로 이동하면서 클라이언트 측 라우팅이 얼마나 빠른지 확인해 보세요.

클라이언트 측 라우팅의 단점

많은 장점에도 불구하고, 클라이언트 측 라우팅에는 몇 가지 단점도 있습니다. 이러한 단점들은 다음과 같습니다.

  • 초기 로딩 속도 저하: 전체 앱을 로드해야 하므로 초기 페이지 로딩 속도가 느려질 수 있습니다. 특히 JavaScript 번들 크기가 큰 경우 로딩 시간이 길어질 수 있습니다.
  • SEO 최적화 어려움: JavaScript가 마크업을 생성하기 때문에 페이지가 SEO에 최적화되지 않을 수 있습니다. 검색 엔진 크롤러는 JavaScript를 제대로 해석하지 못할 수 있습니다.
  • JavaScript 의존성: 모든 기능이 JavaScript에 의존하기 때문에 JavaScript를 지원하지 않거나 비활성화된 브라우저에서는 애플리케이션을 사용할 수 없습니다.

결론

이 튜토리얼에서는 작은 프로젝트를 통해 React 라우팅을 살펴보았습니다. 이 글에서 모든 내용을 다루지는 않았지만, 대부분의 프로젝트에서 사용하게 될 핵심 개념을 다루었습니다. React Router DOM에 대한 더 자세한 내용은 공식 문서를 참고하세요.

다음으로, React 폼 라이브러리에 대한 기사를 읽어보세요.

저자
Korea

기술 트렌드와 실용적인 팁을 전하는 लेखक입니다.