후라이

[SpringMVC] Spring MVC 파헤치기 | 응답편 본문

Spring

[SpringMVC] Spring MVC 파헤치기 | 응답편

힐안 2024. 12. 26. 21:19

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

 

응답 데이터는 크게 세 가지 방식으로 나눌 수 있습니다.

  • 정적 리소스
    - ex) 웹 브라우정 정적인 HTML, css, js를 제공할 때
  • 뷰 템플릿 사용
    - ex) 웹 브라우저에 동적인 HTML을 제공할 때
  • HTTP 메시지 사용
    - ex) HTTP API를 제공하는 경우, HTML이 아닌 데이터를 전달해야하므로, HTTP 메시지 바디에 JSON과 같은 형식의 데이터를 담아 보냄

 

1.1) 정적 리소스

스프링 부트는 클래스 패스의 아래 디렉토리에 있는 정적 리소스를 제공합니다.

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

 

src/main/resources는 리소스를 보관하는 곳이고, 또 클래스 패스의 시작 경로입니다.

만약 src/main/resources/static/basic/hello-from.html이 있으면

웹 브라우저에서 http://localhost:80880/basic/hello-form.html 을 실행하면 되는 것입니다.

 

정적 리소스는 그냥 변경 없는 그대로를 서비스합니다.

버튼 등을 누른다고 해서 경로가 옮겨진다는 둥 동적인 활동이 전혀 일어나지 않습니다.

 

1.2) 뷰 템플릿

뷰 템플릿을 거쳐 HTML이 생성되고, 뷰가 응답을 만들어서 전달합니다.

 

@RequestMapping("/response-view-v2")
 public String responseViewV2(Model model) {
 model.addAttribute("data", "hello!!");
 return "response/hello";
 }

 

이런 식의 뷰를 불러내는 Controller 코드를 보면 뷰의 논리 이름인 response/hello를 반환하면, templates/response/hello.html로 뷰 템플릿이 렌더링 됩니다.

 

앞서 요청편에서와 같이 @ResponseBody, HttpEntity를 사용하면, 뷰 템플릿 말고 HTTP 메시지 바디에 직접 응답 데이터를 출력할 수 있겠습니다.

 

1.3) HTTP 응답 - HTTP API, 메시지 바디에 직접 입력

 

HTTP API를 제공하는 경우에는 HTML이 아니라 데이터를 전달해야 하므로, HTTP 메시지 바디에 JSON과 같은 형식으로 데이터를 담아 보냅니다.

@ResponseBody
@GetMapping("/response-body-string-v3")
public String responseBodyV3() {
 return "ok";
 }

 

위 예시는 String을 보낼 때 입니다.

단순하게 보이겠지만 많은 과정이 생략되어 있습니다.

 

원래는 HttpServletResponse로 getWriter()를 쓰겠습니다만, HttpEntity를 만들어 해당 과정을 간략화할 수 있겠고,

@ResponseBody를 쓰면 view를 사용하지 않고 HTTP 메시지 컨버터를 통해서 HTTP 메시지를 직접 입력하기에 위와 같이 사용하면 됩니다. (물론 ResponseEntity도 동일한 방식으로 동작합니다.)

 

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

 

위 예시는 JSON 데이터를 담은 응답을 전달할 때 입니다.

여기서도 동일하게 그냥 @ResponseBody 애너테이션을 쓰면 됩니다.

다만, 이 경우에는 HTTP Method를 설정하기 어렵습니다. (까다로움) 그래서

@ResponseStatus(HttpStatus.OK) 와 같이 응답 코드를 설정하여 쓸 수 있습니다.

 

사실 @Controller 대신 @RestController를 사용하면 @ResponseBody를 사용하는 효과를 얻을 수 있습니다.
해당 컨트롤러에 모두 적용되는 것입니다.

 


HttpMessageConveter??

 

@ResponseBody는 아래와 같이 동작합니다.

  • HTTP의 Body에 문자 내용을 직접 반환
  • 뷰 리졸버 대신 HttpMessageConverter가 동작
  • 기본 문자 : StringHttpMessageConverter
    기본 객체 : MappingJackson2HttpMessageConverter
    byte 처리 등 여러 HttpMessageConverter가 등록되어 있음

그럼, 여기서 HttpMessageConverter가 뭐냐?
스프링 MVC는 다음의 경우에 HttpMessageConverter를 적용합니다.

  • HTTP 요청 : @RequestBody, HttpEntity(RequestEntity),
  • HTTP 응답 : @ResponseBody, HttpEntity(ResponseEntity),

HTTP 메시지 컨버터는 요청, 응답 둘 다 사용됩니다.

스프링 부트는 다양한 메시지 컨버터를 제공하는데, 대상 클래스 타입과 미디어 타입 둘 다를 체크해서 사용 여부를 결정하게 되고, 만약 만족하지 않으면 다음 메시지 컨버터로 우선순위가 넘어가게 됩니다.

 

요청과 응답의 각각의 케이스를 자세히 살펴보겠습니다.

 

  • HTTP 요청 데이터 읽기
    HTTP 요청이 오면, Controller에서 @RequestBody, HttpEntity 파라미터를 사용할 것입니다.
    -> 메시지 컨버터가 메시지를 읽을 수 있는지 확인을 위해 canRead()를 호출합니다.
    - 대상 클래스 타입을 지원하는가 ex) @RequestBody의 대상 클래스 (byte[], String, HelloData,,)
    - HTTP 요청의 Content-Type 미디어타입을 지원하는가 ex) text/plain, application/json, */*
    -> canRead() 결과 조건을 만족하면 read()를 호출해 객체가 생성되고, 반환합니다.

  • HTTP 응답 데이터 생성
    Controller에서 @ResponseBody, HttpEntity로 값이 반환됩니다.
    -> 메시지 컨버터가 메시지를 쓸 수 있는지 확인을 위해 canWrite()를 호출합니다.
    대상 클래스 타입을 지원하는가 ex) return의 대상 클래스 (byte[], String, HelloData)
    - HTTP 요청의 Accept 미디어 타입을 지원하는가 ex) text/plain, application/json, */*
    -> canWrite() 결과 조건을 만족하면 write()를 호출해서 HTTP 응답 메시지 바디에 데이터를 생성합니다.