스프링 웹 개발 기초
1. 정적 컨텐츠
서버에서 하는 것 없이 파일을 웹 브라우저에 그대로 내려주는 것을 정적 컨텐츠라고 하며 mvc 템플릿은 서버에서 프로그래밍해서 페이지를 동적으로 바꿔주는 것을 의미한다. 이 Model View Controller라고해서 mvc이며 이 패턴으로 개발을 많이 한다. API는 안드로이드나 아이폰으로 개발을 해야할 경우, json이라는 데이터 포맷으로 클라이언트에 데이터를 옮겨주는 방식이다. view, react를 쓸때에도 API로 데이터만 내려주면 화면은 클라이언트가 알아서 그리고 정리하는 API방식을 많이 사용한다.
스프링 부트는 정적 컨텐츠를 자동으로 제공한다. resources 파일 밑에 static 파일 밑에 html 파일을 두고 http://localhost/파일명.html로 접속을 하면 작성해놓은 웹 문서가 그대로 브라우저위에 뿌려진다. 웹 브라우저에서 내장된 톰캣 서버가 이 요청을 받고 이 요청을 스프링 컨테이너에게 넘긴다. 스프링 컨테이너는 컨트롤러쪽에서 해당 이름의 컨트롤러가 있는지 찾아본다. 해당 컨트롤러가 매핑되지 않으면 내부에 있는 resources 안에 있는 해당 이름의 파일을 찾는다.
우리가 저번 수업에 static 폴더 밑에 만든 index.html을 예로 들 수가 있다.
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Hello</title>
</head>
<body>
Hello
<a href="/hello">Hello</a>
</body>
</html>
2. MVC와 템플릿 엔진
MVC는 Model, View, Controller를 의미한다. 과거에는 View와 Controller가 분리되어있지 않았다. View에서 모든 것을 다했다. 예전에는 그렇게 개발을 많이 헀으나, 이것을 Model1방식이라고 하지만, 현재는 MVC 형태로 많이 한다.
이제 Model1을 사용하지 않는 이유는 역할과 책임의 문제이다. View는 화면을 그리는데 모든 역량을 집중해야하며, Controller는 비즈니스 로직과 관련이 있거나, 내부적인 것을 처리하는데 집중해야한다. 그래서 View와 Controller를 분리한 것이다.
Model이라는 것에 화면에 필요한 것을 담아 화면에게 넘겨주는 구조이다. 컨트롤러에서 다음과 같이 helloMvc라는 메서드를 만든다. 요청할때 받은 파라미터를 name이라는 String 파라미터 변수 안에 담아 받으며, 함께 화면에 데이터를 담아 넘길 Model 객체도 파라미터로 받는다.
@GetMapping("hello-mvc")
public String helloMvc(@RequestParam(value="name", required=false) String name, Model model) {
model.addAttribute("name", name);
return "hello-template";
}
이렇게 작성했다면, template 폴더 밑에 hellp-template.html 파일을 다음과 같이 작성한다. 타입리프의 장점은 html을 그대로 쓰고 파일을 그냥 서버 없이 열어봐도 껍데기를 볼수있다는 점이다.
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<body>
<p th:text="'hello ' + ${name}">hello! empty</p>
</body>
</html>
이렇게 해주었다면 주소창에 localhost:8080/hello-mvc?name=spring!라고 입력하여 엔터를 치면 ${name}안에 key를 name으로 입력했던 값이 들어간 상태로 화면이 나타나게 된다.
브라우저에서 localhost:8080/hello-mvc를 넘기면 스프링 부트를 띄울떄 같이 띄우는 내장 톰캣 서버를 거쳐 스프링 컨테이너에 던져지고, 컨테이너는 매핑된 메서드를 찾아 호출하고 리턴할때 hello-template이고 모델은 key:name - value: spring을 담아 스프링이 뷰리졸버에게 넘겨주면 뷰리졸버가 뷰를 찾아 템플릿 엔진에 연결을 시켜준다.
뷰리졸버가 템플릿 밑에서 이름이 똑같은 애를 찾아 타입리프 템플릿엔진에 넘기면 템플릿엔진이 렌더링을 해서 변환을 한 html을 브라우저에게 넘긴다. 정적일때는 변환을 하지않는다.
3. API
이전에 mvc 방식에서 화면을 렌더링한 화면을 웹 브라우저에게 넘겨주는 방식 있는가하면 API방식으로 데이터를 곧바로 브라우저에게 주는 방식이 있다.
일단 먼저 코드를 짜보면서 알아보자. HelloController에 가서 또 하나의 겟 방식 메서드를 만든다.
@GetMapping("hello-string")
@ResponseBody
public String helloString(@RequestParam("name") String name) {
return "hello" + name;
}
가장 중요한 것은 @ResponseBody라는 애노테이션이다. ResponseBody의 의미는 html에 나오는 바디 태그를 의미하는 것이 아니라, http 프로토콜의 바디부를 의미한다. 이 바디에 리턴하는 내용을 직접 넣겠다는 뜻이다. 즉, 이 “hello” + name의 결과물이 클라이언트에게 그대로 내려간다. 따라서 뷰라는 것이 필요가 없다.
이전의 템플릿 엔진은 화면을 갖고 뷰라는 템플릿이 있는 상황에서 조작하는 방식이고, 이것은 데이터를 그대로 내려주는 방식인 것이다. 다만 이것은 문자의 형태로 보내주는 것이다. 이것을 쓸 일은 별로 없다.
데이터를 그 형태 그대로 주려고 한다면, 다음과 같이 메서드를 추가해보자.
static class Hello {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@GetMapping("hello-api")
@ResponseBody
public Hello helloApi(@RequestParam("name") String name) {
Hello hello = new Hello();
hello.setName(name);
return hello;
}
이렇게 다음과 같이 Hello라는 객체를 만들고, 이 객체 안의 name 필드를 사용자가 입력한 값으로 초기화시킨 뒤 리턴해주면 화면에 다음과 같이 객체가 json 형태로 화면에 나타난다.
json은 정보를 key와 value형태로 구조화시킨 형태이다. 요즘에는 과거에는 xml이라는 방식도 많이 쓰였으나, xml방식은 무겁고 열고 닫고를 두번써야하나, 엣날에는 비등비등했다가 요즘에는 거의다 json을 사용한다. spring도 마찬가지로 객체 자체를 리턴하면 이것을 json 방식으로 변환한다. (이것을 xml 방식으로 변환하는 옵션도 있긴 하다.) 레거시같은 경우에는 xml로 연동되는 경우도 있다.
동작원리는 다음과 같다. ResponseBody를 사용하면 웹 브라우저에서 localhost:8080/hello-api를 요청할 경우, 내장 톰캣 서버에서 hello-api를 던진다. 컨테이너는 hello-api에 해당하는 메서드를 매핑하는데 해당 애노테이션이 붙어있는 것을 인지하고, http의 body에 그대로 넣어주려한다. 근데 리턴값이 객체형태이다. 스프링은 리턴형태로 객체가 오면 디폴트가 json 방식으로 데이터를 만들어서 http body에 반환하는 것이다.
이 리턴되는 객체에 대해서는 뷰리졸버가 아니라 스프링에서 세팅을 다해놓은 HttpMessageConverter가 동작을 한다. 얘가 단순 문자열이면 StringConverter(StringHttpMessageConverter)가 동작하고, 객체면 JsonConverter(MappingJackson2HttpMessageConverter - 객체를 json으로 바꿔주는 유명한 라이브러리 중 하나(Jackson과 Gson이 가장 유명하다))가 동작하여 이것을 json 스타일로 바꾼다. 언제까지나 기본으로 쓰는 것이므로 원하는 라이브러리로 바꿔줄 수는 있다. 요청한 상대에게 이 json 형태의 데이터를 보내는 것이다. byte 처리 등등 기타 형태들은 HttpMessageConverter가 기본으로 등록되어있으며 실무에서는 이러한 라이브러리를 별로 바꾸지 않고 그대로 사용하는 편이다.
참고: 클라이언트의 HTTP Accept 헤더와 서버의 컨트롤러 반환 타입 정보 둘을 조합해서 HttpMessageConverter가 선택된다.
Comments