DB/Redis

Spring boot와 도커에서 Redis 적용

동그리담 2024. 6. 20. 16:48

Redis란?

시스템 메모리를 사용하는 키-값 데이터 스토어

인메모리 상태에서 데이터를 처리함으로써 빠르고 가볍다는 장점이 있음

필자는 현 Redis를 JWT 토큰 중 리프래쉬 토큰의 관리와 블랙토큰, 게시글 어뷰징 방지를 위해 적용했습니다.

 

JWT의 로그아웃과 리프레쉬토큰을 구현하면서 Redis를 알게돼었고
추가로 배포시 Redis에 관련된 설정에서 애를 먹어서 글로 정리하게 되었습니다.

 

Build.gradle

implementation 'org.springframework.boot:spring-boot-starter-data-redis'

application.properites (아래의 값은 default value)

spring.data.redis.host=localhost
spring.data.redis.port=6379

 

Repository

package com.trioshop.repository.redis;

import io.jsonwebtoken.lang.Arrays;
import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Repository;

import java.util.Objects;
import java.util.concurrent.TimeUnit;

import static java.lang.String.*;

@Repository
@RequiredArgsConstructor
public class RedisRepository {
    private static final String BLACKLIST_PREFIX = "blacklist:";

    private final RedisTemplate<String, Object> redisTemplate;

    @Resource(name = "redisTemplate")
    private ValueOperations<String, Object> valueOperations;


    public void deleteToken(String userId) {
        redisTemplate.delete(userId);
    }

    public void save(String userId, String refreshToken) {
        valueOperations.set(userId, refreshToken);
    }

    public void viewSave(Long userCode, Long boardCode){
        String board;

        if(redisTemplate.hasKey(valueOf(userCode))) {
            board = (String) valueOperations.get(valueOf(userCode))+boardCode + "_";
        }else {
            board = boardCode + "_";
        }
        valueOperations.set(valueOf(userCode), board, 2, TimeUnit.HOURS);
    }

    public String findById(String userId ) {
        if (userId == null) {
            return null;
        }
        return (String) valueOperations.get(userId);
    }

    public void addTokenToBlacklist(String token, long expirationTime) {
        valueOperations.set(BLACKLIST_PREFIX + token, "true", expirationTime, TimeUnit.MILLISECONDS);
    }

    public Boolean isTokenBlacklisted(String token) {
        return redisTemplate.hasKey(BLACKLIST_PREFIX + token);
    }

    public Boolean isCodeBoardView(Long userCode, Long boardCode) {
        String boards = (String) valueOperations.get(valueOf(userCode));
        if(Objects.nonNull(boards)) {
            String[] boardArr = boards.split("_");
            String boardCodeStr = valueOf(boardCode);
            for (String s : boardArr) {
                if (s.equals(boardCodeStr)) {
                    return false;
                }
            }
        }

        return true;
    }
}

 

이후 필요한 곳에서 상속 받아 사용하면 됩니다.

 

AWS EC2에 도커 컨테이너로 배포시 Redis 설정

저희 프로젝트 팀은 War 파일을 패키징해서 도커 컨테이너에 올려서 배포하는 방안을 채택 하였습니다.

이 과정에서 Redis 연결이 되지않아 문제를 해결하는데 시간을 투자했습니다.

이러한 문제 해결 과정에서 알게  된 사실 몇가지가 있습니다.

기존 Dockerfile만으로 배포시

1. 같은 네트워크 범주 안에 들어가지 않음, 두개를 각 각 실행해줘야함 그러므로 localhost(127.0.0.1)로 접근 불가

이에 대한 해결방안으로 docker-compose를 이용하였습니다.

하지만 이렇게해도 localhost로 접근이 불가능 하였고 그래서 redis에 네트워크 명을 지정해주었습니다.

docker-compose.yml

version: '3.8'
services:
  trioshop:
    image: kkgg0522/trioshop
    container_name: trioshop
    ports:
      - "443:8443"
    environment:
      SPRING_REDIS_HOST: redis
      SPRING_REDIS_PORT: 6379
      SERVER_SSL_KEY_STORE: /app/trio.keystore
      SERVER_SSL_KEY_STORE_PASSWORD: tanadmin
      SERVER_SSL_KEY_STORE_TYPE: PKCS12
      SERVER_SSL_KEY_ALIAS: tomcat
      TZ: "Asia/Seoul"
    volumes:
      - ./trio.keystore:/app/trio.keystore
    depends_on:
      - redis

  redis:
    image: redis:6.2
    ports:
      - "6379:6379"
    volumes:
      - ./redis.conf:/usr/local/etc/redis/redis.conf
    hostname: redis
    command: ["redis-server", "/usr/local/etc/redis/redis.conf"]

application.propertise 변경

spring.data.redis.host=redis