BackEnd/Spring

[Spring] Thymeleaf(타임리프)

Hojung7 2024. 9. 4. 17:46
Thymeleaf (타임리프)

 

웹 및 독립 실행형 환경 모두를 위한 최신 서버 측 Java 템플릿 엔진
HTML 파일에서 th(Thymeleaf) 속성을 이용해 컨트롤러로 부터 전달 받은데이터를 이용해 동적 페이지를 만들 수 있음.
Spring Boot에서는 JSP가 아닌 Thymeleaf 사용을 권장하고 있음.

 

 

🪄 Thymeleaf 사용 준비

 

[Maven] - pom.xml

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

 

[Gradle] - build.gradle

implementation 'org.springframework.boot:spring-boot-starter-thymeleaf’

 

application.properties

# thymeleaf 접두사, 접미사 설정
#미작성 시 기본값
#spring.thymeleaf.prefix=classpath:/templates/
#spring.thymeleaf.suffix=.html

 


@Slf4j  log 필드 생성 및 초기화 자동완성 lombok 어노테이션
@RequestMapping("OOO") /OOO로 시작하는 요청을 매핑
@Controller 컨트롤러임을 명시 + bean 등록

 

▷ Model

 

 - org.springframework.ui 패키지
    
  - Spring에서 데이터를 전달하는 역할의 객체
 
  - 데이터 유지 범위(scope) : 기본 request 
   
  - @SessionAttributes 와 함꼐 사용하면 session scope로 변경
 
  [Model을 이용해서 값을 세팅하는 방법]
  Model.addAttribute("key", value);

 

▷ Servlet/JSP 내장 객체 4종류의 데이터 유지 범위(scope)

page 현재 페이지
request 요청 받은 곳 + 요청 위임(forward) 받은 페이지
session 클라이언트가 서버 최초 접속 시 생성,
연결한 브라우저 종료 또는 세션 시간 만료까지
application  서버 실행 시 1개만 생성  서버가 종료 될 때까지 유지

Spring EL(스프링 표현 언어)

 

 - ${key} : request scope에 세팅된 값 얻어와 출력

 - th:text 속성 
   → 해당 속성이 작성된 태그의 content(시작, 종료 태그 사이)영역에
         "속성값"을 출력
   * 타임리프 th 속성들은 해석이 완료된 후 사라지게 된다!!!!!
  → 응답화면을 브라우저 개발자 도구로 살펴보면 th: 속성이 하나도 보이지 않는다!   

 

th:block 태그

- 타임리프에서 제공하는 유일한 태그
  (나머진 다 속성)
     

 - th 속성을 사용할 만한 마땅한 태그가 없을 경우에
   임시로 사용하는 목적으로 만들어진 태그

 - th:text 보단 조건문, 반복문에서 더 많이 사용함

 

th:each="item : ${list}"  

 - 해당 HTML 요소를 list의 길이 만큼 반복

  - list에 저장된 요소를 순차접근하여 item에 저장

  - 해당 코드가 작성된 HTML요소 내부에서 item 사용 가능

 

th:object와 *{필드명} 사용

  *{key} : 선택 변수, 객체에 포함된 필드를 출력

  th:object 속성 : 작성된 태그 내에서
    지정된 객체의 필드를 쉽게 접근하게 하는 속성


 

#예제1 

 

[main.html]

<!DOCTYPE html>
<html lang="en" >
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Thymeleaf</title>
</head>
<body>
  <!-- fr-test 조각으로 변경 -->
   <div class="aaa" th:replace="~{fragments/temp :: fr-test}"></div>
  <h1>Thymeleaf</h1>

  <ol>
    <li>
      <a href="/example/ex1">타임리프 예제 1</a>
    </li>


</body>
</html>

 

[MainController]

package edu.kh.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller  // 컨트롤러 역할 (요청, 응답 제어) 명시 + Bean 등록
public class MainController {

	// "/" 요청 (== localhost, 최상위 주소) 시
	// 매핑하여 처리하는 메서드
	// -> index.html로 응답하는 것이 아닌
	//			해당 메서드에서 요청 처리/응답 수행
	
	// 장점 : java를 거쳐서 메인페이지가 보여짐
	//    -> 추가 세팅 값, DB 조회 값을 위임된 html에서 출력 가능
	//   == 메인 페이지에서 부터 DB 조회 값이 보여지게 된다
	
	@RequestMapping("/")  
	public String mainPage() {
		
		// 사용하는 템플릿 엔진 : Thymeleaf
		// Thymeleaf를 사용하는 프로젝트에서 forward시
		// 제공하는 접두사 : classpath:/templates/
		// 제공하는 접미사 : .html
		

		// classpath:/templates/common/main.html 파일로 forward
		return "common/main";
		
	}

	
	
}

 

[ExampleController]

package edu.kh.demo.controller;

import java.util.ArrayList;
import java.util.List;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import edu.kh.demo.dto.Student;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;

@Slf4j // log 필드 생성 및 초기화 자동완성 lombok 어노테이션
@RequestMapping("example") // /example로 시작하는 요청을 매핑
@Controller // 컨트롤러임을 명시 + bean 등록
public class ExampleController {

	
//Servlet/JSP 내장 객체 4종류의 데이터 유지 범위(scope)
	
		// 1) page : 현재 페이지
		// 2) request : 요청 받은 곳 + 요청 위임(forward) 받은 페이지
		// 3) session : 클라이언트가 서버 최초 접속 시 생성,
		//				연결한 브라우저 종료 또는 세션 시간 만료까지
		// 4) application : 서버 실행 시 1개만 생성
		//               서버가 종료 될 때까지 유지
		
	
	/* Model
	 *  - org.springframework.ui 패키지
	 *  
	 *  - Spring에서 데이터를 전달하는 역할의 객체
	 *  
	 *  - 데이터 유지 범위(scope) : 기본 request 
	 *  
	 *  - @SessionAttributes 와 함꼐 사용하면 session scope로 변경
	 *  
	 *  [Model을 이용해서 값을 세팅하는 방법]
	 *  Model.addAttribute("key", value);
	 */
     
	@GetMapping("ex1")
	public String ex1(HttpServletRequest req, Model model) {
		
			// request scope에 값 세팅
			req.setAttribute("test1", "HttpServletRequest로 세팅한 값");
		
			// model을 이용해서 request scope 값 세팅
			model.addAttribute("test2", "Model로 세팅한 값");
			
			// 단일 값 세팅(숫자 문자열)
			model.addAttribute("productName", "아이스 아메리카노");
			model.addAttribute("price", 2000);
			
			// 복수 값 세팅(배열, List)
			List<String> fruitList = new ArrayList<>();
			fruitList.add("복숭아");
			fruitList.add("딸기");
			fruitList.add("수박");
			fruitList.add("바나나");
			
			model.addAttribute("fruitList", fruitList);
			
			//DTO 객체를 만들어 Model에 세팅 + 빌더 패턴 사용
			Student std = Student.builder()
								.studentNo("1111")
								.name("짱구")
								.age(15)
								.build();
			// -> 필드 전체가 아닌 일부 초기화 시 활용도가 좋음
			
			
			log.debug("std : {}", std);
																			
			model.addAttribute("std", std);
			
			
		//---------------------------------------------------
			
		// DTO 필드 중 List가 포함되어 있는 경우
			List<String> hobbyList = new ArrayList<>();
			 hobbyList.add("축구");
			 hobbyList.add("독서");
			 hobbyList.add("코딩 공부");
			 
			 Student std2 = Student.builder()
					 			.studentNo("2222")
					 			.name("철수")
					 			.age(20)
					 			.hobbyList(hobbyList)
					 			.build();
			 
			 model.addAttribute("std2", std2);
			 
		// classpath:/templates/ex/result1.html 파일로 
		// forward(요청 위임)
		return"ex/result1";
	}
	
}

 

 

[Student.java]

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString
@Builder // 빌더 패턴을 위한 메서드 자동완성 어노테이션
		 // 빌더 패턴 : 특정한 값으로 초기화된 객체를 쉽게 만들기 위한
		 //			메서드를 만드는 패턴
public class Student {
	private String studentNo;			  // 학번
	private String name;    			// 이름
	private int age;					// 나이
	private List<String> hobbyList; // 취미목록

	}

 

[result1]

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>타임리프 예제1</title>
</head>
<body>
  <h1>타임리프 예제1</h1>
  

  </pre>
  <!-- t:tex -->
  <h4 th:text="${test1}">key값 test1에 세팅된 값</h4>
  <h4 th:text="${test2}">key값 test2에 세팅된 값</h4>

  <hr>

  <h3>단일 값 출력</h3>


  <p>

    상품명 : <th:block th:text="${productName}"></th:block>
    <br>
    가격 : <th:block th:text="${price}"></th:block>원 
   
  </p>

  <hr>
  <h3>복수(다수) 값 출력</h3>
  <p th:text="${fruitList}"></p>

  <h4>fruitList에 저장된 요소 1개씩 출력 - index 이용</h4>
  <pre>
    - 지정된 index 번째 요소를 얻어올 수 있음
    - 단, 작성법은 배열, 리스트 가리지 않고 모두 [index]
  </pre>
  <ul>
    <li th:text="${fruitList[0]}"></li>
    <li th:text="${fruitList[1]}"></li>
    <li th:text="${fruitList[2]}"></li>
    <li th:text="${fruitList[3]}"></li>
  </ul>

  <h4>fruitList에 저장된 요소 1개씩 출력 - th:each 이용</h4>

  <pre>
    th:each="item : ${list}"  
    
    - 해당 HTML 요소를 list의 길이 만큼 반복

    - list에 저장된 요소를 순차접근하여 item에 저장

    - 해당 코드가 작성된 HTML요소 내부에서 item 사용 가능
  </pre>

  <ul>
    <!-- 
      th:block 태그를 이용해서 반복문 th:each 작성
      -> 반복이 끝나면 th:block태그는 사라짐 
    -->
    <th:block th:each="f : ${fruitList}">
      <li th:text="${f}"></li>
    </th:block>
  </ul>

  <hr>
  <h3>DTO 값 출력</h3>

  <ul>
    <li th:text="${std.studentNo}">학번</li>
    <li th:text="${std.name}">이름</li>
    <li th:text="${std.age}">나이</li>
  </ul>

  <h3>th:object와 *{필드명} 사용하기</h3>
  <pre>
  *{key} : 선택 변수, 객체에 포함된 필드를 출력

  th:object 속성 : 작성된 태그 내에서
    지정된 객체의 필드를 쉽게 접근하게 하는 속성

  <ul th:object="${std}">
    <li th:text ="*{studentNo}">학번</li>
    <li th:text ="*{name}">이름</li>
    <li th:text ="*{age}">나이</li>
  </ul>

  <hr>
  
  <h4>DTO 필드 중 List가 포함된 경우</h4>
  <ul th:object="${std2}">
    <li th:text="*{studentNo}"></li>
    <li th:text="*{name}"></li>
    <li th:text="*{age}"></li>

    <ul>
      <li th:each="hobby : *{hobbyList}" 
          th:text="${hobby}">취미</li>

    </ul>

  </ul>

  </pre>
</body>
</html>

\


#예제2

[main.html]

    <li>
      <form action="/example/ex2" method="post">
        <h4>타임리프 예제 2</h4>

        이름 : <input type="text"   name="inputName"> <br>
        나이 : <input type="number" name="inputAge">  <br>
        <br>
        
        색상 :
        Red   <input type="checkbox" name="color" value="Red">
        Green <input type="checkbox" name="color" value="Green">
        Blue  <input type="checkbox" name="color" value="Blue">

        <button>제출하기</button>
      </form>
    </li>

 

[ExampleController]

package edu.kh.demo.controller;

import java.util.ArrayList;
import java.util.List;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import edu.kh.demo.dto.Student;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;

@Slf4j // log 필드 생성 및 초기화 자동완성 lombok 어노테이션
@RequestMapping("example") // /example로 시작하는 요청을 매핑
@Controller // 컨트롤러임을 명시 + bean 등록
public class ExampleController {

	
	/**
	 * 
	 * @param model : Spring에서 데이터를 전달하는 용도의 객체
	 * 													(기본 scope : request)
	 * @return 
	 */
	@PostMapping("ex2") // /example/ex2 POST 방식 요청 매핑
	public String ex2(Model model) {
		
			model.addAttribute("str", "<h1>테스트 중입니다...&times; </h1>");
		
		 // classpath:/templates/ex/result2.html 파일로 
		 // forward(요청 위임)
		 return "ex/result2";
		
	}

 

[result2.html]

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>타임리프 예제2</title>
</head>
<body>
  <h1>타임리프 예제2</h1>

  <h3> ${param.key} : 제출된 파라미터 얻어오기 </h3>

  <!-- | 내용 | : 내용 그대로를 출력 -->

  <!-- 제출된 이름 : 홍길동 -->
  <h4 th:text="|제출된 이름 : ${param.inputName}|"></h4>
  <h4 th:text="|제출된 나이 : ${param.inputAge}|"></h4>

  <!-- 같은 key값의 파라미터가 여러 개면 배열 형태로 반환됨 -->
  <h4 th:text="|선택한 색상 : ${param.color}|"></h4>

  <ol>  <!-- 선택된 색상이 없으면 반복 X -->
    <li th:each="c : ${param.color}"  
        th:text="${c}">
        선택한 색상
    </li>
  </ol>

  <hr>

  <h3> th:text   VS   th:utext</h3>

  <pre>
    th:text = "속성값"
    - 해당 태그에 "속성값"을 내용으로 출력
    - 단, html 태그, 특수문자 해석 X (innerText)

    th:utext = "속성값"
    - 해당 태그에 "속성값"을 내용으로 출력
    - 단, html 태그, 특수문자 해석 O (innerHTML)
  </pre>

  <h4>${str} 값을 th:text로 출력하기 -> innerText</h4>
  <th:block th:text="${str}"></th:block>

  <h4>${str} 값을 th:utext로 출력하기 -> innerHTML</h4>
  <th:block th:utext="${str}"></th:block>


  <hr>
  <h3>th:text / th:utext를 대체하는 특수 기호(괄호)</h3>

  <!-- 
    th:text  == [[...]]
    th:utext == [(...)]
  -->
  <p>
    str(text) : [[${str}]]
  </p>

  <p>
    str(utext) : [(${str})]
  </p>

  
  <script th:inline="javascript">
    // th:text / th:utext 특수 기호 실사용 예시

    const name = /*[[${param.inputName}]]*/ "inputName";
    console.log(name);
  </script>

  <hr>

  <h1>messages.properties 값 얻어와 출력하기</h1>

  <pre>
    #{key} : messages.properties에 작성된 값 중
            key가 일치하는 값을 얻어와 출력
  </pre>

  <h2 th:text="#{app.name}">앱 이름</h2>

  <!-- 이미지 출력(static폴더 기준으로 경로 작성) -->
  <img src="/images/logo.jfif" >

  <hr>
  <!-- th:src 속성 : 타임리프 구문을 해석한 후
                해당 태그의 src 속성으로 대입
    -->
  <img th:src="#{image.logo}">
   <!-- footer.html 파일을 해당 위치에 출력 -->
   <th:block th:replace= "~{fragments/footer}"></th:block>
</body>
</html>

'BackEnd > Spring' 카테고리의 다른 글

[Spring]Spring Starter를 이용한 프로젝트 생성  (0) 2024.10.06
[Spring]Spring Boot 개발 환경 설정  (1) 2024.10.06
[Spring] Spring Boot  (0) 2024.09.03
[Spring]IOC/DI  (0) 2024.09.03
[Spring] Spring Framework 기본개념  (0) 2024.09.03