YS's develop story
Spring Boot + Redis 캐시 적용 가이드 (@Cacheable 활용) 본문
@SpringBootApplication
@EnableJpaAuditing
@EnableCaching //캐싱 기능을 활성화 하겠다는 어노테이션 -> redis활용을 위해 추가
public class SpringJavaApplication {
public static void main(String[] args) {
SpringApplication.run(SpringJavaApplication.class, args);
}
}
@EnableCaching 어노테이션 추가
캐싱 기능을 활성화하겠다는 어노테이션입니다.
//redis
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
관련 gradle 추가
//redis config 설정 클래스들
@Configuration
public class RedisConfig {
@Bean
LettuceConnectionFactory connectionFactory() {
return new LettuceConnectionFactory();
}
@Bean
public RedisTemplate<String, Object> redisTemplate() {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory());
return template;
}
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
RedisCacheConfiguration cacheConfig = RedisCacheConfiguration.defaultCacheConfig()
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()))
.entryTtl(Duration.ofMinutes(10L)); // 캐시의 TTL 설정 (10분)
return RedisCacheManager
.RedisCacheManagerBuilder
.fromConnectionFactory(factory)
.cacheDefaults(cacheConfig)
.build();
}
//Redis는 LocalDateTime과 같은 일부 타입을 직렬화할 수 없기 때문에
//Redis의 직렬화 도구인 GenericJackson2JsonRedisSerializer를 구성하여 Java 8의 시간 관련 타입을 처리할 수 있도록 함.
@Bean
public RedisSerializer<Object> redisSerializer() {
return new GenericJackson2JsonRedisSerializer(new ObjectMapper().registerModule(new JavaTimeModule()));
}
}
Redis config 추가
@Cacheable(cacheNames = "allProducts", key = "#pageable")
//상품 전체 조회
public List<GetProductsResponseDTO> getAllProducts(Pageable pageable) {
Page<Product> productList = productRepository.findAll(pageable);
List<GetProductsResponseDTO> getProductsResponseDTOList = productList.getContent().stream()
.map(product -> {
List<String> imageList = product.getProductImages().stream()
.map(ProductImages::getImageUrl)
.collect(Collectors.toList());
return GetProductsResponseDTO.fromEntity(product, imageList);
})
.collect(Collectors.toList());
return getProductsResponseDTOList;
}
@Cacheable 어노테이션을 메서드에 추가하면 해당 메서드의 리턴값이 캐시에 저장되며,
이후 동일한 매개변수로 호출될 때 캐시된 값을 반환합니다.
Spring의 @Cacheable 어노테이션은 스프링 프레임워크가 제공하는 캐싱 추상화 기능을 이용합니다.
이 기능을 사용하면 메서드의 결과를 캐시에 저장하고, 동일한 매개변수로 메서드가 호출될 때 캐시된 결과를 반환할 수 있습니다.
어노테이션 하나로 이렇게 동작할 수 있는 이유는 Spring이 프록시를 사용하여 메서드 호출을 감싸고,
각 메서드 호출이 발생할 때 캐시 관련 로직을 수행하는 프록시를 생성하기 때문입니다.
이 프록시는 메서드 호출을 처리하기 전에 캐시에 저장된 결과를 확인하고,
저장된 결과가 있는 경우에는 캐시 된 결과를 반환합니다.
따라서 개발자는 어노테이션만으로 간편하게 캐싱 기능을 활성화할 수 있습니다.
이를 통해 반복적으로 동일한 요청이 들어올 때마다 매번 비싼 연산을 수행하지 않고,
캐시된 결과를 효율적으로 반환함으로써 성능을 향상할 수 있습니다.
@Cacheable 어노테이션을 사용하여 캐싱을 적용하면 Spring은 해당 메서드의 호출을 감싸는 프록시 객체를 생성합니다. 이 프록시 객체는 메서드가 호출될 때 캐시 관련 로직을 수행하고, 캐시에 저장된 결과를 반환하거나 메서드의 실행 결과를 캐시에 저장합니다.
프록시 객체는 다음과 같은 과정을 거쳐 생성됩니다:
- @Cacheable 어노테이션이 적용된 메서드가 호출됩니다.
- Spring AOP가 이를 감싸는 프록시 객체를 생성합니다.
- 프록시 객체는 메서드 호출 전에 캐시에 저장된 결과를 확인하고, 저장된 결과가 있는 경우 캐시된 결과를 반환합니다.
- 저장된 결과가 없는 경우 메서드를 실행하고, 실행 결과를 캐시에 저장한 후 반환합니다.
이러한 프록시 패턴을 통해 개발자는 @Cacheable 어노테이션만으로 간편하게 캐싱 기능을 활성화할 수 있습니다.
프록시 객체가 캐싱 관련 로직을 처리하므로 개발자는 비즈니스 로직에만 집중할 수 있습니다.
["java.util.ArrayList",[{"@class":"cohttp://m.practice.spring_java.domain.products.dto.response.GetProductsResponseDTO","productId":12,"name":"도시락","price":3000,"description":"고기도시락","inventory":10,"productImages":["java.util.ArrayList",[]],"productType":"SNACK"},{"@class":"cohttp://m.practice.spring_java.domain.products.dto.response.GetProductsResponseDTO","productId":11,"name":"주먹밥","price":2000,"description":"먹기편한 주먹밥","inventory":10,"productImages":["java.util.ArrayList",[]],"productType":"SNACK"},{"@class":"cohttp://m.practice.spring_java.domain.products.dto.response.GetProductsResponseDTO","productId":10,"name":"아메리카노","price":1500,"description":"시원한 아메리카노","inventory":3,"productImages":["java.util.ArrayList",[]],"productType":"BEVERAGE"},
.....
실제로 해당 상품 조회 api를 실행한 후, Redis를 확인해 본다면
그 결괏값들이 redis에 저장되어 있는 것을 확인할 수 있습니다.
@Cacheable
메서드에 붙여서 사용할 것을 권장하고, 캐시를 저장 또는 조회할 때 사용 합니다.
메서드에서 조회할 캐시 데이터가 캐시 서버에 있는지 확인하고, 있으면 반환하고 없으면 새로 저장한 후 반환
@CachePut
메서드에 붙여서 사용할 것을 권장하고, 캐시를 저장 또는 수정하는 목적으로만 사용합니다.
즉, 메서드에서 조회하고자 하는 데이터는 DB에서 직접 꺼내서 전달하고, 그 결과를 캐시 서버에 반영하기만 함
@CacheEvict
캐시 제거를 위해서 사용합니다.
역시 메서드에 붙여서 사용하는 것이 바람직하고, 삭제 기능을 갖고 있는 메서드에 붙여서 사용할 수 있습니다.
'Spring' 카테고리의 다른 글
Redis를 이용해 JWT 활용하기 (Refresh Token 저장 시 Redis활용) (0) | 2024.05.30 |
---|---|
Spring, OAuth2 + JWT 를 활용하여 소셜 로그인 구현하기 2편 (구글 및 네이버) [Spring 3.1.5, java 17] (0) | 2023.12.11 |
Spring, OAuth2 + JWT 를 활용하여 소셜 로그인 구현하기 1편 (구글 및 네이버) [Spring 3.1.5, java 17] (6) | 2023.12.08 |
spring jpa에 Querydsl 적용하기 [spring 3.1.5, java 17] (0) | 2023.12.06 |
springboot certbot으로 ssl인증서 받아서 https로 배포하기 (1) | 2023.11.30 |