Mustache 란?

💡 학습 목표
    1. Mustache에 대해 설명할 수 있다.
    2. Mustache 기본 문법을 확인해 보자.

1. Mustache 란?

Mustache는 거의 로직이 없는 템플릿 엔진으로, 다양한 프로그래밍 언어에서 사용할 수 있으며, 간단하고 유지보수가 용이한 템플릿을 만드는 데에 유용하다. Spring Boot와 Mustache를 결합함으로써 개발자는 백엔드 로직과 프론트엔드 표현을 분리하여 더 효율적이고 관리하기 쉬운 웹 어플리케이션을 구축할 수 있다.

 

Mustache의 주요 특징

  • 로직을 최소화한 템플릿(Logic-less templates): Mustache 템플릿은 복잡한 로직을 최소화하여, 템플릿 내에서 간단한 조건문과 반복문을 지원한다. 이는 데이터를 표시하는 데에 집중하도록 설계되었다.
{{#users}}
  <p>{{name}} 님이 로그인했습니다.</p>
{{/users}}
{{^users}}
  <p>로그인한 사용자가 없습니다.</p>
{{/users}}

{{#users}}...{{/users}}는 users 목록에 대한 반복을, {{^users}}...{{/users}}는 users가 비어 있을 때의 처리를 나타낸다.

  • 언어 독립성(Language agnostic): Mustache는 JavaScript, Java, Python 등 다양한 프로그래밍 언어에서 사용할 수 있다.
  • 간단함(Simplicity): 템플릿의 구조가 단순하고 이해하기 쉽다.

Spring Boot와 Mustache 의 사용

Spring Boot에서 Mustache를 사용하기 위해, 먼저 spring-boot-starter-mustache 의존성을 프로젝트에 추가해야 한다. 이 스타터는 Mustache 템플릿 엔진과 관련된 모든 필요한 의존성을 제공한다. 그 후, src/main/resources/templates 디렉토리에 Mustache 템플릿 파일을 위치시키고, Spring MVC 컨트롤러를 통해 모델 데이터를 템플릿에 전달하여 동적인 웹 페이지를 생성할 수 있다.

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-mustache'
    // ...
}

 

package com.example.demo._domain.blog.controller;

import org.springframework.stereotype.Controller;

@Controller
public class SampleController {
	
	public String home() {
		return "hello";
	}
	
}

 

2. mustache 활용

 

 

hello.mustache 템플릿 파일은 src/main/resources/templates 디렉토리에 위치해야 하며, 이 파일은 Mustache 템플릿 언어를 사용하여 작성된다. 간단한 "Hello, World!" 예제를 기반으로 한 hello.mustache 파일의 내용은 다음과 같다

<!DOCTYPE html>
<html>
<head>
    <title>Hello Page</title>
</head>
<body>
    <h1>Hello,{{name}}!</h1>
</body>
</html>

spring:
  mustache:
    servlet:
      expose-session-attributes: true  # Mustache 템플릿에서 세션 속성에 접근할 수 있도록 허용
      expose-request-attributes: true  # Mustache 템플릿에서 요청 속성에 접근할 수 있도록 허용

 

package com.example.demo._domain.blog.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

import jakarta.servlet.http.HttpServletRequest;


@Controller
public class SampleController {
	
	// 주소설계 - http://localhost:8080/    (GET) 요청  
	@GetMapping("/")
	public String home(HttpServletRequest request) {
		
		// 뷰 리졸브 동작 
		request.setAttribute("name", "반가워");
		// hello  --> src/main/resources/templates/hello.mustache
		// model.addAttribute("name", " 한글 설정 ");
		return "hello";
	}
	
}

 

3. Mustache 기본 문법

Mustache는 간단하고 직관적인 템플릿 엔진으로, HTML 등의 템플릿에서 데이터를 삽입하거나 간단한 로직을 표현할 수 있다

  • {{값}} 데이터 전달
  • {{#값}} 조건문 혹은 반복문(컬렉션)
  • {{^값}} 부정
  • {{>파일명}} 부분 템플릿
  • {{#컬렉션}} -> 내부에 키 값이 없으면 출력할 때ㅡ {{.}} 으로 사용
  • {{{ }}} 콧수염 태그 - 이스케이프 안됨

 

  • 데이터 표시 (Interpolation): {{값}}
    • 설명: 템플릿에 데이터를 삽입할 때 사용한다.
    <p>안녕하세요, {{name}}님!</p>
    
    만약 name에 "홍길동"이란 값이 전달되면, 결과는 <p>안녕하세요, 홍길동님!</p>가 된다.
  • 섹션 (Sections): {{#값}}...{{/값}}
    • 설명: 조건문이나 반복문을 구현할 때 사용합니다. 값이 참(또는 참 같은 값)이면 내부 내용을 렌더링한다.
    • 조건문 예시
    {{#isMember}}
      <p>회원 전용 콘텐츠입니다.</p>
    {{/isMember}}
    
    isMember가 true일 경우에만 메시지가 표시된다.
    • 반복문 예시
    <ul>
      {{#items}}
        <li>{{name}}: {{price}}원</li>
      {{/items}}
    </ul>
    items가 객체들의 배열이라면, 각 객체의 name과 price를 이용해 리스트를 만든다.
  • 역섹션 (Inverted Sections): {{^값}}...{{/값}}
    • 설명: 값이 거짓(또는 거짓 같은 값)이면 내부 내용을 렌더링한다.
    • 예시
    {{^isMember}}
      <p>로그인 후 이용해 주세요.</p>
    {{/isMember}}
    :isMember가 false일 경우에만 메시지가 표시된다.
  • 부분 템플릿 (Partials): {{>파일명}}
    • 설명: 다른 템플릿 파일을 삽입할 때 사용합니다. 코드의 재사용성을 높여준다.
    • 예시
    <header>
      {{> header}}
    </header>
    :현재 템플릿에 header.mustache 파일의 내용을 포함한다.
  • 현재 값 출력: {{.}}
    • 설명: 반복문 내에서 현재 아이템의 값이 키 없이 단순한 값일 때 사용한다.
    • 예시
    • <ul> {{#colors}} <li>{{.}}</li> {{/colors}} </ul>
    <ul>
      {{#colors}}
        <li>{{.}}</li>
      {{/colors}}
    </ul>
    :colors가 ["빨강", "초록", "파랑"]이라면, 각 문자열 값이 리스트 아이템으로 출력된다.

4. Mustache 추가 개념 정리

  • 데이터 구조 이해하기: Mustache는 전달된 데이터 모델을 기반으로 렌더링한다. 따라서 데이터 구조를 잘 이해하고 있어야 올바르게 템플릿을 작성할 수 있다.
  • 로직 최소화: Mustache는 템플릿에서의 로직 사용을 최소화하여 뷰와 비즈니스 로직을 분리하는 데 도움을 준다.
  • 부분 템플릿 활용: 공통되는 헤더나 푸터 등을 부분 템플릿으로 분리하면 코드의 재사용성과 유지보수성이 높아진다.
  • HTML 이스케이프: Mustache는 기본적으로 데이터 값을 HTML 이스케이프 처리한다. 만약 이스케이프를 원하지 않는다면 {{{값}}} 또는 {{& 값}}을 사용할 수 있다.
<!DOCTYPE html>
<html>
<head>
  <title>{{title}}</title>
</head>
<body>
  {{> header}}

  {{#isMember}}
    <p>안녕하세요, {{name}}님!</p>
    <ul>
      {{#notifications}}
        <li>{{.}}</li>
      {{/notifications}}
    </ul>
  {{/isMember}}

  {{^isMember}}
    <p>로그인해 주세요.</p>
  {{/isMember}}

  {{> footer}}
</body>
</html>
머스태치 태그 연습 하기
package com.example.demo._domain.blog.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class IndexController {

    // 정적 파일을 ---> 어떤 폴더에 넣어야 해 --> static 넣어야 한다.

    @GetMapping({ "/index", "/index2" })
    public String index1(Model model) {

        // 뷰 쪽으로 내려줄 데이터를 만들어 보자.
        String name = "길동";
        int age = 22;
        String email = "<b> a@naver.com</b>";
        String password = "asd123";
        String test = "<script>" + //
                "        alert(\"ㅋㅋ\");" + //
                "    </script>";

        model.addAttribute("name", name);
        model.addAttribute("age", age);
        model.addAttribute("email", email);
        model.addAttribute("password", password);
        model.addAttribute("test", test);

        return "index";
    }

}
구현
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            background-color: #f4f4f9;
            color: #333;
            padding: 20px;
        }

        .container {
            max-width: 600px;
            margin: 0 auto;
            background-color: #fff;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
        }

        h1 {
            text-align: center;
            color: #007bff;
        }

        p {
            font-size: 18px;
            line-height: 1.6;
        }

        p strong {
            color: #007bff;
        }

        .email {
            color: #dc3545;
        }

        .password {
            color: #28a745;
            font-weight: bold;
        }
    </style>
</head>

<body>
    <div class="container">
        <h1>사용자 정보</h1>
        <p><strong>이름:</strong> {{name}}</p>
        <p><strong>나이:</strong> {{age}}세</p>
        <p><strong>이메일:</strong> <span class="email">{{{email}}}</span></p>
        <p><strong>비밀번호:</strong> <span class="password">{{password}}</span></p>
        <p>{{{test}}}</p>
    </div>
</body>

</html>
한글 깨짐 확인
application.properties 코드 추가
spring.application.name=class_mustache

# HTTP 응답의 인코딩을 강제로 UTF-8로 설정합니다.
# 즉, 모든 서버 응답에서 UTF-8 인코딩을 사용하도록 강제합니다.
server.servlet.encoding.force-response=true

# HTTP 요청과 응답에 사용되는 기본 문자 인코딩을 UTF-8로 설정합니다.
# 이 설정은 클라이언트와 서버 간의 통신에서 UTF-8 인코딩을 사용하도록 합니다.
spring.http.encoding.charset=UTF-8

# HTTP 인코딩 기능을 활성화합니다.
# 이 옵션이 true로 설정되면 스프링 부트는 HTTP 요청과 응답에서 설정된 문자 인코딩을 사용합니다.
spring.http.encoding.enabled=true

# 모든 HTTP 요청 및 응답에 대해 UTF-8 인코딩을 강제합니다.
# 클라이언트가 별도의 인코딩을 지정하지 않더라도 UTF-8 인코딩이 사용됩니다.
spring.http.encoding.force=true

spring.output.ansi.enabled=always

 

{{{}}} 의 보안 취약성 

위 코드에서 String test 에 스크립트 문자열을 그대로 입력 하고, musctache에서 그대로 출력했더니

이렇게 스크립트 코드가 작동하게 됨.

이를 방지 하기 위해서

public String index1(Model model) {

    // 뷰 쪽으로 내려줄 데이터를 만들어 보자.
    String name = "길동";
    int age = 22;
    String email = "<b> a@naver.com</b>";
    String password = "asd123";
    String test = "<script>" + //
            "        alert(\"ㅋㅋ\");" + //
            "    </script>";
    test = test.replaceAll("<", "&lt;");
    test = test.replaceAll(">", "&gt;");
    test = test.replaceAll("\\(", "&#40;");
    test = test.replaceAll("\\)", "&#41;");
    test = test.replaceAll("'", "&#x27;");

    model.addAttribute("name", name);
    model.addAttribute("age", age);
    model.addAttribute("email", email);
    model.addAttribute("password", password);
    model.addAttribute("test", test);

    return "index";
}
  • html, css, js 코드에 쓰이는 특수문자를 변환 시켜서 전달한다.

목차로 돌아가기

 

'Spring Boot > Blog 프로젝트 만들기(JPA)' 카테고리의 다른 글

익명 게시판 만들기  (0) 2024.10.07
프로젝트 생성  (0) 2024.10.07
템플릿 엔진이란?  (1) 2024.10.04
글 수정 API 만들기 - 7  (1) 2024.10.02
글 상세보기(조회) API 구현 - 6  (0) 2024.10.02