200 lines
5.3 KiB
Markdown
200 lines
5.3 KiB
Markdown
# Spring Boot의 뷰 렌더링과 Thymeleaf 템플릿
|
|
|
|
Spring Boot에서는 **Thymeleaf**를 사용하여 **HTML 기반의 동적 웹 페이지를 렌더링**할 수 있습니다.
|
|
|
|
## 뷰(View) 렌더링이란?
|
|
|
|
- 클라이언트가 요청을 보내면, **컨트롤러가 요청을 처리하고 데이터를 모델(Model)에 담아 뷰(View)로 전달**합니다.
|
|
- 뷰(View)는 **HTML 페이지**로, Thymeleaf 같은 템플릿 엔진을 사용하면 **서버에서 동적으로 HTML을 생성**할 수 있습니다.
|
|
- Spring Boot에서 대표적인 뷰 템플릿 엔진: **Thymeleaf, JSP, FreeMarker** 등이 있지만, **Thymeleaf가 가장 많이 사용됨**.
|
|
|
|
## Thymeleaf 설정
|
|
|
|
다음과 같이 Thymeleaf 의존성을 추가합니다.
|
|
```kotlin
|
|
implementation("org.springframework.boot:spring-boot-starter-thymeleaf")
|
|
```
|
|
|
|
Spring Boot는 기본적으로 **`src/main/resources/templates/`** 경로에서 Thymeleaf 템플릿을 찾습니다.
|
|
|
|
```
|
|
src/
|
|
├── main/
|
|
│ ├── java/com.example.demo/
|
|
│ ├── resources/
|
|
│ │ ├── templates/ → Thymeleaf HTML 파일 위치
|
|
│ │ │ ├── index.html
|
|
│ │ │ ├── user.html
|
|
│ │ ├── application.properties
|
|
```
|
|
|
|
## 기본 컨트롤러와 템플릿 렌더링
|
|
|
|
### 컨트롤러 작성
|
|
```java
|
|
@Controller
|
|
public class HomeController {
|
|
|
|
@GetMapping("/")
|
|
public String home(Model model) {
|
|
model.addAttribute("message", "Welcome to Thymeleaf!");
|
|
return "index"; // templates/index.html 파일을 렌더링
|
|
}
|
|
}
|
|
```
|
|
**설명:**
|
|
- `@Controller` → HTML을 반환하는 컨트롤러.
|
|
- `Model` → **뷰로 데이터를 전달하는 객체**.
|
|
- `model.addAttribute("message", "Welcome to Thymeleaf!")` → `message`라는 데이터를 전달.
|
|
- `return "index";` → `templates/index.html` 파일을 렌더링.
|
|
|
|
|
|
### Thymeleaf 템플릿 (index.html)
|
|
```html
|
|
<!DOCTYPE html>
|
|
<html xmlns:th="http://www.thymeleaf.org">
|
|
<head>
|
|
<title>Thymeleaf Example</title>
|
|
</head>
|
|
<body>
|
|
<h1 th:text="${message}">Default Message</h1>
|
|
</body>
|
|
</html>
|
|
```
|
|
#### 실행 결과 (`http://localhost:8080/` 요청)
|
|
```html
|
|
<h1>Welcome to Thymeleaf!</h1>
|
|
```
|
|
**설명:**
|
|
- `th:text="${message}"` → `message` 값이 `"Welcome to Thymeleaf!"`로 변경됨.
|
|
- **템플릿 엔진이 동적으로 HTML을 생성**하여 클라이언트에 응답.
|
|
|
|
|
|
## 변수 출력 및 표현식
|
|
|
|
### 기본 표현식 (`th:text`)
|
|
```html
|
|
<p th:text="${username}">Default Name</p>
|
|
```
|
|
- **`${변수명}`** → `Model`에서 전달된 변수를 표시.
|
|
|
|
### 객체의 필드 출력 (`th:text`)
|
|
```java
|
|
class User {
|
|
private String name;
|
|
private int age;
|
|
|
|
public User(String name, int age) { this.name = name; this.age = age; }
|
|
public String getName() { return name; }
|
|
public int getAge() { return age; }
|
|
}
|
|
```
|
|
```java
|
|
@GetMapping("/user")
|
|
public String userProfile(Model model) {
|
|
model.addAttribute("user", new User("Alice", 25));
|
|
return "user";
|
|
}
|
|
```
|
|
```html
|
|
<p th:text="${user.name}">Default Name</p>
|
|
<p th:text="${user.age}">Default Age</p>
|
|
```
|
|
실행 결과 (`/user` 요청):
|
|
```html
|
|
<p>Alice</p>
|
|
<p>25</p>
|
|
```
|
|
|
|
## 반복문 (`th:each`)
|
|
|
|
### 리스트 반복 (`th:each`)
|
|
```java
|
|
@GetMapping("/users")
|
|
public String users(Model model) {
|
|
List<User> userList = Arrays.asList(
|
|
new User("Alice", 25),
|
|
new User("Bob", 30),
|
|
new User("Charlie", 22)
|
|
);
|
|
model.addAttribute("users", userList);
|
|
return "users";
|
|
}
|
|
```
|
|
```html
|
|
<ul>
|
|
<li th:each="user : ${users}">
|
|
<span th:text="${user.name}"></span> - <span th:text="${user.age}"></span>
|
|
</li>
|
|
</ul>
|
|
```
|
|
실행 결과 (`/users` 요청):
|
|
```html
|
|
<ul>
|
|
<li>Alice - 25</li>
|
|
<li>Bob - 30</li>
|
|
<li>Charlie - 22</li>
|
|
</ul>
|
|
```
|
|
|
|
## 조건문 (`th:if`, `th:unless`)
|
|
|
|
### 값이 있을 때만 표시 (`th:if`)
|
|
```html
|
|
<p th:if="${user.age >= 18}">Adult</p>
|
|
```
|
|
|
|
### 값이 없을 때 표시 (`th:unless`)
|
|
```html
|
|
<p th:unless="${user.age >= 18}">Minor</p>
|
|
```
|
|
|
|
|
|
## 폼 처리 (`@PostMapping`)
|
|
|
|
### 컨트롤러에서 폼 데이터 받기
|
|
```java
|
|
@Controller
|
|
@RequestMapping("/form")
|
|
public class FormController {
|
|
|
|
@GetMapping
|
|
public String showForm(Model model) {
|
|
model.addAttribute("user", new User("", 0));
|
|
return "form";
|
|
}
|
|
|
|
@PostMapping
|
|
public String submitForm(@ModelAttribute User user, Model model) {
|
|
model.addAttribute("submittedUser", user);
|
|
return "result";
|
|
}
|
|
}
|
|
```
|
|
|
|
### Thymeleaf 폼 (form.html)
|
|
```html
|
|
<form action="#" th:action="@{/form}" th:object="${user}" method="post">
|
|
Name: <input type="text" th:field="*{name}" />
|
|
Age: <input type="number" th:field="*{age}" />
|
|
<button type="submit">Submit</button>
|
|
</form>
|
|
```
|
|
|
|
결과 페이지 (result.html):
|
|
```html
|
|
<p>Name: <span th:text="${submittedUser.name}"></span></p>
|
|
<p>Age: <span th:text="${submittedUser.age}"></span></p>
|
|
```
|
|
|
|
|
|
## 정리
|
|
| 기능 | Thymeleaf 문법 | 설명 |
|
|
|------|--------------|------|
|
|
| **텍스트 출력** | `th:text="${변수}"` | 변수 값을 출력 |
|
|
| **객체 속성 출력** | `th:text="${객체.필드}"` | 객체의 필드 값을 출력 |
|
|
| **반복문** | `th:each="item : ${리스트}"` | 리스트 반복 출력 |
|
|
| **조건문** | `th:if`, `th:unless` | 조건부 렌더링 |
|
|
| **폼 바인딩** | `th:object` + `th:field` | HTML 폼과 객체 바인딩 |
|
|
| **URL 매핑** | `th:action="@{경로}"` | 폼 요청 경로 설정 |
|