클라이언트가 보내는 HTTP 요청 메시지의 첫 줄에는 시작 라인인 요청 라인(request line)이 있고, 그 밑으로 헤더(header), 본문(body)가 있음.응답 메시지의 첫 줄에는 시작 라인인 상태 라인(status line)이 있고, 그 밑에 헤더(header), 본문(body)가 있음.100 ~ 500번대까지 5개의 그룹으로 나눠져있음.REST API의 응답 표준으로 사용하는 JSON은 키(key)와 값(value)의 쌍으로 된 속성으로 데이터를 표현함.JSON 데이터나 배열을 넣을 수도 있음.RESTHTTP URL로 서버의 자원(resource)을 명시하고, HTTP 메서드(POST, GET, PATCH/PUT, DELETE)로 해당 자원에 대해 CRUD(생성, 조회, 수정, 삭제)하는 것.API클라이언트가 서버의 자원을 요청할 수 있도록 서버에서 제공하는 인터페이스(Interface)임.REST API란 REST 기반으로 API를 구현한 것.REST API를 잘 구현하면 클라이언트가 기기에 구애받지 않고 서버의 자원을 이용할 수 있을 뿐만 아니라, 서버가 클라이언트의 요청에 체계적으로 대응할 수 있어서 서버 프로그램의 재사용성과 확장성이 향상됨.REST API를 구현하려면 REST API의 주소, 즉 URL을 설계해야됨.GET 메서드./api/articles : 전체 데이터 조회./api/articles/{id} : 단일 데이터 조회.POST 메서드./api/articles : 생성.PATCH 메서드./api/articles/{id} : 내용 수정.DELETE 메서드./api/articles/{id} : 삭제.URL 요청을 받아 그 결과를 JSON으로 변환해 줄 컨트롤러(controller)가 필요함.REST API로 요청, 응답을 주고 받을 때는 REST 컨트롤러(rest controller)를 사용해야함.상태 코드를 반환해주기 위해 ResponseEntity클래스도 활용함.
@RESTControllerJSON or 텍스트 같은 데이터를 반환함.@Controller뷰(view) 페이지를 반환함.좌 : RESTController, 우 : Controller

@RestController
public class ArticleApiController {
@Autowired
private ArticleRepository articleRepository;
@GetMapping("/api/articles")
public List<Article> index() {
return articleRepository.findAll();
}
}
@RestController@AutowiredDI)@GetMapping("/api/articles")"/api/articles" 주소로 오는 URL 요청을 받음.List<Article>List<Article>로 설정.return articleRepository.findAll();repository의 findAll메서드를 통해 DB에 저장된 모든 데이터를 가져옴.
@GetMapping("/api/articles/{id}")
public Article show(@PathVariable Long id) {
return articleRepository.findById(id).orElse(null);
}
@GetMapping("/api/articles/{id}")"/api/articles/{id}"주소로 오는 URL 요청을 받음.id마다 URL 요청이 변경되므로 {id}로 변수처리.public Article show(@PathVariable Long id)Article 타입.id로 검색하기 위해서 메서드의 매개변수로 값을 받아옴.{id}와 매개변수 id를 매핑 시켜줌.@PathVariable를 이용해서.return articleRepository.findById(id).orElse(null);findById를 이용해서 id로 엔티티를 가져오거나, 데이터가 없을 경우 null을 반환.
@PostMapping("/api/articles")
public Article create(ArticleForm dto) {
Article article = dto.toEntity();
return articleRepository.save(article);
}
@PostMapping("/api/articles")"/api/articles" 주소로 오는 URL 요청을 받음.public Article create(ArticleForm form)ArticleDTO를 매개변수로 해서 값을 받아옴.Article article = form.toEntity();DTO -> Entity 변환.return articleRepository.save(article);save()메서드를 이용해서 DB에 저장하고 반환함.
POST로 데이터를 보내면 title, content에 null이 나옴.@Controller에서 데이터를 생성할 때는 메서드에 매개변수로 dto를 받아오기만 하면 됐지만REST API에서 데이터를 생성할 때는 JSON 데이터를 받아 와야 해서 매개변수로 dto를 썼다고 해서 받아올 수 있는 게 아님.이럴 때 매개변수 앞에 @RequestBody 어노테이션을 추가 해주면 됨.본문(body)에 실어 보내는 데이터를 create()메서드의 매개변수로 받아올 수 있음.
@PatchMapping("/api/articles/{id}")
public ResponseEntity<Article> update(@PathVariable Long id, @RequestBody ArticleForm dto) {
Article article = dto.toEntity();
Article target = articleRepository.findById(id).orElse(null);
if (target == null || id != article.getId()) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null);
}
Article updated = articleRepository.save(article);
return ResponseEntity.status(HttpStatus.OK).body(updated);
}
@PatchMapping("/api/articles/{id}")@PatchMapping으로 "/api/articles/{id}"이 주소로 오는 URL 요청을 받음.@PathVariable Long id, @RequestBody ArticleForm dto{id}변수를 받아오기 위해서 @PathVariable를 사용했고, 요청 메시지의 Body 데이터를 받아오기 위해서 @RequestBody를 사용.if (target == null || id != article.getId())존재하지 않는 article을 수정 요청한 경우, 수정 요청 id와 요청 본문의 id가 다를 경우.예외처리)return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null);if문이 참일 경우 잘못된 요청이므로 ResponseEntity의 상태(status)에는 BAD_REQUEST(400), Body에는 null을 실어서 반환함.ResponseEntity<Article>ResponseEntity<Article>이렇게 설정해서 ResponseEntity에 Article을 담아서 반환 함.return ResponseEntity.status(HttpStatus.OK).body(updated);updated 변수에 저장해서 ResponseEntity에 담아서 응답으로 보냄.
title 자체를 지우고 내용만 수정할 경우.

title의 기존 값 자체가 없어져서 null값이 되어버렸음. @PatchMapping("/api/articles/{id}")
public ResponseEntity<Article> update(@PathVariable Long id, @RequestBody ArticleForm dto) {
Article article = dto.toEntity();
log.info("id = {}, article = {}", id, article.toString());
Article target = articleRepository.findById(id).orElse(null);
if (target == null || id != article.getId()) {
log.info("잘못된 요청임 id = {}, article = {}", id, article.toString());
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null);
}
target.patch(article); // 수정 된
Article updated = articleRepository.save(target); // 부분
return ResponseEntity.status(HttpStatus.OK).body(updated);
}
public void patch(Article article) { // 새로 만든 메서드.
if (article.title != null) {
this.title = article.title;
}
if (article.content != null) {
this.content = article.content;
}
}
patch() 메서드를 호출해서 수정할 내용이 있을 때만 동작하도록 구현.

@DeleteMapping("/api/articles/{id}")
public ResponseEntity<Article> delete(@PathVariable Long id) {
Article target = articleRepository.findById(id).orElse(null);
if (target == null) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null);
}
articleRepository.delete(target);
return ResponseEntity.status(HttpStatus.OK).build();
}
@DeleteMapping("/api/articles/{id}")@DeleteMapping으로 /api/articles/{id} URL 요청을 받음.if (target == null)articleRepository.delete(target);return ResponseEntity.status(HttpStatus.OK).build();.Body(null)를 쓰거나 .build()써도 결과는 같음.ResponseEntity의 build() 메서드는 HTTP 응답의 Body가 없는 ResponseEntity 객체를 생성함.ResponseEntityREST Controller의 반환형, 즉 REST API의 응답을 위해 사용하는 클래스.HTTP 상태 코드, 헤더, 본문을 실어서 보낼 수 있음.import java.net.URI;
import java.time.Instant;
import java.time.ZonedDateTime;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.Optional;
import java.util.function.Consumer;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.MultiValueMap;
import org.springframework.util.ObjectUtils;
public class ResponseEntity<T> extends HttpEntity<T> {
private final Object status;
public ResponseEntity(HttpStatusCode status) {
this((Object)null, (MultiValueMap)null, (HttpStatusCode)status);
}
public ResponseEntity(@Nullable T body, HttpStatusCode status) {
this(body, (MultiValueMap)null, (HttpStatusCode)status);
}
public ResponseEntity(MultiValueMap<String, String> headers, HttpStatusCode status) {
this((Object)null, headers, (HttpStatusCode)status);
}
public ResponseEntity(@Nullable T body, @Nullable MultiValueMap<String, String> headers, HttpStatusCode status) {
this(body, headers, (Object)status);
}
public ResponseEntity(@Nullable T body, @Nullable MultiValueMap<String, String> headers, int rawStatus) {
this(body, headers, (Object)rawStatus);
}
private ResponseEntity(@Nullable T body, @Nullable MultiValueMap<String, String> headers, Object status) {
super(body, headers);
Assert.notNull(status, "HttpStatusCode must not be null");
this.status = status;
}
// 이하 코드 생략.
}
HttpStatus열거형이름.상수 (형식)import org.springframework.lang.Nullable;
public enum HttpStatus implements HttpStatusCode {
CONTINUE(100, HttpStatus.Series.INFORMATIONAL, "Continue"),
SWITCHING_PROTOCOLS(101, HttpStatus.Series.INFORMATIONAL, "Switching Protocols"),
PROCESSING(102, HttpStatus.Series.INFORMATIONAL, "Processing"),
EARLY_HINTS(103, HttpStatus.Series.INFORMATIONAL, "Early Hints"),
/** @deprecated */
@Deprecated(
since = "6.0.5"
)
CHECKPOINT(103, HttpStatus.Series.INFORMATIONAL, "Checkpoint"),
OK(200, HttpStatus.Series.SUCCESSFUL, "OK"),
CREATED(201, HttpStatus.Series.SUCCESSFUL, "Created"),
ACCEPTED(202, HttpStatus.Series.SUCCESSFUL, "Accepted"),
NON_AUTHORITATIVE_INFORMATION(203, HttpStatus.Series.SUCCESSFUL, "Non-Authoritative Information"),
// 이하 코드 생략.