[SpringBoot(2)] JSON형태 입력받기, TDD

배지원·2022년 11월 21일
0

실습

목록 보기
17/24
post-custom-banner

이전에는 기존에 저장되어있던 DB에서 데이터를 JSON형식으로 출력하는 방식을 했었다. 이에 이번에는 JSON형식으로 받아와 JSON형식으로 출력하는 방식을 구현해보도록 하겠다.

1. Json 입력 받기

(1) Entity

@Entity
@Table(name = "article2")
@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Article {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String title;
    private String content;

    public Article(String title,String content) {
        this.title = title;
        this.content = content;
    }

// ArticleEntity를 ArticleAddResponse DTO로 만들어주는 부분
    public static ArticleAddResponse of2(Article article) {
        return new ArticleAddResponse(article.getId(),
                article.getTitle(),article.getContent());
    }
}
  • DB와 데이터를 주고받기 위해 Entity 클래스를 만들어 준다.
  • DB의 테이블 데이터와 1대1 매칭이 되어야 한다.
  • JSON형식으로 반환해주기 위해 Json 형식의 DTO로 데이터를 넘겨준다.

(2) DTO

  • 이전에는 DTO를 하나만 사용하여 json형식으로 출력하는 방식을 사용했다면 이번에는 입출력 모두 json형식으로 해야하기 때문에 DTO를 요청,반환을 따로 2개를 만들어 구현했다.

ArticleAddRequest

@AllArgsConstructor
@Getter
@EqualsAndHashCode
public class ArticleAddRequest {
    private String title;
    private String content;

// 사용자에게 값을 입력받아 Entity로 변환함
// 이때 생성자 대신 Builder를 통해 좀 더 확실히 맵핑하여 데이터 전송함
    public Article toEntity(){
        Article article = Article.builder()
                .title(this.title)
                .content(this.content)
                .build();

        return article;
    }
}
  • Json형식으로 사용자에게 요청을 받기위한 클래스
  • 값을 입력받으면 변수에 저장후 toEntity를 통해 Entity 클래스에 데이터 전송함
  • 이때 생성자 대신 Builder를 통해 좀 더 정확히 이름을 매칭시켜줌

ArticleAddResponse

@Getter
@NoArgsConstructor
@AllArgsConstructor
public class ArticleAddResponse {
    private Long id;
    private String title;
    private String content;
}
  • Json 형식으로 반환해주기 위한 클래스
  • Entity에서 데이터를 넘겨받아 반환해줌

(3) Controller

@RestController
@RequestMapping("/api/v1/articles")
public class ArticleRestContorller {

    private final ArticleRestService articleRestService;

    public ArticleRestContorller(ArticleRestService articleRestService) {
        this.articleRestService = articleRestService;
    }

    @GetMapping("/{id}")
    public ResponseEntity<ArticleAddResponse> getfind(@PathVariable Long id){
        return ResponseEntity.ok().body(articleRestService.getfind(id));
    }

    @PostMapping
    public ResponseEntity<ArticleAddResponse> addArticle(@RequestBody ArticleAddRequest dto) {      // @RequestBody를 통해 json 형식으로 받음
        ArticleAddResponse response = articleRestService.add(dto);
        return ResponseEntity.ok().body(response);
    }
}
  • RestController를 통해 Json형식으로 데이터를 주고 받는다고 정의

  • ResponseEntity를 통해 Json형식으로 구현가능

    	isOk() - 상태 코드가 200인지 확인
    
    	isNotFount() - 상태 코드가 404인지 확인
    
    	isMethodNotAllowed() - 상태 코드가 405인지 확인
    
    	isInternalServerError() - 상태 코드가 500인지 확인
    
    	is(int status) - 몇 번 응답 상태 코드가 설정되었는지 확인 ex) is(200), is(404)
  • GetMapping에서는 DB에 저장된 데이터를 Json으로 반환해주는 기능을 함

  • PostMapping에서는 사용자가 Json형식으로 데이터를 보내면 @RequsetBody를 통해 ArticleAddRequest DTO에 저장하고 Entity로 변환 후 DB에 저장함


(4) Service

@Service
public class ArticleRestService {

    private final ArticleRepository articleRepository;

    public ArticleRestService(ArticleRepository articleRepository) {
        this.articleRepository = articleRepository;
    }

    public ArticleAddResponse getfind(Long id){
        Optional<Article> optarticle = articleRepository.findById(id);
        Article article = optarticle.get();
        ArticleAddResponse articleDto = Article.of2(article);

        return articleDto;
    }

    public ArticleAddResponse add(ArticleAddRequest dto){
        Article article = dto.toEntity();
        Article savedArticle = articleRepository.save(article);
        return new ArticleAddResponse(savedArticle.getId(), savedArticle.getTitle(), savedArticle.getContent());
    }
}
  • JPA CRUD문을 통해 사용자에게 입력받은 데이터를 가공하는 곳
  • getfind 메서드는 id를 가지고 DB에서 findById를 통해 해당하는 데이터를 호출하여 반환 전용인 ArticleAddResponse DTO에 저장하여 반환함
  • add 메서드는 요청전용인 ArticleAddRequest DTO를 통해 입력받은 값을 Entity에 저장 후 save를 통해 DB에 저장한다.

(5) Repository

@Repository
public interface ArticleRepository extends JpaRepository<Article, Long> {
}

(6) 결과

  • Post를 통해 Body에 Json형식으로 데이터를 입력 후 보내면 Json형식으로 저장된 데이터가 출력되고 DB에 저장이 잘 된 것을 확인할 수 있다.

2. @WebMvcTest TDD

  • 위에 처럼 동작이 잘 되면 좋겠지만 만약 오류가 발생하면 원인을 찾아야 하고 대용량 데이터를 처리할 경우 프로그램을 실행 후 검사하면 시간이 오래 걸리기 때문에 Controller를 검사할 코드를 작성한 것이 좋다.
@WebMvcTest(ArticleRestContorller.class)            // Test할 Controller 클래스 지정
class ArticleRestContorllerTest {

    @Autowired
    MockMvc mockMvc;            // MockMvc를 의존받음

    @Autowired
    ObjectMapper objectMapper;         // json 형식으로 변환하기 위해 사용하는 라이브러리

    @MockBean                // 가짜 객체
    ArticleRestService articleRestService;

    @Test
    @DisplayName("글 등록이 잘 되는지 확인")
    // json 형식으로 값 입력받아 저장되는지 확인
    void add() throws Exception {
        ArticleAddRequest dto = new ArticleAddRequest("제목입니다.","내용입니다.");
        ArticleAddResponse resp = new ArticleAddResponse(1l,dto.getTitle(),dto.getContent());

        // 가짜 객체로 현재 여기서 any를 통해 articleRestService는 어떤값을 받아도 무조건 resp를 반환한다.
        // (service 테스트내용) -> given을 통해 service에 들어가는 객체와 출력되는 객체를 지정하고
        //  verify를 통해 service 메서드의 실행결과 확인함
        given(articleRestService.add(any()))
                .willReturn(resp);

        // controller 테스트 내용
        mockMvc.perform(post("/api/v1/articles")
                        .contentType(MediaType.APPLICATION_JSON)        // Json 타입으로 사용
                        .content(objectMapper.writeValueAsBytes(dto))   // 삽입한 데이터 dto를 json 형식으로 변환
                )
                
                // 입력된 데이터와 예측되는 값을 비교함
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.id").exists())   // 존재여부 확인
                .andExpect(jsonPath("$.title").exists())
                .andExpect(jsonPath("$.title").value("제목입니다."))
                .andExpect(jsonPath("$.content").exists())
                .andDo(print());

      verify(articleRestService).add(dto);            // service의 add 메서드가 실행됬는지 확인함
                                                        // service의 테스트 결과 확인
    }
}
  • Controller를 테스트 하기 위해서는 MockMvc를 통해 가짜 객체를 만들어 검사를 할 수 있다.
  • 현재 DTO를 요청,반환으로 2개를 나누었으므로 요청하는 DTO(dto)에 값을 넣어주고 반환하는 DTO(resp)에도 미리 예상 데이터를 넣어준다.
  • Controller와 의존중인 Service 클래스는 Mock을 통해 가짜 객체를 만들어 내어 의존하게 한다. 이때 현재는 test이므로 DB에는 데이터를 넣을 필요가 없으니 add메서드에는 any()를 사용하여 어떤 값이 들어와도 resp의 값이 출력되도록 한다.
  • 사용자가 입력한 값(dto)은 json형식으로 받기 때문에 타입을 APPLICATION_JSON으로 지정하고 objectMapper를 통해 dto를 json형식으로 바꾼다.

❓ ObjectMapper란?

JSON 컨텐츠를 Java 객체로 deserialization 하거나 Java 객체를 JSON으로 serialization 할 때 사용하는 Jackson 라이브러리의 클래스이다.
ObjectMapper는 생성 비용이 비싸기 때문에 bean/static으로 처리하는 것이 좋다.
Jackson 라이브러리에 관한 내용은 더 공부하고 나중에 따로 작성해보도록 하겠다.

  • 입력받은 데이터를 비교하기 위해서는 jsonPath를 통해 json형식으로 검사를 해야한다.
    .exists( )를 통해 존재 여부를 확인하고, value( )를 통해 데이터의 값 일치 여부를 확인한다.
profile
Web Developer
post-custom-banner

0개의 댓글