본문으로 건너뛰기
고급약 26분

리눅스 AP + tc netem — 팀 공용 저하 랩 만들기

라즈베리파이 등으로 테스트 전용 Wi-Fi를 만들고 커널에서 트래픽을 제어한다. netem의 대역폭·지연·지터·손실 문법과 상·하향 비대칭 구성, 조건 스크립트화, 그리고 재현성이 가장 높은 이유를 다룬다.

  • #tc
  • #netem
  • #리눅스
  • #라즈베리파이
  • #테스트랩
  • #재현성

개념

여기서 AP 는 무선 공유기(Access Point)를 가리킨다. 시험할 기기들을 우리가 만든 공유기에 붙여 두면, 그 공유기에서 일부러 느리게·끊기게 만들어 모든 기기에 같은 조건을 줄 수 있다.

앞선 방법들이 남긴 빈칸

기기 내장 (레슨 3)프록시 (레슨 4)
iOS·Android 동시❌ 각각 다르게 설정⭕
지연·손실 (Android)❌⭕ (HTTP만)
인증서 피닝 앱⭕❌
QUIC / UDP / 소켓⭕❌
조건이 기록으로 남는가❌ GUI 조작△ 스크립트 가능
여러 기기 동시 적용❌△

빈칸을 모두 채우는 방법이 하나 있다. 저하를 OS나 앱 계층이 아니라 커널의 네트워크 큐에서 거는 것 이다.

리눅스 머신 하나를 Wi-Fi 액세스 포인트로 만들고, 그 인터페이스에 tc netem 으로 조건을 건다. 폰은 그냥 그 Wi-Fi에 붙기만 하면 된다. 폰에는 아무 설정도, 인증서도, 개발자 모드도 필요 없다.

  테스트 기기들                리눅스 AP                     인터넷
  (iOS/Android/PC)   ←Wi-Fi→   wlan0  ── eth0 ──→
   설정 불필요                    ↑
                          여기서 커널이 패킷을 지연·폐기·제한
                          TLS·QUIC·소켓 무관하게 전부 적용

TLS 아래 계층에서 동작하므로 인증서 피닝과 무관하고, IP 계층이므로 TCP든 UDP든 QUIC이든 똑같이 걸린다. 레슨 4의 두 가지 큰 한계가 여기서 사라진다.

준비물

하드웨어   라즈베리파이 1대 (또는 남는 리눅스 PC + USB 무선랜)
소프트웨어 hostapd (AP 기능), dnsmasq (DHCP·DNS)
연결       유선 업링크 (eth0) + 무선 AP (wlan0)
구축 시간  초기 반나절 정도

한 번 만들어두면 팀 전체가 계속 쓴다. 구축은 한 번, 사용은 매번 이라 투자 대비 회수가 빠르다.

netem 기본 문법

핵심 명령은 세 개뿐이다.

# 조건 걸기 (처음)
sudo tc qdisc add dev wlan0 root netem <옵션들>
 
# 조건 바꾸기 (이미 걸려 있을 때)
sudo tc qdisc change dev wlan0 root netem <옵션들>
 
# 원복
sudo tc qdisc del dev wlan0 root

add 와 change 를 헷갈리면 에러가 난다. 이미 규칙이 있는데 add 하면 실패하고, 없는데 change 하면 실패한다. 스크립트에서는 보통 del 을 먼저 시도하고(실패해도 무시) add 로 통일한다.

옵션 — 레슨 1의 4축이 그대로 대응된다

rate 10mbit           # ① 대역폭
delay 150ms           # ② 지연
delay 150ms 30ms      # ②+③ 지연 150ms ± 지터 30ms
loss 5%               # ④ 손실

지터를 넣는 방법이 이렇게 간단하다. delay 뒤에 값을 하나 더 적으면 그게 편차다. 레슨 1에서 "조건표에 지터 칸이 없다"고 했던 문제가 여기서는 한 글자 추가로 해결된다.

분포와 상관관계까지 줄 수 있다.

# 정규분포 지터 — 실제 회선에 더 가깝다
delay 150ms 30ms distribution normal
 
# 지연 상관관계 25% — 앞 패킷의 지연이 다음에 영향을 준다
delay 150ms 30ms 25%
 
# 버스트 손실 — 연속으로 잃는 실제 무선 환경에 가깝다
loss 5% 25%

마지막 것이 특히 의미 있다. 실제 무선 손실은 균등하게 흩어지지 않고 몰려서 일어난다. loss 5% 만 쓰면 이상적으로 흩어진 손실이고, loss 5% 25% 는 상관관계를 줘서 연속 손실이 생긴다. 후자가 지하철에서 겪는 상황에 훨씬 가깝다.

조건별 명령 — 상태 모델을 그대로 옮긴다

레슨 1의 4단계를 명령으로 옮기면 이렇게 된다.

# 정상
sudo tc qdisc add dev wlan0 root netem \
     rate 100mbit delay 40ms
 
# 저하 (열악한 환경 — 10Mbps / 150ms / 손실 5%)
sudo tc qdisc change dev wlan0 root netem \
     rate 10mbit delay 150ms 30ms loss 5% 25%
 
# Limited Service (100kbps / 1s / 손실 30%)
sudo tc qdisc change dev wlan0 root netem \
     rate 100kbit delay 1000ms 200ms loss 30%
 
# 단절
sudo tc qdisc change dev wlan0 root netem loss 100%
 
# 원복
sudo tc qdisc del dev wlan0 root

명령 한 줄이 곧 조건의 완전한 명세다. 이게 이 방법의 가장 큰 가치다. 결함 리포트에 이 한 줄을 붙이면 개발자가 똑같은 환경을 재현할 수 있다. "3G 프로파일로 했어요"와는 재현 가능성이 다르다.

상·하향 비대칭 만들기

root 에 거는 규칙은 송신(egress) 방향 에만 적용된다. AP의 wlan0 egress = 폰 입장에서는 다운로드다.

레슨 1에서 강조한 비대칭을 만들려면 업로드 방향도 따로 걸어야 하는데, 이건 ifb(intermediate functional block) 가상 장치로 ingress를 우회시켜 처리한다.

# ifb 준비
sudo modprobe ifb
sudo ip link set dev ifb0 up
 
# wlan0의 수신(폰 → 인터넷 = 업로드)을 ifb0으로 넘긴다
sudo tc qdisc add dev wlan0 handle ffff: ingress
sudo tc filter add dev wlan0 parent ffff: protocol all u32 \
     match u32 0 0 action mirred egress redirect dev ifb0
 
# 다운로드 (wlan0 egress)
sudo tc qdisc add dev wlan0 root netem rate 5mbit delay 150ms
 
# 업로드 (ifb0 egress)
sudo tc qdisc add dev ifb0 root netem rate 500kbit delay 150ms

처음 보면 복잡하지만 한 번 스크립트로 만들어두면 그다음부터는 숫자만 바꾼다. 업로드 경로가 있는 제품이라면 이 구성을 갖출 가치가 충분하다.

조건을 스크립트로 고정한다

이 방법의 재현성은 명령을 스크립트 파일로 남길 때 완성된다.

lab/
├── normal.sh      정상
├── poor.sh        저하
├── limited.sh     Limited Service
├── extreme.sh     극단
├── offline.sh     단절
└── reset.sh       원복

각 스크립트는 조건 식별자와 1:1로 대응시킨다. 레슨 1에서 만든 저하-B, Limited-A 같은 이름이 파일명이 된다.

#!/usr/bin/env bash
# limited.sh — 조건 Limited-A
# 다운 100kbps / RTT 1,000ms ± 200ms / 손실 30%
set -e
IFACE=wlan0
sudo tc qdisc del dev $IFACE root 2>/dev/null || true
sudo tc qdisc add dev $IFACE root netem \
     rate 100kbit delay 1000ms 200ms loss 30%
echo "[Limited-A] 적용됨 — 기기에서 속도를 측정해 확인하세요"
tc qdisc show dev $IFACE

마지막 두 줄이 중요하다. 적용된 규칙을 출력해서 눈으로 확인하게 하고, 측정하라고 알려준다. 레슨 2의 "설정했다 ≠ 적용되었다" 원칙을 스크립트가 스스로 상기시키는 구조다.

운영 규칙 세 가지

1. AP 이름을 고정한다. QA-NET 같은 이름을 쓴다. 사내 일반 Wi-Fi와 헷갈리면 "저하가 안 걸린다"는 소동이 난다. 반대로 모르고 이 AP에 붙은 사람이 "인터넷이 이상하다"고 신고하는 일도 막는다.

2. 실행한 스크립트명과 측정 결과를 결함 리포트에 남긴다. 조건명만 적으면 나중에 스크립트가 바뀌었을 때 어긋난다. 가능하면 적용된 tc 규칙 출력을 그대로 붙인다.

3. 현재 걸린 조건을 누구나 확인할 수 있게 한다.

tc qdisc show dev wlan0

공용 랩에서 가장 흔한 사고가 "앞사람이 걸어둔 조건 위에서 테스트하는 것" 이다. 테스트 시작 전에 이 명령 한 번 돌리는 것을 규칙으로 만든다.

검증 — 정말 걸렸는지 확인하기

기기에서 fast.com을 보는 것도 되지만, 랩에서는 더 정확한 도구를 쓸 수 있다.

# 대역폭 — AP에서 iperf3 서버를 띄우고 폰 앱에서 접속
iperf3 -s
 
# 지연·손실 — AP에서 폰 IP로
ping -c 50 <폰 IP>

ping 출력 마지막의 packet loss 와 stddev 가 각각 손실과 지터에 대응한다. 설정값과 측정값이 얼마나 일치하는지 한 번 확인해두면 그다음부터는 스크립트를 믿을 수 있다.

주의: rate 로 제한한 값은 프로토콜 오버헤드 때문에 측정값이 항상 조금 낮게 나온다. ±20% 이내면 정상 으로 본다.

실무에서 왜 필요한가

재현성이 다른 모든 방법보다 높다

"Charles에서 3G 프리셋으로 했어요"
   → 버전마다 프리셋 값이 다를 수 있다
   → 다른 사람이 같은 조건을 만들었는지 확신할 수 없다
 
"rate 10mbit delay 150ms 30ms loss 5% 25%"
   → 완전한 명세. 누가 언제 실행해도 같다

모듈 28 레슨 8에서 다룬 "증거" 개념이 여기서 자연스럽게 충족된다. 명령 자체가 증거다.

두 OS를 같은 조건으로 동시에 회귀할 수 있다

iOS 기기와 Android 기기를 같은 AP에 붙이면 완전히 동일한 네트워크 조건에서 동시에 테스트할 수 있다. 기기 내장 기능으로는 불가능한 일이다. 플랫폼 간 동작 차이를 볼 때 조건이 같다는 확신이 있어야 비교가 성립한다.

인증서 피닝·QUIC 문제가 사라진다

레슨 4에서 막혔던 앱도 여기서는 그대로 테스트된다. "우리 앱은 피닝 때문에 네트워크 테스트를 못 한다"는 말이 더 이상 성립하지 않는다.

자동화 회귀에 붙는다

스크립트이므로 CI에서 호출할 수 있다. 조건을 바꾸고, 테스트를 돌리고, 원복하는 흐름을 파이프라인으로 만들 수 있다. 레슨 8에서 다룬다.

실습 과제

⚠️ 이 과제는 본인이 관리 권한을 가진 장비와 네트워크에서만 수행한다. 사내 공용 네트워크 장비에 tc 규칙을 거는 것은 다른 사람의 통신에 영향을 준다. AP 구축과 대역 사용은 인프라 담당과 사전에 협의한다.

과제 1 — 로컬에서 netem 문법 익히기

AP를 구축하기 전에, 본인 리눅스 머신(또는 VM·컨테이너)의 루프백이나 가상 인터페이스 에서 문법만 먼저 익힌다.

# 확인
tc qdisc show dev <iface>
 
# 지연 100ms 걸고 ping으로 확인
sudo tc qdisc add dev <iface> root netem delay 100ms
ping -c 10 <대상>
 
# 원복
sudo tc qdisc del dev <iface> root

add / change / del 의 동작 차이를 직접 겪어본다.

과제 2 — 조건 스크립트 6종 작성

레슨 1의 상태 정의표를 스크립트 6개로 옮긴다(normal / poor / limited / extreme / offline / reset).

각 스크립트에 반드시 포함할 것:

  • 주석에 조건 식별자와 목표 수치
  • del 먼저 시도 후 add (멱등하게)
  • 마지막에 tc qdisc show 출력
  • "측정해서 확인하라"는 안내 문구

과제 3 — 지터 유무 비교

같은 평균 지연에서 지터만 다르게 걸고 체감을 비교한다.

조건명령체감
지터 없음delay 200ms
지터 큼delay 200ms 150ms distribution normal

실시간성이 있는 기능 (채팅, 영상, 애니메이션이 서버 응답에 묶인 화면) 에서 비교해야 차이가 드러난다. 평균은 같은데 체감이 다르다는 것을 직접 확인하는 것이 목적이다.

과제 4 — 버스트 손실 비교

loss 5%        vs   loss 5% 25%

두 조건에서 같은 시나리오를 각각 5회씩 돌려 실패 패턴을 비교한다. 후자에서 "가끔 완전히 실패"가 나타난다면 그게 실제 무선 환경에 더 가까운 결과다.

과제 5 — 비대칭 구성하고 업로드 검증

본문의 ifb 구성을 만들어 다운 5Mbps / 업로드 500kbps 조건을 걸고, 업로드 기능을 테스트한다. 레슨 1 과제 3에서 만든 업로드 경로 목록을 여기서 전부 돌린다.

과제 6 — 공용 랩 운영 규칙 문서 만들기

팀이 이 랩을 공유해서 쓴다고 가정하고 운영 규칙을 한 장으로 만든다. 포함할 것:

  • AP 이름과 접속 정보
  • 스크립트 위치와 사용법
  • 테스트 시작 전 tc qdisc show 로 현재 조건 확인 규칙
  • 사용 후 reset.sh 실행 규칙
  • 결함 리포트에 붙일 정보 (스크립트명 + tc 출력 + 측정 스크린샷)

자가 체크리스트

  • 커널 계층에서 저하를 거는 것이 왜 피닝·QUIC 문제를 피하는지 설명할 수 있다
  • tc qdisc add / change / del 의 차이와 실패 조건을 안다
  • rate / delay / loss 가 레슨 1의 어느 축에 대응하는지 안다
  • delay 150ms 30ms 에서 두 번째 값이 무엇인지 안다
  • distribution normal 과 상관관계 값이 무엇을 바꾸는지 설명할 수 있다
  • loss 5% 25% 가 loss 5% 와 어떻게 다른지, 왜 실제에 가까운지 안다
  • root 규칙이 어느 방향에 적용되는지 알고, 비대칭을 만들려면 무엇이 필요한지 안다
  • 조건 스크립트에 포함해야 할 네 가지를 말할 수 있다
  • 공용 랩의 3대 운영 규칙을 설명할 수 있다
  • 설정값과 측정값이 정확히 일치하지 않는 이유와 허용 범위를 안다
  • 이 방법이 다른 방법보다 재현성이 높은 이유를 결함 리포트 관점에서 설명할 수 있다

흔한 실수

앞사람이 걸어둔 조건 위에서 테스트한다. 공용 랩의 1번 사고다. 시작 전 tc qdisc show 를 규칙으로 만들지 않으면 반드시 일어난다.

root 규칙만 걸고 비대칭을 테스트했다고 생각한다. egress 한 방향만 걸린 상태다. 업로드는 그대로 빠르다.

지터를 안 쓴다. delay 150ms 만 쓰면 모든 패킷이 정확히 150ms 지연된 이상적인 회선이 된다. 현실에는 없는 회선이다.

손실 상관관계를 안 쓴다. 균등하게 흩어진 손실은 TCP가 비교적 잘 견딘다. 실제 사고는 몰려서 잃을 때 난다.

원복을 안 하고 자리를 뜬다. 다음 사람이 이유를 모른 채 이상한 결과를 얻는다. reset.sh 실행을 절차에 넣는다.

사내 공용 장비에 규칙을 건다. 본인 테스트를 위해 남의 통신을 느리게 만드는 일이 된다. 전용 AP를 쓰고, 대역과 장비는 사전 협의한다.

설정값과 측정값이 정확히 일치하지 않는다고 도구를 의심한다. 프로토콜 오버헤드 때문에 조금 낮게 나오는 것이 정상이다. ±20%를 기준으로 삼는다.

AP 이름을 사내망과 비슷하게 짓는다. 모르는 사람이 붙어서 "인터넷이 느리다"고 신고하거나, 테스터가 일반망에 붙어놓고 "저하가 안 걸린다"고 헤맨다.

참고 자료

  • man tc-netem — netem의 전체 옵션. 지연 분포, 상관관계, 재정렬(reorder), 중복(duplicate)까지
  • man tc — qdisc 계층 구조와 필터 문법. ifb 구성을 이해하려면 필요하다
  • hostapd / dnsmasq 문서 — 리눅스 머신을 AP로 만드는 표준적 구성
  • iperf3 문서 — 대역폭 실측. 설정값 검증에 fast.com보다 정확하다
  • 이 모듈 레슨 2 왜 시뮬레이션이 적용되지 않는가 — 적용 확인 6단계. 이 방법에도 그대로 적용된다
  • 이 모듈 레슨 8 테스트 케이스 설계와 자동화 — 여기서 만든 스크립트를 CI에 붙이는 방법
  • 모듈 14 CI/CD와 지속적 테스팅 — 파이프라인에서 외부 스크립트를 호출하는 구조

낯선 말 풀이

이 레슨에 나온 약어입니다. 본문에서도 처음 나올 때 점선으로 표시해 두었습니다 (제목과 링크 글자는 그대로 둡니다).

HTTP — 하이퍼텍스트 전송 규약
웹에서 요청과 응답을 주고받는 약속. "무엇을 달라"고 보내면 "여기 있다" 또는 "그런 건 없다"가 돌아온다.
GUI — 그래픽 사용자 인터페이스
글자만 있는 화면이 아니라 그림·버튼·창으로 조작하는 화면. 보통 말하는 "화면"이 이것이다.
OS — 운영체제
기기를 돌리는 바탕 프로그램 — 윈도우, 맥, 안드로이드 같은 것.
TLS — 전송 계층 보안
통신을 암호로 감싸는 기술 그 자체. 웹에서 쓰면 주소가 https로 시작한다.
IP — 아이피 주소
인터넷에 연결된 기기마다 붙는 번호.
TCP — 전송 제어 규약
데이터를 빠뜨리지 않고 순서대로 보내는 약속. 웹도 이 위에서 돌아간다.
CI — 지속적 통합
코드를 올릴 때마다 자동으로 합쳐서 빌드하고 시험까지 돌리는 방식.
VM — 가상 머신
한 컴퓨터 안에 소프트웨어로 만들어 낸 또 다른 컴퓨터.
컨테이너 — 컨테이너
이 커리큘럼에서 두 가지로 쓰인다. ① 프로그램과 그것을 돌리는 데 필요한 것들을 한 상자에 담아, 어느 컴퓨터에서나 똑같이 돌아가게 만든 것 ② 화면에서 여러 요소를 하나로 묶는 바깥 틀. 서버·배포 이야기면 ①, 화면·접근성 이야기면 ②다.

용어집 전체 보기