본문 바로가기

개발/팀 프로젝트

MSA 구조에서의 요청/응답 불일치 문제

문제정의

  • Spring Cloud Service에서 서버간 데이터를 주고 받을 때 데이터의 일관된 형식을 지키지 않게 되면 서로 주고 받지 못하는 문제 발생

해결과정

  • 코드
    • 받아오는 필드들의 구조와 이름을 정확하게 일치시키는 작업이 필요했습니다. user 서버에서 delivery 서버로 요청을 보내는 경우를 예로 들자면
    • 이 부분은 delivery-server의 controller 부분입니다.
     		@GetMapping
        public ResponseEntity<?> getManagers(
                @RequestParam(value = "search", required = false) String search, // 배송 담당자 타입
                @RequestParam(value = "type", required = false) String type, // 배송 담당자 타입
                @RequestParam(value = "delivery_manager_id", required = false) Long deliveryManagerId, // 허브 ID
                @RequestParam(value = "hub_id", required = false) UUID hubId, // 허브 ID
                @RequestParam(value = "order_id", required = false) UUID orderId, // 허브 ID
                @RequestParam(value = "sequence_min", required = false) Integer sequenceMin, // 최소 순번
                @RequestParam(value = "sequence_max", required = false) Integer sequenceMax, // 최대 순번
                @RequestParam(value = "created_from", required = false) String createdFrom, // 생성 시작일
                @RequestParam(value = "created_to", required = false) String createdTo, // 생성 종료일
                @RequestHeader("X-User_Id") String userId,
                @RequestHeader("X-Username") String username,
                @RequestHeader("X-Role") String role,
                Pageable pageable) throws AccessDeniedException {
            Page<DeliveryManagerDto> managers =
                    deliveryManagerService.getManagers(search, type, deliveryManagerId, hubId, orderId, sequenceMin, sequenceMax,
                            createdFrom, createdTo, userId, role, pageable);
    
            return ResponseEntity.status(HttpStatus.OK).body(
                    CommonResponse.success(HttpStatus.OK.value(), "검색 조회가 완료되었습니다.", managers)
            );
        }
        
        @DeleteMapping("/{deliveryManagerId}")
        public ResponseEntity<?> deleteManager(@PathVariable Long deliveryManagerId,
                                             @RequestHeader("X-User_Id") String userId,
                                             @RequestHeader("X-Username") String username,
                                             @RequestHeader("X-Role") String role) throws AccessDeniedException {
            deliveryManagerService.deleteManager(deliveryManagerId, userId, username, role);
            return ResponseEntity.status(HttpStatus.CREATED).body(
                    CommonResponse.success(HttpStatus.CREATED.value(), "배송 담당자 정보 삭제가 완료되었습니다.", null)
            );
        }
        
    
    required false인 부분은 없어도 상관없으나, 기본적으로 required는 true로 설정되어있기 때문에 명시되어있지 않은 부분은 요청 때 반드시 넣어주어야 하겠죠? 

 

  • 아래 부분은 delivery manager를 검색 시 가져오는 json 구조입니다.
  • { "status": 200, "message": "검색 조회가 완료되었습니다.", "data": { "content": [ { "deliveryMangerId": 4, "slackId": "deliverySlack0", "type": "HUB_DELIVERY_MANAGER", "sequence": 2, "createdBy": "master0", "createdAt": "2024-12-16T18:42:00.697351", "updatedAt": "2024-12-16T19:18:30.733477" } ], "pageable": { "pageNumber": 0, "pageSize": 20, "sort": [], "offset": 0, "paged": true, "unpaged": false }, "last": true, "totalPages": 1, "totalElements": 1, "size": 20, "number": 0, "sort": [], "first": true, "numberOfElements": 1, "empty": false } }

 

  • 아래 부분은 user-server의 DeliveryService 인터페이스를 상속받아 사용하는 DeliveryClient 클래스는 다음과 같습니다.
@FeignClient(name = "delivery-service")
public interface DeliveryClient extends DeliveryService {
    @GetMapping("/api/deliveries/delivery-managers")
    ResponseEntity<ApiResponseDto<GetUUIDDto>> getDeliveryManagerByUserId(@RequestParam(name = "delivery_manager_id") Long userId,
                                                                          @RequestHeader(value = "X-User_Id", required = true) @NotBlank String headerUserId,
                                                                          @RequestHeader(value = "X-Username", required = true) @NotBlank String username,
                                                                          @RequestHeader(value = "X-Role", required = true) @NotBlank String role);

    @DeleteMapping("/api/deliveries/delivery-managers/{delivery_manager_id}")
    ResponseEntity<ApiResponseDto<?>> softDeleteDeliveryManager(@PathVariable(name = "delivery_manager_id") Long deliveryManagerId,
                                                                @RequestHeader(value = "X-User_Id", required = true) @NotBlank String headerUserId,
                                                                @RequestHeader(value = "X-Username", required = true) @NotBlank String username,
                                                                @RequestHeader(value = "X-Role", required = true) @NotBlank String role
    );
}

 

 

저는 여기서 필요한 필드값만을 사용하기 위해 다음 dto를 예시로 만들어 사용하였습니다.

@Getter
@AllArgsConstructor
@JsonInclude(JsonInclude.Include.NON_NULL)
public class GetUUIDDto {

    private List<UUIDListDto> content;

    @Getter
    @NoArgsConstructor
    @AllArgsConstructor
    @Builder
    @JsonInclude(JsonInclude.Include.NON_NULL)
    public static class UUIDListDto {
        private Long deliveryManagerId;
    }
}

응답 데이터의 “data” 안의 “content”안의 “deliveryMangerId”만을 사용하기 위해 GetUUIDDto 를 만들어 List 타입으로 content를 받게 하고 inner class를 만들어 deliveryManagerId를 가져올 수 있게 하였습니다.

 

결과

  • 위처럼 실행 후 실제 서버를 실행 후 삭제 요청을 보내니 정상적으로 user와 delivery manager 쪽에서 삭제되는 걸 확인할 수 있었습니다.

깨달은 내용

  • 개인적으로 느낀 건 서버간 통신을 할 때도 client의 개념을 적용시켜야 한다는 것이었습니다. 서버라고 한다면 무조건 요청을 받고 데이터를 넘겨주어야 하는 것으로 알고 있었지만 서버간에도 client 역할이 있고 server의 역할이 있다는 것을 느꼈어요. 왜 FeignClient라고 이름을 지었는지 좀 알게 된 것 같습니다. 데이터를 넘겨줄 때 FeignClient를 통해서 우리가 postman을 통해 요청을 보내는 것처럼 만들어주고, 받아올 수 있는 것을 알게 되고 나니 너무 좁게만 생각하고 있었던 것 같네요. 제가 틀릴 수도 있다는 것을 알고 시야를 넓게 가지게 되는 경험이 됐습니다!

'개발 > 팀 프로젝트' 카테고리의 다른 글

Json 구조의 이해...  (0) 2025.01.03
Postgresql 연결 오류  (0) 2024.12.09
Github를 이용한 팀 프로젝트  (0) 2024.11.12
명세서 작업  (1) 2024.11.11