Playwright와 Thymeleaf를 활용하여 이메일에 첨부파일 보내기
7/7/2025
﹖ 왜 이 기능을 만들었나?
- 사내에서 PDF 형태의 명세서를 메일 첨부파일로 보내는 기능이 필요했다. 처음에는
openhtmltopdf 라이브러리를 사용했으나 CSS 호환성이 너무 안좋았고, 테이블 레이아웃, Bootstrap 등의 CSS가 의도한 대로 나오지 않아 답답했다.
- 브라우저를 띄워 그대로 스냅샷처럼 PDF로 변환하기 때문에, CSS 등의 걱정이 없었다.
- 물론 서버에서 작동되는 것이기 때문에 서버에 부담을 줄 순 있지만 많은 양을 한번에 작업하는 것도 아니고 간단한 Table 형식을 PDF로 변환하는 것이기 때문에 큰 고려사항은 아니라고 판단했다.
⚙️ 구현 과정
- Thymeleaf View Templates을 반환하는 Controller를 만든다. (동적으로 데이터를 가져와서 원하는 형태의 Thymeleaf 템플릿을 만든다.)
- 이는 유지보수시 로컬 주소에서 직접 화면을 보면서 수정할 수 있고, 해당 화면이 PDF로 찍혀서 나오는 것이기 때문에 매우 편리하다.
ViewTestController
@Controller
@RequestMapping("/before-pdf")
@RequiredArgsConstructor
public class ViewTestController {
private final OrderOutService orderOutService;
@GetMapping("/attachment")
public String transOutInvoice(@RequestParam String Parameters, Model model) {
List<Map<String, Object>> Data = xxxService.xxxMapper(Parameters)
model.addAttribute("printData", Data);
return "attachmentHtml";
}
}
- Playwright Service 생성
- 세션이 필요한 경우, 쿠키를 Context에 추가 (필자는 세션이 필요했기 때문에 추가했다.)
- 해당 URL로 접속 후, PDF로 변환 ->
byte[] 형태로 반환.
PlaywrightPdfService
@Service
public class PlaywrightPdfService {
public byte[] generatePdfFromUrl(String url, Map<String, String> cookies) throws Exception {
try (Playwright playwright = Playwright.create()) {
Browser browser = playwright.chromium().launch(new BrowserType.LaunchOptions().setHeadless(true));
BrowserContext context = browser.newContext();
if (cookies != null && !cookies.isEmpty()) {
List<Cookie> cookieList = new ArrayList<>();
for (Map.Entry<String, String> entry : cookies.entrySet()) {
cookieList.add(new Cookie(entry.getKey(), entry.getValue())
.setDomain("localhost")
.setPath("/"));
}
context.addCookies(cookieList);
}
Page page = context.newPage();
page.navigate(url);
byte[] pdfBytes = page.pdf(new Page.PdfOptions()
.setFormat("A4")
.setPrintBackground(true)
);
browser.close();
return pdfBytes;
}
}
}
- 변환한 바이트 배열을 Email 첨부파일에 추가하여 보낸다.
🙌 후기
- 진짜 2-3일 동안 방법을 찾기 위해 많이 노력했던 것 같다, 물론 앞에 서술한 방법 말고도 다른 방법은 존재한다.
단지 내가 모를뿐....
- 브라우저 렌더링 그대로 PDF로 출력되므로 CSS 스트레스가 확 줄어서 너무 좋았다.
- 서버에서 headless chromium을 띄우기 때문에, 서버에 살짝 부담을 줄 순 있으나, 복수 개의 파일이 아니라면 고려하기 좋은 기능인 것 같다.
📌 Playwright Attachment 예제