스프링MVC2

Seung jun Cha·2022년 6월 18일
0

1. Escape, UnEscape

1-1 Escape

  • HTML에서 사용하는 태그와 특수문자를 문자 그대로(HTML 엔티티)로 표현하는 것을 Escape라고 한다. th:text , [[...]](태그없이 문자를 바로 출력)는 기본적으로 이스케이스(escape)를 제공한다.

1-2 UnEscape

  • 실제 서비스를 개발하다 보면 escape를 사용하지 않아서 HTML이 정상 렌더링 되지 않는 수 많은 문제가 발생한다. escape를 기본으로 하고, 꼭 필요한 때만 unescape를 사용하자. th:utext , [(...)]는 UnEscape 제공

2. SpringEL

@GetMapping("/variable")
    public String variable(Model model) {
        User userA = new User("userA", 10);
        User userB = new User("userB", 20);

        List<User> list = new ArrayList<User>();
        list.add(userA);
        list.add(userB);

        Map<String, User> map = new HashMap<String, User>();
        map.put("userA", userA);
        map.put("userB", userB);

        model.addAttribute("user", userA);
        model.addAttribute("users", list);
        model.addAttribute("userMap", map);

        return "basic/variable";
    }
<h1>SpringEL 표현식</h1>
<ul>Object
    <li>${user.username} =    <span th:text="${user.username}"></span></li>
    <li>${user['username']} = <span th:text="${user['username']}"></span></li>
    <li>${user.getUsername()} = <span th:text="${user.getUsername()}"></span></li>
</ul>
<ul>List
    <li>${users[0].username}    = <span th:text="${users[0].username}"></span></li>
    <li>${users[0]['username']} = <span th:text="${users[0]['username']}"></span></li>
    <li>${users[0].getUsername()} = <span th:text="${users[0].getUsername()}"></span></li>
</ul>
<ul>Map
    <li>${userMap['userA'].username} =  <span th:text="${userMap['userA'].username}"></span></li>
    <li>${userMap['userA']['username']} = <span th:text="${userMap['userA']
 ['username']}"></span></li>
    <li>${userMap['userA'].getUsername()} = <span th:text="${userMap['userA'].getUsername()}"></span></li>
</ul>
  • 지역변수 선언
    th:with 를 사용하면 지역 변수를 선언해서 사용할 수 있다. 지역 변수는 선언한 테그 안에서만 사용할 수 있다.
 <h1>지역 변수 - (th:with)</h1>
 <div th:with="first=${users[0]}">
    <p>처음 사람의 이름은 <span th:text="${first.username}"></span></p>
 </div

3. 타임리프 객체

3-1 기본객체

1) ${#request}

2) ${#response}

3) ${#session}

4) ${#servletContext}

5) ${#locale}

3-2 편의객체

1) HTTP 요청 파라미터 접근: param

http://localhost:8080/basic/basic-objects?paramData=HelloParam1
<li>Request Parameter = <span th:text="${param.paramData}"></span></li>
Request Parameter = HelloParam1

2) HTTP 세션 접근: session

 session.setAttribute("sessionData", "hello session");
 <li>session = <span th:text="${session.sessionData}"></span></li>
 session = hello session

3) 스프링 빈 접근: @
예) ${@helloBean.hello('Spring!')}

3-3 유틸리티 객체

1) 유틸리티 객체 레퍼런스 https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#expression-utility-objects

2) 유틸리티 객체 레퍼런스 예시
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#appendix-b-expression-utility-objects

3) #temporals : 자바8 날짜용 유틸리티 객체

@GetMapping("/date")
 public String date(Model model) {
    model.addAttribute("localDateTime", LocalDateTime.now());
 return "basic/date";
 }
<ul>
    <li>default = <span th:text="${localDateTime}"></span></li>
    <li>yyyy-MM-dd HH:mm:ss = <span th:text="${#temporals.format(localDateTime, 
'yyyy-MM-dd HH:mm:ss')}"></span></li>
 </ul>

타임리프에서 자바8 날짜인
LocalDate, LocalDateTime, Instant를 사용하려면 추가 라이브러리가 필요하다. 스프링 부트 타임리프를 사용하면 해당 라이브러리가 자동으로 추가되고 통합된다.

 thymeleaf-extras-java8time

4. URL링크

URL경로에 해당 이름의 {변수명}이 있으면 ()에 있는 변수는 경로변수로 처리, 없으면 ()에 있는 부분은 쿼리 파라미터로 처리

 @GetMapping("/link")
 public String link(Model model) {
    model.addAttribute("param1", "data1");
    model.addAttribute("param2", "data2");
 return "basic/link";
 }
<ul>
    <li><a th:href="@{/hello}">basic url</a></li>
    
    // /hello?param1=data1&param2=data2
    <li><a th:href="@{/hello(param1=${param1}, param2=${param2})}">hello query param</a></li>
    
    // /hello/data1/data2
    <li><a th:href="@{/hello/{param1}/{param2}(param1=${param1}, param2=${param2})}">path variable</a></li>
    
    // hello/data1?param2=data2
    <li><a th:href="@{/hello/{param1}(param1=${param1}, param2=${param2})}">path 
variable + query parameter</a></li>
 </ul>

5. 리터럴

  • 리터럴은 소스 코드상에 고정된 값을 말하는 용어이다.
    문자리터럴은 항상 '(작은 따옴표)로 감싸야 한다. 귀찮으니까 나온게 리터럴 대체문법 <th:text="|hello ${data}|"> | | 내부는 모두 리터럴이 적용된 것으로 보고 변수는 알아서 값을 바꾸어준다.
 <ul>
 <!--주의! 다음 주석을 풀면 예외가 발생함-->
 <!--    <li>"hello world!" = <span th:text="hello world!"></span></li>-->
    <li>'hello' + ' world!' = <span th:text="'hello' + ' world!'"></span></li>
    <li>'hello world!' = <span th:text="'hello world!'"></span></li>
    <li>'hello ' + ${data} = <span th:text="'hello ' + ${data}"></span></li>
    <li>리터럴 대체 |hello ${data}| = <span th:text="|hello ${data}|"></span></li>
 </ul>

6. 연산

6-1 Elvis 연산자 (:?)

조건식의 편의 버전 : th:text="${data} ?: '데이터가없습니다.'"> ${data}에 값이 있으면 출력, 없으면 ㅡ (자바의 삼항연산자와 같은듯)

6-2 No-Operation

_ 인 경우 마치 타임리프가 실행되지 않는 것처럼 동작한다. 이것을 잘 사용하면 HTML의 내용 그대로 활용할 수 있다. 마지막 예를 보면 데이터가 없습니다. 부분이 그대로 출력된다. => th:text="${data} ?: __ " >데이터가 없습니다.<

7. 속성 값 설정, 반복, 조건부 평가, checked

7-1 속성 추가

  • th:* 속성을 지정하면 타임리프는 기존 속성을 타임리프 렌더링 후로 지정한 속성으로 대체한다. 기존 속성이 없다면 새로 만든다.
 <input type="text" name="mock" th:name="userA" />
 
타임리프 렌더링 후 
  <input type="text" name="userA" />

th:attrappend : 속성 값의 뒤에 값을 추가한다.

 th:attrappend = <input type="text" class="text" th:attrappend="class=' large'" /><br/>
 
 렌더링 후
  th:attrappend = <input type="text" class="text large"/>

th:attrprepend : 속성 값의 앞에 값을 추가한다.

 렌더링 후
<input type="text" class="large text"/>

th:classappend : class 속성에 자연스럽게 추가한다.

<input type="text" class="text" th:classappend="large" />
렌더링 할 때, 알아서 자연스럽게 수정.. 종종 사용한다.

<input type="text" class="text large"/>

7-2 반복

th:each="user, userStat : ${users}"
반복의 두번째 파라미터를 설정해서 반복의 상태를 확인 할 수 있다.
두번째 파라미터는 생략 가능한데, 생략하면 지정한 변수명( user ) + Stat 가 된다


@GetMapping("/each")
    public String each(Model model) {
        addUsers(model);
        return "basic/each";
    }

    private void addUsers(Model model){
        List<User> list = new ArrayList<User>();
        User userA = new User("userA", 10);
        User userB = new User("userB", 20);
        User userC = new User("userC", 30);
        list.add(userA);
        list.add(userB);
        list.add(userC);
        model.addAttribute("users", list);
    }
    
		<tr th:each="user : ${users}">
            <td th:text="${user.username}">name</td>
            <td th:text="${user.age}" >age</td>
        </tr>
        
        <tr th:each="user, userStat : ${users}"> 
        (userStat를 선언하지 않아도 사용할 수 있다)
        <td th:text="${userStat.count}">username</td>
        <td th:text="${user.username}">username</td>
        <td  th:text="${user.age}">0</td>
        <td>
            index = <span th:text="${userStat.index}"></span>
            count = <span th:text="${userStat.count}"></span>
            size = <span th:text="${userStat.size}"></span>
            even? = <span th:text="${userStat.even}"></span> 
            odd? = <span th:text="${userStat.odd}"></span>
            first? = <span th:text="${userStat.first}"></span>
            last? = <span th:text="${userStat.last}"></span>
            current = <span th:text="${userStat.current}"></span>
        </td>
        
        even, odd : 홀수, 짝수 여부 (boolean)
        first, last : 처음, 마지막 여부 (boolean)

7-3 조건부 평가 : if, unless

th:text="'미성년자'" th:if="${user.age lt 20}"

th:text="'미성년자'" th:unless="${user.age ge 20}"

조건이 false 인 경우 해당 부분 자체가 렌더링 되지 않고 통째로 사라진다.

7-4 checked

  • HTML에서는 checked 속성이 있으면 속성의 값과는 상관없이 무조건 체크표시가 된다. 타임리프의 th:checked는 값이 false인 경우 checked 속성 자체를 제거한다.
 <input type="checkbox" name="active" th:checked="false" />
렌더링 후

 <input type="checkbox" name="active" />
이러면 HTML에 checked 속성이 없기 때문에 체크되지 않는다.

8. 주석

8-1 표준 HTML 주석

<!-- <span th:text="${data}">html data</span> -->

타임리프가 랜더링을 하지 않아서 페이지 소스를 볼 때, 주석 코드가 보임

8-2 타임리프 파서 주석

<!--/*-->
<span th:text="${data}">html data</span>
<!--*/-->

타임리프가 랜더링에서 주석부분을 제거함. 페이지 소스에서도 안 보임

9. 자바스크립트 Inline

<script th:inline="javascript"> </script>
  • 텍스트 랜더링 : var username = [[${user.username}]];
    인라인 사용 후 렌더링 결과를 보면 문자 타입인 경우 " "를 포함해준다.
var username = "userA";
  • 객체 : var user = [[${user}]];
    객체를 JSON으로 자동으로 변환해준다.
var user = {"username":"userA","age":10};
  • 자바스크립트 인라인은 each를 지원하는데, 다음과 같이 사용한다.
    [# th:each= ....]
[# th:each="user, stat : ${users}"]
 var user[[${stat.count}]] = [[${user}]];

10. 템플릿

10-1 템플릿 조각

(1)/resources/templates/template/fragment/footer.html
<footer th:fragment="copy">
 푸터 자리 입니다.
</footer>

(경로 :: fragment의 이름)으로 해당 이름의 fragment내용을 가지고 옴

<h2>부분 포함 insert</h2> -> <div> 안에 fragment내용을 삽입
<div th:insert="~{template/fragment/footer :: copy}"></div>

<h2>부분 포함 replace</h2> -> <div>전체를 fragment내용으로 교체
<div th:replace="~{template/fragment/footer :: copy}"></div>
(2)/resources/templates/template/fragment/footer.html
<footer th:fragment="copyParam (param1, param2)">
 <p>파라미터 자리 입니다.</p>
 <p th:text="${param1}"></p>
 <p th:text="${param2}"></p>
</footer>
=>변수를 frament에서 선언

/resources/templates/template/fragment/fragmentMain.html
<div th:replace="~{template/fragment/footer :: copyParam ('데이터1', '데이터2')}"></div>
=>변수에 들어갈 값은 메인에서 선언

copyParam(데이터1, 데이터2) -> copyParam (param1, param2) ->${param1}, ${param2}로 들어가서 출력

10-2 템플릿 레이아웃

/resources/templates/template/layout/base.html
<head th:fragment="common_header(title,links)">
 <title th:replace="${title}">레이아웃 타이틀</title>
 
 <th:block th:replace="${links}" />
 

/resources/templates/template/layout/layoutMain.html
<head th:replace="template/layout/base :: common_header(~{::title},~{::link})">
 <title>메인 타이틀</title>
 <link rel="stylesheet" th:href="@{/css/bootstrap.min.css}">
 <link rel="stylesheet" th:href="@{/themes/smoothness/jquery-ui.css}">
</head>

(~{::title},~{::link}) : 태그의 전체 내용으로 대체됨
(~{::title},~{::link}) -> common_header(title,links) ->${title}, ${links}
템플릿 조각이랑 똑같은 방식임
common_header(title,links) 여기서 title, links는 변수명이라서 태그명과 일치하지 않아도 됨

<html전체를 대상으로 하고 일부만 대체할 수도 있음

<html th:fragment="layout (title, content)" xmlns:th="http://
www.thymeleaf.org">
<head>
 <title th:replace="${title}">레이아웃 타이틀</title>
</head>
<body>
<h1>레이아웃 H1</h1>
<div th:replace="${content}">
 <p>레이아웃 컨텐츠</p>
</div>
<footer>
 레이아웃 푸터
</footer>
</body>
</html>

<html th:replace="~{template/layoutExtend/layoutFile :: layout(~{::title}, ~{::section})}"

0개의 댓글