@RequestCache: HTTP 요청 범위 캐싱을 위한 커스텀 애너테이션 개발기 (새 탭에서 열림)

웹 애플리케이션에서 하나의 HTTP 요청 내에 발생하는 중복된 API 호출은 성능 저하와 리소스 낭비를 초래하며, 이를 해결하기 위해 요청 범위(Request Scope) 내에서 결과를 캐싱하는 @RequestCache 커스텀 애너테이션을 개발했습니다. 이 기능은 Spring의 RequestAttribute를 활용해 요청별로 독립적인 캐시 공간을 보장하며, 요청 종료 시 자동으로 메모리가 정리되는 효율적인 생명주기 관리 구조를 가집니다. 이를 통해 복잡한 파라미터 전달이나 부적절한 TTL 설정 문제를 해결하고 시스템의 전반적인 응답 속도를 개선할 수 있습니다.

파라미터 전달 및 범용 캐시의 한계

  • 응답 객체 전달 방식의 복잡성: 데이터를 실제 사용하는 말단 서비스까지 객체를 넘기기 위해 중간 계층의 모든 메서드 시그니처를 수정해야 하며, 이는 코드 가독성을 떨어뜨리고 관리를 어렵게 만듭니다.
  • 전략 패턴의 유연성 저하: 공통 인터페이스를 사용하는 경우, 특정 구현체에서만 필요한 데이터를 파라미터에 포함해야 하므로 인터페이스의 범용성이 훼손됩니다.
  • TTL(Time To Live) 설정의 딜레마: Redis나 로컬 캐시 사용 시 TTL이 너무 짧으면 동일 요청 내 중복 호출을 막지 못하고, 너무 길면 서로 다른 요청 간에 의도치 않은 데이터 공유가 발생하여 데이터 정합성 문제가 생길 수 있습니다.

@RequestCache의 특징과 동작 원리

  • RequestAttribute 기반 저장소: 내부적으로 ThreadLocal을 사용하는 RequestAttribute에 데이터를 저장하여, 스레드 간 격리를 보장하고 각 HTTP 요청마다 독립적인 캐시 인스턴스를 유지합니다.
  • 자동 생명주기 관리: 캐시의 수명이 HTTP 요청의 생명주기와 일치하므로 별도의 만료 시간을 계산할 필요가 없으며, 요청 완료 시 Spring의 FrameworkServlet에 의해 자동으로 정리되어 메모리 누수를 방지합니다.
  • AOP 기반의 간편한 적용: 비즈니스 로직을 수정할 필요 없이 캐싱이 필요한 메서드에 @RequestCache 애너테이션을 선언하는 것만으로 손쉽게 중복 호출을 제거할 수 있습니다.

@RequestScope와 프록시 메커니즘

  • 프록시 패턴 활용: @RequestScope로 선언된 빈은 Spring 컨테이너에 프록시 객체로 등록되며, 실제 메서드 호출 시점에 현재 요청에 해당하는 실제 인스턴스를 찾아 호출을 위임합니다.
  • 상태 저장 방식: AbstractRequestAttributesScope 클래스를 통해 실제 객체가 RequestAttributes 내에 저장되며, 이를 통해 동일 요청 내에서는 같은 인스턴스를 공유하게 됩니다.

동일 요청 내에서 외부 API 호출이 잦거나 복잡한 연산이 반복되는 서비스라면, 전역 캐시를 도입하기 전 @RequestCache와 같은 요청 범위 캐싱을 통해 코드 순수성을 유지하면서도 성능을 최적화할 것을 권장합니다.