spring에서 만들 수 있는 응답 데이터는 크게 3종류로 나누어져 있다.
각 응답 데이터를 어떻게 만들어서 응답하는지에 대해 정리해보고자 한다.
정적 리소스 : js, css, images 등과 같이 변화가 없는 리소스를 의미
동적인 html
을 리턴할 때에는 뷰 템플릿을 이용한다.
HTTP API를 작성 할 때에는 html
이 아니라 데이터를 전달해야해서 HTTP 메시지에 JSON과 같은 데이터를 실어보낼수 있도록 한다.
정적 리소스는 spring에서 코드를 따로 작성해서 응답을 하는 것이 아니라, 정적 리소스들을 미리 약속된 디렉토리에 넣어놓으면 알아서 spring이 정적 리소스를 제공한다.
이 때 미리 약속된 디렉토리는 클래스 패스의 시작 경로인 src/main/resources
안에 있는 /static
, /public
, /resources
, /META-INF/resources
이 있다.
ex
src/main/resources/static/js
에example.js
가 있다면,
/js/example.js
를 요청하면 된다.
뷰 템플릿을 거쳐서html
을 생성하고, 뷰가 응답을 만들어서 전달한다.
이때 spring은 뷰 템플릿 기본 경로를 제공하는데 변경도 가능하다.
기본 경로는 src/main/resources/templates
이곳이다.
뷰 템플릿은 컨트롤러에서 리턴하는 방식이 3가지 정도가 있다.
각 방식의 특징에 대해서 알아보자
이 방식은 컨트롤러 내에서 ModelAndView
를 생성해서 반환하는 방법이다.
생성할 때에 경로를 넣고, 만약 같이 보내야할 데이터가 있다면 생성된 ModelAndView
에 데이터를 추가해서 같이 전송한다.
@RequestMapping("/response-view-v1")
public ModelAndView responseViewV1() {
ModelAndView mav = new ModelAndView("response/hello")
.addObject("data", "hello!");
return mav;
}
이 방식은 보내야할 데이터가 있다면 model에 데이터를 넣고, 리턴은 경로만 리턴을 하는 방식이다. 앞에 ModelAndView
와 기능은 같은데 ModelAndView
를 직접 만들지 않아도 되서 코드가 훨씬 깔끔하다.
@RequestMapping("/response-view-v2")
public String responseViewV2(Model model) {
model.addAttribute("data", "hello!!");
return "response/hello";
}
이 방식은 앞의 String
을 리턴하는 방식과 비슷한데, return을 void로 처리한 방식이다.
이것은 RequestMapping
되는 경로와 뷰 템플릿의 경로가 같으면 사용할 수 있는데, 이 방식은 명시적이지 않아서 추천하지 않는다.
@RequestMapping("/response/hello")
public void responseViewV3(Model model) {
model.addAttribute("data", "hello!!");
}
이번에는 HTTP API를 제공하는 경우 html
이나 뷰 템플릿이 아닌 데이터를 응답으로 보내야하기 때문에 HTTP 메시지에 데이터를 넣어야한다.
물론 정적 리소스, 뷰 템플릿을 사용해도 HTTP 메시지에 최종 데이터가 담겨서 가지만, 정적 리소스는 동적인 데이터를 전송할 수 없고, 뷰 템플릿은 뷰 템플릿을 거쳐서 가야한다. 이렇게 정적 리소스나 뷰 템플릿을 거치지 않고 직접 HTTP 응답 메시지를 전달하는 경우를 알아 보려고 한다.
Servlet을 사용할 때 처럼 HttpServletResponse
를 통해서 직접 전달하는 방식이다.
@GetMapping("/response-body-string-v1")
public void responseBodyV1(HttpServletResponse response) throws IOException
{
response.getWriter().write("ok");
}
ResponseEntity
엔티티는 HTTP 메시지의 헤더, 바디 정보를 가지고HttpEntity
를 상속 받은 객체이다. ResponseEntity
는 여기에 더해서 HTTP 응답 코드를 설정할 수 있다.
HttpStatus
는 HTTP 응답 코드가 정의 되어있다.
전부 상수로 되어있는데200
이라고 적는 것보다HttpStatus.OK
라고 적는 것이 보기에 좀 더 직관적이라 사용한다.
@GetMapping("/response-body-string-v2")
public ResponseEntity<String> responseBodyV2() {
return new ResponseEntity<>("ok", HttpStatus.OK);
}
@GetMapping("/response-body-json-v1")
public ResponseEntity<HelloData> responseBodyJsonV1() {
HelloData helloData = new HelloData();
helloData.setUsername("userA");
helloData.setAge(20);
return new ResponseEntity<>(helloData, HttpStatus.OK);
}
@ResponseBody
어노테이션을 사용하면 리턴 값이 그대로 들어간다.
따로 생성을 하거나 하지 않기 때문에 코드가 가장 간결하다.
HTTP 응답 코드도 설정할 수 있는데,
@ResponseStatus(HttpStatus.OK)
를 사용해서 할 수 있다.
다만 위의ResponseEntity
방식처럼 동적으로 지정할 수는 없어서 동적으로 지정하고 싶다면ResponseEntity
을 사용해서 리턴시켜야한다.
@ResponseBody
@GetMapping("/response-body-string-v3")
public String responseBodyV3() {
return "ok";
}
@ResponseStatus(HttpStatus.OK)
@ResponseBody
@GetMapping("/response-body-json-v2")
public HelloData responseBodyJsonV2() {
HelloData helloData = new HelloData();
helloData.setUsername("userA");
helloData.setAge(20);
return helloData;
}
ResponseEntity
엔티티나@ResponseBody
어노테이션을 사용해서 객체 타입의 데이터를 데이터로 보낸다면HTTP 메시지 컨버터
가 동작해서 JSON 형식으로 변환된다.