Post

백엔드 웹 개발 노트6.3 - Spring MVC HTTP Request

1. HTTP 요청 파라미터 - 쿼리파라미터, HTML FORM

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

  • GET - 쿼리 파라미터
    • 메시지 바디 없이, URL의 쿼리 파라미터에 데이터를 포함해서 전달
    • /url?username=kms&hegiht=178
  • POST - HTML Form
    • content-type:application/x-www-form-urlencoded
    • 메시지 바디에 쿼리 파라미터 형식으로 전달함.
  • HTTP message body에 데이터를 직접 담아서 요청
    • HTTP API에 주로 사용된다. JSON, XML, TEXT
    • 데이터 형식은 주로 JSON이 사용된다.

1.1 GET,쿼리 파라미터로 전송.

자바 파일을 만들자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package hello.springmvc.basic.request;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Slf4j
@Controller
public class RequestParamController {

    //기본적인 형태

    @RequestMapping("/kms-request-param-v1")
    public void requestParamv1(HttpServletRequest request, HttpServletResponse response) throws IOException {

        String username = request.getParameter("username");
        int height = Integer.parseInt(request.getParameter("height"));
        log.info("username = {} , height = {}",username,height);

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

기본적인 예제이다. http://localhost:8080/kms-request-param-v1?username=kms&height=178 으로 들어가면 된다.

1.2 Post로 Form 전송

전송하기 위해 html 파일을 만들어야한다.

경로는 main/resources/static/basic/kms-from.html 이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<form action="/kms-request-param-v1" method="post">
  username: <input type="text" name="username" />
  height: <input type="text" name="height" />
  <button type="submit">전송</button>
</form>
</body>
</html>

<localhost:8080/kms-request-param-v1> 으로 가면 입력폼에 입력하고 전송 버튼을 누르면

위에서 작성한 java에서 요청값을 받아 로그에 잘 찍고 잘 받아온다.

쿼리 파라미터와 다른 점은 뭘까?

url에 데이터를 넣냐 html에 데이터 넣냐의 차이다.

보안적으로 GET Query보다는 Post Form이 더 좋아보이지 않는가?

GET은 데이터를 보낸다는 것 보다는 조회목적으로 쓰자.

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

@RequestParam 애노테이션을 이용해서 요청 파라미터를 관리해보자.

가장 기본적인 형태는 다음과 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package hello.springmvc.basic.request;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Slf4j
@Controller
public class RequestParamController {
    @ResponseBody
    @RequestMapping("/kms-request-param-v2")
    public String requestParamV2(
            @RequestParam("username") String myname,
            @RequestParam("height") int myheight
    ){
        log.info("username = {} , height = {}",myname,myheight);

        return "ok";
    }
}

@ResponseBody 를 사용해준 이유는 @Controller 애노테이션 때문에 리턴값으로 지정한 “ok”를 논리적 뷰 이름으로 판단하지 않게끔 하기 위해서이다. 이렇게 설정하면 @RestController 처럼 HTTP message body에 리턴값을 넣어버려 반환한다.
뒤에서 설명할것!!

만약 HTTP 요청 파라미터 이름이 변수 이름과 같다면 @RequestParam() 속성값을 생략할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package hello.springmvc.basic.request;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Slf4j
@Controller
public class RequestParamController {
    @ResponseBody
    @RequestMapping("/kms-request-param-v3")
    public String requestParamV3(
            @RequestParam String username,
            @RequestParam int height
    ){
        log.info("username = {} , height = {}",username,height);

        return "ok";
    }

}

즉 url에서 요청을 보낼때 http://localhost:8080/kms-request-param-v3?username=kms&height=178 이런식으로 파라미터 이름과 @RequestParam 의 변수값이 같아야 한다.

심지어 위와 같은 상황에서 @RequestParam 을 생략할수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
    package hello.springmvc.basic.request;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Slf4j
@Controller
public class RequestParamController {

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

        return "ok";
    }

}

이러한 방식은 팀원들끼리 협의가 되어있어야 한다. 너무 줄여서 헷갈릴수 있기 때문이다. @RequestParam 을 명시하여 요청 파라미터에서 데이터를 읽는 다는 것을 알 수 있다.

2.1 파라미터 필수 여부 - requestParamRequired

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package hello.springmvc.basic.request;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Slf4j
@Controller
public class RequestParamController {

    @ResponseBody
    @RequestMapping("/kms-request-param-required")
    public String requestParamRequired(
            @RequestParam(required = true) String username,
            @RequestParam(required = false) int height
    ){
        log.info("username = {} , height = {}",username,height);

        return "ok";
    }

}

@RequestParam 의 속성 값으로 ‘required = true’, ‘required = false’가 있다.

딱봐도 username은 꼭 요청파라미터로 들어와야하고, height는 요청 파라미터로 없어도 된다는걸 알수 있다.

다만, 여기서 주의할 점이 있다.

요청 파라미터로,

username=&height=178

username&height=178

라는 값이 들어오면 400(Bad request)가 뜰까??

“ok”가 뜬다. 빈문자로 넣어버리기 때문이다.

만약 height도 required = true로 놓고 ‘height=’나 ‘height’까지만 넣으면 어떻게 될까?

400 error가 뜬다. 이유는 int로 선언해서 그렇다. int로 선언한 변수에는 NULL값을 넣지 못한다.

여기서 필요한건 requestParamDefault 이다.

2.1.1 기본값 적용 - requestParamDefault

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package hello.springmvc.basic.request;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Slf4j
@Controller
public class RequestParamController {

    @ResponseBody
    @RequestMapping("/kms-request-param-required")
    public String requestParamRequired(
            @RequestParam(required = true, defaultValue ="jyb") String username,
            @RequestParam(required = false, defaultValue = "-1") int height
    ){
        log.info("username = {} , height = {}",username,height);

        return "ok";
    }

}

어떤 의미인지 직관적으로 알수 있다.

요청 파라미터가 비어있다(NULL)면 username에는 ‘jyb’를 넣어주고, height에는 ‘-1’를 넣어준다.


또한 파라미터를 Map으로 조회할 수 있다.

2.2 requestParamMap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package hello.springmvc.basic.request;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;

@Slf4j
@Controller
public class RequestParamController {
    @ResponseBody
    @RequestMapping("/kms-request-param-map")
    public String requestParamMap(@RequestParam Map<String,Object> paramMap){
        log.info("username = {} , height = {}",paramMap.get("username"),paramMap.get("height"));

        return "ok";
    }

}

파라미터의 값이 1개가 확실하다면 Map 을 사용해도 되지만, 그렇지 않다면 MultiValueMap 을 사용하자.!

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

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

1
2
3
4
5
6
7
8
9
10
11
package hello.springmvc.basic;

import lombok.Data;

@Data
public class BmiData {
    private String name;
    private int height;
    private int weight;
}

BMI계산에 필요한 user객체이다.

3.1 @Data

  • 롬복에서 제공하는 애노테이션으로 @Getter, @Setter, @ToString, @EqualsAndHashCode, @RequiredArgsConstructor를 자동으로 제공한다.

@ModelAttribute를 적용하면 더 편리하게 파라미터를 받을수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package hello.springmvc.basic.request;

import hello.springmvc.basic.BmiData;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;

@Slf4j
@Controller
public class RequestParamController {

    @ResponseBody
    @RequestMapping("/kms-model-attribute-v1")
    public String modelAttributeV1(@ModelAttribute BmiData bmiData){
        log.info("username = {} , height = {} , weight = {}",bmiData.getName(),bmiData.getHeight(),bmiData.getWeight());
        return "ok";
    }


}

결과

보기에 너무 간편하지만 좀 더 직접적인 이유가 없을까?

만약 저 3가지 변수를 RequestParam으로 받아온다면 코드가 얼마나 길어질지 감이 잡히는가?

그렇기에 객체에 자동으로 넣어주는 @ModelAttribute 가 편한것이다.

참고로 스프링 MVC는 @ModelAttribute 가 있으면 다음과 같이 실행한다.

  1. BmiData 객체 생성
  2. 요청 파라미터의 이름으로 BmiData 객체의 프로퍼티를 찾는다. 해당 프로퍼티의 setter를 호출해서 파라미터의 값을 바인딩한다.

또한, 객체의 값을 수정하면 set~()함수가 호출되고 값을 조회하면 get~()함수가 호출된다.

심지어 위 코드에서 @ModelAttribute 를 생략할 수 있다.

@RequestParam 도 생략이 가능해서 스프링은 생략시 다음과 같은 규칙을 적용한다.

  • String, int, Integer 같은 단순 타입 = @RequestParam 지정.
  • 나머지는 @ModelAttribute(단, argument resolver로 지정해둔 타입 외 ex] HttpServeltResponse 등)
This post is licensed under CC BY 4.0 by the author.