[Spring] @RequestBody, @RequestParam, @ModelAttribute*

[verify$y]ยท2025๋…„ 5์›” 27์ผ

Spring

๋ชฉ๋ก ๋ณด๊ธฐ
8/16

๐Ÿ’ก Spring ํ•ต์‹ฌ ๊ฐœ๋… ์ธํ„ฐ๋ทฐ Q&A


@RequestBody, @RequestParam, @ModelAttribute์€ Spring MVC์—์„œ ํด๋ผ์ด์–ธํŠธ๋กœ๋ถ€ํ„ฐ ์ „๋‹ฌ๋œ ๋ฐ์ดํ„ฐ๋ฅผ Controller ๋ฉ”์„œ๋“œ์˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋ฐ”์ธ๋”ฉํ•  ๋•Œ ์‚ฌ์šฉ๋˜๋Š” ์–ด๋…ธํ…Œ์ด์…˜์ž…๋‹ˆ๋‹ค



@RequestBody

  • HTTP ์š”์ฒญ body์— ๋‹ด๊ธด JSON, XML ๋“ฑ์˜ ๋ฐ์ดํ„ฐ๋ฅผ Java ๊ฐ์ฒด๋กœ ์ง์ ‘ ์—ญ์ง๋ ฌํ™”(deserialize)ํ•ด์„œ ๋งคํ•‘.
  • ์ผ๋ฐ˜์ ์œผ๋กœ application/json ์š”์ฒญ

์˜ˆ์‹œ

  • http request - JSON Body ์ „์†ก
  • ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๋กœ๊ทธ์ธํ•˜๋ ค๋ฉด email๊ณผ password๋ฅผ ์ž…๋ ฅํ•ด์„œ ์„œ๋ฒ„์— ๋ณด๋‚ธ๋‹ค.
POST /login
Content-Type: application/json

{
    "email": "[test@example.com](mailto:test@example.com)",
    "password": "1234"
}


  • controller, DTO
@Data
public class LoginRequestDto {
    private String email;
    private String password;
}

@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody LoginRequestDto request) {
    String email = request.getEmail();
    String password = request.getPassword();
    // ์ธ์ฆ ๋กœ์ง ์ˆ˜ํ–‰
    return ResponseEntity.ok("๋กœ๊ทธ์ธ ์„ฑ๊ณต");
}

๋‚ด๋ถ€์ฒ˜๋ฆฌ

  • HttpMessageConverter โ†’ MappingJackson2HttpMessageConverter๊ฐ€ JSON์„ Java ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜
  • ์š”์ฒญ ๋ณธ๋ฌธ(body) ์ „์ฒด๋ฅผ ํŒŒ์‹ฑํ•˜์—ฌ DTO์— ๋งคํ•‘

๋‹จ์ 

  • form ํƒœ๊ทธ์—์„œ๋Š” ์‚ฌ์šฉ ๋ถˆ๊ฐ€๋Šฅ
  • application/json์„ ๋ช…์‹œํ•˜์ง€ ์•Š์œผ๋ฉด ๋งคํ•‘ ์‹คํŒจ ๊ฐ€๋Šฅ์„ฑ ์žˆ์Œ




@RequestParam ์‚ฌ์šฉ - URL ํŒŒ๋ผ๋ฏธํ„ฐ / Form ํ•„๋“œ 1:1 ๋Œ€์‘

  • form ๋ฐ์ดํ„ฐ ์ „์†ก ์‹œ ํŽธ๋ฆฌ
  • ๋‹จ์ผ ๊ฐ’ ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ๋ช…ํ™•ํ•  ๋•Œ
  • JSON ์‚ฌ์šฉ ๋ถˆ๊ฐ€
  • ํ•„๋“œ๊ฐ€ ๋งŽ์•„์ง€๋ฉด ์ฝ”๋“œ๊ฐ€ ์ง€์ €๋ถ„ํ•ด์ง

์˜ˆ์‹œ

  • http request - ์ฟผ๋ฆฌ ์ŠคํŠธ๋ง
POST /login?email=test@example.com&password=1234

  • controller
@PostMapping("/login")
public ResponseEntity<?> login(
	@RequestParam String email, @RequestParam String password) {
    // ๊ฐ๊ฐ์˜ ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ๊ฐœ๋ณ„ ๋ฐ”์ธ๋”ฉ๋จ
    return ResponseEntity.ok("๋กœ๊ทธ์ธ ์„ฑ๊ณต");
}

๋‚ด๋ถ€ ์ฒ˜๋ฆฌ

  • ServletRequest.getParameter() ๋ฐฉ์‹๊ณผ ์œ ์‚ฌ
  • URL ๋˜๋Š” form ํ•„๋“œ์—์„œ ๋‹จ์ผ ๊ฐ’ ์ถ”์ถœ

๋‹จ์ 

  • ํ•„๋“œ ์ˆ˜๊ฐ€ ๋งŽ์•„์ง€๋ฉด ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ์ง€์ €๋ถ„ํ•ด์ง
  • ๊ตฌ์กฐํ™”๋œ ๊ฐ์ฒด ๋งคํ•‘์ด ์•ˆ๋จ
  • ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ์–ด๋ ต๊ณ  ๋น„ํšจ์œจ์ 



@ModelAttribute ์‚ฌ์šฉ - ํผ ํ•„๋“œ ์ž๋™ ๋ฐ”์ธ๋”ฉ (์ „ํ†ต์ ์ธ ์›น ์•ฑ ๋ฐฉ์‹)

  • form ์ „์†ก์— ์ฃผ๋กœ ์‚ฌ์šฉ (ํด๋ž˜์‹ํ•œ HTML ํผ ๊ธฐ๋ฐ˜ ๋กœ๊ทธ์ธ)
  • HTML form์—์„œ ์ž๋™์œผ๋กœ ๊ฐ์ฒด ํ•„๋“œ ๋ฐ”์ธ๋”ฉ๋จ
  • ์œ ํšจ์„ฑ ๊ฒ€์ฆ์— ์ ํ•ฉ (@Valid์™€ ํ•จ๊ป˜ ์‚ฌ์šฉ ๊ฐ€๋Šฅ)


์˜ˆ์‹œ

  • httpRequest - HTML Form
<form action="/login" method="post">
    <input type="text" name="email">
    <input type="password" name="password">
    <button type="submit">๋กœ๊ทธ์ธ</button>
</form>


  • controller
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

@RestController
public class LoginController {

    @PostMapping("/login")
    public ResponseEntity<?> login(
            @Valid @ModelAttribute LoginForm form,
            BindingResult bindingResult) {

        if (bindingResult.hasErrors()) {
            // ์œ ํšจ์„ฑ ์‹คํŒจ ์‹œ ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€ ๋ฐ˜ํ™˜
            return ResponseEntity.badRequest().body(bindingResult.getAllErrors());
        }

        // ์œ ํšจ์„ฑ ํ†ต๊ณผ โ†’ ๋กœ๊ทธ์ธ ๋กœ์ง
        return ResponseEntity.ok("๋กœ๊ทธ์ธ ์„ฑ๊ณต");
    }
}


  • dto
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Data;

@Data
public class LoginForm {

    @NotBlank(message = "์ด๋ฉ”์ผ์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.")
    @Email(message = "์ด๋ฉ”์ผ ํ˜•์‹์ด ์•„๋‹™๋‹ˆ๋‹ค.")
    private String email;

    @NotBlank(message = "๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.")
    @Size(min = 4, message = "๋น„๋ฐ€๋ฒˆํ˜ธ๋Š” ์ตœ์†Œ 4์ž ์ด์ƒ์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.")
    private String password;
}

๋‚ด๋ถ€์ฒ˜๋ฆฌ

  1. ์‚ฌ์šฉ์ž form ๋ฐ์ดํ„ฐ โ†’ LoginForm ๊ฐ์ฒด์˜ ํ•„๋“œ๋กœ ๋ฐ”์ธ๋”ฉ
  2. @Valid์— ์˜ํ•ด ํ•„๋“œ์˜ ์œ ํšจ์„ฑ ์กฐ๊ฑด ๊ฒ€์‚ฌ ์ˆ˜ํ–‰
  3. ๊ฒ€์‚ฌ ์‹คํŒจ ์‹œ โ†’ bindingResult.hasErrors()๊ฐ€ true๊ฐ€ ๋˜๊ณ  ์˜ค๋ฅ˜ ๋ฆฌ์ŠคํŠธ๊ฐ€ ์ฑ„์›Œ์ง
  4. ์„ฑ๊ณต ์‹œ โ†’ ์ปจํŠธ๋กค๋Ÿฌ ๋‚ด๋ถ€ ๋กœ์ง ์ง„ํ–‰

๋‹จ์ 

  • JSON ์š”์ฒญ์—์„œ๋Š” ๋™์ž‘ํ•˜์ง€ ์•Š์Œ
  • ํ”„๋ก ํŠธ์—”๋“œ๊ฐ€ form์ด ์•„๋‹Œ ๊ฒฝ์šฐ ๋น„ํšจ์œจ์ 



@ModelAttribute + @Valid

  • BindingResult ๋กœ ์‹คํŒจ ์—ฌ๋ถ€ ๋ฐ ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€ ์ง์ ‘ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ์Œ



@RequestBody + @Valid + BindingResult

  • dto
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Data;

@Data
public class LoginRequestDto {

    @NotBlank(message = "์ด๋ฉ”์ผ์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.")
    @Email(message = "์œ ํšจํ•œ ์ด๋ฉ”์ผ ์ฃผ์†Œ๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.")
    private String email;

    @NotBlank(message = "๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.")
    @Size(min = 4, message = "๋น„๋ฐ€๋ฒˆํ˜ธ๋Š” ์ตœ์†Œ 4์ž ์ด์ƒ์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.")
    private String password;
}
  • controller
import jakarta.validation.Valid;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;

@RestController
public class LoginApiController {

    @PostMapping("/api/login")
    public ResponseEntity<?> login(@Valid @RequestBody LoginRequestDto request,
                                   BindingResult bindingResult) {

        if (bindingResult.hasErrors()) {
            return ResponseEntity.badRequest().body(bindingResult.getAllErrors());
        }

        // ๋กœ๊ทธ์ธ ์ธ์ฆ ๋กœ์ง ์ฒ˜๋ฆฌ
        return ResponseEntity.ok("๋กœ๊ทธ์ธ ์„ฑ๊ณต");
    }
}
  • http request
POST /api/login
Content-Type: application/json

{
  "email": "",
  "password": "12"
}

๋™์ž‘ ๊ตฌ์กฐ

  1. @RequestBody โ†’ JSON ๋ฐ์ดํ„ฐ๋ฅผ LoginRequestDto ๊ฐ์ฒด๋กœ ์—ญ์ง๋ ฌํ™”
  2. @Valid โ†’ jakarta.validation ์–ด๋…ธํ…Œ์ด์…˜์„ ๊ธฐ๋ฐ˜์œผ๋กœ DTO ํ•„๋“œ ๊ฒ€์ฆ ์ˆ˜ํ–‰
  3. BindingResult โ†’ ๊ฒ€์ฆ ์‹คํŒจ ์‹œ ์˜ค๋ฅ˜ ๋ชฉ๋ก์„ ๋ณด๊ด€
  4. ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ์ง์ ‘ ์‹คํŒจ ์—ฌ๋ถ€ ํŒ๋‹จ (hasErrors())

์ฃผ์˜ ์‚ฌํ•ญ

  • @Valid๋Š” @RequestBody ๋ฐ”๋กœ ๋’ค์— ์™€์•ผ ํ•จ, ์ˆœ์„œ๊ฐ€ ๋ฐ”๋€Œ๋ฉด validation์ด ์ˆ˜ํ–‰๋˜์ง€ ์•Š์Œ
  • BindingResult๋Š” ๋ฐ˜๋“œ์‹œ @Valid ๋‹ค์Œ ํŒŒ๋ผ๋ฏธํ„ฐ์—ฌ์•ผ ํ•จ, ์˜ˆ์™ธ ๋ฐœ์ƒ์„ ๋ง‰๊ณ  ์˜ค๋ฅ˜ ์ •๋ณด๋ฅผ ์ˆ˜๋™์œผ๋กœ ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ
  • ๊ธ€๋กœ๋ฒŒ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ๊ธฐ(@ControllerAdvice)์™€ ๋ณ‘ํ–‰ ์‚ฌ์šฉ ๊ฐ€๋Šฅ, ์˜ˆ์™ธ ์ผ๊ด„ ์ฒ˜๋ฆฌ ์ „๋žต๊ณผ๋„ ์—ฐ๋™ ๊ฐ€๋Šฅ



@ModelAttribute vs @RequestBody

@ModelAttribute@RequestBody
๋Œ€์ƒ ์š”์ฒญx-www-form-urlencoded, query stringJSON
ํŒŒ๋ผ๋ฏธํ„ฐ ๋งคํ•‘form-data โ†’ JavaBeanJSON โ†’ JavaBean
์œ ํšจ์„ฑ ๊ฒ€์‚ฌ@Valid + BindingResult ์ง€์›@Valid + BindingResult ์ง€์›
์ฃผ ์‚ฌ์šฉ ํ™˜๊ฒฝ์„œ๋ฒ„ ๋ Œ๋”๋ง, HTML formREST API, JS ํ”„๋ก ํŠธ์—”๋“œ
์‚ฌ์šฉ ์˜ˆ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€SPA ๋กœ๊ทธ์ธ API



profile
welcome

0๊ฐœ์˜ ๋Œ“๊ธ€