라우팅을 쓰는 이유
리액트로 일기 어플리케이션을 만들면 [메인] [일기 쓰기] [일기 보기] [수정하기] 와 같이 여러 페이지가 필요할 것이다. MPA인 바닐라 JS라면 여러 개의 html 파일을 만들어 이동을 시킬 수 있겠지만, 리액트는 하나의 html 파일만을 유저에게 보여주는 SPA다. 즉 보여줄 페이지가 1개 밖에 없는데 어떻게 각각의 기능을 하는 여러 페이지를 어떻게 보여줄까?
바로 라우팅 시스템을 사용하여 구현한다. 라우팅 시스템을 이용하면 여러 페이지로 구성된 웹 어플리케이션을 만들 때, 하나의 파일이지만 페이지별로 컴포넌트를 분리해 보여줄 수가 있다.
이러한 기능을 하는 게 2가지가 있는데, 바로 지금 글을 쓰는 리액트 라우터와 다음에 배울 Nextj.js라는 리액트의 프레임워크다. 리액트 라우터는 리액트의 라우팅 관련 라이브러리 중 가장 대중적인 라이브러리다.
리액트 라우터 적용 및 기본 사용 방법
npm i react-router-dom@6
먼저 npm create-react-app 으로 리액트 프로젝트를 새로 생성한 후 리액트 라우터 라이브러리를 설치한다. 6은 버전을 뜻한다.
프로젝트에 리액트 라우터를 적용할 때는 src/index.js 파일에서 react-router-dom에 내장된 BrowserRouter라는 컴포넌트를 사용해 래핑해준다. Hash Router라고 다른 것도 있는데, 얘는 주소에 #(해쉬)가 붙고, 검색 엔진이 읽지 못한다는 특징이 있다. SEO가 지원되지 않는 점이 치명적이라 일반적으로 웹 개발을 할 때에는 BrowserRouter를 사용한다.
BrowserRouter는 HTML5의 History API를 사용한다. 간단하게 브라우저의 히스토리를 관리하고 조작하는 API라 뒤로 가기나 앞으로 가기 같은 버튼으로 페이지에서 다른 페이지로 이동할 수 있다.
BrowserRouter를 사용하려면 해당 컴포넌트를 최상위 태그에 감싸줘야 한다.
import "./App.css";
import { BrowserRouter, Route, Routes } from "react-router-dom";
import Home from "./pages/Home";
import Edit from "./pages/Edit";
import Diary from "./pages/Diary";
import New from "./pages/New";
function App() {
return (
<BrowserRouter>
<div className="App">
<h2>App.js</h2>
</div>
</BrowserRouter>
);
}
export default App;
이렇게 일단 만들어 놨으면, 각 페이지에서 사용할 컴포넌트를 만들어야 한다.
브라우저의 주소 경로에 따라 우리가 원하는 컴포넌트만 보여주려면, Route 컴포넌트를 써야 한다.
그런데 이 Route 컴포넌트도 Routes 컴포넌트 내부에서 사용되어야 한다.
즉 구조는 (BrowserRouter - App - Routes - 보여줄 자식 컴포넌트 페이지 Route)처럼 된다.
먼저 Route는 아래처럼 쓴다.
<Route path="주소의 규칙" element={<보여줄 컴포넌트 이름 />} />
예를 들어 현재 주소에서 /home이 붙으면 Home 컴포넌트를 보여주려 한다.
그럼 <Route path="/home" element={<Home />} /> 으로 적어주면 첫 화면에서 Home 컴포넌트가 노출된다.
Link 컴포넌트로 다른 페이지로 이동하는 링크 만들기
원래 JS에서는 다른 페이지로 이동하는 링크를 <a> 태그로 만들었다.
리액트에서도 가능하긴 한데, 이러면 페이지 전체가 다시 로드되고(새로고침 발생) 리액트의 가상 DOM 상태가 초기화 된다. 리액트의 장점은 가상 DOM을 이용한 부드러운 화면 전환인데 이 부분에서 장점을 잃게 된다.
이럴 때 사용하는 게 Link 컴포넌트다.
Link 컴포넌트도 a 태그를 쓴다고 하는데, 다시 로드하지 않고 BrowserRouter에서 쓰는 History API로 브라우저의 주소 경로만 바꾸는 기능이 내장되어 있다고 한다. 그러니까 Link 컴포넌트를 사용하면 a 태그의 단점인 새로고침 대신 브라우저의 히스토리를 관리해 주소 경로를 바꿔준다는 것이다.
아래와 같이 사용한다.
<Link to="경로">링크 이름</Link>
import "./App.css";
import { BrowserRouter, Route, Routes } from "react-router-dom";
import Home from "./pages/Home";
import Edit from "./pages/Edit";
import Diary from "./pages/Diary";
import New from "./pages/New";
function App() {
return (
<BrowserRouter>
<div className="App">
<h2>App.js</h2>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/new" element={<New />} />
<Route path="/edit" element={<Edit />} />
<Route path="/diary/:id" element={<Diary />} />
</Routes>
<RouteTest />
</div>
</BrowserRouter>
);
}
export default App;
import React from "react";
import { Link } from "react-router-dom";
const RouteTest = () => {
return (
<div>
<Link to={"/"}>HOME</Link>
<br />
<Link to={"/diary"}>DIARY</Link>
<br />
<Link to={"/edit"}>EDIT</Link>
<br />
<Link to={"/new"}>NEW</Link>
</div>
);
};
export default RouteTest;
RouteTest 컴포넌트 안에는 4가지 경로와 경로 이름이 있다.
HOME, DIARY 같이 링크가 생성되어 있고, 그걸 누르면 그 컴포넌트가 그려지면서 주소 창에 path에 적은 경로가 적용된다.
리액트 라우터의 유용한 기능
1) useParams로 경로에 변수 설정하기
이번에 만들 일기 앱에서 일기의 상세 페이지를 누를 때 몇 번 일기인지 주소에 표기하고 싶다.
id를 변수라고 할 때 이걸 경로에 어떻게 나타낼 때 useParams를 쓴다.
즉 현재 URL에서 파라미터를 추출하는데 쓸 건데 아래와 같이 쓴다.
<Route path="/diary/:id" element={<Diary />} />
import React from "react";
import { useParams } from "react-router-dom";
const Diary = () => {
// React에서 제공하는 Hooks는 아님
// 하지만 별도 라이브러리가 자신의 라이브러리 기능을 편하게 하려고 만든 게 사용자 정의 훅(커스텀 훅)
const { id } = useParams();
console.log(id);
return (
<div className="Diary">
<h2>Diary</h2>
<p>이 곳은 일기 상세 페이지입니다.</p>
</div>
);
};
export default Diary;
주소에 http://localhost:3000/diary/1 을 입력하면 콘솔에도 1이 찍힌다.
URL 파라미터는 이같이 경로에 : 형태로 사용하고, 여러 개면 :/:/ 이런 식으로 연결해 쓴다.
이게 작동되는 원리는, useParams()를 쓰면 파라미터가 포함된 객체가 반환된다.
console.log(useParams())를 입력하면 {id: '1'}이 나온다.
이 객체는 보다시피 키값 쌍으로 이뤄져 있고, 키가 라우트 경로에 정의된 파라미터 이름이고 값은 실제 URL에서 전달된 값이다. 위 경우 라우트 경로가 /diary/:id 인데, { id }로 가져온 값이 1이라서 /diary/1이 잘 나타나는 것이다.
2) 쿼리 스트링
쿼리는 웹 페이지에 데이터를 전달하는 가장 간단한 방법이다.
키=값으로 엮여서 전달된다.
import React from "react";
import { useNavigate, useSearchParams } from "react-router-dom";
const Edit = () => {
// 페이지를 이동시키는 함수를 반환함 (navigate는 함수)
const navigate = useNavigate();
// setSearchParams는 useState처럼 params를 바꿔줌
const [searchParams, setSearchParams] = useSearchParams();
const id = searchParams.get("id");
console.log("id: ", id);
const mode = searchParams.get("mode");
console.log("mode: ", mode);
return (
<div className="Edit">
<h2>Edit</h2>
<p>이 곳은 수정하는 곳입니다.</p>
</div>
);
};
export default Edit;
id는 주소에 입력한 id다.
get 메서드는 주어진 검색 파라미터의 첫 번째 값을 전달한다.
http://localhost:3000/edit?id=10&mode=dark
이 때 검색 파라미터의 첫 번째 값이 id 기준 10, mode 기준 dark다.
그래서 콘솔에 찍어 보면 id: 10 mode: dark 가 나오는 것이다.
3) useNavigator
페이지를 이동 시키는 함수를 반환하는 useNavigator
괄호 안에 경로를 입력하면 특정 이벤트를 할 때 그 경로로 보낼 수 있다.
또는 +1, -1로 앞으로 가기와 뒤로 가기를 구현할 수도 있다.
import React from "react";
import { useNavigate, useSearchParams } from "react-router-dom";
const Edit = () => {
// 페이지를 이동시키는 함수를 반환함 (navigate는 함수)
const navigate = useNavigate();
<button onClick={() => setSearchParams({ who: "summermong" })}>
QS 바꾸기
</button>
<button
onClick={() => {
// 함수 navigate 호출
// 로그인 안하면 로그인 페이지로 보내줄 때 사용됨
navigate("/home");
}}
>
집으로...
</button>
<button
onClick={() => {
navigate(-1);
}}
>
뒤로 가기
</button>
}
**참고
'⚛️ React > 💭 한 입 크기 React' 카테고리의 다른 글
[React] React.createContext로 Props Drilling 방지하기 (0) | 2023.07.26 |
---|---|
[React] useReducer로 상태 변화 로직을 분리하기 (0) | 2023.07.26 |
[React] React.memo로 컴포넌트 재사용하기 (0) | 2023.07.26 |
[React] useMemo로 연산한 값 재사용하기 (0) | 2023.07.26 |
[React] 리액트의 생애주기 (Mount, Update, Unmount) (0) | 2023.07.25 |