[이슈해결] Redis를 통한 페이지 간의 데이터 전달

MinSeong Kang·2022년 9월 13일
0

이슈해결

목록 보기
7/12

현재 Spring과 Thyemleaf를 활용하여 [QR코드 기반 간편 주문 서비스]를 개발하고 있다.
장바구니 페이지에서 주문페이지까지 넘어가는 로직에 대해서 구현하는 도중 한가지 이슈가 발생하였다.
아래는 간단한 장바구니페이지와 주문페이지의 UI이다.

사용자가 주문을 하는 순서

  • 사용자가 여러 메뉴와 그에 따른 옵션을 선택하여 장바구니에 담는다.
  • 장바구니 페이지에서 사용자가 담은 장바구니 목록을 확인할 수 있다.
  • 사용자는 먹고가기 혹은 가져가기 선택지 중 하나를 선택하고 주문 버튼을 누르면 주문 페이지로 넘어간다.
  • 주문 페이지에서 요청사항과 결제 수단을 선택하고 결제하기 버튼을 누르면 간편 결제가 이루어진다.

사용자가 해당 절차를 거치면, 하나의 Order 엔티티가 생성되고 데이터 베이스에 저장된다.

현재 Order 엔티티에 저장되어야 할 필드는 다음과 같다.

  • 장바구니목록 (List<Object>)
  • 주문타입 (OrderType.FORHERE or OrderType.TAKEOUT)
  • 총 금액 (int)
  • 요청 사항 (String)
  • 결제 수단 (PayType.CASH or PayType.KAKAOPAY or PayType.NAVERPAY)
  • ...

한 마디로 장바구니 페이지에 보여지는 데이터와 사용자가 선택하는 데이터 + 주문페이지에서 사용자가 입력하고 선택하는 데이터가 Order 엔티티에 저장되어야 한다.


이슈

Spring MVC에서는 Model을 통해 페이지 이동간에 데이터를 주고 받을 수 있다. 사용자가 입력하거나 선택하는 값에 대해서는 쉽게 Model에 담을 수 있지만, 조회되는 데이터들에 대해서는 input hidden 태그를 통해 Model에 담아 넘겨줄 수 있다. 총 금액의 경우 단순 int 타입이라 손쉽게 넘길 수 있었지만 장바구니 목록의 경우 Dto가 List에 담겨져있기 때문에 input hidden 태그를 이용하여 모델에 저장하려 했으나 해결할 수 없었다. 따라서 다른 방법을 생각해야 했다.


이슈 해결 방안 - 1 (데이터베이스 활용)

첫번째로 생각한 로직은 다음과 같다.

  • 장바구니 페이지에서의 데이터를 Order 엔티티에 담고, 나머지는 null로 담아 데이터베이스에 저장한다.
  • 이후 주문하기 버튼 클릭시, Order 엔티티의 id값을 PathVariable로 넘겨준다.
  • 주문 페이지에서는 Path Variable로 넘겨온 id값을 통해 데이터베이스에서 Order 엔티티를 조회하고,
  • 나머지 데이터들을 Order 엔티티를 수정하며 넣어준다.

충분히 다음과 같은 방법으로 주문하기 기능을 구현할 수 있었지만, 엔티티를 2번을 거처서 완성하는 부분과 엔티티를 만들기 위해서 데이터 베이스에 저장,조회,수정 순으로 3번 접근해야하는 것이 마음에 들지 않았다.

  • 안전성 문제 : 엔티티를 한번에 만들지 않고 두번 나누어서 엔티티를 완성한다.
  • 성능 문제 : 엔티티를 완성하기 위해서 데이터베이스에 3번 접근을 해야한다.

이슈 해결 방안 - 2 (Redis 활용)

첫번째 해결방안은 안정성과 성능적인 측면에 문제가 있다고 생각했다..!
페이지 간의 데이터를 데이터베이스를 거쳐서 전달하는 것이 아닌 다른 방법에 대해서 고민하려는 중 Redis가 떠올랐다. 지금까지 한번도 Redis를 사용해본 경험은 없지만, Redis가 어떤 문제를 해결해주고 무엇을 위해 사용되는지는 조금 알고 있었기 때문에 충분히 해당 이슈를 해결할 수 있다고 생각했다.

따라서, Spring Data Redis를 사용하여 Redis를 임시 저장소 & 캐시역할을 하게끔 구현하여 다음과 같은 문제를 해결하였다.

  • 장바구니 페이지에서의 데이터를 Redis에 저장한다.
  • 주문 페이지에서 Redis에 저장되어 있는 데이터를 가져와, 주문 페이지에서의 데이터와 함께 Order 엔티티를 생성하여 저장한다.

로직이 단순해지고 안정성적으로도 성능적으도로 개선이 된 것 같다. 생각보다 Spring Data Redis를 프로젝트에 적용하는 것은 어렵지 않았다.

Docker를 통해 Redis 서버를 구동시키고, Spring Data Redis 의존성을 넣은 후

[application.properties]

spring.redis.host=127.0.0.1
spring.redis.port=6379

[RedisConfig.java]

@Configuration
@EnableRedisRepositories
public class RedisConfig {

    @Value("${spring.redis.host}")
    private String redisHost;

    @Value("${spring.redis.port}")
    private int redisPort;

    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        return new LettuceConnectionFactory(redisHost, redisPort);
    }

    @Bean
    public RedisTemplate<String, Object> redisTemplate() {
        GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();

        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory());
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(genericJackson2JsonRedisSerializer);
        return redisTemplate;
    }
}

[OrderDao.java]

@Repository
@RequiredArgsConstructor
public class OrderDao {

    private final RedisTemplate<String, Object> redisTemplate;

    public void addOrderDtoFromCart(OrderDtoFromCart dto, String userNickName) {
        String key = keyGenerate(userNickName);
        redisTemplate.opsForValue().set(key, dto);
        redisTemplate.expire(key, 5, TimeUnit.MINUTES);
    }

    public OrderDtoFromCart getOrderDtoFromCart(String userNickName) {
        String key = keyGenerate(userNickName);
        return (OrderDtoFromCart) redisTemplate.opsForValue().get(key);
    }

    private String keyGenerate(String key) {
        return key + " : orderDto";
    }
}

코드를 추가하고, Order 컨트롤러에서 위에서 말한 로직을 적용하였다.
Spring Data Redis를 통해 쉽게 OrderDao를 통해 키-벨류로 Redis에 데이터를 저장하고, 조회할 수 있었다.


프로젝트 개선점

개발하고 있는 프로젝트에서는 생각보다 각 컨트롤러에서 동일한 값들에 대해서 데이터베이스 조회를 많이 한다. 이런 부분에 대해서 Redis를 활용한다면, 데이터베이스 서버의 부하 분산을 통해 서버의 안정성을 높일 수 있을 것이라고 생각하고, 성능적으로도 많이 개선될 것이라 생각한다. 따라서 핵심 기능들을 모두 구현한 이후 Redis를 활용하여 성능적으로 더 개선해야겠다는 생각이 들었다.

0개의 댓글