본문으로 건너뛰기
중급약 25분

프록시 스로틀링 — API 하나만 골라 느리게 만들기

PC에서 프록시를 띄워 스마트폰 트래픽의 대역폭·지연을 제어하고 특정 API만 골라 느리게 하거나 오류를 주입한다. 도구 비교와 구성 절차, 그리고 인증서 피닝·QUIC·소켓이라는 세 가지 한계를 다룬다.

  • #프록시
  • #Charles
  • #Proxyman
  • #mitmproxy
  • #오류주입
  • #QUIC
  • #HTTP3

개념

기기 내장 기능으로는 못 하는 것

레슨 3의 방법에는 공통된 한계가 있다. 전부 아니면 전무 라는 점이다. NLC를 켜면 기기의 모든 통신이 느려진다. Android 속도 제한도 마찬가지다.

그런데 실무에서 자주 필요한 건 이런 것이다.

"결제 승인 API만 5초 느리게 하고 나머지는 정상으로 두고 싶다"
"상품 목록은 정상인데 이미지 CDN만 느린 상황을 만들고 싶다"
"이 API가 500을 반환하면 화면이 어떻게 되는지 보고 싶다"
"응답은 오는데 본문이 잘려서 오는 상황을 만들고 싶다"

원인을 격리하려면 조건도 격리할 수 있어야 한다. 전체를 느리게 하면 어느 호출 때문에 화면이 멈췄는지 알 수 없다. 프록시 스로틀링이 이 자리를 채운다.

구성 — 폰 트래픽을 PC로 우회시킨다

레슨 2에서 "PC를 통과하는 트래픽은 PC 도구가 못 본다"고 했는데, 프록시는 예외다. 핫스팟처럼 '통과'시키는 게 아니라, 폰이 명시적으로 PC를 목적지로 삼아 요청을 보내기 때문 이다. 계층 문제가 발생하지 않는다.

1. PC와 폰을 같은 Wi-Fi에 연결한다
 
2. PC에서 프록시를 실행한다 (기본 포트 8888)
 
3. 폰의 Wi-Fi 고급 설정 > 프록시 > 수동
   호스트: PC의 IP,  포트: 8888
 
4. HTTPS 검사가 필요하면 루트 인증서를 설치한다
   (브라우저로 프록시 인증서 배포 주소 접속 → 설치 → 신뢰 설정)
 
5. Throttle 설정을 켜고 테스트를 시작한다

4단계가 중요하다. 인증서를 설치하지 않으면 HTTPS 트래픽은 내용이 보이지 않고, 많은 도구에서 제어도 제한된다. 그리고 이 인증서는 테스트가 끝나면 반드시 제거해야 한다 — 남겨두면 그 기기의 모든 HTTPS 통신을 누군가 가로챌 수 있는 상태가 된다.

도구 비교

도구비용특징
Charles Proxy유료 (평가판 제공)Throttle Settings에 3G·56kbps 등 프리셋 내장. 국내 QA 사용 사례가 많아 정보를 구하기 쉽다
Proxyman무료 · 유료macOS·Windows 지원. 최신 UI, 요청별 지연과 응답 조작이 편하다
mitmproxy무료명령행 기반. 스크립트로 지연·오류 주입을 자동화 할 수 있어 회귀에 붙이기 좋다
Fiddler Classic무료Windows 환경. 모뎀 속도 흉내와 사용자 규칙 지원

선택 기준은 단순하다. GUI로 손으로 조작할 것인가, 스크립트로 반복할 것인가. 탐색적으로 이것저것 해볼 때는 Charles·Proxyman이 빠르고, 회귀에 넣어 매번 같은 조건을 만들 거라면 mitmproxy가 맞다.

이 방법의 진짜 가치 — 호스트별 제어와 오류 주입

대역폭·지연을 거는 것만으로는 레슨 5(tc netem)를 이길 수 없다. 프록시의 고유한 가치는 다른 데 있다.

① 호스트·경로별 조건 차등

api.example.com/payments/*    → 지연 5,000 ms
cdn.example.com/*             → 대역폭 50 kbps
그 외                          → 정상

이렇게 걸어두면 "결제만 느릴 때"와 "이미지만 느릴 때"의 화면이 각각 어떻게 되는지 분리해서 볼 수 있다. 결함 원인을 좁힐 때 결정적이다.

② 응답 조작 — 저하 테스트를 넘어선다

조작확인하는 것
상태 코드 변경 (200 → 500, 503, 429)서버 오류 시 화면과 재시도 동작
응답 지연 후 정상 반환타임아웃 직전 도착 시 처리
응답 본문 잘라내기파싱 실패 시 앱이 죽는가
빈 본문 / 잘못된 JSON방어 코드가 있는가
연결 중 끊기 (reset)네트워크 예외 경로
Retry-After 헤더 부착클라이언트가 이 헤더를 존중하는가

429 + Retry-After 조합은 특히 자주 빠진다. 서버가 "3초 뒤에 다시 오라"고 알려줬는데 클라이언트가 무시하고 즉시 재시도하면, 장애 상황에서 서버를 더 밀어붙이게 된다. 레슨 7에서 자세히 다룬다.

③ 조건부 실패 — 특정 횟수만 실패시키기

mitmproxy 스크립트로 "이 API는 처음 두 번 실패하고 세 번째에 성공" 같은 조건을 만들 수 있다. 재시도 로직이 실제로 동작하는지 확인하는 가장 정확한 방법이다.

# mitmproxy 스크립트 골격 — 처음 2회는 503, 이후 정상
count = 0
 
def request(flow):
    global count
    if "/api/orders" in flow.request.pretty_url:
        count += 1
        if count <= 2:
            flow.response = http.Response.make(
                503, b'{"error":"unavailable"}',
                {"Content-Type": "application/json",
                 "Retry-After": "2"}
            )

한계 ① — 인증서 피닝

앱이 인증서 피닝(certificate pinning) 을 적용했다면, 프록시의 루트 인증서를 신뢰하지 않는다. 앱은 연결을 거부하고, 프록시에는 아무것도 잡히지 않는다.

증상: 브라우저는 프록시를 통과하는데 앱만 통신이 안 된다
      → 피닝을 의심한다

대응은 셋이다.

  1. 디버그 빌드에서 피닝을 끈다 — 가장 정상적인 방법. 개발과 협의해 디버그 빌드에 피닝 예외를 넣는다
  2. 피닝 우회 도구를 쓴다 — 루팅·탈옥이 필요하고 사내 정책과 충돌할 수 있다
  3. 프록시를 포기하고 레슨 5로 간다 — tc netem은 TLS 계층 아래에서 동작하므로 피닝과 무관하다

1번을 요청할 수 있는 관계를 만들어두는 것 이 QA 입장에서 가장 값싸다.

한계 ② — QUIC / HTTP/3

이게 요즘 더 큰 문제다. 많은 서비스가 HTTP/3(QUIC) 를 쓰는데, QUIC은 TCP가 아니라 UDP 위에서 동작 한다. HTTP 프록시는 이 트래픽을 잡지 못한다.

증상: 일부 요청만 프록시에 잡히고 나머지는 안 잡힌다
      → HTTP/3로 붙고 있을 가능성이 높다

확인과 대응:

  • 프록시 세션 목록에 예상한 요청이 전부 있는지 센다. 빠진 게 있으면 의심
  • 테스트 중에는 서버·클라이언트에서 HTTP/3를 비활성화하고 HTTP/2로 내려 붙이게 하는 방법이 있다(개발과 협의 필요)
  • 또는 레슨 5의 tc netem으로 간다. UDP도 똑같이 제어된다

QUIC이 저하 테스트 결과를 바꾼다

단순히 "안 잡힌다"가 아니라, 결과 해석 자체가 달라진다는 점 이 중요하다.

TCP 기반 HTTP/2에서는 하나의 패킷이 손실되면 그 연결의 모든 스트림이 함께 멈춘다 (전송 계층 head-of-line blocking). 반면 HTTP/3에서는 손실된 스트림만 멈추고 나머지는 계속 흐른다.

실측에서도 이 차이가 크게 나타난다. 패킷 손실 3% 조건에서 HTTP/3의 p99 페이지 완료 시간이 1.1초, HTTP/2가 2.4초 로 보고된 사례가 있다. 꼬리 지연에서 절반 수준이다.

QA에게 이게 주는 결론은 둘이다.

  1. 손실 조건 테스트 결과를 프로토콜과 함께 기록한다. "손실 5%에서 3초"는 HTTP/2인지 HTTP/3인지에 따라 전혀 다른 의미다
  2. 프로토콜이 바뀌는 변경(HTTP/3 도입 등)은 네트워크 저하 회귀를 다시 돌려야 하는 변경이다. 기능은 그대로인데 저하 특성이 달라진다

한계 ③ — 소켓·비 HTTP 트래픽

WebSocket 초기 핸드셰이크는 HTTP라 잡히지만, 이후의 프레임은 도구에 따라 제어가 제한적이다. 게임·실시간 통신의 커스텀 UDP 프로토콜은 아예 대상이 아니다.

실시간 기능이 핵심인 제품이라면 프록시는 부분 해법 이라고 보는 편이 정확하다.

실무에서 왜 필요한가

원인 격리가 된다

"주문 화면이 느리다"는 제보를 받았을 때, 전체를 느리게 하면 "느리네요"로 끝난다. 호스트별로 차등을 주면 어느 호출이 병목인지 가 드러난다.

실험 1: 결제 API만 지연 → 화면 멈춤 재현 ✅
실험 2: 이미지 CDN만 지연 → 재현 안 됨
→ 원인은 결제 API 대기. 개발에 넘길 때 근거가 명확하다

서버 없이 오류 경로를 테스트한다

500·503·429 응답에서 화면이 어떻게 되는지 보려면 원래는 서버를 죽이거나 개발 환경을 만져야 한다. 프록시로는 클릭 몇 번이다. QA가 개발에 의존하지 않고 독립적으로 검증할 수 있는 범위가 넓어진다.

이건 모듈 12(API·계약 테스트)의 사고방식과 이어진다. 계약을 위반하는 응답을 만들어 클라이언트의 방어를 확인하는 것이다.

자동화에 넣을 수 있다

mitmproxy 스크립트는 CI에 붙일 수 있다. "재시도 3회 후 성공" 같은 시나리오를 매 빌드마다 자동으로 확인하게 만들 수 있다. 레슨 8에서 자동화 전체 그림을 다룬다.

실습 과제

과제 1 — 프록시 구성하고 세션 확인

프록시 도구 하나를 골라 폰과 연결하고, 앱을 켜서 요청이 실제로 잡히는지 확인한다.

  • 잡힌 요청 개수를 센다
  • HTTPS 본문이 보이는가 (안 보이면 인증서 설치)
  • 예상했는데 안 잡힌 요청이 있는가 → 피닝 또는 HTTP/3 의심

마지막 항목이 이 과제의 진짜 목적이다.

과제 2 — 호스트별 차등 조건 걸기

다음 세 조건을 각각 걸고 화면 동작을 비교한다.

조건대상설정관찰
A전체지연 3,000 ms
B이미지 CDN만대역폭 50 kbps
C핵심 API 하나만지연 5,000 ms

세 조건에서 화면이 각각 다르게 깨진다면 제품이 그 차이를 구분해 처리하고 있다는 뜻이다. 전부 똑같이 흰 화면이 된다면 개선 여지가 있다.

과제 3 — 오류 주입 6종 실행

본문 표의 6가지 조작을 우리 앱의 핵심 API 하나에 차례로 적용하고, 각각의 화면과 로그를 기록한다.

  • 200 → 500
  • 200 → 503 + Retry-After: 3
  • 200 → 429
  • 응답 본문 잘라내기
  • 빈 본문
  • 연결 중 끊기

앱이 죽는(크래시) 조건이 하나라도 있으면 그건 즉시 결함이다.

과제 4 — 재시도 로직 검증

mitmproxy 스크립트(본문 골격 참고)로 "처음 2회 실패, 3회째 성공"을 만들고 다음을 확인한다.

  • 클라이언트가 실제로 재시도하는가
  • 몇 번 재시도하는가
  • 재시도 간격이 일정한가, 늘어나는가 (지수 백오프인가)
  • Retry-After 헤더를 존중하는가
  • 재시도 중 화면은 무엇을 보여주는가

이 결과가 레슨 7의 입력이 된다.

과제 5 — HTTP/3 사용 여부 확인

우리 서비스가 HTTP/3를 쓰는지 확인한다.

curl -sI --http3 https://<우리 도메인>/ | head -1

또는 브라우저 DevTools 네트워크 패널에서 Protocol 컬럼을 켜고 h3 가 있는지 본다(모듈 9에서 배운 DevTools 사용).

HTTP/3를 쓴다면 손실 조건 테스트 결과에 프로토콜을 함께 기록하는 규칙 을 팀에 제안한다.

과제 6 — 인증서 원복 확인

테스트가 끝난 뒤 폰에서 다음을 모두 수행하고 확인한다.

  • 수동 프록시 설정 삭제
  • 루트 인증서 삭제 (설정 > 보안 > 인증서에서 실제로 사라졌는지 확인)
  • 정상 통신 회복 확인

인증서가 남아 있는 기기는 보안 사고 대기 상태다.

자가 체크리스트

  • 프록시가 왜 핫스팟과 달리 계층 문제를 겪지 않는지 설명할 수 있다
  • 프록시 구성 5단계를 순서대로 수행할 수 있다
  • 루트 인증서를 설치해야 하는 이유와, 제거해야 하는 이유를 안다
  • 네 가지 도구의 성격 차이(GUI vs 스크립트)를 구분할 수 있다
  • 호스트·경로별로 조건을 차등하는 것이 왜 원인 격리에 유용한지 설명할 수 있다
  • 응답 조작 6종을 나열하고 각각이 무엇을 확인하는지 안다
  • Retry-After 헤더를 클라이언트가 존중하는지 확인하는 법을 안다
  • "처음 N회 실패" 시나리오로 재시도 로직을 검증할 수 있다
  • 인증서 피닝의 증상과 세 가지 대응 방법을 안다
  • QUIC/HTTP3 트래픽이 HTTP 프록시에 안 잡히는 이유를 설명할 수 있다
  • HTTP/2와 HTTP/3가 패킷 손실에서 다르게 동작하는 이유를 설명할 수 있다
  • 손실 조건 결과에 프로토콜을 함께 기록해야 하는 이유를 안다

흔한 실수

안 잡히는 요청을 세지 않는다. 프록시에 요청이 주르륵 잡히면 잘 되는 줄 안다. 정작 중요한 결제 요청이 피닝이나 HTTP/3 때문에 빠져 있어도 모른다. 예상 요청 개수와 실제 개수를 비교하는 습관 이 필요하다.

전체 스로틀만 쓰고 호스트별 차등을 안 쓴다. 프록시의 가장 큰 장점을 안 쓰는 것이다. 전체를 느리게 할 거면 레슨 3이나 레슨 5가 더 정확하다.

인증서를 안 지우고 끝낸다. 테스트 기기든 개인 기기든, 루트 인증서가 남아 있으면 그 기기의 HTTPS 통신은 신뢰할 수 없는 상태다.

응답 조작을 저하 테스트와 별개로 취급한다. 실제 저하 상황에서는 타임아웃·502·503이 함께 온다. 지연과 오류를 조합해서 테스트해야 실제와 가깝다.

피닝 때문에 안 된다고 결론 내고 멈춘다. 디버그 빌드에 예외를 넣어 달라고 요청하는 것이 가장 정상적인 해법이다. 요청해본 적이 없어서 못 하는 경우가 많다.

HTTP/3 도입을 네트워크 회귀 대상으로 보지 않는다. 기능 변경이 아니니 통과로 넘긴다. 손실·지연 특성이 바뀌므로 저하 회귀를 다시 돌려야 한다.

참고 자료

  • Charles Proxy 문서 — Throttle Settings의 프리셋 값과 사용자 정의. 국내 QA 사용 사례가 많다
  • Proxyman 문서 — Map Local / Breakpoint / 요청별 지연 설정
  • mitmproxy 문서 — 애드온 스크립트 API. request / response 훅으로 조건부 실패를 구현한다
  • Why HTTP/3 Improves Mobile App Performance on Flaky Networks — 손실 구간에서 HTTP/2와 HTTP/3의 꼬리 지연 차이 실측
  • HTTP/3 and QUIC in Production — A Practical Deployment Guide — head-of-line blocking 제거가 실제로 무엇을 바꾸는지
  • 모듈 12 API·계약 테스트 — 계약을 위반하는 응답으로 클라이언트 방어를 확인하는 사고방식
  • 모듈 9 QA를 위한 개발 지식 — 트래픽 가로채기와 DevTools 네트워크 패널
  • 이 모듈 레슨 7 저하 상태 체크포인트 — 여기서 만든 오류 주입 결과를 해석하는 기준

낯선 말 풀이

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

HTTPS — 암호화된 웹 전송
웹 통신을 암호로 감싼 것. 중간에서 누가 들여다봐도 내용을 못 읽는다.
QA — 품질보증
만든 것이 쓸 만한지 확인하고, 애초에 덜 망가지게 만드는 방법을 정하는 일. "테스트"는 그 일의 일부다.
UI — 사용자 인터페이스
사람이 직접 보고 누르는 부분 — 화면, 버튼, 입력창.
GUI — 그래픽 사용자 인터페이스
글자만 있는 화면이 아니라 그림·버튼·창으로 조작하는 화면. 보통 말하는 "화면"이 이것이다.
JSON — 제이슨
프로그램끼리 값을 주고받을 때 쓰는 글자 형식. 괄호와 따옴표로 이름과 값을 짝지어 적는다.
API — 응용 프로그램 인터페이스
프로그램끼리 주고받는 창구. 사람이 화면 버튼을 누르는 대신, 프로그램이 정해진 주소로 요청을 보내 답을 받는다.
TLS — 전송 계층 보안
통신을 암호로 감싸는 기술 그 자체. 웹에서 쓰면 주소가 https로 시작한다.
TCP — 전송 제어 규약
데이터를 빠뜨리지 않고 순서대로 보내는 약속. 웹도 이 위에서 돌아간다.
HTTP — 하이퍼텍스트 전송 규약
웹에서 요청과 응답을 주고받는 약속. "무엇을 달라"고 보내면 "여기 있다" 또는 "그런 건 없다"가 돌아온다.
p99 — 99 백분위
100번 중 99번이 이 시간 안에 끝났다는 뜻. 가장 느린 1%만 뺀 값이다.
CI — 지속적 통합
코드를 올릴 때마다 자동으로 합쳐서 빌드하고 시험까지 돌리는 방식.
CDN — 콘텐츠 전송 망
이미지나 파일을 세계 여러 곳에 미리 복사해 두고 가까운 곳에서 내려 주는 구조.

용어집 전체 보기