4 분 소요

인프런 스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술편을 학습하고 정리한 내용 입니다.

쿼리 파라미터, HTML Form

HTTP 요청 데이터 조회 - 개요

서블릿에서 학습했던 HTTP 요청 데이터를 조회하는 방법을 다시 떠올려 보자. 그리고 서블릿으로 학습했던 내용을 스프링이 얼마나 깔끔하고 효율적으로 바꿔주는지 알아보자.

HTTP 요청 메시지를 통해 클라이언트에서 서버로 데이터를 전달하는 방법을 알아보자.

클라이언트에서 서버로 요청 데이터를 전달할 때는 주로 다음 3가지 방법을 이용한다.

  • GET - 쿼리 파라미터
    • /url?username=kim&age=20
    • 메시지 바디 없이, URL의 쿼리 파라미터에 데이터를 포함해서 전달
    • 예) 검색, 필터, 페이징 등에서 많이 사용하는 방식
  • POST - HTML Form
    • content-type: application/x-www-form-urlencoded
    • 메시지 바디에 쿼리 파라미터 형식으로 전달 username=kim&age=20
    • 예) 회원 가입, 상품 주문, HTML Form 사용
  • HTTP message body에 데이터를 직접 담아서 사용
    • HTTP API에서 주로 사용, JSON, XML, TEXT
    • 데이터 형식은 주로 JSON 사용
    • POST, PUT, PATCH

하나씩 알아보자.

요청 파라미터 - 쿼리 파라미터, HTML Form

HttpServletRequestrequest.getParameter()를 사용하면 다음 두 가지 요청 파라미터를 조회할 수 있다.

hello.springmvc.basic.request.RequestParamController

1
2
3
4
5
6
7
8
9
10
11
12
13
@Slf4j  
@Controller  
public class RequestParamController {  
  
    @RequestMapping("/request-param-v1")  
    public void requestParamV1(HttpServletRequest request, HttpServletResponse response) throws IOException {  
        String username = request.getParameter("username");  
        int age = Integer.parseInt(request.getParameter("age"));  
  
        log.info("username={}, age={}", username, age);  
        response.getWriter().write("ok");  
    }  
}

get, post 둘 다 잘 된다.

@RequestParam

스프링이 제공하는 @RequestParam을 사용하면 요청 파라미터를 매우 편리하게 사용할 수 있다.

1
2
3
4
5
6
7
8
@ResponseBody  
@RequestMapping("/request-param-v2")  
public String requestParamV2 (@RequestParam("username") String memberName,  
                              @RequestParam("age") int memberAge) {  
  
    log.info("username={}, age={}", memberName, memberAge);  
    return "ok";  
}

이제 @RequestParam("username")을 줄여보면

1
2
3
4
5
6
7
8
@ResponseBody  
@RequestMapping("/request-param-v3")  
public String requestParamV3 (@RequestParam String username,  
                              @RequestParam int age) {  
  
    log.info("username={}, age={}", username, age);  
    return "ok";  
}

이렇게 쓸 수 있다. 하지만 스프링 부트 3.2 이상에선 -parameters자바 컴파일러에 이 옵션을 넣어줘야 한다.

스프링 부트 3.2 이상 파라미터 인식 문제 여길 참고하길 바람.

이제 더 줄여보면

1
2
3
4
5
6
7
8
@ResponseBody  
@RequestMapping("/request-param-v4")  
public String requestParamV4 (String username,  
                              int age) {  
  
    log.info("username={}, age={}", username, age);  
    return "ok";  
}

이런 것도 가능하다고 한다.

String , int , Integer 등의 단순 타입이면 @RequestParam 도 생략 가능

참고
이렇게 애노테이션을 완전히 생략 해도 되는데, 너무 없는 것도 약간 과하다는 주관적인 생각. (나 역시 동의)
@RequestParam이 있으면 명확하게 요청 파라미터에서 데이터를 읽는 다는 것을 알 수 있다.

아무튼 잘 된다.

파라미터 필수 여부 - requestParamRequired

1
2
3
4
5
6
7
8
@ResponseBody  
@RequestMapping("/request-param-required")  
public String requestParamRequired (@RequestParam(value = "username", required = true) String username,  
                              @RequestParam(value = "age", required = false) Integer age) {  
  
    log.info("username={}, age={}", username, age);  
    return "ok";  
}

다음과 같이 코드를 작성했다.

1
2
@RequestParam(value = "username", required = true) String username 
@RequestParam(value = "age", required = false) Integer age

보면 username은 required = true 로 되어 있다.

이렇게 해놓고 username을 빼고 요청하면???

400 bad request가 나온다.

참고 : username= 이렇게 빈 문자 넣으면 null 이 아니라 "" 이거다. "" != null

그럼 username만 넣으면 어떻게 될까?

잘 된다.

기본 값 적용 - requestParamDefault

1
2
3
4
5
6
7
@ResponseBody  
@RequestMapping("/request-param-default")  
public String requestParamDefault (@RequestParam(value = "username", defaultValue = "guest") String username,  
                                    @RequestParam(value = "age", defaultValue = "-1") int age) {  
    log.info("username={}, age={}", username, age);  
    return "ok";  
}

자 이번엔

1
2
@RequestParam(value = "username", defaultValue = "guest") String username,  
@RequestParam(value = "age", defaultValue = "-1") int age

다음과 같이 값이 안 넘어 왔을 때, 기본 값을 설정 해 줄 수 있는 옵션이다.

이렇게 잘 출력 된다.

파라미터를 Map으로 조회하기 - requestParamMap

1
2
3
4
5
6
@ResponseBody  
@RequestMapping("/request-param-map")  
public String requestParamMap (@RequestParam Map<String, Object> paramMap) {  
    log.info("username={}, age={}", paramMap.get("username"), paramMap.get("age"));  
    return "ok";  
}

다음과 같이 map으로 받을 수 있다.

잘 된다.

참고로 파라미터를 Map, MultiValueMap으로 조회할 수 있다. (근데 같은 키에 다른 벨류 2개를 쓰는 경우가 있나 싶다.)

HTTP 요청 파라미터 - @ModelAttribute

실제 개발을 하면 요청 파라미터를 받아서 필요한 객체를 만들고 그 객체의 값을 넣어 줘야 한다. 보통 다음과 같이 코드를 작성할 것이다.

1
2
3
4
5
6
@RequestParam String username;
@RequestParam int age;

HelloData data = new HelloData();
data.setUsername(username);
data.setAge(age);

스프링은 이 과정을 완전히 자동화 해주는 @ModelAttribute 기능을 제공한다.

먼저 요청 파라미터를 바인딩 받을 객체를 만들자.

hello.springmvc.basic.HelloData

1
2
3
4
5
@Data  
public class HelloData {  
    private String username;  
    private int age;  
}

자 기존에 @RequestParam을 사용했다면 다음과 같이 작성했을 것이다.

1
2
3
4
5
6
7
8
9
@ResponseBody  
@RequestMapping("/model-attribute-v1")  
public String modelAttributeV1(@RequestParam("username") String username, @RequestParam("age") int age) {  
    HelloData helloData = new HelloData();  
    helloData.setUsername(username);  
    helloData.setAge(age);  
    log.info("username={}, age={}", helloData.getUsername(), helloData.getAge());  
    return "ok";  
}

그런데 이제 @ModelAttribute를 적용하면 다음과 같이 바로 적용할 수 있다.

1
2
3
4
5
6
@ResponseBody  
@RequestMapping("/model-attribute-v1")  
public String modelAttributeV1(@ModelAttribute HelloData helloData) {  
    log.info("username={}, age={}", helloData.getUsername(), helloData.getAge());  
    return "ok";  
}

두 코드는 같은 일을 한다.

마치 마법처럼 HelloData객체가 생성되고, 요청 파라미터의 값도 모두 들어가 있다.

스프링MVC는 @ModelAttribute가 있으면 다음을 실행한다.

  • HelloData 객체를 생성한다.
  • 요청 파라미터의 이름으로 HelloData객체의 프로퍼티를 찾는다. 그리고 해당 프로퍼티의 setter를 호출해서 파라미터의 값을 입력(바인딩)한다.
  • 예) 파라미터 이름이 username이면 setUsername()메서드를 찾아서 호출하면서 값을 입력한다.

프로퍼티

객체에 getUsername(), setUsername() 메서드가 있으면, 이 객체는 username이라는 프로퍼티를 가지고 있다.

username프로퍼티 값을 변경하면 setUsername()이 호출되고, 조회하면 getUsername()이 호출된다.

바인딩 오류

age=abc처럼 숫자가 들어가야 할 곳에 문자를 넣으면 BindException이 발생한다. 이런 바인딩 오류를 처리하는 부분은 나중에 검증 파트에서 다뤄보자.

@ModelAttribute 생략

1
2
3
4
5
6
@ResponseBody  
@RequestMapping("/model-attribute-v2")  
public String modelAttributeV2(HelloData helloData) {  
    log.info("username={}, age={}", helloData.getUsername(), helloData.getAge());  
    return "ok";  
}

가능하다.

@ModelAttribute는 생략할 수 있다. 그런데 @RequestParam도 생략할 수 있으니 혼란이 올 수 있다.

스프링은 해당 생략 시 다음과 같은 규칙을 적용한다.

  • String, int, Integer 같은 단순 타입 = @RequestParam
  • 나머지 = @ModelAttribute(argument resolver로 지정해둔 타입 외)

참고 : argument resolver는 뒤에서 학

태그:

카테고리:

업데이트:

댓글남기기