Springboot Cache 사용기[4] Redis 사용하기
Redis Cache를 사용하기 위해선 먼저 Redis를 사용할 줄 알아야 합니다. 그래서 하나씩 만들어보겠습니다.
Redis는 Nosql DB입니다. 영속성을 지원하며, In memory DB이므로 속도가 빠른 장점으로 Cache서버의 DB로서 많이 사용됩니다.
1. 디펜던시 추가
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
build.gradle에 위의 디펜더 시를 추가해줍니다.
2. RedisConfig
@Configuration @RequiredArgsConstructor @EnableRedisRepositories public class RedisConfig { private final RedisProperties redisProperties; @Bean public RedisConnectionFactory redisConnectionFactory(){ return new LettuceConnectionFactory(redisProperties.getHost(),redisProperties.getPort()); } @Bean public RedisTemplate<?,?> redisTemplate(){ RedisTemplate<?, ?> redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(redisConnectionFactory()); redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer()); return redisTemplate; } }
2-1 RedisConnectionFactory
- Java에서 지원하는 RedisClient는 2가지가 있습니다. (Jedis, Lettuce)
- Jedis는 멀티쓰레드에서 불안정하고, Pool의 한계 등으로 현재 Springboot 2.0 이상에서는 Lettuce가 탑재되고, 사용된다.
- RedisProperties는 Redis의 기본 설정들을 가지고 있습니다.
- RedisProperties를 이용해서 Redis의 Host와 Port를 설정해줍니다.
LettuceConnectionFactory에 설정을 부여해서, 클러스터에 대한 설정도 할 수 있지만 여기서는 진행하지 않겠습니다.
2-2 RedisTemplate
Redis에 데이터를 저장하는 방법은 총 2가지 있습니다.
1. CrudRepository 상속받아 사용
- Redis를 사용할 Repository를 만들고 CrudRepository를 상속받아 사용합니다.
2. RedisTemplate 정의해서 사용
- Redis에 직렬화 설정을 부여해서 저장하는데, 자동으로 직렬화를 수행해줍니다.
- ConnectionFactory를 Luttece를 설정해줍니다.
- setKeySerializer(new StringRedisSerializer()): String으로 들어오는 key에 대한 직렬화를 수행합니다. 즉 key에 대한 직렬화 설정을 해주는 메서드입니다.
- setValueSerializer(new GenericJackson2JsonRedisSerializer)): 저장하려 하는 Object객체를 직렬화 하여 저장합니다. 즉 Value에 대한 직렬화 설정을 하주는 메서드입니다.
- RedisTemplate<String,?>: key값은 항상 String이므로 String, Value는 와일드카드를 사용해서, 다양한 객체를 저장할 수 있도록 해줍니다.
2022.04.29 추가
sprigboot 2.0 이상부터는 config 작성없이 application.properties나 yml로 작성이 가능합니다.
spring: redis: host: localhost port: 6379
3. CrudRepository 사용
CrudRepository를 상속받아서 Redis를 사용해보겠습니다.
3-1. RedisMember class
@Getter @NoArgsConstructor @RedisHash(value = "redisMember") public class RedisMember{ @Id private String id; private String name; private int age; public RedisMember(String name, int age) { this.id= name; this.name = name; this.age = age; } }
- @Id는 javaPersistent의 어노테이션입니다.
- @RedisHash: Redis에 저장될 때 사용되는 prefix입니다. ex) redisMember:key 사용됩니다.
- Id가 Key값으로 사용됩니다. 저희는 name을 key값으로 사용할 예정이므로 Id에도 key를 넣어줍니다. 만약 안 넣어줄 시 Redis에 입력될 때 랜덤 한 값으로 key값이 생성됩니다.

첫 번째는 id를 미지정, 두 번째는 id에 name으로 줬을 때입니다.
prefix로 redisMember가 들어간 것을 볼 수 있습니다.
3-2. MemberRedisRepository
public interface MemberRedisRepository extends CrudRepository<RedisMember,String> { }
- CrudRepository<>를 상속받습니다.
- Id 값을 이용하여 데이터를 찾습니다.
간단한 테스트 코드를 보겠습니다.
@Test @DisplayName("redisRepository 테스트") void repositoryTest(){ memberRedisRepository.save(new RedisMember("hi",10)); RedisMember redisMember = memberRedisRepository.findById("hi").get(); assertThat(redisMember.getName()).isEqualTo("hi"); }
- RedisMember id,name= hi이며, age=10인 객체를 저장합니다.

- redis가 만약 설치되어 있다면, redis-cli를 입력하고 위처럼 입력한다면 값이 저장된 것을 확인할 수 있습니다.
- Id를 가지고 데이터를 찾으므로 값을 변경하고 싶다면, 다시 저장하시면 됩니다.
@Test @DisplayName("redisRepository 데이터변경 테스트") void repositoryUpdateTest(){ memberRedisRepository.save(new RedisMember("hi",10)); memberRedisRepository.save(new RedisMember("hi",11)); RedisMember redisMember = memberRedisRepository.findById("hi").get(); assertThat(redisMember.getAge()).isEqualTo(11); }

- 삭제는 delete를 이용하면 됩니다.
전체를 반환하는 테스트를 해보겠습니다. JPA와 다르게 Iterable <> 형태로 반환됩니다.
@Test @DisplayName("redisRepository 전체 반환 테스트") void repositoryFindAllTest(){ memberRedisRepository.save(new RedisMember("hi1",10)); memberRedisRepository.save(new RedisMember("hi2",11)); Iterable<RedisMember> all = memberRedisRepository.findAll(); List<RedisMember> list = new ArrayList<>(); all.forEach(list::add); assertThat(list.size()).isEqualTo(2); }
- forEach의 람다 표현식으로 List에 담아서 사용합니다.
4. RedisTemplate 사용
위에서 선언한 RedisTemplate를 사용합니다.
먼저 사용가능한 자료구조와 사용하기 위한 객체를 살펴보겠습니다.
메소드명반환 오퍼레이션 Redis 자료구조
opsForValue() | ValueOperations | String |
opsForList() | ListOperations | List |
opsForSet() | SetOperations | Set |
opsForZSet() | ZSetOperations | Sorted Set |
opsForHash() | HashOperations | Hash |
Operations연산자를 얻어서 연산을 수행합니다.
4-1. ValueOperation
Value는 String에 대해서 연산을 진행합니다.
@Autowired RedisTemplate<String, RedisMember> redisMemberRedisTemplate; @Test @DisplayName("redisTemplate value 테스트") void redisTemplateTest(){ ValueOperations<String, RedisMember> stringRedisMemberValueOperations = redisMemberRedisTemplate.opsForValue(); stringRedisMemberValueOperations.set("hi", new RedisMember("hi", 10)); RedisMember redisMember = redisMemberRedisTemplate.opsForValue().get("hi"); assertThat(redisMember.getAge()).isEqualTo(10); }
- 연산자를 먼저 얻습니다. 그 후 set을 이용해서 객체를 저장합니다.
- 객체를 찾을 때 지정한 key를 이용해서 데이터를 찾습니다.
- 여기서 알아야할 부분은 원래 Value는 String에 대해서 연산을 진행하지만, RedisConfig에서 객체에 대한 직렬화 설정을 부여했기 때문에 자동적으로 직렬화와 역직렬 화가 수행돼, RedisMember를 저장하고 데이터를 찾을 수 있습니다.
- 마찬가지로 업데이트 연산은 set(key, 변경)을 이용하면 됩니다.
4-2 ListOperation
List에 대한 연산을 진행합니다.
@Test @DisplayName("redisTemplate list 테스트") void redisTemplateListTest(){ ListOperations<String, RedisMember> stringRedisMemberListOperations = redisMemberRedisTemplate.opsForList(); stringRedisMemberListOperations.rightPush("redisMemberList",new RedisMember("hi1",10)); stringRedisMemberListOperations.rightPush("redisMemberList",new RedisMember("hi2",11)); Long size = stringRedisMemberListOperations.size("redisMemberList"); RedisMember result = stringRedisMemberListOperations.index("redisMemberList", 1); List<RedisMember> redisMemberList = stringRedisMemberListOperations.range("redisMemberList", 0, 1); stringRedisMemberListOperations.set("redisMemberList",0,new RedisMember("m1",10)); }
- rightPush: List에 추가합니다.
- size: 크기를 알 수 있습니다.
- index: 단 건의 데이터를 조회할 수 있습니다.
- range: 다수의 데이터를 조회할 수 있습니다.
- set: 데이터의 값을 변경할 수 있습니다.
4-3 SetOperation
set에 대한 연산을 진행합니다.
@Test @DisplayName("redisTemplate set 테스트") void redisTemplateSetTest(){ SetOperations<String, RedisMember> stringRedisMemberSetOperations = redisMemberRedisTemplate.opsForSet(); stringRedisMemberSetOperations.add("memberSet",new RedisMember("h1",10)); stringRedisMemberSetOperations.add("memberSet",new RedisMember("h2",10)); RedisMember randomRedisMember = stringRedisMemberSetOperations.pop("memberSet"); List<RedisMember> randomMemberSet = stringRedisMemberSetOperations.pop("memberSet", 2); Set<RedisMember> memberSet = stringRedisMemberSetOperations.members("memberSet"); Boolean result = stringRedisMemberSetOperations.isMember("memberSet", new RedisMember("h1", 10)); Long re = stringRedisMemberSetOperations.remove("memberSet", new RedisMember("hi2", 10)); }
- add: set에 추가할 수 있습니다.
- pop(key): set에 저장된 데이터중에서 랜덤 하게 한 개의 데이터를 반환합니다.
- pop(key, count): set에 저장된 데이터중에서 랜덤 하게 count만큼의 데이터를 반환합니다.
- member(key): set에 저장된 모든 데이터를 반환합니다.
- isMember(key, 데이터): 데이터의 존재 유무를 판별합니다.
- remove(key,데이터): 데이터에 관한 삭제를 진행하며, 몇 건의 데이터가 삭제됐는지 반환됩니다.
4-4. ZSetOperation
위에서 본 set과는 다르게 값에 대해 가중치를 주어 정렬을 진행합니다.
@Test @DisplayName("redisTemplate zSet 테스트") void redisTemplateZSetTest(){ ZSetOperations<String, RedisMember> stringRedisMemberZSetOperations = redisMemberRedisTemplate.opsForZSet(); stringRedisMemberZSetOperations.add("memberZSet",new RedisMember("hi1",10),1); stringRedisMemberZSetOperations.add("memberZSet",new RedisMember("hi2",10),2); Long memberZSet = stringRedisMemberZSetOperations.count("memberZSet", 0, 1); RedisMember result = stringRedisMemberZSetOperations.popMin("memberZSet").getValue(); }
- add(key, value, 가중치): 가중치를 기준으로 value에 대한 정렬을 진행합니다.
- count(key, min, max): min~max까지의 개수를 반환합니다.
- popMin(key).getValue(): 가장 작은 가중치를 가진 데이터를 반환합니다.
4-5. HashOperation
hash를 이용하여 key과 hashKey값을 통해서 데이터를 저장, 조회합니다.
hash를 사용하기 위해선 config에 hash key값과 value값을 직렬화 해주는 과정이 필요합니다.
아래와 같이 추가해줍니다.
@Bean public RedisTemplate<String,?> redisTemplate(){ RedisTemplate<String, ?> redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(redisConnectionFactory()); redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setKeySerializer(new StringRedisSerializer()); // 추가 redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer()); // 추가 redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer()); return redisTemplate; }
@Test @DisplayName("redisTemplate hash 테스트") void redisTemplateHashTest(){ HashOperations<String, Object, Object> stringObjectObjectHashOperations = redisMemberRedisTemplate.opsForHash(); stringObjectObjectHashOperations.put("memberHashOne","name",new RedisMember("hi3",10)); Map<String, RedisMember> map = new HashMap<>(); map.put("hi1",new RedisMember("hi1", 10)); map.put("hi2",new RedisMember("hi2", 10)); stringObjectObjectHashOperations.putAll("memberHashMap",map); RedisMember result = (RedisMember) stringObjectObjectHashOperations.get("memberHashMap", "hi1"); RedisMember redisMember = (RedisMember) stringObjectObjectHashOperations.get("memberHashOne", "name"); }
- put(key, hashKey, value): hashKey값을 이용해서 데이터를 저장합니다.
- putAll(key, Map): map의 key값이 hashKey값으로 들어가게 됩니다.
- get(key, hashKey): key값과 hashKey값을 이용해서 데이터를 찾습니다.
정리하기
Redis 디펜더시를 추가하여 사용할 수 있고, RedisConfig를 작성
- RedisConnectionFactory를 지정하고, properties를 주입해준다.
- RedisTemplate: 연산을 위한 Template
Redis에 연산 진행 2가지 방법
1. CrudRepository 상속
- @RedisHash는 prefix를 지정한다.
- Id가 key값으로 들어가게 된다. 미지정 시 랜덤 한 값으로
2. RedisTemplate 사용
- connectionFactory를 설정
- key와 value에 대한 직렬화 방식을 설정해줘 자동으로 직렬화와 역직렬화가 진행
- Value, List, Set, ZSet, Hash에 대한 연산 방법
다음은 Redis의 사용방법을 활용해서 Cache를 적용해보겠습니다. 읽어주셔서 감사합니다.
모든 코드는 아래 링크에서 확인 가능합니다.
https://github.com/rlaehdals/springbootCache
'SpringBoot > cache' 카테고리의 다른 글
Springboot Cache 사용기[5] Redis 이용한 Cache 사용 (0) | 2022.04.16 |
---|---|
Springboot Cache사용기[3] Local-Memory-Cache 동작 과정 (0) | 2022.04.02 |
Springboot Cache 사용기[2] Local-Memory-Cache (2) | 2022.03.30 |
Springboot Cache 사용기[1] (0) | 2022.03.30 |
댓글
이 글 공유하기
다른 글
-
Springboot Cache 사용기[5] Redis 이용한 Cache 사용
Springboot Cache 사용기[5] Redis 이용한 Cache 사용
2022.04.16 -
Springboot Cache사용기[3] Local-Memory-Cache 동작 과정
Springboot Cache사용기[3] Local-Memory-Cache 동작 과정
2022.04.02 -
Springboot Cache 사용기[2] Local-Memory-Cache
Springboot Cache 사용기[2] Local-Memory-Cache
2022.03.30 -
Springboot Cache 사용기[1]
Springboot Cache 사용기[1]
2022.03.30
댓글을 사용할 수 없습니다.