HTTP 요청으로 데이터를 전달하는 방식과,
그에 맞는 Spring Framework 의 애너테이션을 정리합니다
URI 에 데이터를 담아 요청을 보낼 때, 그 데이터를 Path Variable(경로 변수) 라고 합니다.
주로 리소스의 식별자 성격의 데이터를 경로 변수로 사용합니다.
REST 의 리소스 중심적 설계 특성에 잘 어울리는 방식입니다.
Statelessness 와 URL 이 직관적이라는 장점 또한 얻을 수 있습니다.
URI 뒤에 ? 이후에 key=value 형식으로 데이터를 담아 요청을 보낼 때, 그 데이터를 Query Parameter 라고 합니다.
주로 리소스에 접근할 때 적용할 정렬 조건이나 필터링 조건을 Query parameter 로 사용합니다.
& 를 구분자로 사용하여 여러개의 Query Parameter 를 전달할 수 있으며, 같은 key 를 사용하면 리스트로 데이터를 전달할 수도 있습니다.
요청의 구성요소 3가지 라인, 헤더, 본문 중에 본문에 데이터를 담아 요청을 보내는 방식입니다.
주로 URL 에 노출하면 안되는 민감한 정보들이나 복잡한 구조를 가지는 데이터를 한번에 전송하기 위해 사용합니다.
HTML 의 form 테그를 사용하는 웹 폼 데이터나 XML 은 더이상 잘 사용하지 않고, JSON 형식이 가장 널리 사용됩니다.
특수하게 파일같이 다른 형태의 데이터를 요청에서 함께 다루어야 할 때 Multipart 형식을 사용합니다.
org.springframework.web.bind.annotation 의 @PathVariable 애너테이션은 @RequestMapping 애너테이션에서 명시한 경로 변수가 메서드의 어느 매개변수로 매핑되어야 하는지 지정해줍니다.
기본적으로 변수명을 통해 매핑하며, 애너테이션의 name 요소에 이름을 직접 지정할 수도 있습니다.
특정 조건에서 생략 가능하나, 명시해 주는 것이 좋습니다.
org.springframework.web.bind.annotation 의 @RequestParam 애너테이션은 단 하나의 Query Parameter 가 메서드의 어느 매개변수로 매핑되어야 하는지 지정해줍니다.
다음과 같은 요소로 추가 설정을 해줄 수 있습니다:
defaultValue: 해당 key 로 Query Parameter 를 전달 받지 않았다면 사용할 기본값
name: 매핑의 기준이 되는 key 값
required: 필수 요소인지 여부(기본값 true, false 라면 NPE 위험이 있음, defaultValue 가 있다면 그것을 사용)
매개변수가 Java 의 String 클래스나 primitive 타입과 그에 해당하는 wrapper 클래스라면 애너테이션이 생략되었을 때 @RequestParam 으로 간주합니다.
그외의 사용자 정의 클래스라면 @ModelAttribute 로 간주합니다.
org.springframework.web.bind.annotation 의@ModelAttribute 애너테이션은 Query Parameter, 경로변수, 요청 헤더 등을 하나의 객체로 바인딩 해준다고 설명하고 있습니다.
그러나 다른 방식들에서 명확한 역할을 하는 애너테이션이 있기 때문에 @ModelAtrribute 는 여러개의 Query Parameter 를 하나의 객체로 묶어서 다루는 용도로 주로 사용합니다.
앞서 설명했듯이 매개변수가 사용자 정의 클래스라면 생략되었을 때 @ModelAttribute 로 간주합니다.
이렇게 Query Parameter 를 하나의 객체로 묶어서 다루게 되면, @RequestParam 과는 다른 방식으로 기본값이나 필수여부를 설정해야 합니다:
기본값: Java 의 필드변수 초기화 방식을 사용(생성자 호출 시점에 초기화 됨)
필수 여부: @Valid 애너테이션과 함께 사용하여 적절한 애너테이션을 멤버 변수에 적용
Annotation Interface RequestBody 의 @RequestBody 애너테이션은 요청 본문의 데이터를 객체로 매핑해줍니다.
앞서 설명했듯이 애너테이션을 생략한다면 사용자 정의 클래스인 DTO 는 @ModelAttribute 로 간주됩니다.
따라서 @RequestBody 는 생략될 수 없습니다.
⚠️
@RequestBody의 데이터 바인딩 방식은@ModelAttribute와 다릅니다.
@RequestBody는 요청 본문의 데이터 형식(JSON, XML ...)에 맞는 데이터 바인더를 찾아 사용합니다.
많이 사용하는 JSON 의 경우 Spring 은 기본적으로 jackson-databind 를 사용합니다.
여기서 차이가 발생하는데, Spring 에서 제공하는@ModelAttribute매개변수를 하나 이상 가지고있는 생성자 혹은, 기본생성자와 setter 조합으로 데이터를 바인딩합니다.
그러나 jackson-databind 는 Java 의 Reflection API 를 사용하여 데이터를 바인딩합니다.
따라서 요청 본문이 매핑될 객체는 기본 생성자만 public(이마저도 선언하지 않아도 Java 가 자동 생성) 이라면 데이터 바인딩에 성공합니다.
그러나 Query Parameter 의 묶음이 될 객체는 Reflection 없이 객체를 초기화할 방법이 없다면 Spring 역시 데이터 바인딩에 실패합니다.
io.swagger.v3.oas.annotations 의 @ParameterObject 는 swagger API 문서를 보기좋게 만들어 주는 기능을 합니다.
사용자 정의 클래스인 매개변수에 이 애너테이션을 붙인다 한들 매핑해주는 기능은 없습니다.
그러나 매핑이 되는 이유는 앞서 설명했듯이 @ModelAttribute 가 생략되었다고 간주하기 때문입니다.
만일 @RequestBody 로 지정해야 한다면 두 애너테이션을 함께 사용해야 합니다.
@ParameterObject 는 문서에서 JSON 형식의 입력이 아니라 객체 내의 매핑될 필드를 풀어서 문서를 작성합니다.
데이터 전달 방식
| 방식 | 데이터 성격 | 목적 | 예시 | |
|---|---|---|---|---|
| Path Variable | 리소스 식별자 | 특정 리소스 하나를 조회, 수정, 삭제 | /posts/{postId} | |
| Query Parameter | 정렬/필터링 옵션 | 리소스 목록을 검색하거나 정렬 | /posts?status=published | |
| Request Body | 리소스의 내용 | 리소스를 생성하거나 수정할 때의 데이터 | POST /posts |
Spring 애너테이션
| 애너테이션 | 전달 방식 | 상황 |
|---|---|---|
@PathVariable | Path Variable | 경로변수를 매개변수로 매핑 |
@ReqeustParam | Query Parameter | 단일 Query Parameter 를 매개변수로 매핑 |
@ModelAttribute | Query Parameter | 여러개의 Query Parameter 를 묶어서 하나의 객체로 매핑 |
@RequestBody | Request Body | 요청 본문의 데이터를 객체로 매핑 |
참고자료:
https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-controller/ann-methods/requestbody.html#page-title
https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-controller/ann-methods/modelattrib-method-args.html#page-title
https://docs.spring.io/spring-framework/reference/core/validation/beans-beans.html#beans-binding
https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-controller/ann-methods/requestparam.html#page-title
https://docs.spring.io/spring-framework/docs/current/javadoc-api/index.html