본문 바로가기
개발/SpringBoot

SpringBoot Redis 활용 - RedisTemplate, RedisRepository

by 궁즉변 변즉통 통즉구 2022. 12. 16.
반응형

SpringBoot에서 Redis에 접근하기 위한 2가지 방법을 제공하는데 RedisTemplate, RedisRepoitory 이다.

이 2가지를 설정하고 테스트를 실행해보면서 SpringBoot에서 Redis 활용방법을 알아본다.

 

1. SpringBoot Redis 설정

먼저 Redis를 사용하기 위해 의존성 및 빈 설정을 진행한다.

build.gradle에 redis 의존성을 추가한다.

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

 

application.yml에 redis 접속정보를 설정한다.

spring:
  redis:
    host: 127.0.0.1
    port: 6379
    # password: 'xxxx' # 비밀번호 있을 경우 설정

 

Redis 빈설정 RedisConfig파일을 작성한다. RedisProperties를 주입받으면 yml에 설정한 redis 설정 항목들을 객체 형태로 가져와서 사용할 수 있다. RedisTemplate은 StringRedisTemplate 등 편의를 위해 제공해주는 템플릿도 있는데 일반RedisTemplate과 비교를 위해 일단 2개 모두를 등록해봤다.

@RequiredArgsConstructor
@Configuration
public class RedisConfig {

    private final RedisProperties redisProperties;

    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        return new LettuceConnectionFactory(redisProperties.getHost(), redisProperties.getPort());
    }

    /** 
       java Object를 redis에 저장하는 경우 사용
         - StringRedisSerializer를 사용해서 String값에 대한 적용도 가능하나
           default는 JdkSerializationRedisSerializer
    */
    @Bean
    public RedisTemplate<?, ?> redisTemplate() {
        RedisTemplate<byte[], byte[]> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory());
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new StringRedisSerializer());
        return redisTemplate;
    }

    /**
       일반적인 String 값을 key, value로 사용하는 경우 사용
    */
    @Bean
    public StringRedisTemplate stringRedisTemplate(){
        StringRedisTemplate stringRedisTemplate=new StringRedisTemplate();
        stringRedisTemplate.setConnectionFactory(redisConnectionFactory());
        return stringRedisTemplate;
    }
}

먼저 StringRedisTemplate과 RedisTemplate 차이를 테스트 해보면 redis에 저장되는 형식이 차이가 있어 특별한 경우가 아니면 StringRedisTemplate를 사용하면 될 것 같다.

 

2. RedisTemplate 활용

이제 RedisTemplate으로 몇가지 기능들을 테스트해본다. 테스트 코드는 아래와 같다.

@SpringBootTest
public class RedisTemplateTest {

    @Autowired
    StringRedisTemplate redisTemplate;

    @Test
    @DisplayName("String Type 테스트")
    public void testStrings() {
        final String key = "string_key";

        final ValueOperations<String, String> stringValueOperations = redisTemplate.opsForValue();

        stringValueOperations.set(key, "1"); // redis set 명령어
        final String result_1 = stringValueOperations.get(key); // redis get 명령어
        System.out.println("result_1 = " + result_1);

        stringValueOperations.increment(key); // redis incr 명령어
        final String result_2 = stringValueOperations.get(key);
        System.out.println("result_2 = " + result_2);

        // redis-cli> get string_key
    }


    @Test
    @DisplayName("List Type 테스트")
    public void testList() {
        final String key = "list_key";

        final ListOperations<String, String> stringListOperations = redisTemplate.opsForList();

        stringListOperations.rightPush(key, "H");
        stringListOperations.rightPush(key, "e");
        stringListOperations.rightPush(key, "l");
        stringListOperations.rightPush(key, "l");
        stringListOperations.rightPush(key, "o");
        stringListOperations.rightPushAll(key, " ", "t", "e", "s", "t");
        stringListOperations.set(key, 0, "h");  // 0번째 변경

        final String character_1 = stringListOperations.index(key, 1);
        System.out.println("index value = " + character_1);

        final Long size = stringListOperations.size(key);
        System.out.println("list size = " + size); // size

        final List<String> resultRange = stringListOperations.range(key, 0, 9);
        System.out.println("ResultRange = " + Arrays.toString(resultRange.toArray()));
        final List<String> allRange = stringListOperations.range(key, 0, -1);
        System.out.println("AllRange = " + Arrays.toString(allRange.toArray()));

        stringListOperations.trim(key, 2, 5);  // 리스트의 start부터 end만큼 보존하고 나머진 삭제
        System.out.println("2~5 Trim Range = " + stringListOperations.range(key, 0, -1));

        // redis-cli> lrange list_key 0 -1
    }

    @Test
    @DisplayName("Set Type 테스트")
    public void testSet() {
        String key = "set_key";
        SetOperations<String, String> stringSetOperations = redisTemplate.opsForSet();

        stringSetOperations.add(key, "H");
        stringSetOperations.add(key, "e");
        stringSetOperations.add(key, "l");
        stringSetOperations.add(key, "l");
        stringSetOperations.add(key, "o");

        Set<String> set = stringSetOperations.members(key);
        System.out.println("all members = " + Arrays.toString(set.toArray())); // 전체 조회

        Long size = stringSetOperations.size(key);
        System.out.println("size = " + size); // size

        Cursor<String> cursor = stringSetOperations.scan(key, ScanOptions.scanOptions().match("*").count(3).build());
        while(cursor.hasNext()) {
            System.out.println("cursor = " + cursor.next());
        }

        // redis-cli>  smembers set_key
    }

    @Test
    @DisplayName("SortedSet 테스트")
    public void testSortedSet() {
        String key = "sortedset_key";

        ZSetOperations<String, String> stringZSetOperations = redisTemplate.opsForZSet();

        stringZSetOperations.add(key, "o", 20);
        stringZSetOperations.add(key, "H", 1);
        stringZSetOperations.add(key, "e", 5);
        stringZSetOperations.add(key, "l", 10);
        stringZSetOperations.add(key, "l", 15);

        Set<String> range = stringZSetOperations.range(key, 0, 5);
        System.out.println("range = " + Arrays.toString(range.toArray()));

        Long size = stringZSetOperations.size(key);
        System.out.println("size = " + size);

        Set<String> scoreRange = stringZSetOperations.rangeByScore(key, 0, 13);
        System.out.println("scoreRange = " + Arrays.toString(scoreRange.toArray()));

        // redis-cli> zrange sortedset_key 0 -1
    }


    @Test
    @DisplayName("Hash Type 테스트")
    public void testHash() {
        String key = "hash_key3";

        HashOperations<String, Object, Object> stringObjectHashOperations = redisTemplate.opsForHash();

        stringObjectHashOperations.put(key, "Hello", "value1");
        stringObjectHashOperations.put(key, "Hello2", "value2");
        stringObjectHashOperations.put(key, "Hello3", "value3");

        Object hello = stringObjectHashOperations.get(key, "Hello");
        System.out.println("hello = " + hello);

        Map<Object, Object> entries = stringObjectHashOperations.entries(key);
        System.out.println("entries Map = " + entries);
        System.out.println("entries = " + entries.get("Hello2"));

        Long size = stringObjectHashOperations.size(key);
        System.out.println("size = " + size);

        // redis-cli> hget hash_key, hkeys hash_key, hvals hash_key
    }
}

아래와 같이 Redis 타입별로 RedisTemplate에서 제공되는 메소드가 각각 있어서 redis명령이 가능하다.

 

3. RedisRepository 활용

RedisRepository를 활용하면 Spring Data JPA처럼 객체를 기반으로 Redis에 객체를 저장하고 조회 할 수 있다. 

먼저 RedisConfig에 설정을 추가한다.

@RequiredArgsConstructor
@Configuration
@EnableRedisRepositories // 추가 설정
public class RedisConfig {

    private final RedisProperties redisProperties;

    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        return new LettuceConnectionFactory(redisProperties.getHost(), redisProperties.getPort());
    }

    @Bean
    public StringRedisTemplate stringRedisTemplate(){
        StringRedisTemplate stringRedisTemplate=new StringRedisTemplate();
        stringRedisTemplate.setConnectionFactory(redisConnectionFactory());
        return stringRedisTemplate;
    }
}

 

다음으로 redis에 저장할 Domain 객체를 선언하다.

@RedisHash 및 @Id 추가하게 되는데 Redis에 저장되는 key는 @RedisHash의 value + @Id가 붙어있는 멤버변수로 되기 때문이다.

@Getter
@RedisHash(value = "member", timeToLive = 100)
public class Member {

    @Id
    private String id;
    private String fName;
    private String lName;
    private Address address;

    @Builder
    public Member(String id, String fName, String lName, Address address) {
        this.id = id;
        this.fName = fName;
        this.lName = lName;
        this.address = address;
    }
}
@Getter
public class Address {
    private String address;

    public Address(String address) {
        this.address = address;
    }
}

 

RedisRepository를 선언한다.

public interface MemberRedisRepository extends CrudRepository<Member, String> {

}

 

TestCase를 작성하고 돌려본다.

@SpringBootTest
public class RedisRepositoryTest {

    @Autowired
    private MemberRedisRepository redisRepository;

    @Test
    @DisplayName("member 저장 및 조회")
    public void saveMemberTest() {

        Address address = new Address("Korea Seoul");
        Member member = new Member(null, "kildong", "hong", address);

        // 저장
        Member savedPerson = redisRepository.save(member);

        // 조회
        Optional<Member> findMember = redisRepository.findById(savedPerson.getId());

        assertThat(findMember.isPresent()).isEqualTo(Boolean.TRUE);
        assertThat(findMember.get().getFName()).isEqualTo(member.getFName());
    }
}

 

마지막으로 redis에서 확인해본다.

key를 조회해보면 "member" member의 key들을 가지고 있는 set, "member:xxxxxx"는 각 member의 정보를 가지고 있는 hash 이다.

set타입의 member를 조회해보면 아래와 같다.

마지막으로 hash타입의 member를 조회해보면 아래와 같다.

 

반응형

댓글