728x90

 

객체 한 번 입력 안했다고 Exception에 멱살 잡힌 이야기를 정리해 봅니다..

 

농담입니다.

 

RedisTemplate을 사용할 때, Class Type을 명시할 경우, 재사용성이 떨어져서 Object로 Value값을 지정할 경우, 나타나는 오류에 대해 간단히 정리해 보고자 합니다.

 

 

 

1. 상황

RedisTemplate를 광범위하게 사용할 수 있도록 Value를 Object로 확장

@Bean
RedisTemplate<String, Object> objectRedisTemplate(RedisConnectionFactory connectionFactory) {
	ObjectMapper objectMapper = new ObjectMapper()
		.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
		.registerModule(new JavaTimeModule())
		.disable(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS);

	RedisTemplate<String, Object> template = new RedisTemplate<>();
	template.setConnectionFactory(connectionFactory);
	template.setKeySerializer(new StringRedisSerializer());
	template.setValueSerializer(new GenericJackson2JsonRedisSerializer(objectMapper));

	return template;
}

 

 

 

2. 문제

LinkedHashMap을 User 타입으로 Casting하지 못해 ClassCastException 발생

java.lang.ClassCastException: 
class java.util.LinkedHashMap cannot be cast to class com.example.spring_boot_cache.domain.entity.User (java.util.LinkedHashMap is in module java.base of loader 'bootstrap';

GenericJackson2JsonRedisSerializer는 JSON 데이터를 역직렬화할 때, 대상 객체의 타입 정보를 명시적으로 제공하지 않으면 기본적으로 Map 타입(즉, LinkedHashMap)으로 역직렬화

→ LinkedHashMap을 User 객체로 강제 캐스팅할 수 없어 오류 발생

 

 

3. 해결 방법

JSON 데이터를 저장할 때 타입 정보를 포함

@Bean
RedisTemplate<String, Object> objectRedisTemplate(RedisConnectionFactory connectionFactory) {
	PolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator
		.builder()
		.allowIfSubType(Object.class)
		.build();

	ObjectMapper objectMapper = new ObjectMapper()
		.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
		.registerModule(new JavaTimeModule())
		.activateDefaultTyping(ptv, ObjectMapper.DefaultTyping.NON_FINAL)
		.disable(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS);

	RedisTemplate<String, Object> template = new RedisTemplate<>();
	template.setConnectionFactory(connectionFactory);
	template.setKeySerializer(new StringRedisSerializer());
	template.setValueSerializer(new GenericJackson2JsonRedisSerializer(objectMapper));

	return template;
}

Redis에 저장되는 데이터의 타입은 여러 종류(Object)로 다양
Jackson(직렬화 라이브러리)은 기본적으로 타입 정보를 추가하지 않음
데이터를 읽어올 때 원래 타입이 무엇인지 알 수 없는 문제가 발생

PolymorphicTypeValidator: allowIfSubType을 통해 변환할 수 있는 데이터 타입을 정의

activateDefaultTyping: Jackson에서 직렬화/역직렬화 시 객체 타입 정보를 포함하도록 설정하는 메서드
  만일, allowIfSubType에서 허용하지 않은 class type이 redis에 저장되어있는 채로 역직렬화 시도 시, InvalidTypeIdException 발생
PolymorphicTypeValidator: 역직렬화 시 허용된 클래스 타입인지 검증하는 데 사용
DefaultTyping: 비최종 클래스(Non-final class)에 대해서만 타입 정보를 포함
예를 들어, Object나 서브클래스에는 타입 정보가 포함되지만, String, Integer처럼 final로 선언된 클래스는 제외

 

 

+ TTL 설정은 안해서 Redis 값이 Class 정보를 포함하지 않은 채로 남아있게 되면 만날 수 있는 오류..🍂

# 오류
com.fasterxml.jackson.databind.exc.MismatchedInputException: 
Unexpected token (START_OBJECT), expected START_ARRAY: 
need Array value to contain As.WRAPPER_ARRAY type information for class java.lang.Object

# Redis 패키지가 저장되지 않았던 데이터
"{\"id\":3,\"name\":\"hi3\",\"email\":\"hi3@email.com\",\"createdAt\":[2025,1,18,18,25,53,321769000],\"updatedAt\":[2025,1,18,18,25,53,321769000]}"

# Redis 패키지가 저장된 데이터
"[\"com.example.spring_boot_cache.domain.entity.User\",{\"id\":3,\"name\":\"hi3\",\"email\":\"hi3@email.com\",\"createdAt\":[2025,1,18,20,33,16,59381000],\"updatedAt\":[2025,1,18,20,33,16,59381000]}]"

 

 

 

📑

참고 자료

Chat GPT

https://fastcampus.co.kr/dev_online_traffic_data

 

9개 프로젝트로 경험하는 대용량 트래픽 & 데이터 처리 완벽 마스터하기 | 패스트캠퍼스

실무에서 자주 일어나는 대용량 트래픽 & 데이터 처리 업무를 한번에 마스터할 수 있도록 모든 것을 담았습니다. 대기업 & 빅테크 현업 강사진 8인과 함께 하는 고퀄리티 현업 대비형 강의! 타사

fastcampus.co.kr

 

 

728x90