데이터 전송의 기본 원리
회선 교환과 패킷 교환을 비교하고 통계적 다중화의 효과를 확률로 계산해 봅니다. 헤더와 페이로드, MTU와 MSS와 단편화를 정리하고, 처리·대기·전송·전파의 네 가지 지연과 대역폭, 창 크기에 묶인 처리량, 병목과 혼잡을 계산으로 구분합니다.
"인터넷이 느리다"고 말할 때, 정확히 무엇이 느린지 답하기는 쉽지 않습니다. 첫 바이트가 늦게 오는 것인지, 큰 파일이 천천히 받아지는 것인지, 가끔씩 멈추는 것인지에 따라 원인도 해결책도 다릅니다. 이 글에서는 데이터가 네트워크를 지나가는 방식인 교환 방식과, 성능을 나타내는 지연, 대역폭, 처리량을 구분해 봅니다.
회선 교환과 패킷 교환
데이터를 목적지까지 보내는 방식은 크게 두 가지입니다. 회선 교환(circuit switching)은 통신을 시작하기 전에 송신자와 수신자 사이의 경로에 자원을 미리 예약해 두는 방식으로, 옛 전화망이 대표적입니다. 전화를 걸면 두 사람을 위한 회선의 몫이 예약되고, 통화가 끝날 때까지 다른 사람은 그 몫을 쓸 수 없습니다. 한 케이블을 통째로 차지하는 것이 아니라, 시간이나 주파수로 나눈 몫 하나를 예약하는 것입니다.
회선 교환: A와 B 사이의 경로에 몫을 예약해 두고 통화가 끝날 때까지 혼자 쓴다
[A] ════════════════════════> [B]
패킷 교환: 데이터를 패킷으로 나누어, 여러 흐름이 같은 링크를 번갈아 쓴다
[A] ─[1]─[2]─┐
[C] ─[x]─────┼──> 라우터 ──> 목적지
[D] ─[y]─[z]─┘패킷 교환(packet switching)은 데이터를 패킷(packet)이라는 작은 조각으로 나누어, 미리 예약하지 않고 보내는 방식입니다. 패킷마다 목적지 주소 같은 정보가 붙어 있어서, 라우터는 패킷이 올 때마다 다음에 보낼 곳을 정합니다. 같은 흐름의 패킷은 대개 같은 경로로 가지만, 경로가 바뀌거나 부하를 나누는 장비가 있으면 서로 다른 길로 가기도 하므로, 필요하면 받는 쪽의 전송 계층이 순서를 맞추고 잃어버린 것을 다시 받습니다.
| 기준 | 회선 교환 | 패킷 교환 |
|---|---|---|
| 연결 | 통신 전에 경로와 자원을 예약한다 | 예약 없이 패킷마다 다음 홉을 정한다 |
| 자원 효율 | 말이 없는 동안에도 예약한 몫을 차지한다 | 보낼 데이터가 있을 때만 링크를 쓴다 |
| 지연 | 예약한 뒤에는 거의 일정하다 | 혼잡과 대기에 따라 달라진다 |
| 고장 | 예약한 경로가 끊기면 다시 연결해야 한다 | 다른 경로가 있고 라우팅이 바뀌면 돌아갈 수 있다 |
| 쓰임 | 옛 전화망, 전용 회선 | 인터넷 |
패킷 교환의 장점은 사용자들이 쉬는 시간을 다른 사용자가 쓸 수 있다는 데 있습니다. 1Mbps 링크를 쓰는 사용자들이 데이터를 보낼 때는 100kbps가 필요하지만, 대부분의 시간은 쉬고 있어 실제로는 시간의 10%만 보낸다고 합시다. 다음 코드는 사용자가 늘어날 때 동시에 보내는 사람이 링크가 감당할 수 있는 10명을 넘을 확률을 이항 분포로 계산합니다.
from math import comb
LINK_KBPS, USER_KBPS, ACTIVE = 1000, 100, 0.1 # 1Mbps 링크, 쓸 때마다 100kbps, 시간의 10%만 쓴다
capacity = LINK_KBPS // USER_KBPS # 동시에 쓰는 사람이 10명까지는 감당한다
print(f"회선 교환으로 받을 수 있는 사용자: {capacity}명")
for users in (10, 35, 50):
overload = sum(comb(users, k) * ACTIVE**k * (1 - ACTIVE) ** (users - k)
for k in range(capacity + 1, users + 1))
print(f"패킷 교환, 사용자 {users}명: 동시에 {capacity}명을 넘을 확률 {overload:.4%}")회선 교환으로 받을 수 있는 사용자: 10명
패킷 교환, 사용자 10명: 동시에 10명을 넘을 확률 0.0000%
패킷 교환, 사용자 35명: 동시에 10명을 넘을 확률 0.0424%
패킷 교환, 사용자 50명: 동시에 10명을 넘을 확률 0.9355%회선 교환은 사용자마다 100kbps를 예약해야 하므로 10명까지만 받을 수 있습니다. 패킷 교환으로는 35명이 함께 써도 링크가 넘치는 시간이 0.04%에 불과하고, 넘칠 때도 잠깐 기다리게 될 뿐입니다. 사용자들이 동시에 보내지 않는다는 성질을 이용해 같은 링크로 더 많은 사용자를 받는 이것을 통계적 다중화(statistical multiplexing)라고 하며, 인터넷이 패킷 교환으로 만들어진 이유입니다. 다만 사용자가 50명이 되면 넘치는 시간이 1% 가까이로 늘어, 링크가 감당할 수 있는 양을 넘기면 대기와 손실이 급격히 늘어납니다.
패킷의 구조
네트워크에서 주고받는 단위는 대개 헤더(header)와 페이로드(payload)로 나뉩니다. 헤더는 전달과 처리에 필요한 정보로, 계층에 따라 출발지와 목적지 주소, 포트 번호, 상위 프로토콜의 번호, 남은 홉 수(TTL), 순서 번호 같은 것이 들어갑니다. 페이로드는 실제로 전하려는 데이터입니다. 이더넷 프레임처럼 끝에 오류 검사용 FCS 같은 트레일러(trailer)가 붙는 단위도 있습니다.
보내는 쪽의 각 계층은 위 계층에서 받은 데이터에 자기 헤더를 붙여 아래 계층에 넘기는데, 이를 캡슐화(encapsulation)라고 합니다. 받는 쪽은 아래 계층부터 헤더를 하나씩 벗겨 내는 역캡슐화(decapsulation)로 원래의 데이터를 얻습니다. 이 과정은 2장 4편에서 자세히 봅니다.
MTU와 MSS
MTU(maximum transmission unit)는 한 링크가 한 번에 실을 수 있는 IP 패킷의 최대 크기입니다. 보통의 이더넷은 1500바이트이지만, PPPoE나 VPN, 터널을 쓰면 헤더가 더해져 더 작아지고, 데이터센터의 점보 프레임은 9000바이트 안팎으로 큽니다. TCP가 세그먼트 하나에 담는 데이터의 최대 크기는 MSS(maximum segment size)라고 하며, MTU에서 IP 헤더와 TCP 헤더를 뺀 값입니다.
import math
MTU, IP_HEADER, TCP_HEADER = 1500, 20, 20 # 이더넷 MTU, 옵션 없는 IPv4와 TCP 헤더
mss = MTU - IP_HEADER - TCP_HEADER # 세그먼트 하나에 담는 TCP 데이터
data = 10 * 1024 # 보낼 데이터 10KiB
segments = math.ceil(data / mss)
print(f"MSS: {mss}바이트")
print(f"10KiB: 세그먼트 {segments}개, 마지막 세그먼트 {data - mss * (segments - 1)}바이트")MSS: 1460바이트
10KiB: 세그먼트 8개, 마지막 세그먼트 20바이트10,240바이트를 1,460바이트씩 나누면 일곱 개로 10,220바이트를 보내고 20바이트가 남으므로, 마지막의 작은 세그먼트까지 모두 여덟 개가 필요합니다. 이더넷 프레임의 헤더와 FCS는 MTU 1500 바깥에 붙으므로 여기서 빼지 않습니다. 실제 TCP는 RFC 9293에 적힌 대로 상대가 알려 준 MSS와 경로의 MTU를 함께 따지고, 헤더에 옵션이 붙으면 그만큼 데이터를 줄이므로, 이 계산은 옵션이 없다고 가정한 모델입니다.
단편화와 경로 MTU
보내려는 IP 패킷이 경로 중간 링크의 MTU보다 크면 문제가 생깁니다. IPv4에서는 패킷 헤더의 DF(don't fragment) 비트가 꺼져 있으면 라우터가 패킷을 여러 조각으로 나누는 단편화(fragmentation)를 하고, DF가 켜져 있으면 패킷을 버리고 송신자에게 ICMP로 더 작게 보내라고 알립니다. IPv6에서는 중간의 라우터가 단편화를 하지 않으며, RFC 8200에 따라 필요하면 송신자만 단편화합니다.
단편화된 패킷은 조각 하나만 잃어도 전체를 다시 보내야 하고, 라우터와 받는 쪽에 부담을 줍니다. 그래서 요즘은 경로 MTU 발견(path MTU discovery)으로 경로에서 가장 작은 MTU를 알아내 처음부터 그보다 작게 보냅니다. 방화벽이 ICMP를 모두 막으면 이 알림이 송신자에게 닿지 못해, 작은 패킷은 잘 가는데 큰 패킷만 사라지는 이상한 증상이 생기기도 합니다.
더 깊이: 단편화는 왜 피해야 할 일이 되었나
IPv4 헤더에는 단편화를 위한 칸이 따로 있습니다. 같은 원래 패킷에서 나온 조각들은 16비트 식별자(Identification) 값이 같고, 각 조각은 원래 데이터에서 자기가 차지하는 위치를 8바이트 단위의 조각 오프셋(fragment offset)으로 적습니다. 마지막 조각이 아니면 MF(more fragments) 비트를 켭니다. 조각을 다시 합치는 재조립(reassembly)은 중간의 라우터가 아니라 목적지 호스트만 하므로, 조각들은 각자 독립된 패킷처럼 경로를 지나갑니다.
RFC 8900은 이 구조가 오늘날의 망에서 왜 쉽게 깨지는지를 정리한 운영 지침(BCP 230)입니다. 포트 번호가 든 전송 계층 헤더는 첫 조각에만 있으므로, 포트를 보고 판단하는 방화벽이나 로드 밸런서는 나머지 조각을 어떻게 다룰지 정하기 어렵고, 조각을 통째로 버리는 장비도 생깁니다. 또 16비트 식별자는 전송 속도가 높으면 값이 금방 한 바퀴를 돌아 다시 쓰이므로, 서로 다른 패킷의 조각이 잘못 합쳐질 수 있고 TCP나 UDP의 체크섬이 그 오류를 놓칠 수도 있습니다. IPv6는 단편화 헤더의 식별자를 32비트로 넓혔으므로, 이런 재조립 오류가 IPv4만큼 자주 일어나지는 않습니다.
그래서 RFC 8900은 새 프로토콜과 애플리케이션이 IP 단편화에 기대지 않도록 권합니다. TCP처럼 처음부터 작은 단위로 나누어 보내고, 경로 MTU는 ICMP 알림만 믿지 말고 실제로 보낸 패킷이 확인 응답을 받는지로 찾으라는 것입니다. 단편화 자체를 없애자는 것은 아니어서, IPv4의 모든 목적지는 576바이트 패킷을 한 번에 받든 조각으로 받든 받아 낼 수 있어야 하고, IPv6는 모든 링크의 MTU가 1280바이트 이상이어야 한다는 하한을 둡니다.
지연을 이루는 네 가지
지연(latency)은 데이터가 출발지에서 목적지까지 가는 데 걸리는 시간이며, 링크 하나를 지날 때마다 네 가지 지연이 더해집니다. 처리 지연(processing delay)은 장비가 헤더를 읽고 어디로 보낼지 정하는 시간, 대기 지연(queuing delay)은 앞서 온 패킷들이 나갈 때까지 기다리는 시간입니다. 전송 지연(transmission delay)은 패킷의 비트들을 링크에 밀어 넣는 데 걸리는 시간으로 패킷 크기를 전송 속도로 나눈 값이고, 전파 지연(propagation delay)은 링크에 들어간 비트가 반대편까지 이동하는 시간으로 거리를 신호의 속도로 나눈 값입니다.
지연을 만드는 네 가지 요인
송신 장비의 처리와 앞선 데이터의 대기를 더합니다. 1200km를 가정한 전파 속도로 이동합니다. 각 단계의 값과 전환 이유를 비교합니다.
LINK DELAY · DISTANCE / RATE / WORK / QUEUE
전송률을 높여도 거리에서 생기는 전파 시간은 남는다
한 데이터 단위의 지연은 전파·전송·처리·큐잉 요인으로 나누어 봅니다. 각각의 원인을 구분해야 링크 증설·가까운 서버·장비 처리·혼잡 관리의 효과를 판단할 수 있습니다.
첫 비트는 전송 중에도 이동합니다. 합계는 마지막 비트 도착까지의 모델이며 다른 링크·수신 후 처리·응답 경로는 제외했습니다. 전송률만 1Gbps로 높이면 전송 0.012ms, 합계 6.242ms로 줄지만 전파 6ms는 남습니다.
| 유형 | 시간을 만드는 요인 | 줄이는 방법 |
|---|---|---|
| 전파 지연 | 거리 ÷ 매체 내 신호 속도. 같은 속도라면 경로가 길수록 증가합니다. | 가까운 서버·CDN으로 경로 길이를 줄입니다. |
| 전송 지연 | 데이터 비트 수 ÷ 링크 전송률. 큰 패킷·낮은 전송률에서 증가합니다. | 전송률을 높이거나 압축·전송량 축소를 사용합니다. |
| 처리 지연 | 장비의 헤더 검사·처리 시간. 장비 성능과 기능에 따라 달라집니다. | 처리 용량을 확보하고 불필요한 처리를 줄입니다. |
| 큐잉 지연 | 송신 버퍼에서 앞선 데이터를 기다리는 시간. 혼잡·트래픽 집중에 따라 증가합니다. | 혼잡 제어·QoS·트래픽 분산을 적용합니다. |
- 전파 지연
- 시간을 만드는 요인: 거리 ÷ 매체 내 신호 속도. 같은 속도라면 경로가 길수록 증가합니다.
- 줄이는 방법: 가까운 서버·CDN으로 경로 길이를 줄입니다.
- 전송 지연
- 시간을 만드는 요인: 데이터 비트 수 ÷ 링크 전송률. 큰 패킷·낮은 전송률에서 증가합니다.
- 줄이는 방법: 전송률을 높이거나 압축·전송량 축소를 사용합니다.
- 처리 지연
- 시간을 만드는 요인: 장비의 헤더 검사·처리 시간. 장비 성능과 기능에 따라 달라집니다.
- 줄이는 방법: 처리 용량을 확보하고 불필요한 처리를 줄입니다.
- 큐잉 지연
- 시간을 만드는 요인: 송신 버퍼에서 앞선 데이터를 기다리는 시간. 혼잡·트래픽 집중에 따라 증가합니다.
- 줄이는 방법: 혼잡 제어·QoS·트래픽 분산을 적용합니다.
입력 가정을 분리한 계산 예시 · 실제 RTT 측정 아님
전송 시간
1,500바이트 × 8 ÷ 100Mbps = 0.12ms
같은 데이터·1Gbps → 0.012ms
전파 시간
1,200km ÷ 200,000km/s = 편도 6ms
같은 거리·신호 속도 → 전송률과 무관
예시 거리는 경로 관측값이 아니며 전체 지연에는 처리·큐잉과 다른 링크의 시간이 추가됩니다. ping RTT와 curl 누적 시간을 이 두 수치의 측정 결과로 대체하지 않습니다.
이 예의 조건
- 본문과 전체 도표의 예를 따라 처리와 대기부터 마지막 비트 도착까지 진행합니다.
관계도의 보충 설명
| 구분 | 전체 도표의 내용 |
|---|---|
| 관계 설명 | 한 패킷의 마지막 비트를 기준으로 송신 장비의 처리, 대기, 링크에 비트를 넣는 전송, 링크를 건너는 전파를 구분합니다. 주어진 처리 0.03 밀리초, 대기 0.20, 전송 0.12, 전파 6의 합은 6.35 밀리초입니다. 첫 비트는 뒤 비트의 전송 중에도 이동하므로 이 도식은 모든 비트가 링크에 들어간 뒤에야 이동을 시작한다는 뜻이 아닙니다. 다른 링크, 수신 후 처리, 왕복 경로는 제외한 가정 사례입니다. |
| 표의 값 | 전파 지연시간을 만드는 요인: 거리 ÷ 매체 내 신호 속도. 같은 속도라면 경로가 길수록 증가합니다. 줄이는 방법: 가까운 서버·CDN으로 경로 길이를 줄입니다. 전송 지연시간을 만드는 요인: 데이터 비트 수 ÷ 링크 전송률. 큰 패킷·낮은 전송률에서 증가합니다. 줄이는 방법: 전송률을 높이거나 압축·전송량 축소를 사용합니다. 처리 지연시간을 만드는 요인: 장비의 헤더 검사·처리 시간. 장비 성능과 기능에 따라 달라집니다. 줄이는 방법: 처리 용량을 확보하고 불필요한 처리를 줄입니다. 큐잉 지연시간을 만드는 요인: 송신 버퍼에서 앞선 데이터를 기다리는 시간. 혼잡·트래픽 집중에 따라 증가합니다. 줄이는 방법: 혼잡 제어·QoS·트래픽 분산을 적용합니다. |
| 관찰·상황 | 값·보충 정보 | 변화·이유 |
|---|---|---|
| 처리와 대기 송신 장비의 처리와 앞선 데이터의 대기를 더합니다. |
|
|
| 비트 넣기 1500바이트를 100Mbps 링크에 넣습니다. |
|
|
| 마지막 비트 도착 1200km를 가정한 전파 속도로 이동합니다. |
|
|
지연을 만드는 네 가지 요인
송신 장비의 처리와 앞선 데이터의 대기를 더합니다. 1200km를 가정한 전파 속도로 이동합니다. 각 단계의 값과 전환 이유를 비교합니다.
LINK DELAY · DISTANCE / RATE / WORK / QUEUE
전송률을 높여도 거리에서 생기는 전파 시간은 남는다
한 데이터 단위의 지연은 전파·전송·처리·큐잉 요인으로 나누어 봅니다. 각각의 원인을 구분해야 링크 증설·가까운 서버·장비 처리·혼잡 관리의 효과를 판단할 수 있습니다.
첫 비트는 전송 중에도 이동합니다. 합계는 마지막 비트 도착까지의 모델이며 다른 링크·수신 후 처리·응답 경로는 제외했습니다. 전송률만 1Gbps로 높이면 전송 0.012ms, 합계 6.242ms로 줄지만 전파 6ms는 남습니다.
| 유형 | 시간을 만드는 요인 | 줄이는 방법 |
|---|---|---|
| 전파 지연 | 거리 ÷ 매체 내 신호 속도. 같은 속도라면 경로가 길수록 증가합니다. | 가까운 서버·CDN으로 경로 길이를 줄입니다. |
| 전송 지연 | 데이터 비트 수 ÷ 링크 전송률. 큰 패킷·낮은 전송률에서 증가합니다. | 전송률을 높이거나 압축·전송량 축소를 사용합니다. |
| 처리 지연 | 장비의 헤더 검사·처리 시간. 장비 성능과 기능에 따라 달라집니다. | 처리 용량을 확보하고 불필요한 처리를 줄입니다. |
| 큐잉 지연 | 송신 버퍼에서 앞선 데이터를 기다리는 시간. 혼잡·트래픽 집중에 따라 증가합니다. | 혼잡 제어·QoS·트래픽 분산을 적용합니다. |
- 전파 지연
- 시간을 만드는 요인: 거리 ÷ 매체 내 신호 속도. 같은 속도라면 경로가 길수록 증가합니다.
- 줄이는 방법: 가까운 서버·CDN으로 경로 길이를 줄입니다.
- 전송 지연
- 시간을 만드는 요인: 데이터 비트 수 ÷ 링크 전송률. 큰 패킷·낮은 전송률에서 증가합니다.
- 줄이는 방법: 전송률을 높이거나 압축·전송량 축소를 사용합니다.
- 처리 지연
- 시간을 만드는 요인: 장비의 헤더 검사·처리 시간. 장비 성능과 기능에 따라 달라집니다.
- 줄이는 방법: 처리 용량을 확보하고 불필요한 처리를 줄입니다.
- 큐잉 지연
- 시간을 만드는 요인: 송신 버퍼에서 앞선 데이터를 기다리는 시간. 혼잡·트래픽 집중에 따라 증가합니다.
- 줄이는 방법: 혼잡 제어·QoS·트래픽 분산을 적용합니다.
입력 가정을 분리한 계산 예시 · 실제 RTT 측정 아님
전송 시간
1,500바이트 × 8 ÷ 100Mbps = 0.12ms
같은 데이터·1Gbps → 0.012ms
전파 시간
1,200km ÷ 200,000km/s = 편도 6ms
같은 거리·신호 속도 → 전송률과 무관
예시 거리는 경로 관측값이 아니며 전체 지연에는 처리·큐잉과 다른 링크의 시간이 추가됩니다. ping RTT와 curl 누적 시간을 이 두 수치의 측정 결과로 대체하지 않습니다.
이 예의 조건
- 본문과 전체 도표의 예를 따라 처리와 대기부터 마지막 비트 도착까지 진행합니다.
관계도의 보충 설명
| 구분 | 전체 도표의 내용 |
|---|---|
| 관계 설명 | 한 패킷의 마지막 비트를 기준으로 송신 장비의 처리, 대기, 링크에 비트를 넣는 전송, 링크를 건너는 전파를 구분합니다. 주어진 처리 0.03 밀리초, 대기 0.20, 전송 0.12, 전파 6의 합은 6.35 밀리초입니다. 첫 비트는 뒤 비트의 전송 중에도 이동하므로 이 도식은 모든 비트가 링크에 들어간 뒤에야 이동을 시작한다는 뜻이 아닙니다. 다른 링크, 수신 후 처리, 왕복 경로는 제외한 가정 사례입니다. |
| 표의 값 | 전파 지연시간을 만드는 요인: 거리 ÷ 매체 내 신호 속도. 같은 속도라면 경로가 길수록 증가합니다. 줄이는 방법: 가까운 서버·CDN으로 경로 길이를 줄입니다. 전송 지연시간을 만드는 요인: 데이터 비트 수 ÷ 링크 전송률. 큰 패킷·낮은 전송률에서 증가합니다. 줄이는 방법: 전송률을 높이거나 압축·전송량 축소를 사용합니다. 처리 지연시간을 만드는 요인: 장비의 헤더 검사·처리 시간. 장비 성능과 기능에 따라 달라집니다. 줄이는 방법: 처리 용량을 확보하고 불필요한 처리를 줄입니다. 큐잉 지연시간을 만드는 요인: 송신 버퍼에서 앞선 데이터를 기다리는 시간. 혼잡·트래픽 집중에 따라 증가합니다. 줄이는 방법: 혼잡 제어·QoS·트래픽 분산을 적용합니다. |
| 관찰·상황 | 값·보충 정보 | 변화·이유 |
|---|---|---|
| 처리와 대기 송신 장비의 처리와 앞선 데이터의 대기를 더합니다. |
|
|
| 비트 넣기 1500바이트를 100Mbps 링크에 넣습니다. |
|
|
| 마지막 비트 도착 1200km를 가정한 전파 속도로 이동합니다. |
|
|
그림의 링크를 계산으로 따라가 봅니다. 1500바이트 패킷을 처리하는 데 0.03ms, 대기에 0.20ms가 걸리고, 100Mbps 링크로 1,200km를 간다고 가정합니다.
def one_link_ms(size_bytes, rate_bps, distance_km, processing_ms, queuing_ms, speed_km_s=200_000):
transmission = size_bytes * 8 / rate_bps * 1000 # 비트를 링크에 밀어 넣는 시간
propagation = distance_km / speed_km_s * 1000 # 비트가 링크를 따라 이동하는 시간
return processing_ms + queuing_ms + transmission + propagation, transmission
for rate_bps in (100e6, 1e9):
total, tx = one_link_ms(1500, rate_bps, 1200, processing_ms=0.03, queuing_ms=0.20)
print(f"{rate_bps / 1e6:.0f}Mbps: 전송 지연 {tx:.3f}ms, 마지막 비트 도착 {total:.3f}ms")100Mbps: 전송 지연 0.120ms, 마지막 비트 도착 6.350ms
1000Mbps: 전송 지연 0.012ms, 마지막 비트 도착 6.242ms1500바이트는 12,000비트이므로 100Mbps 링크에 밀어 넣는 데 0.12ms가 걸리고, 1,200km를 가는 데 6ms가 걸려, 마지막 비트는 6.35ms에 도착합니다. 첫 비트는 뒤의 비트들을 밀어 넣는 동안 이미 이동하고 있으므로, 이 값은 마지막 비트가 도착하는 시각입니다. 링크를 열 배 빠른 1Gbps로 바꾸어도 줄어드는 것은 전송 지연 0.108ms뿐이고 합계는 6.242ms로 거의 그대로인데, 이 거리에서는 전파 지연이 대부분을 차지하기 때문입니다. 지연을 줄이려면 어느 성분이 큰지부터 알아야 합니다.
실제 경로의 지연은 다음 명령으로 잽니다.
ping -c 10 example.com # 왕복 시간(RTT)의 최소, 평균, 최대, 편차
traceroute -n example.com # 경로의 각 라우터까지의 왕복 시간
curl -o /dev/null -s -w 'dns %{time_namelookup}\nconnect %{time_connect}\ntls %{time_appconnect}\nfirst byte %{time_starttransfer}\ntotal %{time_total}\n' https://example.comcurl의 문서에 따르면 time_으로 시작하는 값들은 모두 요청을 시작한 순간부터 그 단계가 끝날 때까지의 누적 시간입니다. 그래서 단계별 시간은 이웃한 값의 차이로 구합니다. 새로 연결한 HTTPS 요청에서 time_namelookup이 0.010초, time_connect가 0.050초, time_appconnect가 0.090초였다면, 이름 해석에 10ms, TCP 연결에 40ms, 암호화 연결을 맺는 TLS 핸드셰이크(handshake)에 40ms가 걸린 것입니다.
대역폭과 처리량
대역폭(bandwidth)은 링크가 단위 시간에 보낼 수 있는 최대 데이터의 양으로, bps 단위로 나타냅니다. 처리량(throughput)은 실제로 단위 시간에 전달된 데이터의 양입니다. 처리량은 대역폭보다 작은데, 경로에서 가장 좁은 링크, 다른 흐름과의 경쟁, 손실과 재전송, 헤더의 몫 때문입니다.
지연도 처리량을 묶습니다. TCP는 상대의 확인 응답을 받지 않고 보낼 수 있는 데이터의 양인 윈도우(window)를 두는데, 윈도우를 다 보내면 확인 응답이 돌아올 때까지 기다려야 하므로, 한 번의 왕복 시간(RTT)에 윈도우 크기만큼만 보낼 수 있습니다. 아래 코드의 주석과 출력에서는 윈도우를 우리말로 창이라고 적었습니다.
LINK_MBPS = 1000 # 1Gbps 회선
def window_limited_mbps(window_bytes, rtt_ms):
return window_bytes * 8 / (rtt_ms / 1000) / 1e6 # RTT 한 번에 창 크기만큼만 보낼 수 있다
for window_kib, rtt_ms in ((64, 10), (64, 100), (4096, 100)):
limit = min(window_limited_mbps(window_kib * 1024, rtt_ms), LINK_MBPS)
print(f"창 {window_kib}KiB, RTT {rtt_ms}ms: 처리량 최대 {limit:.1f}Mbps")
bdp = LINK_MBPS * 1e6 / 8 * 0.100 # 대역폭 × 지연 곱(바이트)
print(f"RTT 100ms에서 1Gbps를 채우는 데 필요한 창: {bdp / 1024:.0f}KiB")창 64KiB, RTT 10ms: 처리량 최대 52.4Mbps
창 64KiB, RTT 100ms: 처리량 최대 5.2Mbps
창 4096KiB, RTT 100ms: 처리량 최대 335.5Mbps
RTT 100ms에서 1Gbps를 채우는 데 필요한 창: 12207KiB윈도우가 64KiB라면 1Gbps 회선을 쓰더라도 RTT가 100ms인 먼 서버와는 5.2Mbps밖에 내지 못합니다. 회선을 채우려면 대역폭에 RTT를 곱한 만큼, 곧 대역폭-지연 곱(bandwidth-delay product)만큼의 데이터가 늘 전송 중이어야 하며, 여기서는 약 12MiB입니다. 그래서 먼 거리의 큰 전송에서는 윈도우를 키우는 것이 중요하며, TCP의 윈도우가 어떻게 정해지는지는 6장 3편에서 다룹니다.
병목과 혼잡
느린 원인은 크게 두 가지로 나뉩니다. 병목(bottleneck)은 경로에서 가장 좁은 링크가 전체의 처리량을 묶는 것입니다.
서버 ──1Gbps──> 라우터 ──1Gbps──> ISP ──100Mbps──> 사용자
↑
병목: 처리량은 많아야 100Mbps이 경로에서 서버 쪽 링크를 10Gbps로 올려도 사용자 쪽의 100Mbps는 그대로이므로 처리량은 늘지 않습니다. 10GB(십진 단위)를 보내려면 80,000Mb이므로 100Mbps로 적어도 800초가 걸립니다.
혼잡(congestion)은 어느 지점에 트래픽이 한꺼번에 몰려 라우터의 대기열이 길어지는 것입니다. 대기 지연이 늘고, 대기열이 넘치면 패킷이 버려집니다. 라우터는 대기열이 넘치기 전에 패킷을 일부러 버리거나 표시를 남겨 송신자에게 미리 알리기도 하는데, 이를 능동적 대기열 관리(AQM)와 명시적 혼잡 알림(ECN)이라고 합니다. 송신자들이 손실을 보고 재전송만 늘리고 보내는 양을 줄이지 않으면, 재전송이 혼잡을 더 키워 쓸모 있는 데이터가 거의 전달되지 않는 혼잡 붕괴(congestion collapse)가 일어납니다. 1980년대 인터넷에서 실제로 일어난 이 일 때문에 TCP는 손실을 보고 보내는 양을 줄이는 혼잡 제어를 갖추게 되었습니다. ECN은 그 뒤 2001년의 RFC 3168에서 더해졌고, ECN 표시를 받은 송신자도 패킷 하나를 잃었을 때와 같이 보내는 양을 줄입니다.
| 구분 | 병목 | 혼잡 |
|---|---|---|
| 원인 | 경로에서 가장 좁은 링크 | 한때 몰린 트래픽 |
| 지속 | 구조가 바뀌기 전까지 계속된다 | 시간대와 부하에 따라 오르내린다 |
| 증상 | 처리량이 일정한 값에서 더 오르지 않는다 | 지연이 늘고 패킷마다 지연이 들쭉날쭉해지며(지터, jitter) 패킷이 사라진다 |
| 대응 | 회선 증설, CDN, 경로 개선 | 혼잡 제어, 트래픽 분산, QoS, AQM |
mtr -rw -c 20 example.com # 경로의 각 홉에서의 손실률과 지연을 20번 재어 요약
iperf3 -c server.example.com -t 30 # 직접 운영하는 iperf3 서버와의 처리량을 30초 동안 측정mtr에서 중간의 어느 홉만 손실이 크고 그 뒤의 홉들은 멀쩡하다면, 그 라우터가 ICMP 응답을 제한하고 있을 뿐 실제로 패킷을 잃는 것은 아닐 가능성이 큽니다. 손실이 그 홉부터 끝까지 이어질 때 그 구간을 의심합니다. iperf3는 반대편에 직접 띄운 서버가 있어야 처리량을 잴 수 있습니다.
정리
- 회선 교환은 경로의 자원을 예약하고, 패킷 교환은 예약 없이 링크를 나누어 써서 통계적 다중화로 더 많은 사용자를 받습니다.
- 패킷은 헤더와 페이로드로 이루어지며, 계층마다 헤더를 붙이는 캡슐화를 거칩니다.
- MTU는 링크가 실을 수 있는 IP 패킷의 최대 크기, MSS는 세그먼트 하나의 TCP 데이터 최대 크기이며, 경로의 MTU를 넘는 패킷은 단편화되거나 버려집니다.
- 지연은 처리, 대기, 전송, 전파 지연의 합이며, 먼 거리에서는 전파 지연이, 느린 링크에서는 전송 지연이 크게 됩니다.
- 처리량은 가장 좁은 링크와 윈도우 크기 ÷ RTT 가운데 작은 쪽을 넘지 못하며, 병목은 구조적인 제한이고 혼잡은 일시적인 트래픽의 몰림입니다.
연습 문제
1. MTU가 1280바이트라면
IPv4 헤더 20바이트와 TCP 헤더 20바이트에 옵션이 없다고 할 때, MTU가 1280바이트인 경로로 10KiB를 보내면 세그먼트는 몇 개이고 마지막 세그먼트에는 몇 바이트가 담기나요?
해설 보기
MSS는 1280 - 20 - 20 = 1240바이트입니다. 여덟 개로 1240 × 8 = 9,920바이트를 보내고 10,240 - 9,920 = 320바이트가 남으므로, 마지막 세그먼트에 320바이트가 담겨 모두 아홉 개가 됩니다. MTU가 1500일 때보다 세그먼트가 하나 늘고, 그만큼 헤더에 쓰는 바이트도 늘어납니다.
2. 대기가 늘어난 링크
본문의 링크(처리 0.03ms, 전송 0.12ms, 전파 6ms)에서 다른 트래픽이 몰려 대기 지연이 0.20ms에서 2ms로 늘었습니다. 마지막 비트가 도착하는 시각은 얼마가 되나요? 이 지연을 줄이려고 서버를 더 가까운 곳으로 옮기는 것은 알맞은 대응인가요?
해설 보기
0.03 + 2 + 0.12 + 6 = 8.15ms로, 처음보다 1.80ms 늘었습니다. 늘어난 것은 대기 지연뿐이므로, 원인은 거리나 링크의 속도가 아니라 그 지점에 몰린 트래픽입니다. 서버를 옮기면 전파 지연은 줄겠지만 몰린 트래픽은 그대로이므로, 혼잡한 구간을 찾아 트래픽을 나누거나 대역폭을 늘리는 쪽이 원인에 맞는 대응입니다. 지연이 늘었을 때는 어느 성분이 늘었는지부터 확인해야 합니다.
3. 빠른 회선인데 느린 다운로드
1Gbps 회선을 쓰는 사용자가 왕복 시간이 200ms인 해외 서버에서 파일을 받는데 처리량이 2.6Mbps 정도에서 더 오르지 않습니다. 다른 트래픽은 없고 손실도 없습니다. 무엇이 처리량을 묶고 있을 가능성이 크고, 1Gbps를 채우려면 윈도우가 얼마나 커야 하나요?
해설 보기
손실도 경쟁도 없는데 처리량이 일정한 값에 묶여 있다면 윈도우 크기를 의심합니다. 윈도우 64KiB, 곧 65,536바이트를 RTT 200ms마다 보내면 65,536 × 8 ÷ 0.2 ≈ 2.6Mbps이므로, 윈도우가 64KiB로 묶여 있다고 볼 수 있습니다. 1Gbps를 채우려면 대역폭-지연 곱인 10⁹ ÷ 8 × 0.2 = 25,000,000바이트, 약 24MiB의 윈도우가 필요합니다. TCP의 윈도우 확대 옵션과 운영체제의 소켓 버퍼 크기가 이를 허락하는지 확인해야 합니다.
다음 장의 왜 계층이 필요한가에서는 이렇게 복잡한 통신을 나누어 다루기 위한 계층 구조의 생각을 살펴봅니다.