[Gotenberg] Gotenberg 사용하여 PDF 생성
들어가기 전
이번 포스팅에서는 Gotenberg를 사용하여 URL, HTML, Markdown을 PDF로 만드는 방법에 대해서 알아보겠습니다.
Gotenberg 공식문서는 아래와 같습니다.
공식문서에 다양한 방법으로 Gotenberg를 이용할 수 있는 방법을 제공합니다.
이번 포스팅에서는 도커를 활용하여 Gotenberg 설치 및 크롬 모듈을 사용하여 PDF 변환하는 방법에 대해서 알아볼 예정입니다.
A Docker-based API for PDF conversion | Gotenberg
A Docker-based API for PDF conversion
gotenberg.dev
Gotenberg를 접하게 된 계기
업무를 진행하면서 처음에는 HTML 파일을 PDF로 변환하기 위해 OpenHTMLtoPDF 라이브러리를 사용했습니다.
초기에는 간단하게 적용할 수 있어 문제없이 사용했지만, 데이터가 많아지면서 점차 문제가 발생하기 시작했습니다.
해당 라이브러리는 HTML을 파싱 하여 DOM 트리를 생성하고, CSS를 적용한 스타일 트리를 구성하는데 이 과정에서 문서 전체를 한 번에 메모리에 올리는 구조입니다.
여기에 폰트(특히 한글 폰트)까지 함께 로드되면서 애플리케이션의 메모리 사용량이 크게 증가했습니다.
이 문제를 해결하기 위해 자료를 찾아보던 중 Gotenberg를 알게 되었고 애플리케이션에서는 PDF 생성을 위한 HTML만 구성한 뒤 해당 HTML을 Gotenberg로 전달하여 PDF를 생성하도록 구조를 변경했습니다.
구조를 변경한 이후 애플리케이션의 메모리를 많이 사용하는 문제가 해결되었습니다.
결과적으로 메모리를 많이 사용하는 작업을 외부로 분리함으로써 메모리 부족 및 서버 다운 현상을 방지할 수 있었습니다.
지금까지 Gotenberg를 접하게 된 계기에 대해서 알아보았습니다.
이제 Gotenberg가 무엇이고 URL, HTML, Markdown 파일을 PDF로 변환하는 방법에 대해서 알아보겠습니다.
Gotenberg란?
Gotenberg는 문서를 Pdf로 변환해 주는 도커 기반 API입니다.
파일을 전송하면 PDF로 결과를 받아볼 수 있고 Chromium, LibreOffic, 폰트를 직접 관리할 필요가 없습니다.
도커로 Gotenberg 컨테이너 설정하는 방법은 아래와 같습니다.
services:
gotenberg:
image: gotenberg/gotenberg:8
ports:
- "3000:3000"
- 기본 포트: 3000
도커 이미지에 따라 지원하는 기능도 달라집니다.
- gotenberg:8 : Chromium, LibreOffic 및 PDF 엔진 지원
- gotenberg:8-chromium : Chromium 및 PDF 엔진 지원, 오피스 문서 변환 기능 지원 X
- gotenberg:8-libreoffice : LibreOffic 및 PDF 엔진 지원, URL/HTML/Markdown 변환이나 스크린샷 지원 X
이미지 별로 크기가 다르므로 필요한 기능에 맞게 도커 이미지를 선택하시는 것을 추천드리겠습니다.
(이번 포스팅에서는 크롬 모듈을 통해 PDF로 만드는 예제를 진행할 예정입니다. LibreOffic 방식이 궁금하신 분은 위에 언급한 공식문서 보시는 것을 추천드리겠습니다.)
Chromium을 사용하여 PDF 만들기
Chromium 모듈을 이용해서 아래 유형을 PDF로 만드는 방법에 대해서 예제를 통해 알아보겠습니다.
- URL -> PDF
- HTML -> PDF
- Markdown -> PDF
URL을 통해 PDF 생성
URL을 통해서 PDF로 만드는 gotenberg API는 아래와 같습니다.
POST http://{gotenberg Host}:{gotenberg Port}/forms/chromium/convert/url
gotenberg에서 지원해 주는 API를 호출하여 URL 경로에 나오는 화면을 PDF로 만들 수 있습니다.
아래 예제는 URL을 PDF로 만드는 Gotenberg API를 호출하여 PDF로 변환하는 예제입니다.
@RestController
@RequestMapping("/gotenberg/url-to-pdf")
public class UrlToPdfExController {
private final UrlToPdfExService urlToPdfExService;
public UrlToPdfExController(UrlToPdfExService urlToPdfExService) {
this.urlToPdfExService = urlToPdfExService;
}
@GetMapping
public ResponseEntity<Resource> getUrlToPdf(String pdfUrl) {
return
ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=gotenberg_Url_To_Pdf.pdf")
.contentType(MediaType.APPLICATION_PDF)
.body(urlToPdfExService.getUrlToPdf(pdfUrl));
}
}
=====================================================================
@Service
public class UrlToPdfExService {
private static final String GOTENBERG_URL = "http://localhost:3000/forms/chromium/convert/url";
public Resource getUrlToPdf(String pdfUrl) {
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
body.add("url", pdfUrl);
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
HttpEntity<MultiValueMap<String, Object>> requestEntity =
new HttpEntity<>(body, headers);
ResponseEntity<Resource> postedForEntity = restTemplate.postForEntity(GOTENBERG_URL,
requestEntity,
Resource.class);
return postedForEntity
.getBody();
}
}
위 코드 기반으로 애플리케이션을 실행시키고 Postman에서 API를 호출해서 파일 다운로드하면 아래와 같이 URL에 접속했을 때 노출되는 화면을 PDF로 만들 수 있습니다.

HTML을 통해 PDF 생성
HTML을 통해서 PDF로 만드는 gotenberg API는 아래와 같습니다.
POST http://{gotenberg Host}:{gotenberg Port}/forms/chromium/convert/html
gotenberg에서 지원해 주는 API를 호출하여 Html 파일을 PDF로 만들 수 있습니다.
아래 예제는 Html 파일을 PDF로 만드는 Gotenberg API를 호출하여 PDF로 변환하는 예제입니다.
@RestController
@RequestMapping("/gotenberg/html-to-pdf")
public class HtmlToPdfExController {
private final HtmlToPdfExService htmlToPdfExService;
public HtmlToPdfExController(HtmlToPdfExService htmlToPdfExService) {
this.htmlToPdfExService = htmlToPdfExService;
}
@GetMapping
public ResponseEntity<Resource> getHtmlToPdf() throws IOException {
return
ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=gotenberg_Html_To_Pdf.pdf")
.contentType(MediaType.APPLICATION_PDF)
.body(htmlToPdfExService.getHtmlToPdf());
}
}
========================================================================
@Service
public class HtmlToPdfExService {
private static final String GOTENBERG_URL = "http://localhost:3000/forms/chromium/convert/html";
public Resource getHtmlToPdf() throws IOException {
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
ClassPathResource resource =
new ClassPathResource("Html 파일 경로");
File tempFile = File.createTempFile("temp", ".html");
try (InputStream in = resource.getInputStream();
OutputStream out = new FileOutputStream(tempFile)) {
in.transferTo(out);
}
body.add("files", new FileSystemResource(tempFile) {
@Override
public String getFilename() {
return "index.html";
}
});
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
HttpEntity<MultiValueMap<String, Object>> requestEntity =
new HttpEntity<>(body, headers);
ResponseEntity<Resource> postedForEntity = restTemplate.postForEntity(GOTENBERG_URL,
requestEntity,
Resource.class);
return postedForEntity
.getBody();
}
}
위 코드 기반으로 애플리케이션을 실행시키고 Postman에서 API를 호출해서 파일 다운로드하면 아래와 같이 HTMl 파일을 PDF로 만들 수 있습니다.

주의할 점

위 이미지를 보면 gotenberg API를 이용해서 Html을 PDF로 변환 요청을 보낼 때 index.html 파일명으로 요청을 보내야 합니다.
그렇지 않으면 아래와 같은 오류가 발생하면서 원하는 결과를 얻을 수 없습니다.
org.springframework.web.client.HttpClientErrorException$BadRequest:
400 Bad Request on POST request for "http://localhost:3000/forms/chromium/convert/html": "Invalid form data: form file 'index.html' is required"
Markdown을 통해 PDF 생성
HTML을 통해서 PDF로 만드는 gotenberg API는 아래와 같습니다.
POST http://{gotenberg Host}:{gotenberg Port}/forms/chromium/convert/markdown
gotenberg에서 지원해 주는 API를 호출하여 Markdown 파일을 PDF로 만들 수 있습니다.
아래 예제는 Markdown 파일을 PDF로 만드는 Gotenberg API를 호출하여 PDF로 변환하는 예제입니다.
@RestController
@RequestMapping("/gotenberg/markdown-to-pdf")
public class MarkdownToPdfExController {
private final MarkdownToPdfExService markdownToPdfExService;
public MarkdownToPdfExController(MarkdownToPdfExService markdownToPdfExService) {
this.markdownToPdfExService = markdownToPdfExService;
}
@GetMapping
public ResponseEntity<Resource> getMarkdwonToPdf() throws IOException {
return
ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=gotenberg_Markdown_To_Pdf.pdf")
.contentType(MediaType.APPLICATION_PDF)
.body(markdownToPdfExService.getMarkdownToPdf());
}
}
========================================================================
@Service
public class MarkdownToPdfExService {
private static final String GOTENBERG_URL = "http://localhost:3000/forms/chromium/convert/markdown";
public Resource getMarkdownToPdf() throws IOException {
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
ClassPathResource resource =
new ClassPathResource("Html 파일 경로");
File tempFile = File.createTempFile("temp", ".html");
try (InputStream in = resource.getInputStream();
OutputStream out = new FileOutputStream(tempFile)) {
in.transferTo(out);
}
body.add("files", new FileSystemResource(tempFile) {
@Override
public String getFilename() {
return "index.html";
}
});
body.add("files", new ClassPathResource("Markdown 파일 경로"));
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
HttpEntity<MultiValueMap<String, Object>> requestEntity =
new HttpEntity<>(body, headers);
ResponseEntity<Resource> postedForEntity = restTemplate.postForEntity(GOTENBERG_URL,
requestEntity,
Resource.class);
return postedForEntity
.getBody();
}
}
위 코드와 같이 "files" 변수명으로 Html 파일과 Markdown파일을 Body에 담아서 gotenberg API 호출합니다.
그리고 적용할 Markdown을 아래와 같이 Html 파일에 Markdown 파일명을 작성해 주면 됩니다.
여기서 {{ toHTML }} 이 부분은 gotenberg가 인식하는 것이기 때문에 저 부분은 그대로 두고 Markdown 파일명만 변경해 주면 이미지와 같이 Markdown을 PDF로 변환할 수 있습니다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{{ toHTML "Markdown 파일명" }}
</body>
</html>

주의할 점
Html을 PDF로 만드는 거처럼 HTML 파일명은 index.html로 gotenberg에 요청을 해야 합니다.
추가적으로 아래 이미지를 보면 Markdown을 PDF로 변환을 할 때 파일명은 아무거나 해도 되지만 Markdown 파일은 무조건 하나 이상 존재해야 합니다.

마무리
이번 포스팅에서는 Gotenberg를 통해서 URL, HTML, Markdown 파일을 PDF로 만드는 방법에 대해서 알아보았습니다.
상세한 설정값을 공식문서를 통해서 확인하여 상황에 맞게 적용하시는것을 추천드리겠습니다.
포스팅에서 사용된 예제 코드가 있는 깃허브 링크는 아래에 있습니다.