페이지 이동
1. Link
React Router에서 제공하는 Link 컴포넌트에 대해 알아보도록 하겠습니다. Link 컴포넌트는 클릭하면 다른 경로로 이동하는 링크를 생성합니다. 이때 주소는 바뀌지만, 페이지를 새로 불러오진 않습니다.
import { createBrowserRouter, RouterProvider, Link } from 'react-router-dom';
const router = createBrowserRouter([
{
path: '/',
element: <Index />,
},
{
path: '/one',
element: <One />,
},
{
path: '/two',
element: <Two />,
},
{
path: '/three',
element: <Three />,
},
]);
function App() {
return <RouterProvider router={router} />;
}
function Index() {
return (
<div>
<nav>
<Link to="/">home</Link>
<Link to="/one">one</Link>
<Link to="/two">two</Link>
<Link to="/three">three</Link>
</nav>
<h1>home</h1>
</div>
);
}
function One() {
return (
<div>
<nav>
<Link to="/">home</Link>
<Link to="/one">one</Link>
<Link to="/two">two</Link>
<Link to="/three">three</Link>
</nav>
<h1>one</h1>
</div>
);
}
function Two() {
return (
<div>
<nav>
<Link to="/">home</Link>
<Link to="/one">one</Link>
<Link to="/two">two</Link>
<Link to="/three">three</Link>
</nav>
<h1>two</h1>
</div>
);
}
function Three() {
return (
<div>
<nav>
<Link to="/">home</Link>
<Link to="/one">one</Link>
<Link to="/two">two</Link>
<Link to="/three">three</Link>
</nav>
<h1>three</h1>
</div>
);
}
export default App;
Link 컴포넌트를 작성하면 화면에서 링크 요소를 볼 수 있습니다. 이는 HTML의 a 태그와 외관상 똑같아 보이지만 a 태그와 Link 컴포넌트는 동작 방식이 다릅니다.
- a 태그: 클릭 시 href 속성에 작성된 경로로 이동하면서 동시에 페이지를 새로 불러오기 때문에 새로고침이 됩니다. 이로 인해 속도가 느려질 수 있습니다.
- Link 컴포넌트:
HTML5 History API를 사용해서 브라우저의 주소를 바꿔주기 때문에 페이지가 새로고침 되지 않습니다. 대신 필요한 DOM 요소만 변경하여 화면의 일부분만 업데이트합니다. 따라서 a 태그보다 빠른 속도와 부드러운 사용자 경험을 제공합니다.
이러한 이유로 React Router를 사용할 때는 일반적인 페이지 내부 이동에 a 태그 대신 Link 컴포넌트를 사용합니다.
HTML5 History API란?
브라우저의 세션 기록(방문 기록)을 JavaScript로 조작할 수 있게 해주는 웹 API입니다. pushState()와 replaceState() 메서드를 통해 페이지를 새로 불러오지 않고도 URL을 변경할 수 있으며, 이를 통해 SPA에서 부드러운 페이지 전환이 가능해집니다.
History API - Web APIs | MDN1.1 to 속성
to 속성은 접근할 경로를 지정해줍니다. 이때 경로는 상대 경로 혹은 절대 경로로 지정해줄 수 있습니다.
[폴더 구조 예시]
src/
├── pages/
│ ├── Home.jsx
│ ├── About.jsx
│ └── products/
│ ├── index.jsx
│ ├── ProductDetail.jsx
│ └── Category.jsx
├── components/
│ └── Navigation.jsx
└── App.jsx
- 상대 경로
현재 경로를 기준으로 합니다.
// src/pages/products/ProductDetail.jsx
import { Link } from 'react-router-dom';
function ProductDetail() {
return (
<div>
<h1>Product Detail</h1>
{/* 현재 경로: /products/1 */}
<Link to="..">Products List</Link> {/* /products로 이동 */}
<Link to="../category">Categories</Link> {/* /products/category로 이동 */}
<Link to=".">Current Page</Link> {/* 현재 페이지 새로고침 */}
</div>
);
}
- 절대 경로
최상위 루트(/)를 기준으로 합니다.
// src/components/Navigation.jsx
import { Link } from 'react-router-dom';
function Navigation() {
return (
<nav>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
<Link to="/products">Products</Link>
<Link to="/products/category">Categories</Link>
</nav>
);
}
여기서 /about이 /pages/about이 아닌 이유는 파일 경로와 URL 경로는 다른 개념이기 때문입니다.
- 파일 경로: 프로젝트 내 실제 파일 위치 (
src/pages/About.jsx) - URL 경로: 브라우저 주소창에 표시되는 경로 (
/about)
React Router에서 to="/about"은 파일 시스템이 아니라 라우터에 정의한 경로를 참조합니다.
const router = createBrowserRouter([
{
path: '/about', // ← 이 URL 경로를 참조
element: <About />, // About.jsx 파일이 어디 있든 상관없음
},
]);
즉, pages 폴더는 개발자가 코드를 정리하기 위한 구조일 뿐이고, 사용자에게 노출되는 URL은 라우터 설정에서 자유롭게 정의할 수 있습니다.
1.2 to 속성값 (경로 형태)
to의 속성값으로는 문자열이나 객체 형태로 작성할 수 있습니다.
// 문자열
<Link to="/courses?sort=name&sorting=asc#the-hash" />
// 객체
<Link
to={{
pathname: "/courses",
search: "?sort=name&sorting=asc",
hash: "#the-hash"
}}
state={{ fromDashboard: true }}
/>
객체로 넣어줄 경우 pathname, search, hash 프로퍼티의 값들을 넣어서 객체 형태로 작성합니다.
- pathname : 이동할 경로를 나타내는 문자열입니다.
- search : 쿼리스트링을 나타내는 문자열입니다. 위의 경우 쿼리스트링
?sort=name&sorting=asc이 포함된 경로로 이동합니다. - hash : URL의 해시를 나타내는 문자열입니다.
to={{ pathname: '/about', hash: '#contact' }}와 같이 객체 형태로 전달할 경우/about#contact경로로 이동합니다. - state : 이동할 경로와 함께 전달할 상태 정보입니다. URL에 보이지 않으며
useLocation훅을 통해 접근할 수 있습니다.
쿼리스트링이란?
URL의 뒷부분에 작성하여 서버에 데이터를 전달하는 방법 중 하나로 ?key=value 형태입니다. ?sort=name&sorting=asc처럼 &를 사용하여 여러 데이터를 전달할 수 있습니다.
URL 해시(Hash)란?
URL의 # 기호 뒤에 오는 부분으로, 페이지 내 특정 위치로 이동할 때 사용합니다. 예를 들어 /about#contact로 이동하면 about 페이지의 id="contact" 요소로 스크롤됩니다. 해시 값은 서버로 전송되지 않고 브라우저에서만 처리됩니다.
1.3 search 속성과 state 속성의 차이
search 속성과 state 속성은 모두 정보를 전달하는 데 사용되지만, 그 방식과 용도에 중요한 차이가 있습니다.
- 가시성
- search: URL에 직접 표시됩니다. (?key=value 형태)
- state: URL에 표시되지 않습니다. (내부적으로 관리)
- 데이터 크기
- search: URL 길이 제한으로 인해 작은 양의 데이터만 전달 가능합니다.
- state: 비교적 큰 객체도 전달할 수 있습니다.
- 보안
- search: URL에 노출되므로 민감한 정보를 포함해서는 안 됩니다.
- state: URL에 노출되지 않아 상대적으로 더 안전합니다.
- SEO
- search: 검색 엔진이 인식할 수 있어 SEO에 영향을 줄 수 있습니다.
- state: 검색 엔진에 의해 인식되지 않습니다.
- 사용 사례
- search: 필터링, 정렬, 페이지네이션 등 페이지 상태를 표현할 때 주로 사용합니다.
- state: 페이지 간 전환 시 복잡한 객체나 일시적인 데이터를 전달할 때 사용합니다.
// search 속성 사용 예시
<Link to={{ pathname: '/products', search: '?category=electronics&sort=price' }}>
전자제품 (가격순)
</Link>
// state 속성 사용 예시
<Link
to="/product/123"
state={{ fromPage: 'list', lastScrollPosition: 1500 }}
>
제품 상세 보기
</Link>
2. NavLink
NavLink는 Link와 비슷하지만, 현재 경로와 일치할 때 특별한 스타일이나 클래스를 적용할 수 있습니다. 주로 네비게이션 메뉴에서 현재 활성화된 링크를 표시할 때 사용합니다.
import { createBrowserRouter, RouterProvider, NavLink } from 'react-router-dom';
const router = createBrowserRouter([
{
path: '/',
element: <Layout />,
children: [
{ path: '/', element: <Home /> },
{ path: '/about', element: <About /> },
{ path: '/products', element: <Products /> },
],
},
]);
function Layout() {
return (
<div>
<nav>
<NavLink
to="/"
className={({ isActive }) => (isActive ? 'active' : '')}
>
홈
</NavLink>
<NavLink
to="/about"
className={({ isActive }) => (isActive ? 'active' : '')}
>
소개
</NavLink>
<NavLink
to="/products"
className={({ isActive }) => (isActive ? 'active' : '')}
>
상품
</NavLink>
</nav>
<Outlet />
</div>
);
}
function App() {
return <RouterProvider router={router} />;
}
export default App;
/* CSS */
nav a {
margin-right: 10px;
text-decoration: none;
color: #333;
}
nav a.active {
color: blue;
font-weight: bold;
border-bottom: 2px solid blue;
}
NavLink의 className prop은 함수를 받을 수 있으며, 이 함수는 isActive와 isPending 속성을 가진 객체를 매개변수로 받습니다. isActive는 현재 URL이 해당 NavLink의 to 경로와 일치하면 true가 됩니다. 사용자가 현재 보고 있는 페이지의 링크를 강조 표시할 때 사용합니다. isPending은 해당 경로로 이동 중이지만 아직 완료되지 않았을 때 true가 됩니다. loader로 데이터를 불러오는 동안 "로딩 중" 스타일을 적용할 때 유용합니다.
<NavLink
to="/about"
className={({ isActive, isPending }) =>
isPending ? 'pending' : isActive ? 'active' : ''
}
>
소개
</NavLink>
style prop도 같은 방식으로 사용할 수 있습니다.
<NavLink
to="/about"
style={({ isActive }) => ({
fontWeight: isActive ? 'bold' : 'normal',
color: isActive ? 'blue' : 'black',
})}
>
소개
</NavLink>
3. useNavigate
useNavigate 훅은 프로그래밍 방식으로 페이지를 이동할 때 사용합니다. 버튼 클릭, 폼 제출 후, 특정 조건 만족 시 등 사용자의 링크 클릭 없이 페이지를 이동해야 할 때 유용합니다.
import { useNavigate } from 'react-router-dom';
function LoginForm() {
const navigate = useNavigate();
const handleSubmit = async (e) => {
e.preventDefault();
// 로그인 로직 수행
const success = await login();
if (success) {
// 로그인 성공 시 홈으로 이동
navigate('/');
}
};
return (
<form onSubmit={handleSubmit}>
<input type="text" placeholder="아이디" />
<input type="password" placeholder="비밀번호" />
<button type="submit">로그인</button>
</form>
);
}
3.1 navigate 함수의 사용법
import { useNavigate } from 'react-router-dom';
function ProductDetail() {
const navigate = useNavigate();
return (
<div>
<h1>상품 상세</h1>
{/* 특정 경로로 이동 */}
<button onClick={() => navigate('/products')}>상품 목록으로</button>
{/* 한 페이지 뒤로 가기 */}
<button onClick={() => navigate(-1)}>뒤로 가기</button>
{/* 두 페이지 뒤로 가기 */}
<button onClick={() => navigate(-2)}>두 페이지 뒤로</button>
{/* 앞으로 가기 */}
<button onClick={() => navigate(1)}>앞으로 가기</button>
{/* state와 함께 이동 */}
<button
onClick={() => navigate('/cart', { state: { from: '/products/1' } })}
>
장바구니로 이동
</button>
{/* 현재 기록을 대체하며 이동 (뒤로 가기 시 이전 페이지로 돌아가지 않음) */}
<button onClick={() => navigate('/home', { replace: true })}>
홈으로 이동 (replace)
</button>
</div>
);
}
3.2 navigate 옵션
navigate 함수의 두 번째 인자로 옵션 객체를 전달할 수 있습니다.
navigate('/path', {
replace: true, // 현재 기록을 대체 (기본값: false)
state: { key: 'value' }, // 전달할 상태 정보
});
- replace:
true로 설정하면 현재 히스토리 항목을 대체합니다. 사용자가 뒤로 가기를 눌러도 이전 페이지로 돌아가지 않습니다. 로그인 후 리다이렉트 시 자주 사용됩니다. - state: 이동할 페이지에 전달할 상태 정보입니다. URL에 표시되지 않으며
useLocation훅으로 접근할 수 있습니다.
Link vs useNavigate
- Link/NavLink: 사용자가 클릭하여 이동하는 일반적인 링크에 사용합니다.
- useNavigate: 프로그래밍 방식으로 페이지를 이동해야 할 때 사용합니다. (폼 제출 후, 조건부 이동, 타이머 후 이동 등)
접근성(Accessibility) 측면에서 일반적인 네비게이션에는 Link나 NavLink를 사용하는 것이 좋습니다.