현재 사용자가 보고 있는 페이지에서 링크들을 통해 이동할 수 있는 연결되어 있는 페이지를 사전에 미리 불러온다.
이동할 가능성이 있는 모든 페이지를 사전에 미리 불러놓는 기능
빠른 페이지 이동을 위해 제공되는 기능.
우리가 작성한 모든 리액트 컴포넌트들을 자동으로 페이지 별로 스플리팅해서(페이지 별로 분리해서) 저장을 미리 해두기 때문에
서버로부터 가져오는 JS Bundle은 현재 페이지에 필요한 JS 번들만 포함된다.
이유? 초기 접속 요청이 있을 때마다 만약 모든 페이지의 번들파일을 전달할 경우 용량이 너무 커지게 되며 하이드레이션이 늦어진다(TTI가 늦어진다)
ex) "/search" 접속 요청 -> Search JS Bundle
그런데 이렇게 끝나게 되면, 빠른 페이지 이동이 힘들 수 있으므로 프리페칭이라는 기능이 제공된다.
Pre Fetching( 연결된 모든 페이지의 JS Bundle을 불러옴)
원래라면 페이지를 이동할 때마다 자바스크립트 코드를 서버로부터 불러와야 됐었던 단점을, 페이지 이동 요청이 발생하기 전에 미리 불러와 놓음으로써 페이지 이동을 훨씬 빠르게 동작할 수 있도록 만들어놓은 기능임.
프로그래매틱한 페이지 이동은 프리패칭이 이루어지지 않음.
프리패칭 시켜주려면, router 객체의 특정 객체를 이용하여 처리 가능.
useEffect(()=>{},[
router.prefetch('test')
])
프리패칭 해제하려면?
<Link href={"/search"} prefetch={false}>s</Link>
렌더링방식 - 서버사이드 렌더링(SSR)
가장 기본적인 사전 렌더링 방식
브라우저에 접속 요청이 들어올 때마다 서버에서 JS 실행하여 매번 사전 렌더링을 진행함
getServerSideProps()
특정 데이터를 필요로 하는 페이지라면, 매번 백엔드 서버에 데이터를 요청하게 됨
장점: 최신 데이터 유지
단점: 요청이 들어올 때마다 페이지를 새로 생성( 백엔드 등 서버 느리다면? 빌드 타임이 느려져 사용자 요청에 대해 함께 느려질 수 있음)
렌더링방식 - 정적 사이트 생성(SSG)
SSR의 치명적 단점을 해결하는 방법
빌드 타임에 미리 오래 걸릴 것 같은 페이지를 사전 렌더링 해 둠
장점: 매우 빠른 시간안에 사용자는 완성된 페이지를 볼 수 있음
단점: 빌드타임 이후에는 다시 사전렌더링을 하지 않이 때문에, 최신 데이터 반영이 어렵다. (so, 정적인 페이지 적합)
사전 렌더링에 많은 시간이 쇼오되는 페이지더라도 사용자의 요청에는 매우 빠른 속도로 응답 가능
렌더링방식 - 증분 정적 재생성(ISR)
Incremental(증분) Static(정적) Regeneration(재생성)
단순히 그냥 SSG 방식으로 생성된 정적 페이지를 일정 시간(을 기반으로)을 주기로 다시 생성하는 기술임
유통기한 설정하여, 유통기한이 끝나는 순간 이후부터는 다시 SSG를 정적으로 생성하여, 이후의 접속 요청은 업데이트된 페이지를 보여주도록 함
타이머로 정확한 시간대로 측정되는 것은 아님(페이지 재생성 시간 필요)
장점: 매우 빠른 속도로 응답가능(기존 SSG 방식의 장점), 최신 데이터 반영 가능(기존 SSR 방식의 장점) 두 장점 합침
단점: 시간 기반의 ISR을 적용하기 어려운 페이지도 존재 함
ex) 시간과 관계없이 사용자의 행동에 따라 데이터가 업데이트 되는 페이지(게시글 수정, 게시글 삭제) -> 바로 반영되야 하는 페이지
단점2: 불필요한 페이지 재생성
그냥 SSR 처리? NO. 접속자 몰리면 서버 부하.
On-Demand ISR
요청을 받을 때마다 페이지를 다시 생성하는 ISR
ex) 게시글 수정 되었을 때, revalidate 요청하여 '페이지 다시 만들어' 라고 설정해줄 수 있음
페이지 업데이트를 직접 트리거링 가능
Page Router 장점
파일 시스템 기반의 간편한 페이지 라우팅 제공
다양한 방식의 사전 렌더링 제공
Page Router 단점
페이지별 레이아웃 설정이 번거롭다
데이터 페칭이 페이지 컴포넌트에 집중된다
불필요한 컴포넌트들도 JS Bundle에 포함된다
FCP 시점 이후에, 브라우저에게 하이드레이션을 위해 JS Bundle을 전달하는 과정에서, 불필요한 컴포넌트들도 포함된다
불필요한 컴포넌트? 상호작용이 필요없어 하이드레이션이 필요없는 컴포넌트( 사전 렌더링, 하이드레이션을 위해 한 번 더 렌더링 실행됨)
TTI가 늦어지게 됨
앱 라우터에서는 이러한 상호작용이 없는 컴포넌트를 특별히 React Server Component 라고 부른다.
App Router 버전의 페이지 라우팅
무조건 Page.tsx 파일만 페이지 파일로써 취급 됨.
동적 경로(ex: book/1, book/2) 는 폴더로 [id] 만들고 해당 폴더 안에 page.tsx 만들어야 함
layout.tsx 컴포넌트가 부모(상위 폴더 내의 모든 하위 경로의 layout으로써 동작), page.tsx 컴포넌트가 자식
layout이 트리 형태로 계속 있으면 레이아웃은 중첩된다.
앱라우터에서는 서버컴포넌트가 추가되었기 때문에, 서버에서 브라우저에게 JS Bundle 만 전달하는게 아닌, 서버컴포넌트의 결과물인 RSC Payload도 함께 전달된다. (JS 번들만 전달하면, 서버 컴포넌트는 아예 누락이 되어 버리기 때문에)
JS Bundle은 클라이언트 컴포넌트만 포함
RSC Payload(직렬화됨)에는 서버 컴포넌트 포함
라우트 그룹(Route Group)
경로와 관계없이 특정페이지들에만 적용되는 공통 레이아웃을 적용할 때 사용
app 폴더 안에 (라우트그룹이름) 형태로 작성
경로에 영향을 미치지 않음, 동시에 각기 다른 경로를 갖는 페이지 파일들을 하나의 폴더에 묶어두는 기능
경로에 영향없이 레이아웃만 동일하게 할 때 사용
앱라우터의 프리패칭
링크가 있으면 프리패칭이 진행됨(개발모드에서는 동작 X)
앱라우터의 데이터 페칭
페이지 라우터에서는.. 아래(서버에서만 실행됨)에 대해 page 컴포넌트에 받아 처리(page가 클라이언트 컴포넌트. 서버 컴포넌트 역할 둘 다 하므로)
1. SSR(서버 사이드 렌더링) - getServerSideProps()
2. SSG(정적 사이트 생성) - getStaticProps()
Dynamic SSG(동적 경로에 대한 정적 사이트 생성) - getStaticPaths( fallback)
앱라우터에서는..
서버 컴포넌트가 기본이므로, async 를 붙여 서버 컴포넌트를 비동기로 만들어 데이터를 직접 불러올 수 있음
await로 기존 getServerSideProps 등을 대체 ( 컴포넌트가 직접 데이터 페칭)
클라이언트 컴포넌트에는 async 키워드를 사용할 수 없었음 - 브라우저에서 동작시 문제를 일으킬 수 있기 때문에 권장되지 않음
데이터 페칭의 가장 좋은 방법은 ? Fetching data where it's needed (데이터는 필요한 곳에서 직접 불러와라)
데이터 캐시
fetch 메서드를 활용해 불러온 데이터를 Next 서버에서 보관하는 기능
영구적으로 데이터를 보관하거나, 특정 시간을 주기로 갱신시키는 것도 가능
불필요한 데이터 요청의 수를 줄여서 웹 서비스의 성능을 크게 개선할 수 있음
fetch라는 메서드에서만 사용가능 (axios 등 불가능)
적용 예시: const response = await fetch(`~/api`, {cache: "force-cache"});
옵션 종류 :
{cache: "force-cache"} :
요청의 결과 무조건 캐싱, 한번 호출된 이후에는 다시는 호출되지 않음
{cache: "no-store"} :
데이터 페칭의 결과를 저장하지 않는 옵션
캐싱을 아예 하지 않도록 설정하는 옵션임
기본값이라 굳이 설정해주지 않아도 됨
기존 버전에서는 무조건 캐싱하도록 기본값이 되어 있었음
{next: {revalidate: 10}} :
특정 시간을 주기로 캐시를 업데이트 함 (예시는 10초 주기)
마치 Page Router의 ISR 방식과 유사함
Next 서버의 데이터 캐시 저장소에서 10초 지나면 'STALE' , 즉 상했다는 상태가 되고, 이 때 백엔드 서버에 요청을 다시 보내어 캐시를 갱신하게 됨
{next: {tags: ['a']}} :
On-Demand Revalidate
요청이 들어왔을 때 데이터를 최산화 함
On-Demand ISR 같은 방식(추가 개념 학습 필요)
Request Memoization(리퀘스트 메모이제이션)
중복된 요청을 캐싱하여 한 번만 요청할 수 있도록, 다시는 발송하지 않도록 하는 기술
동일한 API로부터 동일한 데이터를 불러오고 있을 때
모두 캐싱 옵션이 설정되지 않아, no-store 상태일 때,
원래는 계속 중복된 데이터를 불러오게 됨
그러나 비효율적이므로,
next에서는 이러한 상황을 방지하기 위해, request memoization 기술을 활용하여 자동으로 중복을 제거해줌
동일한 API가 반복되면, Next 서버에서 데이터 캐시 확인 이전 단계에서 리퀘ㅐ스트 메모이제이션을 수행하고
자동으로 캐싱해주게 됨
데이터 캐시(서버 중단 전까지 영구적 보관)와는 엄연히 다르다.
리퀘스트 메모이제이션은 하나의 페이지를 렌더링 하는 동안에 중복된 API 요청을 캐싱하기 위해 존재함
렌더링이 종료되면 모든 캐시가 소멸된다.
잘 활용하면 좋을수도
그냥 중복 요청 안보내면 되는거 아닌가?
서버 컴포넌트가 도입되었기 때문. 한 페이지의 여러 컴포넌트에서 동일한 요청이 발생할 수 있는 경우가 다수 존재
풀 라우트 캐시(Full Route Cache)
Next 서버측에서 빌드 타임에 특정 페이지의 렌더링 결과를 캐싱하는 기능 ( = 페이지 캐싱)
적합한 페이지는?
nextJS는 어떤 기능을 사용하느냐에 따라 자동으로 Static Page(정적페이지) 또는 Dynamic Page(동적페이지)로 나뉜다.
static page에는 풀 라우트 캐시가 적용됨
Dynamic Page로 설정되는 기준
특정 페이지가 접속 요청을 받을 때마다 매번 변화가 생기거나, 데이터가 달라질 경우
서버 컴포넌트에서, 캐시되지 않는 data fetching을 사용할 경우(캐시 옵션 없을 때)
서버 컴포넌트에서, 페이지 내부에서 동적 함수(쿠키, 헤더, 쿼리스트링)을 사용하는 컴포넌트가 있을 때
Static Page로 설정되는 기준
기본적으로, Dynamic Page가 아니면 모두 Static Page가 된다.
세번째, 네번째 주목. 네번째의 static page에만 full route cache가 적용됨.
풀 라우트 캐시도 revalidate 가능. 마치 ISR
요약 :
Next.js에 존재하는 모든 페이지는 동적함수나 데이터 캐시를 어떻게 하는지에 따라 동적 페이지, 스태틱 페이지로 자동 분류된다.
스태틱 페이지에는 풀라우트 캐시가 적용된다.
빌드 타임에 풀 라우트 캐시로 보관되고, 굉장히 빠른 속도로 페이지 제공하는데 도움됨.
이런 정적 페이지라도, revalidate 설정된 fetch가 일어날 경우, 해당 주기에 따라 계속 업데이트 될 수 있다.
라우트 세그먼트 옵션(권장되지 않음 - 단순 테스트용)
일일히 한 번씩 앱라우터 페이지들을 점검하는게 복잡하게 느껴질 수 있다.
일일히 체크하지 않더라도, 페이지 동작을 강제로 설정할 수 있는 옵션이 라우트 세그먼트 옵션이다.
dynamic 옵션
특정 페이지의 유형을 강제로 static 또는 dynamic 페이지로 설정
auto: 기본값, 아무것도 강제하지 않음 - 지금껏 그래왔던것처럼 동적함수와 데이터 캐시 여부에 따라 static 또는 dynamic 페이지로 설정
force-dynamic : 페이지를 강제로 다이내믹 페이지로 설정
force-static : 페이지를 강제로 static 페이지로 설정
error: 페이지를 강제로 static 페이지로 설정 (동적함수라던가, 캐싱되지 않은 데이터 패칭 등 스태틱으로 설정하면 안되는 이유가 있다면 빌드 오류가 발생하도록 함)
클라이언트 라우터 캐시
브라우저 내에 저장되는 캐시
넥스트 앱에서의 페이지 이동을 효율적으로 진행하기 위해 페이지의 일부 데이터를 보관함
새로고침할 때는 캐시가 지워짐(다시 동작)
페이지들이 같이 사용하는 rootLayout, 라우트그룹 레이아웃 등 레이아웃에 해당하는 부분을 클라이언트 라우터 캐시라는 공간에 캐싱하게 된다.
그 외 페이지 및 기타 등 서버 컴포넌트에 대해서는 별도로 받아오게 됨.
스트리밍과 에러 핸들링
스트리밍
넷플릭스가 큰 용량의 동영상을 아주 빠른 속도로 시청할 수 있게 해주는 것처럼, 서버에서 클라이언트로 데이터를 넘겨 줄때, 보내주는 데이터 크기가 너무 크거나 준비하는데 오래 걸리는 데이터라 빠르게 전송해주기 어려울 때, 데이터를 여러 조각으로 쪼개어 하나하나씩 클라이언트에게 전송하는 기술
이용하게 되면, 일단 뭐라도 빠르게 보여줄 수 있음
느리게 렌더링 되는 부분(async 비동기 등)은? 로딩바 같은 대체 UI를 보여준다.
Dynamic Page에 자주 사용된다. - 풀라우트 캐시에 저장되지 않으므로
마치 음식점에서 밑반찬부터 빠르게 내주는 것과 비슷함.
페이지 스트리밍 적용
page.tsx 컴포넌트와 같은 선상에 loading.tsx 파일 생성
주의할점 1 : 해당하는 경로 아래의 모든 비동기 컴포넌트들이 스트리밍 되도록 설정되버림
ex) search 아래 setting의 페이지가 추가로 존재하고 딜레이가 2초 있다면, 함께 적용되어진다.
주의할점 2: loading.tsx는 async가 붙은 비동기 컴포넌트들에만 스트리밍을 제공함
주의할점 3: 무조건 page.tsx 컴포넌트에만 적용할 수 있다. 레이아웃이나 다른 컴포넌트 파일에는 스트리밍을 설정할 수 없음 (suspense 컴포넌트 활용해야 함)
주의할점 4: 브라우저에서 쿼리스트링이 변경될 때에는 트리거링 되지 않는다.
컴포넌트 스트리밍 적용
suspense 이용
suspens만 이용할 경우 페이지 스트리밍처럼 쿼리스트링에서는 트리거링 되지 않는데, 이때 suspense에 key를 설정해주면 됨
내부 로딩이 완료된 이후에는, 기본적으로 새롭게 로딩 상태로 돌아가지 않는다.
이 때 key값을 바꿔버려서, 쿼리스트링 바뀔때마다 새로운 컴포넌트로 인식하도록 설정하는 것
에러 핸들링
핸들링할 영역의 page.tsx와 같은 영역에 error.tsx 파일 사용하여 한번에 처리
startTransition(()=>{}) : 하나의 콜백 함수를 인수로 전달받아, 안에 있는 UI를 변경시키는 작업들을 모두 일괄적으로 처리해줌
서버 액션
서버액션?
브라우저에서 호출할 수 있는 next 서버에서 실행되는 비동기 함수
함수 내 최상단에 "use server" 추가, with FormData 형식으로 전달 가능하도록 해주는 것