Intro::
로깅시 클라이언트 정보는 횡단 관심사이기 때문에 이를 효과적으로 처리할 수 있는 방법에 대해 알아봅시다.
1. MDC(Mapped Diagnostic Context)
1.
개념
•
MDC는 스레드별로 키–값 쌍을 저장해 두는 로깅 컨텍스트입니다.
•
하나의 HTTP 요청 처리 과정에서 발생하는 모든 로그에 동일한 식별 정보를 포함시킬 수 있습니다.
2.
사용 목적
•
요청별 고유 ID(requestId), 사용자 ID(userId), 클라이언트 IP(clientIp) 등을 한 번만 설정해 두면, 이후 logger.info(…) 호출 시 자동으로 로그 패턴에 포함됩니다.
•
여러 스레드나 비동기 작업에서도 스레드 로컬로 관리되므로, 서로 다른 요청의 로그가 뒤섞이지 않습니다.
3.
주요 메서드
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
@Component
public class MdcInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(
HttpServletRequest request,
HttpServletResponse response,
Object handler) {
// 프록시(X-Forwarded-For) 헤더 우선, 없으면 remoteAddr 사용
String clientIp = request.getHeader("X-Forwarded-For");
if (clientIp == null || clientIp.isEmpty()) {
clientIp = request.getRemoteAddr();
}
// MDC에 저장
MDC.put("clientIp", clientIp);
return true;
}
@Override
public void afterCompletion(
HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) {
// 요청 처리 완료 후 MDC 정리
MDC.remove("clientIp");
}
}
Java
복사
4.
설정
# application.yml
logging:
pattern:
console: "%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} [%X{clientIp}] - %msg%n"]
YAML
복사
# 실행할 때
java -Djava.net.preferIPv4Stack=true -jar build/libs/your-app.jar
Docker
복사
5.
장점
•
로깅 호출문(예: logger.info(…))을 일일이 수정하지 않고도 부가 정보를 포함
•
AOP나 인터셉터 등 어디서든 MDC에 값을 세팅하면, 전체 로깅 흐름에 일괄 적용
2. HandlerInterceptor
1.
개념
•
Spring MVC에서 요청 전·후에 공통 처리를 삽입하기 위한 컴포넌트입니다.
•
preHandle, postHandle, afterCompletion 3가지 메서드를 오버라이드해 사용합니다.
2.
주요 메서드
import com.cowave.cdcproxy.interceptor.MdcInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
private final MdcInterceptor mdcInterceptor;
@Autowired
public WebConfig(MdcInterceptor mdcInterceptor) {
this.mdcInterceptor = mdcInterceptor;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 모든 요청 경로에 MdcInterceptor 적용
registry.addInterceptor(mdcInterceptor)
.addPathPatterns("/**");
}
}
Kotlin
복사
3.
활용 사례
•
로깅 컨텍스트 관리: 요청별 requestId, clientIp 등을 MDC에 자동 세팅
•
인증·권한 검사: 모든 요청에 대해 토큰 유효성 검사를 공통으로 처리
•
성능 측정: preHandle에서 시작 시간, afterCompletion에서 처리 시간 계산
3. MDC + Interceptor 조합
1.
Interceptor에서 MDC에 값을 세팅 →
2.
로깅 패턴에 %X{…}로 포함 →
3.
서비스/컨트롤러에서는 별도 파라미터 없이 logger.info(…) 사용
이로써 비즈니스 로직 코드는 로깅 코드로 복잡해지지 않으면서도, 모든 로그에 일관된 부가 정보를 자동으로 붙일 수 있습니다.