@ModelAttribute와 @RequestBody는 둘 다 Spring MVC 프레임워크에서 사용되는 어노테이션으로, HTTP 요청의 데이터를 컨트롤러 메서드에서 처리하기 위해 사용됩니다.
웹 애플리케이션에서 클라이언트가 서버로 데이터를 전송하는 과정은 다양한 형식으로 이루어집니다. 이러한 데이터를 효과적으로 처리하기 위해 스프링 프레임워크는 두 가지 주요 애너테이션,@ModelAttribute와@RequestBody, 을 제공하고 있습니다.
이 두 애너테이션은 HTTP 요청에서 데이터를 자바 객체로 변환하여 컨트롤러 메서드의 매개변수로 전달하는 데 사용됩니다.
@ModelAttribute 란❓
@ModelAttribute는 Spring MVC에서 HTTP 요청의 데이터를 자바 객체로 변환하고, 이 객체를 컨트롤러 메서드의 매개변수로 전달하는 데 사용되는 어노테이션입니다.
주로 HTML 폼 데이터와 URL 쿼리 파라미터를 처리하는 데 유용합니다. @ModelAttribute 어노테이션은 폼 제출 및 URL 쿼리 파라미터를 바인딩하여 자바 객체로 변환하는 데 사용됩니다.
@ModelAttribute의 주요 기능 ✅
- 폼 데이터 처리
- HTML form에서 제출된 데이터를 자바 객체로 변환합니다.
- form 필드와 객체의 프로퍼티를 자동으로 매핑합니다.
- URL 쿼리 파라미터 처리
- URL 쿼리 파라미터를 객체의 필드에 매핑할 수 있습니다.
- URL에 포함된 데이터와 객체의 속성을 연결합니다.
- 모델 객체 준비
- 컨트롤러 메소드의 실행 전에 모델 객체를 준비하여 뷰에 전달할 수 있습니다.
- @ModelAttribute 애너테이션을 사용하여 컨트롤러 메소드가 호출되기 전에 모델 객체를 준비하고 뷰에서 사용할 수 있게 합니다.
@ModelAttribute 사용 방법
위 코드처럼 Star라는 객체로 받아주었습니다.
@ModelAttribute 어노테이션을 사용하면 Body부분에 들어온 Query String방식의 데이터를 스프링 내부에서 객체로 바꿔주기 때문에 객체에 매핑해서 가져올 수 있습니다.
@ModelAttribute에서 자주 하는 실수 : 변수명 불일치 문제
객체 클래스에서 name ➡️ nam으로 개발자가 실수나 틀리게 작성했을 때 "테스트 결과"를 보면 다음과 같이 나옵니다.
Client에서 서버로 요청을 보내면 name이 null이 들어간 것을 볼 수 있습니다.
<h2>POST /hello/request/form/model</h2>
<form method="POST" action="/hello/request/form/model">
<div>
이름: <input name="name" type="text">
</div>
<div>
나이: <input name="age" type="text">
</div>
<button>전송</button>
</form>
<br>
서버 측 클래스에서 변수명이 nam으로 되어 있습니다.
클라이언트가 name이라는 필드 이름으로 데이터를 전송할 때, 서버는 이를 nam으로 매핑하려고 시도합니다. 결과적으로 name 필드는 null로 처리되며, 이는 데이터가 서버에 제대로 전달되지 않음을 의미합니다.
따라서 개발을 하다 보면 "데이터가 잘 넘어오지 않거나" , "null"이 들어가는 경우가 빈번하게 발생할 텐데 이러한 경우에는 변수명 실수일 경우가 있기 때문에 주의해야 합니다.
클라이언트와 서버 간의 데이터 전달에서 변수명 불일치는 빈번하게 발생할 수 있는 문제입니다.
특히 폼 데이터 처리 시, 클라이언트가 전송한 필드 이름과 서버에서 기대하는 변수명이 일치해야 합니다. 이와 같은 문제를 예방하기 위해 변수명을 정확하게 일치시키고, 디버깅 및 테스트를 통해 데이터를 검증하는 것이 중요합니다.
쿼리 스트링 데이터 처리: @ModelAttribute와 @RequestParam
웹 애플리케이션에서 클라이언트가 서버로 전송하는 데이터는 쿼리 스트링을 통해 전달될 수 있습니다. 이때 Qeury String 방식 즉 @Request Param방식도 ModelAttribute방식으로 받을 수 있습니다.
쿼리 스트링 방식, 즉 RequestParam 방식으로 데이터를 서버에 전달하는 경우, 데이터가 URL의 쿼리 파라미터로 전달됩니다.
이 때 중요한 점은 이러한 데이터가 많아질 경우, "?"와 "&"로 넘어오는데 @RequestParam으로 각각의 값을 받는 것은 코드의 유지보수성을 떨어뜨리고 번거로울 수 있기 때문에@ModelAttribute를 사용하여 한번에 객체로 처리하는 방법이 유용할 수 있습니다.
클라이언트는 다음과 같은 URL로 데이터를 서버에 전송합니다.
http://localhost:8080/hello/request/form/param/model?name=ZINU&age=123
서버 측 컨트롤러
서버에서는 @ModelAttribute를 사용하여 쿼리 스트링으로 전달된 데이터를 객체로 받을 수 있습니다.
Star 클래스
import lombok.Setter;
@Setter
public class Star {
String name;
int age;
public Star(String name, int age) {
this.name = name;
this.age = age;
}
}
클라이언트 화면
위 실행 결과와 같이 정상적으로 데이터가 잘 넘어옵니다.
따라서 쿼리 스트링방식으로 데이터가 많이 넘어온다면 클래스를 하나 만든 다음에 @ModelAttribute를 사용해서 받아 온다면 효율적으로 받아올 수 있습니다.
그러나 이때 주의해야 할 점이 있습니다.
중요 사항 및 주의점
Star 클래스
public class Star {
String name;
int age;
// public Star(String name, int age) {
// this.name = name;
// this.age = age;
// }
}
Star클래스에서 생성자를 주석 처리 한다면 결과는 다음과 같이 나오게 됩니다.
위 실행 결과를 보면 null, 0 즉 데이터를 하나도 받아오지 못합니다.
이유는 객체를 정의할 때(여기서는 Star클래스) 다음과 같은 조건이 있어야 하기 때문입니다:
- 객체 생성
- @ModelAttribute를 사용하여 객체로 데이터를 받을 때, 객체의 필드는 반드시 public 접근자(getters, setters) 또는 생성자 오버로딩이 필요합니다.
- ❗️스프링이 내부적으로 데이터를 객체에 담아주기 때문에 ❗️이들 메서드가 없으면
데이터 바인딩이 제대로 이루어지지 않습니다.
- 디폴트 생성자
- lombok(롬복)을 사용하는 경우에도, 객체는 기본 생성자(default constructor)를 가지며, 롬복의 @Getter, @Setter를 통해 필요한 접근자와 수정자가 자동으로 생성됩니다.
- 디폴트 생성자가 없으면 스프링이 객체를 생성할 수 없습니다.
따라서 lombok을 활용하면 간편하게 해결할 수 있습니다.
@ModelAttribute와 @RequestParam 생략 가능성 및 스프링의 파라미터 구분 방법
Spring MVC에서 @ModelAttribute와 @RequestParam어노테이션은 요청 데이터를 처리하는 두 가지 주요 방식입니다.
그러나 실제로는 위 코드와 같이 @ModelAttribute 어노테이션을 생략해도 스프링이 자동으로 데이터를 바인딩해주기 때문입니다.
정리하자면 생략이 가능한 이유는 다음과 같습니다.
생략 가능한 이유
- @ModelAttribute 생략
- @ModelAttribute는 주로 폼 데이터나 URL 쿼리 파라미터를 객체에 바인딩할 때 사용됩니다.
- 만약 메소드 파라미터가 사용자 정의 객체(예: Star)인 경우, 스프링은 자동으로 쿼리 파라미터를 해당 객체의 필드에 바인딩합니다.
- 이 경우 @ModelAttribute를 생략해도 스프링이 객체의 필드와 요청 파라미터를 매핑할 수 있습니다.
- @RequestParam 생략
- @RequestParam은 요청 파라미터를 메소드의 개별 파라미터에 바인딩하는 데 사용됩니다.
- 그러나 요청 파라미터가 메소드의 개별 파라미터와 일치하는 경우, @RequestParam을 생략해도 스프링은 자동으로 요청 파라미터를 바인딩합니다.
- 특히, 메소드 파라미터가 기본 데이터 타입(int, String 등)이나 그들의 래퍼 클래스일 때 자동 바인딩이 이루어집니다.
그러면 위 "생략 가능한 이유"와 이전 "[Spring] Spring MVC : HTTP 요청으로 데이터 받기 (URL, Query String)"포스팅에서 보았듯이 @RequsetParmam 또한 생략이 가능하다고 했는데, Spring은 어떻게 구별을 할까?❓
Spring의 파라미터 구분 방법
스프링이 파라미터를 어떻게 구분하는지 이해하기 위해, 스프링의 내부 처리 방식을 알아야 합니다.
스프링 MVC는 메소드 파라미터의 타입에 따라 @ModelAttribute와 @RequestParam을 자동으로 처리합니다.
- SimpleValueType
- 스프링이 SimpleValueType으로 간주하는 타입은 기본 데이터 타입 및 그들의 Wrapper 클래스입니다.
- 예를 들어, String, int, Integer, double, Double 등이 이에 해당합니다. 이러한 타입의 파라미터는
@RequestParam없이도 자동으로 바인딩됩니다.
- ComplexType
- 사용자 정의 클래스와 같은 복잡한 타입은
SimpleValueType이 아닙니다. - 스프링은 이러한 복잡한 타입의 파라미터를 처리할 때, 일반적으로 @ModelAttribute를 사용하여 요청 파라미터를 객체의 필드에 바인딩합니다. 그러나 @ModelAttribute가 생략된 경우에도, 스프링은 객체의 필드와 요청 파라미터를 매핑할 수 있습니다.
- 사용자 정의 클래스와 같은 복잡한 타입은
JSON 데이터 처리: @RequestBody
HTTP 요청의 본문에 포함된 JSON 데이터는 @RequestBody를 사용하여 자바 객체로 변환할 수 있습니다.
서버 측 컨트롤러
위와같이 json형식으로 넘어온다면 jackson이 해결해줍니다. 단 @RequestBody 어노테이션으로 지정해야 합니다.
Star 클래스
import lombok.Setter;
@Setter
public class Star {
String name;
int age;
public Star(String name, int age) {
this.name = name;
this.age = age;
}
}
먼저, JSON 데이터와 매핑될 자바 클래스(여기서는 Star)를 정의합니다. 이 클래스는 JSON의 필드와 일치하는 필드를 가져야 하며, 기본 생성자와 접근자(getters) 및 수정자(setters)를 포함해야 합니다.
위 결과처럼 정상적으로 처리할 수 있습니다.
Jackson 라이브러리의 역할
스프링에서 @RequestBody를 사용할 때, Jackson 라이브러리는 JSON 데이터 ➡️ 자바 객체로 변환하는 데 사용됩니다.
Jackson은 다음과 같은 기능을 제공합니다
- JSON 파싱
- JSON 문자열 ➡️ 자바 객체로 변환합니다.
- 객체 직렬화
- 자바 객체를 ➡️ JSON 문자열로 변환합니다.
- 유연한 데이터 바인딩
- 다양한 JSON 구조를 자바 클래스에 매핑할 수 있습니다.
결론
HTTP 요청 본문에 JSON 형식으로 데이터가 포함된 경우, @RequestBody 어노테이션을 사용하여 JSON 데이터를 자바 객체로 변환할 수 있습니다.
이때, Jackson 라이브러리가 JSON과 자바 객체 간의 변환을 자동으로 처리합니다.
'Framework > Spring\Spring boot' 카테고리의 다른 글
[Spring] Spring에서 빈 이름 결정 방식 (0) | 2024.07.28 |
---|---|
[Spring] Spring IoC 컨테이너에 Bean 수동 등록하기 (@Configuration, @Bean) (0) | 2024.07.28 |
[Spring] Spring IoC Container와 Bean 알아보기 (0) | 2024.07.24 |
[Spring] Spring MVC : HTTP 요청으로 데이터 받기 (URL, Query String) (1) | 2024.07.23 |
[Spring] 로그(logger) 알아보기 (0) | 2024.03.17 |