Spring Boot Redis 통합 가이드: 설정, 직렬화 및 활용

1. 개요

1.1 Spring Data Redis

Spring Boot에서 데이터 작업은 Spring Data를 통해 처리됩니다. Spring Data는 Redis, MongoDB, JPA 등 다양한 데이터 소스와의 통합을 지원하며, Spring Data Redis는 Redis와의 연동을 담당합니다.

1.2 Lettuce vs Jedis

Spring Boot 2.x 버전부터 기존의 Jedis가 Lettuce로 대체되었습니다.

  • Jedis: 직접 연결 방식을 사용하며, 멀티스레드 환경에서 안전하지 않습니다. 따라서 Jedis Pool 연결 풀을 사용해야 하며, 여러 문제가 발생할 수 있습니다.
  • Lettuce: Netty 기반으로 동작하며, 인스턴스를 여러 스레드에서 공유할 수 있습니다. 스레드 안전성을 보장하므로 더 적은 수의 연결로 높은 성능을 제공합니다.

2. 자동 설정 분석

2.1 자동 설정 클래스

  1. spring.factories 파일 위치: spring-boot-autoconfigure-2.3.4.RELEASE.jar → META-INF → spring.factories
  2. RedisAutoConfiguration 클래스가 Redis 설정을 자동으로 구성합니다.
  3. RedisProperties 클래스는 application.yml 또는 application.properties의 Redis 설정을 바인딩합니다.

2.2 Jedis Pool 비활성화 이유

RedisAutoConfigurationredisTemplate 메서드는 RedisConnectionFactory를 파라미터로 받습니다. Jedis의 구현체는 일부 메서드가 완전히 구현되지 않아 Spring Boot에서 기본적으로 Lettuce를 권장합니다.

3. 기본 사용법

3.1 프로젝트 설정

의존성 추가:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

application.yml 설정:

spring:
  redis:
    host: 192.168.1.100
    port: 6379

3.2 기본 테스트

@Autowired
private RedisTemplate redisTemplate;

@Test
void testRedisOperations() {
    // RedisTemplate을 사용한 다양한 데이터 타입 연산
    redisTemplate.opsForValue().set("greeting", "안녕하세요");
    String result = (String) redisTemplate.opsForValue().get("greeting");
    System.out.println(result); // 출력: 안녕하세요
    
    // List 연산
    redisTemplate.opsForList().leftPush("fruits", "apple");
    redisTemplate.opsForList().leftPush("fruits", "banana");
    
    // Set 연산
    redisTemplate.opsForSet().add("tags", "java", "spring", "redis");
}

4. 직렬화 설정

4.1 직렬화의 필요성

객체를 Redis에 저장할 때는 직렬화가 필요합니다. 다음 예제는 직렬화 없이 객체를 저장할 때 발생하는 문제를 보여줍니다.

@Data
@AllArgsConstructor
@NoArgsConstructor
@Component
public class Member {
    private String name;
    private Integer age;
}

@Test
public void testObjectSerialization() {
    Member member = new Member("홍길동", 25);
    // 직렬화하지 않으면 오류 발생
    redisTemplate.opsForValue().set("member:1", member); // 오류 발생
}

해결 방법: 엔티티 클래스에 implements Serializable을 추가하거나, 사용자 정의 직렬화를 적용합니다.

4.2 사용자 정의 직렬화 설정

기본 직렬화는 JdkSerializationRedisSerializer를 사용하여 바이너리 형태로 저장되므로, Redis CLI에서 데이터를 읽을 때 가독성이 떨어집니다. JSON 직렬화를 위해 사용자 정의 설정이 필요합니다.

@Configuration
public class RedisConfiguration {
    
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);
        
        // JSON 직렬화 설정
        Jackson2JsonRedisSerializer<Object> jsonSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.activateDefaultTyping(
            LaissezFaireSubTypeValidator.instance, 
            ObjectMapper.DefaultTyping.NON_FINAL
        );
        jsonSerializer.setObjectMapper(objectMapper);
        
        // String 직렬화 설정
        StringRedisSerializer stringSerializer = new StringRedisSerializer();
        
        // Key 직렬화
        template.setKeySerializer(stringSerializer);
        template.setHashKeySerializer(stringSerializer);
        
        // Value 직렬화
        template.setValueSerializer(jsonSerializer);
        template.setHashValueSerializer(jsonSerializer);
        
        template.afterPropertiesSet();
        return template;
    }
}

Redis CLI에서 데이터를 읽을 때 가독성을 높이려면 --raw 옵션을 사용합니다:

redis-cli --raw -p 6379

5. 유틸리티 클래스

RedisTemplate을 래핑하여 편리한 유틸리티 클래스를 제공할 수 있습니다. 이 클래스는 일반적인 Redis 작업을 단순화합니다.

@Component
public class RedisUtils {
    
    private final RedisTemplate<String, Object> redisTemplate;
    
    public RedisUtils(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }
    
    // String 타입 연산
    public void setValue(String key, Object value) {
        redisTemplate.opsForValue().set(key, value);
    }
    
    public Object getValue(String key) {
        return redisTemplate.opsForValue().get(key);
    }
    
    // List 연산
    public void leftPush(String key, Object value) {
        redisTemplate.opsForList().leftPush(key, value);
    }
    
    public Object leftPop(String key) {
        return redisTemplate.opsForList().leftPop(key);
    }
    
    // Set 연산
    public void addToSet(String key, Object... values) {
        redisTemplate.opsForSet().add(key, values);
    }
    
    // Hash 연산
    public void putHash(String key, String hashKey, Object value) {
        redisTemplate.opsForHash().put(key, hashKey, value);
    }
    
    public Object getHashValue(String key, String hashKey) {
        return redisTemplate.opsForHash().get(key, hashKey);
    }
    
    // 키 존재 여부 확인
    public boolean hasKey(String key) {
        return Boolean.TRUE.equals(redisTemplate.hasKey(key));
    }
    
    // 키 삭제
    public boolean deleteKey(String key) {
        return Boolean.TRUE.equals(redisTemplate.delete(key));
    }
}

태그: Spring Boot Redis Lettuce Spring Data Redis 직렬화

7월 24일 20:19에 게시됨