[Spring] @ModelAttribute란?

artp·2025년 5월 21일

spring

목록 보기
4/11
post-thumbnail

@ModelAttributeHTTP 요청 파라미터(쿼리 스트링, 폼 데이터)자바 객체로 자동 바인딩해주는 Spring MVC 애노테이션입니다.

보통 HTML 폼에서 사용자가 입력한 데이터를 처리할 때 사용합니다.

HTML 폼에서 넘어온 여러 입력값들을 한 번에 자바 객체로 모아주는 역할을 수행합니다.

흐름

  1. 사용자가 HTML 폼을 작성하고 제출
  2. 브라우저가 폼 데이터를 name=value 형태로 서버에 제출
  3. Spring이 요청을 받고, 파라미터들을 @ModelAttribute로 처리해야겠다고 판단
  4. 요청 파라미터의 name과 Java 객체의 필드명을 매칭하여 값을 설정
  5. 변환된 객체를 컨트롤러 메서드의 파라미터로 전달

동작 방식

정확한 동작 방식은 다음과 같습니다.

  • HTTP 요청의 Content-Type이 application/x-www-form-urlencoded일 경우 처리
  • 요청 파라미터(form 데이터)를 자바 객체로 바인딩
  • 내부적으로 PropertyEditorConverter를 사용하여 타입 변환

실제 사용 예시 (HTML 폼과 컨트롤러)

HTML 폼

<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>

DTO 클래스 (Data Transfer Object)

@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이 하는 일)

Spring은 요청이 들어오면 다음과 같은 과정을 거칩니다.

  1. HTTP 요청 파라미터를 읽음
  2. @ModelAttribute 애노테이션이 붙은 파라미터를 확인
  3. 해당 타입의 객체를 생성 (기본 생성자 호출)
  4. 요청 파라미터의 name과 일치하는 필드에 값을 바인딩
    • String → 기본 타입 변환 (int, boolean 등)
    • 날짜, 열거형 등 복잡한 타입 변환
    • 중첩 객체 처리
  5. 값이 바인딩된 객체를 컨트롤러 메서드에 파라미터로 전달

@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=홍길동
  • 이 경우 User 객체는 정상적으로 생성됩니다.
  • name 필드만 채워지고, 나머지 필드는 null 또는 기본값으로 채워집니다.
  • 즉, 요청 값이 일부만 있어도 바인딩은 진행됩니다.

단순 타입이라면?

@GetMapping("/hello")
public String hello(@ModelAttribute String name) { ... } // ❌ 컴파일 에러 또는 예외
  • 단순 타입은 @RequestParam을 사용해야 합니다.
  • @ModelAttribute객체 바인딩 전용입니다.

참고: 스프링의 바인딩 기본 전략

  • 파라미터가 객체면 → @ModelAttribute처럼 바인딩 시도
  • 파라미터가 기본형 또는 String 등 단순 타입이면 → @RequestParam처럼 바인딩 시도

    즉, 스프링은 애노테이션이 생략되어 있어도 타입을 보고 바인딩 전략을 판단합니다.

profile
donggyun_ee

0개의 댓글