← Back to Home

SpringBoot-Redis 로 만든 디저트가게 예제

- SpringBoot 와 Redis를 이용한 디저트가게 예제 - Redis를 사용하기위해서 디저트가게 예제를 만들어보자

목차

- A dessert shop example using SpringBoot and Redis - Let's create a dessert shop example to learn how to use Redis

Table of Contents

## 스택
## Stack
- SpringBoot - Lombok - Spring Reactive Web - Spring Data Reactive Redis - Validation - Gradle - Java 11 + - Redis

레디스서버 띄우기

  • 레디스 서버를 실행시켜야 함
  • 로컬에서 임베드로 생성하거나, 로컬에 Docker로 생성하거나, 클라우드에 설치 된 redis 사용
  • docker-compose를 사용해서 설치 함
    • docker-compose.redis.yml
      version: "3"
      services:
          redis-docker:
              image: redis:latest
              command: redis-server --requirepass qwerqwer123 --port 6379
              container_name: "docker-redis"
              labels:
              - "name=redis"
              - "mode=standalone"
              volumes:
              - /Users/wool/Database-docker/data/redis:/data
              ports:
              - 6379:6379
    • redis에 비밀번호를 적용하지 않는다면 --requirepass 옵션을 빼고 작성
    • redis서버 실행
      $ docker-compose -f docker-compose.redis.yml up -d

Configuration 작성하기

  • ReactiveRedisConfiguration.java
  • Redis와 Spring을 연결하기 위해 설정 파일을 작성
  • reactRedisConnectionFactory 빈을 생성하여 레디스 연결 팩토리를 생성
    @Bean
    public ReactiveRedisConnectionFactory reactiveRedisConnectionFactory() {
        return new LettuceConnectionFactory(
            Objects.requireNonNull(env.getProperty("spring.redis.host")),
            Integer.parseInt(Objects.requireNonNull((env.getProperty("spring.redis.port"))))
        );
    }
    혹은 @Value어노테이션을 사용해서 아래와 같이 데이터를 깔끔하게 정리 할 수 있음
        @Value("${spring.redis.host}")
        private String redisHost;
     
        @Value("${spring.redis.port}")
        private int redisPort;
     
        @Bean
        public ReactiveRedisConnectionFactory reactiveRedisConnectionFactory() {
            return new LettuceConnectionFactory(redisHost, redisPort);
        }
  • Redis의 데이터를 주고받아야 하기 때문에 JSON형식을 지원하는 Jackson 라이브러리를 사용한 Operation Bean을 세팅한다.
    @Bean
    public ReactiveRedisOperations<String, Object> redisOperations(ReactiveRedisConnectionFactory reactiveRedisConnectionFactory) {
        Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
     
        RedisSerializationContext.RedisSerializationContextBuilder<String, Object> builder =
                RedisSerializationContext.newSerializationContext(new StringRedisSerializer());
     
        RedisSerializationContext<String, Object> context = builder.value(serializer).hashValue(serializer)
                .hashKey(serializer).build();
     
        return new ReactiveRedisTemplate<>(reactiveRedisConnectionFactory, context);
    }

CRUD API 작성하기

  • CRUD를 작성하기 위해 우선 모델 클래스가 필요

Domain 모델 작성

  • 도메인 객체 생성하기
    • pk, 제품명, 카테고리, 설명, 가격, 출시일 을 가진 Dessert 도메인객체 생성

Database에 접근하는 Repository 작성

  • Redis 데이터베이스와 연결 할 Repository를 작성한다
  • Interface를 사용해서 CRUD 기능을 명시하고, 구현체를 사용해 각 내부 로직을 구현
  • JSON 파싱을 위해서 ObjectMapperUtils 클래스 생성
  • Redis와 연결을 위해서 ReactiveRedisComponent 클래스 생성
  • DessertRepository
    public interface DessertRepository {
     
        Mono<Dessert> save(Dessert dessert);
     
        Mono<Dessert> get(String key);
     
        Flux<Dessert> getAll();
     
        Mono<Long> delete(String id);
    }
  • RedisDessertRepository
    public class RedisDessertRepository implements DessertRepository {
     
        private ReactiveRedisOperations<String, Object> redisOperations;
     
        public RedisDessertRepository(ReactiveRedisOperations<String, Object> redisOperations) {
            this.redisOperations = redisOperations;
        }
     
        @Override
        public Mono<Dessert> save(Dessert dessert) {
            return redisOperations.opsForValue().set(dessert.getId(), dessert);
        }
     
        @Override
        public Mono<Dessert> get(String key) {
            return redisOperations.opsForValue().get(key);
        }
     
        @Override
        public Flux<Dessert> getAll() {
            return redisOperations.keys("*")
                    .flatMap(redisOperations::opsForValue::get)
                    .cast(Dessert.class);
        }
     
        @Override
        public Mono<Long> delete(String id) {
            return redisOperations.delete(id);
        }
    }

Service 작성

  • Interface를 먼저 명시 해 준 후에 구현체로 구현
  • DessertService
    • DessertService
      public interface DessertService {
       
          Mono<Dessert> create(Dessert dessert);
       
          Flux<Dessert> getAll();
       
          Mono<Dessert> getOne(String id);
       
          Mono<Long> deleteById(String id);
       
      }
    • DessertServiceImpl
      @RequiredArgsConstructor
      @Service
      public class DessertServiceImpl implements DessertService {
       
          private final RedisDessertRepository redisDessertRepository;
       
          @Override
          public Mono<Dessert> create(Dessert dessert) {
              return redisDessertRepository.save(dessert);
          }
       
          @Override
          public Flux<Dessert> getAll() {
              return redisDessertRepository.getAll();
          }
       
          @Override
          public Mono<Dessert> getOne(String id) {
              return redisDessertRepository.get(id);
          }
       
          @Override
          public Mono<Long> deleteById(String id) {
              return redisDessertRepository.delete(id);
          }
      }

Contoller 생성

  • DessertController를 생성하여 API Call을 Response 할 수 있도록 작성
    @RestController
    @RequestMapping("/v1")
    @RequiredArgsConstructor
    public class DessertController {
     
        private final DessertServiceImpl dessertService;
     
        @PostMapping("/dessert")
        @ResponseStatus(HttpStatus.CREATED)
        public Mono<Dessert> addDessert(@RequestBody @Valid Dessert dessert) {
            return dessertService.create(dessert);
        }
     
        @GetMapping("/dessert")
        public Flux<Dessert> getAllDessert() {
            return dessertService.getAll();
        }
     
        @GetMapping("/dessert/{id}")
        public Mono<Dessert> getDessert(@PathVariable String id) {
            return dessertService.getOne(id);
        }
     
        @DeleteMapping("/dessert/{id}")
        public Mono<Long> deleteDessert(@PathVariable String id) {
            return dessertService.deleteById(id);
        }
     
    }