[Spring&AWS][4-1] 머스테치에 대해 알아보자

kiteB·2022년 3월 16일
0

Spring-AWS

목록 보기
5/13
post-thumbnail

이 글은 책 「스프링 부트와 AWS로 혼자 구현하는 웹 서비스」를 공부하고 정리한 글입니다.

이번 시간에는 머스테치를 이용하여 화면 영역을 개발하는 방법에 대해 알아보겠다.


[ 머스테치로 화면 구성하기 ]

1. 서버 템플릿 엔진과 머스테치 소개

✅ 템플릿 엔진이란?

지정된 템플릿 양식과 데이터가 합쳐져 HTML 문서를 출력하는 소프트웨어를 말한다.

JSP, Freemarker와 React, Vue는 모두 지정된 템플릿과 데이터를 이용하여 HTML을 생성하는 템플릿 엔진이다. 전자는 서버 템플릿 엔진이라고 불리며, 후자는 클라이언트 템플릿 엔진이라고 불린다.


✅ 서버 템플릿 엔진

서버 템플릿 엔진은 서버에서 구동된다.

  • 서버 템플릿 엔진을 이용한 화면 생성은 서버에서 Java 코드로 문자열을 만든 뒤 이 문자열을 HTML로 변환하여 브라우저로 전달한다.

✅ 클라이언트 템플릿 엔진

클라이언트 템플릿 엔진은 브라우저 위에서 작동한다.

  • 흔히 말하는 Vue.js나 React.js를 이용한 SPA(Single Page Application)는 브라우저에서 화면을 생성한다.
  • 즉, 서버에서 이미 코드가 벗어난 경우이다.
  • 그래서 서버에서는 Json 혹은 Xml 형식의 데이터만 전달하고 클라이언트에서 조립한다.

✅ 머스테치(Mustache)

머스테치는 수많은 언어를 지원하는 가장 심플한 템플릿 엔진이다. JSP와 같이 HTML을 만들어 주는 템플릿 엔진이다.

루비, 자바스크립트, 파이썬, PHP, 자바, 펄, Go, ASP 등 현존하는 대부분 언어를 지원하고 있다. 그러다 보니 자바에서 사용될 때는 서버 템플릿 엔진으로, 자바스크립트에서 사용될 때는 클라이언트 템플릿 엔진으로 모두 사용될 수 있다.


✅ 다른 템플릿 엔진과 머스테치 비교

자바 진영에서는 JSP, Velocity, Freemarker, Thymeleaf 등 다양한 서버 템플릿 엔진이 존재한다.

이 책의 저자가 생각했을 때의 템플릿 엔진들의 단점은 다음과 같다.

1. JSP, Velocity

  • 스프링 부트에서는 권장하지 않는 템플릿 엔진이다.

2. Freemarker

  • 템플릿 엔진으로는 너무 과하게 많은 기능을 지원한다.
  • 높은 자유도로 인해 숙련도가 낮을수록 Freemarker 안에 비즈니스 로직이 추가될 확률이 높다.

3. Thymeleaf

  • 스프링 진영에서 적극적으로 밀고 있지만 문법이 어렵다.
  • HTML 태그에 속성으로 템플릿 기능을 사용하는 방식이 기존 개발자에게 높은 허들로 느껴지는 경우가 많다.
  • Vue.js를 사용해 본 경험이 있어 태그 속성 방식이 익숙한 사람은 Thymeleaf를 선택해도 된다.

반면 머스테치의 장점은 다음과 같다.

4. 머스테치

  • 문법이 다른 템플릿 엔진보다 심플하다.
  • 로직 코드를 사용할 수 없어 View의 역할과 서버의 역할이 명확하게 분리된다.
  • Mustache.jsMustache.java 2가지가 다 있어, 하나의 문법으로 클라이언트/서버 템플릿 모두 사용가능하다.

템플릿 엔진이 너무 많은 기능을 제공하면
API와 템플릿 엔진, 자바스크립트가 서로 로직을 나눠 갖게 되어 유지보수하기가 어렵다.

✅ 머스테치 플러그인

머스테치의 또 다른 장점은 인텔리제이 커뮤니티 버전을 사용해도 플러그인을 사용할 수 있다는 점이다.

Thymeleaf나 JSP 등은 커뮤니티 버전에서 지원하지 않고 인텔리제이 얼티메이트 버전(유료)에서만 공식 지원한다. 이와 달리 머스테치는 커뮤니티 버전에서도 설치 가능한 플러그인이 있다.

이 플러그인을 사용하면 머스테치의 문법 체크, HTML 문법 지원, 자동완성 등이 지원되니 개발할 때 큰 도움을 받을 수 있다.
IntelliJ → Setting → Plugins 에서 머스테치가 설치되어 있지 않다면 설치해주자.


2. 기본 페이지 만들기

✅ 머스테치 사용 준비

스프링 부트 프로젝트에서 머스테치를 편하게 사용할 수 있도록 머스테치 스타터 의존성을 build.gradle에 등록하자.

implementation 'org.springframework.boot:spring-boot-starter-mustache'

머스테치는 스프링 부트에서 공식 지원하는 템플릿 엔진으로,
의존성 하나만 추가하면 다른 스타터 패키지와 마찬가지로 추가 설정 없이 설치를 완료할 수 있다.

✅ 머스테치의 파일 위치

머스테치의 파일 위치는 기본적으로 src/main/resources/templates이다.

이 위치에 머스테치 파일을 두면 스프링 부트에서 자동으로 로딩한다.

✅ index.mustache

{{>layout/header}}
<h1>스프링 부트로 시작하는 웹 서비스</h1>
<div class="col-md-12">
    <div class="row">
        <div class="col-md-6">
            <a href="/post/save" role="button" class="btn btn-primary">글 등록</a>
        </div>
    </div>
</div>
{{>layout/footer}} 

✅ IndexController.java

@Controller
public class IndexController {

    @GetMapping("/")
    public String index() {
        return "index";
    }
}

머스테치 스타터 덕분에 컨트롤러에서 문자열을 반환할 때 앞의 경로와 뒤의 파일 확장자는 자동으로 지정되어 자동으로
src/main/resources/templates/index.mustache로 매핑되어 View Resolver가 처리하게 된다.

📌 참고 | View Resolver란?

URL 요청의 결과를 전달할 타입과 값을 지정하는 관리자 격으로 볼 수 있다.


✅ IndexControllerTest.java

실제로 URL 호출 시 페이지의 내용이 제대로 호출되는지에 대한 테스트이다.

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = RANDOM_PORT)
public class IndexControllerTest {

    @Autowired
    private TestRestTemplate restTemplate;

    @Test
    public void 메인페이지_로딩() {
        //when
        String body = this.restTemplate.getForObject("/", String.class);

        //then
        assertThat(body).contains("스프링 부트로 시작하는 웹 서비스");
    }
}

TestRestTemplate를 통해 "/"로 호출했을 때 index.mustache에 포함된 코드들이 있는지 확인하면 된다. "스프링 부트로 시작하는 웹 서비스" 문자열이 포함되어 있는지만 비교하면 된다.

  • 테스트 실행 결과

  • localhost:8080 접속 결과


3. 게시글 등록 화면 만들기

그냥 HTML로만 개발하기에는 화면이 볼품 없으므로 오픈소스인 부트스트랩을 이용해서 화면을 만들어 보자!

✅ 부트스트랩

부트스트랩은 웹사이트를 쉽게 만들 수 있게 도와주는 HTML, CSS, JS 프레임워크이다.

부트스트랩, 제이쿼리 등 프론트엔드 라이브러리를 사용하는 방법은 2가지이다.

  • 외부 CDN 사용
  • 직접 라이브러리를 받아서 사용하는 방법

외부 CDN을 사용하는 방법이 간단하므로 이 방식을 사용해보도록 하겠다. 하지만 외부 서비스에 의존하게 되는 것이므로 실무에서는 이 방법을 잘 사용하지 않는다.


✅ 레이아웃 방식으로 추가

2개의 라이브러리 부트스트랩과 제이쿼리를 레이아웃 방식으로 index.mustache에 추가해보자. 레이아웃 방식이란 공통 영역을 별도의 파일로 분리하여 필요한 곳에서 가져다 쓰는 방식을 말한다.

이렇게 하면 매번 해당 라이브러리를 머스테치 파일에 추가하지 않아도 된다.

  • header.mustache
<!DOCTYPE HTML>
<html>
<head>
    <title>스프링부트 웹서비스</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />

    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
</head>
<body> 
  • footer.mustache
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"></script>

</body>
</html> 

코드를 보면 css와 js의 위치가 다른 것을 알 수 있다. 이는 페이지 로딩속도를 높이기 위한 것으로, css는 header에, js는 footer에 위치시켰다.

  • index.mustache
{{>layout/header}}
<h1>스프링 부트로 시작하는 웹 서비스</h1>
<div class="col-md-12">
    <div class="row">
        <div class="col-md-6">
            <a href="/post/save" role="button" class="btn btn-primary">글 등록</a>
        </div>
    </div>
</div>
{{>layout/footer}} 
  • {{> }}: 현재 머스테치 파일을 기준으로 다른 파일을 가져온다.

✅ IndexController

/post/save에 해당하는 컨트롤러를 생성해보자.

@Controller
public class IndexController {
    ...
    @GetMapping("/post/save")
    public String postSave() {
        return "post-save";
    }
}

✅ post-save.mustache

{{>layout/header}}

<h1>게시글 등록</h1>

<div class="col-md-12">
    <div class="col-md-4">
        <form>
            <div class="form-group">
                <label for="title">제목</label>
                <input type="text" class="form-control" id="title" placeholder="제목을 입력하세요">
            </div>
            <div class="form-group">
                <label for="author"> 작성자 </label>
                <input type="text" class="form-control" id="author" placeholder="작성자를 입력하세요">
            </div>
            <div class="form-group">
                <label for="content"> 내용 </label>
                <textarea class="form-control" id="content" placeholder="내용을 입력하세요"></textarea>
            </div>
        </form>
        <a href="/" role="button" class="btn btn-secondary">취소</a>
        <button type="button" class="btn btn-primary" id="btn-save">등록</button>
    </div>
</div>

{{>layout/footer}} 

✅ 게시글 등록 실행 결과

localhost:8080 접속 후 글 등록 버튼을 클릭한 후 등록을 하면 된다.

하지만 아직 API를 호출하는 JS가 없어서 게시글 등록 화면에 등록 버튼은 기능이 없어서 동작하지 않는다. JS를 추가해보자.

  • /static/js/app/index.js
var main = {
    init : function () {
        var _this = this;
        $('#btn-save').on('click', function () {
            _this.save();
        });
    },
    save : function () {
        var data = {
            title: $('#title').val(),
            author: $('#author').val(),
            content: $('#content').val()
        };

        $.ajax({
            type: 'POST',
            url: '/api/v1/post',
            dataType: 'json',
            contentType:'application/json; charset=utf-8',
            data: JSON.stringify(data)
        }).done(function() {
            alert('글이 등록되었습니다.');
            window.location.href = '/';
        }).fail(function (error) {
            alert(JSON.stringify(error));
        });
    }
};

main.init(); 
  • footer.mustache
...
<!--index.js 추가-->
<script src="/js/app/index.js"></script>
...
</html>

이제 등록 버튼을 클릭하면

위와 같은 alert가 노출되고,

이렇게 DB에 데이터가 추가된 것을 확인할 수 있다.


다음 시간에는 전체 조회 화면과 게시글 수정/삭제 기능을 추가해보겠다.

profile
🚧 https://coji.tistory.com/ 🏠

0개의 댓글