SPA 의 경우 서버에서 사용자에게 제공하는 페이지는 한 종류이지만, 해당 페이지에서 로딩된 자바스크립트와 현재 사용자 브라우저의 주소 상태에 따라 다양한 화면을 보여줄 수 있다.
다른 주소에 따라 다른 화면을 보여주는 것을 라우팅이라고 한다. 리액트 자체에 이 기능이 내장되어 있지는 않지만, 브라우저 API를 직접 사용하여 이를 관리하거나, 라이브러리를 사용하여 이 작업을 더욱 쉽게 구현할 수 있다.
리액트 라우팅 라이브러리는 리액트 라우터 ( react-router ), 리치 라우터 (reach-router), Next.js 등 여러가지가 있다. 그 중 우리는 리액트 라우터에 대해 다뤄보자.
리액트 라우터는 클라이언트 사이드에서 이루어지는 라우팅을 아주 간단한게 구현할 수 있도록 한다. 더 나아가 나중에 SSR을 할 때에도 라우팅을 도와주는 컴포넌트들을 제공해준다.
라우터를 적용해 볼 프로젝트에 yarn을 사용하여 react-router-dom 라이브러리를 설치해준다.
$ yarn add react-router-dom
프로젝트에 라우터를 적용할 때는 src/index.js 파일에서 react-router-dom에 내장되어 있는 BrowserRouter라는 컴포넌트로 App 컴포넌트를 감싸주면 된다.
BrowserRouter를 통해 웹 애플리케이션에 HTML5의 history API를 사용하여 페이지를 새로고침하지 않고도 주소를 변경하고, 현재 주소에 관련된 정보를 Props로 쉽게 조회하거나 사용할 수 있게 한다.
🧩 src/index.js
import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter } from "react-router-dom";
import App from "./App";
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>,
document.getElementById("root")
);
🧩 Home.js
import React from "react";
const Home = () => {
return (
<div>
<h1>HOME!! 🏠</h1>
<p>가장 먼저 보이는 페이지</p>
</div>
);
};
export default Home;
🧩 About.js
import React from "react";
const About = () => {
return (
<div>
<h1>소개</h1>
<p>이 프로젝트는 리액트를 공부하는 용으로 사용됩니다. </p>
</div>
);
};
export default About;
🧩 App.js
import { Route, Routes } from "react-router-dom";
import About from "./route/About";
import Home from "./route/Home";
const App = () => {
return (
<div>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />}/>
</Routes>
</div>
);
};
export default App;
사용자가 웹 페이지에 접근했을때 가장 먼저 보여주는 화면인 Home 컴포넌트와 프로젝트 소개를 작성한 페이지 About 컴포넌트를 작성한다. 해당 컴포넌트들을 App 컴포넌트 내부에 Route를 사용하여 설정해준다.
👉 실행 결과
👉 react-router-dom v5 비교 _ routes
//v5
const App = () => {
return (
<div>
<Route path="/" component={Home} />
<Route path="/about" component={About}/>
</div>
);
};
v5에서는 위와 같이 작성었는데, 지금은 Routes로 감싸지 않으면 에러가 난다. 기존의 Switch 컴포넌트 네이밍이 Routes로 바뀜
👉 react-router-dom v5 비교 _ exact
'/about' 경로로 들어가면 About 컴포넌트 뿐만 아니라 Home 컴포넌트도 함께 나타났었다. '/about' 경로가 '/' 규칙에도 일치하기 때문에 발생한 현상이다. 이를 해결하기 위해 v5에서는 exact라는 props에 true를 설정해주었다.
//v5
const App = () => {
return (
<div>
<Route path="/" component={Home} exact ={true} />
<Route path="/about" component={About}/>
</div>
);
};
이렇게 exact를 통해 복수의 라우팅을 방지하도록 했었는데 v6 부터는 exact가 제거되었다. 그리고 exact 대신 여러 라우팅을 매칭하기 위해 '*'을 붙인다.
<Route path="/*" element={<NotFound />} />
Link 컴포넌트는 클릭하면 다른 주소로 이동시켜 주는 컴포넌트이다. 일반 웹 어플리케이션에서는 <a>
태그를 사용하여 페이지 전환을 하는데, react router 사용시에는 a 태그를 직접 사용하면 안된다. a 태그는 페이지를 전환하는 과정에서 페이지를 새로 불러오기 때문에 애플리케이션이 들고 있던 상태를 모두 날리고 다시 처음부터 렌더링 하기 때문이다.
Link 컴포넌트를 사용하여 페이지를 전환하면, 페이지를 새로 불러오지 않고 애플리케이션은 그대로 유지한 채로 HTML5 History API 를 사용하여 페이지의 주소만 변경해준다.
Link 컴포넌트 역시 a 태그로 이루어져 있지만, 페이지 전환을 방지하는 기능이 내장되어 있다.
<Link to="path">내용 </Link>
🧩App.js
import { Link, Route, Routes } from "react-router-dom";
import About from "./route/About";
import Home from "./route/Home";
const App = () => {
return (
<div>
<ul>
<li>
<Link to ="/">Home</Link>
</li>
<li>
<Link to ="/about">About</Link>
</li>
</ul>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />}/>
</Routes>
</div>
);
};
export default App;
👉 실행 결과
페이지 주소를 정의할 때 유동적인 값을 전달해야할 때가 있다. 그 방법으로 파라미터와 쿼리가 있다.
어떤 상황에서 어떤 방법을 써야할지에 대한 무조건적인 규칙은 없다. 하지만 일반적으로 파라미터는 특정 아이디 혹은 이름을 사용하여 조회할 때 사용하고, 쿼리는 어떤 키워드를 검색하거나 페이지에 필요한 옵션을 전달할 때 사용한다.
profile 페이지를 만들어 profile/{username} 과 같은 형식으로 username을 props로 받아와서 조회하는 방법을 알아보자.
🧩 Profiles.js
//v5
const data = {
pumpkin :{
name:'호박쿵야',
description:'호박쿵야를 닮아서 호박쿵야'
},
loveKim :{
name : '김사랑',
description:'호박쿵야의 막내 동생'
}
}
const Profile = ({match})=>{
const {username} = match.params;
const profile =data[username]
if(!profile){
return <div>존재하지 않는 사용자입니다.</div>
}
return(
<div>
<h1>안녕하세요 {profile.name}님!</h1>
<p>소개 : {profile.description}</p>
</div>
)
}
export default Profile
//v6
import React from "react";
import { useParams } from "react-router";
const data = {
...
}
const Profile = ()=>{
const {username} = useParams();
const profile =data[username]
if(!profile){
return <div>존재하지 않는 사용자입니다.</div>
}
return(
...
)
}
export default Profile
URL 파라미터를 사용할 때는 라우트로 사용되는 컴포넌트에서 받아오는 match라는 객체 안의 params 값을 참조한다. match 안에는 현재 컴포넌트가 어떤 경로 규칙에 의해 보이는지에 대한 정보가 들어있다.
react-router v6 부터는 useParams() 라는 Hook을 사용해 더욱 간결하게 parmas값을 받을 수 있다.
🧩 App.js
...
import Profile from "./route/Profiles";
const App = () => {
return (
<div>
<ul>
...
<Link to ="/profile/pumpkin">pumpkin의 프로필</Link>
</li>
<li>
<Link to ="/profile/lovekim">lovekim의 프로필</Link>
</li>
</ul>
<Routes>
...
<Route path ="/profile/:username" element={<Profile />}/>
</Routes>
</div>
);
};
👉 실행 결과
이번에는 About 페이지에서 쿼리를 받아오는 코드를 작성해보자. 쿼리는 location 객체에 들어있는 search 값에서 조회할 수 있다. location 객체는 라우트로 사용된 컴포넌트에게 props로 전달되며, 웹 어플리케이션의 현재 주소에 대한 정보를 갖고 있다.
//http://localhost:3000/about?details=true 주소로 들어갔을 때의 location 구조
location : {
"pathname":"/about",
"search" : "?details=true",
"hash" : ""
}
URL 쿼리는 "?details=true"과 같이 문자열로 되어 있고, 특정 값을 읽어 오기 위해서는 문자열을 객체 형태로 반환해야한다. 그 과정에서 qs 라는 라이브러리를 사용한다.
//v5
import React from "react";
import qs from 'qs'
const About = ({location}) => {
const query = qs.parse(location.search,{
ignoreQueryPrefix:true //문자열 맨 앞의 '?'를 생략하는 설정값
})
const showDetail = query.detail ==='true'
return (
<div>
<h1>소개</h1>
<p>이 프로젝트는 리액트를 공부하는 용으로 사용됩니다. </p>
{showDetail && <p>detail 값을 true로 설정하셨네요 👏</p>}
</div>
);
};
export default About;
//v6
import React, { useMemo } from "react";
import { useLocation } from "react-router";
function useQuery(){
const {search} = useLocation();
return useMemo(()=> new URLSearchParams(search), [search])
}
const About = () => {
let query = useQuery();
const showDetail = query.get('detail') ==='true'
return (
...
);
};
export default About;
![](https://velog.velcdn.com/images%2Fa9120a%2Fpost%2F3f92eb1f-dfef-46d2-ada9-98a28e977c4e%2Fq0lj87mz6whntv2zbxdm.png)
https://github.com/remix-run/react-router/blob/main/docs/getting-started/tutorial.md