본문 바로가기
Spring/Spring Boot & Thymeleaf

[Spring Boot & Thymeleaf] 14. 자바스크립트 인라인(Javascript Inline)

by Kloong 2022. 10. 24.

참고

더보기

Spring Boot & Thymeleaf 시리즈는 김영한 님의 "스프링 MVC 2편 - 백엔드 웹 개발 활용 기술" 강의를 정리한 글입니다. 글에 첨부된 사진은 해당 강의의 강의 자료에서 캡쳐한 것입니다. 제 Github에만 올려뒀다가, 정보 공유와 강의 홍보(?)를 위해 블로그에도 업로드합니다.

 

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

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

www.inflearn.com

마크다운 형식으로 작성한 글을 블로그에 다시 올리는 거라 가독성이 많이 떨어집니다. 조금더 편하게 보시려면 아래의 Github repository에서 보시면 됩니다.

 

GitHub - Kloong1/TIL: Today I Learned.

Today I Learned. Contribute to Kloong1/TIL development by creating an account on GitHub.

github.com

타임리프 - 자바스크립트 인라인

  • 타임리프는 자바스크립트 코드 안에서 타임리프를 편리하게 사용할 수 있는 자바스크립트 인라인 기능을 제공한다.
  • 자바스크립트 인라인 기능은 다음과 같이 적용하면 된다.
  • <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 usernameuserA 라는 값을 가진 문자열을 대입하는 작업일 것이다.
  • 하지만 타임리프는 잘못이 없다. 코드 그대로 정확하게 렌더링 했다. 문제는 자바스크립트가 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>

댓글