icon
3장 : App Router 기초

페이지 간 링크 생성

웹 애플리케이션의 본질은 정보와 기능을 제공하고, 사용자가 그 안에서 자유롭게 이동하며 상호작용하는 것입니다. 이를 위해 페이지와 페이지를 연결하는 링크(Link) 는 필수적인 요소입니다. Next.js App Router에서는 페이지 간의 이동을 위해 HTML의 기본 <a> 태그 대신 Next.js가 제공하는 <Link> 컴포넌트를 사용하는 것을 강력히 권장합니다.

이 절에서는 <Link> 컴포넌트의 사용법과 그 이점, 그리고 실제 애플리케이션에서 링크를 효율적으로 관리하는 방법에 대해 자세히 알아보겠습니다.


<a> 태그 대신 <Link>를 사용하는 이유

일반적인 HTML의 <a> 태그를 사용하여 페이지를 이동할 경우, 브라우저는 해당 페이지를 처음부터 다시 로드합니다. 이는 전통적인 웹사이트에서는 일반적이었지만, React와 같은 SPA(Single Page Application) 프레임워크 기반의 애플리케이션에서는 전체 페이지가 깜빡이거나 새로고침되는 듯한 사용자 경험을 제공하게 됩니다.

반면, Next.js의 <Link> 컴포넌트를 사용하면 다음과 같은 중요한 이점을 얻을 수 있습니다.

  • 클라이언트 사이드 탐색 (Client-side Navigation): <Link> 컴포넌트는 페이지 전체를 새로 로드하는 대신, JavaScript를 사용하여 필요한 부분만 업데이트합니다. 이는 마치 데스크톱 애플리케이션처럼 부드럽고 빠른 페이지 전환을 가능하게 합니다. 브라우저의 새로고침 현상 없이 콘텐츠만 바뀌는 것을 경험할 수 있습니다.

  • 코드 스플리팅 및 프리페칭 (Code Splitting and Pre-fetching): Next.js는 <Link> 컴포넌트를 사용하여 페이지를 연결할 때, 해당 페이지에 필요한 JavaScript 코드를 자동으로 분할(Code Splitting)합니다. 그리고 더 나아가, 사용자가 링크를 클릭하기도 전에 해당 링크 대상 페이지의 코드를 미리 로드(Pre-fetching)합니다. 이는 사용자가 링크를 클릭했을 때 페이지 로딩 시간을 거의 느끼지 못하게 하여 사용자 경험을 획기적으로 향상시킵니다.

  • 검색 엔진 최적화 (SEO) 이점: <Link> 컴포넌트는 여전히 내부적으로 <a> 태그로 렌더링되므로, 검색 엔진 크롤러가 웹사이트의 구조를 잘 파악하고 색인화하는 데 문제가 없습니다.


<Link> 컴포넌트를 사용하는 방법은 매우 간단합니다.

  1. 'next/link'에서 임포트: 링크를 사용하고자 하는 컴포넌트 파일 상단에 Link 컴포넌트를 임포트합니다.

    import Link from 'next/link';
  2. href prop 지정: <Link> 컴포넌트의 href prop에 이동하고자 하는 경로를 문자열로 지정합니다. 이 경로는 App Router의 파일 시스템 기반 라우트와 일치해야 합니다.

  3. <a> 태그를 자식으로 사용: Next.js 13 이상부터는 <Link> 컴포넌트의 자식으로 반드시 HTML <a> 태그를 포함해야 합니다. <a> 태그 내부에 링크 텍스트나 다른 React 컴포넌트를 배치합니다.

    src/app/page.tsx
    // src/app/page.tsx (예시)
    
    import Link from 'next/link';
    
    export default function HomePage() {
      return (
        <div>
          <h1>환영합니다!</h1>
          <p>이곳은 저희 웹사이트의 홈 페이지입니다.</p>
          <nav>
            <ul>
              <li>
                <Link href="/about">
                  {/* Link 컴포넌트의 자식으로 <a> 태그를 사용 */}
                  <a>회사 소개 페이지로 이동</a>
                </Link>
              </li>
              <li>
                <Link href="/dashboard">
                  <button>대시보드 보러 가기</button>
                </Link>
              </li>
              <li>
                <Link href="/blog/my-first-post">
                  자세한 게시글 보기 (동적 라우트)
                </Link>
              </li>
            </ul>
          </nav>
        </div>
      );
    }

    코드 설명

    • 첫 번째 <li>에서는 <a> 태그 안에 텍스트를 직접 넣어 링크를 만들었습니다.
    • 두 번째 <li>에서는 <button> 컴포넌트를 <a> 태그 안에 넣어 버튼 클릭 시 이동하도록 했습니다. <a> 태그는 단순히 링크의 역할을 하며, 실제 UI는 button이 담당합니다.
    • 세 번째 <li>는 동적 라우트의 예시입니다. 나중에 자세히 다루겠지만, href에 동적인 경로를 지정할 수 있습니다.

[slug] 또는 [id]와 같이 대괄호로 정의된 동적 라우트 세그먼트를 가진 페이지로 이동할 때도 <Link> 컴포넌트를 사용합니다. 이때 href 속성에는 실제 경로를 문자열로 전달합니다.

예를 들어 src/app/products/[id]/page.tsx라는 동적 라우트가 있다면, 특정 상품 페이지로 이동하는 링크는 다음과 같이 작성할 수 있습니다.

// 특정 상품 목록 페이지에서
import Link from 'next/link';

function ProductList() {
  const products = [
    { id: 'p001', name: '노트북' },
    { id: 'p002', name: '마우스' },
  ];

  return (
    <div>
      <h1>상품 목록</h1>
      <ul>
        {products.map(product => (
          <li key={product.id}>
            <Link href={`/products/${product.id}`}>
              <a>{product.name} 상세 보기</a>
            </Link>
          </li>
        ))}
      </ul>
    </div>
  );
}

보시는 것처럼, JavaScript의 템플릿 리터럴(Template Literal)을 사용하여 동적인 값을 href에 쉽게 삽입할 수 있습니다.


<Link> 컴포넌트는 href 외에도 몇 가지 유용한 prop을 제공합니다.

  • replace: true로 설정하면 현재 히스토리 스택의 항목을 새 항목으로 교체합니다. 브라우저의 뒤로 가기 버튼을 눌렀을 때 해당 페이지로 돌아오지 않게 하고 싶을 때 유용합니다. (기본값: false)

    <Link href="/dashboard" replace>
      <a>대시보드로 이동 (뒤로 가기 방지)</a>
    </Link>
  • scroll: false로 설정하면 새 경로로 이동할 때 스크롤 위치를 맨 위로 올리지 않습니다. (기본값: true) 특정 섹션으로 이동하거나, 스크롤 위치를 유지하고 싶을 때 사용합니다.

    <Link href="/products#section-a" scroll={false}>
      <a>상품 페이지 특정 섹션으로 이동 (스크롤 유지)</a>
    </Link>
  • prefetch: false로 설정하면 해당 링크의 프리페칭 기능을 비활성화합니다. (기본값: true) 매우 많은 링크가 한 페이지에 존재하여 네트워크 부하가 우려될 때 고려해볼 수 있습니다.

    <Link href="/heavy-page" prefetch={false}>
      <a>무거운 페이지로 이동 (미리 로딩 안 함)</a>
    </Link>

useRouter 훅 (클라이언트 컴포넌트에서)

<Link> 컴포넌트는 선언적으로 페이지 이동을 처리하기에 가장 좋은 방법입니다. 하지만 특정 이벤트(예: 폼 제출 후)에 따라 프로그래밍 방식으로 페이지를 이동해야 할 때는 Next.js가 제공하는 useRouter 훅을 사용할 수 있습니다. useRouter는 클라이언트 컴포넌트에서만 사용할 수 있습니다.

// src/app/dashboard/client-button.tsx
"use client"; // 클라이언트 컴포넌트임을 명시

import { useRouter } from 'next/navigation'; // useRouter 임포트

export default function ClientButton() {
  const router = useRouter(); // useRouter 훅 사용

  const handleClick = () => {
    // 버튼 클릭 시 프로그래밍 방식으로 /dashboard/settings 페이지로 이동
    router.push('/dashboard/settings');
  };

  return (
    <button onClick={handleClick}>
      설정 페이지로 이동 (useRouter)
    </button>
  );
}

useRouter 훅은 push, replace, refresh, back 등 다양한 메서드를 제공하여 라우팅을 세밀하게 제어할 수 있게 해줍니다. 이 훅에 대한 더 자세한 내용은 뒤에서 다룰 예정입니다.

페이지 간 링크를 올바르게 생성하고 활용하는 것은 사용자 경험과 애플리케이션 성능에 직접적인 영향을 미칩니다. <Link> 컴포넌트의 이점을 이해하고 적극적으로 활용한다면, 여러분의 Next.js 애플리케이션은 더욱 빠르고 매끄러운 사용성을 제공할 수 있을 것입니다.