📚 특정 URL로 Request를 보내면 들어온 요청을 Controller 내부의 특정 Method와 Mapping 하기 위해 사용한다.
💡 Client로부터 요청이 왔을 때 어떤 Controller가 호출될지 Mapping하는것은 단순히 URL로 Mapping 하는것이 아니라 여러가지 요소(URL, Method 등)를 조합하여 Mapping한다.
- **@RequestMapping**
1. **Spring Boot 3.0 버전 이하**
- URL path `/example, /example**/**` 모두 허용(Mapping)한다.
2. **Spring Boot 3.0 버전 이상(현재 버전)**
- URL path `/example` 만 허용(Mapping)한다.
3. 속성값들을 설정할 때 배열 형태로 다중 설정이 가능하다
ex) `@RequestMapping**({**”/example”, “/example2”, “/example3”**})`**
4. HTTP Method POST, GET, PUT, PATCH, DELETE, HEAD 모두 허용한다
5. method 속성으로 HTTP 메서드를 지정하면 지정된것만 허용한다.
package com.example.springbasicannotation.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
// 응답 데이터를 반환한다.
@RestController
public class RequestMappingController {
// HTTP Method 는 GET만 허용한다.
@RequestMapping(value = "/v1", method = RequestMethod.GET)
public String exampleV1() {
// logic
return "this is sparta!";
}
}

localhost:8080/v1 + POST, PUT, PATCH, DELETE 의 경우
- HTTP 응답 상태코드 405(Client측 에러), Method Not Allowed Exception 반환

1. `Target(ElementType.METHOD)` Method Level에 해당 어노테이션을 적용한다 라는 의미
2. 내부적으로 **`@RequestMapping(method = RequestMethod.GET)`** 을 사용하고 있다.
- 코드예시
```java
// Post, GET, Put, Patch, Delete 모두 가능
@GetMapping(value = "/v2")
public String exampleV2() {
// logic
return "this is sparta!";
}
```
- Spring이 제공하는 Annotation들의 내부에 다 선언되어 있다.
- 대부분의 필요한 기능들이 이미 만들어져 있다. 사용 하면된다.
- @RequestMapping 보다는 직관적이고 축약된 @GetMapping, @PostMapping 형식을 일반적으로 사용한다.
- 실행 결과

- **@PostMapping, @PutMapping, @DeleteMapping, @PatchMapping**
- 모두 위의 @GetMapping과 같은 구조를 가지고 있다.




그럼 실제로는 위 어노테이션들만 사용하고 @RequestMapping은 사용하지 않나요?

- @PostMapping, @PutMapping, @DeleteMapping, @PatchMapping의 Target은 Method Level 이다.
- 반면에 @RequestMapping의 Target은 class, method 레벨에 적용이 가능하다.
- Restful API의 계층 구조
ex) `users/{userId}, category/{categoryId}/product/{productId}`
- prefix로 선언할 URL을 class 레벨에 적용하는 것에 주로 사용된다.
- 코드 예시
@RequestMapping("/prefix")
@RestController
public class RequestMappingController {
// Post, GET, Put, Patch, Delete 모두 가능
@GetMapping(value = "/v3")
public String exampleV3() {
// logic
return "this is sparta!";
}
}

HTTP 특성 중 하나인 비연결성을 극복하여 데이터를 전달하기 위한 방법 중 하나이다. URL로 전달된 값을 파라미터로 받아오는 역할을 수행한다.
경로 변수를 중괄호에 둘러싸인 값으로 사용할 수 있다.
ex) user/{id}
기본적으로 @PathVariable로 설정된 경로 변수는 반드시 값을 가져야 하며 값이 없으면 응답 상태코드 404 Not Found Error가 발생한다.
최근 Restful API를 설계하는 것이 API의 기준이 되며 해당 어노테이션의 사용 빈도가 높아졌다.
Restful API
REST API URI Naming Conventions and Best Practices
Restful API 설계 예시
posts/{postId}/commentsposts/{postId}/commentsposts/{postId}/comments/{commentId}posts/{postId}/comments/{commentId}posts/{postId}/comments/{commentId}@PathVariable 규칙
@RequestMapping("/posts")
@RestController
public class PathVariableController {
// postId로 된 post 단건 조회
@GetMapping("/{postId}")
public String pathVariableV1(@PathVariable("postId") Long data) {
// logic
String result = "PathvariableV1 결과입니다 : " + data;
return result;
}
}

@RequestMapping("/posts")
@RestController
public class PathVariableController {
// 변수명과 같다면 속성값 생략가능
@GetMapping("/{postId}")
public String pathVariableV2(@PathVariable Long postId) {
// logic
String result = "PathvariableV2 결과입니다 : " + postId;
return result;
}
}

@RestController
public class PathVariableController {
@GetMapping("/{postId}/comments/{commentId}")
public String pathVariableV3(
@PathVariable Long postId,
@PathVariable Long commentId
) {
// logic
String result = "PathvariableV3 결과입니다 postId : " + postId + "commentsId : " + commentId;
return result;
}
}

@RequestMapping("/posts/{postId}")
@RestController
public class PathVariableController {
@GetMapping("/comments/{commentId}")
public String pathVariableV4(
@PathVariable Long postId,
@PathVariable Long commentId
) {
// logic
String result = "PathvariableV4 결과입니다 postId : " + postId + "commentsId : " + commentId;
return result;
}
}

속성 설정을 통하여 특정 헤더, 특정 파라미터와 Mapping 할 수 있다.
package com.example.springbasicannotation.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ParameterController {
// parms 속성값 추가
@GetMapping(value = "/users", params = "gender=man")
public String params() {
// logic
String result = "params API가 호출 되었습니다.";
return result;
}
}
- 실제 URL GET `http://localhost:8080/users**?gender=man**` 파라미터가 있어야 호출된다.
- 실행결과

- 파라미터가 없다면?

- 400 Bad Request 클라이언트 측 에러
- **속성 작성 규칙**
1. `params = "gender"`
- params의 key값은 커스텀이 가능하다
- value는 없어도 된다.
2. `params = "!gender"`
- gender가 없어야 한다.
3. `params = "gender=man"`
- gender=man 이어야 한다.
4. `params = "gender!=man"`
- params의 value값이 man가 아니여야 한다.
5. `params = {"gender=man", "gender=woman"}`
- 배열로 속성 값을 여러 개 설정이 가능하다.
@RestController
public class ParameterController {
// headers 속성값 추가
@PostMapping(value = "/users", headers = "Content-Type=application/json")
public String headers() {
// logic
String result = "headers API가 호출 되었습니다.";
return result;
}
}
- Postman → Body → raw → JSON

- Postman → Headers → hidden


- HTTP Header를 사용하기 때문에 Postman으로 테스트 해야 한다.
ex) key=Content-Type / value=application/json
- 실행결과

- 속성 작성 규칙은 위 params 속성 값의 규칙과 같다.
@RestController
public class ParameterController {
// consumes 속성값 추가
@PostMapping(value = "/users", consumes = "application/json") // MediaType.APPLICATION_JSON_VALUE
public String consumes() {
// logic
String result = "consumes API가 호출 되었습니다.";
return result;
}
}
- consumes 속성 value값으로는 이미 Spring에서 제공되는 Enum인 `MediaType.APPLICATION_JSON_VALUE` 형태로 사용한다.

- Postman → Body → raw → JSON

- Postman → Headers → Content-Type → Value

- 파라미터가 없거나 다르다면?

- HTTP 상태코드 405 Unsupported Media Type Exception 발생
- 속성 작성 방법
1. `consumes=”application/json”`
- application/json 미디어 타입 허용
2. `consumes=”!application/json”`
- application/json 제외 미디어 타입 허용
3. `consumes=”application/*”`
- application/ 으로 시작하는 모든 미디어 타입 허용
4. `consumes=”*\/*”`
- 모두 허용
@RestController
public class ParameterController {
// produces 속성값 추가
@GetMapping(value = "/users", produces = "text/plain")
public String produces() {
// logic
String result = "text/plain 데이터 응답";
return result;
}
}
- HTTP 요청 Accept Header에 Media Type이 있어야한다.

- `**/**` : 전체 Media Type 허용
- 실행결과

consumes 속성 사용법과 같다.
위에 나온 모든 MediaType은 Spring이 제공하는 Enum을 사용하면 된다.

ex) produces = “application.json"→ produces = MediaType.APPLICATION_JSON_VALUE
📚 어노테이션 기반 Spring의 Controller는 다양한 파라미터를 쉽게 사용할 수 있도록 지원한다.
- **@Controller의 사용 가능한 파라미터 목록 - 공식 문서**
[Method Arguments :: Spring Framework](https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-controller/ann-methods/arguments.html)
- **@Controller의 사용 가능한 Response 값 목록 - 공식 문서**
[Return Values :: Spring Framework](https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-controller/ann-methods/return-types.html)
- **HTTP 헤더 조회**
- Spring에서 요청 Header에 쉽게 접근할 수 있다.
- HttpServletRequest와 같이 파라미터로 다룰 수 있다.
- **Controller 예시**
<// 로깅
@Slf4j
@RestController
public class RequestHeaderController {
@GetMapping("/request/headers")
public String headers(
HttpServletRequest request, // Servlet에서 사용한것과 같음
HttpServletResponse response, // Servlet에서 사용한것과 같음
@RequestHeader MultiValueMap<String, String> headerMap,
@RequestHeader("host") String host,
@CookieValue(value = "cookie", required = false) String cookie,
HttpMethod httpMethod,
Locale locale
) {
// Servlet
log.info("request={}", request);
log.info("response={}", response);
// @RequestHeader
log.info("headerMap={}", headerMap);
log.info("host={}", host);
// @CookieValue
log.info("cookie={}", cookie);
// HttpMethod
log.info("httpMethod={}", httpMethod);
// Locale
log.info("Locale={}", locale);
return "success";
}
}
- **Postman API 호출**

- **Log 출력 결과**

1. request
- HttpServletRequest 객체 주소 값
2. response
- HttpServletRequest 객체 주소 값
3. headerMap :
hashMap={
user-agent=[PostmanRuntime/7.35.0],
accept=[*/*],
postman-token=[5f324c1c-7902-4750-9e01-2c4d093e8ad6],
host=[localhost:8080],
accept-encoding=[gzip, deflate, br],
connection=[keep-alive]
}
4. host
- host 정보
5. cookie
- Header의 Cookie 값
6. httpMethod
- 호출에 사용한 HttpMethod
7. Locale
- 위치 정보를 나타내는 헤더
- 우선순위가 존재한다.
MultiValueMap
💡 Map과 유사하게 Key, Value 형식으로 구현되어 있지만 하나의 Key가 여러 Value를 가질 수 있다 HTTP Header, Reqeust Parameter와 같이 하나의 Key에 여러 값을 받을 때 사용한다.
ex) key1=value1&key1=value2
예시코드
MultiValueMap<String, String> linkedMultiValuemap = new LinkedMultiValueMap();
// key1에 value1 저장
linkedMultiValuemap.add("key1", "value1");
// key1에 value2 저장
linkedMultiValuemap.add("key1", "value2");
// key1에 저장된 모든 value get
List<String> values = linkedMultiValuemap.get("key1");