Spring/SpringMVC

HTTP 요청 메시지 - JSON & HTTP 응답 - 정적 리소스, 뷰 템플릿

느리지만 꾸준하게 2022. 5. 1. 00:33

 

HTTP API에서 주로 사용하는 JSON 데이터 형식을 알아보자.

 

 

JSON 형식을 다루므로 ObjectMapper을 쓰자.

 

실행을 시켜주면

// RequestBodyJsonController Class

package hello.springmvc.basic.request;


import com.fasterxml.jackson.databind.ObjectMapper;
import hello.springmvc.basic.HelloData;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.util.StreamUtils;
import org.springframework.web.bind.annotation.PostMapping;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;


/**
 * {"username":"hello", "age":20}
 * content-type: application/json
 */
@Slf4j
@Controller
public class RequestBodyJsonController {

    private ObjectMapper objectMapper = new ObjectMapper();

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

}

 

Postman에서 확인하면 잘 동작한다.

2022-04-30 23:59:17.039  INFO 46588 --- [nio-8080-exec-3] h.s.b.request.RequestBodyJsonController  : messageBody={"username":"hello", "age":20}
2022-04-30 23:59:17.087  INFO 46588 --- [nio-8080-exec-3] h.s.b.request.RequestBodyJsonController  : username=hello, age=20

 

 

 

 

v2로는 ResponseBody와 RequestBody를 써서 간단하게 나타내보자.

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

 

 

역시 결과가 잘 나온다.

 

2022-05-01 00:06:23.512  INFO 46669 --- [nio-8080-exec-2] h.s.b.request.RequestBodyJsonController  : messageBody={"username":"hello", "age":20}
2022-05-01 00:06:23.558  INFO 46669 --- [nio-8080-exec-2] h.s.b.request.RequestBodyJsonController  : username=hello, age=20

 

 

 

굳이 objectMapper를 넣어줘야 할까? 더 간단하게 v3로 나타내주자.

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

2022-05-01 00:08:47.464  INFO 46694 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 2 ms
2022-05-01 00:08:47.562  INFO 46694 --- [nio-8080-exec-1] h.s.b.request.RequestBodyJsonController  : username=hello, age=20

 

@RequestBody 객체 파라미터

  • @RequestBody HelloData data
  • @RequestBody에 직접 만든 객체를 지정할 수 있다.

 

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

 

 

 

@RequestBody는 생략 불가능

값이 안들어가진다.

2022-05-01 00:14:15.472  INFO 46748 --- [nio-8080-exec-1] h.s.b.request.RequestBodyJsonController  : username=null, age=0

v3에서 RequestBody를 생략하면 @ModelAttribute가 된다.

@ResponseBody
@PostMapping("/request-body-json-v3")
public String requestBodyJsonV3(@ModelAttribute HelloData helloData) {
    log.info("username={}, age={}", helloData.getUsername(), helloData.getAge());
    return "ok";
}

 

 

스프링은 @ModelAttribute , @RequestParam 해당 생략시 다음과 같은 규칙을 적용한다.
String , int , Integer 같은 단순 타입 = @RequestParam
나머지 = @ModelAttribute (argument resolver 로 지정해둔 타입 외)

 

 

따라서 이 경우 HelloData에 @RequestBody 를 생략하면 @ModelAttribute 가 적용

HelloData data @ModelAttribute HelloData data

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

 

 

HTTP 요청시에 content-type이 application/json인지 꼭! 확인해야 한다. 그래야 JSON을 처리할 수
있는 HTTP 메시지 컨버터가 실행된다

 

 

 

 

 

HttpEntity를 써보자.

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

 

 

 

v5를 한번 살펴보자.

// hellodata 객체가 HTTP message Converter에 의해서 json을 바뀐다.
// 바뀐 json 문자가 http message body에 넣어져 고객 응답으로 나가게 된다.
@ResponseBody
@PostMapping("/request-body-json-v5")
public HelloData requestBodyJsonV5(@RequestBody HelloData data) {
    log.info("username={}, age={}", data.getUsername(), data.getAge());
    return data;
}

 

요청을 한 json 형식같이 응답도 json 형식으로 온다.(객체 => json => 객체)

 

  • @ResponseBody
  • 응답의 경우에도 @ResponseBody 를 사용하면 해당 객체를 HTTP 메시지 바디에 직접 넣어줄 수 있다.
  • 물론 이 경우에도 HttpEntity 를 사용해도 된다.

 

  • @RequestBody 요청
  • JSON 요청 HTTP 메시지 컨버터 객체

 

  • @ResponseBody 응답
  • 객체 HTTP 메시지 컨버터 JSON 응답

 

 

 

 

 

 

 

HTTP 응답 - 정적 리소스, 뷰 템플릿를 살펴보면

  • 정적 리소스
  • 예) 웹 브라우저 정적인 HTML CSS JS을 제공할 때 정적 리소스 사용

 

  • 뷰 템플릿 사용
  • 예) 웹 브라우저에 동적인 HTML을 제공할 때 뷰 템플릿 사용

 

  • HTTP 메시지 사용
  • HTTP API를 제공하는 경우에는 HTML이 아니라 데이터를 전달

 

 

 

 

 

 

 

// ResponseViewController Class

package hello.springmvc.basic.response;


import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class ResponseViewController {

    @RequestMapping("/response-view-v1")
    public ModelAndView responseViewV1() {
        ModelAndView mav = new ModelAndView("response/hello")
                .addObject("data", "hello!");
        return mav;
    }
}

 

 

 

렌더링이 될 때 th: 부분이 p 태그로 변하게 되어서 hello!로 바꾸게 된다.

// 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>

 

 

이제는 string으로 반환해서 v2로 나타내어보자.

@RequestMapping("/response-view-v2")
public String responseViewV1(Model model) {
    model.addAttribute("data", "hello!");
    // 뷰의 논리적인 이름으로 된다.
    return "response/hello";
}

 

 

@ResponseBody를 넣게 되면 어떻게 될까 view로 안가고 문자로 바로 출력된다. (@RestController도 마찬가지이다.)

@ResponseBody
@RequestMapping("/response-view-v2")
public String responseViewV1(Model model) {
    model.addAttribute("data", "hello!");
    // 뷰의 논리적인 이름으로 된다.
    return "response/hello";
}

 

아래 방법도 있는데 그냥 쓰지말자.

@RequestMapping("/response/hello")
public void responseViewV3(Model model) {
    model.addAttribute("data", "hello!");
}

 

 

v2가 제일 나으므로 v2를 쓰자

@RequestMapping("/response-view-v2")
public String responseViewV1(Model model) {
    model.addAttribute("data", "hello!");
    // 뷰의 논리적인 이름으로 된다.
    return "response/hello";
}

 

그리고 처음에 프로젝트 설정할 때 스프링 부트에 타임리프가 들어간다.

dependencies {
   implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'

 

스프링 부트가 자동으로 ThymeleafViewResolver와 필요한 스프링 빈들을 등록해주고 설정도 사용한다.

설정은 기본 값이여서 변경이 필요할 때만 설정하면 된다.

// application.properties

spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html

스프링 부트 타임리프 관련 추가 설정은 여기를 참고하자.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

<출처 김영한: 스프링 MVC 1편 - 벡앤드 웹 개발 핵심 기술>

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-1/dashboard

 

스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술 - 인프런 | 강의

웹 애플리케이션을 개발할 때 필요한 모든 웹 기술을 기초부터 이해하고, 완성할 수 있습니다. 스프링 MVC의 핵심 원리와 구조를 이해하고, 더 깊이있는 백엔드 개발자로 성장할 수 있습니다., -

www.inflearn.com