Thymeleaf 변수 저장 - Thymeleaf byeonsu jeojang

아래와 같이 jstl의 set은 scope등을 활용하여 변수를 활용하고 있다.

<c:set var="userId" value="gd.hong" />
<c:set var="userNm" value="홍길동" />

<!-- 위와 같이 새 변수를 정의하고, 아래와 같이 재 바인딩도 가능하다. -->

<c:set var="userId" value="cs.kim" />
<c:set var="userNm" value="김철수" />


그런 다음 아래처럼 사용하겠죠?

<!-- 렌더링 전 -->
<div>
  <ui>
    <li>${userId}</li>
    <li>${userNm}</li>
  </ul>
</div>

<!-- 렌더링 후 -->
<div>
  <ui>
    <li>cs.kim</li>
    <li>김철수</li>
  </ul>
</div>

자~ 그럼 Thymeleaf 에선 어떻게 쓸까? 다음을 보자!

th:with 를 이용하여 변수(?)처럼를 정의하고, 값을 바인딩 하고 있다!

우선 th:with를 사용하려면, 사용하고자 하는 위치 태그 또는
그 부모태그(그 상위태그도 가능)에 지정하면 됩니다.

만약 아래의 html에서 userId와 userNm을 노출시키려면, thymeleaf의 문법에 의해 아래처럼 표현할수 있다.

<!--/* 렌더링 전 */-->
<div>
  <ui th:with="userId='gd.hong',  userNm='홍길동'   ">
    <li>[[${userId}]]</li>
    <li>[[${userNm}]]</li>
  </ul>
</div>

<!--/* 렌더링 후 */-->
<div>
  <ui >
    <li>gd.hong</li>
    <li>홍길동</li>
  </ul>
</div>

오호~ 잘나오는군!!! 그런데 말입니다!!! 부모태그말고 같은 depth의 태그
즉 형제 엘리먼트에서 사용하면, 어떻게 될까요?
자~ 한번 아래를 봅시다!

<!--/* 렌더링 전 */-->
<div>
  <ui>
    <li th:with="userId='gd.hong',  userNm='홍길동'  ">[[${userId}]] ---- [[${userNm}]]</li>
    <li>[[${userId}]] ---- [[${userNm}]]</li>
  </ul>
</div>

<!--/* 렌더링 후 */-->
<div>
  <ui >
    <li>gd.hong ---- 홍길동</li>
    <li> ---- </li>
  </ul>
</div>

오잉 li 태그에 정의하고 그형제 엘리먼트에서 사용한것뿐인데 변수를 못쓰네?
결론 : with 로 정의한 변수는 형제엘리먼트에는 영향을 미치지 못함.(지새끼만 챙기는 녀석...)


자~그럼 부모태그에서 정의한 걸 자식태그에서 쓰는걸 알겠어~OK!
근데 그값을 하위엘리먼트 어딘가에서 재정의하면 계속 유지되나? 한번 알아봅시다!

<!--/* 렌더링 전 */-->
<div id="1" th:with="userId='gd.hong',  userNm='홍길동'  ">
  <div id="2">[[${userId}]] ---- [[${userNm}]]</div>
  <ui th:with="userId='cs.kim',  userNm='김철수'  ">
    <li id="3" th:with="userId='yh.lee',  userNm='이영희'  ">[[${userId}]] ---- [[${userNm}]]</li>
    <li id="4">[[${userId}]] ---- [[${userNm}]]</li>
  </ul>
  <div id="5">[[${userId}]] ---- [[${userNm}]]</div>
</div>

<!--/* 렌더링 후 */-->
<div id="1">
  <div id="2">gd.hong ---- 홍길동</div>
  <ui>
    <li id="3">yh.lee ---- 이영희</li>
    <li id="4">cs.kim ---- 김철수</li>
  </ul>
  <div id="5">gd.hong ---- 홍길동</div>
</div>

<!--/* 만약 jstl이었다면, 아래처럼 되었겠죠? */-->
<div id="1">
  <div id="2">gd.hong ---- 홍길동</div>
  <ui>
    <li id="3">yh.lee ---- 이영희</li>
    <li id="4">yh.lee ---- 이영희</li>
  </ul>
  <div id="5">yh.lee ---- 이영희</div>
</div>

결론 : with 로 정의한 변수는 하위 엘리먼트에서 재정의하여 사용하는건 반영되지만 그게 마치 변수에 담아서 스코프를 유지하지는 않는다.


보너스 트랙! foreach 문에서는 어떨까요? 과연 같은 depth에서 지정한 값이 유지될까요?

<!--/* 렌더링 전 */-->
<table th:with="isFirst=false">
 <tr th:each="num,status : ${#numbers.sequence(1,3)}" >
   <td>
      <h2 th:if="${isFirst == false}">isFirst : [[${num}]]        
         <th:block th:with="isFirst=true" />
      </h2>
   </td>
   <td>
      <h2 th:if="${isFirst == true}">isNotFirst : [[${num}]] </h2>
   </td>
 </tr>
<table>

<!--/* 렌더링 후 */-->
<table>
 <tr>
   <td>
      <h2>isFirst : 1  </h2>
   </td> 
 </tr>
 <tr>
   <td>
      <h2>isFirst : 2  </h2>
   </td> 
 </tr>
 <tr>
   <td>
      <h2>isFirst : 3  </h2>
   </td> 
 </tr>
<table>

<!--/* 만약 jstl 이였다면, isFirst 변수에 true 가 바인딩되어 아래처럼 되었을것이다. 이차이점을 기억하라!*/-->
<table>
 <tr>
   <td>
      <h2>isFirst : 1  </h2>
   </td> 
 </tr>
 <tr>
   <td>
      <h2>isNotFirst : 2  </h2>
   </td> 
 </tr>
 <tr>
   <td>
      <h2>isNotFirst : 3  </h2>
   </td> 
 </tr>
<table>

위와 같이 상위 table태그에 isFirst 라는 플래그 변수를 정의하고, false란 값을 바인딩했다. 
그리고 숫자배열[1, 2, 3] 만큼 반복하면서 첫번째 row일때만 h2 태그를 찍고 그다음부턴 h2를 찍고싶었다.
(물론 status.first 변수를 사용하면 해결가능하다. 쉽게 이해를 돕고자 플래그를 사용하는 예제를 억지로 만듬)

즉, isFirst 가 false 일때, isFirst=true 로 재 바인딩처리하면, 두번째부턴 h2 태그가 찍히겠지 했는데....
헐... 모든 row에서 h2태그를 출력을 하고 말았다. ㅡㅡ;

결론 : 타임리프의 foreach 문에서 플래그 변수처럼 재바인딩 불가함!

최종결론

1. jstl의 변수 scope 개념과 thymeleaf의 th:with 는 개념이 서로 다르다! 

2. thymeleaf는 html태그 구조상 부모-자식관계일때, 아래쪽으로 영향을 준다.

3. 부모 element에 정의한 변수는 자식 element에서 사용가능

3. 부모 element에 정의한 변수는 자식 element에서 재바인딩 가능, 

   그 자식 element에서 재바인딩된 변수를 손자 element에서 또 사용/재바인딩 가능, 그아래로 계속 가능

4. 형제 element에 정의한 변수를 형제 element에서 사용불가

     (마찬가지로 foreach도 같은 레벨에서 반복하는 것이므로 재바인딩도 불가,
      재바인딩되어 값이 유지될거라 착각하지 말것!)

5. (당연하겠지만) 자식 element에 정의한 변수를 부모 element에서 사용불가