생각정리/Redis

Spring Redis 자료구조 사용

생각중임 2024. 2. 19. 17:00

의존성 주입

gradle에 redis의존성을 추가해 준다.

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

레디스 주소 설정

레디스를 사용할 호스트주소와 포트번호를 지정해 준다.

spring:
    data:
      redis:
        host: localhost
        port: 6379

레디스 설정

Lettuce Client를 이용해 Redis와 연결하고 사용하기 편하게 Redis Template를 수정해 사용을 한다.

크게 사용하는 시리얼라이저는 3가지로 프로젝트와 개인에 맞게 설정해서 사용을 하는 듯하다.

  • GenericJackson2JsonRedisSerializer
    • 제일 많이 사용하는 방법으로 class type에 상관없이 값을 불러올 수 있다.
    • 저장 시 클래스 패키지 명등 클래스 타입 전체를 저장하여 해당 패키지 dto가 아닐 경우, key값을 조회할 수 없다
  • Jackson2JsonRedisSerializer
    • 클래스 정보를 RedisTemplate dto추가될 때마다 Bean을 생성해줘야 한다.
  • StringRedisSerializer
    • Jsackson의 Objectmapper를 이용해 json과 string을 서로 변환하여 사용한다.
@Configuration
public class RedisConfig {
    @Value("${spring.data.redis.host}")
    private String host;

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

    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        return new LettuceConnectionFactory(host, port);
    }

    @Bean
    public RedisTemplate<?, ?> redisTemplate() {
        // template를 주입할 때 자료형을 지정하기 위해 <?, ?>로 한다.
        RedisTemplate<?, ?> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory());

        // JSON 포맷으로 데이터를 저장하는 경우
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(String.class));

        // 일반적인 key:value일 때
        redisTemplate.setDefaultSerializer(new StringRedisSerializer());

        // Hash를 사용할 경우
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());

        return redisTemplate;
    }
}

Redis 자료구조

Redis 자료구조 메소드명 반환 오퍼레이션
String opsForValue() ValueOperations
List opsForList() ListOperations
Set opsForSet() SetOperations
Sorted Set opsForZSet() ZSetOperations
Hash opsForHash() HashOperations

String

@Repository
@RequiredArgsConstructor
public class RedisRepository {

    private final RedisTemplate<String, Object> redisTemplate;
	
    // String 저장
    public void save(String key, String value) {
        ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
        valueOperations.set(key, value);
    }

    // String 조회
    public String getValue(String key) {
        ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
        return (String) valueOperations.get(key);
    }
}
@Service
@RequiredArgsConstructor
public class Service {

    private final RedisRepository redisRepository;

    // String 저장
    public void save(String key, String value) {
        for (int i = 0; i < 5; i++) {
            redisRepository.save("key:"+ key + ":" + i, value + i);
        }
    }

    // String 조회
    public String getValue(String key) {
        return redisRepository.getValue("key:string:" + key);
    }
}

List

@Repository
@RequiredArgsConstructor
public class RedisRepository {

    private final RedisTemplate<String, Object> redisTemplate;
	
	// List 저장
    public void saveList(String key, String value) {
        ListOperations<String, Object> listOperations = redisTemplate.opsForList();
        listOperations.rightPush(key, value);
    }

    // List 인덱스 단일 조회
    public String getListValue(String key, long index) {
        RedisOperations<String, Object> listOperations = redisTemplate.opsForList().getOperations();
//        return (String) listOperations.opsForList().rightPop(key); // 하나의 값을 출력하고 삭제한다.
        return (String) listOperations.opsForList().index(key, index);
    }

    // List 인덱스 범위 조회
    public List<Object> getListValues(String key) {
        RedisOperations<String, Object> listOperations = redisTemplate.opsForList().getOperations();
        return listOperations.opsForList().range(key, 0, -1);
    }
}
@Service
@RequiredArgsConstructor
public class Service {

    private final RedisRepository redisRepository;

    // List 저장
    public void saveList(String key, String value) {
        redisRepository.saveList("listKey:" + key, "listTest" + value);
    }

    // List 단일 조회
    public String getListValue(String key, long index) {
        return redisRepository.getListValue("listKey:" + key, index);
    }

    // List 전체 조회
    public List<Object> getListValues(String key) {
        return redisRepository.getListValues("listKey:" + key);
    }
}

Set

@Repository
@RequiredArgsConstructor
public class RedisRepository {

    private final RedisTemplate<String, Object> redisTemplate;

    // Set 저장
    public void saveSet(String key, String value) {
        SetOperations<String, Object> setOperations = redisTemplate.opsForSet();
        setOperations.add(key, value);
    }

    // Set 단일 조회
    public Object getSetValue(String key) {
        SetOperations<String, Object> setOperations = redisTemplate.opsForSet();
//        return setOperations.pop(key); // 하나의 값을 출력하고 삭제한다.
        return setOperations.randomMember(key); // 랜덤으로 하나의 값을 출력한다.
    }

    // set 전체 조회
    public Set<Object> getSetValues(String key) {
        SetOperations<String, Object> setOperations = redisTemplate.opsForSet();
//        return setOperations.intersect(key); // 집합처럼 다른 키와 합, 교, 차 집합을 구할 수 있음
        return setOperations.members(key);
    }
}
@Service
@RequiredArgsConstructor
public class Service {

    private final RedisRepository redisRepository;

    // Set 저장
    public void saveSet(String key, String value) {
        redisRepository.saveSet("setKey:" + key, "setTest" + value);
    }

    // Set 단일 조회
    public String getSetValue(String key) {
        return (String) redisRepository.getSetValue("setKey:" + key);
    }

    // set 전체 조회
    public Set<Object> getSetValues(String key) {
        return redisRepository.getSetValues("setKey:" + key);
    }
}

Sorted Set

@Repository
@RequiredArgsConstructor
public class RedisRepository {

    private final RedisTemplate<String, Object> redisTemplate;
	
    // Sorted Set 저장
    public void saveZSet(String key, String value, double score) {
        ZSetOperations<String, Object> zSetOperations = redisTemplate.opsForZSet();
        zSetOperations.add(key, value, score);
    }

    // Sorted set 인덱스 범위 조회
    public Set<Object> getZSetIndexValues(String key) {
        ZSetOperations<String, Object> zSetOperations = redisTemplate.opsForZSet();
        return zSetOperations.range(key, 0, -1);
    }

    // Sorted Set 스코어 조회
    public Set<Object> getZSetScoreValues(String key, int min, int max) {
        ZSetOperations<String, Object> zSetOperations = redisTemplate.opsForZSet();
        return zSetOperations.rangeByScore(key, min, max);
    }

    // Sorted Set 스코어 키-벨류 조회
    public Set<Object> getZSetScoreKeyValues(String key, int min, int max) {
        ZSetOperations<String, Object> zSetOperations = redisTemplate.opsForZSet();
        return Collections.singleton(zSetOperations.rangeByScoreWithScores(key, min, max));
    }
}
@Service
@RequiredArgsConstructor
public class Service {

    private final RedisRepository redisRepository;

    // Sorted Set 저장
    public void saveZSet(String key, String value, double score) {
        for (int i = 1; i < 4; i++) {
            redisRepository.saveZSet("score:" + key, value + i , score * i);
        }
    }

    // Sorted set 인덱스 범위 조회
    public Set<Object> getZSetIndexValues(String key) {
        return redisRepository.getZSetIndexValues("score:" + key);
    }

    // Sorted Set 스코어 조회
    public Set<Object> getZSetScoreValues(String key, int min, int max) {
        return redisRepository.getZSetScoreValues("score:" + key, min, max);
    }

    // Sorted Set 스코어 조회
    public Set<Object> getZSetScoreKeyValues(String key, int min, int max) {
        return redisRepository.getZSetScoreKeyValues("score:" + key, min, max);
    }
}

Hash

@Repository
@RequiredArgsConstructor
public class RedisRepository {

    private final RedisTemplate<String, Object> redisTemplate;
	
    // Hash 저장
    public void saveHash(String key, String hashKey, String value) {
        HashOperations<String, Object, Object> hashOperations = redisTemplate.opsForHash();
        hashOperations.put(key, hashKey, value);
    }

    // Hash 다중 저장
    public void saveHash(String key, Map<String, String> value) {
        HashOperations<String, Object, Object> hashOperations = redisTemplate.opsForHash();
        hashOperations.putAll(key, value);
    }

    // Hash 단일 조회
    public Object getHash(String key, String hashKey) {
        HashOperations<String, Object, Object> hashOperations = redisTemplate.opsForHash();
        return hashOperations.get(key, hashKey);
    }

    // Hash key 전체 조회
    public Set<Object> getHashKeys(String key) {
        HashOperations<String, Object, Object> hashOperations = redisTemplate.opsForHash();
        return hashOperations.keys(key);
    }

    // Hash value 전체 조회
    public List<Object> getHashValues(String key) {
        HashOperations<String, Object, Object> hashOperations = redisTemplate.opsForHash();
        return hashOperations.values(key);
    }

    // Hash 전체 조회
    public Map<Object, Object> getHashMaps(String key) {
        HashOperations<String, Object, Object> hashOperations = redisTemplate.opsForHash();
        return hashOperations.entries(key);
    }
}
@Service
@RequiredArgsConstructor
public class Service {

    private final RedisRepository redisRepository;

    // Hash 저장
    public void saveHash(String key, String hashKey, String value) {
        redisRepository.saveHash("hashKey:" + key, "testKey:" + hashKey, "hashTest" + value);
    }

    // Hash 다중 저장
    public void saveAllHash(String key, String hashKey, String value) {
        Map<String, String> map = new HashMap<>();
        map.put("testKey:" + hashKey + "first", value + "first");
        map.put("testKey:" + hashKey + "second", value + "second");
        map.put("testKey:" + hashKey + "third", value + "third");
        redisRepository.saveHash("hashKey:" + key, map);
    }

    // Hash 단일 조회
    public String getHash(String key, String hashKey) {
        return (String) redisRepository.getHash("hashKey:" + key, "testKey:" + hashKey);
    }

    // Hash key 전체 조회
    public Set<Object> getHashKeys(String key) {
        return redisRepository.getHashKeys("hashKey:" + key);
    }

    // Hash value 전체 조회
    public List<Object> getHashValues(String key) {
        return redisRepository.getHashValues("hashKey:" + key);
    }

    // Hash 전체 조회
    public Map<Object, Object> getHashMaps(String key) {
        return redisRepository.getHashMaps("hashKey:" + key);
    }
}
기본적인 자료구조들로 저장을 하고 간단한 조회 정도는 할 줄 알아야 다양한 곳에 적용시킬 때 활용을 잘할 수 있을 거라 보고 저장과 조회 방법들로 다양하게 사용해 봤다. 사용한 메서드들도 오버로딩된 메서드들도 많고 다르게 조회하는 방법들도 더 있지만 기본적으로 사용할 거 같은 방법으로 사용해봤다.
대부분은 API로 사용을 할 경우가 많을 듯하여 간단하게 컨트롤러로 값을 받아 서비스에서 사용하도록 해 postman을 이용해서 테스트를 해보았다.
처음에는 들어가는 값들에 문제가 있어 오류가 간혹 났지만, 하다 보니 사용하는 방법들이 비슷하여 적응하는 데는 문제가 없는 것 같다.