반응형
라우팅(routing)이란?
- 라우팅(routing)이란 사용자가 요청한 리소스를 얻기 위해 웹 서버 내에서 URL에 명시된 리소스를 찾아가는 과정을 가리킨다. 일반적으로 사용자가 요청한 URL을 해석하고 새로운 페이지로 전환하기 위한 데이터를 얻기 위해 웹 서버에 필요한 데이터를 요청하여 전달받고 화면을 전환하는 작업을 통틀어 의미한다.
- 라우팅은 웹 서버 측에서 진행되지만 브라우저 객체 모델(BOM)를 이용하면 실제로는 웹 브라우저에서 동작하는 라우팅 작업을 사용자에게는 서버 측에서 동작하는 것 같이 보여줄 수 있다. 이렇게 웹 브라우저, 즉 클라이언트 측에서 발생하는 라우팅을 클라이언트 사이드 라우팅(Client-Side-Routing, CSR)이라고 한다. 이러한 라우팅 시스템을 활용하면 SPA 방식의 웹 앱을 손쉽게 만들 수 있다.
- SPA 방식의 웹 앱은 사용자가 웹 브라우저를 통해 가장 처음 서버에 접속할 때 단 한 개의 페이지만 내려받는다. 이후 해당 페이지에 포함된 자바스크립트 코드를 통해 사용자에게 마치 여러 페이지가 존재하는 것처럼 알맞은 페이지를 보여주는 것이다.
- 이때 라우팅 시스템은 프로젝트에 포함된 수많은 컴포넌트 중에서 특정 컴포넌트를 찾아 화면에 보여주는 역할을 하게 되며, 사용자가 새로운 페이지를 요청하면 BOM의 Location 객체와 History 객체를 사용하여 웹 브라우저 주소창의 URL 값만 업데이트한 후 라우팅 설정에 따라 다른 컴포넌트를 불러와 새로운 UI를 렌더링한다.
- CSR(Client-Side-Routing)은 실제 웹 서버에 페이지를 요청하는 것은 아니기 때문에 컴포넌트가 바뀌어도 화면의 리렌더링이 발생하지 않기 때문에 애니메이션과 같은 더욱 역동적인 화면을 더욱 빠르게 제공할 수 있게 된다.
React Router
- React Router는 React의 라우팅 기능을 제공하는 라이브러리 중에서 가장 많이 사용되고 있는 라이브러리 중 하나로, 컴포넌트 기반으로 라우팅 시스템을 설정할 수 있다.
- React Router는 Context API를 기반으로 구현되어 있기에 라우팅 기능을 사용하려면 최상위 컴포넌트로 BrowserRouter 컴포넌트를 사용해야만 한다. 또한, 하나의 애플리케이션에는 단 하나의 Router만을 사용해야 한다.
- 이러한 라우팅 관련 라이브러리나 프레임워크는 React에서 공식적으로 지원하는 것이 아니며, React Router 대신 Next.js, react-location 등을 사용할 수도 있다.
React Router 시작하기
- React에서 React Router를 사용하기 위해서는 우선 react-router-dom 라이브러리를 설치해야 한다.
# npm인 경우
npm install react-router-dom
# yarn인 경우
yarn add react-router-dom
- 그리고 react-router-dom 라이브러리로부터 최상위 컴포넌트로 사용하게 될 BrowserRouter 컴포넌트를 불러와 다른 컴포넌트들을 감싸주면 React Router가 적용된다.
- BrowserRouter 컴포넌트는 페이지를 리로딩 하지 않고도 History 객체를 사용하여 주소의 경로 관련 정보를 컴포넌트가 사용할 수 있도록 해 준다.
// index.js
import React, { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { BrowserRouter } from "react-router-dom";
import "./styles.css";
import App from "./App";
const root = createRoot(document.getElementById("root"));
root.render(
<StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</StrictMode>
);
라우팅 설정하기
Route 컴포넌트로 경로 설정하기
- React 프로젝트의 src/index.js 파일 내에서 BrowserRouter 컴포넌트를 사용하여 다른 컴포넌트를 감싸주면 React Router가 적용된다.
- 가장 먼저 각 페이지로 사용하게 될 컴포넌트를 만들어 보자. 우선 React 프로젝트 src 폴더에 components라는 폴더를 생성한다. components 폴더에 사용자에게 가장 먼저 보여질 홈 화면인 Home 컴포넌트와 사이트 소개 페이지인 About 컴포넌트를 각각 생성하여 아래와 같이 코드를 작성한다.
// Home.js
const Home = () => {
return (
<div>
<h2>Hello, React!</h2>
<p>사이트에서 가장 먼저 보여지는 페이지입니다.</p>
</div>
);
};
export default Home;
// About.js
const About = () => {
return (
<div>
<h2>React 소개</h2>
<p>사이트를 소개하는 페이지입니다.</p>
</div>
);
};
export default About;
- 이렇게 생성한 컴포넌트를 사용자의 웹 브라우저 URL 경로에 따라 렌더링되도록 하기 위해서는 Route 컴포넌트를 사용하여 라우팅을 설정해 주어야 한다.
- Route 컴포넌트는 React Router에서 가장 중요한 역할을 하는 컴포넌트다. Route 컴포넌트는 URL 세그먼트(segment)를 데이터 로딩을 위한 컴포넌트와 연결하는 역할을 수행한다. 이렇게 설정된 Route 컴포넌트들은 마지막으로 Routes 컴포넌트를 사용하여 감싸줘야 한다.
// Route 컴포넌트 문법
<Route path="URL세그먼트" element={<컴포넌트/>} />
// App.js
import { Routes, Route } from "react-router-dom";
import Home from "./components/Home";
import About from "./components/About";
const App = () => {
return (
<div>
<h1>React Router</h1>
<Routes>
<Route path="" element={<Home />} />
<Route path="about" element={<About />} />
</Routes>
</div>
);
};
export default App;
- 위의 예제가 동작하고 있는 웹 브라우저의 주소창에서 초기 URL 끝에 ‘/about’를 추가한 후 엔터를 누르게 되면, 소개 페이지인 About 컴포넌트가 랜더링 되는 것을 확인할 수 있다.
Link 컴포넌트로 링크 설정하기
- 앞서 살펴본 예제에서는 소개 페이지로 이동하기 위해 웹 브라우저 주소창의 URL을 직접 수정해야 했다. 하지만 실제 웹 사이트에서는 다른 페이지로 이동할 때 링크를 사용하는 것이 일반적이다.
- HTML에서는 보통 <a>태그를 사용하여 링크를 설정한다. 하지만 React Router에서는 <a>태그를 곧바로 사용하지 않고 Link 컴포넌트를 사용하여 다른 페이지로 이동할 수 있는 링크를 설정한다. <a>태그를 직접 사용하게 되면 사용자가 해당 요소를 클릭할 때 브라우저가 페이지를 새로 불러오기 때문에 React Router에서는 Link 컴포넌트를 사용해야 한다.
- Link 컴포넌트는 내부적으로 페이지의 리로딩을 막은 상태에서 링크할 리소스를 가리키는 href 속성을 가진 <a>엘리먼트를 렌더링한다.
// Link 컴포넌트 문법
<Link to="상대경로">링크 텍스트</Link>
- 다음 예제는 앞선 예제에 Link 컴포넌트를 포함하고 있는 Menu 컴포넌트를 추가하여 사용자가 손쉽게 다른 페이지로 이동할 수 있는 메뉴 기능을 추가한 예제다.
// Menu.js
import { Link } from "react-router-dom";
const Menu = () => {
return (
<div>
<nav>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
<li>
<Link to="/news">News</Link>
</li>
</ul>
</nav>
<hr />
</div>
);
};
export default Menu;
// App.js
import { Routes, Route } from "react-router-dom";
import Home from "./components/Home";
import About from "./components/About";
import News from "./components/News";
import Menu from "./Menu";
const App = () => {
return (
<div>
<h1>React Router</h1>
<Routes>
<Route path="/" element={<Menu />} />
<Route path="" element={<Home />} />
<Route path="about" element={<About />} />
<Route path="news" element={<News />} />
</Routes>
</div>
);
};
export default App;
중첩 라우팅
Outlet 컴포넌트로 중첩된 Route 설정하기
- 앞선 예제에서 메뉴에 포함된 링크를 클릭하게 되면 Menu 컴포넌트는 사라지고 링크된 페이지만이 화면에 렌더링된다. 그런데 화면에서 메뉴가 사라지게 되면 사용자는 이전 페이지나 다른 페이지로 이동할 수 없어지기 때문에 일반적으로 메뉴는 화면에 계속 표시되어 있어야 한다. 따라서 App 컴포넌트의 코드를 아래와 같이 수정한다.
// 중첩된 Outlet 컴포넌트
<Routes>
<Route path="/" element={<Menu />}>
<Route path="" element={<Home />} />
<Route path="about" element={<About />} />
<Route path="news" element={<News />} />
</Route>
</Routes>
- 위의 코드처럼 Route 컴포넌트들을 서로 중첩시킴으로써 상위의 Route 컴포넌트는 항상 화면에 렌더링될 수 있도록 설정할 수 있다. 하지만 이렇게 코드를 변경하면 하위의 Route 컴포넌트들이 화면에 렌더링되지 않는다.
- 이때 중첩된 하위 레벨의 Route들을 함께 렌더링하기 위해서는 부모 레벨의 Route 내부에 Outlet 컴포넌트를 추가해 주면 된다.
- Outlet 컴포넌트는 중첩된 Route 컴포넌트 중에서 하위 레벨의 Route 컴포넌트가 렌더링될 때 중첩된 UI를 표시할 수 있게 해 준다. 상위 레벨의 Route가 정확히 일치하면 하위 레벨의 Index Route를 렌더링하거나, Index Route가 존재하지 않으면 아무것도 렌더링하지 않는다.
// Menu.js
import { Link, Outlet } from "react-router-dom";
const Menu = () => {
return (
<div>
<nav>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
<li>
<Link to="/news">News</Link>
</li>
</ul>
</nav>
<hr />
<Outlet />
</div>
);
};
export default Menu;
- React Router v6 부터 지원되는 Index Route는 Route 컴포넌트에 index prop을 전달함으로써 자식 Route 컴포넌트 중에서 기본적으로 선택될 Route를 설정할 수 있다. Index Route는 부모 Route에 여러 자식 Route가 있는 경우 부모 경로에 ‘/’를 추가한 주소와 연결된다.
// Index Route 문법
<Route index element={<Home />} />
존재하지 않는 경로 처리하기
- 이번에는 사용자가 존재하지 않는 경로에 접근했을 경우에 대해 알아보도자.
- 위의 예제가 실행 중인 웹 브라우저의 주소창에서 URL 주소 마지막에 ‘/board’라는 경로를 추가해 봅시다. 현재 프로젝트에는 board라는 경로가 존재하지 않기 때문에 콘솔에는 경고 메시지가 출력된다. 하지만 애플리케이션이 종료되거나 별도의 동작을 수행하지는 않는다.
- 이것은 사용자 측면에서는 매우 불친절한 방식이며, 사용자에게 존재하지 않는 페이지에 접근하려 했다는 확실한 메시지를 전달해 주는 것이 좋다.
- Route 컴포넌트의 path 속성을 아래와 같이 설정하면, 경로가 존재하지 않는 경우에 연결할 컴포넌트를 설정할 수 있다.
// 존재하지 않는 경로와 연결된 Route 컴포넌트
<Route path="*" element={<NoMatch />} />
// NoMatch.js
import { Link } from "react-router-dom";
const NoMatch = () => {
return (
<div>
<h2>잘못된 경로로 접근하셨습니다.</h2>
<p>
<Link to="/">Home으로 이동하기</Link>
</p>
</div>
);
};
export default NoMatch;
// App.js
<Routes>
<Route path="/" element={<Menu />}>
<Route path="" element={<Home />} />
<Route path="about" element={<About />} />
<Route path="news" element={<News />} />
<Route path="*" element={<NoMatch />} />
</Route>
</Routes>
추가 기능 살펴보기
React Router 추가 기능
- React Router에는 라우팅과 관련된 작업에 유용하게 활용할 수 있는 다양한 컴포넌트와 Hook, 유틸리티 등이 포함되어 있다.
NavLink 컴포넌트로 스타일 적용하기
- NavLink 컴포넌트는 활성(active) 또는 보류(pending)인지 여부를 확인할 수 있는 특수한 종류의 Link 컴포넌트다.
- 이 컴포넌트를 사용하면 링크에서 사용하는 경로가 현재 Route의 경로와 일치하는지 여부를 비교하여, 일치하는 경우 특정 스타일이나 CSS 클래스를 적용하도록 설정할 수 있다. 이를 활용하여 현재 페이지의 계층 구조를 나타내는 탐색 보조 도구인 브레드크럼(breadcrumb)이나 탭(tab) 집합과 같은 탐색 메뉴를 손쉽게 만들 수 있다.
- 앞서 살펴 본 예제의 Link 컴포넌트를 모두 NavLink 컴포넌트로 변경해 보자. NavLink 컴포넌트를 사용하는 첫 번째 방법으로는 전통적인 CSS 클래스를 활용하여 다음과 같이 스타일을 지정할 수 있다.
- NavLink 컴포넌트는 내부적으로는 <nav>요소로 감싸진 <a>요소로 렌더링되며, 현재 Route 경로와 일치할 경우 active 클래스가 자동 추가되므로, 이를 활용하여 스타일을 지정할 수 있다.
// styles.css
nav a.active {
text-decoration: none;
font-weight: bold;
}
- 두 번째 방법으로는 style prop에 isActive 형태와 같은 불리언 값을 인수로 전달 받는 함수를 전달하여 스타일을 지정할 수 있다.
// Menu.js
import { NavLink, Outlet } from "react-router-dom";
const Menu = () => {
const style = ({ isActive }) => {
return {
color: isActive ? "red" : "blue",
fontWeight: isActive ? "bold" : "normal"
};
};
return (
<div>
<nav>
<ul>
<li>
<NavLink style={style} to="/">
Home
</NavLink>
</li>
<li>
<NavLink style={style} to="/about">
About
</NavLink>
</li>
<li>
<NavLink style={style} to="/news">
News
</NavLink>
</li>
</ul>
</nav>
<hr />
<Outlet />
</div>
);
};
export default Menu;
Navigate 컴포넌트로 Redirect 설정하기
- React 애플리케이션의 특정 컴포넌트에는 인증되지 않은 사용자의 접근을 막아야 할 경우가 있다. 예를 들어, MyAccount 컴포넌트는 사용자의 계정 정보를 보여주는 페이지로 사용자의 로그인이 필요하기 때문에 로그인하지 않은 사용자에게는 해당 페이지 대신 로그인 페이지를 대신 보여줘야 한다.
- Navigate 컴포넌트는 렌더링하는 순간 해당 컴포넌트 대신 다른 컴포넌트를 대신 렌더링하도록(redirect) 설정할 때 유용하게 사용할 수 있다.
- 다음 예제는 사용자의 로그인 여부를 체크하여 로그인되지 않은 사용자는 계정 페이지가 아닌 로그인 페이지를 대신 보여주는 예제다. MyAccount 컴포넌트에서 isLoggedIn 변수의 값을 true와 false로 번갈아 변경하면서 그 결과를 살펴보면 된다.
// Login.js
const Login = () => {
return (
<div>
<h3>로그인 해주세요.</h3>
<div>
<input type="email" name="" id="" placeholder="Email" />
</div>
<div>
<input type="password" name="" id="" placeholder="Password" />
</div>
</div>
);
};
export default Login;
// MyAccount.js
import { Navigate } from "react-router-dom";
const MyAccount = () => {
let isLoggedIn = false;
if (!isLoggedIn) {
return <Navigate to="/login" replace={true} />;
}
return <div>나의 계정 페이지에 오신 것을 환영합니다!</div>;
};
export default MyAccount;
// App.js
<Route path="myaccount" element={<MyAccount />} />
추가 Hook 살펴보기
useParams Hook으로 URL 파라미터 구현하기
- useParams는 현재 URL에서 Route 컴포넌트의 path prop의 값과 일치하는 키(key)와 값(value)의 쌍으로 이루어진 객체를 반환하며, 이를 활용하여 주소의 경로를 정의할 때 동적인 값을 사용할 수 있다. 이때 하위 경로는 상위 경로에서 모든 매개변수를 상속 받으며, 주로 ID나 이름을 사용하여 특정 데이터를 연결할 때 사용한다.
- 다음 예제는 params를 통해 숫자로 된 newsId를 넘겨받아 해당 숫자를 페이지 주소로 사용하는 예제다. 우선 NewsData 컴포넌트를 생성하고 다음과 같이 코드를 작성한다.
// NewsData.js
import { useParams } from "react-router-dom";
const data = {
1: {
publisher: "△△일보",
title: "React! 풀스택을 꿈꾸며!"
},
2: {
publisher: "☆☆뉴스",
title: "바닐라 Javascript의 반란!"
}
};
const NewsData = () => {
const params = useParams();
const newsData = data[params.newsId];
return (
<div>
<h2>React News</h2>
{newsData ? (
<div>
<h2>{newsData.publisher}</h2>
<p>{newsData.title}</p>
</div>
) : (
<p>존재하지 않는 뉴스입니다.</p>
)}
</div>
);
};
export default NewsData;
// News.js
import { Link } from "react-router-dom";
const News = () => {
return (
<div>
<h2>React News</h2>
<p>React에 대한 News를 모아놓은 페이지입니다.</p>
<ol>
<li>
<Link to="1">△△일보</Link>
</li>
<li>
<Link to="2">☆☆뉴스</Link>
</li>
</ol>
</div>
);
};
export default News;
- params의 이름은 Route 컴포넌트의 path prop을 통해 설정할 수 있으며, 이때 콜론(:)을 사용하여 경로를 표시한다.
// App.js
import NewsData from "./components/NewsData";
const App = () => {
return (
<div>
<h1>React Router</h1>
<Routes>
<Route path="/" element={<Menu />}>
<Route index element={<Home />} />
<Route path="about" element={<About />} />
<Route path="news" element={<News />} />
<Route path="news/:newsId" element={<NewsData />} />
<Route path="*" element={<NoMatch />} />
</Route>
</Routes>
</div>
);
};
// ...
- 위의 예제에서 News 메뉴를 클릭하고, 페이지에 표시된 뉴스를 선택하자. 웹 브라우저 주소창에서 해당 페이지의 URL 주소를 확인해 보면, 가장 마지막에 우리가 설정한 숫자가 URL 파라미터로 사용되고 있음을 확인할 수 있다.
useSearchParams Hook으로 쿼리스트링 구현하기
- 쿼리스트링(query string)이란 URL 주소 뒤에 사용자의 입력 데이터를 함께 전달하는 가장 단순한 데이터 전달 방법 중 하나다.
- 쿼리스트링은 URL 주소 뒤에 물음표(?)를 시작으로 key=value의 형태로 파라미터를 전달하게 된다. 여러 개의 파라미터를 동시에 전달할 때는 & 문자로 연결하여 한 번에 전달할 수 있다.
- 이를 활용하면 검색 키워드를 전달하거나 정렬 방식을 변경하는 등 데이터를 조회할 때 필요한 옵션을 손쉽게 전달할 수 있다.
- React에서는 useSearchParams Hook을 사용하여 이러한 쿼리스트링을 손쉽게 파싱하여 사용할 수 있다. useSearchParams는 useState와 같이 현재 경로에 대한 검색 파라미터와 이를 업데이트할 수 있는 함수를 배열 형태로 동시에 반환한다.
// 쿼리스트링 문법
URL?key=value[&key=value];
- 다음 예제는 사용자로부터 검색할 뉴스의 ID를 입력받아 해당 데이터를 쿼리스트링으로 생성해 보는 예제다.
// Search.js
import { createSearchParams, useSearchParams } from "react-router-dom";
const Search = () => {
const [searchParams, setSearchParams] = useSearchParams();
return (
<input
value={searchParams.newsId}
onChange={(e) => {
setSearchParams(createSearchParams({ newsId: e.target.value }));
}}
placeholder="검색할 뉴스 ID를 입력하세요."
/>
);
};
export default Search;
// App.js
import { Link, Outlet } from "react-router-dom";
import Search from "./Search";
const Menu = () => {
return (
<div>
<nav>
// ...
</nav>
뉴스 검색 : <Search />
<hr />
<Outlet />
</div>
);
};
export default Menu;
Reference
반응형
'Development > ReactJs' 카테고리의 다른 글
[React] 리액트 Redux - 개요, 사용 및 활용법 (1) | 2025.03.09 |
---|---|
[React] 리액트 Hooks - State, Context, Ref, Effect, Performance, Custom (1) | 2025.03.08 |
[React] 리액트 렌더링 - 조건부 렌더링, 배열 렌더링 (1) | 2025.03.08 |
[React] 리액트 이벤트 핸들링 - 이벤트 핸들링 및 전파 (0) | 2025.03.07 |
[React] 리액트 컴포넌트 스타일링 - Css, Sass, Css Module, Styled-components (0) | 2025.03.07 |