Spring/MVC

스프링 MVC 여러 종류의 요청매핑 사용하는 방법

ssung 2022. 2. 22. 17:20

API매핑

API를 매핑하는 2가지 방식이다. 두 번째 방식은 첫 번째 방식의 축약형이다.

 

(1) @RequestMapping("/hello", method = "RequestMethod.GET")

매핑할 경로와 HTTP메소드를 입력해주는 방식이다.

@RequestMapping("/hello", method = "RequestMethod.GET")
public String hello() {
	return "hello";
}

@Controller어노테이션을 사용할 경우 return하는 이름의 html파일을 찾아서 리턴할 것이고

@RestController어노테이션을 사용한다면 return하는 문자열을 그대로 반환할 것이다.

 

 

(2) @PostMapping("/hello")

@RequestMapping에서 HTTP메소드를 간편하게 표현하는 방법이다.

@PostMapping("/hello")
public String hello() {
	return "hello";
}
  • @GetMapping
  • @PostMapping
  • @DeleteMapping
  • @PutMapping

등 HTTP메소드를 앞에 적음으로서 메소드를 정의하고 경로를 따로 지정해주는 방식으로 사용한다.

 

 

 

 

경로변수 매핑

(1) @PathVariable

경로변수라고 불리며 URL에 들어온 변수를 파라미터로 받을 수 있다.

@GetMapping("/hello/{userId}")
public String hello(@PathVariable("userId") String data) {
	log.info("userId = {}", data);
	
	return "hello";
}

 

PathVariable의 이름과 파라미터의 이름이 같으면 생략할 수 있다.

@GetMapping("/hello/{userId}")
public String hello(@PathVariable String userId) {
	log.info("userId = {}", data);
	
	return "hello";
}

 

 

 

 

쿼리 파라미터, HTMLForm데이터 매핑

(1) @RequestParam

가장 원초적인 방법으로는 HttpServletRequest를 사용해서 getParameter("파라미터이름")의 형식으로 조회한다.

하지만 스프링에서 제공하는 간단한 방법으로 RequestParam을 사용할 수 있다.

@GetMapping("/hello/{userId}")
public String hello(@RequestParam("usernmae") String name, 		// 1번
                    @RequestParam int age, 				// 2번
                    String email) {					// 3번
					
	return "hello";
}

3가지의 방법이 있다.

  1. 파라미터의 이름과 변수로 지정하는 이름이 다를경우
  2. 파라미터의 이름과 변수로 지정하는 이름이 같을 경우
  3. 2번과 동일한 경우에 @RequestParam을 생략할 수 있다.

 

 

RequestParam의 옵션으로는 requireddefaultValue가 있다.

 

required는 클라이언트에서 파라미터를 필수로 보내야하는지 아닌지를 설정할 수 있다. 기본값은 true이고 필요 시 false로 설정할 수 있다.

@GetMapping("/hello/{userId}")
public String hello(
        @RequestParam(required = true) String username
        @RequestParam(required = false) int age) {
					
	return "hello";
}

true로 설정된 변수를 클라이언트에서 넘기지 않을 경우 스프링이 400에러를 띄우게 된다.

 

주의해야할 것은 age가 false이므로 값이 넘어오지 않을 수도 있는데 이때 age의 값이 null로 저장이 된다. 하지만 int변수는 null을 받을 수 없으므로 Integer로 변수를 설정해주어야 한다.

 

 

 

defaultValue는 클라이언트에서 값을 넘기지 않았을 경우 기본으로 넣어주는 값을 설정하는 것이다.

@GetMapping("/hello/{userId}")
public String hello(
        @RequestParam(required = true, defaultValue = "guest") String username
        @RequestParam(required = false, defaultValue = 0) int age) {
					
	return "hello";
}

이 경우에는 age에 값이 넘어오지 않아도 기본 값으로 0이 들어가므로 null이 입력될일이 없기때문에 Integer로 설정할 필요가 없다.

 

만약 defualtValue를 사용하는 경우라면 값이 없을 경우 반드시 기본값으로 채우기 때문에 required가 필요가 없게된다.

 

 

 

(2) @ModelAttribute

객체 파라미터를 받을 때 사용하는 어노테이션이다. 기본 변수인 int, String, Integer등의 변수들은 전부 RequestParam을 통해 받게 되어있고 그 외에 모든 변수들은 ModelAttribute를 통해 받을 수 있다.

 

예시를 위한 객체를 하나 만들어보자

@Data
public class HelloData {

    String username;
    int age;
}

@Data는 롬복 어노테이션으로 getter, setter, 생성자, toString() 등 많은 부분을 자동으로 생성해준다.

 

컨트롤러에서 객체에 데이터를 넣으면 이렇게 직접 객체의 setter를 호출해서 값을 넣어줘야 할 것이다.

@GetMapping("/hello/model")
public String modelTest(HelloData helloData) {
    helloData.setUsername("username");
    helloData.setAge(10);

    return "ok";
}

 

@ModelAttribute를 사용하면 setter를 이용해서 값을 넣는 작업을 spring이 처리해준다.

@GetMapping("/hello/model")
public String modelTest(@ModelAttribute HelloData helloData) {

	return "ok";
}

주의해야할 점은 넣고싶은 객체의 변수명과 넘어오는 파라미터의 이름이 같아야한다. 같은이름을 spring이 찾아서 매핑해주는 것이기 때문에 이름이 다르면 자동으로 매핑되지 않는다.

 

놀랍게도 여기서 조금 더 간단하게 @ModelAttribute도 생략할 수 있다.

@GetMapping("/hello/model")
public String modelTest(HelloData helloData) {

	return "ok";
}

이렇게만 입력해도 파라미터를 자동으로 매핑시켜서 데이터를 가져온다.

 

이렇게 계속 줄이다보면 @RequestParam과 @ModelAttribute중 어느것을 사용하고있는지 모를수도 있기때문에 현명하게 선택해서 사용하면 될 것같다.

 

 

 

 

 

HTTP요청 메세지 - 단순 텍스트

쿼리파라미터와 HTMLForm으로 전송되는 데이터를 제외한 모든 데이터는 위에 설명한 것과는 다른 방식으로 데이터를 받아야 한다.

 

(1) HttpServletRequest

가장 기본적인 방법으로 직접 HttpServletRequest를 통해 데이터를 받은 후 변환해서 사용하는 방법이다.

@PostMapping("/body-v1")
public void requestBodyString(HttpServletRequest request, HttpServletResponse response) throws IOException {
    ServletInputStream inputStream = request.getInputStream();
    String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);

    response.getWriter().write("ok");
}

messageBody를 로그로 찍어보면 HttpBody에 들어간 데이터가 출력된다.

 

 

(2) InputStream

@PostMapping("/body-v2")
public void requestBodyStringV2(InputStream inputStream, Writer writer) throws IOException {
    String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);

    log.info("messageBody = {}", messageBody);

    writer.write("ok");
}

InputStream과 Writer를 바로 받아서 사용할 수도있다. 이렇게하면 코드가 조금 더 깔끔해진다.

 

 

(3) HttpEntity<>()

앞에서 우리가 직접 변환했던 작업을 스프링이 직접 해주는 방식이다. 코드가 훨씬 간결해진다.

@PostMapping("/body-v3")
public HttpEntity<String> requestBodyStringV3(HttpEntity<String> httpEntity) throws IOException {
    String messageBody = httpEntity.getBody();

    return new HttpEntity<>("ok");
}

 

 

(4) @RequestBody, @ResponseBody

모든 과정을 스프링이 해결해주고 매우 간단해졌으며 실무에 가장 많이 사용하는 방식이 @RequestBody와 @ResponseBody이다.

@ResponseBody
@PostMapping("/body-v4")
public String requestBodyStringV4(@RequestBody String messageBody) throws IOException {
	return "ok";
}

@RequestBody를 이용해서 HttpBody의 값을 바로 변수로 가져올 수 있고 @ResponseBody를 이용해서 바로 문자열로 내보낼 수 있다.

 

 

 

 

HTTP요청 메세지 - JSON

일반적인 텍스트 데이터가 아닌 json데이터는 기본적으로 ObjectMapper를 이용해서 파밍해줘야한다.

 

먼저 ObjectMapper클래스를 선언한 후 시작해보겠다.

@Slf4j
@Controller
public class RequestBodyJsonController {

    ObjectMapper objectMapper = new ObjectMapper();
    
    ....
}

 

 

(1) HttpServletRequest

역시나 기본적으로 서블릿을 이용해서 데이터를 받아온 뒤 가공해서 만드는 방법이다.

@PostMapping("/json-v1")
public void RequestBodyJsonV1(HttpServletRequest request, HttpServletResponse response) throws IOException {
    ServletInputStream inputStream = request.getInputStream();
    String message = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);

    HelloData helloData = objectMapper.readValue(message, HelloData.class);

    response.getWriter().write("ok");
}

텍스트 데이터를 받아오는 방법과 비슷하다. HTTPBody의 값을 받아온 후 문자열로 바꾸고 json형태의 문자열을 ObjectMapper를 이용해서 객체로 변환시켜 HelloData에 넣어준다.

 

 

(2) @RequestBody

이 과정을 스프링이 해결해주는 @ReqeustBody라는 것이 있다.

@ResponseBody
@PostMapping("/json-v2")
public String RequestBodyJsonV2(@RequestBody String message) throws IOException {
    HelloData helloData = objectMapper.readValue(message, HelloData.class);

    return "ok";
}

@ReuqestBody가 위의 코드에서 있었던 작업을 처리해주고 바로 문자열로 바꾼 후 ObjectMapper를 이용해서 객체로 변환하는 작업이다.

 

 

여기서 조금 더 간소화 할 수 있다.

@ResponseBody
@PostMapping("/json-v3")
public String RequestBodyJsonV3(@RequestBody HelloData helloData) throws IOException {
	return "ok";
}

문자열로 따로 받는것이 아니라 바로 객체로 넣을 수도 있다.

 

여기서 주의할 점은 @RequestBody는 생략이 안된다는 점이다. 위에서 @RequestParam과 @ModelAttribute는 어노테이션을 생략해도 스프링이 처리해준다고 했다. 하지만 여기서 @RequestBody를 생략한다면 스프링이 @ModelAttribute로 매핑한다고 인식해버린다.

 

@ModelAttribute는 파라미터를 받는 방법이었다. 그렇기 때문에 json데이터는 받을 수가 없고 결과적으로 객체에 값이 들어가지 않게된다.