Spring Boot (+ RESTful)

RESTful 웹 서비스 구축 - @PostMapping / @RequestBody / swagger

wy-family 2024. 12. 30. 18:14

entity, repository, service 까지 만들었다.

첫번째로 만들어볼 REST API 는, 책 데이터를 저장하는 REST API를 만들어볼 것.

 

저장은, 기본적으로 POST, @PostMapping 이다. 요청은 /api/books 로 하겠다.

요청을 처리하려면 Controller 를 만들어야 한다.

@RestController 와 @RequestMapping("/api") 를 붙여준다.

@Tag 는 Swagger 에 표시되는 걸 정할 수 있다고 했다.

 

1. 클래스 선언부

  • @RestController
    • 이 클래스가 REST API의 엔드포인트를 제공하는 컨트롤러라는 것을 의미합니다.
    • @Controller와 @ResponseBody가 합쳐진 형태입니다. 반환하는 값은 HTTP 응답의 바디에 직접 들어갑니다.
  • @RequestMapping("/api")
    • 이 클래스에 정의된 모든 메서드의 기본 URL 패턴이 /api로 시작함을 의미합니다.
    • 예: /api/books 로 접근하게 됩니다.
  • @Tag (Swagger 문서용)
    • API 문서화 도구인 Swagger에서 이 컨트롤러를 "Book Controller"로 표시합니다.
    • description 속성은 컨트롤러에 대한 설명입니다. 문서화 용도입니다.

2. 필드 주입

  • @Autowired
    • Spring Dependency Injection으로 BookService라는 서비스 클래스를 이 컨트롤러에 주입합니다.
    • BookService는 비즈니스 로직 (책을 저장하거나 조회하는 로직)을 처리합니다.
  • 의존성 주입의 장점
    • 객체 생성과 관리를 프레임워크가 대신 해주므로 코드가 깔끔해집니다.

3. 메서드 선언부

어노테이션들

  • @PostMapping(value="/books")
    • HTTP POST 요청을 처리하는 메서드입니다.
    • value="/books"는 /api/books URL로 접근할 때 실행됨을 의미합니다.
  • consumes="application/json"
    • 이 메서드는 JSON 형식의 데이터를 요청 바디로 받습니다.
  • produces="application/json"
    • JSON 형식으로 데이터를 응답합니다.
  • @ResponseStatus(HttpStatus.CREATED)
    • HTTP 상태 코드 201 (Created)를 반환합니다. 성공적으로 리소스를 생성했음을 나타냅니다.
  • Swagger 관련 어노테이션
    • @ApiResponse: 각 상태 코드의 응답에 대한 설명을 문서화합니다.
    • @Operation(summary="Add an Book"): 메서드의 기능 요약을 설명합니다.

메서드 파라미터

  • @RequestBody
    • 클라이언트가 보낸 JSON 데이터를 BookPayloadDTO 객체에 매핑합니다.
  • @Valid
    • BookPayloadDTO에 정의된 유효성 검사를 수행합니다. 예를 들어 필수 필드가 비어있으면 예외를 발생시킵니다.

4. 메서드 구현부

주요 흐름

  1. Book 객체 생성
    • Book 엔티티에 BookPayloadDTO에서 받은 데이터를 설정합니다.
    • bookPayload는 JSON 데이터를 DTO 객체로 변환한 것입니다.
  2. bookService.save(book)
    • 책 데이터를 저장하는 서비스 로직을 호출합니다. 보통 JPA Repository를 사용해 데이터베이스에 저장합니다.
  3. BookViewDTO 반환
    • 저장된 Book 객체의 데이터를 클라이언트에 반환할 수 있도록 DTO로 변환합니다.
    • BookViewDTO는 클라이언트에게 보여줄 데이터만 포함합니다.
  4. ResponseEntity.ok()
    • HTTP 상태 코드 200과 함께 BookViewDTO 객체를 반환합니다.

예외 처리

  • 예외가 발생하면 HTTP 400 상태 코드와 빈 응답 바디를 반환합니다.

5. 클래스 및 DTO 정리

  • BookPayloadDTO
    • 클라이언트가 보내는 JSON 데이터를 받기 위한 DTO입니다.
    • subject, price, author, page 필드가 있을 것입니다.
  • Book
    • 데이터베이스에 저장되는 엔티티입니다.
  • BookViewDTO
    • 저장된 데이터를 클라이언트에 반환할 때 사용하는 DTO입니다.

6. 요약: 전체 흐름

  1. 클라이언트 요청
    • /api/books URL로 POST 요청을 보냄. JSON 형식의 데이터 포함.
  2. 데이터 매핑
    • @RequestBody를 통해 JSON 데이터를 BookPayloadDTO 객체에 매핑.
  3. 유효성 검사
    • @Valid를 통해 필수 데이터가 있는지 검사.
  4. 서비스 호출
    • bookService.save()로 데이터를 저장.
  5. DTO 변환
    • 저장된 Book 데이터를 BookViewDTO로 변환하여 반환.
  6. 응답
    • 성공 시 HTTP 상태 200과 JSON 데이터 반환, 실패 시 HTTP 400 반환.

이 코드는 DTO 패턴을 사용하여 데이터를 처리하고, Service Layer를 통해 비즈니스 로직을 분리하는 등 RESTful API의 모범적인 구조를 따릅니다.


Run 을 한 뒤에 확인해보자.

sboot 라는 DB에 book 이라는 table 이 만들어진 것도 확인할 수 있다.

하지만 아직 아무런 데이터가 들어있지는 않다.

 

우리는 @PostMapping 을 통해서 저장하는 서비스인 REST API 를 만들었다.

그러므로 해당 서비스를 사용해서 데이터 저장이 되는지 테스트를 해보자.

POST 방식으로, http://localhost:8081/api/books 로 요청을 해봐야 한다. 데이터도 JSON 으로 넘겨야한다.

그리고 그 테스트는, swagger 에서도 해볼 수 있다.

 

Schemas 라고 하는 부분의 > 표시를 다 열어보면, Swagger 가 BookPayloadDTO, BookViewDTO 의 코드까지 읽어서 Schemas 를 표시를 해놓은 걸 볼 수 있다.

 

@NotBlank 도 필수 입력을 뜻하고, @Schema() 의 REQUIRED 도 마찬가지로 필수 입력을 뜻한다. 그럼 약간의 차이는 없는건가?

Schema.RequiredMode.REQUIRED는 Swagger 또는 OpenAPI 문서에서 해당 필드가 반드시 제공되어야 함을 명시하는 설정입니다.

상세 설명

  • @Schema는 OpenAPI Specification을 기반으로 Swagger 문서를 생성할 때 사용되는 어노테이션입니다.
  • requiredMode = Schema.RequiredMode.REQUIRED는 이 필드(subject)가 필수 항목임을 명시합니다.

의미

  • 클라이언트 요청이나 API 문서에서 이 필드는 반드시 값이 있어야 한다는 뜻입니다.
  • 만약 필드 값이 제공되지 않으면, 서버는 요청을 거부하거나 에러를 반환할 수 있습니다.

주의점

  • @NotBlank와 함께 사용될 경우, 유효성 검사에서 값이 비어있으면 에러를 발생시킵니다.
  • 즉, requiredMode = REQUIRED는 문서상 필수임을 나타내고, @NotBlank는 런타임에서 유효성을 강제하는 역할을 합니다.

결론적으로, 이 설정은 API 문서에 필수 필드임을 명확히 표현하고, 클라이언트가 이를 준수하도록 유도하는 역할을 합니다.

@NotBlank와 Schema.RequiredMode.REQUIRED는 모두 필수 입력임을 의미하지만, 그 용도적용되는 범위에서 차이가 있습니다.


1. @NotBlank의 역할

  • 기능: 런타임 유효성 검사를 수행합니다.
  • 적용 범위: 서버 사이드 (Java 애플리케이션 내부에서 작동)
  • 설명:
    • 값이 null, 빈 문자열(""), 또는 공백 문자열(" ")인 경우 유효성 검사가 실패합니다.
    • Spring Validation 프레임워크와 함께 동작합니다.
    • HTTP 요청이 들어왔을 때 실제 값이 유효한지를 검사합니다.
  • HTTP 요청 본문에 subject 값이 비어있거나 공백이면 서버가 400 Bad Request 에러를 반환합니다.

2. Schema.RequiredMode.REQUIRED의 역할

  • 기능: API 문서에 필수 필드임을 명시합니다.
  • 적용 범위: API 문서화 도구 (Swagger / OpenAPI)
  • 설명:
    • 필드가 필수 입력임을 Swagger UIOpenAPI Specification 문서에 표시해 줍니다.
    • 문서에 명시만 될 뿐, 실제 런타임에서 필드를 검사하거나 처리하지는 않습니다.
  • Swagger 문서에서 subject가 필수 필드로 표시됩니다.
  • 그러나 이 설정만으로는 값이 비어도 서버에서 에러를 반환하지 않습니다.

차이점 요약

구분 @NotBlanck Schema.RequiredMode=REQUIRED
역할 유효성 검사 수행 API 문서에 필수임을 명시
적용 범위 런타임 (서버 사이드) 문서화 (Swagger/OpenAPI)
영향 필드가 비어있으면 서버에서 에러 반환 필드가 비어도 서버는 검사하지 않음
동작 시점 HTTP 요청 처리 시 API 문서 생성 시
필요한 도구 Spring Validation Swagger/OpenAPI

함께 사용하는 이유

  • @NotBlank는 실제 값을 검사하여 서버에서 필드의 유효성을 강제합니다.
  • Schema.RequiredMode.REQUIRED는 API 문서에 필수 입력임을 나타내어 클라이언트에게 필드를 반드시 제공해야 한다고 알려줍니다.

결론적으로, **@NotBlank**는 서버의 유효성 검사를 담당하고,
**Schema.RequiredMode.REQUIRED**는 API 문서에서 클라이언트에게 필수임을 명시하는 역할을 합니다.

둘을 함께 사용하면 서버 유효성 검사API 문서 필수 필드 명시라는 두 가지 효과를 모두 얻을 수 있습니다.


REST API 인, POST - /api/books 의 > 표시도 열어보자.

그리고 나서 Try it out 을 통해 테스트를 진행해볼 것이다.

그전에,

addBook(@Valid @RequestBody ~~) 라고 했는데, @RequestBody 는 JSON 으로 데이터를 받겠다는 의미다.

데이터 저장하기 (POST) 를 테스트 해보려면 JSON 을 넘겨야 한다.

그래서 Try it out 을 누르면, 

 

어떤 형식의 데이터를 넘겨야 하는지를 보여주고 있다.

required - 필수적으로

{
  "subject": "Java의 친구다",
  "price": 0,
  "author": "string",
  "page": 0
}

의 형태로 데이터를 넘겨야 한다는 걸 의미한다.

application/json 

이라고 표시가 된 것은,

 

 데이터가 넘어오는 타입이 json 타입이라는 걸 의미한다.

/books 라는 @PostMapping 이 실행되기 위해서는, json 타입으로 데이터를 받아야 한다는 걸 의미한다.

그래서 저런 데이터 형식으로 Request (body) 를 하게 되면, Responses 로 보여주고 있는 것이 있다.

Code 가 200 이면은 Book added 라는 메세지가 출력이 되는 것이다.

200 은 성공을 의미하는데, 아직 Execute 를 눌러보지는 않았지만 눌렀을때 성공을 해서 200 이라는 코드값이 나오게 되면, Book added 라는 메세지가 출력이 되도록 해놓은 것이 @ApiResponse 라는 어노테이션이다.

Code 201 은 생성이 제대로 되었다는 걸 의미한다. @ResponseStatus(HttpStatus.CREATED) 와 연관이 된다.

만약 responseCode가 400 이라면 "Please add valid name a description" 이라는 메세지가 날라가도록 해놓았다.

Swagger 에 RESTful API 를 세밀하게, 상세하게 사용하고 싶을 때에는 이렇게 Swagger 관련 @ 어노테이션들을 작성해놓을 수 있다.

 

이제 테스트를 한 번 해보자.

임의로 값을 입력해보았다. 저런 구조를 JSON 문자열, JSON 표기법이라고 한다.

JSON - JavaScript Of Notation

저 데이터를 Request body 로 하는 요청(Request)을 Execute 하게 되면, POST - /api/books 라는 URL, 서버한테 입력한 값을 넘기는 테스트를 진행해보게 된다. 테스트를 하게 되면 아래에서 Responses 로 보여주게 된다.

Execute 를 누르게 되면, 해당 데이터가 정말로 POST - /api/books 로 날라가게 되어서, addBook(~) 메서드, BookPayloadDTO bookPayload 가 해당 데이터를 받아내서 정말로 sboot DB 의 book table 에 insert 가 일어날 것이다.

Execute 를 눌러보자.

이 화면은 Swagger UI 또는 OpenAPI 문서를 통해 제공된 API 요청과 응답에 대한 상세한 예시입니다. 이를 단계별로 설명해 보겠습니다.


1. Curl 명령어

Curl은 터미널이나 명령줄에서 HTTP 요청을 보내기 위한 명령어입니다. 여기서 주어진 명령어는 다음과 같습니다:

분석

  1. -X 'POST'
    • HTTP 메서드: POST는 서버에 새로운 리소스를 생성하는 요청입니다.
  2. http://localhost:8081/api/books
    • URL: 요청을 보낼 서버의 엔드포인트 주소입니다.
      • localhost:8081: 서버가 실행되고 있는 주소와 포트입니다.
      • /api/books: 책 정보를 관리하는 API 경로입니다.
  3. -H 'accept: application/json'
    • 클라이언트가 JSON 형식의 응답을 원한다고 서버에 전달합니다.
  4. -H 'Content-Type: application/json'
    • 요청의 데이터가 JSON 형식임을 서버에 알립니다.
  5. -d '{ ... }'
    • 요청 바디에 포함될 JSON 데이터입니다:
      • subject: 책 제목
      • price: 가격
      • author: 저자
      • page: 총 페이지 수

2. Request URL

  • 이 URL로 요청을 보내게 됩니다. 로컬 서버(localhost)의 /api/books 엔드포인트입니다.

3. Server Response

요청이 성공적으로 처리되었을 때의 응답입니다.

Status Code: 200 OK

  • 200: 요청이 성공적으로 처리되었음을 의미합니다.
  • OK: 표준 HTTP 상태 메시지입니다.

4. Response Body

서버에서 반환한 JSON 응답입니다:

설명

  • id: 새로 생성된 책의 고유 ID입니다. 데이터베이스에 저장될 때 부여된 값입니다.
  • subject: 입력했던 책 제목 "Java의 친구"입니다.
  • price: 입력했던 가격 15000입니다.
  • author: 입력했던 저자 "김철재"입니다.
  • page: 입력했던 총 페이지 수 400입니다.
  • createdAt: 서버에서 기록한 생성 시간입니다. ISO 8601 형식 (yyyy-MM-ddTHH:mm:ss)으로 날짜와 시간이 기록됩니다.

5. Response Headers

서버가 클라이언트에게 반환한 HTTP 응답의 헤더 정보입니다:

설명

  • connection: keep-alive
    • 연결을 유지합니다. 동일한 연결을 통해 여러 요청을 처리할 수 있습니다.
  • content-type: application/json
    • 서버가 반환한 데이터가 JSON 형식임을 나타냅니다.
  • date
    • 응답이 생성된 날짜와 시간입니다.
  • keep-alive: timeout=60
    • 연결이 60초 동안 유지됩니다.
  • transfer-encoding: chunked
    • 응답 데이터를 청크 단위로 나누어 전송합니다.

요약

이 API는 책 정보를 생성하는 POST 요청입니다.

  1. 클라이언트가 JSON 데이터를 서버로 전송하면,
  2. 서버는 요청을 처리하고 새로운 책 리소스를 생성한 후,
  3. 생성된 데이터와 함께 200 OK 상태 코드로 응답합니다.

이와 같은 문서는 API 사용 방법과 결과를 시각적으로 보여주기 위해 Swagger UI나 OpenAPI를 통해 생성되며, 개발자들이 API 테스트 및 연동을 쉽게 할 수 있도록 돕습니다.


 

요청에 대한 응답을 할 때, return ResponseEntity.ok(bookViewDTO); 라고 했다.

insert (save) 가 끝난 다음에 view 데이터, 책 한 권의 데이터, bookViewDTO 가 JSON 형식으로 응답이 될 것이다. 그걸 swagger 가 받아가지고 뿌려주고(보여주고) 있는 것이 Response body 에 해당하는 부분이다.

insert (save) 가 되었다는 건, DB 에 저장이 되었다는 것이므로 MySQL 을 가서 확인을 해보자.


Swagger 와 관련된 @ 어노테이션 들은 사실상 없어도 상관은 없다.

@PostMapping 아래에 있는 4개의 어노테이션은 모두 swagger 와 관련이 있다. 없애고 나서 다시 run 을 해보겠다.

application.yml 에 여전히 create 라고 되어 있으므로, table 도 다시 만들어진다. 그래서 위에서 테스트로 입력했던 book 정보는 MySQL에서 사라졌다.

Swagger 도 다시 접속해보자. @Operation(summary="Add an Book") 은 사라졌다.

@ResponseStatus, @ApiResponse 도 사라졌지만, Code 200 에 대한 건 여전히 있다.

기본적으로 Web 은 성공하면 200 이라는 응답을 한다.

Request body 에서, 예시에서 보여주고 있는 형식으로 요청을 하게 되면,

Response body 에서, 예시로 보여주고 있는 Code 200 과 Description OK 와 보여주고 있는 형식으로 응답을 하게 된다는 걸 보여주고 있다.

@ 어노테이션은 없지만, return ResponseEntity.ok(bookViewDTO); 에서 ok 가 200:OK 을 의미한다. 상태값 200 과 JSON 데이터 (bookViewDTO)  를 하나로 묶어서 응답하는 타입을 ResponseEntity 라고 부른다.

 

결론은, @PostMapping 만 해도 된다는 의미다.

 

여기까지가, @PostMapping 에 대한 내용이었다.

다음 게시물에서 @GetMapping 에 대해서 공부해보겠다.