타임리프 공부
타임 리프 공부
템플릿 설정
앞서 우리는 문자열 ‘question list’를 직접 자바 코드로 작성하여 브라우저에 리턴했다. 하지만 보통 브라우저에 응답하는 문자열은 이 예처럼 자바 코드에서 직접 만들지 않는다. 일반적으로 많이 사용하는 방식은 템플릿(template) 방식이다. 템플릿은 자바 코드를 삽입할 수 있는 HTML 형식의 파일을 말한다.
이러한 템플릿을 사용하기 위해 스프링 부트에서는 템플릿 엔진을 지원한다. 템플릿 엔진에는 Thymeleaf, Mustache, Groovy, Freemarker, Velocity 등이 있는데, 스프링 진영에서 추천하는 타임리프(Thymleaf) 템플릿 엔진을 사용할 것이다.
타임리프를 사용하려면 먼저 설치가 필요하다. 다음과 같이 build.gradle 파일을 수정하여 타임리프를 설치해 보자.
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect'
package com.mysite.sbb.question;
import java.util.List;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
@Controller
public class QuestionController {
private final QuestionRepository questionRepository;
@GetMapping("/question/list")
public String list(Model model) {
List<Question> questionList = this.questionRepository.findAll();
model.addAttribute("questionList", questionList);
return "question_list";
}
}
매개변수로
Model을 지정하면 객체가 자동으로 생성된다.
@RequiredArgsConstructor 애너테이션의 생성자 방식으로 questionRepository 객체를 주입했다. @RequiredArgsConstructor는 롬복(Lombok)이 제공하는 애너테이션으로, final이 붙은 속성을 포함하는 생성자를 자동으로 만들어 주는 역할을 한다. 따라서 스프링 부트(Spring Boot)가 내부적으로 QuestionController를 생성할 때 롬복으로 만들어진 생성자에 의해 questionRepository 객체가 자동으로 주입된다.
그리고 QuestionRepository의 findAll 메서드를 사용하여 질문 목록 데이터인 questionList를 생성하고 Model 객체에 ‘questionList’라는 이름으로 저장했다. 여기서 Model 객체는 자바 클래스(Java class)와 템플릿(template) 간의 연결 고리 역할을 한다. Model 객체에 값을 담아 두면 템플릿에서 그 값을 사용할 수 있다. Model 객체는 따로 생성할 필요 없이 컨트롤러의 메서드에 매개변수로 지정하기만 하면 스프링 부트가 자동으로 Model 객체를 생성한다.
<table>
<thead>
<tr>
<th>제목</th>
<th>작성일시</th>
</tr>
</thead>
<tbody>
<tr th:each="question : ${questionList}">
<td th:text="${question.subject}"></td>
<td th:text="${question.createDate}"></td>
</tr>
</tbody>
</table>
질문 목록을 HTML의 테이블 구조로 표시했다. 여기서 눈여겨볼 코드는 th:each="question : ${questionList}"이다. 여기서 th:는 타임리프에서 사용하는 속성임을 나타내는데, 바로 이 부분이 자바 코드와 연결된다. question_list.html 파일에 사용한 타임리프 속성들을 잠시 살펴보자.
<tr th:each="question : ${questionList}">
QuestionController의 list 메서드에서 조회한 질문 목록 데이터를 ‘questionList’라는 이름으로 Model 객체에 저장했다. 타임리프는 Model 객체에 저장한 questionList를 ${questionList}로 읽을 수 있다. 위의 코드는 questionList에 저장된 데이터를 하나씩 꺼내 question 변수에 대입한 후 questionList의 개수만큼 반복하며 <tr> ... </tr> 문장을 출력하라는 의미이다. 자바의 for each 문을 떠올리면 쉽게 이해할 수 있을 것이다.
다음 코드는 question 객체의 subject를 <td> 태그로 출력한다.
<td th:text="${question.subject}"></td>
다음 코드도 question 객체의 createDate를 출력한다.
<td th:text="${question.createDate}"></td>
자주 사용하는 타임리프(Timeleaf)의 3가지 속성
타임리프(Timeleaf)의 여러 속성 중 다음 3가지 속성을 자주 사용한다. 이 3가지만 알아도 일반적인 화면을 만들기에 충분하다.
1. 분기문 속성
if 문, else if 문과 같은 분기문은 다음과 같이 사용한다.
th:if="${question != null}"
이 경우 question 객체가 null이 아닌 경우에만 이 속성을 포함한 요소가 표시된다.
2. 반복문 속성
th:each 반복문 속성은 자바의 for each 문과 유사하다.
th:each="question : ${questionList}"
반복문 속성은 다음과 같이 사용할 수도 있다.
th:each="question, loop : ${questionList}"
여기서 추가한 loop 객체를 이용하여 루프 내에서 다음과 같이 사용할 수 있다.
- loop.index: 루프의 순서(루프의 반복 순서, 0부터 1씩 증가)
- loop.count: 루프의 순서(루프의 반복 순서, 1부터 1씩 증가)
- loop.size: 반복 객체의 요소 개수(예를 들어 questionList의 요소 개수)
- loop.first: 루프의 첫 번째 순서인 경우 true
- loop.last: 루프의 마지막 순서인 경우 true
- loop.odd: 루프의 홀수 번째 순서인 경우 true
- loop.even: 루프의 짝수 번째 순서인 경우 true
- loop.current: 현재 대입된 객체(여기서는 question과 동일)
3. 텍스트 속성
th:text=(속성)은 해당 요소의 텍스트값을 출력한다.
th:text="${question.subject}"
텍스트는 th:text 속성 대신에 다음처럼 대괄호를 사용하여 값을 직접 출력할 수 있다.
<tr th:each="question : ${questionList}">
<td>[[${question.subject}]]</td>
<td>[[${question.createDate}]]</td>
</tr>
댓글남기기