개요
프로젝트를 진행하며 발생했던 문제 중 해결하는 데 어려움을 겪었던 사항들에 대해 기록하고 추후 같은 문제가 발생했을 경우 정리한 글을 바탕으로 좀 더 빠르게 해결할 수 있도록 할 것이다.
Json 직렬화, 역직렬화 문제
1. Cache 설정
2024-12-11T14:07:10.569+09:00 ERROR 25596 --- [hub] [io-19094-exec-2] [67591dfe8cd348ded9a885facc780af9-d9a885facc780af9] c.s.m.h.p.e.GlobalExceptionHandler : Cannot serialize
2024-12-11T14:07:10.606+09:00 ERROR 25596 --- [hub] [io-19094-exec-2] [ ] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: org.springframework.data.redis.serializer.SerializationException: Cannot serialize] with root cause
- 프로젝트에서 Hub에 대한 값이 변경이 적기 때문에 Caching을 하기 위해 Redis에 Cache 데이터를 저장하려고 했을 때 발생한 에러 메시지이다.
@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
RedisCacheConfiguration configuration = RedisCacheConfiguration
.defaultCacheConfig()
// null을 캐싱하는지
.disableCachingNullValues()
// 기본 캐시 유지 시간 (Time to Live)
.entryTtl(Duration.ofSeconds(60))
// 캐시를 구분하는 접두사 설정
.computePrefixWith(CacheKeyPrefix.simple())
// 캐시에 저장할 값을 어떻게 직렬화 / 역직렬화 할 것인지
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.json()));
return RedisCacheManager
.builder(connectionFactory)
.cacheDefaults(configuration)
.build();
}
}
- 기존 캐시 설정에서 serializeValuesWith 부분에서 RedisSerializer를 캐시를 실습했을 때 처럼 .java()로 사용했는데 보통 Redis와 상호작용을 위해 JSON 직렬화를 사용해야 해서 .json()으로 변경했다.
2. 요청 값에 대한 서버의 반환 타입 불일치
Hibernate: insert into p_hub (address,hubuuid,is_deleted,latitude,longitude,manager_id,name) values (?,?,?,?,?,?,?)
2024-12-11T14:15:13.957+09:00 WARN 11484 --- [hub] [io-19094-exec-2] [67591fe16574a9fe6a448fc034c4a23d-6a448fc034c4a23d] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.HttpMediaTypeNotAcceptableException: No acceptable representation]
// 요청에 대한 반환 값
{
"timestamp": "2024-12-11T05:15:13.974+00:00",
"status": 406,
"error": "Not Acceptable",
"path": "/hubs"
}
- 캐시 파일 설정 이후 서버로 요청을 보내자 다음 에러와 함께 에러 값이 반환 되었다.
문제 해결을 위해 코드를 검토한 결과 JSON 직렬화/역직렬화 관련 문제인 것으로 확인 되었다.
@Entity
@Getter
@Table(name = "p_hub")
@AllArgsConstructor
@NoArgsConstructor
@Builder(access = AccessLevel.PRIVATE)
public class Hub extends BaseEntity implements Serializable {
// Entity Code 생략
}
- 캐시와 연관되서 직렬화/역직렬화가 필요한 것이라고 판단되어 DTO와 Entity에 Serializable을 추가했다.
@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder(access = AccessLevel.PRIVATE)
@ToString
public class HubResponse implements Serializable {
@JsonFormat(shape = JsonFormat.Shape.STRING)
private UUID hubUUID;
private String hubName;
private String hubAddress;
private Double hubLatitude;
private Double hubLongitude;
private String hubManagerId;
public static HubResponse fromHub(Hub hub) {
return HubResponse.builder()
.hubUUID(hub.getHubUUID())
.hubName(hub.getName())
.hubAddress(hub.getAddress())
.hubLatitude(hub.getLatitude())
.hubLongitude(hub.getLongitude())
.hubManagerId(hub.getManagerId())
.build();
}
}
- 추가적으로 JSON이 UUID 타입을 직접적으로 지원하지 않는다고 해 UUID를 String으로 직렬화 하기 위해 @JsonFormat(shape = JsonFormat.Shape.STRING)을 추가했다.
3. PageableDefault 값 설정 문제
2024-12-11T15:21:42.652+09:00 ERROR 48800 --- [hub] [io-19094-exec-7] [67592f76b30a018bfc514cab91d8349e-fc514cab91d8349e] c.s.m.h.p.e.GlobalExceptionHandler : No property 'created' found for type 'Hub'
@GetMapping
public CommonResponse<GetHubsResponse> findAllHubs(
@QuerydslPredicate(root = Hub.class) Predicate predicate,
@PageableDefault(sort = "created_at") Pageable pageable
) {
return CommonResponse.ofSuccess(hubQueryService.findAllHubs(predicate, pageable));
}
- Hub Entity가 BaseEntity를 상속받지 않은 상태에서 sort 값을 지정해서 'created_at'을 찾을 수 없다는 에러였다.
- BaseEntity를 연결하지 않은 상태에서 테스트를 진행하기 위해 pageable default 값을 제거했다.
4. PagedModel이 역직렬화를 하지 못하는 문제
해당 문제는 혼자서 해결하지 못 하고 튜터님을 찾아가서 도움을 받았다.
2024-12-11T15:38:30.373+09:00 ERROR 55956 --- [hub] [io-19094-exec-9] [67593366ba4b3be60fe4b36c212394bb-0fe4b36c212394bb] c.s.m.h.p.e.GlobalExceptionHandler : Could not read JSON:Cannot construct instance of com.sparta.msa.hub.application.dto.GetHubsResponse$HubPage (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
at [Source: REDACTED (StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION disabled); line: 1, column: 145] (through reference chain: com.sparta.msa.hub.application.dto.GetHubsResponse["hubPage"])
기존 코드
@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class GetHubsResponse implements Serializable {
private HubPage hubPage;
public static GetHubsResponse of(Page<Hub> hubPage) {
return GetHubsResponse.builder()
.hubPage(new HubPage(hubPage))
.build();
}
@Getter
@ToString
public static class HubPage extends PagedModel<HubPage.Hub> {
public HubPage(Page<com.sparta.msa.hub.domain.entity.Hub> hubPage) {
super(
new PageImpl<>(
Hub.from(hubPage.getContent()),
hubPage.getPageable(),
hubPage.getTotalElements()
)
);
}
@Getter
@Builder
@AllArgsConstructor
public static class Hub {
@JsonFormat(shape = JsonFormat.Shape.STRING)
private UUID hubUUID;
private String hubName;
private String hubAddress;
private Double latitude;
private Double longitude;
private Long managerId;
public static List<Hub> from(List<com.sparta.msa.hub.domain.entity.Hub> hubList) {
return hubList.stream()
.map(HubPage.Hub::from)
.toList();
}
public static Hub from(com.sparta.msa.hub.domain.entity.Hub hub) {
return Hub.builder()
.hubUUID(hub.getHubUUID())
.hubName(hub.getName())
.hubAddress(hub.getAddress())
.latitude(hub.getLatitude())
.longitude(hub.getLongitude())
.managerId(hub.getManagerId())
.build();
}
}
}
}
- QueryDSL 실습에 사용했던 코드를 수정해서 사용했던 코드로 튜터님과 함께 문제 원인을 찾아본 결과 HubPage가 PagedModel을 상속받는데 여기서 직렬화 관련 문제가 발생했던 것이었다.
코드 수정
public static class HubPage implements Serializable{
private List<Hub> content;
private int pageNumber;
private int pageSize;
private long totalElements;
public static HubPage from(Page<com.sparta.msa.hub.domain.entity.Hub> hubPage) {
return
new HubPage(
HubPage.Hub.from(hubPage.getContent()),
hubPage.getPageable().getPageNumber(),
hubPage.getPageable().getPageSize(),
hubPage.getTotalElements()
);
}
- PagedModel을 별도로 구성해서 사용하는 것으로 직렬화 문제를 해결했다.
- 추후 PagedModel을 사용할 수 있도록 더 알아보고 싶다.
깃 내역 꼬임 문제
1. 문제 발생
Hub CRUD에 대한 기본 개발이 끝난 후 원격 레포지토리의 git 내역을 pull 받는 과정에서 hub 폴더를 루트 폴더로 인식해 hub 폴더 안에 gateway, server, order 등 다른 애플리케이션 폴더가 위치하는 문제가 발생했다.
이를 터미널에서 해결하려는 과정에서 git 내역이 꼬이는 문제가 발생했고, branch를 push해도 hub 자체의 코드를 추적하지 못하고 빈 폴더만 원격 브랜치에 올라가는 문제가 발생했다.
구글링 및 ChatGPT로도 원하는 내용을 찾지 못해 튜터님의 도움을 받아 해결했다.
2. 문제 해결
튜터님이 따로 실험하신 결과 꼬인 깃을 그 프로젝트에서 해결하는 것은 어렵다고 판단하셔서 새로 git clone을 받은 후 기존의 hub 폴더를 복사해 온 후 .git 파일과 불필요 파일을 제거한 후 git 내역을 새로 등록한 후 push 하는 것으로 해결했다.
원인은 hub를 .git파일이 있어서 root 폴더로 인식해서 git pull 시 허브 폴더 내로 다른 msa 폴더들이 위치하게 되는 것이었다.
열심히 작업하던 git 내역이 다 날아간 것은 아쉬운 일이었지만 다행히 작업했던 코드들은 멀쩡했고, 덕분에 .git 파일의 존재와 왜 허브 폴더를 루트 폴더로 인식했는지를 알게되었다.
정리
- 멀쩡하게 동작하던 코드가 UUID와 Cahce로 인해 직렬화/역직렬화 문제가 발생했고, 해결하는데 많은 어려움을 겪었다. 좀 더 공부가 필요할 것 같다.
- git도 모놀리식 구조에서는 겪지 않았던 문제를 겪었고, 해결하는 과정에서 많은 것을 배웠다.
- 프로젝트를 하면서 일어났던 문제가 다음에는 발생하지 않도록 좀 더 주의하고 발생하더라도 이 정리글을 통해 빠른 해결을 할 수 있도록 해야겠다.
'자바 심화 > TIL' 카테고리의 다른 글
프로젝트 회고 (0) | 2024.12.18 |
---|---|
Open Route Service API 사용 (1) | 2024.12.16 |
DDD(Domain-Driven Design) (1) | 2024.12.09 |
SAGA 패턴 (1) | 2024.12.07 |
Kafka - 기초 (0) | 2024.12.06 |