스프링 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 |
댓글