spring boot를 사용하면서 저는 대부분 @RequestBody 어노테이션을 사용하였습니다.
bean을 적극적으로 활용하고 있었기 때문에 @RequestBody를 사용하면 http 요청으로 넘어오는 body 내용이 매핑이 되기 때문입니다.
하지만 이후 등록 페이지를 개발하는 도중 이미지와 파일을 저장 해야하는 경우가 발생하였습니다.
이미지와 파일을 넘겨줄 때 어떤 어노테이션을 사용해서 데이터를 받을까 구글링을 해본 결과
생각보다 다양한 어노테이션이 있었습니다.
1. @RequestBody
HTTP 요청으로 넘어오는 body 내용을 HttpMessageConverter를 통해 JavaObject로 역직렬화한다.
multipart 요청이 아닌 어떤 바이너리 파일을 포함하고 있지 않은 데이터를 받는 역할을 한다.
REST API를 통해 단순 데이터를 주고받는 일반적인 경우에 사용할 수 있다.
* HttpMessageConverter : 간단하게 HTTP 요청과 응답에서 전략패턴을 사용하여 변환해주는 역할을 함.
전략패턴은 하나의 메서드가 여러가지의 대응 방법을 미리 준비해두고 필요한 상황마다 대응 방법을 달리하는 방법을 말한다.
@RequestBody는 HTTP 요청으로 같이 넘어오는 Header의 Content-type을 보고 어떤 Converter를 사용할지 정하기에 Content-type을 반드시 명시해야한다.
Content-type종류
application/json : {key: value} 의 형태로 전송
application/x-www-form-urlencoded : key=value&key=value 형태로 전송(HTML form의 default 값)
multipart/form-data : 파일 업로드 시 사용되며 '파일을 비롯한 여러 데이터가 있음' 이라는 뜻을 가짐(@RequestBody로 받을 수 없음)
Controller에서 받을 때 어노테이션 생략시 @ModelAttribute가 default이므로 반드시 기술하자.
2. @RequestPart
Content-type이 'multipart/form-data'와 관련된 경우에 사용한다.
MultipartFile이 포함되는 경우에 MultiPartResolver가 동작하여 (여기서도 전략패턴이 사용됨) 역지렬화를 하게 됨.
MultipartFile이 포함되지 않는 경우는 @RequestBody와 같이 HttpMessageConverter가 동작하게 된다.
@RequestBody가 필요하지만 Binary Stream이 포함되는 경우(MultipartFile과 같은)에 사용할 수 있다.
3. @RequestParam
하나의 파라미터만 받을 때 사용된다. 기본적으로 파라미터가 필수적으로 들어오게 설정되어 있기에 파라미터가 들어오지 않는 경우 BadRequest가 발생하므로 파라미터가 들어올 수도, 들어오지 않을 수도 있다면 required=false를 주어야 한다.
@RequestParam 또한 @RequestPart와 같이 MultipartFile을 받을 때 사용할 수 있다.
@RequestPart와 다른점은 @RequestParam의 경우 파라미터가 String이나 MultipartFile이 아닌 경우 Convert나 PropertyEditor 에 의해 처리되지만 @RequestPart는 HttpMessageConverter를 이용하여 Content-type을 참고하여 처리한다는 점이다.
이때 하나의 요청 파라미터에만 대응한다고 해서 1개의 MultipartFile만 받을 수 있는게 아닌 List<MultipartFile>의 형태로도 받을 수 있으며
모든 파라미터를 Map<String, String> 처럼 한 번에 받을 수 있으나 많은 데이터를 하나의 파라미터로 받는 것은 유지 보수성 측면에 좋지 못하므로 많은 데이터를 주고받는 경우에는 @RequestBody롸 DTO를 이용하는 편이 좋다.
4. @ModelAttribute
Content-type이 multipart/form-data의 형태를 받을때, HTTP 파라미터를 받는 경우 사용할 수 있다.
즉 HTTP body로 오든 파라미터로 오든 다 받을 수 있고 body와 파라미터가 같이 오는 경우에도 바인딩된다.
이런 형태가 가능한 이유는 @ModelAttribute는 필드 내부와 1:1로 값이 Setter나 Contructor를 통해 매핑되기 때문이다.
@RequestPart와 다른 점은 HttpMessageConverter에 의해 값이 바인딩 되는 것이 아닌 적절한 Setter 혹은 Contructor을 통해 값이 주입된다는 점이다.
즉 해당 DTO의 필드에 접근할 수 있는 적절한 수단이 존재하지 않으면 값이 바인딩 될 수 없다.
HttpMessageConverter가 동작하는 @RequestPart나 @RequestBody의 경우 필드를 찾을 때 ObjectMapper를 이용하고 이 ObjectMapper는 NoArgsContructor와 Getter나 Setter 등을 통해 private field에 접근할 수 있게 구현되어 있다.
이때 접근할 수 있는 이유는 Jackson라이브러리는 Reflecton을 통해서 private field에 값을 할당할 수 있기 때문이다.
@RequestBody와 @ModelAttribute의 차이는?
개발을 하면서 위의 두 어노테이션의 더욱 구체적인 차이가 궁금했다.
@RequestBody | @ModelAttribute |
- 클라이언트가 보내는 HTTP 요청을 JAVA Object로 변환 - bean에서 생성자와 setter가 없는데도 가능 -> 이유? - ObjectMapper를 통해 JSON값을 JAVA객체로 역직렬화 가능 - 역직렬화란 생성자를 거치지 않고 리플렉션을 통해 객체를 구성하는 매커니즘 - 그렇다면 ObjectMapper는 어떻게 JSON에 명시된 필드명 key를 java객체의 필드명과 맵핑시켜 값을 대입할까? - Jackson ObjectMapper는 json오브젝트 필드를 java오브젝트 필드에 맵핑할 때 getter혹은 setter를 사용함 - getter나 setter메서드 명의 접두사(get, set)를 지우고 나머지 문자의 첫 문자를 소문자로 변환한 문자열을 참조하여 필드명을 알아냄. |
클라이언트가 보내는 HTTP 파라미터들을 특정 java Object에 매핑한다. /modelattribute?name=req&age=1 과 같은 Query String 형태 혹은 요청 본문에 삽입되는 form 형태의 데이터를 처리한다. 객체의 필드에 접근해 데이터를 바인딩할 수 있는 생성자 혹은 setter가 필요하다. Query String 및 form 형식이 아닌 데이터는 처리할 수 없다. |
그래서 현재 나의 상황에서는?
등록을 할 때 많은 양의 json 내용과 파일, 이미지를 넘겨야하는 상황이기 때문에, 그리고 등록페이지의 콘텐츠 타입에 따라 데이터 형태도 바뀌기 때문에 @RequestPart를 사용하였다.
@PostMapping("/knowArchsave")
public ResponseEntity<Object> knowInsertArchData (
@RequestPart(value="attach_mfile_info", required = false) MultipartFile[] attach_mfile_info,
@RequestPart(value="cover_mimage_info", required = false) MultipartFile cover_mimage_info,
@RequestPart(value="knKnowData", required = false) knowArchBean data,
@RequestPart(value="knKnowDisplayData", required = false) knowArchDisplayInfoBean data1,
@RequestPart(value="knKnowProgramData", required = false) knowArchProgramBean data2,
@RequestPart(value="knKnowEventData", required = false) knowArchEventBean data3,
@RequestPart(value="serveyData", required = false) List<tollInfoBean> data4,
final HttpSession session,
final HttpServletRequest request,
final Model model) throws Exception {
/.../
}
'BackEnd-Study > Spring boot' 카테고리의 다른 글
Oracle에서 시퀀스(Sequence) 사용하기 (0) | 2024.04.03 |
---|---|
[springboot, mybatis] pagination (0) | 2024.04.03 |
스프링 컨테이너와 Bean (0) | 2024.03.15 |
댓글