Pre-Fetching은 말 그대로 사전에 페이지를 불러오는 기능이다. 사용자가 현재 보고 있는 페이지에서 링크를 통해 이동할 수 있는 페이지, 쉽게 말하자면, 현재 사용자가 보고있는 페이지 내에서 이동할 가능성이 있는 모든 페이지들을 미리 불러와 빠른 페이지 전환을 가능하게 한다.
Next.js는 페이지 전환을 빠르게 처리하기 위해 Pre-Fetching을 제공한다. 사용자가 현재 페이지에서 다른 페이지로 이동하기 전에, 해당 페이지에 필요한 모든 데이터를 미리 불러와 준비해 둔다. 이를 통해 페이지 이동 시 서버에 추가 요청을 할 필요 없이 빠르게 페이지를 전환할 수 있다.
의문점은, Next의 사전 렌더링
으로 인해, 서버에서 html페이지를 응답한 이후에 후속으로 모든 JS코드를 번들 형식으로 전달해 주기 때문에 결국 이런 초기접속 요청이 종료된 이후에 발생하게 되는 페이지 이동들은 서버에게 별도의 추가적인 요청 없이 브라우저에서 직접 JS코드를 실행시켜서 필요한 컴포넌트들을 교체하는 방식 즉, CSR
방식으로 알아서 처리를 하게 된다라고 했는데 왜 다른 페이지로 이동하기 위해추가적인 JS 코드를 불러와야 할까?
Next.js는 효율적인 성능을 위해 페이지별로 JS 코드를 분리(스플리팅)하여 저장을 미리 해두고 전송한다. 즉, 사용자가 특정 페이지에 접속하면, 해당 페이지에 필요한 JS 코드만 전달되며 다른 페이지의 코드는 전달되지 않는다.
예를 들어, /search
페이지에 접속하면, 이 페이지에 해당하는 JS코드들만 전달이 됨. 이는 초기 페이지 로드 시 불필요한 코드를 전달하지 않음으로써 TTI(Time To Interactive)
시간을 줄이고, 페이지 로딩 속도를 최적화하기 위함이다.
그러나 이런 방식에는 단점도 있다. hydration
은 빨라질 수 있겠지만, 오히려 페이지 이동은 느려지고 비효율적으로 바뀌게 됨. 왜냐, 다른 페이지로 이동할 때마다 해당 페이지의 JS 코드를 새로 불러와야 하므로, 페이지 전환 속도가 느려질 수 있다. 특히 사용자가 페이지 이동을 자주 할 때, 이러한 지연은 성능 저하로 이어질 수 있다.
이 문제를 해결하기 위해 Pre-Fetching
이 사용된다. Pre-Fetching
은 사용자가 현재 보고 있는 페이지에서 링크를 통해 이동할 가능성이 있는 모든 페이지의 JS 코드를 미리 불러오는 과정. 이를 통해 페이지 이동 시 서버에 추가적인 요청을 하지 않고, 즉시 페이지를 전환할 수 있다.
Next.js는 사용자가 페이지에 접속한 후, 곧바로 페이지 이동이 이루어지기 전에, 해당 페이지에서 이동할 수 있는 모든 페이지의 JS 코드를 미리 불러와 준비해 둔다. 예를 들어, /
페이지에서 /search
로 이동할 수 있는 링크가 있다면, Pre-Fetching
을 통해 /search
페이지에 필요한 JS 코드를 미리 로드한다.
그렇기 때문에 페이지를 이동할 때에는 결국 추가적인 데이터를 서버에 요청할 필요가 없어져서 기존처럼 CSR
방식의 장점대로 굉장히 빠른 속도로 페이지를 이동시킬 수 있게 되는 것임.
import Link from "next/link";
export default function App() {
return (
<header>
<Link href="/">Home</Link>
<Link href="/search">Search</Link>
</header>
);
}
이 코드를 사용하면, 브라우저가 /
페이지에서 /search
페이지의 JS 코드를 미리 로드함. 그 결과, 사용자가 /search
링크를 클릭할 때 페이지가 빠르게 전환된다.
만약 Pre-Fetching
이 없다면, 사용자가 페이지 이동을 요청할 때마다 서버에 추가로 JS 코드를 요청해야 합니다. 이렇게 되면 페이지 전환이 느려지고, UX가 저하될 수 있다. 이를 방지하기 위해 Next.js는 Pre-Fetching
을 기본적으로 제공하여 페이지 전환 속도를 극대화함.
Pre-Fetching
은, 최종적으로 초기접속 요청시에 hydration
을 더 빠르게 처리할 수 있게 만들어주면서도 동시에 초기접속 요청 이후에 페이지 이동까지 빠르게 처리할 수 있는 두마리 토끼를 다 잡을 수 있는 그런 방식으로 동작한다.
개발 모드에서는 Pre-Fetching
이 작동하지 않는다. 개발 중에는 각 페이지를 서버에서 매번 새로 불러오기 때문에, 네트워크 탭을 열어보면 페이지 이동 시 JS 코드를 매번 서버에서 가져오는 것을 볼 수 있다.
npm run dev
개발 모드에서는 Pre-Fetching이 비활성화됨
Pre-Fetching
동작을 확인하려면 프로덕션 모드에서 Next.js 앱을 실행해야 한다. 프로덕션 모드에서는 Next.js가 페이지별로 JS 파일을 분리해 빌드하고, Pre-Fetching
을 통해 각 페이지에 필요한 JS 코드를 미리 불러온다.
npm run build # 앱을 빌드
npm run start # 프로덕션 모드에서 앱 실행
이런식으로 페이지마다 필요한 JS코드를 매번 불러오고 있는 걸 확인할 수 있다. 그렇기 때문에 프리페칭
동작을 제대로 확인 하려면, 개발모드가 아닌 build
에서 실행하는 프로덕션
모드로 실행을 시켜줘야함.
페이지별 JS번들의 용량까지 함께 출력이 되는 걸 볼 수 있다. 그래서 이렇게 스플리팅까지 되는 걸 확인해 봤으니까 이제는 프리페칭을 확인하기 위해서 npm run start
로 next앱을 프로덕션 모드로 실행시킨다.
그런 다음에 브라우저에서 인덱스 경로로 접속을 해보자 그런 다음에 "새로고침"을 해줘서 네트워크탭에 나오는 내용을 살펴보면 굉장히 많은 네트워크 요청이 이뤄진 걸 볼 수 있는데
search요청을 클릭해서 보면,
이렇게 search페이지에 필요한 JS코드를 미리 프리페칭
해서 불러와놓는걸 볼 수 있다. 그래서search
링크를 클릭해서 search
페이지로 이동한다 하더라도 이 네트워크 탭에는 아무런 요청도 추가로 발생하지 않음.
그런데 가끔 이 페이지에 대한 JS 코드를 다시 불러오는 네트워크 로그를 볼 수 있는데, 이는 캐시가 만료되었기 때문에 다시 불러온 것이다.
Pre-Fetching
은 계속해서 정상적으로 동작하며, 기본적인 동작에는 영향을 주지 않는다.
Link
컴포넌트는 자동으로 Pre-Fetching
을 수행하지만, 프로그래매틱
페이지 이동에서는 Pre-Fetching
이 자동으로 적용되지 않는다. 예를 들어, 버튼 클릭을 통해 /test
페이지로 이동할 때는 Pre-Fetching
이 동작하지 않는다.
이처럼, /test페이지
로 이동이라는 button을 클릭해서 test페이지로 이동을 해보면 이때는 JS번들을 추가로 불러오는 걸 네트워크 탭에서 확인할 수 있다.
import { useRouter } from 'next/router';
import Link from 'next/link';
export default function App() {
const router = useRouter();
const onClickButton = () => {
router.push("/test"); // Programmatic Navigation
};
return (
<header>
<Link href="/">Home</Link>
<Link href="/search">Search</Link>
<button onClick={onClickButton}>Go to Test</button>
</header>
);
}
src의 pages폴더의 _app.tsx파일 APP컴포넌트를 살펴보면 프리페칭
이 이루어졌던 search페이지와 book페이지는 Link컴포넌트로서 구현이 되어있었지만, test페이지의 경우에는, programmatic하게 페이지를 이동하도록 설정해 뒀기 때문임.
만약 Programmatic Navigation
에서도 Pre-Fetching
을 적용하고 싶다면, App컴포넌트가 화면에 mount
되었을 때 useEffect
를 사용해router
객체의 특정 메서드를 통해서 직접 programmatic하게 test페이지를 Pre-Fetching
하도록 코드를 작성해서 특정 페이지의 JS 코드를 미리 불러올 수 있다.
import { useRouter } from 'next/router';
import { useEffect } from 'react';
export default function App() {
const router = useRouter();
useEffect(() => {
router.prefetch("/test"); // /test 페이지를 미리 불러옴
}, []);
const onClickButton = () => {
router.push("/test");
};
return (
<header>
<button onClick={onClickButton}>Go to Test</button>
</header>
);
}
이 코드를 통해 프로그램적으로 이동하는 페이지도 Pre-Fetching
을 적용할 수 있게 됐다. router.prefetch("/test")
를 호출하여 /test
페이지를 미리 불러오면, 버튼을 클릭해 페이지를 이동할 때도 Pre-Fetching
이 적용된 페이지로 빠르게 전환된다.
npm run build
로 next앱을 다시 build하고, 완료되면 npm run start
로 다시 가동한 다음에 프로젝트의 인덱스 경로로 이동해서 새로고침 눌러보면, 네트워크 탭을 확인해 보면 test페이지에 필요한 코드도 미리 프리페칭
이 이루어지고 있는 걸 볼 수 있다. 내가 접속을 잘 안 하는 특정 페이지에 Pre-Fetching
이 필요하지 않은 경우에는, Link
컴포넌트의 prefetch
속성을 false
로 설정하여 Pre-Fetching
을 비활성화할 수 있다.
<Link href={"/search"} prefetch={false}>search</Link>
이렇게 설정하면, /search
페이지는 Pre-Fetching
이 적용되지 않습니다. 사용자가 클릭할 때만 서버에 요청하여 페이지를 전환한다.
Next.js의 Pre-Fetching
은 사용자 경험을 개선하기 위한 강력한 기능이다. 페이지 전환 전에 필요한 리소스를 미리 로드하여 CSR(Client Side Rendering) 방식으로 빠른 페이지 전환을 가능하게 한다.
Pre-Fetching
은 사용자가 이동할 가능성이 있는 페이지의 JS 코드를 미리 불러오는 기능이다.
Link
컴포넌트는 Pre-Fetching
을 자동으로 처리하며, programmatic
페이지 이동에서는 router.prefetch()
로 Pre-Fetching
을 적용할 수 있다.
필요 없는 페이지는 Pre-Fetching
을 비활성화
할 수 있다.