타임리프는 템플릿 엔진이다. 템플릿 엔진은 서버에서 데이터를 받아 우리가 보는 웹 페이지, 즉 HTML 상에 데이터를 넣어 보여주는 도구이다.
build.gradle 파일에 의존성을 추가해야 타임리프를 사용할 수 있다.
dependencies {
...
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
}
■ 타임리프 표현식
| 표현식 | 설명 |
|---|---|
| ${…} | 변수의 값 표현식 |
| #{…} | 속성 파일 값 표현식 |
| @{…} | URL 표현식 |
| *{…} | 선택한 변수의 표현식. th:object에서 선택한 객체에 접근 |
■ 타임리프 문법
| 표현식 | 설명 | 예제 |
|---|---|---|
| th:text | 텍스트를 표현할 때 사용 | th:text=${person.name} |
| th:each | 컬렉션을 반복할 때 사용 | th:each=”person : ${persons}” |
| th:if | 조건이 true인 때만 표시 | th:if=”${person.age} ≥ 20” |
| th:unless | 조건이 false인 때만 표시 | th:unless=”${person.age} ≥ 20” |
| th:href | 이동 경로 | th:href=”@{/person/{id}(id=${person.id})}” |
| th:with | 변숫값으로 지정 | th:with=”name=${person.name}” |
| th:object | 선택한 객체로 지정 | th:object=”${person}” |
API 컨트롤러는 데이터를 직렬화한 JSON 문자열을 반환하며 @RestController 애너테이션을 사용한다. 뷰 컨트롤러는 모델 객체 값을 지정 및 뷰의 이름을 반환하며 @Controller 애너테이션을 사용한다.
@Controller
public class ExampleController {
@GetMapping("/thymeleaf/example")
public String thymeleafExample(Model model) {
Person examplePerson = new Person();
examplePerson.setId(1L);
examplePerson.setName("홍길동");
examplePerson.setAge(11);
examplePerson.setHobbies(List.of("운동", "독서"));
model.addAttribute("person", examplePerson);
model.addAttribute("today", LocalDate.now());
return "example";
}
@Setter
@Getter
class Person {
private Long id;
private String name;
private int age;
private List<String> hobbies;
}
}
@Controller 이므로 뷰의 이름을 반환한다. 스프링은 반환하는 뷰의 이름을 보고 /resources/templates 디렉터리에서 파일을 찾아 웹 브라우저에 해당 파일을 보여준다.
Model 객체는 뷰인 HTML 쪽으로 값을 넘겨주는 객체이다. addAttribute() 메서드로 모델에 값을 저장한다. 모델을 컨트롤러와 뷰의 중간다리 역할이라고 생각하면 된다.
<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>타임리프 익히기</h1>
<p th:text="${#temporals.format(today, 'yyyy-MM-dd')}"></p>
<div th:object="${person}">
<p th:text="|이름 : *{name}|"></p>
<p th:text="|나이 : *{age}|"></p>
<p>취미</p>
<ul th:each="hobby : *{hobbies}">
<li th:text="${hobby}"></li>
<span th:if="${hobby == '운동'}">(대표 취미)</span>
</ul>
</div>
<a th:href="@{/api/articles/{id}(id=${person.id})}">글 보기</a>
</body>
</html>
타임리프의 문법과 표현식을 사용해서 HTML 파일을 작성한다.
#temporals.format() 함수로 LocalDate 타입인 오늘 날짜를 String 타입으로 포매팅한다.
@EntityListeners(AuditingEntityListener.class)
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Article {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", updatable = false)
private Long id;
@Column(name = "title", nullable = false)
private String title;
@Column(name = "content", nullable = false)
private String content;
@CreatedDate
@Column(name = "created_at")
private LocalDateTime createdAt;
@LastModifiedDate
@Column(name = "updated_at")
private LocalDateTime updatedAt;
@Builder
public Article(String title, String content) {
this.title = title;
this.content = content;
}
public void update(String title, String content) {
this.title = title;
this.content = content;
}
}
@CreatedDate 애너테이션을 사용하면 엔티티의 생성 시간을 특정 컬럼에 저장할 수 있다.
@LastModifiedDate 애너테이션을 사용하면 엔티티가 수정될 때 마지막으로 수정된 시간을 특정 컬럼에 저장할 수 있다.
엔티티의 생성 및 수정 시간을 자동으로 감시하고 기록하기 위해 @EntityListeners(AuditingEntityListener.class) 애너테이션을 추가한다.
@EnableJpaAuditing
@SpringBootApplication
public class SpringBootDeveloperApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootDeveloperApplication.class, args);
}
}
@CreatedDate , @LastModifiedDate 애너테이션이 동작하려면 @EnableJpaAuditing 애너테이션을 추가해 JPA Auditing을 활성화해야 한다.