최근 react-router-dom 버전 6로 업그레이드 되었습니다. 버전 6과 버전 5는 많은 차이가 있기 때문에 그부분에 있어서도 중점적으로 확인해보는 것을 권장드립니다.
$ yarn add react-router-dom@6
index.js
import { BrowserRouter } from "react-router-dom";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<BrowserRouter>
<App />
</BrowserRouter>
);
Next.js의 경우 pages폴더 안에서 폴더를 어떻게 만드느냐에 따라서 자동으로 ui를 세팅해주기 때문에 미리 연습해본다는 마음으로 관리를 해보는 것도 좋습니다.App.js
import React from "react";
import { Routes, Route } from "react-router-dom";
import Posts from "./Posts";
import Users from "./Users";
function App() {
return (
<div>
<Routes>
<Route path="posts" element={<Posts />} />
<Route path="users" element={<Users />} />
</Routes>
</div>
);
}
export default App;
Route는 어떤 주소로 접근했을 때 어떤 컴포넌트를 보여주고 싶은지를 정하는 태그입니다.<Route path="접속할 port" element={보여줄 컴포넌트} />Routes주소변경을 감지해서 아래에 있는 route중 일치하는 route를 찾고 보여주는 역할을 합니다.<Link to="posts">Posts</Link> to="" 이동하고 싶은 페이지path="*"을 쓰는 경우 일치하지 않는 모든 경로에 해당하는 경우 해당 컴포넌트를 보여줍니다.function App() {
return (
<div>
<Link to="posts">Posts</Link> | <Link to="users">Users</Link>
<Routes>
<Route path="posts" element={<Posts />} />
<Route path="users" element={<Users />} />
<Route path="*" element={<p>Not Found</p>} />
</Routes>
</div>
);
}
App.js
<Routes>
<Route path="posts" element={<Posts />}>
<Route path=":id" element={<PostDetail />} />
</Route>
</Routes>
Post.js outlet
import { Outlet } from "react-router-dom";
function Posts() {
return (
<div>
<Outlet />
</div>
);
}
App.js
<Route path="posts" element={<Posts />}>
<Route index element={<PostIndex />} /> ✅
<Route path=":id" element={<PostDetail />} />
</Route>
Route구조 정리
- Posts페이지 : nav바라던지, 뒤로가기 공통요소를 넣습니다.
- Index페이지 : PostList
- :id : 디테일페이지
PostDetail
import { useParams } from "react-router-dom";
function PostDetail() {
const params = useParams();
return <div>{params.id}</div>;
}
Posts>PostIndex>index.js
function PostIndex() {
return (
<div>
{postData.map((post) => (
<Link to={`/posts/${post.id}`}>
<p>{post.title}</p>
</Link>
))}
</div>
);
}
posts>PostDetail>index.js
useParams로 얻어낸 해당 아이디에 맞는 디테일 정보를 표시합니다.
function PostDetail() {
const params = useParams();
const post = postData.find((post) => post.id === parseInt(params.id));
return (
<>
****
<div>{post.title}</div>
<div>{post.body}</div>
</>
);
}
단, params.id는 문자, post.id는 숫자이기 때문에 타입을 일치 시켜줘야합니다.
function PostIndex() {
const [searchParams, setSearchParams] = useSearchParams();
const [posts, setPosts] = useState(postData);
useEffect(() => {
setPosts(
postData.filter((post) => {
const filter = searchParams.get("filter");
const title = post.title.toLowerCase();
return filter ? title.includes(filter) : true;
})
);
}, []);
return (
<div>
{posts.map((post) => (
<Link to={`/posts/${post.id}`}>
<p>{post.title}</p>
</Link>
))}
</div>
);
}

function PostIndex() {
const [searchParams, setSearchParams] = useSearchParams();
const [posts, setPosts] = useState(postData);
const searchInputHandler = (e) => {
const filter = e.target.value;
filter ? setSearchParams({ filter }) : setSearchParams({});
};
useEffect(() => {
setPosts(
postData.filter((post) => {
const filter = searchParams.get("filter");
const title = post.title.toLowerCase();
return filter ? title.includes(filter) : true;
})
);
}, [searchParams]);
return (
<div>
<input onChange={searchInputHandler} />
{posts.map((post) => (
<Link to={`/posts/${post.id}`}>
<p>{post.title}</p>
</Link>
))}
</div>
);
}
현재 url관련한 위치를 알려줍니다.
아래와 같이 hash, key, pathname(주소), search, state(주소와 함께 특정정보를 전달할수 있습니다.) 정보가 들어있습니다.

사실 PostDetail 페이지의 경우 단순 id와 일치하는 정보만 필요하기 때문에 postData를 불러올 필요는 없습니다.
즉, const post = postData.find((post) => post.id === parseInt(params.id));부분을 PostIndex페이지에서 내려줘도 괜찮습니다.
PostIndex > index.js
return (
<div>
<input onChange={searchInputHandler} />
{posts.map((post) => (
<Link
to={`/posts/${post.id}`}
state={{
post: posts.find((data) => data.id === post.id),
}} ✅
>
<p>{post.title}</p>
</Link>
))}
</div>
);
PostDetail > index.js
import React from "react";
import { useLocation } from "react-router-dom";
function PostDetail() {
const location = useLocation();
//post가 없더라도 에러가 나지 않도록 null처리
const { post } = location.state ? location.state : { post: null };
//post가 없는 경우 not found
if (!post) return <p>Not Found</p>;
return (
<>
<div>title: {post.title}</div>
<div>body: {post.body}</div>
</>
);
}
export default PostDetail;
onClick={() => navigate(-1, {replace:true})}로 설정하는 경우 기록이 아닌, 상위 루트로 가게 됩니다.import { useLocation, useNavigate } from "react-router-dom";
function PostDetail() {
const location = useLocation();
const navigate = useNavigate(); ✅
const { post } = location.state ? location.state : { post: null };
if (!post) return <p>Not Found!</p>;
return (
<>
<div>title: {post.title}</div>
<div>body: {post.body}</div>
<button onClick={() => navigate("/users", {state: {data:1}})}>유저로 가기</button> ✅
<button onClick={() => navigate(-1)}>뒤로가기</button>
</>
);
}
App.js
function App() {
const navigate = useNavigate();
const location = useLocation();
return (
<div>
<h5>{location.pathname}</h5>
<button onClick={() => navigate(-1)}>뒤로가기</button>
<nav>
<Link to="posts">Posts</Link> | <Link to="users">Users</Link>
</nav>
<Routes>
<Route path="posts" element={<PostIndex />}>
<Route index element={<Posts />} />
<Route path=":id" element={<PostDetail />} />
</Route>
<Route path="users" element={<Users />}>
<Route index element={<UserIndex />} />
<Route path=":id" element={<UserDetail />} />
</Route>
<Route path="*" element={<p>Not Found</p>} />
</Routes>
</div>
);
}
PostIndex > index.js
{
posts.map((post) => (
<NavLink
style={({ isActive }) => ({ color: isActive ? "red" : "black" })}
to={`/posts/${post.id}`}
state={{
post: posts.find((data) => data.id === post.id),
}}
>
<p>{post.title}</p>
</NavLink>
));
}
App.js
<Route path="albums" element={<Albums />}>
<Route index element={<AlbumList />} />
</Route>
Albums > index.js
function Albums() {
return (
<div>
<Outlet />
</div>
);
}
AlbumList > index.js
function AlbumList() {
const [albums, setAlbums] = useState();
const fetchData = async () => {
const { data } = await axios.get(
"https://jsonplaceholder.typicode.com/albums"
);
setAlbums(data);
};
useEffect(() => {
fetchData();
}, []);
return (
<div>
{albums &&
albums.map((album) => (
// to={`${album.id}`}동일합니다.
<Link to={`/albums/${album.id}`}>
<p>{album.title}</p>
</Link>
))}
</div>
);
}