[Redis] Spring Boot 와 Redis 연동 설정 - (RedisTemplate)

2024. 4. 15. 12:22기술 창고/DataBase

728x90
SMALL

Spring Boot 환경에서 Redis 를 사용하기 위한 방법 중 하나인 RedisTemplate 설정 과정과 연동 과정에 대해 정리해보겠습니다.

또한 제가 진행하고자 하는 Spring Boot는 gradle 환경입니다.

 

 

(1) Dependency 추가

Build.gradle 에 Redis 관련 Dependency를 추가해줍니다.

 

 

(2) Redis Connection 설정

spring.data.redis.repositories.enabled=true
spring.redis.host=127.0.0.1
spring.redis.port=6379

application.properties 파일에 Redis Connection 설정을 해줍니다.

 

- spring.data.redis.repositories.enabled : Redis 레포지토리 사용 설정

- spring.redis.host : Redis 사용 호스트 ip

    !! 로컬 환경에서라면 127.0.0.1 이고, 다른 배포 서버 환경에서 진행한다면 해당 배포 서버의 ip 주소를 입력해야 합니다.

- spring.redis.port : Redis 사용 포트 번호 (기본적으로 6379 사용)

 

 

(3) RedisConfig 설정

RedisConfig 

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.jsontype.BasicPolymorphicTypeValidator;
import com.fasterxml.jackson.databind.jsontype.PolymorphicTypeValidator;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfig {

    // RedisTemplate
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){
        PolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator
                .builder()
                .allowIfSubType(Object.class)
                .build();

        ObjectMapper objectMapper = new ObjectMapper()
                .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) // 혹시라도 의도치 않거나 알 수 없는 정보가 들어와 시리얼라이즈를 할 수 없게 될 경우를 대비한 설정값
                .registerModule(new JavaTimeModule())
                .activateDefaultTyping(ptv, ObjectMapper.DefaultTyping.NON_FINAL)
                .disable(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS); // redis를 활용할 객체들에 날짜 정보가 TimeStamp 형식으로 적용되어있을 경우 그대로 RedisTemplate을 사용하면 에러가 발생하므로 그것에 대비하기 위한 설정값

        GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer(objectMapper);

        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory); // Reids Connection 설정
        redisTemplate.setKeySerializer(new StringRedisSerializer()); // RedisTemplate 사용 시 key는 String 타입으로 직렬화
        redisTemplate.setValueSerializer(genericJackson2JsonRedisSerializer); // RedisTemplate에 들어갈 Value는 Generic 타입으로서 어떤 클래스든 Json형식으로 직렬화할 수 있도록 함.

        return redisTemplate;
    }
}

RedisConfig 파일을 만들어 설정하고 RedisTemplate을 Bean 으로 만들어줍니다.

 

 

# RedisTemplate 타입 설정

RedisTemplate<String, Object>

 

RedisTemplate의 유형을 설정해줍니다.

키 값은 String 타입으로 받되, 값에 들어오는 데이터들은 Object 타입으로 지정해줌으로써, 모든 형식의 객체들을 받을 수 있도록 해주었습니다.

# Object 로 설정하면 장점과 단점 또한 존재하는데, 이 부분은 이후 정리해보겠습니다.

 

 

# PolymorphicTypeValidator 설정

PolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator
                .builder()
                .allowIfSubType(Object.class)
                .build();

 

PolymorphicTypeValidator 은 다형성 역직렬화와 함께 사용되는 클래스 이름 기반 하위 유형의 유효성 검사를 처리하는 클래스용 인터페이스 입니다.

Java 클래스 이름을 유형 식별자로 사용할 때, "기본 입력" 및 "명시적 @JsonTypeInfo" 을 통해 연결 가능한 허용 목록을 허용하여 무제한 클래스 이름으로 인해 발생하는 보안 문제를 방지합니다.

즉, RedisTemplate을 Object 타입의 데이터들로 구성하여 운영할 것이므로 Object 클래스 타입을 직렬화, 역직렬화에 사용할 수 있도록 허용해주는 것입니다.

 

 

# ObjectMapper 설정

ObjectMapper objectMapper = new ObjectMapper()
                .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
                .registerModule(new JavaTimeModule())
                .activateDefaultTyping(ptv, ObjectMapper.DefaultTyping.NON_FINAL)
                .disable(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS); 


ObjectMapper는 다양한 데이터 객체 타입들을 매핑하여 바인딩해주는 객체입니다.

내부의 configure 함수를 통해 만약 알 수 없는 설정이 들어올 경우 역직렬화를 수행하지 않도록 off 해주도록 설정, registerModule 함수에 JavaTimeModule을 추가하여 시간적 데이터가 존재할 경우 java.time 객체로 직렬화할 수 있도록 설정, activateDefaultTyping 함수에 앞서 모든 Object 객체들의 역직렬화 유효성 검사 설정 클래스를 적용하고 ObjectMapper.DefaultTyping.NON_FINAL을 설정함으로서 String, Boolean, Integer, Double 타입들을 제외하고 모든 데이터 유형에 기본 유형으로 지정되도록 설정, 마지막으로 disable 함수를 통해 날짜형 데이터가 TimeStamp로 직렬화될 수 있도록 설정해주었습니다.

 

 

# GenericJackson2JsonRedisSerializer  설정GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer(objectMapper);

 

앞에서 설정해준 설정값들을 가지고 이후 들어오는 모든 Object 데이터들을 JSON에 매핑될 수 있도록 GenericJackson2JsonRedisSerializer 시리얼라이저에 적용하여 생성합니다.

 

 

# RedisTemplate 설정

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

 

Object 데이터를 사용하는 RedisTemplate 객체를 만들어준 뒤, RedisConnectionFactory를 통해 Connection 설정을 넣어줍니다.

이 RedisConnectionFactory는 앞서 application.properties에 설정한 connection 설정들을 자동으로 넣어줍니다.

RedisTemplate의 key는 String 타입으로 운영될 것이기 때문에, KeySerializer에는 StringRedisSerializer를, value는 Object 타입의 데이터들로 운영될 것이기 때문에 앞서 위에서 설정해준 GenericJackson2JsonRedisSerializer를 넣어줍니다.

 

이로써, RedisTemplate의 사용 설정 및 커넥션 설정이 완료되었습니다.

이제 어플리케이션을 실행하면 자동으로 Redis가 설정되고 Bean객체로 생성되어 사용 가능하게 될 것입니다.

 

 

(4) 사용 테스트

테스트 Controller

@Slf4j
@RequiredArgsConstructor
@RequestMapping("/test")
@RestController
public class TestController {

    private final DeviceService deviceService;
    private final RedisTemplate<String, Object> deviceRedisTemplate;

    @GetMapping("/redistemplate")
    public DeviceResponseDto getSupplierCode(HttpServletResponse response, @RequestParam String dc){
        log.info("입력 데이터 : {}", dc);

        return useRedisTemplate(response, dc);
    }

    // RedisTemplate 이용
    private DeviceResponseDto useRedisTemplate(HttpServletResponse response, String dc){
        log.info("RedisTemplate 활용");

        String key = "confirm:device:" + dc + ":code";
        Object deviceConfirmData = deviceRedisTemplate.opsForValue().get(key);

        if(deviceConfirmData != null){
            log.info("Redis에 캐시 데이터가 존재 시 해당 캐시 데이터를 빠르게 추출");
            return (DeviceResponseDto)deviceConfirmData;
        }else{
            log.info("Redis에 캐시 데이터가 존재하지 않아 DB에서부터 데이터를 추출 및 세팅");
            return deviceService.getSupplierCode(response, dc);
        }

        return deviceService.getSupplierCode(response, dc);
    }

이제 테스트해보도록 하겠습니다.

 

- private final RedisTemplate<String, Object> deviceRedisTemplate;

RedisTemplate을 의존성 주입을 통해 호출합니다.

 

- Object deviceConfirmData = deviceRedisTemplate.opsForValue().get(key);

RedisTemplate에서 지원하는 opsForValue() 함수를 통해 key값을 기준으로 get하여 Redis에 저장된 데이터를 가지고 옵니다.

원래 opsForValue의 경우 단순 String 텍스트 형식의 Value 데이터를 가져옵니다.

하지만 앞서 RedisTemplate에 GenericJackson2Serializer를 사용함으로서 Object 타입의 데이터들을 설정해주었으므로 모든 Object 타입의 데이터를 가지고 올 수 있습니다.

 

- deviceConfirmData  확인

if(deviceConfirmData != null){
    log.info("Redis에 캐시 데이터가 존재 시 해당 캐시 데이터를 빠르게 추출");
    return (DeviceResponseDto)deviceConfirmData;
}else{
    log.info("Redis에 캐시 데이터가 존재하지 않아 DB에서부터 데이터를 추출 및 세팅");
    return deviceService.getSupplierCode(response, dc);
}

만약, 가지고 온 deviceConfirmData가 존재한다면 곧바로 Redis에 저장된 데이터를 빠르게 가지고오고, DeviceResponseDto 객체로 캐스팅하여 반환합니다.

이 때, 캐스팅 해주는 DeviceResponseDto 객체에는 생성자가 존재해야합니다.

 

Redis에 존재하지 않는다면 service 단으로 넘어가서 실 DB에 접근하여 데이터를 추출하고 Redis에 저장 후 반환하도록 합니다.

당연하게도, 현재로서는 설정하고 구성하기만 했으므로 처음에는 실DB에 접근하여 처리 후 반환할 것입니다.

 

 

이렇게 해서 RedisTemplate을 설정하여 Spring Boot를 연동하고 간단하게 사용하는 Controller까지 구성해보았습니다.

다시 한번 말하지만, 앞서 application.properties에서 현재 사용하고자 하는 환경에 따라 ip를 지정해주어야 합니다.

로컬은 127.0.0.1, 다른 배포 서버에서는 해당 서버의 ip를 입력해서 사용해야 합니다.

또한, 다른 배포 서버에서 사용할 경우 해당 서버에 Redis가 설치된 상태여야 합니다. (혹은 Docker의 경우 Docker 및 Redis 컨테이너 설치 및 실행 중인 상태를 유지 중이여야 함.)

 

 

 

# 이제 어플리케이션을 실행하기 이전에 Redis 서비스가 실행중인 상태여야지 에러가 발생하지 않습니다.

# Redis를 로컬에 설치했다면 로컬에서 서비스를 실행 중이여야 하고, 배포 서버나 Docker에 있는 Redis를 운영하려면 해당 Redis들 또한 실행 중이여야 합니다.

현재 저는 로컬 환경에 Docker를 설치하여 Redis 컨테이너를 실행하고 있기 때문에 docker에서 실행시켜준 상태입니다.

728x90
반응형
LIST