✏️ HTTP request 데이터 조회

박상민·2023년 7월 27일
0

SpringMVC

목록 보기
6/11
post-thumbnail

📌 URL param 데이터 받기

  • @PathVariable을 사용해서 URL 경로에 있는 데이터를 추출할 수 있다.
  • 요즘은 Query String보다 리소스 경로에 식별자를 넣은 스타일을 선호한다.
    • /mapping/{userId}
    • /users/{userId}

✔︎ @PathVariable(경로 변수) 사용

/**
     * PathVariable 사용
     * 변수명이 같으면 생략 가능
     *
     * @PathVariable("userId") String userId -> @PathVariable userId
     * /mapping/userA
     */
    @GetMapping("/mapping/{userId}")
    public String mappingPath(@PathVariable("userId") String data) {
        log.info("mappingPath userId = {}", data);
        return "ok";
    }

@PathVariable은 url 경로가 .../mapping/userA라고 온다면 userA를 변수로 사용할 수 있게 해주는 애노테이션이다.

  • @RequestMapping 은 URL 경로를 템플릿화 할 수 있는데, @PathVariable 을 사용하면 매칭 되는 부분을 편리하게 조회할 수 있다.
  • @PathVariable 의 이름과 파라미터 이름이 같으면 생략할 수 있다.

✔︎ @PathVariable 다중 사용

 @GetMapping("/mapping/users/{userId}/orders/{orderId}")
    public String mappingPath(@PathVariable String userId, @PathVariable Long orderId) {
        log.info("mappingPath userId = {}, order={}", userId, orderId);
        return "ok";
    }

URL 경로가 /mapping/users/{userId}/orders/{orderId} 처럼 다중 변수가 들어올 때도 @PathVariable을 사용할 수 있다.

📌 HTTP 요청 파라미터

HTTP 요청 데이터 조회 - 개요
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 Post로 오며 HTTP request Body에 Query String 형식으로 데이터가 담겨온다.
    • 예) 회원 가입, 상품 주문, HTML Form 사용
  • HTTP message body에 데이터를 직접 담아서 요청
    • HTTP API에서 주로 사용, JSON, XML, TEXT
    • 데이터 형식은 주로 JSON 사용
    • POST, PUT, PATCH

Query String 형식의 데이터를 받는 방법은 2가지가 있다.

  • @RequestParam : 데이터를 단일 필드에 매핑
  • @ModelAttribute : 데이터를 객체에 매핑

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

지금부터 스프링으로 요청 파라미터를 조회하는 방법을 단계적으로 알아보겠다.

[1단계 - request.getParameter()]

@Slf4j
@Controller
public class RequestParamController {
	/**
	* 변환 타입이 없으면서 이렇게 응답에 값을 직접 집어넣으면, 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"));

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

request.getParameter()

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

[2단계 - @RequestParam]

스프링이 제공하는 @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";
}
  • @RequestParam: 파라미터 이름으로 바인딩
  • @ResponseBody: View 조회를 무시하고, HTTP message body에 직접 해당 내용 입력
  • @RequestParamname(value) 속성이 파라미터 이름으로 사용
    • @RequestParam("username) String memberName
      -> request.getParameter("username)

[3단계 - @RequestParam, Name 생략 [권장]]

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

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

@RequestParam의 name과 파라미터 이름이 같다면 생략 가능하다.

[4단계 - @RequestParam 생략]

@ResponseBody
@RequestMapping("/request-param-v4")
public String requestParamV4(String username, int age) {
     log.info("username={}, age={}", username, age);
     return "ok";
 }

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


지금까지 요청 파라미터 조회를 1단계부터 4단계까지 살펴보았다. 4단계가 가장 간결해보이지만 @RequestParam까지 완전히 생략하는 것은 과하다는 주장도 있다. 나 또한 애노테이션이 있는 것이 직관적이고 명확하다고 생각해서 3단계를 사용하는 것을 권장한다. 이는 뒤에서 설명할 ModelAttribute와도 연관이 있다.

[추가 속성 - required, default]

required

/**
* @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(required = true) String username,
       @RequestParam(required = false) Integer age) {

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

@RequestParam.required

  • 파라미터 필수 여부 설정
  • 기본값이 파라미터 필수 (required = true)이다.

default

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

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

파라미터에 값이 없는 경우 defaultValue를 사용하면 기본 값을 적용할 수 있다. 이미 기본 값이 있기 때문에 위에서 소개한 required는 사용 의미가 없다.

defalutValue는 빈 문자의 경우에도 설정한 기본 값이 적용된다.
/url?username="hello"&age="20"로그와, /url?username="hello"&age=로그의 차이점을 출력을 통해 보여주겠다.

출력

위의 로그는 /url?username="hello"&age="20"이고, 아래 로그는 /url?username="hello"&age=이다. 아래 로그를 보면 빈 문자로 설정한 age의 값이 default값인 -1인 것을 알 수 있다.

✔︎ HTTP 요청 파라미터 - @ModelAttriute

우선 @ModelAttribute를 설명하기에 앞서 요청 파라미터를 받아서 필요한 객체를 만들고, 그 객체에 값을 넣어주어야 하기에 다음과 같은 코드르 작성하겠다.

import lombok.Data;

@Data /*@Getter, @Setter, @ToString,
@EqualsAndHashCode, @RequiredArgsConstructor를 자동 저장*/
public class HelloData{
	private String username;
    private int age;
}

[@ModelAttribute 적용 전]

우선 들어가기에 앞서 @ModelAttribute 적용 전과 후의 차이를 보여주기 위해서 @ModelAttribute 적용 전 코드를 보여주겠다.

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

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

[1단계 - @ModelAttribute 적용]

/**
* @ModelAttribute 사용
* model.addAttribute(helloData) 코드도 함께 자동 적용
**/
@ResponseBody
@RequestMapping("/model-attribute-v1")
public String modelAttributeV1(@ModelAttribute HelloData helloData) {
        log.info("username={}, age={}", 
        helloData.getUsername();
        helloData.getAge());
        return "ok";
    }

@ModelAttribute을 적용하니 마법처럼 HelloData 객체가 생성되고, 요청 파라미터의 값도 모두 들어가 있다.

Spring MVC는 @ModelAttribute가 있으면 다음을 실행한다.
1. HelloData 객체를 생성한다.
2. 요청 파라미터의 이름으로 HelloData 객체의 프로퍼티를 찾는다. 그리고 해당 프로퍼티의 setter를 호출해서 파라미터의 값을 입력한다.

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

[2단계 - @ModelAttribute 생략]

/**
* @ModelAttribute 생략 가능
* String, int 같은 단순 타입 = @RequestParam
* argument resolver 로 지정해둔 타입 외 = @ModelAttribute 
*/
@ResponseBody
@RequestMapping("/model-attribute-v2")
public String modelAttributeV2(HelloData helloData) {
        log.info("username={}, age={}"
        helloData.getUsername();
        helloData.getAge());
        return "ok";
    }

@ModelAttribute는 생략할 수 있다.
그런데 @RequestParam도 생략이 가능하다. 어떻게 구분할 수 있을까?


스프링은 @ModelAttribute, @RequestParam 생략시 다음과 같은 규칙을 적용한다.

  • String, int, Inter 같은 단순 타입 = @RequestParam 적용
  • 객체 등 나머지 타입 = @ModelAttribute 적용(argument resolver로 지정해둔 타입 외)

출처
김영한님의 Spring MVC 강의
https://velog.io/@neity16/6-%EC%8A%A4%ED%94%84%EB%A7%81-MVC-10-HTTP-request-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%A1%B0%ED%9A%8C-PathVariable-RequestParam-HttpEntity-%EB%93%B1

profile
스프링 백엔드를 공부중인 대학생입니다!

0개의 댓글