[Day 31 | Spring] Spring Web MVC - HTTP 요청 RequestMapping

y♡ding·2024년 11월 25일

데브코스 TIL - Spring

목록 보기
29/46

Spring Boot의 @RequestMapping은 Spring MVC에서 HTTP 요청을 특정 컨트롤러 메서드에 매핑하기 위해 사용됩니다. 웹 애플리케이션 개발에서 URL 경로와 해당 경로를 처리할 메서드를 연결하는 중요한 어노테이션입니다.


1. @RequestMapping의 기본 개념

  • 역할: HTTP 요청(예: GET, POST, PUT, DELETE 등)을 특정 컨트롤러 클래스나 메서드에 매핑합니다.
  • 적용 위치:
    • 클래스 레벨: 기본 URL 경로 설정.
    • 메서드 레벨: 세부 경로 및 HTTP 메서드 설정.

2. 주요 속성 (Attributes)

속성설명예시
value 또는 path요청 URL 경로를 지정. 단일 경로나 배열 형태로 가능.@RequestMapping("/users")
methodHTTP 메서드 지정 (GET, POST, PUT, DELETE 등). 여러 개 지정 가능.@RequestMapping(value = "/users", method = RequestMethod.GET)
params요청에 특정 쿼리 파라미터가 포함되어야 매핑되도록 설정.@RequestMapping(value = "/users", params = "active=true")
headers요청 헤더 조건에 따라 매핑.@RequestMapping(value = "/users", headers = "X-Header=abc")
consumes요청 본문(Content-Type) 조건을 지정 (JSON, XML 등).@RequestMapping(value = "/users", consumes = "application/json")
produces응답 본문(Content-Type)을 지정. 클라이언트가 기대하는 데이터 형식을 명시.@RequestMapping(value = "/users", produces = "application/json")

3. @RequestMapping과 HTTP 메서드

@RequestMapping은 HTTP 메서드를 명시적으로 지정하지 않으면 모든 메서드(GET, POST, etc.)를 처리합니다.

하지만 더 명확한 코드 작성과 유지보수를 위해 각 HTTP 메서드에 특화된 매핑 어노테이션을 사용하는 것이 일반적입니다:

어노테이션설명
@GetMappingHTTP GET 요청을 처리.
@PostMappingHTTP POST 요청을 처리.
@PutMappingHTTP PUT 요청을 처리.
@DeleteMappingHTTP DELETE 요청을 처리.
@PatchMappingHTTP PATCH 요청을 처리.

4. @RequestMapping의 활용 예제

(1) 클래스 레벨과 메서드 레벨의 매핑

클래스 레벨에서 공통 경로를 지정하고, 메서드 레벨에서 세부 경로를 설정할 수 있습니다.

@RestController
@RequestMapping("/api/users")
public class UserController {

    // GET /api/users
    @GetMapping
    public List<String> getUsers() {
        return List.of("홍길동", "박문수", "이몽룡");
    }

    // GET /api/users/{id}
    @GetMapping("/{id}")
    public String getUserById(@PathVariable Long id) {
        return "User ID: " + id;
    }

    // POST /api/users
    @PostMapping
    public String createUser(@RequestBody Map<String, String> user) {
        return "User created: " + user.get("name");
    }
}

(2) HTTP 메서드 지정

특정 HTTP 메서드만 처리하도록 설정할 수 있습니다.

@RequestMapping(value = "/products", method = RequestMethod.POST)
public String addProduct() {
    return "Product added!";
}

(3) 쿼리 파라미터 기반 매핑

요청에 특정 쿼리 파라미터가 포함된 경우에만 매핑됩니다.

@RequestMapping(value = "/search", params = "keyword")
public String search(@RequestParam String keyword) {
    return "Search result for: " + keyword;
}

(4) 헤더 기반 매핑

특정 요청 헤더가 포함된 요청만 처리합니다.

@RequestMapping(value = "/header-test", headers = "X-Special-Header=Valid")
public String headerTest() {
    return "Header matched!";
}

(5) 콘텐츠 타입 제약

  • Consumes: 요청 본문의 Content-Type이 특정 조건을 만족해야 매핑됩니다.
  • Produces: 응답의 Content-Type을 지정합니다.
@RequestMapping(value = "/json-endpoint", consumes = "application/json", produces = "application/json")
public Map<String, String> jsonEndpoint(@RequestBody Map<String, String> input) {
    input.put("status", "processed");
    return input;
}

5. @RequestMapping vs 단축 어노테이션

단축 어노테이션@RequestMapping 사용 시 동일 코드
@GetMapping("/users")@RequestMapping(value = "/users", method = RequestMethod.GET)
@PostMapping("/users")@RequestMapping(value = "/users", method = RequestMethod.POST)
@PutMapping("/users")@RequestMapping(value = "/users", method = RequestMethod.PUT)

단축 어노테이션을 사용하면 코드가 간결해지고, HTTP 메서드를 더 명확하게 표현할 수 있습니다.


6. 고급 사용 사례: URI 변수와 매개변수

(1) PathVariable

URI의 변수 값을 추출하여 메서드 매개변수로 전달합니다.

@GetMapping("/users/{id}")
public String getUser(@PathVariable("id") Long id) {
    return "User ID: " + id;
}

(2) RequestParam

쿼리 파라미터 값을 추출합니다.

@GetMapping("/search")
public String search(@RequestParam("q") String query) {
    return "Searching for: " + query;
}

(3) RequestBody

요청 본문(JSON 또는 XML)을 매핑하여 객체로 변환합니다.

@PostMapping("/users")
public String createUser(@RequestBody User user) {
    return "User created: " + user.getName();
}

7. 주요 설계 원칙

  • RESTful 설계: @RequestMapping은 RESTful API 설계에 적합하며, 리소스 경로(URL)와 HTTP 메서드를 조합해 일관된 인터페이스를 제공합니다.
  • 단일 책임 원칙: 하나의 컨트롤러 메서드는 하나의 작업만 처리하도록 설계합니다.
  • 명확성: HTTP 메서드와 경로를 명확히 정의하여 요청과 매핑 로직 간의 모호성을 줄입니다.

애스터리스크(*)의 의미

  • 단일 경로 세그먼트에 대해 매칭합니다.
  • URL의 특정 부분을 대체할 수 있는 와일드카드로 사용됩니다.
  • 한 세그먼트는 슬래시(/)로 구분된 URL의 한 조각을 의미합니다.
    • 예: /users/*/users/홍길동은 매칭되지만 /users/admin/settings은 매칭되지 않음.

(1) 단일 경로 세그먼트 매칭

  • *는 슬래시(/) 사이에 있는 하나의 경로 조각만 대체할 수 있습니다.
@RequestMapping("/files/*")
public String handleSingleWildcard() {
    return "Matches any single segment under /files/";
}

예시:

  • 매칭: /files/image, /files/document
  • 매칭되지 않음: /files/images/picture, /files/

(2) PathVariable과 함께 사용

  • *@PathVariable을 조합해 와일드카드로 추출된 경로 세그먼트를 변수로 받을 수 있습니다.
@RequestMapping("/users/*")
public String handleUserWildcard(@PathVariable String id) {
    return "User ID: " + id;
}

주의: Spring MVC에서는 *를 사용할 경우, PathVariable의 이름을 명시적으로 지정할 수 없습니다. URL의 매칭 부분을 동적으로 처리하려면 더 구체적인 패턴 매칭이 필요합니다.


(3) 다중 경로 매칭

  • *를 배열로 사용하여 다양한 경로를 한 번에 매핑할 수 있습니다.
@RequestMapping({"/users/*", "/admins/*"})
public String handleMultipleWildcards() {
    return "Matches both /users/* and /admins/*";
}

3. 매칭 규칙과 특징

(1) 정확한 세그먼트만 매칭

  • /files/* → 정확히 한 세그먼트만 대체 가능.
  • /files/*/edit → 중간 세그먼트 하나만 대체.

(2) 슬래시(/) 경계

  • *는 슬래시(/)를 포함하지 않는 경로 조각만 대체합니다.
  • /users/*/users/홍길동과 매칭되지만 /users/홍길동/프로필과는 매칭되지 않습니다.

4. ***의 차이점**

Spring MVC에서는 ***가 다른 방식으로 작동합니다.

기호의미예시
*단일 경로 세그먼트를 대체./files/*/files/image, /files/doc
**다중 경로 세그먼트를 대체. 중첩된 경로 전체를 포함./files/**/files/image/picture/doc
@RequestMapping("/files/**")
public String handleDoubleWildcard() {
    return "Matches any nested path under /files/";
}
  • * 활용 예시:
  • /files/**/files/images/picture, /files/docs/notes (모두 매칭).

5. 실제 사용 시 주의점

  1. PathVariable과의 조합

    • *는 단일 세그먼트를 대체하지만, @PathVariable로 사용하려면 명시적인 경로 지정이 필요합니다. 불명확한 매핑은 런타임 오류를 초래할 수 있습니다.
  2. 우선순위 문제

    여러 매핑이 동일한 패턴을 가지거나 유사할 경우, Spring은 보다 구체적인 경로를 우선 처리합니다.

    예: /files/image/files/*가 동시에 정의된 경우 /files/image가 먼저 매칭됩니다.

  3. 유효하지 않은 매칭

    • *는 반드시 단일 경로 세그먼트를 의미하므로, 빈 세그먼트(//)나 중첩된 세그먼트에는 매칭되지 않습니다.

6. 예제 코드: 다양한 애스터리스크 사용

@RestController
@RequestMapping("/api")
public class FileController {

    // 단일 세그먼트 매칭
    @RequestMapping("/files/*")
    public String handleSingleSegment() {
        return "Single segment matched!";
    }

    // 다중 세그먼트 매칭
    @RequestMapping("/files/**")
    public String handleNestedSegments() {
        return "Multiple segments matched!";
    }

    // PathVariable과 결합
    @RequestMapping("/users/*")
    public String handleUserWildcard(@PathVariable String id) {
        return "User ID matched with wildcard: " + id;
    }
}

0개의 댓글