0. 목표
[mission 05] feat: add @RequestBody POST API by ssoyeong · Pull Request #10 · ssoyeong/spring-boot-study
미션 간단한 POST API를 만든다. 만든 API를 로컬 환경에서 테스트해본다. 스펙 POST localhost:8080/test/greeting Request Body { "name": string, "age": int } 예시 # 요청 POST localhost:8080/test/greeting # Request body { "nam...
github.com
아래 API를 개발하자
스펙
POST localhost:8080/test/greeting
Request Body
{
"name": string,
"age": int
}
예시
# 요청
POST localhost:8080/test/greeting
# Request body
{
"name": "ssoyeong",
"age": 17
}
# 응답
Hello, ssoyeong! You're 17 years old
1. POST란?
1.1. 특징
(1) 데이터 전송
- POST 요청은 클라이언트에서 서버로 데이터를 전송하는 데 사용됨
- 이 데이터는 Request Body에 포함됨
(2) Request Body
- POST 요청은 Request Body에 데이터를 담아 전송함
- 이 데이터는 주로 HTML이나 JSON 데이터와 같은 형식을 가짐
(3) 안전하지 않음
- POST 요청은 서버의 상태나 데이터를 변경할 수 있으므로 "안전하지 않은" HTTP 메서드로 분류됨
- 새로운 리소스를 생성하고 기존 리소스를 수정하거나, 데이터베이스에 데이터를 추가하면서 서버의 상태가 변경되고 리소스의 상태가 수정됨
1.2. 사용 용도
(1) 데이터 전송
(2) 자원 생성 및 업데이트
(3) 파일 업로드
1.3. 주의 사항
(1) 보안
- POST 요청은 안전하지 않은 요청이므로, 데이터의 무단 변경을 방지하려면 적절한 보안 조치를 취해야 함
(2) 장애 처리
- POST 요청이 실패하거나 중단되었을 때, 서버 상태가 변경될 수 있으므로 장애 처리 및 중복 요청 방지를 고려해야 함
(3) 캐싱
- POST 요청은 일반적으로 캐싱되지 않으므로, 서버에 매번 새로운 요청이 전송됨
1.4. POST 요청 방식에 대한 보안 조치
- CSRF 방지
- 인증 및 권한 관리
- 유효성 검사
- 세션 관리
- HTTPS 사용
- SQL Injection 및 XSS 방어
- 보안 업데이트 및 패치
2. Spring에서 Request Body를 다루는 법
UserDto.java (Record 클래스 사용, 아래에서 살펴보자)
package com.ssoyeong.studyapplication.dto;
public record UserDto(String name, int age) {
}
TestController.java
package com.ssoyeong.studyapplication.controller;
import com.ssoyeong.studyapplication.dto.UserDto;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/test")
public class TestController {
@PostMapping("/greeting")
public ResponseEntity<?> postGreetingRequestBody(@RequestBody UserDto requestUserDto) {
UserDto responseUserDto = new UserDto(requestUserDto.name(), requestUserDto.age());
return ResponseEntity.ok(responseUserDto);
/*
return ResponseEntity.status(HttpStatus.OK).body(responseUserDto);
*/
}
}
(1) Request Body 처리
@RequestBody 어노테이션을 사용해 Request Body의 데이터를 자동으로 자바 객체로 매핑한다.
(2) 응답 생성
데이터 처리 후, 응답 객체를 생성한다.
(3) ResponseEntity 반환
일반적으로 POST 메서드는 ResponseEntity 타입을 반환하고, 이는 HTTP 응답을 래핑하는데 사용된다.
그럼 Spring Boot가 JSON 형식으로 직렬화하고 HTTP 응답으로 제공한다.
왜 ResponseEntity를 주로 사용할까?
- ResponseEntity는 Spring에서 사용되는 클래스로, HTTP 응답을 나타내고 제어하기 위한 클래스임
- 명시적으로 HTTP 응답 코드를 설정할 수 있음
- 응답 내용을 명시적으로 지정할 수 있고, JSON 형식으로 데이터를 반환하는 데 사용할 수 있음
- 응답에 헤더 정보를 추가하거나 커스터마이징할 수 있음
3. Spring에서 Request Body를 역직렬화하는 원리
3.1. 직렬화와 역직렬화
직렬화(Serialization)
- 객체를 바이트 스트림 또는 직렬화된 형식(JSON, XML)으로 변환하는 프로세스
- 직렬화된 데이터는 객체의 상태 및 데이터 멤버를 포함하고 있으며, 이 데이터를 파일에 저장하거나 네트워크를 통해 전송할 수 있음
- 직렬화된 데이터를 다른 시스템, 애플리케이션 또는 서버로 전송하고, 나중에 역직렬화를 수행하여 객체로 복원할 수 있음
역직렬화(Deserialization)
- 직렬화된 데이터를 다시 객체로 변환하는 프로세스
- 직렬화된 데이터를 읽고, 해당 데이터를 기반으로 새로운 객체를 생성하고 초기화함
- 객체는 직렬화되기 전의 객체와 동일한 상태를 가질 것으로 기대됨
java.io.Serializable 인터페이스를 구현한 클래스는 자바 객체를 바이트 스트림으로 직렬화하고 역직렬화하는 데 사용된다.
그렇다면 Spring에서 Request Body를 역직렬화하는 원리는 무엇일까?
3.2. Request Body를 사용하는 요청에 대한 흐름
- HTTP 요청 수신: 클라이언트로부터 들어온 HTTP POST 또는 PUT 요청을 Spring의 Dispatcher Servlet이 받습니다.
- 컨트롤러 메서드 선택: Dispatcher Servlet은 URL과 HTTP 메소드를 기반으로 어떤 컨트롤러 메서드를 호출할지 선택합니다.
- 메서드 매개변수 매칭: 선택된 컨트롤러 메서드의 매개변수 목록을 확인하고, Request Body를 역직렬화할 매개변수를 결정합니다. 이 매개변수에는 @RequestBody 어노테이션이 적용되어야 합니다.
- Request Body 읽기: Dispatcher Servlet은 HTTP 요청의 Body를 읽고, 이 데이터를 역직렬화하는 역할을 하는 메시지 컨버터(Message Converter)를 사용하여 Java 객체로 변환합니다. 주로 JSON 데이터를 처리하는 경우, JSON을 Java 객체로 변환하는 Jackson, Gson, 또는 기타 JSON 라이브러리를 사용합니다.
- 역직렬화: 역직렬화된 데이터를 컨트롤러 메서드의 @RequestBody로 표시된 매개변수에 주입합니다.
- 컨트롤러 메서드 호출: 역직렬화된 데이터가 매개변수로 주입되고, 컨트롤러 메서드가 호출됩니다.
- 응답 생성: 컨트롤러 메서드는 비즈니스 로직을 수행하고, 클라이언트에 반환할 데이터를 생성합니다.
- 응답 직렬화: 생성된 응답 데이터는 다시 메시지 컨버터를 통해 클라이언트가 기대하는 형식(예: JSON, XML)으로 직렬화됩니다.
- HTTP 응답 반환: 직렬화된 응답 데이터가 HTTP 응답으로 클라이언트에게 반환됩니다.
여기서 4. Request Body 읽기 단계에서 실제 데이터를 역직렬화하는 메시지 컨버터에 대해 알아보자
3.3. Message Converter란?
정의
Message Converter는 Spring 프레임워크의 'org.springframework.http.converter.HttpMessageConverter' 인터페이스를 구현한 클래스이다.
DispatcherServlet 내에서 동작하며 HTTP 요청의 Body 데이터를 자바 객체로 변환하고, 반대로 자바 객체를 HTTP 응답의 Body 데이터로 변환하는 역할을 한다.
동작원리
Message Converter는 HTTP 요청의 Content-Type 헤더를 확인해 어떤 데이터 형식(JSON, XML, HTML)으로 요청되었는지 판변한다. 그리고 해당 데이터 형식에 맞는 Message Converter를 선택하여 데이터 변환을 수행한다.
아래와 같은 종류의 Message Converter가 있고, 필요에 따라 사용자 정의 Message Converter를 작성할 수 있다.
MappingJackson2HttpMessageConverter
JSON 형식의 데이터를 처리하는 Converter로 Jackson 라이브러리를 사용함. 주로 RESTful API에서 JSON 데이터를 주고받을 때 사용됨. 내부적으로 ObjectMapper를 사용함
FormHttpMessageConverter
폼 데이터(또는 폼 인코딩된 데이터)를 처리하는 Converter로 HTML 폼과 관련된 데이터를 다룰 때 사용됨
선택된 Message Converter는 HTTP 요청의 Body 데이터를 읽어와 자바 객체로 변환하고, 이는 컨트롤러 메서드의 매개변수로 주입되며 컨트롤러 메서드가 호출됨
4. Java Record 클래스
- JDK16에서 정식 스펙으로 포함된 클래스
- Record 클래스는 불변(immutable) 데이터를 간단하게 표현하고 다루기 위한 목적으로 만들어졌으며, 데이터의 저장과 접근을 편리하게 제공함
주요 특징
(1) 데이터 저장
Record 클래스는 멤버 변수를 자동으로 생성하고 초기화하는 특별한 종류의 클래스이다. 멤버 변수는 final로 선언되어 값을 변경할 수 없어 불변성을 보장한다. Record 클래스는 생성자, getter, equals(), hashCode(), toString() 메서드를 자동 생성한다.
(2) 간결한 선언
Record 클래스를 선언할 때, 클래스 몸체(body)를 작성할 필요가 없다. 멤버 변수의 이름과 타입만 정의하면 컴파일러가 나머지 코드를 자동 생성한다.
(3) 사용 예시
Record 클래스는 데이터 저장용으로 매우 편리하다. 예를 들어, 특정 엔티티를 나타내는 데이터 클래스로 사용할 수 있다.
getter 메서드의 이름은 필드 이름을 따르며, 일반적으로 "get" 접두사가 없다.
5. 코드
package com.ssoyeong.studyapplication.dto;
public record UserDto(String name, int age) {
}
package com.ssoyeong.studyapplication.controller;
import com.ssoyeong.studyapplication.dto.UserDto;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/test")
public class TestController {
private static final String GREETING_MESSAGE = "Hello, %s!";
private static final String AGE_MESSAGE = "You're %d years old";
@PostMapping("/greeting")
public String postGreetingRequestBody(@RequestBody UserDto userDto) {
return GREETING_MESSAGE.formatted(userDto.name()) + " " + AGE_MESSAGE.formatted(userDto.age());
}
}
'Spring' 카테고리의 다른 글
[Spring Framework] web.xml, root-context.xml, servlet-context.xml의 차이 (0) | 2023.10.18 |
---|---|
[Spring Boot] 4. 간단한 GET API를 만든다(3/3) (1) | 2023.10.17 |
[Spring Boot] 3. 간단한 GET API를 만든다(2/3) (0) | 2023.10.14 |
[Spring Boot] 2. 간단한 GET API를 만든다(1/3) (1) | 2023.10.09 |
[Spring Boot] 1. 스프링 부트 프로젝트를 만든다 (0) | 2023.10.01 |