스프링 MVC - 기본 기능1

1. 요청 매핑

MappingController

package hello.springmvc.basic.requestmapping;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.springframework.web.bind.annotation.*;

@RestController
public class MappingController {
    
    private Logger log = LoggerFactory.getLogger(getClass());
    
    /**
    * 기본 요청
    * 둘다 허용 /hello-basic, /hello-basic/
    * HTTP 메서드 모두 허용 GET, HEAD, POST, PUT, PATCH, DELETE
    */
    @RequestMapping("/hello-basic")
    public String helloBasic() {
        log.info("helloBasic");
        return "ok";
    }
}

매핑 정보

  • @RestController

    • @Controller 는 반환 값이 String 이면 뷰 이름으로 인식된다. 그래서 뷰를 찾고 뷰가 랜더링 된다.

    • @RestController 는 반환 값으로 뷰를 찾는 것이 아니라, HTTP 메시지 바디에 바로 입력한다. 따라서

      실행 결과로 ok 메세지를 받을 수 있다. @ResponseBody 와 관련이 있는데, 뒤에서 더 자세히 설명한다.

  • @RequestMapping("/hello-basic")

    • /hello-basic URL 호출이 오면 이 메서드가 실행되도록 매핑한다.

    • 대부분의 속성을 배열[] 로 제공하므로 다중 설정이 가능하다. {"/hello-basic", "/hello-go"}

HTTP 메서드

@RequestMappingmethod 속성으로 HTTP 메서드를 지정하지 않으면 HTTP 메서드와 무관하게 호출된다. 모두 허용 GET, HEAD, POST, PUT, PATCH, DELETE

HTTP 메서드 매핑

HTTP 메서드 매핑 축약

PathVariable(경로 변수) 사용

최근 HTTP API 는 다음과 같이 리소스 경로에 식별자를 넣는 스타일을 선호한다.

  • /mapping/userA

  • /users/1

  • @PathVariable 의 이름과 파라미터 이름이 같으면 생략할 수 있다.

특정 파라미터 조건 매핑

특정 헤더 조건 매핑

미디어 타입 조건 매핑 - HTTP 요청 Content-Type, consume

미디어 타입 조건 매핑 - HTTP 요청 Accept, produce

2. 요청 매핑 - API 예시

회원 관리를 HTTP API 로 만든다 생각하고 매핑을 어떻게 하는지 알아보자.

회원 관리 API

  • 회원 목록 조회: GET /users

  • 회원 등록: POST /users

  • 회원 조회: GET /users/{userId}

  • 회원 수정: PATCH /users/{userId}

  • 회원 삭제: DELETE /users/{userId}

MappingClassController

다른 예제와 HTTP API 경로가 겹칠 수 있기에 앞에 /mapping 을 붙였다.

매핑 방법을 이해했으니, 이제부터 HTTP 요청을 보내는 데이터들을 스프링 MVC 로 어떻게 조회하는지 알아보자.

3. HTTP 요청 - 기본, 헤더 조회

애노테이션 기반의 스프링 컨트롤러는 다양한 파라미터를 지원한다.

RequestHeaderController

  • HttpServletRequest

  • HttpServletResponse

  • HttpMethod : HTTP 메서드를 조회한다. org.springframework.http.HttpMethod

  • Locale : Locale 정보를 조회한다.

  • @RequestHeader MultiValueMap<String, String> headerMap

    • 모든 HTTP 헤더를 MultiValueMap 형식으로 조회한다.

  • @RequestHeader("host") String host

    • 특정 HTTP 헤더를 조회한다.

    • 속성

      • 필수 값 여부: required

      • 기본 값 속성: defaultValue

  • @CookieValue(value = "myCookie", required = false) String cookie

    • 특정 쿠키를 조회한다.

    • 속성

      • 필수 값 여부: required

      • 기본 값: defaultValue

MultiValueMap

  • MAP과 유사한데, 하나의 키에 여러 값을 받을 수 있다.

  • HTTP header, HTTP 쿼리 파라미터와 같이 하나의 키에 여러 값을 받을 때 사용한다.

    • keyA=value1&keyA=value2

4. HTTP 요청 파라미터 - 쿼리 파라미터, HTML Form

HTTP 요청 데이터 조회 - 개요

클라이언트에서 서버로 요청 데이터를 전달할 때는 주로 다음 3가지 방법을 사용한다.

  • GET - 쿼리 파라미터

    • /url?username=hello&age=20

    • 메시지 바디 없이, URL의 쿼리 파라미터에 데이터를 포함해서 전달

    • 예) 검색, 필터, 페이징등에서 많이 사용하는 방식

  • POST - HTML Form

    • content-type: application/x-www-form-urlencoded

    • 메시지 바디에 쿼리 파리미터 형식으로 전달 username=hello&age=20

    • 예) 회원 가입, 상품 주문, HTML Form 사용

  • HTTP message body에 데이터를 직접 담아서 요청

    • HTTP API에서 주로 사용, JSON, XML, TEXT

    • 데이터 형식은 주로 JSON 사용

    • POST, PUT, PATCH

요청 파라미터 - 쿼리 파라미터, HTML Form

HttpServletRequestrequest.getParameter() 를 사용하면 다음 두가지 요청 파라미터를 조회할 수 있다.

GET 쿼리 파리미터 전송 방식이든, POST HTML Form 전송 방식이든 둘다 형식이 같으므로 구분없이 조회할 수 있다.

이것을 간단히 요청 파라미터(request parameter) 조회라 한다.

GET, 쿼리 파라미터 전송

예시 http://localhost:8080/request-param?username=hello&age=20

POST, HTML Form 전송

예시

RequestParamController

request.getParamter()

여기서는 단순히 HttpServletRequest가 제공하는 방식으로 요청 파라미터를 조회했다.

5. HTTP 요청 파라미터 - @RequestParam

스프링이 제공하는 @RequestParam 을 사용하면 요청 파라미터를 매우 편리하게 사용할 수 있다.

requestParamV2

  • @RequestParam : 파라미터 이름으로 바인딩

  • @ResponseBody : View 조회를 무시하고, HTTP message body에 직접 해당 내용 입력

@RequestParam의 name(value) 속성이 파라미터 이름으로 사용

  • @RequestParam("username") String memberName

  • => request.getParameter("username")

requestParamV3

  • HTTP 파라미터 이름이 변수 이름과 같으면 @RequestParam(name="xx") 생략 가능

requestParamV4

  • String, int, Integer 등의 단순 타입이면 @RequestParam 도 생략 가능

참고

이렇게 애노테이션을 완전히 생략해도 되는데, 너무 없는 것도 약간 과하다는 주관적 생각이 있다.

@RequestParam 이 있으면 명확하게 요청 파리미터에서 데이터를 읽는 다는 것을 알 수 있다.

주의 - 스프링 부터 3.2 파라미터 이름 인식 문제

다음 예외가 발생하면 해당 내용을 참고하자.

주로 다음 두 애노테이션에서 문제가 발생한다. @RequestParam, @PathVariable

@RequestParam 관련

@PathVariable 관련(이후에 학습한다)

해결 방안1 (권장)

애노테이션에 이름을 생략하지 않고 다음과 같이 이름을 항상 적어준다. 이 방법을 권장한다.

  • @RequestParam("username") String username

  • @PathVariable("userId") String userId

해결 방안 2,3

pass ...

문제 원인

참고로 이 문제는 Build, Execution, Deployment -> Build Tools -> Gradle에서 Build and run using를 IntelliJ IDEA로 선택한 경우에만 발생한다. Gradle로 선택한 경우에는 Gradle이 컴파일 시점에 해당 옵션을 자동으로 적용해준다.

자바를 컴파일할 때 매개변수 이름을 읽을 수 있도록 남겨두어야 사용할 수 있다. 컴파일 시점에 -parameters 옵션을 사용하면 매개변수 이름을 사용할 수 있게 남겨둔다.

스프링 부트 3.2 전까지는 바이트코드를 파싱해서 매개변수 이름을 추론하려고 시도했다. 하지만 스프링 부트 3.2 부터는 이런 시도를 하지 않는다.

파라미터 필수 여부 - requestParamRequired

주의 - 파라미터 이름만 사용 /request-param-required?username=

  • 파라미터 이름만 있고 값이 없는 경우 빈문자로 통과

주의 - 기본형(primitive) 에 null 입력 /request-param

  • @RequestParam(required = false) int age

  • nullint 에 입력하는 것은 불가능(500 예외 발생)

  • 따라서 null 을 받을 수 있는 Integer로 변경하거나, 또는 다음에 나오는 defaultValue 사용

기본 값 적용 - requestParamDefault

  • 파라미터에 값이 없는 경우 defaultValue 를 사용하면 기본 값을 적용할 수 있다.

  • 이미 기본 값이 있기 때문에 required 는 의미가 없다.

  • defaultValue 는 빈 문자의 경우에도 설정한 기본 값이 적용된다.

    • /request-param-default?username=

파라미터를 Map 으로 조회하기 - requestParamMap

6. HTTP 요청 파라미터 - @ModelAttribute

실제 개발을 하다 요청 파라미터를 받아서 필요한 객체를 만들고 그 객체에 값을 넣어주어야 한다. 보통 다음과 같이 코드를 작성할 것이다.

스프링은 이 과정을 완전히 자동화해주는 @ModelAttribute 기능을 제공한다.

먼저 요청 파라미터를 바인딩 받을 객체를 만들자.

HelloData

@ModelAttribute 적용 - modelAttributeV1

마치 마법처럼 HelloData 객체가 생성되고, 요청 파라미터의 값도 모두 들어가 있다.

스프링MVC는 @ModelAttribute 가 있으면 다음을 실행한다.

  • HelloData 객체를 생성한다.

  • 요청 파라미터의 이름으로 HelloData 객체의 프로퍼티를 찾는다. 그리고 해당 프로퍼티의 setter를 호출해서 파라미터의 값을 입력(바인딩) 한다.

  • 예) 파라미터 이름이 username 이면 setUsername() 메서드를 찾아서 호출하면서 값을 입력한다.

바인딩 오류

age=abc 처럼 숫자가 들어가야 할 곳에 문자를 넣으면 BindException 이 발생한다. 이런 바인딩 오류를 처리하는 방법은 검증 부분에서 다룬다.

@ModelAttribute 생략 - modelAttributeV2

@ModelAttribute 는 생략할 수 있다. 그런데 @RequestParam 도 생략할 수 있으니 혼란이 발생할 수 있다.

스프링은 파라미터 애노테이션 생략시 다음과 같은 규칙을 적용한다.

  • String, int, Integer 같은 단순 타입 =@RequestParam

  • 나머지 = @ModelAttribute (argument resolver 로 지정해둔 타입 외)

Last updated