Project

@RequestBody와 @ModelAttribute를 언제 사용해야 할까?

yerinpark 2024. 2. 26. 16:01

Intro

Spring Boot 프로젝트 진행 중 REST API로 개발하다가 Thymeleaf 사용 차 @Controller 어노테이션으로 변경한 일이 있었다. 모여서 회의하다가 Server Side Rendering와 Client Side Rendering에 대한 개념 등 조금 더 알아보기로 했다.

 

그 중 난 @RequestBody와 @ModelAttribute의 차이점과 언제 활용할 수 있는 지에 대해 알아본 내용을 바탕으로 팀 기술 문서 자료를 준비했다.

 

컨텐츠 주제

  • @RequestBody VS @ModelAttribute 차이점, 유사점 등등 각각 특징 등을 비교해서 포스팅 해주세요.
  • 이때 해당 작업은 어떨 때 구분해서 사용 되는지에 대해서 설명해주고! 해당 작업을 사용한 예시 코드도 좀 부탁드려요.
  • 또한 setter를 필요로 하는 작업과 아닌 작업에 대해서도 언급해주세요!

 

목차

1. @RequestBody

2. @ModelAttribute

3. @RequestBody VS @ModelAttribute 차이점

 

 

@RequestBody

🧙🏼‍♂️ @RequestBody란?

@RequestBody는 HTTP 요청 본문(body)을 읽어와서 객체로 변환합니다. 주로 JSON 또는 XML 형태의 데이터를 전송하고, 해당 데이터를 객체로 변환할 때 사용합니다.

body에 담은 값을 변환하기 때문에, GET 이 아닌 POST 방식에서만 사용이 가능합니다. GET 방식은 Header에 값을 담아서 보내기 때문이에요.

🎈 예시를 통해 알아보기

간단한 예시를 통해 알아봅시다.

클라이언트가 다음과 같은 JSON 형식의 데이터를 서버에게 전송한다고 가정해봅시다:

{
  "id": 1,
  "name": "Yerin Park",
  "age": 20
}

이 데이터를 받아서 처리하는 간단한 코드 예시입니다.

🧑🏼 Person

public class Person {

    private Long id;
    private String name;
    private int age;

    // and so on...

}

🎮 Controller

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class PersonController {

    @PostMapping("/person")
    public String createPerson(@RequestBody Person person) {
        // person 객체로 전송된 데이터 반환
        return "What is your name? : " + person.getName();
    }
}

Controller에서 JSON 데이터를 Person 객체로 변환합니다.

클라이언트가 POST 요청으로 "/person" endpoint에 JSON 데이터를 전송하면, Spring은 해당 JSON 데이터를 Person 객체로 변환하고 Controller method에 주입합니다. 이때 변환 작업은 MappingJackson2HttpMessageConverter 를 통해 요청 본문을 객체로 역직렬화합니다. 이후 Controller method에서는 person 객체를 활용하여 비즈니스 로직을 수행할 수 있습니다.

👩🏼‍🍳 활용법

@Valid 또는 Spring의 @Validated 어노테이션과 함께 사용 가능하며, 유효성 검사 오류는 기본적으로 400 (BAD_REQUEST) 응답으로 처리됩니다.

다른 매개변수에 @Constraint 어노테이션이 있으면 HandlerMethodValidationException이 발생할 수 있습니다.

👨🏼‍💻 예시 코드

@PostMapping("/accounts")
public void handle(@Valid @RequestBody Account account, Errors errors) {
    // ...
}

 

@ModelAttribute

🧙🏼‍♂️ @ModelAttribute란?

@ModelAttribute는 요청 파라미터를 모델 객체에 바인딩하는 역할을 합니다. 클라이언트가 전송한 HTTP 파라미터 및 HTTP 본문(body)의 내용을 해당 객체의 Setter 함수를 통해 1:1로 객체에 데이터를 바인딩합니다. 여기서 HTTP Body 내용은 multipart/form-data 형태이고, 변수 타입이나 변수명 등 바인딩 가능한지 검증 작업이 이루어집니다. 만약, 클라이언트로부터 전송된 데이터를 모델 객체에 바인딩하는 도중에 에러가 발생하면 Spring은 BindException을 던집니다.

@ModelAttribute 는 주로 JSP에서 Form 안에 input 값을 담아 보낼 때 유용하게 사용합니다.

📎 데이터 바인딩

@ModelAttribute는 요청 파라미터를 모델 객체의 필드에 바인딩하는 역할을 합니다. Setter 함수는 해당 필드에 값을 설정하는데 사용됩니다.

🎈 예시를 통해 알아보기

🧑🏼 User

public class User {
    private String username;
    private int age;

    // Getters and setters
}

🎮 Controller

@Controller
public class UserController {

    @GetMapping("/userForm")
    public String getUserForm(Model model) {
        model.addAttribute("user", new User());
        return "userForm";
    }

    @PostMapping("/saveUser")
    public String saveUser(@ModelAttribute("user") User user) {
        // 여기서 user 객체를 이용하여 저장 로직을 수행
        System.out.println("Saved user: " + user.getUsername() + ", Age: " + user.getAge());
        return "redirect:/userForm";
    }
}

여기서 getUserForm 은 사용자에게 입력 폼을 보여주기 위한 메서드입니다.

ModelAttribute를 사용하여 빈 User 객체를 모델에 추가하고, saveUser 메서드는 이 폼에서 입력된 데이터를 받아와서 실제로 저장하는 역할을 합니다.

📄 userForm.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>User Form</title>
</head>
<body>
    <h1>User Form</h1>
    <form action="/saveUser" method="post">
        <label for="username">Username:</label>
        <input type="text" id="username" name="username" required>
        <br>
        <label for="age">Age:</label>
        <input type="number" id="age" name="age" required>
        <br>
        <button type="submit">Save</button>
    </form>
</body>
</html>

사용자가 usernameage를 입력하고 "Save" 버튼을 누르면, 이 정보는 @ModelAttribute를 사용하여 자동으로 User 객체로 바인딩되어 saveUser 메서드에 전달됩니다. 이렇게 하면 컨트롤러에서는 매우 간편하게 모델 객체를 생성하고 데이터를 전달받을 수 있습니다.

👩🏼‍🍳 활용법 및 주의사항

  • 보안 및 모델 객체 디자인 시 특별한 주의가 필요합니다.
    • ⚠️ @ModelAttribute의 보안 취약점 주의 사항
      • @ModelAttribute는 클라이언트로부터 전송된 데이터를 모델 객체에 자동으로 바인딩합니다. 이때 클라이언트가 악의적으로 조작한 데이터를 모델 객체에 주입할 수 있는 보안 취약점이 발생할 수 있습니다.
      • 예를 들어, 모델 객체에 불필요한 속성이나 비즈니스 로직을 담고 있다면 클라이언트가 이를 이용하여 악의적인 행동을 할 수 있습니다.
      • 데이터 바인딩 오류 및 유효성 검사*
      • 데이터 바인딩 중에 클라이언트로부터 전송된 데이터 형식이나 값이 잘못된 경우 MethodArgumentNotValidException이 발생합니다. 이 예외는 클라이언트의 잘못된 입력으로부터 서버를 보호하는 데 중요합니다.
      • @Valid 어노테이션을 사용하면 데이터 바인딩 이후 자동으로 유효성 검사가 적용됩니다. 이를 통해 클라이언트의 잘못된 입력에 대한 유효성을 검증할 수 있습니다.
      • @PostMapping("/update") public String update(@ModelAttribute("user") @Valid User user, BindingResult result) { if (result.hasErrors()) { // 데이터 바인딩 오류 또는 유효성 검사 오류 발생 return "errorPage"; } // 정상적인 처리 userService.updateUser(user); return "successPage"; }
      • 결론*
      • 모델 객체는 클라이언트로부터 전송된 데이터에 필요한 속성만을 가지고 있어야 합니다.
      • 불필요한 속성을 제거하고, 필요한 경우에는 데이터를 적절히 검증하여 사용해야 합니다.
      • 모델 객체 구성 시 비즈니스 로직에 따라 신중히 결정합니다.
  • 데이터 바인딩 오류가 발생하면 기본적으로 MethodArgumentNotValidException이 발생하며, 필요에 따라 컨트롤러 메서드에서 이를 처리할 수 있습니다.
  • @Valid 어노테이션을 사용하여 데이터 바인딩 후 자동으로 유효성 검사를 적용할 수 있습니다.

 

 

 

@RequestBody VS @ModelAttribute 차이점

@RequestBody@ModelAttribute는 두 개 모두 Spring 프레임워크에서 웹 애플리케이션 개발 시 사용되는 어노테이션입니다. 컨트롤러 메서드의 매개변수로 사용되어, HTTP 요청 데이터를 Java 객체로 변환하여 사용할 수 있게 합니다.

그러나, HTTP 요청을 처리하는 방식에서 차이가 있습니다. 다음 분류 표로 비교해보겠습니다.

  @RequestBody @ModelAttribute
JSON/XML 데이터 전송 RESTful API에서 JSON 또는 XML 데이터를 전송할 때 사용 HTML 폼 데이터를 전송하고자 할 때 사용
데이터 바인딩 방식 요청 본문(body)을 읽어와 객체로 변환 요청 파라미터를 모델 객체의 필드에 바인딩
유효성 검사 기본적으로 지원되며, @Valid 어노테이션을 사용하여 유효성 검사 가능 명시적으로 @Valid 어노테이션을 추가해야 유효성 검사 가능
인스턴스 생성 방식 객체 생성을 위한 기본 생성자 또는 "primary constructor" 사용 객체 생성을 위한 기본 생성자 사용
속성 바인딩 설정 요청 본문을 객체로 변환하는 과정에서는 속성 바인딩 설정이 덜 필요 특정 필드만 바인딩하도록 속성 바인딩 설정 가능

👷🏼‍♂️ Setter를 필요로 하는 작업

  • @RequestBody: 생성자를 통해 객체를 초기화하는 경우가 많기 때문에 일반적으로 setter가 필요하지 않습니다.
  • @ModelAttribute: 스프링이 객체를 생성하고 필드에 값을 할당하기 위해 setter를 사용합니다.

이러한 기준을 고려하여 @RequestBody@ModelAttribute를 적절하게 선택하면 됩니다.