스프링

 

1. Thymeleaf 적용

spring boot 2.6.1.RELEASE 버전

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>
		<dependency>
			<groupId>nz.net.ultraq.thymeleaf</groupId>
			<artifactId>thymeleaf-layout-dialect</artifactId>
			<version>3.0.0</version>
		</dependency>

 

 

2 . 사용법

 

표현식

① 변수 : ${ }

② 객체 변숫값 : *{ }

③메시지 : #{ } 

④ 링크 : @{ }

 

예1)

@Getter @Setter
public class Board {

    private int no;
    private String title;
    private String writer;
    private LocalDateTime updateTime;

    Board(){}

    public Board(int no, String title, String writer) {
        this.no = no;
        this.title = title;
        this.writer = writer;
        this.updateTime = LocalDateTime.now();
    }

 

@Controller
public class ThymeController {

    @RequestMapping("/")
    String indexPage(Model model){
        Board board = new Board(1, "테스트 제목", "이종민2");
        model.addAttribute("board", board);
        return "index";
    }

}

 

<tbody>
    <tr th:object="${board}">
        <td><span th:text="*{no}"></span></td>
        <td><span th:text="*{title}"></span></td>
        <td><span th:text="*{writer}"></span></td>
        <td><span th:text="${#temporals.format(board.updateTime, 'yyyy-MM-dd HH:mm')}"></span></td>
    </tr>
</tbody>

 

table의 tbody 부분을 수정했습니다. $로 변수를 가져오고 *를 통해서 선택적 변수를 가져옵니다. 날짜의 경우 LocalDateTime을 변환하기 위해서

temporals개체를 이용했습니다. 이 외에도 다양한 개체들이 존재합니다.

#dates : java.util.Date를 다루기 위해 사용.

#calendars : #dates와 비슷하지만 java.util.Calendar를 위해 사용.

#number : 숫자 객체를 형식화하기 위해 사용.

#strings : String객체를 위해 사용.(contains, startsWith, prepending/appending 등)

#objects : 일반적인 객체를 다룬다.

#bools : boolean을 위해 사용.

#arrays : 배열을 위해 사용.

#lists : 리스트를 위한 유틸리티 메소드.

#sets : set을 위한 유틸리티 메소드.

#maps : map을 위한 유틸리티 메소드.

#aggreates : Array 또는 Collenction에서 집계를 위한 메소드.

#ids : 반복될 수 있는 id 속성을 처리.

     

    예2)

    @Getter @Setter
    public class ItemFormDto {
    
        private Long id;
    
        @NotBlank(message = "상품명은 필수 입력 값입니다.")
        private String itemNm;
    
        @NotNull(message = "가격은 필수 입력 값입니다.")
        private Integer price;
    
        @NotBlank(message = "이름은 필수 입력 값입니다.")
        private Integer itemDetail;
    
        @NotNull(message = "재고는 필수 입력 값입니다.")
        private Integer stockNumber;
    
    
        private ItemSellStatus itemSellStatus;
    
        //1. 상품 저장 후 수정할 때 상품 이미지 정보를 저장하는 리스트
        private List<ItemImgDto> itemImgDtoList=new ArrayList<>();
    
        //2.상품의 이미지 아이디를 저장하는 리스트. 상품 등록 시에는 아직 상품의 이미지를 저장하지 않았기
        //때문에 아무 값도 들어가 있지 않고 수정 시에 이미지 아이디를 담아둘 용도로 사용
        private List<Long> itemImgIds=new ArrayList<>();
    
    
        private static ModelMapper modelMapper =new ModelMapper();
    
        //modelMapper 를 이용하여 엔티티 객체와 DTO 객체 간의 데이터를 복사하여 복사한 객체를 반환해주는 메소드
        public Item createItm(){
            return modelMapper.map(this, Item.class);
        }
    
    
        //modelMapper 를 이용하여 엔티티 객체와 DTO 객체 간의 데이터를 복사하여 복사한 객체를 반환해주는 메소드
        public static ItemFormDto of(Item item){
            return modelMapper.map(item, ItemFormDto.class);
        }
    
    }
    

     

    @Controller
    public class ItemController {
    
        @GetMapping(value = "/admin/item/new")
        public String itemForm(Model model){
            model.addAttribute("itemFormDto", new ItemFormDto());
            return "/item/itemForm";
        }
    }
    

     

     

    itemFormDto 각 ①변수 : ${ } 로 사용    itemDetail  이 ②객체 변숫값 : *{ } 로 사용

    th:each="num: ${#numbers.sequence(1,5)}  의 each 문에서 ${num }  ①변수 : ${ } 로 사용

     

     

     <form role="form" method="post" enctype="multipart/form-data" th:object="${itemFormDto}">
    
    
      <div class="input-group">
                   <div class="input-group-prepend">
                        <span class="input-group-text">상품 상세 내용</span>
                   </div>
                   <textarea class="form-control" aria-label="With textarea" th:field="*{itemDetail}"></textarea>
              </div>
              <p th:if="${#fields.hasErrors('itemDetail')}" th:errors="*{itemDetail}" class="fieldError">Incorrect data</p>
    
              <div th:if="${#lists.isEmpty(itemFormDto.itemImgDtoList)}">
                   <div class="form-group" th:each="num: ${#numbers.sequence(1,5)}">
                        <div class="custom-file img-div">
                             <input type="file" class="custom-file-input" name="itemImgFile">
                             <label class="custom-file-label" th:text="상품이미지 + ${num}"></label>
                        </div>
                   </div>
              </div>
    
     <div th:if="${#strings.isEmpty(itemFormDto.id)}" style="text-align: center">
                   <button th:formaction="@{/admin/item/new}" type="submit" class="btn btn-primary">저장</button>
              </div>

     

    #lists : 리스트를 위한 유틸리티 메소드.


    #sets : set을 위한 유틸리티 메소드.


    #fields

    form 태그에 포함된 action속성 값을 th:action속성 값으로 치환할 수 있으며, th:object속성 값에 Model속성 이름을 지정하여 이 태그 안에서 *{필드이름} 형식으로 사용할 수 있다.

    th:field="*{필드 이름}"을 설정하면 이 HTML필드와 폼 객체(여기서는 CustomerForm)에 포함된 필드를 연결할 수 있고 HTML필드 값이 폼 객체의 해당 필드로 설정된다. 반대로 폼 객체의 필드 값이 Model에서 HTML필드 값을 설정된다.

    입력 검사에서 오류가 발견됐을 경우에는 th:errorclass속성 값이 class속성에 설정된다. 필드에 오류가 있을 경우에만 특정 태그를 표시하고 싶다면 해당 태그에 th:if="${#fields.hasErrors('필드이름')"을 설정한다. th:errors="*{필드이름}"을 설정하면 태그 안에 있는 문자열을 대상 필드에 관련된 에러 메시지로 치환할 수 있다.

     

    ${#numbers.sequence(1,5)}

    타임리프의 유틸리티 객체 #numbers.sequence(start,end) 를 이용하면 start 부터 end 까지 반복 처리를 할 수 잇습니다.

    num 에는 1부터 5까지 숫자가 할당됨

    <!--          상품 이미지 정보를 담고 있는 리스트가 비어 있다면 상품을 등록하는 경우-->
              <div th:if="${#lists.isEmpty(itemFormDto.itemImgDtoList)}">
                   <div class="form-group" th:each="num: ${#numbers.sequence(1,5)}">
                        <div class="custom-file img-div">
                             <input type="file" class="custom-file-input" name="itemImgFile">
                             <label class="custom-file-label" th:text="상품이미지 + ${num}"></label>
                        </div>
                   </div>
              </div>
    

     

     

     

     

     

     

    링크 : @{ }

    @{/admin/item/new}

     

     

     

    ※  data-th-text, th:text 둘 중 아무거나 사용해도 상관없음

    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    import java.time.LocalDateTime;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    @Controller
    @RequestMapping(value ="/thymeleaf")
    public class ThymeleafExController {
    
    
        @GetMapping("/ex00")
        public String indexPage(Model model) {
    
            Map<String, Object> info = new HashMap<>();
            info.put("name", "kim");
            info.put("age", 29);
    
            List<String> datas = new ArrayList<>();
            datas.add("red");
            datas.add("orange");
            datas.add("yellow");
            model.addAttribute("info", info);
            model.addAttribute("datas", datas);
            return "thymeleaf/ex00";
        }
    
    
        @GetMapping(value = "/ex01")
        public String ex01(Model model){
            model.addAttribute("data", "타임리프");
            model.addAttribute("htmlData", "<h1>타임리프</h1>");
            return "thymeleaf/ex01";
        }
    
        /**
         * th:text 예제
         * @param model
         * @return
         */
        @GetMapping(value = "/ex02")
        public String ex02(Model model){
            ItemDto itemDto =new ItemDto();
            itemDto.setItemDetail("상품상세설명");
            itemDto.setItemNm("테스트상품1");
            itemDto.setPrice(1000);
            itemDto.setRegTime(LocalDateTime.now());
    
            model.addAttribute("itemDto", itemDto);
            return "thymeleaf/ex02";
        }
    
    
        /**
         * th:each 예제
         * @param model
         * @return
         */
        @GetMapping(value = "/ex03")
        public String ex03(Model model){
            List<ItemDto> ItemDtoList =new ArrayList<>();
    
            for(int i=0; i<=10; i++){
                ItemDto itemDto =new ItemDto();
                itemDto.setItemDetail("상품상세설명"+i);
                itemDto.setItemNm("테스트상품"+i);
                itemDto.setPrice(1000*i);
                itemDto.setRegTime(LocalDateTime.now());
                ItemDtoList.add(itemDto);
            }
    
            model.addAttribute("ItemDtoList" ,ItemDtoList);
            return "thymeleaf/ex03";
        }
    
    
        /**
         * th:if, th:unless 예제
         * @param model
         * @return
         */
        @GetMapping(value = "/ex04")
        public String ex04(Model model){
            List<ItemDto> itemDtoList =new ArrayList<>();
            for(int i=1; i<=10; i++){
                ItemDto itemDto=new ItemDto();
                itemDto.setItemDetail("상품상세설명"+i);
                itemDto.setItemNm("테스트상품"+i);
                itemDto.setPrice(1000*i);
                itemDto.setRegTime(LocalDateTime.now());
                itemDtoList.add(itemDto);
            }
            model.addAttribute("itemDtoList", itemDtoList);
            return "thymeleaf/ex04";
        }
    
    
        /**
         * th:switch, th:case 예제
         * @param model
         * @return
         */
        @GetMapping(value = "/ex05")
        public String ex05(Model model){
            List<ItemDto> itemDtoList =new ArrayList<>();
            for(int i=1; i<=10; i++){
                ItemDto itemDto=new ItemDto();
                itemDto.setItemDetail("상품상세설명"+i);
                itemDto.setItemNm("테스트상품"+i);
                itemDto.setPrice(1000*i);
                itemDto.setRegTime(LocalDateTime.now());
                itemDtoList.add(itemDto);
            }
            model.addAttribute("itemDtoList", itemDtoList);
            return "thymeleaf/ex05";
        }
    
        /**
         * th:href
         * @return
         */
        @GetMapping("/ex06")
        public String ex06(String param1, String param2, Model model){
            model.addAttribute("param1", param1);
            model.addAttribute("param2", param2);
            return "thymeleaf/ex06";
        }
    
    
     
    }
    

     

     

     

    1)값을 그대로 표출할 때

    <th:block th:text="${text}"></th:block>
    
    or
    <th:block data-th-text="${text}"></th:block
    or 
    
    <span>[[ ${text} ]]</span>

     

     

     

     

    2) ${...} 표현식

    ${...} 표현식을 이용해 컨트롤러에서 전달받은 변수에 접근할 수 있으며 th:속성명 내에서만 사용

    <!DOCTYPE html>
    <html xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <h1>상품 데이터 출력 예제</h1>
        <div>
            상품명 :  <span th:text="${itemDto.itemNm}"></span>
        </div>
        <div>
            상품상세설명 :  <span th:text="${itemDto.itemNm}"></span>
        </div>
        <div>
            상품등록일 : <span th:text="${itemDto.regTime}"></span>
        </div>
        <div>
            상품가격 : <span th:text="${itemDto.price}"></span>
        </div>
    
    </body>
    </html>

     

     

     

    3) @{...} 표현식

    @{...} 표현식은 서버의 contextPath를 의미하며 @{/} 는 "/contextPath/" 를 의미

    <!DOCTYPE html>
    <html xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <h1>th:if</h1>
        <h3>param1 :<span th:text="${param1}"></span></h3>
        <h3>param2:<span th:text="${param2}"></span></h3>
        <div>
            <a th:href="@{/thymeleaf/ex01}">예제1</a>
        </div>
        <div>
            <a th:href="@{https://www.thymeleaf.org}">thyemleaf </a>
        </div>
        <div>
            <a th:href="@{/thymeleaf/ex06(param1='데이터1' ,param2='데이터2')}">전송 </a>
        </div>
    </body>
    </html>

     

     

     

    4)html 태그가 들어가는 데이터 값을 그대로 삽입

    <th:block data-th-utext="${text}"></th:block>
        <!--html 태그가 들어가는 데이터 값을 그대로 삽입하고 싶을 때-->
        <th:block th:utext="${htmlData}"></th:block>
    
    

     

     

     

    5) 문자 합치기

    <div th:text="|My name is ${info.name} !! |"></div>
    <div th:text="'My name is '+ ${info.name} + ' !! '"></div>

     

    <span th:text="|${username}님 환영합니다|"></span>

     

     

     

    6) 비교 연산자

    a)

    <!-- 이항 연산자 -->
    <div th:text="${info.name != 'kim'}"></div>
    <div th:text="${info.age >= 30}"></div>
    
    <!-- 삼항 연산자 -->
    <div th:text="${info.name == 'kim' ? 'ok' : 'no'}"></div>
    

     

    b)

    package com.shop.constant;
    
    public enum ItemSellStatus {
        SELL, SOLD_OUT
    }
            <td th:text="${item.itemSellStatus == T(com.shop.constant.ItemSellStatus).SELL} ? '판매중' : '품절'"></td>
    

     

     

    7) th:value

    <input type='text' th:value="${info.name}">

     

     

     

     

    8) th:if, th:unless

    <th:block th:if="${info.age < 30}">당신은 30대가 아닙니다.</th:block>
    <th:block th:unless="${info.age >= 30}">당신은 30대입니다.</th:block>
    <th:block data-th-if="${test == null}"></th:block>
    <th:block data-th-unless="${test == null}"></th:block>
    <!DOCTYPE html>
    <html xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <h1>th:if, th:unless 예제</h1>
        <table border="1" width="1200">
            <thead>
                <tr>
                    <td width="10%">순번</td>
                    <td>상품명</td>
                    <td>상품설명</td>
                    <td width="15%">가격</td>
                    <td>상품등록일</td>
                </tr>
            </thead>
            <tbody>
                <tr th:each="itemDto, status:${itemDtoList}">
                    <td>
                        <span th:text="${status.index}"></span>
                        (<span th:if="${status.even}" th:text="짝수"></span>
                        <span th:unless="${status.even}" th:text="홀수"></span>)
                    </td>
                    <td th:text="${itemDto.itemNm}"></td>
                    <td th:text="${itemDto.itemDetail}"></td>
                    <td>
    
                        <!-- IF CUSTOMER IS ANONYMOUS -->
                        <div th:if="${itemDto.price} ==5000" th:text="오천원">
                        </div>
                        <!-- ELSE -->
                        <div th:unless="${itemDto.price}==5000" th:text="${itemDto.price}">
                        </div>
    
                    </td>
                    <td th:text="${itemDto.regTime}"></td>
                </tr>
            </tbody>
        </table>
    </body>
    </html>

     

     

     

     

    9) th:switch, th:case

    <th:block th:switch="${info.name}">
      <div th:case="lee"> 당신은 이씨 입니다.</div>
      <div th:case="kim"> 당신은 김씨 입니다.</div>
    </th:block>
    <th:block th:switch="${username}"> 
        <span th:case="홍길동">홍길동님 환영합니다</span> 
        <span th:case="길동이">길동님 환영합니다</span> 
    </th:block>
    <!DOCTYPE html>
    <html xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <h1>th:switch, th:case 예제</h1>
        <table border="1">
            <thead>
                <tr>
                    <td width="10%">순번</td>
                    <td>상품명</td>
                    <td>상품설명</td>
                    <td>가격</td>
                    <td>상품등록일</td>
                </tr>
            </thead>
            <tbody>
                <tr th:each="itemDto, status:${itemDtoList}">
                    <td th:switch="${status.index}">
                        <span th:case="2">이번</span>
                        <span th:case="5">오번</span>
                        <span th:case="*"><span th:text="${status.index}"></span>
                        </span>
                    </td>
                    <td>[[${itemDto.itemNm}]]</td>
                    <td th:text="${itemDto.itemDetail}"></td>
                    <td th:text="${itemDto.price}"></td>
                    <td th:text="${itemDto.regTime}"></td>
                </tr>
            </tbody>
        </table>
    </body>
    </html>

     

     

     

     

    10) th:each

    <th:block th:each="data:${datas}">
    	<h1 th:text="${data}"></h1>
    </th:block>

     

    변수명 앞에 status 변수를 추가해 row에 대한 추가정보를 얻을 수 있다.

     

    <th:block th:each="data,status:${datas}">
    	<h1 th:text="|${status.count} ${data}|"></h1>
    </th:block> 

     

    status 속성
    
    index : 0부터 시작
    count : 1부터 시작
    size : 총 개수
    current : 현재 index의 변수
    event/odd : 짝수/홀수 여부
    first/last : 처음/마지막 여부

     

    <!DOCTYPE html>
    <html xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    
        <h1>th:each 예제</h1>
        <table border="1">
            <thead>
                <tr>
                    <td>순번</td>
                    <td>상품명</td>
                    <td>상품설명</td>
                    <td>가격</td>
                    <td>상품등록일</td>
                </tr>
            </thead>
            <tbody>
                <tr th:each="itemDto, status:${ItemDtoList}">
                    <td th:text="${status.index}"></td>
                    <td th:text="${itemDto.itemNm}"></td>
                    <td th:text="${itemDto.itemDetail}"></td>
                    <td th:text="${itemDto.price}"></td>
                    <td th:text="${itemDto.regTime}"></td>
                </tr>
            </tbody>
    
        </table>
    
    </body>
    </html>

     

     

     

     

     

     11)  and, or 구문 ( if 조건 여러개 가능 )

    <th:block th:if="${view == null and view == ''}">
    <th:block th:if="${view != '홍길동' or view != '길동'}">

     

    <th:block data-th-if="${view == null and view == ''}">
    <th:block data-th-if="${view != '홍길동' or view != '길동'}">

     

     

     

     

     

     

    12) LIST 형식으로 리턴 된 값을 6번 출력

    <span data-th-each="i : ${#numbers.sequence(0,5)}" data-th-if="${i < #lists.size(LIST)}" data-th-text="${LIST[i]?.title}"></span>

     

     

     

     

    13) Thymeleaf 문자열 비교

    <th:block data-th-if="${#strings.equals(userName, testName)}"></th:block>

     

     

     

     

     

    14)  Thymeleaf 문자열, 배열(array), 리스트(list), 셋(sets)이 비어 있는지 확인

    ${#strings.isEmpty(title)} 
    ${#strings.arrayIsEmpty(array)} 
    ${#strings.listIsEmpty(list)} 
    ${#strings.setIsEmpty(set)}

     

     

     

     

    15 )map으로 리턴한 menu에서 menuArea 키 값이 존재하는지 확인

    <th:block th:if="${not #maps.isEmpty(menu)} and ${#maps.containsKey(menu, 'menuArea')}"></th:block>

     

     

     

    16) LIST 반복문을 돌리면서 첫번째 값일 경우 active 클래스 추가

    <div th:each="i : ${#numbers.sequence(0,1)}" th:if="${i < #lists.size(LIST)}" th:classappend="${i == 0} ? 'active':''">
    </div>

     

     

     

    17) data-th-with를 사용하면 변수와 변수 값 지정이 가능

    <th:block th:with="SIZE='123', view='/main'">
      <a th:href="${view}"></a>
    </th:block>

     

     

     

    18) style 지정

    <div th:style="|background: url(${url}) background-size: cover|"></div>

     

     

     

     

    19) 현재 날짜 표출

    <span th:text="${#dates.format(#dates.createNow(), 'yyyy-MM-dd HH:mm:ss')}"></span>

     

     

     

     

    20) secutiry 인증 정보 여부

     ( 타임리프에서 로그인, 로그아웃에 대한 이벤트를 줄 수 있다. )

    <th:block sec:authorize="isAuthenticated()"> 로그인 정보 있음 </th:block>
    <th:block sec:authorize="isAuthenticated() == false"> 로그인 정보 없음 </th:block>

     

     

     

    21)  security에서 인증받은 객체가 존재하는지 확인

    <th:block th:if="${#authorization.getAuthentication() != null and #authorization.expression('isAuthenticated()')}">
    </th:block>

     

     

     

     

    22) property, yml 에서 값 가져와서 사용

    <a th:href="${@environment.getProperty('app.url.logout')}"></a>

     

     

     

    23)

    <form th:action="@{/customers/create}" th:object="${customerForm}" method="post">
    
    <input type="text" id="lastName" name="lastName" th:field="*{lastName}" th:errorclass="error-input" value="민" />
    
    <span th:if="${#fields.hasErrors('lastName')}" th:errors="*{lastName}" class="error-messages">error!</span>
    
    
    

     

    <form>태그에 포함된 action속성 값을 th:action속성 값으로 치환할 수 있으며,  th:object속성 값에 Model속성 이름을 지정하여 이 태그 안에서 *{필드이름} 형식으로 사용할 수 있다. 

     

    th:field="*{필드 이름}"을 설정하면 이 HTML필드와 폼 객체(여기서는 CustomerForm)에 포함된 필드를 연결할 수 있고 HTML필드 값이 폼 객체의 해당 필드로 설정된다. 반대로 폼 객체의 필드 값이 Model에서 HTML필드 값을 설정된다. 
    입력 검사에서 오류가 발견됐을 경우에는 th:errorclass속성 값이 class속성에 설정된다. 

     

    필드에 오류가 있을 경우에만 특정 태그를 표시하고 싶다면 해당 태그에 th:if="${#fields.hasErrors('필드이름')"을 설정한다. th:errors="*{필드이름}"을 설정하면 태그 안에 있는 문자열을 대상 필드에 관련된 에러 메시지로 치환할 수 있다. 



     

     

     

     

     

    참조)

    https://bamdule.tistory.com/216

    https://s-yeonjuu.tistory.com/6

     https://effectivecode.tistory.com/1056

    https://jongminlee0.github.io/2020/03/12/thymeleaf/

    https://velog.io/@leyuri/Thymeleaf-thfield-therrorclass-%EC%86%8D%EC%84%B1

     

     

     

    about author

    PHRASE

    Level 60  라이트

    자기가 살아가는 목적은 자신의 이름을 우리시대의 사건과 연결짓는 것이다. 이 세상에 함께 살고 있는 삶에게 있어서 자신의 이름과 어떤 유익한 일과를 연결짓는 일이다. -링컨

    댓글 ( 4)

    댓글 남기기

    작성