RestApi 만들기 - 입력값 제한하기 + 입력값 이외의 에러 발생 (3)
- SPRING START!/restAPI😢
- 2021. 2. 9. 22:50
API를 만들다보면,
입력값을 제한하고 싶은 경우가 발생합니다.
예를들면,
public class Delivery {
private int id;
private String item;
private String user;
private LocalDateTime deliveryTime;
private LocalDateTime deliveryEndTime;
private DeliveryStatus status;
private Integer itemPrice;
private Integer deliveryCost;
}
이런 객체가 있다고 가정해봅시다.
그런데, id같은 경우 jpa에서 번호를 정해주고,
deliveryStatus는 현재 배달의 진행을 보고 결정 되고,
deliveryCost는 itemPrice의 값을 보고 결정한다고 가정해본다면,
두 값은 입력을 받아서는 안되는 값들입니다.
왜냐하면 외부에서 그 값들을 결정하기 때문입니다.
테스트 코드를 작성해봅시다.
@Test
void create_Delivery() throws Exception {
Delivery delivery = Delivery.builder()
.id(100)
.item("book")
.user("klom")
.build();
Mockito.when(deliveryRepository.save(delivery)).thenReturn(delivery);
mockMvc.perform(post("/api/delivery/")
.accept(MediaTypes.HAL_JSON_VALUE)
.contentType(MediaType.APPLICATION_JSON_VALUE)
.content(objectMapper.writeValueAsString(delivery))
)
.andDo(print())
.andExpect(status().isCreated())
.andExpect(jsonPath("id").value(Matchers.not(100)))
.andExpect(jsonPath("status").value(DeliveryStatus.READY))
.andExpect(jsonPath("deliveryCost").value(5000))
;
}
id값이 100입니다.
저희는 id값은 자동으로 증가되기 원하고 있습니다. 따라서 위 코드는 201가 나와야 됩니다.
하지만
결과는 테스트 실패입니다.
그러면 어떻게 해야 될까요?
방법은 여러가지 입니다.
첫 번째 방법은
@JsonIgnore어노테이션을 붙이면 해결되는 경우입니다.
public class Delivery {
@Id
@GeneratedValue
private int id;
private String item;
private String user;
private LocalDateTime deliveryTime;
private LocalDateTime deliveryEndTime;
@JsonIgnore
private DeliveryStatus status;
private Integer itemPrice;
@JsonIgnore
private Integer deliveryCost;
}
id는 null이 발생해서 일단 붙이지 않았습니다;;
이것을 해결하는 방법은 이따 설명하겠습니다.
그러면
Body = {"id":100,"item":"book","user":"klom","deliveryTime":null,"deliveryEndTime":null,"itemPrice":null}
@JsonIgnore이 붙어있는 곳은 json으로 만들어지지 않는다는 것을 알 수 있습니다.
그렇다면 json도 유지하면서 에러를 성공하는 방법은 없을까요?
dto를 만드는 방법입니다.
dto는 api를 위해 만들어지는 객체라고 생각하면 될것 같습니다.
@NoArgsConstructor
@AllArgsConstructor
@Data
@Builder
public class DeliveryDto {
private String item;
private String user;
private LocalDateTime deliveryTime;
private LocalDateTime deliveryEndTime;
private Integer itemPrice;
}
이렇게 만들구,
이제 입력을 deliveyDto로 받게 하겠습니다.
근데 문제가 발생했습니다.
dto로 받기 때문에 컴파일에러가 발생했습니다.
이럴때는 어떻게 해야될까요?
여기에도 몇 가지 방법이 존재합니다.
첫 번째 방법 builder을 이용해서 값을 바인딩한다.
Delivery deliver = Delivery.builder()
.item(deliveryDto.getItem())
.user(deliveryDto.getUser())
.build();
이렇게 값을 DeliveryDto에서 delivery로 변경되었습니다.
물론 테스트는 실패입니다. 추후에 설명하겠습니다.
두 번째 방법은 Modelmapper을 이용하는 방법입니다.
Modelmapper는 서로 다른 객체를 바인딩해주는 라이브러리입니다.
이 라이브러리는 스프링ioc에서 관리해주지 않기 때문에 직접 빈으로 등록해줘야합니다.
아니면 그냥 쓰던가.
그러면
Delivery deliver = modelMapper.map(deliveryDto, Delivery.class);
단 한줄로 완성을 시킬 수 있습니다.
리플렉션 기술을 사용하기 때문에 좀 그렇다 하는 분들은 사용하지 않는것을 추천드립니다. 그냥 빌더 쓰세요.
자바 버전이 올라가면서 리플렉션 기술도 그리 느리지는 않는다고 합니다.
이제
이 오류를 해결해봅시다.
테스트 코드를 다시 봅시다.
가장 큰 문제는
Mockito.when(deliveryRepository.save(delivery)).thenReturn(delivery);
바로 이 녀석입니다.
이것이 문제인 이유는
컨트롤러에서는 deliveryDto를 받고 있습니다.
그것을 delivery로 바꾸는 작업을 가졌습니다.
근데 생각해야 될것이 이 바꾼 delivery와 테스트의 delivery는 같은 객체일까요?
위 코드는 테스트의 delivery를 말하고 있습니다.
즉, 같은 주소값이 들어가야 위 코드가 정상적으로 동작한다는 뜻입니다.
컨트롤러의 delivery : restapiset.Delivery@3b
테스트의 delivery : restapiset.Delivery@9f
그러면 어떻게 해야 될까요?
여기에도 여러 가지방법이 있습니다.
첫 번째 방법은 모든 delivey객체를 save한다면 delivery를 리턴시키면 됩니다.
import static org.mockito.ArgumentMatchers.any;
Mockito.when(deliveryRepository.save(any(Delivery.class))).thenReturn(delivery);
생각해될것이 모든 delivery객체를 허용할까입니다.
과연 이렇게 해도 좋을까?
두 번째 방법은 슬라이싱 테스트의 위치를 스프링부트 테스트로 수정하는 것입니다.
하지만 문제는 mockMvc를 사용이 불가능합니다.
@SpringBootTest
@AutoConfigureMockMvc
class DeliveryTest {
이걸 붙이는 순간 mockMvc를 사용할 수있게 됩니다.
이렇게 되면 더 이상 목빈은 필요없습니다.
왜냐하면 테스트에서 save할 이유가 없기 때문입니다.
마지막으로 delivery를 수정합시다.
public class Delivery {
@Id
@GeneratedValue
private int id;
private String item;
private String user;
private LocalDateTime deliveryTime;
private LocalDateTime deliveryEndTime;
private DeliveryStatus status = DeliveryStatus.READY;
private Integer itemPrice;
private Integer deliveryCost = 5000;
}
초기값을 이렇게 세팅해 두고,
spring.jackson.deserialization.fail-on-unknown-properties=true
테스트를 하게 되면 잘못된 값이 들어오면 400에러가 발생합니다.
'SPRING START! > restAPI😢' 카테고리의 다른 글
RestApi 만들기 - spring-rest-docs (5) (0) | 2021.02.19 |
---|---|
RestApi 만들기 - HATEOAS (5) (0) | 2021.02.16 |
RestApi 만들기 - badRequest (4) (0) | 2021.02.13 |
RestApi 만들기 - Repository추가하기 (2) (0) | 2021.02.06 |
RestApi 만들기 - 201코드 생성하기 (1) (0) | 2021.01.30 |