이번에 회사에서 받은 데이터를 가지고 PDF를 만들 일이 있어서 Thymeleaf
를 사용하여 적용해 본 방법을 기록하고자 합니다.
build.gradle
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation("org.springframework.boot:spring-boot-starter-thymeleaf")
implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect'
implementation('org.xhtmlrenderer:flying-saucer-pdf-openpdf:9.1.22')
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
}
@Component
@RequiredArgsConstructor
public class TemplateParser {
public String parseHtmlFileToString(String templateName, Map<String, Object> variables) {
// Thymeleaf Resolver 설정
var templateResolver = new ClassLoaderTemplateResolver();
templateResolver.setPrefix("templates/");
templateResolver.setSuffix(".html");
templateResolver.setTemplateMode(TemplateMode.HTML);
// Spring Template Engine으로 위에서 설정한 Thymeleaf Resolver를 사용하도록 설정
var templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(templateResolver);
// Template Engine에서 사용할 변수
var context = new Context();
context.setVariables(variables);
// 렌더링 된 값을 String으로 반환
return templateEngine.process(templateName, context);
}
}
interface TemplateParser
를 만들어 Template Engine
마다 상속받아 구현하는 것이 더 좋은 설계이지만, Spring Boot
를 이용하면 설정파일로 Template Engine
설정할 수 있습니다. Spring Boot
는 기본적으로 Thymeleaf
가 설치 되어 있다면 Thymeleaf
설정을 해줍니다.
즉, 위에서 SpringTemplateEngine
을 의존주입 받아 사용하면 Template Engine
설정하는 부분 생략할 수 있습니다.
@Component
@RequiredArgsConstructor
public class TemplateParser {
private final SpringTemplateEngine templateEngine;
public String parseHtmlFileToString(String templateName, Map<String, Object> variables) {
var context = new Context();
context.setVariables(variables);
return templateEngine.process(templateName, context);
}
}
layout
기능을 사용하기 위해선 org.springframework.boot:spring-boot-starter-thymeleaf
외에 nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect
도 필요합니다.
layout/document.html
<!DOCTYPE html>
<html
lang="ko"
xmlns:th="<http://www.thymeleaf.org>"
xmlns:layout="<http://www.ultraq.net.nz/thymeleaf/layout>"
>
<head>
<meta charset="UTF-8" />
<meta
name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"
/>
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<style th:replace="pdf/approval/document/partials/styles :: commonStyle"></style>
<title th:text="${title}"></title>
</head>
<body>
<div id="main-pdf-container">
<div th:replace="partials/header :: commonHeader"></div>
<div th:replace="partials/commonField :: commonField"></div>
<div layout:fragment="content"></div>
<div th:replace="partials/attachedFiles :: commonApprovalAttachedFiles"></div>
</div>
</body>
</html>
저는 공통적으로 사용 될 layout
용 html
을 하나 만들어서 사용했습니다.
partials/header
부분은 templates
폴더 하위 부터 해당 파일의 절대경로
를 나타내며 ::
이후 commonHeader
는 해당 파일 내에서 th:fragment
로 선언된 이름입니다.
partials/header.html
<!DOCTYPE html>
<html lang="ko" xmlns:th="<http://www.thymeleaf.org>">
<div th:fragment="commonHeader">...</div>
</html>