"라우팅(routing)"이란 사용자가 요청한 URL 경로에 따라 그에 맞는 UI를 보여주는 것을 라우팅이라고 합니다. React App의 경우 새로운 페이지를 요청하는 것이 아니라 URL에 맞는 페이지 컴포넌트를 렌더링해주어야 합니다.
React는 SPA(Single Page Application)로서 하나의 페이지(HTML)을 갖는 애플리케이션입니다. 즉, SPA는 하나의 HTML 파일, 하나의 URL을 가지며 이는 절대 바뀌지 않습니다.
이처럼 변하지 않는 URL이 이상적이지 않습니다.
예를 들어, URL을 다른 사람에게 공유를 하거나 북마크를 하더라도 해당 URL로 접속을 한다면 언제나 시작 페이지를 보여주게 될 것입니다.
우리는 기존처럼 하나의 페이지(SPA), 즉 하나의 HTML 페이지만을 사용하면서 URL에 따라서 화면에 보이는 UI를 바뀌도록 해야하며, URL 경로가 변경되더라도 새로운 HTML 파일을 서버로부터 요청하지도 않으면서 변경된 URL 경로에 따라 그에 맞는 화면(UI)을 사용자에게 보여주어야 합니다. 이를 구현하기 위해서 "react-router-dom" 패키지를 사용해야 합니다.
react-router-dom을 사용하기 위해서 터미널에 아래 명령어를 입력하여 패키지를 설치합니다.
npm install react-router-dom
URL(Uniform Resource Locator)는 리소스(파일)를 리소스의 위치를 통해 식별하는 것입니다.
URL은 프로토콜(http)로 시작되며 일반적으로 호스트 이름과 리소스 경로에 대한 정보를 갖고 있습니다. 또한 쿼리 스트링이나 프래그먼트도 포함 가능합니다.
React Router는 URL의 파일의 위치와 파일명을 나타내는 "Path to the file"에 따라서 렌더링될 페이지 컴포넌트를 결정되도록 합니다.
이 부분을 앞으로는 "URL의 경로"라고 부르겠습니다.
react router는 URL의 경로에 맞는 페이지 컴포넌트를 불러오기 위해서 사용합니다. 즉, URL의 경로에 맞는 HTML 문서가 아닌 페이지 컴포넌트를 화면에 렌더링하기 위해 사용합니다.
예를 들어, URL의 경로가 /content-a
일 때는 Component A를 렌더링 하고 경로가 /content-b
일 때는 Component B를 렌더링 하는 것입니다.
이는 조건적으로 페이지 컴포넌트를 렌더링하는 것과 유사하다고 볼 수 있습니다. 특정 URL의 경로에 따라 렌더링할 페이지 컴포넌트를 결정하는 것입니다.
Route
컴포넌트는 특정 URL 경로일 때 보여질 페이지 컴포넌트를 설정하는 컴포넌트입니다.
import { Route } from 'react-router-dom';
// path 어트리뷰트에는 특정 URL의 경로값을 작성
// element 어트리뷰트에는 Route가 활성화될 때 렌더링될 컴포넌트를 작성
<Route path="/경로" element={<PageComponent />} />
참고로 react-router-dom v6부터는 Route
컴포넌트들을 반드시 "Routes
컴포넌트 자식 컴포넌트로 작성해주어야 합니다".
Routes
컴포넌트의 자식 컴포넌트로 작성한 Route
는 "하나만 활성화"되어 그에 맞는 페이지 컴포넌트를 화면에 렌더링합니다.
import { Routes, Route } from 'react-router-dom';
// Route 컴포넌트는 반드시 Routes 컴포넌트로 감싸주어야 합니다.
<Routes>
// Route 컴포넌트로 URL의 경로값에 따라 렌더링될 컴포넌트를 설정합니다.
<Route path="/경로" element={<PageComponent />}>
<Route path="/경로" element={<PageComponent />}>
</Routes>
Route
컴포넌트에는 path 어트리뷰트를 설정해야 합니다. path 어트리뷰트에는 "특정 URL의 경로값"을 작성해줍니다.
Routes
컴포넌트 내 작성된 Route
컴포넌트의 path 어트리뷰트 값을 현재 URL 경로값과 일치하는지 확인하고, 정확히 일치하는 하나의 Route
컴포넌트를 활성화시키게 됩니다.
기존 react-router-dom v5에서 exact라는 어트리뷰트 작성한 것과 동일하게 매칭되는 Route
를 탐색합니다.
활성화된 Route
컴포넌트는 설정된 페이지 컴포넌트를 그 자리에 렌더링합니다.
즉, URL 경로값과 path 어트리뷰트값이 일치하는 하나의 Route
컴포넌트를 활성화하고 설정된 페이지 컴포넌트를 Route
컴포넌트 위치에 렌더링하게 됩니다.
아래 코드에서 만약 URL의 경로값이 "/apple"인 경우에는 path 어트리뷰트 값이 "/apple"인 Route
가 활성화되고, element 어트리뷰트에 설정된 Apple 컴포넌트가 활성화된 Route
컴포넌트 위치에 렌더링됩니다.
URL 경로값이 "/banana"인 경우 path 어트리뷰트값이 "/banana"인 Route
가 활성되고, element 어트리뷰트에 설정된 Banana 컴포넌트가 활성화된 Route
컴포넌트 위치에 렌더링됩니다.
<Routes>
// "/apple"인 경우 활성화
<Route path="/apple" element={<Apple />} />
// "/banana"인 경우 활성화
<Route path="/banana" element={<Banana />} />
</Routes>
Route
가 활성되었을 때 어떤 페이지 컴포넌트를 화면에 렌더링해야할지 설정하기 위해서 Route
컴포넌트의 element 어트리뷰트에 "페이지 컴포넌트를 작성"해줍니다.
element 어트리뷰트에 작성한 컴포넌트는 활성화된 Route
컴포넌트 위치에 렌더링됩니다.
침고로 Route
가 활성화되어 element 어트리뷰트에 작성된 페이지 컴포넌트가 렌더링될 때 페이지 컴포넌트가 실행되어 렌더링됩니다.
정리하자면 react router의 Routing되는 과정은 아래와 같이 진행됩니다.
현재 URL 경로값과 Route
컴포넌트의 path 어트리뷰트 값이 서로 일치하는 Route
컴포넌트 활성화
활성화된 Route
컴포넌트의 element 어트리뷰트에 작성된 페이지 컴포넌트를 실행하여 활성화된 Route
컴포넌트 위치에 렌더링
우리는 Route를 활성화 하고 다른 react rouuter 기능을 동작하도록 하기위해서 추가작업이 필요합니다.
// index.js
import ReactDOM from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
import App from './App';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<BrowserRouter>
<App />
</BrowserRouter>
);
라우팅을 사용할 루트 컴포넌트를 BrowserRouter
컴포넌트로 감싸줍니다.
위 예제에서는 루트 컴포넌트인 App 컴포넌트를 BrowserRouter
컴포넌트 감싸주었습니다.
BrowserRoute
컴포넌트는 웹 애플리케이션에 HTML5의 History API를 사용하여 페이지를 새로 요청하지 않고도 URL를 변경하고 현재 URL과 관련된 정보를 리액트 컴포넌트에서 사용할 수 있도록 도와줍니다.
URL을 변경하기 위해서 a 엘리먼트를 사용할 수 있습니다. a 태그의 href 어트리뷰트에 변경될 URL을 작성하면 하이퍼링크가 생성되고 해당 URL로 변경됩니다
a 엘리먼트의 href 어트리뷰트에 작성된 URL의 경로로 변경이 되지만 문제점이 존재합니다. 링크를 클릭할 때마다 새로운 HTML 문서를 요청하여 응답받고 URL의 경로값과 매칭되는 Route
컴포넌트를 활성화합니다.
문제가 되는 점은 서버측에 요청을 보내고 새로운 HTML을 응답받아 렌더링한다는 점입니다.
이렇게 링크를 클릭할 때 새로운 HTML 파일을 요청한다는 것은 SPA로 동작하는 React 애플리케이션이 기존 상태를 모두 잃어버리게 된다는 문제점이 발생합니다.
즉, 기존 상태를 갖고 있는 애플리케이션을 버리고 새롭게 요청을 전송하게 되어버립니다. 이는 SPA 구축과 반대되는 개념이며 이상적이지 않습니다.
react-router-dom의 "Link
컴포넌트"를 사용하면 URL의 경로만을 변경하고, 서버에게 새로운 요청은 보내지 않는 링크를 만들 수 있습니다.
참고로 URL 경로가 변경될 때마다 렌더링되는 페이지 컴포넌트가 실행되어 렌더링됩니다.
// to prop에 특정 경로값을 작성
<Link to="경로">링크 이름</Link>
기존 a 엘리먼트를 사용하는 대신에 Link
컴포넌트로 대체하고 href 어트리뷰트를 "to 어트리뷰트"으로 바꿔줍니다.
to 어트리뷰트 값으로 URL의 "절대경로"와 "상대경로"를 문자열로작성할 수 있습니다.
상대경로
: "./경로값"나 "경로값"처럼 상대 경로값을 사용하는 경우 변경될 경로값은 현재 URL의 경로값을 기준 경로로 변경하게 됩니다.
절대경로
: 경로값이 "/"로 시작하는 절대 경로값인 경우 현재 URL 경로값을 to 어트리뷰트에 작성된 경로값으로 변경하게 됩니다.
import { Link } from 'react-router-dom';
// 현재 URL의 경로 "/about"
// 절대 경로, 클릭시 "/apple"로 변경
<Link to="/apple">apple</Link>
// 상대 경로, 클릭시 "/about/apple"로 변경
<Link to="banana">banana</banana>
// 상대 경로, 클릭시 "/about/mango"로 변경
<Link to="./mango">mango</Link>
Link 컴포넌트는 실제 돔에는 "a 엘리먼트"로 렌더링되며 내부적으로 클릭시 "HTML을 요청하는 기본 동작을 막는" 로직이 추가되어 있습니다.
Link 컴포넌트의 to 어트리뷰트에 경로값을 문자열이 아닌 "객체"를 전달할 수도 있습니다.
<Link
to={{
pathname: '/경로값',
search: '?쿼리파라미터=값'
}}
>Link</Link>
to 어트리뷰트에 전달되는 객체는 pathname 프로퍼티에 이동될 경로값을 문자열로, search 프로퍼티에는 쿼리 파라미터와 값을 문자열로 작성할 수 있습니다.
react-router-dom의 NavLink
컴포넌트는 기본적으로 Link 컴포넌트와 같습니다. a 엘리먼트를 생성하고 클릭 이벤트를 캐치하여 브라우저의 기본 동작을 막습니다. 또한 추가적인 기능이 존재합니다.
NavLink
의 style 또는 className 어트리뷰트에 "함수"를 작성하여 스타일을 동적으로 적용할 수 있습니다.
두 어트리뷰트의 값으로는 "객체를 인수로 전달받는 함수"를 값으로 작성합니다.
인수로 전달받는 객체에는 "isActive"라는 프로퍼티가 존재하며 NavLink
를 클릭하여 URL이 변경되면 isActive 프로퍼티 값이 true로 설정됩니다. 그리고 함수의 "반환값으로 어트리뷰트 값이 설정"됩니다.
import { NavLink } from 'react-router-dom';
import calsses from './MainHeader.module.css';
// 인수로 전달받는 객체의 isActive 프로퍼티 값을 통해
// 동적으로 클래스 명이 설정되도록 작성
<NavLink
className={({ isActive }) => isActive ? classes.active : ''}
to="/welcome"
>Welcome</NavLink>
// 인수로 전달받는 객체의 isActive 프로퍼티 값을 통해
// 동적으로 인라인 스타일을 설정
<NavLink
style={({ isActive )} => isActive ? ({ color: blue }) : ({ color: red })}
to="/welcome"
>Welcome</NavLink>
동적 라우팅이란 "하나의 Route 컴포넌트", "하나의 페이지 컴포넌트"를 사용하여 URL의 경로값에 따라 페이지 컴포넌트가 렌더링할 정보가 동적으로 결정되어 렌더링하는 것을 의미합니다.
즉, Route 컴포넌트가 불러와야 하는 페이지 컴포넌트의 구체적인 정보를 URL의 경로값에 따라 동적으로 결정되도록 만들어 주는 것입니다.
동적 라우팅을 사용함으로써 우리는 하나의 페이지 컴포넌트를 "재사용", 또는 "여러 페이지 컴포넌트를 하나의 페이지 컴포넌트로 사용"할 수 있습니다.
우리는 동적 라우팅을 위해 "하나의 Route
를 사용"하고, URL의 경로에 따라 각 상품의 "상세 페이지에 들어갈 내용을 동적으로 결정"되도록 구현하려고 합니다.
동적 라우팅되는 Route
컴포넌트를 구현하기 위해서는 path 어트리뷰트 값에 동적으로 결정되는 경로값을 전달받을 "경로 파라미터"를 작성해야 합니다.
경로 파라미터는 ":경로파라미터"로 작성하여 구현할 수 있습니다.
그리고 반드시 경로를 구분하기 위해서 "/"를 작성해주어야 합니다.
<Route path="/path/:pathParam" element={<Component />} />
위와 같은 Route
는 path 어트리뷰트 값에 작성된 "pathParam"이 경로 파라미터가 되며 동적으로 결정된 URL의 경로값이 할당됩니다.
참고로 "/a/:b/:c/:d"처럼 경로 파라미터를 여러 개 설정할 수도 있습니다.
// App.js
// 현재 URL 경로 "/"
import { Routes, Route, Link } from 'react-router-dom';
import Welcome from './components/Welcome';
import Products from './components/Products';
import ProductDetail from './Components/ProductDetail';
const App = () => {
return (
<div>
<ul>
<li>
<Link to="/products">Products List</ Link>
</li>
<li>
<Link to="/welcome">Welcome</Link>
</li>
</ul>
<Routes>
<Route path="/welcome" element={<Welcome />} />
<Route path="/products" element={<Products />} />
// productId가 경로 파라미터가 되고 경로값이 할당
<Route path="/products/:productId" element={<ProductDetail />} />
</Routes>
</ div>
);
}
export default App;
Products List라는 링크를 누르게 되면 URL 경로가 "/products"로 바뀌게 되고 "/products"를 path 어트리뷰트 값으로 갖고 있는 Route 컴포넌트가 활성화 됩니다.
이후 react router가 활성화된 Route 컴포넌트의 element 어트리뷰트에 작성된 Product 컴포넌트를 화면에 렌더링합니다.
// Products.js
// 현재 URL 경로 "/products"
import { Link } from 'react-router-dom';
const Products = () => {
return (
<section>
<h1>The Products Page</h1>
<ul>
<li>
// 상대 경로인 점에 주의
// "/products/p1"과 동일
<Link to="p1">A Book</Link>
</li>
<li>
// "/products/p2"과 동일
<Link to="p2">A Carpet</Link>
</li>
<li>
// "/products/p3"과 동일
<Link to="p3">An Online Course</Link>
</li>
</ul>
</section>
);
}
export default Products;
Products 컴포넌트는 상품 리스트를 렌더링하는 컴포넌트입니다. 각 상품의 상세 페이지 링크을 Link
컴포넌트 렌더링하였습니다. 만약 A Book 링크를 클릭하면 URL 경로가 "/products/p1"으로 변경이 될 것입니다.
A Book 링크 클릭시 URL이 변경되고 Route 컴포넌트 중에서 path 어트리뷰트 값이 "/products/:productId"인 Route가 활성화가 됩니다. 이때 productId 경로 파라미터 값이 p1으로 결정되고 Route가 활성화 됩니다.
동적으로 결정된 경로 파라미터 값을 취득하기 위해서 useParams
훅을 사용합니다.
즉, 동적 라우팅된 Route
의 element 어트리뷰트에 작성된 페이지 컴포넌트 내에서 useParams
훅을 통해 경로 파라미터 값을 추출할 수 있습니다.
useParams
훅은 객체를 반환하며, 반환된 객체에는 경로 파라미터와 경로값으 프로퍼티로 존재합니다.
이때 경로 파라미터 이름과 동일한 이름으로 프로퍼티 키가 설정되어 있습니다.
// ProductDetail.js
// 현재 URL 경로 "/products/p1"
import { useParams } from 'react-router-dom';
const ProductDetail = () => {
// params 객체에는 :(콜론) 뒤에 작성된 경로 파라미터의 이름이 프로퍼티 키로 설정되고
// 동적으로 결정된 경로값이 프로퍼티 값으로 존재
const params = useParams(); // -> { productId: 'p1' }
return (
<section>
<h2>Product Detail Page</h2>
<p>{params.productId}</p>
</section>
);
};
export default ProductDetail;
이후 추출한 경로 파라미터 값으로 렌더링될 정보를 결정할 수 있습니다.
"중첩된 라우팅"이란 활성화된 페이지 컴포넌트 내에서 Route
를 중첩하여 상위 Route
와 하위 Route
를 "동시에 활성화" 시켜주는 것입니다.
즉, 하나의 Route
만 활성화하는 것이 아니라 "여러 Route
를 동시에 활성화"하는 것입니다.
기본적으로 Routes
컴포넌트 내부에 작성된 Route
컴포넌트 중 현재 URL의 경로값과 path 어트리뷰트값이 일치하는 하나의 Route
만 활성화 되기 때문에 만약 "여러 Route
를 활성화"하고 싶다면 중첩 라우팅을 사용해야 합니다.
<Routes>
// "/경로a"와 "/경로a/경로b"일 때 활성화
<Route path="/경로a" element={<ComponentA />}>
// Route 컴포넌트 중첩 작성
// "/경로a/경로b"일 때 활성화
<Route path="경로b" element={<ComponentB />} />
</Route>
,,,,
</Routes>
위 코드처럼 Route
컴포넌트 자식으로 Route
컴포넌트를 작성하는 방식으로 중첩 라우팅을 구현할 수 있습니다.
이때 중첩된 Route
의 path 어트리뷰트 값으로는 상위 Route
의 path 어트리뷰트 값을 기준 경로로 갖는 상대 경로를 작성해주어야 합니다.
위 코드에서 URL 경로가 "/경로a"일 때는 path 어트리뷰트 값이 "/a"인 Route
가 활성화되고, URL 경로가 "/경로a/경로b"일 때는 path 어트리뷰트 값이 "/경로a"와 "/경로a/경로b"인 두 개의 Route
가 모두 활성화됩니다.
중첩된 Route
의 element 어트리뷰트에 작성된 페이지 컴포넌트가 렌더링될 위치는 상위 Route
의 element 어트리뷰트에 작성된 페이지 컴포넌트가 결정합니다.
상위 페이지 컴포넌트 내에서 하위 페이지 컴포넌트가 렌더링될 위치에 Outlet
컴포넌트를 작성하면 해당 위치에 하위 페이지 컴포넌트가 렌더링됩니다.
import { Outlet } from 'react-router-dom';
const ComponentA = () => {
,,,
return (
<>
,,,
<Outlet /> // -> ComponentB가 렌더링될 위치
,,,
</>
);
}
위 코드처럼 상위 페이지 컴포넌트인 ComponentA 내에서 하위 페이지 컴포넌트인 ComponentB가 렌더링될 위치를 Outlet
컴포넌트로 지정합니다.
중첩 라우팅을 하는 다른 방법도 존재합니다.
<Routes>
<Route path="/경로a/*" element={<ComponentA />} />
</Routes>
상위 역할을 하는 Route
컴포넌트의 path 어트리뷰트 값 끝에 반드시 "/ *"을 작성해야 합니다.
"/ *"를 작성하게 되면 react router가 해당 Route
의 element 어트리뷰트에 작성된 페이지 컴포넌트 내에서 추가적으로 활성화될 Route
를 탐색합니다.
const ComponentA = () => {
,,,
return (
<>
,,,
<Routes>
// "/경로a/경로b" 일 때 활성화
<Route path="경로b" element={<ComponentB />} />
</Routes>
,,,
</>
);
};
위 코드처럼 상위 페이지 컴포넌트 역할을 하는 ComponentA 내에서 Route
컴포넌트를 작성하는 방식으로 중첩 라우팅을 구현할 수 있습니다.
즉, 페이지 컴포넌트 내에서 Route
를 중첩 작성 방식입니다.
URL 경로값이 "/경로a/경로b"가 되면 하위 페이지 컴포넌트인 ComponentB는 해당 Route
위치에 그대로 렌더링됩니다.
주의할 점으로 내부에 중첩되어 작성된 Route
의 path 어트리뷰트 값은 상위 Route
의 path 어트리뷰트 값을 기준 경로로 갖는 상대 경로를 작성해주어야 합니다.
중첩 라우팅으로 특정 URL 경로에서만 보여질 컴포넌트를 설정할 수도 있습니다.
// Products.js
// 현재 URL 경로 "/products"
import { Routes, Route } from 'react-router-dom';
const Products = () => {
return (
<>
<h1>Products Page</h1>
// 중첩 라우팅
<Routes>
// 경로가 "/products"일 때만 렌더링
<Route path="" element={<p>only products page</ p>} />
// "/products/무엇이든"일 때 렌더링
<Route path="m1" element={<Book />} />
<Route path="m2" element={<Car />} />
<Route path="m3" element={<Computer />} />
</Routes>
</>
);
};
export default Products;
only products page라는 문구는 URL 경로가 "/products"일 때만 표시되며, 이외 경우 표시되지 않게 됩니다.
즉, 중첩된 Route
의 path 어트리뷰트 값을 통해 페이지 컴포넌트가 아닌 일반 컴포넌트를 URL 경로값에 따라 조건적으로 렌더링할 수도 있습니다.
react-router-dom v5에서 사용하던 Redirect
컴포넌트는 v6에서 Navigate
컴포넌트를 사용합니다.
Navigate
컴포넌트를 사용하여 "URL 경로를 임의로 변경"하여 보여질 UI(컴포넌트)를 지정할 수 있습니다.
import { Route, Navigate } from 'react-router-dom';
<Route
path="/경로"
element={<Navigate replace to="/변경될경로" />}
/>
URL의 경로가 Route
의 path 어트리뷰트 값과 일치한다면 element 어트리뷰트에 작성된 Navigate
컴포넌트의 to 어트리튜브에 작성된 경로로 변경합니다.
replace 어트리뷰트 작성시 변경된 경로값은 History stack의 기존 경로를 replace하게 됩니다.
import { Routes, Route, Redirect } from 'react-router-dom';
<Routes>
// 초기 경로인 "/"를 "/welcome"으로 변경
<Route path="/" element={<Navigate replace to="/welcome" />} />
<Route path="/welcome" element={<Welcome />} />
<Route path="/products" element={<Products />} />
<Route path="/products/:productid" element={<ProductDetail />} />
</Routes>
페이지 초기에 갖게되는 URL 경로인 "/" 접속시 바로 경로값을 "/welcome"으로 변경합니다. 즉, 초기 URL 경로값으로 "/welcome"을 갖도록 설정합니다.
즉, 초기 URL이 "/welcome"으로 설정되며, path 어트리뷰트 값이 "/welcome"인 Route
가 활성화 되어 Welcome 컴포넌트가 렌더링됩니다.
Navigate
컴포넌트를 이용하여 Not Found Page를 구현할 수도 있습니다.
import { Routes, Route, Navigate } from 'react-router-dom;
cosnt App = () => {
return (
<Routes>
,,,
// 가장 마지막에 Not found Page 구현 가능
<Route path="*" element={<Navigate replace to="/welcome" />} />
</Routes>
);
};
export default App;
Routes
컴포넌트 자식으로 작성된 Route
컴포넌트들은 위에서부터 아래로 현재 URL 경로값와 일치하는 Route
컴포넌트를 활성화 시킵니다.
이때 모든 Route
컴포넌트의 path 어트리뷰트 값와 일치하지 않는다면 위 코드처럼 Routes
컴포넌트 가장 마지막 자식 컴포넌트로 "path 어트리뷰트에 *
"을 작성하여 어떤 경로값과도 매칭되는 Route
컴포넌트를 작성하고 element 어트리뷰트에 Not Found Page를 나타내는 컴포넌트를 작성하여 구현할 수 있습니다.
Navigate
컴포넌트를 통해서 원하는 URL로 임의로 변경하도록 작성할 수도 있습니다.
즉, " * " 경로값은 모든 경로와 매칭됩니다. 앞에 작성한 모든 Route
의 path 어트리뷰트의 경로와 일치하지 않는다면 마지막에 작성된 path 어트리뷰트 값이 " * "인 Route
를 활성화시킵니다.
react-router-dom의 useNavigate
훅을 사용하여 현재 경로를 변경하는 로직을 작성할 수 있습니다.
react-router-dom v5까지는 useHistory
라는 훅을 사용했지만 v6 부터는 "useNavigate"라는 훅을 사용합니다.
useNavigate
훅은 함수를 반환합니다. 반환된 함수에는 두 개의 인수를 전달합니다
첫 번째 인수로 변경될 경로를 작성합니다. 변경될 경로값을 문자열로 전달할 수도 있고, 숫자값을 전달할 수도 있습니다.
옵션으로 두 번째 인수로 객체를 전달할 수 있습니다. 객체의 replace 프로퍼티에 true를 작성하면 현재 경로를 첫 번째 인수로 전달한 경로로 대체합니다. 즉, History stack에 push되지 않고 replace로 동작합니다.
아래 예제들은 현재 URL 경로가 "/products/product-detail"
로 가정하겠습니다.
import { useNavigate } from 'react-router-dom';
const navigate = useNavigate();
// "/products/product-detail/home"로 변경
navigate('home');
// "/products/home"로 변경
navigate('/home');
// "/products"로 변경
navigate(-1);
// 앞으로 이동할 경로 존재하지 않음
navigate(1);
첫 번째 인수로 절대 경로와 상대 경로를 전달할 수 있으며, 이 둘은 경로값을 변경하는 동작이 서로 다릅니다.
절대경로, 즉 "/경로값" 형태인 경우 URL 경로값을 인수로 전달된 값으로 경로를 변경합니다.
상대경로, 즉 "경로값"나 "./경로값" 형태인 경우 현재 URL 경로값을 기준 경로로 갖는 상대 경로로 URL 경로를 변경합니다.
두 번째 인수로 전달한 객체의 replace 프로퍼티 값이 true인 History stack의 경로 정보를 인수로 전달된 경로값으로 대체(replace)합니다. 참고로 두 번째 인수는 옵션으로 사용됩니다.
react-router-dom v6부터는 Prompt
컴포넌트를 지원하고 있지는 않지만 언젠간 추가될 것이라는 Github issue의 댓글이 존재하기 때문에 react-router-dom v5에서의 Prompt
컴포넌트의 사용법을 설명하겠습니다.
Prompt
컴포넌트는 react-router-dom에서 제공하는 컴포넌트로 경로가 변경되기 직전에 경고를 제공해줍니다. 즉, 경로를 변경하기 직전에 경고창을 사용자에게 제공하고 정말로 경로를 변경할 것인지 한 번 더 확인하는 것입니다.
이는 어떤 데이터를 제출하는 양식과 관련된 페이지에서 양식을 작성하다 사용자의 실수로 해당 경로를 벗어나는 경우가 존재할 수 있습니다. 만약 어떠한 경고도 해주지 않고 바로 해당 경로를 벗어나게 된다면 사용자가 작성하던 모든 데이터가 사라지게 됩니다. 이때 사용자에게 정말로 경로를 변경할 것인지 물어보도록 하는 작업을 하기 위해서 Prompt
컴포넌트를 사용할 수 있습니다.
// react-router-dom v5
import { Prompt } from 'react-router-dom';
<Prompt
when={불리언값}
message={location => { return '경고메세지';} }
/>
Prompt
컴포넌트는 when 어트리뷰트의 값이 true일 때만 해당 경로를 벗어나려고 할 때 경고창을 띄어줍니다. when이 false라면 해당 경로를 벗어난다고 해도 경고창을 띄우지 않습니다.
message 어트리뷰트에는 함수를 전달하는데 이때 함수는 location이라는 객체를 전달받고, 이 함수의 반환값으로 작성된 문자열을 경고창의 메세지로 띄웁니다.
쿼리 스트링는 URL의 "경로값 다음에"에 작성됩니다. URL에서 ?(물음표) 뒤에 매개변수와 값 쌍이 &로 구분되어 전달되는 것이 쿼리 스트링으로 이는 로드된 페이지에 추가적이 데이터를 전달해주는 역할을 합니다.
즉, 쿼리 스트링의 경우 Route
매칭에 영향을 주지는 않으며 로드된 페이지의 "추가적인 정보"를 제공하기 위해서사용합니다.
useNavigate
훅이 반환하는 함수를 통해서 URL을 임의로 변경할 수 있었습니다. 이를 통해 쿼리 스트링도 작성할 수 있습니다.
// 현재 URL 경로 "/"
const navigate = useNavigate();
// 경로 가장 마지막 부분에 인수로 전달한 값을 추가
// "/key=value&key2=value2"로 변경
navigate('key=value&key2=value2');
? 뒤에 작성된 key와 key2가 쿼리 파라미터(매개변수)가 되고, value와 value2가 각 쿼리 파리미터에 할당되는 값이 됩니다.
이렇게 useNavigate
훅이 반환한 함수를 통해 쿼리 파라미터와 값을 추가할 수 있습니다.
쿼리 파라미터가 갖는 값을 통해서 페이지 로드시 추가적인 동작을 하도록 하기 위해서는 쿼리 파라미터 값을 추출할 필요가 있습니다.
우리는 react-router-dom의 "useLocation
이라는 훅을 사용하여 현재 페이지의 정보를 갖고 있는 객체(location)"를 사용할 수 있습니다.
import { useLoaction } from 'react-router-dom';
const location = useLoaction();
useLocation
이 반환하는 location 객체는 URL이 변경될 때마다 실행되어 새로운 location 객체를 반환합니다.
useLocation
이 반환하는 객체는 현재 URL의 정보를 제공하는 객체입니다.
현재 URL 경로가 아래와 같을 때 Location 객체의 프로퍼티는 다음과 같습니다.
/products/product-detial?sort=asc&id=p1#shoes
pathname: 현재 URL의 경로 부분(쿼리 파라미터 부분 제외한)
ex) "/products/product-detail"
search: ?를 포함한 쿼리 스트링부분
ex) "?sort=asc&id=p1"
hash: URL의 #을 포함한 해시값
ex) "#shoes"
key : location 객체의 식별값
ex)"zwgcnjl3"
우리는 useLocation
훅이 반환한 location 객체의 search 파라미터를 통해서 쿼리 스트링 값을 추출할 수 있습니다.
가져온 쿼리 스트링 값은 문자열 형태이기 때문에 사용함에 있어서 불편함이 존재합니다. 그래서 우리는 "URLSearchParams
라는 생성자 함수"를 사용여 쿼리 스트링을 객체로 변환할 수 있습니다. URLSearchParams
는 Web API로서 제공되는 함수입니다.
URLSearchParams
함수의 인수로 쿼리 스트링 문자열 값을 전달해주면 객체가 반환됩니다. 이때 객체의 프로퍼티 키로 쿼리 파라미터 이름이 설정되고, 파라미터 값이 프로퍼티 값으로 설정됩니다.
즉, asc가 프로퍼티 키로 존재하고 그 값으로 asc가 존재합니다. 그리고 id라는 프로퍼티 키가 존재하고 그 값으로 p1이 존재하게 됩니다.
주의할 점으로 이때 프로퍼티 값을 그냥 참조하여 가져올 수 없고 생성된 객체로 "get
메서드"를 호출해야 합니다. 이때 인수로는 가져올 프로퍼티 값에 대응되는 프로퍼티 키를 인수로 전달해줍니다.
import { useLocation } from 'react-router-dom';
const loaction = useLoaciton();
const queryParams = new URLSearchParams(location.search);
queryParams.get('sort'); // -> 'asc'
queryParams.get('id'); // -> 'p1'