RequestScope로 요청별 로그 분리하기

2025. 5. 7. 20:28·SpringBoot

문제 상황: 로그가 요청마다 섞여버린다

스프링 웹 애플리케이션에서는 여러 사용자가 동시에 요청을 보낼 수 있다.
하지만 로그는 시간 순으로 출력되기 때문에
어떤 요청에서 발생한 로그인지 구분이 어렵다.

예를 들어 이런 로그가 찍혔다고 해보자:

회원 가입 처리 시작  
상품 주문 시작  
회원 가입 처리 시작  
회원 가입 처리 완료  
상품 주문 시작  
상품 주문 완료
  • 같은 메시지가 반복되거나,
  • 서로 다른 요청의 로그가 뒤섞여 있다면,

이게 어떤 요청에서 나온 건지 디버깅이 매우 어렵다.


해결 전략: 요청마다 UUID를 붙여서 로그를 구분하자

각 HTTP 요청마다 UUID를 하나 생성하고,
그 UUID를 로그 메시지 앞에 붙이면
같은 요청에서 나온 로그끼리는 쉽게 묶어 볼 수 있다.

이를 구현하기 위해
스프링의 @RequestScope 스코프를 사용하면 된다.


웹 스코프를 사용하려면?

@RequestScope는 웹 애플리케이션 환경에서만 사용할 수 있다.
따라서 spring-boot-starter-web 의존성을 추가해야 한다:

// build.gradle
dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
}

이 라이브러리가 포함되면,
스프링 부트가 내장 톰캣 서버를 띄우고
웹 요청을 받을 수 있는 환경이 자동으로 구성된다.


RequestScope 로깅 빈 만들기

@Component
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class RequestLogger {
    private String uuid;
    private String requestURL;

    @PostConstruct
    public void init() {
        this.uuid = UUID.randomUUID().toString();
        System.out.println("[" + uuid + "] request scope bean created");
    }

    @PreDestroy
    public void close() {
        System.out.println("[" + uuid + "] request scope bean closed");
    }

    public void setRequestURL(String requestURL) {
        this.requestURL = requestURL;
    }

    public void log(String message) {
        System.out.println("[" + uuid + "][" + requestURL + "] " + message);
    }
}
  • @RequestScope는 HTTP 요청마다 객체를 새로 만든다
  • proxyMode = TARGET_CLASS를 설정하면
    프록시 객체를 먼저 주입해 두고,
    실제 요청 시점에 진짜 객체로 연결해준다

컨트롤러와 서비스 코드

LogDemoService.java

@Service
@RequiredArgsConstructor
public class LogDemoService {
    private final RequestLogger requestLogger;

    public void logic(String id) {
        requestLogger.log("service id = " + id);
    }
}

LogDemoController.java

@Controller
@RequiredArgsConstructor
public class LogDemoController {
    private final LogDemoService logDemoService;
    private final RequestLogger requestLogger;

    @RequestMapping("/log-demo")
    @ResponseBody
    public String logDemo(HttpServletRequest request) {
        String requestURL = request.getRequestURL().toString();
        requestLogger.setRequestURL(requestURL);

        requestLogger.log("controller test");
        logDemoService.logic("testId");

        return "OK";
    }
}

로그 출력 예시

이제 요청을 보내면 다음과 같은 로그가 찍힌다:

[9e130bc2] request scope bean created  
[9e130bc2][http://localhost:8080/log-demo] controller test  
[9e130bc2][http://localhost:8080/log-demo] service id = testId  
[9e130bc2] request scope bean closed

서로 다른 요청이 들어오면 UUID가 달라져서 요청별 로그 흐름이 명확하게 구분된다.


핵심 개념: 겉보기엔 싱글톤, 실제론 요청마다 생성

RequestLogger는 싱글톤처럼 주입된다.
하지만 실제로는 요청마다 새로운 객체가 생성된다.
그 비밀은 **프록시(proxy)**에 있다.

System.out.println("logger = " + requestLogger.getClass());

출력 결과:

logger = class hello.core.common.RequestLogger$$EnhancerBySpringCGLIB$$...
  • 프록시 객체는 CGLIB로 생성된 "가짜 객체"
  • 이 객체는 실제 요청이 들어오면
    진짜 RequestLogger를 찾아서 내부적으로 위임해준다

왜 좋은가?

  • 컨트롤러나 서비스는 의존성 주입 구조를 바꾸지 않아도 된다
  • RequestLogger는 서비스 계층에서도 바로 쓸 수 있어서 파라미터 전달 없이 로깅 가능
  • 코드가 깔끔하고 테스트도 편하다

정리

  • @RequestScope는 HTTP 요청마다 객체를 새로 만든다
  • proxyMode = TARGET_CLASS 설정으로 싱글톤처럼 주입할 수 있다
  • 실제 주입된 객체는 프록시이고, 요청마다 내부적으로 진짜 객체에게 위임한다
  • 이를 통해 요청 단위로 로그 흐름을 구분하는 구조를 간단하게 만들 수 있다

'SpringBoot' 카테고리의 다른 글

Spring MVC 구조: 어댑터 패턴과 다형성은 왜 중요한가?  (0) 2025.05.20
웹 애플리케이션의 이해  (0) 2025.05.13
스프링 DI: 생존자 주입을 권장하는 이유  (0) 2025.05.05
스프링 빈 자동 등록과 의존성 주입  (1) 2025.05.01
싱글톤 패턴과 스프링의 싱글톤 관리  (0) 2025.04.30
'SpringBoot' 카테고리의 다른 글
  • Spring MVC 구조: 어댑터 패턴과 다형성은 왜 중요한가?
  • 웹 애플리케이션의 이해
  • 스프링 DI: 생존자 주입을 권장하는 이유
  • 스프링 빈 자동 등록과 의존성 주입
moodone
moodone
  • moodone
    무던하게
    moodone
  • 전체
    오늘
    어제
    • 분류 전체보기 (36)
      • Java (7)
      • SpringBoot (24)
      • JavaScript (0)
      • Database (1)
      • Python (0)
      • Git (1)
      • IDE (0)
      • 기타 (3)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    티스토리챌린지
    git
    git bash
    오블완
    Repository
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.5
moodone
RequestScope로 요청별 로그 분리하기
상단으로

티스토리툴바