기본적으로 K-V DB인 Redis는 json 형태의 데이터를 저장한다.
이를 protobuf 형식의 바이너리 형태로 넣으면 몇가지 이점이 있다.
- 사용량을 크게 줄일 수 있다.
- Redis 데이터의 Schema가 생성되므로 명확하게 스펙관리를 할 수 있다.
- Redis의 중첩 데이터를 효율적으로 관리할 수 있다.
똑똑한 사람이 이미 만들어 둔 https://github.com/sewenew/redis-protobuf 패키지를 통해 json - protobuf 직렬화, 역직렬화가 가능하다. 아 정확히는 직렬화가 아니라 마샬링이라고 해야한다.
단점은 우리가 redis 를 직접 조회해서 나오는 데이터는 볼 수 없다. 어차피 서버 통해서 언마샬링 할거니까 괜찮다!
자체적으로 메소드 내에서 ByteArray로 변환하려고 했는데 잘 안되더라. Config에서 RedisTemplate을 설정해줘야 정상 작동했다.
@Bean
fun redisTemplate(): RedisTemplate<String, ByteArray> {
val template = RedisTemplate<String, ByteArray>()
// Key는 String으로, Value는 ByteArray로 설정
template.keySerializer = StringRedisSerializer()
template.valueSerializer = RedisSerializer.byteArray()
return template
}
@Repository
class RedisRepository(
private val redisTemplate: RedisTemplate<String, ByteArray>,
) {
// Protobuf 데이터를 불러오는 메서드
fun <T : com.google.protobuf.Message> getData(
key: String,
parser: com.google.protobuf.Parser<T>
): T? {
val redisValue = redisTemplate.opsForValue().get(key)
return redisValue?.let { parser.parseFrom(redisValue) } // Protobuf 역직렬화
}
// Protobuf를 저장할 때 사용하는 메서드
fun saveData(
key: String,
value: com.google.protobuf.Message // Protobuf Message
) {
try {
val byteArray = value.toByteArray()
println("Saving key: $key with value size: ${byteArray.size}") // 로그 추가
redisTemplate.opsForValue().set(key, byteArray) // Protobuf는 byte array로 저장
} catch (e: Exception) {
e.printStackTrace()
}
}
}
서비스 레이어에서는 mapper를 통해 protobuf로 변환해주는 작업이 필요하다.
// 이벤트 정보 조회 로직
fun getEventInfo(eventNo: Long): EventMasterDto? {
val key = getEventKey(eventNo)
val byteArray = redisRepository.getData(key, RedisEventInfoMessage.parser()) // message
return byteArray?.let { redisEventMapper.toDto(it) } // Protobuf 역직렬화
}
// 이벤트 정보 저장 로직
fun setEventInfo(eventNo: Long, eventDto: EventMasterDto) {
val key = getEventKey(eventNo)
val protobufEvent = redisEventMapper.toProto(eventDto)
redisRepository.saveData(key, protobufEvent)
}
결과적으로 Redis에는 이런 데이터가 들어간다. 데이터 길이가 304다.
{��Sample Event"���*����2PERIOD:���B�ԡ�JCOUPONP�XdbACTIVEjYrYzSample Global Event (EN)�示例全球事件 (ZH)�AUTO����LIMIT��NO_LIMIT�
�RANDOM�LIMIT�2�N�Y�Y�GMARKET��Y� VIP Brand�APPROVED�admin�����Y���Y�Y�encryptedString123
위 데이터를 언마샬링하면 아래와 같은 데이터가 된다. 요 데이터는 600~650 정도 되는듯
{
"eventNo": "123",
"eventGroupNo": "67890",
"eventName": "Sample Event",
"eventStartDate": "2024-09-30T12:00:00Z",
"eventEndDate": "2024-10-10T12:00:00Z",
"appEnablePeriodType": "PERIOD",
"appEnablePeriodStartDate": "2024-09-30T00:00:00Z",
"appEnablePeriodEndDate": "2024-10-10T23:59:00Z",
"rewardType": "COUPON",
"rewardPolicyNo": "1111",
"rewardCnt": 100,
"status": "ACTIVE",
"globalShopUseYn": "Y",
"globalShopMessageUseYn": "Y",
"globalShopEngEventName": "Sample Global Event (EN)",
"globalShopChnEventName": "示例全球事件 (ZH)",
"appWay": "AUTO",
"appDeductCnt": 1,
"appRandomCodeMasterNo": "2222",
"appLimitType": "LIMIT",
"appLimitCnt": 5,
"winLimitType": "NO_LIMIT",
"winLimitCnt": 10,
"winWay": "RANDOM",
"winTotalCntLimitType": "LIMIT",
"winTotalCnt": 50,
"asyncWinYn": "N",
"groupRetryEventYn": "Y",
"myPageExposeYn": "Y",
"siteType": "GMARKET",
"rewardValidDay": 7,
"vipDisplayYn": "Y",
"vipDisplayBrandName": "VIP Brand",
"apprStatus": "APPROVED",
"apprOprt": "admin",
"apprDate": "2024-09-29T10:00:00Z",
"simpleJoinCertUseYn": "Y",
"eventApplyLimitNo": "3333",
"groupEventYn": "Y",
"couponpackUseYn": "Y",
"encStr": "encryptedString123"
}
<참고>
'Back-end' 카테고리의 다른 글
Protobuf와 Map Struct 친해지길 바래 (0) | 2024.10.15 |
---|---|
[Redis] 나야 조회수 (3) | 2024.10.15 |
[gRPC] Armeria + gRPC 띄워보기 (7) | 2024.10.07 |
[gRPC] gRPC란 (3) | 2024.10.02 |
[Spring/Thymeleaf] option 태그에 enum 동적으로 넣기 (0) | 2024.08.10 |