MDC(Mapped Diagnostic Context)에 대해 알아보자

송현진·2025년 4월 14일
0

Spring Boot

목록 보기
14/23

MDC는 요청별 로그 흐름을 구분할 수 있도록 도와주는 로깅 컨텍스트이다. 멀티스레드 환경에서 각 요청은 별도의 스레드에서 처리되기 때문에 MDC는 내부적으로 ThreadLocal<Map<String, String>> 형태로 동작하며 현재 스레드에 컨텍스트 데이터를 저장한다.

  • SLF4J, Logback, Log4j2 등 로깅 프레임워크에서 지원
  • 로그 출력 시 자동으로 해당 스레드의 MDC 값을 로그에 포함

❓왜 사용할까?

Spring Boot와 같은 웹 애플리케이션은 동시에 수많은 요청을 처리하고 각 요청은 별도의 스레드에서 수행된다. 이 때 각 요청의 로그 흐름을 추적하려면 요청마다 고유한 컨텍스트 정보(traceId, method, uri 등)를 로그에 남겨야한다.

MDC를 적용하지 않았을 경우

[INFO] 로그인 완료
[INFO] 상품 등록 완료

이렇게 누가 어떤 요청을 보낸 건지 구분이 불가능하다.

MDC를 적용한 경우

[INFO] [traceId=abc123] 로그인 완료
[INFO] [traceId=abc123] 상품 등록 완료

traceId 덕분에 요청 흐름을 정확히 추적 가능하다.

✅ MDC 사용법

// 값 저장
MDC.put("traceId", UUID.randomUUID().toString());
MDC.put("method", request.getMethod());

// 로그 출력 시 자동 포함
log.info("요청 처리 시작");

// 요청 끝나면 반드시 clear
MDC.clear();

MDC.clear()는 왜 필수 인가?
MDC는 ThreadLocal 기반으로 동작하므로 요청이 끝난 후에도 값이 스레드에 남아 있을 수가 있다. 그래서 스레드 풀을 재사용할 때 다음 요청이 이전 요청 정보가 섞이는 문제가 발생할 수 있기 때문이다. 이렇게 되면 로그가 오염되고 사용자 정보 혼동 등의 심각한 오류 유발 가능성이 생길 수 있다. 그렇기 때문에 반드시 다 사용하고 나선 MDC.clear()를 사용해줘야 한다.

❓ MDC에 어떤 값들을 넣어야 할까?

📌 필수로 넣는 값

Key예시 값설명
traceIdUUID.randomUUID().toString()요청 단위 식별: 요청 흐름 전체 추적
userIduser.getId() or user.getEmail()사용자 식별: 누가 요청했는지 추적 가능
methodHTTP 요청 메서드(GET, POST 등)요청 성격 구분
uri/api/items/1어떤 API에 대한 요청인지 추적
iprequest.getRemoteAddr()보안 로그, 이상 요청 탐지 등

✏️ 상황에 따라 넣을 수 있는 값

Key예시 값설명
authorizationJWT 토큰디버깅 시 유용하나 보안 유의(마스킹 필수)
clientweb, android, ios플랫폼별 요청 구분 시 유용
localeko_KR, en_US글로벌 서비스일 경우 로케일별 분석
requestIdTraceId와 별도로 클라이언트에서 보내는 ID프론트/백 로그 연결에 사용
sessionIdrequest.getSession().getId()동시 로그인 추적 등에 활용 가능

로그 출력시 MDC 값 사용 (Logback 설정 예)

<pattern>
  %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %highlight(%5level) %cyan(%logger) - %magenta(%msg) traceId=%X{traceId} userId=%X{userId}%n
</pattern>
  • %X{key} : MDC에 저장한 key 값
  • %msg : 실제 로그 메세지
    로그 출력 시 자동으로 traceId가 포함되게 된다.

✅ INFO 로그와 MDC의 차이점

구분설명
INFO로그 레벨 (DEBUG < INFO < WARN < ERROR)
MDC로그에 자동 포함되는 추가 컨텍스트 정보

INFO는 로그의 중요도를 나타내는 것이고 MDC는 로그의 부가 정보를 채워주는 기능이다.

Spring Filter에서 traceId 설정 예시

@Component
public class LoggingFilter extends OncePerRequestFilter {
	@Override
    protected void doFilterInternal(HttpServletRequest request, 
    								HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {
     	
        try {
        	MDC.put("traceId", UUID.randomUUID().toString());
            MDC.put("method", request.getMethod());
            MDC.put("uri", request.getRequestURI());
            
            Authentication auth = SecurityContextHolder.getContext().getAuthentication();
            if (auth != null && auth.isAuthenticated()) {
                MDC.put("userId", auth.getName());
            }
            
            filterChain.doFilter(request, response);
        } finally {
        	MDC.clear(); // 반드시 clear해줘야 함
        }
        
    }
}

userId는 내가 UserDetailsImpl에서 유니크한 값인 email로 했기 때문에 난 이메일로 찍히게 된다.

🤔 배운점

지금까지는 traceId, userId를 로그에 직접 붙여 출력했는데, MDC를 활용하면 로그 구성과 관리가 훨씬 깔끔하고 일관성 있게 가능하다는 걸 알게 되었다. 특히 멀티스레드 환경에서 스레드풀 재사용의 문제점 때문에 MDC.clear()가 꼭 필요하다는 걸 알 수 있었다. 앞으로 로그 설계 시 MDC를 적극 활용해 로그 추적성을 높이는 설계를 해야겠다.

profile
개발자가 되고 싶은 취준생

0개의 댓글