🔥 Metric: 숫자 하나가 찍히는 과정
강의 목차
![16:9 가로 일러스트레이션. 가운데에 가로축 'Time'과 세로축 'Value'의 좌표 그리드가 놓여 있고 그 위로 에메랄드 그린과 앰버 톤의 작은 점들을 시간 버킷마다 흩어 표시한다. 그리드 왼쪽에는 슬레이트 그레이 둥근 알약 세 개가 세로로 쌓여 'Namespace', 'MetricName', 'Dimensions[]' 라벨을 달고 있고, 각 알약에서 가는 에메랄드 그린 연결선이 그리드로 들어간다. 그리드 위쪽에는 'PutMetricData → 1 point' 라벨, 아래쪽에는 'Sum / Avg / Min / Max' 시간 버킷 마커가 적혀 있는 흰 배경, 둥근 모서리, 절제된 그림자의 프로페셔널 IT 표지](https://static.codingmax.net/images/courses/b5510c0b9aa561935b6a55bbbc8b3b44.avif)
같은 커스텀 메트릭을 두 번 찍었다고 생각한 적이 있다. 첫 호출에 dimension 값을 service=order 한 줄로 보냈고, 두 번째 호출에는 손이 미끄러져 service=Order로 적었다. 콘솔의 Metrics 트리를 열었더니 같은 메트릭 이름 아래 점이 두 곳에 떠 있었다. 한 점은 service=order 줄에, 다른 한 점은 service=Order 줄에. 한 글자 차이로 같은 메트릭이 두 메트릭이 됐다.
CloudWatch란 무엇인가: AWS 모니터링의 중심 도구에서 메트릭의 형태를 한 줄로 정의했다. namespace + dimension + 시간순 숫자. 그 한 줄을 풀면 점이 어떻게 나뉘는지, 같은 좌표 위에서 어떻게 통계로 모이는지, 시간이 지나면 AWS가 해상도를 어떻게 다시 묶는지가 차례로 드러난다.
namespace, MetricName, Dimensions = 좌표 키
![16:9 가로 다이어그램. 왼쪽 슬레이트 그레이 'Coordinate Key' 패널 안에 에메랄드 그린 알약 세 개를 세로로 쌓아 둔다. 위에서 아래로 'Namespace: App/Order', 'MetricName: Latency', 'Dimensions[] (메트릭당 30개까지): service=order, region=ap-northeast-2, env=prod' 세 묶음. 가운데에 '키로 매핑' 라벨이 붙은 화살표가 오른쪽으로 향하고, 오른쪽에는 옅은 옐로우 톤 '시간 버킷 위 점들' 패널 안에 가로 시간축과 세로 Value 축을 그어 둔 그리드가 있고, period 1, 2, 3, 4 네 칸에 에메랄드와 앰버 점들을 흩어 둔다. 아래쪽 슬레이트 캡션 'Namespace, MetricName, Dimensions 조합이 좌표 키. AWS는 같은 좌표 위에 시간순으로 (Value, Timestamp) 점을 쌓는다.'를 적은 흰 배경, 둥근 모서리, 절제된 그림자의 프로페셔널 IT 다이어그램](https://static.codingmax.net/images/courses/275fac2f2f1349bf21b452211e7da723.avif)
PutMetricData 한 호출은 네 가지를 함께 보낸다. Namespace 한 줄, MetricData[] 배열, 그 배열의 각 항목인 MetricDatum이 가진 MetricName, Dimensions[], Value, Timestamp, Unit, StorageResolution. 이 중 좌표 키 역할을 하는 건 셋이다. namespace, MetricName, dimensions의 값까지 포함한 조합. 이 조합이 한 글자라도 다르면 CloudWatch는 별도 메트릭으로 잡는다. 글자 단위 식별이라 service=order와 service=Order가 다른 좌표인 이유가 그 때문이다.
dimension 한도는 메트릭당 30개. 한 메트릭 데이터 포인트에 dimension을 30개까지 붙일 수 있다는 뜻이다. 이름과 값 페어 하나가 다르면 그 자체로 새 좌표가 되니, 30개를 다 채우려면 그 메트릭이 정말 그만큼 여러 축으로 나뉘어야 한다는 의미다. 페이로드 한도는 따로 정해져 있다. 한 PutMetricData 호출은 1 MB 안에 들어가야 하고, MetricData[]에는 최대 1,000개 항목이 들어간다. 이 한도들은 한 호출의 한도이지 한 메트릭의 한도가 아니다.
Timestamp은 자유롭게 적을 수 있는 게 아니다. AWS는 과거 2주 이내, 미래 2시간 이내 윈도 안에서만 받는다. 이 윈도가 의미하는 건 두 가지다. 수집기가 잠시 멈췄다 다시 붙으면 잃었던 점을 뒤늦게 같은 좌표로 채워 넣을 수 있다는 점, 그리고 24시간을 넘긴 timestamp로 보낸 점이 콘솔에 나타나려면 최소 48시간이 더 필요하다는 단서가 함께 따라붙는다는 점.

같은 좌표에 점이 여러 개 들어오면

같은 좌표(namespace × MetricName × dimensions)에 여러 점이 들어오면, CloudWatch는 그 점들을 period 단위 버킷에 모은다. 버킷 안에서 통계를 자동으로 계산한다. SampleCount(점 개수), Sum(합), Minimum(최솟값), Maximum(최댓값), Average. Average에는 정의식이 있다. Average = Sum / SampleCount. 그래서 같은 메트릭에서 Sum과 Average를 동시에 보면 그 둘은 분리된 숫자가 아니라 같은 데이터의 두 관점이다.
Percentile은 단서가 따라온다. 점들의 원본 분포를 알고 있어야 p95와 p99를 정확히 계산할 수 있어서, 클라이언트가 raw Value를 발행하거나 Values + Counts 배열을 보낼 때만 percentile이 가능하다. 클라이언트가 미리 계산한 StatisticSet(Sum, Min, Max, SampleCount만 들고 들어오는 형태)을 보내면 일반적으로 percentile을 못 얻는다. 단 SampleCount=1이거나 Min=Max인 특수 케이스에서는 점들의 분포가 사실상 결정되니 거기서만 가능하다. percentile이 필요하면 raw로 넣어야 한다는 한 줄을 단단히 정해 두면 된다.
Values + Counts는 한 호출에 한 메트릭당 최대 150개 값을 묶어 보낼 수 있다. 100명 사용자에게 응답한 latency 100점을 한 호출에 묶고 그 좌표에 percentile을 걸면, CloudWatch는 점의 분포 위에 p95와 p99를 다시 계산한다. 그게 raw로 발행하는 이유다.
Standard 60s vs High-resolution 1s

StorageResolution 파라미터는 두 값을 받는다. 60이면 standard(분 단위), 1이면 high-resolution(1초 단위). 기본값은 60. 1로 켜면 AWS는 발행 해상도를 1초 단위까지 허용하고, 조회 옵션도 1초, 5초, 10초, 30초 또는 60초의 배수로 폭이 더 넓다. 그제서야 분 단위 미만 조회를 쓸 수 있다.
비용 이야기가 그 옆에 따라온다. 메트릭 한 개의 단가는 standard와 high-resolution 모두 동일하게 첫 1만 개까지 $0.30/월이다. 단가가 같다면 해상도를 항상 1로 켜면 되지 않냐고 생각할 수 있는데, 거기서 비용이 새기 시작한다. 1초 해상도를 진짜 보려면 1초마다 점을 찍어야 한다. PutMetricData는 호출 1,000건당 $0.01이 따로 붙는다. 한 메트릭을 1초마다 찍으면 한 달에 약 260만 호출이 나가고, $0.01/1,000 단가만 계산해도 호출 비용이 약 $26(무료 한도 1M 호출을 빼면 약 $16) 단위로 메트릭 단가 위에 얹는다. 메트릭의 외형은 같지만 그 메트릭이 부르는 청구서의 무게는 해상도 결정 한 줄에 따라 다르다.
시간이 지나면 AWS가 해상도를 다시 묶는다

같은 좌표 위에 점이 쌓인 다음에는 보존 일정이 자동으로 진행한다. 공식 문서가 명시하는 형태는 이렇다.
- 60초 미만 해상도(high-res 1초): 3시간 보존
- 60초 해상도(standard 1분): 15일 보존
- 5분(300초) 해상도: 63일 보존
- 1시간(3600초) 해상도: 455일 ≈ 15개월 보존
여기서 자주 미끄러지는 건 보존 기간이라는 표현이다. AWS는 데이터를 지우지 않고 해상도만 거칠게 다시 만든다. 1분으로 찍은 점도 15일이 지나면 5분 해상도로만 조회할 수 있고, 다시 63일이 지나면 AWS는 1시간 해상도만 노출한다. 점들의 raw 묶음을 같은 좌표 위에서 더 큰 시간 버킷으로 다시 묶는 구조다.
이 타임라인이 정해 두는 한 줄은 보고 싶은 시간 윈도가 곧 발행 해상도를 결정한다 정도다. 분기 단위 추세가 필요한 메트릭을 1초로 찍을 이유가 없다. 1시간 해상도에 자동으로 모일 항목이니까. 반대로 어떤 사고를 분 단위 미만으로 다시 보고 싶다면 그 사고가 벌어지는 동안 1초로 찍어 두지 않으면 사후에 고해상도 데이터가 없다.
언제 이걸 쓰지 말아야 하는가
세 가지 경우에 CloudWatch metric이 정답이 아니다.
첫째, 좌표 카드가 폭발하는 경우. dimension 값에 사용자 ID, 요청 ID, 타임스탬프 같은 고유 식별자를 넣으면 CloudWatch는 한 메트릭을 백만 단위로 나눈다. AWS 문서가 고유성이 큰 필드는 dimension에 넣지 말라고 분명히 적은 이유다. 그럴 때는 메트릭이 아니라 Logs Insights나 OpenSearch 쪽이 더 맞는다.
둘째, 분 단위 미만 해상도가 상시로 필요한 경우. 1초 해상도를 메트릭마다 별도 호출로 100개를 켜면 호출 비용만 한 달에 $1,500–$2,500 사이로 자란다(무료 한도 흡수 여부에 따라). 짧은 사고 분석 구간이 아니라 상시 1초 해상도가 필요하다면, 메트릭으로 보지 말고 Prometheus, OpenTelemetry 쪽 timeseries DB를 별도로 두는 편이 맞는다.
셋째, latency p99와 p999가 사용자 SLO를 직접 결정하는 경우. Values + Counts가 한 호출에 150값까지인 한도와 raw 발행을 강제하는 단서를 같이 보면, 트래픽이 큰 시스템의 latency p99를 정밀하게 보려면 클라이언트 쪽이 발행 부담을 그대로 진다. 그럴 때는 Datadog, New Relic 같은 APM 도구가 자기 SDK 안에서 raw를 들고 있다가 직접 percentile을 계산해 보내는 구조라 더 잘 맞는다.
한 점이 만든 좌표가 청구서를 만든다
CloudWatch에서 한 메트릭의 무게는 점의 개수가 아니라 좌표의 카드에 따라 정한다. namespace 한 줄, MetricName 한 줄, dimensions 30개 한도의 조합이 만드는 키가 곧 청구 단위이고, 그 키 위에서 점이 쌓이는 해상도(60s 또는 1s)가 호출 비용과 보존 윈도를 함께 결정한다. 한 점을 찍기 전에 좌표 카드와 발행 해상도 두 줄을 미리 정해 두면 청구서는 그 두 줄을 따라간다.
참고 자료
- PutMetricData: Amazon CloudWatch API Reference: 요청 구조와 페이로드 한도(1 MB, 1,000 metrics, 30 dimensions)
- MetricDatum: Amazon CloudWatch API Reference: 한 점의 필드 정의(Value, Values+Counts, StatisticSet, StorageResolution)
- Amazon CloudWatch concepts: User Guide: 자동 다운샘플 retention timeline (3시간 / 15일 / 63일 / 15개월)
- CloudWatch statistics definitions: Sum / Avg / Min / Max / SampleCount / Percentile 정의
- Publishing custom metrics: User Guide: StorageResolution과 raw vs StatisticSet 발행 가이드
- Amazon CloudWatch Pricing: 메트릭 단가($0.30/월), PutMetricData $0.01/1,000 호출












