데이터베이스/Redis

spring boot redis 활용하기

수달하나 2024. 5. 1. 13:22

Redis

 

cache 상태를 저장하기 위해서 서버 자체에서 cache 를 구성해서 사용하는 경우도 있지만 오픈소스인 redis 를 이용해서 cache 환경을 구성하는 경우도 많다.

 

흔히 말해 redis를 사용하면서 얻을 수 있는 장점으로 여러가지 것들을 얘기하지만, 예를 들어 다양한 데이터 구조 혹은 캐싱기능(빠른 계산 결과를 위한) 메세지 브로커 기능 등 사실 이것들을 꼭 redis를 사용해서 구현해야 이득이라고 생각하지는 않는다. 

 

실제로 다양한 데이터 구조는 커스터마이징하게 cache를 구현하는것이 좀더 이득인 경우도 있고 메세지 브로커또한 redis 를 사용할 필요는 없다. 심지어 캐싱기능을 위해서 redis를 사용한다는 것은 말도 안되는 이야기라고 생각한다.

 

내가 생각하는 redis 의 주 사용 이유는 (물론 더 많은 경험이 있으면 바뀔수도 있겠지만) 다중 서버 환경에서 일관성을 유지하기 위함이다.

 

구성 환경

Docker 내부 Redis 최신 서버 설치

Spring Boot 3.2

Java 17

 

https://github.com/Eno1993/redis

 

GitHub - Eno1993/redis

Contribute to Eno1993/redis development by creating an account on GitHub.

github.com

 

활용

RedisConfig

@Configuration
public class redisConfig {
    
    @Bean
    public RedisConnectionFactory redisConnectionFactory(){
        return new LettuceConnectionFactory();
    }

    @Bean
    public RedisTemplate<String, Object> redisTemplate(){
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory());
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(String.class));
        return redisTemplate;
    }

}

 

RedisTemplate<String, Object> 형태의 템플릿을 정의한다.

String 타입의 key 를 통해서 여러 자료구조들을 정의하고 활용한다. key 를 통해 List<Object>, Set<Object> 등의 자료구조에 접근하게 된다.

 

@Service
@RequiredArgsConstructor
public class RedisService {

    private final RedisTemplate<String, Object> redisTemplate;

    public void deleteCache(String redisKey){
        redisTemplate.delete(redisKey);
    }

    public boolean isContainCache(String redisKey){
        return redisTemplate.hasKey(redisKey);
    }

}

 

String 타입의 key 를 이용해 사용중인 자료구조를 확인하거나 제거 할 수 있다.

 

List<Object>

@Service
@RequiredArgsConstructor
public class RedisService {

    private final RedisTemplate<String, Object> redisTemplate;

    public void addWithList(String redisKey, String data){
        redisTemplate.opsForList().rightPush(redisKey, data);
    }

    public void removeWithList(String redisKey, String data){
        redisTemplate.opsForList().remove(redisKey, 0, data);
    }

    public List<String> getList(String redisKey){
        List<Object> objectList = redisTemplate.opsForList().range(redisKey, 0, -1);
        List<String> res = new ArrayList<>();
        for(Object o : objectList){
            res.add((String) o);
        }
        return res;
    }
}

 

 

 기타 메서드

 - leftPush(K key, V value): 리스트의 왼쪽에 요소를 추가.

 - leftPop(K key): 리스트의 왼쪽에서 요소를 꺼냄.

 - rightPush(K key, V value): 리스트의 오른쪽에 요소를 추가.

 - rightPop(K key): 리스트의 오른쪽에서 요소를 꺼냄.

 - range(K key, long start, long end): 리스트의 지정된 범위 내의 요소를 반환.

 - trim(K key, long start, long end): 리스트를 지정된 범위 내의 요소로 잘라냄.

 - size(K key): 리스트의 길이를 반환.

 - index(K key, long index): 리스트의 지정된 인덱스 위치의 요소를 반환.

 - set(K key, long index, V value): 리스트의 지정된 인덱스 위치의 요소를 새 값으로 설정.

 - remove(K key, long count, Object value): 리스트에서 count만큼 value와 동일한 요소를 제거.

   [count==0 : 모든 요소 삭제, count<0 : 오른쪽부터 count 개수 만큼 삭제, count>0 : 왼쪽부터 count 개수 만큼 삭제]

 - leftPushAll(K key, V... values): 리스트의 왼쪽에 여러 요소를 추가.

 - rightPushAll(K key, V... values): 리스트의 오른쪽에 여러 요소를 추가.

 

Set<Object>

@Service
@RequiredArgsConstructor
public class RedisService {

    private final RedisTemplate<String, Object> redisTemplate;

    public void addWithSet(String redisKey, String data){
        redisTemplate.opsForSet().add(redisKey, data);
    }

    public void removeWithSet(String redisKey, String data){
        redisTemplate.opsForSet().remove(redisKey, data);
    }

    public boolean isContainWithSet(String redisKey, String data){
        return redisTemplate.opsForSet().isMember(redisKey, data);
    }

    public Set<String> getSet(String redisKey){
        Set<Object> objectSet = redisTemplate.opsForSet().members(redisKey);
        Set<String> res = new HashSet<>();
        for(Object o : objectSet){
            res.add((String) o);
        }
        return res;
    }

    public Set<String> getSetWithScan(String redisKey){

        ScanOptions scanOptions = ScanOptions.scanOptions().build();
        Cursor<Object> cursor = redisTemplate.opsForSet().scan(redisKey, scanOptions);

        Set<String> res = new HashSet<>();
        while (cursor.hasNext()) {
            res.add((String) cursor.next());
        }
        return res;
    }
}

 

기타 메서드

 - add(K key, V... values): Set에 하나 이상의 멤버를 추가.

 - remove(K key, Object... values): Set에서 하나 이상의 멤버를 제거.

 - isMember(K key, Object o): Set에 특정 멤버가 있는지 확인.

 - members(K key): Set의 모든 멤버를 반환.

 - size(K key): Set의 크기(멤버 수)를 반환.

 - difference(K key, K otherKey): 두 Set 간의 차집합을 반환.

 - union(K key, K otherKey): 두 Set 간의 합집합을 반환.

 - intersect(K key, K otherKey): 두 Set 간의 교집합을 반환.

 - randomMember(K key): Set에서 무작위로 하나의 멤버를 반환.

 - pop(K key): Set에서 무작위로 하나의 멤버를 제거하고 반환.

 - move(K key, V value, K destKey): Set에서 특정 멤버를 다른 Set으로 이동.

 - scan(K key, ScanOptions options): Set 스캔하여 멤버를 검색.

 

SortedSet<Object>

@Service
@RequiredArgsConstructor
public class RedisService {

    private final RedisTemplate<String, Object> redisTemplate;

    public void addWithSortedSet(String redisKey, String data){
        redisTemplate.opsForZSet().add(redisKey, data, System.currentTimeMillis());
    }

    public void removeWithSortedSet(String redisKey, String data){
        redisTemplate.opsForZSet().remove(redisKey, data);
    }

    public boolean isContainWithSortedSet(String redisKey, String data){
        Double score = redisTemplate.opsForZSet().score(redisKey, data);
        return score!=null;
    }

    public long orderWithSortedSet(String redisKey, String data){
        Long rank = redisTemplate.opsForZSet().rank(redisKey, data);
        if(rank!=null){
            return rank;
        }
        return -1l;
    }

    public Set<String> getSortedSet(String redisKey){
        Set<Object> objectSet = redisTemplate.opsForZSet().range(redisKey, 0, -1);
        // objectSet = redisTemplate.opsForZSet().reverseRange(redisKey, 0, -1); //역순 정렬
        Set<String> res = new HashSet<>();
        for(Object o : objectSet){
            res.add((String) o);
        }
        return res;
    }
}

 

기타 메서드

 - add(K key, V value, double score): Sorted Set에 멤버를 추가.

 - remove(K key, Object... values): Sorted Set에서 하나 이상의 멤버를 제거.

 - removeRange(K key, long start, long end): Sorted Set에서 지정된 범위의 멤버들을 제거.

 - range(K key, long start, long end): Sorted Set에서 지정된 범위의 멤버들을 가져옴.

 - rangeByScore(K key, double min, double max): Sorted Set에서 score가 주어진 범위 내에 있는 멤버들을 가져옴.

 - rank(K key, Object value): Sorted Set에서 특정 멤버의 순위를 가져옴.

 - score(K key, Object value): Sorted Set에서 특정 멤버의 score 값을 가져옴.

 - size(K key): Sorted Set 크기(멤버 ) 반환.

 

Map<Object, Object>

@Service
@RequiredArgsConstructor
public class RedisService {

    private final RedisTemplate<String, Object> redisTemplate;

    public void addWithHash(String redisKey, String key, String value){
        redisTemplate.opsForHash().put(redisKey, key, value);
    }

    public void removeWithHash(String redisKey, String key){
        redisTemplate.opsForHash().delete(redisKey, key);
    }

    public boolean isContainWithHash(String redisKey, String key){
        return redisTemplate.opsForHash().hasKey(redisKey, key);
    }

    public String getWithHash(String redisKey, String key){
        Object object = redisTemplate.opsForHash().get(redisKey, key);
        if(object!=null){
            return (String) object;
        }
        return "null";
    }

    public Map<String, String> getWithHashAll(String redisKey){
        Map<Object, Object> objectMap = redisTemplate.opsForHash().entries(redisKey);
        Map<String, String> res = new HashMap<>();
        for(Object o : objectMap.keySet()){
            String key = (String) o;
            String value = (String) objectMap.get(o);
            res.put(key, value);
        }
        return res;
    }
}

 

기타 메서드

 - put(K key, HK hashKey, HV value): Hash에 특정 키(Key)의 특정 필드(Field)와 값을 저장.

 - putAll(K key, Map<? extends HK, ? extends HV> m): Hash에 여러 필드와 값을 일괄 저장.

 - get(K key, Object hashKey): Hash에서 특정 키(Key)의 특정 필드(Field)의 값을 가져옴.

 - entries(K key): Hash의 모든 필드와 값을 가져옴.

 - delete(K key, Object... hashKeys): Hash에서 특정 키(Key)의 하나 이상의 필드를 삭제.

 - hasKey(K key, Object hashKey): Hash에 특정 키(Key)의 특정 필드(Field)가 존재하는지 확인.

 - keys(K key): Hash의 모든 필드를 가져옴.

 - values(K key): Hash의 모든 값을 가져옴.

 - size(K key): Hash의 크기(필드 수)를 반환.

 - increment(K key, HK hashKey, long delta): Hash의 특정 필드의 값을 증가.

 - expire(K key, long timeout, TimeUnit unit): Hash에 대한 만료 시간을 설정.

 - persist(K key): Hash 만료 시간을 제거하여 영속적으로 저장.