@ModelAttribute는 HTTP 요청 파라미터(쿼리 스트링, 폼 데이터)를 자바 객체로 자동 바인딩해주는 Spring MVC 애노테이션입니다.
보통 HTML 폼에서 사용자가 입력한 데이터를 처리할 때 사용합니다.
HTML 폼에서 넘어온 여러 입력값들을 한 번에 자바 객체로 모아주는 역할을 수행합니다.
name=value 형태로 서버에 제출@ModelAttribute로 처리해야겠다고 판단정확한 동작 방식은 다음과 같습니다.
application/x-www-form-urlencoded일 경우 처리PropertyEditor나 Converter를 사용하여 타입 변환<form action="/members/register" method="post">
<div>
<label for="name">이름:</label>
<input type="text" id="name" name="name" value="홍길동">
</div>
<div>
<label for="email">이메일:</label>
<input type="email" id="email" name="email" value="hong@example.com">
</div>
<div>
<label for="age">나이:</label>
<input type="number" id="age" name="age" value="25">
</div>
<div>
<label>관심사:</label>
<input type="checkbox" name="interests" value="programming" checked> 프로그래밍
<input type="checkbox" name="interests" value="music" checked> 음악
<input type="checkbox" name="interests" value="sports"> 스포츠
</div>
<button type="submit">가입하기</button>
</form>
@Getter @Setter
public class MemberForm {
private String name;
private String email;
private Integer age;
private List<String> interests;
// 기본 생성자
public MemberForm() {}
// toString() 메서드 (디버깅용)
@Override
public String toString() {
return "MemberForm{" +
"name='" + name + '\'' +
", email='" + email + '\'' +
", age=" + age +
", interests=" + interests +
'}';
}
}
@Controller
@RequestMapping("/members")
public class MemberController {
private final MemberService memberService;
// 생성자 주입
public MemberController(MemberService memberService) {
this.memberService = memberService;
}
// 폼을 보여주는 GET 메서드
@GetMapping("/register")
public String showRegistrationForm(Model model) {
// 빈 폼 객체를 모델에 추가
model.addAttribute("memberForm", new MemberForm());
return "members/registerForm";
}
// 폼을 처리하는 POST 메서드
@PostMapping("/register")
public String registerMember(@ModelAttribute MemberForm memberForm,
BindingResult bindingResult,
RedirectAttributes redirectAttributes) {
// 유효성 검사 실패 시
if (bindingResult.hasErrors()) {
return "members/registerForm"; // 다시 폼으로
}
// 서비스 레이어로 전달하여 비즈니스 로직 처리
memberService.registerMember(memberForm);
// 리다이렉트 시 일회성 메시지 추가
redirectAttributes.addFlashAttribute("message",
memberForm.getName() + "님 가입을 축하합니다!");
// 성공 시 목록 페이지로 리다이렉트
return "redirect:/members";
}
}
폼이 제출되면 브라우저는 다음과 같은 요청을 생성합니다.
POST /members/register HTTP/1.1
Host: localhost:8080
Content-Type: application/x-www-form-urlencoded
name=홍길동&email=hong@example.com&age=25&interests=programming&interests=music
Spring은 요청이 들어오면 다음과 같은 과정을 거칩니다.
@ModelAttribute 애노테이션이 붙은 파라미터를 확인
@ModelAttribute는 요청 파라미터를 객체에 바인딩할 때 객체의 기본 생성자를 호출한 후 setter 메서드를 사용합니다. 따라서 DTO 클래스에는 반드시 기본 생성자와 setter 메서드가 있어야 합니다.
@PostMapping("/join")
public String join(@ModelAttribute String name) {
...
}
@ModelAttribute는 객체용 애노테이션이기 때문에, String, int 같은 단순 타입에는 사용할 수 없습니다.@RequestParam을 사용해야 합니다.@PostMapping("/join")
public String join(@ModelAttribute User user) {
...
}
POST /join
Content-Type: application/x-www-form-urlencoded
name=홍길동
@GetMapping("/hello")
public String hello(@ModelAttribute String name) { ... } // ❌ 컴파일 에러 또는 예외
@RequestParam을 사용해야 합니다.@ModelAttribute는 객체 바인딩 전용입니다.@ModelAttribute처럼 바인딩 시도String 등 단순 타입이면 → @RequestParam처럼 바인딩 시도즉, 스프링은 애노테이션이 생략되어 있어도 타입을 보고 바인딩 전략을 판단합니다.