TL;DR 성능과 데이터 최신성 사이의 최적의 균형을 찾기 위해 Next.js가 제공하는 다양한 렌더링 전략(SSR, SSG, ISR)의 동작 원리를 파악하고, 최신 패러다임인 서버 컴포넌트(RSC)를 어떻게 활용해야 하는지 상세히 알아봅니다.
웹 애플리케이션을 개발할 때 개발자가 마주하는 가장 큰 고민은 "어떻게 하면 사용자에게 더 빠르게 화면을 보여주면서도, 검색 엔진 최적화(SEO)를 놓치지 않을 것인가?"입니다.
단순한 클라이언트 사이드 렌더링(CSR) 방식은 개발이 편하지만, 자바스크립트 파일이 로드되기 전까지 사용자는 빈 화면을 봐야 하며 구글이나 네이버 같은 검색 로봇이 페이지 내용을 읽어가는 데 한계가 있습니다. 그렇다고 모든 페이지를 매번 서버에서 새로 만들자니(SSR) 서버 부하가 커지고 응답 속도가 느려지는 병목 현상이 발생합니다.
Next.js는 이러한 고민을 해결하기 위해 페이지별로 최적의 렌더링 전략을 선택할 수 있는 강력한 기능들을 제공합니다.
단순히 "하나의 방식"만 고집하는 것이 아니라, 블로그 글처럼 자주 바뀌지 않는 내용은 미리 만들어두고(SSG), 주식 차트처럼 실시간성이 중요한 데이터는 매번 서버에서 가져오는(SSR) 등 상황에 맞는 도구를 골라 쓸 수 있게 해줍니다. 특히 최근에는 리액트 서버 컴포넌트(RSC)라는 개념이 도입되면서 서버와 클라이언트의 경계가 더욱 명확해지고 효율적으로 변했습니다.
빌드 시점에 HTML 파일을 미리 생성해두는 방식입니다. 사용자가 접속하면 서버는 이미 만들어진 파일을 그대로 내어주기만 하면 되므로 속도가 압도적으로 빠릅니다. 주로 소개 페이지나 도움말 문서처럼 정적인 컨텐츠에 사용됩니다.
사용자의 요청이 들어올 때마다 서버에서 즉석으로 HTML을 생성합니다. 데이터가 항상 최신 상태여야 하거나 사용자의 권한에 따라 화면이 달라져야 할 때 필수적입니다.
SSG의 장점(빠른 속도)과 SSR의 장점(데이터 최신성)을 결합한 방식입니다. 설정한 시간(예: 60초)마다 백그라운드에서 정적 페이지를 재생성합니다. 우리의 HooneyLog 블로그가 바로 이 방식을 사용하여 노션의 새 글을 자동으로 감지합니다.
Next.js 13 버전부터 도입된 핵심 기술로, 컴포넌트 자체를 서버에서 실행하여 클라이언트로 전달합니다. 브라우저로 전송되는 자바스크립트 번들 크기를 획기적으로 줄여주며, 서버 환경의 자원(DB, 파일 시스템)에 직접 접근할 수 있어 보안성이 뛰어납니다.
Next.js 15(App Router) 환경에서 각 전략을 구현하는 코드는 다음과 같습니다.
// 1. SSR 구현 (항상 최신 데이터 유지) async function Page() { const res = await fetch('https://api.example.com/data', { cache: 'no-store' }); const data = await res.json(); return <div>{data.title}</div>; } // 2. ISR 구현 (60초마다 갱신) // 이 방식은 성능과 최신성 사이의 최고 타협점입니다. export const revalidate = 60; async function Page() { const res = await fetch('https://api.example.com/data'); // 기본적으로 캐싱됨 const data = await res.json(); return <div>{data.title}</div>; }
서버에서 생성한 데이터와 클라이언트에서 렌더링한 데이터가 다를 때(예: new Date() 사용) 발생하는 에러입니다. 이를 해결하기 위해 서버와 클라이언트가 동일한 포맷을 유지하도록 유틸리티 함수를 사용하거나, useEffect를 통해 클라이언트 사이드에서만 특정 로직이 돌도록 제어해야 합니다.
각 전략을 도입했을 때 얻는 결과와 포기해야 하는 점은 명확합니다.
Next.js는 단순한 라이브러리가 아니라, 현대적인 웹의 모든 복잡한 문제를 해결해주는 완성형 프레임워크입니다.
특히 이번 리팩토링을 통해 우리 블로그에 적용한 ISR 전략은 개인 기술 블로그로서 가장 효율적인 선택이었습니다. 노션이라는 외부 API의 느린 속도를 캐싱으로 극복하면서도, 1분마다 새 글을 체크하는 영리함을 갖췄기 때문입니다.
더 자세한 내용은 Next.js 공식 문서에서 렌더링 파트를 읽어보시는 것을 강력히 추천드립니다.