Back-End (Web)/Spring

[Spring] Request Mapping

JABHACK 2024. 12. 16. 12:36

@RequestMapping

📌클라이언트의 요청 URL과 이를 처리할 컨트롤러의 클래스나 메서드를 매핑하는 데 사용됩니다.

  • 특정 URL로 Request를 보내면 들어온 요청을 Controller 내부의 특정 Method와 Mapping 하기 위해 사용한다.
  • Client로부터 요청이 왔을 때 어떤 Controller가 호출될지 Mapping하는것은 단순히 URL로 Mapping 하는것이 아니라 여러가지 요소(URL, Method 등)를 조합하여 Mapping한다.

 

@RequestMapping과 Handler Adapter의 차이

@RequestMapping Handler Adapter
HTTP 요청과 컨트롤러 메서드를 연결하는 어노테이션 DispatcherServlet과 핸들러(컨트롤러) 사이를 연결하는 구현체
메타데이터로써 동작 (어떤 요청이 어떤 메서드와 연결될지) 요청을 실행 가능한 형태로 변환하고 핸들러 실행.
개발자가 컨트롤러 메서드에 명시적으로 사용. Spring 내부에서 동작하며, 개발자가 직접 호출하지 않음.

 

주요 기능

  • 요청 URL과 매핑: 특정 URL 경로로 들어오는 HTTP 요청을 처리할 메서드나 클래스와 연결.
  • HTTP 메서드 지정: GET, POST, PUT, DELETE 등 특정 HTTP 메서드에만 반응하도록 설정.
  • 요청 조건 추가: 요청 헤더, 파라미터 등 특정 조건에 맞는 요청만 처리 가능.

 

요청 종류

  • @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”**})**
    1. HTTP Method POST, GET, PUT, PATCH, DELETE, HEAD 모두 허용한다
    2. 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!";
    }

}

 

실행결과

 

  • 만약, 속성으로 설정된 HTTP Method로 요청이 오지 않는다면?
    • localhost:8080/v1 + POST, PUT, PATCH, DELETE 의 경우

  • HTTP 응답 상태코드 405(Client측 에러), Method Not Allowed Exception 반환

 

  • @GetMapping

  1. Target(ElementType.METHOD) Method Level에 해당 어노테이션을 적용한다 라는 의미
  2. 내부적으로 @RequestMapping(method = RequestMethod.GET) 을 사용하고 있다.

 

코드예시

// 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의 계층 구조

메소드 레벨(Method Level)이란?

메소드 레벨클래스의 메서드에 적용할 수 있는 어노테이션의 사용 범위를 의미합니다.
즉, 메서드 레벨의 어노테이션은 클래스 내부의 특정 메서드에만 적용되어 동작합니다.

 

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!";
	}

}

 

 

 

 

@PathVariable

📌 URL 경로에 포함된 변수를 메서드의 매개변수로 전달하기 위해 사용됩니다. 주로 RESTful 웹 서비스에서 동적인 요청 처리를 위해 사용됩니다.

  • HTTP 특성 중 하나인 비연결성을 극복하여 데이터를 전달하기 위한 방법 중 하나이다. URL로 전달된 값을 파라미터로 받아오는 역할을 수행한다.

 

동작 원리

  • URL 경로에 **변수(placeholder)**를 정의하고, 해당 값을 컨트롤러 메서드의 매개변수로 전달합니다.
  • @PathVariable은 요청 경로에서 변수 값을 추출하여 메서드 파라미터에 바인딩합니다.

 

  • @PathVariable
    1. 경로 변수를 중괄호에 둘러싸인 값으로 사용할 수 있다.
    ex) user/{id}
    1. 기본적으로 @PathVariable로 설정된 경로 변수는 반드시 값을 가져야 하며 값이 없으면 응답 상태코드 404 Not Found Error가 발생한다.
    2. 최근 Restful API를 설계하는 것이 API의 기준이 되며 해당 어노테이션의 사용 빈도가 높아졌다.
더보기

Restful API를 설계하게 되면 URL path 만으로 어떤 Resource을 사용하는지, HTTP Method 만으로 어떤 기능이 동작되는지 쉽게 알아볼 수 있다.

 

Restful API

https://restfulapi.net/resource-naming/

 

  • Create - POST
  • Read - GET
  • Update - PUT, PATCH
  • Delete - DELETE
  • Restful API 설계 예시
    • postId글의 comment 댓글 작성
      • POST + posts/{postId}/comments
    • postId글의 comment 댓글 전체 조회
      • GET + posts/{postId}/comments
    • postId글의 commentId 댓글 단 건 조회
      • GET + posts/{postId}/comments/{commentId}
    • postId글의 commentId 댓글 수정
      • PUT + posts/{postId}/comments/{commentId}
    • postId글의 commentId 댓글 삭제
      • DELETE + posts/{postId}/comments/{commentId}

 

  • @PathVariable 규칙
    1. 파라미터 변수명과 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;
	}
	
}

 

 

 

2. @PathVariable 다중 사용 가능

 

@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 할 수 있다.

 

Parameter 추가 매핑

  • 특정 파라미터와 매핑하는 방법
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"}
      • 배열로 속성 값을 여러 개 설정이 가능하다.

 

특정 Header 매핑

  • 특정 Header와 매핑하는 방법
@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 속성 값의 규칙과 같다.

 

MediaType 매핑, consume(수용)

  • HTTP Header Content-Type(요청)과 매핑 된다.
@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 → Body → raw → JSON

 

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

  • HTTP 상태코드 405 Unsupported Media Type Exception 발생
  • 속성 작성 방법
    1. consumes=”application/json”
      • application/json 미디어 타입 허용
    2. consumes=”!application/json”
      • application/json 제외 미디어 타입 허용
    3. consumes=”application/*”
      • application/ 으로 시작하는 모든 미디어 타입 허용
    4. consumes=”*\\/*”
      • 모두 허용

 

MediaType 매핑 produces(제공)

  • 요청 헤더의 Accept 값에 따라서 produces 하는 값이 변한다.
@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이 지원하는 Parameter

📌 어노테이션 기반 Spring의 Controller는 다양한 파라미터를 쉽게 사용할 수 있도록 지원한다.

 

  • 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]
}

 

  1. host
    • host 정보
  2. cookie
    • Header의 Cookie 값
  3. httpMethod
    • 호출에 사용한 HttpMethod
  4. 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");

 

'Back-End (Web) > Spring' 카테고리의 다른 글

[Spring] HTTP Message Body & TEXT  (0) 2024.12.18
[Spring] @RequestParam & @ModelAttribute  (0) 2024.12.17
[Spring] Spring Annotation  (1) 2024.12.15
[Spring] Spring MVC 패턴  (1) 2024.12.14
[Spring] Spring Boot  (0) 2024.12.10