본문 바로가기
Java, Kotlin, Spring/Spring REST API

Spring REST API - 입력값 처리

by Wordbe 2021. 1. 23.
728x90

 

 

Spring REST API - 입력값 처리

 

테스트하면서 입력값을 제한해야 하는 필드가 있다.

예를 들어 자동 생성되도록 설정된 id 나 계산해야하는 값들이다.

이를 DTO 사용해서 해결해보자.

jackson 라이브러리를 통해 json 어노테이션을 붙이는 방법도 있다. 에노테이션이 다닥다닥 많이 붙어서 가독성이 안좋아 질가봐 DTO를 따로 만드는 예제를 만든다.

DTO (Data Transfer Object) 에 Entity 객체로부터 입력이 필요한 필드만 가지고와서 모델을 새로 정의한다.

@Builder @AllArgsConstructor @NoArgsConstructor
@Data
public class EventDto {

    private String name;
    private String description;

    private LocalDateTime beginEnrollmentDateTime;
    private LocalDateTime closeEnrollmentDateTime;
    private LocalDateTime beginEventDateTime;
    private LocalDateTime endEventDateTime;

    private String location; // null 이면 온라인 모임
    private int basePrice;
    private int maxPrice;
    private int limitOfEnrollment;
}

Event 객체로부터 입력이 필요한 필드만 가져와서 EventDTO 객체를 만들었다.

 

그리고 원래 로직에 적용하려면 Event 에 EventDto 를 일일이 매핑해주어야 한다.

Event event = Event.builder()
        .name(eventDto.getName())
        .description(eventDto.getDescription())
        .build();

하지만 이를 자동화해주는 라이브러리 ModelMapper가 있다.

 

pom.xml 에 의존성을 추가한다. 버전은 최신버전을 사용하면 된다.

<dependency>
  <groupId>org.modelmapper</groupId>
  <artifactId>modelmapper</artifactId>
  <version>2.3.9</version>
</dependency>

 

ModelMapper 는 공용으로 사용가능한 객체이므로 빈으로 등록해서 사용할 수 있다.

설정파일이나 메인애플리케이션 아래 코드에 아래 빈을 추가해주자.

        @Bean
    public ModelMapper modelMapper() {
        return new ModelMapper();
    }
@RestController
@RequestMapping(value = "/api/events", produces = MediaTypes.HAL_JSON_VALUE)
public class EventController {

    private final EventRepository eventRepository;

    private final ModelMapper modelMapper;

      // eventRepository, modelMapper 생성자
    public EventController(EventRepository eventRepository, ModelMapper modelMapper) {
        this.eventRepository = eventRepository;
        this.modelMapper = modelMapper;
    }

    @PostMapping
    public ResponseEntity createEvent(@RequestBody EventDto eventDto) {
          // 아래와 같이 한 줄 써주면 모델 매핑이 간편하게 된다.
        Event event = modelMapper.map(eventDto, Event.class);

        Event newEvent = this.eventRepository.save(event);
        URI createdUri = linkTo(EventController.class).slash(newEvent.getId()).toUri();
        return ResponseEntity.created(createdUri).body(event);
    }
}



테스트

 

Web 테스트를 할 때 슬라이스 테스트를 하면, Web에 기능이 추가될 때마다 테스트에도 추가할 Mock 이 많이 생긴다. 따라서 통합테스트이긴 하지만 @SpringBootTest 를 사용하면 편리하다. 이 때 MockMvc 를 사용하려면 @AutoConfigureMockMvc 를 붙이는 방법이 좋다.

@SpringBootTest
@AutoConfigureMockMvc
public class EventControllerTest {

    @Autowired
    MockMvc mockMvc;

    @Autowired
    ObjectMapper objectMapper;

    @Test
    public void createEvent() throws Exception {
        Event event = Event.builder()
                          // Event 에는 있지만 EventDto 에 없는 필드
                      .id(100)
                      .free(true)
                .offline(false)
                .eventStatus(EventStatus.PUBLISHED)

                      // EventDto 에만 있는 필드
                .name("Spring")
                ...
                .location("D2 스타트업 팩토리")
                .build();

        mockMvc.perform(post("/api/events")
                    .contentType(MediaType.APPLICATION_JSON)
                    .accept(MediaTypes.HAL_JSON)
                    .content(objectMapper.writeValueAsString(event))
                        )
                .andDo(print())
                ...

                      // 입력값 테스트
                .andExpect(jsonPath("id").value(Matchers.not(100)))
                .andExpect(jsonPath("free").value(Matchers.not(true)))
                .andExpect(jsonPath("eventStatus").value(String.valueOf(EventStatus.DRAFT)));

    }
}

 

 

 


입력값 이외에 필드를 사용했을 때 에러 발생시키기

 

status().isBadRequeust() 메소드를 사용해보자.

@SpringBootTest
@AutoConfigureMockMvc
public class EventControllerTest {
        ...

    @Test
    public void createEvent_badRequest() throws Exception {
        Event event = Event.builder()
                      // Event 에는 있지만 EventDto 에 없는 필드
                      .id(100)
                      .free(true)
                .offline(false)
                .eventStatus(EventStatus.PUBLISHED)

                      // EventDto 에만 있는 필드                      
                .name("Spring")
                ...
                .location("D2 스타트업 팩토리")

                .build();

        mockMvc.perform(post("/api/events")
                .contentType(MediaType.APPLICATION_JSON)
                .accept(MediaTypes.HAL_JSON)
                .content(objectMapper.writeValueAsString(event))
        )
                .andDo(print())
                .andExpect(status().isBadRequest()) // 400 응답으로 예상
        ;
    }
}

위의 예제들에서 이미 이 테스트는 정상적으로 실행이 가동되도록 적용시켜 놓았기 때문에,

추가설정을 통해, Dto 에 없는 필드들의 값을 설정했을 때 에러가 나도록 해보자. (그래야 isBadRequest 테스트는 통과가 된다.)

 

간단한 프로퍼티 설정을 통해 해결할 수 있다.

application.properites

spring.jackson.deserialization.fail-on-unknown-properties=true
  • Serialization : 객체 → JSON
  • Deserialization : JSON → 객체

다음의 테스트같은 경우는 받을 수 없는 unknown 프로퍼티를 가져오므로, error 를 내보낸다.

728x90

'Java, Kotlin, Spring > Spring REST API' 카테고리의 다른 글

Spring REST API - HATEOAS  (0) 2021.01.24
Spring REST API - 테스트코드 파라미터사용  (0) 2021.01.23
Spring REST API - BadRequest  (0) 2021.01.23
Spring REST API TEST  (0) 2021.01.22
Spring REST API  (0) 2021.01.22

댓글