Report

Learnings — 구현 회고

← Home

--- append-only: true created: 2026-04-27 ---

Learnings — TimelyGPT MCP Hub

This file is append-only. New entries go at the bottom under a dated header.

Format

## YYYY-MM-DD — <topic>
- **Pattern**: <reusable pattern with file/command pointer>
- **Anti-pattern**: <what we tried that failed and why>
- **Why it matters**: <hidden constraint, gotcha, perf cliff, etc.>
- **Apply when**: <trigger conditions>

Entries should be:

Skip "what we did" descriptions (use commit messages for that).

---

<!-- Append new entries below -->

2026-04-27 — Bootstrap

2026-04-27 — data.go.kr 활용신청 캐시 반영 지연

2026-04-27 — MOLIT 매매 vs 전월세 필드 차이

2026-04-27 — 날짜 정규화 함정

2026-04-27 — tools/list composite filter (PRD acceptance)

2026-04-28 — OTel + Pino 정석 로깅 (풀스택 trace_id 전파)

Stack: @opentelemetry/sdk-node + nestjs-otel (@Traceable()/@Span()) + nestjs-pino (mixin으로 trace_id 자동 주입) — 백엔드. @vercel/otel + pino (transport 없이 JSON only) — Next.js 프론트. ConsoleSpanExporter + JSON Pino 로그만으로 grep 기반 분석 가능 (Jaeger/Tempo UI 불필요).

핵심 패턴:

  1. tracing.ts는 main.ts 첫 줄에서 import — Nest 부트보다 먼저 SDK.start() 안 하면 instrumentation이 hook 못 검.
  2. Pino mixin()에서 trace.getActiveSpan().spanContext() 추출 → 모든 로그 라인에 trace_id/span_id 자동 주입. 백엔드 프론트 동일 로직.
  3. genReqId로 W3C traceparent 헤더의 trace_id를 req.id에 채택 → 응답 헤더 x-request-id로 echo. 외부에서 trace_id 강제 주입 가능.
  4. @Traceable() 클래스 데코레이터로 한 줄 적용 — 모든 메서드 자동 span. 19개 서비스에 일괄 적용은 awk 한 줄로 처리.
  5. tracer.startActiveSpan()로 핵심 진입점 명시mcp.tool.call ${name} 같은 의미있는 span name + tool.name attribute로 grep/필터 용이.

함정:

Why: 사용자 요청은 "Spring @Trace 어노테이션처럼 붙이기만 하면 자동" — 정확히 OTel + nestjs-otel 조합이 충족. 풀스택 한 trace_id로 묶이는 게 핵심 검증 대상이었음.

How to apply: 새 도구 서비스 추가 시 @Injectable() 아래 @Traceable() 한 줄 추가만 하면 끝. 핵심 진입점만 startActiveSpan 명시. trace_id로 grep하면 풀스택 흐름이 한 줄로 묶여 나옴.

TimelyGPT MCP Hub · 2026.04.30 · 백상현