본문 바로가기
TECH

SSR / CSR / SSG / ISR 🖥️

by Stella-Park 2025. 6. 14.
728x90

"SSG?" 신세계 아니고요 🙅‍♀️

 

우리가 브라우저에 naver.com을 검색해서 들어가면 네이버 메인 페이지가 보이는데 이때 메인 페이지가 보일 때

즉, 렌더링될 때 방식에는 4가지가 있다.

  • SSR (Server-Side Rendering)
  • CSR(Client-Side Rendering)
  • SSG (Static Site Generation)
  • ISR (Incremental Static Regeneration)

단어는 알고 있었지만, 분명한 개념과 차이를 설명하라고 하면 버벅이기 때문에 공부하면서 다시 정리해봤다.

 

SSR (Server-Side Rendering)

SSR은 서버에서 HTML을 렌더링해서 클라이언트에 전달하는 방식이다.

사용자가 웹사이트에 요청을 보내면 서버가 페이지의 HTML을 만들어서 브라우저로 전달하고, 브라우저는 이 HTML을 그대로 렌더링합니다. 이후에 JavaScript가 로드된다.

 

Vue로 예를 들면, SSR(Server-Side Rendering)을 구현하는 가장 일반적인 방법은 Nuxt.js를 사용하는 것이다.

Nuxt는 Vue 기반의 프레임워크로, SSR을 기본으로 지원하면서 설정을 간소화해준다.

 

흐름

  1. 사용자가 페이지 요청
  2. 서버가 HTML 생성 (JavaScript 실행 포함)
  3. HTML이 브라우저로 전송
  4. 브라우저가 즉시 화면에 표시
  5. 이후 JavaScript가 활성화되며 인터랙션 가능해짐 (Hydration)
장점 1) 빠른 첫 페이지 로딩 속도
2) SEO에 유리: 검색엔진이 미리 렌더링된 HTML을 쉽게 읽을 수 있음
3)콘텐츠가 빠르게 보임 (페이지가 흰 화면으로 오래 있는 걸 방지)
단점 1) 사용자와의 상호작용을 위해 JS가 완전히 로드될 때까지 기다려야 함
2) 서버에 부하가 큼: 요청마다 HTML을 렌더링해야 하므로

 

// nuxt.config.js
export default {
  target: 'server', // SSR 모드
  modules: ['@nuxtjs/axios'],
  axios: {
    baseURL: '/', // 예제 API 사용 시 기본값
  },
};


// index.vue
<template>
  <div>
    <h1>SSR 예제 - 게시글 목록</h1>
    <ul>
      <li v-for="post in posts" :key="post.id">
        {{ post.title }}
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  async asyncData({ $axios }) {
    const res = await $axios.get('https://jsonplaceholder.typicode.com/posts');
    return { posts: res.data.slice(0, 5) }; // 앞의 5개만 표시
  }
};
</script>

 

asyncData 함수는 컴포넌트가 생성되기 전에 서버에서 먼저 호출되어 HTML에 데이터가 포함된 상태로 브라우저에 전달된다.

 

Nuxt에서 SSR이 동작하는 방식 요약

  1. 사용자가 페이지 요청 → Node.js 서버에서 Nuxt 앱 실행
  2. asyncData로 API 호출 → 데이터를 Vue 컴포넌트에 주입
  3. 서버가 완성된 HTML을 클라이언트에 전송
  4. 클라이언트는 이미 채워진 HTML을 렌더링 + Vue hydration 진행

 

CSR (Client-Side Rendering)

CSR은 브라우저에서 JavaScript로 페이지를 동적으로 렌더링하는 방식이다.

처음에는 빈 HTML과 JavaScript 번들이 로드되고, 이후 JavaScript가 API 등을 통해 데이터를 받아서 DOM을 구성한다.

 

흐름

  1. 사용자가 페이지 요청
  2. 브라우저가 최소한의 HTML과 JS 번들 수신
  3. JS가 실행되면서 API 요청 수행
  4. 데이터 받아서 클라이언트에서 DOM 구성
장점 1) 클라이언트에서 페이지 간 전환이 빠름
2) 서버 부하 적음
3) SPA(Single Page Application)에 최적화되어 있음
단점 1) 초기 로딩 속도가 느릴 수 있음 (JS가 완전히 로드되어야 화면이 나옴)
2) SEO에 불리함 (검색 엔진이 JS를 실행하지 못하면 빈 HTML만 봄)

 

<template>
  <div>
    <h1>게시글 목록</h1>
    <ul>
      <li v-for="post in posts" :key="post.id">{{ post.title }}</li>
    </ul>
  </div>
</template>


<script>
export default {
  data() {
    return { posts: [] };
  },
  async mounted() {
    const res = await fetch('https://api.example.com/posts');
    this.posts = await res.json();
  }
};
</script>

 

CSR에서는 사용자가 페이지를 요청할 때 HTML은 거의 비어 있고, 브라우저가 JavaScript를 실행한 후에야 화면이 보인다.

 

 

SSG (Static Site Generation)

SSG는 빌드 시점에 HTML을 미리 생성해두는 방식이다.

사용자가 페이지를 요청할 때 서버나 클라이언트에서 별도의 렌더링 작업 없이 미리 만들어진 HTML 파일을 그대로 전달한다.

 

정적 블로그, 마케팅 페이지처럼 변경이 거의 없는 페이지에 매우 적합하다.

 

 흐름

  1. 개발자가 npm run build 실행
  2. API 호출 → 데이터를 바탕으로 HTML 정적으로 생성
  3. Netlify, Vercel 등에 업로드
  4. 사용자는 요청 시 빠르게 HTML을 전달받음 (CDN 활용)
// nuxt.config.js
export default {
  target: 'static', // SSG 모드
  generate: {
    routes: ['/posts/1', '/posts/2'] // 사전 생성할 라우트
  }
}


// vue
<template>
  <div>{{ post.title }}</div>
</template>

<script>
export default {
  async asyncData({ params }) {
    const res = await fetch(`https://jsonplaceholder.typicode.com/posts/${params.id}`);
    const post = await res.json();
    return { post };
  }
}
</script>

 

 

ISR (Incremental Static Regeneration)

ISR은 SSG의 확장된 개념으로, 초기에는 정적으로 HTML을 생성하고, 이후 일정 주기로 서버가 백그라운드에서 페이지를 다시 생성해 캐시를 업데이트한다.

 

정적으로 빠르게, 데이터를 최신 상태로 유지하고 싶을 때 사용하는 방식이다.

 

렌더링 흐름

  1. 페이지 최초 요청 → 정적 페이지 전달 (SSG처럼)
  2. 설정된 시간이 지나면 다음 요청 시 서버가 백그라운드에서 새 HTML 생성
  3. 새 HTML로 자동 업데이트 (사용자는 항상 빠르고 거의 최신 페이지를 보게 됨)
export async function getStaticProps() {
  const res = await fetch('https://api.example.com/posts');
  const data = await res.json();

  return {
    props: { data },
    revalidate: 60 // 60초마다 페이지 재생성 (ISR)
  };
}

 

각각의 장점과 단점이 뚜렷하기 때문에 프로젝트의 성격에 맞게 선택하거나, 경우에 따라서는 혼합(SSR + CSR)으로 사용하기도 한다.

 

위에서 이야기했던 내용을 표로 정리해봤다.

  SSR CSR SSG ISR
초기 로딩 속도 빠름 (HTML 먼저 도착) 느림 (JS 로딩 후 렌더링) 매우 빠름 (정적 파일로 바로 응답) 빠름 (초기엔 정적, 이후 백그라운드 갱신)
SEO 친화도 좋음 나쁨 매우 좋음 매우 좋음
서버 부하 높음 (요청마다 HTML 생성) 낮음 없음 (빌드시 생성) 낮음 (주기적 재생성만 서버 필요)
사용자 경험 초기 화면 빠르게 표시 초기에는 흰 화면이 보일 수 있음 즉시 표시 가능 즉시 표시 가능 + 오래된 경우 자동 업데이트
페이지 전환 새로고침 발생 (기본) 빠름 (SPA처럼 동작) 빠름 (SPA처럼 동작 가능) 빠름 (SPA처럼 동작 가능)
구현 복잡도 비교적 복잡 (Hydration 필요) 비교적 단순 단순 (정적 페이지 구성 중심) 중간 (정적 + 재생성 로직 필요)
데이터 최신성 항상 최신 항상 최신 고정된 시점의 데이터 일정 주기마다 최신 데이터 자동 반영
빌드 시점 필요 ✅ (초기 생성 필요)
서버 메모리 부담 높음 (요청마다 렌더링 처리) 없음 없음 중간 (정적 + 재생성 시점만 부담)
클라이언트 메모리 부담 중간 높음 (모든 렌더링 및 상태 클라이언트 처리) 중간 중간

 

필요 조건 추천 방식
빠른 SEO 최적화 + 최신 콘텐츠 SSR or ISR
자주 바뀌지 않는 정적 콘텐츠 SSG
로그인 필요, 대시보드 등 상호작용 중심 CSR
정적이지만 주기적 업데이트 필요 ISR

 

든든은 앱으로만 사용(webview)되고 있기 때문에 SEO나 서버 부하(증권사 요청 및 응답이 있기 때문에 서버 오류가 잔잔하게 난다)를 작게 하고 싶어서 CSR 방식으로 서비스되고 있다.

모희또도 CSR 방식으로 서비스되고 있다. 빠르게 개발하고 서비스하고 싶은 니즈가 있었고, AWS EC2가 무료 버전을 사용하고 있었기 때문에 서버 부담이 적어야 하고, 웹서비스, webview로도 사용되어야 하기 때문이다.

 

그 외... SPA (Single Page Application)

  • SPA 구조 (Vue, React 등): 한 개의 index.html만 있고, 라우팅은 클라이언트(JavaScript)가 처리
  • 정적 빌드 (yarn build): 서버 사이드 로직 없이 정적인 JS/CSS/HTML 파일로 구성됨
  • 서버 역할은 파일을 서빙하는 것뿐 (렌더링은 브라우저에서 처리)

즉, 서버는 HTML을 만들지 않고, 이미 만들어진 JS 번들을 전송

 

웹페이지의 url은 /login, /my, /home 등 여러 개지만, 서버는 언제나 index.html만 반환해준다. 이후 Vue Router가 동작하여 화면에 각각에 맞는 url의 내용을 보여준다.

 

SPA 기반이 왜 Flutter WebView에서 관리하기 좋은가?

  • WebView는 한 URL만 관리하면 됨
  • 앱-웹 간 메시지 통신이 간단
  • 로컬 캐싱 가능

 

개발은 정확히 알려고 하면 계속 파고 들어가야 하고, 배움에도 끝이 없다 😂

 

728x90

'TECH' 카테고리의 다른 글

TypeScript - private 🕵🏻‍♀️  (0) 2025.06.17
스켈레톤 UI와 Box Model 구성요소  (0) 2025.06.16
Vue / React / Angular 🛜  (6) 2025.06.13
canvas  (4) 2025.05.31
CSS에서 사용되는 단위들  (0) 2025.05.28