스프링부트에서 Playwright Java를 사용해 정적 HTML을 PDF로 자동 변환하는 방법과 AlmaLinux 9 서버에서 500 에러가 발생하는 원인, 필요한 리눅스 패키지 설치 방법을 실무 기준으로 정리합니다.

스프링부트 Playwright로 HTML을 PDF로 변환할 때 500 에러 해결 방법
스프링부트에서 Playwright를 사용해 정적 HTML을 PDF로 자동 변환하려고 하면 로컬에서는 정상 동작하지만, AlmaLinux 9 같은 리눅스 서버에 배포한 뒤 500 에러가 발생하는 경우가 있습니다. 이 글에서는 pom.xml에 Playwright 의존성을 추가하고 HTML을 PDF로 변환하는 기본 구조부터, 서버에서 500 에러가 발생하는 원인과 해결 방법까지 실무 기준으로 정리합니다.
사용 환경
이번 예시는 다음 환경을 기준으로 설명합니다.
| 프레임워크 | Spring Boot |
| 빌드 도구 | Maven |
| 설정 파일 | pom.xml |
| PDF 변환 라이브러리 | Microsoft Playwright Java |
| 서버 OS | AlmaLinux 9 |
| 주요 문제 | HTML PDF 변환 시 500 Internal Server Error 발생 |
pom.xml에 Playwright 의존성 추가
스프링부트에서 Playwright를 사용하려면 pom.xml에 다음 의존성을 추가합니다.
<dependency>
<groupId>com.microsoft.playwright</groupId>
<artifactId>playwright</artifactId>
<version>1.46.0</version>
</dependency>
Playwright는 브라우저를 실제로 실행해서 페이지를 렌더링한 뒤 PDF를 생성합니다. 단순히 HTML 문자열을 PDF로 바꾸는 라이브러리와 다르게, CSS, 폰트, 이미지, 레이아웃 렌더링 결과를 브라우저 기준으로 반영할 수 있다는 장점이 있습니다.
즉, 정적 HTML로 작성한 견적서, 계약서, 보고서, 신청서 같은 문서를 PDF로 자동 생성하기에 적합합니다.
스프링부트에서 HTML을 PDF로 변환하는 기본 예제
예를 들어 src/main/resources/static/sample.html 파일을 PDF로 변환한다고 가정해보겠습니다.
import com.microsoft.playwright.Browser;
import com.microsoft.playwright.BrowserType;
import com.microsoft.playwright.Page;
import com.microsoft.playwright.Playwright;
import org.springframework.stereotype.Service;
import java.nio.file.Paths;
@Service
public class PdfService {
public byte[] convertHtmlToPdf(String htmlFileUrl) {
try (Playwright playwright = Playwright.create()) {
Browser browser = playwright.chromium().launch(
new BrowserType.LaunchOptions()
.setHeadless(true)
);
Page page = browser.newPage();
page.navigate(htmlFileUrl);
byte[] pdf = page.pdf(new Page.PdfOptions()
.setFormat("A4")
.setPrintBackground(true)
);
browser.close();
return pdf;
}
}
}
컨트롤러에서는 다음처럼 PDF 파일을 응답할 수 있습니다.
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RequiredArgsConstructor
@RestController
public class PdfController {
private final PdfService pdfService;
@GetMapping("/pdf")
public ResponseEntity<byte[]> downloadPdf() {
byte[] pdf = pdfService.convertHtmlToPdf("http://localhost:8080/sample.html");
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=sample.pdf")
.contentType(MediaType.APPLICATION_PDF)
.body(pdf);
}
}
로컬에서는 되는데 서버에서 500 에러가 발생하는 이유
Playwright는 내부적으로 Chromium 브라우저를 실행합니다. 문제는 리눅스 서버 환경에서 Chromium이 실행되기 위해 필요한 시스템 라이브러리가 없을 수 있다는 점입니다.
개발 PC에서는 브라우저 실행에 필요한 라이브러리들이 이미 설치되어 있는 경우가 많습니다. 하지만 AlmaLinux 9 같은 서버 OS는 최소 설치 환경으로 구성되는 경우가 많기 때문에 GUI 관련 라이브러리, 그래픽 렌더링 라이브러리, 입력 장치 관련 라이브러리 등이 빠져 있을 수 있습니다.
이 상태에서 스프링부트 애플리케이션이 Playwright를 통해 Chromium을 실행하려고 하면 내부적으로 브라우저 실행에 실패하고, 컨트롤러에서는 결과적으로 500 Internal Server Error가 발생합니다.
대표적인 오류 흐름
실무에서는 보통 다음과 같은 형태로 문제가 나타납니다.
HTTP Status 500 – Internal Server Error
또는 애플리케이션 로그에서 다음과 유사한 내용을 볼 수 있습니다.
com.microsoft.playwright.PlaywrightException
BrowserType.launch: Failed to launch chromium
상황에 따라 다음과 같은 라이브러리 누락 메시지가 함께 표시될 수 있습니다.
error while loading shared libraries: libatk-1.0.so.0
error while loading shared libraries: libxkbcommon.so.0
error while loading shared libraries: libgbm.so.1
핵심은 스프링부트 코드 문제가 아니라, 서버에서 Chromium 실행에 필요한 OS 패키지가 부족하다는 점입니다.
AlmaLinux 9에서 필요한 패키지 설치
AlmaLinux 9에서 다음 패키지들을 설치하면 Playwright PDF 변환 시 발생하던 500 에러를 해결할 수 있습니다.
sudo dnf install -y \
atk \
at-spi2-atk \
at-spi2-core \
libdrm \
libxkbcommon \
libXdamage \
libXfixes \
libXrandr \
mesa-libgbm
한 줄로 실행하려면 다음처럼 입력해도 됩니다.
sudo dnf install -y atk at-spi2-atk at-spi2-core libdrm libxkbcommon libXdamage libXfixes libXrandr mesa-libgbm
설치 후에는 스프링부트 애플리케이션을 재시작해야 합니다.
sudo systemctl restart my-spring-app
직접 실행 중이라면 프로세스를 종료한 뒤 다시 실행합니다.
java -jar app.jar
설치한 패키지들이 필요한 이유
각 패키지는 Chromium 실행에 필요한 런타임 라이브러리와 관련이 있습니다.
| atk | 접근성 관련 GTK 라이브러리 |
| at-spi2-atk | ATK와 AT-SPI 연결 라이브러리 |
| at-spi2-core | 접근성 서비스 관련 코어 라이브러리 |
| libdrm | Direct Rendering Manager 관련 그래픽 라이브러리 |
| libxkbcommon | 키보드 입력 처리 관련 라이브러리 |
| libXdamage | X11 화면 손상 영역 처리 라이브러리 |
| libXfixes | X11 확장 기능 라이브러리 |
| libXrandr | 화면 크기, 회전 관련 X11 라이브러리 |
| mesa-libgbm | 그래픽 버퍼 관리 라이브러리 |
Playwright를 서버에서 사용할 때는 실제 화면이 없어도 Chromium이 내부적으로 렌더링 엔진을 실행합니다. 따라서 headless 모드라고 해도 브라우저 실행에 필요한 시스템 라이브러리는 필요합니다.
Playwright 브라우저 설치 여부도 확인하기
Playwright 의존성만 추가했다고 해서 서버에 Chromium 실행 파일이 항상 준비되는 것은 아닙니다. 배포 환경에서는 Playwright 브라우저 설치도 확인해야 합니다.
Maven 프로젝트에서는 다음 명령을 사용할 수 있습니다.
mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="install chromium"
전체 브라우저를 설치하려면 다음처럼 실행할 수 있습니다.
mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="install"
서버에서는 보통 PDF 변환만 목적이므로 Chromium만 설치해도 충분한 경우가 많습니다.
mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="install chromium"
서버 배포 시 권장 설정
리눅스 서버에서 Playwright를 사용할 때는 Chromium 실행 옵션을 명시적으로 지정하는 것이 좋습니다.
Browser browser = playwright.chromium().launch(
new BrowserType.LaunchOptions()
.setHeadless(true)
.setArgs(List.of(
"--no-sandbox",
"--disable-dev-shm-usage"
))
);
전체 코드는 다음과 같습니다.
import com.microsoft.playwright.Browser;
import com.microsoft.playwright.BrowserType;
import com.microsoft.playwright.Page;
import com.microsoft.playwright.Playwright;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class PdfService {
public byte[] convertHtmlToPdf(String htmlFileUrl) {
try (Playwright playwright = Playwright.create()) {
Browser browser = playwright.chromium().launch(
new BrowserType.LaunchOptions()
.setHeadless(true)
.setArgs(List.of(
"--no-sandbox",
"--disable-dev-shm-usage"
))
);
Page page = browser.newPage();
page.navigate(htmlFileUrl, new Page.NavigateOptions()
.setWaitUntil(com.microsoft.playwright.options.WaitUntilState.NETWORKIDLE)
);
byte[] pdf = page.pdf(new Page.PdfOptions()
.setFormat("A4")
.setPrintBackground(true)
);
browser.close();
return pdf;
}
}
}
--no-sandbox 옵션이 필요한 경우
일부 리눅스 서버나 Docker 환경에서는 Chromium sandbox 기능 때문에 브라우저 실행이 실패할 수 있습니다. 이때 --no-sandbox 옵션을 사용하면 실행 문제가 해결되는 경우가 있습니다.
.setArgs(List.of("--no-sandbox"))
다만 보안상 sandbox를 끄는 옵션이므로, 운영 환경에서는 서버 접근 권한과 실행 계정을 제한하는 것이 좋습니다. 가능하면 Playwright 전용 계정을 만들고 해당 계정으로만 PDF 변환 프로세스를 실행하는 방식이 안전합니다.
정적 HTML 파일을 PDF로 만들 때 주의할 점
정적 HTML을 PDF로 변환할 때는 HTML 파일 자체보다 CSS, 이미지, 폰트 경로 문제 때문에 PDF가 깨지는 경우가 많습니다.
예를 들어 다음과 같은 상대 경로를 사용하면 서버 실행 위치에 따라 리소스를 찾지 못할 수 있습니다.
<link rel="stylesheet" href="./style.css">
<img src="./logo.png">
스프링부트의 static 경로에 리소스를 둔다면 다음처럼 HTTP 경로 기준으로 접근하는 것이 안정적입니다.
<link rel="stylesheet" href="/css/pdf.css">
<img src="/images/logo.png">
Playwright에서 접근하는 URL도 가능하면 다음처럼 명확하게 지정합니다.
page.navigate("http://localhost:8080/sample.html");
PDF 출력용 CSS 예제
PDF 변환용 HTML은 화면용 CSS와 다르게 인쇄 기준 스타일을 따로 작성하는 것이 좋습니다.
@page {
size: A4;
margin: 20mm;
}
body {
font-family: Arial, sans-serif;
font-size: 14px;
color: #222;
}
h1 {
font-size: 24px;
margin-bottom: 20px;
}
.table {
width: 100%;
border-collapse: collapse;
}
.table th,
.table td {
border: 1px solid #ccc;
padding: 8px;
}
.table th {
background-color: #f5f5f5;
}
.page-break {
page-break-before: always;
}
HTML에서는 다음처럼 사용할 수 있습니다.
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>PDF 샘플</title>
<link rel="stylesheet" href="/css/pdf.css">
</head>
<body>
<h1>PDF 변환 테스트</h1>
<table class="table">
<thead>
<tr>
<th>항목</th>
<th>내용</th>
</tr>
</thead>
<tbody>
<tr>
<td>프레임워크</td>
<td>Spring Boot</td>
</tr>
<tr>
<td>PDF 변환</td>
<td>Playwright</td>
</tr>
</tbody>
</table>
</body>
</html>
PDF 한글 폰트 깨짐 문제
PDF에 한글이 포함되어 있다면 서버에 한글 폰트가 설치되어 있어야 합니다. 한글 폰트가 없으면 PDF에서 글자가 깨지거나 네모 박스로 표시될 수 있습니다.
AlmaLinux 계열에서는 나눔 폰트나 Noto CJK 폰트를 설치해서 해결할 수 있습니다.
sudo dnf install -y google-noto-sans-cjk-fonts
설치 후 폰트 캐시를 갱신합니다.
sudo fc-cache -fv
CSS에서는 다음처럼 폰트를 지정합니다.
body {
font-family: "Noto Sans CJK KR", "Noto Sans KR", sans-serif;
}
실무에서 확인해야 할 체크리스트
| pom.xml 의존성 | Playwright Java 의존성 추가 여부 |
| 브라우저 설치 | playwright install chromium 실행 여부 |
| 서버 라이브러리 | Chromium 실행에 필요한 OS 패키지 설치 여부 |
| headless 설정 | .setHeadless(true) 적용 여부 |
| sandbox 옵션 | 서버 환경에 따라 --no-sandbox 필요 여부 |
| HTML 접근 경로 | localhost 또는 내부 URL로 접근 가능한지 |
| CSS, 이미지 경로 | 상대 경로 문제 없는지 |
| 한글 폰트 | 서버에 한글 폰트 설치 여부 |
| 로그 확인 | 500 에러의 실제 원인이 브라우저 실행 실패인지 확인 |
500 에러 해결 순서 정리
스프링부트에서 Playwright PDF 변환 중 500 에러가 발생한다면 다음 순서로 확인하는 것이 좋습니다.
1. 애플리케이션 로그 확인
먼저 500 에러만 보지 말고 서버 로그를 확인합니다.
journalctl -u my-spring-app -f
또는 직접 실행 중이면 콘솔 로그를 확인합니다.
java -jar app.jar
2. Chromium 실행 실패 여부 확인
로그에 다음과 같은 내용이 있다면 OS 라이브러리 부족 가능성이 높습니다.
BrowserType.launch: Failed to launch chromium
3. AlmaLinux 9 패키지 설치
다음 명령을 실행합니다.
sudo dnf install -y atk at-spi2-atk at-spi2-core libdrm libxkbcommon libXdamage libXfixes libXrandr mesa-libgbm
4. Playwright 브라우저 설치
mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="install chromium"
5. 애플리케이션 재시작
sudo systemctl restart my-spring-app
FAQ
Q. Playwright 의존성만 추가하면 PDF 변환이 바로 되나요?
로컬 개발 환경에서는 바로 될 수 있지만, 운영 서버에서는 Chromium 실행에 필요한 OS 라이브러리와 브라우저 설치가 필요할 수 있습니다. 특히 AlmaLinux 9처럼 최소 설치된 서버에서는 추가 패키지 설치가 필요한 경우가 많습니다.
Q. 왜 500 에러가 발생하나요?
스프링부트 컨트롤러에서 PDF 변환 요청을 처리하는 중 Playwright가 Chromium 실행에 실패하면 예외가 발생합니다. 이 예외가 처리되지 않으면 클라이언트에는 500 Internal Server Error로 응답됩니다.
Q. headless 모드인데도 그래픽 관련 라이브러리가 필요한가요?
필요합니다. headless 모드는 화면을 직접 띄우지 않을 뿐, 브라우저 렌더링 엔진은 내부적으로 그래픽, 폰트, 레이아웃 관련 라이브러리를 사용합니다.
Q. HTML은 열리는데 PDF만 실패할 수 있나요?
가능합니다. HTML 페이지 자체는 스프링부트에서 정상 제공되더라도, Playwright가 서버 내부에서 Chromium을 실행하지 못하면 PDF 생성은 실패합니다.
Q. 한글 PDF가 깨질 때는 어떻게 해야 하나요?
서버에 한글 폰트를 설치해야 합니다.
sudo dnf install -y google-noto-sans-cjk-fonts
sudo fc-cache -fv
그리고 CSS에서 한글 폰트를 지정합니다.
body {
font-family: "Noto Sans CJK KR", sans-serif;
}
정리
스프링부트에서 pom.xml에 Playwright 의존성을 추가해 정적 HTML을 PDF로 자동 변환하는 기능은 구현 자체는 어렵지 않습니다. 하지만 AlmaLinux 9 같은 리눅스 서버에서는 Chromium 실행에 필요한 시스템 라이브러리가 부족해서 500 에러가 발생할 수 있습니다.
이번 사례에서는 다음 패키지를 설치해 문제를 해결했습니다.
sudo dnf install -y atk at-spi2-atk at-spi2-core libdrm libxkbcommon libXdamage libXfixes libXrandr mesa-libgbm
결국 핵심은 스프링부트 코드 오류가 아니라, Playwright가 사용하는 Chromium 실행 환경 문제였습니다. 정적 HTML을 PDF로 자동 변환하는 기능을 운영 서버에 배포할 때는 Playwright 의존성, Chromium 설치, 리눅스 패키지, 한글 폰트, HTML 리소스 경로까지 함께 점검하는 것이 좋습니다.
추천 태그
스프링부트, Spring Boot, Playwright, Playwright Java, HTML PDF 변환, 스프링부트 PDF, pom.xml, AlmaLinux9, 리눅스 서버, 500 에러, Chromium, 정적 HTML PDF, Java PDF 생성, 서버 오류 해결, PDF 자동 생성
'실무개발 > BackEnd' 카테고리의 다른 글
| Docker로 워드프레스 설치하고 알뜰폰 위젯 플러그인 만들기 (0) | 2026.06.20 |
|---|---|
| PHP API 리팩토링 후기: SRP 원칙으로 요금제 검색 API 구조 개선하기 (0) | 2026.06.18 |
| PHP7 → PHP8 마이그레이션 오류 모음 및 실무 해결 방법 총정리 (0) | 2026.06.12 |
| Spring Boot WebSocket을 Shared Worker로 운영하는 방법: 여러 탭 연결 최적화 실무 정리 (0) | 2026.06.09 |
| Java 17 Maven 멀티 모듈 프로젝트 구성 방법: core, spring-boot, demo 구조 (0) | 2026.06.02 |
