9장: 웹 브라우저 동작 원리
📋 강의 개요
- 소요 시간: 약 4-6시간
- 난이도: ⭐⭐⭐ 중급
- 선수 지식: HTML, CSS, JavaScript, HTTP
- 학습 목표: 브라우저가 웹 페이지를 렌더링하는 과정을 이해하고 성능 최적화를 할 수 있다.
📚 9.1 브라우저 구조
강의 포인트
- 브라우저는 여러 컴포넌트로 구성
- 각 컴포넌트의 역할 이해
- 렌더링 엔진이 핵심
브라우저 주요 컴포넌트
┌────────────────────────────────────────┐
│ 사용자 인터페이스 (UI) │ ← 주소창, 북마크, 뒤로가기 등
├────────────────────────────────────────┤
│ 브라우저 엔진 │ ← UI와 렌더링 엔진 사이 중개
├────────────────────────────────────────┤
│ 렌더링 엔진 ⭐ │ ← HTML, CSS를 화면에 표시
├────────────────────────────────────────┤
│ 네트워킹 │ ← HTTP 요청 처리
├────────────────────────────────────────┤
│ JavaScript 인터프리터 (V8 등) │ ← JavaScript 실행
├────────────────────────────────────────┤
│ UI 백엔드 │ ← 기본 위젯 그리기
├────────────────────────────────────────┤
│ 데이터 저장소 │ ← Cookie, LocalStorage 등
└────────────────────────────────────────┘
주요 렌더링 엔진
| 브라우저 | 렌더링 엔진 | JavaScript 엔진 |
|---|---|---|
| Chrome | Blink | V8 |
| Firefox | Gecko | SpiderMonkey |
| Safari | WebKit | JavaScriptCore |
| Edge (구) | EdgeHTML | Chakra |
| Edge (신) | Blink | V8 |
📚 9.2 렌더링 엔진의 동작 과정
강의 포인트
- 렌더링은 여러 단계로 진행
- Critical Rendering Path (중요 렌더링 경로)
- 최적화의 기본
렌더링 과정 (전체 흐름)
1. HTML 파싱 → DOM 트리 생성
↓
2. CSS 파싱 → CSSOM 트리 생성
↓
3. DOM + CSSOM → 렌더 트리 생성
↓
4. 레이아웃 (Layout/Reflow) → 위치 계산
↓
5. 페인트 (Paint) → 픽셀로 그리기
↓
6. 합성 (Composite) → 레이어 합성
단계별 상세 설명
1단계: HTML 파싱 → DOM 트리
<!DOCTYPE html>
<html>
<head>
<title>제목</title>
</head>
<body>
<div class="container">
<h1>안녕하세요</h1>
<p>내용입니다</p>
</div>
</body>
</html>
DOM 트리:
html
├─ head
│ └─ title
│ └─ "제목"
└─ body
└─ div.container
├─ h1
│ └─ "안녕하세요"
└─ p
└─ "내용입니다"
파싱 과정:
- 바이트 → 문자 → 토큰 → 노드 → DOM 트리
2단계: CSS 파싱 → CSSOM 트리
body {
font-size: 16px;
}
.container {
width: 800px;
margin: 0 auto;
}
h1 {
font-size: 32px;
color: blue;
}
CSSOM 트리:
body
├─ font-size: 16px
└─ .container
├─ width: 800px
├─ margin: 0 auto
└─ h1
├─ font-size: 32px
└─ color: blue
특징:
- CSS는 렌더링을 차단 (Render Blocking)
- 외부 CSS는 다운로드 완료까지 대기
3단계: 렌더 트리 생성
DOM + CSSOM → 렌더 트리
렌더 트리:
- 화면에 표시될 노드만 포함
- display:none → 제외
- visibility:hidden → 포함 (공간 차지)
- head, script → 제외
렌더 트리 예시:
body (font-size: 16px)
└─ div.container (width: 800px)
├─ h1 (font-size: 32px, color: blue)
└─ p (font-size: 16px)
4단계: 레이아웃 (Layout/Reflow)
각 노드의 정확한 위치와 크기 계산
예:
div.container
x: 300px (화면 중앙)
y: 0px
width: 800px
height: 200px
h1
x: 300px (부모 기준)
y: 0px
width: 800px
height: 50px
레이아웃이 다시 발생하는 경우:
- 요소 추가/삭제
- 크기/위치 변경
- 창 크기 변경
- 폰트 크기 변경
5단계: 페인트 (Paint)
실제 픽셀로 그리기
- 배경색
- 테두리
- 그림자
- 텍스트
- 이미지
등을 그림
페인트가 다시 발생하는 경우:
- 색상 변경
- 배경 이미지 변경
- box-shadow 변경
6단계: 합성 (Composite)
여러 레이어를 합쳐서 최종 화면 생성
레이어:
- position: fixed
- transform: translate3d()
- opacity
- will-change
등이 별도 레이어 생성
📚 9.3 DOM 트리 / CSSOM 트리
DOM (Document Object Model)
// JavaScript로 DOM 조작
const div = document.createElement('div');
div.textContent = '새로운 내용';
document.body.appendChild(div);
// DOM 트리가 변경됨 → 리플로우 발생
CSSOM (CSS Object Model)
// JavaScript로 스타일 조작
const element = document.querySelector('.box');
element.style.width = '500px'; // CSSOM 변경 → 리플로우
// 계산된 스타일 가져오기
const styles = window.getComputedStyle(element);
console.log(styles.width); // "500px"
📚 9.4 렌더 트리
렌더 트리 구성 규칙
<div style="display: none;">
<p>보이지 않음</p>
</div>
<div style="visibility: hidden;">
<p>공간은 차지</p>
</div>
<div>
<p>보임</p>
</div>
렌더 트리:
- display:none div → 제외 ❌
- visibility:hidden div → 포함 ✅ (공간 차지)
- 일반 div → 포함 ✅
📚 9.5 리플로우(Reflow)와 리페인트(Repaint)
강의 포인트
- 리플로우는 비용이 매우 높음
- 리페인트는 상대적으로 가벼움
- 최적화의 핵심은 리플로우 최소화
리플로우 (Reflow / Layout)
레이아웃 재계산 (위치, 크기)
발생 조건:
✅ DOM 추가/삭제
✅ 요소 크기/위치 변경
✅ 폰트 크기 변경
✅ 창 크기 변경
✅ 애니메이션
✅ offsetWidth, scrollTop 등 속성 읽기 (강제 리플로우!)
리플로우 발생 예:
// ❌ 나쁜 예 (3번의 리플로우)
element.style.width = '100px'; // 리플로우
element.style.height = '200px'; // 리플로우
element.style.margin = '10px'; // 리플로우
// ✅ 좋은 예 (1번의 리플로우)
element.style.cssText = 'width: 100px; height: 200px; margin: 10px;';
// ✅ 더 좋은 예 (클래스 사용)
element.classList.add('new-style');
리페인트 (Repaint)
픽셀 다시 그리기 (색상 등)
발생 조건:
✅ 색상 변경
✅ 배경 이미지 변경
✅ 가시성 변경
리페인트만 발생 예:
element.style.color = 'red'; // 리페인트만
element.style.backgroundColor = 'blue'; // 리페인트만
element.style.visibility = 'hidden'; // 리페인트만
리플로우 vs 리페인트 비교
| 작업 | 리플로우 | 리페인트 | 성능 |
|---|---|---|---|
| width, height 변경 | ✅ | ✅ | 느림 😢 |
| margin, padding 변경 | ✅ | ✅ | 느림 😢 |
| position 변경 | ✅ | ✅ | 느림 😢 |
| color 변경 | ❌ | ✅ | 빠름 😊 |
| background 변경 | ❌ | ✅ | 빠름 😊 |
| transform | ❌ | ❌ | 매우 빠름 😍 |
| opacity | ❌ | ❌ | 매우 빠름 😍 |
강제 동기 레이아웃 (Forced Synchronous Layout)
// ❌ 매우 나쁜 예 (강제 리플로우 반복!)
const boxes = document.querySelectorAll('.box');
boxes.forEach(box => {
box.style.width = box.offsetWidth + 10 + 'px';
// offsetWidth 읽기 → 즉시 리플로우 발생!
// 다음 반복에서 또 리플로우... (N번 리플로우)
});
// ✅ 좋은 예 (읽기와 쓰기 분리)
const boxes = document.querySelectorAll('.box');
const widths = [];
// 1. 먼저 모든 값 읽기 (1번 리플로우)
boxes.forEach(box => {
widths.push(box.offsetWidth);
});
// 2. 값 설정 (1번 리플로우)
boxes.forEach((box, i) => {
box.style.width = widths[i] + 10 + 'px';
});
리플로우를 발생시키는 속성들
// ⚠️ 이 속성들을 읽으면 즉시 리플로우 발생!
element.offsetTop
element.offsetLeft
element.offsetWidth
element.offsetHeight
element.scrollTop
element.scrollLeft
element.scrollWidth
element.scrollHeight
element.clientTop
element.clientLeft
element.clientWidth
element.clientHeight
window.getComputedStyle(element)
📚 9.6 브라우저 캐싱
강의 포인트
- 캐싱은 성능 향상의 핵심
- 여러 종류의 캐시 존재
- Cache-Control 헤더가 중요
캐시의 종류
1. 메모리 캐시 (Memory Cache)
RAM에 저장
- 매우 빠름
- 탭 닫으면 사라짐
- 용량 제한
예: 이미지, 스크립트
2. 디스크 캐시 (Disk Cache)
하드디스크에 저장
- 빠름
- 브라우저 재시작 후에도 유지
- 용량 큼
예: CSS, 폰트, 큰 이미지
3. Service Worker 캐시
오프라인 지원
- 개발자가 직접 제어
- PWA의 핵심
캐시 제어 (Cache-Control)
Cache-Control: max-age=3600
→ 3600초(1시간) 동안 캐시
Cache-Control: no-cache
→ 캐시 사용 전에 서버에 확인 (304 응답 가능)
Cache-Control: no-store
→ 절대 캐시 안 함
Cache-Control: public
→ 모든 캐시(프록시 포함)에 저장 가능
Cache-Control: private
→ 브라우저만 캐시 가능
ETag (Entity Tag)
리소스 버전 관리
1. 서버: ETag: "abc123" (리소스 해시값)
2. 클라이언트: 캐시 저장
3. 다음 요청: If-None-Match: "abc123"
4. 서버: 변경 없으면 304 Not Modified
변경 있으면 200 OK + 새 데이터
캐시 무효화 전략
// 파일명에 버전/해시 포함
<link href="style.v1.2.3.css">
<link href="style.abc123.css">
// 쿼리 스트링 사용 (권장 안 함)
<link href="style.css?v=1.2.3">
📚 9.7 개발자 도구 (DevTools) 사용법
강의 포인트
- 개발자 도구는 필수 스킬
- 각 패널의 용도 이해
- 디버깅과 성능 분석
Elements 패널 ⭐
- DOM 트리 확인
- 스타일 실시간 수정
- 계산된 스타일 확인
- 이벤트 리스너 확인
실습:
- F12 → Elements
- 요소 선택 (Ctrl+Shift+C)
- Styles 탭에서 실시간 수정
Console 패널 ⭐⭐⭐
// 로그 출력
console.log('일반 로그');
console.warn('경고');
console.error('에러');
// 객체 출력
console.table([{name: '홍길동', age: 25}]);
// 시간 측정
console.time('작업');
// ... 작업 ...
console.timeEnd('작업'); // 작업: 123.45ms
// 그룹화
console.group('사용자 정보');
console.log('이름: 홍길동');
console.log('나이: 25');
console.groupEnd();
Network 패널 ⭐
- HTTP 요청/응답 확인
- 로딩 시간 분석
- 캐시 확인
- 상태 코드 확인
확인 항목:
- Status: 200, 304, 404 등
- Type: document, script, xhr, img 등
- Size: 파일 크기
- Time: 로딩 시간
- Waterfall: 타임라인
Performance 패널 ⭐
성능 분석
1. 녹화 시작
2. 페이지 조작
3. 녹화 중지
4. 분석:
- FPS (초당 프레임)
- CPU 사용률
- 스크린샷
- 타이밍
Application 패널
- LocalStorage
- SessionStorage
- Cookies
- IndexedDB
- Service Workers
- Cache Storage
Lighthouse 패널 ⭐
웹 사이트 품질 측정
점수:
- Performance (성능)
- Accessibility (접근성)
- Best Practices (모범 사례)
- SEO (검색 엔진 최적화)
개선 사항 제안
🎯 성능 최적화 팁
1. 리플로우 최소화
// ❌ 나쁜 예
for (let i = 0; i < 100; i++) {
const div = document.createElement('div');
document.body.appendChild(div); // 100번 리플로우!
}
// ✅ 좋은 예
const fragment = document.createDocumentFragment();
for (let i = 0; i < 100; i++) {
const div = document.createElement('div');
fragment.appendChild(div);
}
document.body.appendChild(fragment); // 1번 리플로우!
2. 클래스 사용
// ❌ 나쁜 예
element.style.width = '100px';
element.style.height = '200px';
element.style.backgroundColor = 'red';
// ✅ 좋은 예
element.classList.add('styled');
3. transform 사용
/* ❌ 나쁜 예 (리플로우 발생) */
.box {
left: 0;
transition: left 1s;
}
.box:hover {
left: 100px;
}
/* ✅ 좋은 예 (합성만 발생) */
.box {
transform: translateX(0);
transition: transform 1s;
}
.box:hover {
transform: translateX(100px);
}
4. will-change 사용
/* 미리 레이어 생성 (애니메이션 전) */
.box {
will-change: transform, opacity;
}
/* 애니메이션 후 제거 */
.box {
will-change: auto;
}
5. 이미지 최적화
<!-- 반응형 이미지 -->
<img src="small.jpg"
srcset="small.jpg 500w, medium.jpg 1000w, large.jpg 2000w"
sizes="(max-width: 600px) 500px, (max-width: 1200px) 1000px, 2000px">
<!-- Lazy Loading -->
<img src="image.jpg" loading="lazy">
6. 스크립트 로딩 최적화
<!-- ❌ 나쁜 예 (렌더링 차단) -->
<head>
<script src="app.js"></script>
</head>
<!-- ✅ 좋은 예 -->
<head>
<script src="app.js" defer></script> <!-- HTML 파싱 후 실행 -->
<script src="ads.js" async></script> <!-- 비동기 로드 -->
</head>
<!-- ✅ 또는 body 끝에 -->
<body>
<!-- 내용 -->
<script src="app.js"></script>
</body>
7. CSS 최적화
<!-- Critical CSS를 인라인으로 -->
<head>
<style>
/* 첫 화면에 필요한 최소한의 CSS */
body { margin: 0; font-family: Arial; }
.header { background: #333; color: white; }
</style>
<!-- 나머지 CSS는 비동기 로드 -->
<link rel="preload" href="styles.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
</head>
🎯 강의 진행 팁
1-2교시: 브라우저 구조와 렌더링 (2시간)
- 브라우저 컴포넌트
- 렌더링 과정
- 실습: DevTools로 확인
3-4교시: 리플로우와 최적화 (2시간)
- 리플로우/리페인트
- 성능 측정
- 실습: 성능 개선
5-6교시: 캐싱과 DevTools (2시간)
- 캐시 전략
- DevTools 마스터
- 실습: Lighthouse 분석
📝 종합 실습: 성능 최적화
// 최적화 전
function updateList(items) {
const list = document.querySelector('#list');
list.innerHTML = ''; // 리플로우
items.forEach(item => {
const li = document.createElement('li');
li.textContent = item;
list.appendChild(li); // 매번 리플로우!
});
}
// 최적화 후
function updateListOptimized(items) {
const list = document.querySelector('#list');
const fragment = document.createDocumentFragment();
items.forEach(item => {
const li = document.createElement('li');
li.textContent = item;
fragment.appendChild(li); // 메모리에서만 작업
});
list.innerHTML = ''; // 1번 리플로우
list.appendChild(fragment); // 1번 리플로우
// 총 2번의 리플로우만!
}
9장 웹 브라우저 동작 원리를 완료했습니다! 🎓
🎉 축하합니다!
전체 웹 기초 강의를 모두 완료했습니다!
이제 여러분은:
- ✅ HTML로 구조를 만들 수 있습니다
- ✅ CSS로 디자인할 수 있습니다
- ✅ JavaScript로 동적 기능을 구현할 수 있습니다
- ✅ DOM을 조작할 수 있습니다
- ✅ 비동기 처리를 할 수 있습니다
- ✅ jQuery를 사용할 수 있습니다
- ✅ HTTP 통신을 이해합니다
- ✅ 브라우저 동작 원리를 이해합니다
다음 단계: React, Vue, Node.js 등의 프레임워크로 발전하세요! 🚀