Next/Link prefetch 적용하기

D uuu·2024년 7월 18일
0

Next.js

목록 보기
2/5

서비스를 배포한 후 여러 피드백을 받았는데, 그 중 공통적인 문제는 처음 반응 속도가 느리다는 점이었다.
자체적으로 테스트해본 결과, 확실히 간혈적으로 페이지 이동할때 속도가 느려지는 현상이 있었고 사용자 경험에 부정적인 영향을 미칠 수 있기에 성능 개선이 반드시 필요했다!!

이 과정에서 Next.js의 Link 태그와 사전 로드(prefetch) 기능에 대해 알게 되었고, 이를 정리해보고자 한다.

문제 부분을 영상으로 찍어서 보내준 친구😎

문제 원인 파악하기

기존 코드

기존 코드를 보면 음식점을 클릭하면 ⭐️next/navitagion 의 useRouter 메서드를 이용해 해당 음식점 정보를 받아오는 페이지로 이동시킨다. 즉, 사용자가 클릭한 후에야 fetch 가 이루어져 네트워크 상황에 따라 데이터 로딩 시간이 길어질 수 있는 구조였다.

import { useRouter } from 'next/navigation';

const router = useRouter();

// 생략

<div onClick={() => router.push(`/${res.id}`)}>{res.placeName}</div>

Next/Link는 Next.js에서 제공하는 컴포넌트로, 클라이언트 사이드에서 페이지 간 네비게이션을 구현할 때 사용된다. 이 컴포넌트를 사용하면 페이지 간 전환이 빠르고, 브라우저 전체 리로드 없이 페이지 내용만 업데이트할 수 있다. 또한 <a> 태그를 사용하므로 검색 엔진이 페이지 구조를 이해하는데도 도움이 된다고 한다!

prefetch

  • Next/Link 는 prefetch 속성을 지원하는데, 사용자가 링크를 클릭하기 전에 해당 페이지의 데이터를 미리 가져오는 역할을 한다.
  • Next.js는 기본적으로 모든 페이지에 대해 자동 prefetching 을 수행하기 때문에 만약 이 기능을 제어하고 싶다면 prefetch={false} 로 주면 된다.
  • prefetching 은 프로덕션 환경에서만 활성화 된다. 즉, 로컬 환경에서는 확인이 안된다는 말씀!
import Link from 'next/link';

const HomePage = () => (
  <div>
  // prefetch 를 사용하고 싶지 않다면 false 로 주면 된다.
    <Link href="/about" prefetch={false}>
      Go to About Page
    </Link>
  </div>
);

export default HomePage;

Link 태그의 prefetch 적용 후, 배포해서 네트워크 탭을 열어봤다.

스크롤을 내릴 때 뷰포인트 안에 링크가 들어오면, 백그라운드에서 자동으로 fetch 요청이 발생하는 걸 확인할 수 있었다. 덕분에 버튼 클릭 시 데이터를 빠르게 가져올 수 있었다!

prefetch 를 통해 받아오는 것들

근데 네트워크 탭을 보니 prefetch 요청이 발생했지만, 응답 데이터가 비어 있었다. 데이터를 분명 미리 가져온다고 했는데 아무것도 없는 것이다.

자세히 찾아보니까 prefetch 는 정적 자료(JS, CSS)만 미리 가져오며 데이터를 미리 패칭하지는 않는다고 한다. 즉, 데이터를 미리 호출해 받아오는 것이 아니라는 점이다. 데이터 호출은 실제로 클릭했을때 이루어진다!

그럼 도대체 어떻게 동작하는 걸까 궁금해졌다.

확실히 prefetch 기능을 사용하니까 응답 속도가 빨라졌는데, 그 전과 비교했을때 어떤 차이가 있는 건가 싶어서 prefetch 사용 전&후 네트워크 탭을 비교해봤다. 초기 다운로드 받는 js 갯수가 확연하게 차이가 났다.

prefetch 사용 전

prefetch 사용 후

prefetch 사용 전과 비교해봤을때 없던 파일들이 몇개 생겼는데, 그 중 page-698eb9a47bd11e4a.js 파일을 열어보니 경로가 /(detail)/'%5Bid%5D' 으로 이 부분이 다이나믹 라우트를 나타내며 %5Bid%5D 는 URL 인코딩된 [id] 를 의미하는 것 같다. (prefetch 적용한 페이지)

이 파일이 해당 다이나믹 라우트를 렌더링하기 위한 JS 파일을 담고 있는 것 같았다.

어떻게 동작 하는지 정확하게 이해는 어려웠으나, ssr 에서 prefetch 를 사용하면 미리 해당 페이지를 렌더링 하기 위한 JS 파일을 다운 받는다.
prefetch로 다운로드된 JS 파일은 클라이언트 측에서 페이지를 재구성하기 위한 코드와 데이터를 포함하고 있으며, 실제로 사용자가 해당 페이지를 방문했을 때 데이터 패칭을 통해 완전한 페이지를 구성한다. 따라서 prefetch 를 사용했을때 클라이언트 측에서 보다 빠르게 페이지를 재구성(hydration) 할 수 있어서 응답 속도가 개선 된다고 이해를 했다.

정확하게 이해하기에는 어렵다.. 근데 두 네트워크 탭을 비교해보면 prefetch 적용한 페이지에 대한 정적 파일이 미리 로드된 것을 볼 수 있었다.

이해를 돕기 위한 정리

1. Prefetching 작동 방식

Prefetching 은 사용자 경험을 개선하기 위해 필요한 데이터를 미리 가져오는 과정이다.

미리 다운로드된 JS 파일: 예를 들어 %5Bid%5D/page-698eb9a47bd11e4a.js와 같은 파일은 동적 라우트([id] 부분)를 포함하는 페이지의 렌더링을 위한 JavaScript 파일입니다. 이 파일은 정적 파일로서, 서버가 미리 준비하여 클라이언트에게 제공합니다. 이 파일은 초기 HTML을 생성하는 데 사용되며, 실제 데이터 패칭은 이후에 이루어집니다.

Hydration을 위한 준비: 클라이언트가 초기 HTML을 받고 JavaScript 파일을 다운로드하면, 이후에 JavaScript가 페이지를 "hydrate(수화)"하여 인터랙티브한 요소들을 활성화하고 데이터를 동적으로 채웁니다. 이 과정에서 미리 다운로드한 JS 파일이 필요합니다.

2. 뷰포인트에서의 Prefetching과 _rsc 쿼리

뷰포인트에 새로운 링크가 보일 때의 동작: 사용자가 스크롤하여 새로운 링크(예: <a> 태그)가 뷰포인트에 보이면, 브라우저는 해당 링크의 데이터를 미리 가져오기 위해 prefetching 요청을 보냅니다.

_rsc 쿼리에 대해서는 아직 의문이 있는 부분이 남았다. 에러가 발생하는 것은 아니고 정상적으로 잘 동작은 하는데, 초기에 뷰포인트에 보이는 부분에 있어서는 Network 탭을 확인했을때 분명 _rsc로 요청이 가고 정상적으로 네트워크탭의 preview에 RSC 페이로드가 찍히는가하면 그 이후 새로 생성되는 _rsc 쿼리에는 preview, 응답 부분에 아무런 값도 없다;;

결론은 패칭과 렌더링은 정상적으로 동작하지만 네트워크탭의 preview와 response가 나올 때도 있고 안 나올 때도 있어서, 어떤 이유에서 그런건지 잘 모르겠다. 이 부분은 좀 더 공부해봐야 알 것 같다.

뭔지 잘은 몰라도 prefetch 기능 너무 좋은데??

별다른 설정 없이 Link 태그만 사용해도 next.js 에서 알아서 prefetching 을 해주니 너무 편하다는 생각이 들었다.
그러나 눈에 보이지 않을 뿐 백그라운드에선 계속 API 요청이 나가고 있으니 모든 페이지에 남발해서 사용하면 네트워크 트래픽이 증가할 수 있다는 것을 염두에 둬야 한다.

잠깐, next/navigation 과 어떤 차이가 있지 ?

next/navigation 과 next/Link는 둘다 Next.js에서 페이지 간 네비게이션을 처리할때 사용할 수 있다.
둘 다 페이지 전환을 제공하지만, 사용하는 방식과 기능에는 약간의 차이가 있다.

  • next/navigation 은 훅과 함수 기반 네비게이션으로 주로 이벤트 핸들러 내에서 네비게이션을 처리할때 유용하다.
  • 따라서 사용자 입력에 따라 동적으로 페이지를 전환하는 등 다소 복잡한 로직이 필요할때 사용할 수 있다.
  • 참고로 next/navigation 에도 prefetch 기능을 사용할 수 있다.

    const router = useRouter();
    router.prefetch('/dashboard') // 이런식으로 쓰면 된다.

예시 코드

'use client';

import { useRouter } from 'next/navigation';

const HomePage = () => {
  const router = useRouter();

/// 동적으로 라우팅을 다르게 한다거나 사용자와의 인터렉션이 복잡할때 useRouter 훅을 사용해 네비게이션을 구현할 수 있다.

  const handleClick = (num) => {
  	if (num === 1) {
    	router.push('/main');
    } else {
    	router.push('/about')
  };

  return (
    <div>
      <button onClick={() => handleClick(1)}>Go to Main Page</button>
       <button onClick={() => handleClick(2)}>Go to About Page</button>
    </div>
  );
};

export default HomePage;

정리

Next/Link 컴포넌트를 언제 사용하면 좋을지 알게 되었다.
prefetch 가 어떤 역할을 하는지, 어떤 데이터를 미리 불러오는지 알게 되었다.

profile
배우고 느낀 걸 기록하는 공간

0개의 댓글

관련 채용 정보