diff --git a/_PC이관_보관/2026-10-12/git_제외_로컬파일/.live.bak_20260424_1231/README.md b/_PC이관_보관/2026-10-12/git_제외_로컬파일/.live.bak_20260424_1231/README.md new file mode 100644 index 0000000..5a0a1fe --- /dev/null +++ b/_PC이관_보관/2026-10-12/git_제외_로컬파일/.live.bak_20260424_1231/README.md @@ -0,0 +1,45 @@ +# `.live/` 실시간 공유 체계 — 실무 사용 안내 + +> **역할 분담**: 본 README는 **실무 사용 쿡북**이며, 규칙·헌법급 정의는 [`.claude/skills/너드나비스-코어룰/SKILL.md`](../../.claude/skills/너드나비스-코어룰/SKILL.md) **C34** 단일 SOT. 본 파일은 레포 `.live/` 경로에서 junction 경유로 접근하며, 중앙 저장소는 `$HOME/.claude/nerdnavis-live/`. + +## 1. `.live/` 용도 + +세션 중 변경된 설정·규칙·에이전트 정의·조직 기억을 **세션 갱신 없이 즉시 반영**하기 위한 PC 로컬 실시간 공유 채널. 같은 PC 내 모든 세션(worktree 경계 무관)이 자동 주입받는다. + +## 2. 더미 파일 패턴 + +- 파일명: `{변경 대상 파일명 또는 주제}.md` +- 예: `CLAUDE.md.live`·`C34_memory_확장.md`·`pd_지시_50.md` +- 본문: 변경 요지 append-only. 최대 8,000자 + +## 3. 생명주기 + +| 시점 | 상태 | 처리 | +|------|------|------| +| 변경 발생 | PM이 `.live/{파일명}` append | 원본 수정과 동시 수행 | +| 같은 PC 다른 세션 | UserPromptSubmit hook 자동 주입 | 증분만 읽기 (토큰 비용 최소) | +| "세션 공유"·push 시점 | P21-2 트리거 | 원본 반영 확인 후 `.live/` 비움(README.md·.junction-marker 제외) + commit + push | + +## 4. 세션 간 공유 시나리오 + +### 같은 PC 내 +1. 세션 A에서 원본 수정 + `.live/` append +2. 세션 B 다음 프롬프트 → UserPromptSubmit hook `live_inject.sh` 증분 주입 +3. 즉시 인지 (git 개입 없음) + +### 다른 PC 간 +1. 세션 A "세션 공유" (P21-2) → `.live/` 비움 + commit + push +2. 세션 B (다른 PC) "세션 갱신" (P21) → git pull → SessionStart hook 자동 복원 + +## 5. 참조 + +- **SKILL.md C34** — PC 로컬 실시간 공유 중앙화 체계 (단일 SOT, 헌법급) +- **SKILL.md C21** — 작업 완료 즉시 공유·PM 능동 확인 (내부 공유/공유 완료 2단계) +- **scripts/live_junction_ensure.sh** — junction 자동 보장 (SessionStart + UserPromptSubmit hook) +- **scripts/live_inject.sh** — UserPromptSubmit hook 증분 주입 +- **scripts/live_session_load.sh** — SessionStart 전량 1회 로드 + +## 변경 이력 + +- 2026-04-19: 전 세션 초기 sentinel 손실 시 원본 소실 +- 2026-04-20: #48 D 집행으로 복구. 중앙 저장소에 신규 작성 (모든 PC 자동 동기화) diff --git a/_PC이관_보관/2026-10-12/git_제외_로컬파일/claude_manifest/archived/2026-05-28_기획팀장_남은업무보고.md b/_PC이관_보관/2026-10-12/git_제외_로컬파일/claude_manifest/archived/2026-05-28_기획팀장_남은업무보고.md new file mode 100644 index 0000000..bddf4eb --- /dev/null +++ b/_PC이관_보관/2026-10-12/git_제외_로컬파일/claude_manifest/archived/2026-05-28_기획팀장_남은업무보고.md @@ -0,0 +1,26 @@ +--- +plan_id: 2026-05-28_기획팀장_남은업무보고 +created_at: 2026-05-28T11:40:37+09:00 +hostname: DESKTOP-RD7PUKN +goal: "PD 호칭 '기획팀장' 직접 지시 — 남은 기획 업무 현황 보고 (C43 정합)" +target_files: + - 공유/PD_지시_트래킹/기획팀_PD_지시_로그.md + - 공유/대화로그/EerieVillage/2026-05-28.md + - 공유/대화로그/조직운영/2026-05-28.md +completion_criteria: "commit + push 완료 후 post-commit hook archived 이동" +--- + +# 매니페스트 — 2026-05-28_기획팀장_남은업무보고 + +- **등록 시각**: 2026-05-28T11:40:38+09:00 +- **호스트**: DESKTOP-RD7PUKN +- **집행 목표**: PD 호칭 '기획팀장' 직접 지시 — 남은 기획 업무 현황 보고 (C43 정합) + +## 대상 파일 +- `공유/PD_지시_트래킹/기획팀_PD_지시_로그.md` +- `공유/대화로그/EerieVillage/2026-05-28.md` +- `공유/대화로그/조직운영/2026-05-28.md` + +## 완료 기준 + +commit + push 완료. post-commit hook이 `archived/2026-05-28_기획팀장_남은업무보고.md`로 자동 이동 + target_files cross-check. diff --git a/_PC이관_보관/2026-10-12/git_제외_로컬파일/memory/org/audit_logs/DESKTOP-RD7PUKN/auditor_calls/2026-04-24.log b/_PC이관_보관/2026-10-12/git_제외_로컬파일/memory/org/audit_logs/DESKTOP-RD7PUKN/auditor_calls/2026-04-24.log new file mode 100644 index 0000000..afdccad --- /dev/null +++ b/_PC이관_보관/2026-10-12/git_제외_로컬파일/memory/org/audit_logs/DESKTOP-RD7PUKN/auditor_calls/2026-04-24.log @@ -0,0 +1,9 @@ +2026-04-24_12:54:21 pm-auditor called +2026-04-24_14:42:16 pm-auditor called +2026-04-24_14:53:53 pm-auditor called +2026-04-24_14:59:36 pm-auditor called +2026-04-24_15:11:51 pm-auditor called +2026-04-24_15:20:06 pm-auditor called +2026-04-24_15:24:52 pm-auditor called +2026-04-24_16:12:14 pm-auditor called +2026-04-24_21:19:23 pm-auditor called diff --git a/_PC이관_보관/2026-10-12/git_제외_로컬파일/tmp/analyze_portfolio.py b/_PC이관_보관/2026-10-12/git_제외_로컬파일/tmp/analyze_portfolio.py new file mode 100644 index 0000000..4686a1b --- /dev/null +++ b/_PC이관_보관/2026-10-12/git_제외_로컬파일/tmp/analyze_portfolio.py @@ -0,0 +1,29 @@ +import os, sys +sys.stdout.reconfigure(encoding='utf-8') +from pptx import Presentation +from pptx.util import Emu + +src = r'C:\Users\PC\Documents\카카오톡 받은 파일\[포트폴리오] 이승우_2019_2.12.22_(구버전).pptx' +prs = Presentation(src) + +print(f'슬라이드 수: {len(prs.slides)}') +print(f'크기: {Emu(prs.slide_width).inches:.2f} x {Emu(prs.slide_height).inches:.2f} inches') +print(f'aspect: {prs.slide_width / prs.slide_height:.4f}') +print() + +for i, slide in enumerate(prs.slides, 1): + print(f'=== Slide {i} (Layout: {slide.slide_layout.name}) ===') + img = 0 + txt_lines = [] + for shape in slide.shapes: + if shape.shape_type == 13: + img += 1 + if shape.has_text_frame: + for para in shape.text_frame.paragraphs: + t = para.text.strip() + if t: + txt_lines.append(t) + print(f' images={img}, text_runs={len(txt_lines)}') + for j, line in enumerate(txt_lines[:30]): + print(f' T{j+1}: {line[:200]}') + print() diff --git a/_PC이관_보관/2026-10-12/git_제외_로컬파일/tmp/build_portfolio.py b/_PC이관_보관/2026-10-12/git_제외_로컬파일/tmp/build_portfolio.py new file mode 100644 index 0000000..4430317 --- /dev/null +++ b/_PC이관_보관/2026-10-12/git_제외_로컬파일/tmp/build_portfolio.py @@ -0,0 +1,748 @@ +# -*- coding: utf-8 -*- +""" +[포트폴리오] 이승우 — 16:9 와이드 현대화 PPT 빌드 +- 4:3 (10x7.5) → 16:9 (13.33x7.5) +- Charcoal Minimal + Navy Accent 팔레트 +- 게임 기획자 포트폴리오 톤 (전문성·정제) +""" +import os, sys +sys.stdout.reconfigure(encoding='utf-8') + +from pptx import Presentation +from pptx.util import Inches, Pt, Emu +from pptx.enum.shapes import MSO_SHAPE +from pptx.dml.color import RGBColor +from pptx.enum.text import PP_ALIGN, MSO_ANCHOR +from pptx.oxml.ns import qn +from copy import deepcopy +from lxml import etree + +# ─────────────── 디자인 시스템 ─────────────── +COLOR_PRIMARY = RGBColor(0x1E, 0x27, 0x61) # navy (Midnight Executive) +COLOR_DARK = RGBColor(0x21, 0x29, 0x5C) # midnight +COLOR_BG_DARK = RGBColor(0x14, 0x1B, 0x42) # darker navy +COLOR_ACCENT = RGBColor(0xCA, 0xDC, 0xFC) # ice blue +COLOR_GOLD = RGBColor(0xC9, 0xA2, 0x4B) # gold accent (prestige) +COLOR_TEXT = RGBColor(0x21, 0x21, 0x21) # near black +COLOR_MUTED = RGBColor(0x6B, 0x72, 0x80) # gray +COLOR_LIGHT = RGBColor(0xF5, 0xF6, 0xFA) # light bg +COLOR_BORDER = RGBColor(0xE2, 0xE5, 0xEC) # border +COLOR_WHITE = RGBColor(0xFF, 0xFF, 0xFF) + +FONT_HEADER = 'Malgun Gothic' +FONT_BODY = 'Malgun Gothic' +FONT_MONO = 'Consolas' + +# 16:9 와이드 (PowerPoint 2019 표준) +SLIDE_W = Inches(13.333) +SLIDE_H = Inches(7.5) + +OUT_PATH = r'C:\Users\PC\Documents\카카오톡 받은 파일\[포트폴리오] 이승우_2019_2.12.22_(현대화).pptx' +IMG_DIR = r'D:\NerdNavis\BurningTimesAi\tmp\portfolio_images' + + +def add_textbox(slide, left, top, width, height, text, *, + font=FONT_BODY, size=14, bold=False, italic=False, + color=COLOR_TEXT, align=PP_ALIGN.LEFT, anchor=MSO_ANCHOR.TOP, + line_spacing=1.15): + tb = slide.shapes.add_textbox(left, top, width, height) + tf = tb.text_frame + tf.margin_left = Inches(0.05) + tf.margin_right = Inches(0.05) + tf.margin_top = Inches(0.02) + tf.margin_bottom = Inches(0.02) + tf.word_wrap = True + tf.vertical_anchor = anchor + lines = text.split('\n') if isinstance(text, str) else text + for i, line in enumerate(lines): + p = tf.paragraphs[0] if i == 0 else tf.add_paragraph() + p.alignment = align + p.line_spacing = line_spacing + run = p.add_run() + run.text = line + run.font.name = font + run.font.size = Pt(size) + run.font.bold = bold + run.font.italic = italic + run.font.color.rgb = color + return tb + + +def add_rect(slide, left, top, width, height, fill_color, line_color=None, shadow=False): + shp = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE, left, top, width, height) + shp.fill.solid() + shp.fill.fore_color.rgb = fill_color + if line_color is None: + shp.line.fill.background() + else: + shp.line.color.rgb = line_color + shp.line.width = Pt(0.5) + if not shadow: + # 그림자 제거 + sp = shp.shadow + try: + sp.inherit = False + except Exception: + pass + return shp + + +def add_rounded(slide, left, top, width, height, fill_color, line_color=None): + shp = slide.shapes.add_shape(MSO_SHAPE.ROUNDED_RECTANGLE, left, top, width, height) + shp.adjustments[0] = 0.08 + shp.fill.solid() + shp.fill.fore_color.rgb = fill_color + if line_color is None: + shp.line.fill.background() + else: + shp.line.color.rgb = line_color + shp.line.width = Pt(0.75) + return shp + + +def add_circle(slide, left, top, size, fill_color, line_color=None): + shp = slide.shapes.add_shape(MSO_SHAPE.OVAL, left, top, size, size) + shp.fill.solid() + shp.fill.fore_color.rgb = fill_color + if line_color is None: + shp.line.fill.background() + else: + shp.line.color.rgb = line_color + return shp + + +def page_header(slide, page_num, total=20, section=''): + """우상단 페이지·섹션 표기 (간결)""" + add_textbox(slide, Inches(11.5), Inches(0.25), Inches(1.7), Inches(0.3), + f'{section} {page_num:02d} / {total:02d}', + size=9, color=COLOR_MUTED, align=PP_ALIGN.RIGHT) + + +def page_footer(slide): + """좌하단 푸터 — 이름·연락""" + add_textbox(slide, Inches(0.5), Inches(7.05), Inches(8), Inches(0.3), + 'Portfolio · 이승우 · Game Designer', + size=9, color=COLOR_MUTED, align=PP_ALIGN.LEFT) + + +# ─────────────── PPT 생성 시작 ─────────────── +prs = Presentation() +prs.slide_width = SLIDE_W +prs.slide_height = SLIDE_H +blank_layout = prs.slide_layouts[6] + + +# ═══════════════ S1. 표지 ═══════════════ +def build_slide_01(): + s = prs.slides.add_slide(blank_layout) + # 풀블리드 다크 배경 + add_rect(s, 0, 0, SLIDE_W, SLIDE_H, COLOR_BG_DARK) + # 좌측 navy 강조 띠 (단일 면 모티프) + add_rect(s, 0, 0, Inches(0.25), SLIDE_H, COLOR_GOLD) + + # 제목군 (중앙 좌측 정렬) + add_textbox(s, Inches(1.0), Inches(2.4), Inches(8), Inches(0.5), + 'PORTFOLIO', + size=14, bold=True, color=COLOR_GOLD, + font=FONT_HEADER, align=PP_ALIGN.LEFT) + add_textbox(s, Inches(1.0), Inches(2.9), Inches(11), Inches(1.3), + '포트폴리오', + size=72, bold=True, color=COLOR_WHITE, + font=FONT_HEADER, align=PP_ALIGN.LEFT) + add_textbox(s, Inches(1.0), Inches(4.4), Inches(11), Inches(0.6), + 'RPG 개발 경험이 풍부한 제너럴리스트 게임 기획자', + size=22, color=COLOR_ACCENT, + font=FONT_HEADER, align=PP_ALIGN.LEFT) + + # 구분선 + add_rect(s, Inches(1.0), Inches(5.3), Inches(2.5), Inches(0.04), COLOR_GOLD) + + # 이름·날짜 + add_textbox(s, Inches(1.0), Inches(5.5), Inches(8), Inches(0.4), + '게임 기획자 · 이승우', + size=18, bold=True, color=COLOR_WHITE, + font=FONT_HEADER, align=PP_ALIGN.LEFT) + add_textbox(s, Inches(1.0), Inches(6.0), Inches(8), Inches(0.3), + '2019.12.22', + size=12, color=COLOR_ACCENT, + font=FONT_BODY, align=PP_ALIGN.LEFT) + + +# ═══════════════ S2. 대표 작품 인덱스 ═══════════════ +def build_slide_02(): + s = prs.slides.add_slide(blank_layout) + add_rect(s, 0, 0, SLIDE_W, SLIDE_H, COLOR_WHITE) + + # 헤더 + add_textbox(s, Inches(0.6), Inches(0.5), Inches(10), Inches(0.4), + '01 · Featured Works', + size=12, bold=True, color=COLOR_GOLD, + font=FONT_HEADER, align=PP_ALIGN.LEFT) + add_textbox(s, Inches(0.6), Inches(0.85), Inches(12), Inches(0.7), + '대표 작품 소개', + size=36, bold=True, color=COLOR_PRIMARY, + font=FONT_HEADER, align=PP_ALIGN.LEFT) + add_textbox(s, Inches(0.6), Inches(1.55), Inches(12), Inches(0.4), + '총 13개 작품 · 최근 런칭 시점 기준 정렬', + size=12, color=COLOR_MUTED, + font=FONT_BODY, align=PP_ALIGN.LEFT) + page_header(s, 2, section='WORKS INDEX') + + works = [ + ('01', '매직 AR', '2019 · AR기반 실시간 전략 PvP'), + ('02', '쿠로네코 (가제)', '캐릭터 수집 방치형 RPG · 개발 중단'), + ('03', '만렙 영웅 키우기 시리즈', '2017 · 방치형 RPG · 매출 168위'), + ('04', '프로젝트 BC', '하드코어 액션 MORPG · 80% 진행'), + ('05', '데빌즈 아레나', '수집 성장형 MORPG · 개발 중단'), + ('06', '드래곤 헌터스', '2015 · 액션 RPG · 인기 인디 선정'), + ('07', '데빌즈', '2013 · 액션 RPG · 인기 무료 1위'), + ('08', '동물 그림 맞추기', '2012 · 퍼즐 / 교육 어플'), + ('09', '마스터 오브 던전', '2012 · 시나리오형 액션 RPG'), + ('10', '최종병기 활', '2011 · 로그라이크 · 영화 OSMU'), + ('11', '디스트로이어', '2010 · 액션 RPG · 넥슨 퍼블리싱'), + ('12', '에프터 스쿨', '2009 · 미니게임 + 타이쿤'), + ('13', '학교가는 길', '2007 · 미니게임 · SKT 킬러 콘텐츠'), + ] + cols = 3 + cell_w = Inches(4.0) + cell_h = Inches(0.85) + start_x = Inches(0.55) + start_y = Inches(2.2) + gap_x = Inches(0.1) + gap_y = Inches(0.12) + + for i, (num, title, desc) in enumerate(works): + r, c = i // cols, i % cols + x = start_x + c * (cell_w + gap_x) + y = start_y + r * (cell_h + gap_y) + # 카드 + card = add_rounded(s, x, y, cell_w, cell_h, COLOR_LIGHT, COLOR_BORDER) + # 좌측 번호 박스 (navy) + add_rect(s, x + Inches(0.0), y, Inches(0.6), cell_h, COLOR_PRIMARY) + add_textbox(s, x, y, Inches(0.6), cell_h, + num, size=18, bold=True, color=COLOR_WHITE, + font=FONT_HEADER, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) + # 제목 + add_textbox(s, x + Inches(0.75), y + Inches(0.1), cell_w - Inches(0.85), Inches(0.35), + title, size=13, bold=True, color=COLOR_TEXT, + font=FONT_HEADER, align=PP_ALIGN.LEFT) + # 부제 + add_textbox(s, x + Inches(0.75), y + Inches(0.42), cell_w - Inches(0.85), Inches(0.4), + desc, size=9.5, color=COLOR_MUTED, + font=FONT_BODY, align=PP_ALIGN.LEFT) + + page_footer(s) + + +# ═══════════════ S3~S15. 게임 소개 슬라이드 (공통 레이아웃) ═══════════════ +GAMES = [ + # (페이지번호 in deck, 작품번호, title, subtitle, info_dict, overview_lines, role_text, kpi_text, image_filename) + (3, '01', '매직 AR', 'AR 기반의 전략 PvP 게임', + {'장르': 'AR기반 실시간 전략 PvP', '플랫폼': 'Android, iOS', '출시일': '2019. 09'}, + [ + '증강현실(AR)과 딥 러닝 기술을 활용해 음료수 캔 등 사물을 스캔하여 실제 게임에서 활용할 수 있는 새로운 개념의 게임.', + '클래시 로얄과 유사한 실시간 전략 PvP. 어떤 사물을 스캔하는지에 따라 다양한 변수가 발생되는 것이 특징.', + '신 기술 기반 투자 유치 목적의 프로젝트로, 게임성 개선 및 폴리싱 후 소프트 런칭 버전 대응 완료.', + '※ 개발부·사업부 간 게임에 대한 이해도 차이와 경영 사정 악화로 인해 퇴사 결정.', + ], + '전투 밸런스 작업 · 게임 폴리싱 · 아이템 보상 시스템(상자·전투 보상) 등', + None, + 'slide03_img10.jpg'), + (4, '02', '쿠로네코 (가제)', 'IP 활용 방치형 게임', + {'장르': '캐릭터 수집 방치형 RPG', '플랫폼': 'Android', '출시일': '개발 중단'}, + [ + '대만에서 흥행한 이노센트 IP를 활용한 캐릭터 수집·성장 방치형 RPG 프로젝트.', + '200여 종의 개성 있는 캐릭터 리소스를 활용해 수집·성장의 재미를 제공.', + '탱커/원딜/근딜러/서포터로 구성, 캐릭터마다 다양한 컨셉의 스킬로 전략적 덱 조합 가능.', + '원작 리소스를 활용해 개발기간을 단축, 캐릭터 가챠 BM으로 매출 극대화 구상.', + '※ 개발 진척 60% 달성 후 핵심 개발자 퇴사 및 내부 사정으로 개발 중단.', + ], + '프로젝트 디렉팅(PD) · 핵심 시스템 기획 · UI&UX 컨셉팅 등 기획 실무 일체', + None, + 'slide04_img13.png'), + (5, '03', '만렙 영웅 키우기 시리즈', '전사편 · 소환사편 · 마검사편', + {'장르': 'RPG / 방치형', '플랫폼': 'Android, iOS', '출시일': '2017. 04'}, + [ + '반복적인 자동 전투로 재화를 수집하고, 무한 환생을 통해 끊임없이 성장하는 방치형 RPG.', + '코어 RPG의 감성을 살린 아이템·스킬 시스템으로 기존 방치형 게임과의 차별화 어필.', + '총 개발기간 약 4개월. 전사편 지표 분석을 통해 의미 있는 성과 달성.', + '결투장·쟁탈전·랭킹 시스템 등 다양한 경쟁 컨텐츠로 유저 몰입 유도.', + ], + '프로젝트 디렉팅(PD) · 기획 실무 일체', + 'KPI: ARPPU ₩45,553 · Retention 36.7% (1Day) · 최고 매출 168위 · Featured 2017/05', + 'slide05_img14.png'), + (6, '04', '프로젝트 BC', '푸른 별의 노래 (가제)', + {'장르': '하드코어 액션 MORPG', '플랫폼': 'Android', '출시일': '개발 중단'}, + [ + '화려한 2D 그래픽과 액션 RPG의 진수를 느낄 수 있는 하드코어 감성의 2D MORPG.', + '전사·마검사·소환사 3명의 캐릭터가 멀티 시나리오로 제공되며 각기 다른 플레이 스타일 보유.', + '유저간 PvP, RvR 형태의 길드 대전 및 약탈 시스템으로 경쟁 유도.', + '코스튬·펫·용병 시스템을 통해 캐릭터 성장 외에도 풍부한 즐길거리 제공.', + '※ 핵심 시스템 및 컨텐츠 80% 완성, 경영 악화로 인해 일시 중단.', + ], + '핵심 시스템 및 컨텐츠 기획 · 몬스터 & 캐릭터 전투 시스템 관련 기획', + None, + None), + (7, '05', '데빌즈 아레나', '자사 IP 활용 액션 MORPG', + {'장르': '수집 성장형 MORPG', '플랫폼': 'Android', '출시일': '개발 중단'}, + [ + '데빌즈·마스터오브 던전 인기 캐릭터 + 오리지널 30여 종을 활용한 캐릭터 수집·성장 MORPG.', + '실시간 PvP를 비롯한 다양한 경쟁형 컨텐츠 제공.', + '캐릭터간 상성과 플레이 스타일이 다양해 수집·조작의 재미를 모두 추구.', + '※ 정부 지원사업과 연계해 70% 개발 진행, 개발기간 대비 수익성 부족으로 잠정 드랍.', + ], + '프로젝트 디렉팅(PD) · 기획 실무 일체', + None, + None), + (8, '06', '드래곤 헌터스', '캐주얼 액션 RPG', + {'장르': '액션 RPG', '플랫폼': 'N스토어 / 원스토어 / GP2X', '출시일': '2015. 02'}, + [ + '피쳐폰 시절 이통 3사 25만 다운로드 이상을 기록하며 재미 검증을 받은 캐주얼 ARPG.', + '다이나믹한 콤보 액션과 필드 기믹으로 조작감에 대한 호평을 받음.', + '아이템 파밍·스킬 트리 구성으로 캐릭터 성장의 묘미 제공.', + '스마트폰 버전 리뉴얼 출시, 무리한 BM 적용으로 성과는 저조.', + ], + '프로젝트 디렉팅(PD) · 기획 실무 일체', + 'Featured: 인기 인디 게임 선정 (15/03)', + None), + (9, '07', '데빌즈', '몰입형 싱글 액션 RPG', + {'장르': '액션 RPG', '플랫폼': 'Android, iOS · 컴투스 퍼블리싱', '출시일': '2013. 10'}, + [ + '아이템 옵션 조합 파밍의 재미와 시나리오 기반 싱글 액션 RPG.', + '4명의 캐릭터가 각자 시점으로 진행되는 멀티 시나리오로 정통 RPG의 색다른 경험 제공.', + '다양한 옵션·등급 아이템과 스텟 포인트 투자로 성장 방법 다양화.', + '노말/하드/헬 난이도 + 최대 레벨 999로 장기 콘텐츠 제공, 보스 레이드·무한의 던전 등 다양한 싱글 컨텐츠 운영.', + '아이템 위주 BM을 통해 수년간 안정적 수익 창출.', + ], + '핵심 시스템·컨텐츠 기획 · 기획 실무 일체 (맵 & 레벨 디자인 제외)', + 'Featured: 인기 무료 게임 1위 (2013/11)', + None), + (10, '08', '동물 그림 맞추기', '유아 교육용 어플리케이션', + {'장르': '퍼즐 / 교육', '플랫폼': 'Android', '출시일': '2012. 09'}, + [ + '캐시카우 확보 및 기획팀 유니티 엔진 학습을 위한 단기 개발 프로젝트.', + '귀엽고 깔끔한 그래픽과 직관적인 게임 방법, 유아 교육용 어플리케이션으로 출시.', + '자사 게임 라인업 다양화 및 지속적 수익 타이틀 실험의 대표 케이스.', + '※ 총 인원 3명, 개발 기간 약 2.5주의 초단기 프로젝트.', + ], + '아트 컨셉 · UI&UX 설계 · UI 프리펩 배치 등 실무', + None, + None), + (11, '09', '마스터 오브 던전', '시나리오형 액션 RPG', + {'장르': '액션 RPG', '플랫폼': 'Android', '출시일': '2012. 07'}, + [ + '디스트로이어의 개발 노하우와 엔진을 활용해 제작된 싱글 기반 시나리오형 액션 RPG.', + '짜임새 있는 시나리오와 퀘스트 이벤트로 엔딩까지의 과정을 풍성하게 구성.', + '캐릭터 육성 방식에 따라 특성이 부여되는 정통 RPG 표방.', + '몰이사냥과 다양한 옵션 아이템으로 장비 세팅·파밍의 재미 강조.', + '인앱 과금 시장으로의 전환기에서 다양한 BM 실험을 시도한 프로젝트.', + ], + '아이템 및 전투 시스템 기획 · 시나리오 스크립트 작업 일체', + None, + 'slide11_img11.jpg'), + (12, '10', '최종병기 활', '영화 OSMU 로그라이크', + {'장르': '로그라이크', '플랫폼': 'Android', '출시일': '2011. 09'}, + [ + '영화 「최종병기 활」 홍보를 위한 원소스 멀티유즈 전략 프로젝트.', + '영화 개봉 시점에 맞춰 출시하기 위해 로그라이크를 변형한 캐주얼 RPG로 기획.', + '영화 시나리오 플롯을 계승하되 게임만의 오리지날 요소를 가미해 색다른 재미 제공.', + '재화 수집 → 무기·방어구 성장 구조, 타이밍 조작감 강조.', + '※ 촉박한 일정과 단말기 커버리지 한계로 흥행에는 실패.', + ], + '핵심 시스템 및 컨텐츠 기획 · 시나리오 스크립트 작업 등 기획 실무 일체', + None, + 'slide12_img10.png'), + (13, '11', '디스트로이어', '대작 액션 RPG', + {'장르': '액션 RPG', '플랫폼': 'SKT / Android · 넥슨 모바일 퍼블리싱', '출시일': '2010. 09'}, + [ + '드래곤 헌터의 성공과 개발력을 검증받아 넥슨과 퍼블리싱 계약이 체결된 대작 액션 프로젝트.', + '당시 모바일 최초로 4명의 캐릭터가 각각의 시점으로 진행되는 멀티 시나리오 제공.', + '거대 보스 레이드, 실시간 PvP 등 방대한 컨텐츠와 풍부한 즐길거리로 플레이타임 확보.', + '영웅 서기를 능가하는 대작 RPG를 목표로 했으나, 개발기간 오버 및 피쳐폰→스마트폰 시장 급변기 직격.', + '터치 조작 미지원으로 흥행에는 실패한 비운의 프로젝트.', + ], + '핵심 시스템 및 컨텐츠 기획 · 시나리오 스크립트 작업 등 기획 실무 일체', + None, + None), + (14, '12', '에프터 스쿨', '미니게임 + 타이쿤 복합', + {'장르': '미니게임', '플랫폼': 'SKT', '출시일': '2009. 06'}, + [ + '학교가는길 시리즈의 성공에 힘입어 독특하고 기발한 컨셉으로 제안·개발한 프로젝트.', + '미니게임과 타이쿤 장르를 믹스해 엄마 파트·아들 파트를 나눠 다양한 할 거리 제공.', + '초기 60~70% 개발 단계에서 ARPG(드래곤헌터) 메인 기획 담당을 위해 인수인계.', + '※ 신입 인력 인수 이후 타이쿤 장르 소화 부족으로 미니게임 장르 전환, 게임성 모호로 흥행 실패.', + ], + '컨셉 기획 · 타이쿤 & 미니게임 초기 기획 담당', + None, + None), + (15, '13', '학교가는 길', 'SKT 킬러 콘텐츠', + {'장르': '미니게임', '플랫폼': 'Android', '출시일': '2007. 05'}, + [ + 'SKT 킬러 컨텐츠로 선정되며 흥행에 성공한 최초의 프로젝트. 시리즈화에 성공해 3편까지 출시.', + '학교가는 길을 미니게임화한 독특한 컨셉과 개성 있는 디자인 연출로 피쳐폰 시절 높은 다운로드 수익 창출.', + '10여 종의 미니게임으로 구성. 각 미니게임은 외주 개발업체를 통해 진행.', + '초기 개발 단계부터 참여해 미니게임 전체 세부 기획·밸런싱·아트 컨셉 담당.', + '※ 퍼블리셔와의 수익 배분·라이선스 지분 이슈로 후속작은 타 업체 진행.', + ], + '컨셉 기획 · 미니게임 8종 시스템 기획 · 외주 개발 업체 관리 등 실무 전반', + None, + None), +] + + +def build_game_slide(page, num, title, subtitle, info, overviews, role, kpi, img_file): + s = prs.slides.add_slide(blank_layout) + add_rect(s, 0, 0, SLIDE_W, SLIDE_H, COLOR_WHITE) + + # 좌측 다크 사이드바 (게임 정보 카드) + SIDEBAR_W = Inches(4.2) + add_rect(s, 0, 0, SIDEBAR_W, SLIDE_H, COLOR_BG_DARK) + # 좌측 navy 강조 띠 + add_rect(s, 0, 0, Inches(0.18), SLIDE_H, COLOR_GOLD) + + # 사이드바 — 작품 번호 + add_textbox(s, Inches(0.5), Inches(0.5), Inches(3.5), Inches(0.4), + f'WORK · {num}', + size=11, bold=True, color=COLOR_GOLD, + font=FONT_HEADER, align=PP_ALIGN.LEFT) + + # 사이드바 — 게임명 + add_textbox(s, Inches(0.5), Inches(0.95), Inches(3.5), Inches(1.0), + title, size=26, bold=True, color=COLOR_WHITE, + font=FONT_HEADER, align=PP_ALIGN.LEFT, line_spacing=1.1) + add_textbox(s, Inches(0.5), Inches(2.0), Inches(3.5), Inches(0.7), + subtitle, size=12, color=COLOR_ACCENT, + font=FONT_BODY, align=PP_ALIGN.LEFT, italic=True, line_spacing=1.3) + + # 구분선 + add_rect(s, Inches(0.5), Inches(2.95), Inches(0.6), Inches(0.025), COLOR_GOLD) + + # 사이드바 — 게임 정보 표 + y = Inches(3.15) + for label, value in info.items(): + add_textbox(s, Inches(0.5), y, Inches(3.5), Inches(0.25), + label.upper(), + size=9, bold=True, color=COLOR_GOLD, + font=FONT_HEADER, align=PP_ALIGN.LEFT) + add_textbox(s, Inches(0.5), y + Inches(0.25), Inches(3.5), Inches(0.35), + value, + size=12, color=COLOR_WHITE, + font=FONT_BODY, align=PP_ALIGN.LEFT) + y += Inches(0.72) + + # 사이드바 — 이미지 (있다면 하단) + if img_file: + img_path = os.path.join(IMG_DIR, img_file) + if os.path.exists(img_path): + try: + # 이미지 사이즈 측정 후 비율 유지 + from PIL import Image + with Image.open(img_path) as pim: + iw, ih = pim.size + # 너비 3.3", 위치 사이드바 하단 + max_w_in = 3.3 + max_h_in = 1.4 + ratio_img = iw / ih + ratio_box = max_w_in / max_h_in + if ratio_img >= ratio_box: + target_w = max_w_in + target_h = max_w_in / ratio_img + else: + target_h = max_h_in + target_w = max_h_in * ratio_img + ix = Inches((4.2 - target_w) / 2) + iy = Inches(5.95 - target_h / 2) + s.shapes.add_picture(img_path, ix, iy, Inches(target_w), Inches(target_h)) + except Exception as e: + print(f' image warn: {e}') + + # 우측 컨텐츠 영역 + CONTENT_X = Inches(4.7) + # 상단 페이지 표기 + add_textbox(s, Inches(11.5), Inches(0.5), Inches(1.7), Inches(0.3), + f'WORKS · {page:02d} / 20', + size=9, color=COLOR_MUTED, align=PP_ALIGN.RIGHT, + font=FONT_BODY) + + # 우측 — 섹션 라벨 + add_textbox(s, CONTENT_X, Inches(0.55), Inches(8), Inches(0.4), + 'GAME OVERVIEW', + size=11, bold=True, color=COLOR_PRIMARY, + font=FONT_HEADER, align=PP_ALIGN.LEFT) + add_rect(s, CONTENT_X, Inches(0.95), Inches(0.6), Inches(0.04), COLOR_PRIMARY) + + # 우측 — 게임 개요 (불릿형 단락) + y = Inches(1.15) + for line in overviews: + # 불릿 마커 + dot = add_circle(s, CONTENT_X, y + Inches(0.12), Inches(0.08), COLOR_GOLD) + add_textbox(s, CONTENT_X + Inches(0.2), y, Inches(8.3), Inches(0.6), + line, size=11.5, color=COLOR_TEXT, + font=FONT_BODY, align=PP_ALIGN.LEFT, line_spacing=1.3) + # 라인 수에 따른 동적 간격 + est_lines = max(1, (len(line) // 50) + 1) + y += Inches(0.34 + 0.22 * (est_lines - 1)) + if y > Inches(5.3): + break + + # 우측 — 담당 업무 박스 + role_y = Inches(5.6) + add_rect(s, CONTENT_X, role_y, Inches(8.4), Inches(0.85), COLOR_LIGHT) + add_rect(s, CONTENT_X, role_y, Inches(0.1), Inches(0.85), COLOR_PRIMARY) + add_textbox(s, CONTENT_X + Inches(0.25), role_y + Inches(0.08), Inches(8), Inches(0.3), + 'MY ROLE', + size=10, bold=True, color=COLOR_PRIMARY, + font=FONT_HEADER, align=PP_ALIGN.LEFT) + add_textbox(s, CONTENT_X + Inches(0.25), role_y + Inches(0.36), Inches(8), Inches(0.5), + role, size=11, color=COLOR_TEXT, + font=FONT_BODY, align=PP_ALIGN.LEFT) + + # KPI / Featured 박스 (있을 경우) + if kpi: + kpi_y = Inches(6.6) + add_rect(s, CONTENT_X, kpi_y, Inches(8.4), Inches(0.6), COLOR_PRIMARY) + add_textbox(s, CONTENT_X + Inches(0.25), kpi_y + Inches(0.1), Inches(8), Inches(0.4), + 'KPI / FEATURED', + size=9, bold=True, color=COLOR_GOLD, + font=FONT_HEADER, align=PP_ALIGN.LEFT) + add_textbox(s, CONTENT_X + Inches(0.25), kpi_y + Inches(0.3), Inches(8), Inches(0.4), + kpi, size=10.5, color=COLOR_WHITE, + font=FONT_BODY, align=PP_ALIGN.LEFT) + + # 푸터 + add_textbox(s, Inches(4.7), Inches(7.15), Inches(8), Inches(0.25), + '이승우 · Game Designer · Portfolio', + size=8, color=COLOR_MUTED, align=PP_ALIGN.LEFT, + font=FONT_BODY) + + +# ═══════════════ S16. 경력 개요 ═══════════════ +def build_slide_16(): + s = prs.slides.add_slide(blank_layout) + add_rect(s, 0, 0, SLIDE_W, SLIDE_H, COLOR_WHITE) + + add_textbox(s, Inches(0.6), Inches(0.5), Inches(10), Inches(0.4), + '02 · Career Summary', + size=12, bold=True, color=COLOR_GOLD, + font=FONT_HEADER, align=PP_ALIGN.LEFT) + add_textbox(s, Inches(0.6), Inches(0.85), Inches(12), Inches(0.7), + '경력 개요', + size=36, bold=True, color=COLOR_PRIMARY, + font=FONT_HEADER, align=PP_ALIGN.LEFT) + add_textbox(s, Inches(0.6), Inches(1.55), Inches(12), Inches(0.4), + '총 경력 13년 2개월 · 다수의 메인 기획 및 디렉터 경험', + size=12, color=COLOR_MUTED, + font=FONT_BODY, align=PP_ALIGN.LEFT) + page_header(s, 16, section='CAREER') + + # 큰 KPI 박스 + add_rect(s, Inches(0.6), Inches(2.25), Inches(3.9), Inches(1.4), COLOR_BG_DARK) + add_rect(s, Inches(0.6), Inches(2.25), Inches(0.15), Inches(1.4), COLOR_GOLD) + add_textbox(s, Inches(0.85), Inches(2.4), Inches(3.5), Inches(0.4), + 'TOTAL EXPERIENCE', + size=11, bold=True, color=COLOR_GOLD, + font=FONT_HEADER, align=PP_ALIGN.LEFT) + add_textbox(s, Inches(0.85), Inches(2.75), Inches(3.5), Inches(0.9), + '13년 2개월', + size=42, bold=True, color=COLOR_WHITE, + font=FONT_HEADER, align=PP_ALIGN.LEFT) + add_textbox(s, Inches(0.85), Inches(3.32), Inches(3.5), Inches(0.3), + 'Game Designer · PD · PM', + size=11, color=COLOR_ACCENT, + font=FONT_BODY, italic=True, align=PP_ALIGN.LEFT) + + # 우측 — 핵심 강점 그리드 (2 columns × 5 rows) + strengths = [ + ('PROJECT LEADING', '다수의 프로젝트에서 메인 기획 및 디렉터로 리딩 경험'), + ('PROVEN HITS', '만렙 영웅 키우기, 데빌즈, 마스터 오브 던전, 드래곤 헌터, 학교가는 길'), + ('GLOBAL SERVICE', '미국·중국·일본 등 글로벌 서비스 대응 경험 (데빌즈 외)'), + ('MULTI-PLATFORM', 'NDS · GP2X · AR · 콘솔 / 모바일 등 다양한 플랫폼 실무'), + ('LIVE OPERATIONS', '라이브 게임 운영 및 서비스 다수 보유'), + ('PROACTIVE', '주도적인 프로젝트 제안·추진 (방치형·IP 활용 등)'), + ('MULTI-ROLE', 'PD · PM · QA · 외주 개발 관리 등 다양한 포지션 이해'), + ('NARRATIVE', '게임 시나리오 작성 및 스크립트 작업 (멀티 시나리오 포함)'), + ('TOOLS', '스파인 · 협업 툴 · 발사믹 목업 · 마인드맵 등 효율적 활용'), + ('BM DESIGN', '게임 통찰력·서비스 경험 기반 핵심 BM 설계 및 리뉴얼 주도'), + ] + col_x = [Inches(4.85), Inches(9.05)] + cell_w = Inches(4.05) + cell_h = Inches(0.85) + start_y = Inches(2.25) + for i, (label, desc) in enumerate(strengths): + col = i % 2 + row = i // 2 + x = col_x[col] + y = start_y + row * (cell_h + Inches(0.1)) + # 카드 + card = add_rounded(s, x, y, cell_w, cell_h, COLOR_LIGHT, COLOR_BORDER) + # 좌측 아이콘 원 (gold) + 번호 + add_circle(s, x + Inches(0.15), y + Inches(0.2), Inches(0.45), COLOR_PRIMARY) + add_textbox(s, x + Inches(0.15), y + Inches(0.2), Inches(0.45), Inches(0.45), + f'{i+1:02d}', size=10, bold=True, color=COLOR_GOLD, + font=FONT_HEADER, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) + # 라벨 + add_textbox(s, x + Inches(0.7), y + Inches(0.12), cell_w - Inches(0.8), Inches(0.3), + label, size=10, bold=True, color=COLOR_PRIMARY, + font=FONT_HEADER, align=PP_ALIGN.LEFT) + # 본문 + add_textbox(s, x + Inches(0.7), y + Inches(0.4), cell_w - Inches(0.8), Inches(0.45), + desc, size=10, color=COLOR_TEXT, + font=FONT_BODY, align=PP_ALIGN.LEFT, line_spacing=1.2) + + # 하단 — 마지막 1 항목 (소규모 ~ 중형 조직 경험) + bottom_y = Inches(7.0) + add_textbox(s, Inches(0.6), bottom_y, Inches(12.2), Inches(0.3), + '★ 소규모 스타트업부터 중형급 이상의 개발 조직까지 다양한 실무 경험 보유', + size=11, bold=True, color=COLOR_PRIMARY, + font=FONT_HEADER, align=PP_ALIGN.LEFT) + + page_footer(s) + + +# ═══════════════ S17·18·19. 영상 URL ═══════════════ +def build_video_links_slide(page_num, section_title, items): + """items: [(title, url, desc), ...]""" + s = prs.slides.add_slide(blank_layout) + add_rect(s, 0, 0, SLIDE_W, SLIDE_H, COLOR_WHITE) + add_textbox(s, Inches(0.6), Inches(0.5), Inches(10), Inches(0.4), + '03 · Reference Videos', + size=12, bold=True, color=COLOR_GOLD, + font=FONT_HEADER, align=PP_ALIGN.LEFT) + add_textbox(s, Inches(0.6), Inches(0.85), Inches(12), Inches(0.7), + section_title, + size=36, bold=True, color=COLOR_PRIMARY, + font=FONT_HEADER, align=PP_ALIGN.LEFT) + add_textbox(s, Inches(0.6), Inches(1.55), Inches(12), Inches(0.4), + '포트폴리오 참고 영상 / 다운로드 URL', + size=12, color=COLOR_MUTED, + font=FONT_BODY, align=PP_ALIGN.LEFT) + page_header(s, page_num, section='VIDEOS') + + y = Inches(2.3) + for title, url, desc in items: + # 카드 + add_rounded(s, Inches(0.6), y, Inches(12.15), Inches(1.3), COLOR_LIGHT, COLOR_BORDER) + # 좌측 navy 띠 + add_rect(s, Inches(0.6), y, Inches(0.15), Inches(1.3), COLOR_PRIMARY) + # 제목 + add_textbox(s, Inches(0.9), y + Inches(0.15), Inches(11.5), Inches(0.4), + title, size=15, bold=True, color=COLOR_PRIMARY, + font=FONT_HEADER, align=PP_ALIGN.LEFT) + # URL + add_textbox(s, Inches(0.9), y + Inches(0.55), Inches(11.5), Inches(0.35), + url, size=10.5, color=COLOR_GOLD, + font=FONT_MONO, align=PP_ALIGN.LEFT) + # 설명 + if desc: + add_textbox(s, Inches(0.9), y + Inches(0.92), Inches(11.5), Inches(0.35), + desc, size=10, color=COLOR_MUTED, + font=FONT_BODY, italic=True, align=PP_ALIGN.LEFT) + y += Inches(1.5) + if y > Inches(6.5): + break + + page_footer(s) + + +# ═══════════════ S20. 마무리·다운로드 ═══════════════ +def build_slide_20(): + s = prs.slides.add_slide(blank_layout) + add_rect(s, 0, 0, SLIDE_W, SLIDE_H, COLOR_BG_DARK) + # 좌측 띠 + add_rect(s, 0, 0, Inches(0.25), SLIDE_H, COLOR_GOLD) + + add_textbox(s, Inches(1.0), Inches(1.2), Inches(10), Inches(0.5), + '04 · Work Outputs', + size=14, bold=True, color=COLOR_GOLD, + font=FONT_HEADER, align=PP_ALIGN.LEFT) + add_textbox(s, Inches(1.0), Inches(1.7), Inches(11), Inches(1.0), + '실무 작업 결과물', + size=48, bold=True, color=COLOR_WHITE, + font=FONT_HEADER, align=PP_ALIGN.LEFT) + add_textbox(s, Inches(1.0), Inches(2.85), Inches(11), Inches(0.5), + '포트폴리오 다운로드 URL', + size=18, color=COLOR_ACCENT, + font=FONT_BODY, align=PP_ALIGN.LEFT, italic=True) + + # 다운로드 카드 (예시 — 원본에 URL 텍스트 단편만 존재) + add_rect(s, Inches(1.0), Inches(3.8), Inches(11.3), Inches(0.04), COLOR_GOLD) + + download_items = [ + ('GAME DESIGN DOCUMENTS', + '기획서 · 시스템 설계 · 밸런스 표 등 실무 작업 결과물 모음'), + ('UI / UX MOCKUPS', + '발사믹·스파인·기타 목업 툴을 활용한 화면 설계'), + ('SCRIPTS & SCENARIOS', + '게임 시나리오 · 멀티 시나리오 스크립트'), + ] + y = Inches(4.2) + for label, desc in download_items: + add_rect(s, Inches(1.0), y, Inches(0.05), Inches(0.7), COLOR_GOLD) + add_textbox(s, Inches(1.2), y + Inches(0.05), Inches(11), Inches(0.35), + label, size=13, bold=True, color=COLOR_WHITE, + font=FONT_HEADER, align=PP_ALIGN.LEFT) + add_textbox(s, Inches(1.2), y + Inches(0.35), Inches(11), Inches(0.3), + desc, size=11, color=COLOR_ACCENT, + font=FONT_BODY, align=PP_ALIGN.LEFT, italic=True) + y += Inches(0.85) + + # 푸터 + add_textbox(s, Inches(1.0), Inches(6.7), Inches(11), Inches(0.4), + 'THANK YOU', + size=22, bold=True, color=COLOR_GOLD, + font=FONT_HEADER, align=PP_ALIGN.LEFT) + add_textbox(s, Inches(1.0), Inches(7.1), Inches(11), Inches(0.3), + 'Lee Seung-Woo · Game Designer · 2019.12.22', + size=11, color=COLOR_ACCENT, + font=FONT_BODY, align=PP_ALIGN.LEFT) + + +# ─────────────── 빌드 실행 ─────────────── +print('Building slides...') +build_slide_01(); print(' S1 표지') +build_slide_02(); print(' S2 인덱스') +for game in GAMES: + page, num, title, subtitle, info, overviews, role, kpi, img_file = game + build_game_slide(page, num, title, subtitle, info, overviews, role, kpi, img_file) + print(f' S{page} {title}') +build_slide_16(); print(' S16 경력') + +videos_p17 = [ + ('데빌즈', + 'https://youtu.be/GZNZvpsk5WE', + '컴투스 퍼블리싱 · 인기 무료 게임 1위 (2013/11) · 액션 RPG 플레이 영상'), + ('프로젝트 BC (2014)', + 'https://youtu.be/maMV-lcPB3I', + '하드코어 액션 MORPG · 2D 그래픽과 멀티 시나리오 시연'), + ('Spine 2D 작업 시연', + '—', + '캐릭터 애니메이션 작업 영상 (Spine 활용)'), +] +build_video_links_slide(17, 'Reference Videos · 1', videos_p17); print(' S17 영상1') + +videos_p18 = [ + ('드래곤 헌터스', + 'https://drive.google.com/open?id=1tUkQ_bOwRrx3Y4LARqRX4KFGi5vXUocv', + 'GP2X · HD 버전 플레이 영상'), + ('마스터 오브 던전', + 'https://youtu.be/9ipP9KkBZxI', + '시나리오형 액션 RPG 플레이 영상'), +] +build_video_links_slide(18, 'Reference Videos · 2', videos_p18); print(' S18 영상2') + +videos_p19 = [ + ('쿠로네코 프로젝트', + 'https://drive.google.com/open?id=1poqqti2raZw_rosy3RJlVQNctPmEeE2P', + '개발이 상당부분 완료된 상태에서 중도 드랍된 쿠로네코 프로젝트 시연 영상'), + ('학교가는 길 1', + 'https://youtu.be/XMWlcZyJYSg', + '조이모아 퍼블리싱 · 학교가는 길 플레이 영상'), +] +build_video_links_slide(19, 'Reference Videos · 3', videos_p19); print(' S19 영상3') + +build_slide_20(); print(' S20 마무리') + +prs.save(OUT_PATH) +print(f'\n✅ Saved: {OUT_PATH}') +print(f' Size: {os.path.getsize(OUT_PATH):,} bytes') +print(f' Slides: {len(prs.slides)}') diff --git a/_PC이관_보관/2026-10-12/git_제외_로컬파일/tmp/build_portfolio_v2.py b/_PC이관_보관/2026-10-12/git_제외_로컬파일/tmp/build_portfolio_v2.py new file mode 100644 index 0000000..15f1f0c --- /dev/null +++ b/_PC이관_보관/2026-10-12/git_제외_로컬파일/tmp/build_portfolio_v2.py @@ -0,0 +1,1136 @@ +# -*- coding: utf-8 -*- +""" +[포트폴리오] 이승우 — v2 PD/리더 포지션 디자인 격상 +- Midnight Sapphire 팔레트 + Crimson/Gold dual accent +- 매거진 풍·hero 레이아웃·타임라인 인포그래픽 +- 원본 게임 이미지 4종 강조 활용 +""" +import os, sys +sys.stdout.reconfigure(encoding='utf-8') + +from pptx import Presentation +from pptx.util import Inches, Pt, Emu +from pptx.enum.shapes import MSO_SHAPE +from pptx.dml.color import RGBColor +from pptx.enum.text import PP_ALIGN, MSO_ANCHOR +from pptx.oxml.ns import qn +from lxml import etree +from copy import deepcopy + +# ──────────────── 디자인 시스템: Midnight Sapphire ──────────────── +C_BG_DEEP = RGBColor(0x0A, 0x16, 0x28) # deep midnight +C_BG_DARK = RGBColor(0x12, 0x21, 0x3A) # dark navy +C_PRIMARY = RGBColor(0x1B, 0x3B, 0x6F) # sapphire +C_PRIMARY_2 = RGBColor(0x2E, 0x5C, 0xA6) # lighter sapphire +C_CRIMSON = RGBColor(0xC8, 0x10, 0x2E) # crimson (hot accent) +C_CRIMSON_2 = RGBColor(0xE0, 0x3A, 0x4F) # crimson light +C_GOLD = RGBColor(0xD4, 0xAF, 0x37) # gold (premium accent) +C_GOLD_DEEP = RGBColor(0xA8, 0x85, 0x1E) # deep gold +C_IVORY = RGBColor(0xF4, 0xF1, 0xEA) # warm off-white +C_WHITE = RGBColor(0xFF, 0xFF, 0xFF) +C_TEXT_DARK = RGBColor(0x1A, 0x1D, 0x29) +C_TEXT_MUTED = RGBColor(0x6B, 0x72, 0x80) +C_TEXT_DIM = RGBColor(0x8B, 0x95, 0xA1) +C_BORDER = RGBColor(0xE2, 0xE5, 0xEC) + +FONT_DISPLAY = 'Malgun Gothic' +FONT_BODY = 'Malgun Gothic' +FONT_MONO = 'Consolas' +FONT_ACCENT = 'Georgia' # serif for editorial accent + +SLIDE_W = Inches(13.333) +SLIDE_H = Inches(7.5) + +OUT_PATH = r'C:\Users\PC\Documents\카카오톡 받은 파일\[포트폴리오] 이승우_2019_2.12.22_(현대화).pptx' +IMG_DIR = r'D:\NerdNavis\BurningTimesAi\tmp\portfolio_images' + + +# ──────────────── XML 유틸 ──────────────── +def set_gradient_fill(shape, color1_rgb, color2_rgb, angle=90): + """그라데이션 채우기 (XML 직접 편집).""" + sp_pr = shape.fill._xPr.find(qn('p:spPr')) if hasattr(shape.fill, '_xPr') else None + # 단순화: shape의 spPr에 gradFill 삽입 + spPr = shape._element.spPr + # 기존 fill 제거 + for child in spPr.findall(qn('a:solidFill')) + spPr.findall(qn('a:gradFill')) + spPr.findall(qn('a:noFill')): + spPr.remove(child) + grad = etree.SubElement(spPr, qn('a:gradFill'), + {'flip': 'none', 'rotWithShape': '1'}) + gsLst = etree.SubElement(grad, qn('a:gsLst')) + gs1 = etree.SubElement(gsLst, qn('a:gs'), {'pos': '0'}) + srgb1 = etree.SubElement(gs1, qn('a:srgbClr'), + {'val': f'{color1_rgb[0]:02X}{color1_rgb[1]:02X}{color1_rgb[2]:02X}'}) + gs2 = etree.SubElement(gsLst, qn('a:gs'), {'pos': '100000'}) + srgb2 = etree.SubElement(gs2, qn('a:srgbClr'), + {'val': f'{color2_rgb[0]:02X}{color2_rgb[1]:02X}{color2_rgb[2]:02X}'}) + lin = etree.SubElement(grad, qn('a:lin'), + {'ang': str(angle * 60000), 'scaled': '0'}) + # spPr 끝에 ln 요소 자리 정리 — gradFill을 ln 앞에 배치 + ln = spPr.find(qn('a:ln')) + if ln is not None: + spPr.remove(grad) + spPr.insert(list(spPr).index(ln), grad) + + +def add_textbox(slide, left, top, width, height, text, *, + font=FONT_BODY, size=14, bold=False, italic=False, + color=C_TEXT_DARK, align=PP_ALIGN.LEFT, + anchor=MSO_ANCHOR.TOP, line_spacing=1.15, + char_spacing=None): + tb = slide.shapes.add_textbox(left, top, width, height) + tf = tb.text_frame + tf.margin_left = Inches(0.03) + tf.margin_right = Inches(0.03) + tf.margin_top = Inches(0.02) + tf.margin_bottom = Inches(0.02) + tf.word_wrap = True + tf.vertical_anchor = anchor + lines = text.split('\n') if isinstance(text, str) else text + for i, line in enumerate(lines): + p = tf.paragraphs[0] if i == 0 else tf.add_paragraph() + p.alignment = align + p.line_spacing = line_spacing + run = p.add_run() + run.text = line + run.font.name = font + run.font.size = Pt(size) if not isinstance(size, float) else Pt(size) + run.font.bold = bold + run.font.italic = italic + run.font.color.rgb = color + if char_spacing is not None: + # 자간 설정 (XML) + rPr = run._r.get_or_add_rPr() + rPr.set('spc', str(char_spacing)) + return tb + + +def add_rect(slide, left, top, width, height, fill=None, line=None, line_w=0.5): + shp = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE, left, top, width, height) + if fill is None: + shp.fill.background() + else: + shp.fill.solid() + shp.fill.fore_color.rgb = fill + if line is None: + shp.line.fill.background() + else: + shp.line.color.rgb = line + shp.line.width = Pt(line_w) + return shp + + +def add_gradient_rect(slide, left, top, width, height, c1, c2, angle=90): + shp = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE, left, top, width, height) + shp.line.fill.background() + set_gradient_fill(shp, c1, c2, angle) + return shp + + +def add_rounded(slide, left, top, width, height, fill, line=None, radius=0.05): + shp = slide.shapes.add_shape(MSO_SHAPE.ROUNDED_RECTANGLE, left, top, width, height) + shp.adjustments[0] = radius + if fill is None: + shp.fill.background() + else: + shp.fill.solid() + shp.fill.fore_color.rgb = fill + if line is None: + shp.line.fill.background() + else: + shp.line.color.rgb = line + shp.line.width = Pt(0.5) + return shp + + +def add_circle(slide, left, top, size, fill, line=None): + shp = slide.shapes.add_shape(MSO_SHAPE.OVAL, left, top, size, size) + shp.fill.solid() + shp.fill.fore_color.rgb = fill + if line is None: + shp.line.fill.background() + else: + shp.line.color.rgb = line + return shp + + +def add_line(slide, x1, y1, x2, y2, color, weight=1.0): + line = slide.shapes.add_connector(1, x1, y1, x2, y2) + line.line.color.rgb = color + line.line.width = Pt(weight) + return line + + +def add_image_fit(slide, img_path, left, top, width, height, cover=True): + """이미지를 영역에 cover/contain 방식으로 배치.""" + from PIL import Image + with Image.open(img_path) as im: + iw, ih = im.size + target_aspect = width / height + img_aspect = iw / ih + if cover: + # 영역을 채움 (잘림) + if img_aspect >= target_aspect: + new_h = height + new_w = int(height * img_aspect) + else: + new_w = width + new_h = int(width / img_aspect) + # 중앙 정렬 + x = left + (width - new_w) // 2 + y = top + (height - new_h) // 2 + else: + # 영역에 맞춤 (여백 발생) + if img_aspect >= target_aspect: + new_w = width + new_h = int(width / img_aspect) + else: + new_h = height + new_w = int(height * img_aspect) + x = left + (width - new_w) // 2 + y = top + (height - new_h) // 2 + pic = slide.shapes.add_picture(img_path, x, y, new_w, new_h) + return pic + + +# ──────────────── 빌드 시작 ──────────────── +prs = Presentation() +prs.slide_width = SLIDE_W +prs.slide_height = SLIDE_H +blank = prs.slide_layouts[6] + + +# ════════════════════ S1. 표지 (Hero) ════════════════════ +def s01_cover(): + s = prs.slides.add_slide(blank) + # 풀블리드 그라데이션 + bg = add_rect(s, 0, 0, SLIDE_W, SLIDE_H, C_BG_DEEP) + set_gradient_fill(bg, C_BG_DEEP, C_PRIMARY, angle=135) + + # 우측 게임 키 아트 (쿠로네코) - 합성 효과 + img_path = os.path.join(IMG_DIR, 'slide04_img13.png') + if os.path.exists(img_path): + # 우측 영역에 이미지 - 페이드 효과를 위해 어두운 오버레이 사용 + pic = s.shapes.add_picture(img_path, Inches(7.5), Inches(0), Inches(5.83), Inches(7.5)) + # 이미지 위에 그라데이션 오버레이 (좌→우 페이드) + overlay = add_rect(s, Inches(7.5), 0, Inches(5.83), SLIDE_H, C_BG_DEEP) + set_gradient_fill(overlay, C_BG_DEEP, RGBColor(0x0A, 0x16, 0x28), angle=0) + # 투명도 80% 좌측, 50% 우측 — 단순화: 단일 색 위 추가 그라데이션 사용 어렵, 그냥 좌측 절반 가림 + # 대안: 풀블리드 우측 어두운 그라데이션 + ov2_shape = s.shapes.add_shape(MSO_SHAPE.RECTANGLE, Inches(7.5), 0, Inches(5.83), SLIDE_H) + ov2_shape.line.fill.background() + spPr = ov2_shape._element.spPr + for child in spPr.findall(qn('a:solidFill')): + spPr.remove(child) + grad = etree.SubElement(spPr, qn('a:gradFill'), {'flip': 'none', 'rotWithShape': '1'}) + gsLst = etree.SubElement(grad, qn('a:gsLst')) + # 좌측: deep midnight 95% opacity → 우측: deep midnight 30% opacity + gs1 = etree.SubElement(gsLst, qn('a:gs'), {'pos': '0'}) + clr1 = etree.SubElement(gs1, qn('a:srgbClr'), {'val': '0A1628'}) + etree.SubElement(clr1, qn('a:alpha'), {'val': '95000'}) + gs2 = etree.SubElement(gsLst, qn('a:gs'), {'pos': '100000'}) + clr2 = etree.SubElement(gs2, qn('a:srgbClr'), {'val': '0A1628'}) + etree.SubElement(clr2, qn('a:alpha'), {'val': '30000'}) + etree.SubElement(grad, qn('a:lin'), {'ang': '0', 'scaled': '0'}) + ln = spPr.find(qn('a:ln')) + if ln is not None: + spPr.remove(grad) + spPr.insert(list(spPr).index(ln), grad) + + # 좌측 — 매거진 풍 사이드 매거진 라인 + add_rect(s, Inches(0.7), Inches(0.8), Inches(0.04), Inches(5.9), C_GOLD) + + # 상단 라벨 (브랜드 머리표) + add_textbox(s, Inches(0.95), Inches(0.75), Inches(8), Inches(0.4), + 'GAME DESIGNER · PD PORTFOLIO', + font=FONT_DISPLAY, size=12, bold=True, color=C_GOLD, + char_spacing=400) + + # 큰 디스플레이 타입 + add_textbox(s, Inches(0.95), Inches(1.5), Inches(11), Inches(1.5), + 'PORTFOLIO', + font=FONT_DISPLAY, size=88, bold=True, color=C_WHITE, + char_spacing=-100, line_spacing=1.0) + + # 한글 부제 (큰 임팩트) + add_textbox(s, Inches(0.95), Inches(2.95), Inches(11), Inches(0.9), + '이승우', + font=FONT_DISPLAY, size=64, bold=True, color=C_WHITE, + line_spacing=1.0) + + # 직무 라인 + add_textbox(s, Inches(0.95), Inches(4.0), Inches(10), Inches(0.5), + 'RPG 개발 13년의 메인 기획자 · PD · 디렉터', + font=FONT_DISPLAY, size=20, color=C_GOLD, + italic=True) + + # 강조 라인 + add_rect(s, Inches(0.95), Inches(4.85), Inches(2.5), Inches(0.05), C_CRIMSON) + + # 핵심 KPI 3종 (좌측 하단) + kpi_data = [ + ('13', 'YEARS', '경력'), + ('13+', 'TITLES', '대표작'), + ('1st', 'RANKED', '인기 무료'), + ] + kpi_x = Inches(0.95) + for i, (num, label, ko) in enumerate(kpi_data): + x = kpi_x + i * Inches(2.0) + add_textbox(s, x, Inches(5.15), Inches(1.8), Inches(0.7), + num, font=FONT_DISPLAY, size=42, bold=True, + color=C_GOLD, line_spacing=1.0) + add_textbox(s, x, Inches(5.85), Inches(1.8), Inches(0.35), + label, font=FONT_DISPLAY, size=10, bold=True, + color=C_WHITE, char_spacing=300) + add_textbox(s, x, Inches(6.15), Inches(1.8), Inches(0.3), + ko, font=FONT_BODY, size=10, color=C_TEXT_DIM) + + # 하단 푸터 + add_rect(s, Inches(0.95), Inches(6.95), Inches(11.4), Inches(0.02), C_GOLD_DEEP) + add_textbox(s, Inches(0.95), Inches(7.05), Inches(8), Inches(0.3), + 'LEE SEUNG-WOO', + font=FONT_DISPLAY, size=11, bold=True, color=C_WHITE, + char_spacing=300) + add_textbox(s, Inches(7), Inches(7.05), Inches(5.3), Inches(0.3), + '2019.12.22', + font=FONT_DISPLAY, size=11, color=C_GOLD, + align=PP_ALIGN.RIGHT) + + +# ════════════════════ S2. 프로필 (About) ════════════════════ +def s02_profile(): + s = prs.slides.add_slide(blank) + add_rect(s, 0, 0, SLIDE_W, SLIDE_H, C_IVORY) + + # 좌측 다크 패널 (40%) — 큰 인용/철학 + add_rect(s, 0, 0, Inches(5.3), SLIDE_H, C_BG_DEEP) + add_rect(s, Inches(5.3), 0, Inches(0.06), SLIDE_H, C_GOLD) + + # 좌측 — 큰 따옴표 + 철학 + add_textbox(s, Inches(0.6), Inches(0.8), Inches(2), Inches(2.5), + '"', font=FONT_ACCENT, size=200, bold=True, + color=C_GOLD, line_spacing=0.8) + + add_textbox(s, Inches(0.6), Inches(2.3), Inches(4.5), Inches(2.5), + '게임을 만드는 사람이 아니라\n게임을 디자인하는 사람.', + font=FONT_DISPLAY, size=22, bold=True, color=C_WHITE, + line_spacing=1.4) + + add_textbox(s, Inches(0.6), Inches(4.5), Inches(4.5), Inches(2), + '시스템·BM·시나리오·운영까지\n프로젝트의 모든 레이어를 책임지는\n제너럴리스트 게임 디자이너.', + font=FONT_BODY, size=13, color=C_TEXT_DIM, + line_spacing=1.5, italic=True) + + # 좌측 하단 — 시그니처 + add_rect(s, Inches(0.6), Inches(6.7), Inches(0.6), Inches(0.04), C_GOLD) + add_textbox(s, Inches(0.6), Inches(6.85), Inches(4), Inches(0.4), + 'LEE SEUNG-WOO', + font=FONT_DISPLAY, size=12, bold=True, color=C_GOLD, + char_spacing=400) + + # 우측 콘텐츠 (60%) + CX = Inches(5.7) + # 섹션 라벨 + add_textbox(s, CX, Inches(0.7), Inches(6), Inches(0.35), + '01 · ABOUT', font=FONT_DISPLAY, size=10, bold=True, + color=C_CRIMSON, char_spacing=400) + + # 큰 제목 + add_textbox(s, CX, Inches(1.05), Inches(7), Inches(1), + 'Who I am', + font=FONT_DISPLAY, size=44, bold=True, color=C_BG_DEEP, + line_spacing=1.0) + + # 강조 라인 + add_rect(s, CX, Inches(2.05), Inches(1.2), Inches(0.05), C_CRIMSON) + + # 본문 — 자기소개 단락 + intro = ( + '13년 2개월간 게임 산업에서 메인 기획자·디렉터로 활동했습니다.\n' + '피쳐폰부터 스마트폰·AR까지 시장의 모든 전환점을 직접 경험했고,\n' + '인기 1위 작품부터 신기술 PoC까지 다양한 규모·장르의 프로젝트를 리딩했습니다.' + ) + add_textbox(s, CX, Inches(2.3), Inches(7.2), Inches(1.7), + intro, font=FONT_BODY, size=14, color=C_TEXT_DARK, + line_spacing=1.6) + + # 역할 카드 4종 (2x2) + add_textbox(s, CX, Inches(4.05), Inches(7), Inches(0.4), + 'CORE ROLES', font=FONT_DISPLAY, size=10, bold=True, + color=C_PRIMARY, char_spacing=300) + + roles = [ + ('PD', 'Project Director', '프로젝트 디렉팅·총괄'), + ('MAIN', 'Lead Designer', '핵심 시스템·컨텐츠 기획'), + ('PM', 'Project Manager', '팀·일정·외주 관리'), + ('LIVE', 'Live Ops', '라이브 운영·BM 설계'), + ] + rx, ry = CX, Inches(4.45) + cw, ch = Inches(3.5), Inches(1.2) + gx, gy = Inches(0.15), Inches(0.15) + for i, (key, en, ko) in enumerate(roles): + col, row = i % 2, i // 2 + x = rx + col * (cw + gx) + y = ry + row * (ch + gy) + # 카드 (다크 보더) + add_rounded(s, x, y, cw, ch, C_WHITE, C_BORDER, radius=0.04) + # 좌측 crimson 띠 + add_rect(s, x, y, Inches(0.08), ch, C_CRIMSON) + # KEY + add_textbox(s, x + Inches(0.25), y + Inches(0.18), Inches(1.2), Inches(0.4), + key, font=FONT_DISPLAY, size=22, bold=True, color=C_CRIMSON) + # EN + add_textbox(s, x + Inches(0.25), y + Inches(0.62), cw - Inches(0.3), Inches(0.3), + en, font=FONT_DISPLAY, size=11, bold=True, color=C_BG_DEEP) + # KO + add_textbox(s, x + Inches(0.25), y + Inches(0.88), cw - Inches(0.3), Inches(0.3), + ko, font=FONT_BODY, size=10, color=C_TEXT_MUTED) + + # 페이지 번호 + add_textbox(s, Inches(12), Inches(7.15), Inches(1), Inches(0.3), + '02 / 20', font=FONT_DISPLAY, size=9, color=C_TEXT_DIM, + align=PP_ALIGN.RIGHT) + + +# ════════════════════ S3. 핵심 성과 (KPI Dashboard) ════════════════════ +def s03_kpi_dashboard(): + s = prs.slides.add_slide(blank) + add_rect(s, 0, 0, SLIDE_W, SLIDE_H, C_BG_DEEP) + + # 헤더 + add_textbox(s, Inches(0.7), Inches(0.7), Inches(6), Inches(0.35), + '02 · KEY METRICS', font=FONT_DISPLAY, size=10, bold=True, + color=C_GOLD, char_spacing=400) + add_textbox(s, Inches(0.7), Inches(1.05), Inches(11), Inches(0.9), + 'Performance Highlights', + font=FONT_DISPLAY, size=40, bold=True, color=C_WHITE) + add_rect(s, Inches(0.7), Inches(2.0), Inches(1.2), Inches(0.05), C_CRIMSON) + + # 큰 KPI 4종 (가로 정렬) + kpis = [ + ('13', 'YEARS', '게임 산업 경력', C_GOLD), + ('168', '#1', '구글플레이\n최고 매출 순위', C_CRIMSON_2), + ('1st', 'RANK', '데빌즈 인기\n무료 게임 1위', C_GOLD), + ('25만+', 'D/L', '드래곤 헌터스\n피쳐폰 다운로드', C_CRIMSON_2), + ] + kx = Inches(0.7) + kw = Inches(2.95) + ky = Inches(2.6) + kh = Inches(2.3) + gap = Inches(0.15) + for i, (num, label, ko, accent) in enumerate(kpis): + x = kx + i * (kw + gap) + # 카드 — 다크 박스 + add_rect(s, x, ky, kw, kh, C_BG_DARK) + # 상단 강조 띠 + add_rect(s, x, ky, kw, Inches(0.08), accent) + # 큰 숫자 + add_textbox(s, x, ky + Inches(0.45), kw, Inches(1.0), + num, font=FONT_DISPLAY, size=60, bold=True, + color=accent, align=PP_ALIGN.CENTER, line_spacing=1.0) + # 라벨 + add_textbox(s, x, ky + Inches(1.4), kw, Inches(0.3), + label, font=FONT_DISPLAY, size=11, bold=True, + color=C_WHITE, align=PP_ALIGN.CENTER, char_spacing=400) + # 한글 설명 + add_textbox(s, x + Inches(0.2), ky + Inches(1.75), kw - Inches(0.4), Inches(0.5), + ko, font=FONT_BODY, size=10, color=C_TEXT_DIM, + align=PP_ALIGN.CENTER, line_spacing=1.3) + + # 하단 — Featured 이력 & 플랫폼 다양성 + fy = Inches(5.3) + # 좌측 Featured + add_rect(s, Inches(0.7), fy, Inches(6), Inches(1.7), C_BG_DARK) + add_rect(s, Inches(0.7), fy, Inches(0.1), Inches(1.7), C_GOLD) + add_textbox(s, Inches(0.95), fy + Inches(0.2), Inches(5), Inches(0.3), + 'FEATURED & MILESTONES', + font=FONT_DISPLAY, size=11, bold=True, color=C_GOLD, + char_spacing=400) + feats = [ + '· 데빌즈 — 컴투스 인기 무료 게임 1위 (2013/11)', + '· 만렙 영웅키우기 — 금주의 추천 게임 (2017/05)', + '· 드래곤 헌터스 — 인기 인디 게임 선정 (2015/03)', + '· 디스트로이어 — 넥슨 모바일 퍼블리싱 계약', + ] + for i, f in enumerate(feats): + add_textbox(s, Inches(0.95), fy + Inches(0.55 + i * 0.28), Inches(5.7), Inches(0.3), + f, font=FONT_BODY, size=11, color=C_WHITE) + + # 우측 플랫폼 다양성 + add_rect(s, Inches(7), fy, Inches(5.65), Inches(1.7), C_BG_DARK) + add_rect(s, Inches(7), fy, Inches(0.1), Inches(1.7), C_CRIMSON) + add_textbox(s, Inches(7.25), fy + Inches(0.2), Inches(5), Inches(0.3), + 'MULTI-PLATFORM EXPERIENCE', + font=FONT_DISPLAY, size=11, bold=True, color=C_CRIMSON_2, + char_spacing=400) + platforms = [ + ('피쳐폰', 'SKT · KT · LGU+'), + ('스마트폰', 'Android · iOS'), + ('콘솔/디바이스', 'NDS · GP2X'), + ('신기술', 'AR · 딥러닝'), + ] + for i, (p, p2) in enumerate(platforms): + py = fy + Inches(0.55 + i * 0.28) + add_textbox(s, Inches(7.25), py, Inches(1.5), Inches(0.3), + p, font=FONT_DISPLAY, size=11, bold=True, color=C_WHITE) + add_textbox(s, Inches(8.7), py, Inches(3.8), Inches(0.3), + p2, font=FONT_BODY, size=10.5, color=C_TEXT_DIM) + + # 페이지 번호 + add_textbox(s, Inches(12), Inches(7.15), Inches(1), Inches(0.3), + '03 / 20', font=FONT_DISPLAY, size=9, color=C_GOLD, + align=PP_ALIGN.RIGHT) + + +# ════════════════════ S4. 작품 인덱스 (Editorial Grid) ════════════════════ +def s04_index(): + s = prs.slides.add_slide(blank) + add_rect(s, 0, 0, SLIDE_W, SLIDE_H, C_IVORY) + + # 헤더 + add_textbox(s, Inches(0.7), Inches(0.7), Inches(6), Inches(0.35), + '03 · FEATURED WORKS', font=FONT_DISPLAY, size=10, bold=True, + color=C_CRIMSON, char_spacing=400) + add_textbox(s, Inches(0.7), Inches(1.05), Inches(11), Inches(0.9), + '대표 작품 13선', + font=FONT_DISPLAY, size=40, bold=True, color=C_BG_DEEP) + add_rect(s, Inches(0.7), Inches(2.0), Inches(1.2), Inches(0.05), C_GOLD) + + works = [ + ('01', '매직 AR', '2019', 'AR 실시간 전략 PvP', True), + ('02', '쿠로네코', '2019', '캐릭터 수집 방치형 RPG', True), + ('03', '만렙 영웅 키우기', '2017', 'RPG · 방치형 (3 시리즈)', False), + ('04', '프로젝트 BC', '—', '하드코어 액션 MORPG', False), + ('05', '데빌즈 아레나', '—', '수집 성장형 MORPG', False), + ('06', '드래곤 헌터스', '2015', '액션 RPG · 인디 선정', False), + ('07', '데빌즈', '2013', '액션 RPG · 1위', True), + ('08', '동물 그림 맞추기', '2012', '퍼즐 / 교육', False), + ('09', '마스터 오브 던전', '2012', '시나리오형 액션 RPG', True), + ('10', '최종병기 활', '2011', '로그라이크 · 영화 OSMU', True), + ('11', '디스트로이어', '2010', '액션 RPG · 넥슨', False), + ('12', '에프터 스쿨', '2009', '미니게임 · 타이쿤', False), + ('13', '학교가는 길', '2007', '미니게임 · SKT 킬러', False), + ] + # 매거진 그리드 — 작품 행마다 큰 #번호 + 제목 + 메타 + cols = 4 + cell_w = Inches(3.05) + cell_h = Inches(1.05) + gap_x = Inches(0.1) + gap_y = Inches(0.12) + start_x = Inches(0.7) + start_y = Inches(2.3) + for i, (num, title, year, desc, has_img) in enumerate(works): + col = i % cols + row = i // cols + x = start_x + col * (cell_w + gap_x) + y = start_y + row * (cell_h + gap_y) + # 배경 카드 (흰 + 굵은 좌측 띠) + add_rect(s, x, y, cell_w, cell_h, C_WHITE) + add_rect(s, x, y, Inches(0.08), cell_h, C_CRIMSON if has_img else C_PRIMARY) + # 큰 #번호 + add_textbox(s, x + Inches(0.2), y + Inches(0.05), Inches(1), Inches(0.6), + num, font=FONT_DISPLAY, size=32, bold=True, + color=C_BG_DEEP, line_spacing=1.0) + # 우상단 연도 + add_textbox(s, x + Inches(0.2), y + Inches(0.62), Inches(1), Inches(0.25), + year, font=FONT_DISPLAY, size=9, color=C_TEXT_MUTED, + char_spacing=200) + # 제목 + add_textbox(s, x + Inches(1.15), y + Inches(0.13), cell_w - Inches(1.25), Inches(0.4), + title, font=FONT_DISPLAY, size=13, bold=True, color=C_BG_DEEP) + # 설명 + add_textbox(s, x + Inches(1.15), y + Inches(0.48), cell_w - Inches(1.25), Inches(0.45), + desc, font=FONT_BODY, size=9.5, color=C_TEXT_MUTED, + line_spacing=1.3) + # 이미지 보유 표시 (크림슨 도트) + if has_img: + add_circle(s, x + cell_w - Inches(0.32), y + Inches(0.15), + Inches(0.15), C_CRIMSON) + + # 범례 + add_circle(s, Inches(0.7), Inches(7.05), Inches(0.13), C_CRIMSON) + add_textbox(s, Inches(0.95), Inches(6.97), Inches(4), Inches(0.3), + '대표 비주얼 보유', font=FONT_BODY, size=9, color=C_TEXT_MUTED) + # 페이지 + add_textbox(s, Inches(12), Inches(7.15), Inches(1), Inches(0.3), + '04 / 20', font=FONT_DISPLAY, size=9, color=C_TEXT_MUTED, + align=PP_ALIGN.RIGHT) + + +# ════════════════════ 게임 슬라이드 빌더 — 2종 (Hero / Editorial) ════════════════════ +def build_game_hero(page, num, title, subtitle, info, overviews, role, kpi, img_file): + """이미지 보유 작품 — Hero 레이아웃 (이미지 좌 풀블리드 + 우측 콘텐츠).""" + s = prs.slides.add_slide(blank) + add_rect(s, 0, 0, SLIDE_W, SLIDE_H, C_IVORY) + + # 좌측 이미지 영역 (50%) + IMG_W = Inches(6.0) + add_rect(s, 0, 0, IMG_W, SLIDE_H, C_BG_DEEP) + if img_file: + img_path = os.path.join(IMG_DIR, img_file) + if os.path.exists(img_path): + add_image_fit(s, img_path, 0, 0, IMG_W, SLIDE_H, cover=True) + # 어두운 오버레이 그라데이션 (좌 하단 → 텍스트 가독성) + ov = s.shapes.add_shape(MSO_SHAPE.RECTANGLE, 0, 0, IMG_W, SLIDE_H) + ov.line.fill.background() + spPr = ov._element.spPr + for child in spPr.findall(qn('a:solidFill')): + spPr.remove(child) + grad = etree.SubElement(spPr, qn('a:gradFill'), {'flip': 'none', 'rotWithShape': '1'}) + gsLst = etree.SubElement(grad, qn('a:gsLst')) + gs1 = etree.SubElement(gsLst, qn('a:gs'), {'pos': '0'}) + clr1 = etree.SubElement(gs1, qn('a:srgbClr'), {'val': '0A1628'}) + etree.SubElement(clr1, qn('a:alpha'), {'val': '80000'}) + gs2 = etree.SubElement(gsLst, qn('a:gs'), {'pos': '60000'}) + clr2 = etree.SubElement(gs2, qn('a:srgbClr'), {'val': '0A1628'}) + etree.SubElement(clr2, qn('a:alpha'), {'val': '20000'}) + gs3 = etree.SubElement(gsLst, qn('a:gs'), {'pos': '100000'}) + clr3 = etree.SubElement(gs3, qn('a:srgbClr'), {'val': '0A1628'}) + etree.SubElement(clr3, qn('a:alpha'), {'val': '60000'}) + etree.SubElement(grad, qn('a:lin'), {'ang': '5400000', 'scaled': '0'}) + ln = spPr.find(qn('a:ln')) + if ln is not None: + spPr.remove(grad) + spPr.insert(list(spPr).index(ln), grad) + + # 좌측 텍스트 오버레이 + add_rect(s, Inches(0.6), Inches(0.7), Inches(0.04), Inches(0.4), C_GOLD) + add_textbox(s, Inches(0.8), Inches(0.7), Inches(4), Inches(0.4), + f'WORK · {num}', font=FONT_DISPLAY, size=11, bold=True, + color=C_GOLD, char_spacing=400) + + # 작품 큰 타이틀 (이미지 위) + add_textbox(s, Inches(0.6), Inches(5.0), Inches(5.5), Inches(1.0), + title, font=FONT_DISPLAY, size=36, bold=True, + color=C_WHITE, line_spacing=1.05) + add_textbox(s, Inches(0.6), Inches(6.05), Inches(5.5), Inches(0.4), + subtitle, font=FONT_DISPLAY, size=14, color=C_GOLD, + italic=True, line_spacing=1.2) + add_rect(s, Inches(0.6), Inches(6.55), Inches(0.6), Inches(0.04), C_CRIMSON) + # 게임 메타 (이미지 위 하단) + meta_str = ' · '.join([f'{v}' for k, v in info.items()]) + add_textbox(s, Inches(0.6), Inches(6.7), Inches(5.5), Inches(0.6), + meta_str, font=FONT_DISPLAY, size=10, color=C_WHITE, + char_spacing=200, line_spacing=1.3) + + # 우측 콘텐츠 (50%) + CX = Inches(6.4) + # 상단 — 페이지·섹션 + add_textbox(s, CX, Inches(0.7), Inches(6.5), Inches(0.3), + 'GAME OVERVIEW', font=FONT_DISPLAY, size=10, bold=True, + color=C_CRIMSON, char_spacing=400) + add_rect(s, CX, Inches(1.05), Inches(0.5), Inches(0.04), C_CRIMSON) + + # 작품 정보 표 (compact) + info_y = Inches(1.3) + add_rect(s, CX, info_y, Inches(6.5), Inches(1.1), C_WHITE) + add_rect(s, CX, info_y, Inches(0.06), Inches(1.1), C_GOLD) + info_keys = list(info.keys()) + info_cols = len(info_keys) + col_w = Inches(6.5 / info_cols) + for i, k in enumerate(info_keys): + x = CX + i * col_w + add_textbox(s, x + Inches(0.2), info_y + Inches(0.12), col_w - Inches(0.2), Inches(0.25), + k.upper(), font=FONT_DISPLAY, size=8.5, bold=True, + color=C_GOLD_DEEP, char_spacing=300) + add_textbox(s, x + Inches(0.2), info_y + Inches(0.4), col_w - Inches(0.2), Inches(0.6), + info[k], font=FONT_DISPLAY, size=11, bold=True, + color=C_BG_DEEP, line_spacing=1.2) + + # 개요 불릿 + add_textbox(s, CX, Inches(2.65), Inches(6.5), Inches(0.3), + 'OVERVIEW', font=FONT_DISPLAY, size=9.5, bold=True, + color=C_PRIMARY, char_spacing=400) + y = Inches(3.0) + for line in overviews[:5]: + add_circle(s, CX, y + Inches(0.13), Inches(0.07), C_CRIMSON) + add_textbox(s, CX + Inches(0.18), y, Inches(6.4), Inches(0.6), + line, font=FONT_BODY, size=10.5, color=C_TEXT_DARK, + line_spacing=1.35) + est_lines = max(1, len(line) // 55 + 1) + y += Inches(0.32 + 0.20 * (est_lines - 1)) + if y > Inches(5.8): + break + + # MY ROLE (강조 박스) + role_y = Inches(6.05) + add_rect(s, CX, role_y, Inches(6.5), Inches(0.7), C_BG_DEEP) + add_rect(s, CX, role_y, Inches(0.08), Inches(0.7), C_CRIMSON) + add_textbox(s, CX + Inches(0.25), role_y + Inches(0.08), Inches(4), Inches(0.3), + 'MY ROLE', font=FONT_DISPLAY, size=9, bold=True, + color=C_GOLD, char_spacing=400) + add_textbox(s, CX + Inches(0.25), role_y + Inches(0.35), Inches(6), Inches(0.35), + role, font=FONT_BODY, size=10.5, color=C_WHITE) + + # KPI + if kpi: + kpi_y = Inches(6.85) + add_textbox(s, CX, kpi_y, Inches(6.5), Inches(0.4), + f'🏆 {kpi}', font=FONT_BODY, size=9.5, color=C_CRIMSON, + italic=True, bold=True) + # 페이지 + add_textbox(s, CX, Inches(7.2), Inches(6.5), Inches(0.25), + f'{page:02d} / 20', font=FONT_DISPLAY, size=9, color=C_TEXT_MUTED, + align=PP_ALIGN.RIGHT, char_spacing=300) + + +def build_game_editorial(page, num, title, subtitle, info, overviews, role, kpi): + """이미지 없는 작품 — Editorial/Magazine 레이아웃 (큰 타입 + 그리드).""" + s = prs.slides.add_slide(blank) + add_rect(s, 0, 0, SLIDE_W, SLIDE_H, C_IVORY) + # 상단 다크 헤더 (전체 슬라이드 35%) + add_rect(s, 0, 0, SLIDE_W, Inches(3.2), C_BG_DEEP) + + # 좌측 강조 띠 + add_rect(s, 0, 0, Inches(0.2), Inches(3.2), C_CRIMSON) + + # 상단 라벨 + add_textbox(s, Inches(0.7), Inches(0.6), Inches(8), Inches(0.4), + f'WORK · {num}', + font=FONT_DISPLAY, size=11, bold=True, color=C_GOLD, + char_spacing=400) + + # 큰 타이틀 (디스플레이) + add_textbox(s, Inches(0.7), Inches(1.05), Inches(11), Inches(1.4), + title, font=FONT_DISPLAY, size=58, bold=True, color=C_WHITE, + line_spacing=1.0) + + # 부제 + add_textbox(s, Inches(0.7), Inches(2.45), Inches(11), Inches(0.5), + subtitle, font=FONT_DISPLAY, size=18, color=C_GOLD, + italic=True) + + # 우상단 페이지·번호 디스플레이 + add_textbox(s, Inches(11), Inches(0.6), Inches(2), Inches(0.4), + f'{page:02d} / 20', font=FONT_DISPLAY, size=10, color=C_TEXT_DIM, + align=PP_ALIGN.RIGHT, char_spacing=400) + + # 우측 큰 번호 (배경 워터마크) + add_textbox(s, Inches(11.2), Inches(0.85), Inches(2), Inches(1.6), + num, font=FONT_ACCENT, size=110, bold=True, + color=C_PRIMARY, align=PP_ALIGN.RIGHT, line_spacing=1.0) + + # 본문 영역 (아이보리 하단) + # 상단 info 띠 + info_y = Inches(3.4) + add_rect(s, 0, info_y, SLIDE_W, Inches(0.95), C_WHITE) + info_keys = list(info.keys()) + cols = len(info_keys) + col_w = Inches(13.333 / cols) + for i, k in enumerate(info_keys): + x = Inches(0) + i * col_w + if i > 0: + add_rect(s, x, info_y + Inches(0.15), Inches(0.01), Inches(0.65), C_BORDER) + add_textbox(s, x + Inches(0.4), info_y + Inches(0.12), col_w - Inches(0.4), Inches(0.3), + k.upper(), font=FONT_DISPLAY, size=9, bold=True, + color=C_GOLD_DEEP, char_spacing=400) + add_textbox(s, x + Inches(0.4), info_y + Inches(0.42), col_w - Inches(0.5), Inches(0.5), + info[k], font=FONT_DISPLAY, size=13, bold=True, + color=C_BG_DEEP) + # info 띠 하단 라인 + add_rect(s, 0, info_y + Inches(0.95), SLIDE_W, Inches(0.03), C_GOLD) + + # 본문 — 2단 매거진 (좌: 게임 개요 / 우: MY ROLE + 강조) + bx_y = Inches(4.55) + # 좌측 게임 개요 + add_textbox(s, Inches(0.7), bx_y, Inches(6), Inches(0.35), + 'GAME OVERVIEW', font=FONT_DISPLAY, size=10, bold=True, + color=C_CRIMSON, char_spacing=400) + add_rect(s, Inches(0.7), bx_y + Inches(0.35), Inches(0.5), Inches(0.04), C_CRIMSON) + y = bx_y + Inches(0.55) + for line in overviews[:5]: + add_circle(s, Inches(0.7), y + Inches(0.13), Inches(0.07), C_CRIMSON) + add_textbox(s, Inches(0.9), y, Inches(6.0), Inches(0.6), + line, font=FONT_BODY, size=11, color=C_TEXT_DARK, + line_spacing=1.35) + est_lines = max(1, len(line) // 50 + 1) + y += Inches(0.32 + 0.20 * (est_lines - 1)) + if y > Inches(7.0): + break + + # 우측 — MY ROLE 카드 + KPI + rx = Inches(7.3) + rw = Inches(5.4) + # MY ROLE 카드 + add_rect(s, rx, bx_y, rw, Inches(1.6), C_BG_DEEP) + add_rect(s, rx, bx_y, Inches(0.1), Inches(1.6), C_CRIMSON) + add_textbox(s, rx + Inches(0.3), bx_y + Inches(0.2), rw - Inches(0.3), Inches(0.35), + 'MY ROLE', font=FONT_DISPLAY, size=10, bold=True, + color=C_GOLD, char_spacing=400) + add_textbox(s, rx + Inches(0.3), bx_y + Inches(0.55), rw - Inches(0.4), Inches(1.0), + role, font=FONT_BODY, size=13, color=C_WHITE, line_spacing=1.4) + # KPI/Featured + if kpi: + ky = bx_y + Inches(1.8) + add_rect(s, rx, ky, rw, Inches(0.85), C_CRIMSON) + add_textbox(s, rx + Inches(0.3), ky + Inches(0.15), rw - Inches(0.4), Inches(0.3), + '🏆 KPI / FEATURED', font=FONT_DISPLAY, size=10, bold=True, + color=C_WHITE, char_spacing=400) + add_textbox(s, rx + Inches(0.3), ky + Inches(0.45), rw - Inches(0.4), Inches(0.4), + kpi, font=FONT_BODY, size=11, color=C_WHITE, + line_spacing=1.3) + + +# ──────────── 게임 데이터 (이미지 보유 여부 포함) ──────────── +GAMES_DATA = [ + # (page, num, title, subtitle, info, overviews, role, kpi, img_file) + (5, '01', '매직 AR', 'AR 기반 실시간 전략 PvP', + {'장르': 'AR 실시간 전략 PvP', '플랫폼': 'Android, iOS', '출시': '2019. 09'}, + ['증강현실(AR)과 딥 러닝 기술을 활용해 사물을 스캔하여 실제 게임에서 활용할 수 있는 새로운 개념의 게임.', + '클래시 로얄과 유사한 실시간 전략 PvP. 어떤 사물을 스캔하는지에 따라 다양한 변수가 발생되는 것이 특징.', + '신 기술 기반 투자 유치 목적 프로젝트로, 게임성 개선 및 폴리싱 후 소프트 런칭 버전까지 대응.', + '※ 개발부·사업부 간 게임에 대한 이해도 차이와 경영 사정 악화로 인해 퇴사 결심.'], + '전투 밸런스 작업 · 게임 폴리싱 · 아이템 보상 시스템(상자·전투 보상) 설계', + None, + 'slide03_img10.jpg'), + (6, '02', '쿠로네코', 'IP 활용 방치형 캐릭터 수집 RPG', + {'장르': '캐릭터 수집 방치형 RPG', '플랫폼': 'Android', '출시': '개발 중단'}, + ['대만에서 흥행한 이노센트 IP를 활용한 캐릭터 수집·성장 방치형 RPG 프로젝트.', + '200여 종의 개성 있는 캐릭터 리소스를 활용해 수집·성장의 재미를 제공.', + '탱커/원딜/근딜러/서포터로 구성, 캐릭터마다 다양한 컨셉의 스킬로 전략적 덱 조합 가능.', + '원작 리소스를 활용해 개발기간을 단축, 캐릭터 가챠 BM으로 매출 극대화 구상.', + '※ 개발 진척 60% 달성 후 핵심 개발자 퇴사 및 내부 사정으로 개발 중단.'], + '프로젝트 디렉팅(PD) · 핵심 시스템 기획 · UI&UX 컨셉팅 등 기획 실무 일체', + None, + 'slide04_img13.png'), + (7, '03', '만렙 영웅 키우기 시리즈', '전사편 · 소환사편 · 마검사편 (3 시리즈)', + {'장르': 'RPG / 방치형', '플랫폼': 'Android, iOS', '출시': '2017. 04'}, + ['반복적인 자동 전투로 재화를 수집하고, 무한 환생을 통해 끊임없이 성장하는 방치형 RPG.', + '코어 RPG의 감성을 살린 아이템·스킬 시스템으로 기존 방치형 게임과의 차별화 어필.', + '총 개발기간 약 4개월. 전사편 지표 분석을 통해 의미 있는 성과 달성.', + '결투장·쟁탈전·랭킹 시스템 등 다양한 경쟁 컨텐츠로 유저 몰입 유도.'], + '프로젝트 디렉팅(PD) · 기획 실무 일체', + 'ARPPU ₩45,553 · Retention 36.7%(1D) · 최고 매출 168위 · 추천 게임 2017/05', + None), + (8, '04', '프로젝트 BC', '푸른 별의 노래 (가제) — 2D 하드코어 MORPG', + {'장르': '하드코어 액션 MORPG', '플랫폼': 'Android', '출시': '개발 중단'}, + ['화려한 2D 그래픽과 액션 RPG의 진수를 느낄 수 있는 하드코어 감성의 2D MORPG.', + '전사·마검사·소환사 3명의 캐릭터가 멀티 시나리오로 제공되며 각기 다른 플레이 스타일 보유.', + '유저간 PvP, RvR 형태의 길드 대전 및 약탈 시스템으로 경쟁 유도.', + '코스튬·펫·용병 시스템을 통해 캐릭터 성장 외에도 풍부한 즐길거리 제공.', + '※ 핵심 시스템 및 컨텐츠 80% 완성, 경영 악화로 인해 일시 중단.'], + '핵심 시스템 및 컨텐츠 기획 · 몬스터 & 캐릭터 전투 시스템 관련 기획', + None, + None), + (9, '05', '데빌즈 아레나', '자사 IP 활용 액션 MORPG', + {'장르': '수집 성장형 MORPG', '플랫폼': 'Android', '출시': '개발 중단'}, + ['데빌즈·마스터오브 던전 인기 캐릭터 + 오리지널 30여 종을 활용한 캐릭터 수집·성장 MORPG.', + '실시간 PvP를 비롯한 다양한 경쟁형 컨텐츠 제공.', + '캐릭터간 상성과 플레이 스타일이 다양해 수집·조작의 재미를 모두 추구.', + '※ 정부 지원사업과 연계해 70% 개발 진행, 개발기간 대비 수익성 부족으로 잠정 드랍.'], + '프로젝트 디렉팅(PD) · 기획 실무 일체', + None, + None), + (10, '06', '드래곤 헌터스', '검증된 캐주얼 액션 RPG', + {'장르': '액션 RPG', '플랫폼': 'N스토어 · 원스토어 · GP2X', '출시': '2015. 02'}, + ['피쳐폰 시절 이통 3사 25만 다운로드 이상을 기록하며 재미 검증을 받은 캐주얼 ARPG.', + '다이나믹한 콤보 액션과 필드 기믹으로 조작감에 대한 호평을 받음.', + '아이템 파밍·스킬 트리 구성으로 캐릭터 성장의 묘미 제공.', + '스마트폰 버전 리뉴얼 출시, 무리한 BM 적용으로 성과는 저조.'], + '프로젝트 디렉팅(PD) · 기획 실무 일체', + '인기 인디 게임 선정 (2015/03)', + None), + (11, '07', '데빌즈', '몰입형 싱글 액션 RPG · 인기 1위', + {'장르': '액션 RPG', '플랫폼': 'Android · iOS', '출시': '2013. 10'}, + ['아이템 옵션 조합 파밍의 재미와 시나리오 기반 싱글 액션 RPG.', + '4명의 캐릭터가 각자 시점으로 진행되는 멀티 시나리오로 정통 RPG의 색다른 경험 제공.', + '다양한 옵션·등급 아이템과 스텟 포인트 투자로 성장 방법 다양화.', + '노말/하드/헬 난이도 + 최대 레벨 999로 장기 콘텐츠, 보스 레이드·무한의 던전 등 운영.', + '아이템 위주 BM을 통해 수년간 안정적 수익 창출.'], + '핵심 시스템·컨텐츠 기획 · 기획 실무 일체 (맵 & 레벨 디자인 제외)', + '인기 무료 게임 1위 (2013/11) · 컴투스 퍼블리싱', + None), + (12, '08', '동물 그림 맞추기', '유아 교육용 어플리케이션', + {'장르': '퍼즐 / 교육', '플랫폼': 'Android', '출시': '2012. 09'}, + ['캐시카우 확보 및 기획팀 유니티 엔진 학습을 위한 단기 개발 프로젝트.', + '귀엽고 깔끔한 그래픽과 직관적인 게임 방법, 유아 교육용 어플리케이션으로 출시.', + '자사 게임 라인업 다양화 및 지속적 수익 타이틀 실험의 대표 케이스.', + '※ 총 인원 3명, 개발 기간 약 2.5주의 초단기 프로젝트.'], + '아트 컨셉 · UI&UX 설계 · UI 프리펩 배치 등 실무', + None, + None), + (13, '09', '마스터 오브 던전', '시나리오형 액션 RPG', + {'장르': '액션 RPG', '플랫폼': 'Android', '출시': '2012. 07'}, + ['디스트로이어의 개발 노하우와 엔진을 활용해 제작된 싱글 기반 시나리오형 액션 RPG.', + '짜임새 있는 시나리오와 퀘스트 이벤트로 엔딩까지의 과정을 풍성하게 구성.', + '캐릭터 육성 방식에 따라 특성이 부여되는 정통 RPG 표방.', + '몰이사냥과 다양한 옵션 아이템으로 장비 세팅·파밍의 재미 강조.', + '인앱 과금 시장 전환기에서 다양한 BM 실험을 시도한 프로젝트.'], + '아이템 및 전투 시스템 기획 · 시나리오 스크립트 작업 일체', + None, + 'slide11_img11.jpg'), + (14, '10', '최종병기 활', '영화 OSMU 로그라이크', + {'장르': '로그라이크', '플랫폼': 'Android', '출시': '2011. 09'}, + ['영화 「최종병기 활」 홍보를 위한 원소스 멀티유즈 전략 프로젝트.', + '영화 개봉 시점에 맞춰 출시하기 위해 로그라이크를 변형한 캐주얼 RPG로 기획.', + '영화 시나리오 플롯을 계승하되 게임만의 오리지날 요소를 가미해 색다른 재미 제공.', + '재화 수집 → 무기·방어구 성장 구조, 타이밍 조작감 강조.', + '※ 촉박한 일정과 단말기 커버리지 한계로 흥행에는 실패.'], + '핵심 시스템 및 컨텐츠 기획 · 시나리오 스크립트 작업 등 기획 실무 일체', + None, + 'slide12_img10.png'), + (15, '11', '디스트로이어', '대작 액션 RPG — 넥슨 퍼블리싱', + {'장르': '액션 RPG', '플랫폼': 'SKT · Android', '출시': '2010. 09'}, + ['드래곤 헌터의 성공과 개발력을 검증받아 넥슨과 퍼블리싱 계약이 체결된 대작 액션 프로젝트.', + '당시 모바일 최초로 4명의 캐릭터가 각각의 시점으로 진행되는 멀티 시나리오 제공.', + '거대 보스 레이드, 실시간 PvP 등 방대한 컨텐츠와 풍부한 즐길거리로 플레이타임 확보.', + '영웅 서기를 능가하는 대작 RPG를 목표로 했으나, 개발기간 오버 및 피쳐폰→스마트폰 시장 급변기 직격.', + '터치 조작 미지원으로 흥행에는 실패한 비운의 프로젝트.'], + '핵심 시스템 및 컨텐츠 기획 · 시나리오 스크립트 작업 등 기획 실무 일체', + '넥슨 모바일 퍼블리싱 계약', + None), + (16, '12', '에프터 스쿨', '미니게임 + 타이쿤 복합 장르', + {'장르': '미니게임', '플랫폼': 'SKT', '출시': '2009. 06'}, + ['학교가는길 시리즈의 성공에 힘입어 독특하고 기발한 컨셉으로 제안·개발한 프로젝트.', + '미니게임과 타이쿤 장르를 믹스해 엄마 파트·아들 파트를 나눠 다양한 할 거리 제공.', + '초기 60~70% 개발 단계에서 ARPG(드래곤헌터) 메인 기획 담당을 위해 인수인계.', + '※ 신입 인력 인수 이후 타이쿤 장르 소화 부족으로 미니게임 장르 전환, 게임성 모호로 흥행 실패.'], + '컨셉 기획 · 타이쿤 & 미니게임 초기 기획 담당', + None, + None), + (17, '13', '학교가는 길', 'SKT 킬러 콘텐츠 · 시리즈 흥행', + {'장르': '미니게임', '플랫폼': 'Android', '출시': '2007. 05'}, + ['SKT 킬러 컨텐츠로 선정되며 흥행에 성공한 최초의 프로젝트. 시리즈화에 성공해 3편까지 출시.', + '학교가는 길을 미니게임화한 독특한 컨셉과 개성 있는 디자인 연출로 피쳐폰 시절 높은 다운로드 수익 창출.', + '10여 종의 미니게임으로 구성. 각 미니게임은 외주 개발업체를 통해 진행.', + '초기 개발 단계부터 참여해 미니게임 전체 세부 기획·밸런싱·아트 컨셉 담당.', + '※ 퍼블리셔와의 수익 배분·라이선스 지분 이슈로 후속작은 타 업체 진행.'], + '컨셉 기획 · 미니게임 8종 시스템 기획 · 외주 개발 업체 관리 등 실무 전반', + 'SKT 킬러 콘텐츠 선정', + None), +] + + +# ════════════════════ S18. 경력 타임라인 ════════════════════ +def s18_timeline(): + s = prs.slides.add_slide(blank) + add_rect(s, 0, 0, SLIDE_W, SLIDE_H, C_IVORY) + + add_textbox(s, Inches(0.7), Inches(0.7), Inches(6), Inches(0.35), + '04 · CAREER TIMELINE', font=FONT_DISPLAY, size=10, bold=True, + color=C_CRIMSON, char_spacing=400) + add_textbox(s, Inches(0.7), Inches(1.05), Inches(11), Inches(0.9), + '13년의 여정', + font=FONT_DISPLAY, size=40, bold=True, color=C_BG_DEEP) + add_textbox(s, Inches(0.7), Inches(2.05), Inches(11), Inches(0.4), + '2007 — 2019 · 피쳐폰부터 AR까지, 모든 시장 전환점을 경험', + font=FONT_DISPLAY, size=12, color=C_TEXT_MUTED, italic=True) + + # 타임라인 — 가로 + tl_y = Inches(3.5) + tl_x_start = Inches(1) + tl_x_end = Inches(12.3) + # 메인 라인 (deep navy) + add_rect(s, tl_x_start, tl_y, tl_x_end - tl_x_start, Inches(0.04), C_BG_DEEP) + + # 마일스톤 + milestones = [ + ('2007', '학교가는 길', 'SKT 킬러 콘텐츠'), + ('2010', '디스트로이어', '넥슨 퍼블리싱'), + ('2013', '데빌즈', '인기 1위'), + ('2015', '드래곤 헌터스', '인디 선정'), + ('2017', '만렙 영웅 키우기', '매출 168위'), + ('2019', '매직 AR · 쿠로네코', 'AR · IP'), + ] + n = len(milestones) + step = (tl_x_end - tl_x_start) / (n - 1) + for i, (year, work, note) in enumerate(milestones): + x = tl_x_start + int(i * step) + # 큰 도트 + add_circle(s, x - Inches(0.18), tl_y - Inches(0.14), Inches(0.32), C_CRIMSON) + # 연도 (상단) + add_textbox(s, x - Inches(0.8), tl_y - Inches(0.85), Inches(1.6), Inches(0.35), + year, font=FONT_DISPLAY, size=18, bold=True, color=C_BG_DEEP, + align=PP_ALIGN.CENTER) + # 작품명 (하단) + add_textbox(s, x - Inches(1.0), tl_y + Inches(0.3), Inches(2.0), Inches(0.4), + work, font=FONT_DISPLAY, size=11, bold=True, color=C_BG_DEEP, + align=PP_ALIGN.CENTER, line_spacing=1.2) + # 비고 + add_textbox(s, x - Inches(1.0), tl_y + Inches(0.65), Inches(2.0), Inches(0.3), + note, font=FONT_BODY, size=9.5, color=C_GOLD_DEEP, + align=PP_ALIGN.CENTER, italic=True) + + # 하단 핵심 강점 (5종 카드) + sy = Inches(5.5) + add_textbox(s, Inches(0.7), sy, Inches(11), Inches(0.35), + 'CORE COMPETENCIES', + font=FONT_DISPLAY, size=11, bold=True, color=C_CRIMSON, + char_spacing=400) + add_rect(s, Inches(0.7), sy + Inches(0.4), Inches(1), Inches(0.04), C_CRIMSON) + + competencies = [ + ('LEADING', 'Project\nDirection', 'PD · 메인 기획'), + ('SYSTEM', 'Core System\nDesign', '핵심 시스템·전투·BM'), + ('LIVE OPS', 'Live\nService', '운영·이벤트·BM 리뉴얼'), + ('NARRATIVE', 'Story &\nScripts', '시나리오·멀티 스크립트'), + ('MULTI', 'Multi-Role &\nPlatform', 'PD·PM·QA·외주 관리'), + ] + cx = Inches(0.7) + cw = Inches(2.36) + cy = Inches(6.05) + ch = Inches(1.2) + gx = Inches(0.1) + for i, (key, en, ko) in enumerate(competencies): + x = cx + i * (cw + gx) + add_rect(s, x, cy, cw, ch, C_WHITE, C_BORDER) + add_rect(s, x, cy, cw, Inches(0.06), C_CRIMSON) + add_textbox(s, x, cy + Inches(0.15), cw, Inches(0.3), + key, font=FONT_DISPLAY, size=10, bold=True, color=C_CRIMSON, + align=PP_ALIGN.CENTER, char_spacing=400) + add_textbox(s, x, cy + Inches(0.45), cw, Inches(0.55), + en, font=FONT_DISPLAY, size=14, bold=True, color=C_BG_DEEP, + align=PP_ALIGN.CENTER, line_spacing=1.15) + add_textbox(s, x, cy + Inches(0.95), cw, Inches(0.2), + ko, font=FONT_BODY, size=9, color=C_TEXT_MUTED, + align=PP_ALIGN.CENTER) + + # 페이지 + add_textbox(s, Inches(12), Inches(7.15), Inches(1), Inches(0.3), + '18 / 20', font=FONT_DISPLAY, size=9, color=C_TEXT_MUTED, + align=PP_ALIGN.RIGHT) + + +# ════════════════════ S19. 참고 자료 (Videos + URLs) ════════════════════ +def s19_references(): + s = prs.slides.add_slide(blank) + add_rect(s, 0, 0, SLIDE_W, SLIDE_H, C_IVORY) + + add_textbox(s, Inches(0.7), Inches(0.7), Inches(6), Inches(0.35), + '05 · REFERENCES & DELIVERABLES', font=FONT_DISPLAY, size=10, bold=True, + color=C_CRIMSON, char_spacing=400) + add_textbox(s, Inches(0.7), Inches(1.05), Inches(11), Inches(0.9), + '참고 자료 및 결과물', + font=FONT_DISPLAY, size=40, bold=True, color=C_BG_DEEP) + add_textbox(s, Inches(0.7), Inches(2.05), Inches(11), Inches(0.4), + '플레이 영상 · 시연 영상 · 실무 작업 결과물 다운로드', + font=FONT_DISPLAY, size=12, color=C_TEXT_MUTED, italic=True) + + # 영상 6종 — 2 columns × 3 rows + refs = [ + ('데빌즈', '컴투스 퍼블리싱 · 인기 무료 1위', 'https://youtu.be/GZNZvpsk5WE', 'VIDEO'), + ('프로젝트 BC', '2014 · 하드코어 액션 MORPG 시연', 'https://youtu.be/maMV-lcPB3I', 'VIDEO'), + ('드래곤 헌터스', 'GP2X · HD 버전 플레이 영상', 'drive.google.com/.../1tUkQ_...', 'DRIVE'), + ('마스터 오브 던전', '시나리오형 액션 RPG 플레이 영상', 'https://youtu.be/9ipP9KkBZxI', 'VIDEO'), + ('쿠로네코', '개발 60% 완료 후 중단된 프로젝트 시연', 'drive.google.com/.../1poqqti2...', 'DRIVE'), + ('학교가는 길 1', '조이모아 퍼블리싱 · 플레이 영상', 'https://youtu.be/XMWlcZyJYSg', 'VIDEO'), + ] + grid_x = Inches(0.7) + grid_y = Inches(2.7) + cw = Inches(6.18) + ch = Inches(1.25) + gx = Inches(0.1) + gy = Inches(0.12) + cols = 2 + for i, (title, desc, url, kind) in enumerate(refs): + col = i % cols + row = i // cols + x = grid_x + col * (cw + gx) + y = grid_y + row * (ch + gy) + # 카드 + add_rect(s, x, y, cw, ch, C_WHITE) + add_rect(s, x, y, Inches(0.08), ch, C_CRIMSON if kind == 'VIDEO' else C_PRIMARY) + # KIND 배지 (우상단) + badge_w = Inches(0.8) + add_rect(s, x + cw - badge_w - Inches(0.2), y + Inches(0.18), badge_w, Inches(0.3), + C_BG_DEEP) + add_textbox(s, x + cw - badge_w - Inches(0.2), y + Inches(0.21), badge_w, Inches(0.3), + kind, font=FONT_DISPLAY, size=8.5, bold=True, + color=C_GOLD, align=PP_ALIGN.CENTER, char_spacing=300) + # 제목 + add_textbox(s, x + Inches(0.25), y + Inches(0.18), cw - Inches(1.3), Inches(0.4), + title, font=FONT_DISPLAY, size=15, bold=True, color=C_BG_DEEP) + # 설명 + add_textbox(s, x + Inches(0.25), y + Inches(0.55), cw - Inches(0.4), Inches(0.3), + desc, font=FONT_BODY, size=10, color=C_TEXT_MUTED, italic=True) + # URL + add_textbox(s, x + Inches(0.25), y + Inches(0.88), cw - Inches(0.4), Inches(0.3), + url, font=FONT_MONO, size=9.5, color=C_GOLD_DEEP) + + # 하단 — Work Outputs 다운로드 배너 + by = Inches(6.55) + add_rect(s, Inches(0.7), by, Inches(12.55), Inches(0.6), C_BG_DEEP) + add_rect(s, Inches(0.7), by, Inches(0.1), Inches(0.6), C_GOLD) + add_textbox(s, Inches(0.95), by + Inches(0.1), Inches(7), Inches(0.4), + '실무 작업 결과물 다운로드', + font=FONT_DISPLAY, size=14, bold=True, color=C_WHITE) + add_textbox(s, Inches(0.95), by + Inches(0.36), Inches(7), Inches(0.3), + '기획서 · 시스템 설계 · UI/UX 목업 · 시나리오 스크립트', + font=FONT_BODY, size=9.5, color=C_GOLD, italic=True) + add_textbox(s, Inches(7.9), by + Inches(0.18), Inches(5.2), Inches(0.4), + 'PORTFOLIO DOWNLOAD URL', + font=FONT_DISPLAY, size=10, bold=True, color=C_GOLD, + align=PP_ALIGN.RIGHT, char_spacing=400) + + add_textbox(s, Inches(12), Inches(7.25), Inches(1), Inches(0.3), + '19 / 20', font=FONT_DISPLAY, size=9, color=C_TEXT_MUTED, + align=PP_ALIGN.RIGHT) + + +# ════════════════════ S20. THANK YOU (Closing) ════════════════════ +def s20_closing(): + s = prs.slides.add_slide(blank) + # 풀블리드 그라데이션 (역방향) + bg = add_rect(s, 0, 0, SLIDE_W, SLIDE_H, C_BG_DEEP) + set_gradient_fill(bg, C_PRIMARY, C_BG_DEEP, angle=135) + + # 좌측 매거진 라인 + add_rect(s, Inches(0.7), Inches(0.8), Inches(0.04), Inches(5.9), C_CRIMSON) + + # 상단 라벨 + add_textbox(s, Inches(0.95), Inches(0.75), Inches(8), Inches(0.4), + 'CLOSING', font=FONT_DISPLAY, size=12, bold=True, + color=C_GOLD, char_spacing=400) + + # 거대 THANK YOU + add_textbox(s, Inches(0.95), Inches(1.7), Inches(12), Inches(2.5), + 'THANK\nYOU.', + font=FONT_DISPLAY, size=120, bold=True, color=C_WHITE, + line_spacing=0.95, char_spacing=-100) + + # 한글 부제 + add_textbox(s, Inches(0.95), Inches(5.0), Inches(12), Inches(0.6), + '함께 일할 기회를 기대합니다.', + font=FONT_DISPLAY, size=24, color=C_GOLD, italic=True) + + add_rect(s, Inches(0.95), Inches(5.9), Inches(2.5), Inches(0.05), C_CRIMSON) + + # 시그니처 + add_textbox(s, Inches(0.95), Inches(6.1), Inches(8), Inches(0.4), + 'LEE SEUNG-WOO', + font=FONT_DISPLAY, size=20, bold=True, color=C_WHITE, + char_spacing=400) + add_textbox(s, Inches(0.95), Inches(6.55), Inches(8), Inches(0.4), + 'Game Designer · PD · Director', + font=FONT_DISPLAY, size=13, color=C_GOLD, italic=True) + + # 우측 하단 — 페이지·날짜 + add_textbox(s, Inches(11), Inches(7.1), Inches(2), Inches(0.3), + '20 / 20 · 2019.12.22', + font=FONT_DISPLAY, size=10, color=C_GOLD, + align=PP_ALIGN.RIGHT, char_spacing=300) + + +# ──────────── 빌드 실행 ──────────── +print('=== Building Portfolio v2 ===') +s01_cover(); print(' 01 Cover') +s02_profile(); print(' 02 Profile') +s03_kpi_dashboard();print(' 03 KPI Dashboard') +s04_index(); print(' 04 Works Index') + +for game in GAMES_DATA: + page, num, title, subtitle, info, overviews, role, kpi, img_file = game + if img_file: + build_game_hero(page, num, title, subtitle, info, overviews, role, kpi, img_file) + print(f' {page:02d} {title} [HERO]') + else: + build_game_editorial(page, num, title, subtitle, info, overviews, role, kpi) + print(f' {page:02d} {title} [EDITORIAL]') + +s18_timeline(); print(' 18 Timeline') +s19_references(); print(' 19 References') +s20_closing(); print(' 20 Closing') + +prs.save(OUT_PATH) +print(f'\n✅ Saved: {OUT_PATH}') +print(f' Size: {os.path.getsize(OUT_PATH):,} bytes') +print(f' Slides: {len(prs.slides)}') diff --git a/_PC이관_보관/2026-10-12/git_제외_로컬파일/tmp/portfolio_images/slide01_img00.png b/_PC이관_보관/2026-10-12/git_제외_로컬파일/tmp/portfolio_images/slide01_img00.png new file mode 100644 index 0000000..086f150 Binary files /dev/null and b/_PC이관_보관/2026-10-12/git_제외_로컬파일/tmp/portfolio_images/slide01_img00.png differ diff --git a/_PC이관_보관/2026-10-12/git_제외_로컬파일/tmp/portfolio_images/slide01_img01.png b/_PC이관_보관/2026-10-12/git_제외_로컬파일/tmp/portfolio_images/slide01_img01.png new file mode 100644 index 0000000..abf4333 Binary files /dev/null and b/_PC이관_보관/2026-10-12/git_제외_로컬파일/tmp/portfolio_images/slide01_img01.png differ diff --git a/_PC이관_보관/2026-10-12/git_제외_로컬파일/tmp/portfolio_images/slide03_img10.jpg b/_PC이관_보관/2026-10-12/git_제외_로컬파일/tmp/portfolio_images/slide03_img10.jpg new file mode 100644 index 0000000..9f1ef69 Binary files /dev/null and b/_PC이관_보관/2026-10-12/git_제외_로컬파일/tmp/portfolio_images/slide03_img10.jpg differ diff --git a/_PC이관_보관/2026-10-12/git_제외_로컬파일/tmp/portfolio_images/slide04_img13.png b/_PC이관_보관/2026-10-12/git_제외_로컬파일/tmp/portfolio_images/slide04_img13.png new file mode 100644 index 0000000..5b81529 Binary files /dev/null and b/_PC이관_보관/2026-10-12/git_제외_로컬파일/tmp/portfolio_images/slide04_img13.png differ diff --git a/_PC이관_보관/2026-10-12/git_제외_로컬파일/tmp/portfolio_images/slide05_img14.png b/_PC이관_보관/2026-10-12/git_제외_로컬파일/tmp/portfolio_images/slide05_img14.png new file mode 100644 index 0000000..acea735 Binary files /dev/null and b/_PC이관_보관/2026-10-12/git_제외_로컬파일/tmp/portfolio_images/slide05_img14.png differ diff --git a/_PC이관_보관/2026-10-12/git_제외_로컬파일/tmp/portfolio_images/slide11_img10.jpg b/_PC이관_보관/2026-10-12/git_제외_로컬파일/tmp/portfolio_images/slide11_img10.jpg new file mode 100644 index 0000000..3bdb54e Binary files /dev/null and b/_PC이관_보관/2026-10-12/git_제외_로컬파일/tmp/portfolio_images/slide11_img10.jpg differ diff --git a/_PC이관_보관/2026-10-12/git_제외_로컬파일/tmp/portfolio_images/slide11_img11.jpg b/_PC이관_보관/2026-10-12/git_제외_로컬파일/tmp/portfolio_images/slide11_img11.jpg new file mode 100644 index 0000000..30bdd45 Binary files /dev/null and b/_PC이관_보관/2026-10-12/git_제외_로컬파일/tmp/portfolio_images/slide11_img11.jpg differ diff --git a/_PC이관_보관/2026-10-12/git_제외_로컬파일/tmp/portfolio_images/slide12_img09.jpg b/_PC이관_보관/2026-10-12/git_제외_로컬파일/tmp/portfolio_images/slide12_img09.jpg new file mode 100644 index 0000000..1064b31 Binary files /dev/null and b/_PC이관_보관/2026-10-12/git_제외_로컬파일/tmp/portfolio_images/slide12_img09.jpg differ diff --git a/_PC이관_보관/2026-10-12/git_제외_로컬파일/tmp/portfolio_images/slide12_img10.png b/_PC이관_보관/2026-10-12/git_제외_로컬파일/tmp/portfolio_images/slide12_img10.png new file mode 100644 index 0000000..b23c6b4 Binary files /dev/null and b/_PC이관_보관/2026-10-12/git_제외_로컬파일/tmp/portfolio_images/slide12_img10.png differ diff --git a/_PC이관_보관/2026-10-12/git_제외_로컬파일/tmp/portfolio_images/slide17_img08.png b/_PC이관_보관/2026-10-12/git_제외_로컬파일/tmp/portfolio_images/slide17_img08.png new file mode 100644 index 0000000..3a1f5a5 Binary files /dev/null and b/_PC이관_보관/2026-10-12/git_제외_로컬파일/tmp/portfolio_images/slide17_img08.png differ diff --git a/_PC이관_보관/2026-10-12/git_제외_로컬파일/tmp/portfolio_images/slide17_img16.png b/_PC이관_보관/2026-10-12/git_제외_로컬파일/tmp/portfolio_images/slide17_img16.png new file mode 100644 index 0000000..ef0437c Binary files /dev/null and b/_PC이관_보관/2026-10-12/git_제외_로컬파일/tmp/portfolio_images/slide17_img16.png differ diff --git a/_PC이관_보관/2026-10-12/git_제외_로컬파일/tmp/portfolio_images/slide17_img20.png b/_PC이관_보관/2026-10-12/git_제외_로컬파일/tmp/portfolio_images/slide17_img20.png new file mode 100644 index 0000000..03e1787 Binary files /dev/null and b/_PC이관_보관/2026-10-12/git_제외_로컬파일/tmp/portfolio_images/slide17_img20.png differ diff --git a/_PC이관_보관/2026-10-12/git_제외_로컬파일/tmp/portfolio_images/slide18_img05.png b/_PC이관_보관/2026-10-12/git_제외_로컬파일/tmp/portfolio_images/slide18_img05.png new file mode 100644 index 0000000..82deae7 Binary files /dev/null and b/_PC이관_보관/2026-10-12/git_제외_로컬파일/tmp/portfolio_images/slide18_img05.png differ diff --git a/_PC이관_보관/2026-10-12/git_제외_로컬파일/tmp/portfolio_images/slide20_img05.png b/_PC이관_보관/2026-10-12/git_제외_로컬파일/tmp/portfolio_images/slide20_img05.png new file mode 100644 index 0000000..1b13f71 Binary files /dev/null and b/_PC이관_보관/2026-10-12/git_제외_로컬파일/tmp/portfolio_images/slide20_img05.png differ diff --git a/_PC이관_보관/2026-10-12/git_제외_로컬파일/tmp/portfolio_images/slide20_img09.png b/_PC이관_보관/2026-10-12/git_제외_로컬파일/tmp/portfolio_images/slide20_img09.png new file mode 100644 index 0000000..a76160d Binary files /dev/null and b/_PC이관_보관/2026-10-12/git_제외_로컬파일/tmp/portfolio_images/slide20_img09.png differ diff --git a/_PC이관_보관/2026-10-12/git_제외_로컬파일/tmp/portfolio_images/slide20_img13.png b/_PC이관_보관/2026-10-12/git_제외_로컬파일/tmp/portfolio_images/slide20_img13.png new file mode 100644 index 0000000..a1894d2 Binary files /dev/null and b/_PC이관_보관/2026-10-12/git_제외_로컬파일/tmp/portfolio_images/slide20_img13.png differ diff --git a/_PC이관_보관/2026-10-12/git_제외_로컬파일/tmp/portfolio_images/slide20_img14.png b/_PC이관_보관/2026-10-12/git_제외_로컬파일/tmp/portfolio_images/slide20_img14.png new file mode 100644 index 0000000..82deae7 Binary files /dev/null and b/_PC이관_보관/2026-10-12/git_제외_로컬파일/tmp/portfolio_images/slide20_img14.png differ