ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Redisson] 레디스로 객체 저장 시 클래스 (타입)정보 제외하기
    DataBase/Redis 2023. 3. 25. 22:59

    redis client로 redisson을 쓰고 있는데 따로 설정 없이 객체를 저장할 경우 클래스 정보도 함께 저장된다.

     

    데이터 직렬화를 하면서 클래서 정보도 포함시킨다.

     

    redisson은 객체를 사용하기 위해 RBucket 개념을 활용하는데, spring data redis에서 bucket에 관한 설명을 보면 다음과 같다.

     

    Bucket is the data bag for Redis hash structures to be used with RedisData.
    버킷은 레디스의 hash structure로 사용되는 데이터 가방이다. 직역하면 그런데, 객체를 담을 수 있는 녀석이라고 생각하면 되겠다. baeldung에도 object holder로 소개하고 있다.

    데이터 직렬화 시 redisson은 Kyro5Codec를 기본으로 사용하는데 이 Kyro5Codec이 Object에서 클래스 타입을 추출해내서 함께 저장한다.

     

    꽤 타고 들어가야해서 블로그로 보여주긴 가독성이 떨어질듯하다. 궁금하다면, Kyro5Codec에서 encode 메서드 로직에 있는 kryo.writeClassAndObject(output, in);를 타고 들어가면 되겠다

     


    간단한 예제

     

    Cookie 클래스.

    Kyro5Codec 대신 사용할 Codec 도구는 역직렬화 시, 빈 생성자가 필요하기 때문에 빈 생성자를 만들어주어야한다.

    data class Cookie (
        val key: String,
        val name: String,
        val meta: Map<String, Any?>,
        val createdAtUnix: Long
    ) {
        private constructor(): this("", "", emptyMap(), 0)
    }

     

    쿠키를 redis에 저장하는 repository

    @Repository
    class CookieRepository(
        private val redissonClient: RedissonClient
    ) {
    
        fun save(name: String, id: String, meta: Map<String, Any?>) {
            val key = "Cookie:$id"
            val cookieBucket = redissonClient.getBucket<Cookie>(key)
    
            cookieBucket.set(
                Cookie(
                    key = key,
                    name = name,
                    meta = meta,
                    createdAtUnix = LocalDateTime.now().toEpochSecond(ZoneOffset.UTC)
                )
            )
        }
    }

     

    대충 이런 값을 넣어본다.

    fun test() {
            cookieRepository.save("초코파이", "1", mapOf("회사" to "오리온"))
    }

     

    바이트 코드로 바로 들어가는지 알아볼 수 없다..

     

    그나마 레디스 Gui 툴로 본 데이터는 이렇게 보인다

    알아볼 수 있는게 중요한 건 아니고 하고자했던 원래 얘기로 돌아오자면, 클래스 타입의 정보가 들어간다는거다. 회사 리파지토리에 바로 테스트 코드를 작성한거라 모자이크 처리하긴 했지만, 패키지 경로가 보이고 클래스 이름도 보인다.

     

    이렇게 저장하게 되면 클래스의 위치나 이름이 변경되면 역직렬화를 할 수 없을테고, 의미없는 클래스 정보를 나타내는 데이터가 되버린다.

     

    난 그저 map 형식으로 필요한 데이터만 넣고 싶다.

     

    어떤 키워드로 검색해야할지 이래저래 검색해보다 겨우 찾았다.

    redisson 공식 깃헙의 위키를 보면 클래스 타입 정보를 저장하고 싶지 않다면 TypedJsonJacksonCodec을 사용하라고 적혀있는걸 알 수 있다.

     

    Codec를 application.yaml에서 설정할 수 있는 방법도 있긴하지만, TypedJsonJacksonCodec 경우에는 인스턴스화 시키려면 직렬화, 역직렬화 하려는 클래스의 타입을 직접 넣어주어야하며 빈 생성자로 인스턴스화 할 수 있는 코드가 없다.

     

    사용하는 곳에서 직렬화,역직렬화 하려는 코드에서 직접 클래스를 넣어주는 방법으로 처리해야한다.

     

    TypedJsonJacksonCodec의 생성자를 보면 변환하려는 클래스 타입만 넣을 경우 objectMapper는 새로 생성한다. 조금이라도 효율성을 높이기 위해 함수에서 TypedJsonJacksonCodec 생성하기보다는 클래스 변수로 만들자.(그렇지 않으면 함수가 동작할 때마다 objectMapper 객체를 매번 생성할테니까)

     

    수정한 CookieRepository 코드

    @Repository
    class CookieRepository(
        private val redissonClient: RedissonClient
    ) {
        private val codec = TypedJsonJacksonCodec(Cookie::class.java)
    
        fun save(name: String, id: String, meta: Map<String, Any?>) {
            val key = "Cookie:$id"
            val cookieBucket = redissonClient.getBucket<Cookie>(key, codec)
    
            cookieBucket.set(
                Cookie(
                    key = key,
                    name = name,
                    meta = meta,
                    createdAtUnix = LocalDateTime.now().toEpochSecond(ZoneOffset.UTC)
                )
            )
        }
    
        fun get(key: String): Cookie {
            return redissonClient.getBucket<Cookie>(key, codec).get()
        }
    }
     
    역직렬화할 때도 codec을 사용하도록 해줘야한다.

    아까 대충 만든 테스트 코드로 레디스에 객체를 저장해보면 클래스 정보 없이 생각했던 필드와 값만 들어간다.


    결론

    redisson 사용시, 클래스 타입 말고 객체의 데이터만 저장하고 싶을 경우에는 Codec을 디폴트로 쓰는 Kyro5Codec가 아닌 TypedJsonJacksonCodec 을 쓰도록 하자. 
     

     

    출처

    - https://github.com/redisson/redisson/wiki/4.-data-serialization

    - https://www.baeldung.com/redis-redisson

     

     

     

Designed by Tistory.