들어가며
지난 글에서 IP 주소의 구조와 패킷의 개념, 캡슐화, 그리고 계층마다 데이터를 부르는 이름(Stream·Segment·Packet·Frame)을 정리했다. 이번 글은 그 패킷이 실제로 한 호스트에서 만들어져 인터넷을 건너 상대 호스트의 프로세스에 닿기까지, 송신과 수신의 전체 흐름을 한 번에 훑는다. 그리고 IP 헤더가 실제로 어떤 필드로 채워지는지 들여다본다.
이 글에는 아직 본격적으로 다루지 않은 TCP가 일부 등장한다. TCP의 세부는 다음 글에서 다루므로, 여기서는 "전체 흐름이 이렇게 이어지는구나" 정도로 큰 그림을 잡는 데 집중하면 된다.
에펠탑을 택배로 보내려면

파리에 있는 에펠탑을 택배로 보낼 수 있을까? 가능하다. 다만 순서가 있다. 먼저 에펠탑을 분해해서 택배 박스에 들어갈 만한 크기, 즉 MTU 이하로 줄인다. 그다음 운송한다. 마지막으로 도착한 조각들을 다시 조립한다. 분해하고, 운송하고, 조립한다. 이것이 TCP/IP 송·수신의 큰 틀이다.
여기서 짚어 둘 게 하나 있다. 분해는 송신 측에서 하고, 조립은 수신 측에서 한다. 그리고 분해의 결과물 하나하나가 패킷이다. 인터넷이라는 정보의 물류 체계에서는 데이터가 패킷 단위로 유통된다.
규모 감각을 잡아 보자. 네이버 자료실에서 1.4MB짜리 파일을 다운로드한다고 하자. 서버 입장에선 파일을 송신하는 것이고, 내 PC 입장에선 수신하는 것이다. 그런데 패킷의 MTU는 겨우 1.4킬로바이트 남짓이다. 둘의 차이가 약 1024배다. 즉 1.4MB 파일 하나가 1000개 이상의 패킷으로 쪼개져 날아온다. 그리고 이 파일을 실제로 주고받는 주체는 PC나 서버 자체가 아니라, 그 안에서 동작 중인 프로세스다.
송신 측에서 일어나는 일

서버 프로세스가 파일을 보내는 과정을 순서대로 따라가 보자. 보조기억장치(HDD·SSD)에 저장된 원본 파일(가령 a.bmp, 1.4MB)이 출발점이다.
먼저 원본 파일의 일부를 프로세스 버퍼로 읽어 온다(Read). 이때 버퍼 크기는 프로그래머가 정한다. 1.4MB를 통째로 올릴 수도, 일부만 올릴 수도 있다. 그다음 이 데이터를 소켓에 실어 보내는데(send), 이 시점에 데이터가 소켓 I/O 버퍼로 복사된다.
버퍼가 두 개다
입출력에는 버퍼가 따라붙는다. 하나는 프로세스(애플리케이션)가 직접 관리하는 버퍼이고, 다른 하나는 소켓 입출력 버퍼다. 프로세스 버퍼 크기는 개발자가 정하고, 소켓 버퍼는 운영체제가 관리한다. 데이터는 원본에서 프로세스 버퍼로, 다시 소켓 버퍼로 복사되며 내려간다.
데이터가 유저 모드에서 커널로 넘어가며 TCP를 만나면, 여기서 분할(Segmentation)이 일어난다. 연속된 데이터(Stream)를 일정 크기로 잘라 Segment로 만들고, 각 조각에 순서 번호를 붙인다. 1번, 2번, 3번… 원본 파일에는 없던 순서 개념이 이때 생긴다.
순서 번호가 붙은 Segment는 한 층 더 내려가며 포장된다. IP를 만나 IP 헤더가 붙으면 Packet(박스)이 되고, Driver를 거쳐 Ethernet 헤더가 붙으면 Frame(트럭에 실린 상태)이 된다. 이렇게 아래로 내려가며 헤더가 차곡차곡 붙는 것이 Encapsulation이다.
트럭은 갈아탄다 — Packet과 Frame의 차이
Frame이 NIC를 통해 나가면, L2 액세스 스위치를 거쳐 라우터(Gateway)를 만나 인터넷으로 들어간다. 여기서 꼭 기억할 게 있다.
Packet은 유지되고, Frame(트럭)은 갈아탄다
택배 트럭이 출발지에서 목적지까지 한 대로 가지 않는 것처럼, Frame은 한 구간을 지날 때마다 새 트럭으로 바뀐다. 홉을 지날 때마다 Frame은 벗겨지고 다시 씌워진다. 반면 그 안에 든 Packet은 종단까지 그대로 유지된다. L2 Frame은 구간(링크)용, L3 Packet은 종단(end-to-end)용이라고 보면 된다.
이는 지난 글에서 본 LAN과 WAN의 구분과 이어진다. 같은 링크 안에서는 MAC 주소로 식별되는 Frame이 오가지만, 망과 망을 건너는 종단 간 식별은 IP 주소를 단 Packet이 담당한다.
수신 측에서 일어나는 일
목적지에 도착하면 정반대의 일이 벌어진다. 트럭(Frame)이 도착하면 그 안에서 박스(Packet)가 나오고, 박스를 열면 알맹이(Segment)가 나온다. 송신 측이 Encapsulation이었다면, 수신 측은 헤더를 하나씩 벗기는 Decapsulation이다.
여기서 당연한 사실 하나. 택배가 도착하면 우리는 내용물을 갖지 박스를 갖지 않는다. 마찬가지로 Frame에서 Packet을 꺼내면 Frame은 사라지고, Packet에서 Segment를 꺼내면 박스(Packet)도 소멸한다. 위로 올라가는 건 알맹이뿐이다.
꺼낸 Segment는 수신 측 소켓 I/O 버퍼에 차곡차곡 쌓인다. 이 버퍼를 채우는 일은 운영체제의 TCP 스택이 한다. 그러면 수신 프로세스(가령 Chrome)는 이 버퍼에서 데이터를 꺼내 위로 올린다. 이걸 read에 해당하는 동작이라 하는데, 네트워크에서는 이름을 바꿔 receive라고 부른다. 프로세스는 도착한 Segment들을 순서대로 합쳐 원본을 복원한다.
속도차와 Window, 그리고 ACK

여기서 중요한 게 속도차다. 네트워크는 한쪽에서 수신 버퍼를 계속 채운다. 동시에 프로세스는 다른 쪽에서 그 버퍼를 비워 올린다. 채움과 비움이 톱니바퀴처럼 동시에 돈다. 이때 수신 버퍼에 남은 여유 공간의 크기를 Window Size라고 부른다.
TCP는 연결지향 프로토콜이라, 받은 쪽이 잘 받았다고 피드백을 준다. 이것이 ACK(acknowledgement)다. 1번과 2번을 잘 받았으면 "3번을 달라"는 의미로 ACK 3을 보낸다. 즉 ACK 3은 1·2번까지 누적으로 잘 받았다는 뜻이다. 송신 측은 일정량을 보낸 뒤 ACK가 오기를 기다렸다가 다음을 보낸다.
ACK에는 Window가 함께 실린다
ACK가 갈 때는 항상 수신 버퍼의 여유 공간 크기(Window)가 함께 따라간다. 그래서 송신 측은 데이터를 보내기 전에 상대가 받을 공간이 있는지 확인한다. 여유가 있으면 보내고, 없으면 보내지 못한다.
TCP/IP 통신 장애의 네 가지 유형
네트워크는 생각보다 불안정하다. 이 송수신 과정에서 아귀가 안 맞으면 장애가 발생한다. 대표적인 유형이 넷이다.
첫째, Loss(유실). Segment가 전달 중에 사라지는 경우다. 어디서 사라졌는지는 송신자도 모른다. 이건 거의 100% 네트워크 자체의 문제다.
둘째, Re-transmission과 Duplicate ACK(재전송·중복 ACK). 송신 측이 ACK를 기다려도 안 오면, 못 받았겠거니 하고 다시 보낸다(재전송). 그런데 사실은 수신 측이 잘 받고 ACK를 보냈는데 간발의 차로 어긋난 것일 수 있다. 그러면 같은 데이터가 또 도착하고, ACK가 중복으로 발생한다. 이건 네트워크 문제일 수도, 양 끝(엔드포인트) 사이의 합이 안 맞아 생긴 것일 수도 있다.
셋째, Out of order(순서 뒤바뀜). 1번, 2번이 왔는데 3번 대신 4번이 먼저 도착하는 경우다. 순서가 어긋난 것은 운영체제의 TCP 스택이 어느 정도 보정한다. 이건 거의 네트워크 상의 문제다.
넷째, Zero Window(여유 공간 0). 수신 버퍼가 꽉 찼는데 프로세스가 빨리 비우지 못해 Window가 0이 된 상태다. 이러면 송신자가 보내도 받을 공간이 없다. 이건 네트워크가 아니라 엔드포인트 쪽 문제다. 받는 프로그램이 어떤 이유로든(CPU 점유 등) 데이터를 제때 처리하지 못한 것이다.
장애 진단의 첫 질문
장애가 났을 때 먼저 가를 것은 "네트워크 문제냐, 엔드포인트 문제냐"이다. Loss와 Out of order는 네트워크 쪽, Zero Window는 엔드포인트 쪽이 유력하다. 재전송·중복 ACK는 양쪽 다 가능하다. 전체 흐름을 알고 있으면 이 구분이 빨라진다.
IPv4 헤더 형식

전체 흐름을 봤으니 이제 IP 헤더를 들여다보자. L2 Frame은 구조가 단순해 다룰 게 별로 없지만, IP 헤더부터는 형식이 조금 복잡해진다. IP 헤더는 옵션이 없으면 보통 20바이트다.
헤더는 32bit를 한 행으로 끊어 구성된다. 8bit가 1바이트이고, 4bit는 16진수 한 자리에 해당한다. 주요 필드를 보면, 첫 행에 Version, IHL, TOS, Total Length가 있다. Version은 4bit로 IPv4면 항상 4다. IHL(Internet Header Length)은 헤더 길이로 보통 5다. 32bit가 4바이트이므로 5 × 4 = 20바이트, 이래서 헤더 길이가 20바이트가 된다. Total Length는 16bit로 패킷 전체 길이를 담는다. 16bit로 표현 가능한 최댓값이 2의 16제곱 = 65535이므로, IP 패킷은 이론상 최대 약 64KB까지 커질 수 있다. 하지만 실제로는 MTU 1500에 맞춰 운영된다.
둘째 행(Identification, Flags, Fragment Offset)은 전부 단편화와 관련된다.
단편화와 분할은 다르다
지난 글에서 본 분할(Segmentation)은 TCP에서 Stream을 Segment로 자르는 일이었다. 여기서 말하는 단편화(Fragmentation)는 다르다. 패킷이 경로 중간의 어떤 네트워크에 도달했는데 그 망의 MTU가 더 작으면(예: 1500인데 1300밖에 안 됨), 이미 만들어진 IP 패킷을 거기서 한 번 더 쪼개는 것이다. 작은 걸 또 잘라야 할 때 단편화가 일어난다.
셋째 행에는 TTL, Protocol, Header Checksum이 있다. TTL(Time To Live)은 8bit로, 세포의 텔로미어 같은 역할을 한다. 패킷이 홉(라우터)을 하나 지날 때마다 줄어들고, 0이 되면 그 패킷은 폐기된다. 목적지를 영영 못 찾는 패킷이 인터넷을 무한히 떠도는 걸 막는 장치다. Protocol은 이 헤더 안쪽 페이로드를 어떤 형식으로 해석해야 하는지를 알려준다. TCP, UDP, ICMP 등이 각자 고유 번호를 갖는다(TCP는 6). Header Checksum은 전송 중 손상을 검사하기 위한 검사합이다. 보안 기능은 아니다. 마지막 두 행은 각각 32bit짜리 출발지 주소와 목적지 주소다.
이 헤더를 사람이 16진수로 직접 해석하긴 어렵다. 그래서 Wireshark 같은 도구를 쓴다.

Wireshark는 16진수 덩어리를 필드별로 풀어 보여준다. 화면을 보면 Version은 4, Header Length는 20바이트(5), Total Length는 331, Flags는 "Don't fragment", TTL은 128, Protocol은 TCP(6)로 디코딩되어 있다. TTL 128은 보통 Windows 환경의 기본값이다. 16진수 45 00…에서 앞자리 4가 Version, 5가 IHL이다. Protocol 값 6을 보고 그 안쪽 페이로드를 TCP로 해석하고, 다시 그 안을 HTTP로 해석하는 식으로 계층을 따라 풀어 나간다. 네트워크를 다루려면 16진수 표기와 친해지고 Wireshark를 다룰 줄 아는 게 좋다.
여기까지가 패킷의 일생과 IP 헤더다. 패킷은 송신 측에서 분해·캡슐화되어 트럭을 갈아타며 운송되고, 수신 측에서 디캡슐화·조립된다. 그 과정에서 Window와 ACK로 흐름을 맞추고, 어긋나면 네 가지 유형의 장애가 나타난다. IP 헤더는 이 모든 걸 가능하게 하는 20바이트의 정보다.
'Network' 카테고리의 다른 글
| L4 — TCP와 UDP: 연결의 실체와 착각 (0) | 2026.06.09 |
|---|---|
| L3 — 서브넷 마스크부터 ARP까지 (0) | 2026.06.02 |
| L3 — IP 주소와 패킷의 구조 (0) | 2026.06.02 |
| L2 — 데이터링크 계층, NIC와 MAC 그리고 스위칭 (0) | 2026.06.02 |
| 네트워크 입문 — 계층, 식별자, 그리고 Host (0) | 2026.06.02 |
