매주 90장 예배 PPT를 자동으로 만드는 파이프라인 — 웹 에디터, Object Storage, 그리고 Claude 스킬
교회 미디어 봉사를 맡으면 매주 같은 일을 반복하게 됩니다. 찬양 4곡 악보를 찾아 슬라이드로 넣고, 교독문을 절 단위로 나누고, 말씀봉독 본문을 개역개정에서 옮겨 적고, 광고를 정리하고, 설교 제목을 바꿉니다. 평균 90장짜리 PPT가 매주 한 벌씩 나옵니다.
이 글은 그 작업을 웹 입력 → 오브젝트 스토리지 → Claude 스킬 → pptx 로 자동화한 구조를 정리한 것입니다. 6주간 실제 예배에 쓰면서 다듬은 결과이고, 삽질 목록은 별도 글로 나눴습니다.
1. 전체 구조
[담당자] [Astro 웹 에디터] [OCI Object Storage]
찬양 콘티, 교독문 번호, ──▶ material.ts가 검증·직렬화 ──▶ 자료 - 20260906.txt (사람용)
본문 범위, 광고, 설교 토큰 게이트 API 자료 - 20260906.json (자동화용)
│
▼ (OneDrive 동기화)
[Claude Cowork 스킬] [작업 폴더/contexts/]
load_material.py ──▶ find_song.py ──▶ render_song.py ──▶ worship_design.py ──▶ 완성 pptx
원고 읽기 악보 812개 중 LibreOffice로 슬라이드 조립·검증
이번 주 것 매칭 PNG 변환
핵심은 두 가지입니다. 첫째, 매주 바뀌는 것만 입력받습니다. 사도신경, 헌금기도, 축도처럼 고정된 슬라이드는 코드에 상수로 들어 있습니다. 둘째, 입력은 구조화된 JSON으로 저장하고 사람이 읽을 txt는 따로 만듭니다. 처음엔 txt 하나만 저장하고 정규식으로 다시 읽었는데, 그 틈에서 매주 한 번씩 문제가 났습니다. 뒤에서 다시 다룹니다.
2. 입력: Astro 웹 에디터
에디터는 Astro로 만든 정적 페이지 하나와 API 라우트 두 개입니다. 담당자는 토큰을 넣고 폼을 채웁니다.
src/lib/material.ts // 필드 정의, 직렬화, 파싱, 검증 (DOM 의존 없음)
src/pages/automation/.../editor.astro
src/pages/api/bucket-file.ts // GET/POST, x-editor-token 헤더 검사
src/pages/api/bucket-list.ts // _manifest.json 읽기
필드는 MaterialData 하나로 고정돼 있고, 에디터·주보 인쇄뷰·PPT 생성기가 모두 같은 규격을 봅니다.
export const FIELDS = [
"serviceDate", "serviceType",
"praiseMain", "offeringHymn", "hymnThree", "hymnFour",
"responsiveReading", "prayer", "scripture", "sermonTitle",
"announcements", "notes",
"rrFontSize", "rrOddColor", "rrEvenColor",
"scriptureFontSize", "scriptureOddColor", "scriptureEvenColor",
] as const;
저장 버튼을 누르면 같은 데이터를 두 번 올립니다.
// txt: 사람이 읽고 주보 인쇄에 사용
await authedFetch("/api/bucket-file", { method: "POST",
body: JSON.stringify({ name: "자료 - 20260906.txt", content: buildMaterial(data) }) });
// json: 자동화가 읽는 원본 구조
await authedFetch("/api/bucket-file", { method: "POST",
body: JSON.stringify({ name: "자료 - 20260906.json", content: JSON.stringify(data, null, 2) }) });
검증은 저장 전에 클라이언트에서 돌립니다. 필수 항목 누락, 찬송가 장 번호 누락, 선택한 날짜와 다른 날짜가 본문에 섞여 있는 경우(지난주 자료를 불러와 고치다가 생기는 실수) 등을 잡습니다.
3. 저장: OCI Object Storage (S3 호환)
AWS SDK의 S3Client를 OCI 엔드포인트에 붙여 씁니다. 코드는 S3와 동일합니다.
const client = new S3Client({
region,
endpoint: `https://${namespace}.compat.objectstorage.${region}.oraclecloud.com`,
forcePathStyle: true,
credentials: { accessKeyId, secretAccessKey },
});
파일 목록은 별도 _manifest.json에 유지합니다. 저장할 때마다 읽고 다시 쓰는 구조라 동시 저장에는 약하지만, 담당자가 한 명이라 문제된 적은 없습니다. 여러 명이 쓰게 되면 LIST API로 바꾸는 게 맞습니다.
스토리지에 올라간 파일은 로컬 OneDrive 폴더의 contexts/로 가져옵니다. 지금은 이 단계가 수동인데, 예배 순서는 비밀 정보가 아니라서 읽기 전용 공개 엔드포인트 하나를 열면 없앨 수 있습니다.
4. 생성: Claude Cowork 스킬
PPT를 만드는 쪽은 Python 스크립트 몇 개와 그것들을 어떤 순서로 어떻게 쓰는지 적은 스킬 문서(SKILL.md)입니다. "내일 예배 ppt 만들어줘" 한 마디로 스킬이 실행되고, 아래 순서를 밟습니다.
| 단계 | 스크립트 | 하는 일 |
|---|---|---|
| 원고 읽기 | load_material.py | json 우선, 없으면 txt 파싱. 출력 키는 material.ts와 동일 |
| 악보 찾기 | find_song.py | 812개 파일 중 장 번호 정확 매칭 또는 제목 유사도. 후보가 붙으면 사용자에게 물음 |
| 악보 렌더 | render_song.py | LibreOffice headless로 ppt → pdf → png. 완료 마커 있을 때만 캐시 신뢰 |
| 교독문 | parse_kyodokmun.py | docx에서 번호로 찾아 홀수·짝수절 쌍으로 |
| 본문 | find_verses.py | 지난 주보 pptx에서 절 텍스트 재사용. 없으면 대한성서공회 개역개정 페이지 |
| 조립 | worship_design.py | 디자인 규격이 함수로 들어 있음. 마지막에 페이지 번호 일괄 기입 |
스크립트가 결정론적으로 할 수 있는 건 전부 스크립트가 하고, 에이전트는 판단이 필요한 곳만 맡습니다. 원고의 "교독문 65. 시편 150편"이 실제로는 65번=시편 149편일 때 물어보기, 같은 곡 파일이 4:3판과 16:9판 둘 다 있을 때 렌더해 보고 고르기, 원고 오타("우리들의 싸울것은" → 350장 "우리들이 싸울 것은")를 같은 곡으로 판단하기 같은 것들입니다.
5. 디자인은 코드로
처음엔 지난주 pptx를 복사해 슬라이드를 바꿔 끼우는 방식이었습니다. python-pptx는 슬라이드 삭제·이동이 불안정하고, 곡마다 장수가 달라서 매번 인덱스가 어긋났습니다. 지금은 빈 프레젠테이션에 처음부터 쌓습니다.
prs = new_deck()
cover(prs, leader='○○○ 목사')
songs(prs, RENDER['찬양1']) # 악보 이미지 전체화면
creed(prs) # 사도신경 2장 (상수)
name_slide(prs, '헌금기도', '헌금기도', '○○○ 목사')
kyodokmun(prs, header, pairs, odd='000000', even='C2410C', size=44)
scripture(prs, '시편 23편 1-6절', verses, odd='000000', even='C2410C', size=44)
center_lines(prs, '설교', ['설교 “가장 좋은 길”'])
chroma(prs) # 크로마키용 단색 녹색
closing(prs)
stamp_pages(prs) # 'N / 총장수' 일괄 기입
디자인 규격(좌표, 폰트, 색, 영문 라벨칩 폭)은 담당자가 손으로 다듬은 pptx 한 벌을 python-pptx로 읽어 숫자로 뽑아낸 것입니다. 이후 "이런 디자인으로 해줘"가 아니라 "이 파일을 기준으로 해줘"가 됐고, 재현이 정확해졌습니다.
말씀봉독 — 절 번호 홀짝으로 색을 바꾸고, 두 절이 한 장에 들어가면 두 절을 넣는다.
6. 검증은 렌더해서 눈으로
python-pptx로 만든 파일은 python-pptx로 다시 열어 봐야 소용이 없습니다. 도형 개수나 텍스트는 맞아도 넘침, 잘림, 순서 뒤바뀜은 렌더해야 보입니다. 저장 전에 항상 LibreOffice로 PDF를 만들고 ImageMagick으로 contact sheet를 만들어 확인합니다.
soffice --headless --norestore --convert-to pdf --outdir /tmp/build out.pptx
pdftoppm -png -r 42 /tmp/build/out.pdf /tmp/build/c
montage /tmp/build/c-*.png -tile 6x -geometry +3+3 sheet.png
여기에 코드로 세는 것들을 얹습니다. 교독문 슬라이드 수 × 2 = 원본 절 수, 말씀봉독 절 합계 = 원고 범위, 절별 글자색이 규칙대로인지, 크로마키가 설교 다음에 정확히 한 장인지, 각 곡의 PNG가 전부 온전히 열리는지. 이 중 마지막 항목은 사고를 한 번 치고 나서 추가된 것입니다.
7. 6주 운영 결과
- 주당 생성 시간: 사람이 2시간 → 입력 5분 + 자동 생성 10분 내외
- 매주 확인 질문 1~2개 (교독문 번호 불일치, 색상 요청 등). JSON 저장으로 바꾼 뒤 줄어드는 중
- 디자인 변경 1회 (담당자가 pptx를 손으로 고침 → 코드로 옮겨 다음 주부터 반영)
- 사고 1회 (악보 8장 중 5장만 들어감 — 렌더 캐시 문제, 다음 글에서)
가장 크게 느낀 건 자동화의 병목이 스크립트가 아니라 입력의 형식이었다는 점입니다. 사람이 쓴 텍스트를 정규식으로 읽는 구간이 매주 문제를 만들었고, 에디터가 이미 갖고 있던 구조를 그대로 넘기게 하자 대부분 사라졌습니다.

댓글
댓글 쓰기