📌 목차
APIHTTP 요청 - 기본, 헤더 조회HTTP 요청 파라미터 - 쿼리 파라미터, HTML FormHTTP 요청 파라미터 - @ModelAttributeHTTP 요청 메시지 - 단순 텍스트HTTP 요청 메시지 - JSON📌 요청 매핑
이번에는 에노테이션을 통해 어떻게 RequestMapping 경로를 지정하고 매핑을 하는지 알아보자.
package hello.springmvc.basic.requestmapping;
@RestController
public class MappingController {
private Logger log = LoggerFactory.getLogger(MappingController.class);
@RequestMapping("/hello-basic")
public String helloBasic() {
log.info("helloBasic");
return "ok";
}
기본적인 코드는 위와 같다.
@RequestMapping(value = {"/hello-basic", "hello-go"}, method = RequestMethod.GET) 처럼 경로를 여러개 지정하거나 HTTP 상태 코드를 지정할 수 있다.
@RestController 를 사용하면 반환값을 HTTP 메시지 바디에 입력된다.
@GetMapping(value = "/mapping-get-v2")
public String mappingGetV2() {
log.info("mapping-get-v2");
return "ok";
}
상태코드를 따로 입력해서 지정해줄 수 있지만 @GetMapping 에노테이션을 통해서도 지정할 수 있다.
@GetMapping("/mapping/{userId}")
public String mappingPath(@PathVariable String data) {
log.info("mappingPath userId = {}", data); // {} 로 해야함.
return "ok";
}
자주 쓰이는 PathVariable (경로 변수) 을 사용하면 위 코드처럼 쓸 수 있다.
{} 괄호 안에 있는 값은 http://localhost:8080/mapping/userA 같은 링크로 이동할시 userA 가 자동으로 등록된다.
@GetMapping("/mapping/users/{userId}/orders/{orderId}")
public String mappingPath(@PathVariable String userId, @PathVariable Long orderId) {
log.info("mappingPath userId = {} , orderId = {} ", userId, orderId);
return "ok";
}
또한 경로 변수를 다중으로 사용 가능하다.
@GetMapping(value = "/mapping-param", params = "mode=debug")
public String mappingParam() {
log.info("mappingParam");
return "ok";
}
특정 파라미터가 있어야 하는 조건을 만들 수 있다.
http://localhost:8080/mapping-param?mode=debug 처럼 파라미터가 존재해야지 뷰를 읽을 수 있다.
@GetMapping(value = "/mapping-header", headers = "mode=debug")
public String mappingHeader() {
log.info("mappingHeader");
return "ok";
}
header= 에 값을 입력하면 특정 헤더가 있어야한다는 조건을 만들 수 있다.
//Content-Type 지정.
@PostMapping(value = "/mapping-consume", consumes = "application/json")
public String mappingConsumes() {
log.info("mappingConsumes");
return "ok";
}
consumes 를 통해 특정 Content-Type 를 지정할 수 있다. application/json 을 넣으면 json 형식의 데이터만 읽을 수 있다.
// produces = MediaType.TEXT_HTML_VALUE
@PostMapping(value = "mapping-produce", produces = "text/html")
public String mappingProduces() {
log.info("mappingProduces");
return "ok";
}
마지막으로 미디어 타입도 지정할 수 있다. HTTP 요청의 Accept 헤더를 기반으로 매핑하는데 만약 맞지 않으면 406 상태 코드를 반환한다.
📌 요청 매핑 - API
회원관리를 HTTP API 로 만든다고 가정해보고 매핑을 어떻게 해보는지 직접 코드를 만들어 보자.
실제 데이터는 저장하지 않는다.
GET /usersPOST /usersGET /users/{userId}PATCH /users/{userId}DELETE /users/{userId}package hello.springmvc.basic.requestmapping;
@RestController
@RequestMapping("/mapping/users")
public class MappingClassController {
@GetMapping
public String user() {
return "get users";
}
@PostMapping
public String addUser() {
return "post user";
}
@GetMapping("/{userId}")
public String findUser(@PathVariable String userId) {
return "get userId = " + userId;
}
@PatchMapping("/{userId}")
public String updateUser(@PathVariable String userId) {
return "update userId = " + userId;
}
@DeleteMapping("/{userId}")
public String deleteUser(@PathVariable String userId) {
return "delete userId = " + userId;
}
}
@RequestMapping 을 클래스 상단에 적어두면 경로의 중복을 제거할 수 있다.
@PathVariable 을 통해 파라미터를 가져올 수 있고 @PostMapping , @PatchMapping , @DeleteMapping , @GetMapping 등을 사용할 수 있다.
📌 HTTP 요청 - 기본, 헤더 조회
이번에는 HTTP 헤더 정보를 조회하는 법을 알아보자.
package hello.springmvc.basic.request;
@Slf4j
@RestController
public class RequestHeaderController {
@RequestMapping("/headers")
public String headers(HttpServletRequest request, HttpServletResponse response,
HttpMethod httpMethod, Locale locale,
@RequestHeader MultiValueMap<String, String> headerMap, @RequestHeader("host") String host,
@CookieValue(value = "myCookie", required = false) String cookie) {
log.info("request={}", request);
log.info("response={}", response);
log.info("httpMethod={}", httpMethod);
log.info("locale={}", locale);
log.info("headerMap={}", headerMap);
log.info("header host={}", host);
log.info("myCookie={}", cookie);
return "ok";
}
}
헤더 정보를 조회할때 많은 파라미터를 사용할 수 있다.
@RequestHeader MultiValueMap 은 ?age=20&age=10 같이 하나의 키에 여러 값을 받을 수 있다.
@RequestHeader("host") String host 은 특정 HTTP 헤더를 조회 할 수 있다.
📌 HTTP 요청 파라미터 - 쿼리 파라미터, HTML Form
package hello.springmvc.basic.request;
@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");
}
기본적인 서블릿 파라미터를 사용하면 위처럼 사용할 수 있다.
@ResponseBody // RestController 와 동일한 기능
@RequestMapping("request-param-v2")
public String requestParamV2(
@RequestParam("username") String memberName,
@RequestParam("age") int memberAge
) {
log.info("username = {} , age = {} ", memberName, memberAge);
return "ok";
}
다음으로 @RequestParam 에노테이션을 통해 특정 파라미터 값을 받아 올 수 있다.
또한 @ResponseBody 를 사용시 @RestController 와 동일한 기능을 수행한다.
@RequestParam String username, @RequestParam int age
이때 HTTP 파라미터 이름이 변수 이름과 같다면 위 처럼 생략할 수 있다.
String username, int age
또한 String , int , Integer 같이 객체가 아니라 단순 타입이면 @RequestParam 에노테이션도 생략할 수 있다.
하지만 에노테이션을 사용하지 않으면 코드가 명확하지 않다는 단점도 있다.
@RequestParam(required = true, defaultValue = "guest") String username
@RequestParam(required = false, defaultValue = "-1") Integer age
@RequestParam 에서 required 옵션을 통해 파라미터 필수 여부를 결정할 수 있다. 만약 false 면 해당 파라미터가 없어도 코드가 실행된다.
defaultValue 옵션을 통해 기본값을 적용시킬 수 있다. required=false 일때만 의미가 있다.
?username= 처럼 "" 빈문자의 경우에도 적용이 된다.
@ResponseBody
@RequestMapping("request-param-map")
public String requestParamMap(@RequestParam Map<String, Objects> paramMap) {
log.info("username = {} , age = {} ", paramMap.get("username"), paramMap.get("age"));
return "ok";
}
파라미터를 Map 을 통해서도 받을 수 있다. 만약 중복키가 있을 수 있다면 MultiValueMap 도 사용 가능하다.
📌 HTTP 요청 파라미터 - @ModelAttribute
개발을 할때 요청 파라미터를 받아서 필요한 객체를 만들고 그 객체에 값을 넣어줘야 할 때가 있다.
@RequestParam String username;
@RequestParam int age;
HelloData data = new HelloData();
data.setUsername(username);
data.setAge(age);
위와같은 로직을 해야하지만 스프링에서는 @ModelAttribute 에노테이션을 제공한다.
package hello.springmvc.basic;
import lombok.Data;
@Data
public class HelloData {
private String username;
private int age;
}
데이터를 저장할 클래스를 위 코드로 만들었다.
@Data 에노테이션은 롬복의 @Getter , @Setter , @ToString , @EqualsAndHashCode , @RequiredArgsConstructor 에노테이션 기능을 제공한다.
@ResponseBody
@RequestMapping("/model-attribute-v1")
public String modelAttributeV1(@ModelAttribute HelloData helloData) {
log.info("username = {} , age = {} ", helloData.getUsername(), helloData.getAge());
return "ok";
}
따라서 위 코드처럼 @ModelAttribute 옵션을 통해 객체를 불러오고 동시에 파라미터 값을 해당 객체에 저장할 수 있다.
@ModelAttribute 에노테이션을 제거할 수 있다.
📌 HTTP 요청 메시지 - 단순 텍스트
요청 파라미터와 다르게 HTTP 메시지 바디를 통해 데이터가 직접 오는 경우는 @RequestParam , @ModelAttribute 를 사용할 수 없다.
package hello.springmvc.basic.request;
@Slf4j
@Controller
public class RequestBodyStringController {
@PostMapping("/request-body-string-v1")
public void requestBodyString(HttpServletRequest request, HttpServletResponse response) throws IOException {
ServletInputStream inputStream = request.getInputStream();
String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
log.info("messageBody={}", messageBody);
response.getWriter().write("ok");
}
서블릿만을 사용한다면 request.getInputStream() 을 통해 HTTP 메시지 바디를 읽어 올 수 있다.
InputStream inputStream , Writer responseWriter
만약 response , request 를 사용하지 않는다면 파라미터를 위처럼 변경할 수 있다.
@PostMapping("/request-body-string-v3")
public HttpEntity<String> requestBodyStringV3(HttpEntity<String> httpEntity) throws IOException {
String messageBody = httpEntity.getBody();
log.info("messageBody={}", messageBody);
return new HttpEntity<>("ok");
}
HttpEntity 객체를 사용할 수 있다. 응답에도 사용가능하고 Htpp 메시지 컨버터 기능을 사용한다.
@PostMapping("/request-body-string-v4")
@ResponseBody
public String requestBodyStringV4(@RequestBody String messageBody) throws IOException {
log.info("messageBody={}", messageBody);
return "ok";
}
@RequestBody 에노테이션을 사용하면 아주 편리하게 http 메시지 바디 정보를 조회할 수 있다.
📌 HTTP 요청 메시지 - JSON
이번에는 HTTP API 에서 주로 사용하는 json 형식의 데이터를 조회해보자.
package hello.springmvc.basic.request;
@Slf4j
@Controller
public class RequestBodyJsonController {
private ObjectMapper objectMapper = new ObjectMapper();
@PostMapping("/request-body-json-v1")
public void requestBodyJsonV1(HttpServletRequest request, HttpServletResponse response) throws IOException {
ServletInputStream inputStream = request.getInputStream();
String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
log.info("messageBody={}", messageBody);
HelloData helloData = objectMapper.readValue(messageBody, HelloData.class);
log.info("username = {} , age ={}" , helloData.getUsername() , helloData.getAge());
response.getWriter().write("ok");
}
}
기본에 서블릿만을 사용할떄는 위처럼 ObjectMapper 을 사용했어야 했다.
@ResponseBody
@PostMapping("/request-body-json-v2")
public String requestBodyJsonV2(@RequestBody String messageBody) throws IOException {
log.info("messageBody={}", messageBody);
HelloData helloData = objectMapper.readValue(messageBody, HelloData.class);
log.info("username = {} , age ={}", helloData.getUsername(), helloData.getAge());
return "ok";
}
하지만 @RequestBody 에노테이션을 사용하면 objectMapper 을 사용해 messageBody 에 있는 데이터를 helloData 에 저장만 하면 된다.
@ResponseBody
@PostMapping("/request-body-json-v3")
public String requestBodyJsonV3(@RequestBody HelloData helloData) {
log.info("username = {} , age ={}", helloData.getUsername(), helloData.getAge());
return "ok";
}
파라미터 자체를 HelloData 로 설정하면 메시지 컨버터 기능을 통해 objectMapper 가 했던 일을 대신 해준다. 다만 @RequestBody 에노테이션은 생략할 수 없다.
@ResponseBody
@PostMapping("/request-body-json-v4")
public String requestBodyJsonV4(HttpEntity<HelloData> httpEntity) {
HelloData helloData = httpEntity.getBody();
log.info("username = {} , age ={}", helloData.getUsername(), helloData.getAge());
return "ok";
}
HttpEntity 를 통해서도 가능하다.
📌 정리
지금까지 요청 매핑을 하는 방법을 알아보았고 , HTTP 요청에서 헤더를 조회하는 법과 스프링 에노테이션을 통해 간략화 하는법 , HTTP 메시지 바디를 조회하는 방법중 쿼리파라미터와 메시지 바디 (텍스트,json) 을 스프링 에노테이션을 통해 간단하게 알아보는 방법을 살펴보았다.
다음에는 HTTP 응답에서 데이터를 어떻게 응답하는지 알아보자.