HTTP 요청 파라미터 - @ModelAttribute

shinyeongwoon·2022년 12월 28일
0

spring MVC

목록 보기
28/32

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

@RequestParam String username;
@RequestParam int age;

HelloData data = new HelloData();
data.setUsername(username);
data.setAge(age);

스프링은 이 과정을 완전히 자동화해주는 @ModelAttribute기능을 제공한다.
먼저 요청 파라미터를 바인딩 받을 객체를 만들자

HelloData

경로 : hello.springmvc.basic

package hello.springmvc1.basic;

import lombok.Data;

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

롬복 @Data

  • @Getter, @Setter , @ToString, @EqualsAndHashCode,@RequiredArgsConstructor를 자동으로 적용해준다.

@ModelArrtibute 적용 - modelAttributeV1

/**
* @ModelAttribute 사용
* 참고 : model.addAttribute(helloData) 코드도 함께 자동 적용됨, 뒤에 model을 설명할 때 자세히 설명
*/
@ResponseBody
@RequestMapping("/model-attribute-v1")
public String modelAttributeV1(@ModelAttribute HelloData helloData){
	log.info("username={},age={}", helloData.getUsername(), helloData.getAge());
    return "ok";
}

마치 마법처럼 HelloData 객체가 생성되고, 요청 파라미터의 값도 모두 들어가 있다.

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

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

프로퍼티

객체에 getUsename(), setUsername()메서드가 있으면, 이 객체는 username이라는 프로퍼티를 가지고 있다.
username프로퍼티의 값을 변경하면 setUsername()이 호출되고, 조회하면 getUsername()이 호출 된다.

class HelloData{
	getUsername();
    setUsername();
}

바인딩 오류

age=abc처럼 숫자가 들어가야 할 곳에 문자를 넣으면 BindException이 발생한다. 이런 바인딩 오류를 처리하는 방법은 검증 부분에서 다룬다.

@ModelAttribute 생략 - modelAttributeV2

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

@ModelAttribute는 생략할 수 있다.
그런데 @RequestParam도 생략할 수 있으니 혼란이 발생할 수 있다.

스프링은 해당 생략시 다음과 같은 규칙을 적용한다.

  • String,int,Integer 같은 단순 타입 = @RequestParam
  • 나머지 = @ModelAttribute (argument resolver 로 지정해둔 타입 외)

🤞 참고 )
argument resolver는 뒤에서 학습한다.

HTTP 요청 메세지 - 단순 텍스트

서블릿에서 학습한 내용을 떠올려 보자.

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

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

요청 파라미터와 다르게, HTTP 메세지 바디를 통해 데이터가 직접 넘어오는 경우는 @RequestParam,@ModelAttribute를 사용할 수 없다. (물론 HTML Form 형식으로 전달되는 경우는 요청 파라미터로 인정된다.)

먼저 가장 단순한 텍스트 메세지를 HTTP 메세지 바디에 담아서 전송하고 읽어보자
HTTP 메세지 바디의 데이터를 InputStream을 사용해서 직접 읽을 수 있다.

RequestBodyStringController


@Slf4j
@Controller
public class RequestBodyStringController{
	
    @PostMapping("/request-body-string-v1")
    public void requestBodyString(HttpServletRequest request, HttpServletResponse response) throws IOException{
    	
        ServletInputStream inputSteam = request.getInputStream();
        String messageBody = StreamUtils.coptToString(inputStream, StandardCharsets.UTF_8);
        
        log.info("messageBody={}",messageBody);
        response.getWriter().write("ok");
        
    }
    
}

Postman을 사용해서 테스트 해보자
POST http://locahost:8080/request-body-string-v1
Body -> row,Text 선택

Input, output 스트림,Reader - requestBodyStringV2

/**
* InputStream(Reader) : HTTP 요청 메시지 바디의 내용을 직접 조회
* OoutputStream(Writer) : HTTP 응답 메시지 바디에 직접 결과 출력
*/
@PostMapping("/request-body-string-v2")
public void requestBodyStringV2(InputStream inputSteam , Writer responseWriter) throws IOException{
	String messageBody = StreamUtils.copyToString(imputSteam, StandardCharsets.UTF_8);
    log.info("messageBody={}",messageBody);
    responseWriter.write("ok");
}

스프링 MVC는 다음 파라미터를 지원한다.

  • InputStream(Reader) : HTTP 요청 메세지 바디의 내용을 직접 조회
  • OutputStream(Writer) : HTTP 응답 메세지의 바디에 직접 결과 출력

HttpEntity - requestBodyStirngV3

/**
* HttpEntity : HTTP header, body 정보를 편리하게 조회
* - 메시비 바디 정보를 직접 조회(@RequestParam X, @ModelAttribute X)
* - HttpMessageConverter 사용 -> StringHttpMessageConverter 적용
* 
* 응답에서도 HttpEntity 사용 가능
* - 메세지 바디 정보 직접 반환 (view 조회 x)  
* - HttpMessageConverter 사용 -> StringHttpMessageConverter 적용
* 
*/
@PostMapping("/request-body-string-v3")
public HttpEntity<String> requestBodyStringV3(HttpEntity<String> httpEntity){
	String messageBody = httpEntity.getBody();
    log.info("messageBody ={}",messageBody);
    return new HttpEntity<>("ok");
}

스프링 MVC는 다음 파라미터 지원한다.

HttpEntity : Http header,body 정보를 편리하게 조회

  • 메세지 바지 정보를 직접 조회
  • 요청 파라미터를 조회하는 기능과 관계 없음 @RequestParamX, @ModelAttributeX
    HttpEntity는 응답에도 사용 가능
  • 메세지 바디 정보 직접 반환
  • 헤더 정보 포함 가능
  • view 조회 X

HttpEntity를 상속받은 다음 객체들도 같은 기능을 제공한다.

RequestREntity

  • HTTPMethod, url 정보가 추가,요청에서 사용

ResponseEntity

  • HTTP 상태 코드 설정 가능, 응답에서 사용
  • return new ResponseEntity<String>("Hello World",responseHeaders,HttpStatus.CREATED)

🤞참고 )
스프링 MVC 내부에서 HTTP 메세지 바디를 읽어서 문자나 객체로 변환해서 전달해주는데, 이때 HTTP 메세지 컨버터(HttpMessageConverter)라는 기능을 사용한다. 이것은 조금 뒤에 HTTP 메세지 컨버터에서 자세히 설명

@RequestBody - requestBodyStingV4

/**
*@RequestBody
* - 메세지 바디 정보를 직접 조회 (@RequestParam X, @ModelAttribute X)
* - HttpMessageConverter 사용 -> StringHttpMessageConverter 적용
*
*@ResponseBody
*- 메세지 바디 정보 직접 반환(view 조회 X)
*- HttpMessageConverter 사용 -> StringHttpMessageConverter 적용
*/
@ResponseBody
@PostMapping("/request-body-sting-v4")
public String requestBodyStringV4(@RequestBody String messageBody){
	log.info("messageBody={},messageBody);
    return "ok";
}

@RequestBody
@RequestBody를 사용하면 HTTP 메세지 바디 정보를 편리하게 조회할 수 있다.
참고로 헤더 정보가 필요하다면 @RequestHeader를 사용하면 된다.
이렇게 메세지 바디를 직접 조회하는 기능은 요청 파라미터를 조회하는 @RequestParam. @ModelAttribute와는 전혀 관계 없다.

요청 파라미터 vs Http 메세지 바디

요청 파라미터를 조회하는 기능 : @RequestParam, @ModelAttribute
Http 메세지 바디를 직접 조회하는 기능 : @RequestParam

@ResponseBody
@ResponseBody를 사용하면 응답 결과를 HTTP 바디에 직접 담아서 전달할 수 있다.
물론 이 경우에도 view를 사용하지 않는다.

0개의 댓글