컨트롤러에서 뷰를 호출하고 이를 템플릿 엔진인 thymeleaf를 사용하여 동적으로 변경해보자.
컨트롤러
정적 리소스(HTML 파일)
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<link href="../css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container" style="max-width: 600px">
<div class="py-5 text-center">
<h2>상품 목록</h2>
</div>
<div class="row">
<div class="col">
<button class="btn btn-primary float-end"
onclick="location.href='addForm.html'" type="button">상품
등록</button>
</div>
</div>
<hr class="my-4">
<div>
<table class="table">
<thead>
<tr>
<th>ID</th>
<th>상품명</th>
<th>가격</th>
<th>수량</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="item.html">1</a></td>
<td><a href="item.html">테스트 상품1</a></td>
<td>10000</td>
<td>10</td>
</tr>
<tr>
<td><a href="item.html">2</a></td>
<td><a href="item.html">테스트 상품2</a></td>
<td>20000</td>
<td>20</td>
</tr>
</tbody>
</table>
</div>
</div> <!-- /container -->
</body>
</html>
이를 타임리프를 사용하여 동적으로 변경하여보자.
타임리프를 사용하기 위한 th 네임스페이스 추가
xmlns:th="http://www.thymeleaf.org"
BootStrap 경로 절대경로로 변경
상대 경로로 설정되어 있던 bootstrap파일 경로를 타임리프를 사용하여 절대 경로로 변경하였다.
위와 같이 작성하면 타임리프가 기존에 있던 href를 날리고 th가 붙은 href로 설정함
href="value1" 을 th:href="value2" 의 값으로 변경한다.
타임리프 뷰 템플릿을 거치게 되면 원래 값을 th:xxx 값으로 변경한다.
만약 값이 없다면 새로 생성한다. HTML을 그대로 볼 때는 href 속성이 사용되고, 뷰 템플릿을 거치면 th:href 의 값이 href 로 대체되면서 동적으로 변경할 수 있다.
타임리프는 URL 링크를 사용하는 경우 @{...} 를 사용한다. 이것을 URL 링크 표현식이라 한다. URL 링크 표현식을 사용하면 서블릿 컨텍스트를 자동으로 포함한다.
상품 등록 버튼 링크 변경
기본 값이 위와 같이 되어 있는데 이 상태에서 버튼을 누르면 현재 html이 있는 폴더 내에서 addForm.html을 찾기 때문에 404가 발생함.
위와 같이 타임리프를 통해 변경하면 /basic/items/add로 링크를 설정하게 된다. 따라서 우리는 해당 경로로 매핑되는 뷰를 만들어주면 됨.(정적 리소스를 갖다주는 것이 아니라)
여기에서는 리터럴 대체 문법이 사용되었다. ==> | ..... |
타임리프에서 문자와 표현식 등은 분리되어 있기 때문에 더해서 사용해야 한다.
<span th:text="'Welcome to our application, ' + ${user.name} + '!'">
리터럴 대체 문법을 사용하면, 더하기 없이 편리하게 사용할 수 있다.
<span th:text="|Welcome to our application, ${user.name}!|">
만들고 싶은 결과 ==> location.href='/basic/items/add'
그냥 사용하면 문자와 표현식을 각각 따로 더해서 사용해야 하므로 다음과 같이 복잡해진다.
th:onclick="'location.href=' + '\'' + @{/basic/items/add} + '\''"
리터럴 대체 문법을 사용하면 편리하게 처리가능
th:onclick="|location.href='@{/basic/items/add}'|"
실제 상품 목록 출력
정적 리소스에서는 임의로 2개의 상품을 출력하도록 해놓았다.
이를 Loop를 돌려 실제 Repository에서 상품을 꺼내오도록 해야한다.
- th:each를 사용해 모델에 포함된 items가 item변수에 하나씩 담김
모델에는 컨트롤러에서 GET방식으로 /basic/items 로 요청이 들어왔을 때 모든 상품을 리스트에 담고 이 리스트를 모델에 addAttribute를 통해 담아놨다.
th:text는 태그 안에 들어가는 텍스트 값을 의미한다. 타임리프는 프로퍼티 접근법을 사용하기 때문에 item.id / item.price 등이 가능하다. 태그 안의 텍스트를 item.id로 치환시켜주는 역할을 한다.
- item.id = item.getId()
상품 목록이 정상적으로 출력되는지 확인해보자.
컨트롤러에서 @PostConstruct를 통해 생성자 호출 이후에 넣어 놓은 테스트 상품들이 출력이 된 것을 확인할 수 있다.
상품 링크 변경
위와 같이 정적 리소스로 걸려있는 링크들을 변경
- 경로를 템플릿처럼 편리하게 사용할 수 있음
링크를 /basic/items/해당 상품의 id로 매핑시켜놓았다. 템플릿 문법으로 위와 같이 변수를 사용할 수 있음.
경로 변수 뿐만 아니라 쿼리 파라미터도 생성할 수 있다.
- ex) th:href="@{/basic/items/{itemId}(itemId=${item.id}, query='test')}"
- 생성 링크 : http://localhost:8080/basic/items/1?query=test
- 위와 아래의 href는 동일한 결과를 생성한다. 생성해야할 url이 간단하다면 리터럴 대체 문법을 사용하여 item.id으로 바로 값을 꺼내도 괜찮다.
서버를 실행하고 1번 상품을 클릭해보면
원하는 URL로 이동한 것을 확인할 수 있다. 이또한 매핑되는 뷰를 만들어줘야함.
타임 리프 = Natural Template
th:xxx 가 붙은 부분은 서버사이드에서 렌더링 되고, 기존 것을 대체한다.
th:xxx 이 없으면 기존 html의 xxx 속성이 그대로 사용된다.
HTML을 파일로 직접 열었을 때, th:xxx 가 있어도 웹 브라우저는 ht: 속성을 알지 못하므로 무시한다. 따라서 HTML을 파일 보기를 유지하면서 템플릿 기능도 할 수 있다.
타임리프는 순수 HTML을 파일을 웹 브라우저에서 열어도 내용을 확인할 수 있고, 서버를 통해 뷰 템플릿을 거치면 동적으로 변경된 결과를 확인할 수 있다. JSP를 생각해보면, JSP 파일은 웹 브라우저에서 그냥 열면 JSP 소스코드와 HTML이 뒤죽박죽 되어서 정상적인 확인이 불가능하다. 오직 서버를 통해서 JSP를 열어야 한다. 이렇게 순수 HTML을 그대로 유지하면서 뷰 템플릿도 사용할 수 있는 타임리프의 특징을 네츄럴 템플릿이라 함.
반응형
'Spring > Spring MVC' 카테고리의 다른 글
javax에서 제공하는 @Valid, Spring이 제공하는 BindingResult (0) | 2021.05.22 |
---|---|
5/14 공부 내용 (0) | 2021.05.14 |
HTTP Message Converter (0) | 2021.05.11 |
HTTP Response (0) | 2021.05.10 |
HTTP Request Message (0) | 2021.05.09 |