← Back to Home

SpringBoot에서 Redis 캐시를 사용하기

이번에는 SpringBoot와 데이터접근과 관련해서 작성 해 보려고한다

많은 경우에 Spring은 DB에 담긴 데이터를 가져오고 전달하는 역할을 하고있는데 이 때, Redis를 가지고 데이터에 빠르게 접근하는 방법을 정리 해 보려고 한다

Redis는 데이터베이스로도 사용되고, Message Broker로도 사용되지만 Cache Manager에 더 많이 사용된다

한번 만들어보면서 정리 해 보도록 하겠다

사용 할 Redis 간단 설명

  • Redis 사용 형태

    • In Memory Database
      • 데이터베이스 작업을 수행하기 위해 사용, No SQL 데이터베이스 역할을 함
      • No Tables, No Sequences, No Joins의 개념이 있음
      • String, Hash, List, Set 등의 형태로 데이터 저장
  • Cache

    • Redis를 캐시로 사용하여 애플리케이션의 성능을 높인다
  • Message Broker(MQ)

    • Redis를 MQ로 사용하여 메시지를 전달한다
  • Redis 캐시란

    • Redis Cache는 Redis에서 제공하는 캐시 관리 기능
    • 사용자가 응용프로그램의 더 나은 성능을 위해 적용/사용
    • 엑세스 되는 데이터를 저장하는 캐시
    • 한번의 데이터 요청은 하나의 네트워크 호출인데, 이를 최소화 할 수 있음
  • Redis Cache 활성화를 위한 @Annotation

    • @EnableCaching

      • SpringBoot에게 캐싱기능이 필요하다고 전달
      • SpringBoot Starter class에 적용
    • @Cacheable

      • DB에서 애플리케이션으로 데이터를 가져오고 Cache에 저장하는데 사용
      • DB에서 데이터를 가져오는 메서드에 적용
    • @CachePut

      • DB의 데이터 업데이트가 있을 때 Redis Cache에 데이터를 업데이트
      • DB에서 PUT/PATCH와 같은 업데이트에서 사용
    • @CacheEvict

      • DB의 데이터 삭제가 있을 때 Redis Cache에 데이터를 삭제
      • DB에서 DELETE와 같은 삭제에서 사용

프로젝트 구조

아래는 이해를 돕기 위해서 사용 한 프로젝트 구조를 적어보았다

├── build.gradle
├── docker-compose-database.yml
└── src
    ├── main
    │   ├── java/com/example/springbootredissimplestarter
    │   │   ├── SpringBootRedisSimpleStarterApplication.java
    │   │   ├── config/RedisCacheConfig.java
    │   │   ├── controller/OrderController.java
    │   │   ├── domain/Order.java
    │   │   ├── exception/OrderNotFoundException.java
    │   │   ├── repository/OrderRepository.java
    │   │   └── service/OrderService.java
    │   └── resources/application.properties

준비 할 것

당연하지만 Spring BootRedis를 준비해야 한다.

SpringBoot는 Spring Data Redis라는 디펜던시를 통해서 Redis와 연결을 지원한다.

Database

데이터베이스 환경을 먼저 구축하려고 한다. docker-composeDatabase를 구성하고자한다.

version: "3"
services:
    mysql-docker:
    image: arm64v8/mariadb
    ports:
        - "3306:3306"
    environment:
    TZ: Asia/Seoul
    MYSQL_ROOT_PASSWORD: qwerqwer123
    MYSQL_DATABASE: rediswithspring
    MYSQL_USER: paul
    MYSQL_PASSWORD: qwerqwer123
 
    redis-docker:
    image: redis:latest
    command: redis-server --port 6379
    ports:
        - 6379:6379

SpringBoot Dependencies

  • Spring Web
  • Spring Data JPA
  • MYSQL Driver
  • Spring Data Redis
  • Lombok
  • Spring Boot Devtools

SpringBoot와 Redis를 사용하여 프로젝트 작성

배달음식 주문 어플리케이션을 만들어보려고 한다. CRUD와 해당하는 쿼리가 캐싱이 되었는지에 초점을 맞춰보려고 한다.

SpringBoot 어플리케이션 세팅

SpringBoot에게 Redis Cache를 사용 할 것이라고 알려주어야 한다. @EnableCaching을 스타터 클래스에 적용한다.

@SpringBootApplication
@EnableCaching
public class SpringBootRedisSimpleStarterApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringBootRedisSimpleStarterApplication.class, args);
    }
}

application properties

spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/rediswithspring
spring.datasource.username=paul
spring.datasource.password=qwerqwer123
spring.jpa.database-platform=org.hibernate.dialect.MariaDB103Dialect
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update
spring.cache.type=redis
spring.cache.redis.cache-null-values=true

SpringBoot Redis 환경설정 세팅

Data를 가져오고 보낼 때, 우리가 만든 도메인 모델을 Serialize 해 주기 위해 설정이 필요하다.

@Configuration
@EnableCaching
public class RedisCacheConfig {
 
    @Bean
    public CacheManager testCacheManager(RedisConnectionFactory cf) {
        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()))
                .entryTtl(Duration.ofMinutes(3L));
 
        return RedisCacheManager.RedisCacheManagerBuilder.fromConnectionFactory(cf).cacheDefaults(redisCacheConfiguration).build();
    }
}

Domain 모델

Order 모델을 생성한다. DDL에 order이 예약어이기 때문에 테이블 이름 명시(@Table)가 필요하다.

@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "ORDERS")
public class Order {
    @Id
    @GeneratedValue
    private Integer id;
    private String orderCode;
    private String orderObject;
    private String orderStatus;
    private Integer orderPrice;
}

Service with Cache Annotations

@Service
public class OrderServiceImpl implements OrderService {
 
    @Autowired
    private OrderRepository orderRepository;
 
    @Override
    @Cacheable(value = "Order", key = "#orderId", cacheManager = "testCacheManager")
    public Order getOrder(Integer orderId) {
        return orderRepository.findById(orderId).orElseThrow(() -> new OrderNotFoundException("Order Not Found"));
    }
 
    @Override
    @CachePut(value = "Order", key = "#orderId", cacheManager = "testCacheManager")
    public Order updateOrder(Order order, Integer orderId) {
        Order orderObject = orderRepository.findById(orderId).orElseThrow(() -> new OrderNotFoundException("Order Not Found"));
        // status 업데이트 로직
        return orderRepository.save(orderObject);
    }
 
    @Override
    @CacheEvict(value = "Order", key = "#orderId", cacheManager = "testCacheManager")
    public void deleteOrder(Integer orderId) {
        Order orderObject = orderRepository.findById(orderId).orElseThrow(() -> new OrderNotFoundException("Order Not Found"));
        orderRepository.delete(orderObject);
    }
 
    @Override
    @Cacheable(value = "Order", cacheManager = "testCacheManager")
    public List<Order> getAllOrders() {
        return orderRepository.findAll();
    }
}

실행해보기

GET 요청을 많이 날려 보았을 때 쿼리가 Request 한 만큼 찍히지 않는 것이 보인다면 Redis Cache 기능을 사용하고 있는 것을 확인 할 수 있다.

# POST
curl -d '{"orderCode":"AGGEKF123","orderObject":"로제떡볶이","orderStatus":"ready","orderPrice":17000}' \
  -H "Content-Type: application/json" -X POST http://localhost:8080/order
 
# GET
curl -X GET http://localhost:8080/order/1