SPA ์ ๊ฒฝ์ฐ ์๋ฒ์์ ์ฌ์ฉ์์๊ฒ ์ ๊ณตํ๋ ํ์ด์ง๋ ํ ์ข
๋ฅ์ด์ง๋ง, ํด๋น ํ์ด์ง์์ ๋ก๋ฉ๋ ์๋ฐ์คํฌ๋ฆฝํธ์ ํ์ฌ ์ฌ์ฉ์ ๋ธ๋ผ์ฐ์ ์ ์ฃผ์ ์ํ์ ๋ฐ๋ผ ๋ค์ํ ํ๋ฉด์ ๋ณด์ฌ์ค ์ ์๋ค.
๋ค๋ฅธ ์ฃผ์์ ๋ฐ๋ผ ๋ค๋ฅธ ํ๋ฉด์ ๋ณด์ฌ์ฃผ๋ ๊ฒ์ ๋ผ์ฐํ
์ด๋ผ๊ณ ํ๋ค. ๋ฆฌ์กํธ ์์ฒด์ ์ด ๊ธฐ๋ฅ์ด ๋ด์ฅ๋์ด ์์ง๋ ์์ง๋ง, ๋ธ๋ผ์ฐ์ API๋ฅผ ์ง์ ์ฌ์ฉํ์ฌ ์ด๋ฅผ ๊ด๋ฆฌํ๊ฑฐ๋, ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ์ฌ ์ด ์์
์ ๋์ฑ ์ฝ๊ฒ ๊ตฌํํ ์ ์๋ค.
๋ฆฌ์กํธ ๋ผ์ฐํ
๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ๋ฆฌ์กํธ ๋ผ์ฐํฐ ( react-router ), ๋ฆฌ์น ๋ผ์ฐํฐ (reach-router), Next.js ๋ฑ ์ฌ๋ฌ๊ฐ์ง๊ฐ ์๋ค. ๊ทธ ์ค ์ฐ๋ฆฌ๋ ๋ฆฌ์กํธ ๋ผ์ฐํฐ์ ๋ํด ๋ค๋ค๋ณด์.
๋ฆฌ์กํธ ๋ผ์ฐํฐ๋ ํด๋ผ์ด์ธํธ ์ฌ์ด๋์์ ์ด๋ฃจ์ด์ง๋ ๋ผ์ฐํ ์ ์์ฃผ ๊ฐ๋จํ๊ฒ ๊ตฌํํ ์ ์๋๋ก ํ๋ค. ๋ ๋์๊ฐ ๋์ค์ SSR์ ํ ๋์๋ ๋ผ์ฐํ ์ ๋์์ฃผ๋ ์ปดํฌ๋ํธ๋ค์ ์ ๊ณตํด์ค๋ค.
react-route ์ค์น ๋ฐ ์ฌ์ฉ๋ฒ
๋ผ์ฐํฐ๋ฅผ ์ ์ฉํด ๋ณผ ํ๋ก์ ํธ์ 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 ์ปดํฌ๋ํธ ์ฌ์ฉ
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;
๐ ์คํ ๊ฒฐ๊ณผ

URL ํ๋ผ๋ฏธํฐ์ ์ฟผ๋ฆฌ
ํ์ด์ง ์ฃผ์๋ฅผ ์ ์ํ ๋ ์ ๋์ ์ธ ๊ฐ์ ์ ๋ฌํด์ผํ ๋๊ฐ ์๋ค. ๊ทธ ๋ฐฉ๋ฒ์ผ๋ก ํ๋ผ๋ฏธํฐ์ ์ฟผ๋ฆฌ๊ฐ ์๋ค.
- ํ๋ผ๋ฏธํฐ : 'profiles/pumpkin'
- ์ฟผ๋ฆฌ : 'profiles?details=true'
์ด๋ค ์ํฉ์์ ์ด๋ค ๋ฐฉ๋ฒ์ ์จ์ผํ ์ง์ ๋ํ ๋ฌด์กฐ๊ฑด์ ์ธ ๊ท์น์ ์๋ค. ํ์ง๋ง ์ผ๋ฐ์ ์ผ๋ก ํ๋ผ๋ฏธํฐ๋ ํน์ ์์ด๋ ํน์ ์ด๋ฆ์ ์ฌ์ฉํ์ฌ ์กฐํํ ๋ ์ฌ์ฉํ๊ณ , ์ฟผ๋ฆฌ๋ ์ด๋ค ํค์๋๋ฅผ ๊ฒ์ํ๊ฑฐ๋ ํ์ด์ง์ ํ์ํ ์ต์ ์ ์ ๋ฌํ ๋ ์ฌ์ฉํ๋ค.
URL ํ๋ผ๋ฏธํฐ_useParams()
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>
);
};
๐ ์คํ ๊ฒฐ๊ณผ

URL ์ฟผ๋ฆฌ
์ด๋ฒ์๋ 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://github.com/remix-run/react-router/blob/main/docs/getting-started/tutorial.md
GitHub - remix-run/react-router: Declarative routing for React
Declarative routing for React. Contribute to remix-run/react-router development by creating an account on GitHub.
github.com
Error "Error: A <Route> is only ever to be used as the child of <Routes> element"
I am trying to use routing for the first time and followed the exact instructions from Udemy: File App.js: import { Route } from "react-router-dom"; import Welcome from "./Pages/Welc...
stackoverflow.com
