성능 모니터링은 애플리케이션을 성공적으로 운영하는 데 필수적인 요소입니다. 어떤 대상의 성능을 파악하는 가장 기본적인 방법 중 하나는 해당 작업이 실행될 때마다 소요 시간을 측정하고, 그 데이터로부터 통계를 도출하는 것입니다.
평균(Mean)
값들의 집합에 대한 평균은 어떤 것이 얼마나 좋거나 나쁘게 동작하는지 살펴보는 좋은 출발점입니다. 평균은 고려 대상인 모든 값을 더한 뒤, 발생 횟수로 나누어 계산합니다.
Ruby에서는 다음과 같이 평균 응답 시간을 계산할 수 있습니다:
참고: 예제에서는 나눗셈 시 더 정확한 결과를 얻기 위해 총 소요 시간 값을 Float로 변환(cast)했습니다. 그렇지 않으면 Ruby는 가장 가까운 정수로 내림하여 4를 반환하게 됩니다.
중앙값(Median)
또 하나 유용한 통계 지표는 중앙값입니다. 이름은 비슷하게 들리지만, 값들의 집합에서 평균과 중앙값은 서로 다른 개념입니다.
중앙값은 집합의 상반부와 하반부를 구분하는 값입니다.
홀수 개의 값을 가진 데이터셋이라면, 먼저 값을 정렬한 후 가운데 숫자를 선택하면 됩니다. 짝수 개의 값을 가진 경우에는 정렬 후 가운데 두 숫자의 평균이 중앙값이 됩니다.
중앙값은 데이터에 큰 왜곡(skew)이나 긴 꼬리(long tail)가 있는지 확인하는 데 유용한 지표입니다.
위 소요 시간들의 평균은 2000ms라는 단일 이상치(outlier) 때문에 335.83이 됩니다. 반면 중앙값은 3.5에 불과하므로, 데이터에 왜곡이 존재한다는 것을 알 수 있습니다.
데이터셋의 평균과 중앙값을 함께 계산하면 큰 이상치나 긴 꼬리가 있는지 효과적으로 파악할 수 있습니다.
평균만으로는 부족한 이유
평균과 중앙값은 성능을 판단하는 좋은 지표이지만, 전체 상황을 알려주지는 않습니다. 웹페이지를 열 번 요청했을 때 평균은 매우 낮을 수 있지만, 그중 하나 이상의 요청이 여전히 매우 오랜 시간이 걸릴 수 있습니다.
아래 이미지는 AppSignal에서 특정 액션에 대한 99번째(파란색), 90번째(녹색) 백분위수와 평균(빨간색)을 보여줍니다. 99번째와 90번째 백분위수가 평균과 상당히 멀리 떨어져 있고 일부 구간에서 스파이크가 발생하는 것을 확인할 수 있습니다. 이는 평균적인 고객은 좋은 경험을 하지만, 가끔씩 페이지 렌더링을 위해 거의 두 배나 오래 기다려야 하는 사용자가 존재한다는 의미입니다. 이상적으로는 모든 값들을 서로 최대한 가깝게 만들어, 모든 사용자에게 더욱 일관된 경험을 제공하는 것이 바람직합니다.

예를 들어, 10명의 고객이 각각 100밀리초에서 1초 사이의 소요 시간으로 페이지를 요청하는 다음과 같은 소요 시간 집합이 있다고 가정해 봅시다.
이 경우 평균은 190ms에 불과하지만, 한 명의 사용자는 1초라는 매우 불쾌한 응답 시간을 경험했습니다. 평균만 추적하면 웹사이트 성능이 훌륭하다고 착각하기 쉽지만, 실제로는 가끔씩 사용자가 끔찍한 경험을 하게 되는 것입니다.
위 예시는 단 10개의 요청에 불과하지만, 하루에 천 건의 요청이 발생한다면 그중 백 명의 사용자가 나쁜 경험을 했다는 뜻이 됩니다.
백분위수(Percentiles)
값들의 분포를 더 정확하게 파악하기 위해 백분위수를 사용합니다. 백분위수는 중앙값과 유사한 개념으로, 집합의 절반이 그 숫자 아래에 있고 나머지 절반이 위에 있는 지점을 나타내는 값입니다. 마찬가지로 20번째 백분위수는 데이터셋의 숫자 중 20%가 해당 숫자보다 아래에 있음을 의미합니다.
다음과 같은 (정렬된) 집합이 주어졌다고 가정해 봅시다:
20번째 백분위수를 구하려면 다음과 같이 계산합니다: 집합에는 10개의 값이 있으며, 원하는 값은 위치 1(20.0 / 100 * 10 - 1)에 있습니다. 배열 인덱스가 0부터 시작하기 때문입니다. 이 배열은 짝수 개의 항목을 포함하므로, 인덱스 2와 인덱스 + 1인 3 사이의 평균을 계산해야 합니다. 그 결과 20번째 백분위수는 150이 됩니다.
매우 단순한 형태의 Ruby 구현은 다음과 같습니다:
이 percentile 함수는 앞서 살펴본 median 계산과 매우 유사해 보이며, 실제로 median은 50번째 백분위수와 동일합니다.
AppSignal은 위와 같은 통계들을 활용해 애플리케이션의 성능 메트릭을 생성합니다. 단순히 평균에만 의존하는 것이 아니라 90번째와 95번째 백분위수를 함께 계산하여 이상치를 드러내고, 요청 분포를 더 정확하게 파악할 수 있도록 돕습니다. 자세한 내용은 성능 투어 페이지에서 확인하실 수 있습니다.
알아두면 좋은 특이 사항
백분위수와 평균의 계산 방식 특성상, 때때로 90번째 백분위수가 평균보다 아래로 떨어지는 경우가 발생할 수 있습니다. 예를 들어 다음과 같은 데이터셋을 보겠습니다:
이 경우 평균은 182.73이지만, 90번째 백분위수는 겨우 1입니다.
메트릭 수집 시스템이 90번째 백분위수와 평균만 보여준다 하더라도, 90번째 백분위수가 평균보다 낮게 나타난다면 데이터셋 어딘가에 큰 이상치가 존재한다는 사실을 추론할 수 있습니다.
👋 이 글이 도움이 되셨다면, Ruby(on Rails) 성능에 관한 더 많은 글들도 준비되어 있습니다. Ruby 성능 모니터링 체크리스트도 함께 확인해 보세요.
글의 끝에 거의 다 왔습니다
오늘은 여기까지입니다! 다음 포스트에서는 Quantiles를 활용해 모든 고객의 요청에 대한 백분위수를 효율적으로 저장하고 계산하는 방법을 다룰 예정입니다. 통계 및 APM, 에러 추적, 성능 모니터링에 대해 궁금한 점이나 의견이 있으시면 Twitter @AppSignal 또는 이메일로 연락해 주세요.
Robert Beekman
공동 창업자인 Robert는 AppSignal의 첫 번째 커밋을 작성한 인물입니다. 또한 고객 지원의 롤모델이며 코드의 세세한 디테일까지 모두 파악하고 있습니다. 여행과 사진 촬영(동시에!)을 즐깁니다.
Robert Beekman의 모든 글 보기