RE-Heat 개발자 일지

스프링 MVC 2편 - [1] 타임리프 - 기본기능(하편) 본문

백엔드/스프링

스프링 MVC 2편 - [1] 타임리프 - 기본기능(하편)

RE-Heat 2023. 7. 9. 22:33

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-2/dashboard

 

스프링 MVC 2편 - 백엔드 웹 개발 활용 기술 - 인프런 | 강의

웹 애플리케이션 개발에 필요한 모든 웹 기술을 기초부터 이해하고, 완성할 수 있습니다. MVC 2편에서는 MVC 1편의 핵심 원리와 구조 위에 실무 웹 개발에 필요한 모든 활용 기술들을 학습할 수 있

www.inflearn.com

인프런 김영한님의 스프링 MVC 2편 강의를 토대로 정리한 내용입니다.

 

[10] 속성 값 설정

th:* 속성을 지정하면 타임리프는 기존 속성을 th:*로 지정한 속성으로 대체한다. 기존 속성이 없으면 새로 만든다.

예시) <input type="text" name="mock" th:name="userA" /> 
→ 타임리프 렌더링 후 <input type="text" name="userA" />

 

실행결과·코드·attribute.html

 

■ 속성추가 

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

    예시) th:attrappend="class=' large'" → class="text large"

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

    예시) th:attrprepend="class='large '" → class="large text"

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

    예시) th:classappend="large" → class="text large"

    => classappend는 띄어쓰기를 신경 쓸 필요 없어 편리함.

 

■ checked 처리

HTML에선 check속성이 있으면 checked가 true인지 false인지 관계없이 checked처리가 되어버린다.

그래서 타임리프는 개발자의 편의성을 위해 th:checked값이 false이면 checked 속성 자체를 제거한다.

 

<input type="checkbox" name="active" th:checked="false" />
→ 타임리프 렌더링 후:  <input type="checkbox" name="active" />

 

[11] 반복

■ 반복기능

<tr th:each="user : ${users}">

타임리프에서 반복은 th:each를 사용하며 List 외에도 배열, java.util.Iterable , java.util.Enumeration, Map 등에서도 사용할 수 있다.

 

■ 반복상태 유지 기능

<tr th:each="user, userStat : ${users}">

반복의 두 번째 파라미터를 설정해 반복의 상태를 확인할 수 있다.

두 번째 파라미터(userStat)을 생략하면 생략한 지정한 변수명(user) + Stat이 된다.

반복상태유지기능 

  • index : 0부터 시작하는값 
  • count : 1부터 시작하는값 
  • size : 전체사이즈
  • even , odd : 홀수, 짝수여부( boolean ) 
  • first , last :처음, 마지막여부( boolean ) 
  • current : 현재객체

[12] 조건부 평가

타임리프의 조건식은 Java와 굉장히 흡사하다. 

① if문

② unless문(if문 반대)

③ switch~case문

 

BasicController : condition + addUsers()

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

private void addUsers(Model model){
    List<User> list = new ArrayList<>();
    list.add(new User("UserA", 10));
    list.add(new User("UserB", 20));
    list.add(new User("UserC", 30));

    model.addAttribute("users", list);
}

userA의 age는 10, UserB의 age는 20, UserC의 age는 30으로 세팅

 

■ if·unless

타임리프는 해당 조건이 맞지 않으면 태그 자체를 렌더링 되지 않는다.

브라우저에서 소스를 열어보면 미성년자 조건을 충족하지 않은 20, 30에선 <span>태그 자체가 사라진 것을 확인할 수 있다.

 

■ switch 

[13] 주석

■ 타임리프 주석

1. 표준 HTML 주석 <!-- -->

    타임리프가 렌더링하지 않고 그대로 남겨둔다.

2. 타임리프 파서 주석 <!--/*  */-->

    타임리프의 진짜 주석으로, 렌더링하면 주석 부분을 제거한다.

3. 타임리프 프로토타입 주석 <!--/*/ /*/-->

    HTML 파일을 열어보면 주석처리가 되고, 타임리프를 렌더링하면 보이는 기능

 

렌더링 후 브라우저 소스보기 - comments.html

 

브라우저에서 직접 열기

 

타임리프 파서 주석은 렌더링되면 사라지나, 브라우저에서 직접 열면 보인다.

반면 타임리프 프로토타입 주석은 렌더링되면 보이나 브라우저에서 직접 열면 보이지 않는다는 것을 알 수 있다.

 

[14] 블록

<th:block>은 HTML 태그가 아닌 타임리프의 유일한 자체 태그다.

 

block.html

<th:block th:each="user : ${users}">
    <div>
        사용자 이름1 <span th:text="${user.username}"></span>
        사용자 나이1 <span th:text="${user.age}"></span>
    </div>
    <div>
        요약 <span th:text="${user.username} + ' / ' + ${user.age}"></span>
    </div>
</th:block>

반복문을 쓸 때 <div> 한 개는 th:each만 써도 되나, 두 개를 출력하려면 th:block을 써주어야 한다.

 

실행화면

① th:block은 렌더링시 제거되는 특징이 있다. 

② html 태그 안에 넣을 곳이 애매할 때 사용된다.

 

[15] 자바스크립트 인라인

타임리프는 자바스크립트에서 타임리프를 편리하게 사용할 수 있는 자바스크립트 인라인 기능을 제공한다.

자바스크립트 인라인 기능 기본형 :  <script th:inline="javascript">

 

javascript.html & 브라우저 소스 보기

■ 텍스트 렌더링

① var username

   th:inline="javascript" 적용 전

       var username = [[${user.username}]] -> var username = userA;가 돼 "[[${user.username}]]" 큰따옴표를 붙여줘야 함

   th:inline="javascript"적용 후

      var username = "userA"; 쌍따옴표를 붙이지 않아도 타입에 맞게 알아서 출력해 준다.

② var age   

    숫자는 큰따옴표를 붙이지 않아도 돼 둘 다 정상적으로 동작한다.

 

추가] 자바스크립트에서 문제가 될 문자(\)가 포함돼 있으면 이스케이스 처리를 해준다.

BasicController : javascript

@GetMapping("/javascript")
public String javascript(Model model){
    model.addAttribute("user", new User("user\"A\"", 10));
    addUsers(model);

    return "basic/javascript";
}

inline을 사용하면 백슬래쉬(\)가 Escape된 것을 확인할 수 있다. 여기선 백슬래시 대신 원화기호가 출력.

 

■ 자바스크립트 내추럴 템플릿

var username2 = /*[[${user.username}]]*/ "test username"; 

  • 인라인사용 전 → var username2 = /*userA*/ "test username"; 
  • 인라인사용 후 → var username2 = "userA";

인라인 사용 전엔 순수하게 해석해 내추럴 템플릿 기능이 동작하지 않는다. 렌더링 내용은 주석처리까지 된다.

반면 인라인 사용 후엔 결과를 보면 /**/주석 부분이 제거되고 "userA"가 정확하게 적용된다.

 

■ 객체

타임리프 자바스크립트 인라인 기능을 쓰면 객체를 JSON으로 자동 반환한다.

var user = [[${user}]];

  • 인라인 사용 전 var user = BasicController.User(username=userA, age=10); 
  • 인라인 사용 후 var user = {"username":"userA","age":10};

인라인 사용 전엔 toString() 값이 호출되나, 사용 후엔 객체를 JSON으로 변환해 줌.

 

■ 자바스크립트 인라인 each

자바스크립 인라인은 th:each를 제공한다. 단, [#...]로 시작되고 [/]로 끝나야 한다.  

 

자세한 내용은 아래 링크의 13 Textual template modes 부분을 확인하면 된다.

https://www.thymeleaf.org/doc/tutorials/3.1/usingthymeleaf.html#javascript-inlining

 

[16] 템플릿 조각

웹 페이지를 개발할 땐 상단영역·하단 영역·좌측 카테고리 등 여러 페이지에서 함께 사용하는 영역이 있다. 이런 부분을 일일이 코드를 복사해 사용하면 비효율적이므로 타임리프는 1. 템플릿 조각과 2. 레이아웃 기능을 지원해 보다 효율적으로 관리할 수 있도록 해준다.

 

fragmentMain.html & footer.html

 

■ 부분 포함 insert

<div th:insert="~{template/fragment/footer :: copy}"></div>

를 쓰면 footer.html의 th:fragment="copy"라는 부분을 템플릿 조각으로 가져와 사용한다.

insert는 기존 태그인 <div>를 남겨놓은 상태에서 <footer>를 내부에 추가한다는 게 특징이다.

 

■ 부분 포함 replace

<div th:replace="~{template/fragment/footer :: copy}"></div>

를 쓰면 마찬가지로 footer.html의 th:fragment="copy"라는 부분을 템플릿 조각으로 가져와 사용한다.

단, replace는 insert와는 달리 기존 태그 <div>를 대체한다.

 

■ 부분 포함 단순 표현식

<div th:replace="template/fragment/footer :: copy"></div>

~{template/fragment/footer :: copy}처럼 ~{...}를 사용하는 게 원칙이나 템플릿 조각을 쓰는 코드가 단순하면 생략할 수 있다.

 

■ 파라미터 사용

<div th:replace="~{template/fragment/footer :: copyParam ('데이터1', '데이터2')}"></div>

footer.html의 ${param1} , ${param2} 부분을 '데이터1' '데이터2'식으로 입력해 출력할 수 있다. 

 

[17] 템플릿 레이아웃 1

웹페이지에서 공통으로 사용할 레이아웃 페이지를 구성하고 가져올 수 있다. 추가로 타임리프엔 각 페이지마다 필요한 정보를 더 추가해 사용하는 방법도 있다.

 

layoutMain.html & base.html & 실행화면

① logoutMain.html

common_header(~{::title},~{::link}) 매개변수로 모든 title태그(<title>메인타이틀</title>)와

모든 link태그<link ... bootstrap> <link jquery-ui.css>를 전달

 

② base.html

<title th:replace="${title}">은 logoutMain.htlm에서 넘어온 <title>메인 타이틀<title>로 대체됨.<th:block th:replace=${links}/> th:block을 사용하고 ${links}가 있으면 <link>태그 개수만큼 추가됨.실행화면을 보면 <link>태그가 두 개가 넘어온 것을 확인할 수 있다.

레이아웃 개념(base.html)을 두고 필요한 코드 조각(title, link)을 전달해 완성하는 것으로 이해하면 된다.

 

[18] 템플릿 레이아웃 2

앞선 개념을 <head>에 국한되지 않고 <html> 전체에 적용할 수 있다.

 

layoutExtendMain.html & layoutFile.html & 실행화면

① layoutExtendMain.html

<html th:replace="~{template/layoutExtend/layoutFile :: layout(~{::title}, ~{::section})}" xmlns:th="http://www.thymeleaf.org">

title과 section을 layoutFile.html로 넘김

 

② layoutFile.html

head에 th:fragment가 붙어있던 base.html과는 달리 html태그에 th:fragement 속성이 정의돼 있는 것을 확인할 수 있다.

타이틀은 layoutExtendMain에서 가져온 <title>메인 페이지 타이틀</title>로 대체되며

레이아웃 컨텐츠 부분도 역시 <section></section>으로 교체된 것을 확인할 수 있다.