JiwonKKang/[ Spring ] - Controller의 파라미터 바인딩에 대해서

Created Mon, 11 Dec 2023 18:29:27 +0900 Modified Mon, 11 Dec 2023 18:34:45 +0900
1576 Words

@PathVariable

URL에 { }로 들어가는 패스변수를 받음. 하나의 URI템플릿 안에 여러 개를 선언할 수도 있습니다.
일치하지 않는 타입의 값이 들어오면 ‘HTTP 400 - Bad Request’를 발생시킵니다.

@RequestMapping("/hello/{id}")
public String view(@PathVariable("id") int id) {...}

@RequestParam

단일 HTTP 요청 파라미터를 메소드 파라미터에 넣어주는 애노테이션입니다.

public String view(@RequestParam("id") int id)
{...}

해당 파라미터가 반드시 있어야합니다. 만약 없다면 400-Bad Request를 받게 됩니다. 필수가 아니라 선택적으로 받을 수 있게 하려면 required 엘리먼트를 false로 설정해야합니다. 요청 파라미터가 없을 때 defaultValue를 지정할 수도 있습니다.

public String view(@RequestParam(value="id", required=false, defaultValue="50") int id)
{...}
public String view(
    @RequestParam("id") int id, 
    @RequestParam("name") String name, 
    @RequestParam("file") MultipartFile file)
{...}

요청 파라미터 이름을 지정하지 않고 Map<String, String> 타입으로 선언하면 컨트롤러에서 모든 요청 파라미터를 담은 맵으로 받을 수 있습니다. 파라미터 이름, key는 맵의 key, 값은 value에 담겨 전달됩니다.

public String add(@RequestParam Map<String, String> params)
{...}

자바 코드 컴파일 시 디버깅 정보를 모두 삭제하는 수준의 최적화를 하지 않았다면 이름도 생략 가능합니다. 단, 컨트롤러가 요구하는 파라미터의 이름과 요청 파라미터의 이름이 같아야 합니다.

public String view(@RequestParam int id)
{...}

@CookieValue

HTTP 요청과 함께 전달된 쿠키 값을 받을 메서드 파라미터에 넣어줍니다. 쿠키의 이름을 지정해주면 됩니다.
@RequestParam과 마찬가지로 지정된 쿠키 값이 반드시 존재해야하나, requireddefaultValue 엘리먼트를 사용할 수 있습니다.

public String check(@CookieValue("auth") String auth)
{...}

@RequestHeader

요청 헤더정보를 메서드 파라미터에 넣어주는 애노테이션입니다.
@RequestParam과 마찬가지로 지정된 쿠키 값이 반드시 존재해야하나, requireddefaultValue 엘리먼트를 사용할 수 있습니다.

public void header(
    @RequestHeader("Host") String host, 
    @RequestHeader("Keep-Alive") long keepAlive)
{...}

Map, Model, ModelMap

Model, ModelMap은 addAttribute() 메서드를 제공함. 일반적인 Map의 put()처럼 이름을 지정해서 값을 넣어줄 수 있습니다.

@RequestMapping(...)
public void hello(ModelMap model) {
    User user = new User(1, "Spring");
    model.addAttribute(user);    // -> model.addAttribute("user", user)와 동일합니다.
    ...
}

다른 애노테이션이 안 붙어있으면, 파라미터로 선언한 컨트롤러 메서드에 모델 정보를 담는데 사용할 수 있는 오브젝트가 전달됩니다.
모델을 담을 맵은 메서드 내에서 직접 생성할 수도 있습니다. 하지만, 파라미터로 정의하면 핸들러 어댑터에서 미리 만들어서 제공해 주는데, 그걸 사용하는 것이 편합니다. 자동 이름 생성 기능을 제공합니다.


@ModelAttribute

요청 파라미터를 메서드 파라미터에서 1:1로 받으면 @RequestParam이고, 도메인 오브젝트나 DTO 프로퍼티에 요청 파라미터를 바인딩해서 한 번에 받으면 @ModelAttribute 라고 합니다.
하나의 오브젝트에 클라이언트 요청정보를 담아서 한 번에 전달되기 때문에 커맨드 패턴에서 말하는 커맨드 오브젝트라고 부르기도 합니다.

URL의 쿼리스트링으로 들어오는 GET방식 HTTP 요청 정보를 @ModelAttribute가 붙은 파라미터 타입 오브젝트에 모두 담아서 전달합니다. 페이지 내의 폼 데이터를 받는 경우에도 @ModelAttribute를 사용합니다.

@RequestMapping("/user/search")
public String search(@ModelAttribute UserSearch userSearch, Model model) {
    List<User> list = userService.search(userSearch);
    model.addAttribute("userList", list);
    ...
}

페이지 내의 폼 데이터를 받는 경우에도 @ModelAttribute를 사용할 수 있습니다.
이 때는 보통 폼의 내용을 담을 수 있는 도메인 오브젝트나 DTO를 @ModelAttribute 파라미터로 사용합니다.

@RequestMapping("/user/add", method=RequestMethod.POST)
public String add(@ModelAttribute User user) {
    userService.add(user);
    ...
}

@RequestParam@ModelAttribute 두 가지 모두 생략 가능합니다. 스프링은 어떻게 annotation이 없는 파라미터를 보고 이 둘은 구분해내는가?

-> 스프링은 String, int와 같은 단순 타입은 @RequestParam으로 보고, 그 외의 복잡한 오브젝트는 모두 @ModelAttribute가 생략된 것으로 간주합니다.

!! 스프링은 간단한 숫자나 문자로 전달된 요청 파라미터를 복잡한 오브젝트로 변환할 수 있기 때문에 단순 타입이 아니라고 해서 꼭 @ModelAttribute가 생략됬다고 볼 수 없습니다.
그래서 @RequestParam@ModelAttribute을 작성해주는 것을 권장합니다. 매우 단순한 커맨드 오브젝트나 파라미터라면 생략해도 나쁘진 않습니다.

@odelAttribute 컨트롤러가 리턴하는 모델에 파라미터로 전달한 오브젝트를 자동으로 추가해주는 기능을 가지고 있습니다. 이 때 모델 이름은 기본적으로 파라미터 타입의 이름입니다. (Class User => “user”) 다른 이름을 쓰고 싶으면 지정해줄 수 있습니다.