[Spring] HTTP 요청 매핑 관련 어노테이션 정리

박선우·2024년 6월 19일

@RequestMapping

클라이언트 요청에 정보를 어떤 Controller가 처리할지를 매핑하기 위한 어노테이션이다.
@RequestMapping에 URL을 포함하여 해당 Controller 클래스에 명시하여 사용한다.
웹 브라우저에서 해당 URL이 호출되면 Controller 내부의 메서드가 호출된다.

@RequestMapping("/members")
public class MemberController {
		...
}

@Controller

스프링이 자동으로 스프링 빈으로 등록한다. @Controller 어노테이션 내부에 @Component 어노테이션이 포함되어 있어 컴포넌트 스캔의 대싱이 되기 때문이다.
Spring MVC에서 어노테이션 기반 컨트롤러로 인식하게 된다.

@Controller
public class MemberController {
		...
}

@RestController

스프링이 자동으로 스프링 빈으로 등록한다. @RestController 어노테이션 내부에 @Component 어노테이션이 포함되어 있어 컴포넌트 스캔의 대싱이 되기 때문이다.
Spring MVC에서 어노테이션 기반 컨트롤러로 인식하게 된다.

@RestController
@RequestMapping("/members")
public class MemberController {

}

@RestController는 @Controller과 차이점이 있다.

  • @Controller는 반환 값이 String이면 뷰 이름으로 인식된다. 따라서 뷰를 찾고 뷰가 랜더링 된다.
  • @RestController는 반환 값으로 뷰를 찾는 것이 아닌, HTTP 메시지 바디에 바로 입력한다. 따라서 실행 결과로 ok 메시지를 받을 수 있다. @ResponseBody와 관련이 있다.

@RequestMapping과 @Controller

스프링 빈 중에서 @RequestMapping이 있으면, 핸들러 매핑 조회할 때, RequestMappingHandlerMapping이 실행된다.
RequestMappingHandlerMapping은 스프링 빈 중에서 @RequestMapping 또는 @Controller가 클래스 레벨에 붙어 있는 경우에 매핑 정보로 인식하게 된다.

@Controller
@RequestMapping("/members")
public class MemberController {

}

@Componet를 통해 스프링 빈으로 등록하고, @RequestMapping을 통해 매핑 정보로 인식하게 된다.
@Controller 어노테이션 내부에는 @Component가 포함되어 있다.

@RequestMapping과 HTTP 메서드

@RequestMapping은 URL만 매칭 하는 것이 아닌, HTTP 메서드도 함께 구분할 수 있다.
예를 들어, URL이 /new-form이고, HTTP 메서드가 GET인 경우를 모두 만족해야 한다면 다음과 같이 작성해야 한다.

@RequestMapping(value = "/new-form", method = RequestMethod.GET)
public String newForm() {

}

@RequestMapping에 method 속성으로 HTTP 메서드를 지정하지 않으면, HTTP 메서드와 무관하게 GET, HEAD, POST, PUT, PATCH, DELETE 등 모든 HTTP 메서드 호출이 가능하다.

@RequestMapping(value = "/hello-basic", method = RequestMethod.GET)

@RestController
public class MappingController {

    @RequestMapping(value = "/hello-basic", method = RequestMethod.GET)
    public String helloBasic() {

        return "ok";

    }
}

만약 위 코드를 실행한 뒤, POST 요청을 하게 되면 스프링 MVC는 HTTP 405 상태 코드(Method Not Allowed)를 반환한다.

Postman으로 테스트

편리한 매핑 어노테이션

하지만, @RequestMapping 어노테이션을 각각의 매핑 메서드로 제한하는 어노테이션으로 사용하여 편리하게 이용할 수 있다.
@GetMapping, @PostMapping, @PutMapping, @DeleteMapping, @PatchMapping

@GetMapping("/mapping-get")
public String mappingGet() {
    return "ok";
}

@PostMapping("/mapping-post")
public String mappingPost() {
    return "ok";
}

@PutMapping("/mapping-put")
public String mappingPut() {
    return "ok";
}

@DeleteMapping("/mapping-delete")
public String mappingDelete() {
    return "ok";
}

@PatchMapping("/mapping-patch")
public String mappingPatch() {
    return "ok";
}

@RequestMapping 다중 경로

또한, @RequestMapping은 url을 배열로 사용하여 다중 설정이 가능하다.

@RestController
public class MappingController {

    private Logger log = LoggerFactory.getLogger(getClass());

	// 다중 설정
    @RequestMapping({"/hello-basic", "/hello-go"})
    public String helloBasic() {
        log.info("helloBasic");

        return "ok";
    }
}

위 코드의 경우 실행을 하게 되면 웹 브라우저에서 아래와 같이 두 가지 경로에서 확인이 가능하다.
http://localhost:8080/hello-basic
http://localhost:8080/hello-go

PathVariable(경로 변수) 사용

@RequsetMapping과 @GetMapping 등에는 method를 변수로 지정하여 사용이 가능하다.

@GetMapping("/mapping/{userId}")
public String mappingPath(@PathVariable("userId") String id) {
    log.info("mappingPath userId={}", id);
    
    return "ok";
}
 

만약, 변수명이 경로명과 같으면 @PathVariable의 속성을 생략할 수 있다.

@GetMapping("/mapping/{userId}")
public String mappingPath(@PathVariable String userId) {
    log.info("mappingPath userId={}", userId);
    
    return "ok";
}

http://localhost:8080/mapping/userA 와 같이 경로 변수에 해당하는 경로에 변수에 알맞은 값을 입력하여 요청을 보낼 수 있다.

// 출력
2022-10-27 22:07:27.286 INFO 22228 --- [nio-8080-exec-2] h.s.b.requestmapping.MappingController : mappingPath userId=userA

Postman을 통해 테스트가 가능하다.

PathVariable(경로 변수) 다중 매핑

@GetMapping("/mapping/users/{userId}/orders/{orderId}")
public String mappingPath(@PathVariable String userId, @PathVariable Long orderId) {
    log.info("mappingPath userId={}, orderId={}", userId, orderId);
    return "ok";
}

위 경우 http://localhost:8080/mapping/users/userA/orders/100와 같이 경로 변수를 다중으로 사용이 가능하다.
// 출력
2022-10-27 22:14:17.188 INFO 15756 --- [nio-8080-exec-3] h.s.b.requestmapping.MappingController : mappingPath userId=userA, orderId=100

특정 파라미터 조건 매핑

@GetMapping(value = "/mapping-param", params = "mode=debug")
public String mappingParam() {
    log.info("mappingParam");
    return "ok";
}

위 코드와 같이 매핑 어노테이션 속성에 params =을 추가하여 특정 조건에 대한 매핑을 수행할 수 있다.

특정 헤더 조건 매핑

@GetMapping(value = "/mapping-header", headers = "mode=debug")
public String mappingHeader() {
    log.info("mappingHeader");
    return "ok";
}

위 코드와 같이 매핑 어노테이션 속성에 headers =을 추가하여 헤더 조건에 대한 매핑을 수행할 수 있다.

http://localhost:8080/mapping-header에 그냥 접속하면 404 상태 코드를 반환한다.

해당 경로에 접속하기 위해서는 아래 header 정보에 mode를 추가하여 값으로 debug로 지정해주어야 접속이 가능해진다.

특정 헤더 조건 역시, headers에는 “mode”, “!mode”, “mode≠debug 등과 같이 여러 방식의 조건을 사용할 수 있다.

미디어 타입 조건 매핑

consume
consumes를 통해 HTTP 요청 시 미디어 타입 조건도 매핑을 할 수 있다.
Content-Type에 따라 분리하여 매핑할 수 있다.

@PostMapping(value = "/mapping-consume", consumes = "application/json")
public String mappingConsumes() {
    log.info("mappingConsumes");
    return "ok";
}

위 코드의 경우 헤더의 Content-Type이 application/json인 경우에만 호출이 가능하다.

아래 이미지와 같이 헤더의 Content-Type이 조건에 맞지 않는 경우 415 상태 코드를 반환한다.

아래와 같이 Content-Type이 application/json인 경우에만 정상적으로 호출된다.

Accept, produce

헤더의 Accept 헤더를 기반의 미디어 타입으로 매핑한다.

@PostMapping(value = "/mapping-produce", produces = "text/html")
public String mappingProduces() {
    log.info("mappingProduces");
    return "ok";
}

만약, Accept 헤더가 일치하지 않으면 406 상태 코드를 반환한다.

다음과 같이 Accept가 text/html인 경우에 정상적으로 반환한다.

@RequestParam

스프링은 HTTP 요청 파라미터를 @RequestParam으로 받을 수 있다.
만약 요청 파라미터에 username과 age가 있다고 가정한다.

예전 방식

@RequestMapping("/save")
public ModelAndView save(HttpServletRequest request, HttpServletResponse response) {
String username = request.getParameter("username");
int age = Integer.parseInt(request.getParameter("age"));
...
}

@RequestParam 사용

@PostMapping("/save")
public String save(@RequestParam("username") String username, @RequestParam("age") int age, Model model) {
		...
}

HttpServletRequest과 HttpServletResponse을 파라미터로 받아 요청을 변환하고, ModelAndView로 반환하기까지의 불편한 로직이 상당 부분 해소되었다.
또한, Model 파라미터를 통해 내부 로직에 편의 기능을 제공받을 수 있다.

참고한 사이트
https://ittrue.tistory.com/235

profile
개발 관련 궁금한 점이나 새로 알게된 내용을 적는 곳

0개의 댓글