Spring Boot & Thymeleaf 시리즈는 김영한 님의 "스프링 MVC 2편 - 백엔드 웹 개발 활용 기술" 강의를 정리한 글입니다. 글에 첨부된 사진은 해당 강의의 강의 자료에서 캡쳐한 것입니다. 제 Github에만 올려뒀다가, 정보 공유와 강의 홍보(?)를 위해 블로그에도 업로드합니다.
타임리프는 자바스크립트 코드 안에서 타임리프를 편리하게 사용할 수 있는 자바스크립트 인라인 기능을 제공한다.
자바스크립트 인라인 기능은 다음과 같이 적용하면 된다.
<script th:inline="javascript">
BasicController.java 내용 추가
package kloong.thymeleaf.basic;
import ... //생략
@Controller
@RequestMapping("/basic")
public class BasicController {
@GetMapping("/javascript")
public String javascript(Model model) {
model.addAttribute("user", new User("userA", 10));
addUsers(model);
return "basic/javascript";
}
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);
}
@Data
static class User {
private String username;
private int age;
public User(String username, int age) {
this.username = username;
this.age = age;
}
}
}
/resources/templates/basic/javascript.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!-- 자바스크립트 인라인 사용 전 -->
<script>
var username = [[${user.username}]]; //userA -> 문자열 아니므로 자바스크립트 오류
var age = [[${user.age}]]; //10
//자바스크립트 내추럴 템플릿
var username2 = /*[[${user.username}]]*/ "test username";
//객체
var user = [[${user}]]; //toString() 호출 결과 -> 자바스크립트 오류
</script>
<!-- 자바스크립트 인라인 사용 후 -->
<script th:inline="javascript">
var username = [[${user.username}]];
var age = [[${user.age}]];
//자바스크립트 내추럴 템플릿
var username2 = /*[[${user.username}]]*/ "test username";
//객체
var user = [[${user}]];
</script>
</body>
</html>
실행 결과 - 자바스크립트 인라인 사용 전
<!-- 자바스크립트 인라인 사용 전 -->
<script>
var username = userA;
var age = 10;
//자바스크립트 내추럴 템플릿
var username2 = /*userA*/ "test username";
//객체
var user = BasicController.User(username=userA, age=10);
</script>
자바스크립트 인라인을 사용하지 않으면, 타임리프는 자신이 렌더링하고 있는 부분이 자바스크립트 코드 내부임을 알 수가 없다.
따라서 평소 하던 그대로 렌더링을 한다.
[[${user.username}]] 을 userA 로 렌더링 한다.
[[${user.age}]] 을 10 으로 렌더링 한다.
/*[[${user.username}]]*/ 을 /*userA*/ 로 렌더링 한다. 자바스크립트 인라인을 사용하지 않고 있기 때문에 타임리프는 자바스크립트의 주석인 /* */ 를 무시하고 그대로 렌더링한다.
[[${user}]] 는 user.toString() 의 값으로 렌더링한다.
타임리프는 server side에서 렌더링을 하던대로 잘 했다.
문제는 server side에서 타임리프에 의해 렌더링 된 HTML을 받은 웹 브라우저가 자바스크립트 코드를 실행시킬 때 발생한다.
실행 결과 - 자바스크립트 인라인 사용 후
<!-- 자바스크립트 인라인 사용 후 -->
<script>
var username = "userA";
var age = 10;
//자바스크립트 내추럴 템플릿
var username2 = "userA";
//객체
var user = {"username":"userA","age":10};
</script>
자바스크립트 인라인의 동작
자바스크립트 인라인을 사용하면, 타임리프는 해당 부분이 자바스크립트 코드 내부임을 알게 된다. 그래서 자바스크립트 코드에 맞춰서 렌더링 방식을 다르게 하여 동작한다.
텍스트 렌더링
var username = [[${user.username}]];
인라인 사용 전: var username = userA
인라인 사용 후: var username = "userA"
인라인 사용 전 결과를 보면 [[${user.username}]] 가 userA 로 렌더링 된다.
개발자가 원하는 것은 var username 에 userA 라는 값을 가진 문자열을 대입하는 작업일 것이다.
하지만 타임리프는 잘못이 없다. 코드 그대로 정확하게 렌더링 했다. 문제는 자바스크립트가 userA 를 문자열로 인식할 수가 없다. 큰따옴표로 감싸져 있지 않기 때문에 당연하다.
var username = "[[${user.username}]]" 이렇게 하는 것도 방법이 될 수는 있다. 하지만 개발자가 모든 변수에 대해서 변수의 타입이 문자열이면 큰따옴표를 붙이고, 아니면 빼는 작업을 하는 건 너무 귀찮고 오류가 날 가능성도 높다.
자바스크립트 인라인을 사용하면 타임리프가 변수 타입이 문자열인 경우 렌더링 결과를 큰따옴표로 감싸준다.
추가로 자바스크립트에서 문제가 될 수 있는 문자가 포함되어있으면 escape 처리도 해준다.
ex. " -> \"
자바스크립트 natural template
타임리프는 HTML 파일을 직접 열어도 동작하는 내추럴 템플릿 기능을 제공한다.
자바스크립트 인라인을 사용하면, 자바스크립트 주석을 활용해서 자바스크립트 코드 내부에서도 natural template 기능을 사용할 수 있다.
var username2 = /*[[${user.username}]]*/ "test username";
인라인 사용 전: var username2 = /*userA*/ "test username";
인라인 사용 후: var username2 = "userA";
개발자는 HTML 파일을 직접 여는 상황 같이 타임리프가 렌더링을 하지 않는 경우에는 username2 에 "test username" 이 대입 되고, 타임리프가 렌더링을 하면 "userA" 가 대입되는 결과를 원한다. 즉 natural template 기능을 원하는 것이다.
실제로 /* */ 는 자바스크립트의 주석이기 때문에, 파일을 직접 열면 웹 브라우저는 /*[[${user.username}]]*/ 를 무시하고 username2 에 "test username" 을 대입하게 된다.
문제는 자바스크립트 인라인을 사용하지 않으면, 타임리프로 렌더링을 하는 경우에도 타임리프가 자바스크립트 주석을 이해하지 못하기 때문에, 평소 하던대로 다음과 같이 렌더링한다.
var username2 = /*userA*/ "test username";
이러면 타임리프가 동작해도 웹 브라우저는 /* */ 내부의 값을 무시하기 때문에 username2 에는 여전히 "test username" 이 대입되게 된다.
하지만 자바스크립트 인라인을 사용하면 타임리프가 렌더링을 했을 때 /* */ 가 제거되고, "userA" 가 username2 에 대입되게 된다.
즉 자바스크립트 인라인을 통해 자바스크립트 코드 내부에서도 natural template 기능을 사용할 수 있게 된다.
객체 대입
타임리프의 자바스크립트 인라인 기능을 사용하면 객체를 JSON으로 자동으로 변환해준다.
var user = [[${user}]];
인라인 사용 전: var user = BasicController.User(username=userA, age=10);
인라인 사용 후: var user = {"username":"userA","age":10};
인라인 사용 전에는 타임리프가 객체의 toString() 을 호출한다.
인라인 사용 후에는 타임리프가 객체를 JSON 형태로 변환해준다.
자바스크립트에서는 JSON을 변수에 대입할 수 있다.
자바스크립트 인라인 th:each
자바스크립트 인라인은 자바스크립트 코드 내에서도 th:each 를 사용할 수 있게 해준다.
resources/templates/basic/javascript.html 에 추가
<!-- 자바스크립트 인라인 each -->
<script th:inline="javascript">
[# th:each="user : ${users}"]
var user[[${userStat.count}]] = [[${user}]];
[/]
</script>
자바스크립트 인라인 each 결과
<script>
var user1 = {"username":"userA","age":10};
var user2 = {"username":"userB","age":20};
var user3 = {"username":"userC","age":30};
</script>
댓글