JAVA Spring #3

keymu·2024년 11월 12일
0

Template Engine

  • Rendering: Template(HTML) + (Model 속) Data
  • Spring 기본 Template Engine => Thymeleaf
  • Data가 바뀌어도 양식이 하나만 있으면 되기 때문에 재사용성이 높고, 유지 보수가 용이하다.
  • 그러나 서버측 연산부하가 발생한다 (=SSR의 단점)
  • 요즘은 대형 트래픽을 다루면 SSR을 잘 사용하지 않음

SSR : Server-side Rendering
CSR : Client-side Rendering

전체적 개요

  1. 사전에 Member 생성
  2. 데이터 준비(+데이터 초기화)
  3. Standard Expression Syntax 작성
  4. Server 가동 시, 페이지 소스/개발자 도구 보기 생활화

Standard Expression Syntax

  • Literals
  • Variable Expressions: ${...} 변수 표현식

Variable Expressions

  • .(dot)을 사용해 접근
  • Object 변수의 .property => 해당 object의 property 접근(getter 값 호출): 없는 property=ERROR
  • 배열, List<> 변수의 [index] => index번째의 item값
  • Map<k,v>변수의 .key => Map의 value 값: 없는 key=NULL

Conditional expressions

  • (if) ? (then)
  • (if) ? (then) : (else)
  • (value) ?: (defaultvalue)

Iteration / Conditional Evaluation / 변수의 선언

Expression Utility Objects(표현식 유틸리티 객체)

  • #temporals: java.time.* 객체를 다루기 위한 표현식 유틸리티 객체
  • #strings: 문자열 다루기 위한 표현식 유틸리티 객체
  • #list: list를 다루기 위한 표현식 유틸리티 객체

Tag Attribute 방식 Rendering

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<!-- thymeleaf의 attribute, tag 사용하기 위한 namespace 지정 -->
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <div>Thymeleaf Test</div>
    <div th:text="hello">Thymeleaf Test</div> <!-- attribute 값 출력 "hello" -->
    <div th:text="${greeting}">Thymeleaf Test</div> <!-- model 값 출력 -->
    <div th:text="${date}">Thymeleaf Test</div>
    <div th:text="${name}">Thymeleaf Test</div>  <!-- 없는 값 rendering x -->
<!--    <div th:text="">Thymeleaf Test</div> : error-->

    <!-- 표준 html/xml 주석은 thymeleaf 에서도 사용가능, response 됨 -->
    <!--  [[${greeting}]] -->
    <!--  ↑ 일반 html 주석내용은 thymeleaf 는 'process'한다. 'process'원치 않으면 thymeleaf 주석으로 감싸야 한다-->
    <!--/* thymeleaf 주석 (parser-level 주석): 템플릿 엔진에서 처리(파싱)될때 제거된다.  response 안됨  [[${greeting}]] */-->

    <hr>
    <div th:text="${'<h3>' + greeting + '<h3>'}">Thymeleaf Test</div>
    <div th:utext="${'<h3>' + greeting + '<h3>'}">Thymeleaf Test</div> <!-- unescaped text -->
</body>
</html>

아래 코드는 직접 돌려보자.

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div th:text="m"></div>
<div th:text="${m}"></div> <!-- ${ } Variable Expression  객체, 변수 표현.  객체의 경우 toString()값이 표현 -->
<div th:text="${'m'}"></div>
<div th:text="${'aaa'}"></div>
<div th:text="${aaa}"></div> <!-- Model 에 없는 attribute.  에러는 아니다 -->
<div th:text="${'aaa'+'bbb'}"></div>
<div th:text="${aaa+'bbb'}"></div>
<!-- <div th:text="${aaa+bbb}"></div>--> <!-- 에러, 없는 객체/변수 끼리 연산 불가. null 끼리 +연산 불가 ! -->
<div th:text="${'aaa'+10+'bbb'}"></div>
<hr>
<div th:text="${10+10}"></div>
<div th:text="${10 - 10}"></div>
<div th:text="${10*10}"></div>
<div th:text="${10/10}"></div>
<div th:text="${10.0/20}"></div>
<div th:text="${10%4}"></div>
<hr>
<div th:text="10"></div>
<div th:text="10+20"></div>
<div th:text="10-10"></div>
<div th:text="m+20"></div>
<!-- <div th:text="m 20"></div>-->
<div th:text="'m 20'"></div>
<hr>
<div th:text="${m.name}"></div> <!--m.getName()과 같다. field 아니고 property(getter, setter)값 가져오는 것-->
<div th:text="${m.regdate}"></div>
<div th:text="${m.no}"></div>
<div th:text="${m.id}"></div>
<!-- <div th:text="${m.age}"></div>--> <!-- 없는 property 접근=> error -->
<div th:text="${m.nick}"></div> <!-- getnick(), getNick() -->
<div th:text="${m.Nick}"></div>
<!-- <div th:text="${m.nIck}"></div>--> <!-- error -->
<hr>

<!--    논리연산자 사용 가능-->
<div th:text="${true && false}"></div>
<div th:text="${!false}"></div>
<div th:text="${true || false}"></div>
<div th:text="${true and false}"></div>
<div th:text="${not false}"></div>
<div th:text="${true or false}"></div>

<!-- Conditional Expression -->
 <div th:text="(10>4)?'big':'small'"></div>
 <div th:text="(10>4)?'big'"></div>
 <div th:text="(10<4)?'big'"></div>
 <div th:text="(null)?:'default값'"></div>

<!-- escaping 하기  What's up? 출력 -->
<h3>escaping 하기</h3>
 <div th:text="${'What''s up?'}"></div> <!-- ${}안에서는 '' 두개 -->
 <div th:text="'What\'s up?'"></div> <!-- literal인 경우: \ -->

<hr>
<h2>배열, List, Map</h2>
 <div th:text="${arr1}"></div>
 <div th:text="${arr2}"></div>
 <div th:text="${list1}"></div>
 <div th:text="${list2}"></div>
 <div th:text="${map1}"></div>
 <div th:text="${map2}"></div>
<br>
 <div th:text="${arr1[0]}"></div>
 <div th:text="${arr1[1]}"></div>
 <div th:text="${arr1[4]}"></div>
<!-- <div th:text="${arr1[10]}"></div>--> <!-- index 벗어나면 error -->
<div th:text="${arr2[0]}"></div>
<div th:text="${arr2[1]}"></div>
<div th:text="${arr2[1].name}"></div>
<div th:text="${arr2[1].regdate}"></div>

 <div th:text="${list1[0]}"></div> <!-- list에도 배열처럼 [] 사용 가능 -->
 <div th:text="${list1[1]}"></div>
 <div th:text="${list1[4]}"></div>

 <div th:text="${list2[3]}"></div>
 <div th:text="${list2[3].name}"></div>
 <div th:text="${list2[3].regdate}"></div>

<div th:text="${map1}"></div>
 <div th:text="${map1[0]}"></div> <!-- key:0의 값 -->
 <div th:text="${map1[5]}"></div> <!-- null return -->

<div th:text="${map2}"></div>
<div th:text="${map2.AAA}"></div>
<div th:text="${map2[AAA]}"></div>

<!--<div th:text="${map2.XXX}"></div> --> <!-- error -->
<div th:text="${map2[XXX]}"></div>

<div th:text="${map2.BBB.name}"></div>
<!-- <div th:text=""></div>-->
<!-- <div th:text=""></div>-->
<!-- <div th:text=""></div>-->
<!-- <div th:text=""></div>-->
<!-- <div th:text=""></div>-->
<!-- <div th:text=""></div>-->
</body>
</html>

Inline 방식 Rendering

1. Expression inlining: [[...]], [(...)]
2. Text inlining: th:inline="text"
3. JavaScript inlining: <script th:inline="javascript">
4. CSS inlining: <style th:inline="css">

th:each - 반복문 처리
Thymeleaf의 th:each 태그를 사용하면 List나 배열과 같은 반복 가능한 데이터 구조를 순회하며 처리할 수 있다. 이를 통해 동적인 테이블 생성, 리스트 출력 등의 기능을 구현할 수 있다.
th:if, th:unless - 조건부 렌더링
th:if와 th:unless 태그를 사용하면 주어진 조건이 참인지 거짓인지에 따라 해당 요소를 렌더링할지 여부를 결정할 수 있다. 이를 통해 복잡한 UI 로직을 간단하게 구현할 수 있다.
th:with - 지역 변수 선언
th:with 태그를 사용하면 특정 영역 내에서만 유효한 지역 변수를 선언하고 초기화할 수 있다. 이를 통해 템플릿 내에서 재사용 가능한 변수를 만들어 사용할 수 있다.
Expression Utility Objects - 유틸리티 함수 활용
Thymeleaf는 다양한 유틸리티 객체들을 제공한다. #temporals, #strings, #lists, #maps 등의 객체를 활용하면 날짜/시간 처리, 문자열 조작, 리스트/맵 처리 등의 작업을 간단하게 수행할 수 있다.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>Expression Utility Objects</h1>
<h3>#temporals</h3>
<!-- #temporals 는 LocalDateTime, LodalDate, LocalTime -->

<div th:text="${now1}"></div>
<div th:text="${#temporals.format(now1)}"></div> <!-- Locale 문자열 포맷(기본) -->
<div th:text="${#temporals.format(now1, 'yyyy-MM-dd HH:mm:ss')}"></div>

<div th:text="${#temporals.createNow()}"></div>

<h3>#numbers</h3>
<div th:text="${price}"></div>
<div th:text="${#numbers.formatInteger(price, 3, 'COMMA')}"></div>
<div th:with="priceValue=2099.878654">
    <div th:text="${priceValue}"></div>
    <div th:text="${#numbers.formatInteger(priceValue,3,'COMMA')}"></div> <!-- 정수 문자열로 formatting -->
    <div th:text="${#numbers.formatDecimal(priceValue, 5,10,'POINT')}"></div>
    <div th:text="${#numbers.formatDecimal(priceValue, 5, 'COMMA', 10,'POINT')}"></div>
    <div th:text="${#numbers.formatDecimal(priceValue, 3, 'COMMA', 2,'POINT')}"></div>
</div>

<h3>#strings</h3>
    <div th:text="${title}"></div> <!-- This is sample" -->
    <div th:utext="${#strings.replace(title,'s','<u>s</u>')}"></div>
    <div th:text="${#strings.listSplit(title, ' ')}"></div> <!-- 결과: List<String> -->
<ul>
    <li th:each="str:${#strings.listSplit(title, ' ')}" th:text="${str}"></li>
</ul>

<h3>#lists</h3>
    <div th:text="${options}"></div>
    <div th:text="${#lists.size(options)}"></div>
    <div th:text="${#lists.isEmpty(options)}"></div> <!-- 조건부 렌더링 등에서 if unless와 더불어 사용 가능 -->
    <div th:text="${#lists.contains(options, 'BBB')}"></div>
    <div th:text="${#lists.sort(options)}"></div>
    <div th:text="${options}"></div> <!-- 원본 변화 X -->

</body>
</html>

1. 템플릿 레이아웃

Thymeleaf는 템플릿 레이아웃 기능을 제공한다. th:insertth:replace 태그를 사용하여 다른 템플릿 파편(fragment)을 삽입하거나 치환할 수 있다.

<div th:insert="~{thymeleaf/fragments/header::head1}"></div>

은 thymeleaf/fragments/header.html 파일의 head1 fragment를 현재 위치에 삽입한다.

<li th:replace="~{thymeleaf/aaa/bbb/side::left}"></li>

는 thymeleaf/aaa/bbb/side.html 파일의 left fragment로 현재 li 요소를 치환한다. 이를 통해 공통 헤더, 사이드바 등의 요소를 재사용할 수 있다.

2. URL 표현식

Thymeleaf는 다양한 URL 표현식을 제공한다.

@{http://...}: 절대 URL 생성
@{/...}: 컨텍스트 상대 URL 생성
@{~/...}: 서버 상대 URL 생성
<a th:href="@{/thymeleaf/sample1}">sample1</a>

는 /thymeleaf/sample1 URL을 생성한다.

URL 파라미터도 추가할 수 있습니다.

<a th:href="@{/thymeleaf/sample1(p1='aaa', p2='bbb')}">sample1?p1=aaa&p2=bbb</a>

3. 속성 설정

th:attr 속성을 사용하면 HTML 요소의 속성을 동적으로 설정할 수 있다.

<input type="text" th:value="${value1}" th:attr="value=${value1}, title=${value1}">

는 value와 title 속성을 value1 변수 값으로 설정한다.

<button th:onclick="'location.href=\'' + @{/thymeleaf/sample1} + '\''">sample1</button>

는 onclick 속성을 동적으로 생성한다.

4. th:block

th:block 태그를 사용하면 변수 선언과 같은 논리적 블록을 만들 수 있다. 이 블록 내에서 선언한 변수는 하위 요소에서 사용할 수 있고, 블록의 속성만 필요할 때 사용한다.

<th:block th:with="name='John'">...</th:block>

은 name 변수를 블록 내에서 사용할 수 있게 한다.

++

@RequestMapping("/delete")   // "/user/delete"
    public void delMember(HttpServletRequest request, Model model) {
        String id = request.getParameter("id"); // "id" 란 name 의 parameter 값 리턴 (String)
        // 없으면 null 리턴
        System.out.println("id = " + id);
        int age = Integer.parseInt(request.getParameter("age"));
        System.out.println("age = " + age);
        model.addAttribute("mbId", id);
        model.addAttribute("mbAge", age);
        // 동일한 name 의 parameter가 여러개 인 경우
        // getParameterValues(name) => String[] 리턴
        String[] ages = request.getParameterValues("age");
        System.out.println("ages = " + Arrays.toString(ages));
        model.addAttribute("ages", Arrays.toString(ages));
    }
    //-------------------------------------------------------------
    // parameter name 값과 동일한 이름의 매개변수를 핸들러에 지정해서 받아오기
    @RequestMapping("/find1")
    @ResponseBody
//    public String find1(String id, String name){
    public String find1(String name, String id) {  // 매개변수 순서 무관.
        return "user/find1 : id = " + id + ", name = " + name;
    }

    // 숫자 타입은 바로 parsing 하여 받을수 있다.
    @RequestMapping("/find2")
    @ResponseBody
//    public String find2(double id, String name) { // primitive 타입인데, parameter 에 없는 경우, 혹은 parsing 불가능하면 에러 발생
    public String find2(Double id, String name) { // wrapper 타입이면, parameter 없으면 null 값으로 받아옴. parsing 불가하면 에러
        return "user/find2 : id = " + id + ", name = " + name;
    }

    // parameter 를 특정 타입의 자바객체로 변환하여 받아내는 것을 'binding' 한다고 한다
    // parameter "name"-"value"  -->  Java 변수, 객체

    // 동일한 name 의 parameter(들) --> '배열' 혹은 String 매개변수로 받을수 있다.
    @RequestMapping("/find3")
    @ResponseBody
    public String find3(int[] id, String name) {
        return "user/find3 : id=" + Arrays.toString(id) + ", name=" + name;
        // /user/find3?id=10&id=20&id=30&name=aaa&name=bbb
        //   => String 으로 받는 경우 "aaa,bbb" 로 바인딩 된다

    }

    // 만약 request parameter 의 name 과 매개변수가 같을수 없는 상황이면
    // @RequestParam 애노테이션 사용
    @RequestMapping("/find4")
    @ResponseBody
    public String find4(
            @RequestParam(value = "id", required = false, defaultValue = "절미") String userid, // "id" 란 name 의 parameter 값을 userid 매개변수에 binding 해준다
            @RequestParam("name") String username) {
        return "user/find4 : id=" + userid + ", name=" + username;
        // /user/find4?id=10&name=aaaa
    }
profile
Junior Backend Developer

0개의 댓글