본문 바로가기
Books

스프링부트로 개발하는 MSA 컴포넌트 [5]장

by YoonJong 2023. 8. 24.
728x90

 스프링 MVC를 이용한 REST-API 개발

  • Jackson 라이브러리를 포함하고 있어, JSON 에 대한 처리에 대한 추가 설정이 필요없다.
  • 명세에 필수/선택 여부가 반드시 정의되어야 한다.
  • @ResponseBody  → 스프링 MVC 의 View 를 사용하지 않는다.
  • @RestController 를 사용하면, 응답값으로 리턴하는 객체는 마셜링된다.
  • @RequestMappingHandlerMapping → @RequestMapping 으로 구현된 컨트롤러가 대상이면 사용,
  • 사용자 요청과 핸들러를 매핑하는 정보를 관리

 

Controller 클래스 구현

  • 스프링 빈으로 설정되어야 한다
  • @PathVariable
  • @RequestParam
  • @RequestHeader

 

REST-API 응답 메시지 처리

  • 엔티티 객체를 DTO 로 사용하는 것을 지양하자.
  • ObjectMapper 는 멀티스레드 환경에서 안전하게 사용할 수 있다.

 

REST-API POST, PUT 매핑

  • 메시지 바디의 JSON 을 처리하는 방법이 필요
  • @RequestBody
  • Content-Type 을 필수로 지정.

 

ResponseEntity 응답과 Pageable, Sort 클래스

  • ResponseEntity
  • Pageable 클래스

 

Pageable 자동 설정

  • SpringDataWebAutoConfiguration : 웹 서포트 기능 자동 설정
  • properties , yml 의 설정을 통해 쉽게 적용 가능

 

REST-API 검증과 예외 처리

  1. 클라이언트의 요청을 검증하기 위한 방법
  2. 사용자가 잘못된 형식의 값을 입력하거나 아예 입력하지 않아 null 값이 넘어오는 현상.

 

  1. JSR-303 스펙에서 제공하는 에너테이션을 사용하여 쉽고 빠르게 검증 ex ) @Notnull,
@Getter
@ToString
public class HotelRoomUpdateRequest {

    @NotNull(message = "roomType can't be null")
    private HotelRoomType roomType;

    @NotNull(message = "originalPrice can't be null")
    @Min(value = 0, message = "originalPrice must be larger than 0")
    private BigDecimal originalPrice;
}

 

 

@Valid 애너테이션과 예제

  • 검증할 자바 빈 객체를 마킹하는 용도로 사용
  • 위의 @NotNull , @Min 등 과 같이 선언하기만 하면 동작하지 않고 @Valid 를 정의해야 동작한다.
  • 컨틀롤러 클래스의 핸들러 메서드 인자에 설정 + BindingResult 객체 추가
  • BindingResult 는 @Valid 를 붙인 객체 뒤에 선언해야 하는데, 복수 개일 경우 똑같이 복수개 선언해야 한다 .
@PutMapping(path = "/hotels/{hotelId}/rooms/{roomNumber}")
public ResponseEntity<HotelRoomIdResponse> updateHotelRoomByRoomNumber(
        @PathVariable Long hotelId,
        @PathVariable String roomNumber,
        @Valid @RequestBody HotelRoomUpdateRequest hotelRoomUpdateRequest,
        BindingResult bindingResult) {

 

  1. WebDataBinder 를 이용해서 Valid 기능 확장하기

 

  1. JSR-303 이 제공해주는 검증 기능이 명백하지 않고, 아래 (추가) 하는 과정이 번거로우면 Controller 에서 검증하는 것이 아닌 service 에서 검증하는 것 방법이 있다.

 

 

public class HotelRoomReserveValidator implements Validator {

    @Override
    public boolean supports(Class<?> clazz) {
        return HotelRoomReserveRequest.class.equals(clazz);
    }

    @Override
    public void validate(Object target, Errors errors) {
        HotelRoomReserveRequest request = HotelRoomReserveRequest.class.cast(target);

        if (Objects.isNull(request.getCheckInDate())) {
            errors.rejectValue("checkInDate", "NotNull", "checkInDate is null");
            return;
        }
        if (Objects.isNull(request.getCheckOutDate())) {
            errors.rejectValue("checkOutDate", "NotNull", "checkOutDate is null");
            return;
        }
        if (request.getCheckInDate().compareTo(request.getCheckOutDate()) >= 0) {
            errors.rejectValue("checkOutDate", "Constraint Error", "checkOutDate is earlier than checkInDate ");
            return;
        }
    }
}




import javax.validation.Valid;

@RestController
public class HotelRoomReserveController {

    private final ReserveService reserveService;

    public HotelRoomReserveController(ReserveService reserveService) {
        this.reserveService = reserveService;
    }

    @InitBinder
    void initBinder(WebDataBinder binder) {
        binder.addValidators(new HotelRoomReserveValidator());
    }

    @PostMapping(path = "/hotels/{hotelId}/rooms/{roomNumber}/reserve")
    public ResponseEntity<HotelRoomIdResponse> reserveHotelRoomByRoomNumber(
            @PathVariable Long hotelId,
            @PathVariable String roomNumber,
            @Valid @RequestBody HotelRoomReserveRequest reserveRequest,
            BindingResult bindingResult) {

        if (bindingResult.hasErrors()) {
            FieldError fieldError = bindingResult.getFieldError();
            String errorMessage = new StringBuilder(bindingResult.getFieldError().getCode())
                    .append(" [").append(fieldError.getField()).append("] ")
                    .append(fieldError.getDefaultMessage())
                    .toString();

            System.out.println("error  : " + errorMessage);
            return ResponseEntity.badRequest().build();
        }

        System.out.println(reserveRequest.toString());

        Long reservationId = reserveService.reserveHotelRoom(
                hotelId, roomNumber,
                reserveRequest.getCheckInDate(),
                reserveRequest.getCheckOutDate());

        HotelRoomIdResponse body = HotelRoomIdResponse.from(reservationId);
        return ResponseEntity.ok(body);
    }

}

 

 

 

@ControllerAdvice 와 @ExceptionHandler 예외 처리 + @RestControllerAdvice

  • 예외 처리 메커니즘은 견고한 시스템을 만드는데 반드시 필요하다.
  • Unchecked Exception 과 checked Exception
  • try - catch 와 throw
  • @ExceptionHandler 는 controller 에 선언하는데, 선언된 예외만 처리할 수 있다. + @Controller 나 @ControllerAdvice 가 선언된 클래스에서만 동작.
  • @ControllerAdvice 를 설정하면 해당 클래스는 전역 설정 스프링 빈이 된다.
  • 스프링 애플리케이션 전체에서 예외 처리 메서드를 선언 할 수 있다.
  • @RestCOntrollerAdvice = @ControllerAdvice + @ResponseEntity
  • → HttpMessageConverter 로 인해 마셜링.

 

미디어 콘텐츠 내려받기

파일을 내려 받는 방법

  1. HttpMessageConverter 를 사용하여 메시지를 변환
  2. HttpServletResponse 를 사용해서 직접 OutputStream 을 다루는 방법
728x90

댓글