> ## Documentation Index
> Fetch the complete documentation index at: https://docs.meterengine.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 미터 설계

> 이벤트에서 청구할 수량을 뽑는 미터를 정의하고, 이벤트가 들어온 뒤에도 안전하게 운영하는 방법을 설명합니다.

미터는 "어떤 이벤트의 어떤 값을 어떻게 모아 한 달 청구 수량으로 만들지"를 정한 규칙입니다. 미터만으로는 금액이 생기지 않습니다. 미터에 단가를 등록해야 청구 예정액에 금액이 잡힙니다. 단가는 [단가 모델과 버전](https://docs.meterengine.com/pricing/price-models.md)에서 다룹니다.

고객, 미터, 단가는 업무 환경마다 따로 만들어야 합니다. 테스트 환경에서 만든 미터는 실제 환경에 없습니다. 자세한 내용은 [업무 환경](https://docs.meterengine.com/concepts/environments.md)을 보세요.

> [!WARNING]
>
> 이벤트가 한 건이라도 들어온 미터는 이름만 바꿀 수 있습니다. 보낸 이벤트는 지울 수 없으므로 시험 삼아 보낸 이벤트 한 건으로도 계산 기준이 굳습니다. 이 페이지의 설계 순서대로 정의를 확정한 뒤 이벤트를 보내세요.

## 이벤트와 미터의 대응

이벤트 하나에서 여러 미터가 각자 수량을 뽑을 수 있습니다. 아래는 AI 서비스가 생성 요청 한 번마다 보내는 이벤트입니다.

```json 이벤트 예시 theme={null}
{
  "transaction_id": "gen_20261105_000123",
  "customer_id": "8b0f4c2e-6d1a-4f3b-9a57-2c4e8d9b1f60",
  "type": "llm_generation",
  "timestamp": "2026-11-05T10:00:00+09:00",
  "properties": {
    "model": "lumen-fast",
    "input_tokens": 1200,
    "output_tokens": 800
  }
}
```

이 이벤트 하나로 아래 미터를 모두 만들 수 있습니다.

| 미터 code | 이벤트의 `type` | 집계 | 집계 대상 속성 | 차원 키 | 과금 단위 크기 |
| - | - | - | - | - | - |
| `llm-input-tokens` | `llm_generation` | `sum` | `input_tokens` | `model` | 1000 |
| `llm-output-tokens` | `llm_generation` | `sum` | `output_tokens` | `model` | 1000 |
| `llm-requests` | `llm_generation` | `count` | 없음 | `model` | 1 |

* 미터의 `event_type`과 이벤트의 `type`이 글자까지 같아야 그 미터에 집계됩니다. 대소문자도 구분합니다.
* 같은 이벤트와 같은 속성으로 미터를 여러 개 만들 수 있고, 각 미터에 단가를 등록하면 각각 청구됩니다.
* 차원 키(`dimension_properties`)는 속성 값마다 다른 단가를 매길 때 씁니다. 위 예시는 `model` 값마다 단가가 다릅니다. 차원은 [차원과 속성 조합](https://docs.meterengine.com/pricing/dimensions.md)에서 자세히 다룹니다.

## 미터 필드

| 필드 | 필수 | 설명 |
| - | - | - |
| `code` | 예 | 미터를 가리키는 값입니다. API 경로에 들어가며 등록한 뒤 바꿀 수 없습니다. 같은 업무 환경 안에서 유일해야 하고, `search`, `options`, `export`는 쓸 수 없습니다. |
| `name` | 예 | 콘솔과 인보이스에 보이는 이름입니다. 언제든 바꿀 수 있습니다. |
| `event_type` | 예 | 집계할 이벤트의 `type` 값입니다. |
| `aggregation` | 예 | 집계 방식입니다. 아래 표를 보세요. 대소문자를 구분하지 않습니다. |
| `target_property` | `count` 외에는 예 | 이벤트 `properties`에서 집계할 키입니다. `count`에는 넣지 않습니다. |
| `dimension_properties` | 아니요 | 단가를 가르는 `properties` 키의 배열입니다. 생략하면 빈 배열입니다. 같은 키를 두 번 넣거나 빈 키를 넣을 수 없습니다. |
| `unit_size` | 아니요 | 집계량을 나눌 양수입니다. 기본값은 1입니다. |
| `rounding_mode` | 아니요 | `none`, `ceil`(올림), `floor`(내림), `round`(반올림, 사사오입) 가운데 하나입니다. 기본값은 `none`입니다. |
| `rounding_precision` | 아니요 | 반올림할 소수 자릿수 0\~12입니다. 기본값은 0이고, `rounding_mode`가 `none`이면 0으로 저장됩니다. |
| `percentile` | `percentile`만 예 | 0\~100의 백분위입니다. `percentile` 집계에만 넣습니다. |

> [!TIP]
>
> `code`는 URL 경로에 그대로 들어가므로 영문 소문자, 숫자, 하이픈으로 정해 두면 다루기 쉽습니다.

> [!NOTE]
>
> 콘솔의 미터 등록 창과 API 참조에는 code가 도입사 안에서 유일하다고 적혀 있지만, 실제로는 업무 환경마다 따로 정합니다. 테스트 환경과 실제 환경에 같은 code의 미터를 만들 수 있습니다.

## 집계 방식

한 달 동안 한 고객이 보낸 이벤트를 차원 값 조합마다 모아 아래 방식으로 계산합니다.

| `aggregation` | 콘솔 표기 | 계산 | 집계에 드는 값 |
| - | - | - | - |
| `sum` | 합계 (SUM) | 값을 모두 더합니다 | JSON number |
| `count` | 이벤트 수 (COUNT) | 이벤트 건수를 셉니다 | 속성과 관계없이 모든 이벤트 |
| `count_distinct` | 고유 값 수 (COUNT DISTINCT) | 중복을 뺀 값의 수를 셉니다 | JSON 문자열, number, 불리언 |
| `min` | 최솟값 (MIN) | 가장 작은 값 | JSON number |
| `max` | 최댓값 (MAX) | 가장 큰 값 | JSON number |
| `avg` | 평균 (AVG) | 산술평균 | JSON number |
| `first` | 첫 값 (FIRST) | `timestamp`가 가장 이른 이벤트의 값. 시각이 같으면 `transaction_id` 순서 | JSON number |
| `last` | 마지막 값 (LAST) | `timestamp`가 가장 늦은 이벤트의 값. 시각이 같으면 `transaction_id` 순서 | JSON number |
| `median` | 중앙값 (MEDIAN) | 50백분위 값 | JSON number |
| `percentile` | 백분위수 (PERCENTILE) | 지정한 백분위의 값. 두 값 사이면 선형 보간 | JSON number |

> [!WARNING]
>
> 숫자로 집계하는 미터는 값이 JSON number일 때만 셉니다. `"input_tokens": "1200"`처럼 문자열로 보내거나 키를 빠뜨린 이벤트는 저장은 되지만 집계에서 빠지고 오류 응답도 오지 않습니다. 이런 이벤트는 `GET /v1/unbilled-events`의 `non_numeric_value`와 콘솔 '이벤트 매핑 진단' 화면의 '집계할 수량 확인 필요'에 나타납니다. 연동 코드에서 숫자 값을 number로 고정해 보내세요.

## 단위 크기와 반올림

집계량은 `unit_size`로 나눈 뒤 `rounding_mode`와 `rounding_precision`을 적용해 청구 수량이 됩니다. 이 변환은 이벤트마다가 아니라 **한 달, 한 고객, 한 차원 값 조합의 집계 결과에 한 번** 적용합니다.

예를 들어 `unit_size` 1000, `rounding_mode` `ceil`, `rounding_precision` 0인 토큰 미터에 한 고객이 같은 모델로 400토큰 이벤트를 세 번 보내면 다음과 같습니다.

* 집계량: 400 + 400 + 400 = 1200
* 청구 수량: 1200 / 1000 = 1.2, 올림하면 2

이벤트마다 올림했다면 3이 되었겠지만 MeterEngine은 합친 뒤 한 번만 올립니다. 반대로 같은 고객이 서로 다른 모델 두 개로 400토큰씩 보내면 조합마다 따로 올려 각각 1, 합해서 2가 됩니다.

`rounding_mode`가 `none`이면 나눗셈 결과를 반올림하지 않고 정확한 분수로 유지해 금액을 계산합니다. 1/3처럼 끝나지 않는 소수도 근사하지 않습니다.

## 미터 만들기

**1. 이벤트 형식을 먼저 정한다**

연동 코드가 보낼 `type`, 집계할 숫자 속성, 단가를 가를 속성을 정합니다. 숫자 값은 JSON number로, 차원 값은 문자열이든 숫자든 한 가지 타입으로 고정합니다.

**2. 미터를 등록한다**

콘솔 또는 API로 등록합니다. 필요 권한은 admin입니다([화면과 API, 권한](https://docs.meterengine.com/authentication/scopes.md)).

> [tab] 콘솔
>
> 1. 화면 위 '업무 환경'이 원하는 환경(예: '테스트 환경')인지 확인합니다.
> 2. 왼쪽 메뉴 '미터' 화면에서 '미터 등록'을 누릅니다.
> 3. '코드', '이름', '이벤트 타입', '집계 함수'를 넣고, 집계 함수가 '이벤트 수 (COUNT)'가 아니면 '집계 대상 속성'을 넣습니다.
> 4. 단가를 속성 값마다 다르게 매길 거라면 '차원 키'에 키를 쉼표로 나눠 넣습니다.
> 5. '결과 변환'에서 '과금 단위 크기'와 '반올림 방식'을 정합니다. 창 아래 '계산 미리보기'로 식을 확인합니다.
> 6. '저장'을 누릅니다.

> [tab] API
>
> ```bash curl theme={null}
> curl -X POST https://meterengine.com/v1/billable-metrics \
>   -H "Authorization: Bearer me_test_xxxxxxxx" \
>   -H "Content-Type: application/json" \
>   -H "Idempotency-Key: create-metric-llm-input-tokens" \
>   -d '{
>     "code": "llm-input-tokens",
>     "name": "입력 토큰",
>     "event_type": "llm_generation",
>     "aggregation": "sum",
>     "target_property": "input_tokens",
>     "dimension_properties": ["model"],
>     "unit_size": 1000,
>     "rounding_mode": "none",
>     "rounding_precision": 0
>   }'
> ```
>
> ```javascript Node theme={null}
> const response = await fetch("https://meterengine.com/v1/billable-metrics", {
>   method: "POST",
>   headers: {
>     Authorization: "Bearer me_test_xxxxxxxx",
>     "Content-Type": "application/json",
>     "Idempotency-Key": "create-metric-llm-input-tokens",
>   },
>   body: JSON.stringify({
>     code: "llm-input-tokens",
>     name: "입력 토큰",
>     event_type: "llm_generation",
>     aggregation: "sum",
>     target_property: "input_tokens",
>     dimension_properties: ["model"],
>     unit_size: 1000,
>     rounding_mode: "none",
>     rounding_precision: 0,
>   }),
> });
> const metric = await response.json();
> ```
>
> 성공하면 201과 함께 등록한 미터가 옵니다.
>
> ```json 201 응답 theme={null}
> {
>   "code": "llm-input-tokens",
>   "name": "입력 토큰",
>   "event_type": "llm_generation",
>   "aggregation": "sum",
>   "target_property": "input_tokens",
>   "dimension_properties": ["model"],
>   "archived_at": null,
>   "active_from": "0001-01",
>   "inactive_from": null,
>   "unit_size": 1000,
>   "rounding_mode": "none",
>   "rounding_precision": 0,
>   "percentile": null,
>   "version": 0
> }
> ```
>
> `Idempotency-Key`는 선택입니다. 네트워크 오류로 다시 보낼 때 같은 키와 같은 본문을 쓰면 처음 응답을 그대로 돌려받습니다. 규칙은 [멱등 규칙](https://docs.meterengine.com/api-basics/idempotency.md)을 보세요.

**3. 단가를 등록한다**

미터만 있고 단가가 없으면 청구 예정액이 0원입니다. [단가 모델과 버전](https://docs.meterengine.com/pricing/price-models.md)에서 이번 달 단가를 등록합니다.

**4. 시험 이벤트로 수량을 확인한다**

테스트 환경에 이벤트를 보내고 `GET /v1/usage?month=YYYY-MM`나 콘솔 '사용량' 화면에서 미터별 수량을 확인합니다. 확인 방법은 [보낸 데이터 확인하기](https://docs.meterengine.com/integration/verify-usage.md)를 보세요. 이 이벤트를 받은 뒤부터 그 업무 환경에서 이 미터는 이름만 바꿀 수 있습니다.

미터를 등록하기 전에 받은 같은 `type`의 이벤트도 미터를 만든 뒤에는 함께 집계됩니다. 다만 단가는 이번 달 이후로만 넣을 수 있어([시간 규칙](https://docs.meterengine.com/concepts/time-rules.md)) 아직 확정하지 않은 지난달의 사용량은 단가 없는 사용량으로 남고 청구되지 않습니다.

## 미터 오류

| 상태 | `code` | 원인 |
| - | - | - |
| 400 | `validation_error` | `code`, `name`, `event_type`, `aggregation` 가운데 빈 값이 있거나 `rounding_precision`이 0\~12 밖입니다. 수정 요청에서 `If-Match`가 큰따옴표로 감싼 version이 아니거나 `PATCH`에 `If-Match`가 없어도 이 code이며, 이때 `errors`의 `field`는 `If-Match`입니다. |
| 400 | `invalid_billable_metric` | 집계 방식과 대상 속성, 단위, 반올림, 백분위 설정이 맞지 않거나, 차원 키에 중복이나 빈 키가 있거나, `code`가 `search`, `options`, `export`입니다. 어느 필드인지는 `errors`에 있습니다. |
| 409 | `billable_metric_already_exists` | 같은 업무 환경에 같은 `code`의 미터가 있습니다. |
| 409 | `idempotency_conflict` | 같은 `Idempotency-Key`를 다른 본문에 썼습니다. |
| 409 | `billable_metric_has_events` | 이벤트가 있는 미터의 계산 기준을 바꾸려 했습니다. |
| 409 | `resource_archived` | 보관한 미터를 고치려 했습니다. 복원한 뒤 고칩니다. |
| 409 | `resource_version_conflict` | `If-Match`의 version이 지금 version과 다릅니다. 다시 조회한 뒤 고칩니다. |
| 404 | `billable_metric_not_found` | 그런 `code`의 미터가 이 업무 환경에 없습니다. |

오류 응답 형식과 다시 보낼지 판단하는 기준은 [오류 응답과 X-Request-ID](https://docs.meterengine.com/api-basics/errors.md)를 보세요.

## 이벤트가 들어온 뒤의 수정

`PUT /v1/billable-metrics/{code}`는 미터를 요청 본문으로 통째로 덮어씁니다. 본문에서 빠뜨린 선택 필드는 기본값으로 돌아가므로, `GET /v1/billable-metrics/{code}`로 받은 값을 그대로 두고 바꿀 필드만 고쳐 보내세요. 다른 사람과 동시에 고칠 때는 `GET /v1/billable-metrics/{code}` 응답의 `version`을 큰따옴표로 감싸(예: `"3"`) `If-Match` 헤더에 넣고 `PATCH /v1/billable-metrics/{code}`를 쓰면, 그 사이 다른 수정이 있었을 때 409 `resource_version_conflict`로 막힙니다. `GET` 응답의 `ETag` 헤더 값이 이 형식이라 그대로 넣어도 됩니다.

같은 업무 환경에 미터의 `event_type` 이벤트가 한 건이라도 있으면 `name`만 바꿀 수 있습니다. `event_type`, `aggregation`, `target_property`, `dimension_properties`, `unit_size`, `rounding_mode`, `rounding_precision`, `percentile`을 바꾸면 409 `billable_metric_has_events`입니다. 미터를 만들기 전에 받은 이벤트도 여기에 포함됩니다.

이름만 바꿀 때도 나머지 필드를 지금 값 그대로 함께 보내세요. 예를 들어 `dimension_properties`를 빠뜨리면 빈 배열로 바꾸는 요청이 되어, 이벤트가 있는 미터에서는 409 `billable_metric_has_events`로 거절됩니다.

보낸 이벤트는 고치거나 지울 수 없습니다. 잘못 보낸 값을 바로잡는 방법은 [사용량 이벤트 보내기](https://docs.meterengine.com/integration/events.md)를 보세요.

## 계산 기준을 바꿔야 할 때

이벤트가 들어온 미터의 계산 기준은 바꿀 수 없으므로 새 미터를 만들고 옛 미터를 보관합니다. 미터를 보관하면 이번 달까지는 집계하고 다음 달부터 멈춥니다. 그래서 바꾸는 달에는 옛 미터와 새 미터가 같은 이벤트를 함께 셉니다. 두 번 청구되지 않도록 새 미터는 다음 달부터 금액이 생기게 합니다.

**1. 지난달 확정을 먼저 끝낸다**

새 미터는 만들기 전에 받은 이벤트까지 집계하므로, 아직 확정하지 않은 지난달에도 새 미터의 사용량이 단가 없는 사용량으로 나타납니다. 지난달 단가는 넣을 수 없으니 지난달 인보이스를 확정한 뒤에 바꿉니다([인보이스 수동 확정](https://docs.meterengine.com/billing/finalize-invoices.md)).

**2. 새 미터를 만든다**

새 `code`로 바뀐 계산 기준의 미터를 등록합니다. `event_type`은 옛 미터와 같아도 됩니다.

**3. 새 미터에 0원 버전과 실제 단가 버전을 등록한다**

이번 달을 시작 월로 하는 0원 단가와, 다음 달을 시작 월로 하는 실제 단가를 등록합니다(예: 이번 달이 2026-11이면 `effective_from` `2026-11`에 0원, `2026-12`에 실제 단가). 이번 달에 단가를 비워 두면 새 미터의 사용량이 단가 없는 사용량으로 잡혀 청구 예정액의 `unpriced_usages`와 '이벤트 매핑 진단' 화면에 나타납니다. 자동 확정이 켜진 서버에서는 청구 예정액이 0원이 아닌 그 고객의 자동 확정도 보류됩니다. 0원 단가를 두면 이번 달 새 미터 라인은 0원으로 계산됩니다.

**4. 옛 미터를 보관한다**

'미터' 화면의 '보관' 또는 `DELETE /v1/billable-metrics/{code}`로 보관합니다. 옛 미터는 이번 달까지 청구되고 다음 달부터 집계에서 빠집니다.

보관과 복원의 세부 규칙은 [바꾸기, 보관, 복원](https://docs.meterengine.com/pricing/changes-and-archiving.md)을 보세요.

## 다음 단계

> [card] [차원과 속성 조합](https://docs.meterengine.com/pricing/dimensions.md)
>
> 속성 값마다 다른 단가를 매기는 방법

> [card] [단가 모델과 버전](https://docs.meterengine.com/pricing/price-models.md)
>
> 미터에 단가를 등록하는 방법


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.