[REST API] 2. 이벤트 생성 API 개발

두두·2023년 11월 13일
0

RESTful API

목록 보기
2/2


본 포스팅은 백기선님의 스프링 기반 REST API 개발 강좌를 수강하며 정리한 글입니다.

1. 이벤트 API 테스트 클래스 생성

@WebMvcTest

  • MockMvc 빈을 자동 설정
    → 이제 Test에서 MockMvc 주입 받아 사용 가능
  • 웹 관련 빈들 등록

MockMvc

  • 스프링 MVC 테스트 핵심 클래스
  • 웹 서버를 띄우지 않고도 스프링 MVC (DispatcherServlet)가 요청을 처리하는
    과정을 확인 가능
    → 컨트롤러 테스트용으로 자주 쓰임.
  • 간단하게 요청을 만들수 있고 응답을 검증할 수 있는 클래스
  • 웹서버를 띄우지 않기 때문에 조금 더 빠름
    하지만 DispatcherServlet을 띄워야 하기 때문에 단위테스트 보다는 느림!

2. 201 응답 받기

linkTo

createEvent의 요청에 응답을 보댈 때 created 메소드 사용 -> 결과값이 uri가 나와야 함
즉 생성된 이벤트를 조회할 수 있는 uri를 담아서 보내줘야함.

이 uri를 편리하게 만들어주는 게 linkTo 메소드

   URI createdUri = linkTo(EventController.class).slash("{id}").toUri(); 

methodOn

MockMvc.andDo

실제 콘솔에서 어떤 요청과 어떤 응답을 받았는지 확인 가능

RequestMapping

하나의 메소드가 아닌 eventController 클래스에 사용함으로써
value값을 설정해 eventController 클래스의 base url을 설정해줌!

  • produces 값 설정해 리턴 타입 설정
@RequestMapping(value = "/api/events", produces = MediaTypes.HAL_JSON_VALUE)

ObjectMapper

객체를 JSON으로 변환하는 데 사용

    @Autowired
    ObjectMapper objectMapper;
    
   // ... 생략...
   
     mockMvc.perform(post("/api/events/") 
     //... 
     .content(objectMapper.writeValueAsString(event))) //json으로 줘야함 - ObjectMapper이용
     //...
     

오류

JUnit 테스트 코드 작성 중 오류 발생

java.lang.Exception: No runnable methods

올바른 클래스를 import 하지 않았기 때문!

//Before - error
import org.junit.jupiter.api.Test;

//After
import org.junit.Test;

단축어

cmd + shfit + T
테스트 생성

ctrl + R
이전 클래스 실행

ctrl + shift + R
실행


3. 이벤트 Repository

JPA

JpaRepository 상속 받아 EventRepository 생성

@Enumerated(EnumType.STRING)

Enum은 기본 값이 ordinal
→ Enum에 정의된 값들이 순서대로 "정수값"으로 저장됨
→ 추후 정의된 값들의 "순서가 바뀌면" 데이터가 꼬임
➡️ String으로 바꿔주는 게 조아~~

@MockBean

EventControllerTests에서 @WebMvcTest으로 빈을 주입해줬는데
이 어노테이션은 슬라이스 테스트로 웹용 빈만 등록해줌
즉, 레포지토리는 빈으로 등록 안해주기 때문에
Mockkito를 사용해 mock 객체를 만들고 빈으로 등록!

‼️주의
기존 빈을 테스트용 빈이 대체함


4. 입력값 제한하기

id 또는 입력받은 데이터로 계산해야 하는 값들은 입력을 받지 않아야 한다!
➡️ 여러 해결법이 있지만,
여기서는 DTO를 생성해서 해결

EventDto 생성

id랑 추후 계산해야 하는 값 빼고 dto 생성해주면 됨

ModelMapper

이거 참 좋더라
ModelMapper의 map함수 한번이면 source model의 데이터를 destination model로 쫙 옮겨줌

단, 리플렉션 발생 (직접 코드로 옮기는거보다는 느림)

디펜던시 추가하기!

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

통합 테스트 전환

슬라이싱 테스트였던 @WebMvcTest 제거

@SpringBootTest
@AutoConfigureMockMvc
추가

이후에 id, eventStatus, offline, free 값들 주어도 무시하고 있는지 확인!


5. 입력값 이외의 에러 발생

  • 4번까지는 입력값 외의 값이 들어오면 그냥 무시함
  • 이제 에러 발생시키자!

ObjectMapper 커스터마이징

//EventControllerTests.java

//..생략

 @Test
 public void createEvent_Bad_Request() throws Exception {

//...
        mockMvc.perform(post("/api/events/")
                        .contentType(MediaType.APPLICATION_JSON_UTF8)
                        .accept(MediaTypes.HAL_JSON)
                        .content(objectMapper.writeValueAsString(event)))
                .andDo(print()) 
                .andExpect(status().isBadRequest()) // 여기 수정!! 
                //입력값 외의 다른 값이 들어오면 에러 발생시키쟈!!!
        ;
  }

deserialization

Spring - 오브젝트 맵퍼 확장 기능
json → object : deserialization
object → json : serialization

application.properties

spring.jackson.deserialization.fail-on-unknown-properties=true

➡️ 받을 수 없는 unknown -> id, free, offline 등등이 들어오면 bad request 띄워라!


6. Bad Request 처리

  • 있어야 하는 값들! (Dto에 선언된 값)이 없는 경우!

@Valid과 BindingResult

  • BindingResult는 항상 @Valid 바로 다음 인자로 사용해야 함. (스프링 MVC)
  • @Valid 검증 결과를 Errors에 넣음

Dto에 @NotNull, @NotEmpty, @Min, @Max
붙여주면 이를 사용해서 입력값을 바운딩할 때 에러를 확인함!

EventValidator 클래스

그 외 논리적 에러

  • basePrice보다 maxPrice가 더 작거나,,
  • 표 구매 마감시간보다 행사 시작 시간이 더 빠르거나.. 등등

요런거는 EventValidator 클래스 생성 후
검증하는 코드 작성!

TestDescription 인터페이스

  • 테스트 설명용 애노테이션 만들기
  • @Target, @Retention (에노테이션을 붙인 코드를 얼마나 오래 가져갈거냐)

7. Bad Request 응답

JsonSerializer

커스텀 JSON Serializer 만들기!
extends JsonSerializer<T>

왜 만들어야 하냐?!

  • event객체는 자바 빈 스펙을 준수하고 있기 때문에
    Object Mapper의 여러 serializer가 있어서 이 기본 serializer로 json으로 변환이 가능했음
  • Errors 는 자바 빈 스펙을 준수하는 객체가 아님
    따라서 그냥 빈 serializer로 변환 불가능함

@JsonComponent

다 만들었다면 우리가 사용하고 있는 Object Mapper에 방금 만든 ErrorsSerializer을 등록해야 함

이걸 @JsonComponent 이 어노테이션이 해줌.
이제 object mapper는 Errors를 serialize할 때 얘를 사용함


8. 비즈니스 로직 적용

비즈니스 로직 적용됐는지 응답메시지 확인

free 값

basePrice나 maxPrice가 0이면

  • free값은 true로 업데이트

아니면
free값은 false!

offline 값

localtion이 null이거나 비어있으면

  • offline값은 false

아니면
offline값은 true~!


9. 매개변수를 이용한 테스트

테스트 코드를 리팩토링 하자!

  • 테스트에서 중복 코드 제거
  • 매개변수만 바꿀 수 있으면 좋겠는데?
    ➡️ JUnitParams

디펜던시 추가

<dependency>
    <groupId>pl.pragmatists</groupId>
    <artifactId>JUnitParams</artifactId>
    <version>1.1.1</version>
    <scope>test</scope>
</dependency>
profile
멋쟁이가 될테야

0개의 댓글

관련 채용 정보