목차
1. 스프링의 캐싱 방법
2. 사전 준비
3. 예제 코드
1. 스프링의 캐싱 방법
캐싱
- 자주 사용하는 데이터를 어딘가 임시로 저장하고, 빠르게 꺼내 쓰기 위해 사용할 수 있게 하는 프로세스
- 서버의 부담을 줄여 성능을 높이기 위해 사용하기도 함
- ex: DB에서 조회하는게 굉장히 오래 걸리는 데이터를 캐싱해두면, 다음에 조회할 때 DB의 조회 결과를 기다리지 않고 캐싱 영역에서 빠르게 가져다 쓸 수 있음
스프링의 캐싱
- @Cacheable, @CacheEvict 같은 어노테이션을 통해 AOP 기반으로 동작
- 이를 위해서는 @EnableCaching 설정이 필수
2. 사전 준비
이 글에서는 Redis를 이용해 예제 코드를 작성하기 때문에 사전 준비가 필요하다.
별도의 Redis를 사용하지 않는 경우에는 생략해도 된다.
2-1. 로컬에 Redis 설치
🔗 링크로 대체
2-2. Redis 설정
편의를 위해 Spring Data Redis를 이용했다. Gradle의 경우, build.gradle에 아래 설정을 추가하면 된다.
implementation 'org.springframework.boot:spring-boot-starter-data-redis-reactive'
Redis 사용을 위한 설정 파일을 만든다. RedisCacheConfiguration을 여러개 설정할 수도 있고, 더 다양한 옵션을 설정할 수도 있지만 자세한건 생략한다.
@Configuration
public class CacheConfiguration {
@Bean
public RedisCacheManagerBuilderCustomizer redisCacheManagerBuilderCustomizer() {
return builder -> builder.withCacheConfiguration("customerCache", customerCacheConfiguration());
}
private RedisCacheConfiguration customerCacheConfiguration() {
return RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(1)); // 캐싱 시간을 1분으로 설정한다.
}
}
3. 예제 코드
3-1. @EnableCaching 추가
메인 클래스에 @EnableCaching을 추가해주었다.
@EnableCaching
@SpringBootApplication
public class RedisApplication {
public static void main(String[] args) {
SpringApplication.run(RedisApplication.class, args);
}
}
@EnableCaching
* public 메서드에 캐싱 어노테이션이 있는지 모든 Spring bean을 검사하는 post-processor를 동작시킨다.
* 캐싱 어노테이션이 발견되면 메서드 호출을 가로채고, 캐싱 동작을 처리하기 위한 프록시가 자동으로 생성된다.
3-2. 캐싱할 객체 생성
고객 정보를 캐싱한다고 가정한다. Customer와 CustomerRepository를 만들었다.
public class Customer {
private Long id;
private String name;
private String email;
private LocalDate birthday;
// 생성자, getter, setter, ...
}
(DB와 연동하기 귀찮은 관계로) Map을 이용해 Repository를 구현했다.
@Repository
public class CustomerRepository {
private Long id = 1L;
private final Map<Long, Customer> customers = new HashMap<>();
public void save(Customer customer) {
customer.setId(id);
customers.put(id, customer);
id++;
}
public Customer getById(Long id) {
return customers.get(id);
}
public void deleteById(Long id) {
customers.remove(id);
}
public void updateEmail(Long id, String email) {
Customer customer = getById(id);
customer.setEmail(email);
return customer;
}
}
3-3. @Cacheable 예제
이제 캐싱할 준비가 모두 끝났다. 캐싱하고 싶은 데이터를 반환하는 메서드에 @Cacheable을 붙이기만 하면 된다. 만약 이미 캐싱된 데이터를 요청하는 경우, 메서드 호출을 생략하고 캐시에서 결과를 가져온다.
@Service
public class CacheService {
private final CustomerRepository customerRepository;
public CacheService(CustomerRepository customerRepository) {
this.customerRepository = customerRepository;
}
@Cacheable(key = "#id", value = "customerCache")
public Customer findCustomerById(Long id) {
return customerRepository.getById(id);
}
}
@Cacheable 주요 속성
속성 | 설명 |
value | 캐시로 사용될 이름 |
key | 캐시의 key |
condition | 캐시를 저장할 조건 |
unless | 캐시를 저장하지 않을 조건 |
cacheManger | 사용할 캐시 매니저의 이름 지정 |
cacheResolver | 사용할 캐시 리졸버의 이름 지정 |
3-4. @CacheEvict 예제
@CacheEvict는 캐싱된 데이터를 삭제하기 위해 사용한다. 설정에서 직접 캐싱 시간을 지정할 수도 있지만, @CacheEvict를 이용하면 메서드를 호출할 때 직접 제거해줄 수 있다.
@CacheEvict(key = "#id", value = "customerCache")
public void deleteCustomerById(Long id) {
customerRepository.deleteById(id);
}
@CacheEvict 주요 속성
속성 | 설명 |
value | 캐시 이름 |
key | 캐시의 key |
condition | 캐시를 삭제할 조건 |
allEntries | 캐시 전체 삭제 여부 |
beforeInvocation | 메서드 실행 전 캐시 삭제 여부 |
cacheManger | 사용할 캐시 매니저의 이름 지정 |
3-5. @CachePut 예제
@Cacheable은 이미 캐싱된 데이터가 존재하면 메서드를 호출하지 않았다. @CachePut은 메서드는 항상 실행되고 결과는 캐시된다. (캐시 내용을 업데이트 한다.)
@CachePut(key = "#id", value = "customerCache")
public Customer updateCustomerById(Long id, String email) {
return customerRepository.updateEmail(id, email);
}
@CachePut 주요 속성
속성 | 설명 |
value | 캐시 이름 |
key | 캐시의 key |
condition | 캐시를 저장할 조건 |
unless | 캐시를 저장하지 않을 조건 |
cacheManger | 사용할 캐시 매니저의 이름 지정 |
3-6. 추가 예제
여러 캐싱 어노테이션을 사용하고 싶다면 @Caching을 이용해야 한다.
@Caching(evict = {
@CacheEvict("emails"),
@CacheEvict(value="customerCache", key="#customer.email")
})
public String getEmail(Customer customer) {...}
참고
'Develop > Spring+JPA' 카테고리의 다른 글
[JPA] @Where 무시하기 (3) | 2023.09.11 |
---|---|
[Spring Batch] 개념부터 코드까지 (0) | 2023.07.21 |
[Spring] '/', 문자열인가 경로인가 그것이 문제로다 (4) | 2023.05.12 |
[Java] 스프링 부트를 제거해서 생긴 일 (2) | 2023.02.05 |
[Spring/AOP] JDK Dynamic Proxy vs CGLIB Proxy (0) | 2022.11.23 |