Prefetching
사전에(pre-) 미리 불러온다(fetching).
연결되어 있는 페이지들을 사전에 미리 불러온다. 빠른 페이지 이동을 위해 미리 데이터를 로드해둔다.
왜 Prefetching이 필요한가?
생각해보면 이 기능은 뭔가 이상하다. 초기 접속 이후의 활동들은 서버에게 별도 추가 요청 없이 브라우저에서 직접 React 컴포넌트를 교체하는 방식으로 처리한다. 그렇기 때문에 페이지를 이동하더라도 브라우저가 서버에게 추가 리소스를 요구하지 않는 것으로 알고 있는데 prefetching은 왜 있는가?
Code Splitting
Next.js는 모든 JavaScript 코드를 자동으로 페이지별로 분리(splitting)해서 저장한다. 모든 코드가 전달되는 것이 아니라 현재 페이지의 번들만 전달된다. /search로 접속하면 search 페이지의 번들만 전달된다.
이렇게 하는 이유는 파일 크기를 줄이기 위해서다. 모든 페이지의 번들을 한 번에 전달하면 용량이 커져서 하이드레이션이 늦어진다. 그럼 **TTI(Time To Interactive)가 최종적으로 늦어진다.
따라서 페이지를 이동할 때는 해당 페이지의 번들을 새로 요청해야 한다. Prefetching은 페이지 이동 요청 전에 미리 번들을 불러오는 방식이다.
Prefetching 확인하기
개발 모드에서는 작동하지 않는다
npm run dev로 실행하면 prefetching이 작동하지 않는다. 페이지를 이동할 때마다 번들을 매번 불러온다.
Prefetching 동작을 제대로 확인하려면 build 후 production 모드로 실행해야 한다. npm run build를 하면 페이지별로 빌드 결과가 출력된다.
Route (pages) Size First Load JS
┌ ○ / 256 B 82.7 kB
├ /_app 0 B 82.5 kB
├ ○ /404 180 B 82.7 kB
├ ƒ /api/hello 0 B 82.5 kB
├ ○ /book/[id] 300 B 82.8 kB
└ ○ /search 296 B 82.8 kB
+ First Load JS shared by all 82.5 kB
├ chunks/framework-64ad27b21261a9ce.js 44.8 kB
├ chunks/main-a2464d6d00b134e7.js 34 kB
└ other shared chunks (total) 3.61 kB
○ (Static) prerendered as static content
ƒ (Dynamic) server-rendered on demand
위 결과에서 각 페이지별로 번들이 분리(splitting)되어 있는 것을 확인할 수 있다. 각 페이지는 독립적인 크기를 갖고 있으며, 공통 코드는 "shared by all"로 분리되어 있다.
Production 모드에서 확인
Code splitting이 되는 것을 확인했다. npm run start를 하면 localhost:3000에서 네트워크 탭을 열었을 때 많은 요청이 일어난다. search, book에 필요한 페이지들도 모두 prefetching 되어 있다.
브라우저 개발자 도구의 Network 탭을 열고 페이지를 로드하면, 현재 페이지뿐만 아니라 화면에 보이는 Link 컴포넌트가 가리키는 페이지들의 번들도 함께 다운로드된다.
Link 컴포넌트의 자동 Prefetching
Link 컴포넌트로 구현된 링크는 자동으로 prefetching이 이루어진다.
Link 컴포넌트는 기본적으로 viewport에 보이는 링크를 자동으로 prefetch한다. 사용자가 링크를 클릭하기 전에 미리 페이지를 로드해두기 때문에 즉각적인 페이지 전환이 가능하다.
import Link from 'next/link';
export default function Navigation() {
return (
<nav>
{/* 이 링크들은 viewport에 보이면 자동으로 prefetch된다 */}
<Link href="/">홈</Link>
<Link href="/about">소개</Link>
<Link href="/blog">블로그</Link>
</nav>
);
}
이 기능은 프로덕션 빌드에서만 동작하며, 개발 모드에서는 작동하지 않는다.
프로그래매틱 네비게이션의 수동 Prefetching
프로그래매틱하게 구현된 네비게이션은 페이지 이동 시 번들을 다시 요청한다. 여기에서도 prefetching을 하고 싶다면 컴포넌트 마운트 시 router 객체의 메서드를 통해 수동으로 prefetching할 수 있다.
import { useRouter } from 'next/router';
import { useEffect } from 'react';
export default function Page() {
const router = useRouter();
useEffect(() => {
// 컴포넌트 마운트 시 /test 페이지를 미리 로드
router.prefetch('/test');
}, []);
const handleClick = () => {
// 이미 prefetch되어 있으므로 즉시 이동
router.push('/test');
};
return <button onClick={handleClick}>테스트 페이지로 이동</button>;
}
router.prefetch()를 사용하면 특정 페이지의 번들을 미리 로드할 수 있다. 이후 router.push()로 페이지 이동 시 즉각적인 전환이 가능하다.
조건부 Prefetching
특정 조건에서만 prefetch하고 싶을 때도 사용할 수 있다.
import { useRouter } from 'next/router';
import { useEffect } from 'react';
export default function Dashboard({ user }) {
const router = useRouter();
useEffect(() => {
// 관리자일 경우에만 admin 페이지를 prefetch
if (user.isAdmin) {
router.prefetch('/admin');
}
}, [user.isAdmin]);
return <div>대시보드</div>;
}
Prefetching 비활성화
Prefetching을 하고 싶지 않다면 prefetch={false}를 설정한다.
대용량 페이지나 데이터가 자주 변경되는 페이지의 경우 불필요한 네트워크 요청을 줄이고 싶을 수 있다. Link 컴포넌트에서 prefetch={false}를 설정하면 자동 prefetch를 비활성화할 수 있다.
import Link from 'next/link';
export default function Navigation() {
return (
<nav>
{/* 일반 페이지는 prefetch */}
<Link href="/">홈</Link>
{/* 대용량 페이지는 prefetch 비활성화 */}
<Link href="/heavy-page" prefetch={false}>
대용량 페이지
</Link>
{/* 자주 변경되는 페이지도 prefetch 비활성화 */}
<Link href="/live-data" prefetch={false}>
실시간 데이터
</Link>
</nav>
);
}
정리
- Code Splitting: Next.js는 페이지별로 번들을 자동으로 분리한다
- Prefetching: 페이지 이동 전에 미리 번들을 로드하여 빠른 전환을 가능하게 한다
- Link 컴포넌트: viewport에 보이는 링크를 자동으로 prefetch (프로덕션 모드에서만)
- router.prefetch(): 수동으로 특정 페이지를 미리 로드
- prefetch={false}: Link 컴포넌트의 자동 prefetch를 비활성화
- 개발 모드: prefetching이 작동하지 않으므로 테스트는 production 모드에서
참고 자료
- Next.js 공식 문서 - Link prefetching
- [[Navigating]] - 페이지 간 이동 방법