네트워크 학습 노트 목차

성능과 디버깅 — 느림은 어디서 오나, 어떻게 보나

네트워크를 다 배워도, 현장의 질문은 결국 둘로 모인다 — *"왜 느린가"*와 "어디서 막혔나". 이 마지막 편은 그 둘에 답한다. 느림의 물리적 정체를 알면 엉뚱한 곳을 늘리는 헛수고를 피하고, 계층별 도구를 알면 장애를 빠르게 좁힌다. 01편에서 "문제도 계층으로 쪼개 본다"고 했던 그 약속을 여기서 갚는다.

느림의 정체 — 지연 vs 대역폭

성능을 *지연(latency)*과 대역폭(bandwidth) 두 가지로 나눠 봐야 한다. 대역폭한 번에 얼마나 굵게 보내느냐(차선 수), 지연한 조각이 가는 데 얼마나 걸리느냐(편도 시간)다. 흔한 오해가 — *"느리니 대역폭(인터넷 속도)을 올리자"*인데, 대역폭을 아무리 늘려도 지연은 안 줄어든다. 서울-뉴욕은 빛의 속도만으로도 편도 약 40ms, 왕복 80ms가 물리적으로 걸린다(거리 ÷ 광속). 그래서 왕복(RTT)이 많은 통신은 — 대역폭과 무관하게 왕복 횟수만큼 느리다.

다이어그램 로딩 중…

지연은 다시 — 전파 지연(거리/광속, 못 줄임), 전송 지연(데이터 크기/대역폭), 큐잉 지연(혼잡한 라우터에서 줄 섬), 처리 지연으로 쪼개진다. 여기에 패킷 손실(재전송으로 더 느려짐)과 *지터(jitter, 지연의 들쭉날쭉함 — 실시간에 치명적)*가 더해진다. 그래서 *성능 최적화의 핵심은 "왕복을 줄이는 것"*이다 — TCP·TLS 핸드셰이크를 아끼는 연결 재사용(keep-alive·커넥션 풀), 압축으로 보낼 양 줄이기, *CDN*으로 거리 줄이기, 캐싱으로 아예 안 가기. 이 노트 전체가 *"비싼 왕복을 아끼는 법"*의 변주였던 셈이다.

어디서 막혔나 — 계층별 도구

장애는 아래 계층부터 위로 하나씩 끊어 본다 — 각 계층에 전용 청진기가 있다.

계층질문도구
링크·IP상대 컴퓨터까지 닿나? 어디서 막히나?ping(도달·RTT), traceroute(경로·홉별 지연)
DNS이름이 IP로 풀리나?dig·nslookup(각 단계 응답)
전송(TCP)그 포트가 열렸나? 소켓 상태는?telnet/nc(포트 연결), ss/netstat(소켓·TIME_WAIT)
응용(HTTP)실제 응답·헤더·상태코드는?curl -v(요청/응답 상세)
전 계층실제로 어떤 패킷이 오갔나?tcpdump·wireshark(패킷 캡처)

흐름은 이렇다 — ping이 가는데 curl이 실패하면 IP는 닿지만 응용에서 막힌 것이고, dig가 옛 IP를 주면 DNS 캐시·TTL 문제이며(06편), ssTIME_WAIT이 수만 개연결을 너무 자주 맺고 끊는 것이다(04편). 한 계층씩 통과 여부를 확인하면 "어디까지 멀쩡하고 어디서 끊겼다"가 빠르게 좁혀진다.

실무에서는 — 막연한 "느려요"를 숫자로 바꾸는 게 먼저다. ping으로 RTT를, curl -wDNS·연결·TLS·첫바이트(TTFB)·전체 시간을 단계별로 재면 — 어느 단계가 느린지가 드러난다(DNS가 느린지, 핸드셰이크가 느린지, 서버 처리가 느린지). 그리고 대역폭은 충분한데 느리다면 십중팔구 *왕복이 많거나 거리가 멀거나 큐잉(혼잡)*이다 — 대역폭을 늘려도 안 풀린다. 전 세계 사용자를 상대하면 Anycast(같은 IP를 여러 지역에 두고 가장 가까운 곳으로 라우팅)와 CDN으로 거리 자체를 줄이는 게 정석이다.

실제로 눈으로 보기

지연과 대역폭을 따로 잰다

ping -c 20 example.com | tail -2
# 20 packets transmitted, 20 received, 0% packet loss, time 19028ms
# rtt min/avg/max/mdev = 11.204/12.881/18.442/1.821 ms
#                                            │      └ 흔들림(jitter)
#                                            └ 최악값

mdev(흔들림)가 크면 평균이 좋아도 체감이 나쁘다. 실시간성이 중요한 서비스는 평균보다 이 값과 최악값을 본다.

iperf3 -c speedtest.example.com -t 10
# [ ID] Interval        Transfer     Bitrate
# [  5] 0.00-10.00 sec  1.09 GBytes  938 Mbits/sec

둘은 독립이다. 회선을 100Mbps 에서 1Gbps 로 올려도 RTT 는 그대로다 — 거리와 홉 수가 정하기 때문이다.

대역폭이 남는데 느린 이유를 계산한다

처리량 ≈ 윈도우 크기 / RTT

64KB / 200ms = 320KB/s ≈ 2.6Mbps

회선이 1Gbps 여도 이 상한을 못 넘는다. 대역폭 지연 곱(BDP) 이 이것이고, 서울–미국 전송이 느린 근본 이유다.

ss -ti dst <원격IP> | grep -oE 'rtt:[0-9.]+|cwnd:[0-9]+|bytes_acked:[0-9]+'

해법은 대역폭을 늘리는 것이 아니라 — 윈도우를 키우거나(윈도우 스케일링), 연결을 여러 개 쓰거나, 거리를 줄이는 것(CDN) 이다.

어느 홉에서 느려지나

mtr -rwc 50 example.com
# HOST                    Loss%  Snt  Last  Avg  Best  Wrst StDev
# 1. gateway               0.0%   50   0.4  0.5   0.3   1.2   0.1
# 2. 10.20.0.1             0.0%   50   8.2  8.4   7.9  12.1   0.6
# 3. 61.43.x.x            18.0%   50  42.1 44.8  39.2  98.4  11.2   ← 여기
# 4. example.com           0.0%   50  44.0 44.2  43.1  46.0   0.5

3번에서 손실이 18%인데 4번은 0% 인 점이 중요하다. 이런 경우는 대개 그 홉의 라우터가 ICMP 응답을 낮은 우선순위로 처리한 것이지 실제 손실이 아니다.

진짜 손실이면 그 뒤 홉들도 함께 나빠진다. 마지막 홉의 손실률이 0이라면 경로는 정상이다. 이 구분을 모르면 멀쩡한 구간을 붙잡고 시간을 버린다.

어디에 시간이 갔나

curl -s -o /dev/null -w \
'dns   %{time_namelookup}\ntcp   %{time_connect}\ntls   %{time_appconnect}\nttfb  %{time_starttransfer}\ntotal %{time_total}\n' \
https://example.com

# dns   0.021
# tcp   0.045      ← dns 이후 24ms = 핸드셰이크 1RTT
# tls   0.118      ← tcp 이후 73ms = TLS 협상
# ttfb  0.164      ← tls 이후 46ms = 서버 처리
# total 0.166      ← ttfb 이후 2ms = 본문 전송

누적값이라 빼서 봐야 한다. 위 예에서는 TLS 협상이 73ms 로 가장 크다 — TLS 1.3 으로 올리거나 세션 재개를 켜면 여기가 준다. 반대로 ttfb - tls가 크면 네트워크가 아니라 서버가 느린 것이라 네트워크를 아무리 손봐도 안 나아진다.

연결을 재사용하면 앞의 셋이 사라진다

curl -s -o /dev/null -w '%{time_total}\n' https://example.com https://example.com
# 0.166      ← 첫 요청 — DNS·TCP·TLS 전부
# 0.048      ← 두 번째 — 연결 재사용, 서버 처리만

3배 이상 차이가 난다. Keep-Alive 와 커넥션 풀이 성능에 미치는 영향이 이 숫자다. 매 요청마다 연결을 새로 맺는 클라이언트는 이 비용을 계속 낸다.

정리하면, 네트워크 성능은 지연과 대역폭으로 나눠 봐야 한다 — 대역폭을 늘려도 지연(거리·왕복·큐잉)은 안 준다. 그래서 최적화의 핵심은 왕복을 줄이는 것(연결 재사용·압축·CDN·캐싱)이다. 장애는 계층별 도구(ping·traceroute·dig·curl·ss·tcpdump)로 아래부터 위로 끊어 좁히고, "느려요"는 단계별 숫자(RTT·TTFB)로 바꿔 원인을 짚는다. 이렇게 전기 신호(01편)부터 지연 측정(11편)까지 — 네트워크는 결국 "성격 다른 문제를 계층으로 나눠, 각 층이 자기 일만 하게 한" 거대한 협업이다. 그 협업의 첫 페이지에서 본 계층화가, 마지막 페이지의 디버깅까지를 관통한다.

로드밸런싱·프록시·CDN — 한 대로 안 될 때