공식문서
https://reactrouter.com/en/main
npm을 이용해 설치한다
npm i react-router-dom
그 후 사용할 App.js에서 import 한다.
import {createBrowserRouter, RouterProvider} from 'react-router-dom'
SPA 구조에서 URL에 따라 페이지를 전환하기 위해 사용한다.
const Router = () => {
return (
<Routes>
<Route path="/" element={<App/>} />
<Route path="/search" element={<Search/>} />
<Route path="/my" element={<My />} />
</Routes>
);
};
이전 버전에서는 위와 같은 구조를 사용해서 구조를 설정했다.
import {createBrowserRouter, RouterProvider} from 'react-router-dom'
import HomePage from './pages/Homepage';
import EventPage from './pages/EventPage';
function App() {
const router = createBrowserRouter([
{path:'/', element:<HomePage/>},
{path:'/EventPage', element:<EventPage/>},
])
return <div>
hello
</div>;
}
export default App;
이제는 해당 방식으로 사용이 가능하다.
이 상태에서 시작한다.
Pages 폴더에 더미 페이지들을 제작해둔 뒤
const router = createBrowserRouter([
{path:'/', element:<HomePage/>},
{path:'/EventPage', element:<EventPage/>},
])
여기서 router은 마음대로 이름을 지정할 수 있다.
createBreowswerRouter를 사용해 각 url(path)에 따라서 렌더링할 페이지(컴포넌트(element))를 적어준다.
'/' 즉 추가 url이 적혀있지 않은 http://localhost:3000/ 에서는 HomePage 컴포넌트에 있는 내용이 렌더링된다.
http://localhost:3000/EventPage 에서는 HomePage 대신 EventPage가 출력될 것이다.
return <div>
hello
<RouterProvider router={router}/>
</div>;
그 후 RouterProvider를 사용해서 router에 우리가 방금 작성한 객체를 지정해준다.
원래 출력될 hello에 url이 '/' 이므로 HomePage의 내용이 RouterProvider 자리에 출력된 것을 확인.
URL 뒤에 EventPage를 작성해서 다른 페이지를 열려고 했지만 실수를 해서 틀리게 작성하여 오류 페이지가 나왔다.
해당 오류 페이지는 reactrouterdom 에서 지원하는 기본 오류 페이지다. 이 페이지도 추후에 수정 가능.
렌더링 될 내용이 달라지는 것을 확인할 수 있다.
하지만 사용자가 모든 url을 인지하고 검색창에 쳐서 사용한다는 것은 비현실적.
버튼을 사용해서 이동하기로 한다.
HomePage.js
import EventPage from "./EventPage"
import { Link } from "react-router-dom"
const HomePage=()=>
{
return(
<div>
it's HomePage
<Link to ='/EventPage'>Bring me to eventPage</Link>
</div>
)
}
export default HomePage
EventPage.js
import HomePage from "./Homepage"
import { Link } from "react-router-dom"
const EventPage=()=>
{
return(
<div>
it's EventPage
<Link to ='/'>Bring me to homePage</Link>
</div>
)
}
export default EventPage
Link를 사용해서 URL을 바꿀 수 있다. 절대경로를 사용했다.
클릭하면
의도한대로 서로 이동하면서 잘 작동한다.
절대경로를 사용했기에 Link to '/' 으로 맨처음경로로 돌아갈 수 있고 터미널에서 cd 명령어를 사용하듯이
Link to '..' 을 하면 한칸 상위로 올라간다.
이렇게 링크를 적을 때 / 로 시작하면 절대경로고 그렇지 않다면 상대경로를 사용한다.
절대경로와 상대경로의 차이를 예시를 들어서 설명하려고 한다.
{path:'/NewEventPage', element:<NewEventPage/>},
App.js에 경로를 추가하고
<Link to ='/NewEventPage'>Bring me to NewEventPage</Link><br/>
EventPage.js에 링크를 추가했다.
to neweventpage를 클릭하면
이 페이지로 넘어온다.
<Link to ='NewEventPage'>Bring me to NewEventPage</Link><br/>
EventPage에서 절대경로를 상대경로로 바꾸어봤다.
두가지 차이를 발견할 수 있다.
1.링크가 EventPage에서 추가로 /가 붙고 NewEventPage가 추가됐고.
2.에러가 발생했다.
App.js에서 router 설정을 할 때 절대경로로 /NewEventPage를 지정했지만
우리는 EventPage속에서 상대경로로 지정했기 때문에 그 페이지에서 한칸 더 들어가서 NewEventPage라고 적은 URL에
접근했기 때문에 서로 다르게 인지해서 오류가 발생하는 것.
이 구분이 필요한 이유는 뒤에 작성할 내용을 추가한 뒤 추가설명 하겠다.
보통 웹페이지들을 보면 위와 같이 페이지를 이동할 수 있는 네비게이션 바가 존재한다.
위와같은 네비게이션 바를 구현해보려고 한다.
import { Link } from 'react-router-dom';
function MainNavigation() {
return (
<header>
<nav>
<ul>
<li>
<Link to='/'>Home</Link>
</li>
<li>
<Link to='/EventPage'>Events</Link>
</li>
</ul>
</nav>
</header>
);
}
export default MainNavigation;
MainNavigation.js를 작성했다.
const HomePage=()=>
{
return(
<div>
<MainNavigation/>
it's HomePage
<Link to ='/EventPage'>Bring me to eventPage</Link>
</div>
)
}
HomePage.js에 해당 컴포넌트를 import 한뒤 추가해주었다.
상단바에 잘 나오는 것을 확인 할 수 있다.
하지만 EventPage에는 Navigation 컴포넌트를 넣지 않았기 때문에 당연히 나오지 않는다.
이런 방식을 사용해서 네비게이션 바를 사용하려면 모든 페이지에 네비게이션 바를 import 하고 작성해야 하는데
너무 번거로워 진다, 페이지가 200개가 넘으면 모든 페이지에 해당 컴포넌트를 넣어줘야 한다.
페이지의 레이아웃을 관리할 컴포넌트를 추가해주려고 한다.
우선 App.js 에서 router의 구조를 조금 바꿔준다.
function App() {
const router = createBrowserRouter([
{
path: '/',
element: <Layout />,
children: [
{ path: '/', element: <HomePage /> },
{ path: '/EventPage', element: <EventPage /> },
{ path: '/NewEventPage', element: <NewEventPage /> },
],
}
])
가장 기본적인 컴포넌트를 Layout으로 설정하고
그 하위 children으로 여러가지 페이지들을 설정해주었다.
import MainNavigation from "../components/MainNavigation";
import { Outlet } from "react-router-dom";
const Layout=()=>{
return(
<>
<MainNavigation/>
it's layout
<Outlet/>
</>
)
}
export default Layout
Layout.js의 내용은 다음과 같은데 상단에 만들어둔 네비게이션을 배치하고
Outlet의 부분에 우리가 아까 children 속에 넣어둔 컴포넌트들 중 url과 일치하는 컴포넌트가 출력 될 예정이다.
Homepage.js의 중복될 네비게이션은 제거해주고 각 페이지의 Bring me to ~~page 라고 적힌 Link들도 제거해준다.
정상 작동하는 것을 확인 할 수 있다.
이제 에러 페이지를 설정하려고 한다. 단순히 Error! 이라고 적힌 Error 컴포넌트를 작성한다.
function App() {
const router = createBrowserRouter([
{
path: '/',
element: <Layout />,
errorElement:<ErrorPage/>,
children: [
{ index:true, element: <HomePage /> },
{ path: '/EventPage', element: <EventPage /> },
{ path: '/NewEventPage', element: <NewEventPage /> },
],
}
])
그 후 errorElement에 해당 컴포넌트를 넣어준다.
지정하지 않은 url로 가면 해당 에러가 발생한다.
Link에 스타일을 적용하려고 하면 Link 태그가 아닌 a 태그로 적용해야 한다.
Link는 a태그를 기반으로 적용되기 때문.
ul {
display: flex;
gap: 1rem;
}
ul a {
text-decoration: none;
color: var(--color-primary-400);
}
이렇게 작성 했을 때 위와 같이 스타일이 적용된다.
지금 내가 누른 페이지가 활성화 되었을 때 해당 버튼의 색깔을 바꾸려고 한다면
<NavLink
to='/'
className={({isActive}) =>isActive ? 'hi' : undefined}
>
Home</NavLink>
Link 에서 NavLink로 바꾼 뒤 위와 같이 설정한다.
NavLink에선 className을 함수를 받는 property로 사용할 수 있다.
isActive는 react-router-dom에서 제공하며 해당 링크가 활성화 되어 있다면 참 값이 된다.
ul a.hi
{
color:blue;
}
해당 css를 추가하면
활성화 된 페이지일 때 색을 바꿀 수 있다.
라우터를 써야 할 경우를 예시로 하나 들어보면
쇼핑몰 페이지에서 상품 목록이 쭉 나오고 해당 상품을 눌렀을 때 상세 페이지로 들어가는 시나리오다.
상품 목록은 서버에서부터 불러 올 것이고, 해당 상품을 눌렀을 때 그 정보로 그 상품의 정보가
들어있는 페이지로 가야한다. 이때 이동할 이 페이지는 모두 수동으로 정해줄 수 없다. 동적인 URL이 필요하다.
이때는 params라는 것을 사용한다.
children: [
{ index:true, element: <HomePage /> },
{ path: '/EventPage', element: <EventPage /> },
{ path: '/EventPage/:NewEventPage', element: <NewEventPage /> },
],
app.js에서 neweventpage를 eventpage의 하위 페이지로 만들었다.
여기서 NewEventPage 앞에 :이 붙은 것을 볼 수 있는데 이게 역동적 URL이다. 이자리엔 무엇이든 올 수 있다.
params는 라우트의 path를 정의할 때 적어둔 역동적 URL의 정보가 담겨있다.
저 :NewEventPage를 :id로 바꾸어주었다.
import { useParams } from "react-router-dom"
const NewEventPage = () => {
const params = useParams();
return (
<div>
{params.id}
</div>
)
}
export default NewEventPage
이렇게 해주었을 때
URL에 적은대로 출력된다
이를 사용해 상황을 생각해보면 예시로 쇼핑몰을 생각할 수 있다.
서버에서 제공된 물품이 나열되어 있고 상품을 누르면 해당 상품의 상세 페이지로 갈것이다.
이 상세 페이지로 들어가는 URL은 우리가 미리 지정해둘 수 없다. 서버의 데이터를 기반으로 링크를 생성해야 한다.
import { Link } from "react-router-dom"
const EventPage = () => {
const EVENTS = [
{ id: 'e1', title: 'event 1' },
{ id: 'e2', title: 'event 2' },
{ id: 'e3', title: 'event 3' }
]
return (
<div>
<ul>
{EVENTS.map((evt)=>
(
<li key={evt.key}>
<Link to={`/EventPage/${evt.id}`}>{evt.title}</Link>
</li>
))}
</ul>
</div>
)
}
export default EventPage
EVENTS 객체를 서버에서 가져온 정보라고 가정하고 map을 사용해서 그 정보를 <li>태그 형식으로 나열하고
Link도 설정하였다.
클릭했을 때 id가 나오고 정상작동한다.
만약 라우터 설정할때 첫 페이지 path를 '/'이 아닌 '/main'과 같이 수정하고 싶다고 해서
function App() {
const router = createBrowserRouter([
{
path: '/main',
element: <Layout />,
errorElement:<ErrorPage/>,
children: [
{ path: '/', element: <HomePage /> },
{ path: '/EventPage', element: <EventPage /> },
{ path: '/EventPage/:id', element: <NewEventPage /> },
],
}
])
위와같이 하면 에러가 난다. 기본 경로가 /main이므로 /main으로 시작하는 children을 관리해야 하는데
children속에는 절대경로로 적혀있고 /main이 적혀있지 않다. 그러므로 children에 속해있는 객체에서
path의 맨 앞 / 를 제거하고 구동하면 작동한다.
네비게이션의 path도 상대경로로 작성해준다.
하지만 이렇게 네비게이션과 라우터를 상대경로로 설정해주더라도 오류가 생기는 부분이 있다.
아까 map으로 설정한 이벤트 상세페이지가 그러한데
리스트중에 하나를 선택하면 절대경로이기에 /main이 사라지고 바로 /EventPage로 감으로 인해서 에러가 발생한다.
그렇지만 맨앞 / 를 삭제하고 상대경로로 하면
EventPage/EventPage와 같이 되어 오류가 생긴다.
이런 상황에서는
<Link to={`${evt.id}`}>{evt.title}</Link>
와 같이 상대경로로 작성해주어야 한다.
이제 이 화면에서 돌아가는 버튼을 만드려고한다.
const NewEventPage = () => {
const params = useParams();
return (
<>
<div>{params.id}</div>
<Link to='..'>back</Link>
</>
)
}
하지만 이상태에서 back 버튼을 누르면 '..'에 의해 한칸만 돌아가는게 아니라 메인페이지 './root' 로 돌아가버린다
이 이유는 라우터에서 확인한다.
function App() {
const router = createBrowserRouter([
{
path: '/root',
element: <Layout />,
errorElement:<ErrorPage/>,
children: [
{ path: '', element: <HomePage /> },
{ path: 'EventPage', element: <EventPage /> },
{ path: 'EventPage/:id', element: <NewEventPage /> },
],
}
])
우리는 EventPage/:id 에서 한칸 앞으로 오라고 했는데 EventPage로 돌아올 거라고 예상되지만
둘다 같은 children에 묶여있기 때문에 형제관계가 되어있다. 그렇기 때문에 상위 관계에 있는
/root로 돌아오는 것인데 이를 해결하기 위해선
Link 태그에 relative='path' property를 추가해주어야 한다.
relative 가 'route' 라면 기본상태, 우리가 지금 한 것처럼 라우팅 정의에 의해서 상대적으로 이동한다.
그렇지만 relative가 path 라면 그저 url의 한칸을 빼서 돌아가게 만들어준다.
const NewEventPage = () => {
const params = useParams();
return (
<>
<div>{params.id}</div>
<Link to='..' relative='path'>back</Link>
</>
)
}
back을 클릭했을 때 우리 의도대로 동작하게 된다.