개념
여기서 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 rootadd 와 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에서 다룬다.