Spring Boot 3 환경에서 MDC(Mapped Diagnostic Context)를 활용한 로깅의 원리와 실전 적용법을 다룹니다. MDC의 기본 개념부터 실무에서 자주 쓰이는 활용 패턴, 그리고 주의해야 할 점까지 초보자도 쉽게 따라할 수 있도록 설명합니다. 예제 코드는 Kotlin 기반으로 작성되었습니다.

들어가며

서비스가 커지고 트래픽이 많아질수록, 로그의 중요성은 더욱 커집니다. 특히, 여러 요청이 동시에 처리되는 환경에서는 각 요청별로 로그를 구분짓는 것이 필수입니다. 이때 가장 널리 쓰이는 방법이 바로 MDC(Mapped Diagnostic Context)입니다. Spring Boot 3에서는 코틀린과 함께 MDC를 어떻게 활용할 수 있을지, 그리고 실전에서 어떻게 응용할 수 있을지 알아보겠습니다.


1. MDC란 무엇인가?

MDC는 SLF4J 및 Logback, Log4j 등 주요 로깅 프레임워크에서 지원하는 기능으로, 하나의 쓰레드(요청) 내에서만 유효한 key-value 형태의 컨텍스트 정보를 로그에 자동으로 포함시켜줍니다. 흔히 트랜잭션 ID, 사용자 ID, 세션 ID 등을 로그에 남길 때 활용합니다.

  • 주요 특징
    • 쓰레드 로컬(ThreadLocal) 기반으로 동작
    • key-value 형태로 데이터 저장
    • 로그 패턴에 포함시키면 별도 코드 없이 자동 출력

2. Spring Boot 3 + Kotlin에서 MDC 기본 사용법

Spring Boot 3 프로젝트에서 기본적으로 SLF4J와 Logback이 연동되어 있기 때문에 별도 설정 없이 MDC를 사용할 수 있습니다.

import org.slf4j.MDC

fun someServiceMethod() {
    MDC.put("userId", "user-1234")
    try {
        // 비즈니스 로직 실행
        logger.info("사용자 요청 처리 중")
    } finally {
        MDC.clear() // 반드시 클리어해주기!
    }
}
  • MDC.put(key, value): MDC에 값을 추가
  • MDC.get(key): 값 조회
  • MDC.remove(key): 특정 key 제거
  • MDC.clear(): 모든 key 제거 (finally 블록에서 호출 권장)

3. MDC를 활용한 실전 예제: 요청별 트랜잭션 ID 로그 남기기

실제 서비스에서는 각 HTTP 요청마다 고유한 트랜잭션 ID(혹은 requestId)를 MDC에 저장해 로그로 남기는 것이 일반적입니다. 이를 위해 Spring의 OncePerRequestFilter를 활용할 수 있습니다.

import org.slf4j.MDC
import org.springframework.stereotype.Component
import org.springframework.web.filter.OncePerRequestFilter
import java.util.UUID
import javax.servlet.FilterChain
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse

@Component
class MdcLoggingFilter : OncePerRequestFilter() {
    override fun doFilterInternal(
        request: HttpServletRequest,
        response: HttpServletResponse,
        filterChain: FilterChain
    ) {
        val requestId = UUID.randomUUID().toString()
        MDC.put("requestId", requestId)
        try {
            filterChain.doFilter(request, response)
        } finally {
            MDC.clear()
        }
    }
}

이렇게 하면 모든 로그에 requestId가 자동으로 포함됩니다.


4. 로그 패턴에 MDC 값 포함시키기 (Logback 예시)

src/main/resources/logback-spring.xml에서 패턴을 아래처럼 지정하면 MDC 값이 로그에 출력됩니다.

<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg [requestId:%X{requestId}]%n</pattern>

이렇게 하면 로그 예시는 다음과 같습니다:

2025-06-12 23:00:00.123 [http-nio-8080-exec-1] INFO  com.example.MyService - 사용자 요청 처리 중 [requestId:7d2a...]

5. WebFlux 환경에서 MDC 사용시 주의점

WebFlux(리액티브) 환경은 쓰레드가 고정되지 않으므로, 기본 MDC는 동작하지 않습니다. 이 경우 Reactor의 Context와 연동하는 별도 라이브러리(logstash-logback-encoderReactorMDC)를 쓰거나, 직접 연동 처리가 필요합니다. 예시:

Mono.deferContextual { ctx ->
    val userId = ctx.getOrDefault("userId", "anonymous")
    MDC.put("userId", userId)
    try {
        logger.info("WebFlux 요청 처리 중")
        Mono.just("ok")
    } finally {
        MDC.clear()
    }
}

6. MDC 활용시 실전 팁 & 주의사항

  • 반드시 finally 블록에서 MDC.clear() 호출 (메모리 누수 방지)
  • WebFlux/비동기 환경은 별도 처리 필요
  • 민감 정보는 MDC에 저장하지 말 것
  • 공통 필터/인터셉터에서 일관되게 적용
  • 테스트 코드에서도 MDC 값 세팅/클리어 신경쓰기
  • 로그 패턴에 MDC 키가 누락되지 않게 확인

7. 참고 자료 및 레퍼런스


Spring Boot 3에서 MDC를 활용하면 요청별로 로그를 추적하고, 문제를 빠르게 파악할 수 있습니다. 환경에 맞는 적용법과 주의사항을 꼭 숙지하세요!