-
[김영한 스프링] 24. 스프링 MVC 기본 기능 - HTTP 요청 파라미터Spring/스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술 2023. 7. 14. 02:24
HTTP 요청 파라미터 - 쿼리 파라미터, HTML Form
HTTP 요청 데이터 조회 - 개요
클라이언트에서 서버로 요청 데이터를 전달할 때는 주로 다음 3가지 방법을 사용한다.
- GET - 쿼리 파라미터
- /url?username=hello&age=20
- 메시지 바디 없이, URL의 쿼리 파라미터에 데이터를 포함해서 전달
- 예) 검색, 필터, 페이징등에서 많이 사용하는 방식
- POST - HTML Form
- content-type: application/x-www-form-urlencoded
- 메시지 바디에 쿼리 파리미터 형식으로 전달 username=hello&age=20
- 예) 회원 가입, 상품 주문, HTML Form 사용
- HTTP message body에 데이터를 직접 담아서 요청
- HTTP API에서 주로 사용, JSON, XML, TEXT
- 데이터 형식은 주로 JSON 사용
- POST, PUT, PATCH
HTTP 요청 데이터 조회 - 개요
HttpServletRequest의 request.getParameter()를 사용하면 다음 두가지 요청 파라미터를 조회할 수 있다.
GET, 쿼리 파라미터 전송
예시
http://localhost:8080/request-param?username=hello&age=20
POST, HTML Form 전송
예시
POST /request-param ...
content-type: application/x-www-form-urlencoded
username=hello&age=20GET 쿼리 파리미터 전송 방식이든, POST HTML Form 전송 방식이든 둘다 형식이 같으므로 구분 없이 조회할 수 있다.
이것을 간단히 요청 파라미터(request parameter) 조회라 한다.
RequestParamController
package hello.springmvc.basic.request; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @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"); } }
main/java/hello/springmvc/basic/request/RequestParamController 생성
request.getParameter()
여기서는 단순히 HttpServletRequest가 제공하는 방식으로 요청 파라미터를 조회했다.
실행
결과
Post Form 페이지 생성
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Title</title> </head><body> <form action="/request-param-v1" method="post"> username: <input type="text" name="username" /> age: <input type="text" name="age" /> <button type="submit">전송</button> </form> </body> </html>
main/resources/static/basic/hello-form.html 생성
실행
결과
참고
Jar를 사용하면 webapp 경로를 사용할 수 없다. 이제부터 정적 리소스도 클래스 경로에 함께 포함해야 한다.HTTP 요청 파라미터 - @RequestParam
requestParamV2
/** * @RequestParam 사용 * - 파라미터 이름으로 바인딩 * @ResponseBody 추가 * - View 조회를 무시하고, HTTP message body에 직접 해당 내용 입력 */ @ResponseBody @RequestMapping("/request-param-v2") public String requestParamV2(@RequestParam("username") String memberName, @RequestParam("age") int memberAge){ log.info("username={}, age={}", memberName, memberAge); return "ok"; }
@Controller 애노테이션을 사용하고 있어서 return "ok" 뷰를 찾게 됨
그래서 @RestController 애노테이션으로 변경하는 방법도 있지만 @ResponseBody 애노테이션을 사용해서 @RestController 애노테이션과 같은 효과를 냄
@RequestParam : 파라미터 이름으로 바인딩
@ResponseBody : View 조회를 무시하고, HTTP message body에 직접 해당 내용 입력
@RequestParam의 name(value) 속성이 파라미터 이름으로 사용
@RequestParam("username") String memberName -> request.getParameter("username")
실행
결과
requestParamV3
/** * @RequestParam 사용 * HTTP 파라미터 이름이 변수 이름과 같으면 @RequestParam(name="xx") 생략 가능 */ @ResponseBody @RequestMapping("/request-param-v3") public String requestParamV3(@RequestParam String username, @RequestParam int age){ log.info("username={}, age={}", username, age); return "ok"; }
HTTP 파라미터 이름이 변수 이름과 같으면 @RequestParam(name="xx") 생략 가능
실행
결과
requestParamV4
/** * @RequestParam 사용 * String, int 등의 단순 타입이면 @RequestParam 도 생략 가능 */ @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 애노테이션을 생략하면 스프링 MVC는 내부에서 required=false를 적용한다.
required 옵션은 바로 다음에 설명한다.실행
결과
파라미터 필수 여부 - requestParamRequired
/** * @RequestParam.required * /request-param-required -> username이 없으므로 예외 * * 주의! * /request-param-required?username= -> 빈문자로 통과 * * 주의! * /request-param-required * int age -> null을 int에 입력하는 것은 불가능, 따라서 Integer 변경해야 함(또는 다음에 나오는 defaultValue 사용) */ @ResponseBody @RequestMapping("/request-param-required") public String requestParamRequired(@RequestParam(required = true) String username, @RequestParam(required = false) int age){ log.info("username={}, age={}", username, age); return "ok"; }
- @RequestParam.required
- 파라미터 필수 여부
- 기본값이 파라미터 필수(true)이다.
실행
- /request-param 요청
- username이 없으므로 400 예외가 발생한다.
주의! - 기본형(primitive)에 null 입력
- /request-param 요청
- @RequestParam(required = false) int age
null을 int에 입력하는 것은 불가능(500 예외 발생)
따라서 null을 받을 수 있는 Integer로 변경하거나, 또는 다음에 나오는 defaultValue사용
/** * @RequestParam.required * /request-param-required -> username이 없으므로 예외 * * 주의! * /request-param-required?username= -> 빈문자로 통과 * * 주의! * /request-param-required * int age -> null을 int에 입력하는 것은 불가능, 따라서 Integer 변경해야 함(또는 다음에 나오는 defaultValue 사용) */ @ResponseBody @RequestMapping("/request-param-required") public String requestParamRequired(@RequestParam(required = true) String username, @RequestParam(required = false) Integer age){ log.info("username={}, age={}", username, age); return "ok"; }
int에 null이 들어가면 컴파일 오류가 발생하기 때문에 Integer로 변경
주의! - 파라미터 이름만 사용
/request-param?username=
파라미터 이름만 있고 값이 없는 경우 -> 빈문자로 통과
기본 값 적용 - requestParamDefault
/** * @RequestParam * - defaultvalue 사용 * * 참고 : defaultValue는 빈 문자의 경우에도 적용 * /request-param-default?username= */ @ResponseBody @RequestMapping("/request-param-default") public String requestParamDefault(@RequestParam(required = true, defaultValue = "guest") String username, @RequestParam(required = false, defaultValue = "-1") int age){ log.info("username={}, age={}", username, age); return "ok"; }
파라미터에 값이 없는 경우 defaultValue를 사용하면 기본 값을 적용할 수 있다.
이미 기본 값이 있기 때문에 required는 의미가 없다.
defaultValue는 빈 문자의 경우에도 설정한 기본 값이 적용된다.
/request-param-default?username=
실행
결과
파라미터를 Map으로 조회하기 - requestParamMap
/** * @RequestParam Map, MultiValueMap * Map(key=value) * MultiValueMap(key=[value1, value2, ...]) ex) (key=userIds, value=[id1, id2]) */ @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, MultiValueMap으로 조회할 수 있다.
- @RequestParam Map,
- Map(key=value)
- @RequestParam MultiValueMap
- MultiValueMap(key=[value1, value2, ...] ex) (key=userIds, value=[id1, id2])
파라미터의 값이 1개가 확실하다면 Map을 사용해도 되지만, 그렇지 않다면 MultiValueMap을 사용하자.
HTTP 요청 파라미터 - @ModelAttribute
package hello.springmvc.basic; import lombok.Data; @Data public class HelloData { private String username; private int age; }
- 롬복 @Data
- @Getter , @Setter , @ToString , @EqualsAndHashCode , @RequiredArgsConstructor를 자동으로 적용해 준다.
@ModelAttribute 적용 - modelAttributeV1
/** * @ModelAttribute 사용 * 참고: model.addAttribute(helloData) 코드도 함께 자동 적용됨, 뒤에 model을 설명할 때자세히 설명 */ @ResponseBody @RequestMapping("/model-attribute-v1") public String modelAttributeV1(@RequestParam String username, @RequestParam int age){ HelloData helloData = new HelloData(); helloData.setUsername(username); helloData.setAge(age); log.info("username={}, age={}", helloData.getUsername(), helloData.getAge()); log.info("helloData={}", helloData); return "ok"; }
실행
결과
@Data에 ToString이 들어있어 log.info("helloData={}", helloData);가 가능
/** * @ModelAttribute 사용 * 참고: model.addAttribute(helloData) 코드도 함께 자동 적용됨, 뒤에 model을 설명할 때자세히 설명 */ @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()이 호출된다.
class HelloData {
getUsername();
setUsername();
}바인딩 오류
age=abc처럼 숫자가 들어가야 할 곳에 문자를 넣으면 BindException이 발생한다. 이런 바인딩 오류를 처리하는 방법은 검증 부분에서 다룬다.
@ModelAttribute 생략 - modelAttributeV2
/** * @ModelAttribute 생략 가능 * String, int 같은 단순 타입 = @RequestParam * argument resolver 로 지정해둔 타입 외 = @ModelAttribute */ @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로 지정해둔 타입 외)
실행
결과
출처 : https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-1
'Spring > 스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술' 카테고리의 다른 글
[김영한 스프링] 26. 스프링 MVC 기본 기능 - 응답 (0) 2023.07.17 [김영한 스프링] 25. 스프링 MVC 기본 기능 - HTTP 요청 메시지 (0) 2023.07.14 [김영한 스프링] 23. 스프링 MVC 기본 기능 - HTTP 요청 기본, 헤더 조회 (0) 2023.07.14 [김영한 스프링] 22. 스프링 MVC 기본 기능 - 요청 매핑 (0) 2023.07.12 [김영한 스프링] 21. 스프링 MVC 기본 기능 - 로깅 간단히 알아보기 (0) 2023.07.11 - GET - 쿼리 파라미터