-
8월 18일 금요일 TIL 회고록카테고리 없음 2023. 8. 19. 00:01
redis.serializer.serializationexception 에러 문제
게시글을 작성, 작성 후 상세(선택) 조회 까지는 문제 없이 동작하나 게시글을 수정 후 상세(선택) 조회를 하려고 하면 아래와 같은 오류가 출력되면서 조회가 안되었다. 3~4번 새로고침을 하거나 조금 기다리면 오류가 없어지며 상세(선택) 조회가 되기는 하나 오류를 고치고 싶었다.
redis.serializer.serializationexception could not read json: cannot construct instance of `org.springframework.http.responseentity 및 InvalidDefinitionException 도 같이 출력
오류를 해결하려 구글링을 하던 중 아래 블로그를 발견했다.
https://ahndding.tistory.com/24
[JPA] FetchType.Lazy로 인한 JSON 오류 (InvalidDefinitionException: No serializer found for class)
발단 Front에서 엔티티를 저장하는 과정에서 처음 데이터를 저장하는 순간에는 올바르게 작동하지만 수정(edit)하고 저장할 때에는 에러(HttpStatus 500)를 리턴한다는 문제가 발생했다. (다만 DB상으
ahndding.tistory.com
위에 블로그에서 2번 방법을 사용해봤다. (@JsonIgnore 사용)
Board 엔티티에서 FetchType.Lazy 어노테이션을 사용한 필드는 User 필드이므로 User 필드에 @JsonIgnore 어노테이션을 붙였다.
@ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "USER_ID", nullable = false) @JsonIgnore private User user;
이 어노테이션을 사용하면 Board를 JSON 객체화 할때 figure를 제외한 정보들만이 serialize하게된다.
이후 실행한 뒤 Postman으로 확인해보니 여전히 오류가 나왔다. 이 문제는 아니었다.
InvalidDefinitionException 해결법을 사용해봤는데 안되는걸 보면 redis.serializer.serializationexception 가 주 원인일 것 같아서 구글링 해봤다.
https://pika-chu.tistory.com/981
[Error] org.springframework.data.redis.serializer.SerializationException
org.springframework.data.redis.serializer.SerializationException: Cannot serialize; nested exception is org.springframework.core.serializer.support.SerializationFailedException: Failed to serialize object using DefaultSerializer; nested exception is java.i
pika-chu.tistory.com
위에 블로그를 참고하여 다시 시도해봤으나 역시 같은 오류가 출력되었다.
구글링을 하며 찾아보는 것은 그만두고 레디스를 사용한 부분의 코드들을 다시 한 번 봤다.
생각해보니 게시글 조회수를 구현할 때 중복 카운트를 방지하려고 게시글 선택 조회, 수정, 삭제 시 캐시 관련 어노테이션을 붙였다.
BoardController.class
// 게시글 선택 조회 @GetMapping("/boards/{basicBoardId}") @Cacheable(key = "#basicBoardId", value = "boards", cacheManager = "cacheManager") public BasicBoardResponse getSelectBasicBoards(@PathVariable Long basicBoardId, @AuthenticationPrincipal UserDetailsImpl userDetails) { return basicBoardService.getSelectBasicBoards(basicBoardId, userDetails.getUser()); } // 게시글 수정 @PutMapping("/basic-boards/{basicBoardId}") @CachePut(key = "#basicBoardId", value = "boards", cacheManager = "cacheManager") public ResponseEntity<String> updateBasicBoard( @AuthenticationPrincipal UserDetailsImpl userDetails, @RequestPart("request") BasicBoardRequest request, @RequestPart(required = false, name = "images") List<MultipartFile> multipartFiles, @PathVariable Long basicBoardId) throws IOException { basicBoardService.updateBasicBoard(userDetails.getUser(), request, multipartFiles, basicBoardId); return new ResponseEntity<>("게시물 수정이 완료되었습니다.", HttpStatus.OK); } // 게시글 삭제 @DeleteMapping("/basic-boards/{basicBoardId}") @CacheEvict(key = "#basicBoardId", value = "boards", cacheManager = "cacheManager") public ResponseEntity<String> deleteBasicBoard( @AuthenticationPrincipal UserDetailsImpl userDetails, @PathVariable Long basicBoardId) { basicBoardService.deleteBasicBoard(userDetails.getUser(), basicBoardId); return new ResponseEntity<>("게시글이 삭제되었습니다.", HttpStatus.OK); }
cacheManager 메서드
@Bean public CacheManager cacheManager() { RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig() .serializeKeysWith( RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())) .serializeValuesWith( RedisSerializationContext.SerializationPair.fromSerializer( new GenericJackson2JsonRedisSerializer())) // 기본 TTL은 30초로 설정 .entryTtl(Duration.ofSeconds(30)); return RedisCacheManager.RedisCacheManagerBuilder.fromConnectionFactory(redisConnectionFactory) .cacheDefaults(redisCacheConfiguration).build(); }
내 생각은 캐시 관련 어노테이션 때문에 나오는 오류인 것 같았다. (아직 미완성 상태로 냅둔 것 같았다.)
그래서 @Cacheable, @CachePut, @CacheEvict 어노테이션을 주석 처리한 후 실행했다.
실행 결과
글을 작성한 후, 상세 조회
게시글을 수정한 후, 상세 조회
게시글을 수정하고 바로 상세 조회를 해도 오류가 나지 않는다. 조회수 기능을 다시 한 번 수정해야 할 것 같다.
조회수 중복 방지를 하려면 캐시를 사용해서 중복 방지를 하거나, 스케줄러를 사용해 중복 방지를 하거나, 레디스를 사용해서 중복 방지를 할 수 있는데 난 레디스를 사용해서 중복 방지를 해보고 싶은데.. 구글링을 해보면서 공부해야겠다.