Cacheable 삽질
TIL/Java2025. 2. 5. 16:35
서버 내에서 자주 쓰이는 값을 캐싱처리해야 할 일이 있었다.
단순히 String이 아니라, 좀 더 편하게 꺼내 쓰기 위해 자바 객체로 담아 redis 캐시로 저장하기로 했다!
하지만...
DefaultSerializer requires a Serializable payload but received an object of type...
기본 직렬화를 하기 위해서는 Serializable한 데이터가 있어야 한다는 에러 메시지가 떴다.
redis에 캐시를 저장할 때, redis는 데이터를 hash해서 저장한다.
그렇기 때문에, redis에 저장할 클래스는 Serializable을 implement해서 만들어야한다!
Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception
[Request processing failed: org.springframework.data.redis.serializer.SerializationException: Cannot deserialize]
with root cause java.lang.ClassNotFoundException: kr.doodoo.platform.system.model.Setting
at jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641) ~[?:?]
at jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) ~[?:?]
at java.lang.ClassLoader.loadClass(ClassLoader.java:526) ~[?:?] ...
관리자 백엔드에 있는 시스템 설정값을 감싸는 클래스와 플랫폼 서버에서의 클래스 경로가 일치하지 않아 생기는 문제
결국 String으로 저장하고 이를 받아 objectMapper를 통해 해당 클래스로 변환하는 서비스 메서드를 추가함.
public <T> T getSettingValue(SystemSettingKey stKey, Class<T> clazz) {
String systemSettingMap = systemSettingCacheService.getSystemSetting(stKey);
try {
return objectMapper.readValue(systemSettingMap, clazz);
} catch (Exception e) {
log.error(e.getMessage(), e);
throw new JsonParsingException();
}
}
근데 그럼 결국 저 값을 조회할 때마다 ObjectMapper.readValue 해야하는데 그건 또 효율이 떨어질것같음.
그래서 Cacheable 키를 따로 두고, 관리자에서 업데이트할때마다 그 키의 값을 지우도록 함.
public <T> void updateWhenValueExists(T target, T source) {
try {
Field[] fields = source.getClass().getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
Object value = field.get(source);
if (value != null) { // null 이 아닌 값만 업데이트
if (!isPrimitiveOrWrapper(value.getClass())) {
Object targetValue = field.get(target);
Object sourceValue = field.get(source);
updateWhenValueExists(targetValue, sourceValue);
} else {
field.set(target, value);
}
}
}
} catch (Exception e) {
log.error(e);
throw new JsonParsingException();
}
}
private static boolean isPrimitiveOrWrapper(Class<?> type) {
return type.isPrimitive() ||
type.equals(String.class) ||
type.equals(Integer.class) ||
type.equals(Double.class) ||
type.equals(Float.class) ||
type.equals(Long.class) ||
type.equals(Short.class) ||
type.equals(Byte.class) ||
type.equals(Character.class) ||
type.equals(Boolean.class);
}
근데 걍 ObjectMapper의 updateValue를 쓰는게 훨씬 빠름. 대략 15배정도 차이가 났다^^;
https://mangkyu.tistory.com/179
https://velog.io/@alsgus92/Java-Reflection은-무엇이고-언제어떻게-사용하는-것이-좋을까
'TIL > Java' 카테고리의 다른 글
세션 관리 삽질 (0) | 2025.02.25 |
---|---|
@JsonManagedReference와 @JsonBackReference (0) | 2024.10.31 |
프로파일별로 다른 파일이 실행되도록 세팅하기 (0) | 2024.08.02 |