@Cacheable 로 코드 테이블 캐싱@Service
public class CodeService {
@Cacheable(cacheNames = "codes", key = "#groupCode")
public List<CodeDto> findByGroup(String groupCode) {
return codeMapper.selectByGroup(groupCode); // MyBatis 조회
}
@CacheEvict(cacheNames = "codes", key = "#groupCode")
public void update(String groupCode, CodeDto dto) {
codeMapper.update(dto);
}
@CacheEvict(cacheNames = "codes", allEntries = true)
public void reloadAll() {
// 배치로 코드 테이블을 통째로 갱신한 뒤 호출
}
}findByGroup 은 최초 호출만 MyBatis 조회를 타고, 이후 같은 groupCode 는 캐시에서 반환됩니다. update 는 evict 전략으로 해당 그룹만 지우고, reloadAll 은 clear 전략으로 전체를 비웁니다.
@Cacheable(cacheNames = "userExists", unless = "#result == null")
public UserDto findUser(String userId) {
UserDto found = userMapper.selectById(userId);
return found; // null 이면 Spring 기본 설정상 캐시 안 됨(unless 없어도 동일)
}
// null 도 캐시하고 싶다면 별도 캐시에 Optional 로 감싸 저장한다.
@Cacheable(cacheNames = "userExistsNegative")
public Optional<UserDto> findUserSafe(String userId) {
return Optional.ofNullable(userMapper.selectById(userId));
}findUserSafe 는 결과가 없어도 Optional.empty() 라는 "값"을 캐시하므로, 존재하지 않는 ID 를 반복 조회해도 DB 에 매번 가지 않습니다. 이 캐시는 TTL 을 짧게(예: 1분) 두어야 실제로 데이터가 생겼을 때 빨리 반영됩니다.
@Service
public class ReportService {
@Autowired
private ReportService self; // 프록시 주입으로 우회
public void generate() {
// this.summary(...) 로 직접 호출하면 캐시가 동작하지 않는다.
self.summary("2026-09"); // 프록시를 거치므로 캐시가 동작한다.
}
@Cacheable(cacheNames = "reportSummary")
public String summary(String month) {
return heavyAggregate(month);
}
}같은 클래스 안에서 this.summary(...) 로 호출하면 Spring AOP 프록시를 거치지 않아 @Cacheable 이 무시됩니다. 자기 자신을 빈으로 주입받아 self.summary(...) 처럼 호출하면 프록시를 통과해 캐시가 정상 동작합니다.