인프런/스프링 MVC 2편

3) 타임리프 (블록 , 자바스크립트 인라인 , 템플릿 조각, 템플릿 레이아웃)

backend dev 2023. 2. 1.

블록

타임리프의 유일한 자체 태그

 

블록 테스트

컨트롤러

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

private void addUsers(Model model) {
    List<Object> 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);
}

모델에 리스트를 넣어 뷰를 렌더링한다.

 

뷰 템플릿

<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>

위의 코드를 보면 th:each를 통해 반복을 하는데 <div> 태그를 2개씩 반복하고있다.

 

원래 th:*는 속성으므로 태그안에서 동작해야하니까

<div th:each="user : ${users}">
  div안의 내용
</div>

이런식으로 div태그안에 th:each를 이용해 반복문을 돌릴텐데

 

한번에 2개의 div태그안에 반복문을 돌리고 싶을때는 th:block을 이용한다.

<th:block> 태그안에 적는 속성은 <th:block>태그안에 태그들의 속성들로 들어간다고 생각하면 된다. 

한번에 여러태그에 대해 속성을 주고싶다면 <th:block>을 사용한다.

[아래쯤에 템플릿 레이아웃에서도 사용하는 예시가 있다]

 

결과 페이지

이름을 출력하는 div 태그와 요약을 출력하는 div 둘다 잘 반복되는것을 확인할 수 있다.

 

결과 페이지 소스보기

렌더링 되니까 block이라는 태그는 사라진것을 확인할 수 있다.


자바스크립트 인라인

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

 

자바스크립트 인라인 기능은 다음과 같이 적용하면 된다.

<script th:inline="javascript">

 

 

 

자바 스크립트 인라인을 쓰지않았을때

<!-- 자바스크립트 인라인 사용 전 -->
<script>
  var username = [[${user.username}]];
  var age = [[${user.age}]];
  //자바스크립트 내추럴 템플릿
  var username2 = /*[[${user.username}]]*/ "test username";
  //객체
  var user = [[${user}]];
</script>

[[...]]는  컨텐츠 안에서 작성하고 싶다면 사용해야한다.

 

자바 스크립트 인라인을 쓰지않았을때 결과

페이지 소스보기

var username를 보면 userA라고 나와있다.  "userA"라고 나와야 자바스크립트상 오류가 발생하지않는다.

그냥 userA라고 적혀있으면 변수로 취급하기 때문이다.

 

  //자바스크립트 내추럴 템플릿
  var username2 = /*[[${user.username}]]*/ "test username";

자바스크립트 인라인을 사용하지않았을때 이 코드는 어떻게 됬을까

 

//객체
var user = [[${user}]];

객체의 경우 자바스크립트 인라인 사용전 어떻게될까

객체의 toString()이 실행되어 toString의 결과가 나온것을 볼 수 있다.

 

 

 

자바스크립트 인라인을 사용 한 후

 

텍스트 렌더링

 

 

 

 

자바스크립트 내츄럴 템플릿

뒷부분의&nbsp; 문자를 치환해버린다.

그러므로 서버에서 렌더링되면 주석으로 처리된값으로 처리되는것이고

 

웹브라우저에서 그냥 연거라면 주석처리 뒷부분의 값으로 처리된다.

 

그래서 타임리프는 HTML 파일을 직접 열어도 동작하는 내츄럴 템플릿 기능을 제공한다.

 

 

객체

 

자바스크립트 인라인 each

자바스크립트 인라인은 each를 지원한다. ( 코드상에서 루프를 돌려야할때)

 

<!-- 자바스크립트 인라인 each -->
<script th:inline="javascript">
  [# th:each="user, stat : ${users}"]
  var user[[${stat.count}]] = [[${user}]];
  [/]
</script>

자바스크립트의 코드를 보면

 

user를 받고, 반복상태값을 이용해서

var user1 = ~

var user2 = ~

이런식으로 반복을 돌리고 싶은것이다.

 

[  ] 대괄호를 이용해서 [/] 구현해주면 된다.

 

결과페이지 소스보기


 

템플릿 조각

 

웹 페이지를 개발할 때는 공통 영역이 많이 있다.

 

예를 들어서 상단 영역이나 하단 영역, 좌측 카테고리 등등


여러 페이지에서 함께 사용하는 영역들이 있다. 

 

이런 부분을 코드를 복사해서 사용한다면 변경시 여러페이지를 다 수정해야 하므로 상당히 비효율 적이다. 

 

타임리프는 이런 문제를 해결하기 위해 템플릿 조각과 레이아웃 기능을 지원한다.

 

 

 

th:fragment로 템플릿 조각만들기

<footer th:fragment="copy">
  푸터 자리 입니다.
</footer>
<footer th:fragment="copyParam (param1, param2)">
  <p>파라미터 자리 입니다.</p>
  <p th:text="${param1}"></p>
  <p th:text="${param2}"></p>
</footer>

 

th:fragment="이름"

th:fragment="이름 (파라미터1,파라미터2)"

 

fragment의 이름을 메소드명으로 생각하고 fragment를 가져다 쓰면된다.

 

 

템플릿조각 가져오기

 

반드시 fragment expression(조각 표현식) ~{} 를 사용해야한다.

insert

<h2>부분 포함 insert</h2>
<div th:insert="~{template/fragment/footer :: copy}"></div>

insert속성으로인해 div태그안에 copy라는 footer가 불러와진다.

해당 위치에서 frament name인 copy를 가져온다! 라는 뜻이된다.

 

위의 소스 결과를 보면 <div>태그안에

<footer th:fragment="copy">
  푸터 자리 입니다.
</footer>

 copy라는 푸터가 불러와져서 들어가졌다. 즉 insert 속성은 현재 태그안에 탬플릿 조각을 추가해버린다.

웹페이지의 결과

 

replace

<h2>부분 포함 replace</h2>
<div th:replace="~{template/fragment/footer :: copy}"></div>

replace의 경우는 어떻게될까?

 

단어 그대로 현재 태그를 탬플릿조각으로 대체해버렸다.

 

파라미터 사용

다음과 같이 파라미터를 전달해서 동적으로 조각을 렌더링 할 수도 있다.

 

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

 

왜나면 fragment를 만들때

<footer th:fragment="copyParam (param1, param2)">
  <p>파라미터 자리 입니다.</p>
  <p th:text="${param1}"></p>
  <p th:text="${param2}"></p>
</footer>

다음과 같이 파라미터를 받을 수 있게끔 해놓았기 떄문이다!

 


템플릿 레이아웃

 

위에서는 일부코드 조각인 템플릿조각을 사용했다면 

 

이번에는 개념을 더 확장해서 코드 조각을 레이아웃에 넘겨서 사용하는 방법에 대해서 알아보자.

 

 

넘겨질 코드조각

html xmlns:th="http://www.thymeleaf.org">
<head th:fragment="common_header(title,links)">
  <title th:replace="${title}">레이아웃 타이틀</title>
  <!-- 공통 -->
  <link rel="stylesheet" type="text/css" media="all" th:href="@{/css/awesomeapp.css}">
  <link rel="shortcut icon" th:href="@{/images/favicon.ico}">
  <script type="text/javascript" th:src="@{/sh/scripts/codebase.js}"></script>
  <!-- 추가 -->
  <th:block th:replace="${links}" />
</head>

common_header라는 템플릿 조각은 title,links라는 파라미터를 받는다.

<title>이라는 태그에서 받은 파라미터로 해당 <title>태그를 대체해버린다.

 

맨아랫줄  <th:block th:replace="${links}" /> 에서 <th:block>은 들어오는 태그들에 대해 반복을 해주는데

links로 들어오는 <link> 태그들마다 th:replace를 실행해서 <th:block>태그를 <link> 태그로 대체하게끔한다.

 

 

넘겨 받을 레이아웃

예를 들어 홈페이지의 <head>부분은 공통된 부분도 많아서 템플릿조각으로 만들어 놓고싶다.

하지만 페이지마다 <head>의 내용은 조금씩 다르게 구성되어있다.

그렇다고해서 파라미터로 데이터를 받아서 치환해주는것보다.

 

아예 태그를 받아서 치환해버리는 방법이 있다.

<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>

"template/layout/base :: common_header(~{::title},~{::link})"

이 부분을 보면 common_header라는 템플릿 조각을 가져오려고하면서 파라미터로

~{::title} , ~{::link} 라고 적었다. ~{:: } 를 이용하면 현재 페이지의 해당 태그들을 전달해준다.

~{} (조각 표현식)을 꼭 써서 ::를 사용해야한다.  (태그이름을 써야한다)

 

결과

결과를 보면 넘겨받을 레이아웃에서 <title>태그와 <link>태그를 넘겨주면서 코드 조각(템플릿 조각)을 가져오면서

<head>를 대체시켰다.

 

그래서 넘겨받을 레이아웃에 있던 <title>로 바뀐것을 볼 수 있고,

공통부분은 넘겨질 코드조각(템플릿조각)에 있던부분이 그대로왔고

맨아래 넘겨받을 레이아웃에 있던 link 태그 2개가 전달된것을 볼 수 있다.

 

즉 코드조각(템플릿조각)을 가져올건데 가져오는 곳마다 유동적으로 내용을 바꾸고 싶을때 사용한다.

레이아웃에 많이 사용되는 <head>나 그런곳에서 주로 사용된다.

다시 정리

<head>,<footer>등 페이지 레이아웃마다 자주 사용되고, 

페이지마다 살짝살짝 내용이 달라져야할때

 

기본틀과 공통부분과 바뀌어야하는 부분을 지정해서 fragment로 두고

그 fragment를 가지고오면서 쓸때 추가되거나 대체되어야할 태그를 넘기면서

 

유동적으로 내용을 바꾸기 위해 사용하는 방법이다.

 

 

 

템플릿 레이아웃2

템플릿 레이아웃 확장

 

앞서 이야기한 개념을 <head> 정도에만 적용하는게 아니라 <html> 전체에 적용할 수도 있다.

 

 

layoutFIle.html (레이아웃의 틀)

공통적으로 들어가는부분, 추가로 대체할 수 있는 부분이 들어간 레이아웃의 틀로 사용될 html이다.

<!DOCTYPE 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>

보면 title과 content가 대체가능성이 있어보인다.

 

위의 fragment를 가져와 사용하면 title과 content말고 나머지는 일단 모두 같은 구조를 가지게된다.

 

layoutExtendMain.html ( 템플릿 조각을 사용해서 레이아웃을 구성할 html)

<!DOCTYPE html>
<html th:replace="~{template/layoutExtend/layoutFile :: layout(~{::title},~{::section})}"
      xmlns:th="http://www.thymeleaf.org">
<head>
  <title>메인 페이지 타이틀</title>
</head>
<body>
<section>
  <p>메인 페이지 컨텐츠</p>
  <div>메인 페이지 포함 내용</div>
</section>
</body>
</html>

th:replace ~ 부분으로 위에서 만든 fragment로 <html>을 대체하려고 하고있다.

그때 

layout(~{::title},~{::section})

를 보면 fragment를 가져오면서 현재 페이지의 <title>태그들, 현재 페이지의 <section>태그들을 전달한다.

 

fragment는 

th:fragment="layout (title, content)"

이렇게 구현되어있으니까

title에는 넘겨져온 모든 <title>내용이 , content에는 넘겨져온 모든 <section>내용이 들어갈것이고 

<title th:replace="${title}">레이아웃 타이틀</title>
<div th:replace="${content}">
  <p>레이아웃 컨텐츠</p>
</div>

두 부분이 들어온 태그들에 의해 대체될것이고 대체되고 난뒤 결과가 템플릿조각을 호출한 html로 넘어간다.

 

즉 결과는 (layoutExtendMain.html의 렌더링 결과 )

 

이렇게 된다.

 

 

이렇게 레이아웃을 정해놓고 해야

나중에 공통부분의 수정이 필요할때 모든 템플릿을 수정하지않고 해당 레이아웃 html만 수정하면 되니까

이런 구조를 많이 사용한다고 한다.

 

사이트가 작으면 조각조각별로 fragment를 만들어서 (head,footer 이런식으로)

넣는식으로 구성한다고도 한다. (수정에 대한 중복이 발생할 수 있으므로, 간단한 사이트를 만들때 사용하고, 큰 규모이면 레이아웃 전체를 가지는 fragment를 이용한다)

댓글