← 모든 글

MetricKit 재탄생: iOS 27의 상태 인식 텔레메트리

Apple의 WWDC 2026 데모 앱은 하루 종일의 사용 시간 전체에 걸쳐 평균을 낸 초당 15밀리초의 스크롤 히치 비율을 보고했습니다. 같은 데이터를 탭별로 나누자 완전히 다른 이야기가 드러났습니다. 한 탭에서는 1ms/s, 다른 탭에서는 71ms/s였습니다.1 한 화면은 거의 완벽했지만, 다른 화면은 세션의 표현을 빌리자면 “심각한 중단을 겪고 있었습니다.”1 뒤섞인 숫자는 두 사실을 모두 가려 버렸습니다. 세션 222 “Meet the new MetricKit”는 iOS 27이 어떻게 그 간극을 메우는지에 관한 이야기입니다. 프레임워크 API 표면의 근본적인 재구축, 그리고 앱 전체의 현장 메트릭을 상태별 메트릭으로 바꾸는 새로운 동반 프레임워크 StateReporting입니다. 현장 텔레메트리는 마침내 모든 성능 엔지니어가 가장 먼저 묻는 질문에 답할 수 있게 되었습니다. 어느 화면이 느린가?

TL;DR

  • iOS 27에서 MetricKit은 “맥락이 풍부하고 표현력 있는 현대적인 Swift-first API로 처음부터 다시 구축되었으며”, 세션에 나오는 모든 새로운 기능은 새 API에서만 사용할 수 있습니다.1
  • 진입점은 MetricManager 클래스입니다. 앱은 실행 시점에 metricReportsdiagnosticReports 비동기 스트림을 await하고, 두 리포트 타입 모두 Codable이므로 JSONEncoder로 분석 서버에 곧바로 전송할 수 있습니다.1
  • 리포트는 구조화되어 있습니다. intervalEntries는 하루 전체 항목과 더 작은 분할 항목을 담고, .cpu, .memory, .display, .gpu 같은 메트릭 그룹으로 정리되며, peakMemory 같은 개별 값까지 내려갑니다.1
  • iOS 27의 새로운 데이터: 렌더링 성능을 위한 Metal 프레임 레이트 메트릭, 메모리 한도 초과 종료를 위한 메모리 예외 진단, 그리고 개별 크래시 진단을 메트릭 추세와 다시 연결하는 크래시 category입니다.1
  • 핵심 기능은 StateReporting 프레임워크입니다. 앱이 처한 상태(활성 탭, 실험 그룹, 뷰 구성)를 보고하면 MetricKit이 상태별로 메트릭을 집계하여, 하나의 뒤섞인 숫자를 화면별 분석으로 대체합니다.1

처음부터 다시 구축되다

Watch on Apple Developer ↗

MetricKit 팀의 엔지니어 Yonni가 1:23부터 iOS 27 재구축을 소개합니다.

MetricKit의 역할은 바뀌지 않았습니다. 그것은 성능 워크플로의 “수집 부분”으로, 두 종류의 데이터를 제공합니다. 메트릭은 성능의 한 영역이 전반적으로 개선되고 있는지 악화되고 있는지를 알려 주고, 진단은 어떤 코드 경로가 문제를 일으켰는지 알려 줍니다.1 바뀐 것은 그 데이터를 받는 방식 전부입니다. 세션은 분명하게 말합니다. iOS 27에서 프레임워크는 “맥락이 풍부하고 표현력 있는 현대적인 Swift-first API로 처음부터 다시 구축되었으며”, “오늘 제가 다룰 모든 발전은 이 새로운 API 세트에서만 사용할 수 있습니다.”1

새로운 진입점은 MetricManager 클래스입니다. 델리게이트를 등록하고 페이로드를 파싱하는 대신, metricReports 프로퍼티를 통해 비동기 스트림으로 리포트를 await합니다. 두 가지 운영 규칙이 세션에서 곧바로 나옵니다. “지연된 구독으로 인한 데이터 손실을 피하기 위해” 설정을 앱 시작 시점에 하고, “후속 데이터가 준비될 때 스트림이 계속해서 리포트를 전달할 수 있도록” MetricManager를 살아 있게 유지하라는 것입니다.1 Apple은 앱이 실행되자마자 detached task나 전용 서비스 클래스에서 작업을 실행할 것을 권장합니다.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밀리초 사이에 들어오는 것을 보여 줍니다), 행(hang), 애니메이션 메트릭, 그리고 CPU, GPU, 디스크 쓰기, 네트워크 전송 같은 리소스 소비와 더불어, MetricKit은 Metal 프레임 레이트 메트릭을 추가합니다. 세션은 프레임 레이트를 “게임 개발자가 렌더링 성능을 이해하는 데 핵심이 되는 메트릭”이라고 부르며, 최적화 측면을 위해 “Find and fix performance issues in your Metal game”을 가리킵니다.1

실행 시간을 직접 계측하기보다 MetricKit의 실행 메트릭에 의존하세요. Apple은 사용자가 앱 아이콘을 탭한 순간부터 첫 프레임이 그려지는 순간까지 실행 시간을 측정하는데, 이는 프로세스가 존재하기도 전입니다.3 직접 만든 타이머는 코드가 실행된 이후에야 시작될 수 있으므로 main 이전 구간을 완전히 놓치고, 사용자가 실제로 체감하는 실행 시간을 과소 집계합니다. 중요한 부분을 볼 수 없는 타이머를 만드는 대신 MetricKit이 제공하는 히스토그램을 읽으세요.

진단: 백트레이스, 메모리 예외, 그리고 크래시 카테고리

메트릭은 무언가가 후퇴했음을 알려 주고, 진단은 어디에서 그랬는지 알려 줍니다. 크래시나 행처럼 무언가 잘못되면 “시스템이 기기에서 진단을 캡처”하고, 진단 리포트가 “세부 정보를 패키징하여 MetricKit을 통해 즉시 앱에 전달합니다.”1 많은 진단에는 이벤트 발생 시점의 정확한 호출 스택을 보여 주는 백트레이스가 포함됩니다. 세션의 시연에서 심볼화된 백트레이스는 시스템 코드의 스레드 시작 지점에서 출발하여 앱으로 넘어간 뒤, 앱의 submitReport() 함수에서 멈추는데, 이 지점이 실패가 발생한 곳이자 수정의 표적으로 삼을 자리를 표시합니다.1

크래시 진단은 백트레이스, 종료 사유, 그리고 예외 타입을 담습니다. iOS 27에 새로 추가된 종료 category는 “각 크래시가 메트릭에서 어떻게 집계되었는지를 나타내므로”, “비정상 종료가 증가하는 추세라면 이를 개별 진단과 직접 상관 지을 수 있습니다.”1 대시보드의 메트릭 선과 그 뒤에 있는 개별 크래시 리포트가 마침내 하나의 키를 공유합니다.

iOS 27은 메모리 예외 진단도 추가합니다. “앱이나 확장 프로그램이 메모리 한도를 초과하여 종료될 때, 무슨 일이 일어났는지에 대한 더 많은 통찰을 얻게 됩니다.”1 확장 프로그램이 명시적으로 범위에 포함되는데, 이는 위젯이나 확장 프로그램의 메모리 강제 종료를 원격으로 디버깅하는 누구에게나 중요합니다.

소비 방식은 메트릭 쪽을 그대로 따릅니다. MetricManager 인스턴스에서 diagnosticReports를 await하는데, 마찬가지로 앱 실행 시점에 detached task나 서비스 클래스에서 하며, 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초의 히치 시간을 보고하는데, 이는 15ms/s의 히치 비율입니다. 하지만 그 숫자는 “누군가 Reports 탭과 Spending 탭 사이를 오가더라도 모든 앱 사용에 걸친 평균 스크롤 히치 비율”입니다.1 앱이 히치를 일으킨다는 것은 압니다. 어디서인지는 모릅니다.

새로운 StateReporting 프레임워크는 그 혼합을 제거합니다. 상태(state)란 “앱의 구성이나 동작을 설명하기 위해 여러분이 정의하는 정보로, MetricKit이 그 특성들의 함수로서 메트릭을 집계할 수 있게 해 줍니다.”1 사람들이 탭 사이를 이동할 때 앱은 각 전환을 보고하고, MetricKit은 그 상태들을 메트릭 및 진단 데이터와 교차시킵니다.1

데모에서 그 보상은 재구축을 정당화하는 순간입니다. 하나의 뒤섞인 15ms/s 수치 대신, 메트릭이 상태별로 도착합니다. Spending 탭은 1ms/s로 “믿을 수 없을 만큼 부드럽게” 스크롤된 반면, Reports 탭은 “71ms/s로 치솟았습니다.”1 세션은 뒤섞인 숫자로는 결코 뒷받침할 수 없는 결론을 끌어냅니다. “Spending 탭은 훌륭하게 작동하고 있습니다! 하지만 Reports 탭은 심각한 중단을 겪고 있으며, 바로 그곳이 여러분의 최적화 노력이 집중되어야 할 곳입니다.”1 하나의 숫자가 판결이자 작업 지시서가 되었습니다.

상태는 괄호로 묶는 방식이 아니라 전환 모델을 따릅니다. “시작이나 끝 쌍은 없으며, 앱은 주어진 시점에 자신이 처한 조건을 보고합니다.” 그리고 MetricKit은 앱이 각 상태에 머무는 시간을 추적합니다.1

도메인, 메타데이터, 그리고 상태별 인코딩

각 상태는 도메인에 범위가 지정되는데, 도메인은 “앱의 한 기능이나 영역을 설명합니다.” 도메인은 한 번에 하나의 활성 상태만 가질 수 있으며, 별도의 도메인을 두면 여러 상태가 동시에 진행될 수 있습니다.1 세션의 예시는 A/B 실험입니다. 실험적 변경이 켜져 있으면 경비가 데이터베이스에서 작은 배치로 가져와지고, 꺼져 있으면 더 큰 배치로 가져와집니다. 탭 상태와 배치 크기 상태를 별도의 도메인에 두면 “MetricKit이 각 탭과 각 배치 크기에 대해 별도의 메트릭을 전달합니다.”1 같은 파이프라인에서, 현장에서, 화면별 텔레메트리와 실험 결과를 얻는 것입니다.

채택은 세션에서 세 단계입니다. StateReporting 프레임워크를 import하고, 도메인(“일반적으로 역방향 DNS 문자열”)을 생성하여 MetricManager 인스턴스를 설정할 때 등록한 다음, 앱이 각 상태에 진입할 때 전환을 보고하는데, 예를 들어 “Reports”라는 문자열로 식별되는 상태로 전환하는 식입니다.1 더 세밀한 단위를 위해서는 ReportableMetadata 매크로로 자신만의 struct를 정의하고, 그 메타데이터 타입으로 StateReporter를 생성한 뒤, 레이블과 커스텀 타입을 함께 사용하여 전환을 보고합니다. 세션의 ViewConfiguration 예시는 listSize 값과 리스트의 정렬 여부를 담습니다.1 다시 말하지만, 세션은 이 흐름을 전체 시그니처 없이 슬라이드로 보여 주므로, 그 형태를 그대로 복사할 구문이 아니라 문서에서 확인해야 할 것으로 다루세요.

받는 쪽에서는 리포트에 두 번째 축이 생깁니다. 어떤 상태도 보고되기 전에는 메트릭 리포트의 stateEntries 프로퍼티가 비어 있습니다. 채택 후에는 리포트가 StateEntry 값들을 담으며, 각각은 “해당 개별 상태에서 보낸 시간에 걸쳐 집계된 메트릭 값”을 보유합니다.1 서버 파이프라인을 위해서는 인코딩된 출력을 도메인별로 그룹화할 수 있습니다. JSONEncoderuserInfo 프로퍼티에 encodingFormatKey 키를 byStateReportingDomain으로 설정하면, 인코딩된 리포트가 상태 항목과 인터벌 항목을 모두 “리포트에 존재하는 각 도메인과 상태별로 그룹화하여” 제시합니다.1

모범 사례, 그리고 어디서 시작할까

세션은 어렵게 얻은 스키마 설계 조언처럼 들리는 지침으로 마무리합니다. 도메인은 하나의 앱 영역으로 좁게 범위를 지정해야 합니다. 상태 전환은 “일시적인 UI 이벤트가 아니라 안정적이고 의미 있는 단계를 나타내야 합니다.”1 후퇴가 나타날 때 상태만으로도 수정의 표적을 정하기에 충분한 정보를 주도록 각 상태를 설계하세요. 그리고 모든 것을 계측하려는 충동을 억제하세요. “상태가 너무 많으면 데이터가 지나치게 세분화되어 전체 그림을 해석하기가 실제로 더 어려워질 수 있으며”, 오버헤드를 최소화하기 위해 상태 개수에 상한이 존재합니다(세션은 구체적인 수치를 제시하지 않습니다).1 출시 전에 보고된 상태가 기대와 일치하는지 Points of Interest instrument로 검증하세요.1

카디널리티는 메타데이터 쪽에서도 똑같은 함정입니다. 빠르게 변하는 상태 값은 정확한 개수를 보고하기보다 거친 범주(작음, 중간, 큼)로 묶으세요. Apple의 WWDC 2026 성능 랩에서 팀은 MetricKit 채택과 더 넓은 전력 워크플로에 관한 실시간 질문을 받았는데(Apple 성능 팀이 WWDC26 랩에서 말한 내용에 정리되어 있습니다), 그들은 “1,000개 대 1,001개 항목”을 기록하는 것이 통찰 없이 비용만 추가한다고 언급했습니다. 두 값은 같은 성능 영역에 속하므로, 각각에 대해 별개의 상태를 두는 것은 오버헤드만 살 뿐 아무것도 얻지 못합니다.4 동작을 바꾸는 경계를 고르고 그 사이의 모든 것은 하나로 합치세요.

수집 쪽은 시스템의 절반에 불과합니다. 세션은 “모든 기기에 걸쳐 메트릭을 분석하는 것은 데이터 사이언스 문제”라고 직접적으로 말합니다. 리포트를 수집하는 서버를 세우고, 관심 있는 차원을 따라 집계하고, 기준선을 설정하고, 어느 방향으로든 움직임이 있는지 모니터링하세요.1 Codable 리포트와 byStateReportingDomain 인코딩은 바로 그 파이프라인에 데이터를 공급하기 위해 존재합니다.

기존 채택자를 위한 마무리 지침은 명확합니다. “MXMetricManager API를 사용하고 있다면, 이 모든 새로운 기능을 활용하기 위해 새로운 MetricManager API로 마이그레이션하세요.”1 Apple 문서는 이제 그 마이그레이션을 공식화합니다. MXMetricManager는 27.0부터 deprecated로 표시되었으며, “Use MetricManager instead”라는 안내가 함께합니다.2 이번 주기에는 동일한 단계적 27 경계 적용이 다른 곳에서도 이루어졌는데, Image Playground의 iOS 27에서 ImageCreator deprecation이 그 예로, 여기서는 deprecation 경고가 정식 출시 때 완전한 단절로 바뀝니다. 새 API는 세션의 모든 발전이 깃든 곳이며, 세션은 이를 “프레임워크의 미래”로 제시합니다.1

FAQ

iOS 27의 MetricKit에서 실제로 무엇이 바뀌었나요?

프레임워크가 현대적인 Swift-first API로 재구축되었습니다. 진입점은 새로운 MetricManager 클래스이고, 메트릭 및 진단 리포트는 await 가능한 비동기 스트림(metricReports, diagnosticReports)으로 도착하며, 리포트는 직접 JSON 인코딩을 위해 Codable이고, 구조는 intervalEntries와 메트릭 그룹을 통해 코드에서 탐색할 수 있습니다. iOS 27은 또한 Metal 프레임 레이트 메트릭, 메모리 예외 진단, 크래시 진단을 메트릭 집계와 연결하는 크래시 category, 그리고 상태별 메트릭을 위한 StateReporting 프레임워크를 추가합니다.1

StateReporting은 어떤 메트릭이 어떤 상태에 속하는지 어떻게 결정하나요?

앱이 전환을 보고합니다. 즉, 여러분이 정의한 도메인 안에서 자신이 이동해 가는 상태를 보고합니다. MetricKit은 앱이 각 상태에 머무는 시간을 추적하고 그 시간에 걸쳐 메트릭 값을 집계합니다. 시작/끝 쌍은 없으며, 앱은 단지 주어진 시점에 자신이 처한 조건을 보고할 뿐입니다. 그러면 각 상태는 메트릭 리포트에서 자신만의 StateEntry를 얻습니다.1

화면과 실험 그룹처럼 한 번에 두 개 이상의 차원을 추적할 수 있나요?

네. 각 도메인은 한 번에 하나의 활성 상태를 가질 수 있지만, 별도의 도메인은 동시에 실행됩니다. 세션의 경비 앱은 활성 탭을 한 도메인에, 데이터베이스 배치 크기 실험을 다른 도메인에 두며, MetricKit은 각 탭과 각 배치 크기에 대해 별도의 메트릭을 전달합니다.1

모든 UI 이벤트를 상태로 보고해야 하나요?

아니요. 세션은 일시적인 UI 이벤트가 아니라 안정적이고 의미 있는 단계를 나타내는 상태, 하나의 앱 영역으로 좁게 범위가 지정된 도메인, 그리고 전반적인 절제를 권장합니다. 상태가 너무 많으면 데이터를 해석하기 더 어려워지고, 시스템은 오버헤드를 최소화하기 위해 상태 개수에 상한을 둡니다. 출시 전에 Points of Interest instrument로 상태를 검증하세요.1

MXMetricManager에서 반드시 옮겨야 하나요?

세션의 지침은 MXMetricManager에서 새로운 MetricManager API로 마이그레이션하라는 것입니다. 다룬 모든 새 기능(비동기 스트림, Codable 리포트, 상태 인식 메트릭, 새로운 메트릭 및 진단 타입)이 새 API 세트에서만 사용할 수 있기 때문입니다.1


MetricKit은 올해 두 부분으로 이루어진 이야기의 현장 절반입니다. Instruments는 랩에서 히치를 보여 주고, 상태 인식 MetricKit은 실제 사용자에게 어느 화면이 히치를 일으키는지 알려 주는데, 랩 쪽 내용은 Instruments 27과 앱 응답성에서 다룹니다. 71ms/s 탭을 실제로 고치는 렌더링 작업은 iOS 27의 SwiftUI 성능과 상호운용에 있습니다. 그리고 애초에 뒤섞인 평균이 오해를 부르는 이유는 성능의 사각지대의 주제입니다. 전체 시리즈 허브는 Apple Ecosystem 시리즈입니다.

References


  1. Apple, WWDC 2026 session 222, Meet the new MetricKit. Source for the iOS 27 ground-up rebuild and Swift-first API framing, the MetricManager entry point and the metricReports / diagnosticReports async streams, Codable reports and JSONEncoder usage, intervalEntries and metric groups (.cpu, .memory, .display, .gpu, peakMemory), the Metal frame rate metric, memory exception diagnostics, the crash termination category, the submitReport() backtrace walkthrough, the StateReporting framework (domains, transition model, StateReporter, ReportableMetadata, stateEntries, byStateReportingDomain via encodingFormatKey), the expense-app demo numbers (15 ms/s blended; 1 ms/s Spending tab versus 71 ms/s Reports tab), the state best practices and Points of Interest validation, and the guidance to migrate from MXMetricManager to MetricManager

  2. Apple Developer Documentation: MXMetricManager. Marked deprecated as of 27.0, with the guidance “Use MetricManager instead.” 

  3. Apple Developer Documentation: Reducing your app’s launch time. Launch time is measured from the moment the user taps the app icon to the first frame drawn, before the app’s process exists. 

  4. Apple, WWDC 2026 performance group lab, session 8003. Paraphrased from a locally transcribed recording; no official transcript is published. The team advised bucketing fast-changing state metadata into coarse categories, noting that recording “1,000 versus 1,001 items” adds cost without insight. 

관련 게시물

WWDC26 랩에서 Apple 성능 팀이 말한 것

Apple의 Power & Performance 팀이 WWDC26에서 개발자 질문에 실시간으로 답했습니다. MetricKit, Power Profiler, 발열, AI 경합에 관한 현장 지침을 정리합니다.

10 분 소요

앱 응답성을 위한 Instruments 27의 새로운 기능

Instruments 27은 Top Functions, Run Comparisons, Swift executors 인스트루먼트, 그리고 새로운 Inspector 패널을 추가해 CPU, 액터, syscall로 인한 멈…

8 분 소요

76점에서 100점으로: 완벽한 Lighthouse 점수 달성기

개인 포트폴리오 사이트의 모바일 Lighthouse 성능 점수를 76점(CLS 0.493)에서 모든 카테고리 완벽한 100/100/100/100으로 개선한 과정을 소개합니다.

5 분 소요