From 2715daa87cbb4cc43c4ef8d6f4dd2e2a4d756e5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Sun, 22 Mar 2026 20:35:47 +0900 Subject: [PATCH] =?UTF-8?q?docs:=20[barobill]=20=EC=9D=B4=EA=B4=80=20?= =?UTF-8?q?=EC=99=84=EB=A3=8C=20=EB=B3=B4=EA=B3=A0=20PPT=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20(7=EC=8A=AC=EB=9D=BC=EC=9D=B4=EB=93=9C)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- presentations/barobill-migration-report.cjs | 461 +++++++++++++++++++ presentations/barobill-migration-report.pptx | Bin 0 -> 305894 bytes 2 files changed, 461 insertions(+) create mode 100644 presentations/barobill-migration-report.cjs create mode 100644 presentations/barobill-migration-report.pptx diff --git a/presentations/barobill-migration-report.cjs b/presentations/barobill-migration-report.cjs new file mode 100644 index 0000000..45c25e3 --- /dev/null +++ b/presentations/barobill-migration-report.cjs @@ -0,0 +1,461 @@ +const path = require('path'); +module.paths.unshift(path.join(require('os').homedir(), '.claude/skills/pptx-skill/scripts/node_modules')); + +const PptxGenJS = require('pptxgenjs'); + +async function main() { + const pres = new PptxGenJS(); + pres.defineLayout({ name: 'CUSTOM_16x9', width: 10, height: 5.625 }); + pres.layout = 'CUSTOM_16x9'; + + // ─── 색상 팔레트 ─── + const C = { + dark: '1a1a2e', + darkBlue: '16213e', + accent: '0f3460', + blue: '2196F3', + green: '4CAF50', + greenBg: 'E8F5E9', + greenDark: '2E7D32', + orange: 'FF9800', + orangeBg: 'FFF3E0', + red: 'E53935', + redBg: 'FFEBEE', + purple: '7C4DFF', + purpleBg: 'EDE7F6', + white: 'FFFFFF', + gray: '666666', + grayLight: 'F5F5F5', + grayBorder: 'E0E0E0', + text: '333333', + textLight: '999999', + }; + + // ─── 공통 함수 ─── + function addFooter(slide, pageNum, totalPages) { + slide.addShape(pres.ShapeType.rect, { x: 0, y: 5.25, w: 10, h: 0.375, fill: { color: C.dark } }); + slide.addText('SAM 바로빌 서비스 이관 보고 | (주)코드브릿지엑스', { + x: 0.5, y: 5.25, w: 7, h: 0.375, fontSize: 7, color: '888888', fontFace: 'Arial' + }); + slide.addText(`${pageNum} / ${totalPages}`, { + x: 8.5, y: 5.25, w: 1, h: 0.375, fontSize: 7, color: '888888', align: 'right', fontFace: 'Arial' + }); + } + + function addPageTitle(slide, title, subtitle) { + slide.addShape(pres.ShapeType.rect, { x: 0.5, y: 0.35, w: 0.06, h: 0.35, fill: { color: C.blue } }); + slide.addText(title, { x: 0.7, y: 0.28, w: 6, h: 0.5, fontSize: 20, bold: true, color: C.dark, fontFace: 'Arial' }); + if (subtitle) { + slide.addText(subtitle, { x: 0.7, y: 0.72, w: 6, h: 0.3, fontSize: 10, color: C.gray, fontFace: 'Arial' }); + } + } + + function addBadge(slide, x, y, text, bgColor, textColor) { + slide.addShape(pres.ShapeType.roundRect, { x, y, w: text.length * 0.12 + 0.4, h: 0.28, rectRadius: 0.04, fill: { color: bgColor } }); + slide.addText(text, { x, y, w: text.length * 0.12 + 0.4, h: 0.28, fontSize: 8, bold: true, color: textColor, align: 'center', fontFace: 'Arial' }); + } + + const totalPages = 7; + + // ═══════════════════════════════════════════════════════════════ + // 슬라이드 1: 표지 + // ═══════════════════════════════════════════════════════════════ + const slide1 = pres.addSlide(); + slide1.background = { fill: C.dark }; + + // 상단 장식 라인 + slide1.addShape(pres.ShapeType.rect, { x: 0, y: 0, w: 10, h: 0.06, fill: { color: C.blue } }); + + // BI 로고 + slide1.addImage({ path: '/home/aweso/sam/docs/assets/bi/sam_bi_white.png', x: 0.7, y: 0.5, w: 1.2, h: 0.5 }); + + // 제목 영역 + slide1.addText('바로빌 서비스 이관 완료 보고', { + x: 0.7, y: 1.8, w: 8.6, h: 0.8, + fontSize: 32, bold: true, color: C.white, fontFace: 'Arial' + }); + slide1.addText('MNG → API 전체 기능 이관 현황', { + x: 0.7, y: 2.6, w: 8.6, h: 0.5, + fontSize: 16, color: C.blue, fontFace: 'Arial' + }); + + // 구분선 + slide1.addShape(pres.ShapeType.rect, { x: 0.7, y: 3.3, w: 2, h: 0.03, fill: { color: C.blue } }); + + // 메타 정보 + slide1.addText([ + { text: '보고일: 2026-03-22', options: { fontSize: 11, color: '888888' } }, + { text: '\n담당: R&D실', options: { fontSize: 11, color: '888888' } }, + { text: '\n프로젝트: SAM (Smart Automation Management)', options: { fontSize: 11, color: '888888' } }, + ], { x: 0.7, y: 3.6, w: 5, h: 1.2, fontFace: 'Arial', lineSpacingMultiple: 1.5 }); + + // 우측 하이라이트 카드 + slide1.addShape(pres.ShapeType.roundRect, { x: 6.5, y: 3.5, w: 3, h: 1.4, rectRadius: 0.15, fill: { color: '222244' }, line: { color: C.blue, width: 1 } }); + slide1.addText('이관 완료', { x: 6.5, y: 3.6, w: 3, h: 0.3, fontSize: 10, color: C.blue, align: 'center', fontFace: 'Arial' }); + slide1.addText('12 / 12', { x: 6.5, y: 3.9, w: 3, h: 0.5, fontSize: 36, bold: true, color: C.green, align: 'center', fontFace: 'Arial' }); + slide1.addText('100% 완료', { x: 6.5, y: 4.4, w: 3, h: 0.3, fontSize: 12, bold: true, color: C.green, align: 'center', fontFace: 'Arial' }); + + addFooter(slide1, 1, totalPages); + + // ═══════════════════════════════════════════════════════════════ + // 슬라이드 2: 이관 배경 및 목적 + // ═══════════════════════════════════════════════════════════════ + const slide2 = pres.addSlide(); + slide2.background = { fill: C.white }; + addPageTitle(slide2, '이관 배경 및 목적', 'MNG 백오피스 → API 서비스 전환'); + + // 아키텍처 다이어그램 + // Before 카드 + slide2.addShape(pres.ShapeType.roundRect, { x: 0.5, y: 1.3, w: 4.2, h: 3.5, rectRadius: 0.12, fill: { color: C.redBg }, line: { color: 'FFCDD2', width: 1 } }); + addBadge(slide2, 1.6, 1.45, 'AS-IS', C.red, C.white); + slide2.addText('MNG 단독 운영', { x: 0.7, y: 1.9, w: 3.8, h: 0.35, fontSize: 14, bold: true, color: C.red, align: 'center', fontFace: 'Arial' }); + + const asIsItems = [ + 'SOAP 연동이 MNG에만 존재', + '테넌트 1 (본사) 전용 사용', + '서비스 앱(React)에서 접근 불가', + '멀티테넌트 SaaS 제공 불가', + ]; + asIsItems.forEach((item, i) => { + slide2.addShape(pres.ShapeType.roundRect, { x: 0.7, y: 2.5 + i * 0.55, w: 3.8, h: 0.42, rectRadius: 0.06, fill: { color: C.white } }); + slide2.addShape(pres.ShapeType.ellipse, { x: 0.85, y: 2.58 + i * 0.55, w: 0.22, h: 0.22, fill: { color: C.red } }); + slide2.addText('x', { x: 0.85, y: 2.56 + i * 0.55, w: 0.22, h: 0.22, fontSize: 9, bold: true, color: C.white, align: 'center', valign: 'middle', fontFace: 'Arial' }); + slide2.addText(item, { x: 1.2, y: 2.5 + i * 0.55, w: 3.2, h: 0.42, fontSize: 10, color: C.text, valign: 'middle', fontFace: 'Arial' }); + }); + + // 화살표 + slide2.addText('→', { x: 4.6, y: 2.7, w: 0.8, h: 0.5, fontSize: 28, bold: true, color: C.blue, align: 'center', fontFace: 'Arial' }); + + // After 카드 + slide2.addShape(pres.ShapeType.roundRect, { x: 5.3, y: 1.3, w: 4.2, h: 3.5, rectRadius: 0.12, fill: { color: C.greenBg }, line: { color: 'C8E6C9', width: 1 } }); + addBadge(slide2, 6.4, 1.45, 'TO-BE', C.green, C.white); + slide2.addText('API + React 서비스', { x: 5.5, y: 1.9, w: 3.8, h: 0.35, fontSize: 14, bold: true, color: C.greenDark, align: 'center', fontFace: 'Arial' }); + + const toBeItems = [ + 'API에 독립 SOAP 서비스 구축', + '모든 테넌트에서 사용 가능', + 'React UI로 고객 직접 이용', + '멀티테넌트 SaaS 과금 체계', + ]; + toBeItems.forEach((item, i) => { + slide2.addShape(pres.ShapeType.roundRect, { x: 5.5, y: 2.5 + i * 0.55, w: 3.8, h: 0.42, rectRadius: 0.06, fill: { color: C.white } }); + slide2.addShape(pres.ShapeType.ellipse, { x: 5.65, y: 2.58 + i * 0.55, w: 0.22, h: 0.22, fill: { color: C.green } }); + slide2.addText('✓', { x: 5.65, y: 2.56 + i * 0.55, w: 0.22, h: 0.22, fontSize: 9, bold: true, color: C.white, align: 'center', valign: 'middle', fontFace: 'Arial' }); + slide2.addText(item, { x: 6.0, y: 2.5 + i * 0.55, w: 3.2, h: 0.42, fontSize: 10, color: C.text, valign: 'middle', fontFace: 'Arial' }); + }); + + addFooter(slide2, 2, totalPages); + + // ═══════════════════════════════════════════════════════════════ + // 슬라이드 3: 이관 현황 총괄 + // ═══════════════════════════════════════════════════════════════ + const slide3 = pres.addSlide(); + slide3.background = { fill: C.white }; + addPageTitle(slide3, '이관 현황 총괄', '12개 영역 전체 API 이관 완료'); + + // 프로그레스 바 + slide3.addShape(pres.ShapeType.roundRect, { x: 0.5, y: 1.15, w: 9, h: 0.35, rectRadius: 0.06, fill: { color: 'E0E0E0' } }); + slide3.addShape(pres.ShapeType.roundRect, { x: 0.5, y: 1.15, w: 9, h: 0.35, rectRadius: 0.06, fill: { color: C.green } }); + slide3.addText('100% 완료 (12/12 영역)', { x: 0.5, y: 1.15, w: 9, h: 0.35, fontSize: 11, bold: true, color: C.white, align: 'center', fontFace: 'Arial' }); + + // 영역 테이블 + const areas = [ + ['SOAP 연동 서비스', '57개 메서드', C.green], + ['바로빌 설정/회원', '설정 + 회원 CRUD', C.green], + ['계좌 조회 (EAccount)', '13 EP + Sync', C.green], + ['카드 조회 (ECard)', '16 EP + Sync', C.green], + ['홈택스 매입/매출', '13 EP + Sync', C.green], + ['전자세금계산서 발행', '16 EP (SOAP)', C.green], + ['자동 동기화 스케줄러', 'Job (매일 06:00)', C.green], + ['카카오톡 발송', '12 EP', C.blue], + ['SMS 발송', '4 EP', C.blue], + ['구독/과금 관리', '9 EP', C.purple], + ['사용량 관리', '4 EP', C.purple], + ]; + + // 테이블 헤더 + const tblY = 1.7; + slide3.addShape(pres.ShapeType.roundRect, { x: 0.5, y: tblY, w: 9, h: 0.35, rectRadius: 0.04, fill: { color: C.dark } }); + slide3.addText('#', { x: 0.55, y: tblY, w: 0.4, h: 0.35, fontSize: 8, bold: true, color: C.white, align: 'center', valign: 'middle', fontFace: 'Arial' }); + slide3.addText('영역', { x: 1.0, y: tblY, w: 4, h: 0.35, fontSize: 8, bold: true, color: C.white, valign: 'middle', fontFace: 'Arial' }); + slide3.addText('API 규모', { x: 5.2, y: tblY, w: 2, h: 0.35, fontSize: 8, bold: true, color: C.white, align: 'center', valign: 'middle', fontFace: 'Arial' }); + slide3.addText('상태', { x: 7.5, y: tblY, w: 1.8, h: 0.35, fontSize: 8, bold: true, color: C.white, align: 'center', valign: 'middle', fontFace: 'Arial' }); + + areas.forEach((area, i) => { + const rowY = tblY + 0.38 + i * 0.3; + const rowBg = i % 2 === 0 ? C.grayLight : C.white; + slide3.addShape(pres.ShapeType.rect, { x: 0.5, y: rowY, w: 9, h: 0.3, fill: { color: rowBg } }); + slide3.addText(`${i + 1}`, { x: 0.55, y: rowY, w: 0.4, h: 0.3, fontSize: 8, color: C.gray, align: 'center', valign: 'middle', fontFace: 'Arial' }); + slide3.addText(area[0], { x: 1.0, y: rowY, w: 4, h: 0.3, fontSize: 9, color: C.text, valign: 'middle', fontFace: 'Arial' }); + slide3.addText(area[1], { x: 5.2, y: rowY, w: 2, h: 0.3, fontSize: 8, color: C.gray, align: 'center', valign: 'middle', fontFace: 'Arial' }); + // 상태 배지 + slide3.addShape(pres.ShapeType.roundRect, { x: 7.8, y: rowY + 0.04, w: 1.2, h: 0.22, rectRadius: 0.04, fill: { color: area[2] } }); + slide3.addText('완료', { x: 7.8, y: rowY + 0.04, w: 1.2, h: 0.22, fontSize: 8, bold: true, color: C.white, align: 'center', valign: 'middle', fontFace: 'Arial' }); + }); + + // 신규 이관 표시 + slide3.addShape(pres.ShapeType.ellipse, { x: 0.6, y: 4.85, w: 0.15, h: 0.15, fill: { color: C.blue } }); + slide3.addText('이번 세션 신규 이관 (카카오톡/SMS)', { x: 0.85, y: 4.82, w: 3, h: 0.2, fontSize: 7, color: C.blue, fontFace: 'Arial' }); + slide3.addShape(pres.ShapeType.ellipse, { x: 4.0, y: 4.85, w: 0.15, h: 0.15, fill: { color: C.purple } }); + slide3.addText('이번 세션 신규 이관 (과금/사용량)', { x: 4.25, y: 4.82, w: 3, h: 0.2, fontSize: 7, color: C.purple, fontFace: 'Arial' }); + + addFooter(slide3, 3, totalPages); + + // ═══════════════════════════════════════════════════════════════ + // 슬라이드 4: API 엔드포인트 상세 + // ═══════════════════════════════════════════════════════════════ + const slide4 = pres.addSlide(); + slide4.background = { fill: C.white }; + addPageTitle(slide4, 'API 엔드포인트 상세', '총 87+ REST API 엔드포인트 구축'); + + const epGroups = [ + { title: '데이터 조회/관리', color: C.green, bg: C.greenBg, border: 'C8E6C9', items: [ + ['카드 거래', '16 EP'], + ['은행 거래', '13 EP'], + ['홈택스', '13 EP'], + ['세금계산서', '16 EP'], + ]}, + { title: '통신/발송', color: C.blue, bg: 'E3F2FD', border: 'BBDEFB', items: [ + ['카카오톡', '12 EP'], + ['SMS', '4 EP'], + ['동기화', '3 EP'], + ['회원관리', '3 EP'], + ]}, + { title: '관리/과금', color: C.purple, bg: C.purpleBg, border: 'D1C4E9', items: [ + ['과금 관리', '9 EP'], + ['사용량', '4 EP'], + ['설정', '3 EP'], + ['스케줄러', 'Daily Job'], + ]}, + ]; + + const cardW = 2.85; + epGroups.forEach((group, gi) => { + const gx = 0.5 + gi * (cardW + 0.25); + const gy = 1.2; + + // 카드 배경 + slide4.addShape(pres.ShapeType.roundRect, { x: gx, y: gy, w: cardW, h: 3.8, rectRadius: 0.12, fill: { color: group.bg }, line: { color: group.border, width: 1 } }); + + // 카드 헤더 + slide4.addShape(pres.ShapeType.roundRect, { x: gx, y: gy, w: cardW, h: 0.5, rectRadius: 0.12, fill: { color: group.color } }); + // 하단 모서리 채우기 + slide4.addShape(pres.ShapeType.rect, { x: gx, y: gy + 0.38, w: cardW, h: 0.13, fill: { color: group.color } }); + slide4.addText(group.title, { x: gx, y: gy + 0.05, w: cardW, h: 0.4, fontSize: 13, bold: true, color: C.white, align: 'center', valign: 'middle', fontFace: 'Arial' }); + + // 카드 항목 + group.items.forEach((item, ii) => { + const iy = gy + 0.7 + ii * 0.75; + slide4.addShape(pres.ShapeType.roundRect, { x: gx + 0.15, y: iy, w: cardW - 0.3, h: 0.6, rectRadius: 0.08, fill: { color: C.white }, shadow: { type: 'outer', blur: 3, offset: 1, color: '00000010' } }); + slide4.addText(item[0], { x: gx + 0.3, y: iy + 0.05, w: cardW - 0.6, h: 0.22, fontSize: 10, bold: true, color: C.text, fontFace: 'Arial' }); + slide4.addText(item[1], { x: gx + 0.3, y: iy + 0.3, w: cardW - 0.6, h: 0.22, fontSize: 16, bold: true, color: group.color, fontFace: 'Arial' }); + }); + }); + + addFooter(slide4, 4, totalPages); + + // ═══════════════════════════════════════════════════════════════ + // 슬라이드 5: SOAP 메서드 구현율 + // ═══════════════════════════════════════════════════════════════ + const slide5 = pres.addSlide(); + slide5.background = { fill: C.white }; + addPageTitle(slide5, 'SOAP 메서드 구현율', 'MNG 대비 API 100% 이상 구현'); + + const soapData = [ + { name: 'CORPSTATE\n회원관리', mng: 3, api: 3, color: '26A69A' }, + { name: 'BANKACCOUNT\n계좌조회', mng: 13, api: 14, color: '42A5F5' }, + { name: 'CARD\n카드조회', mng: 11, api: 11, color: '5C6BC0' }, + { name: 'TI\n세금계산서', mng: 3, api: 3, color: 'AB47BC' }, + { name: 'KAKAOTALK\n카카오톡', mng: 15, api: 15, color: 'FFA726' }, + { name: 'SMS\n문자전송', mng: 4, api: 4, color: 'EF5350' }, + ]; + + // 차트 영역 + const chartX = 0.5; + const chartY = 1.3; + const barW = 1.3; + const barGap = 0.2; + const maxVal = 16; + const chartH = 2.5; + + soapData.forEach((d, i) => { + const bx = chartX + i * (barW + barGap); + + // MNG 바 + const mngH = (d.mng / maxVal) * chartH; + slide5.addShape(pres.ShapeType.roundRect, { + x: bx, y: chartY + chartH - mngH, w: barW * 0.45, h: mngH, + rectRadius: 0.04, fill: { color: 'BDBDBD' } + }); + slide5.addText(`${d.mng}`, { + x: bx, y: chartY + chartH - mngH - 0.22, w: barW * 0.45, h: 0.22, + fontSize: 9, bold: true, color: C.gray, align: 'center', fontFace: 'Arial' + }); + + // API 바 + const apiH = (d.api / maxVal) * chartH; + slide5.addShape(pres.ShapeType.roundRect, { + x: bx + barW * 0.5, y: chartY + chartH - apiH, w: barW * 0.45, h: apiH, + rectRadius: 0.04, fill: { color: d.color } + }); + slide5.addText(`${d.api}`, { + x: bx + barW * 0.5, y: chartY + chartH - apiH - 0.22, w: barW * 0.45, h: 0.22, + fontSize: 9, bold: true, color: d.color, align: 'center', fontFace: 'Arial' + }); + + // 라벨 + slide5.addText(d.name, { + x: bx, y: chartY + chartH + 0.08, w: barW, h: 0.45, + fontSize: 8, color: C.text, align: 'center', valign: 'top', fontFace: 'Arial' + }); + + // 구현율 + const rate = Math.round((d.api / d.mng) * 100); + slide5.addShape(pres.ShapeType.roundRect, { x: bx + 0.2, y: chartY + chartH + 0.52, w: barW - 0.4, h: 0.22, rectRadius: 0.04, fill: { color: C.greenBg } }); + slide5.addText(`${rate}%`, { x: bx + 0.2, y: chartY + chartH + 0.52, w: barW - 0.4, h: 0.22, fontSize: 8, bold: true, color: C.greenDark, align: 'center', fontFace: 'Arial' }); + }); + + // 범례 + slide5.addShape(pres.ShapeType.rect, { x: 7.5, y: 1.3, w: 0.3, h: 0.18, fill: { color: 'BDBDBD' } }); + slide5.addText('MNG', { x: 7.85, y: 1.28, w: 0.8, h: 0.22, fontSize: 9, color: C.gray, fontFace: 'Arial' }); + slide5.addShape(pres.ShapeType.rect, { x: 7.5, y: 1.55, w: 0.3, h: 0.18, fill: { color: C.blue } }); + slide5.addText('API', { x: 7.85, y: 1.53, w: 0.8, h: 0.22, fontSize: 9, color: C.blue, fontFace: 'Arial' }); + + // 합계 박스 + slide5.addShape(pres.ShapeType.roundRect, { x: 0.5, y: 4.55, w: 9, h: 0.5, rectRadius: 0.08, fill: { color: C.dark } }); + slide5.addText([ + { text: 'SOAP 메서드 합계 ', options: { fontSize: 11, color: '888888' } }, + { text: 'MNG 49개', options: { fontSize: 11, color: 'BDBDBD', bold: true } }, + { text: ' → ', options: { fontSize: 11, color: '888888' } }, + { text: 'API 50개', options: { fontSize: 11, color: C.green, bold: true } }, + { text: ' (100%+)', options: { fontSize: 11, color: C.green } }, + ], { x: 0.5, y: 4.55, w: 9, h: 0.5, align: 'center', valign: 'middle', fontFace: 'Arial' }); + + addFooter(slide5, 5, totalPages); + + // ═══════════════════════════════════════════════════════════════ + // 슬라이드 6: 코드 매핑 요약 + // ═══════════════════════════════════════════════════════════════ + const slide6 = pres.addSlide(); + slide6.background = { fill: C.white }; + addPageTitle(slide6, '코드 매핑 요약', 'MNG → API 파일 대응 관계'); + + const mappings = [ + { cat: 'Service', mng: 6, api: 8, detail: 'SOAP, Sync, Billing, Usage, TaxInvoice' }, + { cat: 'Controller', mng: 14, api: 11, detail: '카드/은행/홈택스/세금계산서/카카오톡/SMS/과금/사용량' }, + { cat: 'Model', mng: 18, api: 17, detail: '거래, 분개, 구독, 과금, 정책 등' }, + { cat: 'Migration', mng: '-', api: 30, detail: '바로빌 관련 테이블 생성/수정' }, + { cat: 'Route', mng: 20, api: '87+', detail: 'finance.php 내 바로빌 라우트 그룹' }, + { cat: 'Job', mng: 1, api: 1, detail: 'SyncBarobillDataJob (매일 자동 동기화)' }, + ]; + + // 테이블 헤더 + const mY = 1.2; + slide6.addShape(pres.ShapeType.roundRect, { x: 0.5, y: mY, w: 9, h: 0.4, rectRadius: 0.04, fill: { color: C.accent } }); + slide6.addText('유형', { x: 0.6, y: mY, w: 1.5, h: 0.4, fontSize: 9, bold: true, color: C.white, valign: 'middle', fontFace: 'Arial' }); + slide6.addText('MNG', { x: 2.2, y: mY, w: 1, h: 0.4, fontSize: 9, bold: true, color: C.white, align: 'center', valign: 'middle', fontFace: 'Arial' }); + slide6.addText('API', { x: 3.3, y: mY, w: 1, h: 0.4, fontSize: 9, bold: true, color: C.white, align: 'center', valign: 'middle', fontFace: 'Arial' }); + slide6.addText('포함 내용', { x: 4.5, y: mY, w: 4.8, h: 0.4, fontSize: 9, bold: true, color: C.white, valign: 'middle', fontFace: 'Arial' }); + + mappings.forEach((m, i) => { + const ry = mY + 0.44 + i * 0.55; + const bg = i % 2 === 0 ? C.grayLight : C.white; + slide6.addShape(pres.ShapeType.roundRect, { x: 0.5, y: ry, w: 9, h: 0.5, rectRadius: 0.04, fill: { color: bg } }); + + // 카테고리 배지 + slide6.addShape(pres.ShapeType.roundRect, { x: 0.65, y: ry + 0.1, w: 1.3, h: 0.3, rectRadius: 0.04, fill: { color: C.accent } }); + slide6.addText(m.cat, { x: 0.65, y: ry + 0.1, w: 1.3, h: 0.3, fontSize: 9, bold: true, color: C.white, align: 'center', valign: 'middle', fontFace: 'Arial' }); + + slide6.addText(`${m.mng}`, { x: 2.2, y: ry, w: 1, h: 0.5, fontSize: 12, color: C.gray, align: 'center', valign: 'middle', fontFace: 'Arial' }); + slide6.addText(`${m.api}`, { x: 3.3, y: ry, w: 1, h: 0.5, fontSize: 12, bold: true, color: C.blue, align: 'center', valign: 'middle', fontFace: 'Arial' }); + slide6.addText(m.detail, { x: 4.5, y: ry, w: 4.8, h: 0.5, fontSize: 9, color: C.text, valign: 'middle', fontFace: 'Arial' }); + }); + + // 이관 원칙 박스 + slide6.addShape(pres.ShapeType.roundRect, { x: 0.5, y: 4.2, w: 9, h: 0.8, rectRadius: 0.08, fill: { color: 'FFF8E1' }, line: { color: 'FFE082', width: 1 } }); + slide6.addText([ + { text: '이관 원칙: ', options: { fontSize: 10, bold: true, color: C.orange } }, + { text: 'MNG 코드를 수정/삭제하지 않고, API에 독립적으로 새로 구현. 같은 DB(samdb)를 공유하되, 각각 독립 SOAP 호출. 멀티테넌트(tenant_id) 격리 필수.', options: { fontSize: 9, color: C.text } }, + ], { x: 0.7, y: 4.25, w: 8.6, h: 0.7, valign: 'middle', fontFace: 'Arial' }); + + addFooter(slide6, 6, totalPages); + + // ═══════════════════════════════════════════════════════════════ + // 슬라이드 7: 향후 계획 (Next Steps) + // ═══════════════════════════════════════════════════════════════ + const slide7 = pres.addSlide(); + slide7.background = { fill: C.white }; + addPageTitle(slide7, '향후 계획', '출시까지의 로드맵'); + + // 타임라인 + const phases = [ + { title: 'Phase 1', sub: 'SOAP 이관\n(API 개발)', status: '완료', color: C.green, statusBg: C.greenBg, statusColor: C.greenDark }, + { title: 'Phase 2', sub: 'UI 구현\n(React 개발)', status: '다음', color: C.blue, statusBg: 'E3F2FD', statusColor: C.blue }, + { title: 'Phase 3', sub: '베타테스트\n(내부→외부)', status: '예정', color: C.orange, statusBg: C.orangeBg, statusColor: C.orange }, + { title: 'Phase 4', sub: '정식 출시\n(온보딩 가동)', status: '예정', color: C.purple, statusBg: C.purpleBg, statusColor: C.purple }, + ]; + + phases.forEach((p, i) => { + const px = 0.5 + i * 2.35; + const py = 1.3; + + // 카드 + slide7.addShape(pres.ShapeType.roundRect, { x: px, y: py, w: 2.15, h: 1.6, rectRadius: 0.1, fill: { color: C.white }, line: { color: p.color, width: 1.5 } }); + + // 넘버 서클 + slide7.addShape(pres.ShapeType.ellipse, { x: px + 0.75, y: py + 0.15, w: 0.6, h: 0.6, fill: { color: p.color } }); + slide7.addText(`${i + 1}`, { x: px + 0.75, y: py + 0.15, w: 0.6, h: 0.6, fontSize: 18, bold: true, color: C.white, align: 'center', valign: 'middle', fontFace: 'Arial' }); + + // Phase 제목 + slide7.addText(p.title, { x: px, y: py + 0.8, w: 2.15, h: 0.25, fontSize: 11, bold: true, color: p.color, align: 'center', fontFace: 'Arial' }); + slide7.addText(p.sub, { x: px, y: py + 1.0, w: 2.15, h: 0.35, fontSize: 8, color: C.gray, align: 'center', fontFace: 'Arial' }); + + // 상태 배지 + slide7.addShape(pres.ShapeType.roundRect, { x: px + 0.55, y: py + 1.38, w: 1, h: 0.2, rectRadius: 0.04, fill: { color: p.statusBg } }); + slide7.addText(p.status, { x: px + 0.55, y: py + 1.38, w: 1, h: 0.2, fontSize: 8, bold: true, color: p.statusColor, align: 'center', valign: 'middle', fontFace: 'Arial' }); + + // 연결선 + if (i < 3) { + slide7.addText('→', { x: px + 2.05, y: py + 0.55, w: 0.4, h: 0.4, fontSize: 16, color: C.grayBorder, align: 'center', fontFace: 'Arial' }); + } + }); + + // 남은 과제 카드 + slide7.addShape(pres.ShapeType.roundRect, { x: 0.5, y: 3.2, w: 4.3, h: 1.7, rectRadius: 0.1, fill: { color: C.white }, line: { color: C.blue, width: 1 } }); + slide7.addText('Phase 2 주요 작업 (React UI)', { x: 0.7, y: 3.3, w: 3.9, h: 0.3, fontSize: 11, bold: true, color: C.blue, fontFace: 'Arial' }); + + const phase2Tasks = [ + '계좌/카드/홈택스 조회 화면', + '세금계산서 발행 화면', + '바로빌 연동 설정 화면 보강', + '카카오톡/SMS 발송 화면', + ]; + phase2Tasks.forEach((task, i) => { + slide7.addShape(pres.ShapeType.ellipse, { x: 0.8, y: 3.7 + i * 0.28, w: 0.12, h: 0.12, fill: { color: C.blue } }); + slide7.addText(task, { x: 1.05, y: 3.65 + i * 0.28, w: 3.5, h: 0.22, fontSize: 9, color: C.text, fontFace: 'Arial' }); + }); + + // 확인 필요 사항 카드 + slide7.addShape(pres.ShapeType.roundRect, { x: 5.2, y: 3.2, w: 4.3, h: 1.7, rectRadius: 0.1, fill: { color: C.white }, line: { color: C.orange, width: 1 } }); + slide7.addText('출시 전 확인 필요 사항', { x: 5.4, y: 3.3, w: 3.9, h: 0.3, fontSize: 11, bold: true, color: C.orange, fontFace: 'Arial' }); + + const confirmTasks = [ + '멀티테넌트 CERTKEY 구조 확인 (바로빌 측)', + '테스트→운영 모드 전환 프로세스', + '공동인증서 등록 URL 제공 플로우', + '충전잔액 모니터링 + 과금 정책 확정', + ]; + confirmTasks.forEach((task, i) => { + slide7.addShape(pres.ShapeType.ellipse, { x: 5.5, y: 3.7 + i * 0.28, w: 0.12, h: 0.12, fill: { color: C.orange } }); + slide7.addText(task, { x: 5.75, y: 3.65 + i * 0.28, w: 3.5, h: 0.22, fontSize: 9, color: C.text, fontFace: 'Arial' }); + }); + + addFooter(slide7, 7, totalPages); + + // ─── 저장 ─── + const outputPath = '/home/aweso/sam/docs/presentations/barobill-migration-report.pptx'; + await pres.writeFile({ fileName: outputPath }); + console.log(`PPTX created: ${outputPath}`); +} + +main().catch(console.error); diff --git a/presentations/barobill-migration-report.pptx b/presentations/barobill-migration-report.pptx new file mode 100644 index 0000000000000000000000000000000000000000..24223fa5ab9271354c29b08f727f0501dc1d6a56 GIT binary patch literal 305894 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