
프로젝트에서 Kafka를 추가적으로 도입해서 상품의 재고를 수정하려던 와중 로직 구현을 완료하고 테스트를 진행하니
Cannot invoke "org.springframework.web.context.request.ServletRequestAttributes.getRequest()" because the return value of "org.springframework.web.context.request.RequestContextHolder.getRequestAttributes()" is null
위의 로그를 보이며, NullPointerException이 발생
Message<String> kafkaMessage = MessageBuilder
.withPayload(EventSerializer.serialize(event)) // 직렬화
.setHeader(KafkaHeaders.TOPIC, topic) // target Topic
.setHeader("X-USER-ID", userId) // Auditor
.build();
kafkaTemplate.send(kafkaMessage);네, Kafka Listener와 같은 비동기 환경에서 HttpServletRequest를 사용할 수 없기 때문에,
RequestContextHolder를 사용하는 부분에서 오류가 발생하는 것입니다.
JPA의 AuditorAware는 보통 HTTP 요청과 관련된 정보를 통해 사용자 ID를 가져와서 감사 로그를 기록하는 데 사용되지만,
비동기 작업에서는 이를 직접 처리할 수 없습니다.AuditorAwareImpl 기존 로직
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
ThreadLocal을 사용해 HttpRequest가 없을 경우를 위해 Setter로 Auditor를 주입하는 로직을 추가
public class AuditorAwareImpl implements AuditorAware<Long> {
// ThreadLocal을 사용해 비동기 환경에서 사용자 ID를 설정
private static final ThreadLocal<String> auditor = new ThreadLocal<>();
@Override
public Optional<Long> getCurrentAuditor() {
String updatedBy;
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (attributes != null) {
HttpServletRequest request = attributes.getRequest();
updatedBy = request.getHeader("X-USER-ID");
if (updatedBy == null) {
updatedBy = "-1"; // 기본 사용자 ID 설정
}
} else {
// HTTP 요청이 없는 경우 (비동기 작업 등), ThreadLocal에서 사용자 ID를 가져옴
updatedBy = auditor.get();
if (updatedBy == null) {
updatedBy = "-1"; // 기본 사용자 ID 설정
}
}
return Optional.of(Long.parseLong(updatedBy));
}
public static void setAuditor(String userId) {
auditor.set(userId);
}
public static void clear() {
auditor.remove();
}
}