스프링 MVC는 요청이 들어오면 어떤 컨트롤러에 매핑하나요?
안녕하세요.
스프링 MVC에서 요청이 왔을 때, 어떤 컨트롤러가 매핑되는지 알아봅시다.
요청 매핑
http://localhost:8080/mapping 으로 들어온 요청을 처리하는 컨트롤러는 아래 코드처럼 만듭니다.
@Slf4j
@RestController
public class MappingController {
@RequestMapping("/mapping")
public String mapping(){
log.info("mapping");
return "ok";
}
}
첫 번째 줄의 @Slf4j는 로그를 찍기 위한 어노테이션으로, 요청 매핑과는 무관합니다.
두 번째 줄의 @RestController와 다섯 번째 줄의 @RequestMapping이 요청 매핑과 관련된 어노테이션 입니다.
클래스 레벨에 붙이는 @RestController는 @Controller와 @ResponseBody가 합쳐진 어노테이션입니다.
@Controller 어노테이션이 붙으면 해당 클래스가 컨트롤러임을 스프링에 알리며, 스프링 빈에 어노테이션 기반 컨트롤러로 등록됩니다.
@ResponseBody는 원래는 메소드 레벨에 붙이는 어노테이션으로, return된 String이 HTTP 메시지 바디에 바로 입력됩니다.
메소드 레벨에 붙은 @RequestMapping은 단어 그대로 요청을 매핑하기 위한 어노테이션으로, 괄호 속의 URL과 같은 요청이 들어오면 처리합니다.
예를 들어서 위의 메소드처럼 "/mapping" 경로를 받는 메소드는 http://localhost:8080/mapping으로 들어온 모든 요청을 받습니다.
들어가 보면 아래 사진처럼 ok가 HTTP 바디에 잘 입력된 것을 확인할 수 있습니다.
그러나 실제로는 @RequestMapping 어노테이션을 잘 사용하지 않습니다.
왜냐하면 @RequestMapping은 모든 HTTP 메소드를 허용하기 때문입니다.
보통 웹을 만들 때 HTTP 메소드를 나눠서 만들기 때문에, 특정 메소드 요청만 허용하는 방식을 많이 사용합니다.
특정 HTTP 메소드를 매핑하는 방식은 2가지가 있습니다.
// 방법1
@RequestMapping(value="/mapping-get-v1", method={RequestMethod.GET, RequestMethod.PUT})
public String mappingGetV1(){
log.info("mapping-get-v1");
return "ok";
}
// 방법2
@GetMapping("/mapping-get-v2")
public String mappingGetV2(){
log.info("mapping-get-v2");
return "ok";
}
첫 번째 방법은 @RequestMapping에 method 속성을 추가해서 HTTP 메소드를 지정하는 방법이고, 두 번째 방법은 @GetMapping, @PostMapping 등 특정 HTTP 메소드를 명시한 어노테이션을 사용하는 방법입니다.
두 번째 방법이 더 편리하고 직관적이므로 많이 사용됩니다.
그러나 위의 예시 코드처럼 두 개 이상의 HTTP 메소드를 매핑하고 싶다면 첫 번째 방법으로 method를 나열해서 사용해야 합니다.
PathVariable(경로 변수)
다음으로 경로 변수를 사용해서 매핑하는 방법을 알아보겠습니다.
경로 변수는 단어 그대로 요청 경로에 변수가 있는 것입니다.
예를 들어서 /mapping/userA, /mapping/userB처럼 사용자의 이름을 변수로 갖는 요청 경로는 어떻게 처리할까요?
중괄호를 사용해서 /mapping/{userId}로 사용하면 됩니다.
예제 코드를 봅시다.
@GetMapping("/mapping/{userId}")
public String mappingPathV1(@PathVariable("userId") String data){
log.info("mapping userId={}",data);
return "ok";
}
@GetMapping에서 경로를 받을 때 경로 변수는 중괄호로 나타냅니다.
그리고 메소드의 파라미터로 @PathVariable("pathVariableName") String parameterName 코드로 데이터를 받습니다.
예를 들어서 http://localhost:8080/mapping/userA로 요청이 들어오면 data 변수의 값은 userA입니다.
더 편리하게 경로 변수를 받는 방법도 있습니다.
@GetMapping("/mapping/users/{userId}/orders/{orderId}")
public String mappingPathV2(@PathVariable String userId, @PathVariable Long orderId){
log.info("mapping Path userId={}, orderId={}", userId, orderId);
return "ok";
}
@PathVariable의 속성값이 사라졌습니다. 경로 변수 중괄호에서 지정한 변수 이름과 사용할 변수 이름이 같으면 @PathVariable의 속성 괄호를 생략할 수 있습니다.
예를 들어서 /mapping/users/userA/orders/34로 요청이 들어오면 userId는 userA로, orderId는 34로 저장됩니다.
참고로 orderId를 Long으로 받기 때문에, /mapping/users/userA/orders/OrderA와 같이 숫자가 아닌 값이 들어오면
Failed to convert value of type 'java.lang.String' to required type 'java.lang.Long' 에러가 납니다.
조건 매핑
다음으로 특정 조건이 만족할 때 매핑되는 컨트롤러를 알아봅시다.
1. 특정 파라미터 조건 매핑
특정 파라미터가 있을 때만 매핑되도록 조건을 추가할 수 있습니다.
예를 들어서 쿼리 파라미터 "mode=debug"가 들어와야 매핑되는 컨트롤러는 아래와 같습니다.
@GetMapping(value="/mapping-param", params="mode=debug")
public String mappingParam(@RequestParam String mode){
log.info("mapping params, mode={}",mode);
return "ok";
}
/**
* 파라미터로 추가 매핑
* params="mode",
* params="!mode"
* params="mode=debug"
* params="mode!=debug" (! = )
* params = {"mode=debug","data=good"}
*/
위 메소드처럼 매핑 어노테이션에 params 속성으로 지정할 수 있습니다. 주석처럼 다양하게 활용할 수 있습니다.
위의 메소드는 /mapping-param?mode=debug 요청이 들어올 때만 호출됩니다.
만약 /mapping-param?mode=debugging처럼 다른 요청이 들어가면 400 Bad Request 에러가 납니다.
2. 특정 헤더 조건 매핑
다음으로 특정 헤더가 있을 때 매핑되는 컨트롤러입니다.
위에서 본 파라미터 조건 매핑과 거의 같습니다.
어노테이션의 속성으로 headers 값을 지정해야 한다는 점만 다릅니다.
코드를 같이 봅시다
@GetMapping(value="/mapping-header", headers="mode=debug")
public String mappingHeader(@RequestHeader String mode){
log.info("mapping header, mode={}",mode);
return "ok";
}
/**
* 특정 헤더로 추가 매핑
* headers="mode",
* headers="!mode"
* headers="mode=debug"
* headers="mode!=debug" (! = )
*/
헤더에 mode=debug를 추가해야 호출되고, 찾는 헤더가 없으면 매핑이 되지 않아서 404 not found 에러가 뜹니다.
3. 미디어 타입 조건 매핑: HTTP 요청 Content-Type, consume
HTTP 요청 메시지의 Content-Type 헤더는 요청 데이터의 타입 정보를 담고 있습니다.
예를 들어서 요청 메시지에서 json 데이터가 전송되면 Content-Type은 application/json입니다.
특정 Content-Type을 갖는 요청만 받고 싶을 때는 consumes를 설정합니다.
서버 입장에서 consume 할 수 있는 데이터이기 때문에 consumes를 사용하는 것 같습니다.
예시 코드를 봅시다.
@PostMapping(value="/mapping-consume", consumes={"application/json", MediaType.TEXT_PLAIN_VALUE})
public String mappingConsumes(){
log.info("mappingConsumes");
return "ok";
}
"application/json"처럼 스트링으로 지정하거나 MediaType.TEXT_PLAIN_VALUE처럼 MediaType 클래스의 상수로 지정할 수 있습니다.
위의 코드는 application/json이나 text/plain 타입의 데이터를 갖는 요청만 받습니다.
만약 다른 타입이거나 데이터가 없으면 415 Unsupported Media Type 에러가 납니다.
4. 미디어 타입 조건 매핑: HTTP 요청 Accept, produce
HTTP 요청 메시지에 Accept 헤더가 포함되어 있을 수 있습니다.
Accept는 요청한 클라이언트가 받고자 하는 데이터 타입을 의미합니다.
예를 들어서 Accept가 application/json이면 서버에서 json 데이터를 전송해야 합니다.
Accept 값을 조건으로 해서 매핑할 수도 있습니다.
produces 속성을 지정하면 되는데요. 서버에서 만드는 데이터이므로 produces인 것 같습니다.
예시 코드를 봅시다.
@PostMapping(value="/mapping-produce", produces=MediaType.APPLICATION_JSON_VALUE)
public String mappingProduces(){
log.info("mappingProduces");
return "ok";
}
위의 메소드는 accept가 application/json이면 매핑되고, text/plain이면 org.springframework.web.HttpMediaTypeNotAcceptableException 에러를 냅니다.
참고로 accept 값이 없을 때도 매핑이 됩니다. accept를 별도로 지정하지 않으면 모든 데이터 타입을 받는다는 의미이기 때문입니다. 그러나 위에서 본 consumes는 데이터가 없어서 Content-Type이 없을 때 매핑되지 않고 에러가 납니다.
이번 게시글에서는 스프링 MVC의 요청 매핑을 알아보았습니다.
다음 게시글에서는 스프링 MVC로 HTTP 요청 헤더와 데이터를 조회하는 방법을 알아보겠습니다.
인프런 '스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술' 강의를 듣고 공부하며 정리한 자료입니다.
잘못된 부분은 피드백 주시면 감사하겠습니다.
글 읽어주셔서 감사합니다 :-)
참고 자료
스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술, 섹션 6. 스프링 MVC - 기본 기능 https://www.inflearn.com/course/스프링-mvc-1
'Spring > 스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술' 카테고리의 다른 글
스프링 MVC - HTTP 응답 데이터 만들기 (0) | 2022.02.01 |
---|---|
스프링 MVC - HTTP 요청 메시지(헤더, 데이터) 조회 정리 (0) | 2022.01.31 |
스프링 MVC를 이용해서 회원 관리 웹 만들기 (0) | 2022.01.28 |
핸들러 매핑, 핸들러 어댑터, 뷰 리졸버 (0) | 2022.01.27 |
스프링 MVC 구조 (0) | 2022.01.26 |
댓글