스프링프레임워크 :: 4.스프링부트 웹프로그래밍 Part2
타임리프(Thymeleaf)
타임리프(Thymeleaf) 자체가 생소 할 수 있습니다만 뷰 템플릿의 구성이 데이터를 표현하기 위한 EL(Expression Language)과 조건체크, 반복 등의 작업을 수행하기 위한 태그라이브러리 혹은 이에 준하는 다른 구성요소로 이루어진다는 구조적 특징만 이해 한다면 배우는 것은 크게 어렵지 않습니다. 특히 스프링 프레임워크와 연동이 수월환 장점이 있어 많은 스프링 프레임워크 기반 프로젝트에서 타임리프를 사용하고 있습니다. 타임리프와 관련한 자세한 정보는 https://www.thymeleaf.org 에서 살펴볼 수 있습니다.
시작하기
Part-1 에서는 스프링 프레임워크를 이용해 Rest Controller 를 구현해 보았으며 Part-2 에서는 MVC 패턴을 가지는 일반적인 웹 어플리케이션 개발을 살펴보게 됩니다.
예전에는 주로 JSP를 뷰 템플릿으로 많이 사용 하였으나 JSP 의 커스텀 태그나 이를 이용한 JSTL, JSP 액션태그 등을 사용함으로써 웹페이지 자체의 독리적인 개발이나 테스트등이 어려워지는 문제가 있었습니다.
또한 최근에는 서버사이드 렌더링이 아닌 클라이언트 렌더링 중심의 프론트엔드 기반 개발도 확대되고 있으며 REST 기반 개발이 보편화 되면서 JSP 의 사용이 예전만큼 필수적인 요소가 되지 못하고 있습니다.
스프링 프레임워크의 경우에는 자바 기반 프레임워크 이지만 JSP에 대한 종속은 없으며 Thymeleaf, JSP, FreeMarker, Velocity 등 다양한 템플릿 엔진과의 연동을 지원하고 있습니다. JSP 사용은 별도의 설정이 필요하며 Thymeleaf 의 경우 pom.xml 에만 추가하면 별도의 설정 없이 사용할 수 있습니다.
DemoWebController 작성
MVC 웹 컨트롤러로 사용할 DemoWebController 클래스를 생성합니다. DemoRestController 와 마찬가지로 별도의 클래스 상속이나 인터페이스 구현 없는 일반 자바 클래스로 만들면 됩니다.
@Controller 애너테이션 추가
클래스 생성후 선언부 위에 다음과 같이 애너테이션을 추가 합니다.
@Controller
@RequestMapping("/web")
public class DemoWebController {
}
- @Controller 는 현재 클래스가 MVC 컨트롤러로 동작하는 것을 알립니다.
- 웹의 기본 URI는 /web 으로 시작 합니다.
테스트 메서드 등록
/web/hello
GET 요청에 동작하는 메서드를 만들어 컨트롤러와 뷰 페이지의 연계 과정을 살펴 보도록 합니다.
@GetMapping("/hello")
public String hello(Model model) {
model.addAttribute("msg", "Hello World");
return "hello";
}
- hello() 메서드의 인자인 Model 객체는 뷰에 전달되는 객체로 컨트롤러에서 뷰로 전달할 값들이 있다면 model.addAttribute() 로 전달 할 수 있음.
- JSP 에서 request.setAttribute() 와 유사하다고 볼 수있다.
- return 타입은 String 이고 값은 단순한 문자열이 아니라 뷰의 이름 즉, 타임리프 html 파일의 이름으로 확장자 없이 이름만 사용 한다.
- 여기서는 htllo.html 을 호출하게 되고 파일의 위치는
resources/templates/
폴더가 기준이 된다.
htllo.html 작성
resources/templates/
폴더에 hello.html 파일을 만듭니다. 파일의 일반적인 html5 구조를 가지고 있습니다. 타임리프의 제어문이나 조건등을 지정하는 속성은 th:XXX
형식으로 사용하는데 이는 html5 표준 규격이 아니므로 다음과 같이 xmlns
를 추가해 주어야 합니다.
여기서는 th
대신 data-th-XXX
와 같이 html5 표준 규격을 사용하므로 <html xmlns:th="http://www.thymeleaf.org">
를 추가할 필요가 없다.
html 문서는 다음과 같이 작성 합니다.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h2>Hello Spring Boot Project</h2>
<hr>
MSG : <span data-th-text="${msg}">hello</span>
</body>
</html>
data-th-text
는 태그 바디에 EL 값을 출력하는 속성이다.- EL 은 JSP 와 유사 하다. $ 이외에 #, @ 등이 추가로 사용 된다.
- 만일 서버를 경유하지 않고 해당 html 을 바로 브라우저에서 열어보면 테스트용으로 넣은 hello 라는 문자가 보이게 된다.
타임리프에서 사용하는 Expression 표기는 다음과 같은 종류가 있습니다.
Variable Expressions: ${...}
Selection Variable Expressions: *{...}
Message Expressions: #{...}
Link URL Expressions: @{...}
Fragment Expressions: ~{...}
실행 및 테스트
스프링 부트 앱을 실행하고 브라우저에서 http://localhost/web/hello
를 입력하도록 합니다.
상품관리 웹 어플리케이션
DemoWebController 확장
이제 Part-1 에서 만들었던 Product 와 ProductManager 를 이용해 웹 어플리케이션을 작성해 봅니다. DemoWebController 에 다음과 같이 등록과 목록을 보여주기 위한 메서드를 추가 합니다.
@Autowired
ProductManager pm;
@GetMapping("/productlist")
public String getProducts(Model model) {
model.addAttribute("products", pm.getDatas());
return "product_list";
}
@PostMapping("/productadd")
public String addProduct(Product p) {
pm.addProduct(p);
return "redirect:/web/productlist";
}
- getProducts()는 ProductManager 를 통해 상품목록을 가지고와
products
라는 이름으로 Model 객체에 넣고product_list.html
로 이동하도록 구현함. - addProduct()는 인자로 Product 객체를 받도록 되어 있는데 html form 에서 작성한 입력항목들의 name 속성값들이 Product 객체로의 필드로 자동 매핑 된다.
- REST 에서와 마찬가지로 pm.addProduct() 를 이용해 상품을 추가하고 목록화면으로 가지전 다시 새로운 목록을 불러와야 하기 때문에
redirect:/web/productlist
를 리턴 하고 있음.
product_list.html
상품목록을 보여주기 위한 html로 타임리프 html 이므로 /resources/templates
폴더에 있어야 합니다. 물론 서브폴더를 만들어도 됩니다.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>product_list.html</title>
</head>
<body>
<h2>Product List</h2>
<hr>
<a href="/product_form.html">New Product</a>
<table border=1>
<tr>
<th>ID</th>
<th>Product Name</th>
<th>Price</th>
</tr>
<tr data-th-each="p : ${products}">
<td data-th-text="${p.id}">1</td>
<td data-th-text="${p.name}">Apple Iphone Xs</td>
<td data-th-text="${p.price}">130000</td>
</tr>
</table>
</body>
</html>
data-th-each
는 for문과 같이 집합형 데이터를 반복적으로 처리할 때 사용. 여기서는<tr>
이 반복 되므로<tr>
에 속성으로 지정함.- 각각의 데이터는
<td></td>
에 들어가면data-th-text
속성으로 지정함.
product_form.html
상품을 등록하기 위한 html 로 별도의 데이터를 표현하지 않는 일반 html 입니다. 이경우 templates
폴더가 아닌 static
폴더에 둘 수 있습니다. 만일 하나의 양식으로 등록과 수정을 모두 처리하는 경우에는 당연히 templates
폴더에 두어야 한다. 이와 관련해서는 추후 다른 강좌에서 다시 다루도록 합니다.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>product_form.html</title>
</head>
<body>
<h2>New Product</h2>
<hr>
<form action="/web/productadd" method="post">
Name: <input type="text" name="name">
Price: <input type="text" name="price">
<button type="submit">Submit</button>
</form>
</body>
</html>
- form 의 method 는
post
로 지정하고 submit 버튼이 눌린경우/web/productadd
를 호출함. - 각각의 입력양식에 name 속성값을 명시해 주어야 하고 Product 클래스의 필드명과 일치해야 함.
실행 및 결과 확인
먼저 목록을 확인합니다.
http://localhost:8080/web/productlist
Add Product 링크를 클릭하면 다음과 같이 양식이 나옵니다. 입력양식에 내용을 작성하고 submit 버튼을 누르면 새로운 항목이 추가되고 다시 목록으로 이동해 추가된 내용을 함께 보여줍니다.