기본 동작 방식 3가지
1. 정적 컨텐츠
2. MVC와 템플릿 엔진
3. API
resources/static 디렉토리 안에 index.html 파일을 넣고 다음과 같이 작성합니다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
정적 컨텐츠 입니다.
</body>
</html>
그리고 바로 스프링 서버를 키고,
로컬호스트에 http://localhost:8080/index.html
로 접속하면 아까 작성한 정적 컨텐츠가 그대로 반환됨을 확인할 수 있습니다.
이는 다음 그림과 같은 작동 원리로 동작합니다.
작동 과정
- 웹 브라우저에서
http://localhost:8080/index.html
로 요청- 내장 톰켓 서버가 요청을 받고 스프링 컨테이너에 넘긴다.
- 스프링 컨테이너는 컨트롤러에서 index 관련 컨트롤러를 찾는다.(컨트롤러가 우선순위를 가짐) -> 하지만 해당 컨트롤러 없음
- 해당 컨트롤러가 없으면 resources/static/ 디렉토리 안에 index.html이 있는지 찾는다. -> 발견
- 발견되면 해당 파일을 넘긴다.
// HelloController.java
package hello.hellospring.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class HelloController {
@GetMapping("hello-mvc")
public String helloMvc(@RequestParam("name") String name, Model model){
model.addAttribute("name", name);
return "hello-template";
}
}
<!-- resources/templates/hello-template.html -->
<html xmlns:th="http://www.thymeleaf.org">
<body>
<p th:text="'hello ' + ${name}">hello! empty</p>
</body>
이렇게 작성 후에,
스프링 서버를 키고 로컬호스트에 http://localhost:8080/hello-mvc?name=수진
으로 접속하면 다음과 같습니다.
이는 다음과 같은 작동 원리로 동작합니다.
작동 과정
- 웹 브라우저에서
http://localhost:8080/hello-mvc?name=수진
로 요청- 내장 톰켓 서버가 요청을 받고 스프링 컨테이너에 넘긴다.
- 스프링 컨테이너는 컨트롤러에서 hello-mvc에 매핑되는 컨트롤러를 찾고, (찾아서) 이를 호출한다.
- 호출된 컨트롤러가 (key, value)값과 함께 모델을 만들고, 리턴될 hello-template과 함께 이를 viewResolver에게 전달한다.
- viewResolver: view를 찾아주고 템플릿과 연결시켜주는 역할
- viewResolver가 templates/hello-template.html을 찾고 이를 Thymeleaf 템플릿 엔진에게 넘긴다.
- Thymeleaf은 렌더링을 한 후, 변환을 한 html을 웹 브라우저에 반환을 한다.
✅ 1 vs 2
// HelloController.java
package hello.hellospring.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class HelloController {
@GetMapping("hello-string")
@ResponseBody // ResponseBody 사용: viewResolver대신 HttpMessageConverter가 동작
public String helloString(@RequestParam("name") String name){
return "hello" + name; // 이 문자열 자체를 http응답으로 전달 // StringConverter 가 동작 -> 문자열 그대로 반환
}
}
이를 호출하면 다음과 같습니다.
코드를 보면 먼저 @ResponseBody 가 새로 붙은 것을 확인할 수 있습니다.
이는 return을 template으로 처리하지 않고,
응답 메시지의 body 부분에 해당 return 값을 전달하게 됩니다.
(예시에서의 해당 return 값: "hello" + name
그래서 viewResolver 대신 HttpMessageConverter가 동작합니다.
Converter 중에서도 여러 개가 있지만,
여기에서는 이 중에서도 단순 문자열을 반환하는 StringConverter가 작동하게 됩니다.
또한 모델이 이용되지 않았습니다.
// HelloController.java
package hello.hellospring.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class HelloController {
@GetMapping("hello-string")
@ResponseBody // ResponseBody 사용: viewResolver대신 HttpMessageConverter가 동작
public String helloString(@RequestParam("name") String name){
return "hello" + name; // 이 문자열 자체를 http응답으로 전달 // StringConverter 가 동작 -> 문자열 그대로 반환
}
@GetMapping("hello-api")
@ResponseBody
public Hello helloApi(@RequestParam("name") String name){
Hello hello = new Hello();
hello.setName(name);
return hello; // "객체"를 반환 -> 객체일 시 JsonConverter가 동작 -> Json형식으로 반환
}
static class Hello {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
}
hello-api를 호출하면 다음과 같습니다.
아래의 hello-api 는 문자열이 아닌 "객체"를 반환합니다.
객체 반환 시에는 여러 converter 중에서도 JsonConverter가 동작하게 되며,
Json형식으로 반환합니다.
위의 @ResponseBody 에서 문자열 반환, 객체를 반환하는 두 개의 작동 원리는 다음 그림과 같습니다.