Next.js, Svelte Kit 등의 프레임워크는 폴더 구조를 기반으로 하는 라우팅을 제공한다.
하지만, React는 라이브러리에서는 공식적으로 라우팅 기능을 지원하지 않아 별도의 라우팅 라이브러리를 사용해야한다.
그 중 가장 많이 사용되는 것이 바로 React Router 라이브러리이다.
그렇다면 라우팅이란 정확히 무엇이고, React Router 라이브러리는 어떻게 사용하는 것인지 알아보도록 하자.
이 글은 React Router v6를 기준으로 작성되었다.
1. 라우팅(Rounting)이란?
라우팅을 이해하기 위해서는 먼저 과거에는 웹이 어떻게 동작했었는지를 알아야 한다.
초창기의 웹은 MPA(Multi Page Application) 방식으로 동작했었다.
MPA 방식은 웹 애플리케이션이 여러 페이지로 이뤄져있어 변경사항이 있을 때마다 서버로 새 페이지를 요청하는 방식이다.
그래서 사용자가 어떤 작업을 수행하거나 페이지를 이동할 때마다 새로운 페이지를 서버에 요청하게 되며, 이때 전체 페이지가 다시 렌더링된다. MPA 방식은 이렇게 전체 페이지가 계속 새로 로딩되면서 새로고침이 발생하므로, 사용자 경험에 좋지 않은 영향을 줄 수 있다.
그리고 웹 기술이 발전하면서, 사용자와의 인터랙션은 더욱 활발해졌고, 이에 따라 MPA 방식만으로는 원활한 사용자 경험을 제공하기가 어려워졌다.
이러한 문제점을 해결하기 위해 나온 방식이 바로 SPA(Single Page Application) 방식이다.
SPA 방식은 단일 페이지로 이뤄져있어 변경사항이 있을 때마다 갱신될 부분에 대해서만 데이터를 요청해서 렌더링 하는 방식이다.
서버로부터 HTML은 한 번만 요청하고, 그 이후에는 한 페이지 안에서 변경된 부분의 데이터만 받아와 화면을 업데이트 해주는 것이다. 따라서 사용자가 페이지를 이동할 때, 새로고침이 발생하지 않는다.
SPA 방식에서는 MPA 방식과 달리, 페이지를 다시 로드하는 것 없이 동적으로 콘텐츠를 변경하기 때문에 라우팅 처리가 필요하다.
라우팅은 사용자가 접근한 URL에 따라 적절한 페이지나 컴포넌트를 동적으로 보여주는 역할을 한다. 그러므로 SPA 방식으로 웹 애플리케이션을 구현할 때는 라우팅을 이용하면 원활한 페이지 전환이 가능해진다.
React 라이브러리는 SPA 방식으로 동작하기 때문에 라우팅 처리를 해줘야 하는데, 대표적으로 React Router와 같은 라이브러리를 사용해 라우팅을 쉽게 구현할 수 있다.
2. 프로젝트에 React Router v6 적용하기
1) react-router-dom 라이브러리 설치하기
yarn add react-router-dom
2) App 컴포넌트 BrowserRouter로 감싸기
라우팅을 설정하기 위해서는 가장 먼저 React Router 라이브러리에 내장된 BrowserRouter 컴포넌트로 App 컴포넌트를 감싸줘야 한다.
//src/index.jsx
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import "./index.css";
import { BrowserRouter } from "react-router-dom";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<BrowserRouter>
<React.StrictMode>
<App />
</React.StrictMode>
</BrowserRouter>
);
📌 BrowserRouter란?
HTML5의 History API를 사용해 요청된 URL에 따라 브라우저의 주소창을 변경하고, UI를 업데이트 해주는 라우터이다.
즉, 사용자가 다른 페이지로 이동하더라도 새로고침이 일어나지 않으면서 주소를 변경하고, 해당 페이지에 필요한 데이터를 렌더링함으로써 UI를 업데이트 해주는 역할을 한다.
3) App 컴포넌트에 Route 설정하기
각 Route 컴포넌트에 경로와 해당 경로에서 보여주고 싶은 컴포넌트를 매칭시켜준다.
그리고 Routes 컴포넌트로 Route 컴포넌트들을 감싸준다.
//src/App.jsx
import { Routes, Route } from "react-router-dom";
import Home from "./Home";
import My from "./My";
function App() {
return (
<Routes>
<Route path="/" element={<Home />} />
<Route path="/my" element={<My />} />
</Routes>
);
}
export default App;
라우트 설정까지 마치고 나면 아래 사진처럼 요청된 URL에 따라 다른 컴포넌트를 보여주는 것을 확인할 수 있다.
📌 Routes란?
사용자가 웹 애플리케이션에서 특정 URL로 이동하면, Routes 컴포넌트는 그 URL과 일치하는 Route 컴포넌트를 찾아서 화면에 보여주는 역할을 한다.
Routes 컴포넌트 안에는 하나 이상의 Route 컴포넌트가 위치할 수 있다.
📌 Route란?
Route 컴포넌트는 사용자의 현재 위치(URL)에 따라 적절한 컴포넌트를 렌더링하는 역할을 한다.
path props에 지정한 경로에 접근한 경우, element props에 지정한 컴포넌트를 보여주는 역할을 한다.
📝 요약
사용자가 특정 URL로 이동하면, BrowserRouter는 History API를 통해 브라우저의 주소창을 갱신한다.
이후 Routes 컴포넌트는 자신의 하위에 있는 Route 컴포넌트들 중에서 주소창의 URL과 일치하는 path를 가진 컴포넌트를 찾는다.
일치하는 Route가 발견되면, 해당 컴포넌트는 element에 정의된 컴포넌트를 화면에 렌더링하게 된다.
4) Link 컴포넌트를 이용해 URL 이동하기
라우팅 설정은 모두 마쳤지만, 현재 상태에서는 URL을 직접 바꿔줘야만 원하는 페이지로 이동할 수 있다.
그렇다면, 어떻게 해야 사용자 인터페이스를 통해 페이지를 이동시킬 수 있을까?
전통적으로 페이지의 이동은 HTML의 a 태그를 통해 이루어진다.
하지만 a 태그는 페이지를 이동할 때 브라우저에서 전체 페이지를 다시 로드하는 방식으로 작동하기 때문에 SPA 방식에서 사용할 수 없는 방법이다.
React Router 라이브러리에서는 SPA의 특징을 고려하여, 페이지를 새로 로드하지 않으면서도 원하는 페이지로 이동시켜주는 컴포넌트가 제공되는데, 그게 바로 Link 컴포넌트다.
Link 컴포넌트를 사용하면 History API를 통해 브라우저의 주소만 변경되며, 페이지 전체를 새로 불러오는 것이 아니라 변경된 부분만 다시 렌더링된다.
좀 더 쉬운 이해를 위해 Link 컴포넌트를 이용한 NavBar 를 만들어보자.
먼저 NavBar 컴포넌트를 다음과 같이 생성한다.
//src/NavBar.jsx
import { Link } from "react-router-dom";
export default function NavBar() {
return (
<div
style={{
backgroundColor: "white",
padding: "25px",
textAlign: "start",
fontSize: "20px",
}}
>
<Link
to="/"
style={{ textDecoration: "none", marginRight: "25px", color: "black" }}
>
Home
</Link>
<Link to="/my" style={{ textDecoration: "none", color: "black" }}>
My
</Link>
</div>
);
}
그리고 App.jsx 파일에서 NavBar 컴포넌트를 렌더링 해준다.
//src/App.jsx
import { Routes, Route } from "react-router-dom";
import Home from "./Home";
import My from "./My";
import NavBar from "./NavBar";
function App() {
return (
<>
<NavBar />
<Routes>
<Route path="/" element={<Home />} />
<Route path="/my" element={<My />} />
</Routes>
</>
);
}
export default App;
아래는 NavBar를 적용한 모습이다.
이제 Home을 클릭하면 “/”경로로, My를 클릭하면 “/my” 경로로 Link 컴포넌트를 이용해 잘 이동되는 것을 확인할 수 있다.
이렇게 Link 컴포넌트는 라우팅 세팅이 모두 끝난 후, 특정 페이지로 이동할 수 있도록 하는 NavBar와 같은 컴포넌트를 만들 때 주로 사용된다.
🚨 실습중 발생했던 Error
//src/App.jsx
// ...
<Routes>
<NavBar />
<Route path="/" element={<Home />} />
<Route path="/my" element={<My />} />
</Routes>
// ...
App.jsx 파일에서 Route 컴포넌트가 아닌 컴포넌트를 렌더링할 때 만약 위 코드처럼 Routes 컴포넌트 안에 렌더링하게 되면 아래와 같은 런타임 에러가 발생한다.
Uncaught runtime errors:
ERROR
[NavBar] is not a <Route> component. All component children of <Routes> must be a <Route> or <React.Fragment>
at invariant (<http://localhost:3000/static/js/bundle.js:976:11>)
at <http://localhost:3000/static/js/bundle.js:40463:108>
at <http://localhost:3000/static/js/bundle.js:42724:21>
at <http://localhost:3000/static/js/bundle.js:42687:21>
at mapIntoArray (<http://localhost:3000/static/js/bundle.js:42590:27>)
at mapIntoArray (<http://localhost:3000/static/js/bundle.js:42635:27>)
at mapChildren (<http://localhost:3000/static/js/bundle.js:42686:7>)
at Object.forEachChildren [as forEach] (<http://localhost:3000/static/js/bundle.js:42723:7>)
at createRoutesFromChildren (<http://localhost:3000/static/js/bundle.js:40451:47>)
at Routes (<http://localhost:3000/static/js/bundle.js:40310:20>)
해당 에러는 Routes의 바로 아래 자식으로는 오직 Route 컴포넌트 또는 React.Framgment 컴포넌트만 사용될 수 있다는 내용이다.
따라서 반드시 Route가 아닌 컴포넌트를 렌더링 할 때는 Routes 컴포넌트 밖에 하도록 하자.