Files
sam-docs/dev/standards/pdf-font-policy.md
김보곤 909d3e11b8 docs: [standards] PDF 생성 시 폰트 정책 추가
- 구글 폰트 외부 로드 금지, isRemoteEnabled 금지
- 운영서버 권한 오류 사례 및 긴급 복구 절차
- 시스템 기본 폰트 사용 가이드, 로컬 폰트 설치 방법
2026-03-11 09:39:41 +09:00

5.6 KiB

PDF 생성 시 폰트 정책

작성일: 2026-03-11 상태: 설계 확정


1. 개요

1.1 목적

DomPDF로 PDF를 생성할 때 폰트 관련 문제(권한 오류, 외부 의존성, 배포 시 재발)를 방지하기 위한 정책이다.

1.2 배경 — 운영서버 장애 사례

급여명세서 PDF 생성 시 500 에러 발생. 원인은 DomPDF가 구글 폰트(Noto Sans KR)를 외부에서 다운로드한 후 캐시 파일(.ufm)을 vendor/dompdf/dompdf/lib/fonts/에 저장하려는데, www-data 유저에 쓰기 권한이 없어서 Permission denied 발생.

fopen(.../vendor/dompdf/dompdf/lib/fonts/noto_sans_kr_normal_...ufm):
Failed to open stream: Permission denied

1.3 핵심 원칙

❌ PDF 뷰에서 구글 폰트(@import, <link>) 사용 금지
❌ DomPDF의 isRemoteEnabled 옵션 사용 금지
✅ 시스템 기본 폰트만 사용
✅ 폰트가 필요하면 로컬 설치 후 DomPDF에 등록

2. 금지 사항

2.1 구글 폰트 외부 로드 금지

❌ @import url('https://fonts.googleapis.com/...');
❌ <link href="https://fonts.googleapis.com/..." rel="stylesheet">
❌ font-face src: url('https://...');

2.2 isRemoteEnabled 옵션 금지

// ❌ 금지 — 외부 리소스 다운로드를 활성화하면 안 됨
$pdf = Pdf::loadView('view', $data)
    ->setOptions(['isRemoteEnabled' => true]);

// ✅ 올바른 사용
$pdf = Pdf::loadView('view', $data)
    ->setPaper('a4');

3. 금지 이유

3.1 배포 시 권한 문제 재발

composer installvendor/를 새로 생성하면 폰트 캐시 디렉토리 권한이 초기화된다. Jenkinsfile에 매번 권한 설정을 추가해야 하는데, vendor/ 내부 특정 경로에 권한을 거는 것은 안티패턴이다.

배포 → composer install → vendor/ 재생성 → 폰트 캐시 권한 초기화 → PDF 생성 실패

3.2 외부 네트워크 의존성

PDF를 생성할 때마다 fonts.googleapis.com에 요청한다. 외부 서버 장애, 네트워크 지연, 방화벽 차단 시 PDF 생성이 실패한다. 내부 ERP에서 외부 서비스에 의존할 이유가 없다.

3.3 PDF에서 웹폰트 불필요

DomPDF는 웹 브라우저가 아니다. 구글 폰트를 다운로드 → 파싱 → 캐싱하는 과정이 불필요한 오버헤드다. 시스템 기본 폰트로 충분하다.


4. 올바른 폰트 사용법

4.1 시스템 기본 폰트 사용 (권장)

/* PDF 뷰(Blade)에서 사용할 font-family */
body {
    font-family: 'Malgun Gothic', 'Apple SD Gothic Neo', sans-serif;
}

4.2 PDF 생성 코드 패턴

// ✅ 올바른 PDF 생성 패턴
$pdf = Pdf::loadView('emails.payslip', ['data' => $data])
    ->setPaper('a4');

$pdfContent = $pdf->output();

4.3 한글 폰트가 필요한 경우 — 로컬 설치

시스템 기본 폰트로 부족하면 서버에 폰트를 직접 설치하고 DomPDF에 등록한다.

# 1. 서버에 폰트 설치 (Level 2 — 사용자 확인 후 실행)
sudo apt install fonts-noto-cjk

# 2. DomPDF 폰트 등록 (php artisan 명령)
php artisan dompdf:font "Noto Sans KR" \
    /usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc

로컬 설치된 폰트는 vendor/ 재생성과 무관하게 유지된다.


5. 기존 코드 수정 가이드

구글 폰트를 사용하는 기존 PDF 뷰를 발견하면 다음 절차로 수정한다.

5.1 Blade 뷰에서 제거

<!-- ❌ 삭제 대상 -->
<style>
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@400;700;800&display=swap');
</style>

<!-- ✅ 수정 후 -->
<style>
body {
    font-family: 'Malgun Gothic', 'Apple SD Gothic Neo', sans-serif;
}
</style>

5.2 서비스/컨트롤러에서 제거

// ❌ 수정 전
$pdf = Pdf::loadView('emails.payslip', ['payslipData' => $payslipData])
    ->setOptions(['isRemoteEnabled' => true])
    ->setPaper('a4');

// ✅ 수정 후
$pdf = Pdf::loadView('emails.payslip', ['payslipData' => $payslipData])
    ->setPaper('a4');

6. 긴급 복구 — 운영서버 권한 오류 발생 시

이미 구글 폰트를 사용하는 코드가 배포되어 권한 오류가 발생한 경우의 즉시 조치이다. 근본 수정(구글 폰트 제거)을 반드시 병행한다.

# Level 2 작업 — 사용자 확인 후 실행
# 운영서버에서 폰트 캐시 디렉토리 권한 수정
sudo chown -R www-data:webservice /home/webservice/mng/current/vendor/dompdf/dompdf/lib/fonts/
sudo chmod -R 775 /home/webservice/mng/current/vendor/dompdf/dompdf/lib/fonts/

이 조치는 임시이다. 재배포 시 vendor/가 새로 생성되면 다시 발생한다.


7. 체크리스트

PDF 뷰 작성 시

  • @import url('https://fonts.googleapis.com/...') 미포함
  • <link href="https://fonts.googleapis.com/..."> 미포함
  • font-family에 구글 폰트명 미포함
  • 시스템 기본 폰트 사용 (Malgun Gothic, sans-serif)

PDF 생성 코드 작성 시

  • isRemoteEnabled 옵션 미사용
  • Pdf::loadView()->setPaper() 패턴 사용

코드 리뷰 시

  • PDF 관련 Blade 뷰에 외부 폰트 URL 없음
  • DomPDF 옵션에 isRemoteEnabled 없음

관련 문서

  • 서버 운영 매뉴얼: dev/deploys/ops-manual/README.md
  • DomPDF 패키지: barryvdh/laravel-dompdf

최종 업데이트: 2026-03-11