이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.

  • Spring MVC는 어떤 조건으로 요청을 handler method에 매핑하는가?
  • @GetMapping, @PostMapping@RequestMapping과 어떤 관계인가?
  • 404, 405, 415, 406은 요청 매핑 관점에서 어떻게 나누어 볼 수 있는가?

개요

Spring MVC 공식 문서는 annotated controller가 @RequestMapping과 그 shortcut 애너테이션으로 요청을 handler method에 매핑한다고 설명한다. 매핑 조건은 path뿐 아니라 HTTP method, params, headers, consumes, produces를 포함할 수 있다. @GetMapping, @PostMapping 같은 애너테이션은 의도를 좁힌 composed annotation이다.

요청 바인딩을 이해하려면 먼저 “어떤 메서드가 선택되는가”를 알아야 한다. 파라미터 바인딩은 handler method가 선택된 다음 일어난다.

원리

@RestController
@RequestMapping("/api/members")
public class MemberController {
 
    @GetMapping("/{id}")
    public MemberResponse find(@PathVariable Long id) {
        return memberService.find(id);
    }
 
    @PostMapping(consumes = "application/json")
    public MemberResponse create(@RequestBody CreateMemberRequest request) {
        return memberService.create(request);
    }
}

@RequestMapping("/api/members")는 클래스 레벨 공통 경로이고, @GetMapping("/{id}"), @PostMapping은 method 레벨 조건이다. Spring은 이 조건들을 합쳐 요청과 맞는 handler method를 찾는다.

Mapping 조건

주요 조건은 다음과 같다.

  • path: /members/{id}
  • HTTP method: GET, POST, PUT, DELETE
  • params: 특정 query parameter 존재 여부
  • headers: 특정 header 조건
  • consumes: 요청 body media type
  • produces: 응답으로 만들 수 있는 media type

이 조건을 구분하면 HTTP 오류도 더 잘 보인다. path가 맞지 않으면 404, path는 맞지만 method가 다르면 405, 요청 body media type이 맞지 않으면 415, 응답 media type 협상이 실패하면 406으로 볼 수 있다.

중요한 점은 “URL이 같다”만으로 handler가 선택되는 것이 아니라는 점이다. path는 맞아도 consumesapplication/json인데 form 요청이 들어오면 다른 결과가 나오고, producesapplication/json인데 클라이언트가 받을 수 없다고 선언하면 응답 협상에서 실패할 수 있다.

실전 팁

  • URL이 맞는지보다 path, method, consumes, produces가 모두 맞는지 확인한다.
  • REST API는 resource path와 HTTP method 조합으로 의미를 표현한다.
  • @RequestMapping 하나에 모든 method를 열어두기보다 @GetMapping, @PostMapping 등으로 의도를 좁힌다.
  • 같은 메서드에 여러 mapping 애너테이션을 겹쳐 붙여 모호하게 만들지 않는다.
  • class-level mapping과 method-level mapping이 합쳐진 최종 path를 확인한다.
  • wildcard나 중복 path가 많아지면 mapping 충돌과 디버깅 비용이 커진다.

위험 신호!

  • 같은 path에 비슷한 조건의 handler method가 여러 개 있다.
  • GET 요청에 body를 기대하는 API를 설계한다.
  • consumes/produces 조건 없이 JSON API와 form 요청을 섞는다.
  • path만 보고 mapping 성공 여부를 판단하고 params, headers, consumes, produces 조건을 확인하지 않는다.
  • 405를 Controller 내부 로직 문제로 디버깅한다.
  • URL path에 action 동사를 과도하게 넣고 HTTP method 의미를 무시한다.

확인 질문

확인 질문

  • @GetMapping은 무엇의 축약형인가?
    • GET method 조건을 가진 @RequestMapping의 축약형이다.
  • 415 Unsupported Media Type은 주로 어떤 조건과 관련되는가?
    • 요청 body의 Content-Type과 handler의 consumes 조건 또는 converter 선택과 관련된다.
  • handler method 선택은 파라미터 바인딩보다 먼저인가?
    • 먼저다. 어떤 method를 실행할지 결정된 뒤 그 method의 인자를 바인딩한다.

참고 문서