렌더링과 Tailwind CSS의 JIT

강연주·2025년 11월 8일

📚 TIL

목록 보기
181/186

웹 렌더링 파이프라인

[서버] HTML/CSS/JS 전달
     ↓
[브라우저 네트워크 계층] 파일 다운로드
     ↓
[브라우저 렌더링 엔진] HTML/CSS 파싱 → DOM/CSSOM 생성
     ↓
[렌더링 엔진] 스타일 계산 → Render Tree 생성
     ↓
[렌더링 엔진] Layout(배치)
     ↓
[렌더링 엔진] Paint(픽셀 그리기)
     ↓
[GPU] Composite(레이어 합성)
     ↓
[브라우저] 화면에 표시

브라우저의 화면 렌더링 과정

1. HTML 파싱 → DOM 생성
태그마다 노드가 생성됨.

2. CSS 파싱 → CSSOM 생성
스타일 시트 내용을 트리 구조로 만듦.

3. DOM + CSSOM = Render Tree 생성
각 요소가 어떤 스타일을 적용받는지 결정.

4. Layout(배치)
각 요소가 화면의 어디에 위치하고 얼마나 큰지 계산.

5. Paint(그리기)
각 요소를 픽셀 단위로 실제로 칠함.

6. Composite
레이어를 합성하고 브라우저에 표시.

📌 중요한 개념

  • DOM이 바뀌면 → Layout & Paint 다시 발생
  • CSS만 바뀌면 → Paint만 다시 발생 가능 (비교적 가벼움)

✅ 2. React 렌더링이 DOM을 “최소 변경”하는 원리

React는 단순 DOM 작업을 직접 하지 않고:

✅ UI = f(state) 함수라고 가정하고 렌더링

상태가 바뀌면 컴포넌트를 다시 함수처럼 실행해서 새로운 Virtual DOM을 만듦.

👉 하지만 바뀐 부분만 실제 DOM에 반영해야 효율적이니까…

✅ Virtual DOM diff(비교) 단계가 등장

React는:

이전 Virtual DOM

새로 계산한 Virtual DOM

둘을 비교함.

✅ className만 바뀌었으니
✅ 실제 DOM에서도 class 속성만 교체
✅ 실제 layout 계산도 paint 재실행도 최소화됨

즉, React가 하는 일은:

“리렌더링은 컴포넌트 함수 재실행일 뿐이고,
실제 DOM 변경은 diff에서 결정된다.”

그래서 성능이 좋아지는 것.

✅ 3. Virtual DOM에서 className 비교는 이렇게 일어남

리렌더링 시

이런 식으로 Virtual DOM 객체가 생성됨.

JS로 표현하면:

{
type: "button",
props: {
className: "rounded p-2 text-white bg-green-500",
children: "동적 버튼"
}
}

이전 Virtual DOM의 같은 버튼 노드와 비교해서:

태그 동일 → 계속 탐색

className 문자열 비교 → 변경됨

따라서 class 속성만 업데이트

→ DOM 속성 바꿔주는 정도면 매우 빠름
→ Layout과 Paint도 브라우저가 최소 범위만 다시 수행

✅ 4. Tailwind JIT는 “클래스 문자열만 보고 스타일 추가하는 엔진”

Tailwind JIT(Just-In-Time)는 이런 구조:

✅ 빌드 시

코드 안의 모든 Tailwind class 문자열을 스캔해서
필요한 CSS만 생성 (트리셰이킹)

예:
bg-green-500이 등장하면 그 CSS 정의만 만들어 둠.

✅ 런타임에는 “정적 CSS”만 브라우저가 읽음

Tailwind는 런타임에 JS 동작이 없음.
그냥 CSS만 브라우저가 적용.

즉,

React가 동적으로 className 문자열만 바꾸면
Tailwind는 그 문자열에 해당하는 CSS를 이미 빌드해 둔 걸 적용한다.

✅ 브라우저 입장에서는 그냥 CSS 클래스가 바뀌었을 뿐
✅ 새로운 JS 비용 없음
✅ Paint만 다시 일어나는 수준

그래서 Tailwind가 빠른 거야.

✅ 5. CSS-in-JS 는 왜 로딩이 느리고 Tailwind보다 무거운가?

CSS-in-JS (styled-components, emotion 등)는 Tailwind와 다름.

✅ CSS-in-JS는 런타임에 JS로 CSS를 생성

즉 이런 코드가:

const Button = styled.button background-color: ${(props) => props.color};;

실행될 때마다 JS가 DOM에

🚨 런타임 비용 발생

JS 실행 시간

DOM에 스타일 태그 주입 비용

해시 생성 비용

SSR 시에도 스타일 문자열 생성

Tailwind는 빌드 시 정적 CSS 생성 → 런타임 작업 0
CSS-in-JS는 런타임에 계속 JS가 실행됨 → 느리고 무거움

✅ 그래서 초기 로딩 시간에 차이가 크게 난다.
✅ Tailwind가 훨씬 빠르고 가볍다.

✅ 6. Server Components와 Tailwind/CSS-in-JS 관계

Next.js의 서버 컴포넌트는:

렌더링을 서버에서 하고

HTML을 완성된 형태로 브라우저에 전달함

JS 번들을 최소화할 수 있음

📌 Tailwind는 SSR과 특히 잘 맞음
→ 이미 CSS를 빌드 타임에 생성해 두기 때문
→ 서버는 그냥 class 문자열이 들어간 HTML을 전달하면 됨
→ 브라우저는 CSS 파일 한 번만 읽으면 됨

✅ JS 다운로드 감소
✅ 스타일 계산 비용 없음
✅ 초기 로딩 빠름

📌 반면 CSS-in-JS는 SSR 단계에서도 스타일을 “문자열로 생성”해야 함
→ 서버에서 스타일 생성 JS 실행
→ 클라이언트에서 hydration할 때도 JS 필요
→ 번들 사이즈 커짐
→ 느림

✅ 7. 전체 흐름을 하나의 그림으로 정리하면
🔷 Tailwind + React + 브라우저 렌더링 흐름

Tailwind가 빌드 시 필요한 CSS만 만들어 둠

React는 리렌더링 시 Virtual DOM만 재계산

Diff에서 달라진 className만 실제 DOM에 패치

브라우저는 변경된 스타일만 다시 Paint

초기 로딩도 빠르고, 동적 업데이트도 빠름

🔶 CSS-in-JS 흐름

런타임에 JS로 스타일 생성

DOM에 style 태그 삽입

브라우저가 다시 CSSOM 생성

Paint

런타임 비용으로 초기 로딩이 느림

CSR, SSR 모두 JS 비용이 큼

profile
아무튼, 개발자

0개의 댓글