Spring 요청 파라미터

바그다드·2023년 4월 22일
0
  • 클라이언트에서 서버로 값을 넘겨줄 때 spring에서는 파라미터를 어떻게 받을 수 있는지 알아보자

클라이언트 -> 서버 데이터 전달

  • 클라이언트에서 서버로 데이터를 전달할 때는 다음 3가지 방법이 주로 사용된다,
  1. 쿼리 파라미터
http://localhost:8080/request-param?username=hello&age=20
  1. HTML form
POST /request-param ...
content-type: application/x-www-form-urlencoded
username=hello&age=20
  1. HTTP body로 직접 전달
  • 쿼리 파라미터를 이용한 전달 방식과 form태그를 이용한 전달 방식의 데이터 형식이 유사한 것을 확인할 수 있다. 따라서 먼저 쿼리파라미터나 form태그를 이용한 전달방식을 먼저 확인해보고, HTTP body에 직접 담아 전달하는 방식을 알아보자

쿼리 파라미터, form태그 데이터 전달 방법

1. @RequestParam

    @ResponseBody
    @RequestMapping("/request-param-default")
    public String requestParamDefault(
            // username이 필수로 넘어와야 함
            @RequestParam(required = true, defaultValue = "guest") String username,
            // age는 넘어오지 않아도 됨
            @RequestParam(required = false, defaultValue = "-1") int age) {
        log.info("username ={}, age ={}", username, age);
        return "ok";
    }
  • requied=false는 파라미터 값이 null이 들어올 수 있음을 뜻하고,
    requied=true라면 파라미터 값이 무조건 넘어와야 한다.
    - 만일 요청이"request-param?username="처럼 파라미터 이름만 있고 값이 없는 경우 빈문자로 "" 넘어오게 된다.
  • 'defaultValue = value'를 설정해주면 파라미터값이 null이어도 defaultValue로 설정한 값이 기본값으로 할당된다.

2. Map

    // 모든 파라미터 값들을 맵의 형태로 가져옴
    // 하나의 파라미터 키에 대한 값이 1개인게 확실하다면 Map을 사용해도 되지만, 여러개일 가능성이 있을 경우
    // MultiValueMap을 사용하자!!!
    @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";
    }
  • 메소드의 매개변수로 Map<String, Object>을 넘겨주면 각 파라미터의 키와 벨류를 맵 타입 데이터로 변환하여 값을 받아준다.

3. @ModelAttribute


    @ResponseBody
    @RequestMapping("/model-attribute-v1")
    public String modelAttributeV1(@ModelAttribute HelloData helloData) {

        log.info("username ={}, age ={}", helloData.getUsername(), helloData.getAge());
        return "ok";
    }
  • 대부분의 경우 클라이언트와 서버간에 데이터를 주고 받을 때 DTO나 VO와 같은 객체를 이용하여 데이터를 주고 받는데, @ModelAttribute를 사용하면 스프링에서 해당 객체의 프로퍼티 이름과 파라미터 키값을 매핑하여 데이터를 입력해준다. 대략적인 동작은 다음과 같다.
  1. HelloData 객체 생성
  2. setter를 호출하여 값을 초기화
  • @ModelAttribute를 생략할 수도 있지만 파라미터를 받아온다는 것을 명시적으로 나타내기 위해 명시해주는 것이 좋다.

HTTP body 데이터 전달 방법

  • 요청 파라미터와 다르게, HTTP 메시지 바디를 통해 데이터가 직접 넘어오는 경우는 @RequestParam나 @ModelAttribute 를 사용할 수 없다.
  • 요청 파라미터를 받아오는 방법도 여러가지가 있지만 대부분은 @RequestBody를 사용하기 때문에 다른 방법들은 참고만 하자

1. 단순 텍스트

@Slf4j
@Controller
public class RequestBodyStringController {

    @PostMapping("/request-body-string-v1")
    public void requestBodyStringV1(HttpServletRequest request, HttpServletResponse response) throws IOException {
        ServletInputStream inputStream = request.getInputStream();
        String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);

        log.info("messageBody={}", messageBody);

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

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

        log.info("messageBody={}", messageBody);

        responseWriter.write("ok");
    }

    @PostMapping("/request-body-string-v3")
    public HttpEntity<String> requestBodyStringV3(HttpEntity<String> httpEntity) throws IOException {
        String messageBody = httpEntity.getBody();

        log.info("messageBody={}", messageBody);

        // ctrl + p : 메소드 매개변수 확인하기
        return new HttpEntity<>("ok");
    }

    @ResponseBody
    @PostMapping("/request-body-string-v4")
    public String requestBodyStringV4(@RequestBody String messageBody, @RequestHeader Map<String, Object> headers) throws IOException {
        for (String key : headers.keySet()) {
            log.info("header key={} , value={}",key, headers.get(key));
        }
        log.info("messageBody={}", messageBody);
        return "ok";
    }
}
  • @RequestBody와 마찬가지로 http body에 직접 응답을 보낼경우 @ResponseBody를 사용하고, @RequestHeader를 이용하여 요청 헤더 값들을 가져올 수 있다!!!

2. JSON데이터

@Slf4j
@Controller
public class RequestBodyJsonController {

    private ObjectMapper objectMapper = new ObjectMapper();
	
    // 1. inputStream 이용
    @PostMapping("/request-body-json-v1")
    public void requestBodyJsonV1(HttpServletRequest request, HttpServletResponse response) throws IOException {
        ServletInputStream inputStream = request.getInputStream();
        String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);

        log.info("messageBody={}", messageBody);
        HelloData helloData = objectMapper.readValue(messageBody, HelloData.class);
        log.info("username={}, age={}",helloData.getUsername(), helloData.getAge());

        response.getWriter().write("ok");
    }
	
    // 2. ObjectMapper 이용
    @ResponseBody
    @PostMapping("/request-body-json-v2")
    public String requestBodyJsonV2(@RequestBody String messageBody) throws IOException {
        log.info("messageBody={}", messageBody);
        HelloData helloData = objectMapper.readValue(messageBody, HelloData.class);
        log.info("username={}, age={}",helloData.getUsername(), helloData.getAge());

        return "ok";
    }
    
	// 3. @RequestBody로 객체와 자동 매핑
    // Http body로 직접 데이터를 보내올경우 @RequestBody 생략 불가능
    // 생략할 경우 @ModelAttribute가 적용이 되어 버린다.
    @ResponseBody
    @PostMapping("/request-body-json-v3")
    public String requestBodyJsonV3(@RequestBody HelloData helloData){
        log.info("username={}, age={}",helloData.getUsername(), helloData.getAge());

        return "ok";
    }

	// 4. HttpEntity 활용
    @ResponseBody
    @PostMapping("/request-body-json-v4")
    public String requestBodyJsonV4(HttpEntity<HelloData> data){
        HelloData helloData = data.getBody();
        log.info("username={}, age={}",helloData.getUsername(), helloData.getAge());

        return "ok";
    }

    /** 5. @RequestBody로 객체와 매핑하고, 객체를 이용해 리턴
     * @RequestBody 생략 불가능(@ModelAttribute 가 적용되어 버림)
     * HttpMessageConverter 사용 -> MappingJackson2HttpMessageConverter (contenttype:
    application/json)
     *
     * @ResponseBody 적용
     * - 메시지 바디 정보 직접 반환(view 조회X)
     * - HttpMessageConverter 사용 -> MappingJackson2HttpMessageConverter 적용
    (Accept: application/json)
     */
    @ResponseBody
    @PostMapping("/request-body-json-v5")
    public HelloData requestBodyJsonV5(@RequestBody HelloData helloData){
        log.info("username={}, age={}",helloData.getUsername(), helloData.getAge());
        return helloData;
    }

}
  1. Inpustream 활용
    Http body를 이용한 데이터 전달 방법도 마찬가지로 Inputstream을 이용해 데이터를 가져올 수 있다.

  2. @RequestBody + ObjectMapper
    HelloData helloData = objectMapper.readValue(messageBody, HelloData.class);
    @RequestBody를 이용해 json형태의 데이터를 String데이터로 가져오고, 이 데이터를 ObjectMapping을 활용하여 객체로 변환할 수 있다.

  3. @RequsetBody
    @RequsetBody를 명시하고 객체 타입을 지정해주면 json데이터와 객체의 파라미터를 매핑하여 값을 초기화 해준다.
    - 여기서는 @RequestBody를 생략할 수 없는데, 생략해버리면 @ModelAttribute가 적용되어 값이 매핑되지 않는다.
    - @ModelAttribute가 적용될 경우에 참조 데이터 타입은 null로, 기본 데이터 타입은 0으로 초기화 된다.

  4. HttpEntity활용
    HttpEntity를 이용하여 json데이터를 가져올 수 있다. getBody()로 http body데이터를 가져오고, getHeaders()로 헤더 정보를 가져올 수 있다.

  5. @RequsetBody + 객체 타입 리턴
    @RequsetBody를 이용해 객체와 매핑하여 json데이터를 가져오고, 마찬가지로 리턴 값을 객체로 지정하면 객체를 이용해 json데이터를 리턴해준다.

  • @RequsetBody 요청 과정
    JSON 요청 -> HTTP 메시지 컨버터 -> 객체
  • @ResponseBody 응답 과정
    객체 -> HTTP 메시지 컨버터 -> JSON 응답
profile
꾸준히 하자!

0개의 댓글