🧭 React Router → Next.js 라우터 마이그레이션 배경
기존 렛츠커리어(LET’S CAREER) 서비스는 React 기반으로 개발되었으며, 라우팅은 react-router-dom을 사용하고 있었다.
하지만 Next.js 도입을 통해 SSR(Server Side Rendering) 과 SEO 최적화, 빌드 효율성 개선 등의 이점을 활용하기로 결정하면서, 라우터 또한 Next.js App Router로 마이그레이션이 필요했다.
처음에는 모든 라우터를 한 번에 전환하기에는 프로젝트 볼륨이 너무 커서,
일부 페이지에서는 react-router-dom
과 Next.js 라우터가 혼용된 상태로 운영되고 있었다.
(내가 처음 렛커 합류했을 때 상태 였음)
이로 인해 리다이렉트 과정에서 Application Error
가 발생하는 문제가 나타났고,
이를 근본적으로 해결하기 위해 react-router-dom
의 완전한 제거를 목표로 마이그레이션 작업을 진행하게 되었다.
태훈님이 작성하신 마이그레이션 가이드
🔍 useSearchParams
React Router에서는 보통 이렇게 사용
1
| const [searchParams, setSearchParams] = useSearchParams();
|
Next.js App Router에서는 단순히 호출만으로 읽기 전용 객체를 얻음
1
| const searchParams = useSearchParams();
|
setSearchParams()
를 대체하려면 router.push()
또는 router.replace()
를 사용해야 합니다.
✅ 예시
1
2
3
4
5
| // React Router
setSearchParams({}, { replace: true });
// Next.js
router.replace(window.location.pathname);
|
🔍 useNavigate → useRouter
React Router의 useNavigate
는 Next.js에서 useRouter
로 대체
1
2
3
4
5
| // React Router
import { useNavigate } from 'react-router-dom';
// Next.js
import { useRouter } from 'next/navigation';
|
기능 | React Router | Next.js |
---|
이동 | navigate('/path') | router.push('/path') |
교체 | navigate('/path', { replace: true }) | router.replace('/path') |
💡 replace 옵션의 차이는 뭘까 ?
1️⃣ setSearchParams({})
(기본)
- 히스토리에 새 항목 추가
- 뒤로가기 시 이전 파라미터 상태로 복원 가능
1
2
3
4
| 1. /products?category=electronics
2. setSearchParams({})
3. /products
← 뒤로가기 → /products?category=electronics
|
2️⃣ setSearchParams({}, { replace: true })
- 히스토리 교체 (새 항목 X)
- 뒤로가기 눌러도 이전 파라미터로 돌아가지 않음
1
2
3
4
| 1. /products?category=electronics
2. setSearchParams({}, { replace: true })
3. /products
← 뒤로가기 → (그 이전 페이지)
|
🔍 useParams
React Router에서는 타입 지정 없이 다음과 같이 사용
1
| const { id } = useParams();
|
Next.js에서는 제네릭 타입을 명시할 수 있음
1
| const { id } = useParams<{ id: string }>();
|
🔍 NavLink → Link + usePathname
NavLink
는 Next.js의 <Link>
컴포넌트와 usePathname()
훅을 조합해 대체합니다.
1
2
3
4
5
6
7
8
9
10
11
| import Link from 'next/link';
import { usePathname } from 'next/navigation';
const pathname = usePathname();
<Link
href="/dashboard"
className={pathname === '/dashboard' ? 'active' : ''}
>
Dashboard
</Link>
|
🔍 window.location.pathname vs usePathname()
둘 다 “현재 경로(path)”를 가져오는 역할이지만…
구분 | window.location.pathname | usePathname() |
---|
설명 | 브라우저 내장 객체(window )를 통해 현재 URL의 경로를 가져옴 | Next.js에서 제공하는 라우팅 훅으로, 현재 경로를 React 컴포넌트 안에서 안전하게 가져옴 |
사용 환경 | 브라우저에서만 사용 가능 (클라이언트 전용) | Next.js App Router 전용 (SSR + CSR 모두 안전) |
SSR(Server Side Rendering) | ❌ 서버에는 window 객체가 없어 오류 발생 | ✅ 서버에서도 문제 없이 작동 |
Next.js 라우팅 연동 | ⚠️ 단순 문자열 기반 접근 | ✅ Next.js 라우팅 시스템과 연동되어 최적화 |
타입 지원 (TS) | ❌ 없음 | ✅ TypeScript 완벽 지원 |
추천 여부 | ⚠️ 임시 테스트용, 브라우저 전용 코드에만 사용 | ✅ Next.js 프로젝트에서는 항상 권장 |
💡 언제 usePathname()
을 써야 할까?
✅ Next.js의 페이지 이동, 쿼리 조작, 리다이렉트 등의 상황에서 “현재 경로를 기반으로 새 URL을 만들 때”
사용 예시 (SSR 안전)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| 'use client'
import { useRouter, usePathname } from 'next/navigation';
function SearchForm({ inputs }) {
const router = useRouter();
const pathname = usePathname(); // 현재 페이지 경로를 안전하게 가져옴
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
// 빈 값은 제거하고, 남은 input만 쿼리로 변환
const newSearchParams = Object.fromEntries(
Object.entries(inputs).filter(([, value]) => value),
);
const params = new URLSearchParams(newSearchParams);
// 현재 페이지 경로 + 새로운 쿼리 적용
router.push(`${pathname}?${params.toString()}`);
};
return <form onSubmit={handleSubmit}>...</form>;
}
|
❌ 비권장 예시 (window 사용)
1
2
| // 작동은 하지만 SSR에서 에러 발생 가능
router.push(`${window.location.pathname}?${params.toString()}`);
|
⚠️ window
는 브라우저에만 존재하므로 서버 렌더링 시(getServerSideProps
, app router
) 사용할 경우 ReferenceError: window is not defined 에러가 발생
전체 비교 요약
구분 | React Router | Next.js |
---|
라우팅 훅 | useNavigate() | useRouter() |
파라미터 읽기 | useParams() | useParams() |
쿼리 읽기 | useSearchParams() | useSearchParams() |
쿼리 수정 | setSearchParams() | router.push()/replace() |
현재 경로 | window.location.pathname | usePathname() |
결론
🔑 핵심 요약
- 라우팅 변경:
useNavigate → useRouter
- 쿼리 관리:
setSearchParams → router.push/replace
- 경로 접근:
window.location.pathname → usePathname()
- UX 개선: 초기화 버튼 등은
replace: true
활용
END