thymeleaf
natural template으로 html처럼 작성한다.
준비
👉🏻1) 별도의 라이브러리 서버의 내부에서 처리되는 템플릿- 별도의 라이브러리가 필요하다.
2) 이클립스에서는 편집 기능을 기본으로 제공하지 않는다.
- 추가적인 플러그인 설치
3) 자동 재시작 기능
- 자동 재시작 기능 코드를 수정하면 자동으로 스프링 부트를 재시작해서 수정된 코드를 반영
컨트롤러 예시
👉🏻@Controller- 사용자 요청과 응답을 처리 (주소 분기 및 매핑) (= servlet 클래스)
- http통신에 관련된 사항만 작성
@GetMapping(”uri”)
- GET방식의 http 주소 요청시 핸들러 특정 메소드에 맵핑하는 어노테이션
👉🏻핸들러 특정 메소드 리턴 타입 String(@Controller를 기재한 클래스에서만)- 논리적인 경로를 의미한다.
- prefix + return + subfix
- prefix ⇒ “classpath:/templates/” (=”src/main/resources/templates/”)
- return ⇒ 뷰
- sufix ⇒ “.html”
👉🏻Model : 화면(뷰)에 전달하는 객체(데이터)👉🏻ModelAndView : 초기에 썻던 방식 (객체 + view 논리적 경로)➡️ html 파일은 templates 폴더 내에 작성한다.
👉🏻타임리프 구문- html 태그안에 xmlns:th="http://www.thymeleaf.org” 를 작성 해줘야 타임리프 구문을 사용할 수 있다.
- 문법 : ${키} → controller 계층에서 보내진 Model의 키값에 대치하는 값을 바인딩한다.
➡️ 인라인 스타일로 작성하는 방법 : [[${타임리프 구문}]]
➡️ 태그의 속성으로 작성하는 방법 : <태그 th:속성=”${타임리프 구문}”></태그>
🗣 타임리프 주석 : <!--/* 내용 */-->
MainController.java
package com.springboot.controller; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; @Controller public class MainController { @GetMapping("/") // 주소 요청 public String mainPage() { return "main"; // /templates/main.html } @GetMapping("/view") // 주소 요청 public String exView(Model model) { /* Model 데이터 처리 */ // addAttribute(String name, Object value) : value 객체를 name 이름으로 추가한다. // 뷰 코드에서 name으로 지정한 이름을 통해서 value를 사용한다. model.addAttribute("contents", "exView view"); return "exView"; // /templates/exViex.html } /* ModelAndView 방식 @GetMapping("/") public ModelAndView main(ModelAndView mav) { // 컨트롤러에서 ModelAndView를 자동으로 객체 생성 mav.addObject("title", "멋쟁이"); mav.addObject("contents", "main화면"); mav.setViewName("main"); return mav; } */ }
main.html
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title th:text="${title}">Insert title here</title> </head> <body> <!-- 인라인스타일로 작성하는 방법 --> <h1>[[${contents}]]</h1> <!-- 태그의 속성으로 작성하는 방법 --> <h1 th:text="${contents}"></h1> </body> </html>
객체 출력 예시
👉🏻Member 클래스 생성(dto)Member.java
package com.thymeleaf.dto; public class Member { private String memberId; private String memberPw; private String memberLevel; private String memberName; private String memberEmail; // 1 기본 생성자, 매개변수 있는 생성자 public Member() {} public Member(String memberId, String memberPw, String memberLevel, String memberName, String memberEmail){ this.memberId = memberId; this.memberPw = memberPw; this.memberLevel = memberLevel; this.memberName = memberName; this.memberEmail = memberEmail; } // 2 set, get public void setMemberId(String memberId) { this.memberId = memberId; } public String getMemberId() { return memberId; } public String getMemberPw() { return memberPw; } public void setMemberPw(String memberPw) { this.memberPw = memberPw; } public String getMemberLevel() { return memberLevel; } public void setMemberLevel(String memberLevel) { this.memberLevel = memberLevel; } public String getMemberName() { return memberName; } public void setMemberName(String memberName) { this.memberName = memberName; } public String getMemberEmail() { return memberEmail; } public void setMemberEmail(String memberEmail) { this.memberEmail = memberEmail; } // toString 출력 @Override public String toString() { final StringBuilder sb = new StringBuilder("Member{"); sb.append("memberId='").append(memberId).append('\''); sb.append(", memberPw='").append(memberPw).append('\''); sb.append(", memberLevel='").append(memberLevel).append('\''); sb.append(", memberName='").append(memberName).append('\''); sb.append(", memberEmail='").append(memberEmail).append('\''); sb.append('}'); return sb.toString(); } }
👉🏻컨트롤러의 핸들러 메소드에서 객체 생성Controller.java
package com.thymeleaf.controller; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; @Controller public class Controller { @GetMapping("/exDto") // 주소요청 public String main(Model model) { model.addAttribute("title", "멋쟁이"); // 매개변수가 있는 생성자로 객체 생성 Member member = new Member("id001", "pw001", "관리자", "홍길동", "홍01@email.com"); model.addAttribute("member", member); // 데이터 전달 return "exClass"; // /templates/exClass.html } }
👉🏻Model을 통해 전달된 데이터를 받아서 화면에 출력주소요청 ➡️ hocalhost/exDto
exClass.html
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <!-- 타임리프 xml 네임 스페이스 설정 --> <head> <meta charset="UTF-8"> <title th:text="${title}">main</title> <!-- title에 담긴 "멋쟁이"가 화면에 출력 --> </head> <body> <h1>[[${member}]]</h1> <!-- member에 담긴 객체가 화면 출력(인라인 방식) --> <h1 th:text="${member}"></h1> <!-- member에 담긴 객체가 화면 출력(태그 속성 방식) --> </body> </html>
thymeleaf 객체 바인딩, 반복문, 조건문, 지역변수
👉🏻@Service- 비즈니스 로직(기능)을 구현(담당)하는 클래스
- 트랜잭션의 단위 및 처리
@PostConstruct
- IOC가 해당 클래스를 Bean(스프링이 관리하는 객체)으로 생성된 후 해당 이벤트 내용을 실행한다.
👉🏻thymeleaf 객체 바인딩- 문법 : ${키}
- 객체 접근
➡️ 도트연산자
➡️ 객체[’키’]
- 이스케이스 vs 언이스케이프
➡️ 이스케이프 : HTML에서 사용되는 특수문자를 HTML 엔티티로 변경하는 것
➡️ HTML 엔티티 : HTML에서 특정 문자들이 예약되어 있기 때문에 표기의 혼란을 방지하기 위해 코드 형태로 표현하는 방식
- javascript 바인딩
➡️ <script type=”text/javascript” th:inline=”javascript”>
(자바스크립트 구문)
</script>
👉🏻thymeleaf 반복문- 단순 반복문 X
- 반복 가능한 객체를 통해 반복문 수행(ex : 배열 및 콜렉션 객체)
예시) <태그 th:each=”객체, 반복시 객체 상태 : ${키}>
(반복하는 범위)
</태그>
- 유효범위 : 태그가 닫힐 때까지
➡️ 타임리프상에서 배열을 생성해서 단순반복문 활용
- #numbers.sequence(초기값, 종료값, 증감)
👉🏻thymeleaf 조건문과 지역변수- 조건문
➡️ 태그 속성을 활용해서 조건문 작성 (th:if, th:unless, th:switch)
➡️ if ~ else if ~ else (기본적으로 if ~ else만 존재한다. else if 가 없고, switch로 활용)
➡️ if(th:if) ~ else(th:unless) 구문 존재
➡️ 예시)
memberInfo != null;
th:if=”${membrerInfo == null}” → true(화면 출력)
- 조건식이 true일 때 true 출력
th:unless=”${emberInfo == null}” → true(화면 출력)
- 조건식이 false일 때 true 출력
➡️ switch(th:switch)
➡️ 예시)
th:switch=”${memberInfo.memberLevel}”
th:case=”’관리자’” …
- 관리자일 경우에 태그 내용 화면 출력
th:case=”’판매자’” …
- 판매자일 경우에 태그 내용 화면 출력
th:case=”*” …
- 관리자나 판매자가 아닐 경우에 태그 내용 화면 출력
- 지역변수
➡️ th:with=”지역변수=’${키}’”
- th:block : 타임리프 { } 블록문
예시)
<th:block th:if”${memberId == ‘id001’}”>
- ⇒ if(memberId == ‘id001’) {
</th:block>
- ⇒ }
<th:block th:each=”member : ${memberList}>
- ⇒ 1️⃣ <% for(int i=0; i=memberList.size(); i++) { %>
- ⇒ 2️⃣ for(Member member : memberList)
</th:block>
- ⇒ 1️⃣ <% } %>
- ⇒ 2️⃣ }
Service.java
package com.thymeleaf.service; import com.thymeleaf.dto.Member; import jakarta.annotation.PostConstruct; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.List; @Service public class MemberService { // @PostConstruct : 해당 클래스를 스프링이 관리하는 객체로 생성 후 이벤트 실행 @PostConstruct public void memberServiceInit() { System.out.println("MemberService 객체 생성"); } // 객체를 반환하는 메소드 public Member getMemberInfo() { Member memberInfo = new Member("id001", "pw001", "관리자", "홍01", "홍01@email.com"); return memberInfo; } // 리스트를 반환 메소드, List 객체 안에 Member객체 9개 생성 public List<Member> getMemberList() { List<Member> lm = new ArrayList<Member>(); String[] memberLevelArr = {"구매자", "관리자", "판매자"}; Member memberInfo = null; for(int i=1; i<10; i+=1){ memberInfo = new Member("id00"+i, "pw00"+i, memberLevelArr[i%3], "홍0"+i, "홍0"+i+"@email.com"); lm.add(memberInfo); } return lm; } }
Controller.java
package com.thymeleaf.controller; import com.thymeleaf.dto.Member; import com.thymeleaf.service.MemberService; import jakarta.annotation.PostConstruct; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import java.util.List; @Controller // 주소 분기 및 맵핑 public class ExamController { // 생성자 메소드 주입방식(@Autowired 명시하지 않아도 의존성 주입) private final MemberService memberService; public ExamController(MemberService memberService){ this.memberService = memberService; } // @PostConstruct : 해당 클래스를 스프링이 관리하는 객체로 생성 후 해당 이벤트 내용 실행 @PostConstruct public void examControllerInit() { System.out.println("ExamController 객체 생성"); } @GetMapping("/exam1") // 주소 요청 (localhost/exam1) public String exam1(Model model) { model.addAttribute("title", "exam1"); Member memberInfo = memberService.getMemberInfo(); model.addAttribute("memberInfo", memberInfo); return "exam/exam1"; // /templates/exam/exam1.html } @GetMapping("/exam2") // 주소 요청 (localhost/exam2) public String exam2(Model model) { model.addAttribute("title", "exam2"); List<Member> memberList = memberService.getMemberList(); model.addAttribute("memberList", memberList); return "exam/exam2"; // /templates/exam/exam2.html } @GetMapping("/exam3") // 주소 요청(localhost/exam3) public String exam3(Model model) { model.addAttribute("title", "exam3"); Member memberInfo = memberService.getMemberInfo(); List<Member> memberList = memberService.getMemberList(); model.addAttribute("memberInfo", memberInfo); model.addAttribute("memberList", memberList); return "exam/exam3"; // /templates/exam/exam3.html } @GetMapping("/exam4") // 주소 요청(localhost/exam4) public String exam4(Model model) { model.addAttribute("title", "exam4"); List<Member> memberList = memberService.getMemberList(); model.addAttribute("memberList", memberList); model.addAttribute("targetId", "id002"); return "exam/exam4"; // /templates/exam/exam4.html } }
exam1.html (주소 : localhost/exam1, 폴더경로 : templates/exam/exam1.html)
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title th:text="${title}">객체 바인딩</title> </head> <body> <!-- 인라인 방식, 태그 속성 방식 --> <h3 th:text="${memberInfo}"></h3> <h3>[[${memberInfo}]]</h3> <!-- 객체접근 : 도트연산자, 객체['키'] --> <h3 th:text="${memberInfo.memberId}"></h3> <h3 th:text="${memberInfo.getMemberId()}"></h3> <h3 th:text="${memberInfo['memberId']}"></h3> <!-- 이스케이프, 언이스케이프 --> <h3 th:text="${'멋쟁이<br>개발자'}"></h3> <!-- br태그까지 텍스트로(코드형태로) 출력(HTML엔티티로 변경) --> <h3 th:utext="${'멋쟁이<br>개발자'}"></h3> <!-- br태그 언이스케이프 --> <div th:utext="${'<p>멋쟁이</p><p>개발자</p>'}"></div> <!-- p태그 언이스케이프 --> <!-- javascript 바인딩 --> <script type="text/javascript" th:inline="javascript"> const memberInfo = [[${memberInfo}]]; console.log(memberInfo); for(let key in memberInfo) { console.log(`${key} : ${memberInfo[key]}`); /* 콘솔창에 출력 */ } </script> </body> </html>
exam2.html (주소 : localhost/exam2, 폴더경로 : templates/exam/exam2.html)
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title th:text="${title}">반복문</title> <style> table{ border: 1px solid black; width: 700px; table-layout: auto; text-align: center; } th{ border: 1px solid black; background-color: cornflowerblue; color: azure; height: 30px; } td{ border: 1px solid black; height: 30px; } </style> </head> <body> <h1>thymeleaf 반복문</h1> <table> <thead> <tr> <th>아이디</th> <th>비밀번호</th> <th>권한</th> <th>이름</th> <th>이메일</th> </tr> </thead> <tbody> <!-- tbody안에 행이 반복해서 생성 되어야 하니까 tr태그에 반복문 사용 --> <tr th:each="l, status : ${memberList}"> <td th:text="${l.memberId}"></td> <td th:text="${l.memberPw}"></td> <td th:text="${l.memberLevel}"></td> <td th:text="${l.memberName}"></td> <td th:text="${l.memberEmail}"></td> </tr> </tbody> <tfoot> <tr th:each="l, status : ${memberList}"> <td th:text="${status.index}">배열의 0번째부터 반환</td> <td th:text="${status.count}">숫자 1부터 반환</td> <td th:text="${status.size}">배열의 전체크기 반환</td> <td th:text="${status.odd + '||' + status.even}">배열의 홀수/짝수 여부</td> <td th:text="${status.first + '||' + status.last}">배열의 첫번째/마지막번째 요소 여부</td> </tr> </tfoot> </table> <!-- 타임리프상에서 배열 생성해서 단순 반복문 --> <div th:each="num : ${#numbers.sequence(1, 10, 2)}"> <label th:text="${num}"></label> </div> </body> </html>
exam3.html (주소 : localhost/exam3, 폴더경로 : templates/exam/exam3.html)
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title th:text="${title}">조건문과 지역변수</title> <style> table{ border: 1px solid black; width: 700px; table-layout: auto; text-align: center; } th{ border: 1px solid black; background-color: cornflowerblue; color: azure; height: 30px; } td{ border: 1px solid black; height: 30px; } </style> </head> <body> <table> <thead> <tr> <th>회원아이디</th> <th>회원비밀번호</th> <th>회원등급</th> <th>회원이름</th> <th>회원이메일</th> </tr> </thead> <tbody> <!-- 타임리프 반복문 --> <tr th:each="l : ${memberList}"> <!-- 조건문 --> <td th:if="${l.memberId == 'id001'}" th:text="${l.memberId}" style="color: red;"></td> <td th:unless="${l.memberId == 'id001'}" th:text="${l.memberId}"></td> <td th:text="${l.memberPw}"></td> <!-- switch 문 --> <th:block th:switch="${l.memberLevel}"> <td th:case="'관리자'" th:text="${l.memberLevel}" style="color: red;"></td> <td th:case="${'판매자'}" th:text="${l.memberLevel}" style="color: blue;"></td> <td th:case="*" th:text="${l.memberLevel}" style="color: green;"></td> </th:block> <td th:text="${l.memberName}"></td> <td>[[${l.memberEmail}]]</td> </tr> </tbody> </table> </body> </html>
exam4.html (주소 : localhost/exam4, 폴더경로 : templates/exam/exam4.html)
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title th:text="${title}">실습</title> <style> table { border: 1px solid black; width: 700px; table-layout: auto; text-align: center; } th { border: 1px solid black; background-color: cornflowerblue; color: azure; height: 30px; } td { border: 1px solid black; height: 30px; } </style> </head> <body> <h1>실습문제</h1> - 회원리스트 객체가 없을 시 '등록된 회원이 없습니다.' <br> - 회원리스트 중 id002 인 회원의 행의 전체 글자색을 blue 지정(지역변수 활용) <br> <table> <thead> <tr> <th>회원아이디</th> <th>회원비밀번호</th> <th>회원권한</th> <th>회원이름</th> <th>회원이메일</th> </tr> </thead> <tbody th:with="checkId = ${targetId}"> <!-- 지역변수 사용 --> <tr th:if="${memberList == null}"> <!-- 조건문 --> <td colspan="5">등록된 회원이 없습니다.</td> </tr> <th:block> <!-- 블록문 --> <tr th:unless="${memberList == null}" th:each="l : ${memberList}" th:style="${(l.memberId == checkId) ? 'color:blue;' : ''}"> <!-- 조건문, 반복문, 지역변수 활용(삼항연산자) --> <td th:text="${l.memberId}"></td> <td th:text="${l.memberPw}"></td> <td th:text="${l.memberLevel}"></td> <td th:text="${l.memberName}"></td> <td th:text="${l.memberEmail}"></td> </tr> </th:block> </tbody> </table> </body> </html>
tag : #Spring #thymeleaf #Controller #dto #Service #PostConstruct #의존성주입 #객체 바인딩 #반복문 #조건문 #지역변수
Uploaded by N2T