BE/스프링 부트 3

11장 REST API의 동작 이해하기

이제하네 2023. 12. 13. 12:00
  • REST API의 구현 과정
    • 조회 요청 : /api/articles 또는 /api/articles/{id}
      → GET 메서드로 Article 목록 전체 또는 단일 Article을 조회
    • 생성 요청 : /api/articles
      → POST 메서드로  새로운 Article을 생성해 목록에 저장
    • 수정 요청 : /api/articles/{id}
      → PATCH 메서드로 특정 Article의 내용을 수정
    • 삭제 요청 : /api/articles/{id}
      → DELETE 메서드로 특정 Article의 내용을 삭제

 

REST API 구현하기

  • REST 컨트롤러 맛보기
    1. api 패키지를 생성후 FirstApiController.java 파일 생성
    2. REST API를 구현할떄는 @Controller(일반 컨트롤러) 대신 @RestController(REST 컨트롤러) 을 사용
    3. localhost:8887/api/hello로 URL 요청이 들어왔을때 HELLO WORLD! 를 출력하는 메서드 작성
    4. 서버를 실행하고 localhost:8887/api/hello에 접속하면 문자열이 잘 반환됨
    5. Talend API Tester 에서도 GET메서드를 선택후 URL을 localhost:8887/api/hello 으로 입력후 SEND버튼 클릭
    6. 응답이 200으로 돌아옴 BODY 부분에 HELLO WORLD!가 반환 
@RestController // REST API용 컨트롤러
public class FirstApiController {
	
	@GetMapping("/api/hello") //URL 요청 접수
	public String hello() {
		return "HELLO WORLD!"; //문자열 반환
	}
}

REST API 동작 확인
Talend API Tester 에서도 확인

 

  • REST 컨트롤러와 일반 컨트롤러의 차이
    • 일반 컨트롤러는 뷰 페이지를 반환
    • REST 컨트롤러는 JSON이나 텍스트같은 데이터를 반환 

일반 컨트롤러는 view 페이지를 반환

 

  • REST API : GET 구현하기
    • 모든 게시글 조회하기
      1. 새로운 REST 컨트롤러를 api 패키지 안에 ArticleApiController.java 파일 생성
      2. @RestController 어노테이션을 추가함
      3. GET 요청을 처리하는 메서드를 생성
        1. @GetMapping으로 URL 요청은 "/api/articles"로 작성
        2. 메서드의 수행 결과로는 Article 묶음을 반환하므로 반환형이 List<Article>인 index() 메서드를 정의후 return문에는  articleRepository의 findAll() 메서드를 사용해 DB에 저장된 모든 Article을 반환
        3. findAll() 메서드를 사용하기 위해서 클래스 내부에 articleRepository을 선언후 @Autowired 어노테이션을 붙여 의존성 주입
@RestController // REST 컨트롤러 선언
public class ArticleApiController {
	@Autowired // 게시글 리파지터리 주입
	private ArticleRepository articleRepository;
	
	@GetMapping("/api/articles") //URL 요청 접수
	public List<Article> index(){ //index() 메서드 정의
		
		return articleRepository.findAll();
	}
	
}

전체 목록 조회 결과

  • REST API : GET 구현하기
    • 단일 게시글 조회하기
      1. 조회하려는 게시글의 id에 따라 URL 요청이 바뀌어야 하므로 @GetMapping 의 URL을 "/api/articles/{id}"로 작성
      2. 메서드 수행 결과로 단일 Article을 반환하므로 반환형은 Article로 작성 후 메서드 이름을 show()로 작성후 return문은 DB에서 id로 검색해 얻은 엔티티를 가져오고 만약 해당 데이터가 없으면 null을 반환하도록 작성
      3. DB에서 id로 검색하려면 매개변수로 id를 받아와야하기 때문에 주소에서 받아올수 있도록 매개변수 앞에 @PathVariable 을 붙임
	@GetMapping("/api/articles/{id}") 
	public Article show(@PathVariable Long id) { 
		//주소에 있는 id를 가져올수 있도록 @PathVariable을 작성
		return articleRepository.findById(id).orElse(null);
	}

단일 데이터 조회 결과

 

  • REST API : POST 구현하기
    1. 데이터 생성 요청을 받아 처리할 메서드를 생성
      1. @PostMapping 으로 "/api/articles" 주소로 오는 URL 요청을 받음
      2. 반환형이 Article 인 create()라는 메서드 정의하고 생성할 데이터를 dto 매개변수로 받아옴  
    2. POST 요청을 보내면 성공 응답이 오긴하지만 응답 본문에 title , content 과 null값으로 나옴
    3. dto 매개변수 앞에 @RequestBody 라는 어노테이션 추가하면 요청시 본문(BODY)에 실어 보내는 데이터를 create() 메서드의 매개변수로 받아올수 있음 
      • 생성자 문제가 있어서 ArticleForm 에 @NoArgsConstructor ,@Getter ,@Setter 추가로 작성
	@PostMapping("/api/articles")
	public Article create(@RequestBody ArticleForm dto) { //create() 메서드 정의 
		 Article article = dto.toEntity();
		 return articleRepository.save(article);
	}

POST 요청 null로 처리됨
데이터 생성 결과

 

  • REST API: PATCH 구현하기
    • 데이터 전체를 수정할 경우
      • 데이터 수정 요청을 받아 처리할 메서드를 생성
        1. @PatchMapping 으로 "/api/articles/{id}" 주소로 오는 URL 요청 받음
        2. 반환형인 Article 인 update() 메서드 정의후 매개변수로 요청 URL의 id와 요청 메세지의 본문 데이터를 받아옴
      • 메서드의 본문은 네 부분으로 나누어 작성
        1. 수정용 엔티티 생성
          1. 클라이언트에서 받은 수정 데이터가 담긴 dto를 DB에서 활용할 수 있도록 엔티티로 변환해 article 변수에 저장
          2. 실행이 잘 되는지 학인하기 위해 id와 article의 내용을 로그로 찍기 위해  클래스위에 @Sif4j어노테이션 추가
        2. DB에서 대상 엔티티를 조회해 가져오기 위해  articleRepository.findById(id)을 통해서 DB에서 해당 id를 가진 엔티티를 가져오되 없다면 null을 반환 , 이렇게 반환한 값은 target이라는 변수에 저장
        3. 잘못된 요청이 들어온 경우 처리하기 위한 코드 작성
          1. 대상 엔티티가 없거나 수정 요청 id 와 본문 id가 다를경우 잘못된 요청 이므로 조건문 실행
          2. 잘못된 요청임을 확일할 수 있도록 id와 article의 내용을 로그로 찍음
          3. 클라이언트 요청 오류이므로 상태 코드 400을 반환해야하지만 단순히 반환형이 Article로 하면 안되서 ResponseEntity에 Article을 담아서 반환해야만 하는 데이터에 상태 코드를 실어 보낼수 있음
          4. if 문의 실행 결과 ResponseEntity 의 상태에는 400 또는 HttpStatus.BAD_REQUEST를 , 본문에는 반환할 데이터가 없으므로 null을 실어 반환 
        4. 정상 응답을 처리
          1. article 엔티티에 담긴 수정용 데이터를 DB에 저장한 후 updated 라는 변수에 저장
          2. 수정된 데이터는 ResponseEntity에 담아서 보냄 이때 정상 응답이므로 상태에는 200 또는  HttpStatus.OK를 싣고 본문에는 받환할 데이터인 updated를 싣는다
	@PatchMapping("/api/articles/{id}")
	public ResponseEntity<Article> update(@PathVariable Long id , @RequestBody ArticleForm dto ) {
		// 1. DTO -> 엔티티 변환하기
		Article article = dto.toEntity(); // dto를 엔티티로 변환
		log.info("id: {}, article: {}", id, article.toString());
	    // 2. 타깃 조회하기
		Article target = articleRepository.findById(id).orElse(null);
	    // 3. 잘못된 요청 처리하기
		if(target == null || id != article.getId()) { //잘못된 요청인지 판별
			// 400, 잘못된 요청 응답
			log.info("잘못된 요청! id: {}, article: {}", id, article.toString());
			 return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null);
		}
	    // 4. 업데이트 및 정상 응답(200)하기
		Article updated = articleRepository.save(article); //article 엔티티 DB에 저장
		return ResponseEntity.status(HttpStatus.OK).body(updated);
	}

잘못된 수정 요청 보내기1

2023-12-11 16:03:11.639  INFO 1772 --- [nio-8887-exec-5] c.e.f.api.ArticleApiController           : 잘못된 요청! id: 1, article: Article(id=3, title=생성하기, content=들어갔2다)

정상적은 수정 요청을 보내면 정상 응답 확인 가능

 

 

REST API: PATCH 구현하기

  • 일부 데이터만 수정할 경우
    1. title을 빼고 보내면 성공 응답은 오지만 title 값이 null이되며 기존 데이터가 날아가기 때문에 코드를 보강해 작성
    2. 앞에서 클라이언트로부터 받은 수정 요청 데이터를 article에 저장 그리고 수정할 대상을 DB에서 조회해 target에 저장되있으므로 기존 데이터에 새 데이터를 붙여주면 일부 데이터만 수정 가능
      1. 만약 patch()라는 메서드가 있다고 가정하고 target에 patch() 메서드로 article(수정할 내용만)을 붙임
      2. 최종적으로 target을 DB에 저장
    3. entity 패키지 안에 있는 Article.java을 열고 patch() 메서드 추가
    4. patch() 메서드는 수정할 내용이 있는 경우에만 동작하도록 if문으로 title이 null이 아니면 this(target)의 title을 갱신 같은 방법으로 content도 갱신
    5.  

title을 빼고 수정 요청을 보내면 title 이 null값이 들어가서 기존 데이터가 사라짐

//Article.java 파일에 patch() 메서드 추가
public void patch(Article article) {
		if (article.title != null)
	        this.title = article.title;
	    if (article.content != null)
	        this.content = article.content;
	}

//ArticleApiController 에 있는update 메서드를 이렇게 수정 
	@PatchMapping("/api/articles/{id}")
	public ResponseEntity<Article> update(@PathVariable Long id , @RequestBody ArticleForm dto ) {
		// 1. DTO -> 엔티티 변환하기
		Article article = dto.toEntity(); // dto를 엔티티로 변환
		log.info("id: {}, article: {}", id, article.toString());
	    // 2. 타깃 조회하기
		Article target = articleRepository.findById(id).orElse(null);
	    // 3. 잘못된 요청 처리하기
		if(target == null || id != article.getId()) { //잘못된 요청인지 판별
			// 400, 잘못된 요청 응답
			log.info("잘못된 요청! id: {}, article: {}", id, article.toString());
			 return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null);
		}
	    // 4. 업데이트 및 정상 응답(200)하기
		target.patch(article);  // <==== 수정된 부분
		Article updated = articleRepository.save(target); // <==== 수정된 부분
		return ResponseEntity.status(HttpStatus.OK).body(updated);
	}

수정하지 않는 title 값 유지

 

  • REST API : DELETE 구현하기
    1. DELETE 요청을 받아 처리할 메서드를 생성
      1. @DeleteMapping으로 "/api/articles/{id}" URL 요청을 받음
      2. 반환형으로 ResponseEntity에 <Article>을 실어 보내는 delete()라는 메서드를 정의하고 URL의 id를 매개변수로 받아옴
    2. 메서드는 세 부분으로 나누어 작성
      1. DB에서 대상 엔티티가 있는지 조회하고 없으면 null을 반환 반환받은 값은 target이라는 변수에 저장
      2. 잘못된 요청을 처리하는 코드를 작성 즉 target이 null이면 ResponseEntity의 상태에는 BAD_REQUEST 본문에는 null을 실어 보냄 
      3. 잘못된 요청이 아니라면 찾은 대상 엔티티를 삭제 그리고 ResponseEntity의 상태에는 httpStatus.OK 본문에는 null을 실어 보냄 
        • return  문의 body(null) 대신 build()으로 작성 가능 
        • ResponseEntity build() 메서드는 HTTP 응답의 body가 없는 ResponseEntity 객체를 생성
        • 따라서 bulid() 메서드로 생성된 객체는 body(null)의 결과와 같음
	@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.OK).body(null);
		//return ResponseEntity.status(HttpStatus.OK).build(); 위에랑 같은 결과
	}

삭제 요청 성공

 

'BE > 스프링 부트 3' 카테고리의 다른 글

13장 테스트 코드 작성하기  (1) 2023.12.19
12장 서비스 계층과 트랜잭션  (0) 2023.12.16
10장 REST API 와 JSON  (0) 2023.12.10
9장 CRUD와 SQL 쿼리 종합  (0) 2023.12.06
8장 게시글 삭제하기: Delete  (2) 2023.12.02