Skip to content
Published on

Prometheus 운영 총정리 — 메트릭 수집, PromQL, 알림, 대시보드, 모범사례

Authors
  • Name
    Twitter

들어가며

현대 인프라는 마이크로서비스, 컨테이너, 서버리스로 분산되면서 수백 개의 구성 요소가 동시에 동작한다. 이런 환경에서 "지금 시스템이 정상인가?"라는 질문에 답하려면 체계적인 메트릭 수집과 모니터링이 필수다. Prometheus는 CNCF 졸업 프로젝트로서 Kubernetes 생태계의 사실상 표준 모니터링 시스템이며, 풀(pull) 기반 메트릭 수집, 강력한 쿼리 언어 PromQL, 유연한 알림 시스템을 제공한다.

이 글에서는 Prometheus의 핵심 개념부터 아키텍처, 설치 및 설정, PromQL 실전 쿼리, Alertmanager 알림 구성, Grafana 대시보드 연동, 대규모 환경 운영 모범사례, 운영 체크리스트, 그리고 흔한 실수까지 한 글에 총정리한다. 프로덕션 환경에서 즉시 적용할 수 있는 실전 중심의 가이드다.


1. 핵심 개념

Pull 기반 모델

Prometheus는 모니터링 대상이 메트릭을 push하는 방식이 아니라, Prometheus 서버가 주기적으로 대상의 /metrics 엔드포인트를 스크레이프(scrape) 하는 pull 기반 모델을 사용한다. 이 방식의 장점은 다음과 같다.

  • 모니터링 대상이 Prometheus의 존재를 알 필요가 없다
  • 서비스 디스커버리와 결합하여 동적 환경에 자연스럽게 대응한다
  • 대상이 다운되면 스크레이프 실패로 즉시 감지된다

시계열 데이터 (Time Series)

Prometheus는 모든 데이터를 시계열(time series) 로 저장한다. 각 시계열은 메트릭 이름과 키-값 쌍의 레이블 조합으로 고유하게 식별된다.

http_requests_total{method="GET", handler="/api/users", status="200"}

메트릭 타입

타입설명사용 예시PromQL 사용
Counter단조 증가하는 누적 값요청 수, 에러 수rate(), increase()
Gauge증감 가능한 현재 값CPU 사용률, 메모리, 온도직접 사용, avg_over_time()
Histogram값의 분포를 버킷별로 측정응답 시간, 요청 크기histogram_quantile()
Summary클라이언트 측에서 quantile 계산응답 시간 (집계 불가)직접 사용 (비권장)

Histogram vs Summary: Histogram은 서버 측에서 quantile을 계산할 수 있어 여러 인스턴스의 집계가 가능하다. Summary는 클라이언트에서 계산하므로 집계가 불가능하다. 대부분의 경우 Histogram을 권장한다.

2. 아키텍처

Prometheus 생태계는 여러 구성 요소가 유기적으로 연결된다.

graph TB
    subgraph Targets
        A[Application /metrics]
        B[Node Exporter]
        C[cAdvisor]
        D[Custom Exporter]
    end

    subgraph "Prometheus Server"
        E[Retrieval<br/>스크레이프 엔진]
        F[TSDB<br/>시계열 데이터베이스]
        G[HTTP Server<br/>PromQL API]
    end

    H[Service Discovery<br/>Kubernetes / Consul / DNS]
    I[Pushgateway<br/>단기 작업용]
    J[Alertmanager<br/>알림 라우팅/그룹핑]
    K[Grafana<br/>대시보드]

    H --> E
    A --> E
    B --> E
    C --> E
    D --> E
    I --> E
    E --> F
    F --> G
    G --> K
    G --> J
    J --> L[Slack / PagerDuty / Email]
구성 요소역할
Prometheus Server메트릭 스크레이프, TSDB 저장, PromQL 쿼리 엔진
Exporters대상 시스템의 메트릭을 Prometheus 형식으로 노출 (node_exporter, mysqld_exporter 등)
Pushgateway단기 배치 작업 등 스크레이프가 어려운 대상의 메트릭을 중계
Alertmanager알림 규칙에 따른 알림 라우팅, 그룹핑, 중복 제거, 억제
Service DiscoveryKubernetes, Consul, DNS 등에서 스크레이프 대상을 동적으로 발견

3. 설치 및 설정

Docker Compose로 전체 스택 구성

# docker-compose.yml
version: '3.8'

services:
  prometheus:
    image: prom/prometheus:v2.53.0
    container_name: prometheus
    ports:
      - '9090:9090'
    volumes:
      - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
      - ./prometheus/rules/:/etc/prometheus/rules/
      - prometheus_data:/prometheus
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.path=/prometheus'
      - '--storage.tsdb.retention.time=30d'
      - '--web.enable-lifecycle'
      - '--web.enable-admin-api'
    restart: unless-stopped

  alertmanager:
    image: prom/alertmanager:v0.27.0
    container_name: alertmanager
    ports:
      - '9093:9093'
    volumes:
      - ./alertmanager/alertmanager.yml:/etc/alertmanager/alertmanager.yml
    restart: unless-stopped

  grafana:
    image: grafana/grafana:11.1.0
    container_name: grafana
    ports:
      - '3000:3000'
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin
    volumes:
      - grafana_data:/var/lib/grafana
      - ./grafana/provisioning/:/etc/grafana/provisioning/
    restart: unless-stopped

  node-exporter:
    image: prom/node-exporter:v1.8.1
    container_name: node-exporter
    ports:
      - '9100:9100'
    restart: unless-stopped

volumes:
  prometheus_data:
  grafana_data:

prometheus.yml 설정

# prometheus/prometheus.yml
global:
  scrape_interval: 15s # 기본 스크레이프 간격
  evaluation_interval: 15s # 알림 규칙 평가 간격
  scrape_timeout: 10s # 스크레이프 타임아웃

alerting:
  alertmanagers:
    - static_configs:
        - targets:
            - alertmanager:9093

rule_files:
  - /etc/prometheus/rules/*.yml

scrape_configs:
  # Prometheus 자기 자신 모니터링
  - job_name: 'prometheus'
    static_configs:
      - targets: ['localhost:9090']

  # Node Exporter
  - job_name: 'node-exporter'
    static_configs:
      - targets: ['node-exporter:9100']

  # 애플리케이션 (relabel_configs 활용)
  - job_name: 'app'
    metrics_path: /metrics
    scheme: http
    static_configs:
      - targets: ['app:8080']
        labels:
          env: production
          team: backend

  # Kubernetes 서비스 디스커버리 (참고)
  - job_name: 'kubernetes-pods'
    kubernetes_sd_configs:
      - role: pod
    relabel_configs:
      - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
        action: keep
        regex: true
      - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
        action: replace
        target_label: __metrics_path__
        regex: (.+)
      - source_labels: [__meta_kubernetes_namespace]
        action: replace
        target_label: namespace
      - source_labels: [__meta_kubernetes_pod_name]
        action: replace
        target_label: pod

4. PromQL 실전

rate()와 irate()

# 5분간 초당 요청 비율 (알림에 적합)
sum(rate(http_requests_total[5m])) by (service)

# 순간 변화율 (대시보드 시각화에 적합)
sum(irate(http_requests_total[$__rate_interval])) by (service)

rate()는 알림 규칙에서, irate()는 대시보드에서 사용한다. rate()의 range window는 scrape_interval의 4배 이상으로 설정해야 한다.

histogram_quantile()

# 95번째 백분위수 응답 시간
histogram_quantile(0.95,
  sum(rate(http_request_duration_seconds_bucket[5m])) by (le, service)
)

# 50번째 백분위수 (중간값)
histogram_quantile(0.50,
  sum(rate(http_request_duration_seconds_bucket[5m])) by (le)
)

집계 연산자 (Aggregation Operators)

# 서비스별 에러율 상위 5개
topk(5,
  sum(rate(http_requests_total{status=~"5.."}[5m])) by (service)
  /
  sum(rate(http_requests_total[5m])) by (service)
)

# 네임스페이스별 메모리 사용량
sum(container_memory_usage_bytes{container!=""}) by (namespace)

# CPU 사용률이 80% 초과인 노드
node_cpu_seconds_total{mode="idle"} < 0.2

# predict_linear으로 디스크 용량 예측 (4시간 뒤)
predict_linear(
  node_filesystem_avail_bytes{mountpoint="/"}[6h], 4 * 3600
) < 0

유용한 함수 모음

함수용도예시
rate()시간 범위의 초당 평균 변화율rate(http_requests_total[5m])
irate()최근 두 점의 순간 변화율irate(http_requests_total[5m])
increase()시간 범위의 총 증가량increase(http_requests_total[1h])
histogram_quantile()히스토그램에서 분위수 계산histogram_quantile(0.99, ...)
predict_linear()선형 회귀로 미래 값 예측predict_linear(disk_free[6h], 3600*4)
absent()시계열이 없으면 1 반환absent(up{job="app"})
changes()시간 범위 내 값 변경 횟수changes(process_start_time_seconds[1h])

5. Alertmanager 알림 설정

알림 규칙 정의

# prometheus/rules/alerts.yml
groups:
  - name: instance-alerts
    rules:
      - alert: InstanceDown
        expr: up == 0
        for: 3m
        labels:
          severity: critical
        annotations:
          summary: '인스턴스 {{ $labels.instance }}가 다운되었습니다'
          description: '{{ $labels.job }} 작업의 {{ $labels.instance }}가 3분 이상 응답하지 않습니다.'

      - alert: HighErrorRate
        expr: |
          sum(rate(http_requests_total{status=~"5.."}[5m])) by (service)
          /
          sum(rate(http_requests_total[5m])) by (service)
          > 0.05
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: '{{ $labels.service }} 에러율 {{ $value | humanizePercentage }} 초과'
          description: '서비스 {{ $labels.service }}의 5xx 에러율이 5%를 초과했습니다.'

      - alert: HighMemoryUsage
        expr: |
          (1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) > 0.9
        for: 10m
        labels:
          severity: warning
        annotations:
          summary: '노드 메모리 사용률 90% 초과'

      - alert: DiskSpaceRunningOut
        expr: |
          predict_linear(node_filesystem_avail_bytes{mountpoint="/"}[6h], 24*3600) < 0
        for: 30m
        labels:
          severity: warning
        annotations:
          summary: '24시간 내 디스크 공간이 부족할 것으로 예측됩니다'

      - alert: HighLatencyP95
        expr: |
          histogram_quantile(0.95,
            sum(rate(http_request_duration_seconds_bucket[5m])) by (le, service)
          ) > 1.0
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: '{{ $labels.service }} P95 응답 시간 1초 초과'

Alertmanager 설정

# alertmanager/alertmanager.yml
global:
  resolve_timeout: 5m
  slack_api_url: 'https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK'

route:
  receiver: 'default-slack'
  group_by: ['alertname', 'service']
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 4h
  routes:
    - match:
        severity: critical
      receiver: 'pagerduty-critical'
      repeat_interval: 1h
    - match:
        severity: warning
      receiver: 'slack-warnings'
      repeat_interval: 4h

receivers:
  - name: 'default-slack'
    slack_configs:
      - channel: '#alerts'
        title: '{{ .GroupLabels.alertname }}'
        text: '{{ range .Alerts }}{{ .Annotations.summary }}{{ end }}'
        send_resolved: true

  - name: 'pagerduty-critical'
    pagerduty_configs:
      - service_key: 'YOUR_PAGERDUTY_SERVICE_KEY'
        severity: critical

  - name: 'slack-warnings'
    slack_configs:
      - channel: '#alerts-warning'
        send_resolved: true

inhibit_rules:
  - source_match:
      severity: critical
    target_match:
      severity: warning
    equal: ['alertname', 'instance']

6. Grafana 대시보드 연동

데이터 소스 프로비저닝

# grafana/provisioning/datasources/prometheus.yml
apiVersion: 1

datasources:
  - name: Prometheus
    type: prometheus
    access: proxy
    url: http://prometheus:9090
    isDefault: true
    editable: true
    jsonData:
      timeInterval: '15s'
      httpMethod: POST

핵심 대시보드 패널 예시

{
  "title": "Service Error Rate",
  "type": "timeseries",
  "datasource": "Prometheus",
  "targets": [
    {
      "expr": "sum(rate(http_requests_total{status=~\"5..\"}[5m])) by (service) / sum(rate(http_requests_total[5m])) by (service)",
      "legendFormat": "{{ service }}"
    }
  ],
  "fieldConfig": {
    "defaults": {
      "unit": "percentunit",
      "thresholds": {
        "steps": [
          { "value": 0, "color": "green" },
          { "value": 0.01, "color": "yellow" },
          { "value": 0.05, "color": "red" }
        ]
      }
    }
  }
}

권장 대시보드 패널 구성

패널PromQL용도
에러율sum(rate(http_requests_total{status=~"5.."}[5m])) by (service) / sum(rate(http_requests_total[5m])) by (service)서비스별 에러율 추이
QPSsum(rate(http_requests_total[5m])) by (service)초당 요청 수
P95 Latencyhistogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket[5m])) by (le, service))95번째 백분위수 응답 시간
CPU 사용률1 - avg(rate(node_cpu_seconds_total{mode="idle"}[5m])) by (instance)노드별 CPU 사용률
메모리 사용량1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes노드별 메모리 사용률
디스크 잔여량node_filesystem_avail_bytes{mountpoint="/"}루트 파일시스템 남은 용량

7. 대규모 운영 모범사례

Federation (연합)

여러 Prometheus 서버의 메트릭을 상위 Prometheus가 수집하는 구조다. 팀/클러스터별로 로컬 Prometheus를 운영하고, 글로벌 뷰가 필요한 메트릭만 연합 서버로 올린다.

# 글로벌 Prometheus의 scrape_configs
scrape_configs:
  - job_name: 'federate'
    honor_labels: true
    metrics_path: '/federate'
    params:
      'match[]':
        - '{job=~".+"}' # 모든 job 메트릭
        - '{__name__=~"job:.*"}' # Recording Rule 결과만
    static_configs:
      - targets:
          - 'prometheus-team-a:9090'
          - 'prometheus-team-b:9090'

장기 저장소: Thanos / Cortex / Mimir

Prometheus의 로컬 TSDB는 기본적으로 15~30일 보존에 적합하다. 장기 보존과 글로벌 쿼리가 필요하면 다음 솔루션을 검토한다.

솔루션특징적합한 환경
ThanosSidecar 패턴, 오브젝트 스토리지, 글로벌 쿼리기존 Prometheus에 사이드카 추가
Cortex멀티테넌트, 수평 확장, 마이크로서비스 아키텍처대규모 SaaS 환경
Grafana MimirCortex 포크, 성능 개선, Grafana 생태계 통합Grafana 스택 사용 시
VictoriaMetrics고성능, 낮은 리소스, PromQL 호환비용 최적화가 중요할 때

카디널리티 관리

고카디널리티(레이블 값 조합의 폭발적 증가)는 Prometheus 성능 저하의 가장 흔한 원인이다.

# relabel_configs로 불필요한 레이블 제거
relabel_configs:
  - action: labeldrop
    regex: '(pod_template_hash|controller_revision_hash)'

# metric_relabel_configs로 고카디널리티 메트릭 제거
metric_relabel_configs:
  - source_labels: [__name__]
    regex: '(go_gc_.*|go_memstats_.*)'
    action: drop

카디널리티 점검 PromQL:

# 시계열 수가 가장 많은 메트릭 상위 10개
topk(10, count by (__name__) ({__name__=~".+"}))

# 특정 메트릭의 카디널리티 확인
count(http_requests_total) by (service, method, status)

8. 운영 체크리스트

항목권장 설정비고
보존 기간15~30일 (로컬 TSDB)장기: Thanos/Mimir로 오브젝트 스토리지
백업TSDB 스냅샷 API 활용POST /api/v1/admin/tsdb/snapshot
HA 구성동일 설정의 Prometheus 2대 + Alertmanager 클러스터링Alertmanager는 gossip 프로토콜로 중복 알림 방지
보안TLS + Basic Auth 또는 OAuth2 Proxy--web.config.file로 TLS/Auth 설정
리소스시계열 100만 개당 약 2GB RAMWAL과 Head chunks 메모리 고려
스크레이프 간격15s (기본), 중요 메트릭은 10s너무 짧으면 부하 증가, 너무 길면 해상도 저하
알림 테스트amtool check-config, promtool check rulesCI/CD에 통합 권장
Recording Rules자주 사용하는 쿼리를 사전 계산대시보드 성능 향상, record: 네이밍 컨벤션 준수
카디널리티 모니터링prometheus_tsdb_head_series 추이 감시급격한 증가 시 원인 메트릭 추적

9. 흔한 실수

  1. rate()에 너무 짧은 range window 사용 -- rate(metric[1m]) 대신 scrape_interval의 최소 4배(15s라면 [1m], 30s라면 [2m])를 사용해야 한다. 너무 짧으면 데이터 포인트 누락으로 결과가 0이 된다.

  2. 알림에 irate() 사용 -- irate()는 순간 변화율이라 노이즈에 민감하다. 알림 규칙에서는 반드시 rate()를 사용한다.

  3. for 절 없이 알림 정의 -- for: 0s이면 한 번의 스파이크에도 알림이 발화한다. 최소 3~5분의 for 기간을 설정한다.

  4. 고카디널리티 레이블 사용 -- user_id, request_id, trace_id 같은 고유 값을 레이블에 넣으면 시계열이 무한 증가한다. 이런 값은 로그나 트레이스에 저장한다.

  5. Summary 타입 남용 -- Summary의 quantile은 인스턴스 간 집계가 불가능하다. 여러 인스턴스를 운영한다면 Histogram을 사용한다.

  6. Pushgateway 남용 -- Pushgateway는 단기 배치 작업 전용이다. 장기 실행 서비스의 메트릭을 Pushgateway로 보내면 대상 다운 감지가 불가능하다.

  7. Alertmanager 라우팅 우선순위 무시 -- 라우트 매칭은 상위에서 하위로 진행된다. 구체적인 매칭을 먼저 배치하고 기본 수신자를 마지막에 둔다.

  8. Recording Rules 네이밍 컨벤션 미준수 -- level:metric:operations 형식(예: job:http_requests_total:rate5m)을 따라야 대시보드와 알림에서 구분이 쉽다.

  9. TSDB 보존 기간과 디스크 용량 불일치 -- retention을 90일로 설정했지만 디스크가 30일분만 감당할 수 있으면 Prometheus가 OOM으로 죽는다. --storage.tsdb.retention.size로 디스크 기반 보존 제한을 병행한다.

  10. 모니터링 시스템 자체를 모니터링하지 않음 -- Prometheus 자체의 up, prometheus_tsdb_head_series, prometheus_engine_query_duration_seconds를 반드시 감시한다.

10. 요약

Prometheus는 클라우드 네이티브 환경의 표준 모니터링 시스템이다. 핵심을 정리하면 다음과 같다.

  • Pull 기반 모델로 동적 환경에 자연스럽게 적응하며, 서비스 디스커버리와 결합한다
  • 4가지 메트릭 타입(Counter, Gauge, Histogram, Summary) 중 Histogram을 우선 사용한다
  • PromQL은 rate(), histogram_quantile(), predict_linear() 등 강력한 함수를 제공한다
  • Alertmanager는 라우팅, 그룹핑, 억제, 중복 제거로 알림 피로를 줄인다
  • Grafana 연동으로 에러율, QPS, 레이턴시, 리소스 사용량 대시보드를 구성한다
  • 대규모 환경에서는 Thanos/Mimir로 장기 저장, Federation으로 글로벌 뷰, 카디널리티 관리로 성능을 유지한다
  • 운영 체크리스트를 주기적으로 점검하고, 흔한 실수를 피한다

이 글에서 다룬 설정과 쿼리를 기반으로 자신의 환경에 맞게 조정하면, 안정적이고 확장 가능한 모니터링 시스템을 구축할 수 있다.

퀴즈

Q1: Prometheus가 push 기반이 아닌 pull 기반 모델을 사용하는 주요 이유는 무엇인가?

Pull 기반 모델에서는 모니터링 대상이 Prometheus의 존재를 알 필요가 없으며, 서비스 디스커버리와 자연스럽게 결합할 수 있다. 또한 대상이 다운되면 스크레이프 실패로 즉시 감지되는 장점이 있다. 이는 동적으로 스케일링되는 클라우드 네이티브 환경에 특히 적합하다.

Q2: Counter 타입 메트릭에 직접 값을 사용하지 않고 rate()를 적용하는 이유는? Counter는 단조 증가하는 누적 값이므로, 직접 값을 보면 시간이 지날수록 계속 커지기만 하여 의미 있는 정보를 얻기 어렵다. rate()를 적용하면 초당 변화율을 계산하여 현재 처리량(예: 초당 요청 수)을 파악할 수 있다. 또한 rate()는 카운터 리셋(재시작 등)을 자동으로 처리한다.

Q3: Histogram과 Summary의 가장 큰 차이점은 무엇이며, 왜 Histogram이 권장되는가? Histogram은 서버 측(Prometheus)에서 histogram_quantile() 함수로 분위수를 계산하므로 여러 인스턴스의 데이터를 집계할 수 있다. 반면 Summary는 클라이언트 측에서 분위수를 미리 계산하므로 인스턴스 간 집계가 수학적으로 불가능하다. 마이크로서비스 환경에서 여러 인스턴스를 운영하는 것이 일반적이므로 Histogram이 권장된다.

Q4: 알림 규칙에서 rate() 대신 irate()를 사용하면 왜 문제가 되는가? irate()는 가장 최근 두 데이터 포인트 간의 순간 변화율을 계산하므로 짧은 스파이크에도 민감하게 반응한다. 알림에 사용하면 일시적인 노이즈에도 알림이 발화하여 오탐(false positive)이 급증하고, 알림 피로를 유발한다. rate()는 지정된 시간 범위 전체의 평균이므로 일시적 변동에 안정적이다.

Q5: Alertmanager의 group_by, group_wait, group_interval은 각각 어떤 역할을 하는가?

group_by는 알림을 묶는 기준 레이블을 지정한다(예: alertname, service). group_wait는 같은 그룹의 알림이 모일 때까지 대기하는 초기 시간(예: 30초)이다. group_interval은 이미 알림이 전송된 그룹에 새 알림이 추가되었을 때 다시 전송하는 간격이다. 이 설정들을 적절히 조합하면 알림 폭풍을 방지할 수 있다.

Q6: 고카디널리티(high cardinality)가 Prometheus에 위험한 이유와 대응 방법은? 레이블 값 조합이 폭발적으로 증가하면 시계열 수가 급증하여 Prometheus의 메모리(Head chunks)와 디스크 사용량이 기하급수적으로 늘어난다. 대응 방법으로는 user_id 같은 고유 값을 레이블에 넣지 않고, metric_relabel_configs로 불필요한 메트릭을 drop하며, prometheus_tsdb_head_series 메트릭으로 시계열 수를 모니터링하는 것이 있다.

Q7: Thanos와 Grafana Mimir의 주요 차이점은? Thanos는 기존 Prometheus에 Sidecar를 추가하는 패턴으로, 오브젝트 스토리지에 블록을 업로드하여 장기 저장과 글로벌 쿼리를 제공한다. Grafana Mimir(Cortex 포크)는 자체적으로 수평 확장이 가능한 마이크로서비스 아키텍처로, remote_write를 통해 데이터를 수신하며 멀티테넌시와 Grafana 생태계 통합이 강점이다. 기존 Prometheus에 최소 변경을 원하면 Thanos, Grafana 스택을 사용하면 Mimir가 적합하다.

Q8: predict_linear() 함수는 어떤 상황에서 유용하며, 어떻게 알림에 활용하는가? predict_linear()은 과거 데이터를 기반으로 선형 회귀를 수행하여 미래 시점의 값을 예측한다. 디스크 용량 부족 같은 점진적 문제를 사전에 감지하는 데 유용하다. 예를 들어, predict_linear(node_filesystem_avail_bytes[6h], 24*3600) < 0 이면 "지난 6시간 추이를 기반으로 24시간 뒤 디스크가 가득 찰 것"이라는 알림을 사전에 보낼 수 있다.