Spring HTTP 헤더 및 요청 매개변수를 검색하는 방법


1. @Controller에서 헤더 정보 가져오기

헤더 정보가 필요한 경우가 많은데 Spring @Controller는 헤더 뿐만 아니라 파라미터를 통해 많은 정보를 가져올 수 있다.
사실 자주 사용하는 것만 기억하고 가끔 사용하는 것은 찾아본다.
아래의 공식 매뉴얼을 참조하십시오.

@RequestMapping("/headers")
public String headers(
        HttpServletRequest reqeust,
        HttpServletResponse response,
        HttpMethod httpMethod,  // method 정보
        Locale locale,  // 위치 정보
        @RequestHeader MultiValueMap<String, String> headerMap,  // 모든 헤더 정보
        @RequestHeader("host") String host,  // 개별 헤더 정보
        @CookieValue(value = "myCookie", required = false) String cookie  // 개별 쿠키 정보
) {
    return "파라미터로 여러가지 헤더 정보를 받을 수 있습니다.
"; }

* MultiValueMap: Map과 비슷하지만 하나의 키에 대해 여러 값을 받을 수 있다.
하나의 키로 헤더에 여러 값을 넣을 수 있기 때문에 받을 때 MultiValueMap을 사용하는 것이 좋다.

MultiValueMap<String, String> map = new LinkedMultiValueMap();
map.add("keyA", "value1");
map.add("keyA", "value2");

// (value1, value2)
List<String> values = map.get("keyA");

– @Conroller에 사용 가능한 매개변수 목록 공식 매뉴얼

서블릿 스택의 웹

Spring Web MVC는 Servlet API를 기반으로 구축된 최초의 웹 프레임워크이며 처음부터 Spring 프레임워크에 포함되었습니다.
“Spring Web MVC”라는 공식 명칭은 소스 모듈(spring-webmvc)의 이름에서 유래했지만 더 일반적입니다.

docs.spring.io

– @Conroller 공식 매뉴얼에 대한 사용 가능한 응답 값 목록

서블릿 스택의 웹

Spring Web MVC는 Servlet API를 기반으로 구축된 최초의 웹 프레임워크이며 처음부터 Spring 프레임워크에 포함되었습니다.
“Spring Web MVC”라는 공식 명칭은 소스 모듈(spring-webmvc)의 이름에서 유래했지만 더 일반적입니다.

docs.spring.io

– 아래 코드는 Lombok의 @Slf4j 사용 시 자동으로 생성됩니다.

private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(getClass.class); // 해당 클래스명

2. 요청 매개변수

쿼리 매개 변수가 Get 메서드로 전달되거나 HTML 양식 데이터가 Post 메서드로 전달될 때 매개 변수를 검색하는 방법을 살펴보겠습니다.
얼핏 보면 이해하기 쉽지만 시간이 지나면 작은 부분들이 혼란스러워집니다.
(나…)

2-1. @RequestParam

– 파라미터명과 변수명을 다르게 사용하는 경우

public void foo(
        @RequestParam("username") String memberName,
        @RequestParam("age") int memberAge
) {}

– 파라미터 이름은 생략 가능하나, 파라미터 이름과 변수 이름은 동일해야 합니다.

public void foo(
        @RequestParam String username,
        @RequestParam int age
) {}

– String, int, Integer 등 단순 타입의 경우 @RequestParam 어노테이션도 생략 가능하다.

– 단, Spring이 생소한 팀원을 고려한다면 명시적으로 포함시키는 것을 권장한다.

public String foo(String memberName, int memberAge) {}

– 필수 파라미터를 옵션으로 입력할 수 있습니다.
기본값은 true입니다.

– ‘/request-param?username=”과 같이 매개변수 이름만 있고 값이 없으면 빈 문자가 전달됩니다.

– 주의!
프리미티브 타입에 null을 포함하지 않도록 주의하고, 래퍼 클래스를 사용하거나 필요한 경우 defaultValue를 삽입합니다.

public void foo(
        @RequestParam(required = true) String username, // 생략하면 true
        @RequestParam(required = false) Integer age   // int 타입은 null이 오면 500에러
) {}
public void foo(
        @RequestParam(defaultValue="guest") String username,
        @RequestParam(defaultValue="-1") int age
) {}

– defaultValue가 있는 경우에는 값이 있든 없든 기본 값이 포함되어 있기 때문에 요구조건은 의미가 없다.

– 참고로 위에서 언급한 것처럼 username을 빈 문자로 입력해도 defaultValue 설정 값이 입력된다.

2-2. @RequestParam을 맵으로 가져오기

public void foo(@RequestParam Map<String, Object> paramMap) {
    paramMap.get("key1");
    paramMap.get("key2");
}

– MultiValueMap으로 쿼리할 수도 있습니다.
(다음과 같이 들어오는 경우: “?userId=1&userId=2”)

2-3. @ModelAttribute

– @RequestParam으로 받을 수도 있지만 @ModelAttribute 주석을 사용하여 모델 클래스에 직접 바인딩할 수도 있습니다.

@Data
private class User {
    private String name;
    private int age;
}

// '/foo?name=keesun&age=20' 요청이 들어오면
public void foo(@ModelAttribute User user) {
    // user.name, user.age 바인딩
}

– @ModelAttribute도 생략 가능

// '/foo?name=keesun&age=20' 요청이 들어오면
public void foo(User user) {
    // user.name, user.age 바인딩
}

– Spring은 생략 시 다음과 같은 규칙을 적용합니다.

– String, int, Integer와 같은 단순 유형 = “@RequestParam

– 나머지 = “@ModelAttribute” (HttpServeltRequest와 같이 인수 해결자로 지정된 유형 제외)

3. HTTP 요청 메시지

요청 본문에 포함된 데이터를 검색할 때 @RequestParam 또는 @ModelAttribute를 사용하지 마십시오. 본문을 직접 검색할 때 StreamUtils.copyToString()과 같이 HttpServletRequest.getInputStream()을 사용하여 스트림 데이터를 가져와 직접 변환할 수 있습니다.
그러나 Spring은 HttpEntity<> 또는 @RequestBody를 통해 이 번거로운 프로세스를 쉽게 검색할 수 있도록 도와줍니다.

– 입력 스트림직접 수령하는 경우

@PostMapping("/request-body-string-v2")
public void requestBodyString(InputStream inputStream, Writer responseWriter)
throws IOException {
	String messageBody = StreamUtils.copyToString(inputStream,
	StandardCharsets.UTF_8);
 	log.info("messageBody={}", messageBody);
 	responseWriter.write("ok");
}

– Http엔티티 수신되면 자동으로 변환됩니다.
(내부적으로 HttpMessageConverter라는 변환기 기능을 사용합니다.
)

– 헤더 및 본문 정보를 편리하게 검색할 수 있습니다.

– 메시지 본문 정보 직접 반환

– HttpEntity를 상속받은 RequestEntity와 ResponseEntity 객체도 동일한 기능을 제공한다.

@PostMapping("/request-body-string-v3")
public HttpEntity<String> requestBodyString(HttpEntity<String> httpEntity) {
	String messageBody = httpEntity.getBody();
	log.info("messageBody={}", messageBody);
	return new HttpEntity<>("ok");
}

– @요청 본문

– 참고로 헤더 정보가 필요한 경우 HttpEntity 또는 @RequestHeader를 사용할 수 있습니다.

– 이 메시지 본문을 직접 조회하는 기능은 요청 파라미터를 조회하는 @RequestParam 및 @ModelAttribute와는 아무런 관련이 없습니다.

– 직접 생성한 오브젝트를 지정할 수 있습니다.
텍스트뿐만 아니라 JSON도 객체로 변환합니다.

– @RequestBody를 생략하면 @ModelAttribute로 동작하므로 생략할 수 없다.

@ResponseBody
@PostMapping("/request-body-string-v4")
public String requestBodyString(@RequestBody String messageBody) {
	log.info("messageBody={}", messageBody);
	return "ok";
}

요청 매개변수와 HTTP 메시지 본문

– 요청 매개변수 검색 기능: @RequestParam , @ModelAttribute
– HTTP 메시지 본문을 직접 쿼리하는 기능: @RequestBody

– @ResponseBody
@ResponseBody를 사용하면 HTTP 메시지 본문에 직접 응답 결과를 전달할 수 있습니다.
물론 이 경우 뷰는 사용하지 않는다.

(참조)

Spring MVC Part 1 – 백엔드 웹 개발 핵심 기술 – 김영한