서버 사이드 렌더링은 사용자가 페이지를 요청하면 서버에서 HTML을 완전히 렌더링한 뒤 브라우저로 전달하는 방식입니다.
클라이언트 사이드 렌더링은 서버가 기본 HTML 과 JS 번들을 보내주고, 이후 브라우저에서 자바스크립트가 실행되어 화면을 구성하는 방식입니다.
해당 포스팅에서는 서버 사이드 렌더링과 클라이언트 사이드 렌더링 각각의 특징과 차이, 구분 전략부터 실무에서 쓸 수 있는 혼합 전략을 Next 환경을 적용하여 정리했습니다.
앞서 설명했듯 SSR 은 사용자가 페이지를 요청하면 서버에서 HTML 을 완전히 렌더링한 뒤 브라우저로 전달하는 방식입니다. 사용자는 서버가 보낸 완성된 HTML 을 바로 받아 화면을 볼 수 있고, 이후 필요한 경우 자바스크립트가 실행되며 동적인 기능이 붙습니다.
동작 방식을 크게 보면
장점으론 HTML 이 준비된 상태로 전달되므로 빠른 초기 로딩 속도, 검색 엔진이 완성된 HTMl 을 쉽게 인덱싱 가능하여 SEO 친화적입니다. 단점은 요청마다 서버가 렌더링해야 하므로 트래픽이 많은 경우 서버 부하가 생기고, 페이지 이동 시마다 새로 요청하기 때문에 화면이 깜빡일 수 있어 사용자 경험에 한계가 있을 수 있습니다.
NextJS 에서는 크게 두 가지 패턴으로 SSR 을 지원합니다.
첫 번째는 getServerSideProps 입니다. 요청이 들어올 때마다 실행되어, 서버에서 데이터를 가져와 HTML 에 미리 렌더링하는 기능으로 항상 최신 데이터를 보장하지만, 매 요청마다 서버 호출을 하기 때문에 성능 비용이 증가됩니다.
두 번째는 getStaticProps + getStaticPaths (SSG) 입니다. 빌드 시점에 미리 HTML 을 생성해 두고, 요청 시 캐싱된 HTML 을 제공하는 기능인데 빠르고 서버 부하가 적지만, 데이터가 변경되면 빌드를 다시 해야 한다는 단점이 있습니다.
세 번쨰는 위를 보완한 ISR (Incremental Static Regeneration) 입니다. 일정 주기로 정적 페이지를 재생성하는 기능인데. SSR 과 SSG 의 장점을 섞은 방식입니다.
NextJS 의 SSR 은 단순히 '서버에서 렌더링한다' 수준이 아니라, 빌드 시점 / 요청 시점 / 갱신 주기를 개발자가 조정할 수 있다는 게 핵심입니다.
CSR 은 서버가 기본 HTML 과 JS 번들을 보내주고, 이후 브라우저에서 자바스크립트가 실행되어 화면을 구성하는 방식입니다. 초기에는 빈 HTML 을 받지만, JS가 데이터를 불러와 뷰를 그립니다.
동작 방식을 크게 보면
장점으론 페이지 전환 시 새로고침 없이 동작되기 때문에 부드러운 사용자 경험이 가능하고, 대부분의 렌더링 작업을 클라이언트가 담당하기 때문에 서버 부하가 감소됩니다. 또한 실시간 갱신 및 상태 관리 등에 적합하여 풍부한 인터랙션이 가능합니다.
단점으론 브라우저가 JS 를 받아 실행해야 화면이 보이기 때문에 초기 로딩이 느리며. 클라이언트 리소스에 의존하기 때문에 저성능 기기에서는 느려질 수 있습니다.
NextJS 에서의 CSR 은 React 컴포넌트 단위에서 자연스럽게 발생합니다. 초기에는 SSR/SSG를 통해 HTML 을 보여주고, 이후 클라이언트 축에서 React 가 'hydrate' 하여 이벤트가 붙습니다. API 호출이나 클라이언트 상태 관리(Recoil, Zustand, Redux 등)는 CSR 로 처리합니다. 즉, 데이터 패칭과 화면 갱신은 은 클라이언트에서 자유롭게 이뤄집니다.
// pages/index.tsx
import { useState } from "react";
// SSR 데이터 패칭 후 CSR 컴포넌트로 전달
export async function getServerSideProps() {
const res = await fetch('https://api.example.com/posts');
const posts = await res.json();
return { props: { posts } };
}
export default function Home({ posts }) {
const [likes, setLikes] = useState(0);
return (
<div>
<ul>
{posts.map(p => <li key={p.id}>{p.title}</li>)}
</ul>
<button onClick={() => setLikes(likes + 1)}>👍 {likes}</button>
</div>
);
}
순수 SSR 의 한계는 초기 화면은 빠르게 뜨지만, 인터랙션이 많을 수록 서버에 매번 요청을 해야 되기 때문에 비효율적인 부분이고, 순수 CSR 의 한계는 앱 같은 부드러운 경험은 좋지만, 첫 로딩 속도와 SEO 문제가 있을 수 있습니다. 그래서 SSR 로 초기 화면을 빠르게 보여주고, 이후 CSR 로 동적 기능을 붙여 상호작용을 풍부하게 하여 각자의 한계를 보완할 수 있습니다.
SSR 로 내려온 HTMl 은 단순히 '정적 HTML'이 아니라, React 가 hydration 과정에서 이벤트 핸들러를 붙이고 상태를 복원하는 대상입니다. 만약 JS 가 실행되지 않으면 SSR 로 그려진 HTMl 은 보이지만 버튼 클릭 같은 인터랙션은 작동하지 않을 수 있습니다. 따라서 NextJS 의 핵심은 SSR 이 초기 상태를 제공하고 CSR 이 이를 살아있는 앱으로 만든다는 점입니다.
SEO 가 필요한 초기 페이지에서는 SSR 을 사용하고, 사용자 행동 기반 인터랙션은 CSR 로 처리, 복잡한 대시보드의 경우 초기 레이아웃은 SSR, 내부 차트.데이터는 CSR 로 lazy load 처리할 수 있습니다.
이렇게 NextJS 에서는 SSR 과 CSR 을 자유롭게 조합할 수 있어 서비스 특성에 맞는 아키텍처를 설계할 수 있습니다.