Post

React Router -> Next.js Router: 혼용으로 인한 오류와 마이그레이션 과정

React Router -> Next.js Router: 혼용으로 인한 오류와 마이그레이션 과정

🧭 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 RouterNext.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는 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.pathnameusePathname()
설명브라우저 내장 객체(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 RouterNext.js
라우팅 훅useNavigate()useRouter()
파라미터 읽기useParams()useParams()
쿼리 읽기useSearchParams()useSearchParams()
쿼리 수정setSearchParams()router.push()/replace()
현재 경로window.location.pathnameusePathname()

결론

🔑 핵심 요약

  • 라우팅 변경: useNavigate → useRouter
  • 쿼리 관리: setSearchParams → router.push/replace
  • 경로 접근: window.location.pathname → usePathname()
  • UX 개선: 초기화 버튼 등은 replace: true 활용

END

This post is licensed under CC BY 4.0 by the author.