[Spring] @Cacheable에서 발생하는 self-invocation 문제 - 컴도리돌이
@Cacheable self-invocation (in effect, a method within the target object calling another method of the target object). The cache annotation will be ignored at runtime
@Cacheable 어노테이션을 사용해서 메서드 결과를 캐싱하려고 할 때, 같은 클래스 내의 다른 메서드를 호출하면 캐싱이 적용되지 않는 문제가 발생합니다. 저 역시 처음 이 문제를 겪고는 “왜 캐싱이 안 되지?”라는 의문을 가졌어요. 🤔
예를 들어, 아래처럼 getList 메서드에서 getTypeAList 메서드를 호출할 때 @Cacheable을 설정했는데도 캐시가 적용되지 않더라고요
public SiteListResponse getList(String type) throws Exception {
return getTypeAList(type);
}
@Cacheable(value = "getTypeA", cacheManager = "comdolliManager")
public List<String> getTypeAList(String type) throws Exception {
// Some logic to retrieve data
return result;
}
왜일까요? 이유는 @Cacheable 어노테이션이 프록시 방식을 사용해 캐싱을 처리하기 때문입니다. Spring에서 @Cacheable은 기본적으로 AOP 프록시 패턴을 통해 메서드를 가로채고 캐시 처리를 수행해요. 이 프록시는 클래스 외부에서 호출될 때만 적용됩니다. 즉 self-invocation이라 불리는 클래스 내부 메서드 간 호출은 프록시를 거치지 않기 때문에 @Cacheable이 무시돼요! 🤓
[Spring] AOP(Aspect-Oriented Programming)에 대해서 - 컴도리돌이
개발을 하면서 "이 부분은 코드가 반복되는데, 더 좋은 방법이 없을까"라는 생각을 정말 매 순간 하는 거 같아요. 🥲 그중 하나의 해결 책으로 AOP가 그 해답이 될 수 있어요 🤔AOP(Aspect-Oriented Prog
comdolidol-i.tistory.com
그러므로 캐시를 적용하려는 메서드를 같은 클래스가 아닌 다른 빈(Bean)에서 호출하도록 설계해야합니다. 가장 일반적인 방법은 인터페이스를 만들어 getTypeAList 메서드를 호출할 때 Spring이 프록시를 적용할 수 있도록 하는 것이에요.
아래처럼 getTypeAList 메서드를 별도의 서비스 클래스로 분리하면 문제 없이 캐시가 적용됩니다.
// testService.java
@Service
public class TestService {
@Cacheable(value = "getTypeA", cacheManager = "comdolliManager")
public List<String> getTypeAList(String type) throws Exception {
// Some logic to retrieve data
return result;
}
}
// MainService.java
@Service
public class MainService {
private final TestService testService;
public MainService(TestService testService) {
this.testService = testService;
}
public SiteListResponse getList(String type) throws Exception {
return testService.getTypeAList(type);
}
}
이제 getList 메서드가 TestService를 통해 getTypeAList를 호출하게 되었기 때문에, @Cacheable이 정상적으로 작동돼요. 이처럼 Spring의 프록시 메커니즘을 이해하고 설계하는 것이 중요합니다. 자칫 잘못된 방식으로 설계하게 되면 캐싱이 적용되지 않을 뿐 아니라, 코드의 유지보수성에도 영향을 미칠 수 있습니다 🤔