o-ohi-code 님의 블로그

브라우저, CSR, SSR 렌더링 과정 본문

실무

브라우저, CSR, SSR 렌더링 과정

o-ohi-code 2025. 4. 17. 00:00

 

브라우저 렌더링 과정?, CSR, SSR 렌더링 과정? 잉 SPA, MPA? SEO 는 또 뭐고? 

실무의 길은 멀고도 험난한 것 같다. 

 

맨날 공부하고 그때는 분명 이해하고 넘어간 개념인데도, 지나가면 또 까먹어버리는 내 모습이 보인다.

 

그래서 오늘은 명확하게 브라우저의 렌더링 과정을 정리해보려고 한다.

 

 브라우저는 사용자가 웹에 접근하고, 웹페이지를 표시해주는 소프트웨어이다.

  ex) 대표적인 웹브라우저로 Chrome, Safari, Edge.. 등이 있다.

 

 렌더링(Rendering)코드(HTML, CSS, JS 등) 을 눈에 보이는 화면으로 바꾸는 과정이다.

 

브라우저의 렌더링 과정

 1. HTML 파싱 → DOM 생성

 2. CSS 파싱 → CSSOM 생성

 3. DOM + CSSOM → Render Tree 생성

 4. Layout (위치 계산)

 5. Paint (색, 모양 계산)

 6. Composite  순서로 진행된다.

 

참고로 브라우저는 HTML, CSS, JS를 해석하고 처리하는 기능을 모두 가지고 있다. 

 

이렇게 과정만 본다면 실무나, 프로젝트를 진행할 때 내가 제대로 적용하고 있는 것인지 의문이 들기 마련이다.

따라서 내가 진행했던 프로젝트를 예시로 들어 렌더링 과정이 어떻게 진행되고 있는지 정리해 보았다.

 

렌더링

 우선 URL 을 입력한다. 

   ㄴ /board 페이지 들어갔다고 가정하면, 화면에는 (게시글 제목, 작성자, 날짜 등) 이 보일 것이다.

   이런 과정을 렌더링 이라고 한다.

  

이때 브라우저는 다음 순서로 작동하게 된다.

1. HTML 파싱 - DOM tree 생성

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vite + React</title>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/main.jsx"></script>
  </body>
</html>

 

브라우저는 이걸 해석해서 DOM Tree 를 이렇게 구성한다.

├── html (lang="en")
│   ├── head
│   │   ├── meta (charset="UTF-8")
│   │   ├── link (rel="icon", href="/vite.svg")
│   │   ├── meta (name="viewport", content="width=device-width, initial-scale=1.0")
│   │   └── title ("Vite + React")
│   └── body
│       ├── div#root
│       └── script (type="module", src="/src/main.jsx")

 

DOM(Document Object Model) : HTML 문서를 트리 구조의 객체로 표현한 것을 말한다.

 구지 트리 구조를 만드는 이유

   - 화면에 그릴 준비를 하기 위해. (이렇게 DOM Tree) 형식으로 변경 하지 않으면 렌더링을 할 수가 없기 때문이다.

   - 자바스크립트로 조작 가능하게 하기 위해 (document.querySelector, element.innerHTML 등).

   - 브라우저 내부 렌더링 엔진이 이 구조를 바탕으로 스타일 계산, 레이아웃, 페인트, 화면 출력까지 이어가기 때문이다.

즉, DOM Tree 는 화면을 구성하고, 바꾸고, 그릴 수 있게 해주는 기반 구조인 것이다.

 

DOM Tree 이건 아직 "비어 있는 틀" 로, React가 들어오기 전까지는 아무것도 안 보인다.

 

 여기서 비어 있는 틀은 React가 아무것도 렌더링 안 했을 때, DOM Tree 상으로는 div#root 만 있는 상태를 말한다.

 이 상태를 흔히 비어 있는 틀 이라고 표현하는데,

 

 div#root는 React가 UI를 렌더링 할 준비가 된 빈 공간(틀) 을 의미하고,  

 React가 실행되면 그 안에 실제 UI 요소들이 채워지면서 동작하는 것이다.


2. CSS 파싱 → CSSOM 생성

 css 를 읽어서 각 요소에 적용할 스타일을 분석한다.

 ex) 예를들어 board.css 의 일부분이 있다고 가정 

.pagination-button {
    padding: 10px 16px;
    background-color: #e0e0e0;
    color: #333;
    border: none;
    border-radius: 10px;
    cursor: pointer;
    font-weight: 600;
    font-size: 15px;
    transition: all 0.2s ease;
}

이 스타일 정보들을 구조화해서 만든 게 CSSOM 이다 "어떤 태그에 어떤 스타일이 적용돼야 하는지" 정리된 스타일 트리


3. DOM (DOM Tree) + CSSOM → Render Tree 생성

 React가 root 안에 아래처럼 컴포넌트를 렌더링한다고 가정하면

<div className="container">
            <h2>📋 게시글 목록</h2>

            <table className="board-table">
                <thead>
                    <tr>
                        <th>
                            <input
                                type="checkbox"
                                checked={selectedPosts.length === boardList.length}
                                onChange={handleSelectAllChange}
                            />
                        </th>
                        <th>글 번호</th>
                        {columns.map(col => (
                            <th key={col.field}>{col.headerName}</th>
                        ))}
                    </tr>
                </thead>
                <tbody>{renderTableBody()}</tbody>
            </table>

            <div style={{ marginTop: "20px" }}>{renderPagination()}</div>


            <div className="button-group">
                <button className="btn-write" onClick={() => navigate('/write')}>글 쓰기</button>
                <button className="btn-delete" onClick={handleDelete}>삭제</button>
                <button className="btn-logout" onClick={handleLogout}>로그아웃</button>
            </div>
        </div>
div.container
├── h2 ("📋 게시글 목록")
├── table.board-table
│   ├── thead
│   │   └── tr
│   │       ├── th (checkbox input)
│   │       ├── th ("글 번호")
│   │       └── th * N (columns.map 결과)
│   └── tbody (renderTableBody() 결과들)
├── div (pagination 들어가는 곳)
├── div.button-group
│   ├── button.btn-write
│   ├── button.btn-delete
│   └── button.btn-logout

 

위의 코드가 DOM Tree 형식으로 변형되어 들어온 결과이고,

CSSOM 에 따라 각 태그에 어떤 색/폰트/크기 쓸지 결정 

-> 이걸 합쳐서 Render Tree 를 만든다.

 

 Render Tree

 

  무엇을 어떻게 보여줄지를 정리한 브라우저의 내부 구조이다.

   DOM 무엇이 있는가, CSSOM 어떻게 보여줄 것인가, 이 둘을 결합해서 어떻게 그릴지를 정리한 최종 구조이다.


4. Layout 단계 (Reflow)

 각 요소의 크기와 위치를 계산한다.

  ex) th는 몇 px, td 는 줄 바꿈, 글자가 많은 경우 줄 바꿈, 좌우 여백은?

    ㄴ 이런 모든 걸 계산해서 화면 상 위치 좌표로 변경해준다.


5. Paint 단계

 색, 테두리, 그림자 등 시각적 정보들을 픽셀로 변환한다.

  ex) 배경 파랑색 (#007bff)..

    ㄴ 브라우저는 요소들을 각 픽셀로 색칠하기 시작한다.


6. Composite 단계

 Paint 한 것들을 레이어 별로 합쳐서 최종 화면에 보여준다.

  ex) 테이블 하나, 글쓰기 버튼 하나, 페이지 버튼들 하나

    ㄴ 각 레이어가 나뉘고, 브라우저는 이걸 합쳐서 나의 게시판 페이지를 완성 한다.

 

브라우저의 렌더링 흐름을 간단히 정리하면

HTML 파싱 HTML 코드를 읽어서 DOM Tree(문서 구조 트리) 생성

CSS 파싱 <style> 또는 CSS 파일을 해석해서 CSSOM Tree(스타일 구조 트리) 생성

DOM + CSSOM → Render Tree 생성 어떤 요소를 어떻게 보여줄지 정리 보이지 않는 요소(ex. display: none)는 제외됨

Layout 단계 각 요소의 위치와 크기 계산 페이지의 실제 좌표 정보 생성

Paint 단계 색상, 글꼴, 테두리, 배경 등 시각 정보 픽셀로 표현

Compositing 단계 여러 레이어를 하나로 합성해서 화면에 출력

 

하지만 이렇게 브라우저 렌더링만 사용하면 좋았겠지만.. 

기술이 발전하면서 웹은 단순히 보여주기가 아닌 앱처럼 작동하기로 진화해 버렸다. 

CSR/ SSR이 필요한 이유 

 옛날) 블로그, 뉴스, 회사 홈페이지 같은 (정적인 정보만) 있었다면,

 현재) 쇼핑몰, 채팅, 실시간 피드, 사용자 인증 등 

   ㄴ 페이지 전환 많고, 실시간 반응 빠르고, 유저마다 화면이 다르기 때문에 

 

 문제가 발생한다.

  로딩이 느리거나, SEO 가 안 돼거나, 서버 부하 문제, 사용자마다 다르게 보여지는 페이지

  

 때문에 이 문제를 해결하는 전략이 CSR, SSR 인 것이다.

CSR 렌더링 과정

 CSR (Client Side Rendering) :

  브라우저(클라이언트)에서 JS가 실행된 후, 그 JS가 DOM을 만들고, 브라우저가 화면을 그리는 방식

  ex) Facebook, csr 기반 spa, React로 구성, 초기 로딩 후 빠르게 동작

        Instagram, 게시물 클릭 시 전체 페이지 새로고침 없이 뷰만 변경

        Gmail(웹 버전), 메일 리스트에서 메일 보기로 이동해도 페이지 새로고침 없음

[CSR 렌더링]

1. URL 요청 → 서버 응답 → HTML 빈 껍데기만 전달
  	→ /public/index.html or /root/index.html 
  
2. HTML 파싱 시작 <HTML>, <Body> 등 DOM 으로 변환 시작 
	→ 파싱을 하는 이유
    	HTML은 항상 *텍스트 형태* 로 전송된다.
        ex) <body>
              <h1>안녕하세요</h1>
            </body>
        이건 그냥 문자열일 뿐, DOM Tree 로 변경해줘야 
        렌더링 엔진이 그걸 Layout + Painter 할 수 있다.
    
3. <script src=...> 만남 → 즉시 JS 파일 요청 (다운로드 시작)

4. 파싱 진행 <div id="root"> 등 DOM Tree 구성

5. HTML 파싱 완료 → DOM Tree 일단 완성 (비어 있지만 구조는 존재)

6. 3)에서 다운로드 완료한 JS 파일 실행 → (main.jsx)

7. react가 실행 → DOM 조작 
	→ Virtual DOM → 실제 DOM → #root 에 삽입
    
8. 브라우저 렌더링 
 	→ Render Tree 생성 - Layout - Paint - 화면에 나타난다.

 

 여기서 헷갈리면 안되는 건 CSR에서도 렌더링은 브라우저가 한다.

   무슨 말이냐고 하면,

   위의 6번을 보면, csr 은 브라우저에서 js가 실행되어 DOM을 만들고, 

   그 DOM 을 브라우저 렌더링 엔진이 화면에 그려주는 방식이다.

 

   결국 렌더링의 "타이밍""재료 생성 주체" 만 다를 뿐, 렌더링 자체는 언제나 브라우저가 한다는 것이다. 

 SSR 렌더링 과정

SSR (Server Side Rendering) :

  서버에서 미리 완성된 HTML을 만들어 보내고,

  브라우저는 그걸 받아서 곧바로 화면을 그리는 방식이다.

  

  ex) 네이버, 빠른 초기 로딩 & SEO 최적화를 위해 SSR 사용

        블로그 플랫폼, 검색 노출 중요 SSR 기반

        쿠팡, 상품 정보 SEO 를 위해 SSR 적용

 

  여기서 중요한 점 

    SSR은 이미 완성된 HTML 즉, 렌더링이 완료된 결과를 받게 되는 것이다.

      ㄴ 이 html은 React 같은 라이브러리를 서버에서 실행해 미리 렌더링 된 결과로,

          브라우저는 이를 받자마자 바로 DOM 으로 파싱하고 화면에 표시할 수 있다.

 

    반면, CSR은 빈 HTML 을 받고, 이후 브라우저에서 JS 실행 DOM 을 생성하여 브라우저가 렌더링하는 구조로 SSR 과는 차이가 있다.

[SSR 렌더링]

0. URL 요청
   → 서버가 HTML 전체 생성해서 응답 (내용 포함)

1. HTML 다운로드 시작

2. HTML 파싱하면서...
   → <link> 만나면 CSS 요청
   → <script> 만나면 JS 요청 (다운로드 시작)

3. CSS 수신 → CSSOM 생성

4. HTML 파싱 완료 → DOM Tree 완성 (내용 포함된 상태)

5. DOM + CSSOM → Render Tree 생성

💥 이 시점에서 이미 화면에 내용 보일 수 있음 (JS 없이도!)

6. JS 실행
   → React/Vue 실행
   → 상태/이벤트 연결을 위한 hydrate 작업 수행

7. 최종 렌더링 완료 (동작 가능 상태 완성)

 

여기서 hydrate는 서버가 보낸 정적 HTML 에 React 가 상태와 이벤트 등을 연결하여 "앱답게" 만들어 주는 과정이다.

 

 

하지만 이렇게 끝나면 좋겠지만, 

렌더링이라는 건 단순히 브라우저 안에서만 일어나는 일이 아니었다.

 

React, Vue 같은 프레임워크를 공부하다 보면 

앞에서 다루었던 CSR, SSR 같은 단어가 수시로 등장하고, 이걸 이해하기 위해선

자연스럽게 정적 vs 동적 렌더링의 개념도 알아야 한다.

 

게다가 프로젝트를 구성하는 방식에 따라서는 SPA(Single Page Application)MPA(Multi Page Applicaion)로 나뉘기도 한다.

 

  • 어떤 방식으로 렌더링이 시작되는지, 
  • 누가 처음 화면을 만들어 주는지 (브라우저? 서버?)
  • 페이지 전환은 어떻게 처리 되는지
  • 정적 HTML 파일만 있으면 되나? 아니면 서버에서 그때그때 데이터를 받아야 하나? 

이런 모든 개념이 서로 얽혀 있고, 결국 웹을 설계하는 방식, 즉 아키텍처 와도 연결되어 있다.

 

아키텍처 란?

https://o-ohi-code.tistory.com/53

 

그럼 차근차근 용어들을 짚고 넘어가보자.

정적 HTML, 동적 HTML

 정적 HTML (Static HTML) :

   미리 만들어진 HTML 파일, 서버는 파일을 그냥 "전달" 만 한다.

   ex) index.html, about.html 같은 걸 서버가 바로 응답

 

 동적 HTML (Dynamic HTML) :

   사용자 요청에 따라 서버가 HTML 을 생성해 준다. 서버에서 템플릿 엔진, React Server 등 실행

   ex) 로그인한 유저 정보에 따라 HTML 이 다르게 만들어진다.

 

그럼 자연스럽게 드는 의문이 있다. SSR, CSR 은 정적일까? 동적일까? 

 

  SSR은 동적 HTML 을 생성해주는 방식이다.

  • ssr은 사용자의 상태, 권한, 데이터에 따라
  • React를 서버에서 실행해서 그때 그때 HTML을 생산 해서 보내주는 방식이다.
  • 이게 바로 정적인 파일이 아닌 "동적 HTML" 이다.

 CSR 은 정적 HTML 울 생성해주는 방식이다.

  • CSR은 정적 HTML(빈 껍데기) 와 정적 JS 만 받는다.
  • 그걸 브라우저가 실행해서 동적으로 DOM 을 구성한다.
  • 따라서 CSR의 HTML 자체는 정적이지만
  • 실제 페이지는 브라우저에서 동적으로 만들어지게 된다.

  즉, SSR 은 동적 HTML 을 서버에서 만들어준 것,

       CSR = 정적 HTML + 정적 JS → 브라우저가 실행해서 동적 화면 구성이 되는 원리이다.

SPA vs MPA

  SPA (Single Page Application) :

  • 한 개의 HTML 로 모든 페이지 구성
  • js가 내부적으로 라우팅 (새로고침 X) 
  • 부드럽고 빠름
  •  React, Vue, Angular (SPA 기반 프레임워크)
  • ex) Gmail, Facebook, Notion

  MPA (Multi Page Application) : 

  • 페이지마다 HTML 따로 존재
  • URL 이동 시 브라우저 전체 새로고침
  • 전통적, 약간 딜레이
  • JSP, PHP, Django, Thymeleaf 등 템플릿 기반
  • ex) 네이버 뉴스, 쇼핑몰 카테고리 이동 등
  • 네이버 메인 페이지 (www.naver.com), 네이버 뉴스 (news.naver.com), 네이버 블로그 (blog.naver.com) 이런 느낌으로 페이지 마다 HTML 이 따로 존재한다.

 핵심은 페이지 전환 방식이다.

   spa는 라우팅이 JS 기반 (클라이언트 내부에서 처리)

   mpa 는 라우팅이 서버 기반(브라우저가 새 요청 보냄)

 

하지만 주의애햐 할 점은 csr은 무조건 spa 이고, ssr은 무조건 mpa 인 것은 아니라는 것이다.

 

실제 구조는 

  csr + spa 가 많지만, csr + mpa 도 가능하고,

  ssr + spa 구조도 존재한다 (next.js 등)

  React는 ssr, mpa 도 구현 가능 하다. (next.js, react server Components 등)

 

예시로 설명하면

  1. React + CRA (create-react-app)  

       → CSR + SPA  (가장 일반적인 React)

  2. React + Next.js (정적 생성)

       → SSR + SPA (서버에서 HTML을 만들어 주지만, 한 페이지 구조)

  3. React + Next.js (MPA 구성)

       → SSR + MPA (페이지마다 서버에서 HTML 생성, 사용자 요청마다 별도 렌더링 발생 → MPA 처럼 동작)

  4. React + Vite + React Router + 서버 분리 (**현재 내 프로젝트**)

       → CSR + SPA (전통적 방식)

 

정리하면

🔹 정적 vs 동적 렌더링 (HTML 생성 방식에 따라 나뉜다)

 정적 렌더링은 미리 만들어진 HTML을 그대로 제공
 동적 렌더링은 요청 시점에 HTML을 생성해서 제공

 

🔹 CSR vs SSR (렌더링 타이밍과 위치)

 CSR은 브라우저가 JS로 DOM을 만들어서 화면을 그리는 방식
 SSR은 서버가 HTML을 완성해서 보내고, 브라우저가 바로 렌더링

 

🔹 SPA vs MPA (페이지 구성 & 전환 방식)

 SPA는 HTML 한 장으로 JS가 라우팅과 전환을 처리하는 방식
 MPA는 URL 이동마다 HTML 파일을 새로 요청하는 방식

 

 

'실무' 카테고리의 다른 글

MES 란?  (0) 2025.04.03
MSA 란?  (0) 2025.04.03