CSR: 서버에서 빈 Html을 전달하고 자바스크립트를 이용해서 그림을 그리고 나서 화면에 보여주는 것
이미지 출처) 한 입 크기로 잘라먹는 Next.js 강의
✅ 장점:
☑️ 단점:
서버에서 자바스크립트로 미리 html을 그린 후에 가져다 주는 것
= 완성된 html과 추가적 javascript 파일을 브라우저에 전달
서버에서 완성된 html 파일을 전달하는데 자바스크립트 파일은 왜 전달할까?
=> 자바스크립트 파일을 추가로 전달하는 이유는
"서버가 할 수 없는 작업을 브라우저가 처리하도록 하기 위해서"
예를 들어 이벤트(클릭, 스크롤), 버튼을 클릭했을때 팝업이 뜸
[하이드레이션]
즉, HTML은 이미 화면에 보이지만, 클릭이나 입력 같은 사용자와의 상호작용은 아직 동작하지 않는 상태이다.
이때 리액트가 이벤트 핸들러(예: 버튼 클릭할 때 실행되는 onClick)나 상태 관리 로직(useState, useEffect 등)을 연결해서, 화면이 완전히 작동 가능하도록 만드는 작업이 하이드레이션이다.
기존 리액트 앱의 FCP(초기 접속 속도)가 느린 문제를 해결하기 위해서 Next.js는 사전렌더링이라고 부르는 새로운 방식의 렌더링을 제공함
이미지 출처) 한 입 크기로 잘라먹는 Next.js 강의!
뷰포트(화면 영역) 안에 링크가 보이면, 그 링크가 가리키는 페이지의 일부 리소스를 미리 로드(prefetch)한다.
사용자가 클릭했을 때 거의 즉각적으로 페이지를 표시할 수 있어 페이지 전환이 자연스러움
Link 태그는 서버 컴포넌트도 마치 CSR처럼 js(RSC Payload)로 불러온다.
Next.js는 현재 접속요청한 페이지에 해당하는 자바스크립트 코드들만 따로따로 보내주게 됨
=> 근데 이러면, 초기 접속 이후에 발생하게 되는 페이지 이동들은
클라이언트 사이드 렌더링의 방식으로 추가적인 요청없이
바로바로 처리 될 수가 없음
=> 다시 페이지를 이동하려고 하면 추가로 자바스크립트 코드를
불러와야하는 과정이 필요함
=> 이러한 문제를 방지하기 위해서 프리페칭이 나옴
프리페칭: 이동할 수 있는 가능성이 있는 모든 페이지들의 자바스크립트 코드를 미리 불러와 놓는 과정
자바스크립트 번들에는 클라이언트 컴포넌트만 포함되며, 서버 컴포넌트의 데이터는 제외된다.
하지만 대부분의 페이지는 서버 컴포넌트와 클라이언트 컴포넌트가 혼합되어 구성되므로, 서버 컴포넌트의 데이터 없이 클라이언트 컴포넌트만 전달되면 페이지가 정상적으로 렌더링되지 않는다.
이를 해결하기 위해, 서버 컴포넌트를 실행한 결과물(RSC Payload)이 자바스크립트 번들과 함께 브라우저로 전달된다. 브라우저는 전달받은 RSC 페이로드와 자바스크립트 번들을 조합해 최종적으로 페이지를 완성하고 적절히 교체한다.
프리렌더링의 한 종류: 빌드시 완성된 html을 미리 만듬
#### 용어정리
(1) 빌드
소스 코드를 실행 가능한 파일로 만드는 과정
컴파일 과정도 포함하고, 그 외에 최적화, 파일 묶기(번들링), 압축 등 웹사이트가 배포 가능한 상태로 만드는 모든 작업을 포함
(2) 컴파일
보통 컴파일은 고급 언어로 작성된 코드를 기계어로 변환을 의미하지만,
=> 자바스크립트에서의 컴파일러는 JSX나 최신 JavaScript 코드(React 코드)를 브라우저가 이해할 수 있는 순수 JavaScript 코드로 변환해줌
✅ 장점:
사전렌더링에 많은 시간이 소요되는 페이지더라도 사용자의 요청에는 매우 빠른 속도로 응답 가능
☑️ 단점:
다시는 페이지를 새롭게 사전렌더링 하지 않기 때문에 최신 데이터 반영은 어렵다.
프리렌더링의 한 종류: 요청 시마다 서버에서 최신 데이터를 받아 HTML을 생성해서 브라우저로 전송
사용예시
- 사용자별 맞춤 페이지: 로그인한 사용자마다 다른 정보를 보여줄 때
- 실시간 데이터가 중요한 경우: 주식 시세, 실시간 검색 결과, 수강신청이나 티켓팅
const res = await fetch("http://examples.api", {
cache: "no-store",
});
fetch를 사용할 때 'no-store' 옵션을 주면, 서버 측에서 데이터를 매번 새로 가져오게 된다.
이건 서버 측 캐시를 사용하지 않겠다는 선언이지, 클라이언트 측의 캐시, 특히 Next.js의 Route Cache와는 별개의 이야기다.
예를 들어 어떤 페이지에서 매번 최신 데이터를 보여줘야 한다고 하자. fetch에 'no-store'를 사용했으니 서버에서는 항상 최신 데이터를 가져다준다.
하지만 클라이언트 측에 Route Cache가 남아있다면, 사용자는 여전히 이전 데이터를 보게 된다.
즉, UI에 과거 데이터가 노출되는 문제가 생긴다.
이럴 때 필요한 게 바로 캐시 무효화다.
Next.js에서는 이를 위한 API를 제공하고 있다:
revalidatePath(path: string)revalidateTag(tag: string)이 API들은 next/cache 모듈에서 import할 수 있으며, 사용 시 서버 캐시뿐 아니라 클라이언트 캐시까지 모두 무효화해준다.
"use server";
import { revalidatePath, revalidateTag } from "next/cache";
type RevalidateOptions =
| { type: "path"; path: string; kind: "page" | "layout" }
| { type: "tag"; tag: string };
/**
* revalidate 함수: 매개변수에 따라 revalidatePath 또는 revalidateTag 실행
* @param options - revalidation 옵션 (path 또는 tag)
*/
export const revalidate = (options: RevalidateOptions): void => {
if (options.type === "path") {
// revalidatePath 호출
revalidatePath(options.path, options.kind);
} else if (options.type === "tag") {
// revalidateTag 호출
revalidateTag(options.tag);
}
};
프리렌더링의 한 종류: 빌드 시 정적 파일을 생성하되, 일정 주기나 조건에 따라 페이지를 백그라운드에서 재생성
=> 새 버전 생성중에도 기존 캐시 버전 제공
✅ 장점:
굉장히 빠른 속도로 브라우저에게 응답이 가능함.(기존 SSG의 장점)
주기적으로 페이지를 업데이트하여 최신데이터 반영가능(기존 SSR의 장점)
프리렌더링 ❌ -> 하지만 Next.js에서는 약간 애매하다.
React는 완전히 빈 HTML을 브라우저에 전달 후 모든 내용을 Javascript로 렌더링한다.
=>Next.js는 최소한의 HTML은 미리 만든 후, 추가 요청이 필요한 데이터만 브라우저에서 렌더링
GET, POST, PUT, DELETE 등 다양한 HTTP 메서드를 라우트 핸들러 내에서 쉽게 처리할 수 있어, RESTful API 구현이 편리하다. NextResponse 객체를 통해 JSON, 쿠키 설정, 헤더 제어 등의 처리가 쉽다.🧐 Next.js에서 API 연결 시 라우트 핸들러를 꼭 사용해야 할까?
- 반드시 그럴 필요는 없음
- 서버 컴포넌트에서 호출하는 API 는 Server Action 이 자동으로 됨으로, 굳이 라우트핸들러로 다시 한번 은닉화를 시킬 필요는 없다.
- Server Action 이 더 좋은 선택이다.
✅ 공통점
❗차이점
route.ts 라는 파일을 추가로 만들고, method에 맞게 함수를 만들어야 한다."use server"를 작성한다. revalidatePath 와 같은 함수 사용 시 자동으로 데이터 패치 가능