TM Taeyang Moon
Demo · 아키텍처 해설

BookForge — AI 책(PDF) 출판 플랫폼

주제를 입력하면 8단계 품질 파이프라인이 '출판 품질의 책 PDF'를 생성

Azure OpenAI gpt-5.4(키리스)8단계 품질 파이프라인근거 그라운딩(환각 차단)WeasyPrint 조판Container AppsManaged Identity
🔗
라이브 사이트bookforge-api.greensea-643eec7e.koreacentral.azurecontainerapps.io/
사이트 열기 →

이 문서는 고객이 입력한 주제로 "출판 품질의 책"을 PDF로 생성하는 플랫폼(BookForge) 을 하나의 Azure 아키텍처 사례로 보고, 어떤 서비스로 어떻게 구성했는지, 왜 그렇게 설계했는지를 직접 구축하려는 엔지니어 관점에서 설명합니다. (기준일: 2026-06-17, 리전: Container Apps = Korea Central, Azure OpenAI = Korea Central)

⚠️ 면책: 본 데모로 생성되는 책은 AI 저작물입니다. 사실·수치는 참고용이며, 중요한 의사결정 전 원출처 확인을 권장합니다.


한눈에 보기

BookForge는 "주제·형태·내용·참고자료·분량 입력 → 8단계 품질 파이프라인 → 표지·목차·본문·참고문헌까지 조판된 책 PDF"를 만들어내는 웹 플랫폼입니다. 이 데모의 성패 지표는 단 하나, 책 내용의 품질입니다. 그래서 "한 번에 길게 쓰기"가 아니라 여러 단계로 깎아내는 방식으로 설계했습니다.


🎨 동화책 모드 (그림책 + 페이지별 일러스트)

성인 대상 "책"과 달리, 동화책의 성패는 그림 품질 + 연령 적합성 + 캐릭터 일관성입니다. 그래서 별도 파이프라인을 둡니다.


아키텍처

  인터넷 ──HTTPS──▶  Azure Container Apps (FastAPI + 정적 프런트, Korea Central)
                      bookforge-api  (api/server.py, ingress 8000, 단일 레플리카)
                      - GET  /                     입력 폼 + 진행상황 실시간 폴링 UI(web/index.html)
                      - POST /api/books            (multipart) 작업 시작 → {job_id}, 데몬 스레드로 파이프라인 실행
                      - GET  /api/books/{id}       진행상태 폴링(단계·진행률·장별 상태·로그)
                      - GET  /api/books/{id}/pdf   완성 PDF 다운로드
                      - 시스템 관리 ID(키리스)
                              │
            ┌─────────────────┼──────────────────────────────────┐
            ▼                 ▼                                    ▼
   app/grounding.py    app/pipeline.py (8단계 오케스트레이션)   app/render.py
    파일/URL/웹검색       기획→그라운딩→아웃라인→집필→비평·수정     Markdown→HTML→PDF
    → 코퍼스 + 장별        →일관성→교정→렌더 (진행상태 실시간 보고)  WeasyPrint(표지/목차/
    근거 추출(환각 차단)          │                                 본문/참고문헌, 나눔폰트)
                                ▼
                          app/llm.py — gpt-5.4 (키리스 AAD)
                          DefaultAzureCredential → AzureOpenAI
                          foundry-uzrz5ojtsjvae · gpt-5.4
                          (reasoning 모델: max_completion_tokens)
                              │
                              ▼
                          app/store.py — 작업상태·산출물 저장
                          컨테이너 로컬 스토리지(데모) / Blob+PE(프로덕션)

구성 서비스와 역할

서비스 역할 중요 포인트
Azure Container Apps (FastAPI) 웹 백엔드 + 정적 프런트 bookforge-api 폼 입력 → 8단계 파이프라인을 데몬 스레드로 실행, 진행상태 폴링. 단일 레플리카(생성·폴링·다운로드 일관성). 시스템 관리 ID(키리스)
Azure OpenAI / Foundry — gpt-5.4 모든 단계의 추론(기획·집필·비평·일관성) 키리스(AAD 토큰). reasoning 모델이라 max_completion_tokens 사용
Azure OpenAI — gpt-image-1 (동화책) 페이지별 일러스트 생성 키리스(AAD). koreacentral엔 이미지 모델이 없어 eastus2(foundry-bookimg-mty)에 별도 배포. 표지 1024×1536(high)·페이지 1024×1024(medium)
그라운딩(grounding.py) 근거의 단일 진실 소스 업로드/URL/웹검색 → 코퍼스 → 장별 "검증된 근거" 추출. 수치·인용은 근거 내로 제한(환각 차단)
WeasyPrint(render.py) 책 조판(HTML→PDF) CSS Paged Media: 표지·판권·자동 목차(페이지번호)·본문(명조)·참고문헌. 컨테이너에 한글 폰트(나눔/노토) 설치
저장소(store.py) 작업상태·산출물 보관 데모는 컨테이너 로컬(거버넌스 무관·단순). 프로덕션은 Blob + Private Endpoint로 확장
Azure Container Registry (Basic) 이미지 보관 az acr build로 WeasyPrint 런타임+한글폰트 포함 이미지 빌드

왜 이렇게 설계했나 (핵심 결정)

  1. 품질이 성패 → 단발 생성이 아니라 다단계 "깎아내기". LLM에게 "좋은 책 한 권 써줘"라고 한 번에 시키면 평균적이고 밋밋한 글이 나옵니다. 그래서 기획으로 방향(thesis)을 고정하고, 아웃라인으로 중복 없는 점층 구성을 짠 뒤, 장을 쓰고 → 별도의 편집자 페르소나가 비평·재작성합니다. 같은 모델이라도 "쓰는 역할"과 "고치는 역할"을 분리하면 결과 밀도가 눈에 띄게 올라갑니다.

  2. 근거 그라운딩으로 환각 차단. 책의 신뢰도는 "구체적인데 틀린 숫자" 하나로 무너집니다. 그래서 참고자료 (파일·URL·웹검색)를 코퍼스로 만들고, 각 장마다 그 장에 필요한 근거만 추출해 주입합니다. 집필·수정 프롬프트는 "구체 수치·인용은 이 근거 안에서만"이라고 못박습니다. 참고자료가 없으면 모델 지식으로 쓰되 수치 환각에 주의하도록 명시합니다.

  3. 앞 장 요약을 이어붙이는 연속 집필. 장을 독립적으로 쓰면 중복·모순이 생깁니다. 각 장을 쓴 뒤 3문장으로 요약해 다음 장 프롬프트의 맥락으로 넣어, 이어지는 한 권의 책처럼 흐르게 했습니다.

  4. 키리스(시크릿 0). 사용자 구독은 Azure OpenAI 키 인증을 차단(disableLocalAuth=true)합니다. 그래서 DefaultAzureCredentialAAD 토큰을 받아 호출하고, 배포 환경에서는 Managed Identity + Cognitive Services OpenAI User 역할만 부여합니다. 유출될 키가 존재하지 않습니다.

  5. 거버넌스와 싸우지 않는 저장 선택(데모). 처음엔 산출물을 Blob(관리 ID)에 저장하려 했지만, 중앙 거버넌스가 스토리지 publicNetworkAccess즉시 Disabled로 강제해 ACA(Consumption, VNet 없음)에서 쓰기가 AuthorizationFailure로 막혔습니다. 데모에서는 컨테이너 로컬 스토리지 + 단일 레플리카로 단순화해 거버넌스와 무관하게 안정적으로 동작시켰습니다. (영속·다중 레플리카가 필요한 프로덕션은 아래 참고.)

  6. 출판물다운 조판에 투자. 내용이 좋아도 "메모장 텍스트"처럼 보이면 가치가 전달되지 않습니다. WeasyPrint로 표지·자동 목차(CSS target-counter로 실제 페이지번호)·명조 본문·참고문헌을 만들어 첫인상부터 책이 되게 했습니다. 본문은 세리프(가독성), 제목은 산세리프(위계)로 분리했습니다.


직접 부딪힌 함정과 해결 (재현 방지)


직접 구축 가이드 (요약)

# 로컬 실행
cd demos/bookforge
python3 -m venv .venv && source .venv/bin/activate
pip install -r api/requirements.txt        # WeasyPrint는 시스템 pango 필요(macOS: brew install pango)
az login
./run-local.sh                              # → http://localhost:8000

# Azure 배포(Container Apps, 키리스)
az login && az account set --subscription <SUB_ID>
./deploy.sh                                 # ACR 빌드 → ACA 생성 → 관리ID에 AOAI 역할 부여
# 정리: az group delete -n rg-bookforge --yes --no-wait

프로덕션 확장: 영속·다중 레플리카가 필요하면 산출물 저장을 Blob + Private Endpoint로 전환하세요. MCAPS 거버넌스가 스토리지 공개 접근을 주기적으로 잠그므로, VNet 주입형 Container Apps + Private Endpoint 패턴이 필요합니다(이 포털의 "거버넌스 면역 스토리지" 데모 참고).


고객 가치 (비즈니스 관점)

← 데모 목록학습 포털 홈