멋쟁이v의 개발일지

[Spring] thymeleaf (환경설정, 컨트롤러, 객체 바인딩, 반복문, 조건문, 지역변수) 본문

0년차/Spring

[Spring] thymeleaf (환경설정, 컨트롤러, 객체 바인딩, 반복문, 조건문, 지역변수)

멋쟁이v 2023. 6. 26. 20:35
728x90
320x100


thymeleaf

💡
서버 사이드 렌더링 (JSP와 유사)

natural template으로 html처럼 작성한다.

  • 준비
    👉🏻
    1) 별도의 라이브러리 서버의 내부에서 처리되는 템플릿
    • 별도의 라이브러리가 필요하다.

    2) 이클립스에서는 편집 기능을 기본으로 제공하지 않는다.

    • 추가적인 플러그인 설치

    3) 자동 재시작 기능

    • 자동 재시작 기능 코드를 수정하면 자동으로 스프링 부트를 재시작해서 수정된 코드를 반영

  • 프로젝트 생성 및 설정
    👉🏻
    Templates 항목에서 Thymeleaf 추가한다.

    DevTools →> 이용한 즉시 리로딩

    ➡️ static(정적) 폴더

    • js, css, html, 이미지 파일들을 추가하는 경로

    ➡️ templates 폴더

    • 내부에는 Thymeleaf를 이용한 템플릿

    👉🏻
    application.properties 설정

    ➡️ 확장자는 ‘html’을 기본으로 설정

    ➡️ 인코딩은 ‘utf-8’ 방식

    ➡️ Mime 타입은 ‘text/html’

    ➡️ 서버 내부의 cache는 ‘true’

    ➡️ spring.thymeleaf.cache = false (true : 개발할 때 수정 작업 후 새로고침 시 반영 X)

  • 컨트롤러 예시
    👉🏻
    @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

728x90
320x100
Comments