다다의 개발일지 6v6

[Next.js] Next.js에서 <a> 대신 <Link>를 사용하는 이유 ?! - SPA를 위해 본문

Frontend/Next.js

[Next.js] Next.js에서 <a> 대신 <Link>를 사용하는 이유 ?! - SPA를 위해

dev6v6 2025. 3. 1. 19:17

우리가 하고 싶은 건 페이지 전체 리로딩 없이 필요한 콘텐츠만 로딩하는 것인데
이를 위해 Next.js에서 <a> 대신 <Link>를 사용하면 SPA(Single Page Application)처럼 동작할 수 있다

 

우선 <a> 태그를 사용하면 발생하는 문제점!

페이지 전체가 새로고침됨

기본적으로 <a> 태그를 사용하면 브라우저가 새로운 요청을 보내서 페이지 전체를 다시 로드한다.

<a href="/about">About 페이지 이동</a>
  • href="/about"을 클릭하면 서버에 요청을 보내고 페이지가 새로고침됨
  • 방문했던 페이지라도 다시 다운로드하여 렌더링하기 때문에 비효율적

결과적으로

  • 기존 상태(state)가 초기화됨 (예: 입력 폼 내용, 로그인 상태 등)
  • 페이지 전환 속도가 느려짐

 

 <Link> 태그를 사용하면? (Next.js 방식)

1️⃣ 클라이언트 사이드 내비게이션 (CSR) 적용

import Link from "next/link";

<Link href="/about">About 페이지 이동</Link>
  • Next.js의 <Link> 컴포넌트는 페이지 전체 새로고침 없이 필요한 부분만 바꿔서 렌더링
  • 기존 상태(state)가 유지됨! (ex. 입력 폼 내용 유지)
  • 빠른 페이지 이동 가능

2️⃣ 자동 캐싱으로 최적화됨

<Link>를 사용하면 Next.js는 이전에 방문한 페이지를 캐싱해서 다시 방문할 때 서버 요청 없이 즉시 렌더링 가능!

<Link href="/profile">프로필 페이지</Link>
  • 사용자가 /profile을 한 번 방문하면 Next.js가 자동으로 캐싱
  • 다시 /profile을 클릭하면 즉시 로드됨 (서버 요청 X)
  • 네트워크 트래픽 감소 + 성능 최적화!

3️⃣ 미리 로드 (프리페치) 기능 지원

Next.js의 <Link>는 기본적으로 페이지를 미리 로드(Preload) 해서
사용자가 클릭하기 전에 이미 준비된 상태이다

<Link href="/dashboard">대시보드</Link>
  • 사용자가 <Link>를 포함한 페이지를 보면, Next.js가 백그라운드에서 자동으로 /dashboard 페이지를 미리 로드함
  • 사용자가 클릭하면 즉시 페이지 이동 -> 엄청 빠르게 느껴짐

프리페치 기능이 기본적으로 활성화되어 있음 (모바일 환경에서는 자동 비활성화)

  • 미리 로드가 필요 없는 경우 → <Link prefetch={false}> 사용 가능

4️⃣ replace, scroll, shallow 같은 추가 기능 지원

replace (기존 히스토리를 대체하고 뒤로 가기 불가능하게 하기)

<Link href="/dashboard" replace>대시보드</Link>
  • 사용자가 뒤로 가기를 눌러도 이전 페이지로 돌아갈 수 없음

scroll (페이지 이동 시 스크롤 위치 유지하기)

<Link href="/dashboard" scroll={false}>대시보드</Link>
  • 페이지 이동 후에도 현재 스크롤 위치 유지

shallow (데이터만 변경하고 getServerSideProps, getStaticProps 실행 방지)

<Link href="/profile" shallow>프로필</Link>
  • 같은 페이지에서 URL만 변경하고 서버 요청을 보내지 않음
더보기

shallow 옵션을 언제 사용해야 할까?

 

shallow 옵션을 사용하면 URL만 변경하고 서버 요청을 보내지 않음.
즉, getServerSideProps, getStaticProps가 다시 실행되지 않음불필요한 데이터 요청을 방지할 수 있음!

1️⃣ URL만 변경하고 데이터 요청을 방지하고 싶을 때

예를 들어, ?tab=profile 같은 URL 쿼리 파라미터를 변경할 때
굳이 서버에서 데이터를 다시 불러올 필요가 없으면 shallow를 사용하면 된다!

📌 예제: URL 쿼리 파라미터 변경 (탭 변경)

import { useRouter } from "next/router";

export default function ProfilePage() {
  const router = useRouter();

  return (
    <div>
      <h1>프로필 페이지</h1>
      <button onClick={() => router.push("/profile?tab=posts", undefined, { shallow: true })}>
        게시물 보기
      </button>
      <button onClick={() => router.push("/profile?tab=likes", undefined, { shallow: true })}>
        좋아요 보기
      </button>
    </div>
  );
}

동작 방식

  • ?tab=posts 또는 ?tab=likes로 URL은 변경됨
  • 하지만 서버에 새로운 데이터 요청을 보내지 않음!
  • 클라이언트 측에서 상태(state)만 변경하여 즉시 UI 업데이트 가능

🚀 결과적으로

  • shallow를 사용하면 서버 요청 없이 클라이언트 측에서 UI만 업데이트 가능
  • 불필요한 서버 요청을 줄여 성능 최적화 가능

2️⃣ getServerSideProps 실행을 막고 싶을 때

서버에서 데이터를 가져오는 페이지에서 URL이 바뀔 때마다 불필요한 데이터 요청이 발생할 수 있음.
이때 shallow를 사용하면 서버 요청을 막고 클라이언트에서만 URL을 변경할 수 있다.

 

예제: getServerSideProps가 있는 페이지

export async function getServerSideProps(context) {
  console.log("서버에서 데이터 가져오는 중...");
  return {
    props: { time: new Date().toISOString() },
  };
}

export default function Page({ time }) {
  const router = useRouter();

  return (
    <div>
      <h1>서버에서 가져온 시간: {time}</h1>
      <button onClick={() => router.push("/?page=2", undefined, { shallow: true })}>
        다음 페이지 이동 (shallow)
      </button>
    </div>
  );
}

 

shallow을 사용하면?

  • ?page=2로 이동해도 getServerSideProps가 실행되지 않음!
  • 즉, 서버에서 데이터를 다시 불러오지 않고 클라이언트에서만 URL이 변경됨
  • 따라서 페이지 내 클라이언트 상태(state)를 활용해서 UI를 즉시 업데이트 가능

3️⃣ URL 상태를 활용하는 SPA(Single Page Application)에서 사용

SPA에서는 URL을 상태(state)처럼 활용할 때가 많아.
예를 들어, 모달을 띄울 때 /profile?modal=true처럼 URL을 변경할 수 있음.
하지만 단순히 UI만 변경할 거라면, 서버에 불필요한 요청이 가지 않도록 shallow를 사용하면 좋다

 

📌 예제: 모달 열기/닫기

const router = useRouter();

function openModal() {
  router.push("/profile?modal=true", undefined, { shallow: true });
}

function closeModal() {
  router.push("/profile", undefined, { shallow: true });
}

return (
  <>
    <button onClick={openModal}>모달 열기</button>
    {router.query.modal && (
      <div className="modal">
        <p>모달 창</p>
        <button onClick={closeModal}>닫기</button>
      </div>
    )}
  </>
);

shallow을 사용하면?

  • /profile?modal=true로 변경해도 서버 요청 없이 모달만 UI에서 렌더링됨
  • 서버 부하를 줄이고, 즉각적인 UI 업데이트 가능!
URL 쿼리 파라미터 변경 (예: ?tab=posts) 서버 요청 없이 클라이언트에서 UI 변경
getServerSideProps 실행 방지 서버 요청을 줄이고, 기존 데이터 유지
SPA에서 모달/필터 적용 (예: ?modal=true) UI만 즉시 변경하고 서버 요청 방지

 

 

❌ 과거 방법 (Next.js에서 비효율적인 방식)

<Link href="/about">
  <a>About 페이지 이동</a>
</Link>
  • 과거 Next.js에서는 <Link> 내부에 <a> 태그를 감싸는 방식을 사용했지만, 최신 Next.js에서는 필요 없음!
  • <a> 태그를 <Link>로 교체하는 것만으로도 충분

올바른 방법

<Link href="/about">About 페이지 이동</Link>

 

 

 

Next.js 프로젝트에서는 반드시 <Link>를 사용해야 빠르고 최적화된 페이지 이동이 가능하다