브라우저 주소창에 “www.google.com”을 치고 엔터를 누르면 무슨 일이 일어날까. “www.google.com”처럼 사람이 외우기 쉽게 지은 주소를 도메인이라고 부른다. 그런데 컴퓨터끼리는 이름이 아니라 IP라는 숫자 주소로 찾아간다. 그래서 이름을 숫자로 바꾸는 DNS부터, 실제 내용을 주고받는 HTTP까지 여러 단계가 차례로 일어난다.
단순해 보이는 이 행동 하나가 실제로는 한반도에서 태평양을 건너 미국까지 갔다가 다시 돌아오는 과정을 거친다. 이 글에서 나는 그 여정을 한 단계씩 따라간다.
네트워크 환경 설정하기
먼저 가정을 정해 둔다. 뒤의 모든 단계가 이 가정 위에서 움직인다.
우리 집 네트워크는 사설 IP 하나와 공인 IP 하나로 되어 있다
우리 집에는 이런 것들이 있다고 하자.
- 내 PC: 192.168.0.2 (사설 IP)
- 홈 게이트웨이(공유기): 192.168.0.1. 내 PC가 집 밖으로 보낼 것을 일단 넘기는 게이트웨이이자, 패킷(네트워크로 오가는 바이트 덩어리 하나)의 갈 길을 정하는 라우터다
- 외부 공인 IP: 통신사가 공유기에 빌려준 공인 IP (예: 58.123.45.67). 통신사가 필요하면 바꿀 수 있게 빌려주는 것이라 「동적 IP」라고도 부른다
이 구조가 중요한 이유는 주소가 두 종류이기 때문이다. 사설 IP는 집집마다 같은 번호(192.168.0.2)를 써도 되는 대신 바깥에서는 그 주소로 찾아올 수 없다. 공인 IP는 인터넷 전체에서 하나뿐이라 바깥에서도 찾아올 수 있다. 그래서 집 안에서는 사설 IP로 통신하지만, 인터넷으로 나갈 때는 공인 IP로 바꿔야 한다. 이 바꾸는 일은 뒤의 NAT 섹션에서 본다.
DNS 조회: “도메인명을 IP 주소로 변환하기”
1단계: 내 PC는 먼저 로컬 DNS 서버에 묻는다
브라우저에 www.google.com을 입력하면 가장 먼저 이 질문이 나간다.
내 PC → 로컬 DNS 서버(보통 통신사 DNS) : "www.google.com의 IP를 주세요"
서버는 요청을 받아 처리해 주는 쪽이다. 로컬 DNS 서버는 이름을 IP로 바꿔 주는 일을 맡은 서버다. 내 PC가 어느 DNS 서버에 물을지는 PC의 네트워크 설정에 들어 있다. 실제 값은 Windows 터미널에서 ipconfig /all(내 PC의 네트워크 설정을 보여 주는 명령)로 볼 수 있다. 내 PC의 IP는 처음부터 정해져 있던 것이 아니라, 집 네트워크에 붙을 때 공유기가 나눠 준 것이다(이렇게 IP를 나눠 주는 규약을 DHCP라고 부른다). 실제로 우리 집에서 쳐 보니 IP를 나눠 준 곳(DHCP Server)이 공유기였고, 그때 DNS 서버 주소(통신사의 DNS 서버)도 함께 받아 와 있었다. 공유기가 PC에 IP를 줄 때 물어볼 DNS 서버 주소도 같이 알려 준 것이다. 우리 집 공유기의 실제 주소도 위 가정(192.168.0.1)과 달랐지만, 이 글은 끝까지 가정한 주소로 설명한다(실제 주소는 공개 글이라 적지 않았다).
로컬 DNS 서버는 이미 알아본 적 있는 도메인이면 캐시에 적어 둔 답을 바로 돌려준다. 처음 보는 도메인이면 다른 DNS 서버들에게 차례로 물어서 답을 찾아온다.
2단계: 로컬 DNS 서버가 Root부터 차례로 물어 내려간다
DNS 서버들은 층을 이루고 있다. 각 서버가 맡은 범위가 다르다.
- Root DNS: 맨 꼭대기 서버. 「.com은 누가 맡는지」처럼 맨 끝 이름표가 어디로 가야 하는지만 안다
- TLD DNS: TLD(Top-Level Domain)는
.com,.kr처럼 주소 맨 끝 이름표다. .com TLD 서버는 「google.com은 누가 맡는지」를 안다 - Authoritative DNS: google.com의 진짜 주소록을 들고 있는 서버. 「www.google.com의 IP는 무엇이다」를 최종으로 답한다
192.168.0.2"] B["로컬 DNS 서버
통신사 DNS"] C["Root DNS
최상위"] D["TLD DNS
.com 관리"] E["Authoritative DNS
google.com 관리"] A -->|1. www.google.com?| B B -->|2. www.google.com?| C C -->|3. .com은 TLD 서버에 물어라| B B -->|4. www.google.com?| D D -->|5. google.com은 Auth 서버에 물어라| B B -->|6. www.google.com?| E E -->|7. 142.250.196.164| B B -->|8. 142.250.196.164| A style A fill:#4a4a6a style B fill:#6a4a6a style C fill:#4a6a6a style D fill:#6a6a4a style E fill:#6a5a4a
그림의 화살표는 모두 로컬 DNS 서버(B)를 오간다. Root와 TLD는 자기가 대신 물으러 가지 않는다. 「거기는 내가 모르니 이 서버에 물어라」라고 다음 서버를 알려 줄 뿐이고, 실제로 다음 서버에 묻는 일은 로컬 DNS 서버가 한다. 이렇게 세 번 물어서 결국 google.com을 직접 맡는 Authoritative DNS 서버에게서 IP 주소(142.250.196.164)를 받는다. 이 글의 IP 주소들은 설명을 위한 예시다.
이 과정을 흔히 「재귀적 DNS 질의」라고 부른다. 자바의 재귀(메서드가 자기를 다시 부르는 것)와는 뜻이 다르다. 내 PC 쪽에서 보면 「끝까지 알아내서 최종 답만 달라」고 한 번 맡기고, 로컬 DNS 서버가 그 일을 끝까지 대신 하는 방식이다. DNS 규격 문서는 이 방식을 recursive(재귀) 방식이라고 이름 붙였다. 로컬 DNS 서버가 Root · TLD · Authoritative에게 하나씩 묻는 쪽은 「다음 서버를 알려 주면 내가 다시 묻겠다」는 방식이라 반복적(iterative) 질의라고 따로 부른다(RFC 1034 4.3.1). RFC는 인터넷 규약을 적어 둔 공식 문서이고, 뒤의 숫자는 문서 번호와 절 번호다. 이 글에서 「(RFC …)」로 적은 곳은 그 문서에서 확인한 내용이다.
패킷 생성: OSI 7계층으로 내려가기 (캡슐화)
IP 주소를 알았으니 이제 실제 데이터를 보낼 차례다. 네트워크로 오가는 바이트 덩어리 하나를 패킷이라고 부른다.
패킷은 한 번에 완성되지 않는다. 통신을 일곱 단계로 나눠 보는 OSI 7계층 모델로 보면, 데이터가 위층에서 아래층으로 내려가면서 층마다 자기 헤더(누가 어디로 보내는지 적은 앞부분)를 하나씩 덧붙인다. 이걸 캡슐화라고 부른다. 자바에서 말하는 캡슐화(필드를 숨기는 것)와는 다른 말이다.
먼저 짚어 둘 순서가 있다. 아래 그림은 HTTP 요청을 예로 들었지만, 실제로 이 길을 처음 타고 내려가는 패킷은 HTTP 요청이 아니다. 뒤에서 볼 TCP 연결을 여는 인사(SYN) 패킷이 먼저 같은 길로 내려가서 같은 여정을 거치고, 연결이 열린 뒤에야 HTTP 요청이 이 그림대로 내려간다. 내려가는 길은 어느 패킷이든 같아서 여기서는 HTTP 요청으로 보여 준다.
HTTP GET 요청"] B["6계층: Presentation Layer
암호화/인코딩"] C["5계층: Session Layer
세션 관리"] D["4계층: Transport Layer
TCP 헤더 추가
출발지포트:54321
목적지포트:443"] E["3계층: Network Layer
IP 헤더 추가
출발지IP: 192.168.0.2
목적지IP: 142.250.196.164"] F["2계층: Data Link Layer
이더넷 헤더 추가
목적지MAC: 게이트웨이MAC"] G["1계층: Physical Layer
전기신호로 변환"] A --> B --> C --> D --> E --> F --> G style A fill:#4a4a6a style D fill:#6a4a6a style E fill:#4a6a6a style F fill:#6a6a4a style G fill:#6a5a4a
그림을 위에서부터 따라간다.
- 7계층: 브라우저가 보낼 내용(HTTP 요청)을 만든다
- 6 · 5계층: 교과서 모델에서는 암호화 · 글자 변환(인코딩) · 대화 관리(세션)를 맡는 층이다. 그런데 실제 인터넷 규약은 링크 · 인터넷 · 전송 · 응용의 네 층으로 정의되어 있어서(RFC 1122), 이 두 층의 일은 브라우저 같은 응용 프로그램이 같이 맡는다. 이 글의 그림에 맞춰 보면 이더넷의 1 · 2계층이 링크 층, IP 헤더를 붙이는 3계층이 인터넷 층, TCP의 4계층이 전송 층, 나머지 5~7계층이 응용 층 자리다. 따로 헤더를 붙이는 층이 아니라서 뒤의 받는 쪽 그림에서는 뺐다
- 4계층: TCP 헤더를 붙인다. 포트는 한 컴퓨터 안에서 어느 프로그램에게 줄지 가르는 번호다. 목적지 포트 443은 암호화한 HTTP(HTTPS)가 기본으로 쓰는 번호다(RFC 2818). 출발지 포트 54321은 내 PC 운영체제가 이번 연결에 쓰라고 골라 준 번호를 예로 든 것이다
- 3계층: IP 헤더를 붙인다. 출발지와 최종 목적지 IP가 여기에 적힌다
- 2계층: 이더넷 헤더를 붙인다. 여기에 적는 목적지는 구글이 아니라 공유기다(바로 아래에서 이유를 본다)
- 1계층: 바이트를 선으로 보낼 신호로 바꾼다. 와이파이라면 전기신호 대신 전파가 된다
MAC 주소는 최종 목적지가 아니라 바로 다음 기계를 가리킨다
여기서 질문이 생긴다. “목적지가 미국의 구글 서버인데, 왜 2계층 목적지는 게이트웨이의 MAC 주소일까?”
MAC 주소는 랜 카드(PC를 랜선에 잇는 부품)나 와이파이 칩마다 붙은 번호다. IP 주소가 「최종 목적지」라면 MAC 주소는 「같은 네트워크 안에서 바로 다음에 받을 기계」다.
- MAC 주소는 같은 네트워크 안에서만 통한다. 여기서 같은 네트워크란 공유기 하나 아래에 물려 있는 기계들이다
- 내 PC(192.168.0.2)와 공유기(192.168.0.1)는 점으로 나뉜 네 덩어리 중 앞 세 덩어리(192.168.0)가 같은 192.168.0.x 대역이다. 마지막 x 덩어리만 다른 같은 묶음이라는 뜻이다. 미국 구글은 이 묶음 밖에 있다
- 앞 몇 덩어리까지 같아야 같은 묶음인지는 네트워크 설정의 서브넷 마스크가 정한다. 우리 집 PC에서
ipconfig /all로 보니 255.255.255.0이었고, 이것은 앞 세 덩어리까지 같으면 같은 묶음이라는 뜻이다 - 그래서 일단 게이트웨이(다음 홉)의 MAC 주소로 보내고, 게이트웨이가 받아서 그다음으로 넘긴다
그런데 내 PC는 공유기의 IP(192.168.0.1)만 알지 MAC 주소는 모른다. 그래서 먼저 ARP(Address Resolution Protocol) 로 알아낸다. 「192.168.0.1을 가진 기계는 MAC 주소를 알려 달라」를 같은 네트워크 전체에 한 번에 외치면, 공유기가 자기 MAC 주소로 답한다(RFC 826).
NAT: 사설 IP를 공인 IP로 변환하기
게이트웨이에 도착한 패킷의 출발지 IP는 192.168.0.2(사설 IP)다.
문제: 인터넷의 라우터들은 「이 IP 대역은 우리 쪽으로 보내라」를 서로 알려 주며 길을 만든다(그 방법은 뒤의 BGP 섹션에서 본다). 그런데 사설 IP 대역은 집 안에서만 쓰기로 약속한 주소라서, 이 대역으로 가는 길은 서로 알려 주지 않기로 했다(RFC 1918). 게다가 192.168.0.2는 수많은 집이 똑같이 쓰는 번호라 어느 집인지 가를 수도 없다. 그래서 구글 서버가 192.168.0.2로 응답을 보내도 우리 집에 닿지 않는다.
해결책: NAT (Network Address Translation)
Src: 192.168.0.2:54321
Dst: 142.250.196.164:443"] B["게이트웨이
NAT 변환"] C["외부 패킷
Src: 58.123.45.67:54321
Dst: 142.250.196.164:443"] D["NAT 테이블에 저장
192.168.0.2:54321
↔
58.123.45.67:54321"] A --> B --> C B --> D style A fill:#4a4a6a style B fill:#6a4a6a style C fill:#4a6a6a style D fill:#6a5a4a
그림 읽는 법부터 적는다. Src는 출발지, Dst는 목적지다. 192.168.0.2:54321처럼 콜론 뒤에 붙은 숫자는 포트다. 즉 「192.168.0.2 기계의 54321번 포트」다.
게이트웨이는 이렇게 한다.
- 출발지 IP 192.168.0.2 를 58.123.45.67 (자신의 공인 IP)로 바꾼다
- 바꾼 짝을 NAT 테이블(짝을 적어 두는 표)에 한 줄 적는다.
↔는 「왼쪽과 오른쪽이 서로 짝」이라는 뜻이다. 이 줄은 「바깥에서 58.123.45.67:54321로 오는 것은 안쪽 192.168.0.2:54321에게 준다」로 읽는다 - 나중에 응답이 오면 이 표를 보고 거꾸로 바꿔 준다
그림에서는 포트 54321이 그대로다. 집 안의 다른 PC도 같은 54321번을 쓰고 있으면 바깥에서 둘을 가를 수 없으므로, 그때 공유기는 IP뿐 아니라 바깥쪽 포트 번호도 바꿔서 겹치지 않게 한다. IP와 포트를 함께 바꾸는 이 방식을 NAPT라고 부른다(RFC 3022). 이 그림은 겹치지 않는 경우라 포트를 그대로 뒀다.
이것이 바깥에서 우리 집 PC로 먼저 연결을 걸 수 없는 이유다. NAT 테이블의 줄은 안쪽에서 먼저 패킷을 내보낼 때 생긴다. 바깥에서 먼저 온 패킷은 테이블에 맞는 줄이 없어서 공유기가 누구에게 줄지 모른다. 그 패킷이 실제로 어떻게 되는지는 뒤의 「게이트웨이에서의 역방향 NAT」에서 본다.
라우팅: 수십 개의 라우터를 거쳐 미국까지 가기
이제 패킷이 게이트웨이를 떠나 인터넷으로 나간다. 통신사들의 큰 회선망(백본)을 지나 바다 밑에 깔린 해저 케이블을 타고 태평양을 건너 미국까지 가야 한다.
각 라우터는 목적지 IP만 보고 다음 홉을 정한다
여기서 말하는 라우터는 집 공유기와 같은 일을 하는 기계다. 다만 통신사 안에 있는 훨씬 큰 장비들이다.
경로 위의 모든 라우터는 이 일을 되풀이한다.
- 패킷의 목적지 IP(142.250.196.164) 를 본다
- 자신의 라우팅 테이블 을 찾는다. 「목적지가 이 대역이면 저쪽으로 넘긴다」는 줄들이 적힌 표다
- 목적지 IP에 맞는 줄이 여럿이면 가장 구체적인 줄을 고른다. 예를 들어 「142.250.x.x는 A쪽」과 「142.250.196.x는 B쪽」이 둘 다 맞으면, 앞자리가 더 길게 맞는 B쪽을 따른다(RFC 1812의 longest match)
- 그 줄이 가리키는 다음 홉(Next Hop) 에게 넘긴다
192.168.0.2"] B["게이트웨이
58.123.45.67"] C["한국 ISP
라우터"] D["아시아 지역
라우터"] E["태평양
해저케이블"] F["미국 ISP
라우터"] G["구글 서버
142.250.196.164"] A -->|NAT 변환| B --> C --> D --> E --> F --> G style A fill:#4a4a6a style B fill:#6a4a6a style C fill:#4a6a6a style D fill:#6a6a4a style E fill:#6a5a6a style F fill:#6a4a7a style G fill:#7a4a6a
ISP는 인터넷 서비스 제공자, 즉 집에 인터넷을 넣어 주는 통신사와 통신사끼리를 이어 주는 회사들이다. 그림의 칸 하나하나는 실제로는 라우터 여러 대를 묶어 그린 것이다.
라우터가 몇 대인지는 직접 재 봤다. 2026-10-20에 내 PC에서 traceroute(Windows에서는 tracert)로 www.google.com까지의 경로를 찍으니 12번째 홉에서 구글 서버에 닿았다. 수천 대가 아니라 십여 대 단위다. 뒤에서 볼 TTL 기본값이 64 또는 128이라는 것도 같은 이야기다. TTL은 라우터를 지날 때마다 1씩 줄고 0이 되면 패킷을 버리게 하는 숫자라서, 라우터 수천 대를 거쳐야 하는 길이라면 패킷이 도착 전에 버려진다.
“최단 경로”가 항상 물리적 거리는 아니다
라우팅이 무조건 「물리적으로 가장 짧은 길」을 고르는 것은 아니다.
통신사끼리는 BGP(Border Gateway Protocol) 라는 규약으로 「우리 쪽으로 보내면 이 IP 대역에 닿는다」를 서로 알려 준다. 길이 여럿이면 BGP는 이런 기준으로 하나를 고른다(RFC 4271 9.1).
- 각 통신사가 정해 둔 정책: 통신사끼리는 회선을 서로 이어 쓰는 대가로 돈을 주고받는 계약을 맺는다. 그래서 같은 곳에 닿는 길이라도 돈을 덜 내는 쪽, 계약한 쪽을 먼저 쓰도록 정해 둔다
- 거쳐 가는 통신사 수: 정책이 같으면 거쳐 가는 통신사가 적은 길을 고른다
- 그 밖에 통신사 안에서 다음 출구(BGP가 알려 준 다음 라우터)까지 가는 비용 같은 기준으로 순서대로 동점을 가른다
RFC 4271에 적힌 기준에는 실시간 혼잡도가 없다. 그래서 경로는 물리적 거리보다 계약과 정책을 더 따른다. 그리고 같은 기준을 돌아오는 쪽 통신사들이 따로 적용하므로 응답이 올 때는 다른 경로로 올 수도 있다. 가는 길과 오는 길이 다른 것을 비대칭 라우팅이라고 부른다.
TTL: 무한 루프 방지
그런데 라우팅 테이블이 꼬여서 패킷이 라우터 사이를 계속 맴돈다면 어떻게 될까. 이를 막으려고 IP 헤더에 TTL(Time To Live) 값을 넣는다.
- 보내는 쪽(내 PC의 운영체제)이 처음 값을 넣는다
- 패킷을 처리하는 라우터마다 TTL 값을 1 이상 줄인다(RFC 791). 규격은 이 값을 원래 초 단위로 정했고, 처리에 1초가 안 걸려도 최소 1은 줄이라고 한다. 그래서 「1 이상」이다
- TTL이 0이 되면 패킷은 버려진다
- 처음 값은 보통 64 또는 128이다
이름은 「살아 있는 시간」이지만 라우터마다 줄어드니 실제로는 거칠 수 있는 라우터 수의 한도처럼 쓰인다. 처음 값이 64와 128 두 가지인 것은 운영체제마다 기본값을 다르게 정해 두기 때문인데, 어느 운영체제가 어느 값을 쓰는지는 확인하지 않았다.
TCP 3-Way Handshake: 안정적인 연결 수립하기
구글 서버까지 가는 길이 생겼다. 하지만 바로 HTTP 요청을 보내지는 않는다. 먼저 TCP 연결을 연다.
TCP는 「끊기지 않고 순서가 지켜지는 연결」을 만들어 주는 규약이다. 인터넷 아래층(IP)은 패킷을 하나씩 따로 보낼 뿐이라, 중간에 하나가 사라지거나 순서가 뒤바뀌어도 모른다. HTTP는 내용이 빠짐없이 순서대로 도착한다고 믿고 움직이므로, 그걸 보장해 주는 TCP 연결이 먼저 있어야 한다. 연결을 연다는 것은 양쪽이 「지금부터 보내는 바이트에 붙일 번호」를 서로 맞춰 두는 것이다. 그 번호가 시퀀스 번호(Seq)다.
3-Way Handshake는 시퀀스 번호를 서로 확인하는 세 번의 인사다
3-way handshake는 TCP 연결을 열 때 주고받는 세 번의 인사다.
192.168.0.2:54321 participant Server as 구글 서버
142.250.196.164:443 PC->>Server: 1. SYN
Seq=1000 Note over PC,Server: "연결하고 싶어요!" Server->>PC: 2. SYN-ACK
Seq=2000, Ack=1001 Note over PC,Server: "좋아요! 나는 2000부터 시작할게요" PC->>Server: 3. ACK
Seq=1001, Ack=2001 Note over PC,Server: "확인했어요!" Note over PC,Server: 연결 수립 완료!
각 단계의 의미는 이렇다. SYN은 「번호를 맞추자(synchronize)」, ACK는 「받았다(acknowledge)」는 표시다.
- SYN: 내 PC가 「내 번호는 1000부터 시작한다」를 보낸다. 1000은 설명하기 쉽게 고른 예시 숫자다
- SYN-ACK: 서버가 「내 번호는 2000부터 시작한다」와 함께
Ack=1001을 보낸다. Ack 값은 「여기까지 받았으니 다음에는 이 번호를 보내라」는 뜻이다(RFC 9293 3.4). SYN에는 내용 바이트가 없지만 SYN 표시 자체가 번호 하나(1000)를 차지한다고 규격이 정해 두었다(RFC 9293 3.4). 그래서 1000번을 받은 셈이고, 다음으로 기다리는 번호는 1001이다. 내 PC가 3번에서 Seq=1001을 쓰는 것도 같은 이유다 - ACK: 내 PC가
Ack=2001로 서버의 2000번을 받았다고 답한다
이제 양쪽 모두 상대의 시작 번호를 안다. 그래서 어느 쪽이 보낸 바이트든 번호로 빠진 것과 순서를 확인할 수 있는 상태가 됐다.
HTTP 요청 및 응답
연결이 열렸으니 이제 실제 내용을 주고받는다.
여기서 한 단계를 일부러 건너뛴다. 목적지 포트가 443이므로 실제로는 HTTPS다. HTTPS에서는 TCP 연결이 열린 뒤 HTTP 요청을 보내기 전에 TLS 인사를 먼저 마치고, 그 뒤 HTTP 내용은 전부 암호화해서 보낸다(RFC 2818 2.1). 그래서 실제로 선 위를 지나가는 것은 아래 같은 글자가 아니라 암호화된 바이트다. TLS는 이 글에서 다루지 않고 다음 글로 넘긴다. 아래는 암호화를 풀었을 때의 HTTP 내용이다.
브라우저는 요청 줄 하나와 헤더 여러 줄을 보낸다
GET / HTTP/1.1
Host: www.google.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
Accept: text/html,application/xhtml+xml
Accept-Language: ko-KR,ko;q=0.9
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
한 줄씩 따라간다.
GET / HTTP/1.1: 「/(사이트의 첫 페이지)를 달라(GET), HTTP 1.1판 규칙으로 말한다」Host: www.google.com: 어느 사이트를 원하는지 이름으로 다시 적는다. IP 하나에 여러 사이트가 올라가 있을 수 있어서, 서버는 이 줄을 보고 어느 사이트의 페이지를 줄지 가른다(RFC 9110 7.2)User-Agent: ...: 요청을 보낸 프로그램(브라우저)이 무엇인지 알린다. 괄호 안은 운영체제 정보(여기서는 64비트 Windows)다. 앞의Mozilla/5.0같은 조각이 각각 무엇을 뜻하는지는 이 글에서 따지지 않는다Accept: ...: 받을 수 있는 내용 형식이다.text/html과application/xhtml+xml은 둘 다 HTML 계열 문서 형식의 이름이다. 즉 HTML 문서를 원한다Accept-Language: ko-KR,ko;q=0.9: 원하는 언어다.q는 선호도 점수로 0부터 1까지이고, 안 적으면 1이다(RFC 9110 12.4.2). 한국어(한국)를 1점, 그냥 한국어를 0.9점으로 원한다는 뜻이다Accept-Encoding: gzip, deflate, br: 풀 수 있는 압축 방식들이다. gzip · deflate · br 셋 다 압축 방식 이름이다Connection: keep-alive: 응답을 받은 뒤에도 연결을 닫지 말고 남겨 두자는 표시다. 뒤의 「효율화 기법들」에서 다시 본다
이런 이름: 값 줄 하나하나를 HTTP 헤더라고 부른다. 앞에서 본 TCP · IP 헤더와 이름은 같지만, 이쪽은 HTTP가 자기 내용 앞에 붙이는 부가 정보다.
구글은 상태 줄과 헤더, 빈 줄, 본문 순서로 응답한다
HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
Content-Length: 15234
Set-Cookie: NID=...; expires=...
Server: gws
<!DOCTYPE html>
<html>
<head><title>Google</title></head>
<body>
<!-- HTML 컨텐츠 -->
</body>
</html>
한 줄씩 따라간다.
HTTP/1.1 200 OK: 상태 코드 200, 즉 요청이 성공했다는 뜻이다Content-Type: text/html; charset=UTF-8: 본문이 HTML이고, 글자는 UTF-8이라는 인코딩 규칙으로 읽으라는 뜻이다(Content-Type)Content-Length: 15234: 본문 길이가 15234바이트라는 뜻이다(RFC 9110 8.6)Set-Cookie: ...: 브라우저에게 쿠키를 저장해 두라고 넘긴다.NID는 쿠키의 이름,expires는 언제까지 보관할지이고, 실제 값은...로 생략했다Server: gws: 응답을 만든 서버 프로그램 정보다(RFC 9110 10.2.4). gws가 정확히 무엇의 이름인지는 확인하지 않았다- 빈 줄: 헤더가 여기서 끝난다는 표시다. 그 아래가 본문이다(RFC 9112 2.1)
<!DOCTYPE html>이하: 본문인 HTML이다. 브라우저가 이걸 읽어 화면을 그린다.<head>에는 탭에 뜨는 제목(<title>) 같은 페이지 정보가,<body>에는 화면에 보이는 내용이 들어간다.<!-- -->는 화면에 나오지 않는 주석이고, 여기서는 실제 내용을 생략했다는 표시로 썼다
역방향 여행: 응답이 돌아오다
이제 구글의 응답이 다시 한국으로 돌아와야 한다.
응답은 갈 때와 다른 경로로 올 수 있다
앞의 BGP 섹션에서 봤듯, 응답은 갈 때와 다른 경로로 돌아올 수 있다.
예를 들면 이런 식이다(실제 경로를 잰 것이 아니라 모양을 보여 주려는 가상의 예다).
- 간 길: 한국 → 일본 → 미국 서부 → 구글
- 온 길: 구글 → 미국 동부 → 유럽 → 한국
왜 그럴까.
- 돌아오는 길은 구글 쪽 통신사들이 자기 BGP 정책으로 따로 고른다
- 통신사끼리 이어진 방식(누가 누구와 계약했나)이 방향마다 다를 수 있다
그리고 응답 하나가 패킷 하나에 다 들어가지 않는다. 위의 응답처럼 15234바이트짜리 본문은 여러 TCP 패킷으로 나뉘어 온다(TCP가 나눈 한 조각을 TCP 세그먼트라고 부르고, 그것을 IP 헤더로 감싼 것이 패킷이다). 이 조각들은 도착 순서가 뒤바뀌거나 중간에 하나가 사라질 수 있다. 그래서 받는 쪽 TCP가 시퀀스 번호를 보고 원래 순서대로 다시 이어 붙인다. 앞에서 연결을 열 때 번호를 맞춘 이유가 이것이다.
게이트웨이에서의 역방향 NAT
응답 패킷이 게이트웨이(58.123.45.67)에 도착했다.
- 목적지: 58.123.45.67:54321
- 게이트웨이가 NAT 테이블을 찾는다:
192.168.0.2:54321 ←→ 58.123.45.67:54321 - 이 줄은 「58.123.45.67:54321로 온 것은 192.168.0.2:54321에게 준다」로 읽는다. 그래서 패킷의 목적지를 192.168.0.2:54321로 바꾼다
- 내 PC로 넘긴다
만약 NAT 테이블에 맞는 줄이 없으면 공유기는 누구에게 줄지 몰라 패킷을 버린다. 이것이 바깥에서 집 안 PC로 먼저 들어올 수 없는 이유다.
역캡슐화: 계층을 올라가며 헤더를 떼어 낸다
응답 패킷이 내 PC(192.168.0.2)에 도착했다. 이제 보낼 때와 반대로, 아래층부터 헤더를 하나씩 떼어 내면서 원래 내용을 꺼낸다.
전기신호 수신"] B["2계층: Data Link Layer
이더넷 헤더 제거
IP 패킷 추출"] C["3계층: Network Layer
IP 헤더 제거
TCP 세그먼트 추출"] D["4계층: Transport Layer
TCP 헤더 제거
HTTP 데이터 추출"] E["7계층: Application Layer
HTTP 응답 파싱
브라우저 렌더링"] A --> B --> C --> D --> E style A fill:#6a5a4a style B fill:#6a6a4a style C fill:#4a6a6a style D fill:#6a4a6a style E fill:#4a4a6a
5 · 6계층이 빠진 것은 앞의 캡슐화 섹션에서 적은 대로 그 일을 브라우저가 같이 맡기 때문이다. 맨 위에서 브라우저는 HTTP 응답을 파싱(글자를 규칙대로 읽어 구조로 바꾸기)하고, 렌더링(화면에 그리기)한다.
각 층은 이렇게 한다.
- 자기 헤더를 검사하고 떼어 낸다. 체크섬(오는 길에 바이트가 바뀌지 않았는지 보는 검사용 숫자) 검사에 실패하면 그 데이터를 망가진 것으로 보고 넘기지 않는다
- 나머지를 위층으로 넘긴다
- 빠지거나 망가진 데이터를 다시 받아 오는 일은 모든 층이 하는 것이 아니라 TCP가 한다. TCP에는 「못 받았다」고 알리는 신호가 따로 없다. 보낸 쪽이 「받았다(ACK)」 신호를 정해진 시간 안에 받지 못하면 그 부분을 다시 보낸다(RFC 9293)
추가 리소스 요청: HTML 파싱 후의 과정
HTML을 받은 브라우저는 그것을 파싱하다가 「이 파일도 받아 와야 한다」는 표시들을 찾는다.
<link rel="stylesheet" href="https://www.google.com/style.css">
<img src="https://www.google.com/logo.png">
<script src="https://www.google.com/app.js"></script>
한 줄씩 따라간다(주소는 설명용 예시다).
<link rel="stylesheet" href="...">: 화면 꾸밈을 정하는 CSS 파일을href주소에서 받아 오라는 표시다<img src="...">:src주소의 그림을 받아 와서 이 자리에 보여 주라는 표시다<script src="...">:src주소의 JavaScript 파일을 받아 와서 실행하라는 표시다
브라우저는 이렇게 따로 받아 와야 하는 파일(리소스)마다 HTTP 요청을 새로 보낸다.
Google 메인 페이지 하나를 다 띄우려면 이런 요청이 들어간다.
- HTML 요청 1개
- CSS 파일 여러 개
- 이미지 파일들
- JavaScript 파일들
- 방문 기록을 모으는 분석용 스크립트들
- 광고를 보내 주는 다른 서버들
수십 개 이상의 HTTP 요청이 동시에(병렬로) 나간다. 이 개수는 내가 직접 세어 본 것이 아니다.
효율화 기법들
그렇다면 요청마다 TCP 3-Way Handshake를 다시 할까. DNS도 다시 물을까.
그렇지 않다. 브라우저는 이렇게 줄인다.
- HTTP Keep-Alive: 같은 서버면 열어 둔 TCP 연결을 다시 쓴다. 앞의
Connection: keep-alive줄이 이것이다. HTTP/1.1은 따로 말하지 않아도 연결을 남겨 두는 것이 기본이다(RFC 9112 9.3) - DNS 캐싱: 한 번 알아낸 IP는 내 PC의 메모리(캐시)에 적어 두고, 같은 도메인을 다시 물을 때 꺼내 쓴다. 앞에서 본 로컬 DNS 서버의 캐시와는 다른, 내 PC 쪽의 캐시다
- HTTP/2: HTTP의 두 번째 판이다. 하나의 TCP 연결 안에서 요청 여러 개를 잘게 나눠 섞어 보내서 동시에 처리한다(RFC 9113)
이런 최적화가 있어야 웹 페이지가 기다림 없이 뜨는 것처럼 느껴진다.
실무적 이해의 중요성
이제 다음 상황들을 설명할 수 있다.
이 흐름을 알면 풀리는 네 가지 질문
Q: 바깥 사람이 우리 집 PC에 먼저 연결을 걸 수 없는 이유는?
- A: NAT 때문이다. 안에서 먼저 내보낸 연결의 응답만 NAT 테이블로 찾아 들어오고, 바깥에서 먼저 온 패킷은 받을 PC를 몰라 버려진다. 그래서 NAT가 「안에서 바깥으로만 열리는 문」처럼 걸러 주는 셈이다
Q: 간혹 외국 사이트가 느린 이유는?
- A: 해저 케이블 용량 부족, 통신사끼리 이어진 곳에서 생기는 병목, BGP가 물리적 거리보다 계약을 따라 길을 고르는 것 등이 원인이 된다
Q: VPN을 써서 다른 나라 IP로 접속하면 뭐가 달라지나?
- A: 내 패킷이 먼저 VPN 서버까지 갔다가 거기서 목적지로 나간다. 그래서 목적지 서버가 보는 출발지 IP가 우리 집 공인 IP가 아니라 VPN 서버의 IP가 되고, 라우팅 경로도 달라져서 그 나라의 라우터들을 거쳐 가게 된다
Q: DNS가 느리면 웹 전체가 느려지는 이유는?
- A: 처음 보는 도메인에 HTTP 요청을 보내려면 그 전에 DNS 조회가 끝나야 한다. 캐시 덕분에 같은 도메인은 다시 묻지 않지만, 앞에서 봤듯 페이지 하나가 분석 · 광고 서버처럼 여러 도메인에 요청을 보내므로 처음 보는 도메인마다 DNS 조회가 반복된다. 그 조회가 느리면 그 뒤의 요청이 전부 기다린다
마치며
한국의 PC에서 미국의 서버로 통신하는 과정은 이렇게 정리된다.
- DNS 조회: 도메인 이름을 IP로 바꾼다. 내 PC는 로컬 DNS 서버에 한 번 맡기고, 로컬 DNS 서버가 Root → TLD → Authoritative 순으로 묻는다
- 캡슐화: 각 층에서 헤더를 덧붙인다
- NAT: 사설 IP를 공인 IP로 바꾼다
- 라우팅: 십여 대 단위의 라우터가 각자 다음 홉을 골라 넘긴다
- TCP 3-Way Handshake: 시퀀스 번호를 맞춰 연결을 연다(실제로는 이 단계의 패킷이 2~4를 먼저 거친다)
- HTTP 요청/응답: 실제 내용을 주고받는다(443 포트라면 그 전에 TLS 인사를 한다)
- 역캡슐화: 아래층부터 헤더를 떼어 내며 내용을 꺼낸다
- 렌더링: 브라우저가 HTML · CSS · JavaScript를 읽어 화면을 그린다
엔터 한 번이 이렇게 많은 층을 거쳐 이루어진다.
이 과정을 아는 것은 다음의 출발점이다.
- 네트워크 문제를 어디서 찾을지 가르는 첫걸음
- 웹 애플리케이션이 느린 곳을 찾아 빠르게 만드는 기초
- 네트워크 보안의 바탕
다음에는 이 과정에서 생기는 보안 위협들(도청 = 중간에서 엿보기, man-in-the-middle = 중간에서 가로채 바꿔치기, DNS 스푸핑 = 가짜 IP를 답으로 주기) 과 HTTPS/TLS 암호화를 살펴본다.
궁금한 점 나누기
이 과정에서 이해가 안 가는 부분이 있었는지 궁금하다. 직접 경로를 보고 싶다면 Windows 터미널에서 tracert www.google.com을 쳐 보면 된다. 내가 쳤을 때는 12번째 줄에서 구글에 닿았다. 경험이 있다면 댓글로 나눠 주면 좋겠다.
자신만의 철학을 만들어가는 중입니다.
댓글남기기