PPT Korean-style Design System
Overview
This is a PPT design system optimized for the Korean business environment.
It generates professional and highly readable slides using a font combination of Pretendard (sans-serif) + Chosun Ilbo Myeongjo (serif).
★ PPT Native Element Mapping (Core Principles)
Absolute Principle: Tabular data must use addTable(), and numerical comparisons/trends must use addChart().
Do not simulate tables/charts with shapes (addShape) and text (addText).
Element Selection Decision Tree
콘텐츠 유형 판별
├─ 행×열 구조의 데이터인가? ──────────→ addTable()
│ 예: 매출표, 일정표, 비교표, 가격표, 스펙표, 예산표
│
├─ 수치의 추이/비교/비율인가? ─────────→ addChart()
│ 예: 매출 추이, 점유율, KPI 변화, 분기 비교
│
├─ 외부 이미지/로고/사진인가? ─────────→ addImage()
│ 예: 제품 사진, 회사 로고, 스크린샷, 아이콘
│
├─ 단순 텍스트 블록인가? ──────────────→ addText()
│ 예: 제목, 본문 단락, 글머리 기호 목록, 인용문
│
├─ 장식/배경/구분선/도형인가? ─────────→ addShape()
│ 예: 배경 사각형, 구분선, 강조 바, 아이콘 원형
│
└─ 오디오/비디오인가? ─────────────────→ addMedia()
Element-by-Element Usage Mapping Table
| PptxGenJS Method |
Purpose |
Usage Scenario |
Absolutely Prohibited |
addTable() |
Structured data |
Sales tables, schedules, comparison tables, price tables, budget tables, spec tables, curriculum tables, staffing status, checklists |
Drawing grids with addShape() |
addChart() |
Data visualization |
Bar charts, line charts, pie charts, donut charts, area charts, radar charts, bubble charts, scatter plots |
Drawing bars/circles with addShape() |
addImage() |
Inserting external images |
Photos, logos, screenshots, SVG→PNG converted icons |
Putting base64 into addText |
addText() |
Text blocks |
Titles, body text, captions, bullet points, quotations, numbered lists |
Listing table data as text |
addShape() |
Decoration/layout assistance |
Background rectangles, divider lines, accent bars, card backgrounds, circular icon backgrounds |
Simulating tables/charts |
addNotes() |
Speaker notes |
Presentation scripts, reference notes, anticipated Q&A answers |
- |
⚠️ Common Mistakes and Corrections
❌ 잘못된 사용 (표를 도형으로 시뮬레이션)
──────────────────────────────
// 이렇게 하지 말 것!
slide.addShape('rect', { x:1, y:1, w:3, h:0.4, fill:'1A1F36' }); // 헤더 배경
slide.addText('항목', { x:1, y:1, w:1, h:0.4, color:'FFFFFF' }); // 헤더 텍스트
slide.addText('1월', { x:2, y:1, w:1, h:0.4, color:'FFFFFF' });
slide.addShape('line', { x:1, y:1.4, w:3, h:0 }); // 구분선
slide.addText('매출', { x:1, y:1.4, w:1, h:0.4 }); // 데이터 행
slide.addText('100M', { x:2, y:1.4, w:1, h:0.4 });
// ... 수십 줄의 반복 코드
✅ 올바른 사용 (네이티브 테이블)
──────────────────────────────
const rows = [
[{ text:'항목', options: STYLE.tableHeader },
{ text:'1월', options: STYLE.tableHeader }],
[{ text:'매출', options: STYLE.tableCell },
{ text:'100M', options: {...STYLE.tableCellRight} }]
];
slide.addTable(rows, TABLE_OPTIONS);
// 깔끔하고, PowerPoint에서 편집 가능
Font System
Default Fonts (included in the fonts/ folder)
| Font |
Purpose |
File Name |
Weight |
| Pretendard |
Sans-serif (titles/body/captions) |
Pretendard-{Weight}.otf |
9종: Thin(100)~Black(900) |
| Chosun Ilbo Myeongjo |
Serif (quotations/emphasis/formality) |
ChosunilboNM.ttf |
Regular |
Pretendard 9 Weight Guide
| Weight |
File Name |
Purpose |
| Thin (100) |
Pretendard-Thin.otf |
Decorative large text, watermarks |
| ExtraLight (200) |
Pretendard-ExtraLight.otf |
Subtitles (light theme), background text |
| Light (300) |
Pretendard-Light.otf |
Long body text, descriptive text |
| Regular (400) |
Pretendard-Regular.otf |
Default body text, captions |
| Medium (500) |
Pretendard-Medium.otf |
Emphasized body text, labels |
| SemiBold (600) |
Pretendard-SemiBold.otf |
Subheadings, card titles |
| Bold (700) |
Pretendard-Bold.otf |
Section titles, headers |
| ExtraBold (800) |
Pretendard-ExtraBold.otf |
Main titles, slide titles |
| Black (900) |
Pretendard-Black.otf |
KPI numbers, hero text |
Font Application Rules
제목(Title): Pretendard ExtraBold / Black 36~44pt
소제목(Subtitle): Pretendard SemiBold / Bold 24~28pt
본문(Body): Pretendard Regular / Medium 16~20pt
캡션(Caption): Pretendard Light / Regular 12~14pt
인용구(Quote): 조선일보명조 Regular 18~22pt (격식체, 인용문)
숫자강조(KPI): Pretendard Black 48~72pt
장식텍스트: Pretendard Thin / ExtraLight 60~120pt (배경 대형 텍스트)
Letter Spacing & Line Height
| Element |
Letter Spacing |
Line spacing |
PptxGenJS properties |
| Title |
-0.5pt ~ 0pt |
1.1배 |
charSpacing: -0.5, lineSpacingMultiple: 1.1 |
| Subtitle |
0pt |
1.2배 |
lineSpacingMultiple: 1.2 |
| Body |
0.5pt ~ 1pt |
1.4~1.5배 |
charSpacing: 0.5, lineSpacingMultiple: 1.4 |
| Caption |
0.5pt |
1.3배 |
charSpacing: 0.5, lineSpacingMultiple: 1.3 |
Color system (6:3:1 rule)
Default palette: "Midnight Executive"
const COLORS = {
// 메인 60% - 배경/여백
bg_primary: 'FFFFFF', // 흰 배경
bg_secondary: 'F5F7FA', // 연한 회색 배경
bg_dark: '1A1F36', // 다크 네이비 (표지/구분)
// 보조 30% - 본문/구조
text_primary: '1A1F36', // 제목 텍스트 (다크 네이비)
text_secondary: '4A5568', // 본문 텍스트 (차콜 그레이)
text_tertiary: '718096', // 캡션/보조 (미디엄 그레이)
text_on_dark: 'FFFFFF', // 다크 배경 위 텍스트
// 강조 10% - 포인트/CTA
accent_blue: '4A7BF7', // 메인 블루
accent_cyan: '00D4AA', // 시안 그린
accent_yellow: 'FFB020', // 골드 옐로우
accent_red: 'FF6B6B', // 경고/주의 레드
accent_purple: '8B5CF6', // 보조 퍼플
};
Additional palette options
"Warm Corporate" (warm corporate tone)
const WARM = {
bg_dark: '2D1B4E', accent_blue: 'E8725A', accent_cyan: '4ECDC4',
accent_yellow: 'F9C74F', accent_purple: '7B68EE'
};
"Nature Green" (eco-friendly/ESG)
const GREEN = {
bg_dark: '1B3A2D', accent_blue: '2D9CDB', accent_cyan: '27AE60',
accent_yellow: 'F2C94C', accent_red: 'EB5757'
};
"Minimal Mono" (minimal)
const MONO = {
bg_dark: '111111', accent_blue: '333333', accent_cyan: '666666',
accent_yellow: 'AAAAAA', text_primary: '111111'
};
Layout system
Slide size
- For presentations: 16:9 (default, width 13.33" x height 7.5")
- For print/reports: A4 (width 11.69" x height 8.27")
Margin guide (based on 16:9)
+-------------------------------------+
| 0.6" 0.6" |
| +-------------------------------+ |
| | 콘텐츠 영역 | | 상단: 0.5"
| | 가로: 12.13" / 세로: 6.5" | |
| | | |
| +-------------------------------+ |
| 0.5" | 하단: 0.5"
+-------------------------------------+
| Area |
Position (x, y) |
Size (w, h) |
| Left margin |
x=0.6" |
- |
| Right margin |
- |
w=12.13" (13.33-0.6-0.6) |
| Top margin |
y=0.5" |
- |
| Bottom margin |
- |
h=6.5" (7.5-0.5-0.5) |
| Page number |
x=12.3", y=7.1" |
fontSize: 9pt |
Core layout patterns
1. Title Slide
[다크 배경 전체]
중앙: 과정명 (44pt ExtraBold, 흰색)
중앙 아래: 부제목 (20pt Regular, 70% 흰색)
하단: 발표자/날짜 (14pt, 50% 흰색)
2. Section Divider
[좌측 40% 다크 배경 | 우측 60% 밝은 배경]
좌측: 섹션 번호 (72pt Black, accent_cyan)
우측: 섹션 제목 (36pt Bold) + 설명 (16pt Regular)
3. Two Column
[상단: 제목 바]
[좌측 50% | 우측 50%]
각 컬럼: 소제목 (24pt Bold) + 본문 (16pt Regular)
컬럼 간격: 0.4"
4. Card Grid
[상단: 제목 바]
[2x2 또는 2x3 카드 그리드]
카드: 둥근 모서리 (rectRadius=0.1)
카드 간격: 0.3"
카드 헤더: accent 색상 바 (높이 0.08")
5. Timeline
[상단: 제목 바]
[좌측 색상 바 | 우측 항목 리스트]
각 항목: 시간 (Bold) + 내용 (Regular) + 구분선
6. KPI Dashboard (Data Dashboard)
[상단: 제목 바]
[3~4열 KPI 카드]
숫자: Pretendard Black 48pt (accent 색상)
라벨: Regular 14pt (text_tertiary)
[하단: 차트/그래프 영역] → addChart() 사용
7. Data Table — NEW
[상단: 제목 바]
[네이티브 테이블] → addTable() 사용
헤더: 다크 배경 + 흰색 텍스트
짝수행: 연한 회색 배경 (zebra stripe)
합계행: bold + 상단 border 강조
8. Chart & Insight — NEW
[상단: 제목 바]
[좌측 60%: addChart() | 우측 40%: 핵심 인사이트 텍스트]
인사이트: 숫자 강조 (KPI 스타일) + 짧은 설명
PptxGenJS native element detailed guide
1. addTable() — Table
For tabular data, always addTable()use this.
Table style constants
// ===== 테이블 공통 스타일 =====
const TABLE_STYLE = {
// 헤더 셀 (다크 배경)
header: {
bold: true,
fill: { color: COLORS.bg_dark },
color: COLORS.text_on_dark,
fontFace: 'Pretendard',
fontSize: 11,
align: 'center',
valign: 'middle'
},
// 일반 셀
cell: {
fontFace: 'Pretendard',
fontSize: 11,
color: COLORS.text_secondary,
valign: 'middle'
},
// 숫자 셀 (우측 정렬)
cellRight: {
fontFace: 'Pretendard',
fontSize: 11,
color: COLORS.text_secondary,
align: 'right',
valign: 'middle'
},
// 짝수행 배경 (zebra stripe)
cellAlt: {
fontFace: 'Pretendard',
fontSize: 11,
color: COLORS.text_secondary,
fill: { color: COLORS.bg_secondary },
valign: 'middle'
},
// 합계/소계 행
cellTotal: {
bold: true,
fontFace: 'Pretendard',
fontSize: 11,
color: COLORS.text_primary,
border: [{ type: 'solid', pt: 1.5, color: COLORS.text_primary }, null, null, null],
valign: 'middle'
}
};
// 테이블 공통 옵션
const TABLE_OPTIONS = {
x: 0.6,
y: 1.8, // 제목 바 아래
w: 12.13,
border: { type: 'solid', pt: 0.5, color: 'E2E8F0' },
autoPage: false,
margin: [5, 8, 5, 8] // 셀 내부 여백 [top, right, bottom, left] pt
};
Table helper functions
/**
* 디자인 시스템 적용된 테이블 추가
* @param {Slide} slide
* @param {string[]} headers - 헤더 텍스트 배열
* @param {Array<Array<string|{text,options}>>} dataRows - 데이터 행 배열
* @param {object} opts - 추가 옵션 (x, y, w, colW, rowH 등)
*/
function addStyledTable(slide, headers, dataRows, opts = {}) {
const rows = [];
// 헤더 행
rows.push(headers.map(h => ({
text: h,
options: { ...TABLE_STYLE.header }
})));
// 데이터 행 (zebra stripe 자동 적용)
dataRows.forEach((row, i) => {
const isAlt = i % 2 === 1;
const baseStyle = isAlt ? TABLE_STYLE.cellAlt : TABLE_STYLE.cell;
rows.push(row.map(cell => {
if (typeof cell === 'string') {
return { text: cell, options: { ...baseStyle } };
}
// {text, options} 객체인 경우 스타일 병합
return { text: cell.text, options: { ...baseStyle, ...cell.options } };
}));
});
slide.addTable(rows, {
...TABLE_OPTIONS,
...opts
});
}
/**
* colspan을 활용한 제목 행 포함 테이블
*/
function addTitledTable(slide, tableTitle, headers, dataRows, opts = {}) {
const colCount = headers.length;
const rows = [];
// 테이블 제목 행 (colspan 병합)
rows.push([{
text: tableTitle,
options: {
colspan: colCount,
bold: true,
fill: { color: COLORS.bg_dark },
color: COLORS.text_on_dark,
fontFace: 'Pretendard',
fontSize: 13,
align: 'center',
valign: 'middle'
}
}]);
// 헤더 행
rows.push(headers.map(h => ({
text: h,
options: {
bold: true,
fill: { color: COLORS.bg_secondary },
color: COLORS.text_primary,
fontFace: 'Pretendard',
fontSize: 11,
align: 'center',
valign: 'middle'
}
})));
// 데이터 행
dataRows.forEach((row, i) => {
const isAlt = i % 2 === 1;
rows.push(row.map(cell => {
const base = isAlt
? { ...TABLE_STYLE.cellAlt }
: { ...TABLE_STYLE.cell };
if (typeof cell === 'string') return { text: cell, options: base };
return { text: cell.text, options: { ...base, ...cell.options } };
}));
});
slide.addTable(rows, { ...TABLE_OPTIONS, ...opts });
}
Table usage example
// ===== 매출 계획표 =====
addTitledTable(slide, '2026년 매출 계획표',
['구분', 'Q1', 'Q2', 'Q3', 'Q4', '연간 합계'],
[
['온라인',
{ text: '120M', options: { align: 'right' } },
{ text: '150M', options: { align: 'right' } },
{ text: '180M', options: { align: 'right' } },
{ text: '200M', options: { align: 'right' } },
{ text: '650M', options: { align: 'right', bold: true } }],
['오프라인',
{ text: '80M', options: { align: 'right' } },
{ text: '90M', options: { align: 'right' } },
{ text: '100M', options: { align: 'right' } },
{ text: '110M', options: { align: 'right' } },
{ text: '380M', options: { align: 'right', bold: true } }],
],
{ colW: [2, 1.8, 1.8, 1.8, 1.8, 2.5] }
);
// ===== 일정표/시간표 =====
addStyledTable(slide,
['교시', '시간', '과목', '내용', '비고'],
[
['1', '09:00~09:50', 'Excel 기초', '데이터 정리 자동화', '이론'],
['2', '10:00~10:50', 'Excel 분석', 'KPI 대시보드 설계', '이론+실습'],
['3', '11:00~11:50', 'PPT 생성', '슬라이드 자동 변환', '이론'],
],
{ colW: [1, 2.2, 2.5, 4, 1.8], rowH: [0.45, 0.4, 0.4, 0.4] }
);
// ===== 비교표 (스펙/기능 비교) =====
addStyledTable(slide,
['기능', 'Free', 'Pro', 'Enterprise'],
[
['스킬 수', '12개', { text: '24개', options: { bold: true, color: COLORS.accent_blue } }, '무제한'],
['지원', '커뮤니티', '이메일', { text: '전담 매니저', options: { bold: true } }],
['가격', '무료', '월 9,900원', '문의'],
]
);
2. addChart() — Chart
For trends/comparisons/proportions of numerical data, always addChart()use this.
Chart style constants
// ===== 차트 공통 스타일 =====
const CHART_STYLE = {
// 공통 기본값
base: {
showTitle: true,
titleFontFace: 'Pretendard',
titleFontSize: 14,
titleColor: COLORS.text_primary,
showLegend: true,
legendFontFace: 'Pretendard',
legendFontSize: 9,
legendColor: COLORS.text_secondary,
catAxisLabelFontFace: 'Pretendard',
catAxisLabelFontSize: 10,
catAxisLabelColor: COLORS.text_tertiary,
valAxisLabelFontFace: 'Pretendard',
valAxisLabelFontSize: 10,
valAxisLabelColor: COLORS.text_tertiary,
},
// 차트 색상 팔레트 (최대 6색)
colors: [
COLORS.accent_blue, // 4A7BF7
COLORS.accent_cyan, // 00D4AA
COLORS.accent_yellow, // FFB020
COLORS.accent_red, // FF6B6B
COLORS.accent_purple, // 8B5CF6
'38BDF8' // 라이트 블루 (6번째)
]
};
Usage criteria by chart type
| Data type |
Chart type |
PptxGenJS constant |
| Size comparison by item |
Vertical bar |
pptx.charts.BAR |
| Time-series trend/change |
Line |
pptx.charts.LINE |
| Proportion of total (5 or fewer) |
Pie |
pptx.charts.PIE |
| Proportion of total (central KPI) |
Doughnut |
pptx.charts.DOUGHNUT |
| Trend + cumulative amount |
Area |
pptx.charts.AREA |
| Multidimensional item comparison |
Radar |
pptx.charts.RADAR |
| Relationship between two variables |
Scatter |
pptx.charts.SCATTER |
| Relationship among three variables |
Bubble |
pptx.charts.BUBBLE |
Chart helper functions
/**
* 디자인 시스템 적용된 차트 추가
* @param {Slide} slide
* @param {object} pptx - PptxGenJS 인스턴스 (charts 참조용)
* @param {string} type - 'BAR'|'LINE'|'PIE'|'DOUGHNUT'|'AREA'|'RADAR'|'SCATTER'
* @param {Array} chartData - [{name, labels, values}]
* @param {object} opts - 위치/크기/추가 옵션
*/
function addStyledChart(slide, pptx, type, chartData, opts = {}) {
const typeMap = {
BAR: pptx.charts.BAR,
LINE: pptx.charts.LINE,
PIE: pptx.charts.PIE,
DOUGHNUT: pptx.charts.DOUGHNUT,
AREA: pptx.charts.AREA,
RADAR: pptx.charts.RADAR,
SCATTER: pptx.charts.SCATTER,
BUBBLE: pptx.charts.BUBBLE
};
const defaults = {
x: 0.6, y: 1.8, w: 12.13, h: 5.0,
...CHART_STYLE.base,
chartColors: CHART_STYLE.colors.slice(0, chartData.length || 6),
...opts
};
// 차트 유형별 기본값 추가
if (type === 'BAR') {
defaults.barGapWidthPct = 80;
defaults.catAxisOrientation = 'minMax';
defaults.valAxisOrientation = 'minMax';
}
if (type === 'LINE') {
defaults.lineDataSymbol = 'circle';
defaults.lineDataSymbolSize = 8;
defaults.lineSmooth = false;
}
if (type === 'PIE' || type === 'DOUGHNUT') {
defaults.showPercent = true;
defaults.showLegend = true;
defaults.legendPos = 'b';
defaults.chartColors = CHART_STYLE.colors.slice(0, chartData[0]?.values?.length || 6);
}
slide.addChart(typeMap[type], chartData, defaults);
}
Chart usage example
// ===== 매출 추이 (꺾은선) =====
addStyledChart(slide, pptx, 'LINE',
[{ name: '매출(억)', labels: ['1월','2월','3월','4월','5월','6월'],
values: [12, 15, 18, 22, 25, 30] }],
{ x: 0.6, y: 1.8, w: 7, h: 4.5, title: '2026 상반기 매출 추이' }
);
// ===== 부서별 비교 (막대) =====
addStyledChart(slide, pptx, 'BAR',
[
{ name: '목표', labels: ['영업','마케팅','개발','인사'], values: [100, 80, 120, 50] },
{ name: '실적', labels: ['영업','마케팅','개발','인사'], values: [95, 85, 110, 48] }
],
{ x: 0.6, y: 1.8, w: 12.13, h: 5, title: '부서별 목표 대비 실적' }
);
// ===== 구성비 (원형) =====
addStyledChart(slide, pptx, 'PIE',
[{ name: '점유율', labels: ['제품A','제품B','제품C','기타'],
values: [45, 25, 20, 10] }],
{ x: 3, y: 1.5, w: 7, h: 5, title: '제품별 매출 구성비' }
);
// ===== 진행률 (도넛) =====
addStyledChart(slide, pptx, 'DOUGHNUT',
[{ name: '진행률', labels: ['완료','잔여'], values: [75, 25] }],
{ x: 4, y: 2, w: 5, h: 4, title: '프로젝트 진행률',
chartColors: [COLORS.accent_cyan, 'E2E8F0'] }
);
3. addImage() — Image
// 파일 경로로 삽입
slide.addImage({
path: '/path/to/image.png',
x: 0.6, y: 1.8, w: 5, h: 3.5
});
// SVG → PNG 변환 후 삽입 (sharp 라이브러리)
const sharp = require('sharp');
const svgBuffer = Buffer.from(svgString);
const pngBuffer = await sharp(svgBuffer).png().toBuffer();
const base64 = pngBuffer.toString('base64');
slide.addImage({
data: 'image/png;base64,' + base64,
x: 0.6, y: 1.8, w: 5, h: 3.5
});
// 로고 삽입 (우측 하단)
slide.addImage({
path: '/path/to/logo.png',
x: 11.5, y: 6.8, w: 1.2, h: 0.5
});
4. addText() — Text
// 제목
slide.addText('슬라이드 제목', {
x: 0.6, y: 0.65, w: 10, h: 0.6,
fontSize: 28, fontFace: 'Pretendard', bold: true,
color: COLORS.text_primary, charSpacing: -0.3
});
// 글머리 기호 목록 (bullet: true)
slide.addText([
{ text: '첫 번째 항목', options: { bullet: true, indentLevel: 0 } },
{ text: '두 번째 항목', options: { bullet: true, indentLevel: 0 } },
{ text: '하위 항목', options: { bullet: true, indentLevel: 1 } },
], {
x: 0.6, y: 1.8, w: 12.13, h: 4,
fontSize: 16, fontFace: 'Pretendard',
color: COLORS.text_secondary,
lineSpacingMultiple: 1.5,
paraSpaceAfter: 6
});
// 번호 목록 (bullet: {type:'number'})
slide.addText([
{ text: '첫 번째 단계', options: { bullet: { type: 'number' } } },
{ text: '두 번째 단계', options: { bullet: { type: 'number' } } },
], {
x: 0.6, y: 1.8, w: 12.13, h: 3,
fontSize: 16, fontFace: 'Pretendard',
color: COLORS.text_secondary
});
// 인용문 (명조체)
slide.addText('\u201C변화를 두려워하지 마세요\u201D', {
x: 2, y: 3, w: 9, h: 1,
fontSize: 22, fontFace: 'ChosunilboNM', italic: true,
color: COLORS.text_tertiary, align: 'center'
});
5. addShape() — Decoration/layout assistance only
// ✅ 올바른 사용: 배경 영역
slide.addShape('rect', {
x: 0, y: 0, w: '100%', h: '100%',
fill: { color: COLORS.bg_dark }
});
// ✅ 올바른 사용: 악센트 라인
slide.addShape('rect', {
x: 0.6, y: 0.5, w: 1.2, h: 0.06,
fill: { color: COLORS.accent_blue }
});
// ✅ 올바른 사용: 카드 배경 (둥근 모서리)
slide.addShape('roundRect', {
x: 0.6, y: 1.8, w: 5.5, h: 3,
rectRadius: 0.1,
fill: { color: 'FFFFFF' },
shadow: { type: 'outer', blur: 6, offset: 2, color: '00000015' }
});
// ✅ 올바른 사용: 구분선
slide.addShape('line', {
x: 0.6, y: 4, w: 12.13, h: 0,
line: { color: 'E2E8F0', width: 0.5 }
});
// ❌ 금지: 표 격자를 도형으로 그리기
// ❌ 금지: 막대 차트를 사각형으로 그리기
6. addNotes() — Speaker notes
slide.addNotes('이 슬라이드에서는 매출 추이를 설명합니다.\n핵심 포인트: Q3 성장률 40% 강조');
PptxGenJS code conventions
Font embedding
// Pretendard OTF 경로 (스킬 폴더 기준)
const FONT_DIR = path.join(__dirname, 'fonts');
// PptxGenJS에서 폰트 등록은 지원하지 않으므로
// 시스템에 Pretendard가 설치된 환경에서 다음과 같이 사용:
const FONTS = {
title: { fontFace: 'Pretendard', bold: true }, // ExtraBold/Black
subtitle: { fontFace: 'Pretendard', bold: true }, // SemiBold/Bold
body: { fontFace: 'Pretendard', bold: false }, // Regular/Medium
caption: { fontFace: 'Pretendard', bold: false }, // Light/Regular
serif: { fontFace: 'ChosunilboNM', bold: false }, // 조선일보명조
kpi: { fontFace: 'Pretendard', bold: true }, // Black
deco: { fontFace: 'Pretendard', bold: false }, // Thin/ExtraLight (장식용)
};
Slide creation helper
// 표준 제목 바 추가 함수
function addTitleBar(slide, title, subtitle = '') {
// 얇은 accent 라인
slide.addShape('rect', {
x: 0.6, y: 0.5, w: 1.2, h: 0.06,
fill: { color: COLORS.accent_blue }
});
// 제목
slide.addText(title, {
x: 0.6, y: 0.65, w: 10, h: 0.6,
fontSize: 28, fontFace: 'Pretendard', bold: true,
color: COLORS.text_primary, charSpacing: -0.3
});
// 부제목 (있을 경우)
if (subtitle) {
slide.addText(subtitle, {
x: 0.6, y: 1.25, w: 10, h: 0.4,
fontSize: 16, fontFace: 'Pretendard',
color: COLORS.text_tertiary
});
}
}
// 카드 생성 함수 (장식용 - 데이터 표시 아님)
function addCard(slide, { x, y, w, h, title, body, accentColor }) {
// 카드 배경
slide.addShape('roundRect', {
x, y, w, h, rectRadius: 0.1,
fill: { color: 'FFFFFF' },
shadow: { type: 'outer', blur: 6, offset: 2, color: '00000015' }
});
// 상단 accent 바
slide.addShape('rect', {
x: x + 0.02, y, w: w - 0.04, h: 0.06,
fill: { color: accentColor || COLORS.accent_blue }
});
// 카드 제목
slide.addText(title, {
x: x + 0.2, y: y + 0.2, w: w - 0.4, h: 0.35,
fontSize: 16, fontFace: 'Pretendard', bold: true,
color: COLORS.text_primary
});
// 카드 본문
slide.addText(body, {
x: x + 0.2, y: y + 0.55, w: w - 0.4, h: h - 0.75,
fontSize: 13, fontFace: 'Pretendard',
color: COLORS.text_secondary,
lineSpacingMultiple: 1.4, valign: 'top'
});
}
// 페이지 번호 추가
function addPageNumber(slide, num, total) {
slide.addText(`${num} / ${total}`, {
x: 12.0, y: 7.05, w: 1.0, h: 0.3,
fontSize: 9, fontFace: 'Pretendard',
color: COLORS.text_tertiary, align: 'right'
});
}
Practical slide composition patterns
Pattern A: Sales report slide
const slide = pptx.addSlide();
addTitleBar(slide, '월별 매출 실적', '2026년 상반기');
// 상단: KPI 카드 3개 (addText + addShape)
// → KPI 숫자는 카드 형태 = addShape(배경) + addText(숫자)
['매출 합계|₩3.2B', '전년 대비|+18%', '목표 달성률|94%'].forEach((item, i) => {
const [label, value] = item.split('|');
addCard(slide, {
x: 0.6 + i * 4.1, y: 1.6, w: 3.8, h: 1.2,
title: label, body: value, accentColor: CHART_STYLE.colors[i]
});
});
// 중단: 데이터 테이블 (addTable)
addStyledTable(slide, ['월','매출','비용','영업이익','이익률'], [...data],
{ y: 3.1, rowH: [0.4, 0.35, 0.35, 0.35, 0.35, 0.35, 0.35] });
Pattern B: Comparative Analysis Slide
const slide = pptx.addSlide();
addTitleBar(slide, '경쟁사 비교 분석');
// 좌측: 비교 차트 (addChart)
addStyledChart(slide, pptx, 'BAR',
[
{ name: '자사', labels: ['가격','품질','서비스','인지도'], values: [85, 92, 88, 70] },
{ name: '경쟁A', labels: ['가격','품질','서비스','인지도'], values: [90, 80, 75, 85] }
],
{ x: 0.6, y: 1.6, w: 6.5, h: 5.2, title: '' }
);
// 우측: 비교표 (addTable)
addStyledTable(slide, ['항목','자사','경쟁A'],
[['가격', '85', '90'], ['품질', '92', '80'], ['서비스', '88', '75']],
{ x: 7.5, y: 1.6, w: 5.2 }
);
Pattern C: Project Status Dashboard
const slide = pptx.addSlide();
addTitleBar(slide, '프로젝트 현황 대시보드', '2026년 2월 기준');
// 좌측 상단: 진행률 도넛 (addChart)
addStyledChart(slide, pptx, 'DOUGHNUT',
[{ name: '진행', labels: ['완료','잔여'], values: [72, 28] }],
{ x: 0.6, y: 1.6, w: 4, h: 3, showTitle: false,
chartColors: [COLORS.accent_cyan, 'E2E8F0'] }
);
// 우측 상단: 주간 이슈 테이블 (addTable)
addStyledTable(slide, ['이슈','담당','상태','기한'],
[['서버 지연', '김개발', '진행중', '2/20'],
['UI 버그', '박디자', '완료', '2/18']],
{ x: 5, y: 1.6, w: 7.7, rowH: [0.4, 0.35, 0.35] }
);
// 하단: 마일스톤 타임라인 (addTable로 구현 가능)
addStyledTable(slide, ['마일스톤','시작일','종료일','진행률','상태'],
[['기획', '1/15', '2/10', '100%', '\u2705'],
['개발', '2/11', '4/30', '45%', '\uD83D\uDD27'],
['테스트', '5/1', '5/31', '0%', '\u23F3']],
{ x: 0.6, y: 5.0, w: 12.13, rowH: [0.4, 0.35, 0.35, 0.35] }
);
6x6 Content Rule
- Per slide maximum 6 bullet points
- Per bullet within 6 words (15-18 characters in Korean)
- On one slide, 1 key message
- If there is a lot of text, split into 2 slides
Image & Icon Guide
Use SVG Icons
- Separately,
svg-diagram use skills to generate SVG icons/diagrams matching the theme colors
- Convert SVG → PNG then insert (using the sharp library)
Hero Image Rules
- Use only 1 key image per slide
- Image selection criteria: topic, mood, resolution, color, whitespace
2025-2026 Korean-style PPT Design Trends
Functional Minimalism
- Exclude unnecessary decorative elements, content-centered design
- Actively utilize wide white space
- Strengthen the one slide = one message principle
- Background: pure white (#FFFFFF) or light gray (#F5F7FA~#F8FAFC) as default
Modular Layout
- Card-based grid system (2x2, 2x3, 1+2 variations)
- Place independent information units in each module
- Maintain consistent spacing between modules (0.3"~0.4")
- Ensure consistency with repeatable layout patterns
Accent Color Strategy
- Background: light gray (#F5F7FA)
- Accent: navy (#1A1F36) + 1-2 accent colors
- 6:3:1 rule: background 60% + text 30% + emphasis 10%
- Prefer flat solid colors over gradients
Typography
- Pretendard -- established as the standard font for Korean business PPT
- Fine weight control through the use of variable fonts
- Titles in ExtraBoldBlack, body text in RegularMedium
- Letter spacing: tight for titles (-0.5pt), slightly wider for body text (+0.5pt)
- Use serif fonts (Chosun Ilbo Myeongjo) as accents only for quotes/emphasis
Enhanced Data Visualization
- Number-centered KPI card layouts are popular
- 2D flat charts instead of complex 3D charts (using addChart)
- Infographic style combining icons + numbers
- Linked with SVG diagrams (using the svg-diagram skill)
Checklist (QA)
After generating the document, check the following:
- Title 3644pt / subtitle 2428pt / body text within 16~20pt range
- Letter spacing: title -0.50pt / body text 0.51pt
- Line spacing: body text 1.4x or more
- Comply with 6:3:1 color ratio
- Margins: left/right 0.6" / top/bottom 0.5" or more
- One slide, one message principle
- Within 2 font types (Pretendard + Myeongjo)
- No Korean text corruption (check Pretendard font)
- Table data → check use of addTable() (shape simulation prohibited)
- Numeric comparison/trend → check use of addChart() (shape simulation prohibited)
- Image/logo → check use of addImage()
- Bullet points → check use of addText({ bullet: true })