[Spring - MVC] MVC -기본 기능 part2 (8)

조대훈·2024년 2월 28일

김영한 Spring MVC -1

목록 보기
8/9
post-thumbnail

HTTP 요청 파라미터 -@RequestParam

스프링이 제공하는 @RequestParam을 사용하면 요청 파라미터를 매우 편리하게 사용할 수 있다.

@ResponseBody
@RequesMapping("/request-param-v2")
public String requestParamV2(@RequestParam("username") String memberName, @RequestParam("age")int memberAge){

	log.info("username={},age={}",memberName,memberAge);
	return "ok";
}
  • @RequestParam 파라미터 이름 바인딩
  • @ResponseBody 클래스에@Controller가 달려있을 때 Veiw조회를 무시하고 Http message body에 return 값을 직접 입력한다.

@RequestParamrequest.getParameter()을 축약되어진 것이라고 보면 된다.

축약 1단계 - 파라메터 생략

리퀘스트 파람에 파라메터명을 안넣어도 된다.

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

HTTP 파라미터 이름이 변수 이름과 같으면 파라메터명 생략이 가능하다.

축약 2단계 - 어노테이션 생략

심지어 리퀘스트 파람 어노테이션을 안달아도 된다.

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

단순 타입이면 어노테이션도 생략 가능하다
String, int, Integer

하지만 요청 어노테이션 마저 생략 해버리면 Http 요청 파라메터에서 데이터를 읽어온다는 것에 직관성이 사라져버린다.

Spring3.x 이상 부터는 어노테이션 생략시 컴파일 오류가 발생 하는데 build를 Intellij 로 해서 그렇다. gradlef로 build시 오류가 나지 않는다.

required=true || flase

필수 입력 값: true or false

@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";  
}

여담으로 Integer는 객체형이므로 NULL 값이 들어갈 수 있지만 int0 이라도 무조건 들어가야 한다.

따라서 int age 로 파라메터 설정 후 입력하지 않으면 500 server error가 난다.

required=true인 username을 입력하지 않으면 400 error 가 발생한다.

NULL"" 빈 문자는 다른 값이다

default=""

@ResponseBody  
@RequestMapping("/request-param-default")  
public String requestParamDefault(  
        @RequestParam(required = true,
        defaultValue = "guest")String username,  
        @RequestParam(required = false,
        )Integer age){  
  
    log.info("username={},age={}", username, age);  
  
    return "ok";  
}

빈 문자null 로 취급해 defaultValue 가 들어간다

username"" 입력시 defaultValueguest 가 적용

Map으로 @RequestParam

@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으로 @RequestParam()을 받는다.

  • @RequestParam Map ,
    - Map(key=value)
  • @RequestParam MultiValueMap
    - MultiValueMap(key=[value1, value2, ...] ex) (key=userIds, value=[id1,id2])

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


HTTP 요청 파라미터 -@ModelAttribute

실제 개발엔 요청 파라미터를 받아서 필요한 객체를 만들고, 그 객체에 값을 넣어주어야 한다.

@RequestParam String username;
@RequestParam int age;

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

스프링은 이 과정은 완전히 자동화 해주는 @ModelAttribute 기능을 제공 한다.

@ModelAttribute 적용 전

@ResponseBody  
public String modelAttributeV1(@ModelAttribute String username, @RequestParam int age) {  
    HelloData helloData = new HelloData();  
    helloData.setAge(age);  
    helloData.setUsername(username);  
  
    log.info("username={},ag={}", username, age);  
    log.info("helloData={}", helloData);  
  
    return "ok";  
}

@ModelAttribute 적용 후

@ResponseBody  
@RequestMapping("/model-attribute-v1")  
public String modelAttributeV1(@ModelAttribute HelloData helloData) {  
    log.info("username={},age={}", helloData.getUsername(), helloData.getAge());  
    return "ok";  
}

바인딩할 객체에 요청한 값을 set하는 과정 없이 단순히 @ModelAttribute 만 넣으면 알아서 바인딩을 해준다

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

파라미터 이름이 username 이면 setUserName()을 찾아 호출하여 값을 입력한다

프로퍼티
객체에 getUsername() , setUsername() 메서드가 있으면, 이 객체는 username 이라는 프로퍼티를 가지고 있 다.

username 프로퍼티의 값을 변경하면 setUsername() 이 호출되고, 조회하면 getUsername() 이 호출된다.

 class HelloData {
     getUsername();
     setUsername();

}

바인딩 오류
age=abc 처럼 숫자가 들어가야 할 곳에 문자를 넣으면 BindException 이 발생한다.

@ModelAttribute 생략

@ResponseBody
@RequestMapping("/model-attribute-v2")
public String modelAttributeV2(HelloData helloData){

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

     return "ok";
 }
}

@RequstParam도 생략이 가능하다면서 구분은 어떻게 하지?

  • String,int,Integer 단순 타입은 @RequestParam
  • 나머지는 @ModelAttriute
    - ArgumentResolver는 제외

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

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

  • HTTP API에서 주로 사용, JSON, XML, TEXT
  • POST, PUT, PATCH

요청 파라미터와 다르게 HTTP body를 통해 들어오는 데이터는 @RequestParam, @ModelAttribute를 쓸수 없다.
GET,HTML Form 형식은 가능하다

축약 전

@PostMapping("/request-body-string-v1")  
public void requestBodyString(HttpServletRequest request, HttpServletResponse response)throws Exception {  
    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 Exception {  
    String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);  
  
    log.info("messageBody={}", messageBody);  
  
    responseWriter.write("ok");  
}

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

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

축약 후 -2

    @PostMapping("/request-body-string-v3")  
    public HttpEntity requestBodyStringV2(HttpEntity<String> httpEntity)throws Exception {  
  

        String messageBody = httpEntity.getBody();  
//        변환된 바디를 꺼냄  
  
        log.info("messageBody={}", messageBody);  
//       반화 또한 비슷하게 HttpEntity 자체를 반환하면 된다. 파라메터로 body부분을 지정할 수 있다.  
        return new HttpEntity<>("ok");  
    }

스프링이 알아서 클라이언트로 부터 받는 데이터의 종류를 보고 알아서 변환해준다.
불필요한 인풋 스트림을 얻는 코드나, 문자형식을 지정해주는 코드가 필요 없어졌다.

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

HttpEntity : HttpHeader,body 정보를 편리하게 조회한다.

  • 메시지 바디 정보를 직접 조회
  • 요청 파라미터를 조회하는 기능과는 관계 없다 !!!
    - @RequestParam, @ModelAtrribute와 무관

HttpEntity응답에도 사용 가능

  • 메세지 바디 정보 직접 변환
  • 헤더 정보 포함 기능
  • view 조회X

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

RequestEntity

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

ResponseEntity

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

스프링 MVC 내부에서 HTTP 메시지 바디를 읽어서 문자나 객체로 변환해서 전달해주는데, 이때 HTTP 메시지 컨버터(HttpMessageConverter) 라는 기능을 사용한다.

축약 3-

    @PostMapping("/request-body-string-v3")  
    public HttpEntity requestBodyStringV2(RequestEntity<String> httpEntity)throws Exception {  
  
        String messageBody = httpEntity.getBody();  
        log.info("messageBody={}", messageBody);  
        return new ResponseEntity<String>("ok",HttpStatus.CREATED);  
    }

축약 4-


@ResponseBody  
@PostMapping("/request-body-string-v4")  
public String requestBodyStringV4(@RequestBody String messageBody)throws Exception {  
    log.info("messageBody={}", messageBody);  
  
    return "ok";  
}

@RequstBody

@RequestBody 를 사용하면 HTTP 메시지 바디 정보를 편하게 조회할 수 있다. 헤더 정보가 필요하다면 HttpEntity 혹은 @RequestHeader 를 사용하면 된다.

메세지 바디를 직접 조회하는 기능은 파라미터 조회 기능과는 관계가 없다!
@RequestParam,@ModelAttribute

@ResponseBody

@ResponseBody 를 사용하면 응답 결과를 HTTP 메시지 바디에 직접 담아서 전달 가능하다.


HTTP 요청 메세지- JSON

HTTP API에서 주로 사용하는 JSON 데이터 형식 조회

축약 전 - 기존 HttpServlet 방식

  • HttpServletRequest 를 사용해서 직접 HTTP 메시지 바디에 데이터를 읽어온 후 , 문자로 변환한다
  • 문자로된 JSON 데이터를 Jackson 라이브러리인 objectMapper 를 사용해서 자바 객체로 변환한다.
@PostMapping("/reuqest-body-json-v1")  
public void requestBodyJsonV1(HttpServletRequest request, HttpServletResponse response) throws Exception {  
    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");  
}  
  1. requestgetInputStream() 얻기
  2. InputStream() 에서 얻어온 데이터를 문자로 직접 변환하기
  3. 문자로 변환된 JSON 데이터를 objectMapper를 사용해 직접 자바 객체로 변환하기

축약 1- @RequestBody 문자 변환

@ResponseBody  
@PostMapping("/reuqest-body-json-v2")  
public String requestBodyJsonV2(@RequestBody String messageBody) throws Exception {  
  
    log.info("messageBody={}",messageBody);  
    HelloData helloData = objectMapper.readValue(messageBody, HelloData.class);  
    log.info("username={},age={}", helloData.getUsername(), helloData.getAge());  
  
    return "ok";  //바디에 직접 반환해서 String "ok";
}  
  • @RequestBody 를 사용해 HTTP 메세지에서 데이터를 꺼내고 messageBody 에저장한다.
  • 문자로된 JSON 데이터인 messageBody를 objectMapper를 통해서 자바 객체로 변환한다.

@ResponseBody : HttpMessageConverter,StringMessageConverter사용
@RequestBody :바디에 직접 반환,
HttpMessageConverter,StringMessageConverter사용

문자로 변환하고, JSON으로 변환하는 과정이 전 부 다 귀찮다.. @ModelAttribute 처럼 객체로 한 번에 변환 시키고 싶다...

축약2- @RequestBody 객체 변환

@ResponseBody  
@PostMapping("/reuqest-body-json-v3")  
public String requestBodyJsonV2(@RequestBody HelloData helloData) throws Exception {  
  
    log.info("username={},age={}", helloData.getUsername(), helloData.getAge());
  
    return "ok";  
}

@RequestBody 객체 파라미터
@RequestBody HelloData data
@RequestBody 에 직접 만든 객체를 지정할 수 있다.

HttpEntity , @RequestBody 를 사용하면 HTTP 메시지 컨버터가 HTTP 메시지 바디의 내용을 우리가 원하는 문 자나 객체 등으로 변환해준다.
HTTP 메시지 컨버터는 문자 뿐만 아니라 JSON도 객체로 변환해주는데, 우리가 방금 V2에서 했던 작업을 대신 처리 해준다. @RequestBody는 생략 불가능

@ModelAttribute 에서 학습한 내용을 떠올려보자.

스프링은 @ModelAttributeㅋ , @RequestParam 과 같은 해당 애노테이션을 생략시 다음과 같은 규칙을 적용한다. String , int , Integer 같은 단순 타입 = @RequestParam
나머지 = @ModelAttribute (argument resolver 로 지정해둔 타입 외)
따라서 이 경우 HelloData에 @RequestBody 를 생략하면 @ModelAttribute 가 적용되어버린다. HelloData data @ModelAttribute HelloData data

따라서 생략하면 HTTP 메시지 바디가 아니라 요청 파라미터를 처리하게 된다.

축약3- HttpEntity 사용

@ResponseBody  
@PostMapping("/reuqest-body-json-v4")  
public String requestBodyJsonV4(@RequestBody HttpEntity<HelloData> data) throws Exception {  
  
    HelloData body = data.getBody();  
    log.info("username={},age={}", body.getUsername(), body.getAge());  
  
    return "ok";  
}

축약4 파싱한 객체를 그대로 반환시 우리에게 보이는 과정 JSON-> ?? ->JSON

@ResponseBody  
@PostMapping("/reuqest-body-json-v5")  
public HelloData requestBodyJsonV5(@RequestBody HelloData data) throws Exception {  
  
    log.info("username={},age={}", data.getUsername(), data.getAge());  
  
    return data;  
}
* @RequestBody 생략 불가능(@ModelAttribute 가 적용되어 버림)  
* HttpMessageConverter 사용 -> MappingJackson2HttpMessageConverter (content-type:
 application/json)

- *  @ResponseBody 적용
- *  - 메시지 바디 정보 직접 반환(view 조회X)  
- *  - HttpMessageConverter 사용 -> MappingJackson2HttpMessageConverter 적용(Accept:
application/json)
  • @ResponseBody
    • 응답의 경우에도 @ResponseBody 를 사용하면 해당 객체를 HTTP 메시지 바디에 직접 넣어줄 수 있다. 물론 이 경우에도 HttpEntity 를 사용해도 된다.
  • @RequestBody 요청
    - JSON 요청 -> 메세지 컨버터 -> 객체
  • @ResponseBody 응답
    - 객체 ->HTTP 메시지 컨버터 ->JSON 응답

HTTP 응답 - 정적 리소스, 뷰 템플릿

HTTP 요청에는 크게 3가지가 있고 응답도 마찬가지로 3가지가 있다.
정적 리소스, 뷰 템플릿, HTTP 메시지

  • 정적 리소스
    - 브라우저에 정적인 HTML, CSS, js
  • 뷰 템플릿 사용
    - 웹브라우저에 동적인 HTML을 제공할 때는 뷰 템플릿을 사용한다
  • HTTP 메시지 사용
    - HTTP API를 제공하는 경우에는 HTML이 아니라 데이터를 전달해야 하므로, HTTP 메세지 바디에 JSON같은 형식으로 데이터를 실어 보낸다.

정적 리소스

스프링 부트는 클래스 패스의 다음 디렉토리에 있는 정적 리소스를 제공한다

/static /public /resources /META-INF/resources ,

src/main/resources는 리소스를 보관하는 곳이고, 클래스패스의 시작이다. 다음 디렉토리에 리소스를 넣어두면 스프링 부트가 정적 리소스로 서비스를 제공한다.

정적 리소스 경로

src/main/resources/static
다음 경로에 파일이 들어있으면

src/main/resources/static/basic/hello-form.html 웹 브라우저에서 다음과 같이 실행하면 된다.

http://localhost:8080/basic/hello-form.html 정적 리소스는 해당 파일을 변경 없이 그대로 서비스하는 것이다.

뷰 템플릿

뷰 템플릿을 거쳐서 HTML이 생성되고, 뷰가 응답을 만들어서 전달한다.
일반적으로 HTML을 동적으로 생성하는 용도로 사용하지만, 다른 것들도 가능하다. 뷰 템플릿이 만들 수 있는 것이라 면 뭐든지 가능하다.

스프링 부트는 기본 뷰 템플릿 경로를 제공한다.

뷰 템플릿 경로 src/main/resources/templates

뷰 템플릿 생성 src/main/resources/templates/response/hello.html


 <!DOCTYPE html>
 <html xmlns:th="http://www.thymeleaf.org">
 <head>
     <meta charset="UTF-8">
     <title>Title</title>
 </head>

<body>
 <p th:text="${data}">empty</p>
</body>
</html>
@RequestMapping("/response-view-v1")  
public ModelAndView responseView1(){  
    ModelAndView mav = new ModelAndView("response/hello")  
            .addObject("data", "hello");  
    return mav;  
}  
  
@RequestMapping("/response-view-v2")  
public String responseView2(Model model){  
    model.addAttribute("data", "hello!");  
    return "response/hello";  
}
 @RequestMapping("/response/hello")  
    public void responseView3(Model model){  
        model.addAttribute("data", "hello!");  
//        컨트롤러의 이름과 뷰의 논리적 이름이 같고 아무것도 반환을 안하면  
//        컨트롤러의 이름이 주소가 됨  
  
    }
profile
백엔드 개발자를 꿈꾸고 있습니다.

0개의 댓글