본문 바로가기

React

[Next.js] 페이지 이동을 위한 Link와 useRouter(), 그리고 form submit

이번에 페이지 이동을 위해 useRouter() 훅을 자연스럽게 사용하다가, 문득 Vite 같은 환경에서 사용하는 useNavigate() 훅과 비슷하게 쓰고 있지는 않은지 생각하게 되었다. Link 와 같은 방식도 존재하는데 과연 useRouter()를 사용하는 방식이 최선인가에 대한 고민을 하게 되었다.

 

 

고민하게 된 이유는 다음과 같다.

  1. 단순히 페이지 이동을 위한 버튼 하나 때문에 클라이언트 컴포넌트로 선언해야 하는가?
  2. 클라이언트 컴포넌트로 선언함에는 DOM 조작이나 api, 혹은 react 훅을 사용한 상태 관리, 이벤트 처리 등이 근거가 되어야 하는데, 단순히 “로그인하기”와 같은 페이지 링크 버튼은 어떤 조건에도 부합하지 않음.

즉, 어떤 근거도 없이 페이지 이동 = useRouter()로 착각하며 사용했던 것이다.

 

Next.js 에서 페이지를 이동할 수 있는 방법에는 크게 Link 컴포넌트와 useRouter() 훅, 그리고 form submit 을 통한 페이지 이동이 있다.

 

먼저, form submit 방식은 다음과 같다.

<form
  action="/search"
  method="GET"
>
  <input
	type="text"
	name="q"
	placeholder="검색어를 입력해주세요."
  />
  <button type="submit" />
</form>

 

이러한 폼이 있다면, 'Enter' 나 'submit 버튼'을 통해 제출 시 ‘/search?q=(검색어)’로 페이지 이동을 하게 된다. 장점은 굳이 button의 onClick이벤트와 useRouter를 통해 페이지 이동을 하지 않아도 되기에 클라이언트 컴포넌트로 선언할 필요가 없고, js 번들의 사이즈를 줄일 수 있다.

 

 

폼 제출 방식은 다른 두 방식과는 결이 다르니, 남은 두 방식을 비교해보자.

 

Link 컴포넌트의 사용은

import Link from 'next/link';

function Header() {
  return (
    <Link href="/">
      Home
    </Link>
  );
}

export default Header;

 

 

이러한 방식으로 사용될 수 있다.

 

 

다음으로 useRouter() 훅의 사용은

'use client';

import { useRouter } from 'next/navigation';

function NavigateButton() {
  const router = useRouter();

  const handleClick = () => {
    router.push('/home');
  };
  
  ...

  return (
    <button onClick={handleClick}>
      홈
    </button>
  );
}

export default NavigateButton;

 

이런 식으로 사용할 수 있다.

 

 

두 방식을 비교하면, 단지 button 태그등을 Link로 변경하고 이동하는 방식에 약간의 차이만 있는 것처럼 보인다.

특별히 비교가 될만한 부분은 ‘use client’; 이 부분이다.

useRouter는 react의 훅으로 클라이언트 컴포넌트에서 사용되어야 하지만, Link 컴포넌트는 서버 컴포넌트에서 사용된다. 즉, 이 사실만으로도 단순히 페이지 이동을 위한 버튼 등의 구현이 목적이라면 SEO 최적화를 위해서라고 useRouter()를 사용하기 보단 Link 컴포넌트를 사용하는 것이 좋아보인다.

또한, Link 컴포넌트는 연결된 페이지를 미리 prefetching하는 부가적 기능이 있으니 더더욱 이점이 있다.

 

그럼 이번엔 useRouter는 어디에 사용되는지를 알아보면,

주로 동적/조건부 페이지 이동이 필요할 때 사용된다고 할 수 있다.

'use client';

import { useRouter } from 'next/navigation';

function LoginForm() {
  const router = useRouter();
  
  ...

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    
    const loginSuccess = await loginApi(username, password);

    if (loginSuccess) {
      router.push('/manage');
    } else {
      alert('로그인에 실패했습니다. 아이디와 비밀번호를 확인하세요.');
    }
  };
  
  ...
}

export default LoginForm;

 

예를 들면 로그인 성공/실패 여부에 따른 페이지 이동이나, 로그인 유저 권한에 따른 페이지 접근 경로 등 사용자의 동작에 의한 라우팅이 아닌, 코드 내에서 프로그래밍적으로 제어해야 하는 상황에서 주로 사용된다.

 

 

그럼 이제 실사용을 위해 간단히 기준과 특징을 정리해보자.

Link :

  • 단순한 정적 페이지 이동에 적합하다.
  • 클릭만으로 특정 경로로 이동할 때 사용한다.
  • SEO 최적화와 자동 프리패칭 기능을 제공한다.
  • 서버 컴포넌트에서도 바로 사용할 수 있다.

useRouter :

  • 동적이거나 조건부 페이지 이동이 필요할 때 사용한다.
  • 로그인 처리 후 리다이렉트, 권한 검사, 폼 검증 후 이동 등 복잡한 로직과 결합된 이동에 적합하다.
  • 클라이언트 컴포넌트에서만 사용할 수 있다.
  • 프로그래밍적으로 페이지 이동 경로나 히스토리 기록을 제어할 수 있다.