728x90

 

웹 서비스를 만들기 위해서는 클라이언트와 서버 간에 데이터를 주고받을 필요가 있습니다.

그리고 REST API같은 HTTP API를 제작하기 위해서도 데이터의 처리방법에 대해 알 필요가 있습니다.

이전에 스프링을 사용하지 않고 이클립스 IDE만 사용해서 컨트롤러를 만들어 봤었습니다.

이때에는 매번 메서드에 HttpServletRequest를 매개변수로 사용하고 .getParameter()를 사용해

데이터를 처리하느라 좀 귀찮은 점이 있었습니다. 하지만 스프링을 사용하면 HTTP 요청과 응답 데이터를

쉽게 처리할 수 있습니다. 

 

우선 요청 데이터를 처리하는 것에 대해 정리하고 응답 데이터 처리에 대해 정리해보겠습니다.

응답 데이터 처리는 요청 데이터 처리와 중복되는 것이 많기 때문에 간략하게 해보겠습니다.

 

HTTP 요청 데이터 조회

 

HTTP 요청 메시지를 통해 클라이언트에서 서버로 데이터를 전달하는 방법은 주로 세 가지 방법을 사용합니다.

  • GET - 쿼리 파라미터
  • POST - HTML Form
  • HTTP message body

GET과 POST의 HTML Form 데이터를 처리하는 방법은 같습니다.

그전에 GET과 POST의 데이터 전달 방법에 대해 먼저 알아보겠습니다.

일단 GET의 데이터는 메시지 바디 없이 URL의 ?뒤에 Key=Value의 형식으로 전달합니다.

ex) localhost:8080/user?username=kim&userage=22

POST의  Form 데이터는 메시지 바디로 전달하지만 형식은 똑같이 쿼리 파라미터 형식으로 전달합니다.

username=kim&userage=22

이때 HTTP 헤더의 content-type은 application/x-www-form-urlencoded입니다.

 

이렇게 쿼리 파라미터 형식으로 전달하는 두 데이터를 처리하는 방법에는 크게 세 가지가 있습니다.

앞서 이야기했던 HttpServletRequest의 .getParameter를 사용하는 방법과

@RequestParam을 사용하는 방법 그리고 @ModelAttribute를 사용하는 방법이 있습니다.

 

@RequestParam

 

해당 어노테이션은 해당 어노테이션에 값으로 설정한 데이터가 들어오면 매핑해주는 역할을 합니다.

그리고 파라미터의 이름과 데이터의 Key값이 동일하면 어노테이션을 생략할 수 있습니다.

// URL -> http://localhost:8080/request-param?username=kim&age=24

// 1번 방법
@RequestMapping("/request-param")
public String requestParamV2(
	@RequestParam("username") String memberName,
	@RequestParam("age") int memberAge){...}

// 2번 방법 어노테이션 안의 값 생략
@RequestMapping("/request-param")
public String requestParamV2(
	@RequestParam String username,
 	@RequestParam int age){...}

// 3번 방법 어노테이션 자체를 생략
@RequestMapping("/request-param")
public String requestParamV2(String username,int age){...}

 

추가적으로 @RequestParam는 required 속성을 가지고 있습니다.

디폴트 값이 true인데 이럴 경우 해당하는 파라미터의 요청 데이터가 넘어오지 않으면 400 에러를 반환합니다.

하지만 ""처럼 빈 값을 넘겨도 아무런 에러를 반환하지 않습니다.

그리고 기본형 타입의 파라미터에 required=false를 설정했을 때는 값이 넘어오지 않으면 null값을 주는 것이 불가능하여

500 에러를 반환하기 때문에 이를 처리하는 것이 필요합니다.

이때에는 다른 속성인 defaultValue나 래퍼클래스를 사용하면 됩니다.

// required 속성 사용
@RequestMapping
public String requestParamDefault(
        @RequestParam(required = true) String username,
        @RequestParam(required = false) int age) {...} 
        // age 해당하는 값이 안넘어 올 경우 500에러를 반환한다.

// defaultValue 속성 사용
@RequestMapping
public String requestParamDefault(
		@RequestParam(defaultValue = "guest") String username,
		@RequestParam(defaultValue = "-1") int age){...}
        
// 래퍼클래스 사용
@RequestMapping
public String requestParamDefault(
		@RequestParam String username,
		@RequestParam(required = false) Integer age){...}
        // 래퍼클래스를 사용해 null값 오류를 해결할 수 있다.

 

@ModelAttribute

 

스프링은 객체에 요청 파라미터를 바인딩하는 기능도 가지고 있습니다.

이게 @ModelAttribute의 기능입니다. 해당 어노테이션도 생략이 가능합니다.

@RequestMapping
public String modelAttributeV1(@ModelAttribute HelloData helloData) {...}


// @ModelAttribute 생략
@RequestMapping
public String modelAttributeV1(HelloData helloData) {...}

위와 같이 작성 가능합니다.

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

  • HelloData 객체를 생성합니다.
  • 요청 파라미터의 이름으로 HelloData 객체의 프로퍼티를 찾습니다.
  • 해당 프로퍼티의 setter를 호출해 파라미터의 값을 바인딩합니다.

때문에 객체로 사용할 클래스에는 setter 메서드를 만들어 둬야 합니다.

 

앞서 이야기한 @RequestParam과 @ModelAttribute는 둘 다 생략할 수 있습니다.

때문에 스프링은 생략 시 다음과 같은 규칙을 적용합니다.

  • String, int, Integer와 같은 단순 타입은  @RequestParam을 사용합니다.
  • argument resolver로 지정해둔 타입을 제외한 나머지는 @ModelAttribute 사용합니다.

 

HTTP Message Body

 

다음은 HTTP 메시지 바디에 담긴 단순 텍스트 데이터를 처리하는 방법입니다.

HTTP API에 주로 사용되고 JSON 형식이나 XML 형식을 주로 사용합니다.

 

이렇게 메시지 바디로 넘어오는 데이터는 앞서 이야기한 @RequestParam과 @ModelAttribute를 사용할 수 없습니다.

HTML form으로 넘어오는 데이터를 제외하고요.

요청 메시지 바디로 넘어오는 데이터를 조회하는 방법은 아래와 같습니다. 

  • InputStream
  • HttpEntity
  • RequestEntity - HttpEntity를 상속받은 클래스로 추가 기능을 제공합니다.
  • @RequestBody

 

이중에서는 @RequestBody를 사용하는 게 가장 편해 보였습니다.

스트림은 바이트 타입으로 넘어오기 때문에 다시 한번 타입에 맞춰 변경해주는 작업이 필요합니다.

그리고 HttpEntity나 @RequestBody는 HTTP 메시지 컨버터를 통해 바디의 내용을 문자나 객체 등으로 변환시켜줍니다.

하지만 HttpEntity는 getBody()로 한 번 더 꺼내는 과정이 필요합니다. 

// @RequestBody
@PostMapping("/request-body-json")
public HelloData requestBodyJsonV5(@RequestBody HelloData data) {...}

// HttpEntity
@PostMapping("/request-body-json")
public String requestBodyJsonV4(HttpEntity<HelloData> httpEntity) {...}

 

HTTP 응답

 

스프링에서 응답 데이터를 만드는 방법은 크게 세 가지입니다.

  • 정적 리소스
  • 뷰 템플릿 사용
  • HTTP 메시지 사용

 

 

HTTP 메시지 사용

 

HTTP API나 비동기 통신을 통해 데이터의 특정 값만 제공해야 하는 경우에는

HTTP 바디에 직접 데이터를 실어서 보낼 필요가 있습니다.

 

만약 컨트롤러의 메서드가 전부 특정 데이터만 전달해야 하는 경우에는 클래스에 

@RestController나 @ResponseBody 어노테이션을 달아 사용할 수 있습니다. 

@RestController는 @Controller와 @ResponseBody 합쳐진 것이기 때문에 둘 중 무엇을 사용하든 상관없습니다.

 

스프링의 경우 String으로 반환하면 뷰 리졸버가 실행되어서 뷰를 찾고 렌더링 합니다.

하지만 @ResponseBody가 달린 클래스나 메서드는 반환 값을 HTTP 응답 메시지 바디에 직접 실어 보냅니다.

그리고 해당 어노테이션을 쓰지 않고 HttpEntity나 ResponseEntity를 사용해서 응답할 수도 있습니다.

ResponseEntity를 사용할 경우 HTTP 응답 코드를 설정할 수 있는데 @ResponseBody는 따로 설정할 수 없습니다.

하지만 @ResponseStatus를 사용한다면 @ResponseBody로 반환하는 메서드도 응답 코드를 설정할 수 있습니다.

 

// @ResponseBody와 @ResponseStatus 사용
@ResponseStatus(HttpStatus.OK)
@ResponseBody
@GetMapping("/response-body-json")
public HelloData responseBodyJson() {
    HelloData helloData = new HelloData();
    helloData.setUsername("userA");
    helloData.setAge(20);
    
    return helloData;
}

// ResponseEntity 사용
@GetMapping("/response-body-json")
public ResponseEntity<HelloData> responseBodyJson() {
    HelloData helloData = new HelloData();
    helloData.setUsername("userA");
    helloData.setAge(20);
    
    return new ResponseEntity<>(helloData, HttpStatus.OK);
}

 

 

참조

HTTP 메시지 컨버터

argument resolver

728x90

+ Recent posts