yeon's blog

[Spring] HTTP 요청 파라미터 - 쿼리 파라미터, HTML Form, @RequestParam, @ModelAttribute 본문

Spring/Spring 개념

[Spring] HTTP 요청 파라미터 - 쿼리 파라미터, HTML Form, @RequestParam, @ModelAttribute

yeonii 2024. 1. 14. 14:32

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

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

 

1. GET - 쿼리 파라미터

  • ?username=hello&age=20
  • 메시지 바디 없이, URL의 쿼리 파라미터에 데이터를 포함하여 전달
  • 검색, 필터, 페이징 등에서 많이 사용하는 방식

2. POST - HTML Form

  • Content-Type: application/x-www-form-urlencoded
  • 메시지 바디에 쿼리 파라미터 형식으로 전달: username=hello&age=20
  • HTML Form 사용

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

  • HTTP API에서 주로 사용 JSON, XML, TEXT 등
  • 데이터 형식은 주로 JSON 사용
  • POST, PUT, PATCH 에 이용

 

RequestParamController 전체코드

package hello.springmvc.basic.request;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import java.io.IOException;

@Slf4j
@Controller
public class RequestParamController {

    @RequestMapping("/request-param-v1")
    public void requestParamV1(HttpServletRequest request, HttpServletResponse response) throws IOException {
        String username = request.getParameter("username");
        int age = Integer.parseInt(request.getParameter("age"));
        log.info("username={}, age={}", username, age);

        response.getWriter().write("ok");
    }
}

 

출력

2024-01-14T13:29:28.189+09:00  INFO 4160 --- [nio-8080-exec-1] h.s.b.request.RequestParamController     : username=kim, age=20

HTTP 요청 파라미터 - @RequestParam

스프링은 HTTP 요청 파라미터를 @RequestParam으로 받을 수 있다.

요청 파라미터에 username, age가 있다고 가정하고 여러가지 방법을 알아보자 !

 

HttpServletRequest가 제공하는 방식으로 요청 파라미터를 조회

/**
 * 반환 타입이 없으면서 이렇게 응답에 값을 직접 집어넣으면, view 조회X
 */
@RequestMapping("/request-param-v1")
public void requestParamV1(HttpServletRequest request, HttpServletResponse response) throws IOException {
    String username = request.getParameter("username");
    int age = Integer.parseInt(request.getParameter("age"));
    log.info("username={}, age={}", username, age);

    response.getWriter().write("ok");
}

 

@RequestParam 사용

/**
 * @RequestParam 사용
 * - 파라미터 이름으로 바인딩
 * @ResponseBody 추가
 * - View 조회를 무시하고, HTTP message body에 직접 해당 내용 입력
 */
@ResponseBody
@RequestMapping("/request-param-v2")
public String requestParamV2(
        @RequestParam("username") String memberName,
        @RequestParam("age") int memberAge) {

    log.info("username={}, age={}", memberName, memberAge);
    return "ok";
}

 

위의 코드에서 HTTP 파라미터의 이름과 변수의 이름을 같게 만들어 name 속성을 생략할 수 있다.

@RequestParam("username") String memeberName → @RequestParam String username

/**
 * @RequestParam 사용
 * HTTP 파라미터 이름이 변수 이름과 같으면 @RequestParam(name="xx") 생략 가능
 */
@ResponseBody
@RequestMapping("/request-param-v3")
public String requestParamV3(
        @RequestParam String username,
        @RequestParam int age) {

    log.info("username={}, age={}", username, age);
    return "ok";
}

 

더 나아가, 아예 @RequestParam을 생략할 수도 있다.

모든 경우에 가능한 것은 아니고 String, int, Integer 같은 단순 타입에서만 생략이 가능하다.

/**
 * @RequestParam 사용
 * String, int 등의 단순 타입이면 @RequestParam 도 생략 가능
 */
@ResponseBody
@RequestMapping("/request-param-v4")
public String requestParamV4(String username, int age) {
    log.info("username={}, age={}", username, age);
    return "ok";
}

 

@RequestParam 속성

name

파라미터의 이름을 지정하는 것 (*다른 속성이 없을 경우 "name = " 생략 가능)

 

required

파라미터의 필수 여부를 결정하는 것 (*기본값: true)

/**
 * @RequestParam.required
 * /request-param-required -> username이 없으므로 예외 *
 * 주의!
 * /request-param-required?username= -> 빈문자로 통과 *
 * 주의!
 * /request-param-required
 * int age -> null을 int에 입력하는 것은 불가능, 따라서 Integer 변경해야 함(또는 다음에 나오는 defaultValue 사용)
 */
@ResponseBody
@RequestMapping("/request-param-required")
public String requestParamRequired(
        @RequestParam String username, // required 속성 설정 x -> 기본값: true
        @RequestParam(required = false) Integer age) { // int에 null 값 입력 불가능 -> Integer로 변경

    log.info("username={}, age={}", username, age);
    return "ok";
}

주의 ‼️ - 기본형(primitive)null 입력

  • username 입력 x → HTTP 상태 코드 400 에러 발생
  • age 입력 x → HTTP 상태 코드 500 에러 발생
    • `required = false`이기 때문에 필수 값이 아니지만, age의 타입은 int 타입이기 때문에 null 값을 허용하지 않기 때문이다
    • age 타입을 Integer로 변경해주어야 500 에러가 발생하지 않는다.

주의 ‼️ - 파라미터 이름만 사용

  • “/request-param?username=” 요청
  • 파라미터의 이름만 있고 값이 없는 경우 → 빈문자로 취급하여 예외가 발생하지 x
  • 값이 없는 null과 빈문자 “”는 서로 다른 개념이다.

defaultValue

파라미터에 값이 없는 경우 defaultValue 속성을 사용해 기본 값을 적용할 수 있다.

defaultValue를 설정하는 경우, 기본 값이 설정되어 있기 때문에 required의 속성을 함께 사용하더라고 required는 의미가 없어진다.

/**
 * @RequestParam
 * - defaultValue 사용 *
 * 참고: defaultValue는 빈 문자의 경우에도 적용 * /request-param-default?username=
 */
@ResponseBody
@RequestMapping("/request-param-default")
public String requestParamDefault(
        @RequestParam(defaultValue = "guest") String username,
        @RequestParam(defaultValue = "-1") Integer age) {

    log.info("username={}, age={}", username, age);
    return "ok";
}

 

 

파라미터를 Map으로 조회

/**
 * @RequestParam Map, MultiValueMap
 * Map(key=value)
 * MultiValueMap(key=[value1, value2, ...]) ex) (key=userIds, value=[id1, id2])
 */
@ResponseBody
@RequestMapping("/request-param-map")
public String requestParamMap(@RequestParam Map<String, Object> paramMap) {

    log.info("username={}, age={}", paramMap.get("username"), paramMap.get("age"));
    return "ok";
}

 


HTTP 요청 파라미터 - @ModelAttribute

개발을 하면 요청 파라미터를 받아서 필요한 객체를 만들고 그 객체에 값을 넣어주어야 한다.

 

먼저, 요청 파라미터를 바인딩 받을 객체를 만든다.

 

HelloData 전체코드

package hello.springmvc.basic;

import lombok.Data;

@Data
public class HelloData {
    private String username;
    private int age;
}

 

@Data: @Getter, @Setter, @ToString, @EqualsAndHashCode, @RequiredArgsConstructor 자동으로 적용

 

@ModelAttribute를 사용하지 않는 경우

@ResponseBody
@RequestMapping("/model-attribute-v1")
public String modelAttributeV1(@RequestParam String username, @RequestParam int age) {
    HelloData helloData = new HelloData();
    helloData.setUsername(username);
    helloData.setAge(age);

    log.info("helloData={}", helloData);
    return "ok";
}

 

 

@ModelAttribute를 사용하는 경우

@ResponseBody
@RequestMapping("/model-attribute-v1")
public String modelAttributeV1(@ModelAttribute HelloData helloData) {
    log.info("helloData={}", helloData);
    return "ok";
}

 

 

스프링 MVC는 @ModelAttribute가 있는 경우 다음과 같은 과정을 거치게 된다.

  • HelloData 객체를 생성한다.
  • 요청 파라미터의 이름으로 HelloData 객체의 프로퍼티를 찾는다.
  • 그리고 해당 프로퍼티의 setter를 호출하여 파라미터의 값을 입력(바인딩)한다.
  • ex. 파라미터의 이름이 username이면 setUsername() 메서드를 찾아 호출하여 값을 입력한다.

 

@ModelAttribute를 생략하는 경우

@ResponseBody
@RequestMapping("/model-attribute-v2")
public String modelAttributeV2(HelloData helloData) {
    log.info("helloData={}", helloData);
    return "ok";
}

 

 

스프링에서는 생략으로 인한 혼란을 방지하기 위해 다음과 같은 규칙을 적용하고 있다.

String, int, Integer와 같은 단순 타입의 경우 → @RequestParam

나머지의 경우(argument resolver로 지정해둔 타입 외) → @ModelAttribute