- 들어가며 — 1초 안에 벌어지는 대서사시
- 1단계 — URL 파싱과 사전 처리
- 2단계 — DNS 해석: 이름을 주소로
- 3단계 — TCP 3-way 핸드셰이크: 연결을 세우다
- 4단계 — TLS 핸드셰이크: 안전한 통로 만들기
- 5단계 — HTTP 요청 보내기
- 6단계 — 서버의 처리와 응답
- 7단계 — HTML 파싱과 DOM 만들기
- 8단계 — 크리티컬 렌더링 경로: 픽셀까지
- 전체 흐름 다시 보기
- 마치며
- 참고 자료
들어가며 — 1초 안에 벌어지는 대서사시
"주소창에 URL을 입력하고 엔터를 누르면 무슨 일이 일어나나요?" 이 질문은 면접 단골이지만, 사실 그 이유가 있습니다. 이 한 줄짜리 동작 안에 웹이라는 시스템의 거의 모든 층이 압축되어 있기 때문입니다. DNS, 라우팅, 전송 계층, 암호화, 애플리케이션 프로토콜, 그리고 렌더링 엔진까지. 하나를 제대로 설명하려면 전부를 조금씩 알아야 합니다.
이 글은 그 여정을 순서대로 따라갑니다. 브라우저가 URL을 해석하는 순간부터 화면에 첫 픽셀이 그려지는 순간까지, 각 단계에서 실제로 무엇이 오가는지 짚어 보겠습니다. 예시 주소는 https://example.com/store/cart?id=42 라고 하겠습니다.
1단계 — URL 파싱과 사전 처리
엔터를 누른 직후, 브라우저는 먼저 주소창에 입력된 문자열이 무엇인지 판단합니다. 이것이 URL인지, 아니면 검색어인지부터 결정합니다. 공백이 있거나 점(.)이 없으면 기본 검색 엔진으로 넘기고, URL처럼 보이면 파싱을 시작합니다.
URL은 여러 부분으로 나뉩니다.
https://example.com:443/store/cart?id=42#top
└─┬─┘ └────┬────┘└┬┘└───┬────┘└──┬──┘└┬┘
스킴 호스트 포트 경로 쿼리 프래그먼트
- 스킴(scheme):
https. 어떤 프로토콜로 통신할지 정합니다. - 호스트(host):
example.com. 접속할 서버의 도메인 이름입니다. - 포트(port): 생략하면
https는 443,http는 80이 기본입니다. - 경로(path), 쿼리(query), 프래그먼트(fragment): 서버 안에서 어떤 자원을 원하는지, 어떤 파라미터를 붙이는지, 문서 내 어디로 스크롤할지를 나타냅니다.
이 단계에서 브라우저는 몇 가지 최적화를 이미 수행합니다. HSTS(HTTP Strict Transport Security) 목록에 있는 도메인이면 http로 입력해도 강제로 https로 바꿉니다. 그리고 브라우저 캐시, 서비스 워커, HTTP 캐시에 이 자원이 있는지도 확인해서, 있으면 네트워크를 아예 건너뛸 수도 있습니다.
2단계 — DNS 해석: 이름을 주소로
호스트 이름 example.com은 사람을 위한 것입니다. 네트워크는 IP 주소로 통신하므로, 도메인 이름을 IP 주소로 바꾸는 과정이 필요합니다. 이것이 DNS(Domain Name System) 해석입니다.
브라우저는 여러 층의 캐시를 순서대로 뒤집니다. 캐시에서 답을 찾으면 그 즉시 끝나므로, 실제 질의는 캐시가 모두 비었을 때만 발생합니다.
- 브라우저 자체 DNS 캐시를 봅니다.
- 없으면 운영체제의 리졸버 캐시(그리고
hosts파일)를 봅니다. - 그래도 없으면 설정된 재귀 리졸버(recursive resolver), 보통 ISP나 공용 DNS(예: 8.8.8.8)에게 묻습니다.
재귀 리졸버가 답을 모르면, 이제 진짜 재귀 질의가 시작됩니다. 리졸버는 여러 권한 서버를 차례로 방문합니다.
재귀 리졸버
│ 1) "example.com 의 IP는?"
▼
루트 서버 -> ".com 담당은 저쪽 TLD 서버야"
│
▼
.com TLD 서버 -> "example.com 담당 네임서버는 여기야"
│
▼
권한 네임서버 -> "example.com 은 93.184.216.34 야"
│
▼
재귀 리졸버가 결과를 캐시하고 브라우저에 반환
이 과정에서 각 응답에는 **TTL(Time To Live)**이 붙어 있습니다. TTL 동안은 리졸버가 그 답을 캐시에 보관하므로, 같은 도메인을 다시 물으면 루트까지 갈 필요 없이 빠르게 답합니다. DNS가 대체로 빠르게 느껴지는 이유가 바로 이 다층 캐시 덕분입니다.
성능을 위해 브라우저는 페이지 로딩 전에 미리 DNS를 조회해 두는 DNS 프리페치도 합니다. 링크에 마우스를 올리는 순간 뒤에서 조용히 해석을 시작해 두는 식입니다.
3단계 — TCP 3-way 핸드셰이크: 연결을 세우다
IP 주소를 알았으니 이제 그 서버와 연결을 맺어야 합니다. https는 신뢰성 있는 전송 계층인 TCP 위에서 동작하므로, 데이터를 보내기 전에 먼저 TCP 연결을 세웁니다. 이것이 유명한 3-way 핸드셰이크입니다.
클라이언트 서버
│ ── SYN (seq=x) ─────────────►│ "연결하고 싶어"
│ │
│ ◄──── SYN-ACK (seq=y, ack=x+1)│ "좋아, 나도 준비됐어"
│ │
│ ── ACK (ack=y+1) ───────────►│ "확인, 시작하자"
▼ ▼
이제 양방향 데이터 전송 가능
세 번의 왕복 중 두 번의 메시지가 오간 뒤부터 실질적으로 연결이 성립됩니다. 각 단계에서 시퀀스 번호(seq)를 교환하는데, 이는 이후 데이터의 순서를 맞추고 유실을 감지하는 기준이 됩니다. 이 핸드셰이크는 최소 1 RTT(round-trip time, 왕복 시간)를 소모합니다. 서버가 지구 반대편에 있다면 이 한 번의 왕복만으로도 수백 밀리초가 들 수 있습니다.
참고로 최신 프로토콜인 HTTP/3은 TCP 대신 UDP 기반의 QUIC을 사용해 이 연결 수립과 다음 단계인 암호화를 하나로 합쳐 지연을 줄입니다. 하지만 개념을 이해하기에는 전통적인 TCP + TLS 조합이 가장 명확하므로, 이 글은 그 흐름을 따릅니다.
4단계 — TLS 핸드셰이크: 안전한 통로 만들기
TCP 연결이 섰지만, 그 위로 흐르는 데이터는 아직 평문입니다. https의 's'는 TLS(Transport Layer Security)를 뜻하며, 실제 HTTP 데이터를 보내기 전에 암호화된 통로를 먼저 만듭니다. TLS 핸드셰이크가 하는 일은 크게 세 가지입니다. 서버가 진짜 그 서버인지 인증하고, 통신을 암호화할 열쇠를 교환하며, 사용할 암호 방식을 협상합니다.
TLS 1.3 기준으로 흐름을 단순화하면 이렇습니다.
클라이언트 서버
│ ── ClientHello ──────────────────►│ 지원하는 암호 스위트, 키 공유 목록
│ │
│ ◄── ServerHello, Certificate ─────│ 선택한 암호, 인증서, 키 공유
│ + Finished │
│ │
│ ── Finished ─────────────────────►│ 검증 완료
▼ ▼
이후 모든 HTTP 데이터는 암호화되어 흐른다
핵심 단계를 풀어 보면 이렇습니다.
- 인증서 검증: 서버는 자신의 인증서를 보냅니다. 브라우저는 이 인증서가 신뢰된 인증 기관(CA)의 서명을 받은 것인지, 도메인 이름이 일치하는지, 만료되지 않았는지를 확인합니다. 하나라도 어긋나면 그 무서운 "안전하지 않은 연결" 경고가 뜹니다.
- 키 교환: 양쪽은 공개키 암호(예: ECDHE)를 이용해, 도청자가 오가는 메시지를 다 봐도 알아낼 수 없는 공유 비밀을 만들어 냅니다. 이 비밀에서 실제 데이터를 암호화할 대칭키가 유도됩니다.
- 암호 스위트 협상: 어떤 암호화·해시 알고리즘 조합을 쓸지 정합니다.
TLS 1.3은 이 전체를 1 RTT로 줄였고, 예전에 방문한 서버라면 0-RTT 재개까지 가능합니다. 이전 버전인 TLS 1.2가 2 RTT를 쓰던 것과 비교하면 큰 개선입니다. 인증서 검증, 암호 스위트, 핸드셰이크 흐름을 직접 눈으로 실험해 보고 싶다면 이 사이트의 인증·보안 실험실에서 TLS와 관련 개념을 다뤄 볼 수 있습니다.
5단계 — HTTP 요청 보내기
암호화된 통로가 완성되었으니, 드디어 브라우저가 원하던 것을 요청할 차례입니다. 브라우저는 HTTP 요청 메시지를 만들어 보냅니다. HTTP/1.1 기준으로 요청은 대략 이런 텍스트 형태입니다.
GET /store/cart?id=42 HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0 ...
Accept: text/html,application/xhtml+xml
Accept-Encoding: gzip, br
Cookie: session=abc123
Connection: keep-alive
각 줄의 의미를 짚어 봅시다.
- 요청 라인: 메서드(
GET), 경로(/store/cart?id=42), 프로토콜 버전. - Host 헤더: 한 IP에 여러 사이트가 올라가 있는 경우(가상 호스팅), 어떤 도메인을 원하는지 알려 줍니다.
- Accept 계열 헤더: 어떤 콘텐츠 형식과 압축을 받을 수 있는지 서버에 알립니다.
- Cookie 헤더: 이전에 서버가 심어 둔 쿠키를 되돌려 보내, 로그인 상태 같은 것을 유지합니다.
HTTP/2와 HTTP/3에서는 이 헤더들이 사람이 읽는 텍스트가 아니라 이진 프레임으로 압축되어 전송되고, 한 연결에서 여러 요청을 동시에(멀티플렉싱) 보낼 수 있습니다. 하지만 논리적으로 담기는 정보는 위와 같습니다.
6단계 — 서버의 처리와 응답
요청은 네트워크를 건너 서버에 도착합니다. 그런데 "서버"는 대개 단일 기계가 아닙니다. 요청은 보통 여러 계층을 거칩니다.
요청 --> [CDN / 엣지 캐시] --> [로드 밸런서] --> [리버스 프록시]
│(캐시 히트면 여기서 바로 응답)
--> [웹/애플리케이션 서버] --> [데이터베이스 / 캐시]
- CDN과 엣지 캐시: 정적 자원(이미지, CSS, JS)이나 캐시 가능한 페이지는 사용자와 가까운 엣지 서버가 바로 응답합니다. 그러면 원본 서버까지 갈 필요가 없어 훨씬 빠릅니다.
- 로드 밸런서: 트래픽을 여러 서버 인스턴스에 분산합니다.
- 애플리케이션 서버: 실제 로직을 실행합니다. 라우팅을 하고, 데이터베이스에서 데이터를 읽고, HTML을 렌더링하거나 JSON을 만듭니다.
처리가 끝나면 서버는 HTTP 응답을 돌려줍니다. 응답의 첫 줄에는 상태 코드가 실립니다.
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Content-Encoding: br
Cache-Control: max-age=3600
Set-Cookie: session=abc123; HttpOnly; Secure
<!DOCTYPE html>
<html> ... </html>
상태 코드는 결과를 세 자리 숫자로 요약합니다. 200은 성공, 301·302는 리다이렉트, 404는 자원 없음, 500은 서버 오류입니다. 이 숫자들의 정확한 의미와 뉘앙스가 궁금하다면 HTTP 상태 코드 도감에서 각 코드를 하나씩 살펴볼 수 있습니다. 응답 본문은 대개 gzip이나 br로 압축되어 오므로, 브라우저는 먼저 압축을 풀어야 합니다.
7단계 — HTML 파싱과 DOM 만들기
브라우저가 HTML 바이트를 받기 시작하면, 처음부터 끝까지 다 받기를 기다리지 않고 도착하는 대로 파싱을 시작합니다. 파싱의 목표는 문서를 트리 구조인 **DOM(Document Object Model)**으로 바꾸는 것입니다.
HTML 바이트 --> 토큰화 --> 노드 생성 --> DOM 트리
파싱 도중 브라우저는 추가로 필요한 자원을 만납니다. <link>로 참조된 CSS, <script>로 참조된 자바스크립트, <img>의 이미지 등입니다. 이때 중요한 두 가지 성질이 있습니다.
- CSS는 렌더링을 막습니다(render-blocking): 브라우저는 스타일을 모두 알기 전에는 화면을 안전하게 그릴 수 없습니다. 그래서 CSS를 받아 파싱해 **CSSOM(CSS Object Model)**을 만들 때까지 렌더링을 미룹니다.
- 스크립트는 파싱을 막을 수 있습니다(parser-blocking): 일반
<script>는 만나는 순간 HTML 파싱을 멈추고 스크립트를 내려받아 실행합니다. 스크립트가 DOM을 바꿀 수 있기 때문입니다. 이를 피하려고async나defer속성을 붙여 파싱과 병행하거나 나중으로 미룹니다.
브라우저는 이 와중에도 프리로드 스캐너를 돌려, 본격적으로 파싱하기 전에 앞으로 필요할 자원들을 미리 내려받기 시작합니다. 덕분에 스크립트가 파싱을 막는 동안에도 다른 자원 다운로드는 계속됩니다.
8단계 — 크리티컬 렌더링 경로: 픽셀까지
DOM과 CSSOM이 준비되면, 브라우저는 이 둘을 합쳐 화면을 그리는 일련의 단계를 거칩니다. 이 전체 과정을 **크리티컬 렌더링 경로(Critical Rendering Path)**라고 부릅니다.
DOM + CSSOM --> 렌더 트리 --> 레이아웃 --> 페인트 --> 합성
각 단계는 이런 일을 합니다.
- 렌더 트리(Render Tree): DOM과 CSSOM을 결합하되, 실제로 화면에 보이는 노드만 담습니다.
display: none인 요소는 여기서 빠집니다. - 레이아웃(Layout / Reflow): 각 요소가 화면 어디에, 얼마만 한 크기로 놓일지 기하학적 위치를 계산합니다. 뷰포트 크기가 바뀌면 이 계산을 다시 해야 합니다.
- 페인트(Paint): 각 요소의 픽셀을 실제로 칠합니다. 색, 텍스트, 이미지, 그림자 등을 채웁니다.
- 합성(Composite): 여러 레이어로 나뉜 페인트 결과를 올바른 순서로 합쳐 최종 화면을 만듭니다. GPU가 이 일을 가속합니다.
여기서 성능과 직결되는 개념이 **리플로우(reflow)**와 **리페인트(repaint)**입니다. 자바스크립트로 요소의 크기나 위치를 바꾸면 레이아웃부터 다시 계산되는 리플로우가 일어나고, 색만 바꾸면 레이아웃은 건드리지 않고 다시 칠하는 리페인트만 일어납니다. 리플로우가 리페인트보다 비싸므로, 부드러운 애니메이션을 위해서는 레이아웃을 유발하지 않는 속성(transform, opacity 등)을 쓰는 것이 유리합니다. 이 속성들은 합성 단계에서만 처리되어 레이아웃과 페인트를 건너뛸 수 있기 때문입니다.
전체 흐름 다시 보기
지금까지의 여정을 한눈에 정리하면 이렇습니다.
1. URL 파싱 입력 문자열을 스킴/호스트/경로로 분해
2. DNS 해석 도메인 이름 -> IP (다층 캐시 + 재귀 질의)
3. TCP 핸드셰이크 3-way 로 신뢰성 있는 연결 수립 (1 RTT)
4. TLS 핸드셰이크 인증 + 키 교환 + 암호 협상 (1 RTT)
5. HTTP 요청 메서드/헤더/쿠키를 담아 전송
6. 서버 처리 CDN/LB/앱서버/DB 를 거쳐 응답 생성
7. HTML 파싱 바이트 -> DOM, CSS -> CSSOM
8. 렌더링 렌더 트리 -> 레이아웃 -> 페인트 -> 합성
각 단계는 그 자체로 책 한 권 분량의 깊이를 가지고 있지만, 큰 그림에서 보면 결국 하나의 목표를 향합니다. "사람이 읽을 수 있는 주소"를 "화면 위의 픽셀"로 바꾸는 것입니다.
마치며
주소창에 URL을 입력하고 엔터를 누르는 그 짧은 순간에, 이름 해석, 연결 수립, 신원 인증, 암호화, 데이터 전송, 문서 파싱, 화면 렌더링이라는 완전히 다른 성격의 작업들이 정확한 순서로 맞물려 돌아갑니다. 이 흐름을 이해하면 성능 문제를 만났을 때 어느 단계를 의심해야 할지, 보안 경고가 떴을 때 무엇이 어긋난 것인지 훨씬 빠르게 감을 잡을 수 있습니다.
다음에 페이지 로딩이 느리게 느껴진다면, 이 여덟 단계 중 어디에서 시간이 새고 있을지 상상해 보세요. DNS일까, 핸드셰이크일까, 서버 처리일까, 아니면 렌더링일까. 그 질문을 던질 수 있다는 것만으로도 이미 웹을 한 층 더 깊이 보게 된 것입니다.
참고 자료
- MDN: What happens when you navigate to a URL — https://developer.mozilla.org/en-US/docs/Web/Performance/How_browsers_work
- MDN: Populating the page — how browsers work — https://developer.mozilla.org/en-US/docs/Web/Performance/How_browsers_work
- Cloudflare Learning: What is DNS? — https://www.cloudflare.com/learning/dns/what-is-dns/
- Cloudflare Learning: What happens in a TLS handshake? — https://www.cloudflare.com/learning/ssl/what-happens-in-a-tls-handshake/
- web.dev: Critical rendering path — https://web.dev/articles/critical-rendering-path
- High Performance Browser Networking (Ilya Grigorik) — https://hpbn.co/
현재 단락 (1/121)
"주소창에 URL을 입력하고 엔터를 누르면 무슨 일이 일어나나요?" 이 질문은 면접 단골이지만, 사실 그 이유가 있습니다. 이 한 줄짜리 동작 안에 웹이라는 시스템의 거의 모든 층...