본문 바로가기
클라우드/opentelemetry

📊 내 애플리케이션 데이터, 어디로 보내야 할까? OpenTelemetry 데이터 Export 완벽 가이드

by gasbugs 2025. 11. 3.

안녕하세요! 오늘은 우리가 개발하는 애플리케이션에서 수집된 수많은 데이터(추적, 메트릭, 로그)를 어떻게 원하는 분석 시스템으로 보낼 수 있는지, 그 전체적인 여정과 핵심적인 방법에 대해 알아보겠습니다. 🧐

 

많은 분들이 데이터를 '어떻게 만드느냐'에 집중하지만, '어떻게 보내느냐'를 모르면 데이터는 애플리케이션 내부에 갇혀버리고 맙니다. 데이터의 생성부터 최종 목적지까지의 전체 흐름을 이해하는 것이 중요합니다!

 


🗺️ 데이터의 대장정: 어디서 태어나 어디로 가는가?

애플리케이션에서 생성된 원격 측정(Telemetry) 데이터가 분석 도구에 도달하기까지의 여정은 크게 4단계로 나눌 수 있습니다.

  1. 계측 (Instrumentation) 💻: 개발자가 코드에 특정 라이브러리(API Package)를 사용하여 "이 작업은 50ms 걸렸어", "여기서 DB 호출을 시작했어"와 같은 데이터를 생성하는 단계입니다.
  2. 표준화 (Standardization) 🏷️: 생성된 데이터에 http.method = "GET"처럼 누구나 이해할 수 있는 표준화된 이름표(Semantic Conventions)를 붙여주는 단계입니다. 데이터의 '의미'를 통일하는 과정이죠.
  3. 처리 (Processing) ⚙️: 수집된 데이터를 샘플링하거나 필터링하는 등 중간 처리 과정을 거칩니다.
  4. 내보내기 (Exporting) 🚀: 마지막으로, 이 모든 데이터를 Jaeger, Prometheus, Datadog과 같은 최종 목적지로 전송하는 단계입니다. 오늘의 주인공이죠!

이 전체적인 그림을 이해하는 것이 각 부분을 개별적으로 아는 것보다 훨씬 중요합니다. 그럼 각 단계의 주요 행위자인 API와 Semantic Conventions가 '내보내기'와는 어떻게 다른지 먼저 짚고 넘어가겠습니다.


🤔 잠깐, API와 Semantic Conventions는 데이터를 보내는 역할이 아닌가요?

결론부터 말하면, 아닙니다! 이 둘은 데이터를 '만들고' '설명하는' 중요한 역할을 하지만, 외부로 '전송하는' 기능과는 거리가 멉니다.

API Package: 데이터를 '생성'하는 건축가 🏗️

API 패키지는 우리 코드 안에서 추적 데이터(Span)를 시작하고, 컨텍스트를 전파하는 등 원격 측정 데이터를 '만드는' 도구입니다. 개발자가 직접 코드를 통해 계측하는 인터페이스를 제공하는 것이죠.

주요 역할:

  • 새로운 스팬(Span) 시작 및 종료
  • 스팬에 이벤트나 속성(Attribute) 추가
  • 서비스 간 컨텍스트 전파

즉, 데이터를 '생산'하는 첫 단계를 책임집니다. 하지만 이 데이터를 특정 시스템으로 어떻게 보낼지에 대한 설정은 전혀 관여하지 않습니다.

Semantic Conventions: 데이터에 '이름표'를 붙이는 사서 🏷️

시맨틱 컨벤션은 데이터의 '내용'을 표준화하는 약속입니다. 예를 들어, 어떤 개발자는 HTTP 메서드를 http_method로 기록하고, 다른 개발자는 http.req.method로 기록한다면 데이터를 통합해서 분석하기 매우 어렵겠죠?

시맨틱 컨벤션은 http.method, db.system, rpc.service와 같이 데이터 필드의 이름을 표준으로 정의하여, 어떤 언어나 프레임워크에서 수집된 데이터든 일관된 의미를 갖도록 보장합니다.

 

주요 역할:

  • 추적, 메트릭, 로그 데이터의 표준 명명 규칙 정의
  • 데이터의 일관성과 상호 운용성 보장

이처럼 데이터의 내용을 표준화할 뿐, 이 데이터를 어디로 '내보낼지' 결정하는 역할은 하지 않습니다.


🚀 진짜 주인공 등장: Exporter Package!

그렇다면 수집된 데이터는 대체 누가 내보내는 걸까요? 바로 Exporter가 그 역할을 담당합니다!

Exporter는 OpenTelemetry SDK가 수집하고 처리한 데이터를 특정 백엔드(분석 시스템)가 이해할 수 있는 형식으로 변환하여 전송하는 플러그인입니다.

다양한 종류의 Exporter가 존재하며, 목적에 맞게 선택할 수 있습니다.

  • Console Exporter: 데이터를 터미널 콘솔에 출력합니다. 개발 및 디버깅 용도로 매우 유용합니다.
  • OTLP Exporter: OpenTelemetry의 표준 프로토콜(OTLP)을 사용하여 데이터를 전송합니다. 특정 벤더에 종속되지 않고 OpenTelemetry를 지원하는 모든 백엔드와 유연하게 연동할 수 있습니다.
  • Vendor-specific Exporter: Jaeger, Prometheus, Zipkin 등 특정 분석 도구의 고유 포맷에 맞춰 데이터를 전송하는 Exporter입니다.

🛠️ 실전 예제: 코드로 Exporter 설정하고 데이터 확인하기

말로만 설명하면 감이 잘 안 오시죠? Node.js 환경에서 Console Exporter를 설정하여 스팬 데이터가 어떻게 생성되고 외부로 '출력'되는지 코드로 직접 확인해 보겠습니다.

1. 코드 설정 (Node.js)

ConsoleSpanExporter를 사용하여 TracerProvider를 설정하는 코드입니다. 이 코드는 생성되는 모든 스팬 정보를 콘솔에 출력하도록 지시합니다.

// 필요한 모듈 import
const { NodeTracerProvider } = require('@opentelemetry/sdk-trace-node');
const { ConsoleSpanExporter, SimpleSpanProcessor } = require('@opentelemetry/sdk-trace-base');
const { registerInstrumentations } = require('@opentelemetry/instrumentation');
const { HttpInstrumentation } = require('@opentelemetry/instrumentation-http');

// 1. Exporter 설정: 모든 스팬을 콘솔에 출력하도록 ConsoleSpanExporter를 생성합니다.
const consoleExporter = new ConsoleSpanExporter();

// 2. Provider 설정: Tracer를 생성하고 관리하는 주체입니다.
const provider = new NodeTracerProvider();

// 3. Provider와 Exporter 연결: SimpleSpanProcessor를 통해 스팬이 종료될 때마다 즉시 consoleExporter로 보내도록 설정합니다.
provider.addSpanProcessor(new SimpleSpanProcessor(consoleExporter));

// 4. Provider 등록: OpenTelemetry API가 이 Provider를 사용하도록 전역으로 등록합니다.
provider.register();

// 자동 계측 설정 (예: HTTP 요청)
registerInstrumentations({
  instrumentations: [new HttpInstrumentation()],
});

console.log("✅ OpenTelemetry with ConsoleExporter is running...");

// 테스트를 위해 간단한 HTTP 서버 실행
const http = require('http');
http.createServer((req, res) => {
  res.end('Hello, World!');
}).listen(8080);

2. Raw 데이터 확인 (콘솔 출력)

위 코드를 실행하고 curl http://localhost:8080 명령으로 서버에 요청을 보내면, 터미널에 다음과 같은 스팬 데이터(Raw Data)가 출력되는 것을 볼 수 있습니다. 이것이 바로 ConsoleExporter가 '내보낸' 결과물입니다!

{
  "traceId": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6",
  "parentId": undefined,
  "name": "HTTP GET",
  "id": "1a2b3c4d5e6f7a8b",
  "kind": 1,
  "timestamp": 1762021445123456,
  "duration": 15678,
  "attributes": {
    "http.method": "GET",
    "http.target": "/",
    "http.status_code": 200,
    "net.peer.ip": "::1"
  },
  "status": {
    "code": 0
  },
  "events": []
}

 

보시는 것처럼, http.method와 같은 시맨틱 컨벤션에 따라 이름이 붙여진 데이터가 Exporter에 의해 깔끔한 JSON 형식으로 출력되었습니다. 만약 Jaeger로 데이터를 보내고 싶다면 ConsoleSpanExporter 대신 JaegerExporter를 설정해주기만 하면 됩니다.


✨ 정리하며

데이터 관찰 가능성(Observability) 파이프라인을 구축할 때 전체적인 데이터의 흐름을 이해하는 것은 매우 중요합니다.

  • API는 데이터를 '생성'하고,
  • Semantic Conventions는 데이터에 일관된 '의미'를 부여하며,
  • Exporter는 최종적으로 이 데이터를 원하는 목적지로 '전송'합니다.

이제 각 구성 요소가 어떤 역할을 하는지, 그리고 이들이 어떻게 조화롭게 동작하는지 명확히 이해되셨나요? 여러분의 시스템에 맞는 Exporter를 선택하여 흩어져 있는 데이터를 한곳에 모아 강력한 인사이트를 얻어보시길 바랍니다!