브라우저 동작 원리 (Browser Rendering)

한눈에 보기

브라우저가 URL을 받아 서버에서 자원을 가져오고 HTML, CSS, 자바스크립트를 해석해 픽셀로 바꾸는 과정을 다룬다. DNS 조회부터 파싱, 레이아웃, 페인트, 합성까지 렌더링 파이프라인과 이벤트 루프를 설명한다.

최종 업데이트 2026.09.17

브라우저는 주소창에 입력된 URL(Uniform Resource Locator)을 받아 서버에서 자원을 가져오고, HTML과 CSS, 자바스크립트(JavaScript)를 해석해 화면의 픽셀로 바꾸는 프로그램이다. 이 과정은 크게 네트워크 요청 단계와 렌더링 파이프라인 단계로 나뉘며, 렌더링은 파싱, 스타일 계산, 레이아웃, 페인트, 합성의 순서로 진행된다. 각 단계가 어디서 병목이 되는지 이해하면 웹 성능 최적화의 근거를 설명할 수 있다.

URL 입력부터 응답 수신까지

사용자가 주소를 입력하면 브라우저는 먼저 DNS(Domain Name System) 조회로 도메인 이름을 IP 주소로 변환한다. 브라우저 캐시, 운영체제 캐시, 로컬 DNS 서버를 차례로 확인하고, 없으면 재귀 질의로 권한 있는 네임서버까지 찾아간다. IP를 얻으면 TCP(Transmission Control Protocol)의 3방향 핸드셰이크로 연결을 맺고, HTTPS라면 TLS(Transport Layer Security) 핸드셰이크로 인증서 검증과 암호화 키 협상을 추가로 수행한다. HTTP/3를 쓰는 경우에는 TCP 대신 UDP 기반의 QUIC이 전송과 암호화 협상을 한 번에 처리해 연결 지연을 줄인다. 연결이 준비되면 브라우저가 HTTP 요청을 보내고, 서버는 상태 코드와 헤더, HTML 본문을 응답한다. 브라우저는 본문 전체를 기다리지 않고 도착하는 대로 스트리밍 방식으로 해석을 시작한다.

파싱: DOM과 CSSOM 만들기

HTML 파서는 문서를 읽으며 토큰화와 트리 구축을 거쳐 DOM(Document Object Model) 트리를 만든다. 도중에 CSS 파일을 만나면 내려받아 CSSOM(CSS Object Model) 트리를 만들고, 두 트리를 결합해 실제로 화면에 표시될 노드와 그 최종 스타일을 담은 렌더 트리(Render Tree)를 구성한다. display 속성이 none인 요소는 렌더 트리에서 빠진다. 이 단계에서 중요한 개념이 렌더링 차단이다. CSS는 스타일이 확정되기 전에 잘못된 화면이 보이는 것을 막기 위해 렌더링을 차단하고, 일반 스크립트 태그는 문서를 조작할 수 있으므로 HTML 파싱 자체를 멈추고 실행된다. 그래서 스크립트에 지연 실행을 지시하는 defer나 병렬 로드 후 즉시 실행하는 async 속성을 붙여 파서 차단을 피하는 것이 기본 최적화다. 한편 파서가 멈춰 있는 동안에도 프리로드 스캐너(Preload Scanner)가 문서를 미리 훑어 필요한 자원의 다운로드를 앞당긴다.

레이아웃, 페인트, 합성

렌더 트리가 만들어지면 레이아웃(Layout) 단계에서 뷰포트 크기를 기준으로 각 요소의 정확한 위치와 크기를 계산한다. 리플로우(Reflow)라고도 부른다. 다음 페인트(Paint) 단계에서 텍스트, 색상, 테두리, 그림자 같은 시각 정보를 레이어별로 그리는 명령 목록을 만든다. 마지막 합성(Composite) 단계에서는 여러 레이어를 GPU(Graphics Processing Unit)의 도움으로 합쳐 최종 화면을 출력한다. 이 파이프라인은 변경이 생길 때마다 부분적으로 다시 실행되는데, 어느 단계부터 다시 하느냐가 성능을 좌우한다. 요소의 크기나 위치를 바꾸면 레이아웃부터 전부 다시 해야 하고, 색만 바꾸면 페인트부터, transform이나 opacity만 바꾸면 합성만 다시 하면 된다. 애니메이션에 transform과 opacity를 권장하는 이유가 여기에 있다. 또 자바스크립트에서 스타일 변경과 오프셋 읽기를 반복하면 브라우저가 매번 강제로 레이아웃을 계산하는 레이아웃 스래싱(Layout Thrashing)이 발생하므로 읽기와 쓰기를 묶어 처리해야 한다.

자바스크립트 실행 모델

렌더링과 자바스크립트 실행은 기본적으로 같은 메인 스레드를 공유한다. 자바스크립트는 단일 스레드로 동작하며, 오래 걸리는 동기 작업이 실행되는 동안에는 화면 갱신과 사용자 입력 처리가 모두 멈춘다. 비동기 작업은 이벤트 루프(Event Loop)가 관리한다. 타이머나 네트워크 응답의 콜백은 태스크 큐에, 프로미스(Promise)의 후속 처리는 우선순위가 더 높은 마이크로태스크 큐에 들어가며, 콜 스택이 비면 이벤트 루프가 큐에서 작업을 꺼내 실행한다. 브라우저는 보통 초당 60회 화면을 갱신하므로 한 프레임에 쓸 수 있는 시간은 약 16밀리초다. 이 안에 스크립트 실행과 렌더링 파이프라인이 끝나지 않으면 화면이 끊기는 것처럼 보인다. 무거운 연산은 웹 워커(Web Worker)로 분리해 메인 스레드를 비워 두는 것이 해법이다.

면접에서 이렇게 물어본다

  • 주소창에 URL을 입력하고 화면이 뜨기까지의 과정을 처음부터 끝까지 설명해 보라.
  • DOM, CSSOM, 렌더 트리는 각각 무엇이고 어떤 관계인가.
  • 스크립트 태그의 defer와 async의 차이를 파싱 차단 관점에서 설명해 보라.
  • 리플로우와 리페인트의 차이, 그리고 애니메이션에 transform을 권장하는 이유를 말해 보라.
  • 이벤트 루프에서 태스크 큐와 마이크로태스크 큐의 처리 순서를 설명해 보라.
웹 문서 전체 보기

이 개념, 내 코드에서는 어떻게 물어볼까요

데브핏(DevFit)에 채용공고 URL을 붙여 넣고 레포지토리를 연결하면, 내가 쓴 커밋에서 예상 질문과 어필 포인트를 만듭니다. 하루 10회까지 무료입니다. 합불 판정이나 지원자 랭킹은 만들지 않고, 최종 판단은 사람이 합니다.

공고 넣고 시작하기