새로워진 MetricKit: iOS 27의 상태 인식 텔레메트리
Apple의 WWDC 2026 데모 앱은 하루 사용 전체를 평균 낸 결과 초당 15밀리초의 스크롤 히치율을 보고했습니다. 그런데 같은 데이터를 탭별로 나누자 완전히 다른 이야기가 드러났습니다. 한쪽 탭은 1 ms/s, 다른 쪽 탭은 71 ms/s였던 것입니다.1 한 화면은 거의 완벽했지만, 다른 화면은 세션의 표현을 빌리자면 “심각한 중단을 겪고” 있었습니다.1 평균값은 이 두 가지 사실을 모두 가려 버렸습니다. 세션 222 “Meet the new MetricKit”은 iOS 27이 이 간극을 어떻게 메우는지에 관한 이야기입니다. 프레임워크의 API 전반을 처음부터 다시 만들었고, 여기에 새로운 동반 프레임워크인 StateReporting을 더해 앱 전체의 필드 메트릭을 상태별 메트릭으로 바꿔 줍니다. 이제 필드 텔레메트리는 모든 성능 엔지니어가 가장 먼저 던지는 질문에 답할 수 있게 되었습니다. 바로 “어느 화면이 느린가?”라는 질문입니다.
TL;DR
- iOS 27에서 MetricKit은 “맥락이 풍부하고 표현력 있는 현대적인 Swift 우선 API로 처음부터 다시 만들어졌으며”, 세션에서 소개하는 새로운 기능은 모두 이 새 API에서만 사용할 수 있습니다.1
- 진입점은
MetricManager클래스입니다. 앱은 실행 시점에metricReports와diagnosticReports비동기 스트림을 await하며, 두 보고서 타입 모두Codable이므로JSONEncoder로 곧바로 분석 서버에 전송할 수 있습니다.1 - 보고서는 구조화되어 있습니다.
intervalEntries는 하루 전체 항목과 더 작은 세부 항목을 함께 담으며,.cpu,.memory,.display,.gpu같은 메트릭 그룹으로 정리되고, 나아가peakMemory같은 개별 값까지 내려갑니다.1 - iOS 27의 새로운 데이터로는 렌더링 성능을 위한 Metal 프레임률 메트릭, 메모리 한도 초과로 인한 종료를 위한 메모리 예외 진단, 그리고 개별 크래시 진단을 메트릭 추세와 이어 주는 크래시
category가 추가되었습니다.1 - 핵심 기능은
StateReporting프레임워크입니다. 앱이 처한 상태(활성 탭, 실험 분기, 뷰 구성)를 보고하면 MetricKit이 상태별로 메트릭을 집계하여, 하나의 평균값을 화면별 분석으로 대체해 줍니다.1
처음부터 다시 만들다
MetricKit 팀의 엔지니어 Yonni가 1:23부터 iOS 27의 재구축을 소개합니다.
MetricKit의 역할은 바뀌지 않았습니다. 성능 워크플로에서 “수집을 담당하는 부분”이며 두 종류의 데이터를 제공합니다. 메트릭은 어떤 성능 영역이 전반적으로 나아지는지 나빠지는지를 알려 주고, 진단은 어느 코드 경로가 문제를 일으켰는지를 알려 줍니다.1 바뀐 것은 그 데이터를 받는 방식 전부입니다. 세션은 분명하게 말합니다. iOS 27에서 프레임워크는 “맥락이 풍부하고 표현력 있는 현대적인 Swift 우선 API로 처음부터 다시 만들어졌으며”, “오늘 제가 다룰 모든 발전은 이 새로운 API 세트에서만 사용할 수 있습니다”.1
새로운 진입점은 MetricManager 클래스입니다. 델리게이트를 등록하고 페이로드를 파싱하는 대신, metricReports 속성을 비동기 스트림으로 await하여 보고서를 받습니다. 운영 규칙 두 가지가 세션에서 그대로 나옵니다. “지연된 구독으로 인한 데이터 손실을 피하기 위해” 설정은 앱 시작 시점에 수행하고, “이후 데이터가 준비되는 대로 스트림이 계속 보고서를 전달할 수 있도록” MetricManager를 계속 살아 있게 유지하라는 것입니다.1 Apple은 앱이 실행되자마자 이 작업을 detached 태스크나 전용 서비스 클래스에서 실행할 것을 권장합니다.1
세션에서는 코드가 슬라이드로 제시되므로, 아래 스니펫은 세션 설명에 맞춘 예시용 호출 형태입니다. 출시 전에 Apple 공식 문서에서 정확한 시그니처를 확인하세요.
// Illustrative call shape based on session 222; verify against the docs.
let manager = MetricManager()
Task.detached {
for await report in manager.metricReports {
// Encode and ship, or inspect specific groups.
}
}
예전에는 보고서를 서버로 보내려면 불투명한 페이로드 데이터를 다뤄야 했습니다. 이제 MetricReport 값은 Codable입니다. “JSONEncoder를 하나 만들어 보고서 전체를 인코딩하기만 하면 됩니다”.1 문서 전체가 아니라 특정 값이 필요하다면, 보고서는 완전히 구조화되어 있습니다. intervalEntries를 순회하면 거기에는 “하루 전체 집계 항목과, 가능한 경우 더 작은 세부 윈도우”가 포함됩니다. 보통 각각 몇 시간 단위이며, 해당 메트릭이 존재할 때만 나타납니다.1 각 인터벌 안에서 메트릭은 메트릭 그룹으로 정리되며, “각 그룹은 .cpu, .memory, .display, .gpu처럼 시스템의 한 측면을 나타냅니다”.1 관심 있는 그룹까지 좁혀 들어간 뒤(세션의 예시는 memoryMetrics를 가져옵니다), 메트릭 케이스를 switch하여 peakMemory 같은 개별 값에 도달합니다.1
메트릭 카탈로그도 iOS 27에서 늘어납니다. 실행 시간 히스토그램(세션의 예시에서는 대부분의 실행이 510~540밀리초 사이에 들어옵니다), 행, 애니메이션 메트릭, 그리고 CPU·GPU·디스크 쓰기·네트워크 전송 같은 리소스 소비에 더해, MetricKit에 Metal 프레임률 메트릭이 추가되었습니다. 세션은 프레임률을 “게임 개발자가 렌더링 성능을 이해하기 위한 핵심 메트릭”이라 부르며, 최적화 측면에 대해서는 “Find and fix performance issues in your Metal game”을 참고하도록 안내합니다.1
진단: 백트레이스, 메모리 예외, 그리고 크래시 카테고리
메트릭은 무언가가 퇴보했음을 알려 주고, 진단은 그것이 어디에서 일어났는지를 알려 줍니다. 크래시나 행처럼 무언가 잘못되면 “시스템이 기기에서 진단을 캡처”하고, 진단 보고서가 “그 세부 사항을 묶어 MetricKit을 통해 즉시 앱으로 전달”합니다.1 많은 진단에는 이벤트 발생 시점의 정확한 호출 스택을 보여 주는 백트레이스가 포함됩니다. 세션의 설명에서 심볼화된 백트레이스는 시스템 코드의 스레드 시작 지점에서 출발해 앱으로 넘어가고, 앱의 submitReport() 함수에서 멈춥니다. 이는 실패 지점이자 수정을 겨냥해야 할 위치를 표시합니다.1
크래시 진단은 백트레이스, 종료 사유, 그리고 예외 타입을 담고 있습니다. iOS 27에서 새로 추가된 종료 category는 “각 크래시가 메트릭에서 어떻게 집계되었는지를 나타내므로”, “비정상 종료가 증가 추세라면 그것을 개별 진단과 직접 연관 지을 수 있습니다”.1 대시보드의 메트릭 선과 그 뒤의 개별 크래시 보고서가 마침내 공통의 키를 공유하게 된 것입니다.
iOS 27은 메모리 예외 진단도 추가합니다. “앱이나 익스텐션이 메모리 한도를 초과해 종료될 때, 무슨 일이 있었는지에 대한 더 깊은 통찰을 얻게 됩니다”.1 익스텐션도 명시적으로 범위에 포함되며, 이는 위젯이나 익스텐션의 메모리 종료를 원격으로 디버깅하는 모든 이에게 중요합니다.
수집 방식은 메트릭 쪽을 그대로 따릅니다. MetricManager 인스턴스에서 diagnosticReports를 await하며, 이 역시 앱 실행 시점에 detached 태스크나 서비스 클래스에서 수행합니다. DiagnosticReport 값도 Codable이므로 동일한 인코딩-전송 파이프라인에 태울 수 있습니다.1 보고서가 구조화되어 있으므로 진단 케이스를 switch할 수 있습니다. 크래시 케이스는 백트레이스, 사유, category를 내놓고, 행 케이스는 다른 처리로 라우팅할 수 있습니다.1
// Illustrative call shape based on session 222; verify against the docs.
for await report in manager.diagnosticReports {
switch /* diagnostic case */ {
case /* crash */: break // backtrace, reason, category
case /* hang */: break // handle separately
default: break
}
}
StateReporting: 하나의 평균값에서 화면별 진실로
위의 모든 내용은 여전히 앱 전체 텔레메트리를 설명할 뿐이며, 앱 전체 텔레메트리에는 한계가 있습니다. 세션의 경비 정산 앱이 그 문제를 구체적으로 보여 줍니다. 이 앱은 기능을 Reports 탭과 Spending 탭으로 정리합니다. 하루 동안 MetricKit은 5분간의 스크롤에 걸쳐 총 4.5초의 히치 시간, 즉 15 ms/s의 히치율을 보고합니다. 하지만 그 수치는 “누군가 Reports 탭과 Spending 탭을 오가더라도 앱 사용 전체에 걸친 평균 스크롤 히치율”입니다.1 앱이 히치한다는 사실은 알 수 있습니다. 하지만 어디에서 히치하는지는 알 수 없습니다.
새로운 StateReporting 프레임워크는 이 뒤섞임을 없앱니다. 상태란 “앱의 구성이나 동작을 설명하기 위해 여러분이 정의하는 정보로, MetricKit이 그 특성들을 함수로 삼아 메트릭을 집계할 수 있게 해 주는 것”입니다.1 사용자가 탭 사이를 오갈 때마다 앱은 각 전환을 보고하고, MetricKit은 그 상태들을 메트릭 및 진단 데이터와 교차시킵니다.1
데모에서의 성과야말로 이 재구축을 정당화하는 순간입니다. 하나로 뭉뚱그린 15 ms/s 수치 대신, 메트릭이 상태별로 도착합니다. Spending 탭의 스크롤은 1 ms/s로 “믿을 수 없을 만큼 부드러웠던” 반면, Reports 탭은 “71 ms/s까지 치솟았습니다”.1 세션은 평균값으로는 결코 뒷받침할 수 없었던 결론을 끌어냅니다. “Spending 탭은 훌륭하게 동작합니다! 하지만 Reports 탭은 심각한 중단을 겪고 있으며, 바로 그곳에 최적화 노력을 집중해야 합니다”.1 하나의 숫자가 판정이자 작업 지시서가 되었습니다.
상태는 시작과 끝으로 감싸는 방식이 아니라 전환 모델을 따릅니다. “시작과 끝의 쌍은 없습니다. 앱은 매 순간 자신이 처한 조건을 보고합니다”. 그리고 MetricKit은 앱이 각 상태에 얼마나 머무는지를 추적합니다.1
도메인, 메타데이터, 그리고 상태별 인코딩
각 상태는 하나의 도메인에 범위 지정됩니다. 도메인은 “앱의 한 기능이나 영역을 설명”합니다. 하나의 도메인은 한 번에 하나의 활성 상태만 담을 수 있지만, 도메인을 분리하면 여러 상태가 동시에 진행될 수 있습니다.1 세션의 예시는 A/B 실험입니다. 실험적 변경을 켜면 경비가 데이터베이스에서 작은 배치로 가져와지고, 끄면 더 큰 배치로 가져와집니다. 탭 상태와 배치 크기 상태를 서로 다른 도메인에 두면 “MetricKit은 각 탭과 각 배치 크기에 대해 별도의 메트릭을 전달”합니다.1 화면별 텔레메트리와 실험 결과를 같은 파이프라인에서, 그것도 필드에서 얻는 것입니다.
도입은 세션에서 세 단계로 나뉩니다. 먼저 StateReporting 프레임워크를 임포트하고, 도메인을 만든 뒤(“보통 역방향 DNS 문자열”) MetricManager 인스턴스를 설정할 때 그것을 등록합니다. 그다음, 앱이 각 상태에 진입할 때 전환을 보고합니다. 예를 들어 “Reports”라는 문자열로 식별되는 상태로의 전환과 같은 식입니다.1 더 세밀한 단위가 필요하다면, ReportableMetadata 매크로로 직접 만든 구조체를 정의하고, 그 메타데이터 타입을 갖는 StateReporter를 만든 뒤, 레이블과 사용자 정의 타입을 함께 담아 전환을 보고합니다. 세션의 ViewConfiguration 예시는 listSize 값과 리스트의 정렬 여부를 담고 있습니다.1 다시 말하지만, 세션은 이 흐름을 완전한 시그니처 없이 슬라이드로 보여 주므로, 그 형태는 그대로 베껴 쓸 구문이 아니라 문서에서 확인해야 할 것으로 다뤄야 합니다.
받는 쪽에서는 보고서에 두 번째 축이 더해집니다. 어떤 상태도 보고되기 전에는 메트릭 보고서의 stateEntries 속성이 비어 있습니다. 도입 후에는 보고서가 StateEntry 값을 담으며, 각각은 “그 개별 상태에 머문 시간에 걸쳐 집계된 메트릭 값”을 담습니다.1 서버 파이프라인을 위해서는 인코딩된 출력을 도메인별로 묶을 수 있습니다. JSONEncoder의 userInfo 속성에 encodingFormatKey 키를 byStateReportingDomain으로 설정하면, 인코딩된 보고서는 상태 항목과 인터벌 항목을 모두 “보고서에 존재하는 각 도메인과 상태별로 묶어서” 제시합니다.1
모범 사례, 그리고 어디서부터 시작할까
세션은 어렵게 얻어 낸 스키마 설계 조언처럼 읽히는 지침으로 마무리됩니다. 도메인은 하나의 앱 영역에 좁게 범위 지정되어야 합니다. 상태 전환은 “일시적인 UI 이벤트가 아니라 안정적이고 의미 있는 단계를 나타내야 합니다”.1 퇴보가 나타났을 때 그 상태만으로 수정 대상을 겨냥할 충분한 정보를 얻을 수 있도록 각 상태를 설계하세요. 그리고 무엇이든 계측하고 싶은 충동은 자제하세요. “상태가 너무 많으면 데이터가 지나치게 세분화되어 오히려 전체 그림을 해석하기 어려워질 수 있습니다”. 상태 수에는 오버헤드를 최소화하기 위한 상한이 존재합니다(세션은 구체적인 수치를 제시하지 않습니다).1 출시 전에는 Points of Interest 인스트루먼트로 보고된 상태가 기대와 일치하는지 검증하세요.1
수집 쪽은 시스템의 절반에 불과합니다. 세션은 “모든 기기에 걸쳐 메트릭을 분석하는 것은 데이터 과학 문제입니다”라고 솔직하게 말합니다. 보고서를 수집하는 서버를 세우고, 관심 있는 차원을 따라 집계하고, 기준선을 세운 뒤, 어느 방향으로의 변화든 모니터링하는 것입니다.1 Codable 보고서와 byStateReportingDomain 인코딩은 바로 그 파이프라인을 먹여 살리기 위해 존재합니다.
기존 채택자를 위한 마지막 지시는 명확합니다. “MXMetricManager API를 사용하고 있다면, 이 모든 새 기능을 활용하기 위해 새로운 MetricManager API로 마이그레이션하세요”.1 새로운 API야말로 세션의 모든 발전이 깃든 곳이며, 세션은 그것을 “프레임워크의 미래”로 제시합니다.1
FAQ
iOS 27에서 MetricKit은 실제로 무엇이 바뀌었나요?
프레임워크가 현대적인 Swift 우선 API로 재구축되었습니다. 진입점은 새로운 MetricManager 클래스입니다. 메트릭과 진단 보고서는 await할 수 있는 비동기 스트림(metricReports, diagnosticReports)으로 도착하며, 보고서는 Codable이라 곧바로 JSON으로 인코딩할 수 있고, 그 구조는 intervalEntries와 메트릭 그룹을 통해 코드로 탐색할 수 있습니다. iOS 27은 또한 Metal 프레임률 메트릭, 메모리 예외 진단, 크래시 진단을 메트릭 집계와 연결하는 크래시 category, 그리고 상태별 메트릭을 위한 StateReporting 프레임워크를 추가합니다.1
StateReporting은 어떤 메트릭이 어떤 상태에 속하는지 어떻게 결정하나요?
앱이 전환을 보고합니다. 즉, 여러분이 정의한 도메인 안에서 옮겨 가는 상태를 보고하는 것입니다. MetricKit은 앱이 각 상태에 머무는 시간을 추적하고, 그곳에서 보낸 시간에 걸쳐 메트릭 값을 집계합니다. 시작과 끝의 쌍은 없습니다. 앱은 그저 매 순간 자신이 처한 조건을 보고할 뿐입니다. 그러면 각 상태는 메트릭 보고서 안에 자신만의 StateEntry를 갖게 됩니다.1
화면과 실험 분기처럼 둘 이상의 차원을 동시에 추적할 수 있나요?
네. 각 도메인은 한 번에 하나의 활성 상태를 담을 수 있지만, 서로 다른 도메인은 동시에 동작합니다. 세션의 경비 앱은 활성 탭을 한 도메인에, 데이터베이스 배치 크기 실험을 다른 도메인에 두며, MetricKit은 각 탭과 각 배치 크기에 대해 별도의 메트릭을 전달합니다.1
모든 UI 이벤트를 상태로 보고해야 하나요?
아니요. 세션은 일시적인 UI 이벤트가 아니라 안정적이고 의미 있는 단계를 나타내는 상태, 하나의 앱 영역에 좁게 범위 지정된 도메인, 그리고 전반적인 절제를 권장합니다. 상태가 너무 많으면 데이터를 해석하기 어려워지고, 시스템은 오버헤드를 최소화하기 위해 상태 수에 상한을 둡니다. 출시 전에 Points of Interest 인스트루먼트로 상태를 검증하세요.1
MXMetricManager에서 반드시 옮겨야 하나요?
세션의 지침은 MXMetricManager에서 새로운 MetricManager API로 마이그레이션하라는 것입니다. 다뤄진 모든 새 기능(비동기 스트림, Codable 보고서, 상태 인식 메트릭, 새로운 메트릭과 진단 타입)이 새 API 세트에서만 사용할 수 있기 때문입니다.1
MetricKit은 올해의 두 부분으로 이루어진 이야기에서 필드 쪽 절반입니다. Instruments는 실험실에서 히치를 보여 주고, 상태 인식 MetricKit은 실제 사용자에게 어느 화면이 히치하는지를 알려 줍니다. 실험실 쪽 이야기는 Instruments 27과 앱 응답성에서 다룹니다. 71 ms/s 탭을 실제로 고치는 렌더링 작업은 iOS 27의 SwiftUI 성능과 상호 운용에 있습니다. 그리고 애초에 평균값이 왜 오해를 부르는지는 성능의 사각지대의 주제입니다. 시리즈 전체 허브는 Apple Ecosystem 시리즈입니다.
참고 문헌
-
Apple, WWDC 2026 session 222, Meet the new MetricKit. iOS 27의 처음부터의 재구축과 Swift 우선 API라는 위치 지정,
MetricManager진입점과metricReports/diagnosticReports비동기 스트림,Codable보고서와JSONEncoder사용,intervalEntries와 메트릭 그룹(.cpu,.memory,.display,.gpu,peakMemory), Metal 프레임률 메트릭, 메모리 예외 진단, 크래시 종료category,submitReport()백트레이스 설명,StateReporting프레임워크(도메인, 전환 모델,StateReporter,ReportableMetadata,stateEntries,encodingFormatKey를 통한byStateReportingDomain), 경비 앱 데모 수치(평균 15 ms/s, Spending 탭 1 ms/s 대 Reports 탭 71 ms/s), 상태 모범 사례와 Points of Interest 검증, 그리고MXMetricManager에서MetricManager로 마이그레이션하라는 지침의 출처. ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩
