응답 시간 열 개를 모아 평균만 보니 크게 느리지 않습니다. 그런데 마지막 요청 하나를 기다린 사용자는 전혀 다르게 느낄 수 있습니다. 평균은 모든 시간을 더한 뒤 요청 수로 나누지만, 백분위수는 정렬한 값의 위치를 봅니다. 두 숫자는 다른 질문에 답합니다. 이 글에서는 실제 서비스 로그 대신 고정한 열 개의 가상 밀리초 값을 사용해 P50·P95의 계산법, 작은 표본의 흔들림, 서버별 P95를 평균하면 생기는 오류를 함께 확인합니다. 빠른 서비스라고 홍보하는 기준이나 성능 순위를 만드는 실험은 아닙니다.

같은 열 개의 값에서 평균과 끝부분을 같이 봅니다.

입력은 100, 110, 120, 130, 140, 150, 160, 170, 180, 2000밀리초입니다. 합계 3260을 열 개로 나누면 평균은 326밀리초입니다. 앞의 아홉 요청은 200밀리초 안쪽이지만 마지막 요청은 2초입니다. 평균이 326이라는 설명은 산술적으로 맞아도 마지막 요청의 대기 상황을 보여 주지는 않습니다. 그래서 요청 수, 단위, 최댓값과 함께 중간과 꼬리 부분을 분리합니다.

이 입력은 어떤 AI나 사이트를 실제로 호출해 얻은 로그가 아닙니다. 긴 꼬리 하나가 요약값에 미치는 영향을 확인하기 위해 만든 수열입니다. 코드의 inputMs에 전체 값을 넣었으므로 독자가 직접 정렬하고 합계를 검산할 수 있습니다. 시간 단위를 초로 바꾸더라도 전체 값을 일관되게 바꾸면 순서는 같습니다. 일부만 초로 읽고 나머지는 밀리초로 읽으면 백분위수 이전에 입력 자체가 잘못됩니다. 결측값과 실패 요청을 어떻게 처리했는지도 실제 보고서에는 따로 적어야 합니다.

입력 범위관측 수평균 ms최근접 P95 ms
전체 고정 수열103262000
마지막 한 건 제외9140180
변화의 이유관측 제외성능 개선 아님계산 대상 변경

P95라는 이름만으로 계산 결과가 정해지지는 않습니다.

이 글의 첫 규칙은 최근접 순위 방식입니다. 정렬된 n개에서 올림한 n×p 번째 값을 고릅니다. n=10, p=0.95이면 9.5를 올려 열 번째 값인 2000을 선택합니다. P50은 다섯 번째 값 140입니다. 짝수 개 자료의 중앙값을 가운데 두 값의 평균으로 정의하면 145가 되므로, 이 P50 값 역시 사용한 순위 정의를 함께 읽어야 합니다. 이름이 비슷해도 함수가 실제로 무엇을 계산하는지 확인하는 이유입니다.

두 번째 규칙은 R7 선형 보간입니다. 0부터 세는 위치 (n−1)×p를 계산하면 8.55가 됩니다. 아홉 번째 값 180과 열 번째 값 2000 사이를 55% 이동하면 1181입니다. 어느 입력에도 1181밀리초 요청은 없지만 보간 규칙에서는 유효한 추정값입니다. 코드가 반환한 p95Nearest=2000과 p95LinearR7=1181은 서로의 오류 증거가 아닙니다. 같은 표본과 다른 정의의 결과입니다. 비교할 대시보드에는 계산 방식이나 라이브러리의 method까지 남기는 편이 정확합니다.

보간의 소수 자릿수를 늘려도 표본의 한계를 줄이지는 못합니다. 같은 입력의 P95가 1181.0000이라고 표시되면 계산은 정밀해 보이지만 끝부분을 지탱하는 값은 여전히 180과 2000 두 개뿐입니다. 결과를 반올림하기 전에 원시 계산값을 보존하고, 화면의 정밀도는 독자가 내릴 판단에 맞춥니다. 측정 도구의 해상도가 10밀리초라면 소수 밀리초를 여러 자리 적는 것도 별도 설명이 필요합니다. 이 자료의 입력 정수는 가상 값이며 실제 계측 해상도를 반영하지 않습니다.

간단한 손검산도 가능합니다. 최근접 순위의 경우 정렬 후 인덱스를 1부터 붙이고 ceil(n×p)를 표시합니다. 보간은 두 이웃 값과 비율 0.55를 표 옆에 남깁니다. 숫자 두 개의 평균을 내야 하는지 혼동하는 경우 이 네 항목을 적어 보면 계산 방식이 분명해집니다. 같은 열에 순위식 결과와 보간 결과를 섞지 말고 각각 method 필드를 둡니다.

요청 하나를 빼면 무엇이 달라지는지 먼저 기록합니다.

마지막 2000을 제외한 아홉 값의 평균은 140, 최근접 순위 P95는 180입니다. 느린 요청 하나를 빼는 순간 평균은 326에서 140으로, P95는 2000에서 180으로 바뀝니다. 이것을 그대로 개선 성과라고 부를 수는 없습니다. 시스템이 빨라진 것이 아니라 분석 대상이 달라졌기 때문입니다. 타임아웃을 집계에서 제외하는 설정도 비슷한 착시를 만들 수 있으므로 제외 사유와 실패 수를 별도로 보여 줘야 합니다.

열 개 표본에서 한 건은 전체의 10%입니다. 그런 자료로 1% 꼬리 행동까지 정밀하게 설명하려면 정보가 부족합니다. 백분위수를 계산할 수 있다는 사실과 모집단의 꼬리를 안정적으로 추정했다는 주장은 다릅니다. 로그에 한 건이 더 들어올 때 어떤 순위가 바뀌는지, 관측 기간과 요청 유형이 대표성을 갖는지 먼저 봅니다. 몇 개 이상이면 무조건 충분하다는 숫자를 정하기보다 보고하려는 꼬리 확률, 변동성, 반복 관측 조건에 따라 자료를 설계해야 합니다.

서버별 P95의 평균은 전체 P95가 아닙니다.

두 서버를 가정합니다. A는 모두 1밀리초인 요청 100개, B는 1000밀리초 요청 한 개를 처리했습니다. 각 서버의 최근접 순위 P95는 1과 1000입니다. 이를 단순 평균하면 500.5가 됩니다. 하지만 실제 요청 101개를 합쳐 정렬한 전체 P95는 1입니다. 서버 두 개에 같은 비중을 주는 계산과 요청 101개에 같은 비중을 주는 계산을 섞었기 때문에 결과가 갈립니다.

가중 평균으로 바꾸면 백분위수를 복원할 수 있다는 뜻도 아닙니다. 백분위수 한 개에는 그 아래와 위에 어떤 값이 얼마나 있는지의 정보가 사라져 있습니다. 원시 관측이나 적절한 분포 표현이 있어야 합친 자료의 백분위수를 다시 계산할 수 있습니다. Prometheus 문서에서 미리 계산한 요약 분위수를 여러 인스턴스 사이에 단순 집계할 수 없다고 설명하는 대목이 이 문제와 연결됩니다. 운영 도구에서는 히스토그램의 구간과 집계 방식을 확인해야 하며, 여기의 정확한 원시 순위와 버킷 추정값도 동일하게 취급하지 않습니다.

서로 다른 기간과 요청 종류를 섞기 전에 질문을 정합니다.

초기 화면 열기와 큰 파일 업로드를 같은 응답 시간 열에 넣으면 백분위수는 계산되지만 무엇의 사용 경험을 설명하는지 흐려집니다. 인증 여부, 지역, 캐시 적중, 정상·실패 상태, 요청 크기가 다른 자료를 섞었다면 그룹 구성이 바뀐 것만으로 P95가 움직일 수 있습니다. 같은 지표 이름으로 지난주와 비교하려면 집계 기간, 포함 경로와 제외 조건을 맞추거나 차이를 공개합니다.

보고 단위를 서버가 아니라 사용자 세션으로 잡을 수도 있지만 그때는 입력과 해석을 다시 정의해야 합니다. 요청 단위 P95를 사용자의 95%가 그 안에 모든 작업을 끝냈다는 뜻으로 바꿔 쓰면 안 됩니다. 한 사용자가 여러 요청을 만들고 느린 요청이 특정 사용자에게 몰릴 수도 있기 때문입니다. 이 예제는 요청마다 동등한 한 값이 있다는 가정만 다룹니다. 실제 사용자 이해도나 매출·전환율과의 관계는 관측하지 않았습니다. 수익화 화면에 적용하더라도 지연 계산과 사업 효과 주장은 별도로 입증해야 합니다.

실패한 요약을 고칠 때는 원시 건수를 옆에 둡니다.

“P95가 180밀리초로 좋아졌다”는 문장을 검토한다고 합시다. 이전 값 2000과 이후 값 180이 같은 열 개 요청에서 나온 것이 아니라, 이후 자료에서 느린 한 건을 삭제해 만든 값이라면 개선 설명을 지웁니다. 대신 “이 입력의 전체 열 건 P95는 2000이며 마지막 한 건을 제외한 아홉 건은 180이다”라고 씁니다. 독자가 변경의 원인을 결과표에서 바로 찾을 수 있습니다.

운영 표에는 기간, 전체 요청 수, 성공 수, 오류 수, 제외 수, 평균, P50·P95, 계산 방식과 단위를 함께 둡니다. 한 셀에 모든 수치를 넣기보다 같은 분모를 쓰는지 확인할 수 있게 행을 구성합니다. 요청 수가 매우 적은 구간은 값을 숨기기보다 작은 표본임을 표시하고 원시 분포를 같이 볼 수 있게 합니다. 빈 구간은 0밀리초가 아니라 관측 없음입니다. 실제로 측정된 빠른 응답과 자료가 수집되지 않은 상태를 같은 값으로 만들지 않는 것이 이후 경보 판단에도 도움이 됩니다.

숫자를 검산하는 작은 코드와 성능 시험은 다릅니다.

다운로드 코드는 두 백분위수 함수와 고정 입력을 제공합니다. 서버 A의 원시 배열 100개와 B의 배열 1개에도 같은 순위 함수를 실제로 적용한 뒤, 그 결과의 평균과 합친 배열의 P95를 각각 계산합니다. 결과 JSON의 inputGroupsMs에서 집계 전 두 입력을 직접 확인할 수 있습니다. 무작위 자료를 생성하거나 네트워크를 호출하지 않으므로 입력을 그대로 두면 같은 판정값이 나옵니다. 평균 326, P95 2000과 1181, 한 건 제외 후 P95 180, 합친 101건 P95 1을 확인했습니다. 자동 검증문은 이 예상값이 깨졌을 때 실행을 멈춥니다. 실제 지연이 얼마나 빠른지 측정한 코드로 읽으면 안 됩니다.

다른 자료로 바꿀 때는 우선 음수, 무한대, 문자, 결측값의 취급을 정해야 합니다. 이 고정 예제에는 정상적인 숫자만 있으므로 모든 로그 파서의 입력 검증을 대신하지 않습니다. 동일한 요청을 재시도한 기록을 독립 관측으로 셀지, 사용자가 취소한 요청을 끝까지 기다린 시간과 섞을지 같은 문제도 남아 있습니다. 계산 함수만 복사한 뒤 보고서 문장을 자동 생성하기보다는 입력 계약을 먼저 고정하고 작은 수열로 기대 순위를 확인한 다음 실제 자료의 분포를 대조합니다.

실제 보고서는 한 문장보다 비교표 한 줄을 완성합니다.

실무에서는 “평균이 빠르다”를 “어느 집계에서 어떤 위치가 얼마다”로 바꿔 적습니다. 예를 들어 “가상 열 건의 평균은 326밀리초, 최근접 순위 P50은 140, P95는 2000이다. R7 보간 P95는 1181이며 실제 서비스 측정이 아니다”라고 쓰면 분모와 방법, 결과의 한계가 모두 보입니다. 한 수치만 굵게 표시해 계산 정의를 각주 깊숙이 보내지 않습니다.

후속 행동도 결과와 연결합니다. 꼬리 한 건의 원인을 찾으려면 해당 요청의 단계별 기록이 필요하고, 집계 기간을 비교하려면 같은 포함 조건의 자료를 다시 모아야 합니다. 이 글의 수열은 병목 위치, 네트워크 상태 또는 AI 모델 속도를 판별하지 못합니다. 원시 입력을 보존하고 제외 전후를 나눠 기록하는 습관부터 적용하세요. 요청의 전후 데이터가 바뀌지 않았는지 확인하는 일은 CSV 변경 대조 글과, 비율을 합치며 분모가 바뀌는 일은 전환율 합산 글과 함께 보면 서로 다른 요약 오류를 구분하기 쉽습니다.

입력과 판정을 다시 확인하려면

이 글의 가상 입력과 코드는 2026년 10월 15일 로컬 Python 3에서 실행했습니다. 글의 발행일과 실행일은 다릅니다. 별도 설치 패키지 없이 Python 표준 라이브러리로 실행할 수 있습니다.

터미널에서 python3 latency-percentile-small-samples-20261015.py를 실행하면 JSON을 출력합니다. 사용자 자료나 비밀번호는 필요하지 않습니다. 입력과 실행 환경은 결과 JSON에 함께 남겼습니다.

계산 기준을 확인한 원문과 다음 작업

공식 원문은 2026-10-15에 확인했습니다. 문서가 설명하는 API·정의와 이 글의 가상 계산 결과는 구분해서 읽어 주세요.

함께 보면 좋은 글: CSV 변경 대조, 전환율 합산 검산.