24.10.03 20:31 작성
·
40
0
안녕하세요! 강의를 수강하고 제 프로젝트에서 캐싱을 적용하고 싶어서 따라 적용해보고 있습니다.
package com.ecommerceproduct.api.controller.product.dto.response;
import com.ecommerceproduct.domain.product.repository.dao.ProductDetailDao;
import com.ecommerceproduct.domain.product.type.OptionType;
import com.ecommerceproduct.domain.product.type.ProductCategory;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import lombok.Builder;
@Builder
public record ProductDetailResponse(
Long id,
String name,
StoreInfo store,
int quantity,
ProductCategory category,
String thumbnailImgUrl,
int basePrice,
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
LocalDateTime createdDateTime,
List<ProductOptionInfo> options
) {
public static ProductDetailResponse from(List<ProductDetailDao> daos){
List<ProductOptionInfo> options = new ArrayList<>();
daos.forEach(dao -> options.add(ProductOptionInfo.from(dao.option())));
ProductDetailDao dao = daos.get(0);
return ProductDetailResponse.builder()
.id(dao.id())
.name(dao.name())
.store(StoreInfo.from(dao.store()))
.quantity(dao.quantity())
.category(dao.category())
.thumbnailImgUrl(dao.thumbnailImgUrl())
.basePrice(dao.basePrice())
.createdDateTime(dao.createdDateTime())
.options(options)
.build();
}
public record StoreInfo(
Long storeId,
String name
) {
public static StoreInfo from(ProductDetailDao.StoreInfo daoStore){
return new StoreInfo(daoStore.storeId(), daoStore.name());
}
}
public record ProductOptionInfo(
Long optionId,
String name,
int count,
int price,
OptionType optionType
){
public static ProductOptionInfo from(ProductDetailDao.ProductOptionInfo option){
return new ProductOptionInfo(
option.optionId(),
option.name(),
option.count(),
option.price(),
option.optionType()
);
}
}
}
해당 클래스를 반환하는 메서드에 @Cacheable을 적용하려고 합니다.
@Cacheable(cacheNames = "getProduct", key = "'product:productId:' + #productId", cacheManager = "매니저이름")
public ProductDetailResponse get(Long productId) {
return ProductDetailResponse.from(productRepository.findWithOptions(productId));
}
강의에서 알려주신 매니저와 동일하게 매니저를 bean으로 등록해서 사용해본결과
직렬화/역질렬화가 안되는것으로 보입니다.
매니저에 설정을
serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(
new Jackson2JsonRedisSerializer<>(objectMapper, ProductDetailResponse.class)))
처럼 명시적으로 타입을 지정해주면 문제가 없는 것 같은데 record여서 안되는 걸까 싶어서 class로 바꾸어도 되지는 않더라고요! 강의에서의 Board 클래스는 문제없이 직렬화/역직렬화가 되는데 제가만든 dto가 안되는 이유는 강의의 Board가 @Entity클래스여서 인걸까요??
그렇다면 dto로 쓰려면 매니저를 각각의 dto마다 매니저를 하나씩 만들어주어야할까요?
답변 2
0
2024. 10. 03. 21:16
안녕하세요 후시딘님!
직렬화/역직렬화를 하는 과정에서 getter 함수가 필요합니다!
Board 엔티티에서는 직렬화/역직렬화가 정상적으로 수행되기 위해
getter 함수를 만들어주었습니다.
위에서 DTO를 만드셔서 사용하실 때 getter 함수를 추가해보시고
정상적으로 작동하는 지 확인 한 번 해보세요!
지금 만드신 복잡한 코드에서는 테스트가 어려울 수 있으니
먼저 간단한 클래스를 만드셔서 getter를 추가했을 때와 추가하지 않았을 때
작동 상의 차이가 있는 지 먼저 체크해보시면 좋을 것 같습니다~!!
혹시 테스트해보시고 안 되신다면 또 질문 남겨주시기 바랍니다:D
0
안녕하세요, 인프런 AI 인턴입니다. 후시딘님께서 캐싱 적용에 관해 질문을 주셨네요. Redis에서 직렬화/역직렬화 문제를 해결하기 위해 어떤 접근 방식을 사용할 수 있는지 답변드리겠습니다.
Spring Cache에서 객체를 캐시할 때, Jackson2JsonRedisSerializer를 사용하여 JSON 형식으로 직렬화를 진행합니다. 여기서 중요한 점은 Spring Cache와 Jackson이 객체를 직렬화/역직렬화할 때 어떤 방식으로 이 작업을 수행하는지 이해하는 것입니다.
@Cacheable
을 사용하면 메서드가 리턴하는 객체가 직렬화되어 Redis에 저장됩니다. 이때 사용하는 시리얼라이저가 해당 객체를 직렬화할 수 있어야 합니다.Jackson2JsonRedisSerializer를 사용할 때 ObjectMapper에서 기본 설정을 확인하세요. 만약 record 형태를 사용하고 있다면, Jackson2JsonRedisSerializer를 사용할 때 클래스의 타입을 명확하게 지정해야 합니다:
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(
new Jackson2JsonRedisSerializer<>(ProductDetailResponse.class))
);
return RedisCacheManager.builder(redisConnectionFactory)
.cacheDefaults(cacheConfiguration)
.build();
}
DTO마다 별도의 CacheManager를 생성하는 것은 관리상 비효율적일 수 있습니다. 하나의 공통 CacheManager를 사용하고, 공통 Serialize 설정에서 각 DTO가 올바르게 직렬화/역직렬화 될 수 있게 합니다.
직렬화/역직렬화 시 ObjectMapper에서 참조하는 모듈이나 설정이 충분한지 확인합니다. 예를 들어, JavaTimeModule
을 등록하여 LocalDateTime을 처리할 수 있도록 할 필요가 있습니다:
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
주의사항: record나 특정한 객체에 대해 Jackson이 제대로 동작하지 않는다면, 해당 클래스의 구조 혹은 의존성을 점검하시는 것이 필요합니다.
추가적으로 도움이 필요하시면 아래 질문 링크에서도 더 많은 정보를 얻을 수 있습니다:
이 외에 추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 질문에 대해 더 명확한 답변 드릴 수 있도록 하겠습니다.
저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.