PDF 리포트 한글 깨짐? 시스템 폰트 탐지로 해결한 실전 기록

궁금하면 500원·2025년 6월 23일

미생의 개발 이야기

목록 보기
49/60

폰트 호환성, 그 난관을 넘어서

건강 관리 앱 PDF 리포트 한글 깨짐 해결기

안녕하세요!

건강 관리 애플리케이션 개발 프로젝트에서 사용자의 건강 데이터를 PDF 리포트 형태로 생성하는 기능을 구현하며 겪었던 한글 폰트 호환성 문제와 해결 과정을 공유하고자 합니다.

이 경험은 단순한 기술적 문제를 넘어, 크로스 플랫폼 개발의 복잡성과 사용자 경험의 중요성을 깊이 이해하게 된 계기가 되었습니다.


💡 왜 PDF 리포트였을까요? 프로젝트의 시작을

저희 건강 관리 앱의 핵심 목표 중 하나는 사용자가 자신의 건강 데이터를 직관적으로 파악하고, 필요시 의료진과도 손쉽게 공유할 수 있도록 돕는 것이었습니다.

이를 위해 수면 시간, 걸음 수, 심박수, 스트레스 수치 등 다양한 건강 데이터를 시각화한 PDF 리포트 생성 기능을 기획했습니다.

단순히 데이터를 나열하는 것을 넘어, 한눈에 건강 상태를 파악할 수 있는 유려한 리포트를 제공하는 것이 중요했습니다.

🚨 한글 폰트 호환성 문제 발생했던 예상치 못한 복병

개발 초기, PDF 리포트 생성 기능을 테스트하던 중 다음과 같은 에러를 마주했습니다.

java.io.IOException: src/main/resources/fonts/malgun.ttf not found as file or resource.

iText 라이브러리를 사용하여 PDF를 생성하고 있었는데, 한글을 표시하기 위해 맑은 고딕(Malgun Gothic) 폰트 파일을 찾지 못했다는 오류였습니다.

처음에는 단순히 폰트 파일 경로 문제라고 생각했지만, 곧 더 깊은 근본 원인을 발견하게 되었습니다.

문제의 근본 원인 분석

PDF는 기본적으로 라틴 문자를 기반으로 설계되어 있습니다.

따라서 한글, 중국어, 일본어와 같은 아시아 언어를 PDF에 정확히 표시하려면, 해당 언어를 지원하는 폰트를 명시적으로 지정해 주어야 합니다.

기존 코드에서는 src/main/resources/fonts/malgun.ttf와 같이 특정 폰트 파일 경로를 하드코딩하여 직접 참조하고 있었습니다.

이러한 방식은 다음과 같은 치명적인 문제를 안고 있었습니다.

  • 배포 환경 의존성: 폰트 파일이 해당 경로에 존재하지 않으면 애플리케이션이 정상적으로 실행되지 않는, 환경 의존적인 코드가 되어버립니다.

  • 저작권 문제: 상용 폰트를 프로젝트에 직접 포함할 경우, 라이선스 문제가 발생할 수 있습니다.

  • 플랫폼 호환성: Windows, macOS, Linux 등 운영체제마다 폰트 시스템과 기본 설치 폰트가 다르기 때문에, 특정 OS에 종속적인 코드는 다른 OS에서 문제를 일으킬 수 있습니다.

🛠️ "Graceful Degradation" 문제 해결 전략

이러한 문제들을 해결하기 위해 여러 접근 방식을 고민했고, 최종적으로 시스템 폰트를 활용하는 방향으로 결정했습니다.

이는 "우아한 성능 저하(Graceful Degradation)" 개념을 적용하여, 최선의 폰트가 없더라도 애플리케이션이 중단되지 않고 최소한의 기능이라도 제공할 수 있도록 하는 전략입니다.

1단계: 문제 분석 및 해결 방향 설정

두 가지 주요 해결 방안을 고려했습니다.

방법 1: 폰트 파일 직접 포함

  • 장점: 폰트가 확실히 보장되어 가장 안정적으로 한글을 표시할 수 있습니다.
  • 단점: 저작권 문제, 애플리케이션 파일 크기 증가, 폰트 관리의 복잡성 등의 단점이 있었습니다.

방법 2: 시스템 폰트 활용 (최종 채택)

  • 장점: 라이선스 문제에서 자유롭고, 각 OS에 최적화된 폰트를 사용할 수 있습니다.

  • 단점: 시스템별로 설치된 폰트 종류에 차이가 있을 수 있습니다.

  • 선택 이유: 장점이 단점을 상회하며, 유지보수 측면에서도 더 유리하다고 판단했습니다. 단점은 자동 탐지 로직으로 보완하기로 했습니다.

2단계: 시스템 폰트 자동 탐지 로직 구현

각 운영체제(Windows, macOS, Linux)별로 일반적으로 설치되어 있는 한글 폰트들의 경로를 조사하고, 이를 순차적으로 탐지하여 사용 가능한 폰트를 찾아 적용하는 로직을 구현했습니다.

private fun getKoreanFont(): PdfFont {
    val fontPaths = listOf(
        // Windows 폰트 우선 탐색 (맑은 고딕, 굴림, 바탕)
        "C:\\Windows\\Fonts\\malgun.ttf",
        "C:\\Windows\\Fonts\\gulim.ttc",
        "C:\\Windows\\Fonts\\batang.ttc",

        // macOS 폰트 탐색 (AppleSDGothicNeo, AppleMyungjo)
        "/System/Library/Fonts/AppleSDGothicNeo.ttc",
        "/Library/Fonts/AppleMyungjo.ttf",

        // Linux 폰트 탐색 (나눔고딕, 나눔명조 - 일반적으로 많이 설치됨)
        "/usr/share/fonts/truetype/nanum/NanumGothic.ttf",
        "/usr/share/fonts/truetype/nanum/NanumMyeongjo.ttf"
    )

    // 각 폰트 경로를 순차적으로 확인하여 사용 가능한 폰트 탐색
    for (fontPath in fontPaths) {
        try {
            val file = java.io.File(fontPath)
            if (file.exists()) {
                // 폰트 파일을 찾으면 해당 폰트로 PdfFont 생성
                return PdfFontFactory.createFont(fontPath, PdfEncodings.IDENTITY_H)
            }
        } catch (e: Exception) {
            // 해당 폰트를 사용할 수 없는 경우, 다음 폰트로 시도
            continue
        }
    }

    // 모든 한글 폰트를 찾지 못한 경우, 대체 폰트(Helvetica) 사용
    // 이 경우 한글이 깨질 수 있지만, 애플리케이션 중단을 막는 '최후의 보루'
    return PdfFontFactory.createFont("Helvetica", PdfEncodings.IDENTITY_H)
}

이 로직의 핵심은 "우아한 성능 저하(Graceful Degradation)" 입니다.
최적의 한글 폰트부터 시도하고, 실패하면 차선책으로 넘어가며, 최종적으로는 기본 폰트라도 사용하여 애플리케이션이 비정상적으로 종료되는 것을 방지했습니다.

3단계: 차트 글자 깨짐 문제 해결

PDF 리포트에는 심박수 변화를 시각적으로 보여주는 차트가 포함되어 있었습니다. 그런데 차트 하단의 한글 텍스트(축 제목, 범례 등)가 잘리거나 깨져서 표시되는 문제가 발생했습니다.

문제의 원인은 크게 두 가지였습니다.

  • 폰트 크기 부적절: 차트 영역 대비 폰트가 너무 커서 공간이 부족했습니다.
  • 차트 크기 부족: 텍스트를 모두 표시하기에는 차트 캔버스 자체가 작았습니다.
    해결책으로 다음과 같이 개선했습니다.
// 차트 폰트 크기를 요소별로 세분화하여 적용
chart.title.font = koreanFont.deriveFont(Font.BOLD, 14f)             // 차트 제목 폰트 크기 조정
plot.domainAxis.labelFont = koreanFont.deriveFont(Font.PLAIN, 12f)   // 축 제목 폰트 크기 조정
plot.domainAxis.tickLabelFont = koreanFont.deriveFont(Font.PLAIN, 10f) // 눈금 레이블 폰트 크기 조정

// 차트 크기를 확대하여 여유 공간 확보 (예: 400x300 -> 500x350)
chart.createBufferedImage(500, 350)

차트의 각 요소에 맞는 적절한 폰트 크기를 적용하고, 차트 자체의 크기를 확대하여 텍스트가 충분히 표시될 수 있도록 여유 공간을 확보했습니다.

4단계: 사용자 경험(UX) 개선

기술적인 문제 해결과 더불어, 최종 사용자가 리포트를 더욱 쉽게 이해하고 활용할 수 있도록 UX 측면에서도 개선을 진행했습니다.

  • 기간 표시 한글화: "daily"와 같은 영문 표현을 "일자별"로 변경하여 직관성을 높였습니다.
  • 수치 포맷팅: 평균값을 소수점 첫째 자리로 제한하여 가독성을 향상시켰습니다.
  • 차트 중앙 정렬: PDF 내에서 차트가 중앙에 보기 좋게 배치되도록 레이아웃을 조정했습니다.

📚 이번 경험을 통해 얻은 것들 기술적 학습 포인트

  • 국제화(i18n)의 복잡성 이해

    단순히 텍스트를 번역하는 것을 넘어, 각 언어의 문자 체계와 폰트를 지원하는 것이 얼마나 복잡하고 중요한지 깨달았습니다.
    특히 PDF처럼 레이아웃이 중요한 문서에서는 폰트 선택이 최종 결과물의 품질을 크게 좌우한다는 점을 체감했습니다.

  • 크로스 플랫폼 개발의 도전

    동일한 Java/Kotlin 코드라도 실행 환경(Windows, macOS, Linux)에 따라 다르게 동작할 수 있다는 점을 배웠습니다.

이는 각 OS의 파일 시스템, 폰트 시스템 등 환경적 차이에서 비롯됩니다. 다양한 환경을 고려한 설계의 중요성을 다시 한번 상기했습니다.

  • 견고한 에러 처리의 중요성

    단순히 에러를 수정하는 것을 넘어, 다양한 환경에서 발생할 수 있는 예외 상황을 미리 대비하는 "방어적 프로그래밍"의 중요성을 이해했습니다.
    try-catch 블록과 대체 폰트 적용 로직은 이러한 철학의 결과물입니다.

✅ 결과 및 개선 효과

이번 문제 해결을 통해 다음과 같은 긍정적인 효과를 얻을 수 있었습니다.

기술적 개선

  • 안정성 향상: 폰트 파일에 대한 직접적인 의존성을 제거하여 배포 환경으로부터 독립성을 확보했습니다.

  • 호환성 개선: Windows, macOS, Linux 등 모든 주요 운영체제 환경에서 PDF 리포트가 정상적으로 생성되도록 개선했습니다.

  • 유지보수성 향상: 하드코딩된 폰트 경로를 제거하고 유연한 로직을 적용하여 코드의 유지보수성을 높였습니다.

사용자 경험 개선

  • 시각적 품질 향상: 차트의 글자 깨짐 현상을 완벽하게 해결하여 리포트의 시각적 품질을 크게 향상시켰습니다.

  • 직관성 개선: 한글 용어 사용과 수치 포맷팅을 통해 사용자의 리포트 이해도를 높였습니다.

  • 일관성 확보: 어떤 환경에서 앱을 사용하든 동일한 고품질의 PDF 리포트를 받아볼 수 있게 되었습니다.

🚀 향후 확장 가능성

이번에 구현한 시스템 폰트 자동 탐지 로직은 단순히 한글 폰트 문제 해결을 넘어, 향후 애플리케이션의 다국어 지원(i18n) 기능으로 확장될 수 있는 잠재력을 가지고 있습니다.

예를 들어,

  • 영어, 중국어, 일본어 등 다른 언어 폰트 지원
  • 사용자가 선호하는 폰트 설정 기능 추가
  • 폰트 품질, 가독성 등에 따른 우선순위 조정 로직 고도화

등으로 발전시킬 수 있을 것입니다.

마무리하며

이번 프로젝트에서 겪었던 폰트 호환성 문제는 단순히 코드를 수정하는 것을 넘어, 실제 사용자가 사용할 제품을 만들 때 고려해야 할 다양한 요소들을 경험하게 해주었습니다.

특히, 다양한 환경에서 일관된 사용자 경험을 제공하는 것이 얼마나 중요하고 도전적인 일인지 깨달았습니다.

앞으로도 이런 값진 경험을 바탕으로, 더욱 견고하고 사용자 친화적인 애플리케이션을 개발해 나가는 개발자가 되겠습니다.

긴 글 읽어주셔서 감사합니다!

profile
에러가 나도 괜찮아 — 그건 내가 배우고 있다는 증거야.

0개의 댓글