본문 바로가기
🎃🧸토이 프로젝트

PSYThinktank 프로젝트 mustache 도입기

by 캔 2024. 1. 7.

작년 말, 현재 운영 중인 사이드 프로젝트 중 하나인 PSYThinktank에 mustache를 도입했던 것을 회고해보려고 한다.

 

도입 이유

기존 프로젝트는 Java 1.8과 JSP를 사용하고 있었다. 하지만, 자바 최신 21 버전이 출시된 마당에 제일 오래된 LTS인 자바 8 버전은 너무 낡았다고 생각했다. 특히, 자바 11과 17을 거치면서 불변 컬렉션과 레코드 등 트렌드에 맞는 최신 문법을 사용할 수 없는 단점이 있었다. 그래서 자바 버전을 올리기로 결정하였다.

 

버전 상향을 결정하면서, 또 하나 결정해야 할 것이 있었다. 바로 패키징 방식을 war로 할 것이냐, jar로 할 것이냐였다. 기존에 외장 톰캣을 사용하고 있었기에 war로 패키징 후 톰캣 디렉터리에 파일을 추가해 주는 방식으로 배포하고 있었다. 하지만, jar로 패키징 할 경우 서버에 톰캣이 존재하지 않아도 jar 파일만 실행하면 내장 톰캣으로 서버를 실행할 수 있었다. 이런 방식으로 하면 AWS 인스턴스 생성 시에 별도로 WAS를 설치하지 않아도 되므로 관리 포인트가 줄어든다는 장점이 있다. 그래서 jar로 패키징하려고 마음먹었다.

 

그러나 jar로 패키징할 경우, JSP를 사용할 수 없다는 문제가 발생한다. 어떤 블로그에서는 JSP 경로를 변경하면 우회적으로 사용할 수 있는 방법이 있다고 하는데, 스프링 부트 공식 문서상으로는 사용 불가하다.

JSP Limitations

When running a Spring Boot application that uses an embedded servlet container (and is packaged as an executable archive), there are some limitations in the JSP support.
* With Jetty and Tomcat, it should work if you use war packaging. An executable war will work when launched with java -jar, and will also be deployable to any standard container. JSPs are not supported when using an executable jar.
* Undertow does not support JSPs. Creating a custom error.jsp page does not override the default view for error handling. Custom error pages should be used instead.

-- 이하 번역 --

JSP 한계
내장 서블릿 컨테이너를 사용하고 실행가능한 압축 파일로 패키징된 스프링 부트 애플리케이션 실행 시 JSP 지원에 한계점이 있습니다.
* 제티나 톰캣의 경우, war 패키징 사용 시 작동할 것입니다. 실행가능한 war는 java -jar로 시작했을 때 동작할 것이고 모든 표준 컨테이너에서 배포가 가능할 것입니다. JSP는 실행가능한 jar를 사용하는 경우에는 지원되지 않습니다.
* 언더토우는 JSP를 지원하지 않습니다. 커스텀 error.jsp 페이지는 에러 처리를 위한 기본 뷰를 오버라이드하지 않습니다. 커스텀 에러 페이지를 사용하여야 합니다.

https://docs.spring.io/spring-boot/docs/3.2.x/reference/htmlsingle/#web.servlet.embedded-container.jsp-limitations

 

Spring Boot Reference Documentation

This section goes into more detail about how you should use Spring Boot. It covers topics such as build systems, auto-configuration, and how to run your applications. We also cover some Spring Boot best practices. Although there is nothing particularly spe

docs.spring.io

 

결국 템플릿 엔진을 변경해야 했다. 선택지는 스프링 부트가 자동 설정을 지원하는 thymeleaf, freemarker, mustache, 세 가지였다.

 

thymeleaf와 freemarker를 선택하지 않은 이유 중 하나는 조건문이나 반복문 등 로직을 지원하기 때문에 JSP와 문법이 다를 뿐 사실상 다른 게 없다는 것이다. 반면에 mustache의 경우에는 공식 문서에서도 언급돼 있듯이 로직이 없는(logic-less) 템플릿 엔진이다. 스프링 웹 MVC를 사용하면서도 뷰에서 로직을 허용하는 것은 바람직하지 않다. 특히, MVC에 대한 이해가 떨어지는 개발자들에게는 뷰에서 로직을 작성할 수 있도록 가능성을 열어주게 된다. 실제로 나도 기존 프로젝트에서 JSP에 로직을 넣고 있었다.

 

PSYThinktank 프로젝트처럼 프론트엔드와 백엔드엔드 개발이 완전히 분리되지 않는 상황에서 뷰를 생성해야 하면 템플릿 엔진을 사용해야 한다. 하지만, 모델과 컨트롤러로부터 받은 데이터를 뷰에서 가공하여 표시하는 것은 MVC 패턴에서 각각의 책임을 분리하려는 취지와는 맞지 않다. 그렇기에 애초부터 뷰에 로직을 넣기 어려운 구조를 강제하는 mustache가 마음에 들었다.

 

코드

이번에는 실제 코드를 한번 살펴보자. 위가 JSP를 사용한 기존 코드이고 아래가 mustache를 사용한 새로운 코드이다.

<tr>
    <td class="table-light text-center">지분 변동</td>
    <td>
        <table class="table">
            <c:forEach var="i" items="${share}" varStatus="svs">
                <tr>
                    <td class="table-light text-center"><c:if test="${i.date != temp}">${i.date}<c:set var="currentShare" value="${i.share}"/></c:if></td>
                    <td>
                        ${i.holderName}
                    </td>
                    <td>${i.share}</td>
                </tr>
                <c:set var="temp" value="${i.date}"/>
            </c:forEach>						
        </table>
    </td>
</tr>
<tr>
    <td class="table-light text-center">지분 변동</td>
    <td>
        <div>
            <canvas id="myChart"></canvas>
        </div>
        <table class="table">
            {{#share.result}}
                <tr>
                    <td class="table-light text-center">
                        {{#date}}
                        	{{.}}
                        {{/date}}
                    </td>
                    <td>
                        {{holderName}}
                    </td>
                    <td>
                        {{value}}
                    </td>
                </tr>
            {{/share.result}}
        </table>
    </td>
</tr>

기존 코드에서는 <c:foreach> 태그를 통해 리스트를 출력하고 있다. 이때, <c:if> 태그와 <c:set> 태그를 사용해서 날짜를 변수에 저장하고 저장한 변수가 다음 날짜와 비교했을 때 다르면 날짜를 출력한다. 즉, 다시 말해 목록을 출력할 때 같은 날짜 중에 첫 번째 항목에만 날짜를 표시하고 그다음 항목들은 표시하지 않는다. 컨트롤러에서 전달한 데이터를 뷰에 와서 한 번 더 가공하고 있는 것이다. 뷰와 로직이 공존하고 있는 것이다. 이런 데이터 처리는 컨트롤러에서 처리한 후에 뷰에 전달해 주면 될 일이다. 아래 코드에서는 mustache를 사용함으로써 로직이 완전히 제거된 모습이다. JSP를 사용했을 때보다 훨씬 간결하고 깔끔해졌다.

 

뷰와 로직의 분리를 강제해 준다는 점에서 mustache에 대한 선호가 한층 높아졌다. 앞으로의 프로젝트에서도 템플릿 엔진 사용 시 mustache를 사용하고, 협업 시에도 mustache 사용을 제안하고 싶다.

'🎃🧸토이 프로젝트' 카테고리의 다른 글

자바 변수 자동 추가  (0) 2021.06.22