구조적 로깅 실전: Correlation ID, 로그 레벨, 민감정보 마스킹으로 장애 분석 가능하게 만들기
로그는 문자열이 아니라 사건 기록이다
장애가 나면 가장 먼저 로그를 봅니다. 하지만 많은 서비스의 로그는 정작 장애 순간에 도움이 되지 않습니다. "error occurred" 같은 메시지만 남아 있고, 어떤 사용자 요청인지, 어떤 주문인지, 어떤 외부 API 호출과 연결되는지 알 수 없습니다. 반대로 너무 많은 로그가 쌓여 중요한 신호가 묻히기도 합니다.
좋은 로그는 사람이 읽을 수 있으면서도 시스템이 검색하고 집계할 수 있어야 합니다. 그래서 구조적 로깅이 필요합니다. JSON 형태로 timestamp, level, service, traceId, userId, requestPath, latencyMs 같은 필드를 일관되게 남기면 로그 검색 도구에서 빠르게 필터링할 수 있습니다. 로그는 텍스트 파일이 아니라 운영 데이터입니다.
이 글에서는 운영 환경에서 로그를 설계하는 기준을 정리합니다. Correlation ID를 어떻게 전파하는지, 로그 레벨을 어떻게 나누는지, 민감정보를 어떻게 마스킹하는지, 로그 비용과 보존 기간을 어떻게 다루는지 살펴봅니다.

1. Correlation ID는 요청의 실마리다
마이크로서비스나 외부 API 연동이 많은 시스템에서는 하나의 사용자 요청이 여러 서비스와 큐, 데이터베이스를 거칩니다. 이때 각 서비스가 제각각 로그를 남기면 나중에 한 요청의 흐름을 복원하기 어렵습니다. Correlation ID는 이 흐름을 하나로 묶는 식별자입니다.
HTTP 요청이 들어오면 gateway나 edge에서 requestId를 만들고, 이를 내부 서비스 호출 헤더에 전파합니다. 비동기 메시지를 발행할 때도 message header에 같은 id를 넣습니다. 컨슈머는 이 값을 로그 컨텍스트에 포함합니다. 이렇게 하면 로그 검색에서 requestId 하나로 API 요청, 내부 RPC, 메시지 처리, 외부 결제 호출을 따라갈 수 있습니다.
Trace 시스템을 쓰고 있다면 traceId와 spanId를 함께 남기는 것이 좋습니다. 로그와 트레이스를 연결하면 대시보드에서 느린 span을 클릭한 뒤 관련 로그로 바로 이동할 수 있습니다. 장애 대응 시간은 도구의 화려함보다 연결성에서 줄어듭니다.
2. 로그 레벨은 알림 정책과 연결된다
debug, info, warn, error를 단순 취향으로 쓰면 운영에서 혼란이 생깁니다. info는 정상 흐름의 핵심 이벤트, warn은 즉시 실패는 아니지만 확인이 필요한 비정상 조건, error는 요청 실패나 사용자 영향이 있는 오류에 사용합니다. debug는 개발과 임시 진단용이며 운영 기본 수집 대상에서 제외할 수 있어야 합니다.
자주 발생하는 비즈니스 실패를 error로 남기는 것도 문제입니다. 사용자가 잘못된 비밀번호를 입력하거나 유효하지 않은 쿠폰을 넣는 것은 시스템 장애가 아닙니다. 이런 이벤트가 error 로그를 채우면 실제 장애 신호가 묻힙니다. 반대로 외부 API timeout, 데이터 불일치, retry exhausted는 error로 남기고 알림과 연결할 수 있어야 합니다.
로그 레벨은 알림 정책과 맞물려야 합니다. error 로그 수가 일정 기준을 넘으면 알림을 울릴 수 있지만, error 정의가 느슨하면 알림 피로가 생깁니다. 로그 레벨을 정하는 일은 단순 코드 스타일이 아니라 온콜 품질을 결정하는 운영 설계입니다.
3. 민감정보는 남기기 전에 지운다
로그는 검색하기 쉽고 오래 보관되기 때문에 민감정보 유출 위험이 큽니다. 비밀번호, 토큰, 세션 쿠키, 주민등록번호, 결제 카드 정보, 인증 헤더는 절대 원문으로 남기면 안 됩니다. "나중에 지우면 된다"는 접근은 위험합니다. 로그 수집기와 백업, 외부 분석 도구까지 이미 복제됐을 수 있습니다.
가장 안전한 방식은 로깅 라이브러리나 middleware 레벨에서 마스킹 규칙을 강제하는 것입니다. Authorization, Cookie, Set-Cookie 같은 헤더는 기본적으로 제거합니다. JSON body에서는 password, token, secret, cardNumber 같은 키를 마스킹합니다. 필요하다면 allowlist 방식으로 기록할 필드만 명시합니다.
개발자가 로그 메시지에 직접 객체를 통째로 넣는 습관도 줄여야 합니다. 사용자 객체 전체를 찍으면 예상하지 못한 개인정보가 포함될 수 있습니다. 필요한 식별자와 상태만 선택해 구조화된 필드로 남기는 것이 안전합니다.
4. 로그 비용과 보존 기간도 설계 대상이다
로그는 무료가 아닙니다. 고트래픽 서비스에서 요청마다 큰 JSON body를 남기면 저장 비용과 검색 비용이 빠르게 증가합니다. 모든 성공 요청을 상세히 남기는 대신 샘플링을 적용하거나, 핵심 이벤트 중심으로 줄이는 전략이 필요합니다. 장애 분석에 필요한 로그와 단순 호기심 로그를 구분해야 합니다.
보존 기간도 데이터 성격에 따라 달라야 합니다. 보안 감사 로그는 오래 보관해야 할 수 있지만, 상세 debug 로그는 짧게 가져가는 것이 현실적입니다. 개인정보가 포함될 가능성이 있는 로그는 접근 권한과 보존 기간을 더 엄격히 관리해야 합니다. 로그 인덱스도 서비스, 환경, 레벨 기준으로 나누면 검색 성능과 비용을 조절하기 쉽습니다.
운영에서는 로그 누락도 감시해야 합니다. 로그 수집 에이전트가 죽거나, 파이프라인 지연이 커지거나, 특정 서비스 로그량이 갑자기 0이 되면 장애 분석 능력을 잃습니다. 로그 시스템 자체도 관측 대상입니다.
실무 체크리스트
- 모든 요청 로그에 requestId 또는 traceId가 포함되는가
- 서비스 간 HTTP, 메시지, 잡 실행 경로에 correlation context가 전파되는가
- error 로그가 실제 사용자 영향 또는 운영 조치가 필요한 실패만 담는가
- 인증 헤더, 쿠키, 토큰, 비밀번호가 마스킹되는가
- 객체 전체를 로그로 남기는 코드가 없는가
- 로그량, 수집 지연, 파이프라인 오류를 모니터링하는가
- 보존 기간과 접근 권한이 로그 종류별로 구분되어 있는가
5. 로그 필드 이름은 조직 표준으로 맞춘다
구조적 로깅을 도입해도 서비스마다 필드 이름이 다르면 검색이 어려워집니다. 어떤 서비스는 requestId를 쓰고, 다른 서비스는 correlation_id를 쓰고, 또 다른 서비스는 traceID를 쓰면 공통 대시보드와 알림을 만들기 어렵습니다. 로그 필드는 조직 표준으로 정해야 합니다. timestamp, level, service, environment, version, traceId, spanId, requestId, userId, tenantId, route, statusCode, latencyMs 같은 기본 필드를 문서화합니다.
필드 타입도 중요합니다. latencyMs가 어떤 서비스에서는 문자열이고 다른 서비스에서는 숫자라면 집계가 깨집니다. statusCode도 숫자로 맞추고, boolean 값은 true/false로 통일합니다. 로그 수집 시스템은 스키마가 느슨해 보이지만, 운영 분석에서는 일관된 타입이 큰 차이를 만듭니다.
도메인 필드는 필요한 만큼만 추가합니다. 주문 서비스라면 orderId, paymentId, provider 같은 필드가 유용합니다. 하지만 요청 body 전체를 넣는 방식은 피합니다. 나중에 검색할 가능성이 높은 식별자와 상태만 명시적으로 넣습니다. "무엇을 검색할 것인가"를 기준으로 필드를 선택하면 로그가 과도하게 커지는 것을 막을 수 있습니다.
6. 샘플링은 성공 로그와 실패 로그를 다르게 다룬다
트래픽이 많은 서비스는 모든 요청 로그를 영구 저장하기 어렵습니다. 이때 샘플링을 적용할 수 있지만, 실패 로그까지 같은 비율로 줄이면 장애 분석이 어려워집니다. 일반적으로 성공 요청은 낮은 비율로 샘플링하고, error와 warn은 전량 보관하거나 더 긴 기간 보관합니다. 느린 요청도 별도 기준으로 보존할 수 있습니다.
샘플링 정책은 로그 레벨, route, tenant, latency, status code에 따라 달라질 수 있습니다. 예를 들어 health check는 거의 저장하지 않고, 결제 요청은 성공 로그도 일정 기간 보관할 수 있습니다. 보안 이벤트와 감사 로그는 일반 애플리케이션 로그와 다른 정책을 가져야 합니다. 비용을 줄이되 장애와 감사에 필요한 증거는 남겨야 합니다.
샘플링된 로그에는 sampling rate를 필드로 남깁니다. 그래야 나중에 로그 수를 기반으로 대략적인 전체 요청 수를 추정할 수 있습니다. 샘플링 여부가 숨겨져 있으면 운영자는 로그가 적은 것이 실제 요청이 적은 것인지, 샘플링 때문인지 판단하기 어렵습니다.
7. 로그와 메트릭, 트레이스는 서로 연결되어야 한다
로그만으로 모든 장애를 분석하려고 하면 검색 범위가 너무 넓습니다. 먼저 메트릭으로 어떤 지표가 나빠졌는지 보고, 트레이스로 느린 경로를 찾고, 마지막에 로그로 구체적인 실패 원인을 확인하는 흐름이 효율적입니다. 이를 위해 세 도구가 같은 traceId, service, environment, version 필드를 공유해야 합니다.
배포 버전도 중요한 필드입니다. 새 버전 배포 후 특정 오류가 증가했을 때 version 필드가 없으면 원인 파악이 늦어집니다. canary 배포에서는 old version과 new version의 error rate를 로그와 메트릭 모두에서 비교할 수 있어야 합니다. 로그는 단독 저장소가 아니라 관측성 데이터 모델의 일부입니다.
사용자 신고 대응에서도 연결성이 중요합니다. 고객지원팀이 받은 시간, 사용자 ID, requestId를 운영팀에 전달하면 해당 요청의 trace와 로그를 바로 찾을 수 있습니다. requestId를 응답 헤더나 오류 화면에 노출하는 방식도 고려할 수 있습니다. 단, 내부 정보가 과도하게 노출되지 않도록 형식과 범위를 정해야 합니다.
8. 로그 품질은 장애 후에 반드시 평가한다
장애 회고에서는 원인과 조치만 보지 말고 로그가 충분했는지도 평가해야 합니다. 필요한 requestId가 없었는지, 외부 API 응답 code가 누락됐는지, error 로그가 너무 일반적이었는지, 민감정보 때문에 로그를 공유하지 못했는지 확인합니다. 장애 중에 "이 값을 로그에 남겼어야 했다"는 말이 나왔다면 그것은 다음 개선 과제입니다.
로그 품질 평가는 구체적이어야 합니다. "로그를 더 남긴다"가 아니라 "payment provider timeout 로그에 provider, operation, retryCount, requestId를 추가한다"처럼 작성합니다. 그래야 비용을 통제하면서 분석에 필요한 정보만 늘릴 수 있습니다. 로그가 많아지는 것과 로그가 좋아지는 것은 다릅니다.
개발 환경에서도 로그 형태를 확인할 수 있어야 합니다. 로컬에서는 사람이 읽기 쉬운 pretty log를 쓰더라도, 테스트나 staging에서는 production과 같은 구조적 로그를 검증하는 편이 좋습니다. 필수 필드가 빠진 로그는 lint나 테스트로 잡을 수 있습니다. 운영에서 처음 발견하는 것보다 배포 전에 발견하는 것이 훨씬 싸게 먹힙니다.
9. 감사 로그와 애플리케이션 로그는 분리한다
모든 로그가 같은 목적을 갖지는 않습니다. 애플리케이션 로그는 장애 분석과 디버깅을 위한 것이고, 감사 로그는 누가 언제 어떤 중요한 행동을 했는지 증명하기 위한 것입니다. 관리자 권한 변경, 결제 환불, 개인정보 조회, API key 생성, 보안 설정 변경은 일반 debug 로그가 아니라 감사 로그로 남겨야 합니다.
감사 로그는 수정과 삭제가 어렵게 관리해야 합니다. 접근 권한도 더 좁게 가져가고, 보존 기간도 규정에 맞춰 길게 둘 수 있습니다. 반면 애플리케이션 상세 로그는 비용과 개인정보 위험 때문에 짧게 보관할 수 있습니다. 두 종류를 같은 저장소에 같은 정책으로 넣으면 과도하게 오래 보관하거나, 반대로 필요한 증거를 너무 빨리 지울 위험이 있습니다.
감사 로그에는 변경 전후 값, 수행자, 대상 리소스, 요청 출처, 승인 정보가 포함되어야 합니다. 단, 민감한 값 자체는 마스킹하거나 해시로 남깁니다. 운영자가 나중에 "누가 이 설정을 바꿨는가"에 답할 수 있어야 하고, 동시에 로그가 새로운 유출 경로가 되면 안 됩니다.
결론: 좋은 로그는 장애 때 질문에 답한다
로그의 목적은 많이 남기는 것이 아닙니다. 장애가 났을 때 "어떤 요청이, 어느 서비스에서, 어떤 조건으로, 왜 실패했는가"에 답하는 것입니다. 이를 위해서는 구조화된 필드, correlation id, 일관된 레벨, 민감정보 마스킹, 비용 관리가 함께 필요합니다.
로그는 나중에 붙이는 디버깅 장치가 아니라 서비스 계약의 일부입니다. 운영자가 실제로 검색하고 판단할 수 있는 형태로 남겨야 장애 대응 도구가 됩니다.