모델, 뷰, 컨트롤러로 나누어서 개발하는 걸 MVC 패턴이라고 한다. 요즘에는 대부분 이렇게 개발을 한다고 한다. 백엔드, 프론트를 모두 통틀어서 생각해보면 당연히 비즈니스 로직은 분리 되어 있는 건 맞고 그렇다면 전체적인 맥락에서는 지금 대부분의 것들은 관심사를 분리해서 사용하고 있는 것이 대부분이기 때문에 MVC 패턴을 따른다고 할 수 있을까?
templates 폴더에 새로운 html 파일을 만들고.. 새로운 controller 메서드를 정의한다음에 RequestParam으로 파라미터를 받아서 요청할 수 있게 한다.
RequsetParam은 기본적으로 required가 true다.
package com.example.demo.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;
@Controller
public class HelloController {
@GetMapping("hello")
public String helloSpring(Model model) {
model.addAttribute("dataSet", "hello! spring!!");
return "hello";
}
@GetMapping("hello-mvc")
public String helloMVC(@RequestParam(value = "name", required = false) String name, Model model) {
model.addAttribute("name", name);
return "hello";
}
}
@GetMapping("hello-mvc")
// 생략 가능
public String helloMVC(@RequestParam("name") String name, Model model) {
model.addAttribute("name", name);
return "hello";
}
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Hello</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<p th:text="'안녕하세요. ' + (${dataSet} != null ? ${dataSet} : '') + (${name} != null ? ' ' + ${name} : '')"></p>
</body>
</html>
기존에 정적 컨텐츠를 내려주는 방식이나 템플릿 엔진을 이용해서 html을 이용해서 내려주는 방식과는 다르게 데이터자체를 내려주는 방식을 API 방식이라고 이야기한다.
스프링을 이용해서 기존 API 방식으로 내려주려고 할 때는 @ResponseBody 어노테이션을 사용하면 된다.
@GetMapping("hello-string")
@ResponseBody
public String helloString(@RequestParam("name") String name) {
return name;
}
API는 보통 JSON 방식의 객체로 내려오게 되는데, 톰캣 엔진을 통해 컨트롤러가 감지할 때 값이 그냥 문자열로 들어가게 되면 HttpMessageConverter가 StringConverter를 작동시켜서 문자열로 그대로 보내주게 되고 자바의 클래스로 정의하게 되면 인스턴스는 객체 형식이기 때문에 객체가 감지되면 JsonConverter를 이용해서 객체로 변환해서 내려주게 된다. 이때 보통 기본적으로 설정되어 있는 JsonConverter는 Jackson 을 사용한다.
@GetMapping("hello-api")
@ResponseBody
public Hello helloAPI(@RequestParam("name") String name, @RequestParam("phone") String phone, @RequestParam("age") int age) {
Hello hello = new Hello();
hello.setName(name);
hello.setPhone(phone);
hello.setAge(age);
return hello;
}
static class Hello {
private String name;
private String phone;
private int age;
public void setName(String name) {
this.name = name;
}
public void setPhone(String phone) {
this.phone = phone;
}
public void setAge(int age) {
this.age = age;
}
}
하지만 위의 코드는 동작하지 않는다. 이유는 getter가 없기 때문이다.
Jackson 라이브러리는 객체를 JSON으로 변환할 때 객체의 getter 메서드를 사용한다. Hello
클래스에 getter 메서드가 정의되어 있지 않으면, Jackson은 해당 필드의 값을 JSON으로 변환할 수 없다. 따라서, Hello
클래스에 getter 메서드를 추가해야 한다.
그리고 int 값의 경우도 스프링이 알아서 변환해서 적용시켜준다. URL은 string 값이다. 프론트에서는 그렇게 내려오는 코드를 숫자값과 비교하려면 변환해서 비교작업을 해야 했다. 하지만, Spring Framework는 @RequestParam
을 통해 전달받은 문자열 파라미터를 자동으로 해당 타입으로 변환해주는 타입 변환 기능을 제공한다. 즉, URL 쿼리 파라미터에서 전달된 문자열을 컨트롤러 메서드의 인자 타입(int, Integer 등)에 맞게 자동으로 변환해준다.
그래서 아래와 같이 변경하면 이제 여러가지 파라미터를 받는 api가 하나 완성된다.
@GetMapping("hello-api")
@ResponseBody
public Hello helloAPI(@RequestParam("name") String name, @RequestParam("phone") String phone, @RequestParam("age") int age) {
Hello hello = new Hello();
hello.setName(name);
hello.setPhone(phone);
hello.setAge(age);
return hello;
}
static class Hello {
private String name;
private String phone;
private int age;
public String getName() {
return name;
}
public String getPhone() {
return phone;
}
public int getAge() {
return age;
}
public void setName(String name) {
this.name = name;
}
public void setPhone(String phone) {
this.phone = phone;
}
public void setAge(int age) {
this.age = age;
}
}