👉 Mission. 기존 Article 데이터를 CRUD 하기 위한 Rest API를 구현하시오
비동기 통신이란, request에 대한 response를 기다리지 않고 계속 request를 보내는 방식이다. 따라서 응답 순서는 요청 순서와 다를 수 있다. 해당 방식은 결과보다는 더 빠른 성능이 요구되는 서비스에 적절하고, 결과가 중요한 서비스에서는 추천하지 않는 방식이다.
클라이언트에서 서버로 통신하는 메시지를 요청(request) 메시지라고 하며, 서버에서 클라이언트로 통신하는 메시지를 응답(response) 메시지라고 한다.
웹에서 화면전환(새로고침) 없이 이루어지는 동작들은 대부분 비동기 통신으로 이루어진다.
비동기 통신을 하기위해서는 클라이언트에서 서버로 요청 메세지를 보낼 때, 본문에 데이터를 담아서 보내야 하고, 서버에서 클라이언트로 응답을 보낼때에도 본문에 데이터를 담아서 보내야 한다.
이 본문이 바로 body 이다.
즉, 요청본문 requestBody, 응답본문 responseBody 을 담아서 보내야 한다.
RequestBody와 ResponseBody는 모두 비동기 처리 방식이다. 두 어노테이션은 모두 웹 페이지와 Java가 통신할 때 객체를 변환하는 역할을 한다.
@RequestBody
: HTTP 요청의 body 내용을 Java 객체로 변환할 때 사용한다.
@ResponseBody
: Java 객체의 내용을 HTTP 요청의 body로 변환할 때 사용한다.
즉, 클라이언트에서 서버로 필요한 데이터를 요청하기 위해 JSON 데이터를 요청 본문에 담아 서버로 보내면, 서버에서는 @RequestBody
어노테이션을 사용하여 HTTP 요청 본문에 담긴 값들을 자바객체로 변환시켜, 객체에 저장한다.
서버에서 클라이언트로 응답 데이터를 전송하기 위해 @ResponseBody
어노테이션을 사용하여 자바 객체를 HTTP 응답 본문의 객체로 변환하여 클라이언트로 전송한다.
@RestController
를 사용하는 경우에는 자동으로 return 값에 ResponseBody를 붙여 자바 객체가 매핑되어 전달되기 때문에@ResponseBody
의 생략이 가능하다!!!
여기서 구현할 Rest API 의 주소 설계
GET
/api/articles
: article 목록 조회GET
/api/articles/{id}
: article 단일 조회POST
/api/articles
: 생성PATCH
/api/articles/{id}
: 수정DELETE
/api/articles/{id}
: 삭제주소 설계가 끝났다면, 요청을 받아 JSON으로 반환해줄 Controller가 필요 => "REST 컨트롤러"
@RestController
: Rest API용 컨트롤러, JSON 반환
@Controller
는 View 템플릿 페이지를 반환하지만, @RestController
는 (일반적으로) JSON(데이터)를 반환함ResponseEntity
: Spring MVC에서 HTTP 요청에 대한 응답을 제어하는 데 사용하는 클래스.
ResponseEntity<T> responseEntity = new ResponseEntity<>(body, headers, status);
T
: Response Body의 타입body
: Response Body 객체headers
: 설정할 HTTP Headerstatus
: HTTP Response Status Codeapi 패키지 생성 > ArticleApiController.java 파일 생성
1. GET /api/articles : article 목록 조회
파일명 : api/ArticleApiController.java
//GET - 전체 article 조회
@GetMapping("/api/articles")
public List<Article> index(){
return articleRepository.findAll();
}
2. GET /api/articles/{id} : article 단일 조회
//GET - 단일 article 조회
@GetMapping("/api/articles/{id}")
public Article index(@PathVariable Long id){
return articleRepository.findById(id).orElse(null);
}
3. POST /api/articles : 생성
//POST - 생성
@PostMapping("/api/articles")
public ResponseEntity<Article> create(@RequestBody ArticleForm dto){
Article saved = articleRepository.save(dto.toEntity());
return ResponseEntity.status(HttpStatus.CREATED).body(saved);
}
@RequestBody
: 이 어노테이션이 붙은 파라미터에는 http 요청의 본문(body)이 그대로 전달된다. 따라서 이 요청 본문(request body)에 담긴 값을 자바 객체로 변환한다.4. PATCH /api/articles/{id} : 수정
//PATCH
@PatchMapping("/api/articles/{id}")
public ResponseEntity<Article> update(@RequestBody ArticleForm dto,
@PathVariable Long id){
//1. 수정용 엔티티 생성
Article updated_article = dto.toEntity();
//2. 대상 엔티티 조회
Article target = articleRepository.findById(id).orElse(null);
//3. 잘못된 요청 처리 (대상이 없거나, (요청 url과 body의)id가 다른 경우)
if(target == null || id != updated_article.getId()){
//400, 잘못된 요청 응답 보내기
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null); //BAD_REQUEST = 400
}
//4. 업데이트 및 정상 응답(200)
Article updated = articleRepository.save(updated_article);
return ResponseEntity.status(HttpStatus.OK).body(updated); //OK = 200
}
자, 그런데 이렇게 하면 한 가지 문제가 있다.
수정은 정상적으로 작동하지만, 만약 요청하는 body에 어떤 데이터를 하나 빼먹는다면?
사진과 같이 title을 빼먹고 보내면 수정된 게시글은 title이 null 값을 가지게 된다.
따라서 이 문제를 해결하기 위해, entity 파일에서 patch 라는 함수를 만들어 '수정된 데이터만 기존 데이터에 붙여(patch)주는' 방식으로 동작하게 해보자.
파일명 : entity/Article.java
public void patch(Article updatedArticle) {
if(updatedArticle.title != null)
this.title = updatedArticle.title;
if(updatedArticle.content != null)
this.content = updatedArticle.content;
}
body 에서 id 를 빼먹으면 어쩌죠? -> body에 id가 없으면 '잘못된 요청 처리'하는 코드에서 id가 다른 경우로 400처리 돼서 어차피 수정 안돼요~
patch 메서드를 추가했으니 돌아와서 마무리 코드를 작성해보자.
//4. 업데이트 및 정상 응답(200)
target.patch(updated_article);
Article updated = articleRepository.save(target);
return ResponseEntity.status(HttpStatus.OK).body(updated); //OK = 200
patch 메서드에서 this를 통해 해당 데이터에 입력되게끔 코드를 짰으니 save(updated_article)
-> save(target)
으로 변경해주면 된다.
title 을 빼먹어도 null 이 아닌 기존 데이터 "가가가가"가 유지되는 것을 확인할 수 있다. 성공!
5. DELETE /api/articles/{id} : 삭제
//DELETE - 삭제
@DeleteMapping("/api/articles/{id}")
public ResponseEntity<Article> delete(@PathVariable Long id){
//1. 대상 찾기
Article target = articleRepository.findById(id).orElse(null);
//2. 잘못된 요청 처리
if(target == null){
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null);
}
//3. 대상 삭제
articleRepository.delete(target);
return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
}
ResponseEntity.status(HttpStatus.OK).build()
: 몸체 내용이 없다면 다음과 같이 body를 지정하지 않고 build()로 바로 생성해도 된다.1. @RestController
2. ResponseEntity
강의 출처 : https://www.youtube.com/watch?v=_vDACE13Ubc&list=PLyebPLlVYXCiYdYaWRKgCqvnCFrLEANXt&index=1 [스프링 부트 입문 - 홍팍]
참고 사이트
https://cheershennah.tistory.com/179 - 클라이언트-서버 비동기 통신 & @RequestBody 와 @ReponseBody 의 차이
https://burningfalls.github.io/java/what-is-response-entity/ - ResponseEntity 란?