docs: [guides] HTML → PPTX 변환 도구 사용법 가이드 추가
- 슬라이드 작성법, 변환 스크립트 구조, 실행 방법 포함 - 기존 사용 사례, 문제 해결, 빠른 시작 가이드 포함
This commit is contained in:
387
sam/docs/guides/pptx-generation-guide.md
Normal file
387
sam/docs/guides/pptx-generation-guide.md
Normal file
@@ -0,0 +1,387 @@
|
|||||||
|
# HTML → PPTX 변환 도구 사용법
|
||||||
|
|
||||||
|
> **작성일**: 2026-03-01
|
||||||
|
> **상태**: 확정
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. 개요
|
||||||
|
|
||||||
|
HTML 슬라이드 파일을 PowerPoint(PPTX)로 변환하는 로컬 도구이다.
|
||||||
|
Playwright(브라우저 렌더링) + PptxGenJS(PPTX 생성)를 조합하여, HTML/CSS로 디자인한 슬라이드를 그대로 PPTX로 출력한다.
|
||||||
|
|
||||||
|
### 1.1 구성 요소
|
||||||
|
|
||||||
|
```
|
||||||
|
~/.claude/skills/pptx-skill/scripts/
|
||||||
|
├── html2pptx.js ← 핵심 변환 엔진 (HTML → PPTX)
|
||||||
|
└── node_modules/
|
||||||
|
├── pptxgenjs ← PPTX 파일 생성 라이브러리
|
||||||
|
├── playwright ← 브라우저 렌더링 (HTML 파싱)
|
||||||
|
├── sharp ← 이미지 처리
|
||||||
|
└── ...
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.2 사전 조건
|
||||||
|
|
||||||
|
| 항목 | 현재 설치 상태 |
|
||||||
|
|------|---------------|
|
||||||
|
| Node.js | v24.13.0 (`~/.nvm/versions/node/`) |
|
||||||
|
| html2pptx.js | `~/.claude/skills/pptx-skill/scripts/html2pptx.js` |
|
||||||
|
| pptxgenjs | 위 scripts/node_modules 안에 설치됨 |
|
||||||
|
| playwright | 위 scripts/node_modules 안에 설치됨 |
|
||||||
|
|
||||||
|
> 별도 `npm install`이 필요 없다. 이미 모든 의존성이 설치되어 있다.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. 작업 흐름
|
||||||
|
|
||||||
|
```
|
||||||
|
① HTML 슬라이드 작성 (slides/ 폴더)
|
||||||
|
↓
|
||||||
|
② 변환 스크립트 작성 (convert.cjs)
|
||||||
|
↓
|
||||||
|
③ 터미널에서 실행: node convert.cjs
|
||||||
|
↓
|
||||||
|
④ PPTX 파일 생성 완료
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. HTML 슬라이드 작성법
|
||||||
|
|
||||||
|
### 3.1 기본 템플릿
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<!-- 폰트: Pretendard (CDN) -->
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/static/pretendard.min.css">
|
||||||
|
<style>
|
||||||
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||||
|
body {
|
||||||
|
/* ⚠️ 반드시 width/height를 pt 단위로 지정 */
|
||||||
|
width: 720pt; height: 405pt; /* 16:9 가로형 */
|
||||||
|
font-family: 'Pretendard', sans-serif;
|
||||||
|
background: #0F2439;
|
||||||
|
padding: 32pt 40pt;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<!-- 여기에 슬라이드 내용 작성 -->
|
||||||
|
<h1 style="font-size: 24pt; color: #ffffff;">제목</h1>
|
||||||
|
<p style="font-size: 12pt; color: rgba(255,255,255,0.6);">본문 내용</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.2 슬라이드 크기 (body width/height)
|
||||||
|
|
||||||
|
| 용도 | body 크기 | 변환 스크립트 layout |
|
||||||
|
|------|----------|---------------------|
|
||||||
|
| **16:9 가로형** (발표용) | `width: 720pt; height: 405pt;` | `width: 10, height: 5.625` |
|
||||||
|
| **9:16 세로형** (브로셔) | `width: 405pt; height: 720pt;` | `width: 5.625, height: 10` |
|
||||||
|
| **4:3 가로형** (구형) | `width: 720pt; height: 540pt;` | `width: 10, height: 7.5` |
|
||||||
|
|
||||||
|
> **중요**: HTML의 body 크기와 변환 스크립트의 layout 크기가 일치해야 한다. 불일치 시 에러 발생.
|
||||||
|
|
||||||
|
### 3.3 필수 규칙
|
||||||
|
|
||||||
|
#### 텍스트 줄바꿈 방지 (가장 중요)
|
||||||
|
|
||||||
|
브라우저와 PowerPoint의 폰트 렌더링 차이로, HTML에서 한 줄인 텍스트가 PPTX에서 두 줄로 넘어가는 문제가 발생한다.
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!-- ✅ 한 줄짜리 텍스트에는 반드시 white-space: nowrap -->
|
||||||
|
<p style="white-space: nowrap; font-size: 10pt;">이 텍스트는 한 줄입니다</p>
|
||||||
|
|
||||||
|
<!-- ❌ nowrap 없으면 PPTX에서 줄바꿈될 수 있음 -->
|
||||||
|
<p style="font-size: 10pt;">이 텍스트는 한 줄입니다</p>
|
||||||
|
|
||||||
|
<!-- ✅ 의도적 멀티라인은 nowrap 불필요 -->
|
||||||
|
<p style="font-size: 10pt; line-height: 1.6;">
|
||||||
|
여러 줄로 의도된 텍스트입니다.<br>
|
||||||
|
이 경우 nowrap을 넣지 않는다.
|
||||||
|
</p>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 적용 대상
|
||||||
|
|
||||||
|
- 뱃지 텍스트 (01, UC-01 등)
|
||||||
|
- 카드 제목, 태그, 짧은 라벨
|
||||||
|
- 푸터 텍스트
|
||||||
|
- 단일행 `<p>` 태그 전부
|
||||||
|
|
||||||
|
#### 이미지 경로
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!-- ✅ 절대 경로 사용 -->
|
||||||
|
<img src="/home/aweso/sam/docs/assets/bi/sam_bi_white.png" style="height: 24pt;">
|
||||||
|
|
||||||
|
<!-- ❌ 상대 경로는 동작하지 않을 수 있음 -->
|
||||||
|
<img src="../../assets/bi/sam_bi_white.png">
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 스타일 작성
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!-- ✅ 인라인 스타일 사용 (가장 안정적) -->
|
||||||
|
<div style="background: #2E86AB; padding: 8pt; border-radius: 6pt;">
|
||||||
|
|
||||||
|
<!-- ⚠️ <style> 태그의 클래스도 사용 가능하지만, 인라인이 더 안정적 -->
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.4 사용 가능한 CSS 속성
|
||||||
|
|
||||||
|
| 속성 | 지원 | 비고 |
|
||||||
|
|------|:----:|------|
|
||||||
|
| background (색상) | ✅ | 단색, rgba 모두 지원 |
|
||||||
|
| background (그라데이션) | ✅ | linear-gradient 지원 |
|
||||||
|
| border | ✅ | 색상, 두께, radius |
|
||||||
|
| border-radius | ✅ | px, pt 단위 |
|
||||||
|
| font-size, font-weight | ✅ | pt 단위 권장 |
|
||||||
|
| color | ✅ | hex, rgba |
|
||||||
|
| padding, margin | ✅ | pt 단위 권장 |
|
||||||
|
| display: flex | ✅ | gap, align-items 등 |
|
||||||
|
| white-space: nowrap | ✅ | 줄바꿈 방지 (필수) |
|
||||||
|
| opacity | ✅ | |
|
||||||
|
| box-shadow | ⚠️ | 부분 지원 |
|
||||||
|
| transform | ❌ | 미지원 |
|
||||||
|
| animation | ❌ | 미지원 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. 변환 스크립트 작성법
|
||||||
|
|
||||||
|
### 4.1 기본 구조 (복사해서 사용)
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
// ① 패키지 경로 설정 (이 두 줄은 항상 동일)
|
||||||
|
module.paths.unshift(
|
||||||
|
path.join(require('os').homedir(), '.claude/skills/pptx-skill/scripts/node_modules')
|
||||||
|
);
|
||||||
|
const PptxGenJS = require('pptxgenjs');
|
||||||
|
const html2pptx = require(
|
||||||
|
path.join(require('os').homedir(), '.claude/skills/pptx-skill/scripts/html2pptx.js')
|
||||||
|
);
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const pres = new PptxGenJS();
|
||||||
|
|
||||||
|
// ② 레이아웃 설정 (HTML body 크기와 일치해야 함)
|
||||||
|
pres.defineLayout({ name: 'CUSTOM', width: 10, height: 5.625 });
|
||||||
|
pres.layout = 'CUSTOM';
|
||||||
|
|
||||||
|
// ③ HTML 파일 변환
|
||||||
|
await html2pptx('/절대경로/slides/slide-01.html', pres);
|
||||||
|
|
||||||
|
// ④ PPTX 출력
|
||||||
|
await pres.writeFile({ fileName: '/절대경로/output.pptx' });
|
||||||
|
console.log('완료!');
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch(console.error);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.2 실전 예시: 여러 슬라이드 변환
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const path = require('path');
|
||||||
|
module.paths.unshift(
|
||||||
|
path.join(require('os').homedir(), '.claude/skills/pptx-skill/scripts/node_modules')
|
||||||
|
);
|
||||||
|
const PptxGenJS = require('pptxgenjs');
|
||||||
|
const html2pptx = require(
|
||||||
|
path.join(require('os').homedir(), '.claude/skills/pptx-skill/scripts/html2pptx.js')
|
||||||
|
);
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const pres = new PptxGenJS();
|
||||||
|
|
||||||
|
// 16:9 가로형
|
||||||
|
pres.defineLayout({ name: 'CUSTOM_16x9', width: 10, height: 5.625 });
|
||||||
|
pres.layout = 'CUSTOM_16x9';
|
||||||
|
|
||||||
|
const slidesDir = path.join(__dirname, 'slides');
|
||||||
|
|
||||||
|
// 변환할 HTML 파일 목록
|
||||||
|
const slides = [
|
||||||
|
'slide-01.html',
|
||||||
|
'slide-02.html',
|
||||||
|
'slide-03.html',
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const file of slides) {
|
||||||
|
console.log(`Converting ${file} ...`);
|
||||||
|
try {
|
||||||
|
await html2pptx(path.join(slidesDir, file), pres);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`Error: ${err.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const outputPath = path.join(__dirname, 'my-presentation.pptx');
|
||||||
|
await pres.writeFile({ fileName: outputPath });
|
||||||
|
console.log(`\nPPTX created: ${outputPath}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch(console.error);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.3 실전 예시: 번호 기반 자동 변환 (18슬라이드)
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// slide-01.html ~ slide-18.html 자동 변환
|
||||||
|
const totalSlides = 18;
|
||||||
|
for (let i = 1; i <= totalSlides; i++) {
|
||||||
|
const num = String(i).padStart(2, '0');
|
||||||
|
const htmlFile = path.join(slidesDir, `slide-${num}.html`);
|
||||||
|
await html2pptx(htmlFile, pres);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. 실행 방법
|
||||||
|
|
||||||
|
### 5.1 터미널에서 직접 실행
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 해당 폴더로 이동 후 실행
|
||||||
|
cd /home/aweso/sam/docs/brochure
|
||||||
|
node convert-2page.cjs
|
||||||
|
|
||||||
|
# 또는 절대 경로로 실행
|
||||||
|
node /home/aweso/sam/docs/brochure/convert-2page.cjs
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.2 실행 결과
|
||||||
|
|
||||||
|
```
|
||||||
|
Converting brochure-2page-front.html ...
|
||||||
|
Converting brochure-2page-back.html ...
|
||||||
|
|
||||||
|
PPTX created: /home/aweso/sam/docs/brochure/sam-brochure-2page.pptx
|
||||||
|
```
|
||||||
|
|
||||||
|
> 에러가 나면 HTML body 크기와 layout 설정 불일치가 가장 흔한 원인이다.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. 프로젝트 내 기존 사용 사례
|
||||||
|
|
||||||
|
| 경로 | 슬라이드 수 | 레이아웃 | 용도 |
|
||||||
|
|------|:-----------:|:--------:|------|
|
||||||
|
| `docs/usecase/` | 18장 | 16:9 가로 | 방화셔터 제안서 |
|
||||||
|
| `docs/usecase/brochure/` | 1장 / 2장 | 9:16 세로 | 방화셔터 브로셔 |
|
||||||
|
| `docs/brochure/` | 1장 / 2장 | 9:16 세로 | SAM 범용 브로셔 |
|
||||||
|
| `docs/plans/slides/` | N장 | 16:9 가로 | 배포 계획 발표 |
|
||||||
|
| `docs/rules/slides/` | N장 | 16:9 가로 | 정책 규칙 문서 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. 폴더 구조 권장 패턴
|
||||||
|
|
||||||
|
새 PPTX를 만들 때 아래 구조를 따른다:
|
||||||
|
|
||||||
|
```
|
||||||
|
docs/my-document/
|
||||||
|
├── slides/ ← HTML 슬라이드 파일들
|
||||||
|
│ ├── slide-01.html
|
||||||
|
│ ├── slide-02.html
|
||||||
|
│ └── slide-03.html
|
||||||
|
├── convert.cjs ← 변환 스크립트
|
||||||
|
└── my-document.pptx ← 생성된 PPTX (node convert.cjs 실행 후)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. 문제 해결
|
||||||
|
|
||||||
|
| 증상 | 원인 | 해결 |
|
||||||
|
|------|------|------|
|
||||||
|
| `Error: dimensions don't match` | HTML body 크기 ≠ layout 설정 | body의 width/height와 `defineLayout` 값 맞추기 |
|
||||||
|
| 텍스트가 PPTX에서 줄바꿈됨 | `white-space: nowrap` 미적용 | 단일행 `<p>` 태그에 nowrap 추가 |
|
||||||
|
| 이미지 안 보임 | 상대 경로 사용 | 절대 경로(`/home/aweso/...`)로 변경 |
|
||||||
|
| `Cannot find module 'pptxgenjs'` | module.paths 설정 누락 | 스크립트 상단 2줄(module.paths.unshift) 확인 |
|
||||||
|
| `Cannot find module 'playwright'` | Playwright 미설치 | `cd ~/.claude/skills/pptx-skill/scripts && npx playwright install chromium` |
|
||||||
|
| PPTX는 생성되지만 빈 슬라이드 | HTML 내용 없음 | HTML 파일을 브라우저로 열어 확인 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. 빠른 시작 (3분 가이드)
|
||||||
|
|
||||||
|
### Step 1: 폴더 만들기
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir -p /home/aweso/sam/docs/my-pptx/slides
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 2: HTML 슬라이드 만들기
|
||||||
|
|
||||||
|
`slides/slide-01.html` 파일 생성:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/static/pretendard.min.css">
|
||||||
|
<style>
|
||||||
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||||
|
body {
|
||||||
|
width: 720pt; height: 405pt;
|
||||||
|
font-family: 'Pretendard', sans-serif;
|
||||||
|
background: #0F2439;
|
||||||
|
padding: 60pt 80pt;
|
||||||
|
display: flex; flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p style="white-space: nowrap; font-size: 10pt; color: #10B981; margin-bottom: 12pt;">MY COMPANY</p>
|
||||||
|
<h1 style="font-size: 36pt; font-weight: 800; color: #ffffff;">발표 제목을 여기에</h1>
|
||||||
|
<p style="font-size: 14pt; color: rgba(255,255,255,0.5); margin-top: 16pt;">부제목 또는 설명</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 3: 변환 스크립트 만들기
|
||||||
|
|
||||||
|
`convert.cjs` 파일 생성:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const path = require('path');
|
||||||
|
module.paths.unshift(path.join(require('os').homedir(), '.claude/skills/pptx-skill/scripts/node_modules'));
|
||||||
|
const PptxGenJS = require('pptxgenjs');
|
||||||
|
const html2pptx = require(path.join(require('os').homedir(), '.claude/skills/pptx-skill/scripts/html2pptx.js'));
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const pres = new PptxGenJS();
|
||||||
|
pres.defineLayout({ name: 'CUSTOM_16x9', width: 10, height: 5.625 });
|
||||||
|
pres.layout = 'CUSTOM_16x9';
|
||||||
|
|
||||||
|
await html2pptx(path.join(__dirname, 'slides', 'slide-01.html'), pres);
|
||||||
|
|
||||||
|
await pres.writeFile({ fileName: path.join(__dirname, 'my-presentation.pptx') });
|
||||||
|
console.log('완료!');
|
||||||
|
}
|
||||||
|
main().catch(console.error);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 4: 실행
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /home/aweso/sam/docs/my-pptx
|
||||||
|
node convert.cjs
|
||||||
|
# → my-presentation.pptx 생성됨
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**최종 업데이트**: 2026-03-01
|
||||||
Reference in New Issue
Block a user