• 티스토리 홈
  • 프로필사진
    알쓸개잡
  • 방명록
  • 공지사항
  • 태그
  • 블로그 관리
  • 글 작성
알쓸개잡
  • 프로필사진
    알쓸개잡
    • 분류 전체보기 (92)
      • 스프링부트 (52)
      • AWS (5)
      • 쿠버네티스 (7)
      • 자바 (19)
      • 인프라 (0)
      • ETC (8)
  • 방문자 수
    • 전체:
    • 오늘:
    • 어제:
  • 최근 댓글
      등록된 댓글이 없습니다.
    • 최근 공지
        등록된 공지가 없습니다.
      • 반응형
      # Home
      # 공지사항
      #
      # 태그
      # 검색결과
      # 방명록
      • Spring Data Redis - cache 기능 사용하기
        2024년 06월 02일
        • 알쓸개잡
        • 작성자
        • 2024.06.02.:28

        Spring Cache에서 Redis를 활용하는 방법에는 여러 가지가 있다. Spring Boot와 Spring Data Redis를 사용하여 Redis를 캐시로 설정하는 방법에 대해서 정리해 본다.

         

        Spring Cache의 주요 어노테이션에 대해서 다음 포스팅을 참고하면 도움이 될 것이다.

        2024.05.20 - [스프링부트] - Spring Cache 주요 어노테이션

         

        Spring Data Redis 관련 포스팅은 다음 포스팅을 참고하기 바란다.

        2024.05.12 - [스프링부트] - Spring Data Redis - Redis Repository 사용

        2024.04.29 - [스프링부트] - Spring Data Redis - RedisTemplate의 HashOperations

        2024.04.25 - [스프링부트] - Spring Data Redis - RedisTemplate 트랜잭션

        2024.04.24 - [스프링부트] - Spring Data Redis - RedisTemplate의 ValueOperations

        2024.03.24 - [스프링부트] - Spring Data Redis - Auto Configuration을 이용한 Redis 연결 설정

         

        의존성 추가

        spring cache와 redis를 사용하기 위한 디펜던시는 다음과 같다.

        dependencies {
            implementation 'org.springframework.boot:spring-boot-starter-cache'
            implementation 'org.springframework.boot:spring-boot-starter-data-redis'
        }

         

         

        Spring Cache관련 설정

        spring cache를 사용하기 위한 application.yml의 설정은 다음과 같다.

        spring:
          data:
            redis:
            ...
            redis 관련 설정
            ...
          cache:
            type: redis
            redis:
              cache-null-values: false
              key-prefix: "myCache::"
              use-key-prefix: true
              time-to-live: 1m
              enable-statistics: false

         

        spring.cache.type: 캐시 타입을 지정한다. Redis를 캐시로 사용할 경우 'redis'로 지정한다.

        spring.cache.redis.time-to-live: 캐시 항목의 만료 시간을 설정한다. Duration 타입으로 지정한다.

        spring.cache.redis.cache-null-values: null 값을 캐시에 저장 허용할지 여부를 설정한다. 기본값은 true다.

        spring.cache.redis.key-prefix: 캐시 키의 접두사를 설정한다. 기본값은 빈 문자열이다.

        spring.cache.redis.use-key-prefix: 키 접두사를 사용할지 여부를 설정한다. 기본값은 true다.

         

        Spring Cache AutoConfiguration

        spring cache의 auto configuration은 spring-boot-autoconfigure 디펜던시에 정의되어 있다.

        org.springframework.boot.autoconfigure.cache 패키지에 spring cache 관련 auto configuration을 위한 클래스들이 정의되어 있다.

        @AutoConfiguration(
            after = {CouchbaseDataAutoConfiguration.class, HazelcastAutoConfiguration.class, HibernateJpaAutoConfiguration.class, RedisAutoConfiguration.class}
        )
        @ConditionalOnClass({CacheManager.class})
        @ConditionalOnBean({CacheAspectSupport.class})
        @ConditionalOnMissingBean(
            value = {CacheManager.class},
            name = {"cacheResolver"}
        )
        @EnableConfigurationProperties({CacheProperties.class})
        @Import({CacheConfigurationImportSelector.class, CacheManagerEntityManagerFactoryDependsOnPostProcessor.class})

        org.springframework.boot.autoconfigure.redis 패키지에 있는 CacheAutoConfiguration 클래스를 보면 설정 항목을 로딩하는 클래스는 CacheProperties.class인 것을 알 수 있다.

         

        CacheProperties.class에는 다음과 같은 어노테이션이 정의되어 있다.

        @ConfigurationProperties(
            prefix = "spring.cache"
        )

        spring.cache 아래의 설정 항목들을 로딩한다는 것을 알 수 있다.

        CacheProperties.class 에 CacheType 멤버 변수가 정의되어 있는데 이를 통해 Spring Cache가 지원하는 저장소를 확인할 수 있다.

        public enum CacheType {
            GENERIC,
            JCACHE,
            HAZELCAST,
            COUCHBASE,
            INFINISPAN,
            REDIS,
            CACHE2K,
            CAFFEINE,
            SIMPLE,
            NONE;
        
            private CacheType() {
            }
        }

        CAFFEINE, HAZELCAST 등 cache 역할을 하는 여러 저장소를 지원하는 것을 알 수 있다.

         

        Spring cache의 저장소로 redis를 사용하기 위해서는 RedisAutoConfiguration이 우선 수행되어 Redis 연결을 위한 ConnectionFactory, RedisTemplate과 같은 필요한 빈들이 등록된 상태여야 한다.

         

        org.springframework.boot.autoconfigure.cache 패키지에 보면 RedisCacheConfiguration 클래스를 확인할 수 있다.

        RedisCacheConfiguration에서는 spring cache 저장소로 redis를 사용하기 위한 RedisCacheManager 인스턴스를 빈으로 등록한다.

        RedisCacheManager는 RedisCacheManager 인스턴스를 생성할 때 다음과 같은 빈들을 주입받는다.

        • CacheProperties : spring cache 관련 설정 참조 정보를 담고 있는 클래스. spring.cache.redis 설정항목을 로딩하여 적용한다.
        • CacheManagerCustomizers: RedisCacheManager를 생성 후 사용자가 직접 변경 사항을 적용하기 위한 클래스.
        • RedisCacheManagerBuilderCustomizer: RedisCacheManager를 생성하기 위한 builder에 사용자가 직접 변경 사항을 적용하기 위한 클래스.

         

        Spring CacheManagerBuilderCustomizer

        Spring boot autoconfiguration에 의해서 생성되는 CacheManager는 기본적으로 JdkSerializationRedisSerializer를 사용한다.

        JdkSerializationRedisSerializer를 사용하게 되면 Redis에 저장되는 데이터의 형태를 읽기가 쉽지 않고 객체를 저장하는 경우에는

        Serializable 인터페이스를 확장해야 하는 번거로움이 있어서 권장되지 않는 직렬화 방식이다.

        이와 관련하여 직렬화 방식을 변경하고자 하면 CacheManagerBuilderCustomizer에 우리가 원하는 직렬화 방식을 설정해 주면 된다.

        CacheManagerBuilderCustomizer 빈을 등록하는 방법은 다음과 같다.

        //기본적으로 자동 구성되는 RedisCacheManager 에 RedisCacheConfiguration 만 커스텀하게 변경할 수 있다.
        @Bean
        public RedisCacheManagerBuilderCustomizer redisCacheManagerBuilderCustomizer( CacheProperties cacheProperties ) {
            //serializer 를 GenericJackson2JsonRedisSerializer 로 변경.
            return builder -> {
                RedisCacheConfiguration cacheConfiguration = builder.cacheDefaults()
                        .serializeKeysWith( RedisSerializationContext.SerializationPair.fromSerializer( RedisSerializer.string() ) )
                        .serializeValuesWith( RedisSerializationContext.SerializationPair.fromSerializer( RedisSerializer.json() ) );
        
                builder.cacheDefaults( cacheConfiguration );
            };
        }

        만약 application.yml 에 spring.cache.redis 관련 설정을 하지 않았다면 RedisCacheManagerBuilderCustomizer를 통해서

        spring.cache.redis 관련 설정을 직접 할 수 있다.

         

        RedisCacheManagerBuilderCustomizer 클래스는 FunctionalInterface로 정의되어 있다.

        @FunctionalInterface
        public interface RedisCacheManagerBuilderCustomizer {
            void customize(RedisCacheManager.RedisCacheManagerBuilder builder);
        }

        customize 메서드의 파라미터로 현재 생성 중인 RedisCacheManagerBuilder가 전달된다.

        우리는 인자로 전달되는 RedisCacheManagerBuilder인스턴스를 가지고 설정만 하면 되는 것이다.

         

        샘플 코드

        샘플 코드를 통해서 redis 캐시가 제대로 동작하는지 확인해 보자.

        application.yml

        spring:
          ...
          cache:
            type: redis
            redis:
              cache-null-values: false
              key-prefix: "myCache::"
              use-key-prefix: true
              time-to-live: 1m
              enable-statistics: false

        key-prefix로 cache로 저장될 때 myCache:: 를 키 앞에 붙이도록 설정하였다.

        key-prefix와 use-key-prefix가 설정된 경우 저장되는 키 형식은
        {key-prefix}{cache-name}::{식별자} 형태가 되겠다.
        use-key-prefix를 false로 지정하게 되면 {key-prefix}와 {cache-name} 은 키에 포함되지 않고 {식별자}로만 키가 생성된다.

         

        Person 클래스 정의

        @NoArgsConstructor
        @Setter
        public class Persistent implements Persistable<UUID> {
            @Id
            protected UUID id;
        
            public Persistent(UUID id) {
                this.id = id;
            }
        
            @Override
            public UUID getId() {
                return id;
            }
        
            @Override
            @JsonIgnore
            public boolean isNew() {
                return id == null;
            }
        }
        @Getter
        @Setter
        @NoArgsConstructor
        public class Person extends Persistent {
            private String personName;
            private Integer personAge;
            private String personNation;
            private String comment;
        
            @Builder
            public Person( UUID id, 
                           String personName, 
                           Integer personAge, 
                           String personNation, 
                           String comment ) {
                super(id);
                this.personName = personName;
                this.personAge = personAge;
                this.personNation = personNation;
                this.comment = comment;
            }
        }

         

        PersonService 클래스 정의

        @Service
        @Slf4j
        @CacheConfig( cacheNames = "persons" )
        public class PersonService {
        
            @Cacheable( key = "#personId" )
            public Person findPerson( UUID personId ) {
                log.info( "findPerson called, personId={}", personId );
                //아래 로직이 실행된다는 것은 persons 캐시에 personId 식별자를 가진 엔트리가 존재하지 않는 것을 의미.
                //보통은 DB에서 조회하는 로직이 들어가지만 편의상 새로운 Person 인스턴스를 생성하여 리턴한다.
                return Person.builder()
                        .id( personId )
                        .personName( "cached-person" )
                        .personNation( "korea" )
                        .personAge( 100 )
                        .comment( "cached data" )
                        .build();
            }
        
            @CachePut( key = "#person.id" )
            public Person savePerson( Person person ) {
                log.info( "savePerson called, person={}", person );
                //보통은 DB에 업데이트하는 로직이 들어가지만 편의상 person 인스턴스를 그대로 리턴한다.
                //리턴되는 인스턴스는 cache 에 업데이트 된다.
                return person;
            }
        
            @CachePut( key = "#person.id" )
            public Person updatePerson( Person person ) {
                log.info( "updatePerson called, person={}", person );
                //보통은 DB에 업데이트하는 로직이 들어가지만 편의상 person 인스턴스를 그대로 리턴한다.
                //리턴되는 인스턴스는 cache 에 업데이트 된다.
                return person;
            }
        
            @CacheEvict( key = "#personId" )
            public void deletePerson( UUID personId ) {
                log.info( "deletePerson called, personId={}", personId );
                //보통은 DB에서 데이터를 삭제하는 로직이 들어가지만 편의상 로그만 생성한다.
                //메서드 실행이 완료되면 personId 에 해당하는 엔트리가 캐시에서 삭제된다.
            }
        }

        편의상 spring cache의 redis 동작여부를 판단하기 위함이니 따로 DB를 사용하진 않았다.

        cache명은 'persons' 다.

        Spring Cache를 위해서 사용되는 어노테이션에 대해서는 

        2024.05.20 - [스프링부트] - Spring Cache 주요 어노테이션  포스팅을 참고하기 바란다.

         

        Controller 클래스

        @RestController
        @RequestMapping("/persons")
        @RequiredArgsConstructor
        public class PersonController {
            private final PersonService personService;
        
            @GetMapping(path = "/{personId}", produces = MediaType.APPLICATION_JSON_VALUE )
            public ResponseEntity<Person> getPerson( @PathVariable UUID personId ) {
                Person person = personService.findPerson( personId );
                return ResponseEntity.ok( person );
            }
        
            @PostMapping(path = "", produces = MediaType.APPLICATION_JSON_VALUE )
            public ResponseEntity<Person> createPerson( @RequestBody Person person ) {
                Person savedPerson = personService.savePerson( person );
                return ResponseEntity.ok( savedPerson );
            }
        
            @PutMapping(path = "", produces = MediaType.APPLICATION_JSON_VALUE)
            public ResponseEntity<Person> updatePerson( @RequestBody Person person ) {
                Person updatedPerson = personService.updatePerson( person );
                return ResponseEntity.ok( updatedPerson );
            }
        
            @DeleteMapping(path = "/{personId}")
            public ResponseEntity<Void> deletePerson( @PathVariable UUID personId ) {
                personService.deletePerson( personId );
                return ResponseEntity.noContent().build();
            }
        }

         

         

        API 호출

        API를 호출했을 때 redis에 저장되는 값을 확인해 보자. redis에 저장된 JSON 문자열을 pretty 하게 보도록 jq를 설치하였다.

        참고하기 바란다.

         

        저장

        request
        POST http://localhost:9090/persons/00000000-0000-0000-0000-000000000001
        
        request body
        {
            "id": "00000000-0000-0000-0000-000000000001",
            "personName": "saved-person",
            "personAge": 200,
            "personNation": "korea",
            "comment": "saved data"
        }

        위와 같이 저장 API를 호출 했을 때 redis에 저장되는 데이터는 다음과 같다.

         

         ~/ redis-cli get "myCache::persons::00000000-0000-0000-0000-000000000001" | jq .
        {
          "@class": "com.example.spring.redis.model.Person",
          "id": "00000000-0000-0000-0000-000000000001",
          "personName": "saved-person",
          "personAge": 200,
          "personNation": "korea",
          "comment": "saved data"
        }

         

        조회

        request
        GET http://localhost:9090/persons/00000000-0000-0000-0000-000000000001
        
        response body
        {
            "id": "00000000-0000-0000-0000-000000000001",
            "personName": "saved-person",
            "personAge": 200,
            "personNation": "korea",
            "comment": "saved data"
        }

        person id '00000000-0000-0000-0000-000000000001'를 조회하는 API를 호출 했을 때 PersonService 클래스의

        findPerson 메서드에서 기록하는 'findPerson called, personId={}'와 같은 로그는 생성되지 않는다. 이를 통해서 findPerson 메서드의 로직이 실행되지 않고 redis cache에서 데이터를 가져왔음을 알 수 있다.

         

        수정

        request
        PUT http://localhost:9090/persons
        request body
        {
            "id": "00000000-0000-0000-0000-000000000001",
            "personName": "modified-person",
            "personAge": 100,
            "personNation": "korea",
            "comment": "modified data"
        }

         

        redis cache는 다음과 같이 변경됨을 알 수 있다.

         ~/ redis-cli get "myCache::persons::00000000-0000-0000-0000-000000000001" | jq .
        {
          "@class": "com.example.spring.redis.model.Person",
          "id": "00000000-0000-0000-0000-000000000001",
          "personName": "modified-person",
          "personAge": 100,
          "personNation": "korea",
          "comment": "modified data"
        }

         

        삭제

        request
        DELETE http://localhost:9090/persons/00000000-0000-0000-0000-000000000001

         

        redis 결과

        127.0.0.1:6379> scan 0
        1) "0"
        2) (empty array)

        redis에 저장된 person 데이터는 삭제되었다.

         

        지금까지 spring cache 저장소로 redis를 사용하는 방법에 대해서 정리해 보았다.

        대부분의 설정은 application.yml(properties) 파일의 spring.cache.redis.~ 에서 설정 하되 @Configuration 클래스에서RedisCacheManagerBuilderCustomizer 빈을 정의하여 serializer를 변경하여 사용하면 spring cache 저장소로 redis를 간단히 적용 가능하다.

         

        샘플 코드는 GITLAB 링크에서 확인할 수 있다.

         

        끝.

        반응형
        저작자표시 비영리 변경금지 (새창열림)

        '스프링부트' 카테고리의 다른 글

        외부 설정 파일 로딩하기  (0) 2024.08.18
        logback을 이용한 spring boot 애플리케이션 syslog 전송하기  (0) 2024.06.07
        Spring Cache 주요 어노테이션  (0) 2024.05.20
        Spring 이벤트 시스템을 이용한 실시간 데이터 변경 감지 및 처리  (0) 2024.05.17
        Spring Data Redis - Redis Repository 사용  (0) 2024.05.12
        다음글
        다음 글이 없습니다.
        이전글
        이전 글이 없습니다.
        댓글
      조회된 결과가 없습니다.
      스킨 업데이트 안내
      현재 이용하고 계신 스킨의 버전보다 더 높은 최신 버전이 감지 되었습니다. 최신버전 스킨 파일을 다운로드 받을 수 있는 페이지로 이동하시겠습니까?
      ("아니오" 를 선택할 시 30일 동안 최신 버전이 감지되어도 모달 창이 표시되지 않습니다.)
      목차
      표시할 목차가 없습니다.
        • 안녕하세요
        • 감사해요
        • 잘있어요

        티스토리툴바