https://developer.apple.com/videos/play/wwdc2021/10212
Analyze HTTP traffic in Instruments - WWDC21 - Videos - Apple Developer
Learn to use the Instruments Network template to record and analyze your app's HTTP traffic. We'll show you how to explore and visualize...
developer.apple.com

오늘 글에서는 Instruments에서의 HTTP Traffic Instrument에 관하여 다룹니다.

- 모든 Apple 기기에서 동작함
- 새로운 HTTP/3 프로토콜이나 VPN을 통한 트래픽을 확인할 수 있음
- 트래픽이 실행 중인 프로세스에 속하는지, 디스크 캐시에 저장된 요청이나 네트워크 오류 확인 가능
- URLSession, URLSessionTaskd와 같은 고수준 API로 표시되어, API가 어떻게 사용되고 네트워크 요청의 수명 주기에 어떻게 반영되는지 이해 가능
HTTP instrument UI overview

Network로 시스템 트래픽을 기록하면, 위와 같이 HTTP Traffic trace의 hierarchy를 알 수 있습니다.
구체적인 hierarchy는 다음과 같습니다.

- 최상위 수준에서는, HTTP Traffic Instrument가 실행된 모든 URLSession task의 개요를 볼 수 있습니다.
- 앱의 실행 수명 동안 HTTP 트래픽 활동이 급증한 지점을 감지하는 것에 유용합니다.

- 트래픽이 프로세스별로 나뉘어 표시됩니다.
- 디버깅 가능한 모든 프로세스에서 발생한 트래픽을 보여줄 뿐만 아니라, 백그라운드에서 실행된 트래픽도 검사할 수 있습니다.

- 각 프로세스 아래에는 해당 프로세스가 사용한 모든 URLSession이 포함되어 있습니다.
- 그래프를 통해 개별 task의 실행 간격을 확인할 수 있습니다.
- 코드에서 sessionDescription 속성을 설정하면 세션 객체를 시각적으로 더 잘 볼 수 있습니다.

- 마지막 계층에서는 트래픽이 요청한 도메인별로 분류됩니다.
- task를 구성하는 개별 트랜잭션 및 그 상태에 대한 더 많은 세부 정보를 확인할 수 있습니다.

예시로 살펴보면, 몇 개의 task가 특정 도메인에서 데이터를 로드하고 있는 것을 알 수 있습니다. 이 중 하나를 집중적으로 살펴보며 task의 구조를 분석하겠습니다.

하나의 task 간격(interval)에는 많은 정보가 포함되어 있습니다.
이를 좀 더 추상화해 살펴보면, 아래와 같습니다.

- Task 수준
- Task 객체가 최상위 개념입니다.
- 하나의 Task는 하나 이상의 Transaction으로 구성됩니다.
- Transaction
- 트랜잭션은 HTTP request와 response의 쌍으로 이루어집니다.

Task 수준에서는 코드가 API와 어떻게 상호작용하는지를 나타냅니다.
다시 말해, task를 만들고 resume()를 호출하면 task의 간격이 시작되고, completion block이 호출되기 직전에 이 간격이 종료되는 방식입니다. 각 Task는 taskDescription으로 이름을 지정할 수도 있고, 이 이름을 Instruments에서 라벨로 볼 수 있습니다.
또한 task identifier도 함께 표시되어, 다른 데이터와 교차 참조가 가능합니다.

만약 task가 오류로 종료되면, 해당 오류가 라벨에 표시되므로 디버깅이 더 쉬워집니다.
Task는 여러 개의 트랜잭션으로 구성될 수도 있습니다.

예를 들어, 위와 같이 apple.com의 시작 페이지를 로드하는 task를 실행했다고 가정합니다. 해당 URL은 제대로 된 URL이 아니기 때문에, 서버에서 올바른 URL로 리디렉션하라는 응답을 받게 됩니다.

URLSession은 요청을 보낼 때 자동으로 리디렉션을 처리합니다. 따라서 301 응답을 반환하는 대신, 자동으로 새로운 요청을 만듭니다. 그리고 이 두번째 트랜잭션에서 받은 응답이 최종적인 Task로 반환됩니다.
이렇게 트랜잭션은 HTTP 요청 및 응답 조합을 나타내고, 요청된 URL, 전송된 데이터 정보나 기타 HTTP 계층 정보를 갖고 있습니다.
Transaction Label

트랜잭션 라벨에서는 다음과 같은 정보를 확인할 수 있습니다.
- Request
- HTTP Method
- HTTP Version
- 요청에 Authorization 헤더 또는 Cookie 헤더가 포함되었는지 여부
- Response
- HTTP Status Code
- 응답에 쿠키가 포함되었는지 여부
- 응답의 Content-Type
트랜잭션의 시작부터 완료까지 걸린 시간뿐만 아니라, 세부적인 시간 정보도 캡처됩니다.
Transaction states

트랜잭션을 좀 더 구체적으로 분석하면 아래와 같습니다.
- 트랜잭션 Start
- URL 로딩 시스템이 요청을 만들고, 먼저 캐시된 응답이 있는지 확인합니다.
- 만약 유효한 캐시 응답이 없다면, 요청을 네트워크에 전송할 준비를 합니다.
- Blocked(대기 상태)
- 트랜잭션이 사용 가능한 Connection을 확보될 때까지 대기합니다.
- Sending Request(요청 전송)
- 트랜잭션이 실제로 네트워크에 전송됩니다.
- 요청의 마지막 바이트가 전송될 때까지 이 상태가 유지됩니다.
- Waiting for Response(응답 대기 상태)
- 요청이 전송된 후, 서버로부터 응답이 올 때까지 대기합니다.
- Receiving Response(응답 수신)
- 서버의 응답을 첫 번째 바이트부터 마지막 바이트까지 수신하는 상태입니다.
- 트랜잭션 완료(Transaction Completion)
- 마지막 바이트를 받은 후, URL 로딩 시스템은 이 응답이 성공적인지 확인하고, 필요에 따라 추가 처리를 수행합니다.

GET 요청의 경우, 캐시 조회와 요청을 전송하는 과정이 짧기 때문에 위와 같이 간결하게 표시됩니다.
Solving performance and correctness issues

예시로 소개된 앱은 강아지 사진들이 피드 형태로 제공되는 플랫폼입니다.
처음에는 이미지 로딩에 많은 시간이 걸린다는 문제가 있었는데요.

문제 개선을 위해서 HTTP Traffic Instrument를 사용할 수 있었습니다. 해당 작업으로 Release 빌드를 하면, 최적화된 상태에서 실행되어 실제 사용자 환경에서의 성능을 정확하게 측정할 수 있게 됩니다.

- 기존 "Network Connections" Instrument (하단)
- 새로운 "HTTP Traffic" Instrument (상단)

이 때, 네트워크 트래픽을 캡처하는 것은 개인정보나, 사용자 인증 정보까지 포함될 수도 있기 때문에 trace file을 신중히 관리하라는 경고 팝업이 뜹니다.

(위와 같이 네트워크 트래픽을 캡처했습니다.)

Option 키를 누른 상태에서 특정 트래픽 구간을 드래그하면 확대해서 볼 수 있습니다.

- 첫 번째 task
- 서버에서 이미지 목록을 가져오는 요청
- 이는 앱의 최신 강아지 사진 섹션을 채우는 역할을 합니다.

- 두 번째 task
- 첫 번째 요청이 완료되면, 서버에서 받은 각각의 이미지에 대해 썸네일을 개별적으로 요청합니다.

위 사진은 썸네일 요청이 걸린 시간을 확인하기 위해, 특정 영역을 드래그한 모습으로 7초 이상 걸린 것을 확인할 수 있습니다.

이를 확인하면, 초반 몇 개의 이미지는 빠르게 로드되었지만, 스크롤을 내릴수록 로딩 시간이 길어지는데요.
이러한 현상은 네트워크 병목 현상(network congestion)이 원인일 가능성이 큽니다. 한 번에 너무 많은 요청을 보내 요청이 Block State에 오래 머무르는 것이지요.
HTTP Transactions by Connection

각 요청이 왜 지연되는지 확인하기 위해 HTTP Transactions by Connection으로 전환하는 모습입니다.
트랜잭션들이 각각 어떤 Connection에서 실행되었는지 확인할 수 있습니다.

- 총 6개의 Connection이 사용되었습니다.
- Connection 1의 트랜잭션들을 분석해 보니, 점점 로딩 시간이 길어지는 계단 패턴이 보였습니다.
- 각 트랜잭션이 이전 트랜잭션이 끝날 때까지 Blocked된 상태로 남아 있었습니다.

이 문제의 원인은 Head of Line Blocking이라는 현상 때문입니다.
HTTP/1.1의 한계와 HTTP/2 도입
Head of Line Blocking은 HTTP/1.1이 한 개의 연결에서 한 번에 하나의 요청만 처리할 수 있기 때문에 발생하는 문제입니다.

이 문제는 HTTP/2부터 해결되었는데요.

그 결과, 어떤 task도 오랜 시간 Blocked 상태로 머무르지 않고, 이전에는 6개의 연결이 사용된 것에 대비, 하나의 연결만 사용되는 것을 알 수 있습니다. 이에 따라 트랜잭션들이 대기하는 시간이 거의 없고, 모든 요청 또한 3초 이내에 완료되는 모습입니다. (2배 이상 향상)
그 다음으로 해결하는 문제는 로그인이 유지되지 않는 이슈인데요.

- 첫 번째 요청
- 사용자가 하트 버튼을 처음 눌렀을 때 서버로 보낸 요청
- 401 Unauthorized 오류 응답을 받음
- 로그인 후 재시도 요청
- 사용자가 로그인 정보를 입력한 후, 다시 같은 요청을 보냄
- 201 응답을 받음
- 두 번째 즐겨찾기 요청
- 사용자가 다른 강아지 사진을 즐겨찾기하려고 시도
- 다시 401 Unauthorized 응답을 받음
- 그리고 다시 로그인 화면이 나타남 (이전 로그인 내용이 저장되지 않음)

첫 번째 요청(하트 버튼을 처음 눌렀을 때)을 자세히 들여다보겠습니다.
- 요청이 401 오류로 실패한 후, 사용자가 로그인하면 두 번째 트랜잭션이 동일한 요청을 다시 시도합니다.
- 이 두 번째 요청에서는 201 응답을 받으며 성공적으로 즐겨찾기가 추가됨을 확인할 수 있습니다.

하지만, 두 번째 즐겨찾기 요청이 실패한 이유는 무엇일까요?
요청을 자세히 살펴보면, 쿠키 아이콘이 표시되지 않는 것을 볼 수 있는데, 이는 서버로 보낸 요청에 쿠키가 포함되지 않았다는 의미입니다. 이전 로그인 후 서버에서 인증 쿠키를 제공했는데, 클라이언트가 이를 저장하지 못했음을 의미합니다.
그래서 서버가 쿠키를 제대로 설정했는지 확인하기 위해 찾아보던 도중, Set-Cookie 헤더가 응답에 포함한 부분을 찾았는데요.
서버는 쿠키를 제대로 제공했는데 왜 클라이언트가 이를 저장하지 못했을까요?

이는 Set-Cookie 헤더의 만료 날짜를 보면 알 수 있습니다. 날짜가 이미 만료되어 있기에, 클라이언트가 쿠키를 무효화하고 전송하지 않았던 것입니다. 이는 클라이언트 코드가 아니라 서버 쪽의 버그임을 알 수 있습니다.
세 번재 문제는, 즐겨찾기 탭으로 이동했을 때, 방금 추가한 강아지 사진이 목록에 나타나지 않는 점입니다.

해당 요청을 살펴보니 아주 짧은 시간 안에 요청이 끝났고, Local Cache라고 표시된 것을 확인할 수 있습니다.
이 요청이 네트워크를 통해 서버에서 데이터를 받아온 것이 아니라, 로컬 캐시에서 불러왔다는 뜻입니다.
이 문제를 해결하기 위해, HTTP 캐시 정책을 조정해야 합니다.

- 서버 측에서 Cache-Control: no-store 헤더를 설정하여 즐겨찾기 목록이 캐싱되지 않도록 변경
- 클라이언트에서 reloadRevalidatingCacheData 캐시 정책을 설정하여 서버에 변경 사항을 확인 후, 필요하면 새로운 데이터를 요청하도록 변경
Auditing your application behavior
마지막으로, 로그인 SDK를 통합했다고 가정해봅시다.

(위와 같이 SDK를 드래그 앤 드랍해서 추가함)


여기에서 이상한 점을 발견했는데요.
일반적으로 로그인 버튼을 눌러야만 네트워크 요청이 발생하는데, 아무런 조작을 하지 않았는데도 Pets SDK에서 이미 여러 개의 네트워크 요청을 보내고 있는 것입니다. 어떻게 된 것일까요?

해당 트래픽을 자세히 분석해봅시다.
- 트랙 뷰에서 Pets SDK의 URLSession을 확인해 보니, 로그인을 시도하기도 전에 SDK가 네트워크 요청을 보낸 것을 알 수 있었습니다.
- 트래픽을 확대하여 개별 요청들을 분석해 보니,
- 여러 개의 POST 요청이 analytics 엔드포인트로 전송됨
- backtrace를 확인해 보니, CoreLocation이 관여하고 있음

더 자세히 살펴보니, Request Body에 GPS 좌표가 포함되어 있네요. 즉, 해당 SDK가 사용자 위치 정보를 서버로 전송하고 있는 것입니다. 이는 명백한 개인정보 보호 위반이기에, SDK 통합을 중단하고 문제를 보고하기로 결정합니다.
우선 해당 캡처 내용을 파일로 추출합니다.

그리고 트레이스 데이터를 HAR(HTTP Archive) 형식으로 변환합니다. HAR 파일은 JSON 기반의 표준 형식이기 때문에, 다른 팀에서도 쉽게 분석이 가능합니다.


이처럼 HTTP Traffic Instrument로 앱이 발생하는 네트워크 트래픽을 분석하고, 예상치 못한 네트워크 요청이나 성능, 보안 문제 등을 감지할 수 있습니다.
'WWDC' 카테고리의 다른 글
[WWDC] Meet async/await in Swift (0) | 2025.03.14 |
---|---|
[WWDC] Protect mutable state with Swift actors (0) | 2025.03.12 |
[WWDC] iOS Memory Deep Dive (1) | 2025.03.05 |
[WWDC] Design protocol interfaces in Swift (0) | 2025.02.14 |
[WWDC] Embrace Swift generics (1) | 2025.02.08 |