Spring boot

(스프링 부트 입문)머스태치 학습을 위한 프로젝트 - 1

mynote6676 2025. 6. 19. 08:49

머스테치(Mustache)소개

머스테치는 로직 없는 템플릿 엔진으로 , HTML과 데이터를 결합해 동적 페이지를 생성합니다. 스프링 부트에서 기본적으로

지원되며, 간단한 문법을 통해 데이터 바인딩과 반복 처리가 가능합니다. 

https://mustache.github.io/

 

{{ mustache }}

Logic-less templates. Available in Ruby, JavaScript, Python, Erlang, Elixir, PHP, Perl, Raku, Objective-C, Java, C#/.NET, Android, C++, CFEngine, Go, Lua, ooc, ActionScript, ColdFusion, Scala, Clojure[Script], Clojure, Fantom, CoffeeScript, D, Haskell, XQu

mustache.github.io

 

MVC 패턴에서의 데이터 흐름

  • Controller : 비즈니스 로직을 처리하고 데이터를 준비
  • Model: 데이터 전달을 위한 컨테이너 역활
  • view: 데이터를 사용자에게 표현 

스프링 부트에서 뷰로 데이터를 전달하는 방식

데이터 전달의 기본 흐름

커트롤러 -> model 객체 -> 탬플릿 엔진 -> 완성된 HTML

 

model 객체란?

model은 데이터를 담는 상자입니다. 컨트롤러에서 뷰로 정보를 전달할 때 사용하는 임시 저장소  역활을 합니다.

 

Model 객체의 특징

Model은 요청 범위(Request Scope)내에서 존재하는 데이터 컨테이너입니다.

  • 생명주기 : HTTP 요청이 시작될 때 생성되고, 응답이 완료되면 소멸
  • 데이터 격리 : 각 요청마다 독립적인 Model 인스턴스 생성
  • 상태 비저장 : 요청 간 데이터 공유 불가 (Stateless)

뷰 렌더링 원리

템플릿 엔진은 서버 사이드 렌더링(SSR)방식으로 동작합니다.

1. 템플릿 파싱: 정적 HTML과 동적 태그를 구분

2. 데이터 주입: Model의 데이터를 템플릿의 플레이스홀더에 삽입

3. 최종 렌더링: 완성된 HTML을 클라이언트로 전송

 

기본 프로젝트 설정 및 확인

더보기

 

의존성 설정 확인 - 일부만 사용할 예정

dependencies {

	implementation 'org.springframework.boot:spring-boot-starter-mustache'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	compileOnly 'org.projectlombok:lombok'
	developmentOnly 'org.springframework.boot:spring-boot-devtools'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

 

yml 파일 변환 후 설정 확인 

spring:
  mustache:
  #템플릿 파일의 확장자를 .mustache로 지정하영 Mustache
  #템플릿 파일을 인식하도록 설정
  suffix: .mustache
  #템플릿 파일이 위치한 
  #기본 경로를 src/main/resources/templates/로 설정
  prefix: classpath:/templates/
  # 개발 중 템플릿 캐시를 비활성화하여 코드
  # 수정 후 바로 반영되도록 함 (프로덕션에서는 true로 설정 권장)
  cache: false
  #템플릿 파일을 UTF-8 인코딩으로 처리하여 한글 등 다양한 문자 지원
  charset: UTF-8
  
  server:
  #애플리케이션이 실행될 포트를 8080으로 설정(기본값은 8080, 필요 시 변경 가능)
    port: 8080
    
  logging:
    level:
    # Spring Web 관련 로그를 DEBUG 수준으로
    # 설정하여 요청/응답 세부 사항을 개발 중 확인 가능
    org.springfranework.web:DEBUG

 

 

 

머스태치 사용을 위한 디렉토리 구조 설계

 

더보기

1.2 디렉토리 구조

학습 개념: 관심사의 분리(Separation of Concerns)

 

 

메인 클래스 확인

더보기
package com.tenco.mustache;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * 메인 애플리케이션 클래스
 *
 * - @SpringBootApplication: 스프링 부트 자동 설정 활성화
 * - 컴포넌트 스캔의 시작점
 * - 내장 톰캣 서버 실행
 */
@SpringBootApplication
public class MustacheApplication {

	public static void main(String[] args) {
		SpringApplication.run(MustacheApplication.class, args);
	}
}

시나리오 코드 1단계

더보기
package com.tenco.mustache.controller;

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

import java.util.ArrayList;
import java.util.List;

@Controller
public class Exam1MustacheController {

	@GetMapping("/example/test1)
     public String basic1(Model model) {
        // 컨트롤러에서 뷰로 데이터를 전달 하는 여러 방법 중
        // Model 객체 사용

        // 1. 데이터를 Model 객체에 추가 하기
        // 1.1 기본 변수 출력 : 머스태치 파일에서 {{key}} 값으로 출력할 수 있다.
        model.addAttribute("stringValue", "안녕 머스태치야~");
        model.addAttribute("intValue", 1234);

        // 2.  데이터를 Model 객체에 추가 하기
        // 2.1 HTML 이스케이프 해제: {{{key}}}로 HTML 태그를 렌더링하도록 설정 (가능한 사용 x 주의해서 사용)
        //     XSS 보안에 취약해 진다.
        model.addAttribute("htmlContent", "<strong>굵게</strong>와 <em>기울임</em> 텍스트");

        // 3. 데이터를 Model 객체에 추가 하기
        // 3.1 섹션 : {{#key}} ..... {{/key}}
        //           #key 값이 참으로 평가 될 때 렌더링 됨  (Truthy - null 아니거나 빈 컬렉션이 아닐 때)
        model.addAttribute("hasData", true);
        model.addAttribute("data", "비밀 메시지");

        // 4. 데이터를 Model 객체에 추가 하기
        // 4.1 부정 섹션: {{^key}} .... {{/key}} 로 조건이 거짓일 때 렌더링, null 값 설정
        //               #key 값이 거짓으로 평가 될 때 렌더링 됨  (Falsy - null 이거나 빈 컬렉션이 일 때)
        model.addAttribute("noData", null);

        // 5.  데이터를 Model 객체에 추가 하기
        // 5.1 컬렉션 반복: {{#collection}}으로 리스트를 반복 출력
        List<String> items = new ArrayList<>();
        items.add("사과");
        items.add("바나나");
        items.add("오렌지");
        model.addAttribute("items", items);


        // 6. 주석: 템플릿에서 {{! comment }}로 렌더링되지 않는 주석 처리
        // (컨트롤러에서는 별도 처리 불필요, 뷰에서 확인)

        // 7. 부분 템플릿: {{> partialName}}으로 다른 템플릿 포함
        // (컨트롤러에서는 별도 데이터 추가 불필요, 뷰에서 설정)

        // prefix :  classpath:/templates/
        // 작성   :  examples/test1
        // suffix:   .mustache
        // 완성 : /templates/examples/test.mustache
        return "examples/test1";
    }
    
}

 

|head.mustache

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
  <header>
    <h1>여기는 header 영역이야</h1>
  </header>

 

|examples/teat.mustache

 

<!--<!DOCTYPE html>-->
<!--<html lang="ko">-->
<!--<head>-->
<!--    <meta charset="UTF-8">-->
<!--    <title>머스태치 학습 예제</title>-->
<!--</head>-->
<!--<body>-->
{{> examples/head}}

<h1>머스태치 학습 예제 1 </h1>

<p>문자열: {{stringValue}}</p>
<p>숫자: {{intValue}}</p>

<p>HTML 렌더링(가능한 사용 금지 - 보안에 취약): {{{htmlContent}}}</p>

{{#hasData}}
    <p>데이터 존재: {{data}}</p>
{{/hasData}}

{{^noData}}
    <p>데이터가 없습니다.</p>
{{/noData}}

<ul>
    {{#items}}
        {{! 키 값이 없는 컬렉션을 처리할 때 . 을 사용한다. }}
        <li>{{.}}</li>
    {{/items}}
</ul>

{{! 이 부분은 출력되지 않습니다 }}

</body>
</html>

 

 시나리오 코드 2 단계

 

더보기

Exam2MustacheController - 1차 코드

package com.tenco.mustache.controller;

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

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

/**
 * Mustache 문법 학습을 위한 단계별 예제
 */
@Controller
@RequestMapping("/mustache") // 대문 달기 - 각 메서드에서 공통 사용
public class Exam2MustacheController {

    /**
     * 1. 기본 변수 출력 ({{key}}) 학습
     * URL: http://localhost:8080/mustache/basic-variables
     */
    @GetMapping("/basic-variables")
    public String basicVariables(Model model) {
        // 다양한 데이터 타입의 기본 변수들
        model.addAttribute("key", "key");
        model.addAttribute("pageTitle", "기본 변수 출력 학습");
        model.addAttribute("message", "안녕하세요, Mustache입니다!");
        model.addAttribute("userName", "김개발자");
        model.addAttribute("userAge", 28);
        model.addAttribute("userScore", 95.5);
        model.addAttribute("isActive", true);
        model.addAttribute("currentDate", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));

        return "examples/basic2";
    }
    
}

 

examples/basic1.mustache

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{{pageTitle}}</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container mt-5">
    <h1 class="text-center mb-4">{{pageTitle}}</h1>

    <div class="card">
        <div class="card-header">
            <h3>기본 변수 출력 ({{key}}) 예제</h3>
        </div>
        <div class="card-body">
            <h4>문법 설명</h4>
            <p class="text-muted">컨트롤러에서 전달한 데이터를 {{key}} 형태로 출력합니다.</p>

            <h4>결과</h4>
            <ul class="list-group">
                <li class="list-group-item"><strong>메시지:</strong> {{message}}</li>
                <li class="list-group-item"><strong>사용자명:</strong> {{userName}}</li>
                <li class="list-group-item"><strong>나이:</strong> {{userAge}}세</li>
                <li class="list-group-item"><strong>점수:</strong> {{userScore}}점</li>
                <li class="list-group-item"><strong>활성 상태:</strong> {{isActive}}</li>
                <li class="list-group-item"><strong>현재 시간:</strong> {{currentDate}}</li>
            </ul>

            <div class="mt-4">
                <h5>템플릿 코드</h5>
                <pre class="bg-light p-3 rounded"><code>&lt;p&gt;사용자명: {{userName}}&lt;/p&gt;
&lt;p&gt;나이: {{userAge}}세&lt;/p&gt;
&lt;p&gt;점수: {{userScore}}점&lt;/p&gt;</code></pre>
            </div>
        </div>
    </div>

    <div class="text-center mt-4">
        <a href="/mustache/html-escape" class="btn btn-primary">다음: HTML 이스케이프 학습</a>
    </div>
</div>
</body>
</html>
728x90