frontend

[Next.js]렌더링 방식(CSR, SSR, SSG, ISR) - 퍼포먼스 성능 비교

프론트엔드코린이 2025. 1. 25. 00:19

렌더링이란?

쉽게 말해서 우리가 평소에 핸드폰으로 유튜브를 보거나 컴퓨터를 통해서 쇼핑몰 사이트에서 쇼핑을 하듯이 화면에 출력이 되는걸 렌더링이라고 표현을 해!

 

프코 : 인제 렌더링이 무엇인지 바로 느낌이 오지??

자 그럼 렌더링 방식은 무엇이냐?

  1. 먼저 초창기에는 MPA(Multi-Page-Application)라는 SSR방식으로 화면을 렌더링 했었어.
    • 여러 개의 정적 페이지를 미리 만들어 놓은 후 각 페이지별로 서버에 요청을 해서 HTML 문서를 서버에서 받아오는 방식
    • 이러한 MPA의 방식은 미리 만들어 놓은 페이지를 다른 페이지로 이동 시 전체 페이지를 새로 로드하므로, 깜빡거리는 느낌이 사용자 경험이 떨어질 수 있고, 오래 걸리는 게 단점이었어!

초기 MPA 통신 방법

  1. MPA 방식의 단점을 보완하고자 SPA(Single-Page-Application) 기반의 CSR(Client-Side-Rendering) 렌더링 방법이 탄생했어~!!
    • 메타(구 페이스북)에서 개발한 JavaScript기반 라이브러리로, React 라이브러리를 활용해서 CSR을 구현하는 방식으로 발전했어!!
    • 자 그래서 React는 무엇이고? CSR은 뭔데..? SPA는 또 뭐야...?
      1. React 란? CSR을 구현하기 위한 대표적인 도구라고 생각하면 될 거야!
      2. CSR이란? 브라우저가 특정 주소를 요청하면 서버는 특정 주소에 해당하는 id="root"텅 빈 HTML 파일을 브라우저에 전송해. 이 파일을 하나의 HTML 파일을 의미해서 SPA라는 의미를 뜻 해!
      3. 브라우저는 서버에서 넘겨준 빈 HTML파일을 먼저 브라우저가 읽을 수 있게 파싱을 거친 후 화면에 렌더링을 시작해 그 과정에서 파일에 script태그를 읽어 들인 후 다시 서버에 script태그가 담긴 자바스크립트 파일을 요청을 하면 서버는 요청에 맞는 모든 script태그가 담긴 파일을 다시 브라우저에 전송을 해
      4. 요약하자면 클라이언트는 먼저 빈 HTML을 파일을 파싱 후 하면에 렌더링을 한 후 그 이후에 자바스크립트 코드가 담긴 파일을 재요청해서 받은 후 자바스크립트 파일을 파싱DOM을 동적으로 조작해 화면에 렌더링을 시작해!
    • SPA(하나의 페이지)방식을 사용함으로써 페이지 이동시 전체 페이지를 로드하는 방식이 아닌 컴포넌트 단위인 자바스크립트 파일이 변경되는 부분만 실제 DOM과 가상 DOM의 차이를 인식 후 차이나는 부분만 리렌더링 한 후 리페인팅 과정을 거쳐 전체 화면이 깜빡이는 현상의 단점을 개선하고 부드럽게 변화하는 UI를 시전함으로써 사용자 경험(UX)이 크게 향상되는 장점이야!
    • 즉 초기에 최초 한번 서버에 요청을 한 뒤에는 페이지 이동 시에 변화되는 곳을 인식 후 DOM을 조작 후 렌더링을 해주는데 이는 서버에 통신을 주고받는 방식이 아니라 클라이언트에서 처리함으로 페이지 이동시 빠른 로딩이 큰 장점이야!
    • 오 그럼 MPA방식의 단점도 개선했고 사용자 경험도 증가했으니 완벽한 거 아니야?
      • 그렇다고 하고 싶지만... 여기서 또 단점이 존재해... 많이 어렵지....? 그래도 한번 끝까지 읽어주길 바래!
        • 브라우저는 초기에 빈 HTML을 파싱 후 화면에 렌더링 하잖아? 근데 이 과정에서 빈화면을 렌더링 하니 아무런 정보가 담겨 있지 않겠지..?
        • 이 과정에서 검색엔진 SEO(Search-Engine-Optimization)에 불리해... ㅠㅠ
        • SEO란 : 웹사이트가 유기적인(무료) 검색 방식을 통해 검색 엔진에서 상위에 노출될 수 있도록 최적화하는 과정이라고 생각하면 돼!
        • 서비스를 제공하는 사업자 입장에서는 최대한 자기 사이트를 노출시켜 접촉을 시켜야 하는데? 아주 큰 단점이 되는 거네....?

특정 url 요청을 하면 서버는 하나의 빈 HTML 파일을 브라우저에 전송 후 그 이후에 모든 JavaScript 파일을 브라우저에 넘겨주고 있어!

 

 

3. 여기서 CSR의 가장 큰 단점 중 하나인 SEO 검색엔진의 불리함으로 인해 이를 개선하기 위해 다시 SSR 방식의 렌더링 방식을 다시 사용해!

  • 자 여기서 SSR(Server-Side-Rendering)방식이 무엇이냐?
    • 여기서 인제 Next.js 라는 프레임워크가 등장해!
      • Next.js 프레임 워크란 : page router & app router 2가지 버전이 존재하며 SSR방식을 지원해주고 있어 
      • page router와 app router의 차이점은 데이터 패칭 부분에서 다른점이라고 생각하면 될거야!
    • 브라우저가 페이지 이동 시 한 번만 서버에 요청하는 게 아닌 매번 요청하는 방식을 뜻 해!
    • 서버는 매번 HTML 페이지를 동적으로 생성하여 브라우저에게 전달하는 방식이야!
    • 어..? 그럼 MPA 방식의 단점이 다시 생기는 게 아니야...?

4. 자 이러한 CSR 방식SSR 방식의 단점을 보완하고자 나온 게 SSG(Static-Site-Generation) 방식의 렌더링 기법이 탄생했어!!

  • SSG란?
    • 브라우저가 특정 주소로 서버에게 요청하는 과정에서 서버는 빌드 시점모든 정적 페이지가 미리 생성해 놔
    • 그 후 브라우저가 특정 url을 요청하면, 서버는 이미 미리 생성된 정적 HTML 파일을 전달해
    • SSR과의 차이점이 바로 이거야! SSR방식은 요청을 받은 후에 동적으로 HTML파일을 생성하고 SSG방식은 요청 전 빌드 시점에 정적 페이지를 만들어 놓은 후 미리 생성된 정적 HTML 파일을 건네주는 게 SSR과 SSG의 차이점이야!

  • 그럼 CSR과 SSR의 단점의 두 마리 토끼를 다잡았으니 더 이상... 또 다른 단점이 존재하진 않겠지....?
    • 미안... SSG 방식도 단점이 존재하고 있어....
    • 위 표에 보면 데이터가 업데이트되면 재배포가 필요한 단점이 있어...!

5. SSG의 단점을 극복하고자 나온 방식이 ISR(Incremental-Static-Regeneration)이야 !

  • ISR이란 SSG의 단점을 보완하면서도 동적 콘테츠를 처리할 수 있는 유연한 방법을 제공하는 렌더링 기법이야!
  • revalidate 옵션을 사용해 일정 시간마다 데이터를 최신으로 받아온 후 업데이트 하여 재생성되도록 하는 옵션!!

........어려워.. 어려워!!!!!!!!!

CSR, SSR 방식의 lighthouse - 퍼포먼스 성능 차이 비교

자 먼저 Next.js 기준 CSR 방식의 코드를 보여줄게!

"use client";

import React, { useEffect, useState } from "react";
import Card from "./Card";
import { Article } from "../types/article";

export default function CSRPage() {
  const [fetchData, setFetchData] = useState<Article[]>([]);
  console.log("fetchData :", fetchData);

  useEffect(() => {
    const fetchData = async () => {
      const res = await fetch(`https://newsapi.org/v2/everything?q=bitcoin&apiKey=${process.env.NEXT_PUBLIC_NEWS_API_KEY}`);
      const { articles } = await res.json();
      console.log("data :", articles);
      setFetchData(articles);
    };

    fetchData();
  }, []);

  return (
    <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 relative h-[800px] gap-6 p-20">
      {fetchData.map((el, idx) => (
        <Card key={idx}>
          <Card.Title>{el.title}</Card.Title>
          <Card.Author>{el.author}</Card.Author>
          <Card.Content>{el.content}</Card.Content>
        </Card>
      ))}
    </div>
  );
}

CSR기준 Lighthouse 성능 지표 이미지

SSR기반 코드를 보여줄게!

import Card from "./Card";
import { Article } from "../types/article";

async function fetchData() {
  const res = await fetch(`https://newsapi.org/v2/everything?q=bitcoin&apiKey=${process.env.NEXT_PUBLIC_NEWS_API_KEY}`);

  const { articles } = await res.json();

  return articles;
}

export default async function SSRPage() {
  const data: Article[] = await fetchData();

  return (
    <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 relative h-[800px] gap-6 p-20">
      {data.map((el, idx) => (
        <Card key={idx}>
          <Card.Title>{el.title}</Card.Title>
          <Card.Author>{el.author}</Card.Author>
          <Card.Content>{el.content}</Card.Content>
        </Card>
      ))}
    </div>
  );
}

SSR기준 Lighthouse 성능 지표 이미지

  • 참고로 Next.js App Router버전은 모든 환경이 Server Component에서 동작되고 있어!
  • 위 테스트 과정은 배포된 상황에서 테스트 수치가 아닌 개발모드에서 테스트된 수치야!

느낀 점

  1. CSR을 활용할 때 SEO문제점을 해결하려면 SSR 혹은 SSG 방식을 사용하여 초기 완성된 페이지를 전달하여 검색엔진이 정보를 확인할 수 있게 하는 게 좋다고 생각한다.
  2. SSR이 필요한 프로젝트의 요구사항은 SEO가 중요한 프로젝트에서 SSR이 적합하다고 생각한다.
  3. SSG와 CSR의 주요 차이점은 SSG는 빌드 시점에 미리 모든 정적 페이지를 만들어 놓고 요청이 들어오면 미리 만들어 놓은 페이지를 전달하는 방식이라면 CSR은 먼저 빈 HTML화면을 파일을 브라우저에 렌더링 후 이후에 자바스크립트 파일을 사용해 DOM을 조작해 화면을 또 렌더링 한다는 과정에서 초기 로딩속도가 느리고 검색엔진에 불리하다는 게 내 생각이다.

 

내가 공부하고 느낀 점을 적는 블로그 형식이라 틀린 내용이 있을 수 있어.

위 블로그 내용을 보고 내용이 이상하다 싶으면 언제든 피드백은 환영이야! 

 

나는 소통을 좋아하는 사람이니깐 주저 말고 착한? 피드백만 해줘...!

 

그럼 다음 블로그 때 보자! 안녕👍