길벗 코딩자율학습단 11일차

donghan378·2025년 1월 26일

11일차

~11.3 REST API 구현하기(종이책 p.303~332)

11장 HTTP와 REST 컨트롤러

11.1 REST API의 동작 이해하기

※ JSON의 값으로 또다른 JSON 데이터나 배열을 넣을 수도 있다.

REST API 란 REST 기반으로 API를 구현한 것이라고 할 수 있다. REST API를 잘 구현하면 클라이언트가 기기에 구애받지 않고 서버의 자원을 이용할 수 있을 뿐만 아니라, 서버가 클라이언트의 요청에 체계적으로 대응할 수 있어서 서버 프로그램의 재사용성과 확장성이 좋아진다.

  • REST: HTTP URL로 서버의 자원(resources)을 명시하고, HTTP 메서드(POST, GET, PATCH/PUT, DELETE)로 해당 자원에 대해 CRUD(생성, 조회, 수정, 삭제)하는 것을 말한다.
  • API: 클라이언트가 서버의 자원을 요청할 수 있도록 서버에서 제공하는 인터페이스(interface)이다.

11.2 REST API의 구현 과정

REST API를 구현하려면 REST API의 주소, 즉 URL을 설계해야 한다.

이전에 만든 게시판의 Article 데이터를 CRUD하기 위해 REST API 주소를 다음과 같이 설계한다.

  • 조회 요청: /api/articles 또는 /api/articles/{id} → GET 메서드로 Article 목록 전체 또는 단일 Article을 조회한다.
  • 생성 요청: /api/articles → POST 메서드로 새로운 Article을 생성해 목록에 저장한다.
  • 수정 요청: /api/articles/{id} → PATCH 메서드로 특정 Article의 내용을 수정한다.
  • 삭제 요청: /api/articles/{id} → DELETE 메서드로 특정 Article을 삭제한다.

주소 설계가 끝났다면 URL 요청을 받아 그 결과를 JSON으로 반환해 줄 컨트롤러도 만들어야한다.

게스판을 만들 때는 일반 컨트롤러(ArticleController)를 사용했지만, REST API로 요청과 응답을 주고받을 때는 REST 컨트롤러를 사용한다. 또한 응답할 때 적절한 상태 코드를 반환하기 위해 ResponseEntity라는 클래스도 활용한다.

11.3 REST API 구현하기

com.example.firstproject에 새로운 패키지를 만든다. 이름은 마지막에 api를 붙인 com.example.firstporject.api로 생성한다.

REST 컨트롤러 맛보기

먼저 간단하게 REST API의 동작을 구현하며 REST 컨트롤러의 개념을 이해해본다.

  • api 패키지에서 FirstApiController라는 이름으로 클래스를 만든다.
  • 게시판을 만들 때는 클래스 위에 @Controller 어노테이션을 붙였지만 REST API를 구현할 때는 @RestController를 사용한다.
  • localhost:8080/api/hello로 URL 요청이 들어왔을 때 hello world!를 출력하는 메서드를 작성한다.
    
    public class FirstApiController{
    	@GetMapping(”/api/hello”)
    	public String hello(){
    		return “hello world!”;
    	}
    }
    
  • 서버를 실행하고 localhost:8080/api/hello에 접속하면 hello world!가 잘 반환되는 걸 볼 수 있다.
  • Talend API Tester에 접속해 GET 메서드를 선택하고 http://localhost:8080/api/hello를 URL로 입력하고 Send 해보면 응답이 200으로 오는 것을 확인할 수 있다.

REST 컨트롤러와 일반 컨트롤러의 차이

이전에 만든 FirstController를 열면 “/hi”라는 URL 요청을 보냈을 때 greetings.mustache 파일을 반환하도록 되어있다. 이를 Talend API Tester에서 테스트해 보면 응답 BODY에 HTML 코드가 있는 것을 확인할 수 있다.

즉, REST 컨트롤러는 JSON이나 텍스트 같은 데이터를 반환하는 반면 일반 컨트롤러는 뷰 페이지를 반환한다는 차이점이 있는 것이다.

REST API: GET 구현하기

이제 본격적으로 게시판 데이터의 CRUD를 위한 REST API를 구현해본다.

먼저 GET 요청을 받아 처리할 메서드를 만들어 본다.

  1. 새로운 REST 컨트롤러를 만든다. api 패키지에 ArticleApiController로 클래스를 만든다.
  2. @RestController 어노테이션을 추가해서 이 클래스가 REST 컨트롤러임을 선언한다.
  3. @GetMapping으로 “/api/articles” 주소로 오는 URL 요청을 받는다.
  4. 메서드 수행 결과로 Article 묶음을 반환하므로 반환형이 List<Article>인 index()라는 메서드를 정의한다.
  5. return 문에는 articleRepository의 findAll() 메서드를 사용해 DB에 저장된 모든 Article을 가져와 반환한다.
  6. 그러나 articleRepository가 정의되지 않았으므로 클래스 내부에 articleRepository를 선언해주고 @Autowired 어노테이션을 붙여 의존성을 주입해준다.
  7. Talend API Tester에서 GET 요청을 http://localhost:8080/api/articles로 보내면 Article 데이터 3개가 응답으로 오는 것을 확인할 수 있다.
    • Aritcle 클래스에 getter 메서드가 id에 대한 것 밖에 없어서 처음에는 id에 대한 정보만 응답으로 왔다. title과 content에 대한 getter 메서드도 작성해서 재실행해보니 3개 모두 잘 오는 것을 확인할 수 있었다.

이번에는 모든 게시글이 아닌 단일 게시글만 조회하는 요청을 처리해본다.

  1. index 메서드를 복사해 아래에 붙여 넣고, @GetMapping의 URL을 “/api/articles/{id}”로 수정한다.
  2. 단일 Article을 반환하므로 메서드의 반환형을 Article로 수정하고 메서드 이름은 show()로 수정한다.
  3. return 문은 DB에서 id로 검색해 얻은 엔티티를 가져오도록 수정하고, 만약 없으면 null을 반환하도록 한다.
  4. DB에서 id로 검색하려면 show() 메서드의 매개변수로 id를 받아 와야 한다. 이때 id는 요청 URL에서 가지고 오므로 매개변수 앞에 @PathVariable을 붙인다.

REST API: POST 구현하기

데이터 생성 요청을 받아 처리할 메서드를 만든다.

  1. @PostMapping으로 “/api/articles” 주소로 오는 URL 요청을 받는다.

  2. 반환형이 Article인 create()라는 메서드를 정의하고 데이터를 dto 매개변수로 받아 온다.

  3. 이렇게 받아온 dto는 DB에서 활용할 수 있도록 엔티티로 변환해 article 변수에 넣고, articleRepository를 통해 DB에 저장한 후 반환한다.

  4. 서버를 재시작하고 Talend API Tester에서 POST 요청으로 { “title”: “AAAA”, “content”: “123123” }으로 보내면 성공 응답은 돌아오지만 응답 본문에 title과 content가 null로 나온다.

  5. 웹 페이지에 게시판 폼을 만들고 데이터를 생성할 때는 컨트롤러의 메서드에 매개변수로 dto를 받아 오기만 하면 됐지만, REST API에서 데이터를 생성할 때는 JSON 데이터를 받아 와야 하므로 dto 매개변수 앞에 @RequestBody라는 어노테이션을 추가해 줘야 한다.

    이렇게 추가하면 요청 시 본문(BODY)에 실어 보내는 데이터를 create() 메서드의 매개변수로 받아 올 수 있다.

  6. 서버를 재시작하고 Send 버튼을 누르면 성공적으로 title과 content도 생성된 것을 확인할 수 있다.

    DB에 접속해서 확인해도 잘 생성된 것을 확인할 수 있다.

REST API: PATCH 구현하기

데이터 수정 요청을 받아 처리할 메서드를 만든다.

  1. @PatchMapping으로 “/api/articles/{id}” 주소로 오는 URL 요청을 받는다.

  2. 반환형이 Article인 update()라는 메서드를 정의하고 매개변수로 요청 URL의 id와 요청 메시지의 본문 데이터를 받아온다.

  3. 먼저 수정용 엔티티를 만든다.

    • 클라이언트에서 받은 수정 데이터가 담긴 dto를 DB에서 활용할 수 있도록 엔티티로 변환해 aritcle 변수에 저장한다.
    • 실행이 잘 되는지 확인하기 위해 로그를 찍어본다. @Slf4j 어노테이션을 추가해줘야한다.
  4. DB에 대상 엔티티가 있는지 조회해 가져온다.

    • articleRepository.findById(id)를 통해서 해당 id를 가진 엔티티를 가져오고 target이라는 이름의 변수에 저장한다. 없다면 null을 반환한다.
  5. 잘못된 요청이 들어온 경우를 처리한다.

    • 대상 엔티티가 없거나(target == null) 수정 요청 id와 본문 id가 다를(id != article.getId) 경우 잘못된 요청이므로 조건문을 실행한다.
    • 잘못된 요청임을 확인할 수 있도록 id와 article의 내용을 로그로 찍는다.
    • 클라이언트 요청 오류이므로 상태 코드 400을 반환해야 하는데, Article을 ResponseEntity에 담아서 반환해야만 반환하는 데이터에 상태 코드를 실어 보낼 수 있어서 update() 메서드의 반환형을 Article에서 ResponseEntity<Article>로 바꿔준다.
    • if문의 실행 결과로 ResponseEntity의 상태(status)에는 400 또는 HttpStatus.BAD_REQUEST를, 본문(body)에는 반환할 데이터가 없으므로 null을 실어 반환한다.

    ※ ResponseEntity는 REST 컨트롤러의 반환형, 즉 REST API의 응답을 위해 사용하는 클래스이다. REST API 요청을 받아 응답할 때 이 클래스에 HTTP 상태 코드, 헤더, 본문을 실어 보낼 수 있다.

  6. 마지막으로 정상 응답을 처리한다.

    • article 엔티티에 담긴 수정용 데이터를 DB에 저장 후 updated라는 이름의 변수에 저장한다.
    • 수정된 데이터는 ResponseEntity에 담아서 보낸다. 상태(status)에는 200 또는 HttpStatus.OK를 싣고, 본문(body)에는 반환할 데이터인 updated를 싣는다.

이 과정까지 하고 수정 요청을 하면 정상적으로 작동하는 것을 확인할 수 있지만, 데이트의 일부분(title은 빼고)만 수정하겠다고 요청을 보내면 성공 응답은 오지만 수정하는 것 외의 부분(title)은 null값이 되어 기존 값이 날라간다.

target에는 기존 데이터가 있고, article에는 수정할 데이터가 있으므로 기존 데이터에 새 데이터를 붙여 주면 일부 데이터만 수정할 수 있다.

  1. Article 엔티티 코드에 patch() 메서드를 작성한다.

    • if문으로 article(수정 엔티티)의 title이 null이 아니면, 즉 갱신할 값이 있다면 this(target)의 title을 갱신해 준다.
    • 같은 방법으로 content도 갱신해 준다.
    public void patch(Article article) {
    	if(article.title != null)
    		this.title = article.title;
    	if(article.content != null)
    		this.content = article.content;
    }
  2. target에 patch() 메서드로 article(수정할 내용만)을 붙인다. 그리고 최종적으로 target을 DB에 저장한다.

REST API: DELETE 구현하기

DELETE 요청을 받아 처리할 메서드를 만든다.

  1. @DeleteMapping으로 “/api/articles/{id}” URL 요청을 받는다.
  2. 반환형으로 ResponseEntity에 <Article>을 실어 보내는 delete()라는 메서드를 정의하고 URL의 id를 매개변수로 받아온다.
  3. DB에서 삭제할 대상 엔티티가 있는지 조회하고 없으면 null을 반환한다. 반환받은 값은 target이라는 변수에 저장한다.
  4. 잘못된 요청을 처리하는 코드를 작성한다.
    • target이 null이면 ResponseEntity의 상태(status)에는 BAD_REQUEST, 본문(body)에는 null을 실어 보낸다.
  5. 잘못된 요청이 아니라면 찾은 대상 엔티티를 삭제한다. 그리고 ResponseEntity의 상태에는 HttpStatus.OK, 본문(body)에는 null을 실어 보낸다.
    • body(null) 대신 build()를 작성해도 된다. ResponseEntity의 build() 메서드는 HTTP 응답의 body가 없는 ResponseEntity 객체를 생성하므로 build() 메서드로 생성된 객체는 body(null)의 결과와 같다.
  6. 서버를 재시작하고 DELETE 요청으로 http://localhost:8080/api/articles/2 URL을 주면 성공 응답이 오는 것을 볼 수 있고, 실제 DB에도 삭제된 모습을 볼 수 있다.

0개의 댓글