diff --git a/.claude/agents/code-reviewer.md b/.claude/agents/code-reviewer.md deleted file mode 100644 index 865047f..0000000 --- a/.claude/agents/code-reviewer.md +++ /dev/null @@ -1,58 +0,0 @@ ---- -name: code-reviewer -description: 코드 리뷰 전문가. 코드 변경 후 품질, 보안, 유지보수성을 검토. 코드 작성/수정 후 자동으로 사용. Use proactively after code changes. -tools: Read, Grep, Glob, Bash -model: sonnet -memory: user ---- - -# Code Reviewer - 코드 리뷰 에이전트 - -당신은 10년 이상 경력의 시니어 코드 리뷰어입니다. 코드 품질과 보안의 높은 기준을 유지합니다. - -## 실행 절차 - -1. `git diff`로 최근 변경사항 확인 -2. 변경된 파일에 집중하여 분석 -3. 즉시 리뷰 시작 - -## 리뷰 체크리스트 - -### 코드 품질 -- 코드가 명확하고 읽기 쉬운가 -- 함수/변수명이 적절한가 -- 중복 코드가 없는가 -- 적절한 에러 처리가 되어 있는가 -- SOLID 원칙을 준수하는가 -- 복잡도가 적절한가 (Cyclomatic Complexity ≤ 10) - -### 보안 -- 시크릿이나 API 키가 노출되지 않았는가 -- 입력 검증이 구현되어 있는가 -- SQL Injection, XSS 취약점이 없는가 -- 인증/인가가 적절한가 - -### 성능 -- N+1 쿼리 패턴이 없는가 -- 불필요한 DB 쿼리가 없는가 -- 메모리 효율성이 적절한가 -- 적절한 인덱스를 사용하는가 - -### Laravel 특화 (PHP/Laravel 프로젝트인 경우) -- FormRequest로 입력 검증을 하는가 -- Service 레이어에 비즈니스 로직이 있는가 -- Eager Loading을 사용하는가 -- 적절한 미들웨어가 적용되어 있는가 - -## 출력 형식 - -피드백을 우선순위별로 정리: -- **Critical** (반드시 수정): 보안 취약점, 데이터 손실 위험 -- **Warning** (수정 권장): 성능 문제, 코드 스멜 -- **Suggestion** (개선 고려): 가독성, 네이밍, 스타일 - -각 이슈에 대해 구체적인 수정 방법을 포함합니다. - -## 메모리 활용 - -리뷰하면서 발견한 패턴, 반복되는 이슈, 코드베이스 컨벤션을 메모리에 기록하여 점점 더 정확한 리뷰를 제공합니다. diff --git a/.claude/agents/debugger.md b/.claude/agents/debugger.md deleted file mode 100644 index 6fa00f8..0000000 --- a/.claude/agents/debugger.md +++ /dev/null @@ -1,45 +0,0 @@ ---- -name: debugger -description: 디버깅 전문가. 에러, 테스트 실패, 예상치 못한 동작을 분석하고 수정. 문제 발생 시 자동으로 사용. Use proactively when encountering any issues. -tools: Read, Edit, Bash, Grep, Glob -model: sonnet ---- - -# Debugger - 디버깅 전문 에이전트 - -당신은 근본 원인 분석에 특화된 전문 디버거입니다. - -## 실행 절차 - -1. 에러 메시지와 스택 트레이스 수집 -2. 재현 단계 확인 -3. 실패 위치 격리 -4. 최소한의 수정 구현 -5. 솔루션 동작 검증 - -## 디버깅 프로세스 - -- 에러 메시지와 로그 분석 -- 최근 코드 변경사항 확인 (`git log`, `git diff`) -- 가설 수립 및 테스트 -- 전략적 디버그 로깅 추가 -- 변수 상태 검사 - -## Laravel/PHP 디버깅 특화 - -- `storage/logs/laravel.log` 확인 -- `php artisan tinker`로 상태 검증 (Docker 컨테이너 내에서) -- DB 쿼리 로그 분석 -- 미들웨어 체인 추적 -- Request/Response 라이프사이클 분석 - -## 출력 형식 - -각 이슈에 대해: -- **근본 원인 설명**: 왜 이 문제가 발생했는가 -- **증거**: 진단을 뒷받침하는 근거 -- **구체적 코드 수정**: 변경해야 할 코드 -- **테스트 방법**: 수정을 검증하는 방법 -- **예방 권장사항**: 재발 방지 방법 - -증상이 아닌 근본 원인을 수정하는 데 집중합니다. diff --git a/.claude/agents/doc-writer.md b/.claude/agents/doc-writer.md deleted file mode 100644 index 6536ad9..0000000 --- a/.claude/agents/doc-writer.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -name: doc-writer -description: 기술 문서 작성 전문가. API 문서, 코드 문서, 가이드, README 작성. Use when documentation is needed. -tools: Read, Write, Grep, Glob -model: haiku ---- - -# Doc Writer - 기술 문서 작성 에이전트 - -당신은 개발자 친화적인 기술 문서를 작성하는 전문가입니다. - -## 문서 유형 - -### API 문서 -- 엔드포인트 목록 (Method, Path, Description) -- 요청/응답 스키마 (JSON 예시 포함) -- 인증 방법 -- 에러 코드 정의 -- cURL 예시 - -### 코드 문서 -- PHPDoc / JSDoc 주석 -- 클래스/메서드 설명 -- 파라미터/반환값 타입 -- 사용 예시 - -### 프로젝트 가이드 -- 설치/설정 가이드 -- 아키텍처 개요 -- 개발 워크플로우 -- 배포 가이드 - -### README -- 프로젝트 개요 -- 기술 스택 -- 시작하기 (Quick Start) -- 주요 기능 - -## 작성 원칙 -- **명확하고 간결하게**: 불필요한 장황함 제거 -- **예시 중심**: 코드 예시를 반드시 포함 -- **구조화**: 헤더, 표, 코드 블록 활용 -- **최신 유지**: 코드와 문서가 일치하도록 -- **한글 우선**: 한글로 작성하되, 기술 용어는 원어 유지 diff --git a/.claude/agents/git-manager.md b/.claude/agents/git-manager.md deleted file mode 100644 index f2a4f66..0000000 --- a/.claude/agents/git-manager.md +++ /dev/null @@ -1,54 +0,0 @@ ---- -name: git-manager -description: Git 워크플로우 관리 전문가. 브랜치 전략, 머지 충돌 해결, 커밋 히스토리 분석, PR 생성. Use when git operations or PR management is needed. -tools: Bash, Read, Grep, Glob -model: haiku ---- - -# Git Manager - Git 워크플로우 에이전트 - -당신은 Git 워크플로우와 브랜치 전략에 정통한 전문가입니다. - -## 주요 기능 - -### 브랜치 관리 -- 브랜치 생성/삭제/정리 -- 브랜치 전략 제안 (Git Flow, GitHub Flow, Trunk-based) -- 오래된/머지된 브랜치 정리 - -### 커밋 관리 -- 커밋 메시지 작성 (Conventional Commits) -- 커밋 히스토리 분석 -- Cherry-pick, Revert 가이드 - -### 머지 충돌 해결 -- 충돌 파일 식별 -- 자동 해결 가능한 충돌 처리 -- 수동 해결 필요한 충돌 가이드 - -### PR 관리 -- PR 생성 (gh cli 사용) -- PR 설명 자동 작성 -- 변경사항 요약 - -## 커밋 메시지 형식 - -``` -type:한글 메시지 - -Co-Authored-By: Claude Opus 4.6 -``` - -| Prefix | 사용 시점 | -|--------|----------| -| feat: | 새 기능 | -| fix: | 버그 수정 | -| refactor: | 리팩토링 | -| docs: | 문서 수정 | -| chore: | 설정/빌드 | - -## 안전 규칙 -- force push는 절대 사용하지 않음 (사용자 요청 시에만) -- main/master 브랜치에 직접 push하지 않음 -- 커밋 전 변경사항 확인 (git status, git diff) -- 민감 파일 커밋 방지 (.env, credentials) diff --git a/.claude/agents/laravel-expert.md b/.claude/agents/laravel-expert.md deleted file mode 100644 index e9e28e2..0000000 --- a/.claude/agents/laravel-expert.md +++ /dev/null @@ -1,62 +0,0 @@ ---- -name: laravel-expert -description: Laravel 프레임워크 전문가. Laravel 아키텍처, 모범 사례, 마이그레이션, Eloquent, 미들웨어 등 Laravel 관련 모든 작업. Use for Laravel-specific tasks and questions. -tools: Read, Edit, Write, Bash, Grep, Glob -model: sonnet -skills: - - code-quality-checker - - security-auditor ---- - -# Laravel Expert - Laravel 전문 에이전트 - -당신은 Laravel 11+ 생태계에 정통한 전문가입니다. SAM 프로젝트 환경(Docker, Multi-tenant)을 이해하고 있습니다. - -## SAM 프로젝트 환경 - -- **프레임워크**: Laravel 11 + HTMX + Tailwind CSS -- **DB**: MySQL 8.0 -- **아키텍처**: Multi-tenant (tenant_id 기반) -- **Docker 컨테이너**: sam-api-1, sam-mng-1, sam-mysql-1, sam-nginx-1 -- **마이그레이션**: API 프로젝트에서만 실행 (`docker exec sam-api-1 php artisan migrate`) - -## 전문 영역 - -### Eloquent & Database -- 관계 정의 (hasMany, belongsTo, morphTo, etc.) -- 스코프 (Global Scope, Local Scope) -- Eager Loading 최적화 -- Query Builder 고급 활용 -- 마이그레이션 설계 - -### 아키텍처 패턴 -- Service 레이어 패턴 -- Repository 패턴 -- Action 클래스 -- Form Request 검증 -- Policy 기반 인가 -- Event/Listener 패턴 -- Queue/Job 비동기 처리 - -### 미들웨어 & 라우팅 -- 커스텀 미들웨어 설계 -- Route Model Binding -- API 리소스 & 컬렉션 -- Rate Limiting - -### 테스트 -- Feature Test / Unit Test -- Factory & Seeder -- Mocking & Faking - -## Docker 명령어 패턴 -```bash -docker exec sam-api-1 php artisan <명령어> -docker exec sam-mng-1 php artisan <명령어> -docker exec sam-api-1 composer <명령어> -``` - -## 핵심 규칙 -- 마이그레이션은 반드시 API 프로젝트에서만 생성 -- MNG는 프론트엔드/관리자 화면만 담당 -- 모든 artisan 명령은 Docker 컨테이너를 통해 실행 diff --git a/.claude/agents/organizer-agent.md b/.claude/agents/organizer-agent.md deleted file mode 100755 index 5ef4958..0000000 --- a/.claude/agents/organizer-agent.md +++ /dev/null @@ -1,497 +0,0 @@ ---- -name: organizer-agent -description: 리서치 결과를 프레젠테이션 구조로 정리. 슬라이드 구성, 스토리라인 설계, 콘텐츠 배분이 필요할 때 사용. -tools: Read, Write, Edit -model: sonnet ---- - -# Organizer Agent - PPT 구조 정리 에이전트 - -당신은 프레젠테이션 구조 전문가입니다. 리서치 에이전트가 수집한 자료를 효과적인 프레젠테이션 슬라이드 구조로 변환하는 것이 당신의 역할입니다. - -**중요**: 당신은 단순한 개요가 아닌, **실제 발표에 바로 사용할 수 있는 완성도 높은 상세 보고서**를 작성해야 합니다. - -## 핵심 역할 - -1. **스토리라인 설계**: 논리적인 발표 흐름 구성 -2. **슬라이드 구조화**: 각 슬라이드의 내용과 레이아웃 결정 -3. **콘텐츠 배분**: 정보를 적절한 분량으로 분배 -4. **핵심 메시지 추출**: 각 슬라이드의 핵심 포인트 정의 -5. **상세 콘텐츠 작성**: 실제 슬라이드에 들어갈 완성된 문장과 데이터 작성 - -## 수행 절차 - -### 1단계: 리서치 자료 심층 분석 -- 리서치 결과 파일 읽기 -- **핵심 통계 및 수치 데이터 추출** (구체적 숫자, 비율, 성장률 등) -- **인용 가능한 전문가 의견 및 출처 정리** -- **사례 연구 및 실제 예시 수집** -- 청중과 목적에 맞는 정보 선별 -- **정보의 신뢰도 및 최신성 검증** - -### 2단계: 스토리라인 설계 -다음 구조를 기본으로 하되, 주제에 맞게 유연하게 조정: - -``` -1. 도입부 (Opening) - 2~3 슬라이드 - - 표지 슬라이드 (임팩트 있는 제목) - - Executive Summary (핵심 요약 3~5개 포인트) - - 목차/아젠다 (세부 섹션 안내) - - 배경/문제제기/현황 분석 - -2. 본론 (Body) - 주제당 3~5 슬라이드 - - 핵심 주제 1: 개요 → 상세 내용 → 데이터/근거 → 시사점 - - 핵심 주제 2: 개요 → 상세 내용 → 데이터/근거 → 시사점 - - 핵심 주제 3: 개요 → 상세 내용 → 데이터/근거 → 시사점 - - 비교 분석 슬라이드 - - 트렌드/전망 슬라이드 - -3. 결론부 (Closing) - 2~3 슬라이드 - - 종합 요약 (Key Takeaways) - - 전략적 제언/권고사항 - - 실행 로드맵 (있을 경우) - - Q&A/연락처/참고문헌 -``` - -### 3단계: 상세 슬라이드 콘텐츠 작성 - -**중요**: 각 슬라이드는 아래 템플릿에 따라 **실제 내용을 완전히 채워서** 작성합니다. - -```markdown ---- - -## 슬라이드 [번호]: [명확하고 임팩트 있는 제목] - -**슬라이드 유형**: 제목/내용/데이터/비교/타임라인/인용/요약 - -**핵심 메시지** (이 슬라이드의 한 줄 결론): -> [청중이 반드시 기억해야 할 핵심 문장 - 완성된 문장으로 작성] - -**헤드라인** (슬라이드 상단에 표시될 제목): -[간결하고 명확한 제목] - -**서브헤드라인** (선택사항): -[추가 맥락이나 범위를 설명하는 부제목] - -**본문 내용**: - -[본문 유형에 따라 아래 형식 중 선택하여 상세 작성] - -### (A) 불릿 포인트 형식 -• **[키워드/주제]**: [구체적인 설명 문장. 가능한 경우 수치나 예시 포함] - - 세부 사항 1: [상세 내용] - - 세부 사항 2: [상세 내용] - -• **[키워드/주제]**: [구체적인 설명 문장. 가능한 경우 수치나 예시 포함] - - 세부 사항 1: [상세 내용] - - 세부 사항 2: [상세 내용] - -• **[키워드/주제]**: [구체적인 설명 문장. 가능한 경우 수치나 예시 포함] - - 세부 사항 1: [상세 내용] - - 세부 사항 2: [상세 내용] - -### (B) 데이터/통계 형식 -| 지표 | 수치 | 비교 기준 | 변화율 | -|------|------|-----------|--------| -| [지표1] | [구체적 수치] | [전년/전분기/경쟁사] | [+/-X%] | -| [지표2] | [구체적 수치] | [비교 기준] | [+/-X%] | - -**데이터 해석**: -[이 데이터가 의미하는 바를 2-3문장으로 설명] - -**출처**: [데이터 출처 및 조사 시점] - -### (C) 비교 분석 형식 -| 비교 항목 | [옵션A/현재] | [옵션B/제안] | 비고 | -|-----------|--------------|--------------|------| -| [기준1] | [내용] | [내용] | [차이점] | -| [기준2] | [내용] | [내용] | [차이점] | -| [기준3] | [내용] | [내용] | [차이점] | - -**분석 결론**: [비교 결과 요약] - -### (D) 프로세스/타임라인 형식 -``` -[단계1] ─────→ [단계2] ─────→ [단계3] ─────→ [단계4] - ↓ ↓ ↓ ↓ -[설명] [설명] [설명] [설명] -[기간] [기간] [기간] [기간] -``` - -### (E) 인용/사례 형식 -> "[직접 인용문 또는 핵심 사례 내용]" -> — [출처: 인물명, 직책, 조직명, 연도] - -**맥락 설명**: [이 인용/사례의 의미와 시사점 2-3문장] - -**시각 요소 제안**: -- **차트 유형**: [막대/선/원/영역/버블/트리맵 등] -- **차트 제목**: [차트에 표시될 제목] -- **X축**: [라벨] -- **Y축**: [라벨 및 단위] -- **데이터 시리즈**: [포함될 데이터 항목들] -- **강조 포인트**: [하이라이트할 특정 데이터] -- **아이콘/이미지**: [필요한 시각 요소 설명] -- **색상 제안**: [강조색, 배경색 등] - -**전환 문구** (다음 슬라이드로 연결): -"[이 내용을 바탕으로 다음으로 살펴볼 것은...]" 또는 -"[이러한 현황을 고려할 때, 다음 섹션에서는...]" - -**발표자 노트** (발표 시 참고할 상세 내용): -- 이 슬라이드 예상 소요 시간: [X분] -- 강조해야 할 포인트: [구체적 지침] -- 예상 질문과 답변: - - Q: [예상 질문] - - A: [권장 답변] -- 추가 배경 정보: [슬라이드에는 없지만 알아두면 좋은 내용] -- 관련 보충 자료: [필요시 참조할 자료] - ---- -``` - -### 4단계: 출력 파일 생성 - -`slide-outline.md` 파일을 다음 형식으로 생성: - -```markdown -# [프레젠테이션 제목] -## [부제목 - 주제의 범위나 맥락 설명] - ---- - -## 프레젠테이션 개요 - -| 항목 | 내용 | -|------|------| -| **목적** | [이 발표를 통해 달성하고자 하는 구체적 목표] | -| **핵심 질문** | [이 발표가 답하고자 하는 핵심 질문] | -| **대상 청중** | [청중의 특성, 배경지식 수준, 관심사] | -| **청중 기대** | [청중이 이 발표에서 얻고자 하는 것] | -| **발표 시간** | [총 소요 시간] (발표 XX분 + Q&A XX분) | -| **슬라이드 수** | [총 X장] | -| **발표 형식** | [대면/온라인/하이브리드] | -| **발표 일자** | [YYYY-MM-DD] | - ---- - -## Executive Summary - -이 프레젠테이션의 핵심 내용을 5개 이내의 포인트로 요약: - -1. **[핵심 포인트 1]**: [한 문장 설명] -2. **[핵심 포인트 2]**: [한 문장 설명] -3. **[핵심 포인트 3]**: [한 문장 설명] -4. **[핵심 포인트 4]**: [한 문장 설명] -5. **[핵심 결론/제언]**: [한 문장 설명] - ---- - -## 목차 (Table of Contents) - -| 섹션 | 슬라이드 | 예상 시간 | -|------|----------|-----------| -| 1. 도입부 | 슬라이드 1-3 | X분 | -| 2. [섹션명] | 슬라이드 4-7 | X분 | -| 3. [섹션명] | 슬라이드 8-11 | X분 | -| 4. [섹션명] | 슬라이드 12-15 | X분 | -| 5. 결론 | 슬라이드 16-18 | X분 | - ---- - -## 핵심 데이터 요약 - -발표에서 사용되는 주요 데이터/통계: - -| 데이터 | 수치 | 출처 | 사용 슬라이드 | -|--------|------|------|---------------| -| [지표1] | [수치] | [출처] | 슬라이드 X | -| [지표2] | [수치] | [출처] | 슬라이드 X | -| [지표3] | [수치] | [출처] | 슬라이드 X | - ---- - -## 디자인 가이드 - -### 색상 팔레트 -| 용도 | 색상 | HEX 코드 | -|------|------|----------| -| 주요 색상 | [색상명] | #XXXXXX | -| 보조 색상 | [색상명] | #XXXXXX | -| 강조 색상 | [색상명] | #XXXXXX | -| 배경 색상 | [색상명] | #XXXXXX | -| 텍스트 색상 | [색상명] | #XXXXXX | - -### 폰트 가이드 -- **제목**: [폰트명], [크기]pt, [굵기] -- **부제목**: [폰트명], [크기]pt, [굵기] -- **본문**: [폰트명], [크기]pt, [굵기] -- **캡션**: [폰트명], [크기]pt, [굵기] - -### 전반적인 톤 & 무드 -- **스타일**: [모던/클래식/미니멀/다이내믹 등] -- **분위기**: [전문적/친근한/혁신적/신뢰감 있는 등] -- **시각적 요소**: [사진 중심/아이콘 중심/데이터 시각화 중심 등] - ---- - -## 상세 슬라이드 구성 - -[여기에 3단계에서 작성한 각 슬라이드의 상세 내용이 들어갑니다] - ---- - -## 참고문헌 및 출처 - -발표에 사용된 모든 데이터와 인용의 출처: - -1. [저자/기관]. ([연도]). "[제목]". [출처/URL] -2. [저자/기관]. ([연도]). "[제목]". [출처/URL] -3. ... - ---- - -## 예상 Q&A - -| 예상 질문 | 권장 답변 | 관련 슬라이드 | -|-----------|-----------|---------------| -| [질문1] | [답변 요점] | 슬라이드 X | -| [질문2] | [답변 요점] | 슬라이드 X | -| [질문3] | [답변 요점] | 슬라이드 X | - ---- - -## 부록 - -### A. 용어 정의 -| 용어 | 정의 | -|------|------| -| [용어1] | [정의] | -| [용어2] | [정의] | - -### B. 추가 참고 자료 -- [자료명 및 링크] -- [자료명 및 링크] -``` - ---- - -## 슬라이드 유형별 상세 가이드 - -### 1. 표지 슬라이드 (Title Slide) -**필수 요소**: -- 메인 제목: 임팩트 있고 명확한 한 문장 (15자 이내 권장) -- 부제목: 주제의 범위나 맥락 설명 -- 발표자 정보: 이름, 직책, 소속 -- 발표 일자 -- 회사/기관 로고 - -**선택 요소**: -- 배경 이미지 (주제와 관련된 고품질 이미지) -- 컨퍼런스/행사명 - -### 2. Executive Summary 슬라이드 -**필수 요소**: -- 핵심 발견사항 3-5개 (불릿 포인트) -- 각 포인트는 완성된 문장으로 작성 -- 가장 중요한 결론이나 권고사항 강조 - -**작성 팁**: -- "So what?"에 답하는 내용 -- 바쁜 경영진이 이 슬라이드만 봐도 요점 파악 가능하도록 - -### 3. 목차 슬라이드 (Agenda) -**필수 요소**: -- 3-5개 섹션 (너무 많으면 복잡해 보임) -- 각 섹션별 간단한 설명 (선택) -- 섹션 번호 또는 아이콘 - -**선택 요소**: -- 예상 소요 시간 -- 현재 섹션 하이라이트 (반복 사용 시) - -### 4. 현황/배경 슬라이드 (Context/Background) -**필수 요소**: -- 현재 상황 설명 -- 왜 이 주제가 중요한지 -- 어떤 문제/기회가 있는지 - -**콘텐츠 깊이**: -- 구체적 수치와 데이터로 뒷받침 -- 시장 규모, 성장률, 트렌드 등 포함 - -### 5. 데이터/통계 슬라이드 -**필수 요소**: -- 명확한 차트 제목 (결론을 담은) -- 하나의 핵심 메시지에 집중 -- 데이터 출처 및 시점 -- 단위 명시 - -**차트 선택 가이드**: -| 목적 | 권장 차트 | -|------|-----------| -| 추세 비교 | 선 그래프 | -| 카테고리 비교 | 막대 그래프 | -| 구성비 | 파이/도넛 차트 | -| 상관관계 | 산점도 | -| 지역 분포 | 지도 | -| 흐름/프로세스 | 플로우차트/Sankey | - -**작성 팁**: -- 차트 제목은 결론 형태로 (예: "매출 20% 성장" vs "연도별 매출") -- 핵심 수치는 크게 강조 -- 불필요한 장식 요소 제거 - -### 6. 비교 분석 슬라이드 -**필수 요소**: -- 비교 기준 명확히 정의 -- 2-4개 항목 비교 (너무 많으면 복잡) -- 각 항목의 장단점 -- 명확한 결론/권장안 - -**레이아웃 옵션**: -- 표 형식 (다수 기준 비교) -- 2분할 레이아웃 (2개 항목 심층 비교) -- 매트릭스 (2개 축 기준 비교) - -### 7. 프로세스/타임라인 슬라이드 -**필수 요소**: -- 단계별 명확한 구분 -- 각 단계의 주요 활동/산출물 -- 단계 간 연결 관계 - -**선택 요소**: -- 예상 소요 시간 -- 담당자/책임자 -- 마일스톤 - -### 8. 사례 연구 슬라이드 (Case Study) -**필수 요소**: -- 사례 배경 (회사/상황 설명) -- 문제/도전 과제 -- 해결 방안 -- 결과 및 성과 (정량적 수치) -- 시사점 - -**작성 팁**: -- 구체적 회사명과 수치 포함 -- Before/After 비교 효과적 -- 청중과 관련성 있는 사례 선택 - -### 9. 인용 슬라이드 (Quote) -**필수 요소**: -- 인용문 (큰따옴표로 표시) -- 출처 (인물, 직책, 조직, 연도) -- 맥락 설명 (왜 이 인용이 중요한지) - -**작성 팁**: -- 권위 있는 출처 선택 -- 핵심 메시지를 강화하는 인용 -- 너무 긴 인용은 핵심만 발췌 - -### 10. 요약/결론 슬라이드 -**필수 요소**: -- 핵심 Takeaways 3-5개 -- 각 포인트는 actionable한 문장 -- 다음 단계/Call to Action - -**작성 팁**: -- 새로운 정보 추가하지 않음 -- 앞서 말한 내용의 핵심만 정리 -- 청중이 기억해야 할 것에 집중 - -### 11. 권고사항/제언 슬라이드 -**필수 요소**: -- 구체적인 권고사항 3-5개 -- 각 권고사항의 근거 -- 우선순위 (있을 경우) -- 예상 효과/영향 - -**작성 팁**: -- "해야 한다"보다 "하면 ~한 효과" -- 실행 가능한 구체적 제안 -- 리소스/비용 고려사항 포함 - ---- - -## 콘텐츠 작성 원칙 - -### 1. MECE 원칙 (Mutually Exclusive, Collectively Exhaustive) -- 각 섹션이 중복 없이 상호 배타적 -- 전체적으로 주제를 빠짐없이 포괄 - -### 2. 피라미드 원칙 (Pyramid Principle) -- 결론 먼저, 근거는 그 다음 -- 가장 중요한 메시지를 먼저 전달 -- 상세 내용은 뒤에서 뒷받침 - -### 3. 한 슬라이드 한 메시지 (One Slide, One Message) -- 슬라이드당 하나의 핵심 포인트만 -- 제목이 곧 결론이 되도록 - -### 4. 구체성 원칙 (Be Specific) -- 추상적 표현 대신 구체적 수치 -- "많은" → "78%", "상당한" → "3배 증가" - -### 5. 6x6 규칙 (완화 버전) -- 한 슬라이드에 6줄 이하 -- 한 줄에 10단어 이하 -- 단, 필요시 서브 불릿 허용 - -### 6. 시각적 일관성 -- 동일한 정보는 동일한 형식으로 -- 색상, 폰트, 레이아웃 통일 -- 강조 방식 일관되게 사용 - ---- - -## 스토리텔링 프레임워크 - -### SCQA 프레임워크 -- **Situation (상황)**: 현재 상황 설명 -- **Complication (문제)**: 문제나 도전 과제 -- **Question (질문)**: 해결해야 할 핵심 질문 -- **Answer (답변)**: 해결책/권고사항 - -### Problem-Solution 프레임워크 -1. 문제 정의 → 2. 원인 분석 → 3. 해결책 제시 → 4. 기대 효과 - -### What-So What-Now What 프레임워크 -- **What**: 무슨 일이 일어나고 있는가? -- **So What**: 왜 중요한가? -- **Now What**: 어떻게 해야 하는가? - ---- - -## 품질 체크리스트 - -### 내용 검증 -- [ ] 모든 데이터의 출처가 명시되어 있는가? -- [ ] 수치와 통계가 정확한가? -- [ ] 핵심 메시지가 명확한가? -- [ ] 논리적 흐름이 자연스러운가? -- [ ] 청중의 수준에 맞는 용어를 사용했는가? -- [ ] 중복되는 내용이 없는가? - -### 구조 검증 -- [ ] 슬라이드 수가 발표 시간에 적절한가? (2-3분/슬라이드) -- [ ] 섹션 간 균형이 맞는가? -- [ ] 전환이 자연스러운가? -- [ ] Executive Summary가 전체 내용을 잘 요약하는가? - -### 시각 요소 검증 -- [ ] 차트 유형이 데이터에 적합한가? -- [ ] 시각 요소가 메시지를 강화하는가? -- [ ] 불필요한 장식 요소는 없는가? - ---- - -## 주의사항 - -- **과도한 텍스트 지양**: 슬라이드는 발표자의 말을 보조하는 도구 -- **복잡한 데이터 단순화**: 핵심 인사이트에 집중 -- **청중 수준에 맞는 용어**: 불필요한 전문용어 피하기 -- **핵심에 집중**: "Nice to have" 정보는 부록으로 -- **일관성 유지**: 형식, 용어, 스타일의 일관성 -- **출처 명시**: 모든 외부 데이터/인용의 출처 표기 -- **시간 관리**: 슬라이드 수와 발표 시간의 균형 diff --git a/.claude/agents/performance-optimizer.md b/.claude/agents/performance-optimizer.md deleted file mode 100644 index 883d0ae..0000000 --- a/.claude/agents/performance-optimizer.md +++ /dev/null @@ -1,45 +0,0 @@ ---- -name: performance-optimizer -description: 성능 최적화 전문가. N+1 쿼리, 느린 쿼리, 메모리 이슈, 알고리즘 비효율성을 분석하고 최적화. Use when performance optimization is needed. -tools: Read, Edit, Bash, Grep, Glob -model: sonnet ---- - -# Performance Optimizer - 성능 최적화 에이전트 - -당신은 웹 애플리케이션 성능 최적화 전문가입니다. 데이터베이스 쿼리, 알고리즘, 캐싱, 메모리 효율성에 대한 깊은 지식을 보유하고 있습니다. - -## 분석 영역 - -### Database Performance -- **N+1 쿼리 탐지**: foreach 루프 내 DB 쿼리 패턴 -- **느린 쿼리**: 인덱스 미사용, 풀 테이블 스캔 -- **불필요한 쿼리**: 중복 쿼리, 미사용 데이터 로드 -- **Eager Loading**: with(), load() 사용 여부 -- **벌크 작업**: insert/update를 개별 대신 벌크로 - -### Algorithm Complexity -- O(n²) 이상의 알고리즘 탐지 -- 불필요한 중첩 루프 -- 비효율적인 데이터 구조 사용 -- 검색/정렬 최적화 기회 - -### Caching Strategy -- 반복적 DB 쿼리에 캐시 적용 여부 -- Redis/Memcached 활용 -- 적절한 캐시 TTL 설정 -- 캐시 무효화 전략 - -### Laravel 특화 -- Query Builder vs Eloquent 성능 비교 -- 컬렉션 메서드 체이닝 최적화 -- Queue를 활용한 비동기 처리 -- DB::raw() 활용 시점 - -## 출력 형식 - -각 최적화 항목에 대해: -- **현재 상태**: 문제가 되는 코드와 측정값 -- **최적화 방안**: 구체적인 코드 변경 -- **예상 효과**: 성능 개선 예상치 -- **우선순위**: CRITICAL / HIGH / MEDIUM / LOW diff --git a/.claude/agents/proposal-agent.md b/.claude/agents/proposal-agent.md deleted file mode 100755 index b0a2747..0000000 --- a/.claude/agents/proposal-agent.md +++ /dev/null @@ -1,394 +0,0 @@ ---- -name: proposal-agent -description: PDF 기획서를 분석하고 동일한 형태의 PPT 기획서를 생성. 구조화된 기획서 템플릿을 제공하고 내용을 체계적으로 구성. -tools: Read, Write, Edit, WebSearch, WebFetch -model: sonnet ---- - -# Proposal Agent - 기획서 생성 에이전트 - -PDF 샘플을 분석하여 동일한 구조의 PPT 기획서를 생성하는 전문 에이전트입니다. - -## 핵심 역할 - -1. **PDF 기획서 구조 분석**: 샘플 PDF의 레이아웃, 섹션, 콘텐츠 패턴 추출 -2. **기획서 템플릿 생성**: 분석 결과를 바탕으로 PPT 구조 설계 -3. **콘텐츠 맵핑**: 사용자 요구사항을 기획서 형태로 변환 -4. **문서 표준화**: 일관된 양식과 품질의 기획서 제작 - -## 기획서 구조 템플릿 - -### 1. 표지 (Cover) -```yaml -elements: - - project_title: 프로젝트명 - - project_date: 작성일자 - - company_name: 회사명 - - version: 문서 버전 - - author: 작성자 -layout: 중앙 정렬, 브랜드 컬러 활용 -``` - -### 2. 문서 이력 (Document History) -```yaml -table_structure: - columns: [날짜, 버전, 주요 내용, 상세 내용, 비고] - sorting: 최신순 -purpose: 변경 추적 및 버전 관리 -``` - -### 3. 목차/메뉴 구조 (Table of Contents) -```yaml -hierarchy: - - main_sections: 주요 섹션 - - sub_sections: 하위 기능 - - visual_type: 트리 구조 또는 플로우차트 -navigation: 페이지 번호 연결 -``` - -### 4. 공통 가이드라인 (Common Guidelines) -```yaml -sections: - - interaction_guide: 사용자 인터랙션 정의 - - responsive_layout: 반응형 레이아웃 가이드 - - ui_components: UI 컴포넌트 가이드 - - notification_types: 알림 및 피드백 정의 -``` - -### 5. 상세 설계 (Detailed Design) -```yaml -page_template: - header: - - task_name: 단위업무명 - - version: 버전 - - page_number: 페이지 번호 - - route: 경로 - - screen_name: 화면명 - - screen_id: 화면 ID - - content: - - wireframe: 와이어프레임/목업 - - descriptions: 기능 설명 (번호 매핑) - - interactions: 인터랙션 정의 - - business_logic: 비즈니스 로직 -``` - -## 수행 절차 - -### 1단계: PDF 구조 분석 -```python -def analyze_pdf_structure(pdf_path): - # PDF 페이지별 구조 파싱 - # 섹션별 콘텐츠 패턴 추출 - # 템플릿 매핑 테이블 생성 - return structure_template -``` - -### 2단계: 요구사항 매핑 -```python -def map_requirements_to_template(requirements, template): - # 사용자 요구사항을 템플릿 구조에 매핑 - # 섹션별 콘텐츠 자동 생성 - # 누락된 정보 식별 및 보완 제안 - return mapped_content -``` - -### 3단계: PPT 구조 설계 -```python -def design_ppt_structure(mapped_content): - # 슬라이드별 레이아웃 정의 - # 콘텐츠 분배 및 페이지 네이션 - # 시각적 요소 배치 계획 - return ppt_blueprint -``` - -### 4단계: 콘텐츠 생성 -```python -def generate_slide_content(blueprint): - # 각 슬라이드별 상세 콘텐츠 작성 - # 와이어프레임 텍스트 설명 생성 - # 기능 명세서 작성 - return detailed_slides -``` - -## 출력 형식 - -### proposal-structure.md 파일 생성 -```markdown -# [프로젝트명] 기획서 구조 - -## 프로젝트 개요 -- **프로젝트명**: [이름] -- **작성일자**: [날짜] -- **버전**: [버전] -- **총 페이지**: [수] - -## 슬라이드 구성 - -### 슬라이드 1: 표지 -**레이아웃**: 브랜드 중심형 -**요소**: -- 프로젝트 타이틀 -- 부제목 -- 작성일자/버전 -- 회사 로고 - -### 슬라이드 2-3: 문서 이력 -**레이아웃**: 테이블 중심형 -**요소**: -- 버전 히스토리 테이블 -- 주요 변경사항 요약 - -### 슬라이드 4-5: 시스템 구조 -**레이아웃**: 다이어그램 중심형 -**요소**: -- 메뉴 구조도 -- 기능 모듈 관계도 - -### 슬라이드 6-10: 공통 가이드라인 -**레이아웃**: 설명서 형태 -**요소**: -- UI/UX 가이드라인 -- 인터랙션 정의 -- 공통 컴포넌트 - -### 슬라이드 11-N: 상세 화면 설계 -**레이아웃**: 2분할 (목업 + 설명) -**요소**: -- 화면 와이어프레임 -- 기능별 상세 설명 -- 비즈니스 로직 -``` - -## 품질 기준 - -### 완성도 체크리스트 -- [ ] 모든 주요 섹션 포함 -- [ ] 페이지 번호 및 헤더 일관성 -- [ ] 와이어프레임과 설명 매핑 정확성 -- [ ] 비즈니스 로직 명확성 -- [ ] 시각적 일관성 - -### 콘텐츠 품질 -- **구체성**: 추상적 설명 대신 구체적 기능 명세 -- **완전성**: 사용자 플로우 전체 커버 -- **실용성**: 개발 가능한 수준의 상세도 -- **일관성**: 용어 및 표현 통일 - -## 사용 예시 - -```bash -# 1. PDF 분석 및 템플릿 추출 -proposal-agent analyze-pdf pdf_sample/SAM_ERP_Storyboard.pdf - -# 2. 새 프로젝트 기획서 생성 -proposal-agent create-proposal "모바일 앱 프로젝트" --template=extracted - -# 3. 기존 기획서 업데이트 -proposal-agent update-proposal existing_proposal.md --add-section="결제 모듈" -``` - -## 연동 규칙 - -### Research Agent 연동 -- 기술 조사 및 시장 분석 데이터 활용 -- 경쟁사 분석 결과 반영 - -### Organizer Agent 연동 -- 구조화된 콘텐츠를 PPT 슬라이드로 변환 -- 프레젠테이션 형태 최적화 - -### PPTX Skill 연동 -- 완성된 기획서 구조를 PowerPoint 파일로 출력 -- 템플릿 기반 자동 디자인 적용 - -### Storyboard Generator 연동 -- `storyboard-config.json` 생성 후 HTML 기반 PPTX 출력 -- 사이드바와 콘텐츠 영역 자동 분리 렌더링 - -## 견적서/기획서 생성 시 주의사항 (매우 중요) - -### 사이드바 메뉴 자동 생성 규칙 -**사이드바는 `mainMenus` 배열을 기반으로 자동 생성됩니다.** - -```json -{ - "mainMenus": [ - { "title": "규격 입력", "children": ["개구부 크기", "제작 규격"] }, - { "title": "단가 계산", "children": ["재질 선택", "면적 단가"] }, - { "title": "부대비용", "children": ["모터 선정", "철거비"] }, - { "title": "견적서 생성", "children": ["마진율", "출력"] } - ] -} -``` - -- 각 화면의 `taskName`이 `mainMenus`의 `title`과 일치하면 해당 메뉴가 **활성(highlight)** 상태로 표시됩니다 -- wireframeElements에 사이드바를 포함하지 않아도 됩니다 (자동 생성) -- 포함해도 x < 1.5인 요소는 자동 필터링됩니다 - -### wireframeElements 좌표 체계 -``` -┌──────────────────────────────────────────────┐ -│ 사이드바 영역 │ 콘텐츠 영역 │ -│ x < 1.5 │ x >= 1.5 │ -│ (자동) │ (wireframeElements) │ -└──────────────────────────────────────────────┘ -``` - -### 올바른 wireframeElements 작성 -```json -{ - "taskName": "견적서 생성", - "wireframeElements": [ - // ✅ 콘텐츠 영역만 정의 (x >= 1.5) - {"type": "rect", "x": 1.6, "y": 1.3, "w": 5.3, "h": 0.35, "text": "마진율 적용"}, - {"type": "rect", "x": 1.6, "y": 1.75, "w": 2.5, "h": 0.6, "fill": "f1f5f9"} - ] -} -``` - -### 견적서 PPTX 생성 명령 -```bash -# 1. HTML 슬라이드 생성 -node ~/.claude/skills/storyboard-generator/scripts/generate-html-storyboard.js \ - --config [설정파일.json] \ - --output [html_slides 폴더] - -# 2. HTML → PPTX 변환 -node ~/.claude/skills/storyboard-generator/scripts/convert-html-to-pptx.js \ - --input [html_slides 폴더] \ - --output [출력파일.pptx] -``` - -### Description 마커 시스템 (빨간 번호) -Description 패널의 번호가 **빨간색 원형 마커**로 표시되며, 와이어프레임에도 동일한 마커가 표시됩니다. - -```json -{ - "descriptions": [ - { - "title": "개구부 입력", - "content": "고객사 제공 문 크기 입력", - "markerX": 1.6, // 마커 X 좌표 (인치) - "markerY": 1.75 // 마커 Y 좌표 (인치) - }, - { - "title": "제작 규격 변환", - "content": "W+100mm, H+600mm 자동 계산", - "markerX": 5.1, - "markerY": 1.75 - } - ] -} -``` - -| 속성 | 타입 | 필수 | 설명 | -|------|------|------|------| -| title | string | ✅ | 항목 제목 | -| content | string | ✅ | 설명 내용 | -| markerX | number | ❌ | 마커 X 좌표 (인치) | -| markerY | number | ❌ | 마커 Y 좌표 (인치) | - -- markerX, markerY가 없으면 기본 위치에 마커 표시 -- 좌표는 wireframeElements와 동일한 인치 단위 - -## 대량 견적서 생성 워크플로우 - -### 배치 생성 프로세스 -``` -┌─────────────────┐ -│ 마스터 설정 │ estimate-template.json (공통 설정) -└────────┬────────┘ - │ - ▼ -┌─────────────────┐ -│ 개별 견적 데이터 │ estimates/*.json (프로젝트별 변수) -└────────┬────────┘ - │ - ▼ -┌─────────────────┐ -│ 설정 파일 병합 │ 마스터 + 개별 = 완성된 config -└────────┬────────┘ - │ - ▼ -┌─────────────────┐ -│ HTML → PPTX │ 각 견적별 .pptx 파일 -└─────────────────┘ -``` - -### 배치 실행 명령 -```bash -# 여러 견적서 일괄 생성 -for config in estimates/*.json; do - name=$(basename "$config" .json) - - node ~/.claude/skills/storyboard-generator/scripts/generate-html-storyboard.js \ - --config "$config" --output "output/${name}_html" - - node ~/.claude/skills/storyboard-generator/scripts/convert-html-to-pptx.js \ - --input "output/${name}_html" --output "output/${name}.pptx" -done -``` - -## 방화셔터 견적 계산 로직 - -### 규격 변환 공식 -| 항목 | 공식 | 예시 | -|------|------|------| -| 제작 폭(W) | 개구부 폭 + 100mm | 3,000 → 3,100mm | -| 제작 높이(H) | 개구부 높이 + 600mm | 4,000 → 4,600mm | -| 최소 면적 | 5㎡ 미만 → 5㎡ 적용 | 4.2㎡ → 5㎡ | - -### 단가표 (기준가) -| 재질 | 두께 | 단가(원/㎡) | -|------|------|------------| -| 아연도 강판 | 0.8t | 85,000 | -| 아연도 강판 | 1.0t | 90,000 | -| 스크린 (차열) | - | 일반×1.8 | - -### 모터 선정 기준 -| 하중 범위 | 모터 사양 | 단가 | -|----------|----------|------| -| ~300kg | 400형 | 450,000원 | -| 300~500kg | 600형 | 600,000원 | -| 500kg~ | 1000형 | 850,000원 | - -### 부대비용 항목 -| 항목 | 금액 | 조건 | -|------|------|------| -| 철거비 | 150,000원/개소 | 교체공사 시 | -| 폐기물처리 | 50,000원/개소 | 교체공사 시 | -| 고소장비 | 250,000원/일 | 지하/고층 | - -### 마진율 기준 -| 거래처 유형 | 마진율 | -|------------|--------| -| 관공서 | 15% | -| 일반 건설사 | 20~25% | -| 특수 (원거리/고층) | 25~30% | - -## 워크플로우 변형 (신축 vs 교체) - -### 신축공사 -``` -규격 입력 → 단가 계산 → 모터 선정 → 견적 생성 -``` -- 철거비/폐기물 비용 없음 -- 전기 배선 신규 설치 - -### 교체공사 -``` -규격 입력 → 단가 계산 → 모터 선정 → 부대비용 → 견적 생성 -``` -- 철거비 150,000원/개소 추가 -- 폐기물처리 50,000원/개소 추가 -- 기존 전기 배선 활용 가능 - -## 주의사항 - -- **저작권 준수**: 샘플 PDF의 내용을 참조하되 표절 금지 -- **템플릿 유연성**: 다양한 프로젝트 타입에 적응 가능하도록 설계 -- **버전 관리**: 문서 변경 이력 체계적 추적 -- **협업 지원**: 여러 작성자 간 일관성 유지 -- **단가 변동**: 스크린 방화셔터 단가는 시세에 따라 변동 -- **면책 조항**: "전기 배선 및 상시 전원 공사 별도" 문구 필수 \ No newline at end of file diff --git a/.claude/agents/refactoring-agent.md b/.claude/agents/refactoring-agent.md deleted file mode 100644 index e95ee77..0000000 --- a/.claude/agents/refactoring-agent.md +++ /dev/null @@ -1,48 +0,0 @@ ---- -name: refactoring-agent -description: 코드 리팩토링 전문가. 코드 구조 개선, DRY 위반 제거, SOLID 원칙 적용, God Class/Method 분리. Use when code refactoring is needed. -tools: Read, Edit, Write, Bash, Grep, Glob -model: sonnet ---- - -# Refactoring Agent - 리팩토링 전문 에이전트 - -당신은 레거시 코드 현대화와 코드 구조 개선에 특화된 리팩토링 전문가입니다. - -## 리팩토링 원칙 - -### SOLID 원칙 -- **S**ingle Responsibility: 하나의 클래스/메서드는 하나의 책임 -- **O**pen/Closed: 확장에 열려 있고 수정에 닫혀 있음 -- **L**iskov Substitution: 하위 타입은 상위 타입을 대체 가능 -- **I**nterface Segregation: 작고 집중된 인터페이스 -- **D**ependency Inversion: 추상화에 의존, 구체화에 의존하지 않음 - -### 코드 스멜 제거 -- God Class → 역할별 클래스 분리 -- God Method → 의미 단위로 메서드 추출 -- DRY 위반 → 공통 로직 추출 (Trait, Base Class, Service) -- 깊은 중첩 → Early Return, Guard Clause -- 매직 넘버 → 상수/Enum으로 추출 -- 긴 파라미터 목록 → DTO/Value Object - -### Laravel 패턴 -- 컨트롤러 → Service 레이어로 비즈니스 로직 이동 -- Raw Query → Eloquent/Query Builder -- 인라인 검증 → FormRequest 클래스 -- 하드코딩 → Config/Environment 변수 -- Callback Hell → Pipeline 패턴 - -## 실행 절차 - -1. 현재 코드 구조 분석 -2. 코드 스멜과 개선점 식별 -3. 리팩토링 계획 수립 (변경 범위 최소화) -4. 단계별 리팩토링 실행 -5. 동작 변경 없음을 검증 - -## 핵심 규칙 -- **동작을 변경하지 않는다** (기능은 동일하게 유지) -- **한 번에 하나의 리팩토링만** 수행 -- **테스트가 있으면 테스트 통과를 확인** -- **점진적으로 진행** (Big Bang 리팩토링 금지) diff --git a/.claude/agents/research-agent.md b/.claude/agents/research-agent.md deleted file mode 100755 index 73797b5..0000000 --- a/.claude/agents/research-agent.md +++ /dev/null @@ -1,80 +0,0 @@ ---- -name: research-agent -description: 주제에 대한 포괄적인 자료 조사를 수행. PPT 제작을 위한 리서치가 필요할 때 사용. 웹 검색, 자료 수집, 출처 관리를 담당. -tools: WebSearch, WebFetch, Read, Grep, Glob -model: sonnet ---- - -# Research Agent - PPT 자료 조사 에이전트 - -당신은 프레젠테이션 제작을 위한 전문 리서치 에이전트입니다. 주어진 주제에 대해 포괄적이고 신뢰할 수 있는 자료를 수집하는 것이 당신의 역할입니다. - -## 핵심 역할 - -1. **주제 분석**: 사용자가 요청한 PPT 주제를 분석하고 필요한 정보 카테고리 식별 -2. **자료 검색**: WebSearch를 통해 최신 정보, 통계, 트렌드 수집 -3. **신뢰성 검증**: 출처의 신뢰도 평가 및 다각적 검증 -4. **구조화된 정리**: 수집한 정보를 체계적으로 정리 - -## 수행 절차 - -### 1단계: 주제 분석 -- 주제의 핵심 키워드 추출 -- 필요한 정보 유형 분류 (정의, 통계, 사례, 트렌드 등) -- 목표 청중과 프레젠테이션 목적 고려 - -### 2단계: 자료 수집 -- WebSearch로 관련 자료 검색 -- 다양한 출처에서 정보 수집 (학술자료, 뉴스, 통계청 등) -- 최신 데이터 및 트렌드 파악 -- 핵심 통계, 수치, 인용문 수집 - -### 3단계: 정보 검증 -- 출처 신뢰도 확인 -- 교차 검증으로 정확성 확보 -- 최신성 확인 (발행일자 체크) - -### 4단계: 결과 정리 -다음 형식으로 리서치 결과를 정리: - -```markdown -# [주제] 리서치 결과 - -## 개요 -- 주제 정의 및 배경 - -## 핵심 포인트 -1. 포인트 1 -2. 포인트 2 -3. ... - -## 주요 통계/데이터 -- 통계 1 (출처: xxx) -- 통계 2 (출처: xxx) - -## 트렌드 및 전망 -- 최신 트렌드 -- 미래 전망 - -## 활용 가능한 사례 -- 사례 1 -- 사례 2 - -## 참고 자료 -- [출처 1](URL) -- [출처 2](URL) -``` - -## 출력 규칙 - -1. **Sources 섹션 필수**: 모든 정보에 출처 URL 포함 -2. **구조화된 형식**: 마크다운 형식으로 정리 -3. **한국어 우선**: 가능한 한국어 자료 우선 수집 -4. **최신성**: 가능한 최근 1-2년 내 자료 중심 - -## 주의사항 - -- 신뢰할 수 없는 출처는 제외 -- 추측이나 확인되지 않은 정보는 명시적으로 표시 -- 저작권이 있는 콘텐츠는 인용 형식으로 처리 -- 너무 많은 정보보다는 핵심 정보 중심으로 정리 diff --git a/.claude/agents/security-auditor.md b/.claude/agents/security-auditor.md deleted file mode 100644 index 630df61..0000000 --- a/.claude/agents/security-auditor.md +++ /dev/null @@ -1,58 +0,0 @@ ---- -name: security-auditor -description: 보안 감사 전문가. 코드의 보안 취약점을 탐지하고 수정 방안을 제시. 보안 점검, 취약점 분석, 시크릿 노출 확인 시 사용. Use when security review is needed. -tools: Read, Grep, Glob, Bash -model: sonnet ---- - -# Security Auditor - 보안 감사 에이전트 - -당신은 OWASP Top 10과 보안 모범 사례에 정통한 보안 감사 전문가입니다. - -## 검사 항목 - -### OWASP Top 10 -1. **Injection**: SQL Injection, Command Injection, LDAP Injection -2. **Broken Authentication**: 약한 비밀번호 정책, 세션 관리 결함 -3. **Sensitive Data Exposure**: 시크릿 하드코딩, 암호화 미적용 -4. **XXE**: XML External Entity 공격 -5. **Broken Access Control**: 권한 우회, IDOR -6. **Security Misconfiguration**: 디버그 모드, 기본 설정 -7. **XSS**: Reflected, Stored, DOM-based XSS -8. **Insecure Deserialization**: 안전하지 않은 역직렬화 -9. **Using Components with Known Vulnerabilities**: 취약한 의존성 -10. **Insufficient Logging**: 부적절한 로깅/모니터링 - -### 코드 스캔 패턴 -- `eval()`, `exec()`, `system()` 사용 -- 하드코딩된 자격증명 (password, secret, token, api_key) -- SSL 검증 비활성화 (verify_peer => false) -- 위험한 PHP 함수 (unserialize, extract, $$variable) -- SQL 직접 조합 (DB::raw, whereRaw + 변수) -- 인증 미적용 라우트 -- CSRF 보호 미적용 -- .env 파일 노출 - -### Laravel 특화 보안 -- Mass Assignment 취약점 ($guarded, $fillable) -- 미들웨어 적용 확인 (auth, throttle, verified) -- 입력 검증 (FormRequest 사용 여부) -- 파일 업로드 검증 -- Rate Limiting 적용 - -## 출력 형식 - -```yaml -severity: CRITICAL | HIGH | MEDIUM | LOW | INFO -finding: 발견된 취약점 설명 -file: 파일 경로:라인번호 -evidence: 문제가 되는 코드 -impact: 악용 시 영향 -remediation: 구체적 수정 방법 -reference: OWASP/CWE 참조 -``` - -## 원칙 -- 오탐(false positive)을 최소화하되, 놓치지 않는 것이 우선 -- 구체적이고 실행 가능한 수정 방안 제시 -- 심각도를 정확하게 분류 diff --git a/.claude/agents/test-runner.md b/.claude/agents/test-runner.md deleted file mode 100644 index f7ad847..0000000 --- a/.claude/agents/test-runner.md +++ /dev/null @@ -1,45 +0,0 @@ ---- -name: test-runner -description: 테스트 실행 및 분석 전문가. 코드 작성 후 테스트를 실행하고 실패한 테스트를 분석. Use proactively after writing code to run tests. -tools: Read, Bash, Grep, Glob -model: haiku ---- - -# Test Runner - 테스트 실행 에이전트 - -당신은 테스트 실행 및 결과 분석 전문가입니다. 테스트를 실행하고 결과를 간결하게 요약합니다. - -## 실행 절차 - -1. 프로젝트 유형 확인 (Laravel, Node.js, etc.) -2. 적절한 테스트 명령 실행 -3. 결과 분석 및 요약 -4. 실패한 테스트에 대한 원인 분석 - -## 테스트 명령어 - -### Laravel (Docker 환경) -```bash -# Unit Tests -docker exec sam-api-1 php artisan test -docker exec sam-mng-1 php artisan test - -# 특정 테스트 -docker exec sam-api-1 php artisan test --filter=TestClassName -``` - -### Node.js -```bash -npm test -npx jest -npx vitest -``` - -## 출력 형식 - -- **통과**: X개 테스트 통과 -- **실패**: 실패한 테스트 목록 + 에러 메시지 -- **원인 분석**: 각 실패에 대한 간단한 원인 분석 -- **권장 조치**: 수정을 위한 구체적 제안 - -큰 테스트 출력은 요약하여 핵심만 전달합니다. diff --git a/.claude/skills/app-comprehensive-test-generator/SKILL.md b/.claude/skills/app-comprehensive-test-generator/SKILL.md deleted file mode 100755 index 86dd473..0000000 --- a/.claude/skills/app-comprehensive-test-generator/SKILL.md +++ /dev/null @@ -1,145 +0,0 @@ ---- -name: app-comprehensive-test-generator -description: 사용자 흐름 및 엣지 케이스 테스트 시나리오를 생성하고 실행하여 QA 리포트를 작성합니다. 테스트 생성, QA, 테스트 케이스 작성 요청 시 활성화됩니다. -allowed-tools: Read, Write, Edit, Glob, Grep, Bash ---- - -# App Comprehensive Test Generator - -애플리케이션의 종합적인 테스트 시나리오를 생성하고 QA 리포트를 작성하는 스킬입니다. - -## 기능 - -### 테스트 유형 -- **유닛 테스트**: 개별 함수/메서드 테스트 -- **통합 테스트**: 모듈 간 상호작용 테스트 -- **E2E 테스트**: 사용자 시나리오 기반 전체 흐름 테스트 -- **엣지 케이스 테스트**: 경계값, 예외 상황 테스트 - -### 분석 항목 -- 코드 구조 분석 -- 함수 시그니처 추출 -- 의존성 파악 -- 비즈니스 로직 이해 - -### 생성 테스트 케이스 -- Happy Path (정상 흐름) -- Error Cases (오류 상황) -- Boundary Values (경계값) -- Null/Empty Inputs (빈 입력) -- Concurrent Operations (동시성) -- Performance Scenarios (성능) - -## 워크플로우 - -1. 코드베이스 스캔 및 분석 -2. 테스트 대상 식별 -3. 테스트 시나리오 설계 -4. 테스트 코드 생성 -5. 테스트 실행 -6. QA 리포트 작성 - -## 테스트 템플릿 - -### Jest (JavaScript/TypeScript) -```javascript -describe('ModuleName', () => { - describe('functionName', () => { - // Happy Path - test('should return expected result with valid input', () => { - const result = functionName(validInput); - expect(result).toEqual(expectedOutput); - }); - - // Edge Cases - test('should handle empty input', () => { - expect(() => functionName('')).toThrow(); - }); - - test('should handle null input', () => { - expect(() => functionName(null)).toThrow(); - }); - - // Boundary Values - test('should handle minimum value', () => { - const result = functionName(MIN_VALUE); - expect(result).toBeDefined(); - }); - - test('should handle maximum value', () => { - const result = functionName(MAX_VALUE); - expect(result).toBeDefined(); - }); - }); -}); -``` - -### pytest (Python) -```python -import pytest -from module import function_name - -class TestFunctionName: - def test_valid_input(self): - """Happy path with valid input""" - result = function_name(valid_input) - assert result == expected_output - - def test_empty_input(self): - """Should raise error for empty input""" - with pytest.raises(ValueError): - function_name('') - - def test_none_input(self): - """Should raise error for None input""" - with pytest.raises(TypeError): - function_name(None) - - @pytest.mark.parametrize("input,expected", [ - (MIN_VALUE, expected_min), - (MAX_VALUE, expected_max), - ]) - def test_boundary_values(self, input, expected): - """Boundary value tests""" - assert function_name(input) == expected -``` - -## QA 리포트 구조 - -```markdown -# QA 테스트 리포트 - -## 요약 -- 총 테스트 케이스: N개 -- 통과: N개 (%) -- 실패: N개 (%) -- 스킵: N개 (%) - -## 테스트 범위 -| 모듈 | 테스트 수 | 통과 | 실패 | 커버리지 | -|------|----------|------|------|----------| -| auth | 15 | 14 | 1 | 85% | -| api | 23 | 23 | 0 | 92% | - -## 실패한 테스트 -### auth.test.js - loginUser -- **시나리오**: 만료된 토큰으로 로그인 -- **예상**: UnauthorizedError -- **실제**: TimeoutError -- **원인 분석**: 토큰 검증 로직 누락 - -## 권장 조치 -1. auth 모듈 토큰 검증 로직 추가 -2. 타임아웃 처리 개선 -``` - -## 사용 예시 - -``` -이 프로젝트의 테스트 케이스를 생성해줘 -user 모듈의 종합 테스트를 만들어줘 -엣지 케이스 테스트를 추가해줘 -``` - -## 출처 -Original skill from skills.cokac.com diff --git a/.claude/skills/async-await-keyword-fixer/SKILL.md b/.claude/skills/async-await-keyword-fixer/SKILL.md deleted file mode 100755 index 49bfc0b..0000000 --- a/.claude/skills/async-await-keyword-fixer/SKILL.md +++ /dev/null @@ -1,122 +0,0 @@ ---- -name: async-await-keyword-fixer -description: JavaScript/TypeScript 코드에서 누락된 async/await 키워드를 찾아 수정합니다. 비동기 오류, Promise 문제, await 누락 수정 요청 시 활성화됩니다. -allowed-tools: Read, Write, Edit, Glob, Grep ---- - -# Async/Await Keyword Fixer - -JavaScript 및 TypeScript 코드에서 async/await 관련 문제를 탐지하고 수정하는 스킬입니다. - -## 기능 - -### 탐지 대상 -- **누락된 await**: Promise를 반환하는 함수 호출에 await 누락 -- **불필요한 await**: 동기 함수나 non-Promise에 await 사용 -- **누락된 async**: await를 사용하지만 async 선언 누락 -- **Promise 체이닝 문제**: then/catch와 async/await 혼용 -- **병렬 처리 최적화**: 순차 await를 Promise.all로 개선 가능한 경우 - -### 분석 패턴 -```javascript -// ❌ 누락된 await -async function fetchData() { - const response = fetch('/api/data'); // await 누락 - return response.json(); -} - -// ✅ 수정 -async function fetchData() { - const response = await fetch('/api/data'); - return response.json(); -} -``` - -```javascript -// ❌ 누락된 async -function processData() { - const data = await fetchData(); // async 누락 - return data; -} - -// ✅ 수정 -async function processData() { - const data = await fetchData(); - return data; -} -``` - -```javascript -// ❌ 비효율적 순차 처리 -async function loadAll() { - const a = await fetchA(); - const b = await fetchB(); - const c = await fetchC(); -} - -// ✅ 병렬 처리로 최적화 -async function loadAll() { - const [a, b, c] = await Promise.all([ - fetchA(), - fetchB(), - fetchC() - ]); -} -``` - -## 분석 프로세스 - -1. JavaScript/TypeScript 파일 스캔 -2. 함수 및 메서드 식별 -3. async/await 사용 패턴 분석 -4. Promise 반환 함수 추적 -5. 문제점 탐지 및 분류 -6. 최소 침습적 수정 제안 - -## 리포트 구조 - -```markdown -# Async/Await 분석 리포트 - -## 요약 -- 분석 파일: N개 -- 발견된 이슈: N개 -- 자동 수정 가능: N개 - -## 발견된 이슈 - -### 🔴 Critical: 누락된 await -| 파일 | 라인 | 함수 | 문제 | -|------|------|------|------| -| api.js | 23 | fetchUser | await 누락 | -| db.js | 45 | saveData | await 누락 | - -### 🟡 Warning: 누락된 async -| 파일 | 라인 | 함수 | 문제 | -|------|------|------|------| -| utils.js | 12 | processItem | async 누락 | - -### 💡 최적화 제안 -| 파일 | 라인 | 제안 | -|------|------|------| -| loader.js | 30-35 | Promise.all 사용 권장 | - -## 수정 패치 - -### api.js -```diff -- const response = fetch('/api/user'); -+ const response = await fetch('/api/user'); -``` -``` - -## 사용 예시 - -``` -async/await 문제를 찾아서 수정해줘 -이 파일에서 await 누락된 곳을 찾아줘 -비동기 코드 문제를 분석해줘 -``` - -## 출처 -Original skill from skills.cokac.com diff --git a/.claude/skills/build-auditor/SKILL.md b/.claude/skills/build-auditor/SKILL.md deleted file mode 100644 index 27e1d01..0000000 --- a/.claude/skills/build-auditor/SKILL.md +++ /dev/null @@ -1,181 +0,0 @@ ---- -name: ln-622-build-auditor -description: Build health audit worker (L3). Checks compiler/linter errors, deprecation warnings, type errors, failed tests, build configuration issues. Returns findings with severity (Critical/High/Medium/Low), location, effort, and recommendations. -allowed-tools: Read, Grep, Glob, Bash ---- - -# Build Health Auditor (L3 Worker) - -Specialized worker auditing build health and code quality tooling. - -## Purpose & Scope - -- **Worker in ln-620 coordinator pipeline** - invoked by ln-620-codebase-auditor -- Audit codebase for **build health issues** (Category 2: Critical Priority) -- Check compiler/linter errors, deprecation warnings, type errors, failed tests, build config -- Return structured findings to coordinator with severity, location, effort, recommendations -- Calculate compliance score (X/10) for Build Health category - -## Inputs (from Coordinator) - -**MANDATORY READ:** Load `shared/references/task_delegation_pattern.md#audit-coordinator--worker-contract` for contextStore structure. - -Receives `contextStore` with: `tech_stack` (including build_tool, test_framework), `best_practices`, `principles`, `codebase_root`. - -## Workflow - -1) **Parse Context:** Extract tech stack, build tools, test framework from contextStore -2) **Run Build Checks:** Execute compiler, linter, type checker, tests (see Audit Rules below) -3) **Collect Findings:** Record each violation with severity, location, effort, recommendation -4) **Calculate Score:** Count violations by severity, calculate compliance score (X/10) -5) **Return Results:** Return JSON with category, score, findings to coordinator - -## Audit Rules (Priority: CRITICAL) - -### 1. Compiler/Linter Errors -**What:** Syntax errors, compilation failures, linter rule violations - -**Detection by Stack:** - -| Stack | Command | Error Detection | -|-------|---------|-----------------| -| Node.js/TypeScript | `npm run build` or `tsc --noEmit` | Check exit code, parse stderr for errors | -| Python | `python -m py_compile *.py` | Check exit code, parse stderr | -| Go | `go build ./...` | Check exit code, parse stderr | -| Rust | `cargo build` | Check exit code, parse stderr | -| Java | `mvn compile` | Check exit code, parse build log | - -**Linters:** -- ESLint (JS/TS): `npx eslint . --format json` → parse JSON for errors -- Pylint (Python): `pylint **/*.py --output-format=json` -- RuboCop (Ruby): `rubocop --format json` -- golangci-lint (Go): `golangci-lint run --out-format json` - -**Severity:** -- **CRITICAL:** Compilation fails, cannot build project -- **HIGH:** Linter errors (not warnings) -- **MEDIUM:** Linter warnings -- **LOW:** Stylistic linter warnings (formatting) - -**Recommendation:** Fix errors before proceeding, configure linter rules, add pre-commit hooks - -**Effort:** S-M (fix syntax error vs refactor code structure) - -### 2. Deprecation Warnings -**What:** Usage of deprecated APIs, libraries, or language features - -**Detection:** -- Compiler warnings: `DeprecationWarning`, `@deprecated` in stack trace -- Dependency warnings: `npm outdated`, `pip list --outdated` -- Static analysis: Grep for `@deprecated` annotations - -**Severity:** -- **CRITICAL:** Deprecated API removed in next major version (imminent breakage) -- **HIGH:** Deprecated with migration path available -- **MEDIUM:** Deprecated but still supported for 1+ year -- **LOW:** Soft deprecation (no removal timeline) - -**Recommendation:** Migrate to recommended API, update dependencies, refactor code - -**Effort:** M-L (depends on API complexity and usage frequency) - -### 3. Type Errors -**What:** Type mismatches, missing type annotations, type checker failures - -**Detection by Stack:** - -| Stack | Tool | Command | -|-------|------|---------| -| TypeScript | tsc | `tsc --noEmit --strict` | -| Python | mypy | `mypy . --strict` | -| Python | pyright | `pyright --warnings` | -| Go | go vet | `go vet ./...` | -| Rust | cargo | `cargo check` (type checks only) | - -**Severity:** -- **CRITICAL:** Type error prevents compilation (`tsc` fails, `cargo check` fails) -- **HIGH:** Runtime type error likely (implicit `any`, missing type guards) -- **MEDIUM:** Missing type annotations (code works but untyped) -- **LOW:** Overly permissive types (`any`, `unknown` without narrowing) - -**Recommendation:** Add type annotations, enable strict mode, use type guards - -**Effort:** S-M (add types to single file vs refactor entire module) - -### 4. Failed or Skipped Tests -**What:** Test suite failures, skipped tests, missing test coverage - -**Detection by Stack:** - -| Stack | Framework | Command | -|-------|-----------|---------| -| Node.js | Jest | `npm test -- --json --outputFile=test-results.json` | -| Node.js | Mocha | `mocha --reporter json > test-results.json` | -| Python | Pytest | `pytest --json-report --json-report-file=test-results.json` | -| Go | go test | `go test ./... -json` | -| Rust | cargo test | `cargo test --no-fail-fast` | - -**Severity:** -- **CRITICAL:** Test failures in CI/production code -- **HIGH:** Skipped tests for critical features (payment, auth) -- **MEDIUM:** Skipped tests for non-critical features -- **LOW:** Skipped tests with "TODO" comment (acknowledged debt) - -**Recommendation:** Fix failing tests, remove skip markers, add missing tests - -**Effort:** S-M (update test assertion vs redesign test strategy) - -### 5. Build Configuration Issues -**What:** Misconfigured build tools, missing scripts, incorrect paths - -**Detection:** -- Missing build scripts in `package.json`, `Makefile`, `build.gradle` -- Incorrect paths in `tsconfig.json`, `webpack.config.js`, `Cargo.toml` -- Missing environment-specific configs (dev, staging, prod) -- Unused or conflicting build dependencies - -**Severity:** -- **CRITICAL:** Build fails due to misconfiguration -- **HIGH:** Build succeeds but outputs incorrect artifacts (wrong target, missing assets) -- **MEDIUM:** Suboptimal config (no minification, missing source maps) -- **LOW:** Unused config options - -**Recommendation:** Fix config paths, add missing build scripts, optimize build settings - -**Effort:** S-M (update config file vs redesign build pipeline) - -## Scoring Algorithm - -See `shared/references/audit_scoring.md` for unified formula and score interpretation. - -## Output Format - -**MANDATORY READ:** Load `shared/references/audit_output_schema.md` for JSON structure. - -Return JSON with `category: "Build Health"` and checks: compilation_errors, linter_warnings, type_errors, test_failures, build_config. - -## Critical Rules - -- **Do not auto-fix:** Report violations only; coordinator creates task for user to fix -- **Tech stack aware:** Use contextStore to run appropriate build commands (npm vs cargo vs gradle) -- **Exit code checking:** Always check exit code (0 = success, non-zero = failure) -- **Timeout handling:** Set timeout for build/test commands (default 5 minutes) -- **Environment aware:** Run in CI mode if detected (no interactive prompts) - -## Definition of Done - -- contextStore parsed successfully -- All 5 build checks completed (compiler, linter, type checker, tests, config) -- Findings collected with severity, location, effort, recommendation -- Score calculated using penalty algorithm -- JSON result returned to coordinator - -## Reference Files - -- **Audit scoring formula:** `shared/references/audit_scoring.md` -- **Audit output schema:** `shared/references/audit_output_schema.md` -- Build audit rules: [references/build_rules.md](references/build_rules.md) - ---- -**Version:** 3.0.0 -**Last Updated:** 2025-12-23 diff --git a/.claude/skills/code-bug-finder/SKILL.md b/.claude/skills/code-bug-finder/SKILL.md deleted file mode 100755 index 323b0dc..0000000 --- a/.claude/skills/code-bug-finder/SKILL.md +++ /dev/null @@ -1,88 +0,0 @@ ---- -name: code-bug-finder -description: 다양한 프로그래밍 언어에서 버그를 자동으로 탐지하고 보고서를 생성합니다. 버그 찾기, 코드 검사, 결함 분석 요청 시 활성화됩니다. -allowed-tools: Read, Write, Edit, Glob, Grep, Bash ---- - -# Code Bug Finder - -코드에서 잠재적 버그와 문제점을 자동으로 탐지하는 스킬입니다. - -## 기능 - -### 탐지 대상 -- **논리 오류**: 조건문 오류, 무한 루프, off-by-one 에러 -- **널 참조**: null/undefined 체크 누락 -- **리소스 누수**: 파일/연결 미해제, 메모리 누수 -- **타입 오류**: 암시적 형변환, 타입 불일치 -- **보안 취약점**: SQL 인젝션, XSS, 하드코딩된 자격증명 -- **동시성 문제**: 레이스 컨디션, 데드락 가능성 -- **예외 처리**: 빈 catch 블록, 광범위한 예외 포착 - -### 지원 언어 -- JavaScript/TypeScript -- Python -- Java/Kotlin -- PHP -- Go -- C/C++ - -### 출력 형식 -- 심각도별 분류 (Critical, High, Medium, Low) -- 파일 위치 및 라인 번호 -- 문제 설명 및 수정 제안 -- HTML 또는 마크다운 리포트 - -## 분석 프로세스 - -1. 코드베이스 스캔 및 파일 수집 -2. 언어별 패턴 매칭 적용 -3. 정적 분석 수행 -4. 발견된 이슈 분류 및 우선순위 지정 -5. 수정 제안 생성 -6. 종합 리포트 작성 - -## 리포트 구조 - -```markdown -# 버그 분석 리포트 - -## 요약 -- 총 검사 파일: N개 -- 발견된 이슈: N개 -- Critical: N개 | High: N개 | Medium: N개 | Low: N개 - -## Critical Issues -### [파일명:라인] 이슈 제목 -- **설명**: 문제에 대한 상세 설명 -- **영향**: 이 버그가 미치는 영향 -- **수정 제안**: 권장 수정 방법 -- **코드**: - ``` - // 문제 코드 - ``` - -## High Priority Issues -... - -## Medium Priority Issues -... - -## Low Priority Issues -... - -## 권장 사항 -- 즉시 조치 필요 항목 -- 점진적 개선 항목 -``` - -## 사용 예시 - -``` -이 프로젝트에서 버그를 찾아줘 -src 폴더의 코드를 검사하고 문제점을 알려줘 -보안 취약점이 있는지 분석해줘 -``` - -## 출처 -Original skill from skills.cokac.com diff --git a/.claude/skills/code-commenter/SKILL.md b/.claude/skills/code-commenter/SKILL.md deleted file mode 100755 index ae1ae8e..0000000 --- a/.claude/skills/code-commenter/SKILL.md +++ /dev/null @@ -1,169 +0,0 @@ ---- -name: code-commenter -description: 프로그램 소스 코드에 이해하기 쉬운 주석을 추가합니다. 코드 주석 추가, 코드 설명, 문서화 요청 시 활성화됩니다. -allowed-tools: Read, Write, Edit, Glob, Grep ---- - -# Code Commenter - -소스 코드에 초보자도 이해할 수 있는 설명적인 주석을 추가하는 스킬입니다. - -## 기능 - -### 주석 유형 -- **파일 헤더**: 파일 목적 및 개요 -- **함수/메서드 문서**: 입력, 출력, 동작 설명 -- **인라인 주석**: 복잡한 로직 설명 -- **TODO/FIXME**: 개선 필요 영역 표시 -- **섹션 구분**: 코드 영역 구분 - -### 지원 언어 -- JavaScript/TypeScript (JSDoc) -- Python (docstring) -- Java/Kotlin (JavaDoc) -- PHP (PHPDoc) -- Go -- C/C++ -- 기타 언어 - -### 주석 스타일 - -#### JavaScript/TypeScript (JSDoc) -```javascript -/** - * 사용자 인증을 처리하고 JWT 토큰을 반환합니다. - * - * @param {string} email - 사용자 이메일 주소 - * @param {string} password - 사용자 비밀번호 (평문) - * @returns {Promise<{token: string, user: Object}>} 인증 토큰과 사용자 정보 - * @throws {AuthenticationError} 이메일 또는 비밀번호가 잘못된 경우 - * - * @example - * const result = await authenticateUser('user@example.com', 'password123'); - * console.log(result.token); // 'eyJhbGc...' - */ -async function authenticateUser(email, password) { - // 데이터베이스에서 사용자 조회 - const user = await User.findByEmail(email); - - // 비밀번호 해시 비교로 인증 검증 - const isValid = await bcrypt.compare(password, user.passwordHash); - - if (!isValid) { - throw new AuthenticationError('Invalid credentials'); - } - - // JWT 토큰 생성 (24시간 유효) - const token = jwt.sign({ userId: user.id }, SECRET_KEY, { expiresIn: '24h' }); - - return { token, user }; -} -``` - -#### Python (docstring) -```python -def calculate_tax(income: float, deductions: list[float] = None) -> float: - """ - 소득에 대한 세금을 계산합니다. - - 누진세율을 적용하여 과세표준에 따른 세금을 계산합니다. - 공제 항목이 있으면 소득에서 차감한 후 계산합니다. - - Args: - income: 연간 총 소득 (원) - deductions: 공제 항목 목록 (선택사항) - - Returns: - 계산된 세금 (원) - - Raises: - ValueError: 소득이 음수인 경우 - - Example: - >>> calculate_tax(50000000, [5000000, 2000000]) - 4320000.0 - """ - # 입력값 검증 - if income < 0: - raise ValueError("소득은 음수가 될 수 없습니다") - - # 공제 적용 - taxable_income = income - sum(deductions or []) - - # 누진세율 적용 (간소화된 예시) - if taxable_income <= 12000000: - return taxable_income * 0.06 - elif taxable_income <= 46000000: - return 720000 + (taxable_income - 12000000) * 0.15 - else: - return 5820000 + (taxable_income - 46000000) * 0.24 -``` - -#### Java (JavaDoc) -```java -/** - * 주문 처리 서비스 - * - *

고객의 주문을 생성, 수정, 취소하는 기능을 제공합니다. - * 재고 확인 및 결제 처리와 연동됩니다.

- * - * @author 개발팀 - * @version 1.0 - * @since 2024-01-01 - */ -public class OrderService { - - /** - * 새로운 주문을 생성합니다. - * - *

장바구니의 상품들을 주문으로 변환하고, - * 재고를 확인한 후 결제를 진행합니다.

- * - * @param customerId 고객 ID - * @param cartItems 장바구니 상품 목록 - * @param paymentMethod 결제 수단 - * @return 생성된 주문 객체 - * @throws InsufficientStockException 재고가 부족한 경우 - * @throws PaymentFailedException 결제가 실패한 경우 - */ - public Order createOrder(Long customerId, List cartItems, PaymentMethod paymentMethod) { - // 재고 확인 - validateStock(cartItems); - - // 주문 생성 - Order order = new Order(customerId, cartItems); - - // 결제 처리 - processPayment(order, paymentMethod); - - return orderRepository.save(order); - } -} -``` - -## 주석 가이드라인 - -### DO (권장) -- 복잡한 비즈니스 로직 설명 -- 알고리즘의 의도 설명 -- 비직관적인 결정의 이유 설명 -- 외부 의존성 및 부작용 문서화 -- 매개변수 제약조건 명시 - -### DON'T (피해야 할 것) -- 코드를 그대로 반복하는 주석 -- 너무 명백한 내용 설명 -- 오래된/부정확한 주석 방치 -- 주석으로 코드 숨기기 -- 과도한 주석으로 가독성 저해 - -## 사용 예시 - -``` -이 코드에 설명 주석을 추가해줘 -함수들에 JSDoc 주석을 작성해줘 -초보자도 이해할 수 있게 코드를 문서화해줘 -``` - -## 출처 -Original skill from skills.cokac.com diff --git a/.claude/skills/code-flow-web-doc-generator/SKILL.md b/.claude/skills/code-flow-web-doc-generator/SKILL.md deleted file mode 100755 index d23eea0..0000000 --- a/.claude/skills/code-flow-web-doc-generator/SKILL.md +++ /dev/null @@ -1,124 +0,0 @@ ---- -name: code-flow-web-doc-generator -description: 소스 코드를 분석하여 호출 흐름과 데이터 흐름 다이어그램이 포함된 자체 완결형 HTML 문서를 생성합니다. 코드 흐름 문서화, 시퀀스 다이어그램 생성, 코드 이해를 위한 시각화 요청 시 활성화됩니다. -allowed-tools: Read, Write, Edit, Glob, Grep ---- - -# Code Flow Web Document Generator - -소스 코드를 분석하여 시각적 다이어그램과 설명이 포함된 자체 완결형 HTML 문서를 생성하는 스킬입니다. - -## 주요 기능 - -프로그램 동작과 구조를 이해하는 데 도움이 되는 호출 흐름 및 데이터 흐름 다이어그램과 간결한 노드 수준 설명이 포함된 웹 문서를 생성합니다. - -## 워크플로우 - -1. 코드 입력 수신 (단일 파일, 다중 파일, 저장소, 또는 코드 스니펫) -2. 함수, 클래스, import, 호출 관계 추출 -3. 컴포넌트 간 데이터 흐름 식별 -4. Mermaid 형식으로 모듈 및 시퀀스 다이어그램 생성 -5. 요약, 다이어그램, 상세 노드 설명 섹션이 포함된 구조화된 HTML 생성 - -## 기능 상세 - -### 다이어그램 유형 -- 고수준 모듈 다이어그램 -- 호출 시퀀스 흐름 - -### 출력 형식 -- 인라인 CSS 및 스크립트가 포함된 단일 자체 완결형 HTML 파일 - -### 코드 분석 -- 함수 역할, 입력, 출력, 부작용 추출 - -### 문서화 -- 코드 발췌 (3-12줄) -- 핫스팟 분석 -- 재생성 지침 포함 - -## 사용 사례 - -- 단일 파일 스크립트 분석 -- 다중 모듈 웹 서비스 문서화 -- 이벤트 기반 코드 설명 -- 코드 리뷰 준비 및 온보딩 - -## HTML 출력 템플릿 - -```html - - - - - 코드 흐름 문서 - - - - - -
- -
- -
- -
-

프로젝트 요약

- -
- - -
-

모듈 구조

-
- graph TD - A[Entry Point] --> B[Module A] - A --> C[Module B] -
-
- - -
-

호출 흐름

-
- sequenceDiagram - participant User - participant API - participant DB -
-
- - -
-

함수 상세

- -
- - -
-

데이터 흐름

- -
-
- - - - -``` - -## 사용 예시 - -``` -이 파일의 코드 흐름을 다이어그램으로 문서화해줘 -src 폴더의 호출 관계를 시퀀스 다이어그램으로 만들어줘 -이 함수들의 데이터 흐름을 HTML 문서로 생성해줘 -``` - -## 출처 -Original skill from skills.cokac.com diff --git a/.claude/skills/code-flow-web-report/SKILL.md b/.claude/skills/code-flow-web-report/SKILL.md deleted file mode 100755 index 6c7f8a9..0000000 --- a/.claude/skills/code-flow-web-report/SKILL.md +++ /dev/null @@ -1,179 +0,0 @@ ---- -name: code-flow-web-report -description: 웹 애플리케이션의 런타임 흐름을 시각화하는 인터랙티브 리포트를 생성합니다. 라우트, 컨트롤러, 서비스 흐름 분석 요청 시 활성화됩니다. -allowed-tools: Read, Write, Edit, Glob, Grep, Bash ---- - -# Code Flow Web Report - -웹 애플리케이션의 요청 처리 흐름을 시각화하는 인터랙티브 HTML 리포트를 생성하는 스킬입니다. - -## 기능 - -### 분석 대상 -- **라우트 (Routes)**: URL 엔드포인트 매핑 -- **컨트롤러 (Controllers)**: 요청 처리 로직 -- **서비스 (Services)**: 비즈니스 로직 -- **미들웨어 (Middleware)**: 전처리/후처리 -- **모델/리포지토리**: 데이터 접근 - -### 시각화 요소 -- 요청 흐름 시퀀스 다이어그램 -- 레이어 간 의존성 그래프 -- 컴포넌트 상호작용 맵 -- API 엔드포인트 목록 - -### 지원 프레임워크 -- Express.js / Koa.js -- NestJS -- Django / Flask -- Laravel / Symfony -- Spring Boot - -## 분석 프로세스 - -1. 라우트 정의 파일 스캔 -2. 컨트롤러/핸들러 매핑 추출 -3. 서비스 의존성 분석 -4. 미들웨어 체인 파악 -5. 데이터 흐름 추적 -6. 인터랙티브 HTML 생성 - -## HTML 템플릿 - -```html - - - - - 코드 흐름 리포트 - - - - - - -
-
-

🔄 코드 흐름 리포트

-
-
- -
- -
-

📊 요약

-
-
-
24
-
API 엔드포인트
-
-
-
8
-
컨트롤러
-
-
-
12
-
서비스
-
-
-
5
-
미들웨어
-
-
-
- - -
-

🏗️ 아키텍처

-
- graph TD - Client[클라이언트] --> Router[라우터] - Router --> MW[미들웨어] - MW --> Controller[컨트롤러] - Controller --> Service[서비스] - Service --> Repository[리포지토리] - Repository --> DB[(데이터베이스)] -
-
- - -
-

🔄 요청 흐름 예시

-
- sequenceDiagram - participant C as Client - participant R as Router - participant M as Middleware - participant CT as Controller - participant S as Service - participant DB as Database - - C->>R: POST /api/users - R->>M: authMiddleware() - M->>CT: UserController.create() - CT->>S: UserService.createUser() - S->>DB: INSERT INTO users - DB-->>S: user record - S-->>CT: User object - CT-->>C: 201 Created -
-
- - -
-

📡 API 엔드포인트

-
-
-
- GET - /api/users - UserController.list -
-
-
-
- POST - /api/users - UserController.create -
-
-
-
- PUT - /api/users/:id - UserController.update -
-
-
-
- DELETE - /api/users/:id - UserController.delete -
-
-
-
-
- - - - -``` - -## 사용 예시 - -``` -이 웹앱의 코드 흐름을 분석해서 리포트로 만들어줘 -API 요청이 어떻게 처리되는지 시각화해줘 -라우트에서 DB까지의 흐름을 다이어그램으로 보여줘 -``` - -## 출처 -Original skill from skills.cokac.com diff --git a/.claude/skills/code-principles-auditor/SKILL.md b/.claude/skills/code-principles-auditor/SKILL.md deleted file mode 100644 index 7d0f7f4..0000000 --- a/.claude/skills/code-principles-auditor/SKILL.md +++ /dev/null @@ -1,422 +0,0 @@ ---- -name: ln-623-code-principles-auditor -description: Code principles audit worker (L3). Checks DRY (7 types), KISS/YAGNI, TODOs, error handling, DI patterns. Returns findings with severity, location, effort, recommendations. -allowed-tools: Read, Grep, Glob, Bash ---- - -# Code Principles Auditor (L3 Worker) - -Specialized worker auditing code principles (DRY, KISS, YAGNI) and design patterns. - -## Purpose & Scope - -- **Worker in ln-620 coordinator pipeline** - invoked by ln-620-codebase-auditor -- Audit **code principles** (DRY/KISS/YAGNI, error handling, DI) -- Check DRY/KISS/YAGNI violations, TODO/FIXME, workarounds, error handling -- Return structured findings with severity, location, effort, recommendations -- Calculate compliance score (X/10) for Code Principles category - -## Inputs (from Coordinator) - -**MANDATORY READ:** Load `shared/references/task_delegation_pattern.md#audit-coordinator--worker-contract` for contextStore structure. - -Receives `contextStore` with: `tech_stack`, `best_practices`, `principles`, `codebase_root`. - -**Domain-aware:** Supports `domain_mode` + `current_domain` (see `audit_output_schema.md#domain-aware-worker-output`). - -## Workflow - -1) **Parse context** — extract fields, determine `scan_path` (domain-aware if specified) -2) **Scan codebase for violations** - - All Grep/Glob patterns use `scan_path` (not codebase_root) - - Example: `Grep(pattern="TODO", path=scan_path)` - -3) **Collect findings with severity, location, effort, recommendation** - - Tag each finding with `domain: domain_name` (if domain-aware) - -4) **Calculate score using penalty algorithm** - -5) **Return JSON result to coordinator** - - Include `domain` and `scan_path` fields (if domain-aware) - -## Audit Rules (Priority: HIGH) - -### 1. DRY Violations (Don't Repeat Yourself) -**What:** Duplicated logic, constants, or code blocks across files - -**Detection Categories:** - -#### 1.1. Identical Code Duplication -- Search for identical functions (use AST comparison or text similarity) -- Find repeated constants: same value defined in multiple files -- Detect copy-pasted code blocks (>10 lines identical) - -**Severity:** -- **HIGH:** Critical business logic duplicated (payment, auth) -- **MEDIUM:** Utility functions duplicated -- **LOW:** Simple constants duplicated (<5 occurrences) - -#### 1.2. Duplicated Validation Logic -**What:** Same validation patterns repeated across validators/controllers - -**Detection:** -- Email validation: `/@.*\./` regex patterns in multiple files -- Password validation: `/.{8,}/`, strength checks repeated -- Phone validation: phone number regex duplicated -- Common patterns: `isValid*`, `validate*`, `check*` functions with similar logic - -**Severity:** -- **HIGH:** Auth/payment validation duplicated (inconsistency risk) -- **MEDIUM:** User input validation duplicated (3+ occurrences) -- **LOW:** Simple format checks duplicated (<3 occurrences) - -**Recommendation:** Extract to shared validators module (`validators/common.ts`) - -**Effort:** M (extract validators, update imports) - -#### 1.3. Repeated Error Messages -**What:** Hardcoded error messages instead of centralized error catalog - -**Detection:** -- Grep for hardcoded strings in `throw new Error("...")`, `res.status(400).json({ error: "..." })` -- Find repeated messages: "User not found", "Invalid credentials", "Unauthorized access" -- Check for missing error constants file: `errors.ts`, `error-messages.ts`, `constants/errors.ts` - -**Severity:** -- **MEDIUM:** Critical error messages hardcoded (auth, payment) - inconsistency risk -- **MEDIUM:** No centralized error messages file -- **LOW:** Same error message in <3 places - -**Recommendation:** -- Create central error messages file (`constants/error-messages.ts`) -- Define error catalog: `const ERRORS = { USER_NOT_FOUND: "User not found", ... }` -- Replace hardcoded strings with constants: `throw new Error(ERRORS.USER_NOT_FOUND)` - -**Effort:** M (create error catalog, replace hardcoded strings) - -#### 1.4. Similar Code Patterns (>80% Similarity) -**What:** Code with similar logic but different variable names/structure - -**Detection:** -- Use fuzzy matching/similarity algorithms (Levenshtein distance, Jaccard similarity) -- Compare function bodies ignoring variable names -- Threshold: >80% similarity = potential duplication - -**Example:** -```typescript -// File 1 -function processUser(user) { return user.name.toUpperCase(); } - -// File 2 -function formatUserName(u) { return u.name.toUpperCase(); } -// ✅ Same logic, different names - DETECTED -``` - -**Severity:** -- **MEDIUM:** Similar business logic (>80% similarity) in critical paths -- **LOW:** Similar utility functions (<3 occurrences) - -**Recommendation:** Extract common logic, create shared helper function - -**Effort:** M (refactor to shared module) - -#### 1.5. Duplicated SQL Queries -**What:** Same SQL queries/ORM calls in different controllers/services - -**Detection:** -- Find repeated raw SQL strings: `SELECT * FROM users WHERE id = ?` -- ORM duplicates: `User.findOne({ where: { email } })` in multiple files -- Grep for common patterns: `SELECT`, `INSERT`, `UPDATE`, `DELETE` with similar structure - -**Severity:** -- **HIGH:** Critical queries duplicated (payment, auth) -- **MEDIUM:** Common queries duplicated (3+ occurrences) -- **LOW:** Simple queries duplicated (<3 occurrences) - -**Recommendation:** Extract to Repository layer, create query methods - -**Effort:** M (create repository methods, update callers) - -#### 1.6. Copy-Pasted Tests -**What:** Test files with identical structure (arrange-act-assert duplicated) - -**Detection:** -- Find tests with >80% similar setup/teardown -- Repeated test data: same fixtures defined in multiple test files -- Pattern: `beforeEach`, `afterEach` with identical code - -**Severity:** -- **MEDIUM:** Test setup duplicated in 5+ files -- **LOW:** Similar test utilities duplicated (<5 files) - -**Recommendation:** Extract to test helpers (`tests/helpers/*`), use shared fixtures - -**Effort:** M (create test utilities, refactor tests) - -#### 1.7. Repeated API Response Structures -**What:** Duplicated response objects instead of shared DTOs - -**Detection:** -- Find repeated object structures in API responses: - ```typescript - return { id: user.id, name: user.name, email: user.email } - ``` -- Check for missing DTOs folder: `dtos/`, `responses/`, `models/` -- Grep for common patterns: `return { ... }` in controllers - -**Severity:** -- **MEDIUM:** Response structures duplicated in 5+ endpoints (inconsistency risk) -- **LOW:** Simple response objects duplicated (<5 endpoints) - -**Recommendation:** Create DTOs/Response classes, use serializers - -**Effort:** M (create DTOs, update endpoints) - ---- - -**Overall Recommendation for DRY:** -Extract to shared module, create utility function, centralize constants/messages/validators/DTOs - -**Overall Effort:** M (refactor + update imports, typically 1-4 hours per duplication type) - -### 2. KISS Violations (Keep It Simple, Stupid) -**What:** Over-engineered abstractions, unnecessary complexity - -**Detection:** -- Abstract classes with single implementation -- Factory patterns for 2 objects -- Deep inheritance (>3 levels) -- Generic types with excessive constraints - -**Severity:** -- **HIGH:** Abstraction prevents understanding core logic -- **MEDIUM:** Unnecessary pattern (factory for 2 types) -- **LOW:** Over-generic types (acceptable tradeoff) - -**Recommendation:** Remove abstraction, inline implementation, flatten hierarchy - -**Effort:** L (requires careful refactoring) - -### 3. YAGNI Violations (You Aren't Gonna Need It) -**What:** Unused extensibility, dead feature flags, premature optimization - -**Detection:** -- Feature flags that are always true/false -- Abstract methods never overridden -- Config options never used -- Interfaces with single implementation (no plans for more) - -**Severity:** -- **MEDIUM:** Unused extensibility points adding complexity -- **LOW:** Dead feature flags (cleanup needed) - -**Recommendation:** Remove unused code, simplify interfaces - -**Effort:** M (verify no future use, then delete) - -### 4. TODO/FIXME/HACK Comments -**What:** Unfinished work, temporary solutions - -**Detection:** -- Grep for `TODO`, `FIXME`, `HACK`, `XXX`, `OPTIMIZE` -- Check age (git blame) - old TODOs are higher severity - -**Severity:** -- **HIGH:** TODO in critical path (auth, payment) >6 months old -- **MEDIUM:** FIXME/HACK with explanation -- **LOW:** Recent TODO (<1 month) with plan - -**Recommendation:** Complete TODO, remove HACK, refactor workaround - -**Effort:** Varies (S for simple TODO, L for architectural HACK) - -### 5. Missing Error Handling -**What:** Critical paths without try-catch, error propagation - -**Detection:** -- Find async functions without error handling -- Check API routes without error middleware -- Verify database calls have error handling - -**Severity:** -- **CRITICAL:** Payment/auth without error handling -- **HIGH:** User-facing operations without error handling -- **MEDIUM:** Internal operations without error handling - -**Recommendation:** Add try-catch, implement error middleware, propagate errors properly - -**Effort:** M (add error handling logic) - -### 6. Centralized Error Handling -**What:** Errors handled inconsistently across different contexts (web requests, cron jobs, background tasks) - -**Detection:** -- Search for centralized error handler class/module: `ErrorHandler`, `errorHandler`, `error-handler.ts/js/py` -- Check if error middleware delegates to handler: `errorHandler.handleError(err)` or similar -- Verify all async routes use promises or async/await (Express 5+ auto-catches rejections) -- Check for error transformation (sanitize stack traces for users in production) -- **Anti-pattern check:** Look for `process.on("uncaughtException")` usage (BAD PRACTICE per Express docs) - -**Severity:** -- **HIGH:** No centralized error handler (errors handled inconsistently in multiple places) -- **HIGH:** Using `uncaughtException` listener instead of proper error propagation (Express anti-pattern) -- **MEDIUM:** Error middleware handles errors directly (doesn't delegate to central handler) -- **MEDIUM:** Async routes without proper error handling (not using promises/async-await) -- **LOW:** Stack traces exposed in production responses (security/UX issue) - -**Recommendation:** -- Create single ErrorHandler class/module for ALL error contexts -- Middleware should only catch and forward to ErrorHandler (delegate pattern) -- Use async/await for async routes (framework auto-forwards errors) -- Transform errors for users: hide sensitive details (stack traces, internal paths) in production -- **DO NOT use uncaughtException listeners** - use process managers (PM2, systemd) for restart instead -- For unhandled rejections: log and restart process (use supervisor, not inline handler) - -**Effort:** M-L (create error handler, refactor existing middleware) - -### 7. Dependency Injection / Centralized Init -**What:** Direct imports/instantiation instead of dependency injection, scattered initialization - -**Detection:** -- Check for DI container usage: - - Node.js: `inversify`, `awilix`, `tsyringe`, `typedi` packages - - Python: `dependency_injector`, `injector` packages - - Java: Spring `@Autowired`, `@Inject` annotations - - .NET: Built-in DI in ASP.NET Core, `IServiceCollection` -- Grep for direct instantiations in business logic: `new SomeService()`, `new SomeRepository()` -- Check for centralized Init/Bootstrap module: `bootstrap.ts`, `init.py`, `Startup.cs`, `app.module.ts` -- Verify controllers/services receive dependencies via constructor/parameters, not direct imports - -**Severity:** -- **MEDIUM:** No DI container (hard to test, tight coupling, difficult to swap implementations) -- **MEDIUM:** Direct instantiation in business logic (`new Service()` in controllers/services) -- **LOW:** Mixed DI and direct imports (inconsistent pattern) - -**Recommendation:** -- Use DI container for dependency management (Inversify, Awilix, Spring, built-in .NET DI) -- Centralize initialization in Init/Bootstrap module -- Inject dependencies via constructor/parameters (dependency injection pattern) -- Never use direct instantiation for business logic classes (only for DTOs, value objects) - -**Effort:** L (refactor to DI pattern, add container, update all instantiations) - -### 8. Missing Best Practices Guide -**What:** No architecture/design best practices documentation for developers - -**Detection:** -- Check for architecture guide files: - - `docs/architecture.md`, `docs/best-practices.md`, `docs/design-patterns.md` - - `ARCHITECTURE.md`, `CONTRIBUTING.md` (architecture section) -- Verify content includes: layering rules, error handling patterns, DI usage, coding conventions - -**Severity:** -- **LOW:** No architecture guide (harder for new developers to understand patterns and conventions) - -**Recommendation:** -- Create `docs/architecture.md` with project-specific patterns: - - Document layering: Controller→Service→Repository→DB - - Error handling: centralized ErrorHandler pattern - - Dependency Injection: how to add new services/repositories - - Coding conventions: naming, file organization, imports -- Include examples from existing codebase -- Keep framework-agnostic (principles, not specific implementations) - -**Effort:** S (create markdown file, ~1-2 hours documentation) - -## Scoring Algorithm - -See `shared/references/audit_scoring.md` for unified formula and score interpretation. - -## Output Format - -Return JSON to coordinator: - -**Global mode output:** -```json -{ - "category": "Architecture & Design", - "score": 6, - "total_issues": 12, - "critical": 2, - "high": 4, - "medium": 4, - "low": 2, - "checks": [ - {"id": "dry_violations", "name": "DRY Violations", "status": "failed", "details": "4 duplications found"}, - {"id": "kiss_violations", "name": "KISS Violations", "status": "warning", "details": "1 over-engineered abstraction"}, - {"id": "yagni_violations", "name": "YAGNI Violations", "status": "passed", "details": "No unused code"}, - {"id": "solid_violations", "name": "SOLID Violations", "status": "failed", "details": "2 SRP violations"} - ], - "findings": [...] -} -``` - -**Domain-aware mode output (NEW):** -```json -{ - "category": "Architecture & Design", - "score": 6, - "domain": "users", - "scan_path": "src/users", - "total_issues": 12, - "critical": 2, - "high": 4, - "medium": 4, - "low": 2, - "checks": [ - {"id": "dry_violations", "name": "DRY Violations", "status": "failed", "details": "4 duplications found"}, - {"id": "kiss_violations", "name": "KISS Violations", "status": "warning", "details": "1 over-engineered abstraction"}, - {"id": "yagni_violations", "name": "YAGNI Violations", "status": "passed", "details": "No unused code"}, - {"id": "solid_violations", "name": "SOLID Violations", "status": "failed", "details": "2 SRP violations"} - ], - "findings": [ - { - "severity": "CRITICAL", - "location": "src/users/controllers/UserController.ts:45", - "issue": "Controller directly uses Repository (layer boundary break)", - "principle": "Layer Separation (Clean Architecture)", - "recommendation": "Create UserService, inject into controller", - "effort": "L", - "domain": "users" - }, - { - "severity": "HIGH", - "location": "src/users/services/UserService.ts:45", - "issue": "DRY violation - duplicate validation logic", - "principle": "DRY Principle", - "recommendation": "Extract to shared validators module", - "effort": "M", - "domain": "users" - } - ] -} -``` - -## Critical Rules - -- **Do not auto-fix:** Report only -- **Domain-aware scanning:** If `domain_mode="domain-aware"`, scan ONLY `scan_path` (not entire codebase) -- **Tag findings:** Include `domain` field in each finding when domain-aware -- **Context-aware:** Use project's `principles.md` to define what's acceptable -- **Age matters:** Old TODOs are higher severity than recent ones -- **Effort realism:** S = <1h, M = 1-4h, L = >4h - -## Definition of Done - -- contextStore parsed (including domain_mode and current_domain) -- scan_path determined (domain path or codebase root) -- All 8 checks completed (scoped to scan_path): - - DRY (7 subcategories), KISS, YAGNI, TODOs, Error Handling, Centralized Errors, DI/Init, Best Practices Guide -- Findings collected with severity, location, effort, recommendation, domain -- Score calculated -- JSON returned to coordinator with domain metadata - -## Reference Files - -- **Audit scoring formula:** `shared/references/audit_scoring.md` -- **Audit output schema:** `shared/references/audit_output_schema.md` -- Architecture rules: [references/architecture_rules.md](references/architecture_rules.md) - ---- -**Version:** 4.1.0 -**Last Updated:** 2026-01-29 diff --git a/.claude/skills/code-quality-auditor/SKILL.md b/.claude/skills/code-quality-auditor/SKILL.md deleted file mode 100644 index c879e4b..0000000 --- a/.claude/skills/code-quality-auditor/SKILL.md +++ /dev/null @@ -1,302 +0,0 @@ ---- -name: ln-624-code-quality-auditor -description: "Code quality audit worker (L3). Checks cyclomatic complexity, deep nesting, long methods, god classes, method signature quality, O(n²) algorithms, N+1 queries, magic numbers/constants. Returns findings with severity, location, effort, recommendations." -allowed-tools: Read, Grep, Glob, Bash ---- - -# Code Quality Auditor (L3 Worker) - -Specialized worker auditing code complexity, method signatures, algorithms, and constants management. - -## Purpose & Scope - -- **Worker in ln-620 coordinator pipeline** - invoked by ln-620-codebase-auditor -- Audit **code quality** (Categories 5+6+NEW: Medium Priority) -- Check complexity metrics, method signature quality, algorithmic efficiency, constants management -- Return structured findings with severity, location, effort, recommendations -- Calculate compliance score (X/10) for Code Quality category - -## Inputs (from Coordinator) - -**MANDATORY READ:** Load `shared/references/task_delegation_pattern.md#audit-coordinator--worker-contract` for contextStore structure. - -Receives `contextStore` with: `tech_stack`, `best_practices`, `principles`, `codebase_root`. - -**Domain-aware:** Supports `domain_mode` + `current_domain` (see `audit_output_schema.md#domain-aware-worker-output`). - -## Workflow - -1) **Parse context** — extract fields, determine `scan_path` (domain-aware if specified) -2) **Scan codebase for violations** - - All Grep/Glob patterns use `scan_path` (not codebase_root) - - Example: `Grep(pattern="if.*if.*if", path=scan_path)` for nesting detection - -3) **Collect findings with severity, location, effort, recommendation** - - Tag each finding with `domain: domain_name` (if domain-aware) - -4) **Calculate score using penalty algorithm** - -5) **Return JSON result to coordinator** - - Include `domain` and `scan_path` fields (if domain-aware) - -## Audit Rules (Priority: MEDIUM) - -### 1. Cyclomatic Complexity -**What:** Too many decision points in single function (> 10) - -**Detection:** -- Count if/else, switch/case, ternary, &&, ||, for, while -- Use tools: `eslint-plugin-complexity`, `radon` (Python), `gocyclo` (Go) - -**Severity:** -- **HIGH:** Complexity > 20 (extremely hard to test) -- **MEDIUM:** Complexity 11-20 (refactor recommended) -- **LOW:** Complexity 8-10 (acceptable but monitor) - -**Recommendation:** Split function, extract helper methods, use early returns - -**Effort:** M-L (depends on complexity) - -### 2. Deep Nesting (> 4 levels) -**What:** Nested if/for/while blocks too deep - -**Detection:** -- Count indentation levels -- Pattern: if { if { if { if { if { ... } } } } } - -**Severity:** -- **HIGH:** > 6 levels (unreadable) -- **MEDIUM:** 5-6 levels -- **LOW:** 4 levels - -**Recommendation:** Extract functions, use guard clauses, invert conditions - -**Effort:** M (refactor structure) - -### 3. Long Methods (> 50 lines) -**What:** Functions too long, doing too much - -**Detection:** -- Count lines between function start and end -- Exclude comments, blank lines - -**Severity:** -- **HIGH:** > 100 lines -- **MEDIUM:** 51-100 lines -- **LOW:** 40-50 lines (borderline) - -**Recommendation:** Split into smaller functions, apply Single Responsibility - -**Effort:** M (extract logic) - -### 4. God Classes/Modules (> 500 lines) -**What:** Files with too many responsibilities - -**Detection:** -- Count lines in file (exclude comments) -- Check number of public methods/functions - -**Severity:** -- **HIGH:** > 1000 lines -- **MEDIUM:** 501-1000 lines -- **LOW:** 400-500 lines - -**Recommendation:** Split into multiple files, apply separation of concerns - -**Effort:** L (major refactor) - -### 5. Too Many Parameters (> 5) -**What:** Functions with excessive parameters - -**Detection:** -- Count function parameters -- Check constructors, methods - -**Severity:** -- **MEDIUM:** 6-8 parameters -- **LOW:** 5 parameters (borderline) - -**Recommendation:** Use parameter object, builder pattern, default parameters - -**Effort:** S-M (refactor signature + calls) - -### 6. O(n²) or Worse Algorithms -**What:** Inefficient nested loops over collections - -**Detection:** -- Nested for loops: `for (i) { for (j) { ... } }` -- Nested array methods: `arr.map(x => arr.filter(...))` - -**Severity:** -- **HIGH:** O(n²) in hot path (API request handler) -- **MEDIUM:** O(n²) in occasional operations -- **LOW:** O(n²) on small datasets (n < 100) - -**Recommendation:** Use hash maps, optimize with single pass, use better data structures - -**Effort:** M (algorithm redesign) - -### 7. N+1 Query Patterns -**What:** ORM lazy loading causing N+1 queries - -**Detection:** -- Find loops with database queries inside -- Check ORM patterns: `users.forEach(u => u.getPosts())` - -**Severity:** -- **CRITICAL:** N+1 in API endpoint (performance disaster) -- **HIGH:** N+1 in frequent operations -- **MEDIUM:** N+1 in admin panel - -**Recommendation:** Use eager loading, batch queries, JOIN - -**Effort:** M (change ORM query) - -### 8. Constants Management (NEW) -**What:** Magic numbers/strings, decentralized constants, duplicates - -**Detection:** - -| Issue | Pattern | Example | -|-------|---------|---------| -| Magic numbers | Hardcoded numbers in conditions/calculations | `if (status === 2)` | -| Magic strings | Hardcoded strings in comparisons | `if (role === 'admin')` | -| Decentralized | Constants scattered across files | `MAX_SIZE = 100` in 5 files | -| Duplicates | Same value multiple times | `STATUS_ACTIVE = 1` in 3 places | -| No central file | Missing `constants.ts` or `config.py` | No single source of truth | - -**Severity:** -- **HIGH:** Magic numbers in business logic (payment amounts, statuses) -- **MEDIUM:** Duplicate constants (same value defined 3+ times) -- **MEDIUM:** No central constants file -- **LOW:** Magic strings in logging/debugging - -**Recommendation:** -- Create central constants file (`constants.ts`, `config.py`, `constants.go`) -- Extract magic numbers to named constants: `const STATUS_ACTIVE = 1` -- Consolidate duplicates, import from central file -- Use enums for related constants - -**Effort:** M (extract constants, update imports, consolidate) - -### 9. Method Signature Quality -**What:** Poor method contracts reducing readability and maintainability - -**Detection:** - -| Issue | Pattern | Example | -|-------|---------|---------| -| Boolean flag params | >=2 boolean params in signature | `def process(data, is_async: bool, skip_validation: bool)` | -| Too many optional params | >=3 optional params with defaults | `def query(db, limit=10, offset=0, sort="id", order="asc")` | -| Inconsistent verb naming | Different verbs for same operation type in one module | `get_user()` vs `fetch_account()` vs `load_profile()` | -| Unclear return type | `-> dict`, `-> Any`, `-> tuple` without TypedDict/NamedTuple | `def get_stats() -> dict` instead of `-> StatsResponse` | - -**Severity:** -- **MEDIUM:** Boolean flag params (use enum/strategy), unclear return types -- **LOW:** Too many optional params, inconsistent naming - -**Recommendation:** -- Boolean flags: replace with enum, strategy pattern, or separate methods -- Optional params: group into config/options dataclass -- Naming: standardize verb conventions per module (`get_` for sync, `fetch_` for async, etc.) -- Return types: use TypedDict, NamedTuple, or dataclass instead of raw dict/tuple - -**Effort:** S-M (refactor signatures + callers) - -## Scoring Algorithm - -See `shared/references/audit_scoring.md` for unified formula and score interpretation. - -## Output Format - -Return JSON to coordinator: - -**Global mode output:** -```json -{ - "category": "Code Quality", - "score": 6, - "total_issues": 12, - "critical": 1, - "high": 3, - "medium": 5, - "low": 3, - "checks": [ - {"id": "cyclomatic_complexity", "name": "Cyclomatic Complexity", "status": "failed", "details": "2 functions exceed threshold"}, - {"id": "deep_nesting", "name": "Deep Nesting", "status": "warning", "details": "1 function with 5 levels"}, - {"id": "long_methods", "name": "Long Methods", "status": "passed", "details": "No methods exceed 50 lines"}, - {"id": "magic_numbers", "name": "Magic Numbers", "status": "failed", "details": "5 magic numbers found"} - ], - "findings": [...] -} -``` - -**Domain-aware mode output (NEW):** -```json -{ - "category": "Code Quality", - "score": 7, - "domain": "orders", - "scan_path": "src/orders", - "total_issues": 8, - "critical": 0, - "high": 2, - "medium": 4, - "low": 2, - "checks": [ - {"id": "cyclomatic_complexity", "name": "Cyclomatic Complexity", "status": "failed", "details": "1 function exceeds threshold"}, - {"id": "deep_nesting", "name": "Deep Nesting", "status": "passed", "details": "No deep nesting detected"}, - {"id": "long_methods", "name": "Long Methods", "status": "passed", "details": "No methods exceed 50 lines"}, - {"id": "magic_numbers", "name": "Magic Numbers", "status": "warning", "details": "1 magic number found"} - ], - "findings": [ - { - "severity": "HIGH", - "location": "src/orders/services/OrderService.ts:120", - "issue": "Cyclomatic complexity 22 (threshold: 10)", - "principle": "Code Complexity / Maintainability", - "recommendation": "Split into smaller methods", - "effort": "M", - "domain": "orders" - }, - { - "severity": "MEDIUM", - "location": "src/orders/controllers/OrderController.ts:45", - "issue": "Magic number '3' used for order status", - "principle": "Constants Management", - "recommendation": "Extract: const ORDER_STATUS_SHIPPED = 3", - "effort": "S", - "domain": "orders" - } - ] -} -``` - -## Critical Rules - -- **Do not auto-fix:** Report only -- **Domain-aware scanning:** If `domain_mode="domain-aware"`, scan ONLY `scan_path` (not entire codebase) -- **Tag findings:** Include `domain` field in each finding when domain-aware -- **Context-aware:** Small functions (n < 100) with O(n²) may be acceptable -- **Constants detection:** Exclude test files, configs, examples -- **Metrics tools:** Use existing tools when available (ESLint complexity plugin, radon, gocyclo) - -## Definition of Done - -- contextStore parsed (including domain_mode and current_domain) -- scan_path determined (domain path or codebase root) -- All 9 checks completed (scoped to scan_path): - - complexity, nesting, length, god classes, parameters, O(n²), N+1, constants, method signatures -- Findings collected with severity, location, effort, recommendation, domain -- Score calculated -- JSON returned to coordinator with domain metadata - -## Reference Files - -- **Audit scoring formula:** `shared/references/audit_scoring.md` -- **Audit output schema:** `shared/references/audit_output_schema.md` -- Code quality rules: [references/code_quality_rules.md](references/code_quality_rules.md) - ---- -**Version:** 3.0.0 -**Last Updated:** 2025-12-23 diff --git a/.claude/skills/code-quality-checker/SKILL.md b/.claude/skills/code-quality-checker/SKILL.md deleted file mode 100644 index a56d183..0000000 --- a/.claude/skills/code-quality-checker/SKILL.md +++ /dev/null @@ -1,207 +0,0 @@ ---- -name: ln-501-code-quality-checker -description: "Worker that checks DRY/KISS/YAGNI/architecture compliance with quantitative Code Quality Score. Validates architectural decisions via MCP Ref: (1) Optimality - is chosen approach the best? (2) Compliance - does it follow best practices? (3) Performance - algorithms, configs, bottlenecks. Reports issues with SEC-, PERF-, MNT-, ARCH-, BP-, OPT- prefixes." ---- - -# Code Quality Checker - -Analyzes Done implementation tasks with quantitative Code Quality Score based on metrics, MCP Ref validation, and issue penalties. - -## Purpose & Scope -- Load Story and Done implementation tasks (exclude test tasks) -- Calculate Code Quality Score using metrics and issue penalties -- **MCP Ref validation:** Verify optimality, best practices, and performance via external sources -- Check for DRY/KISS/YAGNI violations, architecture boundary breaks, security issues -- Produce quantitative verdict with structured issue list; never edits Linear or kanban - -## Code Metrics - -| Metric | Threshold | Penalty | -|--------|-----------|---------| -| **Cyclomatic Complexity** | ≤10 OK, 11-20 warning, >20 fail | -5 (warning), -10 (fail) per function | -| **Function size** | ≤50 lines OK, >50 warning | -3 per function | -| **File size** | ≤500 lines OK, >500 warning | -5 per file | -| **Nesting depth** | ≤3 OK, >3 warning | -3 per instance | -| **Parameter count** | ≤4 OK, >4 warning | -2 per function | - -## Code Quality Score - -Formula: `Code Quality Score = 100 - metric_penalties - issue_penalties` - -**Issue penalties by severity:** - -| Severity | Penalty | Examples | -|----------|---------|----------| -| **high** | -20 | Security vulnerability, O(n²)+ algorithm, N+1 query | -| **medium** | -10 | DRY violation, suboptimal approach, missing config | -| **low** | -3 | Naming convention, minor code smell | - -**Score interpretation:** - -| Score | Status | Verdict | -|-------|--------|---------| -| 90-100 | Excellent | PASS | -| 70-89 | Acceptable | CONCERNS | -| <70 | Below threshold | ISSUES_FOUND | - -## Issue Prefixes - -| Prefix | Category | Default Severity | MCP Ref | -|--------|----------|------------------|---------| -| SEC- | Security (auth, validation, secrets) | high | — | -| PERF- | Performance (algorithms, configs, bottlenecks) | medium/high | ✓ Required | -| MNT- | Maintainability (DRY, SOLID, complexity) | medium | — | -| ARCH- | Architecture (layers, boundaries, patterns) | medium | — | -| BP- | Best Practices (implementation differs from recommended) | medium | ✓ Required | -| OPT- | Optimality (better approach exists for this goal) | medium | ✓ Required | - -**PERF- subcategories:** - -| Prefix | Category | Severity | -|--------|----------|----------| -| PERF-ALG- | Algorithm complexity (Big O) | high if O(n²)+ | -| PERF-CFG- | Package/library configuration | medium | -| PERF-PTN- | Architectural pattern performance | high | -| PERF-DB- | Database queries, indexes | high | - -## When to Use -- **Invoked by ln-500-story-quality-gate** Pass 1 (first gate) -- All implementation tasks in Story status = Done -- Before regression testing (ln-502) and test planning (ln-510) - -## Workflow (concise) -1) Load Story (full) and Done implementation tasks (full descriptions) via Linear; skip tasks with label "tests". -2) Collect affected files from tasks (Affected Components/Existing Code Impact) and recent commits/diffs if noted. -3) **Calculate code metrics:** - - Cyclomatic Complexity per function (target ≤10) - - Function size (target ≤50 lines) - - File size (target ≤500 lines) - - Nesting depth (target ≤3) - - Parameter count (target ≤4) - -3.5) **MCP Ref Validation (MANDATORY for code changes):** - - **Level 1 — OPTIMALITY (OPT-):** - - Extract goal from task (e.g., "user authentication", "caching", "API rate limiting") - - Research alternatives: `ref_search_documentation("{goal} approaches comparison {tech_stack} 2026")` - - Compare chosen approach vs alternatives for project context - - Flag suboptimal choices as OPT- issues - - **Level 2 — BEST PRACTICES (BP-):** - - Research: `ref_search_documentation("{chosen_approach} best practices {tech_stack} 2026")` - - For libraries: `query-docs(library_id, "best practices implementation patterns")` - - Flag deviations from recommended patterns as BP- issues - - **Level 3 — PERFORMANCE (PERF-):** - - **PERF-ALG:** Analyze algorithm complexity (detect O(n²)+, research optimal via MCP Ref) - - **PERF-CFG:** Check library configs (connection pooling, batch sizes, timeouts) via `query-docs` - - **PERF-PTN:** Research pattern pitfalls: `ref_search_documentation("{pattern} performance bottlenecks")` - - **PERF-DB:** Check for N+1, missing indexes via `query-docs(orm_library_id, "query optimization")` - - **Triggers for MCP Ref validation:** - - New dependency added (package.json/requirements.txt changed) - - New pattern/library used - - API/database changes - - Loops/recursion in critical paths - - ORM queries added - -4) **Analyze code for static issues (assign prefixes):** - - SEC-: hardcoded creds, unvalidated input, SQL injection, race conditions - - MNT-: DRY violations, dead code, complex conditionals, poor naming - - ARCH-: layer violations, circular dependencies, guide non-compliance - -5) **Calculate Code Quality Score:** - - Start with 100 - - Subtract metric penalties (see Code Metrics table) - - Subtract issue penalties (see Issue penalties table) - -6) Output verdict with score and structured issues. Add Linear comment with findings. - -## Critical Rules -- Read guides mentioned in Story/Tasks before judging compliance. -- **MCP Ref validation:** For ANY architectural change, MUST verify via ref_search_documentation before judging. -- **Context7 for libraries:** When reviewing library usage, query-docs to verify correct patterns. -- Language preservation in comments (EN/RU). -- Do not create tasks or change statuses; caller decides next actions. - -## Definition of Done -- Story and Done implementation tasks loaded (test tasks excluded). -- Code metrics calculated (Cyclomatic Complexity, function/file sizes). -- **MCP Ref validation completed:** - - OPT-: Optimality checked (is chosen approach the best for the goal?) - - BP-: Best practices verified (correct implementation of chosen approach?) - - PERF-: Performance analyzed (algorithms, configs, patterns, DB) -- Issues identified with prefixes and severity, sources from MCP Ref/Context7. -- Code Quality Score calculated. -- **Output format:** - ```yaml - verdict: PASS | CONCERNS | ISSUES_FOUND - code_quality_score: {0-100} - metrics: - avg_cyclomatic_complexity: {value} - functions_over_50_lines: {count} - files_over_500_lines: {count} - issues: - # OPTIMALITY - - id: "OPT-001" - severity: medium - file: "src/auth/index.ts" - goal: "User session management" - finding: "Suboptimal approach for session management" - chosen: "Custom JWT with localStorage" - recommended: "httpOnly cookies + refresh token rotation" - reason: "httpOnly cookies prevent XSS token theft" - source: "ref://owasp-session-management" - - # BEST PRACTICES - - id: "BP-001" - severity: medium - file: "src/api/routes.ts" - finding: "POST for idempotent operation" - best_practice: "Use PUT for idempotent updates (RFC 7231)" - source: "ref://api-design-guide#idempotency" - - # PERFORMANCE - Algorithm - - id: "PERF-ALG-001" - severity: high - file: "src/utils/search.ts:42" - finding: "Nested loops cause O(n²) complexity" - current: "O(n²) - nested filter().find()" - optimal: "O(n) - use Map/Set for lookup" - source: "ref://javascript-performance#data-structures" - - # PERFORMANCE - Config - - id: "PERF-CFG-001" - severity: medium - file: "src/db/connection.ts" - finding: "Missing connection pool config" - current_config: "default (pool: undefined)" - recommended: "pool: { min: 2, max: 10 }" - source: "context7://pg#connection-pooling" - - # PERFORMANCE - Database - - id: "PERF-DB-001" - severity: high - file: "src/repositories/user.ts:89" - finding: "N+1 query pattern detected" - issue: "users.map(u => u.posts) triggers N queries" - solution: "Use eager loading: include: { posts: true }" - source: "context7://prisma#eager-loading" - - # MAINTAINABILITY - - id: "MNT-001" - severity: medium - file: "src/service.ts:42" - finding: "DRY violation: duplicate validation logic" - suggested_action: "Extract to shared validator" - ``` -- Linear comment posted with findings. - -## Reference Files -- Code metrics: `references/code_metrics.md` (thresholds and penalties) -- Guides: `docs/guides/` -- Templates for context: `shared/templates/task_template_implementation.md` - ---- -**Version:** 5.0.0 (Added 3-level MCP Ref validation: Optimality, Best Practices, Performance with PERF-ALG/CFG/PTN/DB subcategories) -**Last Updated:** 2026-01-29 diff --git a/.claude/skills/code-refactoring/SKILL.md b/.claude/skills/code-refactoring/SKILL.md deleted file mode 100755 index fdee8a2..0000000 --- a/.claude/skills/code-refactoring/SKILL.md +++ /dev/null @@ -1,111 +0,0 @@ ---- -name: code-refactoring -description: 코드 리팩토링 권장사항, 성능 분석, 구체적인 코드 패치를 제공합니다. 리팩토링, 코드 개선, 성능 최적화 요청 시 활성화됩니다. -allowed-tools: Read, Write, Edit, Glob, Grep ---- - -# Code Refactoring - -코드 품질 개선을 위한 리팩토링 분석 및 권장사항을 제공하는 스킬입니다. - -## 기능 - -### 분석 영역 -- **코드 스멜 탐지**: 중복 코드, 긴 메서드, 거대 클래스 -- **복잡도 분석**: 순환 복잡도, 인지 복잡도 -- **의존성 분석**: 결합도, 응집도 평가 -- **성능 이슈**: 비효율적 알고리즘, N+1 쿼리 -- **가독성**: 네이밍, 구조화, 주석 품질 - -### 리팩토링 패턴 -- Extract Method / Extract Class -- Replace Conditional with Polymorphism -- Introduce Parameter Object -- Replace Magic Number with Symbolic Constant -- Decompose Conditional -- Replace Nested Conditional with Guard Clauses - -### 출력 -- 문제점 상세 설명 -- 리팩토링 전후 코드 비교 -- 구체적인 수정 패치 -- 성능 영향 분석 - -## 분석 프로세스 - -1. 코드베이스 스캔 -2. 코드 스멜 및 안티패턴 탐지 -3. 복잡도 메트릭 계산 -4. 개선 우선순위 산정 -5. 리팩토링 제안 생성 -6. 전후 비교 코드 제공 - -## 리포트 구조 - -```markdown -# 리팩토링 분석 리포트 - -## 요약 -- 분석 파일: N개 -- 발견된 코드 스멜: N개 -- 권장 리팩토링: N건 - -## 🔴 High Impact 리팩토링 - -### 1. [파일명] 긴 메서드 분리 -**현재 상태** -- 메서드: `processOrder()` -- 라인 수: 150줄 -- 순환 복잡도: 25 - -**문제점** -- 단일 책임 원칙 위반 -- 테스트 어려움 -- 유지보수 복잡 - -**권장 리팩토링: Extract Method** - -Before: -```java -public void processOrder(Order order) { - // 150줄의 복잡한 로직 -} -``` - -After: -```java -public void processOrder(Order order) { - validateOrder(order); - calculateTotal(order); - applyDiscounts(order); - processPayment(order); - sendConfirmation(order); -} - -private void validateOrder(Order order) { ... } -private void calculateTotal(Order order) { ... } -// ... -``` - -**예상 효과** -- 복잡도: 25 → 5 -- 테스트 용이성 향상 -- 재사용성 증가 - -## 🟡 Medium Impact 리팩토링 -... - -## 성능 최적화 제안 -... -``` - -## 사용 예시 - -``` -이 코드를 리팩토링 해줘 -src 폴더의 코드 품질을 분석하고 개선점을 알려줘 -이 함수의 복잡도를 낮추는 방법을 제안해줘 -``` - -## 출처 -Original skill from skills.cokac.com diff --git a/.claude/skills/codebase-analysis-web-report/SKILL.md b/.claude/skills/codebase-analysis-web-report/SKILL.md deleted file mode 100755 index 689f1eb..0000000 --- a/.claude/skills/codebase-analysis-web-report/SKILL.md +++ /dev/null @@ -1,70 +0,0 @@ ---- -name: codebase-analysis-web-report -description: 코드베이스를 분석하여 아키텍처, 호출/데이터 흐름 그래프, 인터랙티브 다이어그램, 검색 기능이 포함된 자체 완결형 HTML 리포트를 생성합니다. 저장소 분석, 프로젝트 디렉토리 분석, 소스 파일 분석 요청 시 활성화됩니다. -allowed-tools: Read, Write, Edit, Glob, Grep, Bash ---- - -# Codebase Analysis Web Report Generator - -코드베이스를 분석하여 자체 완결형 인터랙티브 HTML 리포트를 생성하는 스킬입니다. - -## 기능 - -### 입력 처리 -- 저장소 URL, 파일 업로드, 디렉토리 경로 지원 -- 프레임워크별 분석 지원 (React, Node, Flask, PHP 등) -- 대규모 코드베이스의 경우 자동 범위 지정 또는 사용자 지정 요청 - -### 분석 구성요소 -- 모듈, 클래스, 함수, API 추출을 위한 정적 코드 분석 -- 호출 관계 매핑 및 의존성 추출 -- 메트릭 계산 (LOC, 복잡도 추정) -- 진입점 및 통합 지점 식별 - -### 출력 생성 -- 제어/데이터 흐름 그래프 (개략적 및 세부적) -- 모듈 설명이 포함된 텍스트 요약 -- 우선순위가 지정된 개선 권장사항이 포함된 발견사항 섹션 -- 네비게이션, 인터랙티브 다이어그램, 검색 기능, 소스 코드 미리보기가 포함된 자체 완결형 HTML 리포트 - -## 구현 노트 - -- 가능한 경우 AST 파싱 사용; 그 외에는 정규식 휴리스틱 사용 -- 인터랙티브 다이어그램에 Mermaid 또는 Cytoscape.js 우선 사용 -- 샘플링 또는 범위 제한을 통해 대규모 저장소 처리 -- 프레임워크별 구조 표시 (React 컴포넌트, 라우팅, DB 접근 패턴) - -## 리포트 구조 - -```html - - - - - 코드베이스 분석 리포트 - - - - - - - - - - - - - - -``` - -## 사용 예시 - -``` -이 프로젝트의 코드베이스를 분석해서 HTML 리포트로 만들어줘 -src 폴더의 아키텍처를 분석하고 다이어그램으로 시각화해줘 -이 저장소의 구조를 분석해서 웹 문서로 생성해줘 -``` - -## 출처 -Original skill by Dongchan Lee (@vluylbhtqq) from skills.cokac.com diff --git a/.claude/skills/concurrency-auditor/SKILL.md b/.claude/skills/concurrency-auditor/SKILL.md deleted file mode 100644 index 8dbd3a4..0000000 --- a/.claude/skills/concurrency-auditor/SKILL.md +++ /dev/null @@ -1,194 +0,0 @@ ---- -name: ln-628-concurrency-auditor -description: Concurrency audit worker (L3). Checks race conditions, missing async/await, resource contention, thread safety, deadlock potential. Returns findings with severity, location, effort, recommendations. -allowed-tools: Read, Grep, Glob, Bash ---- - -# Concurrency Auditor (L3 Worker) - -Specialized worker auditing concurrency and async patterns. - -## Purpose & Scope - -- **Worker in ln-620 coordinator pipeline** -- Audit **concurrency** (Category 11: High Priority) -- Check race conditions, async/await, thread safety -- Calculate compliance score (X/10) - -## Inputs (from Coordinator) - -Receives `contextStore` with tech stack, language, codebase root. - -## Workflow - -1) Parse context -2) Check concurrency patterns -3) Collect findings -4) Calculate score -5) Return JSON - -## Audit Rules - -### 1. Race Conditions -**What:** Shared state modified without synchronization - -**Detection Patterns:** - -| Language | Pattern | Grep | -|----------|---------|------| -| Python | Global modified in async | `global\s+\w+` inside `async def` | -| TypeScript | Module-level let in async | `^let\s+\w+` at file scope + async function modifies it | -| Go | Map access without mutex | `map\[.*\].*=` without `sync.Mutex` in same file | -| All | Shared cache | `cache\[.*\]\s*=` or `cache\.set` without lock | - -**Severity:** -- **CRITICAL:** Race in payment/auth (`payment`, `balance`, `auth`, `token` in variable name) -- **HIGH:** Race in user-facing feature -- **MEDIUM:** Race in background job - -**Recommendation:** Use locks, atomic operations, message queues - -**Effort:** M-L - -### 2. Missing Async/Await -**What:** Callback hell or unhandled promises - -**Detection Patterns:** - -| Issue | Grep | Example | -|-------|------|---------| -| Callback hell | `\.then\(.*\.then\(.*\.then\(` | `.then().then().then()` | -| Fire-and-forget | `async.*\(\)` not preceded by `await` | `saveToDb()` without await | -| Missing await | `return\s+new\s+Promise` in async function | Should just `return await` or `return` value | -| Dangling promise | `\.catch\(\s*\)` | Empty catch swallows errors | - -**Severity:** -- **HIGH:** Fire-and-forget async (can cause data loss) -- **MEDIUM:** Callback hell (hard to maintain) -- **LOW:** Mixed Promise styles - -**Recommendation:** Convert to async/await, always await or handle promises - -**Effort:** M - -### 3. Resource Contention -**What:** Multiple processes competing for same resource - -**Detection Patterns:** - -| Issue | Grep | Example | -|-------|------|---------| -| File lock missing | `open\(.*["']w["']\)` without `flock` or `lockfile` | Concurrent file writes | -| Connection exhaustion | `create_engine\(.*pool_size` check if pool_size < 5 | DB pool too small | -| Concurrent writes | `writeFile` or `fs\.write` without lock check | File corruption risk | - -**Severity:** -- **HIGH:** File corruption risk, DB exhaustion -- **MEDIUM:** Performance degradation - -**Recommendation:** Use connection pooling, file locking, `asyncio.Lock` - -**Effort:** M - -### 4. Thread Safety Violations -**What:** Shared mutable state without synchronization - -**Detection Patterns:** - -| Language | Safe Pattern | Unsafe Pattern | -|----------|--------------|----------------| -| Go | `sync.Mutex` with map | `map[...]` without Mutex in same struct | -| Rust | `Arc>` | `Rc>` in multi-threaded context | -| Java | `synchronized` or `ConcurrentHashMap` | `HashMap` shared between threads | -| Python | `threading.Lock` | Global dict modified in threads | - -**Grep patterns:** -- Go unsafe: `type.*struct\s*{[^}]*map\[` without `sync.Mutex` in same struct -- Python unsafe: `global\s+\w+` in function + `threading.Thread` in same file - -**Severity:** **HIGH** (data corruption possible) - -**Recommendation:** Use thread-safe primitives - -**Effort:** M - -### 5. Deadlock Potential -**What:** Lock acquisition in inconsistent order - -**Detection Patterns:** - -| Issue | Grep | Example | -|-------|------|---------| -| Nested locks | `with\s+\w+_lock:.*with\s+\w+_lock:` (multiline) | Lock A then Lock B | -| Lock in loop | `for.*:.*\.acquire\(\)` | Lock acquired repeatedly without release | -| Lock + external call | `.acquire\(\)` followed by `await` or `requests.` | Holding lock during I/O | - -**Severity:** **HIGH** (deadlock freezes application) - -**Recommendation:** Consistent lock ordering, timeout locks (`asyncio.wait_for`) - -**Effort:** L - -### 6. Blocking I/O in Event Loop (Python asyncio) -**What:** Synchronous blocking calls inside async functions - -**Detection Patterns:** - -| Blocking Call | Grep in `async def` | Replacement | -|---------------|---------------------|-------------| -| `time.sleep` | `time\.sleep` inside async def | `await asyncio.sleep` | -| `requests.` | `requests\.(get\|post)` inside async def | `httpx` or `aiohttp` | -| `open()` file | `open\(` inside async def | `aiofiles.open` | - -**Severity:** -- **HIGH:** Blocks entire event loop -- **MEDIUM:** Minor blocking (<100ms) - -**Recommendation:** Use async alternatives - -**Effort:** S-M - -## Scoring Algorithm - -See `shared/references/audit_scoring.md` for unified formula and score interpretation. - -## Output Format - -```json -{ - "category": "Concurrency", - "score": 7, - "total_issues": 4, - "critical": 0, - "high": 2, - "medium": 2, - "low": 0, - "checks": [ - {"id": "race_conditions", "name": "Race Conditions", "status": "passed", "details": "No shared state modified without synchronization"}, - {"id": "missing_await", "name": "Missing Await", "status": "failed", "details": "2 fire-and-forget async calls found"}, - {"id": "resource_contention", "name": "Resource Contention", "status": "warning", "details": "DB pool_size=3 may be insufficient"}, - {"id": "thread_safety", "name": "Thread Safety", "status": "passed", "details": "All shared state properly synchronized"}, - {"id": "deadlock_potential", "name": "Deadlock Potential", "status": "passed", "details": "No nested locks or inconsistent ordering"}, - {"id": "blocking_io", "name": "Blocking I/O", "status": "failed", "details": "time.sleep in async context"} - ], - "findings": [ - { - "severity": "HIGH", - "location": "src/services/payment.ts:45", - "issue": "Shared state 'balanceCache' modified without synchronization", - "principle": "Thread Safety / Concurrency Control", - "recommendation": "Use mutex or atomic operations for balanceCache updates", - "effort": "M" - } - ] -} -``` - -## Reference Files - -- **Audit scoring formula:** `shared/references/audit_scoring.md` -- **Audit output schema:** `shared/references/audit_output_schema.md` - ---- -**Version:** 3.0.0 -**Last Updated:** 2025-12-23 diff --git a/.claude/skills/coverage-improvement-planner/SKILL.md b/.claude/skills/coverage-improvement-planner/SKILL.md deleted file mode 100755 index 3d00f68..0000000 --- a/.claude/skills/coverage-improvement-planner/SKILL.md +++ /dev/null @@ -1,96 +0,0 @@ ---- -name: coverage-improvement-planner -description: 테스트 커버리지를 분석하고 개선 계획을 수립하며 전후 비교 리포트를 생성합니다. 테스트 커버리지, 테스트 개선, 품질 향상 요청 시 활성화됩니다. -allowed-tools: Read, Write, Edit, Glob, Grep, Bash ---- - -# Coverage Improvement Planner - -테스트 커버리지를 분석하고 체계적인 개선 계획을 수립하는 스킬입니다. - -## 기능 - -### 분석 항목 -- **라인 커버리지**: 실행된 코드 라인 비율 -- **브랜치 커버리지**: 조건문 분기 테스트 비율 -- **함수 커버리지**: 테스트된 함수 비율 -- **파일 커버리지**: 테스트가 있는 파일 비율 - -### 개선 계획 수립 -- 미테스트 코드 영역 식별 -- 우선순위 기반 테스트 추가 권장 -- 복잡도 높은 코드 집중 분석 -- 테스트 케이스 템플릿 제공 - -### 지원 프레임워크 -- Jest (JavaScript/TypeScript) -- pytest (Python) -- JUnit (Java/Kotlin) -- PHPUnit (PHP) -- Go test - -## 워크플로우 - -1. 현재 커버리지 수집 및 분석 -2. 커버리지 갭 식별 -3. 비즈니스 중요도 기반 우선순위 산정 -4. 테스트 케이스 템플릿 생성 -5. 구현 후 전후 비교 리포트 생성 - -## 리포트 구조 - -```markdown -# 테스트 커버리지 개선 계획 - -## 현재 상태 -| 메트릭 | 현재 | 목표 | 갭 | -|--------|------|------|-----| -| 라인 커버리지 | 65% | 80% | 15% | -| 브랜치 커버리지 | 45% | 70% | 25% | -| 함수 커버리지 | 70% | 85% | 15% | - -## 우선순위별 개선 대상 - -### 🔴 High Priority (비즈니스 크리티컬) -1. **payment/checkout.js** - - 현재: 30% | 목표: 90% - - 미테스트 함수: processPayment, validateCard - - 권장 테스트 케이스: 5개 - -### 🟡 Medium Priority -... - -### 🟢 Low Priority -... - -## 권장 테스트 케이스 - -### checkout.test.js -```javascript -describe('processPayment', () => { - test('성공적인 결제 처리', () => { - // 테스트 코드 - }); - - test('잘못된 카드 정보 처리', () => { - // 테스트 코드 - }); -}); -``` - -## 예상 결과 -- 개선 후 예상 라인 커버리지: 82% -- 추가 테스트 케이스: 23개 -- 예상 소요 시간: 개발자 판단 -``` - -## 사용 예시 - -``` -테스트 커버리지를 분석하고 개선 계획을 세워줘 -커버리지를 80%로 올리려면 어떤 테스트가 필요해? -미테스트 코드 영역을 찾아서 테스트 템플릿을 만들어줘 -``` - -## 출처 -Original skill from skills.cokac.com diff --git a/.claude/skills/dead-code-auditor/SKILL.md b/.claude/skills/dead-code-auditor/SKILL.md deleted file mode 100644 index d6ff905..0000000 --- a/.claude/skills/dead-code-auditor/SKILL.md +++ /dev/null @@ -1,142 +0,0 @@ ---- -name: ln-626-dead-code-auditor -description: Dead code & legacy audit worker (L3). Checks unreachable code, unused imports/variables/functions, commented-out code, backward compatibility shims, deprecated patterns. Returns findings. -allowed-tools: Read, Grep, Glob, Bash ---- - -# Dead Code Auditor (L3 Worker) - -Specialized worker auditing unused and unreachable code. - -## Purpose & Scope - -- **Worker in ln-620 coordinator pipeline** -- Audit **dead code** (Category 9: Low Priority) -- Find unused imports, variables, functions, commented-out code -- Calculate compliance score (X/10) - -## Inputs (from Coordinator) - -Receives `contextStore` with tech stack, codebase root. - -## Workflow - -1) Parse context -2) Run dead code detection (linters, grep) -3) Collect findings -4) Calculate score -5) Return JSON - -## Audit Rules - -### 1. Unreachable Code -**Detection:** -- Linter rules: `no-unreachable` (ESLint) -- Check code after `return`, `throw`, `break` - -**Severity:** MEDIUM - -### 2. Unused Imports/Variables/Functions -**Detection:** -- ESLint: `no-unused-vars` -- TypeScript: `noUnusedLocals`, `noUnusedParameters` -- Python: `flake8` with `F401`, `F841` - -**Severity:** -- **MEDIUM:** Unused functions (dead weight) -- **LOW:** Unused imports (cleanup needed) - -### 3. Commented-Out Code -**Detection:** -- Grep for `//.*{` or `/*.*function` patterns -- Large comment blocks (>10 lines) with code syntax - -**Severity:** LOW - -**Recommendation:** Delete (git preserves history) - -### 4. Legacy Code & Backward Compatibility -**What:** Backward compatibility shims, deprecated patterns, old code that should be removed - -**Detection:** -- Renamed variables/functions with old aliases: - - Pattern: `const oldName = newName` or `export { newModule as oldModule }` - - Pattern: `function oldFunc() { return newFunc(); }` (wrapper for backward compatibility) -- Deprecated exports/re-exports: - - Grep for `// DEPRECATED`, `@deprecated` JSDoc tags - - Pattern: `export.*as.*old.*` or `export.*legacy.*` -- Conditional code for old versions: - - Pattern: `if.*legacy.*` or `if.*old.*version.*` or `isOldVersion ? oldFunc() : newFunc()` -- Migration shims and adapters: - - Pattern: `migrate.*`, `Legacy.*Adapter`, `.*Shim`, `.*Compat` -- Comment markers: - - Grep for `// backward compatibility`, `// legacy support`, `// TODO: remove in v` - - Grep for `// old implementation`, `// deprecated`, `// kept for backward` - -**Severity:** -- **HIGH:** Backward compatibility shims in critical paths (auth, payment, core features) -- **MEDIUM:** Deprecated exports still in use, migration code from >6 months ago -- **LOW:** Recent migration code (<3 months), planned deprecation with clear removal timeline - -**Recommendation:** -- Remove backward compatibility shims - breaking changes are acceptable when properly versioned -- Delete old implementations - keep only the correct/new version -- Remove deprecated exports - update consumers to use new API -- Delete migration code after grace period (3-6 months) -- Clean legacy support comments - git history preserves old implementations - -**Effort:** -- **S:** Remove simple aliases, delete deprecated exports -- **M:** Refactor code using old APIs to new APIs -- **L:** Remove complex backward compatibility layer affecting multiple modules - -## Scoring Algorithm - -See `shared/references/audit_scoring.md` for unified formula and score interpretation. - -## Output Format - -```json -{ - "category": "Dead Code", - "score": 6, - "total_issues": 12, - "critical": 0, - "high": 2, - "medium": 3, - "low": 7, - "checks": [ - {"id": "unreachable_code", "name": "Unreachable Code", "status": "passed", "details": "No unreachable code detected"}, - {"id": "unused_exports", "name": "Unused Exports", "status": "failed", "details": "3 unused functions found"}, - {"id": "commented_code", "name": "Commented Code", "status": "warning", "details": "7 blocks of commented code"}, - {"id": "legacy_shims", "name": "Legacy Shims", "status": "failed", "details": "2 backward compatibility shims"} - ], - "findings": [ - { - "severity": "MEDIUM", - "location": "src/utils/helpers.ts:45", - "issue": "Function 'formatDate' is never used", - "principle": "Code Maintainability / Clean Code", - "recommendation": "Remove unused function or export if needed elsewhere", - "effort": "S" - }, - { - "severity": "HIGH", - "location": "src/api/v1/auth.ts:12-15", - "issue": "Backward compatibility shim for old password validation (6+ months old)", - "principle": "No Legacy Code / Clean Architecture", - "recommendation": "Remove old password validation, keep only new implementation. Update API version if breaking.", - "effort": "M" - } - ] -} -``` - -## Reference Files - -- **Audit scoring formula:** `shared/references/audit_scoring.md` -- **Audit output schema:** `shared/references/audit_output_schema.md` - ---- -**Version:** 3.0.0 -**Last Updated:** 2025-12-23 diff --git a/.claude/skills/dependencies-auditor/SKILL.md b/.claude/skills/dependencies-auditor/SKILL.md deleted file mode 100644 index 6606e22..0000000 --- a/.claude/skills/dependencies-auditor/SKILL.md +++ /dev/null @@ -1,193 +0,0 @@ ---- -name: ln-625-dependencies-auditor -description: "Dependencies audit worker (L3). Checks outdated packages, unused deps, reinvented wheels, vulnerability scan (CVE/CVSS). Supports mode: full | vulnerabilities_only." -allowed-tools: Read, Grep, Glob, Bash ---- - -# Dependencies & Reuse Auditor (L3 Worker) - -Specialized worker auditing dependency management, code reuse, and security vulnerabilities. - -## Purpose & Scope - -- **Worker in ln-620 coordinator pipeline** (full audit mode) -- **Worker in ln-760 security-setup pipeline** (vulnerabilities_only mode) -- Audit **dependencies and reuse** (Categories 7+8: Medium Priority) -- Check outdated packages, unused deps, wheel reinvention, **CVE vulnerabilities** -- Calculate compliance score (X/10) - -## Parameters - -| Param | Values | Default | Description | -|-------|--------|---------|-------------| -| mode | `full` / `vulnerabilities_only` | `full` | `full` = all 5 checks, `vulnerabilities_only` = only CVE scan | - -## Inputs (from Coordinator) - -Receives `contextStore` with tech stack, package manifest paths, codebase root. - -**From ln-620 (codebase-auditor):** mode=full (default) -**From ln-760 (security-setup):** mode=vulnerabilities_only - -## Workflow - -1) Parse context + mode parameter -2) Run dependency checks (based on mode) -3) Collect findings -4) Calculate score -5) Return JSON - ---- - -## Audit Rules (5 Checks) - -### 1. Outdated Packages -**Mode:** full only - -**Detection:** -- Run `npm outdated --json` (Node.js) -- Run `pip list --outdated --format=json` (Python) -- Run `cargo outdated --format=json` (Rust) - -**Severity:** -- **HIGH:** Major version behind (security risk) -- **MEDIUM:** Minor version behind -- **LOW:** Patch version behind - -**Recommendation:** Update to latest version, test for breaking changes - -**Effort:** S-M (update version, run tests) - -### 2. Unused Dependencies -**Mode:** full only - -**Detection:** -- Parse package.json/requirements.txt -- Grep codebase for `import`/`require` statements -- Find dependencies never imported - -**Severity:** -- **MEDIUM:** Unused production dependency (bloats bundle) -- **LOW:** Unused dev dependency - -**Recommendation:** Remove from package manifest - -**Effort:** S (delete line, test) - -### 3. Available Features Not Used -**Mode:** full only - -**Detection:** -- Check for axios when native fetch available (Node 18+) -- Check for lodash when Array methods sufficient -- Check for moment when Date.toLocaleString sufficient - -**Severity:** -- **MEDIUM:** Unnecessary dependency (increases bundle size) - -**Recommendation:** Use native alternative - -**Effort:** M (refactor code to use native API) - -### 4. Custom Implementations -**Mode:** full only - -**Detection:** -- Grep for custom sorting algorithms -- Check for hand-rolled validation (vs validator.js) -- Find custom date parsing (vs date-fns/dayjs) - -**Severity:** -- **HIGH:** Custom crypto (security risk) -- **MEDIUM:** Custom utilities with well-tested alternatives - -**Recommendation:** Replace with established library - -**Effort:** M (integrate library, replace calls) - -### 5. Vulnerability Scan (CVE/CVSS) -**Mode:** full AND vulnerabilities_only - -**Detection:** -- Detect ecosystems: npm, NuGet, pip, Go, Bundler, Cargo, Composer -- Run audit commands per `references/vulnerability_commands.md` -- Parse results with CVSS mapping per `shared/references/cvss_severity_mapping.md` - -**Severity:** -- **CRITICAL:** CVSS 9.0-10.0 (immediate fix required) -- **HIGH:** CVSS 7.0-8.9 (fix within 48h) -- **MEDIUM:** CVSS 4.0-6.9 (fix within 1 week) -- **LOW:** CVSS 0.1-3.9 (fix when convenient) - -**Fix Classification:** -- Patch update (x.x.Y) → safe auto-fix -- Minor update (x.Y.0) → usually safe -- Major update (Y.0.0) → manual review required -- No fix available → document and monitor - -**Recommendation:** Update to fixed version, verify lock file integrity - -**Effort:** S-L (depends on breaking changes) - ---- - -## Scoring Algorithm - -See `shared/references/audit_scoring.md` for unified formula and score interpretation. - -**Note:** When mode=vulnerabilities_only, score based only on vulnerability findings. - -## Output Format - -```json -{ - "category": "Dependencies & Reuse", - "mode": "full", - "score": 7, - "total_issues": 12, - "critical": 1, - "high": 3, - "medium": 5, - "low": 3, - "checks": [ - {"id": "outdated_packages", "name": "Outdated Packages", "status": "failed", "details": "2 packages behind major versions"}, - {"id": "unused_deps", "name": "Unused Dependencies", "status": "warning", "details": "4 unused dev dependencies"}, - {"id": "available_natives", "name": "Available Natives", "status": "passed", "details": "No unnecessary polyfills"}, - {"id": "custom_implementations", "name": "Custom Implementations", "status": "warning", "details": "2 custom utilities found"}, - {"id": "vulnerability_scan", "name": "Vulnerability Scan (CVE)", "status": "failed", "details": "1 critical, 2 high vulnerabilities"} - ], - "findings": [ - { - "severity": "CRITICAL", - "location": "package.json", - "issue": "lodash@4.17.15 has CVE-2021-23337 (CVSS 7.2)", - "principle": "Security / Vulnerability Management", - "recommendation": "Update to lodash@4.17.21", - "effort": "S", - "fix_type": "patch" - }, - { - "severity": "HIGH", - "location": "package.json:15", - "issue": "express v4.17.0 (current: v4.19.2, 2 major versions behind)", - "principle": "Dependency Management / Security Updates", - "recommendation": "Update to v4.19.2 for security fixes", - "effort": "M" - } - ] -} -``` - -## Reference Files - -| File | Purpose | -|------|---------| -| `references/vulnerability_commands.md` | Ecosystem-specific audit commands | -| `references/ci_integration_guide.md` | CI/CD integration guidance | -| `shared/references/cvss_severity_mapping.md` | CVSS to severity level mapping | -| `shared/references/audit_scoring.md` | Audit scoring formula | -| `shared/references/audit_output_schema.md` | Audit output schema | - ---- -**Version:** 4.0.0 -**Last Updated:** 2026-02-05 diff --git a/.claude/skills/design-skill/SKILL.md b/.claude/skills/design-skill/SKILL.md deleted file mode 100755 index c0d725c..0000000 --- a/.claude/skills/design-skill/SKILL.md +++ /dev/null @@ -1,949 +0,0 @@ ---- -name: design-skill -description: 프레젠테이션 슬라이드를 미려한 HTML로 디자인. 슬라이드 HTML 생성, 시각적 디자인, 레이아웃 구성이 필요할 때 사용. ---- - -# Design Skill - 프로페셔널 프레젠테이션 디자인 시스템 - -최고 수준의 비즈니스 프레젠테이션을 위한 HTML 슬라이드 디자인 스킬입니다. -미니멀하고 세련된 디자인, 전문적인 타이포그래피, 정교한 레이아웃을 제공합니다. - ---- - -## 핵심 디자인 철학 - -### 1. Less is More -- 불필요한 장식 요소 제거 -- 콘텐츠가 주인공이 되는 디자인 -- 여백(Whitespace)을 적극 활용 -- 시각적 계층 구조 명확화 - -### 2. 타이포그래피 중심 디자인 -- Pretendard를 기본 폰트로 사용 -- 폰트 크기 대비로 시각적 임팩트 생성 -- 자간과 행간의 섬세한 조절 -- 웨이트 변화로 강조점 표현 - -### 3. 전략적 색상 사용 -- 제한된 색상 팔레트 (2-3색) -- 모노톤 기반 + 포인트 컬러 -- 배경색으로 분위기 연출 -- 고대비로 가독성 확보 - ---- - -## 기본 설정 - -### 슬라이드 크기 (16:9 기본) -```html - -``` - -### 지원 비율 -| 비율 | 크기 | 용도 | -|------|------|------| -| 16:9 | 720pt × 405pt | 기본, 모니터/화면 | -| 4:3 | 720pt × 540pt | 구형 프로젝터 | -| 16:10 | 720pt × 450pt | 맥북 | - -### 기본 폰트 스택 -```css -font-family: 'Pretendard', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; -``` - -### Pretendard 웹폰트 CDN -```html - -``` - ---- - -## 타이포그래피 시스템 - -### 폰트 크기 스케일 -| 용도 | 크기 | 웨이트 | 사용 예시 | -|------|------|--------|----------| -| Hero Title | 72-96pt | 700-800 | 표지 메인 타이틀 | -| Section Title | 48-60pt | 700 | 섹션 구분 제목 | -| Slide Title | 32-40pt | 600-700 | 슬라이드 제목 | -| Subtitle | 20-24pt | 500 | 부제목, 설명 | -| Body | 16-20pt | 400 | 본문 텍스트 | -| Caption | 12-14pt | 400 | 캡션, 출처 | -| Label | 10-12pt | 500-600 | 뱃지, 태그 | - -### 자간 설정 (letter-spacing) -```css -/* 대형 제목: 타이트하게 */ -letter-spacing: -0.02em; - -/* 중형 제목 */ -letter-spacing: -0.01em; - -/* 본문: 기본 */ -letter-spacing: 0; - -/* 캡션, 레이블: 약간 넓게 */ -letter-spacing: 0.02em; -``` - -### 행간 설정 (line-height) -```css -/* 제목 */ -line-height: 1.2; - -/* 본문 */ -line-height: 1.6 - 1.8; - -/* 한 줄 텍스트 */ -line-height: 1; -``` - ---- - -## 색상 팔레트 시스템 - -### 1. Executive Minimal (기본 권장) -세련된 비즈니스 프레젠테이션용 -```css ---bg-primary: #f5f5f0; /* 웜 화이트 배경 */ ---bg-secondary: #e8e8e3; /* 서브 배경 */ ---bg-dark: #1a1a1a; /* 다크 배경 */ ---text-primary: #1a1a1a; /* 메인 텍스트 */ ---text-secondary: #666666; /* 보조 텍스트 */ ---text-light: #999999; /* 약한 텍스트 */ ---accent: #1a1a1a; /* 강조 (검정) */ ---border: #d4d4d0; /* 테두리 */ -``` - -### 2. Sage Professional -차분하고 신뢰감 있는 톤 -```css ---bg-primary: #b8c4b8; /* 세이지 그린 배경 */ ---bg-secondary: #a3b0a3; /* 짙은 세이지 */ ---bg-light: #f8faf8; /* 밝은 배경 */ ---text-primary: #1a1a1a; /* 메인 텍스트 */ ---text-secondary: #3d3d3d; /* 보조 텍스트 */ ---accent: #2d2d2d; /* 강조 */ ---border: #9aa89a; /* 테두리 */ -``` - -### 3. Modern Dark -임팩트 있는 다크 테마 -```css ---bg-primary: #0f0f0f; /* 순수 다크 */ ---bg-secondary: #1a1a1a; /* 카드 배경 */ ---bg-elevated: #252525; /* 강조 영역 */ ---text-primary: #ffffff; /* 메인 텍스트 */ ---text-secondary: #b0b0b0; /* 보조 텍스트 */ ---accent: #ffffff; /* 강조 (화이트) */ ---border: #333333; /* 테두리 */ -``` - -### 4. Corporate Blue -전통적 비즈니스 톤 -```css ---bg-primary: #ffffff; /* 화이트 배경 */ ---bg-secondary: #f7f9fc; /* 밝은 블루 그레이 */ ---text-primary: #1e2a3a; /* 다크 네이비 */ ---text-secondary: #5a6b7d; /* 블루 그레이 */ ---accent: #2563eb; /* 블루 강조 */ ---border: #e2e8f0; /* 테두리 */ -``` - -### 5. Warm Neutral -따뜻하고 친근한 톤 -```css ---bg-primary: #faf8f5; /* 크림 화이트 */ ---bg-secondary: #f0ebe3; /* 웜 베이지 */ ---text-primary: #2d2a26; /* 다크 브라운 */ ---text-secondary: #6b6560; /* 미디움 브라운 */ ---accent: #c45a3b; /* 테라코타 */ ---border: #ddd8d0; /* 테두리 */ -``` - ---- - -## 레이아웃 시스템 - -### 여백 기준 (padding/margin) -```css -/* 슬라이드 전체 여백 */ -padding: 48pt; - -/* 섹션 간 여백 */ -gap: 32pt; - -/* 요소 간 여백 */ -gap: 16pt; - -/* 텍스트 블록 내 여백 */ -gap: 8pt; -``` - -### 그리드 시스템 -```css -/* 2단 레이아웃 */ -display: grid; -grid-template-columns: 1fr 1fr; -gap: 32pt; - -/* 3단 레이아웃 */ -grid-template-columns: repeat(3, 1fr); - -/* 비대칭 레이아웃 (40:60) */ -grid-template-columns: 2fr 3fr; - -/* 비대칭 레이아웃 (30:70) */ -grid-template-columns: 1fr 2.3fr; -``` - ---- - -## 디자인 컴포넌트 - -> **html2pptx 호환성 주의**: 텍스트 요소(`

`, `

`-`

`)에 직접 border, background, shadow를 적용하면 변환 오류가 발생합니다. 반드시 `
`로 감싸서 스타일을 적용하세요. - -### 1. 뱃지/태그 -```html - -
-

PRESENTATION

-
-``` - -### 2. 섹션 넘버 -```html - -
-

SECTION 1

-
-``` - -### 3. 로고 영역 -```html -
-
-

*

-
-

LogoName

-
-``` - -### 4. 아이콘 버튼 -```html -
-

-
-``` - -### 5. 구분선 -```html -
-``` - -### 6. 정보 그리드 -```html -
-
-

Contact

-

334556774

-
-
-

Date

-

March 2025

-
-
-``` - ---- - -## 슬라이드 템플릿 - -### 1. 표지 슬라이드 (Cover) -```html - - - - - - - - -
-
-
-
-

*

-
-

LogoName

-
-
-

PRESENTATION

-
-
-
-
-

OUR PROJECT

-
-
-

-
-
-
- - -
-

- Business Deck -

-

- Presented by Luna Martinez -

-
- - -
-
-

Contact

-

334556774

-
-
-

Date

-

March 2025

-
-
-

Website

-

www.yourwebsite.com

-
-
- - -``` - -### 2. 목차 슬라이드 (Contents) -```html - - - - - - - - -
-

©2025 YOUR BRAND. ALL RIGHTS RESERVED.

-

- Our
Contents -

-
-

-
-
- - -
-
-

SECTION 1

-

SECTION TITLE

-

(1)

-
-
-

SECTION 2

-

SECTION TITLE

-

(2)

-
-
-

SECTION 3

-

SECTION TITLE

-

(3)

-
-
-

SECTION 4

-

SECTION TITLE

-

(4)

-
-
-

SECTION 5

-

SECTION TITLE

-

(5)

-
-
- - -``` - -### 3. 섹션 구분 슬라이드 (Section Divider) -```html - - - - - - - - -
-
-

SECTION 1

-
-

©2025 YOUR BRAND

-
- - -
-

- Introduction -

-

- Brief description of what this section covers and why it matters. -

-
- - -
-

01

-
- - -``` - -### 4. 콘텐츠 슬라이드 (Content) -```html - - - - - - - - -
-
-

SECTION 1

-

Main Topic

-
-

02

-
- - -
-
-

Key Point One

-

- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore. -

-
-
-

Key Point Two

-

- Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip. -

-
-
- - -
-

www.yourwebsite.com

-

©2025 YOUR BRAND

-
- - -``` - -### 5. 통계/데이터 슬라이드 (Statistics) -```html - - - - - - - - -
-

Key Metrics

-

03

-
- - -
-
-

Revenue Growth

-
-

85%

-

Year over year

-
-
-
-

Active Users

-
-

2.4M

-

+340K this quarter

-
-
-
-

Customer Satisfaction

-
-

4.9

-

Out of 5.0 rating

-
-
-
- - -
-

Source: Internal Analytics 2025

-
- - -``` - -### 6. 이미지 + 텍스트 슬라이드 (Split Layout) -```html - - - - - - - - -
-
-

©2025 YOUR BRAND

-
- - -
-

FEATURE

-

- Transform Your Business -

-

- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. -

-
-

Learn more

-
-

-
-
-
- - -``` - -### 7. 팀 소개 슬라이드 (Team) -```html - - - - - - - - -
-

Our Team

-

05

-
- - -
-
-
-

John Smith

-

CEO & Founder

-
-
-
-

Sarah Johnson

-

CTO

-
-
-
-

Mike Chen

-

Design Lead

-
-
-
-

Emily Davis

-

Marketing

-
-
- - -``` - -### 8. 인용문 슬라이드 (Quote) -```html - - - - - - - -

"

-

- The best way to predict the future is to create it. -

-
-

Peter Drucker

-

Management Consultant

-
- - -``` - -### 9. 타임라인 슬라이드 (Timeline) -```html - - - - - - - - -
-

Our Journey

-
- - -
-
- -
- - -
-
-

2020

-

Company Founded

-
-
-
-

2021

-

First Product Launch

-
-
-
-

2023

-

Series A Funding

-
-
-
-

2025

-

Global Expansion

-
-
-
- - -
-

06

-
- - -``` - -### 10. 마무리 슬라이드 (Closing) -```html - - - - - - - - -
-
-

*

-
-

LogoName

-
- - -
-

- Thank You -

-

- Questions? Let's discuss. -

-
- - -
-
-

Email

-

hello@company.com

-
-
-

Phone

-

+82 10-1234-5678

-
-
-

Website

-

www.company.com

-
-
- - -``` - ---- - -## 고급 디자인 패턴 - -### 비대칭 레이아웃 -시선을 끄는 독창적인 구성 -```css -/* 황금비율 기반 */ -grid-template-columns: 1fr 1.618fr; - -/* 극단적 비대칭 */ -grid-template-columns: 1fr 3fr; -``` - -### 오버레이 텍스트 -이미지 위 텍스트 배치 -```html -
-
-
-

Overlay Text

-
-
-``` - -### 그라데이션 오버레이 -```html -
-``` - -### 카드 스타일 -```html -
-``` - ---- - -## 텍스트 사용 규칙 - -### 필수 태그 -```html - -

,

-

,
    ,
      ,
    1. - - -
      텍스트
      -텍스트 -``` - -### 권장 사용법 -```html - -

      제목

      -

      본문 텍스트

      - - -
      텍스트 직접 입력
      -``` - ---- - -## 출력 및 파일 구조 - -### 파일 저장 규칙 -``` -slides/ -├── slide-01.html (표지) -├── slide-02.html (목차) -├── slide-03.html (섹션 구분) -├── slide-04.html (내용) -├── ... -└── slide-XX.html (마무리) -``` - -### 파일 명명 규칙 -- 2자리 숫자 사용: `slide-01.html`, `slide-02.html` -- 순서대로 명명 -- 특수문자, 공백 사용 금지 - ---- - -## 워크플로우 - -1. **분석**: `slide-outline.md` 읽고 콘텐츠 파악 -2. **테마 결정**: 색상 팔레트, 전체적인 무드 선택 -3. **구조 설계**: 슬라이드별 레이아웃 타입 결정 -4. **디자인 실행**: 각 슬라이드 HTML 생성 -5. **일관성 검토**: 전체 프레젠테이션의 통일성 확인 -6. **저장**: `slides/` 디렉토리에 파일 저장 - ---- - -## 주의사항 - -1. **CSS 그라데이션**: PowerPoint 변환 시 지원 안됨 - 배경 이미지로 대체 -2. **웹폰트**: Pretendard CDN 링크 항상 포함 -3. **이미지 경로**: 절대 경로 또는 URL 사용 -4. **호환성**: 모든 색상에 # 포함 -5. **텍스트 규칙**: div/span에 직접 텍스트 금지 diff --git a/.claude/skills/differential-review/SKILL.md b/.claude/skills/differential-review/SKILL.md deleted file mode 100644 index b14a515..0000000 --- a/.claude/skills/differential-review/SKILL.md +++ /dev/null @@ -1,220 +0,0 @@ ---- -name: differential-review -description: > - Performs security-focused differential review of code changes (PRs, commits, diffs). - Adapts analysis depth to codebase size, uses git history for context, calculates - blast radius, checks test coverage, and generates comprehensive markdown reports. - Automatically detects and prevents security regressions. -allowed-tools: - - Read - - Write - - Grep - - Glob - - Bash ---- - -# Differential Security Review - -Security-focused code review for PRs, commits, and diffs. - -## Core Principles - -1. **Risk-First**: Focus on auth, crypto, value transfer, external calls -2. **Evidence-Based**: Every finding backed by git history, line numbers, attack scenarios -3. **Adaptive**: Scale to codebase size (SMALL/MEDIUM/LARGE) -4. **Honest**: Explicitly state coverage limits and confidence level -5. **Output-Driven**: Always generate comprehensive markdown report file - ---- - -## Rationalizations (Do Not Skip) - -| Rationalization | Why It's Wrong | Required Action | -|-----------------|----------------|-----------------| -| "Small PR, quick review" | Heartbleed was 2 lines | Classify by RISK, not size | -| "I know this codebase" | Familiarity breeds blind spots | Build explicit baseline context | -| "Git history takes too long" | History reveals regressions | Never skip Phase 1 | -| "Blast radius is obvious" | You'll miss transitive callers | Calculate quantitatively | -| "No tests = not my problem" | Missing tests = elevated risk rating | Flag in report, elevate severity | -| "Just a refactor, no security impact" | Refactors break invariants | Analyze as HIGH until proven LOW | -| "I'll explain verbally" | No artifact = findings lost | Always write report | - ---- - -## Quick Reference - -### Codebase Size Strategy - -| Codebase Size | Strategy | Approach | -|---------------|----------|----------| -| SMALL (<20 files) | DEEP | Read all deps, full git blame | -| MEDIUM (20-200) | FOCUSED | 1-hop deps, priority files | -| LARGE (200+) | SURGICAL | Critical paths only | - -### Risk Level Triggers - -| Risk Level | Triggers | -|------------|----------| -| HIGH | Auth, crypto, external calls, value transfer, validation removal | -| MEDIUM | Business logic, state changes, new public APIs | -| LOW | Comments, tests, UI, logging | - ---- - -## Workflow Overview - -``` -Pre-Analysis → Phase 0: Triage → Phase 1: Code Analysis → Phase 2: Test Coverage - ↓ ↓ ↓ ↓ -Phase 3: Blast Radius → Phase 4: Deep Context → Phase 5: Adversarial → Phase 6: Report -``` - ---- - -## Decision Tree - -**Starting a review?** - -``` -├─ Need detailed phase-by-phase methodology? -│ └─ Read: methodology.md -│ (Pre-Analysis + Phases 0-4: triage, code analysis, test coverage, blast radius) -│ -├─ Analyzing HIGH RISK change? -│ └─ Read: adversarial.md -│ (Phase 5: Attacker modeling, exploit scenarios, exploitability rating) -│ -├─ Writing the final report? -│ └─ Read: reporting.md -│ (Phase 6: Report structure, templates, formatting guidelines) -│ -├─ Looking for specific vulnerability patterns? -│ └─ Read: patterns.md -│ (Regressions, reentrancy, access control, overflow, etc.) -│ -└─ Quick triage only? - └─ Use Quick Reference above, skip detailed docs -``` - ---- - -## Quality Checklist - -Before delivering: - -- [ ] All changed files analyzed -- [ ] Git blame on removed security code -- [ ] Blast radius calculated for HIGH risk -- [ ] Attack scenarios are concrete (not generic) -- [ ] Findings reference specific line numbers + commits -- [ ] Report file generated -- [ ] User notified with summary - ---- - -## Integration - -**audit-context-building skill:** -- Pre-Analysis: Build baseline context -- Phase 4: Deep context on HIGH RISK changes - -**issue-writer skill:** -- Transform findings into formal audit reports -- Command: `issue-writer --input DIFFERENTIAL_REVIEW_REPORT.md --format audit-report` - ---- - -## Example Usage - -### Quick Triage (Small PR) -``` -Input: 5 file PR, 2 HIGH RISK files -Strategy: Use Quick Reference -1. Classify risk level per file (2 HIGH, 3 LOW) -2. Focus on 2 HIGH files only -3. Git blame removed code -4. Generate minimal report -Time: ~30 minutes -``` - -### Standard Review (Medium Codebase) -``` -Input: 80 files, 12 HIGH RISK changes -Strategy: FOCUSED (see methodology.md) -1. Full workflow on HIGH RISK files -2. Surface scan on MEDIUM -3. Skip LOW risk files -4. Complete report with all sections -Time: ~3-4 hours -``` - -### Deep Audit (Large, Critical Change) -``` -Input: 450 files, auth system rewrite -Strategy: SURGICAL + audit-context-building -1. Baseline context with audit-context-building -2. Deep analysis on auth changes only -3. Blast radius analysis -4. Adversarial modeling -5. Comprehensive report -Time: ~6-8 hours -``` - ---- - -## When NOT to Use This Skill - -- **Greenfield code** (no baseline to compare) -- **Documentation-only changes** (no security impact) -- **Formatting/linting** (cosmetic changes) -- **User explicitly requests quick summary only** (they accept risk) - -For these cases, use standard code review instead. - ---- - -## Red Flags (Stop and Investigate) - -**Immediate escalation triggers:** -- Removed code from "security", "CVE", or "fix" commits -- Access control modifiers removed (onlyOwner, internal → external) -- Validation removed without replacement -- External calls added without checks -- High blast radius (50+ callers) + HIGH risk change - -These patterns require adversarial analysis even in quick triage. - ---- - -## Tips for Best Results - -**Do:** -- Start with git blame for removed code -- Calculate blast radius early to prioritize -- Generate concrete attack scenarios -- Reference specific line numbers and commits -- Be honest about coverage limitations -- Always generate the output file - -**Don't:** -- Skip git history analysis -- Make generic findings without evidence -- Claim full analysis when time-limited -- Forget to check test coverage -- Miss high blast radius changes -- Output report only to chat (file required) - ---- - -## Supporting Documentation - -- **[methodology.md](methodology.md)** - Detailed phase-by-phase workflow (Phases 0-4) -- **[adversarial.md](adversarial.md)** - Attacker modeling and exploit scenarios (Phase 5) -- **[reporting.md](reporting.md)** - Report structure and formatting (Phase 6) -- **[patterns.md](patterns.md)** - Common vulnerability patterns reference - ---- - -**For first-time users:** Start with [methodology.md](methodology.md) to understand the complete workflow. - -**For experienced users:** Use this page's Quick Reference and Decision Tree to navigate directly to needed content. diff --git a/.claude/skills/duplicate-file-cleaner/SKILL.md b/.claude/skills/duplicate-file-cleaner/SKILL.md deleted file mode 100755 index 863a19c..0000000 --- a/.claude/skills/duplicate-file-cleaner/SKILL.md +++ /dev/null @@ -1,154 +0,0 @@ ---- -name: duplicate-file-cleaner -description: 중복 이미지 및 미디어 파일을 메타데이터 기반으로 찾아 안전하게 제거합니다. 중복 파일 정리, 파일 정리, 용량 확보 요청 시 활성화됩니다. -allowed-tools: Read, Write, Edit, Glob, Grep, Bash ---- - -# Duplicate File Cleaner Expert - -중복 파일을 탐지하고 안전하게 제거하는 스킬입니다. - -## 기능 - -### 탐지 방법 -- **해시 비교**: MD5/SHA256 체크섬으로 정확한 중복 탐지 -- **파일명 유사도**: 이름 기반 잠재적 중복 탐지 -- **메타데이터 분석**: EXIF, 생성일시, 크기 비교 -- **퍼지 매칭**: 리사이즈/재인코딩된 유사 파일 탐지 - -### 지원 파일 유형 -- 이미지: jpg, png, gif, webp, bmp, tiff -- 비디오: mp4, avi, mkv, mov, wmv -- 오디오: mp3, wav, flac, aac -- 문서: pdf, doc, xls (선택적) - -### 안전 기능 -- 삭제 전 미리보기 -- 휴지통으로 이동 (영구 삭제 방지) -- 원본 보존 우선 (수정일 기준) -- 롤백 가능한 로그 생성 - -## 사용 스크립트 - -### Python 중복 탐지 -```python -import hashlib -import os -from pathlib import Path -from collections import defaultdict - -def get_file_hash(filepath, chunk_size=8192): - """파일의 MD5 해시 계산""" - hasher = hashlib.md5() - with open(filepath, 'rb') as f: - while chunk := f.read(chunk_size): - hasher.update(chunk) - return hasher.hexdigest() - -def find_duplicates(directory, extensions=None): - """디렉토리에서 중복 파일 찾기""" - hash_map = defaultdict(list) - - for filepath in Path(directory).rglob('*'): - if not filepath.is_file(): - continue - if extensions and filepath.suffix.lower() not in extensions: - continue - - file_hash = get_file_hash(filepath) - hash_map[file_hash].append(filepath) - - # 중복된 것만 반환 - return {h: files for h, files in hash_map.items() if len(files) > 1} - -def generate_report(duplicates): - """중복 파일 리포트 생성""" - total_size = 0 - report = ["# 중복 파일 리포트\n"] - - for hash_val, files in duplicates.items(): - size = os.path.getsize(files[0]) - savings = size * (len(files) - 1) - total_size += savings - - report.append(f"\n## 해시: {hash_val[:8]}...") - report.append(f"크기: {size:,} bytes | 절약 가능: {savings:,} bytes") - for f in files: - mtime = os.path.getmtime(f) - report.append(f" - {f} (수정: {mtime})") - - report.append(f"\n---\n총 절약 가능 용량: {total_size:,} bytes ({total_size/1024/1024:.2f} MB)") - return '\n'.join(report) - -# 사용 예시 -extensions = {'.jpg', '.jpeg', '.png', '.gif', '.mp4'} -duplicates = find_duplicates('/path/to/directory', extensions) -print(generate_report(duplicates)) -``` - -### Bash 빠른 탐지 -```bash -#!/bin/bash -# 중복 파일 빠른 탐지 (크기 + 해시 기반) - -find "$1" -type f -name "*.jpg" -o -name "*.png" | while read file; do - size=$(stat -f%z "$file" 2>/dev/null || stat -c%s "$file") - hash=$(md5sum "$file" | cut -d' ' -f1) - echo "$size $hash $file" -done | sort | uniq -D -w 40 -``` - -## 리포트 구조 - -```markdown -# 중복 파일 분석 리포트 - -## 요약 -- 스캔 파일: 1,234개 -- 중복 그룹: 45개 -- 중복 파일: 123개 -- 절약 가능 용량: 2.3 GB - -## 중복 그룹 상세 - -### 그룹 1 (3개 파일, 15.2 MB 절약 가능) -| 파일 | 경로 | 수정일 | 권장 | -|------|------|--------|------| -| photo.jpg | /photos/2023/ | 2023-05-01 | ✅ 유지 | -| photo(1).jpg | /downloads/ | 2023-06-15 | ❌ 삭제 | -| IMG_001.jpg | /backup/ | 2023-05-01 | ❌ 삭제 | - -### 그룹 2 ... - -## 권장 조치 -1. 자동 선택된 78개 파일 삭제 (1.8 GB) -2. 수동 검토 필요: 12개 그룹 -3. 백업 폴더 정리 권장 - -## 실행 명령 -```bash -# 안전 삭제 (휴지통으로 이동) -trash-put file1.jpg file2.jpg ... - -# 또는 영구 삭제 (주의!) -rm file1.jpg file2.jpg ... -``` -``` - -## 사용 예시 - -``` -이 폴더의 중복 파일을 찾아줘 -사진 폴더에서 중복 이미지를 정리해줘 -용량을 확보하기 위해 중복 파일을 분석해줘 -``` - -## 주의사항 - -- 삭제 전 항상 백업 권장 -- 시스템 파일은 스캔에서 제외 -- 심볼릭 링크 주의 필요 -- 클라우드 동기화 폴더 주의 - -## 출처 -Original skill from skills.cokac.com diff --git a/.claude/skills/flutter-ux-hardening/SKILL.md b/.claude/skills/flutter-ux-hardening/SKILL.md deleted file mode 100755 index eb84592..0000000 --- a/.claude/skills/flutter-ux-hardening/SKILL.md +++ /dev/null @@ -1,165 +0,0 @@ ---- -name: flutter-ux-hardening -description: Flutter 앱의 UI/UX를 계획, 구현, 테스트, 검토하고 반복적으로 강화하는 가이드 에이전트 스킬입니다. Flutter UI 개선, UX 강화, 접근성 개선 요청 시 활성화됩니다. -allowed-tools: Read, Write, Edit, Glob, Grep, Bash ---- - -# Flutter UX Hardening - -Flutter 앱의 UI/UX를 체계적으로 분석하고 개선하는 스킬입니다. - -## 기능 - -### 분석 영역 -- **UI 일관성**: 디자인 시스템 준수 여부 -- **UX 흐름**: 사용자 여정 최적화 -- **접근성**: a11y 지원 (스크린 리더, 색상 대비 등) -- **반응형**: 다양한 화면 크기 대응 -- **에지 케이스**: 빈 상태, 로딩, 에러 처리 - -### 강화 프로세스 - -1. **계획 (Plan)** - - 현재 UI/UX 감사 - - 개선 영역 식별 - - 우선순위 설정 - -2. **구현 (Implement)** - - 위젯 리팩토링 - - 애니메이션 추가 - - 접근성 속성 추가 - -3. **테스트 (Test)** - - 위젯 테스트 작성 - - 통합 테스트 추가 - - 골든 테스트 설정 - -4. **검토 (Review)** - - 코드 품질 검사 - - 성능 프로파일링 - - 사용자 피드백 반영 - -5. **반복 (Iterate)** - - 지속적 개선 - - A/B 테스트 적용 - -## 체크리스트 - -### 접근성 (Accessibility) -```dart -// ✅ Semantics 레이블 추가 -Semantics( - label: '장바구니에 추가', - child: IconButton( - icon: Icon(Icons.add_shopping_cart), - onPressed: _addToCart, - ), -) - -// ✅ 충분한 터치 타겟 크기 (48x48 이상) -SizedBox( - width: 48, - height: 48, - child: IconButton(...), -) - -// ✅ 색상 대비 확인 -// WCAG AA: 4.5:1 (일반 텍스트), 3:1 (큰 텍스트) -``` - -### 에지 케이스 처리 -```dart -// 빈 상태 -if (items.isEmpty) { - return EmptyStateWidget( - icon: Icons.inbox, - title: '항목이 없습니다', - subtitle: '새 항목을 추가해보세요', - action: ElevatedButton( - onPressed: _addItem, - child: Text('항목 추가'), - ), - ); -} - -// 로딩 상태 -if (isLoading) { - return ShimmerLoadingWidget(); -} - -// 에러 상태 -if (hasError) { - return ErrorWidget( - message: error.message, - onRetry: _retry, - ); -} -``` - -### 반응형 레이아웃 -```dart -LayoutBuilder( - builder: (context, constraints) { - if (constraints.maxWidth > 900) { - return DesktopLayout(); - } else if (constraints.maxWidth > 600) { - return TabletLayout(); - } - return MobileLayout(); - }, -) -``` - -### 애니메이션 -```dart -// 부드러운 전환 -AnimatedContainer( - duration: Duration(milliseconds: 300), - curve: Curves.easeInOut, - // ... -) - -// 히어로 애니메이션 -Hero( - tag: 'item-${item.id}', - child: ItemCard(item: item), -) -``` - -## 리포트 구조 - -```markdown -# Flutter UX 강화 리포트 - -## 현재 상태 분석 -- UI 일관성: ⭐⭐⭐☆☆ -- 접근성: ⭐⭐☆☆☆ -- 반응형: ⭐⭐⭐⭐☆ -- 에지 케이스 처리: ⭐⭐☆☆☆ - -## 식별된 개선 영역 -1. 접근성 레이블 누락 (15개 위젯) -2. 빈 상태 UI 미구현 (3개 화면) -3. 로딩 스켈레톤 미적용 (5개 리스트) - -## 권장 조치 -| 우선순위 | 영역 | 조치 | -|----------|------|------| -| 높음 | 접근성 | Semantics 추가 | -| 높음 | 에러 | 에러 바운더리 구현 | -| 중간 | 로딩 | Shimmer 효과 추가 | - -## 구현 계획 -... -``` - -## 사용 예시 - -``` -Flutter 앱의 UX를 개선해줘 -접근성을 강화하고 싶어 -빈 상태와 에러 처리를 추가해줘 -``` - -## 출처 -Original skill from skills.cokac.com diff --git a/.claude/skills/frontend-design/SKILL.md b/.claude/skills/frontend-design/SKILL.md deleted file mode 100644 index 5be498e..0000000 --- a/.claude/skills/frontend-design/SKILL.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -name: frontend-design -description: Create distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, artifacts, posters, or applications (examples include websites, landing pages, dashboards, React components, HTML/CSS layouts, or when styling/beautifying any web UI). Generates creative, polished code and UI design that avoids generic AI aesthetics. -license: Complete terms in LICENSE.txt ---- - -This skill guides creation of distinctive, production-grade frontend interfaces that avoid generic "AI slop" aesthetics. Implement real working code with exceptional attention to aesthetic details and creative choices. - -The user provides frontend requirements: a component, page, application, or interface to build. They may include context about the purpose, audience, or technical constraints. - -## Design Thinking - -Before coding, understand the context and commit to a BOLD aesthetic direction: -- **Purpose**: What problem does this interface solve? Who uses it? -- **Tone**: Pick an extreme: brutally minimal, maximalist chaos, retro-futuristic, organic/natural, luxury/refined, playful/toy-like, editorial/magazine, brutalist/raw, art deco/geometric, soft/pastel, industrial/utilitarian, etc. There are so many flavors to choose from. Use these for inspiration but design one that is true to the aesthetic direction. -- **Constraints**: Technical requirements (framework, performance, accessibility). -- **Differentiation**: What makes this UNFORGETTABLE? What's the one thing someone will remember? - -**CRITICAL**: Choose a clear conceptual direction and execute it with precision. Bold maximalism and refined minimalism both work - the key is intentionality, not intensity. - -Then implement working code (HTML/CSS/JS, React, Vue, etc.) that is: -- Production-grade and functional -- Visually striking and memorable -- Cohesive with a clear aesthetic point-of-view -- Meticulously refined in every detail - -## Frontend Aesthetics Guidelines - -Focus on: -- **Typography**: Choose fonts that are beautiful, unique, and interesting. Avoid generic fonts like Arial and Inter; opt instead for distinctive choices that elevate the frontend's aesthetics; unexpected, characterful font choices. Pair a distinctive display font with a refined body font. -- **Color & Theme**: Commit to a cohesive aesthetic. Use CSS variables for consistency. Dominant colors with sharp accents outperform timid, evenly-distributed palettes. -- **Motion**: Use animations for effects and micro-interactions. Prioritize CSS-only solutions for HTML. Use Motion library for React when available. Focus on high-impact moments: one well-orchestrated page load with staggered reveals (animation-delay) creates more delight than scattered micro-interactions. Use scroll-triggering and hover states that surprise. -- **Spatial Composition**: Unexpected layouts. Asymmetry. Overlap. Diagonal flow. Grid-breaking elements. Generous negative space OR controlled density. -- **Backgrounds & Visual Details**: Create atmosphere and depth rather than defaulting to solid colors. Add contextual effects and textures that match the overall aesthetic. Apply creative forms like gradient meshes, noise textures, geometric patterns, layered transparencies, dramatic shadows, decorative borders, custom cursors, and grain overlays. - -NEVER use generic AI-generated aesthetics like overused font families (Inter, Roboto, Arial, system fonts), cliched color schemes (particularly purple gradients on white backgrounds), predictable layouts and component patterns, and cookie-cutter design that lacks context-specific character. - -Interpret creatively and make unexpected choices that feel genuinely designed for the context. No design should be the same. Vary between light and dark themes, different fonts, different aesthetics. NEVER converge on common choices (Space Grotesk, for example) across generations. - -**IMPORTANT**: Match implementation complexity to the aesthetic vision. Maximalist designs need elaborate code with extensive animations and effects. Minimalist or refined designs need restraint, precision, and careful attention to spacing, typography, and subtle details. Elegance comes from executing the vision well. - -Remember: Claude is capable of extraordinary creative work. Don't hold back, show what can truly be created when thinking outside the box and committing fully to a distinctive vision. diff --git a/.claude/skills/insecure-defaults/SKILL.md b/.claude/skills/insecure-defaults/SKILL.md deleted file mode 100644 index d1b29cd..0000000 --- a/.claude/skills/insecure-defaults/SKILL.md +++ /dev/null @@ -1,117 +0,0 @@ ---- -name: insecure-defaults -description: "Detects fail-open insecure defaults (hardcoded secrets, weak auth, permissive security) that allow apps to run insecurely in production. Use when auditing security, reviewing config management, or analyzing environment variable handling." -allowed-tools: - - Read - - Grep - - Glob - - Bash ---- - -# Insecure Defaults Detection - -Finds **fail-open** vulnerabilities where apps run insecurely with missing configuration. Distinguishes exploitable defaults from fail-secure patterns that crash safely. - -- **Fail-open (CRITICAL):** `SECRET = env.get('KEY') or 'default'` → App runs with weak secret -- **Fail-secure (SAFE):** `SECRET = env['KEY']` → App crashes if missing - -## When to Use - -- **Security audits** of production applications (auth, crypto, API security) -- **Configuration review** of deployment files, IaC templates, Docker configs -- **Code review** of environment variable handling and secrets management -- **Pre-deployment checks** for hardcoded credentials or weak defaults - -## When NOT to Use - -Do not use this skill for: -- **Test fixtures** explicitly scoped to test environments (files in `test/`, `spec/`, `__tests__/`) -- **Example/template files** (`.example`, `.template`, `.sample` suffixes) -- **Development-only tools** (local Docker Compose for dev, debug scripts) -- **Documentation examples** in README.md or docs/ directories -- **Build-time configuration** that gets replaced during deployment -- **Crash-on-missing behavior** where app won't start without proper config (fail-secure) - -When in doubt: trace the code path to determine if the app runs with the default or crashes. - -## Rationalizations to Reject - -- **"It's just a development default"** → If it reaches production code, it's a finding -- **"The production config overrides it"** → Verify prod config exists; code-level vulnerability remains if not -- **"This would never run without proper config"** → Prove it with code trace; many apps fail silently -- **"It's behind authentication"** → Defense in depth; compromised session still exploits weak defaults -- **"We'll fix it before release"** → Document now; "later" rarely comes - -## Workflow - -Follow this workflow for every potential finding: - -### 1. SEARCH: Perform Project Discovery and Find Insecure Defaults - -Determine language, framework, and project conventions. Use this information to further discover things like secret storage locations, secret usage patterns, credentialed third-party integrations, cryptography, and any other relevant configuration. Further use information to analyze insecure default configurations. - -**Example** -Search for patterns in `**/config/`, `**/auth/`, `**/database/`, and env files: -- **Fallback secrets:** `getenv.*\) or ['"]`, `process\.env\.[A-Z_]+ \|\| ['"]`, `ENV\.fetch.*default:` -- **Hardcoded credentials:** `password.*=.*['"][^'"]{8,}['"]`, `api[_-]?key.*=.*['"][^'"]+['"]` -- **Weak defaults:** `DEBUG.*=.*true`, `AUTH.*=.*false`, `CORS.*=.*\*` -- **Crypto algorithms:** `MD5|SHA1|DES|RC4|ECB` in security contexts - -Tailor search approach based on discovery results. - -Focus on production-reachable code, not test fixtures or example files. - -### 2. VERIFY: Actual Behavior -For each match, trace the code path to understand runtime behavior. - -**Questions to answer:** -- When is this code executed? (Startup vs. runtime) -- What happens if a configuration variable is missing? -- Is there validation that enforces secure configuration? - -### 3. CONFIRM: Production Impact -Determine if this issue reaches production: - -If production config provides the variable → Lower severity (but still a code-level vulnerability) -If production config missing or uses default → CRITICAL - -### 4. REPORT: with Evidence - -**Example report:** -``` -Finding: Hardcoded JWT Secret Fallback -Location: src/auth/jwt.ts:15 -Pattern: const secret = process.env.JWT_SECRET || 'default'; - -Verification: App starts without JWT_SECRET; secret used in jwt.sign() at line 42 -Production Impact: Dockerfile missing JWT_SECRET -Exploitation: Attacker forges JWTs using 'default', gains unauthorized access -``` - -## Quick Verification Checklist - -**Fallback Secrets:** `SECRET = env.get(X) or Y` -→ Verify: App starts without env var? Secret used in crypto/auth? -→ Skip: Test fixtures, example files - -**Default Credentials:** Hardcoded `username`/`password` pairs -→ Verify: Active in deployed config? No runtime override? -→ Skip: Disabled accounts, documentation examples - -**Fail-Open Security:** `AUTH_REQUIRED = env.get(X, 'false')` -→ Verify: Default is insecure (false/disabled/permissive)? -→ Safe: App crashes or default is secure (true/enabled/restricted) - -**Weak Crypto:** MD5/SHA1/DES/RC4/ECB in security contexts -→ Verify: Used for passwords, encryption, or tokens? -→ Skip: Checksums, non-security hashing - -**Permissive Access:** CORS `*`, permissions `0777`, public-by-default -→ Verify: Default allows unauthorized access? -→ Skip: Explicitly configured permissiveness with justification - -**Debug Features:** Stack traces, introspection, verbose errors -→ Verify: Enabled by default? Exposed in responses? -→ Skip: Logging-only, not user-facing - -For detailed examples and counter-examples, see [examples.md](references/examples.md). diff --git a/.claude/skills/layer-boundary-auditor/SKILL.md b/.claude/skills/layer-boundary-auditor/SKILL.md deleted file mode 100644 index 5c3ddce..0000000 --- a/.claude/skills/layer-boundary-auditor/SKILL.md +++ /dev/null @@ -1,323 +0,0 @@ ---- -name: ln-642-layer-boundary-auditor -description: "L3 Worker. Audits layer boundaries + cross-layer consistency: I/O violations, transaction boundaries (commit ownership), session ownership (DI vs local), async consistency (sync I/O in async), fire-and-forget tasks." ---- - -# Layer Boundary Auditor - -L3 Worker that audits architectural layer boundaries and detects violations. - -## Purpose & Scope - -- Read architecture.md to discover project's layer structure -- Detect layer violations (I/O code outside infrastructure layer) -- **Detect cross-layer consistency issues:** - - Transaction boundaries (commit/rollback ownership) - - Session ownership (DI vs local) - - Async consistency (sync I/O in async) - - Fire-and-forget tasks (unhandled exceptions) -- Check pattern coverage (all HTTP calls use client abstraction) -- Detect error handling duplication -- Return violations list to coordinator - -## Input (from ln-640) - -``` -- architecture_path: string # Path to docs/architecture.md -- codebase_root: string # Root directory to scan -- skip_violations: string[] # Files to skip (legacy) -``` - -## Workflow - -### Phase 1: Discover Architecture - -``` -Read docs/architecture.md - -Extract from Section 4.2 (Top-Level Decomposition): - - architecture_type: "Layered" | "Hexagonal" | "Clean" | "MVC" | etc. - - layers: [{name, directories[], purpose}] - -Extract from Section 5.3 (Infrastructure Layer Components): - - infrastructure_components: [{name, responsibility}] - -IF architecture.md not found: - Use fallback presets from common_patterns.md - -Build ruleset: - FOR EACH layer: - allowed_deps = layers that can be imported - forbidden_deps = layers that cannot be imported -``` - -### Phase 2: Detect Layer Violations - -``` -FOR EACH violation_type IN common_patterns.md I/O Pattern Boundary Rules: - grep_pattern = violation_type.detection_grep - forbidden_dirs = violation_type.forbidden_in - - matches = Grep(grep_pattern, codebase_root, include="*.py,*.ts,*.js") - - FOR EACH match IN matches: - IF match.path NOT IN skip_violations: - IF any(forbidden IN match.path FOR forbidden IN forbidden_dirs): - violations.append({ - type: "layer_violation", - severity: "HIGH", - pattern: violation_type.name, - file: match.path, - line: match.line, - code: match.context, - allowed_in: violation_type.allowed_in, - suggestion: f"Move to {violation_type.allowed_in}" - }) -``` - -### Phase 2.5: Cross-Layer Consistency Checks - -#### 2.5.1 Transaction Boundary Violations - -**What:** commit()/rollback() called at inconsistent layers (repo + service + API) - -**Detection:** -``` -repo_commits = Grep("\.commit\(\)|\.rollback\(\)", "**/repositories/**/*.py") -service_commits = Grep("\.commit\(\)|\.rollback\(\)", "**/services/**/*.py") -api_commits = Grep("\.commit\(\)|\.rollback\(\)", "**/api/**/*.py") - -layers_with_commits = count([repo_commits, service_commits, api_commits].filter(len > 0)) -``` - -**Safe Patterns (ignore):** -- Comment "# best-effort telemetry" in same context -- File ends with `_callbacks.py` (progress notifiers) -- Explicit `# UoW boundary` comment - -**Violation Rules:** - -| Condition | Severity | Issue | -|-----------|----------|-------| -| layers_with_commits >= 3 | CRITICAL | Mixed UoW ownership across all layers | -| repo + api commits | HIGH | Transaction control bypasses service layer | -| repo + service commits | HIGH | Ambiguous UoW owner (repo vs service) | -| service + api commits | MEDIUM | Transaction control spans service + API | - -**Recommendation:** Choose single UoW owner (service layer recommended), remove commit() from other layers - -**Effort:** L (requires architectural decision + refactoring) - -#### 2.5.2 Session Ownership Violations - -**What:** Mixed DI-injected and locally-created sessions in same call chain - -**Detection:** -``` -di_session = Grep("Depends\(get_session\)|Depends\(get_db\)", "**/api/**/*.py") -local_session = Grep("AsyncSessionLocal\(\)|async_sessionmaker", "**/services/**/*.py") -local_in_repo = Grep("AsyncSessionLocal\(\)", "**/repositories/**/*.py") -``` - -**Violation Rules:** - -| Condition | Severity | Issue | -|-----------|----------|-------| -| di_session AND local_in_repo in same module | HIGH | Repo creates own session while API injects different | -| local_session in service calling DI-based repo | MEDIUM | Session mismatch in call chain | - -**Recommendation:** Use DI consistently OR use local sessions consistently. Document exceptions (e.g., telemetry) - -**Effort:** M - -#### 2.5.3 Async Consistency Violations - -**What:** Synchronous blocking I/O inside async functions - -**Detection:** -``` -# For each file with "async def": -sync_file_io = Grep("\.read_bytes\(\)|\.read_text\(\)|\.write_bytes\(\)|\.write_text\(\)", file) -sync_open = Grep("(? 0: - coverage = len(abstracted_calls) / len(all_http_calls) * 100 - IF coverage < 90%: - violations.append({ - type: "low_coverage", - severity: "MEDIUM", - pattern: "HTTP Client Abstraction", - coverage: coverage, - uncovered_files: files with direct calls outside infrastructure - }) - -# Error Handling Duplication -http_error_handlers = Grep("except\\s+(httpx\\.|aiohttp\\.|requests\\.)", codebase_root) -unique_files = set(f.path for f in http_error_handlers) - -IF len(unique_files) > 2: - violations.append({ - type: "duplication", - severity: "MEDIUM", - pattern: "HTTP Error Handling", - files: list(unique_files), - suggestion: "Centralize in infrastructure layer" - }) -``` - -### Phase 3.5: Calculate Score - -See `shared/references/audit_scoring.md` for unified formula and score interpretation. - -### Phase 4: Return Result - -```json -{ - "category": "Layer Boundary", - "score": 4.5, - "total_issues": 8, - "critical": 1, - "high": 3, - "medium": 4, - "low": 0, - "architecture": { - "type": "Layered", - "layers": ["api", "services", "domain", "infrastructure"] - }, - "checks": [ - {"id": "io_isolation", "name": "I/O Isolation", "status": "failed", "details": "HTTP client found in domain layer"}, - {"id": "http_abstraction", "name": "HTTP Abstraction", "status": "warning", "details": "75% coverage, 3 direct calls outside infrastructure"}, - {"id": "error_centralization", "name": "Error Centralization", "status": "failed", "details": "HTTP error handlers in 4 files, should be centralized"}, - {"id": "transaction_boundary", "name": "Transaction Boundary", "status": "failed", "details": "commit() in repos (3), services (2), api (4) - mixed UoW ownership"}, - {"id": "session_ownership", "name": "Session Ownership", "status": "passed", "details": "DI-based sessions used consistently"}, - {"id": "async_consistency", "name": "Async Consistency", "status": "failed", "details": "Blocking I/O in async functions detected"}, - {"id": "fire_and_forget", "name": "Fire-and-Forget Handling", "status": "warning", "details": "2 tasks without error handlers"} - ], - "findings": [ - { - "severity": "CRITICAL", - "location": "app/", - "issue": "Mixed UoW ownership: commit() found in repositories (3), services (2), api (4)", - "principle": "Layer Boundary / Transaction Control", - "recommendation": "Choose single UoW owner (service layer recommended), remove commit() from other layers", - "effort": "L" - }, - { - "severity": "HIGH", - "location": "app/services/job/service.py:45", - "issue": "Blocking file I/O in async: Path.read_bytes() inside async def process_job()", - "principle": "Layer Boundary / Async Consistency", - "recommendation": "Use asyncio.to_thread(path.read_bytes) or aiofiles", - "effort": "S" - }, - { - "severity": "HIGH", - "location": "app/domain/pdf/parser.py:45", - "issue": "Layer violation: HTTP client used in domain layer", - "principle": "Layer Boundary / I/O Isolation", - "recommendation": "Move httpx.AsyncClient to infrastructure/http/clients/", - "effort": "M" - }, - { - "severity": "MEDIUM", - "location": "app/api/v1/jobs.py:78", - "issue": "Fire-and-forget task without error handler: create_task(notify_user())", - "principle": "Layer Boundary / Task Error Handling", - "recommendation": "Add task.add_done_callback(handle_exception) or document with # fire-and-forget comment", - "effort": "S" - } - ], - "coverage": { - "http_abstraction": 75, - "error_centralization": false, - "transaction_boundary_consistent": false, - "session_ownership_consistent": true, - "async_io_consistent": false, - "fire_and_forget_handled": false - } -} -``` - -## Critical Rules - -- **Read architecture.md first** - never assume architecture type -- **Skip violations list** - respect legacy files marked for gradual fix -- **File + line + code** - always provide exact location with context -- **Actionable suggestions** - always tell WHERE to move the code -- **No false positives** - verify path contains forbidden dir, not just substring - -## Definition of Done - -- Architecture discovered from docs/architecture.md (or fallback used) -- All violation types from common_patterns.md checked -- **Cross-layer consistency checked:** - - Transaction boundaries analyzed (commit/rollback distribution) - - Session ownership analyzed (DI vs local) - - Async consistency analyzed (sync I/O in async functions) - - Fire-and-forget tasks analyzed (error handling) -- Coverage calculated for HTTP abstraction + 4 consistency metrics -- Violations list with severity, location, suggestion -- Summary counts returned to coordinator - -## Reference Files - -- Layer rules: `../ln-640-pattern-evolution-auditor/references/common_patterns.md` -- Scoring impact: `../ln-640-pattern-evolution-auditor/references/scoring_rules.md` - ---- - -**Version:** 2.0.0 -**Last Updated:** 2026-02-04 diff --git a/.claude/skills/node-debug-logging-middleware/SKILL.md b/.claude/skills/node-debug-logging-middleware/SKILL.md deleted file mode 100755 index 72c297f..0000000 --- a/.claude/skills/node-debug-logging-middleware/SKILL.md +++ /dev/null @@ -1,223 +0,0 @@ ---- -name: node-debug-logging-middleware -description: Node.js Express/Koa 앱에 상세 디버깅 로그를 출력하는 미들웨어를 추가합니다. 미들웨어 로깅, 요청 추적, Node.js 디버깅 요청 시 활성화됩니다. -allowed-tools: Read, Write, Edit, Glob, Grep, Bash ---- - -# Node Debug Logging Middleware - -Node.js 웹 애플리케이션에 상세한 디버깅 로그 미들웨어를 추가하는 스킬입니다. - -## 기능 - -### 로깅 대상 -- HTTP 요청/응답 상세 정보 -- 요청 바디 및 쿼리 파라미터 -- 응답 시간 및 상태 코드 -- 에러 스택 트레이스 -- 메모리 사용량 - -### 지원 프레임워크 -- Express.js -- Koa.js -- Fastify -- NestJS - -### 출력 옵션 -- 콘솔 (개발환경) -- 파일 (프로덕션) -- 외부 서비스 (Sentry, Datadog 등) - -## Express.js 미들웨어 - -```javascript -const fs = require('fs'); -const path = require('path'); - -/** - * 디버그 로깅 미들웨어 생성 - * @param {Object} options - 설정 옵션 - * @param {string} options.logDir - 로그 파일 디렉토리 - * @param {boolean} options.logBody - 요청/응답 바디 로깅 여부 - * @param {boolean} options.logHeaders - 헤더 로깅 여부 - * @param {number} options.bodyLimit - 바디 로깅 최대 크기 (bytes) - */ -function createDebugLogger(options = {}) { - const { - logDir = './logs', - logBody = true, - logHeaders = false, - bodyLimit = 10000 - } = options; - - // 로그 디렉토리 생성 - if (!fs.existsSync(logDir)) { - fs.mkdirSync(logDir, { recursive: true }); - } - - const getLogStream = () => { - const date = new Date().toISOString().split('T')[0]; - const logFile = path.join(logDir, `debug-${date}.log`); - return fs.createWriteStream(logFile, { flags: 'a' }); - }; - - return (req, res, next) => { - const startTime = Date.now(); - const requestId = Math.random().toString(36).substring(7); - - // 요청 정보 수집 - const requestLog = { - id: requestId, - timestamp: new Date().toISOString(), - method: req.method, - url: req.originalUrl, - ip: req.ip || req.connection.remoteAddress, - userAgent: req.get('User-Agent') - }; - - if (logHeaders) { - requestLog.headers = req.headers; - } - - if (logBody && req.body && Object.keys(req.body).length > 0) { - const bodyStr = JSON.stringify(req.body); - requestLog.body = bodyStr.length > bodyLimit - ? bodyStr.substring(0, bodyLimit) + '...[truncated]' - : req.body; - } - - // 응답 가로채기 - const originalSend = res.send; - let responseBody; - - res.send = function(body) { - responseBody = body; - return originalSend.call(this, body); - }; - - // 응답 완료 시 로깅 - res.on('finish', () => { - const duration = Date.now() - startTime; - - const logEntry = { - ...requestLog, - response: { - statusCode: res.statusCode, - duration: `${duration}ms`, - contentLength: res.get('Content-Length') - } - }; - - if (logBody && responseBody && res.statusCode >= 400) { - try { - const bodyStr = typeof responseBody === 'string' - ? responseBody - : JSON.stringify(responseBody); - logEntry.response.body = bodyStr.length > bodyLimit - ? bodyStr.substring(0, bodyLimit) + '...[truncated]' - : responseBody; - } catch (e) { - // 바디 파싱 실패 무시 - } - } - - // 콘솔 출력 - const color = res.statusCode >= 500 ? '\x1b[31m' - : res.statusCode >= 400 ? '\x1b[33m' - : '\x1b[32m'; - console.log( - `${color}[${requestLog.timestamp}] ${req.method} ${req.originalUrl} ${res.statusCode} ${duration}ms\x1b[0m` - ); - - // 파일 출력 - const stream = getLogStream(); - stream.write(JSON.stringify(logEntry) + '\n'); - stream.end(); - }); - - next(); - }; -} - -module.exports = createDebugLogger; -``` - -## 사용법 - -```javascript -const express = require('express'); -const createDebugLogger = require('./middleware/debugLogger'); - -const app = express(); - -// JSON 파싱 (로깅 전에 설정) -app.use(express.json()); - -// 디버그 로거 적용 -app.use(createDebugLogger({ - logDir: './logs', - logBody: true, - logHeaders: process.env.NODE_ENV === 'development', - bodyLimit: 5000 -})); - -// 라우트 정의 -app.get('/api/users', (req, res) => { - res.json({ users: [] }); -}); - -app.listen(3000); -``` - -## Koa.js 버전 - -```javascript -async function koaDebugLogger(ctx, next) { - const startTime = Date.now(); - - console.log(`--> ${ctx.method} ${ctx.url}`); - - try { - await next(); - } catch (err) { - console.error(`[ERROR] ${err.message}`); - throw err; - } - - const duration = Date.now() - startTime; - console.log(`<-- ${ctx.method} ${ctx.url} ${ctx.status} ${duration}ms`); -} - -// 사용 -app.use(koaDebugLogger); -``` - -## 로그 출력 예시 - -```json -{ - "id": "a1b2c3", - "timestamp": "2024-01-15T10:30:45.123Z", - "method": "POST", - "url": "/api/users", - "ip": "192.168.1.100", - "userAgent": "Mozilla/5.0...", - "body": { "name": "홍길동", "email": "hong@example.com" }, - "response": { - "statusCode": 201, - "duration": "45ms", - "contentLength": "156" - } -} -``` - -## 사용 예시 - -``` -Express 앱에 요청 로깅 미들웨어를 추가해줘 -API 호출을 디버깅하기 위한 로그를 설정해줘 -Node.js 서버에 상세 로깅을 추가해줘 -``` - -## 출처 -Original skill from skills.cokac.com diff --git a/.claude/skills/npm-release-manager/SKILL.md b/.claude/skills/npm-release-manager/SKILL.md deleted file mode 100755 index ef78823..0000000 --- a/.claude/skills/npm-release-manager/SKILL.md +++ /dev/null @@ -1,129 +0,0 @@ ---- -name: npm-release-manager -description: NPM 패키지 배포 프로세스를 자동화합니다. 버전 관리, changelog 생성, npm 배포 요청 시 활성화됩니다. -allowed-tools: Read, Write, Edit, Glob, Grep, Bash ---- - -# NPM Release Manager - -NPM 패키지의 릴리스 프로세스를 자동화하는 스킬입니다. - -## 기능 - -### 릴리스 워크플로우 -1. **변경 사항 분석**: git diff로 변경 내용 파악 -2. **버전 결정**: Semantic Versioning (major.minor.patch) -3. **Changelog 생성**: 커밋 메시지 기반 자동 생성 -4. **버전 범프**: package.json 버전 업데이트 -5. **Git 태그**: 버전 태그 생성 -6. **NPM 배포**: npm publish 실행 - -### Semantic Versioning 가이드 -- **Major (x.0.0)**: 호환되지 않는 API 변경 -- **Minor (0.x.0)**: 하위 호환 기능 추가 -- **Patch (0.0.x)**: 하위 호환 버그 수정 - -### Changelog 형식 -```markdown -# Changelog - -## [1.2.0] - 2024-01-15 - -### Added -- 새로운 기능 A 추가 -- 새로운 기능 B 추가 - -### Changed -- 기존 기능 C 개선 - -### Fixed -- 버그 D 수정 -- 버그 E 수정 - -### Deprecated -- 기능 F 지원 중단 예정 - -### Removed -- 기능 G 제거 - -### Security -- 보안 취약점 H 패치 -``` - -## 릴리스 프로세스 - -### 1. 사전 검사 -```bash -# 테스트 실행 -npm test - -# 린트 검사 -npm run lint - -# 빌드 확인 -npm run build -``` - -### 2. 버전 업데이트 -```bash -# patch 릴리스 -npm version patch - -# minor 릴리스 -npm version minor - -# major 릴리스 -npm version major -``` - -### 3. Changelog 업데이트 -- 커밋 메시지 분석 -- 카테고리별 분류 -- CHANGELOG.md 업데이트 - -### 4. 배포 -```bash -# npm 로그인 확인 -npm whoami - -# 배포 -npm publish - -# 또는 스코프 패키지 -npm publish --access public -``` - -### 5. Git 푸시 -```bash -git push origin main --tags -``` - -## 커밋 메시지 규약 - -Conventional Commits 형식 권장: -- `feat:` 새로운 기능 (minor) -- `fix:` 버그 수정 (patch) -- `docs:` 문서 변경 -- `style:` 코드 스타일 변경 -- `refactor:` 리팩토링 -- `test:` 테스트 추가/수정 -- `chore:` 빌드/도구 변경 -- `BREAKING CHANGE:` 호환되지 않는 변경 (major) - -## 사용 예시 - -``` -새 버전을 배포해줘 -패치 릴리스를 진행해줘 -changelog를 업데이트하고 npm에 배포해줘 -``` - -## 주의사항 - -- npm 로그인 상태 확인 필요 -- 2FA 설정 시 OTP 입력 필요 -- private 패키지는 유료 계정 필요 -- 배포 전 테스트 통과 필수 - -## 출처 -Original skill from skills.cokac.com diff --git a/.claude/skills/observability-auditor/SKILL.md b/.claude/skills/observability-auditor/SKILL.md deleted file mode 100644 index 0ea24aa..0000000 --- a/.claude/skills/observability-auditor/SKILL.md +++ /dev/null @@ -1,134 +0,0 @@ ---- -name: ln-627-observability-auditor -description: Observability audit worker (L3). Checks structured logging, health check endpoints, metrics collection, request tracing, log levels. Returns findings with severity, location, effort, recommendations. -allowed-tools: Read, Grep, Glob, Bash ---- - -# Observability Auditor (L3 Worker) - -Specialized worker auditing logging, monitoring, and observability. - -## Purpose & Scope - -- **Worker in ln-620 coordinator pipeline** -- Audit **observability** (Category 10: Medium Priority) -- Check logging, health checks, metrics, tracing -- Calculate compliance score (X/10) - -## Inputs (from Coordinator) - -Receives `contextStore` with tech stack, framework, codebase root. - -## Workflow - -1) Parse context -2) Check observability patterns -3) Collect findings -4) Calculate score -5) Return JSON - -## Audit Rules - -### 1. Structured Logging -**Detection:** -- Grep for `console.log` (unstructured) -- Check for proper logger: winston, pino, logrus, zap - -**Severity:** -- **MEDIUM:** Production code using console.log -- **LOW:** Dev code using console.log - -**Recommendation:** Use structured logger (winston, pino) - -**Effort:** M (add logger, replace calls) - -### 2. Health Check Endpoints -**Detection:** -- Grep for `/health`, `/ready`, `/live` routes -- Check API route definitions - -**Severity:** -- **HIGH:** No health check endpoint (monitoring blind spot) - -**Recommendation:** Add `/health` endpoint - -**Effort:** S (add simple route) - -### 3. Metrics Collection -**Detection:** -- Check for Prometheus client, StatsD, CloudWatch -- Grep for metric recording: `histogram`, `counter` - -**Severity:** -- **MEDIUM:** No metrics instrumentation - -**Recommendation:** Add Prometheus metrics - -**Effort:** M (instrument code) - -### 4. Request Tracing -**Detection:** -- Check for correlation IDs in logs -- Verify trace propagation (OpenTelemetry, Zipkin) - -**Severity:** -- **MEDIUM:** No correlation IDs (hard to debug distributed systems) - -**Recommendation:** Add request ID middleware - -**Effort:** M (add middleware, propagate IDs) - -### 5. Log Levels -**Detection:** -- Check if logger supports levels (info, warn, error, debug) -- Verify proper level usage - -**Severity:** -- **LOW:** Only error logging (insufficient visibility) - -**Recommendation:** Add info/debug logs - -**Effort:** S (add log statements) - -## Scoring Algorithm - -See `shared/references/audit_scoring.md` for unified formula and score interpretation. - -## Output Format - -```json -{ - "category": "Observability", - "score": 6, - "total_issues": 5, - "critical": 0, - "high": 1, - "medium": 3, - "low": 1, - "checks": [ - {"id": "structured_logging", "name": "Structured Logging", "status": "warning", "details": "3 console.log calls in production code"}, - {"id": "health_endpoints", "name": "Health Endpoints", "status": "failed", "details": "No /health endpoint found"}, - {"id": "metrics_collection", "name": "Metrics Collection", "status": "passed", "details": "Prometheus client configured"}, - {"id": "request_tracing", "name": "Request Tracing", "status": "warning", "details": "Correlation IDs missing in 2 services"} - ], - "findings": [ - { - "severity": "HIGH", - "location": "src/api/server.ts", - "issue": "No /health endpoint for monitoring", - "principle": "Observability / Health Checks", - "recommendation": "Add GET /health route returning { status: 'ok', uptime, ... }", - "effort": "S" - } - ] -} -``` - -## Reference Files - -- **Audit scoring formula:** `shared/references/audit_scoring.md` -- **Audit output schema:** `shared/references/audit_output_schema.md` - ---- -**Version:** 3.0.0 -**Last Updated:** 2025-12-23 diff --git a/.claude/skills/pdf-template-skill/SKILL.md b/.claude/skills/pdf-template-skill/SKILL.md deleted file mode 100755 index 243c834..0000000 --- a/.claude/skills/pdf-template-skill/SKILL.md +++ /dev/null @@ -1,211 +0,0 @@ -# PDF Template Skill - -PDF 문서의 구조와 디자인을 분석하여 동일한 형태의 PPTX 템플릿을 생성하는 기술입니다. - -## 📋 기능 개요 - -### 핵심 기능 -- **PDF 구조 분석**: 레이아웃, 폰트, 색상, 위치 정보 추출 -- **템플릿 생성**: PDF와 동일한 형태의 PPTX 템플릿 제작 -- **내용 교체**: 기존 내용을 새로운 데이터로 자동 교체 -- **형태 보존**: 원본 PDF의 디자인과 레이아웃 완벽 유지 - -### 지원 기능 -- 다중 슬라이드 템플릿 추출 -- 테이블 구조 인식 및 재생성 -- 이미지 및 로고 영역 매핑 -- 폰트 및 색상 스타일 보존 - -## 🚀 사용법 - -### 1. PDF 템플릿 분석 -```bash -node .claude/skills/pdf-template-skill/scripts/analyze-template.js \ - --input pdf_sample/sample.pdf \ - --output templates/sample_template.json -``` - -### 2. 내용 교체로 PPTX 생성 -```bash -node .claude/skills/pdf-template-skill/scripts/generate-from-template.js \ - --template templates/sample_template.json \ - --data project_data.json \ - --output result.pptx -``` - -### 3. 통합 실행 -```bash -npm run template-extract # PDF → 템플릿 추출 -npm run template-generate # 템플릿 → PPTX 생성 -``` - -## 📊 템플릿 구조 - -### Template JSON Format -```json -{ - "metadata": { - "source": "sample.pdf", - "pages": 5, - "extractedAt": "2025-01-03", - "title": "SAM ERP 견적서" - }, - "slides": [ - { - "slideNumber": 1, - "type": "cover", - "layout": { - "width": 10, - "height": 7.5 - }, - "elements": [ - { - "type": "text", - "id": "title", - "position": { "x": 1, "y": 3, "w": 8, "h": 1.5 }, - "style": { - "fontSize": 36, - "bold": true, - "color": "0066CC", - "align": "center" - }, - "placeholder": "{{title}}", - "defaultValue": "견적서" - } - ] - } - ], - "styles": { - "colors": { - "primary": "0066CC", - "secondary": "333333", - "accent": "00AA00" - }, - "fonts": { - "title": { "face": "Arial", "size": 24, "bold": true }, - "body": { "face": "Arial", "size": 12 } - } - }, - "variables": { - "title": "string", - "company": "string", - "date": "date", - "items": "array" - } -} -``` - -### Data Input Format -```json -{ - "title": "스마트 재고 관리 시스템", - "company": "(주) 테크솔루션", - "date": "2025-01-03", - "client": { - "name": "고객사명", - "site": "프로젝트명", - "address": "주소" - }, - "items": [ - { - "name": "상품명", - "quantity": 1, - "price": 1000000, - "description": "상세 설명" - } - ] -} -``` - -## 🔧 고급 기능 - -### 1. 동적 테이블 생성 -```javascript -// 테이블 구조 정의 -{ - "type": "table", - "id": "itemsTable", - "template": { - "headers": ["번호", "품명", "수량", "단가", "금액"], - "rowTemplate": "{{index}}, {{name}}, {{quantity}}, {{price}}, {{total}}", - "dataSource": "items" - } -} -``` - -### 2. 조건부 콘텐츠 -```javascript -// 조건부 표시 -{ - "type": "conditional", - "condition": "items.length > 10", - "ifTrue": { /* 대용량 테이블 레이아웃 */ }, - "ifFalse": { /* 기본 테이블 레이아웃 */ } -} -``` - -### 3. 계산 필드 -```javascript -// 자동 계산 -{ - "type": "calculated", - "formula": "SUM(items.price * items.quantity)", - "format": "currency" -} -``` - -## 📋 워크플로우 - -### Phase 1: PDF 분석 -1. **페이지 분할**: PDF를 개별 페이지로 분리 -2. **요소 인식**: 텍스트, 이미지, 테이블, 도형 위치 추출 -3. **스타일 분석**: 폰트, 색상, 크기 정보 수집 -4. **구조 매핑**: 논리적 구조와 시각적 배치 연결 - -### Phase 2: 템플릿 생성 -1. **변수 식별**: 교체 가능한 콘텐츠 영역 표시 -2. **레이아웃 정의**: 고정 요소와 가변 요소 분리 -3. **스타일 시트**: 일관된 디자인 규칙 정의 -4. **검증**: 생성된 템플릿의 정확성 확인 - -### Phase 3: 콘텐츠 적용 -1. **데이터 바인딩**: JSON 데이터를 템플릿 변수에 매핑 -2. **PPTX 생성**: PptxGenJS를 사용한 슬라이드 생성 -3. **스타일 적용**: 원본과 동일한 디자인 재현 -4. **품질 검증**: 생성된 PPTX의 형태 확인 - -## 🎯 적용 사례 - -### 1. 견적서 시스템 -- **템플릿**: SAM ERP 견적서 PDF -- **변수**: 고객정보, 상품목록, 금액계산 -- **결과**: 동일한 형태의 견적서 PPTX - -### 2. 기획서 시스템 -- **템플릿**: 표준 기획서 PDF -- **변수**: 프로젝트 정보, 기능 명세, 일정 -- **결과**: 일관된 형태의 기획서 PPTX - -### 3. 보고서 시스템 -- **템플릿**: 월간 보고서 PDF -- **변수**: 실적 데이터, 차트, 분석 내용 -- **결과**: 표준화된 보고서 PPTX - -## 🔮 확장 계획 - -### Short-term -- [ ] 더 정교한 테이블 구조 인식 -- [ ] 차트 및 그래프 템플릿 지원 -- [ ] 다국어 템플릿 처리 - -### Long-term -- [ ] AI 기반 레이아웃 최적화 -- [ ] 실시간 템플릿 편집기 -- [ ] 클라우드 템플릿 저장소 - -## 📚 참고 자료 - -- PptxGenJS Documentation -- PDF.js Parser Reference -- Template Engine Best Practices -- Design Pattern Guidelines \ No newline at end of file diff --git a/.claude/skills/pdf-template-skill/scripts/analyze-template.js b/.claude/skills/pdf-template-skill/scripts/analyze-template.js deleted file mode 100755 index 756e621..0000000 --- a/.claude/skills/pdf-template-skill/scripts/analyze-template.js +++ /dev/null @@ -1,572 +0,0 @@ -/** - * PDF 템플릿 분석기 - PDF 구조를 분석하여 템플릿 생성 - */ - -const fs = require('fs').promises; -const path = require('path'); - -// PDF 템플릿 분석 클래스 -class PDFTemplateAnalyzer { - constructor() { - this.template = { - metadata: {}, - slides: [], - styles: { - colors: {}, - fonts: {} - }, - variables: {} - }; - } - - /** - * SAM ERP 견적서 PDF 분석 (수동 매핑) - */ - async analyzeSAMEstimate(inputPath) { - console.log(`📖 SAM ERP 견적서 PDF 분석: ${inputPath}`); - - // 메타데이터 설정 - this.template.metadata = { - source: path.basename(inputPath), - pages: 5, - extractedAt: new Date().toISOString().split('T')[0], - title: "SAM ERP 견적서", - type: "estimate" - }; - - // 스타일 정의 - this.template.styles = { - colors: { - primary: '0066CC', // SAM 파란색 - secondary: '333333', // 진한 회색 - headerBg: 'F0F8FF', // 연한 파란색 - border: 'CCCCCC', // 테두리 회색 - white: 'FFFFFF', - black: '000000', - red: 'CC0000', - green: '008000' - }, - fonts: { - title: { face: 'Arial', size: 32, bold: true }, - heading: { face: 'Arial', size: 24, bold: true }, - subheading: { face: 'Arial', size: 18, bold: true }, - body: { face: 'Arial', size: 12 }, - small: { face: 'Arial', size: 10 } - } - }; - - // 슬라이드 템플릿 생성 - this.createCoverSlideTemplate(); - this.createMainScreenTemplate(); - this.createDetailScreenTemplate(); - this.createEstimateDocTemplate(); - this.createEstimateDetailTemplate(); - - // 변수 정의 - this.template.variables = { - // 회사 정보 - company: { type: 'string', default: '(주) 주일기업' }, - documentNumber: { type: 'string', default: 'ABC123' }, - date: { type: 'date', default: '2025-01-03' }, - - // 고객 정보 - clientName: { type: 'string', default: '회사명' }, - siteName: { type: 'string', default: '현장명' }, - clientAddress: { type: 'string', default: '주소명' }, - clientContact: { type: 'string', default: '연락처' }, - clientPhone: { type: 'string', default: '010-1234-5678' }, - - // 견적 정보 - estimateNumber: { type: 'string', default: '123123' }, - estimator: { type: 'string', default: '이름' }, - totalAmount: { type: 'number', default: 93950000 }, - bidDate: { type: 'date', default: '2025-12-12' }, - - // 상품 목록 - items: { - type: 'array', - default: [ - { - no: 1, - name: 'FSSB01(주차장)', - product: '제품명', - width: 2530, - height: 2550, - quantity: 1, - unit: 'SET', - materialCost: 1420000, - laborCost: 510000, - totalCost: 1930000 - } - ] - }, - - // 요약 정보 - summary: { - type: 'object', - default: { - description: '셔터설치공사', - quantity: 1, - unit: '식', - materialTotal: 78540000, - laborTotal: 15410000, - grandTotal: 93950000 - } - }, - - // 기타 정보 - notes: { type: 'string', default: '부가세 별도 / 현설조건에 따름' } - }; - - console.log('✅ SAM ERP 견적서 템플릿 분석 완료'); - return this.template; - } - - /** - * 표지 슬라이드 템플릿 - */ - createCoverSlideTemplate() { - this.template.slides.push({ - slideNumber: 1, - type: 'cover', - name: '표지', - layout: { width: 10, height: 7.5 }, - elements: [ - { - type: 'background', - id: 'coverBackground', - style: { color: '{{colors.primary}}' } - }, - { - type: 'shape', - id: 'logoArea', - shapeType: 'rect', - position: { x: 1, y: 1, w: 2, h: 1 }, - style: { - fill: { color: '{{colors.white}}' }, - line: { color: '{{colors.border}}', width: 1 } - } - }, - { - type: 'text', - id: 'logoText', - position: { x: 1, y: 1, w: 2, h: 0.5 }, - content: 'SAM', - style: { - fontSize: 24, - bold: true, - color: '{{colors.primary}}', - align: 'center' - } - }, - { - type: 'text', - id: 'logoSubtext', - position: { x: 1, y: 1.5, w: 2, h: 0.5 }, - content: 'Smart Automation Management', - style: { - fontSize: 10, - color: '{{colors.secondary}}', - align: 'center' - } - }, - { - type: 'text', - id: 'mainTitle', - position: { x: 2, y: 3, w: 6, h: 1.5 }, - content: '{{title}}', - placeholder: '{{title}}', - style: { - fontSize: 48, - bold: true, - color: '{{colors.white}}', - align: 'center' - } - }, - { - type: 'text', - id: 'subtitle', - position: { x: 2, y: 4.8, w: 6, h: 0.8 }, - content: 'SAM ERP 견적관리 시스템', - style: { - fontSize: 24, - color: '{{colors.white}}', - align: 'center' - } - }, - { - type: 'text', - id: 'dateAndCompany', - position: { x: 7.5, y: 7, w: 2.5, h: 1.5 }, - content: '{{date}}\\n\\n{{company}}', - placeholder: '{{date}}\\n\\n{{company}}', - style: { - fontSize: 12, - color: '{{colors.white}}', - align: 'right' - } - } - ] - }); - } - - /** - * 견적관리 메인 화면 템플릿 - */ - createMainScreenTemplate() { - this.template.slides.push({ - slideNumber: 2, - type: 'main_screen', - name: '견적관리 메인', - layout: { width: 10, height: 7.5 }, - elements: [ - { - type: 'text', - id: 'pageTitle', - position: { x: 0.5, y: 0.3, w: 6, h: 0.6 }, - content: '견적관리', - style: { - fontSize: 24, - bold: true, - color: '{{colors.secondary}}' - } - }, - { - type: 'text', - id: 'pageDescription', - position: { x: 0.5, y: 0.9, w: 6, h: 0.4 }, - content: '견적을 관리합니다', - style: { - fontSize: 14, - color: '{{colors.secondary}}' - } - }, - { - type: 'shape', - id: 'filterArea', - shapeType: 'rect', - position: { x: 0.5, y: 1.5, w: 9, h: 1 }, - style: { - fill: { color: '{{colors.headerBg}}' }, - line: { color: '{{colors.border}}', width: 1 } - } - }, - { - type: 'dynamicStats', - id: 'statsBoxes', - position: { x: 2, y: 2.8, w: 6, h: 1.2 }, - dataSource: 'stats', - template: { - type: 'statBox', - width: 1.8, - spacing: 0.1 - } - }, - { - type: 'table', - id: 'estimateList', - position: { x: 0.5, y: 4.5, w: 9, h: 2.5 }, - dataSource: 'estimates', - headers: ['견적번호', '거래처', '현장명', '견적자', '총 개소', '견적금액', '견적완료일', '입찰일', '상태', '작업'], - style: { - border: { pt: 1, color: '{{colors.border}}' }, - fontSize: 10, - headerBg: '{{colors.headerBg}}' - } - } - ] - }); - } - - /** - * 견적 상세 화면 템플릿 - */ - createDetailScreenTemplate() { - this.template.slides.push({ - slideNumber: 3, - type: 'detail_screen', - name: '견적 상세', - layout: { width: 10, height: 7.5 }, - elements: [ - { - type: 'text', - id: 'pageTitle', - position: { x: 0.5, y: 0.3, w: 6, h: 0.6 }, - content: '견적 상세', - style: { - fontSize: 24, - bold: true, - color: '{{colors.secondary}}' - } - }, - { - type: 'buttonGroup', - id: 'actionButtons', - position: { x: 4, y: 0.3, w: 4.5, h: 0.6 }, - buttons: [ - { text: '견적서 보기', action: 'viewEstimate' }, - { text: '전자결재', action: 'approval' }, - { text: '수정', action: 'edit' } - ] - }, - { - type: 'infoSection', - id: 'estimateInfo', - position: { x: 0.5, y: 1.2, w: 9, h: 1.8 }, - title: '견적 정보', - fields: [ - { label: '견적번호', field: 'estimateNumber' }, - { label: '견적자', field: 'estimator' }, - { label: '견적금액', field: 'totalAmount', format: 'currency' }, - { label: '상태', field: 'status' } - ] - }, - { - type: 'infoSection', - id: 'siteInfo', - position: { x: 0.5, y: 3.2, w: 9, h: 1.8 }, - title: '현장설명회 정보', - fields: [ - { label: '현설번호', field: 'siteNumber' }, - { label: '거래처명', field: 'clientName' }, - { label: '현장설명회 일자', field: 'siteDate' }, - { label: '참석자', field: 'attendee' } - ] - }, - { - type: 'infoSection', - id: 'bidInfo', - position: { x: 0.5, y: 5.2, w: 9, h: 1.5 }, - title: '입찰 정보', - fields: [ - { label: '현장명', field: 'siteName' }, - { label: '입찰일자', field: 'bidDate' }, - { label: '개소', field: 'quantity' }, - { label: '공사기간', field: 'constructionPeriod' } - ] - } - ] - }); - } - - /** - * 견적서 문서 (요약) 템플릿 - */ - createEstimateDocTemplate() { - this.template.slides.push({ - slideNumber: 4, - type: 'estimate_document', - name: '견적서 문서 (요약)', - layout: { width: 10, height: 7.5 }, - elements: [ - { - type: 'text', - id: 'documentTitle', - position: { x: 3, y: 0.5, w: 4, h: 0.8 }, - content: '견적서', - style: { - fontSize: 32, - bold: true, - color: '{{colors.secondary}}', - align: 'center' - } - }, - { - type: 'text', - id: 'documentInfo', - position: { x: 3, y: 1.3, w: 4, h: 0.4 }, - content: '문서번호: {{documentNumber}} | 작성일자: {{date}}', - placeholder: '문서번호: {{documentNumber}} | 작성일자: {{date}}', - style: { - fontSize: 12, - color: '{{colors.secondary}}', - align: 'center' - } - }, - { - type: 'table', - id: 'companyInfoTable', - position: { x: 1, y: 2, w: 8, h: 2 }, - dataSource: 'companyInfo', - template: 'companyInfoLayout' - }, - { - type: 'text', - id: 'estimateIntro', - position: { x: 8, y: 3.7, w: 2, h: 0.4 }, - content: '하기와 같이 見積합니다.', - style: { - fontSize: 12, - color: '{{colors.secondary}}', - align: 'center' - } - }, - { - type: 'table', - id: 'summaryTable', - position: { x: 1, y: 4.5, w: 8, h: 1.5 }, - dataSource: 'summary', - headers: ['명칭', '수량', '단위', '재료비', '노무비', '합계', '비고'], - template: 'summaryLayout' - }, - { - type: 'text', - id: 'specialNotes', - position: { x: 1, y: 6.2, w: 8, h: 0.4 }, - content: '* 특기사항: {{notes}}', - placeholder: '* 특기사항: {{notes}}', - style: { - fontSize: 11, - color: '{{colors.secondary}}' - } - } - ] - }); - } - - /** - * 견적서 문서 (상세) 템플릿 - */ - createEstimateDetailTemplate() { - this.template.slides.push({ - slideNumber: 5, - type: 'estimate_detail', - name: '견적서 문서 (상세)', - layout: { width: 10, height: 7.5 }, - elements: [ - { - type: 'text', - id: 'detailTitle', - position: { x: 3, y: 0.5, w: 4, h: 0.6 }, - content: '견적 상세 내역', - style: { - fontSize: 20, - bold: true, - color: '{{colors.secondary}}', - align: 'center' - } - }, - { - type: 'table', - id: 'detailTable', - position: { x: 0.2, y: 1.5, w: 9.6, h: 4 }, - dataSource: 'items', - headers: [ - 'NO', '명칭', '제품', '규격(mm)', '', '수량', '단위', - '재료비', '', '노무비', '', '합계', '', '비고' - ], - subHeaders: [ - '', '', '', '가로(W)', '높이(H)', '', '', - '단가', '금액', '단가', '금액', '단가', '금액', '' - ], - template: 'detailItemLayout', - style: { - border: { pt: 1, color: '{{colors.border}}' }, - fontSize: 8, - headerBg: '{{colors.headerBg}}' - } - } - ] - }); - } - - /** - * 템플릿을 JSON 파일로 저장 - */ - async saveTemplate(outputPath) { - const dir = path.dirname(outputPath); - await fs.mkdir(dir, { recursive: true }); - - await fs.writeFile( - outputPath, - JSON.stringify(this.template, null, 2), - 'utf8' - ); - - console.log(`✅ 템플릿 저장 완료: ${outputPath}`); - return outputPath; - } - - /** - * 범용 PDF 분석 (확장 가능) - */ - async analyzeGenericPDF(inputPath) { - // TODO: 실제 PDF 파싱 로직 구현 - console.log(`📖 범용 PDF 분석: ${inputPath}`); - - // 기본 템플릿 구조 생성 - this.template.metadata = { - source: path.basename(inputPath), - extractedAt: new Date().toISOString().split('T')[0], - title: "Generic Template", - type: "generic" - }; - - // 기본 스타일 - this.template.styles = { - colors: { - primary: '0066CC', - secondary: '333333', - background: 'FFFFFF' - }, - fonts: { - title: { face: 'Arial', size: 24, bold: true }, - body: { face: 'Arial', size: 12 } - } - }; - - console.log('✅ 범용 PDF 분석 완료 (기본 템플릿)'); - return this.template; - } -} - -// 메인 실행 함수 -async function analyzeTemplate(inputPath, outputPath, type = 'sam') { - try { - console.log('🚀 PDF 템플릿 분석 시작'); - console.log(`📄 입력: ${inputPath}`); - console.log(`📊 출력: ${outputPath}`); - - const analyzer = new PDFTemplateAnalyzer(); - - let template; - if (type === 'sam') { - template = await analyzer.analyzeSAMEstimate(inputPath); - } else { - template = await analyzer.analyzeGenericPDF(inputPath); - } - - await analyzer.saveTemplate(outputPath); - console.log('✅ 템플릿 분석 완료!'); - - return outputPath; - - } catch (error) { - console.error('❌ 템플릿 분석 실패:', error.message); - throw error; - } -} - -// 명령행 인수 처리 -async function main() { - const args = process.argv.slice(2); - const inputFlag = args.find(arg => arg.startsWith('--input=')); - const outputFlag = args.find(arg => arg.startsWith('--output=')); - const typeFlag = args.find(arg => arg.startsWith('--type=')); - - const inputPath = inputFlag ? inputFlag.split('=')[1] : 'pdf_sample/SAM_견적관리.pdf'; - const outputPath = outputFlag ? outputFlag.split('=')[1] : 'templates/sam_estimate_template.json'; - const type = typeFlag ? typeFlag.split('=')[1] : 'sam'; - - // 출력 디렉토리 생성 - await fs.mkdir(path.dirname(outputPath), { recursive: true }); - - await analyzeTemplate(inputPath, outputPath, type); -} - -// 직접 실행시 -if (require.main === module) { - main().catch(console.error); -} - -module.exports = { analyzeTemplate, PDFTemplateAnalyzer }; \ No newline at end of file diff --git a/.claude/skills/pdf-template-skill/scripts/generate-from-template.js b/.claude/skills/pdf-template-skill/scripts/generate-from-template.js deleted file mode 100755 index 84d9548..0000000 --- a/.claude/skills/pdf-template-skill/scripts/generate-from-template.js +++ /dev/null @@ -1,453 +0,0 @@ -/** - * 템플릿 기반 PPTX 생성기 - 템플릿과 데이터를 결합하여 PPTX 생성 - */ - -const fs = require('fs').promises; -const path = require('path'); -const PptxGenJS = require('pptxgenjs'); - -// 템플릿 변수 처리 엔진 -class TemplateEngine { - constructor(data) { - this.data = data; - } - - /** - * 템플릿 변수 치환 - */ - resolve(template) { - if (typeof template === 'string') { - return this.resolveString(template); - } else if (Array.isArray(template)) { - return template.map(item => this.resolve(item)); - } else if (typeof template === 'object' && template !== null) { - const result = {}; - for (const [key, value] of Object.entries(template)) { - result[key] = this.resolve(value); - } - return result; - } - return template; - } - - /** - * 문자열 템플릿 변수 치환 - */ - resolveString(template) { - return template.replace(/\{\{([^}]+)\}\}/g, (match, path) => { - const value = this.getNestedValue(this.data, path.trim()); - return value !== undefined ? value : match; - }); - } - - /** - * 중첩 객체 값 추출 - */ - getNestedValue(obj, path) { - return path.split('.').reduce((current, key) => { - return current && current[key] !== undefined ? current[key] : undefined; - }, obj); - } - - /** - * 숫자 포맷팅 - */ - formatNumber(value, format) { - if (format === 'currency') { - return '₩' + value.toLocaleString(); - } - return value.toString(); - } - - /** - * 날짜 포맷팅 - */ - formatDate(value, format = 'YYYY-MM-DD') { - const date = new Date(value); - return date.toISOString().split('T')[0]; - } -} - -// 템플릿 기반 PPTX 생성기 -class TemplatePPTXGenerator { - constructor() { - this.pptx = new PptxGenJS(); - this.pptx.layout = 'LAYOUT_16x9'; - } - - /** - * 템플릿을 기반으로 PPTX 생성 - */ - async generateFromTemplate(templatePath, dataPath) { - console.log('📊 템플릿 기반 PPTX 생성 중...'); - - // 템플릿과 데이터 로드 - const template = await this.loadTemplate(templatePath); - const data = await this.loadData(dataPath); - - // 템플릿 엔진 초기화 - const engine = new TemplateEngine(data); - - // 색상 및 스타일 준비 - const resolvedStyles = engine.resolve(template.styles); - this.colors = resolvedStyles.colors; - this.fonts = resolvedStyles.fonts; - - // 각 슬라이드 생성 - for (const slideTemplate of template.slides) { - await this.createSlideFromTemplate(slideTemplate, engine); - } - - console.log(`✅ 템플릿 기반 PPTX 생성 완료: ${template.slides.length}개 슬라이드`); - return this.pptx; - } - - /** - * 템플릿 파일 로드 - */ - async loadTemplate(templatePath) { - const content = await fs.readFile(templatePath, 'utf8'); - return JSON.parse(content); - } - - /** - * 데이터 파일 로드 - */ - async loadData(dataPath) { - const content = await fs.readFile(dataPath, 'utf8'); - return JSON.parse(content); - } - - /** - * 슬라이드 템플릿을 기반으로 슬라이드 생성 - */ - async createSlideFromTemplate(slideTemplate, engine) { - const slide = this.pptx.addSlide(); - - console.log(`📄 슬라이드 ${slideTemplate.slideNumber}: ${slideTemplate.name}`); - - // 각 요소 생성 - for (const element of slideTemplate.elements) { - await this.createElement(slide, element, engine); - } - } - - /** - * 요소 생성 - */ - async createElement(slide, element, engine) { - const resolvedElement = engine.resolve(element); - - switch (element.type) { - case 'background': - this.createBackground(slide, resolvedElement); - break; - - case 'text': - this.createText(slide, resolvedElement); - break; - - case 'shape': - this.createShape(slide, resolvedElement); - break; - - case 'table': - this.createTable(slide, resolvedElement, engine); - break; - - case 'buttonGroup': - this.createButtonGroup(slide, resolvedElement); - break; - - case 'infoSection': - this.createInfoSection(slide, resolvedElement, engine); - break; - - case 'dynamicStats': - this.createDynamicStats(slide, resolvedElement, engine); - break; - - default: - console.log(`⚠️ 미지원 요소 타입: ${element.type}`); - } - } - - /** - * 배경 생성 - */ - createBackground(slide, element) { - slide.background = { color: element.style.color }; - } - - /** - * 텍스트 생성 - */ - createText(slide, element) { - const options = { - x: element.position.x, - y: element.position.y, - w: element.position.w, - h: element.position.h, - ...element.style - }; - - slide.addText(element.content || element.placeholder || '', options); - } - - /** - * 도형 생성 - */ - createShape(slide, element) { - const options = { - x: element.position.x, - y: element.position.y, - w: element.position.w, - h: element.position.h, - ...element.style - }; - - slide.addShape(element.shapeType || 'rect', options); - } - - /** - * 테이블 생성 - */ - createTable(slide, element, engine) { - let tableData = []; - - // 헤더 추가 - if (element.headers) { - tableData.push(element.headers); - if (element.subHeaders) { - tableData.push(element.subHeaders); - } - } - - // 데이터 소스에서 행 생성 - if (element.dataSource) { - const sourceData = engine.getNestedValue(engine.data, element.dataSource); - if (Array.isArray(sourceData)) { - sourceData.forEach((item, index) => { - const row = this.generateTableRow(element, item, index, engine); - tableData.push(row); - }); - } - } - - const options = { - x: element.position.x, - y: element.position.y, - w: element.position.w, - h: element.position.h, - border: { pt: 1, color: '757575' }, - fontSize: 10, - margin: 0.1, - ...element.style - }; - - slide.addTable(tableData, options); - } - - /** - * 테이블 행 생성 - */ - generateTableRow(element, item, index, engine) { - if (element.template === 'detailItemLayout') { - return [ - (index + 1).toString(), - item.name || '', - item.product || '', - item.width ? item.width.toLocaleString() : '', - item.height ? item.height.toLocaleString() : '', - item.quantity ? item.quantity.toString() : '', - item.unit || '', - item.materialCost ? engine.formatNumber(item.materialCost, 'currency') : '', - item.materialCost && item.quantity ? - engine.formatNumber(item.materialCost * item.quantity, 'currency') : '', - item.laborCost ? engine.formatNumber(item.laborCost, 'currency') : '', - item.laborCost && item.quantity ? - engine.formatNumber(item.laborCost * item.quantity, 'currency') : '', - item.totalCost ? engine.formatNumber(item.totalCost, 'currency') : '', - item.totalCost && item.quantity ? - engine.formatNumber(item.totalCost * item.quantity, 'currency') : '', - item.memo || '' - ]; - } - - // 기본 행 생성 - return Object.values(item); - } - - /** - * 버튼 그룹 생성 - */ - createButtonGroup(slide, element) { - element.buttons.forEach((button, index) => { - const xPos = element.position.x + index * 1.5; - - // 버튼 배경 - slide.addShape('rect', { - x: xPos, - y: element.position.y, - w: 1.3, - h: element.position.h, - fill: { color: this.colors.primary }, - line: { color: this.colors.primary, width: 1 } - }); - - // 버튼 텍스트 - slide.addText(button.text, { - x: xPos, - y: element.position.y, - w: 1.3, - h: element.position.h, - fontSize: 12, - bold: true, - color: 'FFFFFF', - align: 'center' - }); - }); - } - - /** - * 정보 섹션 생성 - */ - createInfoSection(slide, element, engine) { - // 섹션 배경 - slide.addShape('rect', { - x: element.position.x, - y: element.position.y, - w: element.position.w, - h: element.position.h, - fill: { color: 'FFFFFF' }, - line: { color: 'CCCCCC', width: 1 } - }); - - // 섹션 제목 - slide.addText(element.title, { - x: element.position.x + 0.2, - y: element.position.y + 0.2, - w: element.position.w - 0.4, - h: 0.4, - fontSize: 14, - bold: true, - color: '333333' - }); - - // 필드들 - element.fields.forEach((field, index) => { - const row = Math.floor(index / 2); - const col = index % 2; - const xPos = element.position.x + 0.5 + col * 4; - const yPos = element.position.y + 0.8 + row * 0.6; - - const value = engine.getNestedValue(engine.data, field.field); - const formattedValue = field.format === 'currency' ? - engine.formatNumber(value, 'currency') : value; - - slide.addText(`${field.label}: ${formattedValue || ''}`, { - x: xPos, - y: yPos, - w: 3.5, - h: 0.4, - fontSize: 11, - color: '333333' - }); - }); - } - - /** - * 동적 통계 박스 생성 - */ - createDynamicStats(slide, element, engine) { - const statsData = engine.getNestedValue(engine.data, element.dataSource) || [ - { label: '전체 견적', value: '9', color: this.colors.primary }, - { label: '견적대기', value: '5', color: 'CC0000' }, - { label: '견적완료', value: '4', color: '008000' } - ]; - - statsData.forEach((stat, index) => { - const xPos = element.position.x + index * 2; - - // 통계 박스 - slide.addShape('rect', { - x: xPos, - y: element.position.y, - w: element.template.width, - h: element.position.h, - fill: { color: 'FFFFFF' }, - line: { color: 'CCCCCC', width: 1 } - }); - - // 통계 값 - slide.addText(stat.value, { - x: xPos, - y: element.position.y + 0.2, - w: element.template.width, - h: 0.8, - fontSize: 24, - bold: true, - color: stat.color, - align: 'center' - }); - - // 통계 라벨 - slide.addText(stat.label, { - x: xPos, - y: element.position.y + 0.8, - w: element.template.width, - h: 0.4, - fontSize: 12, - color: '333333', - align: 'center' - }); - }); - } -} - -// 메인 실행 함수 -async function generateFromTemplate(templatePath, dataPath, outputPath) { - try { - console.log('🚀 템플릿 기반 PPTX 생성 시작'); - console.log(`📋 템플릿: ${templatePath}`); - console.log(`📊 데이터: ${dataPath}`); - console.log(`📄 출력: ${outputPath}`); - - const generator = new TemplatePPTXGenerator(); - const pptx = await generator.generateFromTemplate(templatePath, dataPath); - - // 파일 저장 - await pptx.writeFile({ fileName: outputPath }); - console.log('✅ 템플릿 기반 PPTX 생성 완료!'); - - return outputPath; - - } catch (error) { - console.error('❌ 생성 실패:', error.message); - throw error; - } -} - -// 명령행 인수 처리 -async function main() { - const args = process.argv.slice(2); - const templateFlag = args.find(arg => arg.startsWith('--template=')); - const dataFlag = args.find(arg => arg.startsWith('--data=')); - const outputFlag = args.find(arg => arg.startsWith('--output=')); - - const templatePath = templateFlag ? templateFlag.split('=')[1] : 'templates/sam_estimate_template.json'; - const dataPath = dataFlag ? dataFlag.split('=')[1] : 'data/sample_estimate_data.json'; - const outputPath = outputFlag ? outputFlag.split('=')[1] : 'pptx/template_generated.pptx'; - - // 출력 디렉토리 생성 - await fs.mkdir(path.dirname(outputPath), { recursive: true }); - - await generateFromTemplate(templatePath, dataPath, outputPath); -} - -// 직접 실행시 -if (require.main === module) { - main().catch(console.error); -} - -module.exports = { generateFromTemplate, TemplatePPTXGenerator, TemplateEngine }; \ No newline at end of file diff --git a/.claude/skills/ppt-auto-generator/SKILL.md b/.claude/skills/ppt-auto-generator/SKILL.md deleted file mode 100755 index 7f0b159..0000000 --- a/.claude/skills/ppt-auto-generator/SKILL.md +++ /dev/null @@ -1,193 +0,0 @@ ---- -name: ppt-auto-generator -description: 마크다운 또는 텍스트 아웃라인에서 PowerPoint 프레젠테이션을 생성합니다. PPT 생성, 슬라이드 제작, 발표자료 작성 요청 시 활성화됩니다. -allowed-tools: Read, Write, Edit, Glob, Grep, Bash ---- - -# PPT Auto Generator - -마크다운이나 텍스트 아웃라인을 기반으로 PowerPoint 프레젠테이션을 생성하는 스킬입니다. - -## 기능 - -### 입력 형식 -- 마크다운 문서 -- 텍스트 아웃라인 -- 구조화된 데이터 (JSON/YAML) - -### 생성 요소 -- 제목 슬라이드 -- 목차 슬라이드 -- 콘텐츠 슬라이드 -- 불릿 포인트 -- 이미지 플레이스홀더 -- 차트 및 표 -- 요약 슬라이드 - -### 출력 형식 -- Python-pptx 스크립트 -- HTML 슬라이드 (reveal.js) -- 마크다운 슬라이드 (Marp) - -## 마크다운 입력 형식 - -```markdown -# 프레젠테이션 제목 - -## 슬라이드 1: 소개 -- 첫 번째 포인트 -- 두 번째 포인트 -- 세 번째 포인트 - -## 슬라이드 2: 주요 내용 -### 섹션 A -- 내용 1 -- 내용 2 - -### 섹션 B -- 내용 3 -- 내용 4 - -## 슬라이드 3: 데이터 -| 항목 | 값 | -|------|-----| -| A | 100 | -| B | 200 | - -## 슬라이드 4: 결론 -- 핵심 메시지 -- 다음 단계 -``` - -## Python-pptx 템플릿 - -```python -from pptx import Presentation -from pptx.util import Inches, Pt -from pptx.enum.text import PP_ALIGN - -def create_presentation(content): - prs = Presentation() - - # 제목 슬라이드 - title_slide = prs.slides.add_slide(prs.slide_layouts[0]) - title_slide.shapes.title.text = content['title'] - title_slide.placeholders[1].text = content['subtitle'] - - # 콘텐츠 슬라이드 - for slide_content in content['slides']: - slide = prs.slides.add_slide(prs.slide_layouts[1]) - slide.shapes.title.text = slide_content['title'] - - body = slide.placeholders[1] - tf = body.text_frame - - for point in slide_content['points']: - p = tf.add_paragraph() - p.text = point - p.level = 0 - - prs.save('output.pptx') - return 'output.pptx' -``` - -## Marp 마크다운 템플릿 - -```markdown ---- -marp: true -theme: default -paginate: true ---- - -# 프레젠테이션 제목 - -발표자: 이름 -날짜: 2024-01-15 - ---- - -## 목차 - -1. 소개 -2. 주요 내용 -3. 결론 - ---- - -## 소개 - -- 배경 설명 -- 목적 -- 범위 - ---- - -## 주요 내용 - -### 핵심 포인트 - -- 첫 번째 -- 두 번째 -- 세 번째 - ---- - -## 결론 - -- 요약 -- 다음 단계 -- Q&A -``` - -## Reveal.js HTML 템플릿 - -```html - - - - - - - -
      -
      -
      -

      제목

      -

      부제목

      -
      -
      -

      슬라이드 제목

      -
        -
      • 포인트 1
      • -
      • 포인트 2
      • -
      -
      -
      -
      - - - - -``` - -## 사용 예시 - -``` -이 마크다운을 PPT로 만들어줘 -발표자료를 슬라이드로 변환해줘 -프로젝트 소개 PPT를 생성해줘 -``` - -## 필요 패키지 - -```bash -# Python -pip install python-pptx - -# Node.js (Marp) -npm install @marp-team/marp-cli -``` - -## 출처 -Original skill from skills.cokac.com diff --git a/.claude/skills/pptx-skill/SKILL.md b/.claude/skills/pptx-skill/SKILL.md deleted file mode 100755 index b207833..0000000 --- a/.claude/skills/pptx-skill/SKILL.md +++ /dev/null @@ -1,434 +0,0 @@ ---- -name: pptx-skill -description: HTML 슬라이드를 PowerPoint(PPTX) 파일로 변환합니다. PPTX 생성, PPT 변환, 프레젠테이션 만들기, 슬라이드를 파워포인트로 요청 시 사용합니다. -allowed-tools: Read, Write, Edit, Glob, Grep, Bash ---- - -# PPTX Skill - PowerPoint 프레젠테이션 생성 - -PowerPoint 프레젠테이션 파일(.pptx)을 생성하는 스킬입니다. -두 가지 방법을 제공하며, **방법 A (Direct PptxGenJS)를 우선 사용**합니다. - -## 방법 선택 가이드 - -| | 방법 A: Direct PptxGenJS (권장) | 방법 B: HTML → PPTX | -|---|---|---| -| **의존성** | Node.js + pptxgenjs만 필요 | Playwright + Chromium 브라우저 필요 | -| **원리** | JS 코드로 도형/텍스트 직접 배치 | HTML을 브라우저 렌더링 후 변환 | -| **PPT 편집** | 생성 후 텍스트/도형 직접 수정 가능 | 이미지 기반이라 수정 어려움 | -| **한글 폰트** | PPT 뷰어 폰트 사용 (깨짐 없음) | 시스템 폰트 필요 | -| **환경 제약** | 없음 | Chromium 시스템 라이브러리 필요 | -| **적합한 경우** | 대부분의 프레젠테이션 | 복잡한 CSS 레이아웃이 필요할 때 | - ---- - -## 방법 A: Direct PptxGenJS (권장) - -PptxGenJS API를 직접 호출하여 네이티브 PPTX 요소를 생성합니다. -브라우저 없이 Node.js만으로 동작하며, 생성된 PPT에서 텍스트/도형 편집이 가능합니다. - -### 사용법 - -#### 1단계: .cjs 변환 스크립트 작성 - -**주의**: 프로젝트에 `"type": "module"`이 설정된 경우 `.cjs` 확장자를 사용해야 합니다. - -```javascript -// convert.cjs -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(); - - // 커스텀 레이아웃 정의 (16:9 = 10" x 5.625") - pres.defineLayout({ name: 'CUSTOM_16x9', width: 10, height: 5.625 }); - pres.layout = 'CUSTOM_16x9'; - - // --- 슬라이드 1 --- - const slide1 = pres.addSlide(); - slide1.background = { fill: '1a1a2e' }; - - slide1.addText('제목', { - x: 1, y: 2, w: 8, h: 1, - fontSize: 32, bold: true, color: 'FFFFFF', - align: 'center', fontFace: 'Arial' - }); - - // --- 저장 --- - await pres.writeFile({ fileName: 'output.pptx' }); - console.log('PPTX created!'); -} - -main().catch(console.error); -``` - -#### 2단계: 실행 - -```bash -node convert.cjs -``` - -#### 3단계: 결과 확인 (선택) - -```bash -python ~/.claude/skills/pptx-skill/scripts/thumbnail.py output.pptx thumbnail-grid -``` - -### 핵심 규칙 - -#### 색상 코드 (매우 중요) -PptxGenJS에서 색상 코드는 반드시 `#` 없이 사용해야 합니다. - -```javascript -// ✅ 올바른 사용 -{ color: 'FF0000' } -{ fill: { color: '1e3a5f' } } - -// ❌ 잘못된 사용 - 파일 손상 유발 -{ color: '#FF0000' } -``` - -#### 좌표 체계 -모든 위치/크기는 **인치(inch)** 단위입니다. - -``` -슬라이드 크기: width=10, height=5.625 (16:9) -좌표 원점: 좌측 상단 (0, 0) - -x: 좌측에서의 거리 -y: 상단에서의 거리 -w: 너비 -h: 높이 -``` - -### PptxGenJS API 빠른 참조 - -#### 슬라이드 배경 -```javascript -slide.background = { fill: '1a1a2e' }; // 단색 -slide.background = { path: 'background.png' }; // 이미지 -``` - -#### 텍스트 추가 -```javascript -// 단순 텍스트 -slide.addText('제목', { - x: 0.5, y: 0.5, w: 9, h: 1, - fontSize: 36, bold: true, color: '1a1a2e', - align: 'center', // left, center, right - valign: 'middle', // top, middle, bottom - fontFace: 'Arial' -}); - -// 복합 텍스트 (여러 스타일 혼합) -slide.addText([ - { text: '굵은 텍스트', options: { bold: true, color: 'FF0000', fontSize: 14 } }, - { text: ' 일반 텍스트', options: { color: '333333', fontSize: 14 } } -], { x: 1, y: 2, w: 8, h: 0.5, fontFace: 'Arial' }); - -// 줄바꿈 -slide.addText([ - { text: '첫째 줄', options: { fontSize: 14, bold: true, color: 'FFFFFF' } }, - { text: '\n둘째 줄', options: { fontSize: 9, color: 'CCCCCC' } } -], { x: 1, y: 2, w: 4, h: 0.7, align: 'center', valign: 'middle', fontFace: 'Arial' }); -``` - -#### 도형 추가 -```javascript -// 사각형 -slide.addShape(pres.ShapeType.rect, { - x: 0.5, y: 1, w: 3, h: 2, - fill: { color: '1e3a5f' }, - line: { color: '333333', width: 1 } -}); - -// 둥근 사각형 -slide.addShape(pres.ShapeType.roundRect, { - x: 1, y: 1, w: 4, h: 0.5, - rectRadius: 0.1, - fill: { color: 'F0FAF0' }, - line: { color: 'C8E6C9', width: 0.5 } -}); - -// 원/타원 -slide.addShape(pres.ShapeType.ellipse, { - x: 2, y: 2, w: 0.5, h: 0.5, - fill: { color: '4CAF50' } -}); -``` - -#### 이미지 추가 -```javascript -slide.addImage({ - path: 'image.png', // 파일 경로 - x: 1, y: 2, w: 4, h: 3 -}); - -slide.addImage({ - data: 'base64...', // Base64 인코딩 - x: 1, y: 2, w: 4, h: 3 -}); -``` - -#### 차트 추가 -```javascript -slide.addChart(pres.ChartType.bar, [{ - name: '시리즈 1', - labels: ['A', 'B', 'C'], - values: [10, 20, 30] -}], { x: 1, y: 2, w: 8, h: 4 }); - -slide.addChart(pres.ChartType.pie, [...], {...}); -slide.addChart(pres.ChartType.line, [...], {...}); -``` - -### 디자인 패턴 (자주 사용하는 구성요소) - -#### 헤더 바 + 제목 -```javascript -slide.addShape(pres.ShapeType.rect, { - x: 0.5, y: 0.35, w: 0.06, h: 0.35, - fill: { color: '4CAF50' } -}); -slide.addText('페이지 제목', { - x: 0.7, y: 0.3, w: 5, h: 0.45, - fontSize: 18, bold: true, color: '1a1a2e', fontFace: 'Arial' -}); -``` - -#### 배지/태그 -```javascript -slide.addShape(pres.ShapeType.roundRect, { - x: 5, y: 0.35, w: 1, h: 0.3, - rectRadius: 0.04, - fill: { color: 'E8F5E9' } -}); -slide.addText('총 28%', { - x: 5, y: 0.35, w: 1, h: 0.3, - fontSize: 8, bold: true, color: '2E7D32', - align: 'center', fontFace: 'Arial' -}); -``` - -#### 카드 (배경 + 제목 + 항목) -```javascript -// 카드 배경 -slide.addShape(pres.ShapeType.roundRect, { - x: 0.5, y: 1.5, w: 4, h: 3, - rectRadius: 0.15, - fill: { color: 'F0FAF0' }, - line: { color: '4CAF50', width: 1.5 } -}); -// 카드 내부 항목 (흰색 행) -slide.addShape(pres.ShapeType.roundRect, { - x: 0.7, y: 2.3, w: 3.6, h: 0.4, - rectRadius: 0.06, - fill: { color: 'FFFFFF' } -}); -slide.addText('항목명', { x: 0.8, y: 2.3, w: 2, h: 0.4, fontSize: 10, color: '333333', fontFace: 'Arial' }); -slide.addText('값', { x: 2.8, y: 2.3, w: 1.4, h: 0.4, fontSize: 14, bold: true, color: '4CAF50', align: 'right', fontFace: 'Arial' }); -``` - -#### 넘버 서클 -```javascript -slide.addShape(pres.ShapeType.ellipse, { - x: 1, y: 1, w: 0.45, h: 0.45, - fill: { color: '2196F3' } -}); -slide.addText('1', { - x: 1, y: 1, w: 0.45, h: 0.45, - fontSize: 14, bold: true, color: 'FFFFFF', - align: 'center', valign: 'middle', fontFace: 'Arial' -}); -``` - -#### 비율 바 (프로그레스 형태) -```javascript -// 큰 비율 -slide.addShape(pres.ShapeType.roundRect, { - x: 0.5, y: 1, w: 6.4, h: 0.7, - rectRadius: 0.1, fill: { color: '4CAF50' } -}); -slide.addText([ - { text: '20%', options: { fontSize: 20, bold: true, color: 'FFFFFF' } }, - { text: '\n판매자 수당', options: { fontSize: 9, color: 'DDFFDD' } } -], { x: 0.5, y: 1, w: 6.4, h: 0.7, align: 'center', valign: 'middle', fontFace: 'Arial' }); - -// 작은 비율 -slide.addShape(pres.ShapeType.roundRect, { - x: 7, y: 1, w: 1.6, h: 0.7, - rectRadius: 0.1, fill: { color: '2196F3' } -}); -``` - -#### 반복 카드 생성 (데이터 기반) -```javascript -const cards = [ - { title: '항목 A', color: '4CAF50', bg: 'F0FAF0', border: 'C8E6C9', items: [['라벨1', '값1'], ['라벨2', '값2']] }, - { title: '항목 B', color: '2196F3', bg: 'E3F2FD', border: 'BBDEFB', items: [['라벨1', '값1'], ['라벨2', '값2']] }, -]; -const cardW = 2.85; -cards.forEach((card, i) => { - const x = 0.5 + i * (cardW + 0.2); - const y = 1.9; - // 카드 배경 - slide.addShape(pres.ShapeType.roundRect, { x, y, w: cardW, h: 3, rectRadius: 0.1, fill: { color: card.bg }, line: { color: card.border, width: 0.5 } }); - // 카드 제목 - slide.addText(card.title, { x: x + 0.2, y: y + 0.1, w: cardW - 0.4, h: 0.3, fontSize: 11, bold: true, color: card.color, fontFace: 'Arial' }); - // 항목 반복 - card.items.forEach((item, j) => { - const iy = y + 0.5 + j * 0.7; - slide.addShape(pres.ShapeType.roundRect, { x: x + 0.1, y: iy, w: cardW - 0.2, h: 0.55, rectRadius: 0.05, fill: { color: 'FFFFFF' } }); - slide.addText(item[0], { x: x + 0.2, y: iy + 0.03, w: cardW - 0.4, h: 0.2, fontSize: 8, color: '999999', fontFace: 'Arial' }); - slide.addText(item[1], { x: x + 0.2, y: iy + 0.25, w: cardW - 0.4, h: 0.2, fontSize: 10, bold: true, color: '333333', fontFace: 'Arial' }); - }); -}); -``` - -#### 대외비 스탬프 -```javascript -function addConfidentialBadge(slide) { - slide.addShape(pres.ShapeType.roundRect, { x: 8.3, y: 0.15, w: 1.4, h: 0.35, rectRadius: 0.04, fill: { color: 'D32F2F' } }); - slide.addText('CONFIDENTIAL', { x: 8.3, y: 0.12, w: 1.4, h: 0.22, fontSize: 7, bold: true, color: 'FFFFFF', align: 'center', fontFace: 'Arial' }); - slide.addText('대 외 비', { x: 8.3, y: 0.28, w: 1.4, h: 0.22, fontSize: 8, bold: true, color: 'FFCDD2', align: 'center', fontFace: 'Arial' }); -} -// 각 슬라이드에 적용 -addConfidentialBadge(slide1); -``` - -#### 어두운 배경 테이블 -```javascript -// 테이블 컨테이너 -slide.addShape(pres.ShapeType.roundRect, { x: 5, y: 2.7, w: 4.5, h: 2.5, rectRadius: 0.1, fill: { color: '1a1a2e' } }); -// 헤더 -slide.addText('구분', { x: 5.2, y: 3.0, w: 1.5, h: 0.25, fontSize: 8, color: '888888', fontFace: 'Arial' }); -slide.addText('값 A', { x: 6.7, y: 3.0, w: 1.2, h: 0.25, fontSize: 8, bold: true, color: '4CAF50', align: 'center', fontFace: 'Arial' }); -// 구분선 -slide.addShape(pres.ShapeType.rect, { x: 5.2, y: 3.25, w: 4, h: 0.01, fill: { color: '333344' } }); -// 데이터 행 -slide.addText('항목', { x: 5.2, y: 3.35, w: 1.5, h: 0.3, fontSize: 9, color: 'CCCCCC', fontFace: 'Arial' }); -slide.addText('20%', { x: 6.7, y: 3.35, w: 1.2, h: 0.3, fontSize: 10, bold: true, color: '4CAF50', align: 'center', fontFace: 'Arial' }); -``` - -### 슬라이드 크기 참조 - -| 비율 | 크기 (pt) | 크기 (inch) | defineLayout | -|------|-----------|-------------|--------------| -| 16:9 | 720 x 405 | 10 x 5.625 | `{ width: 10, height: 5.625 }` | -| 4:3 | 720 x 540 | 10 x 7.5 | `{ width: 10, height: 7.5 }` | -| 16:10 | 720 x 450 | 10 x 6.25 | `{ width: 10, height: 6.25 }` | - -**중요**: `LAYOUT_WIDE`는 13.3" x 7.5"이므로 사용하지 마세요. 반드시 커스텀 레이아웃을 정의하세요. - ---- - -## 방법 B: HTML → PPTX 변환 (Playwright 필요) - -HTML 슬라이드를 Playwright 브라우저로 렌더링하여 PPTX로 변환합니다. -복잡한 CSS 레이아웃(flexbox, grid, 그라데이션)이 필요한 경우에 사용합니다. - -**전제 조건**: Playwright + Chromium 브라우저 + 시스템 라이브러리가 설치되어 있어야 합니다. -```bash -cd ~/.claude/skills/pptx-skill/scripts && npm install playwright pptxgenjs sharp -npx playwright install chromium -npx playwright install-deps chromium # 시스템 라이브러리 (sudo 필요) -``` - -### 사용법 - -#### 1단계: HTML 슬라이드 준비 - -```bash -ls slides/*.html -``` - -각 HTML 파일 규격: -- 크기: 720pt x 405pt (16:9 비율) -- 인코딩: UTF-8 -- 폰트: 웹 안전 폰트 사용 - -#### 2단계: 변환 스크립트 작성 및 실행 - -```javascript -const path = require('path'); -module.paths.unshift(path.join(require('os').homedir(), '.claude/skills/pptx-skill/scripts/node_modules')); - -const PptxGenJS = require('pptxgenjs'); -const html2pptx = require(path.join(require('os').homedir(), '.claude/skills/pptx-skill/scripts/html2pptx.js')); - -const pres = new PptxGenJS(); -pres.defineLayout({ name: 'CUSTOM_16x9', width: 10, height: 5.625 }); -pres.layout = 'CUSTOM_16x9'; - -await html2pptx('slides/slide-01.html', pres); -await html2pptx('slides/slide-02.html', pres); - -await pres.writeFile({ fileName: 'presentation.pptx' }); -``` - -#### 3단계: 결과 확인 - -```bash -python ~/.claude/skills/pptx-skill/scripts/thumbnail.py output.pptx thumbnail-grid -``` - -### HTML 작성 규칙 (방법 B 전용) - -1. **텍스트 요소에 border/background 금지** - div로 감싸기 -2. **불릿 기호(-, *, ., .)로 시작하는 텍스트 금지** - ul/ol 사용 -3. **모든 텍스트는 p/h1-h6 태그로 감싸기** -4. **table 태그 사용 금지** - div + flexbox로 대체 -5. **HTML body 크기와 PptxGenJS 레이아웃 일치 필수** -6. **빈 값 표시 시 하이픈(-) 금지** - 빈 문자열 또는 N/A 사용 - ---- - -## 공통 유틸리티 - -### thumbnail.py - 미리보기 생성 - -```bash -python ~/.claude/skills/pptx-skill/scripts/thumbnail.py presentation.pptx output-thumbnail -``` - -옵션: -- `--cols N`: 열 수 (기본 5, 범위 3-6) -- `--outline-placeholders`: 플레이스홀더 영역 표시 - -### pack.py / unpack.py - PPTX XML 편집 - -```bash -# PPTX 언패킹 (XML로 분해) -python ~/.claude/skills/pptx-skill/ooxml/scripts/unpack.py presentation.pptx output_dir - -# PPTX 패킹 (XML을 PPTX로 조립) -python ~/.claude/skills/pptx-skill/ooxml/scripts/pack.py input_dir presentation.pptx - -# 구조 검증 -python ~/.claude/skills/pptx-skill/ooxml/scripts/validate.py unpacked_dir --original presentation.pptx -``` - -## 필수 의존성 - -### Node.js 패키지 (방법 A/B 공통) -```bash -cd ~/.claude/skills/pptx-skill/scripts && npm install pptxgenjs -``` - -### 추가 패키지 (방법 B만) -```bash -cd ~/.claude/skills/pptx-skill/scripts && npm install playwright sharp -``` - -### Python 패키지 (thumbnail/ooxml 사용 시) -```bash -pip install pillow defusedxml -``` - -## 추가 문서 - -- [html2pptx.md](html2pptx.md) - HTML to PPTX 변환 상세 가이드 (방법 B) -- [ooxml.md](ooxml.md) - Office Open XML 기술 참조 diff --git a/.claude/skills/pptx-skill/html2pptx.md b/.claude/skills/pptx-skill/html2pptx.md deleted file mode 100755 index d59383b..0000000 --- a/.claude/skills/pptx-skill/html2pptx.md +++ /dev/null @@ -1,769 +0,0 @@ -# HTML to PowerPoint Guide - -Convert HTML slides to PowerPoint presentations with accurate positioning using the `html2pptx.js` library. - -## Table of Contents - -1. [Creating HTML Slides](#creating-html-slides) -2. [Using the html2pptx Library](#using-the-html2pptx-library) -3. [Using PptxGenJS](#using-pptxgenjs) - ---- - -## Creating HTML Slides - -Every HTML slide must include proper body dimensions: - -### Layout Dimensions - -- **16:9** (default): `width: 720pt; height: 405pt` -- **4:3**: `width: 720pt; height: 540pt` -- **16:10**: `width: 720pt; height: 450pt` - -### Supported Elements - -- `

      `, `

      `-`

      ` - Text with styling -- `
        `, `
          ` - Lists (never use manual bullets •, -, *) -- ``, `` - Bold text (inline formatting) -- ``, `` - Italic text (inline formatting) -- `` - Underlined text (inline formatting) -- `` - Inline formatting with CSS styles (bold, italic, underline, color) -- `
          ` - Line breaks -- `
          ` with bg/border - Becomes shape -- `` - Images -- `class="placeholder"` - Reserved space for charts (returns `{ id, x, y, w, h }`) - -### Critical Text Rules - -**ALL text MUST be inside `

          `, `

          `-`

          `, `
            `, or `
              ` tags:** -- ✅ Correct: `

              Text here

              ` -- ❌ Wrong: `
              Text here
              ` - **Text will NOT appear in PowerPoint** -- ❌ Wrong: `Text` - **Text will NOT appear in PowerPoint** -- Text in `
              ` or `` without a text tag will be silently ignored - -**NEVER use manual bullet symbols (•, -, *, ·, etc.)** - Use `
                ` or `
                  ` lists instead -- ❌ Wrong: `

                  - 항목

                  ` (hyphen/dash) -- ❌ Wrong: `

                  • 항목

                  ` (bullet) -- ❌ Wrong: `

                  * 항목

                  ` (asterisk) -- ❌ Wrong: `

                  · 항목

                  ` (middle dot) -- ✅ Correct: Use `
                  • 항목
                  ` or `margin-left` for indentation - -**Empty values should NOT use hyphen:** -- ❌ Wrong: `

                  -

                  ` (will cause bullet symbol error) -- ✅ Correct: `

                  ` (empty) or `

                  N/A

                  ` - -**ONLY use web-safe fonts that are universally available:** -- ✅ Web-safe fonts: `Arial`, `Helvetica`, `Times New Roman`, `Georgia`, `Courier New`, `Verdana`, `Tahoma`, `Trebuchet MS`, `Impact`, `Comic Sans MS` -- ❌ Wrong: `'Segoe UI'`, `'SF Pro'`, `'Roboto'`, custom fonts - **Might cause rendering issues** - -### Styling - -- Use `display: flex` on body to prevent margin collapse from breaking overflow validation -- Use `margin` for spacing (padding included in size) -- Inline formatting: Use ``, ``, `` tags OR `` with CSS styles - - `` supports: `font-weight: bold`, `font-style: italic`, `text-decoration: underline`, `color: #rrggbb` - - `` does NOT support: `margin`, `padding` (not supported in PowerPoint text runs) - - Example: `Bold blue text` -- Flexbox works - positions calculated from rendered layout -- Use hex colors with `#` prefix in CSS -- **Text alignment**: Use CSS `text-align` (`center`, `right`, etc.) when needed as a hint to PptxGenJS for text formatting if text lengths are slightly off - -### Shape Styling (DIV elements only) - -**IMPORTANT: Backgrounds, borders, and shadows only work on `
                  ` elements, NOT on text elements (`

                  `, `

                  `-`

                  `, `
                    `, `
                      `)** - -- **Backgrounds**: CSS `background` or `background-color` on `
                      ` elements only - - Example: `
                      ` - Creates a shape with background -- **Borders**: CSS `border` on `
                      ` elements converts to PowerPoint shape borders - - Supports uniform borders: `border: 2px solid #333333` - - Supports partial borders: `border-left`, `border-right`, `border-top`, `border-bottom` (rendered as line shapes) - - Example: `
                      ` -- **Border radius**: CSS `border-radius` on `
                      ` elements for rounded corners - - `border-radius: 50%` or higher creates circular shape - - Percentages <50% calculated relative to shape's smaller dimension - - Supports px and pt units (e.g., `border-radius: 8pt;`, `border-radius: 12px;`) - - Example: `
                      ` on 100x200px box = 25% of 100px = 25px radius -- **Box shadows**: CSS `box-shadow` on `
                      ` elements converts to PowerPoint shadows - - Supports outer shadows only (inset shadows are ignored to prevent corruption) - - Example: `
                      ` - - Note: Inset/inner shadows are not supported by PowerPoint and will be skipped - -### Icons & Gradients - -- **CRITICAL: Never use CSS gradients (`linear-gradient`, `radial-gradient`)** - They don't convert to PowerPoint -- **ALWAYS create gradient/icon PNGs FIRST using Sharp, then reference in HTML** -- For gradients: Rasterize SVG to PNG background images -- For icons: Rasterize react-icons SVG to PNG images -- All visual effects must be pre-rendered as raster images before HTML rendering - -**Rasterizing Icons with Sharp:** - -```javascript -const React = require('react'); -const ReactDOMServer = require('react-dom/server'); -const sharp = require('sharp'); -const { FaHome } = require('react-icons/fa'); - -async function rasterizeIconPng(IconComponent, color, size = "256", filename) { - const svgString = ReactDOMServer.renderToStaticMarkup( - React.createElement(IconComponent, { color: `#${color}`, size: size }) - ); - - // Convert SVG to PNG using Sharp - await sharp(Buffer.from(svgString)) - .png() - .toFile(filename); - - return filename; -} - -// Usage: Rasterize icon before using in HTML -const iconPath = await rasterizeIconPng(FaHome, "4472c4", "256", "home-icon.png"); -// Then reference in HTML: -``` - -**Rasterizing Gradients with Sharp:** - -```javascript -const sharp = require('sharp'); - -async function createGradientBackground(filename) { - const svg = ` - - - - - - - - `; - - await sharp(Buffer.from(svg)) - .png() - .toFile(filename); - - return filename; -} - -// Usage: Create gradient background before HTML -const bgPath = await createGradientBackground("gradient-bg.png"); -// Then in HTML: -``` - -### Example - -```html - - - - - - -
                      -

                      Recipe Title

                      -
                        -
                      • Item: Description
                      • -
                      -

                      Text with bold, italic, underline.

                      -
                      - - -
                      -

                      5

                      -
                      -
                      - - -``` - -## Using the html2pptx Library - -### Dependencies - -These libraries have been globally installed and are available to use: -- `pptxgenjs` -- `playwright` -- `sharp` - -### Basic Usage - -```javascript -const pptxgen = require('pptxgenjs'); -const html2pptx = require('./html2pptx'); - -const pptx = new pptxgen(); -pptx.layout = 'LAYOUT_16x9'; // Must match HTML body dimensions - -const { slide, placeholders } = await html2pptx('slide1.html', pptx); - -// Add chart to placeholder area -if (placeholders.length > 0) { - slide.addChart(pptx.charts.LINE, chartData, placeholders[0]); -} - -await pptx.writeFile('output.pptx'); -``` - -### API Reference - -#### Function Signature -```javascript -await html2pptx(htmlFile, pres, options) -``` - -#### Parameters -- `htmlFile` (string): Path to HTML file (absolute or relative) -- `pres` (pptxgen): PptxGenJS presentation instance with layout already set -- `options` (object, optional): - - `tmpDir` (string): Temporary directory for generated files (default: `process.env.TMPDIR || '/tmp'`) - - `slide` (object): Existing slide to reuse (default: creates new slide) - -#### Returns -```javascript -{ - slide: pptxgenSlide, // The created/updated slide - placeholders: [ // Array of placeholder positions - { id: string, x: number, y: number, w: number, h: number }, - ... - ] -} -``` - -### Validation - -The library automatically validates and collects all errors before throwing: - -1. **HTML dimensions must match presentation layout** - Reports dimension mismatches -2. **Content must not overflow body** - Reports overflow with exact measurements -3. **CSS gradients** - Reports unsupported gradient usage -4. **Text element styling** - Reports backgrounds/borders/shadows on text elements (only allowed on divs) - -**All validation errors are collected and reported together** in a single error message, allowing you to fix all issues at once instead of one at a time. - -### Working with Placeholders - -```javascript -const { slide, placeholders } = await html2pptx('slide.html', pptx); - -// Use first placeholder -slide.addChart(pptx.charts.BAR, data, placeholders[0]); - -// Find by ID -const chartArea = placeholders.find(p => p.id === 'chart-area'); -slide.addChart(pptx.charts.LINE, data, chartArea); -``` - -### Complete Example - -```javascript -const pptxgen = require('pptxgenjs'); -const html2pptx = require('./html2pptx'); - -async function createPresentation() { - const pptx = new pptxgen(); - pptx.layout = 'LAYOUT_16x9'; - pptx.author = 'Your Name'; - pptx.title = 'My Presentation'; - - // Slide 1: Title - const { slide: slide1 } = await html2pptx('slides/title.html', pptx); - - // Slide 2: Content with chart - const { slide: slide2, placeholders } = await html2pptx('slides/data.html', pptx); - - const chartData = [{ - name: 'Sales', - labels: ['Q1', 'Q2', 'Q3', 'Q4'], - values: [4500, 5500, 6200, 7100] - }]; - - slide2.addChart(pptx.charts.BAR, chartData, { - ...placeholders[0], - showTitle: true, - title: 'Quarterly Sales', - showCatAxisTitle: true, - catAxisTitle: 'Quarter', - showValAxisTitle: true, - valAxisTitle: 'Sales ($000s)' - }); - - // Save - await pptx.writeFile({ fileName: 'presentation.pptx' }); - console.log('Presentation created successfully!'); -} - -createPresentation().catch(console.error); -``` - -## Using PptxGenJS - -After converting HTML to slides with `html2pptx`, you'll use PptxGenJS to add dynamic content like charts, images, and additional elements. - -### ⚠️ Critical Rules - -#### Colors -- **NEVER use `#` prefix** with hex colors in PptxGenJS - causes file corruption -- ✅ Correct: `color: "FF0000"`, `fill: { color: "0066CC" }` -- ❌ Wrong: `color: "#FF0000"` (breaks document) - -### Adding Images - -Always calculate aspect ratios from actual image dimensions: - -```javascript -// Get image dimensions: identify image.png | grep -o '[0-9]* x [0-9]*' -const imgWidth = 1860, imgHeight = 1519; // From actual file -const aspectRatio = imgWidth / imgHeight; - -const h = 3; // Max height -const w = h * aspectRatio; -const x = (10 - w) / 2; // Center on 16:9 slide - -slide.addImage({ path: "chart.png", x, y: 1.5, w, h }); -``` - -### Adding Text - -```javascript -// Rich text with formatting -slide.addText([ - { text: "Bold ", options: { bold: true } }, - { text: "Italic ", options: { italic: true } }, - { text: "Normal" } -], { - x: 1, y: 2, w: 8, h: 1 -}); -``` - -### Adding Shapes - -```javascript -// Rectangle -slide.addShape(pptx.shapes.RECTANGLE, { - x: 1, y: 1, w: 3, h: 2, - fill: { color: "4472C4" }, - line: { color: "000000", width: 2 } -}); - -// Circle -slide.addShape(pptx.shapes.OVAL, { - x: 5, y: 1, w: 2, h: 2, - fill: { color: "ED7D31" } -}); - -// Rounded rectangle -slide.addShape(pptx.shapes.ROUNDED_RECTANGLE, { - x: 1, y: 4, w: 3, h: 1.5, - fill: { color: "70AD47" }, - rectRadius: 0.2 -}); -``` - -### Adding Charts - -**Required for most charts:** Axis labels using `catAxisTitle` (category) and `valAxisTitle` (value). - -**Chart Data Format:** -- Use **single series with all labels** for simple bar/line charts -- Each series creates a separate legend entry -- Labels array defines X-axis values - -**Time Series Data - Choose Correct Granularity:** -- **< 30 days**: Use daily grouping (e.g., "10-01", "10-02") - avoid monthly aggregation that creates single-point charts -- **30-365 days**: Use monthly grouping (e.g., "2024-01", "2024-02") -- **> 365 days**: Use yearly grouping (e.g., "2023", "2024") -- **Validate**: Charts with only 1 data point likely indicate incorrect aggregation for the time period - -```javascript -const { slide, placeholders } = await html2pptx('slide.html', pptx); - -// CORRECT: Single series with all labels -slide.addChart(pptx.charts.BAR, [{ - name: "Sales 2024", - labels: ["Q1", "Q2", "Q3", "Q4"], - values: [4500, 5500, 6200, 7100] -}], { - ...placeholders[0], // Use placeholder position - barDir: 'col', // 'col' = vertical bars, 'bar' = horizontal - showTitle: true, - title: 'Quarterly Sales', - showLegend: false, // No legend needed for single series - // Required axis labels - showCatAxisTitle: true, - catAxisTitle: 'Quarter', - showValAxisTitle: true, - valAxisTitle: 'Sales ($000s)', - // Optional: Control scaling (adjust min based on data range for better visualization) - valAxisMaxVal: 8000, - valAxisMinVal: 0, // Use 0 for counts/amounts; for clustered data (e.g., 4500-7100), consider starting closer to min value - valAxisMajorUnit: 2000, // Control y-axis label spacing to prevent crowding - catAxisLabelRotate: 45, // Rotate labels if crowded - dataLabelPosition: 'outEnd', - dataLabelColor: '000000', - // Use single color for single-series charts - chartColors: ["4472C4"] // All bars same color -}); -``` - -#### Scatter Chart - -**IMPORTANT**: Scatter chart data format is unusual - first series contains X-axis values, subsequent series contain Y-values: - -```javascript -// Prepare data -const data1 = [{ x: 10, y: 20 }, { x: 15, y: 25 }, { x: 20, y: 30 }]; -const data2 = [{ x: 12, y: 18 }, { x: 18, y: 22 }]; - -const allXValues = [...data1.map(d => d.x), ...data2.map(d => d.x)]; - -slide.addChart(pptx.charts.SCATTER, [ - { name: 'X-Axis', values: allXValues }, // First series = X values - { name: 'Series 1', values: data1.map(d => d.y) }, // Y values only - { name: 'Series 2', values: data2.map(d => d.y) } // Y values only -], { - x: 1, y: 1, w: 8, h: 4, - lineSize: 0, // 0 = no connecting lines - lineDataSymbol: 'circle', - lineDataSymbolSize: 6, - showCatAxisTitle: true, - catAxisTitle: 'X Axis', - showValAxisTitle: true, - valAxisTitle: 'Y Axis', - chartColors: ["4472C4", "ED7D31"] -}); -``` - -#### Line Chart - -```javascript -slide.addChart(pptx.charts.LINE, [{ - name: "Temperature", - labels: ["Jan", "Feb", "Mar", "Apr"], - values: [32, 35, 42, 55] -}], { - x: 1, y: 1, w: 8, h: 4, - lineSize: 4, - lineSmooth: true, - // Required axis labels - showCatAxisTitle: true, - catAxisTitle: 'Month', - showValAxisTitle: true, - valAxisTitle: 'Temperature (°F)', - // Optional: Y-axis range (set min based on data range for better visualization) - valAxisMinVal: 0, // For ranges starting at 0 (counts, percentages, etc.) - valAxisMaxVal: 60, - valAxisMajorUnit: 20, // Control y-axis label spacing to prevent crowding (e.g., 10, 20, 25) - // valAxisMinVal: 30, // PREFERRED: For data clustered in a range (e.g., 32-55 or ratings 3-5), start axis closer to min value to show variation - // Optional: Chart colors - chartColors: ["4472C4", "ED7D31", "A5A5A5"] -}); -``` - -#### Pie Chart (No Axis Labels Required) - -**CRITICAL**: Pie charts require a **single data series** with all categories in the `labels` array and corresponding values in the `values` array. - -```javascript -slide.addChart(pptx.charts.PIE, [{ - name: "Market Share", - labels: ["Product A", "Product B", "Other"], // All categories in one array - values: [35, 45, 20] // All values in one array -}], { - x: 2, y: 1, w: 6, h: 4, - showPercent: true, - showLegend: true, - legendPos: 'r', // right - chartColors: ["4472C4", "ED7D31", "A5A5A5"] -}); -``` - -#### Multiple Data Series - -```javascript -slide.addChart(pptx.charts.LINE, [ - { - name: "Product A", - labels: ["Q1", "Q2", "Q3", "Q4"], - values: [10, 20, 30, 40] - }, - { - name: "Product B", - labels: ["Q1", "Q2", "Q3", "Q4"], - values: [15, 25, 20, 35] - } -], { - x: 1, y: 1, w: 8, h: 4, - showCatAxisTitle: true, - catAxisTitle: 'Quarter', - showValAxisTitle: true, - valAxisTitle: 'Revenue ($M)' -}); -``` - -### Chart Colors - -**CRITICAL**: Use hex colors **without** the `#` prefix - including `#` causes file corruption. - -**Align chart colors with your chosen design palette**, ensuring sufficient contrast and distinctiveness for data visualization. Adjust colors for: -- Strong contrast between adjacent series -- Readability against slide backgrounds -- Accessibility (avoid red-green only combinations) - -```javascript -// Example: Ocean palette-inspired chart colors (adjusted for contrast) -const chartColors = ["16A085", "FF6B9D", "2C3E50", "F39C12", "9B59B6"]; - -// Single-series chart: Use one color for all bars/points -slide.addChart(pptx.charts.BAR, [{ - name: "Sales", - labels: ["Q1", "Q2", "Q3", "Q4"], - values: [4500, 5500, 6200, 7100] -}], { - ...placeholders[0], - chartColors: ["16A085"], // All bars same color - showLegend: false -}); - -// Multi-series chart: Each series gets a different color -slide.addChart(pptx.charts.LINE, [ - { name: "Product A", labels: ["Q1", "Q2", "Q3"], values: [10, 20, 30] }, - { name: "Product B", labels: ["Q1", "Q2", "Q3"], values: [15, 25, 20] } -], { - ...placeholders[0], - chartColors: ["16A085", "FF6B9D"] // One color per series -}); -``` - -### Adding Tables - -Tables can be added with basic or advanced formatting: - -#### Basic Table - -```javascript -slide.addTable([ - ["Header 1", "Header 2", "Header 3"], - ["Row 1, Col 1", "Row 1, Col 2", "Row 1, Col 3"], - ["Row 2, Col 1", "Row 2, Col 2", "Row 2, Col 3"] -], { - x: 0.5, - y: 1, - w: 9, - h: 3, - border: { pt: 1, color: "999999" }, - fill: { color: "F1F1F1" } -}); -``` - -#### Table with Custom Formatting - -```javascript -const tableData = [ - // Header row with custom styling - [ - { text: "Product", options: { fill: { color: "4472C4" }, color: "FFFFFF", bold: true } }, - { text: "Revenue", options: { fill: { color: "4472C4" }, color: "FFFFFF", bold: true } }, - { text: "Growth", options: { fill: { color: "4472C4" }, color: "FFFFFF", bold: true } } - ], - // Data rows - ["Product A", "$50M", "+15%"], - ["Product B", "$35M", "+22%"], - ["Product C", "$28M", "+8%"] -]; - -slide.addTable(tableData, { - x: 1, - y: 1.5, - w: 8, - h: 3, - colW: [3, 2.5, 2.5], // Column widths - rowH: [0.5, 0.6, 0.6, 0.6], // Row heights - border: { pt: 1, color: "CCCCCC" }, - align: "center", - valign: "middle", - fontSize: 14 -}); -``` - -#### Table with Merged Cells - -```javascript -const mergedTableData = [ - [ - { text: "Q1 Results", options: { colspan: 3, fill: { color: "4472C4" }, color: "FFFFFF", bold: true } } - ], - ["Product", "Sales", "Market Share"], - ["Product A", "$25M", "35%"], - ["Product B", "$18M", "25%"] -]; - -slide.addTable(mergedTableData, { - x: 1, - y: 1, - w: 8, - h: 2.5, - colW: [3, 2.5, 2.5], - border: { pt: 1, color: "DDDDDD" } -}); -``` - -### Table Options - -Common table options: -- `x, y, w, h` - Position and size -- `colW` - Array of column widths (in inches) -- `rowH` - Array of row heights (in inches) -- `border` - Border style: `{ pt: 1, color: "999999" }` -- `fill` - Background color (no # prefix) -- `align` - Text alignment: "left", "center", "right" -- `valign` - Vertical alignment: "top", "middle", "bottom" -- `fontSize` - Text size -- `autoPage` - Auto-create new slides if content overflows - ---- - -## Storyboard/Wireframe HTML Guide (스토리보드/와이어프레임) - -스토리보드나 와이어프레임 HTML 슬라이드 작성 시 참고 가이드입니다. - -### Recommended Structure - -```html - - - - - - - - - -
                      - -
                      - - -
                      -
                      -

                      Description

                      -
                      - -
                      - - -``` - -### Common UI Components - -#### Checkbox -```html - -
                      - - -
                      -``` - -#### Icons (using emoji) -```html -

                      ✏️

                      -

                      -

                      🔔

                      -

                      🔍

                      -

                      📅

                      -``` - -#### Numbered Badge -```html -
                      -
                      -

                      01

                      -
                      -
                      -

                      전체 ▼

                      -
                      -
                      -``` - -#### Status Badge -```html -
                      -

                      견적대기

                      -
                      -``` - -#### Sidebar Menu with Hierarchy -```html - -

                      입찰관리

                      - - -

                      거래처관리

                      -

                      현장설명회관리

                      - - -
                      -

                      견적관리

                      -
                      -``` - -#### Table with Flexbox -```html -
                      - -
                      -
                      -
                      -
                      -
                      -

                      컬럼1

                      -
                      -
                      -

                      컬럼2

                      -
                      -
                      - -
                      -
                      -
                      -
                      -
                      -

                      데이터1

                      -
                      -
                      -

                      데이터2

                      -
                      -
                      -
                      -``` - -### Common Mistakes to Avoid - -1. **Never use `-` to start text** (treated as bullet symbol) -2. **Never use `-` for empty values** (use empty string or "N/A") -3. **Never use `*` to mark important items** (use `[공통]` instead) -4. **Never apply border/background directly to `

                      ` tags** (wrap with `

                      `) -5. **Never use `` tags** (use flexbox instead) \ No newline at end of file diff --git a/.claude/skills/pptx-skill/ooxml.md b/.claude/skills/pptx-skill/ooxml.md deleted file mode 100755 index 951b3cf..0000000 --- a/.claude/skills/pptx-skill/ooxml.md +++ /dev/null @@ -1,427 +0,0 @@ -# Office Open XML Technical Reference for PowerPoint - -**Important: Read this entire document before starting.** Critical XML schema rules and formatting requirements are covered throughout. Incorrect implementation can create invalid PPTX files that PowerPoint cannot open. - -## Technical Guidelines - -### Schema Compliance -- **Element ordering in ``**: ``, ``, `` -- **Whitespace**: Add `xml:space='preserve'` to `` elements with leading/trailing spaces -- **Unicode**: Escape characters in ASCII content: `"` becomes `“` -- **Images**: Add to `ppt/media/`, reference in slide XML, set dimensions to fit slide bounds -- **Relationships**: Update `ppt/slides/_rels/slideN.xml.rels` for each slide's resources -- **Dirty attribute**: Add `dirty="0"` to `` and `` elements to indicate clean state - -## Presentation Structure - -### Basic Slide Structure -```xml - - - - - ... - ... - - - - -``` - -### Text Box / Shape with Text -```xml - - - - - - - - - - - - - - - - - - - - - - Slide Title - - - - -``` - -### Text Formatting -```xml - - - - Bold Text - - - - - - Italic Text - - - - - - Underlined - - - - - - - - - - Highlighted Text - - - - - - - - - - Colored Arial 24pt - - - - - - - - - - Formatted text - -``` - -### Lists -```xml - - - - - - - First bullet point - - - - - - - - - - First numbered item - - - - - - - - - - Indented bullet - - -``` - -### Shapes -```xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -``` - -### Images -```xml - - - - - - - - - - - - - - - - - - - - - - - - - - -``` - -### Tables -```xml - - - - - - - - - - - - - - - - - - - - - - - - - - - Cell 1 - - - - - - - - - - - Cell 2 - - - - - - - - - -``` - -### Slide Layouts - -```xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -``` - -## File Updates - -When adding content, update these files: - -**`ppt/_rels/presentation.xml.rels`:** -```xml - - -``` - -**`ppt/slides/_rels/slide1.xml.rels`:** -```xml - - -``` - -**`[Content_Types].xml`:** -```xml - - - -``` - -**`ppt/presentation.xml`:** -```xml - - - - -``` - -**`docProps/app.xml`:** Update slide count and statistics -```xml -2 -10 -50 -``` - -## Slide Operations - -### Adding a New Slide -When adding a slide to the end of the presentation: - -1. **Create the slide file** (`ppt/slides/slideN.xml`) -2. **Update `[Content_Types].xml`**: Add Override for the new slide -3. **Update `ppt/_rels/presentation.xml.rels`**: Add relationship for the new slide -4. **Update `ppt/presentation.xml`**: Add slide ID to `` -5. **Create slide relationships** (`ppt/slides/_rels/slideN.xml.rels`) if needed -6. **Update `docProps/app.xml`**: Increment slide count and update statistics (if present) - -### Duplicating a Slide -1. Copy the source slide XML file with a new name -2. Update all IDs in the new slide to be unique -3. Follow the "Adding a New Slide" steps above -4. **CRITICAL**: Remove or update any notes slide references in `_rels` files -5. Remove references to unused media files - -### Reordering Slides -1. **Update `ppt/presentation.xml`**: Reorder `` elements in `` -2. The order of `` elements determines slide order -3. Keep slide IDs and relationship IDs unchanged - -Example: -```xml - - - - - - - - - - - - - -``` - -### Deleting a Slide -1. **Remove from `ppt/presentation.xml`**: Delete the `` entry -2. **Remove from `ppt/_rels/presentation.xml.rels`**: Delete the relationship -3. **Remove from `[Content_Types].xml`**: Delete the Override entry -4. **Delete files**: Remove `ppt/slides/slideN.xml` and `ppt/slides/_rels/slideN.xml.rels` -5. **Update `docProps/app.xml`**: Decrement slide count and update statistics -6. **Clean up unused media**: Remove orphaned images from `ppt/media/` - -Note: Don't renumber remaining slides - keep their original IDs and filenames. - - -## Common Errors to Avoid - -- **Encodings**: Escape unicode characters in ASCII content: `"` becomes `“` -- **Images**: Add to `ppt/media/` and update relationship files -- **Lists**: Omit bullets from list headers -- **IDs**: Use valid hexadecimal values for UUIDs -- **Themes**: Check all themes in `theme` directory for colors - -## Validation Checklist for Template-Based Presentations - -### Before Packing, Always: -- **Clean unused resources**: Remove unreferenced media, fonts, and notes directories -- **Fix Content_Types.xml**: Declare ALL slides, layouts, and themes present in the package -- **Fix relationship IDs**: - - Remove font embed references if not using embedded fonts -- **Remove broken references**: Check all `_rels` files for references to deleted resources - -### Common Template Duplication Pitfalls: -- Multiple slides referencing the same notes slide after duplication -- Image/media references from template slides that no longer exist -- Font embedding references when fonts aren't included -- Missing slideLayout declarations for layouts 12-25 -- docProps directory may not unpack - this is optional \ No newline at end of file diff --git a/.claude/skills/pptx-skill/ooxml/scripts/pack.py b/.claude/skills/pptx-skill/ooxml/scripts/pack.py deleted file mode 100755 index 68bc088..0000000 --- a/.claude/skills/pptx-skill/ooxml/scripts/pack.py +++ /dev/null @@ -1,159 +0,0 @@ -#!/usr/bin/env python3 -""" -Tool to pack a directory into a .docx, .pptx, or .xlsx file with XML formatting undone. - -Example usage: - python pack.py [--force] -""" - -import argparse -import shutil -import subprocess -import sys -import tempfile -import defusedxml.minidom -import zipfile -from pathlib import Path - - -def main(): - parser = argparse.ArgumentParser(description="Pack a directory into an Office file") - parser.add_argument("input_directory", help="Unpacked Office document directory") - parser.add_argument("output_file", help="Output Office file (.docx/.pptx/.xlsx)") - parser.add_argument("--force", action="store_true", help="Skip validation") - args = parser.parse_args() - - try: - success = pack_document( - args.input_directory, args.output_file, validate=not args.force - ) - - # Show warning if validation was skipped - if args.force: - print("Warning: Skipped validation, file may be corrupt", file=sys.stderr) - # Exit with error if validation failed - elif not success: - print("Contents would produce a corrupt file.", file=sys.stderr) - print("Please validate XML before repacking.", file=sys.stderr) - print("Use --force to skip validation and pack anyway.", file=sys.stderr) - sys.exit(1) - - except ValueError as e: - sys.exit(f"Error: {e}") - - -def pack_document(input_dir, output_file, validate=False): - """Pack a directory into an Office file (.docx/.pptx/.xlsx). - - Args: - input_dir: Path to unpacked Office document directory - output_file: Path to output Office file - validate: If True, validates with soffice (default: False) - - Returns: - bool: True if successful, False if validation failed - """ - input_dir = Path(input_dir) - output_file = Path(output_file) - - if not input_dir.is_dir(): - raise ValueError(f"{input_dir} is not a directory") - if output_file.suffix.lower() not in {".docx", ".pptx", ".xlsx"}: - raise ValueError(f"{output_file} must be a .docx, .pptx, or .xlsx file") - - # Work in temporary directory to avoid modifying original - with tempfile.TemporaryDirectory() as temp_dir: - temp_content_dir = Path(temp_dir) / "content" - shutil.copytree(input_dir, temp_content_dir) - - # Process XML files to remove pretty-printing whitespace - for pattern in ["*.xml", "*.rels"]: - for xml_file in temp_content_dir.rglob(pattern): - condense_xml(xml_file) - - # Create final Office file as zip archive - output_file.parent.mkdir(parents=True, exist_ok=True) - with zipfile.ZipFile(output_file, "w", zipfile.ZIP_DEFLATED) as zf: - for f in temp_content_dir.rglob("*"): - if f.is_file(): - zf.write(f, f.relative_to(temp_content_dir)) - - # Validate if requested - if validate: - if not validate_document(output_file): - output_file.unlink() # Delete the corrupt file - return False - - return True - - -def validate_document(doc_path): - """Validate document by converting to HTML with soffice.""" - # Determine the correct filter based on file extension - match doc_path.suffix.lower(): - case ".docx": - filter_name = "html:HTML" - case ".pptx": - filter_name = "html:impress_html_Export" - case ".xlsx": - filter_name = "html:HTML (StarCalc)" - - with tempfile.TemporaryDirectory() as temp_dir: - try: - result = subprocess.run( - [ - "soffice", - "--headless", - "--convert-to", - filter_name, - "--outdir", - temp_dir, - str(doc_path), - ], - capture_output=True, - timeout=10, - text=True, - ) - if not (Path(temp_dir) / f"{doc_path.stem}.html").exists(): - error_msg = result.stderr.strip() or "Document validation failed" - print(f"Validation error: {error_msg}", file=sys.stderr) - return False - return True - except FileNotFoundError: - print("Warning: soffice not found. Skipping validation.", file=sys.stderr) - return True - except subprocess.TimeoutExpired: - print("Validation error: Timeout during conversion", file=sys.stderr) - return False - except Exception as e: - print(f"Validation error: {e}", file=sys.stderr) - return False - - -def condense_xml(xml_file): - """Strip unnecessary whitespace and remove comments.""" - with open(xml_file, "r", encoding="utf-8") as f: - dom = defusedxml.minidom.parse(f) - - # Process each element to remove whitespace and comments - for element in dom.getElementsByTagName("*"): - # Skip w:t elements and their processing - if element.tagName.endswith(":t"): - continue - - # Remove whitespace-only text nodes and comment nodes - for child in list(element.childNodes): - if ( - child.nodeType == child.TEXT_NODE - and child.nodeValue - and child.nodeValue.strip() == "" - ) or child.nodeType == child.COMMENT_NODE: - element.removeChild(child) - - # Write back the condensed XML - with open(xml_file, "wb") as f: - f.write(dom.toxml(encoding="UTF-8")) - - -if __name__ == "__main__": - main() diff --git a/.claude/skills/pptx-skill/ooxml/scripts/unpack.py b/.claude/skills/pptx-skill/ooxml/scripts/unpack.py deleted file mode 100755 index 4938798..0000000 --- a/.claude/skills/pptx-skill/ooxml/scripts/unpack.py +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env python3 -"""Unpack and format XML contents of Office files (.docx, .pptx, .xlsx)""" - -import random -import sys -import defusedxml.minidom -import zipfile -from pathlib import Path - -# Get command line arguments -assert len(sys.argv) == 3, "Usage: python unpack.py " -input_file, output_dir = sys.argv[1], sys.argv[2] - -# Extract and format -output_path = Path(output_dir) -output_path.mkdir(parents=True, exist_ok=True) -zipfile.ZipFile(input_file).extractall(output_path) - -# Pretty print all XML files -xml_files = list(output_path.rglob("*.xml")) + list(output_path.rglob("*.rels")) -for xml_file in xml_files: - content = xml_file.read_text(encoding="utf-8") - dom = defusedxml.minidom.parseString(content) - xml_file.write_bytes(dom.toprettyxml(indent=" ", encoding="ascii")) - -# For .docx files, suggest an RSID for tracked changes -if input_file.endswith(".docx"): - suggested_rsid = "".join(random.choices("0123456789ABCDEF", k=8)) - print(f"Suggested RSID for edit session: {suggested_rsid}") diff --git a/.claude/skills/pptx-skill/ooxml/scripts/validate.py b/.claude/skills/pptx-skill/ooxml/scripts/validate.py deleted file mode 100755 index 508c589..0000000 --- a/.claude/skills/pptx-skill/ooxml/scripts/validate.py +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/env python3 -""" -Command line tool to validate Office document XML files against XSD schemas and tracked changes. - -Usage: - python validate.py --original -""" - -import argparse -import sys -from pathlib import Path - -from validation import DOCXSchemaValidator, PPTXSchemaValidator, RedliningValidator - - -def main(): - parser = argparse.ArgumentParser(description="Validate Office document XML files") - parser.add_argument( - "unpacked_dir", - help="Path to unpacked Office document directory", - ) - parser.add_argument( - "--original", - required=True, - help="Path to original file (.docx/.pptx/.xlsx)", - ) - parser.add_argument( - "-v", - "--verbose", - action="store_true", - help="Enable verbose output", - ) - args = parser.parse_args() - - # Validate paths - unpacked_dir = Path(args.unpacked_dir) - original_file = Path(args.original) - file_extension = original_file.suffix.lower() - assert unpacked_dir.is_dir(), f"Error: {unpacked_dir} is not a directory" - assert original_file.is_file(), f"Error: {original_file} is not a file" - assert file_extension in [".docx", ".pptx", ".xlsx"], ( - f"Error: {original_file} must be a .docx, .pptx, or .xlsx file" - ) - - # Run validations - match file_extension: - case ".docx": - validators = [DOCXSchemaValidator, RedliningValidator] - case ".pptx": - validators = [PPTXSchemaValidator] - case _: - print(f"Error: Validation not supported for file type {file_extension}") - sys.exit(1) - - # Run validators - success = True - for V in validators: - validator = V(unpacked_dir, original_file, verbose=args.verbose) - if not validator.validate(): - success = False - - if success: - print("All validations PASSED!") - - sys.exit(0 if success else 1) - - -if __name__ == "__main__": - main() diff --git a/.claude/skills/pptx-skill/scripts/enhanced-pptx-generator.js b/.claude/skills/pptx-skill/scripts/enhanced-pptx-generator.js deleted file mode 100755 index c76eb0c..0000000 --- a/.claude/skills/pptx-skill/scripts/enhanced-pptx-generator.js +++ /dev/null @@ -1,359 +0,0 @@ -/** - * Enhanced PPTX Generator - 재사용 가능한 프레젠테이션 생성 모듈 - * 카드형 배경, 아이콘, 통계 카드 등 시각적 요소를 포함한 PowerPoint 생성 - */ - -const pptxgen = require('pptxgenjs'); - -class EnhancedPPTXGenerator { - constructor(options = {}) { - this.pres = new pptxgen(); - this.pres.layout = options.layout || 'LAYOUT_16x9'; - this.pres.author = options.author || 'Enhanced PPTX Generator'; - this.pres.title = options.title || 'Generated Presentation'; - - // 기본 브랜드 색상 설정 - this.brandColor = options.brandColor || 'FF0000'; - this.backgroundColor = options.backgroundColor || 'ffffff'; - this.cardColor = options.cardColor || 'f8f9fa'; - this.borderColor = options.borderColor || 'e2e8f0'; - } - - /** - * 카드형 배경 도형 추가 - */ - addCard(slide, x, y, width, height, options = {}) { - const cardOptions = { - x, y, w: width, h: height, - fill: { color: options.fillColor || this.cardColor }, - line: { - color: options.borderColor || this.borderColor, - width: options.borderWidth || 1 - }, - rectRadius: options.radius || 0.15 - }; - - slide.addShape(this.pres.ShapeType.rect, cardOptions); - - // 상단 컬러 바 추가 (옵션) - if (options.topBar) { - slide.addShape(this.pres.ShapeType.rect, { - x, y, w: width, h: options.topBarHeight || 0.15, - fill: { color: options.topBarColor || this.brandColor } - }); - } - - return cardOptions; - } - - /** - * 원형 아이콘 배경 추가 - */ - addIconBackground(slide, x, y, size, iconText, options = {}) { - // 원형 배경 - slide.addShape(this.pres.ShapeType.ellipse, { - x, y, w: size, h: size, - fill: { color: options.backgroundColor || this.brandColor }, - line: { color: options.borderColor || this.brandColor, width: options.borderWidth || 0 } - }); - - // 아이콘 텍스트 - slide.addText(iconText, { - x, y, w: size, h: size, - fontSize: options.fontSize || (size * 40), // 크기에 비례한 폰트 - color: options.textColor || 'ffffff', - align: 'center', - valign: 'middle' - }); - - return { x, y, size }; - } - - /** - * 통계 카드 추가 - */ - addStatCard(slide, x, y, width, height, statData, options = {}) { - const { number, description, icon, color } = statData; - - // 카드 배경 - slide.addShape(this.pres.ShapeType.rect, { - x, y, w: width, h: height, - fill: { color: options.backgroundColor || 'ffffff' }, - line: { color: color || this.brandColor, width: options.borderWidth || 2 }, - rectRadius: options.radius || 0.15 - }); - - // 아이콘 (선택사항) - if (icon) { - slide.addText(icon, { - x: x + 0.1, y: y + 0.1, w: 0.5, h: 0.5, - fontSize: options.iconSize || 24 - }); - } - - // 숫자/통계 - slide.addText(number, { - x: x + (icon ? 0.7 : 0.1), - y: y + 0.15, - w: width - (icon ? 0.8 : 0.2), - h: height * 0.4, - fontSize: options.numberSize || 18, - bold: true, - color: color || this.brandColor, - align: 'center' - }); - - // 설명 - slide.addText(description, { - x: x + 0.1, - y: y + height * 0.6, - w: width - 0.2, - h: height * 0.3, - fontSize: options.descriptionSize || 11, - color: '666666', - align: 'center' - }); - - return { x, y, width, height }; - } - - /** - * 타임라인 단계 추가 - */ - addTimelineStep(slide, x, y, stepNumber, title, description, options = {}) { - const stepColor = options.completed ? this.brandColor : 'f0f0f0'; - const textColor = options.completed ? 'ffffff' : '666666'; - - // 원형 단계 번호 - this.addIconBackground(slide, x, y, 0.6, stepNumber.toString(), { - backgroundColor: stepColor, - textColor: textColor, - fontSize: 20 - }); - - // 제목 - slide.addText(title, { - x: x + 0.8, y: y, w: 7, h: 0.4, - fontSize: 16, bold: true, color: this.brandColor - }); - - // 설명 - slide.addText(description, { - x: x + 0.8, y: y + 0.4, w: 7, h: 0.4, - fontSize: 12, color: '666666' - }); - - return { x, y }; - } - - /** - * 제목 슬라이드 생성 - */ - createTitleSlide(mainTitle, subtitle, description, options = {}) { - const slide = this.pres.addSlide(); - slide.background = { color: this.backgroundColor }; - - // 메인 카드 - this.addCard(slide, 1.5, 1, 7, 4, { - topBar: true, - topBarColor: this.brandColor - }); - - // 메인 제목 - slide.addText(mainTitle, { - x: 1.5, y: 1.8, w: 7, h: 1, - fontSize: options.titleSize || 48, - bold: true, - color: '282828', - align: 'center' - }); - - // 부제목 - if (subtitle) { - slide.addText(subtitle, { - x: 1.5, y: 2.8, w: 7, h: 0.8, - fontSize: options.subtitleSize || 24, - bold: true, - color: this.brandColor, - align: 'center' - }); - } - - // 설명 - if (description) { - slide.addText(description, { - x: 1.5, y: 3.6, w: 7, h: 0.6, - fontSize: options.descriptionSize || 16, - color: '666666', - align: 'center' - }); - } - - return slide; - } - - /** - * 콘텐츠 슬라이드 생성 - */ - createContentSlide(title, items = [], options = {}) { - const slide = this.pres.addSlide(); - slide.background = { color: this.backgroundColor }; - - // 제목 - slide.addText(title, { - x: 0.5, y: 0.5, w: 9, h: 0.8, - fontSize: options.titleSize || 28, - bold: true, - color: '282828' - }); - - // 아이템들을 카드 형태로 배치 - items.forEach((item, index) => { - const cols = options.columns || 2; - const x = 0.5 + (index % cols) * (9 / cols); - const y = 1.5 + Math.floor(index / cols) * (options.itemHeight || 1.5); - const width = (9 / cols) - 0.3; - const height = options.itemHeight || 1.3; - - this.addCard(slide, x, y, width, height, { - topBar: true - }); - - // 아이콘 - if (item.icon) { - this.addIconBackground(slide, x + 0.2, y + 0.2, 0.6, item.icon, { - backgroundColor: this.brandColor - }); - } - - // 제목 - slide.addText(item.title, { - x: x + (item.icon ? 1 : 0.3), - y: y + 0.3, - w: width - (item.icon ? 1.3 : 0.6), - h: 0.4, - fontSize: 16, bold: true, color: this.brandColor - }); - - // 설명 - if (item.description) { - slide.addText(item.description, { - x: x + 0.3, - y: y + 0.8, - w: width - 0.6, - h: 0.4, - fontSize: 11, color: '666666' - }); - } - }); - - return slide; - } - - /** - * 통계 슬라이드 생성 - */ - createStatsSlide(title, stats = [], options = {}) { - const slide = this.pres.addSlide(); - slide.background = { color: this.backgroundColor }; - - // 제목 - slide.addText(title, { - x: 0.5, y: 0.5, w: 9, h: 0.8, - fontSize: options.titleSize || 28, - bold: true, - color: '282828' - }); - - // 통계 카드들 - stats.forEach((stat, index) => { - const x = 0.25 + index * (9 / stats.length); - const width = (9 / stats.length) - 0.3; - - this.addStatCard(slide, x, 2, width, 1.5, stat); - }); - - return slide; - } - - /** - * 파일 저장 - */ - async save(filename) { - const fs = require('fs'); - const path = require('path'); - - // pptx 폴더 경로 생성 - const pptxDir = 'pptx'; - if (!fs.existsSync(pptxDir)) { - fs.mkdirSync(pptxDir, { recursive: true }); - } - - // 파일 경로 설정 - const fullPath = path.join(pptxDir, filename); - - await this.pres.writeFile({ fileName: fullPath }); - return { - filename: fullPath, - slideCount: this.pres.slides.length, - success: true - }; - } - - /** - * 슬라이드 수 반환 - */ - getSlideCount() { - return this.pres.slides.length; - } -} - -// 편의 함수들 -const createJoCodingPresentation = async (options = {}) => { - const generator = new EnhancedPPTXGenerator({ - title: '조코딩(조웅현) - 국내 1위 코딩 교육 유튜버', - author: '조코딩', - brandColor: 'FF0000', - ...options - }); - - // 슬라이드 1: 표지 - generator.createTitleSlide( - '조코딩', - 'JoCoding', - '국내 1위 코딩 교육 유튜버' - ); - - // 슬라이드 2: 프로필 - const profileItems = [ - { - icon: '👤', - title: '기본 정보', - description: '• 실명: 조웅현\n• 전공: 고려대 환경생태공학부\n• 경력: 비전공자 → 개발자 → 교육자' - }, - { - icon: '🚀', - title: '현재 활동', - description: '• 유튜브: 68만 구독자\n• 회사: ㈜코드마피아 대표\n• 서비스: 조카소 AI 개발/운영' - } - ]; - - generator.createContentSlide('프로필 개요', profileItems); - - // 슬라이드 3: 통계 - const stats = [ - { number: '68만+', description: '유튜브 구독자', icon: '📺', color: 'FF0000' }, - { number: '2019', description: '시작 연도', icon: '🗓️', color: '666666' }, - { number: '2,000만+', description: '동물상 테스트', icon: '🤖', color: 'ff6600' }, - { number: '1,000+', description: '교육생', icon: '🎓', color: '00aa00' } - ]; - - generator.createStatsSlide('주요 성과', stats); - - return await generator.save(options.filename || '조코딩_프레젠테이션_모듈생성.pptx'); -}; - -module.exports = { - EnhancedPPTXGenerator, - createJoCodingPresentation -}; \ No newline at end of file diff --git a/.claude/skills/pptx-skill/scripts/html2pptx.cjs b/.claude/skills/pptx-skill/scripts/html2pptx.cjs deleted file mode 100755 index 437bf7c..0000000 --- a/.claude/skills/pptx-skill/scripts/html2pptx.cjs +++ /dev/null @@ -1,979 +0,0 @@ -/** - * html2pptx - Convert HTML slide to pptxgenjs slide with positioned elements - * - * USAGE: - * const pptx = new pptxgen(); - * pptx.layout = 'LAYOUT_16x9'; // Must match HTML body dimensions - * - * const { slide, placeholders } = await html2pptx('slide.html', pptx); - * slide.addChart(pptx.charts.LINE, data, placeholders[0]); - * - * await pptx.writeFile('output.pptx'); - * - * FEATURES: - * - Converts HTML to PowerPoint with accurate positioning - * - Supports text, images, shapes, and bullet lists - * - Extracts placeholder elements (class="placeholder") with positions - * - Handles CSS gradients, borders, and margins - * - * VALIDATION: - * - Uses body width/height from HTML for viewport sizing - * - Throws error if HTML dimensions don't match presentation layout - * - Throws error if content overflows body (with overflow details) - * - * RETURNS: - * { slide, placeholders } where placeholders is an array of { id, x, y, w, h } - */ - -const { chromium } = require('playwright'); -const path = require('path'); -const sharp = require('sharp'); - -const PT_PER_PX = 0.75; -const PX_PER_IN = 96; -const EMU_PER_IN = 914400; - -// Helper: Get body dimensions and check for overflow -async function getBodyDimensions(page) { - const bodyDimensions = await page.evaluate(() => { - const body = document.body; - const style = window.getComputedStyle(body); - - return { - width: parseFloat(style.width), - height: parseFloat(style.height), - scrollWidth: body.scrollWidth, - scrollHeight: body.scrollHeight - }; - }); - - const errors = []; - const widthOverflowPx = Math.max(0, bodyDimensions.scrollWidth - bodyDimensions.width - 1); - const heightOverflowPx = Math.max(0, bodyDimensions.scrollHeight - bodyDimensions.height - 1); - - const widthOverflowPt = widthOverflowPx * PT_PER_PX; - const heightOverflowPt = heightOverflowPx * PT_PER_PX; - - if (widthOverflowPt > 0 || heightOverflowPt > 0) { - const directions = []; - if (widthOverflowPt > 0) directions.push(`${widthOverflowPt.toFixed(1)}pt horizontally`); - if (heightOverflowPt > 0) directions.push(`${heightOverflowPt.toFixed(1)}pt vertically`); - const reminder = heightOverflowPt > 0 ? ' (Remember: leave 0.5" margin at bottom of slide)' : ''; - errors.push(`HTML content overflows body by ${directions.join(' and ')}${reminder}`); - } - - return { ...bodyDimensions, errors }; -} - -// Helper: Validate dimensions match presentation layout -function validateDimensions(bodyDimensions, pres) { - const errors = []; - const widthInches = bodyDimensions.width / PX_PER_IN; - const heightInches = bodyDimensions.height / PX_PER_IN; - - if (pres.presLayout) { - const layoutWidth = pres.presLayout.width / EMU_PER_IN; - const layoutHeight = pres.presLayout.height / EMU_PER_IN; - - if (Math.abs(layoutWidth - widthInches) > 0.1 || Math.abs(layoutHeight - heightInches) > 0.1) { - errors.push( - `HTML dimensions (${widthInches.toFixed(1)}" × ${heightInches.toFixed(1)}") ` + - `don't match presentation layout (${layoutWidth.toFixed(1)}" × ${layoutHeight.toFixed(1)}")` - ); - } - } - return errors; -} - -function validateTextBoxPosition(slideData, bodyDimensions) { - const errors = []; - const slideHeightInches = bodyDimensions.height / PX_PER_IN; - const minBottomMargin = 0.5; // 0.5 inches from bottom - - for (const el of slideData.elements) { - // Check text elements (p, h1-h6, list) - if (['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'list'].includes(el.type)) { - const fontSize = el.style?.fontSize || 0; - const bottomEdge = el.position.y + el.position.h; - const distanceFromBottom = slideHeightInches - bottomEdge; - - if (fontSize > 12 && distanceFromBottom < minBottomMargin) { - const getText = () => { - if (typeof el.text === 'string') return el.text; - if (Array.isArray(el.text)) return el.text.find(t => t.text)?.text || ''; - if (Array.isArray(el.items)) return el.items.find(item => item.text)?.text || ''; - return ''; - }; - const textPrefix = getText().substring(0, 50) + (getText().length > 50 ? '...' : ''); - - errors.push( - `Text box "${textPrefix}" ends too close to bottom edge ` + - `(${distanceFromBottom.toFixed(2)}" from bottom, minimum ${minBottomMargin}" required)` - ); - } - } - } - - return errors; -} - -// Helper: Add background to slide -async function addBackground(slideData, targetSlide, tmpDir) { - if (slideData.background.type === 'image' && slideData.background.path) { - let imagePath = slideData.background.path.startsWith('file://') - ? slideData.background.path.replace('file://', '') - : slideData.background.path; - targetSlide.background = { path: imagePath }; - } else if (slideData.background.type === 'color' && slideData.background.value) { - targetSlide.background = { color: slideData.background.value }; - } -} - -// Helper: Add elements to slide -function addElements(slideData, targetSlide, pres) { - for (const el of slideData.elements) { - if (el.type === 'image') { - let imagePath = el.src.startsWith('file://') ? el.src.replace('file://', '') : el.src; - targetSlide.addImage({ - path: imagePath, - x: el.position.x, - y: el.position.y, - w: el.position.w, - h: el.position.h - }); - } else if (el.type === 'line') { - targetSlide.addShape(pres.ShapeType.line, { - x: el.x1, - y: el.y1, - w: el.x2 - el.x1, - h: el.y2 - el.y1, - line: { color: el.color, width: el.width } - }); - } else if (el.type === 'shape') { - const shapeOptions = { - x: el.position.x, - y: el.position.y, - w: el.position.w, - h: el.position.h, - shape: el.shape.rectRadius > 0 ? pres.ShapeType.roundRect : pres.ShapeType.rect - }; - - if (el.shape.fill) { - shapeOptions.fill = { color: el.shape.fill }; - if (el.shape.transparency != null) shapeOptions.fill.transparency = el.shape.transparency; - } - if (el.shape.line) shapeOptions.line = el.shape.line; - if (el.shape.rectRadius > 0) shapeOptions.rectRadius = el.shape.rectRadius; - if (el.shape.shadow) shapeOptions.shadow = el.shape.shadow; - - targetSlide.addText(el.text || '', shapeOptions); - } else if (el.type === 'list') { - const listOptions = { - x: el.position.x, - y: el.position.y, - w: el.position.w, - h: el.position.h, - fontSize: el.style.fontSize, - fontFace: el.style.fontFace, - color: el.style.color, - align: el.style.align, - valign: 'top', - lineSpacing: el.style.lineSpacing, - paraSpaceBefore: el.style.paraSpaceBefore, - paraSpaceAfter: el.style.paraSpaceAfter, - margin: el.style.margin - }; - if (el.style.margin) listOptions.margin = el.style.margin; - targetSlide.addText(el.items, listOptions); - } else { - // Check if text is single-line (height suggests one line) - const lineHeight = el.style.lineSpacing || el.style.fontSize * 1.2; - const isSingleLine = el.position.h <= lineHeight * 1.5; - - let adjustedX = el.position.x; - let adjustedW = el.position.w; - - // Make single-line text 2% wider to account for underestimate - if (isSingleLine) { - const widthIncrease = el.position.w * 0.02; - const align = el.style.align; - - if (align === 'center') { - // Center: expand both sides - adjustedX = el.position.x - (widthIncrease / 2); - adjustedW = el.position.w + widthIncrease; - } else if (align === 'right') { - // Right: expand to the left - adjustedX = el.position.x - widthIncrease; - adjustedW = el.position.w + widthIncrease; - } else { - // Left (default): expand to the right - adjustedW = el.position.w + widthIncrease; - } - } - - const textOptions = { - x: adjustedX, - y: el.position.y, - w: adjustedW, - h: el.position.h, - fontSize: el.style.fontSize, - fontFace: el.style.fontFace, - color: el.style.color, - bold: el.style.bold, - italic: el.style.italic, - underline: el.style.underline, - valign: 'top', - lineSpacing: el.style.lineSpacing, - paraSpaceBefore: el.style.paraSpaceBefore, - paraSpaceAfter: el.style.paraSpaceAfter, - inset: 0 // Remove default PowerPoint internal padding - }; - - if (el.style.align) textOptions.align = el.style.align; - if (el.style.margin) textOptions.margin = el.style.margin; - if (el.style.rotate !== undefined) textOptions.rotate = el.style.rotate; - if (el.style.transparency !== null && el.style.transparency !== undefined) textOptions.transparency = el.style.transparency; - - targetSlide.addText(el.text, textOptions); - } - } -} - -// Helper: Extract slide data from HTML page -async function extractSlideData(page) { - return await page.evaluate(() => { - const PT_PER_PX = 0.75; - const PX_PER_IN = 96; - - // Fonts that are single-weight and should not have bold applied - // (applying bold causes PowerPoint to use faux bold which makes text wider) - const SINGLE_WEIGHT_FONTS = ['impact']; - - // Helper: Check if a font should skip bold formatting - const shouldSkipBold = (fontFamily) => { - if (!fontFamily) return false; - const normalizedFont = fontFamily.toLowerCase().replace(/['"]/g, '').split(',')[0].trim(); - return SINGLE_WEIGHT_FONTS.includes(normalizedFont); - }; - - // Unit conversion helpers - const pxToInch = (px) => px / PX_PER_IN; - const pxToPoints = (pxStr) => parseFloat(pxStr) * PT_PER_PX; - const rgbToHex = (rgbStr) => { - // Handle transparent backgrounds by defaulting to white - if (rgbStr === 'rgba(0, 0, 0, 0)' || rgbStr === 'transparent') return 'FFFFFF'; - - const match = rgbStr.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/); - if (!match) return 'FFFFFF'; - return match.slice(1).map(n => parseInt(n).toString(16).padStart(2, '0')).join(''); - }; - - const extractAlpha = (rgbStr) => { - const match = rgbStr.match(/rgba\((\d+),\s*(\d+),\s*(\d+),\s*([\d.]+)\)/); - if (!match || !match[4]) return null; - const alpha = parseFloat(match[4]); - return Math.round((1 - alpha) * 100); - }; - - const applyTextTransform = (text, textTransform) => { - if (textTransform === 'uppercase') return text.toUpperCase(); - if (textTransform === 'lowercase') return text.toLowerCase(); - if (textTransform === 'capitalize') { - return text.replace(/\b\w/g, c => c.toUpperCase()); - } - return text; - }; - - // Extract rotation angle from CSS transform and writing-mode - const getRotation = (transform, writingMode) => { - let angle = 0; - - // Handle writing-mode first - // PowerPoint: 90° = text rotated 90° clockwise (reads top to bottom, letters upright) - // PowerPoint: 270° = text rotated 270° clockwise (reads bottom to top, letters upright) - if (writingMode === 'vertical-rl') { - // vertical-rl alone = text reads top to bottom = 90° in PowerPoint - angle = 90; - } else if (writingMode === 'vertical-lr') { - // vertical-lr alone = text reads bottom to top = 270° in PowerPoint - angle = 270; - } - - // Then add any transform rotation - if (transform && transform !== 'none') { - // Try to match rotate() function - const rotateMatch = transform.match(/rotate\((-?\d+(?:\.\d+)?)deg\)/); - if (rotateMatch) { - angle += parseFloat(rotateMatch[1]); - } else { - // Browser may compute as matrix - extract rotation from matrix - const matrixMatch = transform.match(/matrix\(([^)]+)\)/); - if (matrixMatch) { - const values = matrixMatch[1].split(',').map(parseFloat); - // matrix(a, b, c, d, e, f) where rotation = atan2(b, a) - const matrixAngle = Math.atan2(values[1], values[0]) * (180 / Math.PI); - angle += Math.round(matrixAngle); - } - } - } - - // Normalize to 0-359 range - angle = angle % 360; - if (angle < 0) angle += 360; - - return angle === 0 ? null : angle; - }; - - // Get position/dimensions accounting for rotation - const getPositionAndSize = (el, rect, rotation) => { - if (rotation === null) { - return { x: rect.left, y: rect.top, w: rect.width, h: rect.height }; - } - - // For 90° or 270° rotations, swap width and height - // because PowerPoint applies rotation to the original (unrotated) box - const isVertical = rotation === 90 || rotation === 270; - - if (isVertical) { - // The browser shows us the rotated dimensions (tall box for vertical text) - // But PowerPoint needs the pre-rotation dimensions (wide box that will be rotated) - // So we swap: browser's height becomes PPT's width, browser's width becomes PPT's height - const centerX = rect.left + rect.width / 2; - const centerY = rect.top + rect.height / 2; - - return { - x: centerX - rect.height / 2, - y: centerY - rect.width / 2, - w: rect.height, - h: rect.width - }; - } - - // For other rotations, use element's offset dimensions - const centerX = rect.left + rect.width / 2; - const centerY = rect.top + rect.height / 2; - return { - x: centerX - el.offsetWidth / 2, - y: centerY - el.offsetHeight / 2, - w: el.offsetWidth, - h: el.offsetHeight - }; - }; - - // Parse CSS box-shadow into PptxGenJS shadow properties - const parseBoxShadow = (boxShadow) => { - if (!boxShadow || boxShadow === 'none') return null; - - // Browser computed style format: "rgba(0, 0, 0, 0.3) 2px 2px 8px 0px [inset]" - // CSS format: "[inset] 2px 2px 8px 0px rgba(0, 0, 0, 0.3)" - - const insetMatch = boxShadow.match(/inset/); - - // IMPORTANT: PptxGenJS/PowerPoint doesn't properly support inset shadows - // Only process outer shadows to avoid file corruption - if (insetMatch) return null; - - // Extract color first (rgba or rgb at start) - const colorMatch = boxShadow.match(/rgba?\([^)]+\)/); - - // Extract numeric values (handles both px and pt units) - const parts = boxShadow.match(/([-\d.]+)(px|pt)/g); - - if (!parts || parts.length < 2) return null; - - const offsetX = parseFloat(parts[0]); - const offsetY = parseFloat(parts[1]); - const blur = parts.length > 2 ? parseFloat(parts[2]) : 0; - - // Calculate angle from offsets (in degrees, 0 = right, 90 = down) - let angle = 0; - if (offsetX !== 0 || offsetY !== 0) { - angle = Math.atan2(offsetY, offsetX) * (180 / Math.PI); - if (angle < 0) angle += 360; - } - - // Calculate offset distance (hypotenuse) - const offset = Math.sqrt(offsetX * offsetX + offsetY * offsetY) * PT_PER_PX; - - // Extract opacity from rgba - let opacity = 0.5; - if (colorMatch) { - const opacityMatch = colorMatch[0].match(/[\d.]+\)$/); - if (opacityMatch) { - opacity = parseFloat(opacityMatch[0].replace(')', '')); - } - } - - return { - type: 'outer', - angle: Math.round(angle), - blur: blur * 0.75, // Convert to points - color: colorMatch ? rgbToHex(colorMatch[0]) : '000000', - offset: offset, - opacity - }; - }; - - // Parse inline formatting tags (, , , , , ) into text runs - const parseInlineFormatting = (element, baseOptions = {}, runs = [], baseTextTransform = (x) => x) => { - let prevNodeIsText = false; - - element.childNodes.forEach((node) => { - let textTransform = baseTextTransform; - - const isText = node.nodeType === Node.TEXT_NODE || node.tagName === 'BR'; - if (isText) { - const text = node.tagName === 'BR' ? '\n' : textTransform(node.textContent.replace(/\s+/g, ' ')); - const prevRun = runs[runs.length - 1]; - if (prevNodeIsText && prevRun) { - prevRun.text += text; - } else { - runs.push({ text, options: { ...baseOptions } }); - } - - } else if (node.nodeType === Node.ELEMENT_NODE && node.textContent.trim()) { - const options = { ...baseOptions }; - const computed = window.getComputedStyle(node); - - // Handle inline elements with computed styles - if (node.tagName === 'SPAN' || node.tagName === 'B' || node.tagName === 'STRONG' || node.tagName === 'I' || node.tagName === 'EM' || node.tagName === 'U') { - const isBold = computed.fontWeight === 'bold' || parseInt(computed.fontWeight) >= 600; - if (isBold && !shouldSkipBold(computed.fontFamily)) options.bold = true; - if (computed.fontStyle === 'italic') options.italic = true; - if (computed.textDecoration && computed.textDecoration.includes('underline')) options.underline = true; - if (computed.color && computed.color !== 'rgb(0, 0, 0)') { - options.color = rgbToHex(computed.color); - const transparency = extractAlpha(computed.color); - if (transparency !== null) options.transparency = transparency; - } - if (computed.fontSize) options.fontSize = pxToPoints(computed.fontSize); - - // Apply text-transform on the span element itself - if (computed.textTransform && computed.textTransform !== 'none') { - const transformStr = computed.textTransform; - textTransform = (text) => applyTextTransform(text, transformStr); - } - - // Validate: Check for margins on inline elements - if (computed.marginLeft && parseFloat(computed.marginLeft) > 0) { - errors.push(`Inline element <${node.tagName.toLowerCase()}> has margin-left which is not supported in PowerPoint. Remove margin from inline elements.`); - } - if (computed.marginRight && parseFloat(computed.marginRight) > 0) { - errors.push(`Inline element <${node.tagName.toLowerCase()}> has margin-right which is not supported in PowerPoint. Remove margin from inline elements.`); - } - if (computed.marginTop && parseFloat(computed.marginTop) > 0) { - errors.push(`Inline element <${node.tagName.toLowerCase()}> has margin-top which is not supported in PowerPoint. Remove margin from inline elements.`); - } - if (computed.marginBottom && parseFloat(computed.marginBottom) > 0) { - errors.push(`Inline element <${node.tagName.toLowerCase()}> has margin-bottom which is not supported in PowerPoint. Remove margin from inline elements.`); - } - - // Recursively process the child node. This will flatten nested spans into multiple runs. - parseInlineFormatting(node, options, runs, textTransform); - } - } - - prevNodeIsText = isText; - }); - - // Trim leading space from first run and trailing space from last run - if (runs.length > 0) { - runs[0].text = runs[0].text.replace(/^\s+/, ''); - runs[runs.length - 1].text = runs[runs.length - 1].text.replace(/\s+$/, ''); - } - - return runs.filter(r => r.text.length > 0); - }; - - // Extract background from body (image or color) - const body = document.body; - const bodyStyle = window.getComputedStyle(body); - const bgImage = bodyStyle.backgroundImage; - const bgColor = bodyStyle.backgroundColor; - - // Collect validation errors - const errors = []; - - // Validate: Check for CSS gradients - if (bgImage && (bgImage.includes('linear-gradient') || bgImage.includes('radial-gradient'))) { - errors.push( - 'CSS gradients are not supported. Use Sharp to rasterize gradients as PNG images first, ' + - 'then reference with background-image: url(\'gradient.png\')' - ); - } - - let background; - if (bgImage && bgImage !== 'none') { - // Extract URL from url("...") or url(...) - const urlMatch = bgImage.match(/url\(["']?([^"')]+)["']?\)/); - if (urlMatch) { - background = { - type: 'image', - path: urlMatch[1] - }; - } else { - background = { - type: 'color', - value: rgbToHex(bgColor) - }; - } - } else { - background = { - type: 'color', - value: rgbToHex(bgColor) - }; - } - - // Process all elements - const elements = []; - const placeholders = []; - const textTags = ['P', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'UL', 'OL', 'LI']; - const processed = new Set(); - - document.querySelectorAll('*').forEach((el) => { - if (processed.has(el)) return; - - // Validate text elements don't have backgrounds, borders, or shadows - if (textTags.includes(el.tagName)) { - const computed = window.getComputedStyle(el); - const hasBg = computed.backgroundColor && computed.backgroundColor !== 'rgba(0, 0, 0, 0)'; - const hasBorder = (computed.borderWidth && parseFloat(computed.borderWidth) > 0) || - (computed.borderTopWidth && parseFloat(computed.borderTopWidth) > 0) || - (computed.borderRightWidth && parseFloat(computed.borderRightWidth) > 0) || - (computed.borderBottomWidth && parseFloat(computed.borderBottomWidth) > 0) || - (computed.borderLeftWidth && parseFloat(computed.borderLeftWidth) > 0); - const hasShadow = computed.boxShadow && computed.boxShadow !== 'none'; - - if (hasBg || hasBorder || hasShadow) { - errors.push( - `Text element <${el.tagName.toLowerCase()}> has ${hasBg ? 'background' : hasBorder ? 'border' : 'shadow'}. ` + - 'Backgrounds, borders, and shadows are only supported on
                      elements, not text elements.' - ); - return; - } - } - - // Extract placeholder elements (for charts, etc.) - if (el.className && el.className.includes('placeholder')) { - const rect = el.getBoundingClientRect(); - if (rect.width === 0 || rect.height === 0) { - errors.push( - `Placeholder "${el.id || 'unnamed'}" has ${rect.width === 0 ? 'width: 0' : 'height: 0'}. Check the layout CSS.` - ); - } else { - placeholders.push({ - id: el.id || `placeholder-${placeholders.length}`, - x: pxToInch(rect.left), - y: pxToInch(rect.top), - w: pxToInch(rect.width), - h: pxToInch(rect.height) - }); - } - processed.add(el); - return; - } - - // Extract images - if (el.tagName === 'IMG') { - const rect = el.getBoundingClientRect(); - if (rect.width > 0 && rect.height > 0) { - elements.push({ - type: 'image', - src: el.src, - position: { - x: pxToInch(rect.left), - y: pxToInch(rect.top), - w: pxToInch(rect.width), - h: pxToInch(rect.height) - } - }); - processed.add(el); - return; - } - } - - // Extract DIVs with backgrounds/borders as shapes - const isContainer = el.tagName === 'DIV' && !textTags.includes(el.tagName); - if (isContainer) { - const computed = window.getComputedStyle(el); - const hasBg = computed.backgroundColor && computed.backgroundColor !== 'rgba(0, 0, 0, 0)'; - - // Validate: Check for unwrapped text content in DIV - for (const node of el.childNodes) { - if (node.nodeType === Node.TEXT_NODE) { - const text = node.textContent.trim(); - if (text) { - errors.push( - `DIV element contains unwrapped text "${text.substring(0, 50)}${text.length > 50 ? '...' : ''}". ` + - 'All text must be wrapped in

                      ,

                      -

                      ,
                        , or
                          tags to appear in PowerPoint.' - ); - } - } - } - - // Check for background images on shapes - const bgImage = computed.backgroundImage; - if (bgImage && bgImage !== 'none') { - errors.push( - 'Background images on DIV elements are not supported. ' + - 'Use solid colors or borders for shapes, or use slide.addImage() in PptxGenJS to layer images.' - ); - return; - } - - // Check for borders - both uniform and partial - const borderTop = computed.borderTopWidth; - const borderRight = computed.borderRightWidth; - const borderBottom = computed.borderBottomWidth; - const borderLeft = computed.borderLeftWidth; - const borders = [borderTop, borderRight, borderBottom, borderLeft].map(b => parseFloat(b) || 0); - const hasBorder = borders.some(b => b > 0); - const hasUniformBorder = hasBorder && borders.every(b => b === borders[0]); - const borderLines = []; - - if (hasBorder && !hasUniformBorder) { - const rect = el.getBoundingClientRect(); - const x = pxToInch(rect.left); - const y = pxToInch(rect.top); - const w = pxToInch(rect.width); - const h = pxToInch(rect.height); - - // Collect lines to add after shape (inset by half the line width to center on edge) - if (parseFloat(borderTop) > 0) { - const widthPt = pxToPoints(borderTop); - const inset = (widthPt / 72) / 2; // Convert points to inches, then half - borderLines.push({ - type: 'line', - x1: x, y1: y + inset, x2: x + w, y2: y + inset, - width: widthPt, - color: rgbToHex(computed.borderTopColor) - }); - } - if (parseFloat(borderRight) > 0) { - const widthPt = pxToPoints(borderRight); - const inset = (widthPt / 72) / 2; - borderLines.push({ - type: 'line', - x1: x + w - inset, y1: y, x2: x + w - inset, y2: y + h, - width: widthPt, - color: rgbToHex(computed.borderRightColor) - }); - } - if (parseFloat(borderBottom) > 0) { - const widthPt = pxToPoints(borderBottom); - const inset = (widthPt / 72) / 2; - borderLines.push({ - type: 'line', - x1: x, y1: y + h - inset, x2: x + w, y2: y + h - inset, - width: widthPt, - color: rgbToHex(computed.borderBottomColor) - }); - } - if (parseFloat(borderLeft) > 0) { - const widthPt = pxToPoints(borderLeft); - const inset = (widthPt / 72) / 2; - borderLines.push({ - type: 'line', - x1: x + inset, y1: y, x2: x + inset, y2: y + h, - width: widthPt, - color: rgbToHex(computed.borderLeftColor) - }); - } - } - - if (hasBg || hasBorder) { - const rect = el.getBoundingClientRect(); - if (rect.width > 0 && rect.height > 0) { - const shadow = parseBoxShadow(computed.boxShadow); - - // Only add shape if there's background or uniform border - if (hasBg || hasUniformBorder) { - elements.push({ - type: 'shape', - text: '', // Shape only - child text elements render on top - position: { - x: pxToInch(rect.left), - y: pxToInch(rect.top), - w: pxToInch(rect.width), - h: pxToInch(rect.height) - }, - shape: { - fill: hasBg ? rgbToHex(computed.backgroundColor) : null, - transparency: hasBg ? extractAlpha(computed.backgroundColor) : null, - line: hasUniformBorder ? { - color: rgbToHex(computed.borderColor), - width: pxToPoints(computed.borderWidth) - } : null, - // Convert border-radius to rectRadius (in inches) - // % values: 50%+ = circle (1), <50% = percentage of min dimension - // pt values: divide by 72 (72pt = 1 inch) - // px values: divide by 96 (96px = 1 inch) - rectRadius: (() => { - const radius = computed.borderRadius; - const radiusValue = parseFloat(radius); - if (radiusValue === 0) return 0; - - if (radius.includes('%')) { - if (radiusValue >= 50) return 1; - // Calculate percentage of smaller dimension - const minDim = Math.min(rect.width, rect.height); - return (radiusValue / 100) * pxToInch(minDim); - } - - if (radius.includes('pt')) return radiusValue / 72; - return radiusValue / PX_PER_IN; - })(), - shadow: shadow - } - }); - } - - // Add partial border lines - elements.push(...borderLines); - - processed.add(el); - return; - } - } - } - - // Extract bullet lists as single text block - if (el.tagName === 'UL' || el.tagName === 'OL') { - const rect = el.getBoundingClientRect(); - if (rect.width === 0 || rect.height === 0) return; - - const liElements = Array.from(el.querySelectorAll('li')); - const items = []; - const ulComputed = window.getComputedStyle(el); - const ulPaddingLeftPt = pxToPoints(ulComputed.paddingLeft); - - // Split: margin-left for bullet position, indent for text position - // margin-left + indent = ul padding-left - const marginLeft = ulPaddingLeftPt * 0.5; - const textIndent = ulPaddingLeftPt * 0.5; - - liElements.forEach((li, idx) => { - const isLast = idx === liElements.length - 1; - const runs = parseInlineFormatting(li, { breakLine: false }); - // Clean manual bullets from first run - if (runs.length > 0) { - runs[0].text = runs[0].text.replace(/^[•\-\*▪▸]\s*/, ''); - runs[0].options.bullet = { indent: textIndent }; - } - // Set breakLine on last run - if (runs.length > 0 && !isLast) { - runs[runs.length - 1].options.breakLine = true; - } - items.push(...runs); - }); - - const computed = window.getComputedStyle(liElements[0] || el); - - elements.push({ - type: 'list', - items: items, - position: { - x: pxToInch(rect.left), - y: pxToInch(rect.top), - w: pxToInch(rect.width), - h: pxToInch(rect.height) - }, - style: { - fontSize: pxToPoints(computed.fontSize), - fontFace: computed.fontFamily.split(',')[0].replace(/['"]/g, '').trim(), - color: rgbToHex(computed.color), - transparency: extractAlpha(computed.color), - align: computed.textAlign === 'start' ? 'left' : computed.textAlign, - lineSpacing: computed.lineHeight && computed.lineHeight !== 'normal' ? pxToPoints(computed.lineHeight) : null, - paraSpaceBefore: 0, - paraSpaceAfter: pxToPoints(computed.marginBottom), - // PptxGenJS margin array is [left, right, bottom, top] - margin: [marginLeft, 0, 0, 0] - } - }); - - liElements.forEach(li => processed.add(li)); - processed.add(el); - return; - } - - // Extract text elements (P, H1, H2, etc.) - if (!textTags.includes(el.tagName)) return; - - const rect = el.getBoundingClientRect(); - const text = el.textContent.trim(); - if (rect.width === 0 || rect.height === 0 || !text) return; - - // Validate: Check for manual bullet symbols in text elements (not in lists) - if (el.tagName !== 'LI' && /^[•\-\*▪▸○●◆◇■□]\s/.test(text.trimStart())) { - errors.push( - `Text element <${el.tagName.toLowerCase()}> starts with bullet symbol "${text.substring(0, 20)}...". ` + - 'Use
                            or
                              lists instead of manual bullet symbols.' - ); - return; - } - - const computed = window.getComputedStyle(el); - const rotation = getRotation(computed.transform, computed.writingMode); - const { x, y, w, h } = getPositionAndSize(el, rect, rotation); - - const baseStyle = { - fontSize: pxToPoints(computed.fontSize), - fontFace: computed.fontFamily.split(',')[0].replace(/['"]/g, '').trim(), - color: rgbToHex(computed.color), - align: computed.textAlign === 'start' ? 'left' : computed.textAlign, - lineSpacing: pxToPoints(computed.lineHeight), - paraSpaceBefore: pxToPoints(computed.marginTop), - paraSpaceAfter: pxToPoints(computed.marginBottom), - // PptxGenJS margin array is [left, right, bottom, top] (not [top, right, bottom, left] as documented) - margin: [ - pxToPoints(computed.paddingLeft), - pxToPoints(computed.paddingRight), - pxToPoints(computed.paddingBottom), - pxToPoints(computed.paddingTop) - ] - }; - - const transparency = extractAlpha(computed.color); - if (transparency !== null) baseStyle.transparency = transparency; - - if (rotation !== null) baseStyle.rotate = rotation; - - const hasFormatting = el.querySelector('b, i, u, strong, em, span, br'); - - if (hasFormatting) { - // Text with inline formatting - const transformStr = computed.textTransform; - const runs = parseInlineFormatting(el, {}, [], (str) => applyTextTransform(str, transformStr)); - - // Adjust lineSpacing based on largest fontSize in runs - const adjustedStyle = { ...baseStyle }; - if (adjustedStyle.lineSpacing) { - const maxFontSize = Math.max( - adjustedStyle.fontSize, - ...runs.map(r => r.options?.fontSize || 0) - ); - if (maxFontSize > adjustedStyle.fontSize) { - const lineHeightMultiplier = adjustedStyle.lineSpacing / adjustedStyle.fontSize; - adjustedStyle.lineSpacing = maxFontSize * lineHeightMultiplier; - } - } - - elements.push({ - type: el.tagName.toLowerCase(), - text: runs, - position: { x: pxToInch(x), y: pxToInch(y), w: pxToInch(w), h: pxToInch(h) }, - style: adjustedStyle - }); - } else { - // Plain text - inherit CSS formatting - const textTransform = computed.textTransform; - const transformedText = applyTextTransform(text, textTransform); - - const isBold = computed.fontWeight === 'bold' || parseInt(computed.fontWeight) >= 600; - - elements.push({ - type: el.tagName.toLowerCase(), - text: transformedText, - position: { x: pxToInch(x), y: pxToInch(y), w: pxToInch(w), h: pxToInch(h) }, - style: { - ...baseStyle, - bold: isBold && !shouldSkipBold(computed.fontFamily), - italic: computed.fontStyle === 'italic', - underline: computed.textDecoration.includes('underline') - } - }); - } - - processed.add(el); - }); - - return { background, elements, placeholders, errors }; - }); -} - -async function html2pptx(htmlFile, pres, options = {}) { - const { - tmpDir = process.env.TMPDIR || '/tmp', - slide = null - } = options; - - try { - // Use Chrome on macOS, default Chromium on Unix - const launchOptions = { env: { TMPDIR: tmpDir } }; - if (process.platform === 'darwin') { - launchOptions.channel = 'chrome'; - } - - const browser = await chromium.launch(launchOptions); - - let bodyDimensions; - let slideData; - - const filePath = path.isAbsolute(htmlFile) ? htmlFile : path.join(process.cwd(), htmlFile); - const validationErrors = []; - - try { - const page = await browser.newPage(); - page.on('console', (msg) => { - // Log the message text to your test runner's console - console.log(`Browser console: ${msg.text()}`); - }); - - await page.goto(`file://${filePath}`); - - bodyDimensions = await getBodyDimensions(page); - - await page.setViewportSize({ - width: Math.round(bodyDimensions.width), - height: Math.round(bodyDimensions.height) - }); - - slideData = await extractSlideData(page); - } finally { - await browser.close(); - } - - // Collect all validation errors - if (bodyDimensions.errors && bodyDimensions.errors.length > 0) { - validationErrors.push(...bodyDimensions.errors); - } - - const dimensionErrors = validateDimensions(bodyDimensions, pres); - if (dimensionErrors.length > 0) { - validationErrors.push(...dimensionErrors); - } - - const textBoxPositionErrors = validateTextBoxPosition(slideData, bodyDimensions); - if (textBoxPositionErrors.length > 0) { - validationErrors.push(...textBoxPositionErrors); - } - - if (slideData.errors && slideData.errors.length > 0) { - validationErrors.push(...slideData.errors); - } - - // Throw all errors at once if any exist - if (validationErrors.length > 0) { - const errorMessage = validationErrors.length === 1 - ? validationErrors[0] - : `Multiple validation errors found:\n${validationErrors.map((e, i) => ` ${i + 1}. ${e}`).join('\n')}`; - throw new Error(errorMessage); - } - - const targetSlide = slide || pres.addSlide(); - - await addBackground(slideData, targetSlide, tmpDir); - addElements(slideData, targetSlide, pres); - - return { slide: targetSlide, placeholders: slideData.placeholders }; - } catch (error) { - if (!error.message.startsWith(htmlFile)) { - throw new Error(`${htmlFile}: ${error.message}`); - } - throw error; - } -} - -module.exports = html2pptx; \ No newline at end of file diff --git a/.claude/skills/pptx-skill/scripts/html2pptx.js b/.claude/skills/pptx-skill/scripts/html2pptx.js deleted file mode 100755 index 437bf7c..0000000 --- a/.claude/skills/pptx-skill/scripts/html2pptx.js +++ /dev/null @@ -1,979 +0,0 @@ -/** - * html2pptx - Convert HTML slide to pptxgenjs slide with positioned elements - * - * USAGE: - * const pptx = new pptxgen(); - * pptx.layout = 'LAYOUT_16x9'; // Must match HTML body dimensions - * - * const { slide, placeholders } = await html2pptx('slide.html', pptx); - * slide.addChart(pptx.charts.LINE, data, placeholders[0]); - * - * await pptx.writeFile('output.pptx'); - * - * FEATURES: - * - Converts HTML to PowerPoint with accurate positioning - * - Supports text, images, shapes, and bullet lists - * - Extracts placeholder elements (class="placeholder") with positions - * - Handles CSS gradients, borders, and margins - * - * VALIDATION: - * - Uses body width/height from HTML for viewport sizing - * - Throws error if HTML dimensions don't match presentation layout - * - Throws error if content overflows body (with overflow details) - * - * RETURNS: - * { slide, placeholders } where placeholders is an array of { id, x, y, w, h } - */ - -const { chromium } = require('playwright'); -const path = require('path'); -const sharp = require('sharp'); - -const PT_PER_PX = 0.75; -const PX_PER_IN = 96; -const EMU_PER_IN = 914400; - -// Helper: Get body dimensions and check for overflow -async function getBodyDimensions(page) { - const bodyDimensions = await page.evaluate(() => { - const body = document.body; - const style = window.getComputedStyle(body); - - return { - width: parseFloat(style.width), - height: parseFloat(style.height), - scrollWidth: body.scrollWidth, - scrollHeight: body.scrollHeight - }; - }); - - const errors = []; - const widthOverflowPx = Math.max(0, bodyDimensions.scrollWidth - bodyDimensions.width - 1); - const heightOverflowPx = Math.max(0, bodyDimensions.scrollHeight - bodyDimensions.height - 1); - - const widthOverflowPt = widthOverflowPx * PT_PER_PX; - const heightOverflowPt = heightOverflowPx * PT_PER_PX; - - if (widthOverflowPt > 0 || heightOverflowPt > 0) { - const directions = []; - if (widthOverflowPt > 0) directions.push(`${widthOverflowPt.toFixed(1)}pt horizontally`); - if (heightOverflowPt > 0) directions.push(`${heightOverflowPt.toFixed(1)}pt vertically`); - const reminder = heightOverflowPt > 0 ? ' (Remember: leave 0.5" margin at bottom of slide)' : ''; - errors.push(`HTML content overflows body by ${directions.join(' and ')}${reminder}`); - } - - return { ...bodyDimensions, errors }; -} - -// Helper: Validate dimensions match presentation layout -function validateDimensions(bodyDimensions, pres) { - const errors = []; - const widthInches = bodyDimensions.width / PX_PER_IN; - const heightInches = bodyDimensions.height / PX_PER_IN; - - if (pres.presLayout) { - const layoutWidth = pres.presLayout.width / EMU_PER_IN; - const layoutHeight = pres.presLayout.height / EMU_PER_IN; - - if (Math.abs(layoutWidth - widthInches) > 0.1 || Math.abs(layoutHeight - heightInches) > 0.1) { - errors.push( - `HTML dimensions (${widthInches.toFixed(1)}" × ${heightInches.toFixed(1)}") ` + - `don't match presentation layout (${layoutWidth.toFixed(1)}" × ${layoutHeight.toFixed(1)}")` - ); - } - } - return errors; -} - -function validateTextBoxPosition(slideData, bodyDimensions) { - const errors = []; - const slideHeightInches = bodyDimensions.height / PX_PER_IN; - const minBottomMargin = 0.5; // 0.5 inches from bottom - - for (const el of slideData.elements) { - // Check text elements (p, h1-h6, list) - if (['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'list'].includes(el.type)) { - const fontSize = el.style?.fontSize || 0; - const bottomEdge = el.position.y + el.position.h; - const distanceFromBottom = slideHeightInches - bottomEdge; - - if (fontSize > 12 && distanceFromBottom < minBottomMargin) { - const getText = () => { - if (typeof el.text === 'string') return el.text; - if (Array.isArray(el.text)) return el.text.find(t => t.text)?.text || ''; - if (Array.isArray(el.items)) return el.items.find(item => item.text)?.text || ''; - return ''; - }; - const textPrefix = getText().substring(0, 50) + (getText().length > 50 ? '...' : ''); - - errors.push( - `Text box "${textPrefix}" ends too close to bottom edge ` + - `(${distanceFromBottom.toFixed(2)}" from bottom, minimum ${minBottomMargin}" required)` - ); - } - } - } - - return errors; -} - -// Helper: Add background to slide -async function addBackground(slideData, targetSlide, tmpDir) { - if (slideData.background.type === 'image' && slideData.background.path) { - let imagePath = slideData.background.path.startsWith('file://') - ? slideData.background.path.replace('file://', '') - : slideData.background.path; - targetSlide.background = { path: imagePath }; - } else if (slideData.background.type === 'color' && slideData.background.value) { - targetSlide.background = { color: slideData.background.value }; - } -} - -// Helper: Add elements to slide -function addElements(slideData, targetSlide, pres) { - for (const el of slideData.elements) { - if (el.type === 'image') { - let imagePath = el.src.startsWith('file://') ? el.src.replace('file://', '') : el.src; - targetSlide.addImage({ - path: imagePath, - x: el.position.x, - y: el.position.y, - w: el.position.w, - h: el.position.h - }); - } else if (el.type === 'line') { - targetSlide.addShape(pres.ShapeType.line, { - x: el.x1, - y: el.y1, - w: el.x2 - el.x1, - h: el.y2 - el.y1, - line: { color: el.color, width: el.width } - }); - } else if (el.type === 'shape') { - const shapeOptions = { - x: el.position.x, - y: el.position.y, - w: el.position.w, - h: el.position.h, - shape: el.shape.rectRadius > 0 ? pres.ShapeType.roundRect : pres.ShapeType.rect - }; - - if (el.shape.fill) { - shapeOptions.fill = { color: el.shape.fill }; - if (el.shape.transparency != null) shapeOptions.fill.transparency = el.shape.transparency; - } - if (el.shape.line) shapeOptions.line = el.shape.line; - if (el.shape.rectRadius > 0) shapeOptions.rectRadius = el.shape.rectRadius; - if (el.shape.shadow) shapeOptions.shadow = el.shape.shadow; - - targetSlide.addText(el.text || '', shapeOptions); - } else if (el.type === 'list') { - const listOptions = { - x: el.position.x, - y: el.position.y, - w: el.position.w, - h: el.position.h, - fontSize: el.style.fontSize, - fontFace: el.style.fontFace, - color: el.style.color, - align: el.style.align, - valign: 'top', - lineSpacing: el.style.lineSpacing, - paraSpaceBefore: el.style.paraSpaceBefore, - paraSpaceAfter: el.style.paraSpaceAfter, - margin: el.style.margin - }; - if (el.style.margin) listOptions.margin = el.style.margin; - targetSlide.addText(el.items, listOptions); - } else { - // Check if text is single-line (height suggests one line) - const lineHeight = el.style.lineSpacing || el.style.fontSize * 1.2; - const isSingleLine = el.position.h <= lineHeight * 1.5; - - let adjustedX = el.position.x; - let adjustedW = el.position.w; - - // Make single-line text 2% wider to account for underestimate - if (isSingleLine) { - const widthIncrease = el.position.w * 0.02; - const align = el.style.align; - - if (align === 'center') { - // Center: expand both sides - adjustedX = el.position.x - (widthIncrease / 2); - adjustedW = el.position.w + widthIncrease; - } else if (align === 'right') { - // Right: expand to the left - adjustedX = el.position.x - widthIncrease; - adjustedW = el.position.w + widthIncrease; - } else { - // Left (default): expand to the right - adjustedW = el.position.w + widthIncrease; - } - } - - const textOptions = { - x: adjustedX, - y: el.position.y, - w: adjustedW, - h: el.position.h, - fontSize: el.style.fontSize, - fontFace: el.style.fontFace, - color: el.style.color, - bold: el.style.bold, - italic: el.style.italic, - underline: el.style.underline, - valign: 'top', - lineSpacing: el.style.lineSpacing, - paraSpaceBefore: el.style.paraSpaceBefore, - paraSpaceAfter: el.style.paraSpaceAfter, - inset: 0 // Remove default PowerPoint internal padding - }; - - if (el.style.align) textOptions.align = el.style.align; - if (el.style.margin) textOptions.margin = el.style.margin; - if (el.style.rotate !== undefined) textOptions.rotate = el.style.rotate; - if (el.style.transparency !== null && el.style.transparency !== undefined) textOptions.transparency = el.style.transparency; - - targetSlide.addText(el.text, textOptions); - } - } -} - -// Helper: Extract slide data from HTML page -async function extractSlideData(page) { - return await page.evaluate(() => { - const PT_PER_PX = 0.75; - const PX_PER_IN = 96; - - // Fonts that are single-weight and should not have bold applied - // (applying bold causes PowerPoint to use faux bold which makes text wider) - const SINGLE_WEIGHT_FONTS = ['impact']; - - // Helper: Check if a font should skip bold formatting - const shouldSkipBold = (fontFamily) => { - if (!fontFamily) return false; - const normalizedFont = fontFamily.toLowerCase().replace(/['"]/g, '').split(',')[0].trim(); - return SINGLE_WEIGHT_FONTS.includes(normalizedFont); - }; - - // Unit conversion helpers - const pxToInch = (px) => px / PX_PER_IN; - const pxToPoints = (pxStr) => parseFloat(pxStr) * PT_PER_PX; - const rgbToHex = (rgbStr) => { - // Handle transparent backgrounds by defaulting to white - if (rgbStr === 'rgba(0, 0, 0, 0)' || rgbStr === 'transparent') return 'FFFFFF'; - - const match = rgbStr.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/); - if (!match) return 'FFFFFF'; - return match.slice(1).map(n => parseInt(n).toString(16).padStart(2, '0')).join(''); - }; - - const extractAlpha = (rgbStr) => { - const match = rgbStr.match(/rgba\((\d+),\s*(\d+),\s*(\d+),\s*([\d.]+)\)/); - if (!match || !match[4]) return null; - const alpha = parseFloat(match[4]); - return Math.round((1 - alpha) * 100); - }; - - const applyTextTransform = (text, textTransform) => { - if (textTransform === 'uppercase') return text.toUpperCase(); - if (textTransform === 'lowercase') return text.toLowerCase(); - if (textTransform === 'capitalize') { - return text.replace(/\b\w/g, c => c.toUpperCase()); - } - return text; - }; - - // Extract rotation angle from CSS transform and writing-mode - const getRotation = (transform, writingMode) => { - let angle = 0; - - // Handle writing-mode first - // PowerPoint: 90° = text rotated 90° clockwise (reads top to bottom, letters upright) - // PowerPoint: 270° = text rotated 270° clockwise (reads bottom to top, letters upright) - if (writingMode === 'vertical-rl') { - // vertical-rl alone = text reads top to bottom = 90° in PowerPoint - angle = 90; - } else if (writingMode === 'vertical-lr') { - // vertical-lr alone = text reads bottom to top = 270° in PowerPoint - angle = 270; - } - - // Then add any transform rotation - if (transform && transform !== 'none') { - // Try to match rotate() function - const rotateMatch = transform.match(/rotate\((-?\d+(?:\.\d+)?)deg\)/); - if (rotateMatch) { - angle += parseFloat(rotateMatch[1]); - } else { - // Browser may compute as matrix - extract rotation from matrix - const matrixMatch = transform.match(/matrix\(([^)]+)\)/); - if (matrixMatch) { - const values = matrixMatch[1].split(',').map(parseFloat); - // matrix(a, b, c, d, e, f) where rotation = atan2(b, a) - const matrixAngle = Math.atan2(values[1], values[0]) * (180 / Math.PI); - angle += Math.round(matrixAngle); - } - } - } - - // Normalize to 0-359 range - angle = angle % 360; - if (angle < 0) angle += 360; - - return angle === 0 ? null : angle; - }; - - // Get position/dimensions accounting for rotation - const getPositionAndSize = (el, rect, rotation) => { - if (rotation === null) { - return { x: rect.left, y: rect.top, w: rect.width, h: rect.height }; - } - - // For 90° or 270° rotations, swap width and height - // because PowerPoint applies rotation to the original (unrotated) box - const isVertical = rotation === 90 || rotation === 270; - - if (isVertical) { - // The browser shows us the rotated dimensions (tall box for vertical text) - // But PowerPoint needs the pre-rotation dimensions (wide box that will be rotated) - // So we swap: browser's height becomes PPT's width, browser's width becomes PPT's height - const centerX = rect.left + rect.width / 2; - const centerY = rect.top + rect.height / 2; - - return { - x: centerX - rect.height / 2, - y: centerY - rect.width / 2, - w: rect.height, - h: rect.width - }; - } - - // For other rotations, use element's offset dimensions - const centerX = rect.left + rect.width / 2; - const centerY = rect.top + rect.height / 2; - return { - x: centerX - el.offsetWidth / 2, - y: centerY - el.offsetHeight / 2, - w: el.offsetWidth, - h: el.offsetHeight - }; - }; - - // Parse CSS box-shadow into PptxGenJS shadow properties - const parseBoxShadow = (boxShadow) => { - if (!boxShadow || boxShadow === 'none') return null; - - // Browser computed style format: "rgba(0, 0, 0, 0.3) 2px 2px 8px 0px [inset]" - // CSS format: "[inset] 2px 2px 8px 0px rgba(0, 0, 0, 0.3)" - - const insetMatch = boxShadow.match(/inset/); - - // IMPORTANT: PptxGenJS/PowerPoint doesn't properly support inset shadows - // Only process outer shadows to avoid file corruption - if (insetMatch) return null; - - // Extract color first (rgba or rgb at start) - const colorMatch = boxShadow.match(/rgba?\([^)]+\)/); - - // Extract numeric values (handles both px and pt units) - const parts = boxShadow.match(/([-\d.]+)(px|pt)/g); - - if (!parts || parts.length < 2) return null; - - const offsetX = parseFloat(parts[0]); - const offsetY = parseFloat(parts[1]); - const blur = parts.length > 2 ? parseFloat(parts[2]) : 0; - - // Calculate angle from offsets (in degrees, 0 = right, 90 = down) - let angle = 0; - if (offsetX !== 0 || offsetY !== 0) { - angle = Math.atan2(offsetY, offsetX) * (180 / Math.PI); - if (angle < 0) angle += 360; - } - - // Calculate offset distance (hypotenuse) - const offset = Math.sqrt(offsetX * offsetX + offsetY * offsetY) * PT_PER_PX; - - // Extract opacity from rgba - let opacity = 0.5; - if (colorMatch) { - const opacityMatch = colorMatch[0].match(/[\d.]+\)$/); - if (opacityMatch) { - opacity = parseFloat(opacityMatch[0].replace(')', '')); - } - } - - return { - type: 'outer', - angle: Math.round(angle), - blur: blur * 0.75, // Convert to points - color: colorMatch ? rgbToHex(colorMatch[0]) : '000000', - offset: offset, - opacity - }; - }; - - // Parse inline formatting tags (, , , , , ) into text runs - const parseInlineFormatting = (element, baseOptions = {}, runs = [], baseTextTransform = (x) => x) => { - let prevNodeIsText = false; - - element.childNodes.forEach((node) => { - let textTransform = baseTextTransform; - - const isText = node.nodeType === Node.TEXT_NODE || node.tagName === 'BR'; - if (isText) { - const text = node.tagName === 'BR' ? '\n' : textTransform(node.textContent.replace(/\s+/g, ' ')); - const prevRun = runs[runs.length - 1]; - if (prevNodeIsText && prevRun) { - prevRun.text += text; - } else { - runs.push({ text, options: { ...baseOptions } }); - } - - } else if (node.nodeType === Node.ELEMENT_NODE && node.textContent.trim()) { - const options = { ...baseOptions }; - const computed = window.getComputedStyle(node); - - // Handle inline elements with computed styles - if (node.tagName === 'SPAN' || node.tagName === 'B' || node.tagName === 'STRONG' || node.tagName === 'I' || node.tagName === 'EM' || node.tagName === 'U') { - const isBold = computed.fontWeight === 'bold' || parseInt(computed.fontWeight) >= 600; - if (isBold && !shouldSkipBold(computed.fontFamily)) options.bold = true; - if (computed.fontStyle === 'italic') options.italic = true; - if (computed.textDecoration && computed.textDecoration.includes('underline')) options.underline = true; - if (computed.color && computed.color !== 'rgb(0, 0, 0)') { - options.color = rgbToHex(computed.color); - const transparency = extractAlpha(computed.color); - if (transparency !== null) options.transparency = transparency; - } - if (computed.fontSize) options.fontSize = pxToPoints(computed.fontSize); - - // Apply text-transform on the span element itself - if (computed.textTransform && computed.textTransform !== 'none') { - const transformStr = computed.textTransform; - textTransform = (text) => applyTextTransform(text, transformStr); - } - - // Validate: Check for margins on inline elements - if (computed.marginLeft && parseFloat(computed.marginLeft) > 0) { - errors.push(`Inline element <${node.tagName.toLowerCase()}> has margin-left which is not supported in PowerPoint. Remove margin from inline elements.`); - } - if (computed.marginRight && parseFloat(computed.marginRight) > 0) { - errors.push(`Inline element <${node.tagName.toLowerCase()}> has margin-right which is not supported in PowerPoint. Remove margin from inline elements.`); - } - if (computed.marginTop && parseFloat(computed.marginTop) > 0) { - errors.push(`Inline element <${node.tagName.toLowerCase()}> has margin-top which is not supported in PowerPoint. Remove margin from inline elements.`); - } - if (computed.marginBottom && parseFloat(computed.marginBottom) > 0) { - errors.push(`Inline element <${node.tagName.toLowerCase()}> has margin-bottom which is not supported in PowerPoint. Remove margin from inline elements.`); - } - - // Recursively process the child node. This will flatten nested spans into multiple runs. - parseInlineFormatting(node, options, runs, textTransform); - } - } - - prevNodeIsText = isText; - }); - - // Trim leading space from first run and trailing space from last run - if (runs.length > 0) { - runs[0].text = runs[0].text.replace(/^\s+/, ''); - runs[runs.length - 1].text = runs[runs.length - 1].text.replace(/\s+$/, ''); - } - - return runs.filter(r => r.text.length > 0); - }; - - // Extract background from body (image or color) - const body = document.body; - const bodyStyle = window.getComputedStyle(body); - const bgImage = bodyStyle.backgroundImage; - const bgColor = bodyStyle.backgroundColor; - - // Collect validation errors - const errors = []; - - // Validate: Check for CSS gradients - if (bgImage && (bgImage.includes('linear-gradient') || bgImage.includes('radial-gradient'))) { - errors.push( - 'CSS gradients are not supported. Use Sharp to rasterize gradients as PNG images first, ' + - 'then reference with background-image: url(\'gradient.png\')' - ); - } - - let background; - if (bgImage && bgImage !== 'none') { - // Extract URL from url("...") or url(...) - const urlMatch = bgImage.match(/url\(["']?([^"')]+)["']?\)/); - if (urlMatch) { - background = { - type: 'image', - path: urlMatch[1] - }; - } else { - background = { - type: 'color', - value: rgbToHex(bgColor) - }; - } - } else { - background = { - type: 'color', - value: rgbToHex(bgColor) - }; - } - - // Process all elements - const elements = []; - const placeholders = []; - const textTags = ['P', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'UL', 'OL', 'LI']; - const processed = new Set(); - - document.querySelectorAll('*').forEach((el) => { - if (processed.has(el)) return; - - // Validate text elements don't have backgrounds, borders, or shadows - if (textTags.includes(el.tagName)) { - const computed = window.getComputedStyle(el); - const hasBg = computed.backgroundColor && computed.backgroundColor !== 'rgba(0, 0, 0, 0)'; - const hasBorder = (computed.borderWidth && parseFloat(computed.borderWidth) > 0) || - (computed.borderTopWidth && parseFloat(computed.borderTopWidth) > 0) || - (computed.borderRightWidth && parseFloat(computed.borderRightWidth) > 0) || - (computed.borderBottomWidth && parseFloat(computed.borderBottomWidth) > 0) || - (computed.borderLeftWidth && parseFloat(computed.borderLeftWidth) > 0); - const hasShadow = computed.boxShadow && computed.boxShadow !== 'none'; - - if (hasBg || hasBorder || hasShadow) { - errors.push( - `Text element <${el.tagName.toLowerCase()}> has ${hasBg ? 'background' : hasBorder ? 'border' : 'shadow'}. ` + - 'Backgrounds, borders, and shadows are only supported on
                              elements, not text elements.' - ); - return; - } - } - - // Extract placeholder elements (for charts, etc.) - if (el.className && el.className.includes('placeholder')) { - const rect = el.getBoundingClientRect(); - if (rect.width === 0 || rect.height === 0) { - errors.push( - `Placeholder "${el.id || 'unnamed'}" has ${rect.width === 0 ? 'width: 0' : 'height: 0'}. Check the layout CSS.` - ); - } else { - placeholders.push({ - id: el.id || `placeholder-${placeholders.length}`, - x: pxToInch(rect.left), - y: pxToInch(rect.top), - w: pxToInch(rect.width), - h: pxToInch(rect.height) - }); - } - processed.add(el); - return; - } - - // Extract images - if (el.tagName === 'IMG') { - const rect = el.getBoundingClientRect(); - if (rect.width > 0 && rect.height > 0) { - elements.push({ - type: 'image', - src: el.src, - position: { - x: pxToInch(rect.left), - y: pxToInch(rect.top), - w: pxToInch(rect.width), - h: pxToInch(rect.height) - } - }); - processed.add(el); - return; - } - } - - // Extract DIVs with backgrounds/borders as shapes - const isContainer = el.tagName === 'DIV' && !textTags.includes(el.tagName); - if (isContainer) { - const computed = window.getComputedStyle(el); - const hasBg = computed.backgroundColor && computed.backgroundColor !== 'rgba(0, 0, 0, 0)'; - - // Validate: Check for unwrapped text content in DIV - for (const node of el.childNodes) { - if (node.nodeType === Node.TEXT_NODE) { - const text = node.textContent.trim(); - if (text) { - errors.push( - `DIV element contains unwrapped text "${text.substring(0, 50)}${text.length > 50 ? '...' : ''}". ` + - 'All text must be wrapped in

                              ,

                              -

                              ,
                                , or
                                  tags to appear in PowerPoint.' - ); - } - } - } - - // Check for background images on shapes - const bgImage = computed.backgroundImage; - if (bgImage && bgImage !== 'none') { - errors.push( - 'Background images on DIV elements are not supported. ' + - 'Use solid colors or borders for shapes, or use slide.addImage() in PptxGenJS to layer images.' - ); - return; - } - - // Check for borders - both uniform and partial - const borderTop = computed.borderTopWidth; - const borderRight = computed.borderRightWidth; - const borderBottom = computed.borderBottomWidth; - const borderLeft = computed.borderLeftWidth; - const borders = [borderTop, borderRight, borderBottom, borderLeft].map(b => parseFloat(b) || 0); - const hasBorder = borders.some(b => b > 0); - const hasUniformBorder = hasBorder && borders.every(b => b === borders[0]); - const borderLines = []; - - if (hasBorder && !hasUniformBorder) { - const rect = el.getBoundingClientRect(); - const x = pxToInch(rect.left); - const y = pxToInch(rect.top); - const w = pxToInch(rect.width); - const h = pxToInch(rect.height); - - // Collect lines to add after shape (inset by half the line width to center on edge) - if (parseFloat(borderTop) > 0) { - const widthPt = pxToPoints(borderTop); - const inset = (widthPt / 72) / 2; // Convert points to inches, then half - borderLines.push({ - type: 'line', - x1: x, y1: y + inset, x2: x + w, y2: y + inset, - width: widthPt, - color: rgbToHex(computed.borderTopColor) - }); - } - if (parseFloat(borderRight) > 0) { - const widthPt = pxToPoints(borderRight); - const inset = (widthPt / 72) / 2; - borderLines.push({ - type: 'line', - x1: x + w - inset, y1: y, x2: x + w - inset, y2: y + h, - width: widthPt, - color: rgbToHex(computed.borderRightColor) - }); - } - if (parseFloat(borderBottom) > 0) { - const widthPt = pxToPoints(borderBottom); - const inset = (widthPt / 72) / 2; - borderLines.push({ - type: 'line', - x1: x, y1: y + h - inset, x2: x + w, y2: y + h - inset, - width: widthPt, - color: rgbToHex(computed.borderBottomColor) - }); - } - if (parseFloat(borderLeft) > 0) { - const widthPt = pxToPoints(borderLeft); - const inset = (widthPt / 72) / 2; - borderLines.push({ - type: 'line', - x1: x + inset, y1: y, x2: x + inset, y2: y + h, - width: widthPt, - color: rgbToHex(computed.borderLeftColor) - }); - } - } - - if (hasBg || hasBorder) { - const rect = el.getBoundingClientRect(); - if (rect.width > 0 && rect.height > 0) { - const shadow = parseBoxShadow(computed.boxShadow); - - // Only add shape if there's background or uniform border - if (hasBg || hasUniformBorder) { - elements.push({ - type: 'shape', - text: '', // Shape only - child text elements render on top - position: { - x: pxToInch(rect.left), - y: pxToInch(rect.top), - w: pxToInch(rect.width), - h: pxToInch(rect.height) - }, - shape: { - fill: hasBg ? rgbToHex(computed.backgroundColor) : null, - transparency: hasBg ? extractAlpha(computed.backgroundColor) : null, - line: hasUniformBorder ? { - color: rgbToHex(computed.borderColor), - width: pxToPoints(computed.borderWidth) - } : null, - // Convert border-radius to rectRadius (in inches) - // % values: 50%+ = circle (1), <50% = percentage of min dimension - // pt values: divide by 72 (72pt = 1 inch) - // px values: divide by 96 (96px = 1 inch) - rectRadius: (() => { - const radius = computed.borderRadius; - const radiusValue = parseFloat(radius); - if (radiusValue === 0) return 0; - - if (radius.includes('%')) { - if (radiusValue >= 50) return 1; - // Calculate percentage of smaller dimension - const minDim = Math.min(rect.width, rect.height); - return (radiusValue / 100) * pxToInch(minDim); - } - - if (radius.includes('pt')) return radiusValue / 72; - return radiusValue / PX_PER_IN; - })(), - shadow: shadow - } - }); - } - - // Add partial border lines - elements.push(...borderLines); - - processed.add(el); - return; - } - } - } - - // Extract bullet lists as single text block - if (el.tagName === 'UL' || el.tagName === 'OL') { - const rect = el.getBoundingClientRect(); - if (rect.width === 0 || rect.height === 0) return; - - const liElements = Array.from(el.querySelectorAll('li')); - const items = []; - const ulComputed = window.getComputedStyle(el); - const ulPaddingLeftPt = pxToPoints(ulComputed.paddingLeft); - - // Split: margin-left for bullet position, indent for text position - // margin-left + indent = ul padding-left - const marginLeft = ulPaddingLeftPt * 0.5; - const textIndent = ulPaddingLeftPt * 0.5; - - liElements.forEach((li, idx) => { - const isLast = idx === liElements.length - 1; - const runs = parseInlineFormatting(li, { breakLine: false }); - // Clean manual bullets from first run - if (runs.length > 0) { - runs[0].text = runs[0].text.replace(/^[•\-\*▪▸]\s*/, ''); - runs[0].options.bullet = { indent: textIndent }; - } - // Set breakLine on last run - if (runs.length > 0 && !isLast) { - runs[runs.length - 1].options.breakLine = true; - } - items.push(...runs); - }); - - const computed = window.getComputedStyle(liElements[0] || el); - - elements.push({ - type: 'list', - items: items, - position: { - x: pxToInch(rect.left), - y: pxToInch(rect.top), - w: pxToInch(rect.width), - h: pxToInch(rect.height) - }, - style: { - fontSize: pxToPoints(computed.fontSize), - fontFace: computed.fontFamily.split(',')[0].replace(/['"]/g, '').trim(), - color: rgbToHex(computed.color), - transparency: extractAlpha(computed.color), - align: computed.textAlign === 'start' ? 'left' : computed.textAlign, - lineSpacing: computed.lineHeight && computed.lineHeight !== 'normal' ? pxToPoints(computed.lineHeight) : null, - paraSpaceBefore: 0, - paraSpaceAfter: pxToPoints(computed.marginBottom), - // PptxGenJS margin array is [left, right, bottom, top] - margin: [marginLeft, 0, 0, 0] - } - }); - - liElements.forEach(li => processed.add(li)); - processed.add(el); - return; - } - - // Extract text elements (P, H1, H2, etc.) - if (!textTags.includes(el.tagName)) return; - - const rect = el.getBoundingClientRect(); - const text = el.textContent.trim(); - if (rect.width === 0 || rect.height === 0 || !text) return; - - // Validate: Check for manual bullet symbols in text elements (not in lists) - if (el.tagName !== 'LI' && /^[•\-\*▪▸○●◆◇■□]\s/.test(text.trimStart())) { - errors.push( - `Text element <${el.tagName.toLowerCase()}> starts with bullet symbol "${text.substring(0, 20)}...". ` + - 'Use
                                    or
                                      lists instead of manual bullet symbols.' - ); - return; - } - - const computed = window.getComputedStyle(el); - const rotation = getRotation(computed.transform, computed.writingMode); - const { x, y, w, h } = getPositionAndSize(el, rect, rotation); - - const baseStyle = { - fontSize: pxToPoints(computed.fontSize), - fontFace: computed.fontFamily.split(',')[0].replace(/['"]/g, '').trim(), - color: rgbToHex(computed.color), - align: computed.textAlign === 'start' ? 'left' : computed.textAlign, - lineSpacing: pxToPoints(computed.lineHeight), - paraSpaceBefore: pxToPoints(computed.marginTop), - paraSpaceAfter: pxToPoints(computed.marginBottom), - // PptxGenJS margin array is [left, right, bottom, top] (not [top, right, bottom, left] as documented) - margin: [ - pxToPoints(computed.paddingLeft), - pxToPoints(computed.paddingRight), - pxToPoints(computed.paddingBottom), - pxToPoints(computed.paddingTop) - ] - }; - - const transparency = extractAlpha(computed.color); - if (transparency !== null) baseStyle.transparency = transparency; - - if (rotation !== null) baseStyle.rotate = rotation; - - const hasFormatting = el.querySelector('b, i, u, strong, em, span, br'); - - if (hasFormatting) { - // Text with inline formatting - const transformStr = computed.textTransform; - const runs = parseInlineFormatting(el, {}, [], (str) => applyTextTransform(str, transformStr)); - - // Adjust lineSpacing based on largest fontSize in runs - const adjustedStyle = { ...baseStyle }; - if (adjustedStyle.lineSpacing) { - const maxFontSize = Math.max( - adjustedStyle.fontSize, - ...runs.map(r => r.options?.fontSize || 0) - ); - if (maxFontSize > adjustedStyle.fontSize) { - const lineHeightMultiplier = adjustedStyle.lineSpacing / adjustedStyle.fontSize; - adjustedStyle.lineSpacing = maxFontSize * lineHeightMultiplier; - } - } - - elements.push({ - type: el.tagName.toLowerCase(), - text: runs, - position: { x: pxToInch(x), y: pxToInch(y), w: pxToInch(w), h: pxToInch(h) }, - style: adjustedStyle - }); - } else { - // Plain text - inherit CSS formatting - const textTransform = computed.textTransform; - const transformedText = applyTextTransform(text, textTransform); - - const isBold = computed.fontWeight === 'bold' || parseInt(computed.fontWeight) >= 600; - - elements.push({ - type: el.tagName.toLowerCase(), - text: transformedText, - position: { x: pxToInch(x), y: pxToInch(y), w: pxToInch(w), h: pxToInch(h) }, - style: { - ...baseStyle, - bold: isBold && !shouldSkipBold(computed.fontFamily), - italic: computed.fontStyle === 'italic', - underline: computed.textDecoration.includes('underline') - } - }); - } - - processed.add(el); - }); - - return { background, elements, placeholders, errors }; - }); -} - -async function html2pptx(htmlFile, pres, options = {}) { - const { - tmpDir = process.env.TMPDIR || '/tmp', - slide = null - } = options; - - try { - // Use Chrome on macOS, default Chromium on Unix - const launchOptions = { env: { TMPDIR: tmpDir } }; - if (process.platform === 'darwin') { - launchOptions.channel = 'chrome'; - } - - const browser = await chromium.launch(launchOptions); - - let bodyDimensions; - let slideData; - - const filePath = path.isAbsolute(htmlFile) ? htmlFile : path.join(process.cwd(), htmlFile); - const validationErrors = []; - - try { - const page = await browser.newPage(); - page.on('console', (msg) => { - // Log the message text to your test runner's console - console.log(`Browser console: ${msg.text()}`); - }); - - await page.goto(`file://${filePath}`); - - bodyDimensions = await getBodyDimensions(page); - - await page.setViewportSize({ - width: Math.round(bodyDimensions.width), - height: Math.round(bodyDimensions.height) - }); - - slideData = await extractSlideData(page); - } finally { - await browser.close(); - } - - // Collect all validation errors - if (bodyDimensions.errors && bodyDimensions.errors.length > 0) { - validationErrors.push(...bodyDimensions.errors); - } - - const dimensionErrors = validateDimensions(bodyDimensions, pres); - if (dimensionErrors.length > 0) { - validationErrors.push(...dimensionErrors); - } - - const textBoxPositionErrors = validateTextBoxPosition(slideData, bodyDimensions); - if (textBoxPositionErrors.length > 0) { - validationErrors.push(...textBoxPositionErrors); - } - - if (slideData.errors && slideData.errors.length > 0) { - validationErrors.push(...slideData.errors); - } - - // Throw all errors at once if any exist - if (validationErrors.length > 0) { - const errorMessage = validationErrors.length === 1 - ? validationErrors[0] - : `Multiple validation errors found:\n${validationErrors.map((e, i) => ` ${i + 1}. ${e}`).join('\n')}`; - throw new Error(errorMessage); - } - - const targetSlide = slide || pres.addSlide(); - - await addBackground(slideData, targetSlide, tmpDir); - addElements(slideData, targetSlide, pres); - - return { slide: targetSlide, placeholders: slideData.placeholders }; - } catch (error) { - if (!error.message.startsWith(htmlFile)) { - throw new Error(`${htmlFile}: ${error.message}`); - } - throw error; - } -} - -module.exports = html2pptx; \ No newline at end of file diff --git a/.claude/skills/pptx-skill/scripts/package-lock.json b/.claude/skills/pptx-skill/scripts/package-lock.json deleted file mode 100755 index 44e44ff..0000000 --- a/.claude/skills/pptx-skill/scripts/package-lock.json +++ /dev/null @@ -1,761 +0,0 @@ -{ - "name": "scripts", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "dependencies": { - "playwright": "^1.58.2", - "pptxgenjs": "^4.0.1", - "sharp": "^0.34.5" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz", - "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@img/colour": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", - "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/@img/sharp-darwin-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", - "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.2.4" - } - }, - "node_modules/@img/sharp-darwin-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", - "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.2.4" - } - }, - "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", - "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", - "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", - "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", - "cpu": [ - "arm" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", - "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", - "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", - "cpu": [ - "ppc64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-riscv64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", - "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", - "cpu": [ - "riscv64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", - "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", - "cpu": [ - "s390x" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", - "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", - "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", - "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-linux-arm": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", - "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", - "cpu": [ - "arm" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", - "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-ppc64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", - "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", - "cpu": [ - "ppc64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-ppc64": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-riscv64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", - "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", - "cpu": [ - "riscv64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-riscv64": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-s390x": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", - "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", - "cpu": [ - "s390x" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", - "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.2.4" - } - }, - "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", - "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" - } - }, - "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", - "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.2.4" - } - }, - "node_modules/@img/sharp-wasm32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", - "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", - "cpu": [ - "wasm32" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", - "optional": true, - "dependencies": { - "@emnapi/runtime": "^1.7.0" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", - "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-ia32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", - "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", - "cpu": [ - "ia32" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", - "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@types/node": { - "version": "22.19.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.3.tgz", - "integrity": "sha512-1N9SBnWYOJTrNZCdh/yJE+t910Y128BoyY+zBLWhL3r0TYzlTmFdXrPwHL9DyFZmlEXNQQolTZh3KHV31QDhyA==", - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "license": "MIT" - }, - "node_modules/detect-libc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", - "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "license": "Apache-2.0", - "engines": { - "node": ">=8" - } - }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/https": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/https/-/https-1.0.0.tgz", - "integrity": "sha512-4EC57ddXrkaF0x83Oj8sM6SLQHAWXw90Skqu2M4AEWENZ3F02dFJE/GARA8igO79tcgYqGrD7ae4f5L3um2lgg==", - "license": "ISC" - }, - "node_modules/image-size": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.2.1.tgz", - "integrity": "sha512-rH+46sQJ2dlwfjfhCyNx5thzrv+dtmBIhPHk0zgRUukHzZ/kRueTJXoYYsclBaKcSMBWuGbOFXtioLpzTb5euw==", - "license": "MIT", - "dependencies": { - "queue": "6.0.2" - }, - "bin": { - "image-size": "bin/image-size.js" - }, - "engines": { - "node": ">=16.x" - } - }, - "node_modules/immediate": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", - "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", - "license": "MIT" - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "license": "MIT" - }, - "node_modules/jszip": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", - "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", - "license": "(MIT OR GPL-3.0-or-later)", - "dependencies": { - "lie": "~3.3.0", - "pako": "~1.0.2", - "readable-stream": "~2.3.6", - "setimmediate": "^1.0.5" - } - }, - "node_modules/lie": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", - "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", - "license": "MIT", - "dependencies": { - "immediate": "~3.0.5" - } - }, - "node_modules/pako": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", - "license": "(MIT AND Zlib)" - }, - "node_modules/playwright": { - "version": "1.58.2", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.58.2.tgz", - "integrity": "sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A==", - "license": "Apache-2.0", - "dependencies": { - "playwright-core": "1.58.2" - }, - "bin": { - "playwright": "cli.js" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "fsevents": "2.3.2" - } - }, - "node_modules/playwright-core": { - "version": "1.58.2", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.58.2.tgz", - "integrity": "sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==", - "license": "Apache-2.0", - "bin": { - "playwright-core": "cli.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/pptxgenjs": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pptxgenjs/-/pptxgenjs-4.0.1.tgz", - "integrity": "sha512-TeJISr8wouAuXw4C1F/mC33xbZs/FuEG6nH9FG1Zj+nuPcGMP5YRHl6X+j3HSUnS1f3at6k75ZZXPMZlA5Lj9A==", - "license": "MIT", - "dependencies": { - "@types/node": "^22.8.1", - "https": "^1.0.0", - "image-size": "^1.2.1", - "jszip": "^3.10.1" - } - }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "license": "MIT" - }, - "node_modules/queue": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz", - "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==", - "license": "MIT", - "dependencies": { - "inherits": "~2.0.3" - } - }, - "node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "license": "MIT" - }, - "node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", - "license": "MIT" - }, - "node_modules/sharp": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", - "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", - "hasInstallScript": true, - "license": "Apache-2.0", - "dependencies": { - "@img/colour": "^1.0.0", - "detect-libc": "^2.1.2", - "semver": "^7.7.3" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.34.5", - "@img/sharp-darwin-x64": "0.34.5", - "@img/sharp-libvips-darwin-arm64": "1.2.4", - "@img/sharp-libvips-darwin-x64": "1.2.4", - "@img/sharp-libvips-linux-arm": "1.2.4", - "@img/sharp-libvips-linux-arm64": "1.2.4", - "@img/sharp-libvips-linux-ppc64": "1.2.4", - "@img/sharp-libvips-linux-riscv64": "1.2.4", - "@img/sharp-libvips-linux-s390x": "1.2.4", - "@img/sharp-libvips-linux-x64": "1.2.4", - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", - "@img/sharp-libvips-linuxmusl-x64": "1.2.4", - "@img/sharp-linux-arm": "0.34.5", - "@img/sharp-linux-arm64": "0.34.5", - "@img/sharp-linux-ppc64": "0.34.5", - "@img/sharp-linux-riscv64": "0.34.5", - "@img/sharp-linux-s390x": "0.34.5", - "@img/sharp-linux-x64": "0.34.5", - "@img/sharp-linuxmusl-arm64": "0.34.5", - "@img/sharp-linuxmusl-x64": "0.34.5", - "@img/sharp-wasm32": "0.34.5", - "@img/sharp-win32-arm64": "0.34.5", - "@img/sharp-win32-ia32": "0.34.5", - "@img/sharp-win32-x64": "0.34.5" - } - }, - "node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD", - "optional": true - }, - "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "license": "MIT" - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "license": "MIT" - } - } -} diff --git a/.claude/skills/pptx-skill/scripts/package.json b/.claude/skills/pptx-skill/scripts/package.json deleted file mode 100755 index 9f60b83..0000000 --- a/.claude/skills/pptx-skill/scripts/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "dependencies": { - "playwright": "^1.58.2", - "pptxgenjs": "^4.0.1", - "sharp": "^0.34.5" - } -} diff --git a/.claude/skills/pptx-skill/scripts/thumbnail.py b/.claude/skills/pptx-skill/scripts/thumbnail.py deleted file mode 100755 index 5c7fdf1..0000000 --- a/.claude/skills/pptx-skill/scripts/thumbnail.py +++ /dev/null @@ -1,450 +0,0 @@ -#!/usr/bin/env python3 -""" -Create thumbnail grids from PowerPoint presentation slides. - -Creates a grid layout of slide thumbnails with configurable columns (max 6). -Each grid contains up to cols×(cols+1) images. For presentations with more -slides, multiple numbered grid files are created automatically. - -The program outputs the names of all files created. - -Output: -- Single grid: {prefix}.jpg (if slides fit in one grid) -- Multiple grids: {prefix}-1.jpg, {prefix}-2.jpg, etc. - -Grid limits by column count: -- 3 cols: max 12 slides per grid (3×4) -- 4 cols: max 20 slides per grid (4×5) -- 5 cols: max 30 slides per grid (5×6) [default] -- 6 cols: max 42 slides per grid (6×7) - -Usage: - python thumbnail.py input.pptx [output_prefix] [--cols N] [--outline-placeholders] - -Examples: - python thumbnail.py presentation.pptx - # Creates: thumbnails.jpg (using default prefix) - # Outputs: - # Created 1 grid(s): - # - thumbnails.jpg - - python thumbnail.py large-deck.pptx grid --cols 4 - # Creates: grid-1.jpg, grid-2.jpg, grid-3.jpg - # Outputs: - # Created 3 grid(s): - # - grid-1.jpg - # - grid-2.jpg - # - grid-3.jpg - - python thumbnail.py template.pptx analysis --outline-placeholders - # Creates thumbnail grids with red outlines around text placeholders -""" - -import argparse -import subprocess -import sys -import tempfile -from pathlib import Path - -from inventory import extract_text_inventory -from PIL import Image, ImageDraw, ImageFont -from pptx import Presentation - -# Constants -THUMBNAIL_WIDTH = 300 # Fixed thumbnail width in pixels -CONVERSION_DPI = 100 # DPI for PDF to image conversion -MAX_COLS = 6 # Maximum number of columns -DEFAULT_COLS = 5 # Default number of columns -JPEG_QUALITY = 95 # JPEG compression quality - -# Grid layout constants -GRID_PADDING = 20 # Padding between thumbnails -BORDER_WIDTH = 2 # Border width around thumbnails -FONT_SIZE_RATIO = 0.12 # Font size as fraction of thumbnail width -LABEL_PADDING_RATIO = 0.4 # Label padding as fraction of font size - - -def main(): - parser = argparse.ArgumentParser( - description="Create thumbnail grids from PowerPoint slides." - ) - parser.add_argument("input", help="Input PowerPoint file (.pptx)") - parser.add_argument( - "output_prefix", - nargs="?", - default="thumbnails", - help="Output prefix for image files (default: thumbnails, will create prefix.jpg or prefix-N.jpg)", - ) - parser.add_argument( - "--cols", - type=int, - default=DEFAULT_COLS, - help=f"Number of columns (default: {DEFAULT_COLS}, max: {MAX_COLS})", - ) - parser.add_argument( - "--outline-placeholders", - action="store_true", - help="Outline text placeholders with a colored border", - ) - - args = parser.parse_args() - - # Validate columns - cols = min(args.cols, MAX_COLS) - if args.cols > MAX_COLS: - print(f"Warning: Columns limited to {MAX_COLS} (requested {args.cols})") - - # Validate input - input_path = Path(args.input) - if not input_path.exists() or input_path.suffix.lower() != ".pptx": - print(f"Error: Invalid PowerPoint file: {args.input}") - sys.exit(1) - - # Construct output path (always JPG) - output_path = Path(f"{args.output_prefix}.jpg") - - print(f"Processing: {args.input}") - - try: - with tempfile.TemporaryDirectory() as temp_dir: - # Get placeholder regions if outlining is enabled - placeholder_regions = None - slide_dimensions = None - if args.outline_placeholders: - print("Extracting placeholder regions...") - placeholder_regions, slide_dimensions = get_placeholder_regions( - input_path - ) - if placeholder_regions: - print(f"Found placeholders on {len(placeholder_regions)} slides") - - # Convert slides to images - slide_images = convert_to_images(input_path, Path(temp_dir), CONVERSION_DPI) - if not slide_images: - print("Error: No slides found") - sys.exit(1) - - print(f"Found {len(slide_images)} slides") - - # Create grids (max cols×(cols+1) images per grid) - grid_files = create_grids( - slide_images, - cols, - THUMBNAIL_WIDTH, - output_path, - placeholder_regions, - slide_dimensions, - ) - - # Print saved files - print(f"Created {len(grid_files)} grid(s):") - for grid_file in grid_files: - print(f" - {grid_file}") - - except Exception as e: - print(f"Error: {e}") - sys.exit(1) - - -def create_hidden_slide_placeholder(size): - """Create placeholder image for hidden slides.""" - img = Image.new("RGB", size, color="#F0F0F0") - draw = ImageDraw.Draw(img) - line_width = max(5, min(size) // 100) - draw.line([(0, 0), size], fill="#CCCCCC", width=line_width) - draw.line([(size[0], 0), (0, size[1])], fill="#CCCCCC", width=line_width) - return img - - -def get_placeholder_regions(pptx_path): - """Extract ALL text regions from the presentation. - - Returns a tuple of (placeholder_regions, slide_dimensions). - text_regions is a dict mapping slide indices to lists of text regions. - Each region is a dict with 'left', 'top', 'width', 'height' in inches. - slide_dimensions is a tuple of (width_inches, height_inches). - """ - prs = Presentation(str(pptx_path)) - inventory = extract_text_inventory(pptx_path, prs) - placeholder_regions = {} - - # Get actual slide dimensions in inches (EMU to inches conversion) - slide_width_inches = (prs.slide_width or 9144000) / 914400.0 - slide_height_inches = (prs.slide_height or 5143500) / 914400.0 - - for slide_key, shapes in inventory.items(): - # Extract slide index from "slide-N" format - slide_idx = int(slide_key.split("-")[1]) - regions = [] - - for shape_key, shape_data in shapes.items(): - # The inventory only contains shapes with text, so all shapes should be highlighted - regions.append( - { - "left": shape_data.left, - "top": shape_data.top, - "width": shape_data.width, - "height": shape_data.height, - } - ) - - if regions: - placeholder_regions[slide_idx] = regions - - return placeholder_regions, (slide_width_inches, slide_height_inches) - - -def convert_to_images(pptx_path, temp_dir, dpi): - """Convert PowerPoint to images via PDF, handling hidden slides.""" - # Detect hidden slides - print("Analyzing presentation...") - prs = Presentation(str(pptx_path)) - total_slides = len(prs.slides) - - # Find hidden slides (1-based indexing for display) - hidden_slides = { - idx + 1 - for idx, slide in enumerate(prs.slides) - if slide.element.get("show") == "0" - } - - print(f"Total slides: {total_slides}") - if hidden_slides: - print(f"Hidden slides: {sorted(hidden_slides)}") - - pdf_path = temp_dir / f"{pptx_path.stem}.pdf" - - # Convert to PDF - print("Converting to PDF...") - result = subprocess.run( - [ - "soffice", - "--headless", - "--convert-to", - "pdf", - "--outdir", - str(temp_dir), - str(pptx_path), - ], - capture_output=True, - text=True, - ) - if result.returncode != 0 or not pdf_path.exists(): - raise RuntimeError("PDF conversion failed") - - # Convert PDF to images - print(f"Converting to images at {dpi} DPI...") - result = subprocess.run( - ["pdftoppm", "-jpeg", "-r", str(dpi), str(pdf_path), str(temp_dir / "slide")], - capture_output=True, - text=True, - ) - if result.returncode != 0: - raise RuntimeError("Image conversion failed") - - visible_images = sorted(temp_dir.glob("slide-*.jpg")) - - # Create full list with placeholders for hidden slides - all_images = [] - visible_idx = 0 - - # Get placeholder dimensions from first visible slide - if visible_images: - with Image.open(visible_images[0]) as img: - placeholder_size = img.size - else: - placeholder_size = (1920, 1080) - - for slide_num in range(1, total_slides + 1): - if slide_num in hidden_slides: - # Create placeholder image for hidden slide - placeholder_path = temp_dir / f"hidden-{slide_num:03d}.jpg" - placeholder_img = create_hidden_slide_placeholder(placeholder_size) - placeholder_img.save(placeholder_path, "JPEG") - all_images.append(placeholder_path) - else: - # Use the actual visible slide image - if visible_idx < len(visible_images): - all_images.append(visible_images[visible_idx]) - visible_idx += 1 - - return all_images - - -def create_grids( - image_paths, - cols, - width, - output_path, - placeholder_regions=None, - slide_dimensions=None, -): - """Create multiple thumbnail grids from slide images, max cols×(cols+1) images per grid.""" - # Maximum images per grid is cols × (cols + 1) for better proportions - max_images_per_grid = cols * (cols + 1) - grid_files = [] - - print( - f"Creating grids with {cols} columns (max {max_images_per_grid} images per grid)" - ) - - # Split images into chunks - for chunk_idx, start_idx in enumerate( - range(0, len(image_paths), max_images_per_grid) - ): - end_idx = min(start_idx + max_images_per_grid, len(image_paths)) - chunk_images = image_paths[start_idx:end_idx] - - # Create grid for this chunk - grid = create_grid( - chunk_images, cols, width, start_idx, placeholder_regions, slide_dimensions - ) - - # Generate output filename - if len(image_paths) <= max_images_per_grid: - # Single grid - use base filename without suffix - grid_filename = output_path - else: - # Multiple grids - insert index before extension with dash - stem = output_path.stem - suffix = output_path.suffix - grid_filename = output_path.parent / f"{stem}-{chunk_idx + 1}{suffix}" - - # Save grid - grid_filename.parent.mkdir(parents=True, exist_ok=True) - grid.save(str(grid_filename), quality=JPEG_QUALITY) - grid_files.append(str(grid_filename)) - - return grid_files - - -def create_grid( - image_paths, - cols, - width, - start_slide_num=0, - placeholder_regions=None, - slide_dimensions=None, -): - """Create thumbnail grid from slide images with optional placeholder outlining.""" - font_size = int(width * FONT_SIZE_RATIO) - label_padding = int(font_size * LABEL_PADDING_RATIO) - - # Get dimensions - with Image.open(image_paths[0]) as img: - aspect = img.height / img.width - height = int(width * aspect) - - # Calculate grid size - rows = (len(image_paths) + cols - 1) // cols - grid_w = cols * width + (cols + 1) * GRID_PADDING - grid_h = rows * (height + font_size + label_padding * 2) + (rows + 1) * GRID_PADDING - - # Create grid - grid = Image.new("RGB", (grid_w, grid_h), "white") - draw = ImageDraw.Draw(grid) - - # Load font with size based on thumbnail width - try: - # Use Pillow's default font with size - font = ImageFont.load_default(size=font_size) - except Exception: - # Fall back to basic default font if size parameter not supported - font = ImageFont.load_default() - - # Place thumbnails - for i, img_path in enumerate(image_paths): - row, col = i // cols, i % cols - x = col * width + (col + 1) * GRID_PADDING - y_base = ( - row * (height + font_size + label_padding * 2) + (row + 1) * GRID_PADDING - ) - - # Add label with actual slide number - label = f"{start_slide_num + i}" - bbox = draw.textbbox((0, 0), label, font=font) - text_w = bbox[2] - bbox[0] - draw.text( - (x + (width - text_w) // 2, y_base + label_padding), - label, - fill="black", - font=font, - ) - - # Add thumbnail below label with proportional spacing - y_thumbnail = y_base + label_padding + font_size + label_padding - - with Image.open(img_path) as img: - # Get original dimensions before thumbnail - orig_w, orig_h = img.size - - # Apply placeholder outlines if enabled - if placeholder_regions and (start_slide_num + i) in placeholder_regions: - # Convert to RGBA for transparency support - if img.mode != "RGBA": - img = img.convert("RGBA") - - # Get the regions for this slide - regions = placeholder_regions[start_slide_num + i] - - # Calculate scale factors using actual slide dimensions - if slide_dimensions: - slide_width_inches, slide_height_inches = slide_dimensions - else: - # Fallback: estimate from image size at CONVERSION_DPI - slide_width_inches = orig_w / CONVERSION_DPI - slide_height_inches = orig_h / CONVERSION_DPI - - x_scale = orig_w / slide_width_inches - y_scale = orig_h / slide_height_inches - - # Create a highlight overlay - overlay = Image.new("RGBA", img.size, (255, 255, 255, 0)) - overlay_draw = ImageDraw.Draw(overlay) - - # Highlight each placeholder region - for region in regions: - # Convert from inches to pixels in the original image - px_left = int(region["left"] * x_scale) - px_top = int(region["top"] * y_scale) - px_width = int(region["width"] * x_scale) - px_height = int(region["height"] * y_scale) - - # Draw highlight outline with red color and thick stroke - # Using a bright red outline instead of fill - stroke_width = max( - 5, min(orig_w, orig_h) // 150 - ) # Thicker proportional stroke width - overlay_draw.rectangle( - [(px_left, px_top), (px_left + px_width, px_top + px_height)], - outline=(255, 0, 0, 255), # Bright red, fully opaque - width=stroke_width, - ) - - # Composite the overlay onto the image using alpha blending - img = Image.alpha_composite(img, overlay) - # Convert back to RGB for JPEG saving - img = img.convert("RGB") - - img.thumbnail((width, height), Image.Resampling.LANCZOS) - w, h = img.size - tx = x + (width - w) // 2 - ty = y_thumbnail + (height - h) // 2 - grid.paste(img, (tx, ty)) - - # Add border - if BORDER_WIDTH > 0: - draw.rectangle( - [ - (tx - BORDER_WIDTH, ty - BORDER_WIDTH), - (tx + w + BORDER_WIDTH - 1, ty + h + BORDER_WIDTH - 1), - ], - outline="gray", - width=BORDER_WIDTH, - ) - - return grid - - -if __name__ == "__main__": - main() diff --git a/.claude/skills/proposal-skill/SKILL.md b/.claude/skills/proposal-skill/SKILL.md deleted file mode 100755 index 23db019..0000000 --- a/.claude/skills/proposal-skill/SKILL.md +++ /dev/null @@ -1,301 +0,0 @@ ---- -name: proposal-skill -description: PDF 기획서 분석 및 PPT 기획서 생성을 위한 고급 스킬. 템플릿 추출, 구조 매핑, 자동 콘텐츠 생성 기능 제공. ---- - -# Proposal Skill - 기획서 생성 스킬 - -PDF 샘플을 분석하여 동일한 형태의 PPT 기획서를 자동 생성하는 전문 스킬입니다. - -## 기능 개요 - -### 1. PDF 구조 분석 (PDF Structure Analysis) -PDF 기획서의 레이아웃과 콘텐츠 패턴을 자동으로 분석 - -### 2. 템플릿 추출 (Template Extraction) -분석 결과를 바탕으로 재사용 가능한 템플릿 생성 - -### 3. 콘텐츠 매핑 (Content Mapping) -사용자 요구사항을 기획서 구조에 자동 매핑 - -### 4. PPT 자동 생성 (Auto PPT Generation) -완성된 기획서를 PowerPoint 파일로 출력 - -## 핵심 워크플로우 - -### PDF → 템플릿 추출 워크플로우 - -```mermaid -graph TD - A[PDF 샘플] --> B[페이지별 구조 분석] - B --> C[섹션 패턴 인식] - C --> D[레이아웃 템플릿 추출] - D --> E[재사용 템플릿 저장] -``` - -### 기획서 생성 워크플로우 - -```mermaid -graph TD - A[사용자 요구사항] --> B[템플릿 매칭] - B --> C[콘텐츠 자동 매핑] - C --> D[구조화된 문서 생성] - D --> E[PPT 슬라이드 변환] - E --> F[완성된 기획서] -``` - -## 스크립트 구조 - -### 1. pdf-analyzer.js -PDF 파일 구조 분석 및 템플릿 추출 - -```javascript -import { PDFAnalyzer } from './pdf-analyzer.js'; - -// PDF 구조 분석 -const analyzer = new PDFAnalyzer(); -const structure = await analyzer.analyze('pdf_sample/SAM_ERP_Storyboard.pdf'); - -// 템플릿 추출 -const template = await analyzer.extractTemplate(structure); -await analyzer.saveTemplate(template, 'templates/erp_storyboard.json'); -``` - -### 2. proposal-generator.js -기획서 자동 생성 엔진 - -```javascript -import { ProposalGenerator } from './proposal-generator.js'; - -const generator = new ProposalGenerator(); - -// 템플릿 기반 기획서 생성 -const proposal = await generator.create({ - template: 'templates/erp_storyboard.json', - project: { - name: '모바일 앱 개발', - type: 'mobile_app', - requirements: requirements_data - } -}); - -// 구조화된 문서 출력 -await generator.export(proposal, 'output/mobile_app_proposal.md'); -``` - -### 3. ppt-converter.js -Markdown 기획서를 PPT로 변환 - -```javascript -import { PPTConverter } from './ppt-converter.js'; - -const converter = new PPTConverter(); - -// 기획서를 PPT로 변환 -await converter.convertProposal({ - input: 'output/mobile_app_proposal.md', - template: 'templates/proposal_template.pptx', - output: 'final/mobile_app_proposal.pptx' -}); -``` - -## 템플릿 구조 정의 - -### template-schema.json -```json -{ - "template_info": { - "name": "ERP Storyboard Template", - "version": "1.0", - "source": "SAM_ERP_Storyboard.pdf", - "created": "2025-01-03" - }, - "page_templates": [ - { - "type": "cover", - "layout": "center_aligned", - "elements": { - "title": { "position": "center_top", "style": "heading1" }, - "subtitle": { "position": "center_middle", "style": "heading2" }, - "date": { "position": "bottom_right", "style": "caption" }, - "logo": { "position": "bottom_left", "style": "image" } - } - }, - { - "type": "document_history", - "layout": "table_centered", - "elements": { - "table": { - "columns": ["날짜", "버전", "주요 내용", "상세 내용", "비고"], - "style": "bordered_table" - } - } - }, - { - "type": "system_structure", - "layout": "diagram_centered", - "elements": { - "diagram": { "type": "hierarchical_tree", "style": "flowchart" }, - "description": { "position": "bottom", "style": "body_text" } - } - }, - { - "type": "detail_screen", - "layout": "two_column", - "elements": { - "wireframe": { "position": "left_column", "style": "image_frame" }, - "description": { "position": "right_column", "style": "numbered_list" }, - "header_info": { "position": "top_table", "style": "info_table" } - } - } - ] -} -``` - -## 자동 콘텐츠 생성 규칙 - -### 1. 표지 생성 -```javascript -function generateCover(projectInfo) { - return { - title: projectInfo.name, - subtitle: `${projectInfo.type} 프로젝트 기획서`, - date: new Date().toISOString().split('T')[0].replace(/-/g, '.'), - version: "D1.0", - company: projectInfo.company || "Company Name" - }; -} -``` - -### 2. 문서 히스토리 생성 -```javascript -function generateHistory(projectInfo) { - const today = new Date().toISOString().split('T')[0].replace(/-/g, '.'); - return [{ - date: today, - version: "D1.0", - main_content: "초안 작성", - detailed_content: `${projectInfo.name} 프로젝트 초안 작성`, - mark: "" - }]; -} -``` - -### 3. 시스템 구조 생성 -```javascript -function generateSystemStructure(requirements) { - // 요구사항에서 메뉴 구조 자동 추출 - const menuStructure = extractMenuFromRequirements(requirements); - return { - type: "hierarchical_diagram", - nodes: menuStructure, - connections: generateConnections(menuStructure) - }; -} -``` - -### 4. 상세 화면 생성 -```javascript -function generateDetailScreens(screens) { - return screens.map((screen, index) => ({ - page_number: index + 5, // 기본 페이지 이후 시작 - screen_info: { - task_name: screen.module, - version: "D1.0", - route: screen.path, - screen_name: screen.name, - screen_id: screen.id - }, - wireframe: generateWireframeDescription(screen), - descriptions: generateFeatureDescriptions(screen.features) - })); -} -``` - -## 사용법 - -### 1. PDF 템플릿 추출 -```bash -node .claude/skills/proposal-skill/scripts/pdf-analyzer.js \ - --input pdf_sample/SAM_ERP_Storyboard.pdf \ - --output templates/extracted_template.json -``` - -### 2. 새 기획서 생성 -```bash -node .claude/skills/proposal-skill/scripts/proposal-generator.js \ - --template templates/extracted_template.json \ - --project project_config.json \ - --output output/new_proposal.md -``` - -### 3. PPT 변환 -```bash -node .claude/skills/proposal-skill/scripts/ppt-converter.js \ - --input output/new_proposal.md \ - --output final/proposal.pptx -``` - -### 4. 통합 실행 -```bash -npm run create-proposal -- \ - --template pdf_sample/SAM_ERP_Storyboard.pdf \ - --project "모바일 앱 개발" \ - --requirements requirements.json \ - --output final/mobile_app_proposal.pptx -``` - -## 고급 기능 - -### 1. 템플릿 자동 매칭 -사용자 프로젝트 타입에 따라 최적 템플릿 자동 선택 - -```javascript -const templateMatcher = { - 'mobile_app': 'templates/mobile_template.json', - 'web_service': 'templates/web_template.json', - 'erp_system': 'templates/erp_template.json', - 'ecommerce': 'templates/ecommerce_template.json' -}; -``` - -### 2. 인터랙티브 콘텐츠 생성 -사용자와의 대화를 통한 단계적 기획서 완성 - -### 3. 버전 관리 시스템 -기획서 변경 이력 자동 추적 및 관리 - -### 4. 협업 기능 -여러 작성자 간 동시 편집 지원 - -## 품질 보증 - -### 자동 검증 기능 -- 필수 섹션 누락 검사 -- 페이지 번호 연속성 확인 -- 참조 무결성 검증 -- 템플릿 준수성 검사 - -### 콘텐츠 품질 기준 -- **완성도**: 모든 필수 정보 포함 -- **일관성**: 용어 및 스타일 통일 -- **정확성**: 요구사항과 결과물 일치 -- **실용성**: 실제 개발 가능한 수준 - -## 확장성 - -### 새로운 템플릿 추가 -1. PDF 샘플을 `pdf_sample/` 디렉토리에 추가 -2. `pdf-analyzer.js`로 구조 분석 및 템플릿 추출 -3. 추출된 템플릿을 `templates/` 디렉토리에 저장 -4. `template-registry.json`에 새 템플릿 등록 - -### 커스텀 생성 규칙 -프로젝트별 특수 요구사항에 맞는 생성 규칙 추가 가능 - -## 연동 가능한 도구 - -- **Research Agent**: 시장 조사 데이터 자동 반영 -- **Organizer Agent**: 콘텐츠 구조화 및 PPT 변환 -- **PPTX Skill**: PowerPoint 파일 최종 생성 -- **Design Skill**: 브랜드 가이드라인 적용 \ No newline at end of file diff --git a/.claude/skills/proposal-skill/scripts/pdf-analyzer.js b/.claude/skills/proposal-skill/scripts/pdf-analyzer.js deleted file mode 100755 index c83a0ea..0000000 --- a/.claude/skills/proposal-skill/scripts/pdf-analyzer.js +++ /dev/null @@ -1,393 +0,0 @@ -/** - * PDF 기획서 구조 분석 및 템플릿 추출 스크립트 - * SAM_ERP_Storyboard.pdf를 분석하여 재사용 가능한 템플릿 생성 - */ - -const fs = require('fs').promises; -const path = require('path'); - -class PDFAnalyzer { - constructor() { - this.pagePatterns = new Map(); - this.extractedTemplate = {}; - } - - /** - * PDF 파일 구조 분석 - */ - async analyze(pdfPath) { - console.log(`📊 PDF 구조 분석 시작: ${pdfPath}`); - - // SAM_ERP 스토리보드 구조 패턴 정의 (PDF 분석 결과 기반) - const structure = { - metadata: { - source: path.basename(pdfPath), - total_pages: 17, // 확인된 페이지 수 - analysis_date: new Date().toISOString(), - document_type: "storyboard_specification" - }, - - sections: [ - { - type: "cover", - pages: [1], - pattern: { - layout: "brand_centered", - elements: ["project_title", "company_name", "date"], - color_scheme: "green_brand" - } - }, - { - type: "document_history", - pages: [2], - pattern: { - layout: "table_full_width", - elements: ["version_table"], - table_columns: ["Date", "Version", "Main Contents", "Detailed Contents", "MARK"] - } - }, - { - type: "menu_structure", - pages: [3], - pattern: { - layout: "hierarchical_diagram", - elements: ["system_tree", "navigation_flow"], - diagram_type: "organizational_chart" - } - }, - { - type: "common_guidelines", - pages: [4, 5, 6, 7, 8], - pattern: { - layout: "documentation_style", - elements: ["section_header", "content_blocks", "code_examples"], - subsections: ["interaction", "responsive", "template", "notifications"] - } - }, - { - type: "detail_screens", - pages: [9, 10, 11, 12, 13, 14, 15, 16, 17], - pattern: { - layout: "wireframe_with_description", - elements: ["header_table", "wireframe_image", "numbered_descriptions"], - header_structure: { - task_name: "단위업무명", - version: "버전", - page_number: "Page Number", - route: "경로", - screen_name: "화면명", - screen_id: "화면 ID" - } - } - } - ] - }; - - console.log(`✅ 구조 분석 완료: ${structure.sections.length}개 섹션 식별`); - return structure; - } - - /** - * 분석 결과에서 재사용 가능한 템플릿 추출 - */ - async extractTemplate(structure) { - console.log("🎨 템플릿 추출 중..."); - - const template = { - template_info: { - name: "ERP Storyboard Template", - version: "1.0", - source: structure.metadata.source, - created: structure.metadata.analysis_date, - description: "ERP 시스템 스토리보드 기획서 템플릿" - }, - - // 페이지 템플릿 정의 - page_templates: [ - // 표지 템플릿 - { - type: "cover", - name: "브랜드 표지", - layout: { - type: "center_aligned", - background: "brand_gradient", - color_scheme: { - primary: "#8BC34A", - secondary: "#FFFFFF", - accent: "#2E7D32" - } - }, - elements: { - project_title: { - position: { x: "center", y: "30%" }, - style: "heading1_bold", - font_size: 48, - color: "white" - }, - company_name: { - position: { x: "right", y: "bottom" }, - style: "corporate", - font_size: 16, - color: "dark_gray" - }, - date: { - position: { x: "right", y: "bottom-20" }, - style: "caption", - font_size: 12, - color: "medium_gray" - }, - brand_logo: { - position: { x: "left", y: "middle" }, - size: { width: 200, height: 100 }, - type: "image_placeholder" - } - } - }, - - // 문서 히스토리 템플릿 - { - type: "document_history", - name: "문서 이력 관리", - layout: { - type: "table_centered", - padding: 40 - }, - elements: { - section_title: { - position: { x: "left", y: "top" }, - content: "Document History", - style: "section_heading" - }, - history_table: { - position: { x: "center", y: "middle" }, - table_config: { - columns: [ - { name: "Date", width: "15%" }, - { name: "Version", width: "10%" }, - { name: "Main Contents", width: "20%" }, - { name: "Detailed Contents", width: "45%" }, - { name: "MARK", width: "10%" } - ], - style: "bordered_table", - header_color: "#8BC34A", - alternate_rows: true - } - } - } - }, - - // 메뉴 구조 템플릿 - { - type: "menu_structure", - name: "시스템 구조도", - layout: { - type: "diagram_centered", - padding: 30 - }, - elements: { - section_title: { - position: { x: "left", y: "top" }, - content: "Menu Structure", - style: "section_heading" - }, - system_diagram: { - position: { x: "center", y: "middle" }, - diagram_config: { - type: "hierarchical_tree", - root_node_style: "rounded_rectangle", - connection_style: "straight_lines", - node_colors: { - level_1: "#8BC34A", - level_2: "#C8E6C9", - level_3: "#E8F5E8" - } - } - } - } - }, - - // 공통 가이드라인 템플릿 - { - type: "common_guidelines", - name: "공통 가이드라인", - layout: { - type: "documentation_style", - padding: 35 - }, - elements: { - section_header: { - position: { x: "left", y: "top" }, - style: "section_divider", - background_color: "#8BC34A" - }, - content_blocks: { - position: { x: "full_width", y: "main_area" }, - style: "structured_content", - spacing: 20 - }, - code_examples: { - style: "code_block", - background_color: "#F5F5F5", - border_color: "#E0E0E0" - } - } - }, - - // 상세 화면 템플릿 - { - type: "detail_screen", - name: "상세 화면 설계", - layout: { - type: "wireframe_with_description", - padding: 25 - }, - elements: { - header_table: { - position: { x: "top", y: "header" }, - table_config: { - columns: [ - { name: "단위업무명", width: "20%" }, - { name: "버전", width: "15%" }, - { name: "Page Number", width: "15%" }, - { name: "경로", width: "25%" }, - { name: "화면명", width: "15%" }, - { name: "화면 ID", width: "10%" } - ], - style: "info_table_header" - } - }, - wireframe_area: { - position: { x: "left", y: "main", width: "60%" }, - style: "wireframe_container", - border: "solid_gray", - background: "light_gray" - }, - description_area: { - position: { x: "right", y: "main", width: "35%" }, - style: "numbered_descriptions", - list_style: "numbered_with_icons" - } - } - } - ], - - // 콘텐츠 생성 규칙 - content_rules: { - auto_numbering: { - pages: true, - descriptions: true, - sections: false - }, - naming_conventions: { - screen_ids: "snake_case", - route_format: "path/screen_name", - version_format: "D1.x" - }, - required_sections: [ - "cover", - "document_history", - "menu_structure", - "common_guidelines" - ] - }, - - // 디자인 시스템 - design_system: { - colors: { - primary: "#8BC34A", - secondary: "#4CAF50", - accent: "#2E7D32", - text_primary: "#212121", - text_secondary: "#757575", - background: "#FFFFFF", - surface: "#F5F5F5", - border: "#E0E0E0" - }, - typography: { - heading1: { font: "Noto Sans", size: 32, weight: "bold" }, - heading2: { font: "Noto Sans", size: 24, weight: "bold" }, - heading3: { font: "Noto Sans", size: 18, weight: "medium" }, - body: { font: "Noto Sans", size: 14, weight: "regular" }, - caption: { font: "Noto Sans", size: 12, weight: "regular" } - }, - spacing: { - page_margin: 40, - section_gap: 30, - element_gap: 15, - line_height: 1.5 - } - } - }; - - console.log("✅ 템플릿 추출 완료"); - return template; - } - - /** - * 추출된 템플릿을 JSON 파일로 저장 - */ - async saveTemplate(template, outputPath) { - try { - const templateDir = path.dirname(outputPath); - await fs.mkdir(templateDir, { recursive: true }); - - await fs.writeFile( - outputPath, - JSON.stringify(template, null, 2), - 'utf8' - ); - - console.log(`💾 템플릿 저장 완료: ${outputPath}`); - return outputPath; - } catch (error) { - console.error("❌ 템플릿 저장 실패:", error); - throw error; - } - } - - /** - * 기본 ERP 템플릿 생성 (샘플 PDF 기반) - */ - async createDefaultTemplate() { - const structure = await this.analyze('pdf_sample/SAM_ERP_Storyboard.pdf'); - const template = await this.extractTemplate(structure); - const outputPath = '.claude/skills/proposal-skill/templates/erp_storyboard_template.json'; - - await this.saveTemplate(template, outputPath); - return outputPath; - } -} - -// 명령줄 실행 지원 -async function main() { - const args = process.argv.slice(2); - const analyzer = new PDFAnalyzer(); - - if (args.includes('--create-default')) { - console.log("🚀 기본 ERP 템플릿 생성 중..."); - const templatePath = await analyzer.createDefaultTemplate(); - console.log(`✅ 완료: ${templatePath}`); - } else if (args.includes('--help')) { - console.log(` -📊 PDF 분석기 사용법: - -기본 템플릿 생성: - node pdf-analyzer.js --create-default - -커스텀 PDF 분석: - node pdf-analyzer.js --input path/to/pdf --output path/to/template.json - -도움말: - node pdf-analyzer.js --help - `); - } else { - // 기본 실행 - await analyzer.createDefaultTemplate(); - } -} - -if (require.main === module) { - main().catch(console.error); -} - -module.exports = { PDFAnalyzer }; \ No newline at end of file diff --git a/.claude/skills/proposal-skill/scripts/proposal-generator.js b/.claude/skills/proposal-skill/scripts/proposal-generator.js deleted file mode 100755 index c2f2d66..0000000 --- a/.claude/skills/proposal-skill/scripts/proposal-generator.js +++ /dev/null @@ -1,829 +0,0 @@ -/** - * 기획서 자동 생성 엔진 - * 템플릿과 요구사항을 바탕으로 완성된 기획서 생성 - */ - -const fs = require('fs').promises; -const path = require('path'); - -class ProposalGenerator { - constructor() { - this.template = null; - this.projectInfo = null; - } - - /** - * 템플릿 로드 - */ - async loadTemplate(templatePath) { - try { - const templateContent = await fs.readFile(templatePath, 'utf8'); - this.template = JSON.parse(templateContent); - console.log(`📋 템플릿 로드: ${this.template.template_info.name}`); - return this.template; - } catch (error) { - console.error("❌ 템플릿 로드 실패:", error); - throw error; - } - } - - /** - * 프로젝트 정보 설정 - */ - setProjectInfo(projectInfo) { - this.projectInfo = { - name: projectInfo.name || "새 프로젝트", - type: projectInfo.type || "web_service", - company: projectInfo.company || "Company Name", - author: projectInfo.author || "작성자", - description: projectInfo.description || "", - requirements: projectInfo.requirements || [], - features: projectInfo.features || [], - target_users: projectInfo.target_users || [], - ...projectInfo - }; - console.log(`🎯 프로젝트 설정: ${this.projectInfo.name}`); - } - - /** - * 기획서 생성 - */ - async generate() { - if (!this.template || !this.projectInfo) { - throw new Error("템플릿과 프로젝트 정보가 필요합니다."); - } - - console.log("📝 기획서 생성 중..."); - - const proposal = { - metadata: this.generateMetadata(), - cover: this.generateCover(), - document_history: this.generateDocumentHistory(), - menu_structure: this.generateMenuStructure(), - common_guidelines: this.generateCommonGuidelines(), - detail_screens: this.generateDetailScreens() - }; - - return proposal; - } - - /** - * 메타데이터 생성 - */ - generateMetadata() { - return { - title: this.projectInfo.name, - subtitle: `${this.projectInfo.type} 프로젝트 기획서`, - version: "D1.0", - created_date: new Date().toISOString().split('T')[0].replace(/-/g, '.'), - author: this.projectInfo.author, - company: this.projectInfo.company, - total_pages: this.estimatePageCount(), - template_used: this.template.template_info.name - }; - } - - /** - * 표지 생성 - */ - generateCover() { - return { - type: "cover", - title: this.projectInfo.name, - subtitle: this.generateSubtitle(), - date: new Date().toISOString().split('T')[0].replace(/-/g, '.'), - version: "D1.0", - company: this.projectInfo.company, - author: this.projectInfo.author, - description: this.projectInfo.description - }; - } - - /** - * 부제목 자동 생성 - */ - generateSubtitle() { - const typeMapping = { - 'mobile_app': '모바일 애플리케이션', - 'web_service': '웹 서비스', - 'erp_system': 'ERP 시스템', - 'ecommerce': '이커머스 플랫폼', - 'cms': '콘텐츠 관리 시스템', - 'crm': '고객관계관리 시스템' - }; - - return `${typeMapping[this.projectInfo.type] || '시스템'} 기획서`; - } - - /** - * 문서 히스토리 생성 - */ - generateDocumentHistory() { - const today = new Date().toISOString().split('T')[0].replace(/-/g, '.'); - - return { - type: "document_history", - table: [ - { - date: today, - version: "D1.0", - main_content: "초안 작성", - detailed_content: `${this.projectInfo.name} 프로젝트 기획서 초안 작성`, - mark: "" - } - ] - }; - } - - /** - * 메뉴 구조 생성 - */ - generateMenuStructure() { - let menuStructure; - - if (this.projectInfo.requirements && this.projectInfo.requirements.length > 0) { - // 요구사항에서 메뉴 구조 추출 - menuStructure = this.extractMenuFromRequirements(); - } else { - // 기본 구조 사용 - menuStructure = this.getDefaultMenuStructure(); - } - - return { - type: "menu_structure", - title: "Menu Structure", - structure: menuStructure - }; - } - - /** - * 요구사항에서 메뉴 구조 추출 - */ - extractMenuFromRequirements() { - const requirements = this.projectInfo.requirements; - const menuMap = new Map(); - - // 요구사항을 카테고리별로 분류 - requirements.forEach(req => { - const category = req.category || this.guessCategory(req.title); - if (!menuMap.has(category)) { - menuMap.set(category, []); - } - menuMap.get(category).push({ - name: req.title, - description: req.description, - priority: req.priority || 'medium' - }); - }); - - // 계층 구조로 변환 - const structure = { - root: this.projectInfo.name, - children: [] - }; - - for (const [category, items] of menuMap) { - structure.children.push({ - name: category, - children: items.map(item => ({ - name: item.name, - leaf: true, - description: item.description - })) - }); - } - - return structure; - } - - /** - * 카테고리 추측 - */ - guessCategory(title) { - const categoryKeywords = { - '사용자관리': ['사용자', '회원', '계정', '인증', '로그인'], - '콘텐츠관리': ['게시글', '콘텐츠', '글', '포스트', '댓글'], - '결제관리': ['결제', '구매', '주문', '결제수단', '카드'], - '상품관리': ['상품', '제품', '상품목록', '카탈로그'], - '시스템관리': ['설정', '관리자', '백오피스', '시스템'], - '통계/분석': ['통계', '분석', '리포트', '대시보드', '차트'] - }; - - for (const [category, keywords] of Object.entries(categoryKeywords)) { - if (keywords.some(keyword => title.includes(keyword))) { - return category; - } - } - - return '기타기능'; - } - - /** - * 기본 메뉴 구조 - */ - getDefaultMenuStructure() { - const defaultStructures = { - 'mobile_app': { - root: "모바일 앱", - children: [ - { name: "인증", children: ["로그인", "회원가입", "비밀번호 찾기"] }, - { name: "메인", children: ["홈", "검색", "카테고리"] }, - { name: "사용자", children: ["프로필", "설정", "알림"] }, - { name: "콘텐츠", children: ["목록", "상세", "작성"] } - ] - }, - 'web_service': { - root: "웹 서비스", - children: [ - { name: "사용자관리", children: ["회원가입", "로그인", "프로필관리"] }, - { name: "콘텐츠관리", children: ["작성", "편집", "삭제", "검색"] }, - { name: "시스템관리", children: ["설정", "통계", "백업"] } - ] - } - }; - - return defaultStructures[this.projectInfo.type] || defaultStructures['web_service']; - } - - /** - * 공통 가이드라인 생성 - */ - generateCommonGuidelines() { - return { - type: "common_guidelines", - sections: [ - { - title: "사용자 인터랙션", - content: this.generateInteractionGuidelines() - }, - { - title: "반응형 디자인", - content: this.generateResponsiveGuidelines() - }, - { - title: "화면 템플릿", - content: this.generateScreenTemplateGuidelines() - }, - { - title: "알림 시스템", - content: this.generateNotificationGuidelines() - } - ] - }; - } - - /** - * 인터랙션 가이드라인 생성 - */ - generateInteractionGuidelines() { - const interactions = [ - { type: "Tap", description: "일정영역을 사용자가 터치합니다.", apply: "Yes" }, - { type: "Scroll Up/Down", description: "상하 스크롤 동작", apply: "Yes" }, - { type: "Swipe Left/Right", description: "좌우 스와이프 동작", apply: "Yes" }, - { type: "Drag & Drop", description: "드래그 앤 드롭 기능", apply: "Yes" } - ]; - - return { - type: "interaction_table", - data: interactions - }; - } - - /** - * 반응형 가이드라인 생성 - */ - generateResponsiveGuidelines() { - return { - type: "responsive_guide", - breakpoints: [ - { device: "모바일", range: "< 640px", note: "기본" }, - { device: "태블릿", range: "768px ~ 1023px", note: "md" }, - { device: "데스크탑", range: "1024px+", note: "lg" }, - { device: "대형 모니터", range: "1280px+", note: "xl" } - ] - }; - } - - /** - * 화면 템플릿 가이드라인 생성 - */ - generateScreenTemplateGuidelines() { - return { - type: "screen_template", - elements: [ - { name: "Header", description: "상단 네비게이션 및 타이틀" }, - { name: "Content", description: "주요 콘텐츠 영역" }, - { name: "Footer", description: "하단 정보 및 링크" }, - { name: "Sidebar", description: "사이드 메뉴 (PC 버전)" } - ] - }; - } - - /** - * 알림 시스템 가이드라인 생성 - */ - generateNotificationGuidelines() { - return { - type: "notification_guide", - types: [ - { name: "알림 Alert", description: "사용자에게 상황을 알려주기 위한 팝업" }, - { name: "확인 Alert", description: "사용자에게 확인이 필요할 경우 제공되는 팝업" }, - { name: "토스트 메시지", description: "단순 Notify (2~3)초 후 페이지 내에서 Fade out" } - ] - }; - } - - /** - * 상세 화면 생성 - */ - generateDetailScreens() { - const screens = []; - - if (this.projectInfo.features && this.projectInfo.features.length > 0) { - // 기능 목록에서 화면 생성 - this.projectInfo.features.forEach((feature, index) => { - screens.push(this.generateScreenFromFeature(feature, index)); - }); - } else if (this.projectInfo.requirements && this.projectInfo.requirements.length > 0) { - // 요구사항에서 화면 생성 - this.projectInfo.requirements.forEach((req, index) => { - screens.push(this.generateScreenFromRequirement(req, index)); - }); - } else { - // 기본 화면들 생성 - screens.push(...this.generateDefaultScreens()); - } - - return { - type: "detail_screens", - screens: screens - }; - } - - /** - * 기능에서 화면 생성 - */ - generateScreenFromFeature(feature, index) { - return { - page_number: 10 + index, // 기본 페이지 이후 - screen_info: { - task_name: this.projectInfo.name, - version: "D1.0", - route: this.generateRoute(feature.name), - screen_name: feature.name, - screen_id: this.generateScreenId(feature.name) - }, - wireframe: { - type: "feature_screen", - description: `${feature.name} 화면 구성`, - elements: this.generateUIElements(feature) - }, - descriptions: this.generateFeatureDescriptions(feature) - }; - } - - /** - * 요구사항에서 화면 생성 - */ - generateScreenFromRequirement(requirement, index) { - return { - page_number: 10 + index, - screen_info: { - task_name: this.projectInfo.name, - version: "D1.0", - route: this.generateRoute(requirement.title), - screen_name: requirement.title, - screen_id: this.generateScreenId(requirement.title) - }, - wireframe: { - type: "requirement_screen", - description: `${requirement.title} 화면`, - elements: this.generateUIElementsFromRequirement(requirement) - }, - descriptions: this.generateRequirementDescriptions(requirement) - }; - } - - /** - * 기본 화면들 생성 - */ - generateDefaultScreens() { - const defaultScreens = [ - { - name: "메인 화면", - route: "/main", - features: ["네비게이션", "주요 콘텐츠", "검색"] - }, - { - name: "로그인", - route: "/auth/login", - features: ["이메일 입력", "비밀번호 입력", "로그인 버튼"] - }, - { - name: "목록 화면", - route: "/list", - features: ["검색 필터", "목록 표시", "페이지네이션"] - } - ]; - - return defaultScreens.map((screen, index) => ({ - page_number: 10 + index, - screen_info: { - task_name: this.projectInfo.name, - version: "D1.0", - route: screen.route, - screen_name: screen.name, - screen_id: this.generateScreenId(screen.name) - }, - wireframe: { - type: "default_screen", - description: `${screen.name} 구성`, - elements: screen.features - }, - descriptions: this.generateDefaultDescriptions(screen) - })); - } - - /** - * 라우트 생성 - */ - generateRoute(screenName) { - return '/' + screenName - .toLowerCase() - .replace(/\s+/g, '_') - .replace(/[^a-z0-9_]/g, ''); - } - - /** - * 화면 ID 생성 - */ - generateScreenId(screenName) { - return screenName - .toLowerCase() - .replace(/\s+/g, '_') - .replace(/[^a-z0-9_]/g, ''); - } - - /** - * UI 요소 생성 - */ - generateUIElements(feature) { - // 기능 타입에 따른 UI 요소 자동 생성 - const elementMap = { - 'login': ['이메일 입력', '비밀번호 입력', '로그인 버튼', '회원가입 링크'], - 'list': ['검색바', '필터', '목록 아이템', '페이지네이션'], - 'form': ['입력 필드', '제출 버튼', '취소 버튼', '유효성 검사'], - 'detail': ['제목', '내용', '수정 버튼', '삭제 버튼', '목록으로 버튼'] - }; - - const featureType = this.guessFeatureType(feature.name); - return elementMap[featureType] || ['헤더', '메인 콘텐츠', '푸터']; - } - - /** - * 기능 타입 추측 - */ - guessFeatureType(featureName) { - const typeKeywords = { - 'login': ['로그인', '인증', '로그인'], - 'list': ['목록', '리스트', '조회'], - 'form': ['등록', '작성', '입력', '수정'], - 'detail': ['상세', '세부', '정보'] - }; - - for (const [type, keywords] of Object.entries(typeKeywords)) { - if (keywords.some(keyword => featureName.includes(keyword))) { - return type; - } - } - - return 'default'; - } - - /** - * 기능 설명 생성 - */ - generateFeatureDescriptions(feature) { - const descriptions = []; - - if (feature.elements) { - feature.elements.forEach((element, index) => { - descriptions.push({ - number: index + 1, - title: element, - description: `${element} 기능 구현` - }); - }); - } - - return descriptions; - } - - /** - * 페이지 수 추정 - */ - estimatePageCount() { - let pageCount = 5; // 기본 페이지 (표지, 히스토리, 구조, 가이드라인) - - if (this.projectInfo.features) { - pageCount += this.projectInfo.features.length; - } else if (this.projectInfo.requirements) { - pageCount += this.projectInfo.requirements.length; - } else { - pageCount += 3; // 기본 화면 수 - } - - return pageCount; - } - - /** - * Markdown 형태로 기획서 출력 - */ - async exportToMarkdown(proposal, outputPath) { - const markdown = this.generateMarkdown(proposal); - - const outputDir = path.dirname(outputPath); - await fs.mkdir(outputDir, { recursive: true }); - - await fs.writeFile(outputPath, markdown, 'utf8'); - console.log(`📄 Markdown 기획서 생성: ${outputPath}`); - - return outputPath; - } - - /** - * Markdown 생성 - */ - generateMarkdown(proposal) { - let markdown = ''; - - // 표지 - markdown += `# ${proposal.cover.title}\n`; - markdown += `## ${proposal.cover.subtitle}\n\n`; - markdown += `**버전**: ${proposal.cover.version} \n`; - markdown += `**작성일**: ${proposal.cover.date} \n`; - markdown += `**작성자**: ${proposal.cover.author} \n`; - markdown += `**회사**: ${proposal.cover.company} \n\n`; - - if (proposal.cover.description) { - markdown += `**프로젝트 설명**: ${proposal.cover.description}\n\n`; - } - - markdown += `---\n\n`; - - // 문서 히스토리 - markdown += `## Document History\n\n`; - markdown += `| 날짜 | 버전 | 주요 내용 | 상세 내용 | 비고 |\n`; - markdown += `|------|------|-----------|-----------|------|\n`; - - proposal.document_history.table.forEach(row => { - markdown += `| ${row.date} | ${row.version} | ${row.main_content} | ${row.detailed_content} | ${row.mark} |\n`; - }); - - markdown += `\n---\n\n`; - - // 메뉴 구조 - markdown += `## Menu Structure\n\n`; - markdown += this.generateMenuMarkdown(proposal.menu_structure.structure); - markdown += `\n---\n\n`; - - // 공통 가이드라인 - markdown += `## 공통 가이드라인\n\n`; - proposal.common_guidelines.sections.forEach(section => { - markdown += `### ${section.title}\n\n`; - markdown += this.generateSectionMarkdown(section.content); - markdown += `\n`; - }); - - markdown += `---\n\n`; - - // 상세 화면들 - markdown += `## 상세 화면 설계\n\n`; - proposal.detail_screens.screens.forEach((screen, index) => { - markdown += `### 슬라이드 ${screen.page_number}: ${screen.screen_info.screen_name}\n\n`; - - markdown += `**화면 정보**:\n`; - markdown += `- 단위업무명: ${screen.screen_info.task_name}\n`; - markdown += `- 경로: ${screen.screen_info.route}\n`; - markdown += `- 화면 ID: ${screen.screen_info.screen_id}\n`; - markdown += `- 버전: ${screen.screen_info.version}\n\n`; - - markdown += `**화면 구성**:\n`; - if (Array.isArray(screen.wireframe.elements)) { - screen.wireframe.elements.forEach((element, idx) => { - markdown += `${idx + 1}. ${element}\n`; - }); - } - - markdown += `\n**기능 설명**:\n`; - if (screen.descriptions && screen.descriptions.length > 0) { - screen.descriptions.forEach(desc => { - markdown += `${desc.number}. **${desc.title}**: ${desc.description}\n`; - }); - } - - markdown += `\n---\n\n`; - }); - - return markdown; - } - - /** - * 메뉴 구조 Markdown 생성 - */ - generateMenuMarkdown(structure, depth = 0) { - let markdown = ''; - const indent = ' '.repeat(depth); - - if (structure.root && depth === 0) { - markdown += `- **${structure.root}**\n`; - } - - if (structure.children) { - structure.children.forEach(child => { - if (typeof child === 'string') { - markdown += `${indent}- ${child}\n`; - } else { - markdown += `${indent}- **${child.name}**\n`; - if (child.children) { - child.children.forEach(subChild => { - const subIndent = ' '.repeat(depth + 1); - if (typeof subChild === 'string') { - markdown += `${subIndent}- ${subChild}\n`; - } else { - markdown += `${subIndent}- ${subChild.name}\n`; - } - }); - } - } - }); - } - - return markdown; - } - - /** - * 섹션 Markdown 생성 - */ - generateSectionMarkdown(content) { - if (content.type === 'interaction_table') { - let markdown = `| Type | Description | Apply |\n|------|-------------|-------|\n`; - content.data.forEach(item => { - markdown += `| ${item.type} | ${item.description} | ${item.apply} |\n`; - }); - return markdown; - } - - if (content.type === 'responsive_guide') { - let markdown = `**브레이크 포인트**:\n`; - content.breakpoints.forEach(bp => { - markdown += `- ${bp.device}: ${bp.range} (${bp.note})\n`; - }); - return markdown; - } - - return JSON.stringify(content, null, 2); - } - - // 요구사항에서 UI 요소 생성 - generateUIElementsFromRequirement(requirement) { - // 요구사항 설명에서 UI 요소 추출 (간단한 키워드 매칭) - const description = requirement.description || requirement.title; - const elements = []; - - // 일반적인 UI 패턴 매칭 - if (description.includes('로그인') || description.includes('인증')) { - elements.push('이메일 입력', '비밀번호 입력', '로그인 버튼'); - } - if (description.includes('목록') || description.includes('조회')) { - elements.push('검색바', '필터', '목록 아이템'); - } - if (description.includes('등록') || description.includes('작성')) { - elements.push('입력 폼', '제출 버튼', '취소 버튼'); - } - - // 기본 요소가 없으면 일반적인 화면 구성 추가 - if (elements.length === 0) { - elements.push('헤더', '메인 콘텐츠', '네비게이션'); - } - - return elements; - } - - // 요구사항 설명 생성 - generateRequirementDescriptions(requirement) { - const descriptions = []; - - descriptions.push({ - number: 1, - title: requirement.title, - description: requirement.description || `${requirement.title} 기능 구현` - }); - - // 우선순위가 있으면 추가 - if (requirement.priority) { - descriptions.push({ - number: 2, - title: "우선순위", - description: `개발 우선순위: ${requirement.priority}` - }); - } - - return descriptions; - } - - // 기본 설명 생성 - generateDefaultDescriptions(screen) { - const descriptions = []; - - screen.features.forEach((feature, index) => { - descriptions.push({ - number: index + 1, - title: feature, - description: `${feature} 구현 및 사용자 인터랙션 처리` - }); - }); - - return descriptions; - } -} - -// 명령줄 실행 지원 -async function main() { - const args = process.argv.slice(2); - - if (args.includes('--help')) { - console.log(` -📝 기획서 생성기 사용법: - -기본 사용: - node proposal-generator.js - -프로젝트 정보로 생성: - node proposal-generator.js --project project.json --output proposal.md - -템플릿 지정: - node proposal-generator.js --template template.json --project project.json - -예시: - node proposal-generator.js --project "모바일 앱" --type mobile_app --output mobile_proposal.md - `); - return; - } - - try { - const generator = new ProposalGenerator(); - - // 템플릿 로드 - const templatePath = '.claude/skills/proposal-skill/templates/erp_storyboard_template.json'; - await generator.loadTemplate(templatePath); - - // 샘플 프로젝트 정보 - const sampleProject = { - name: "모바일 쇼핑몰 앱", - type: "mobile_app", - company: "Sample Company", - author: "기획팀", - description: "사용자 친화적인 모바일 쇼핑 경험을 제공하는 앱", - requirements: [ - { - title: "사용자 로그인", - description: "이메일/비밀번호를 통한 사용자 인증 기능", - category: "사용자관리", - priority: "high" - }, - { - title: "상품 목록 조회", - description: "카테고리별 상품 목록 및 검색 기능", - category: "상품관리", - priority: "high" - }, - { - title: "장바구니", - description: "상품 선택 및 장바구니 관리 기능", - category: "결제관리", - priority: "medium" - } - ] - }; - - generator.setProjectInfo(sampleProject); - - const proposal = await generator.generate(); - const outputPath = 'output/sample_proposal.md'; - - await generator.exportToMarkdown(proposal, outputPath); - - console.log("✅ 샘플 기획서 생성 완료!"); - console.log(`📄 출력 파일: ${outputPath}`); - - } catch (error) { - console.error("❌ 기획서 생성 실패:", error); - } -} - -if (require.main === module) { - main(); -} - -module.exports = { ProposalGenerator }; \ No newline at end of file diff --git a/.claude/skills/query-efficiency-auditor/SKILL.md b/.claude/skills/query-efficiency-auditor/SKILL.md deleted file mode 100644 index 9080207..0000000 --- a/.claude/skills/query-efficiency-auditor/SKILL.md +++ /dev/null @@ -1,202 +0,0 @@ ---- -name: ln-651-query-efficiency-auditor -description: "Query efficiency audit worker (L3). Checks redundant entity fetches, N-UPDATE/DELETE loops, unnecessary resolves, over-fetching, missing bulk operations, wrong caching scope. Returns findings with severity, location, effort, recommendations." -allowed-tools: Read, Grep, Glob, Bash ---- - -# Query Efficiency Auditor (L3 Worker) - -Specialized worker auditing database query patterns for redundancy, inefficiency, and misuse. - -## Purpose & Scope - -- **Worker in ln-650 coordinator pipeline** - invoked by ln-650-persistence-performance-auditor -- Audit **query efficiency** (Priority: HIGH) -- Check redundant fetches, batch operation misuse, caching scope problems -- Return structured findings with severity, location, effort, recommendations -- Calculate compliance score (X/10) for Query Efficiency category - -## Inputs (from Coordinator) - -**MANDATORY READ:** Load `shared/references/task_delegation_pattern.md#audit-coordinator--worker-contract` for contextStore structure. - -Receives `contextStore` with: `tech_stack`, `best_practices`, `db_config` (database type, ORM settings), `codebase_root`. - -**Domain-aware:** Supports `domain_mode` + `current_domain`. - -## Workflow - -1) **Parse context from contextStore** - - Extract tech_stack, best_practices, db_config - - Determine scan_path (same logic as ln-624) - -2) **Scan codebase for violations** - - All Grep/Glob patterns use `scan_path` - - Trace call chains for redundant fetches (requires reading caller + callee) - -3) **Collect findings with severity, location, effort, recommendation** - -4) **Calculate score using penalty algorithm** - -5) **Return JSON result to coordinator** - -## Audit Rules (Priority: HIGH) - -### 1. Redundant Entity Fetch -**What:** Same entity fetched from DB twice in a call chain - -**Detection:** -- Find function A that calls `repo.get(id)` or `session.get(Model, id)`, then passes `id` (not object) to function B -- Function B also calls `repo.get(id)` or `session.get(Model, id)` for the same entity -- Common pattern: `acquire_next_pending()` returns job, but `_process_job(job_id)` re-fetches it - -**Detection patterns (Python/SQLAlchemy):** -- Grep for `repo.*get_by_id|session\.get\(|session\.query.*filter.*id` in service/handler files -- Trace: if function receives `entity_id: int/UUID` AND internally does `repo.get(entity_id)`, check if caller already has entity object -- Check `expire_on_commit` setting: if `False`, objects remain valid after commit - -**Severity:** -- **HIGH:** Redundant fetch in API request handler (adds latency per request) -- **MEDIUM:** Redundant fetch in background job (less critical) - -**Recommendation:** Pass entity object instead of ID, or remove second fetch when `expire_on_commit=False` - -**Effort:** S (change signature to accept object instead of ID) - -### 2. N-UPDATE/DELETE Loop -**What:** Loop of individual UPDATE/DELETE operations instead of single batch query - -**Detection:** -- Pattern: `for item in items: await repo.update(item.id, ...)` or `for item in items: await repo.delete(item.id)` -- Pattern: `for item in items: session.execute(update(Model).where(...))` - -**Detection patterns:** -- Grep for `for .* in .*:` followed by `repo\.(update|delete|reset|save|mark_)` within 1-3 lines -- Grep for `for .* in .*:` followed by `session\.execute\(.*update\(` within 1-3 lines - -**Severity:** -- **HIGH:** Loop over >10 items (N separate round-trips to DB) -- **MEDIUM:** Loop over <=10 items - -**Recommendation:** Replace with single `UPDATE ... WHERE id IN (...)` or `session.execute(update(Model).where(Model.id.in_(ids)))` - -**Effort:** M (rewrite query + test) - -### 3. Unnecessary Resolve -**What:** Re-resolving a value from DB when it is already available in the caller's scope - -**Detection:** -- Method receives `profile_id` and resolves engine from it, but caller already determined `engine` -- Method receives `lang_code` and looks up dialect_id, but caller already has both `lang` and `dialect` -- Pattern: function receives `X_id`, does `get(X_id)`, extracts `.field`, when caller already has `field` - -**Severity:** -- **MEDIUM:** Extra DB query per invocation, especially in high-frequency paths - -**Recommendation:** Split method into two variants: `with_known_value(value, ...)` and `resolving_value(id, ...)`; or pass resolved value directly - -**Effort:** S-M (refactor signature, update callers) - -### 4. Over-Fetching -**What:** Loading full ORM model when only few fields are needed - -**Detection:** -- `session.query(Model)` or `select(Model)` without `.options(load_only(...))` for models with >10 columns -- Especially in list/search endpoints that return many rows -- Pattern: loading full entity but only using 2-3 fields - -**Severity:** -- **MEDIUM:** Large models (>15 columns) in list endpoints -- **LOW:** Small models (<10 columns) or single-entity endpoints - -**Recommendation:** Use `load_only()`, `defer()`, or raw `select(Model.col1, Model.col2)` for list queries - -**Effort:** S (add load_only to query) - -### 5. Missing Bulk Operations -**What:** Sequential INSERT/DELETE/UPDATE instead of bulk operations - -**Detection:** -- `for item in items: session.add(item)` instead of `session.add_all(items)` -- `for item in items: session.delete(item)` instead of bulk delete -- Pattern: loop with single `INSERT` per iteration - -**Severity:** -- **MEDIUM:** Any sequential add/delete in loop (missed batch optimization) - -**Recommendation:** Use `session.add_all()`, `session.execute(insert(Model).values(list_of_dicts))`, `bulk_save_objects()` - -**Effort:** S (replace loop with bulk call) - -### 6. Wrong Caching Scope -**What:** Request-scoped cache for data that rarely changes (should be app-scoped) - -**Detection:** -- Service registered as request-scoped (e.g., via FastAPI `Depends()`) with internal cache (`_cache` dict, `_loaded` flag) -- Cache populated by expensive query (JOINs, aggregations) per each request -- Data TTL >> request duration (e.g., engine configurations, language lists, feature flags) - -**Detection patterns:** -- Find classes with `_cache`, `_loaded`, `_initialized` attributes -- Check if class is created per-request (via DI registration scope) -- Compare: data change frequency vs cache lifetime - -**Severity:** -- **HIGH:** Expensive query (JOINs, subqueries) cached only per-request -- **MEDIUM:** Simple query cached per-request - -**Recommendation:** Move cache to app-scoped service (singleton), add TTL-based invalidation, or use CacheService with configurable TTL - -**Effort:** M (change DI scope, add TTL logic) - -## Scoring Algorithm - -See `shared/references/audit_scoring.md` for unified formula and score interpretation. - -## Output Format - -Return JSON to coordinator: - -```json -{ - "category": "Query Efficiency", - "score": 6, - "total_issues": 8, - "critical": 0, - "high": 3, - "medium": 4, - "low": 1, - "findings": [ - { - "severity": "HIGH", - "location": "app/infrastructure/messaging/job_processor.py:434", - "issue": "Redundant entity fetch: job re-fetched by ID after acquire_next_pending already returned it", - "principle": "Query Efficiency / DRY Data Access", - "recommendation": "Pass job object to _process_job instead of job_id", - "effort": "S" - } - ] -} -``` - -## Critical Rules - -- **Do not auto-fix:** Report only -- **Trace call chains:** Rules 1 and 3 require reading both caller and callee -- **ORM-aware:** Check `expire_on_commit`, `autoflush`, session scope before flagging redundant fetches -- **Context-aware:** Small datasets or infrequent operations may justify simpler code -- **Exclude tests:** Do not flag test fixtures or setup code - -## Definition of Done - -- contextStore parsed (tech_stack, db_config, ORM settings) -- scan_path determined (domain path or codebase root) -- All 6 checks completed: - - redundant fetch, N-UPDATE loop, unnecessary resolve, over-fetching, bulk ops, caching scope -- Findings collected with severity, location, effort, recommendation -- Score calculated -- JSON returned to coordinator - ---- -**Version:** 1.0.0 -**Last Updated:** 2026-02-04 diff --git a/.claude/skills/regression-checker/SKILL.md b/.claude/skills/regression-checker/SKILL.md deleted file mode 100644 index 648816e..0000000 --- a/.claude/skills/regression-checker/SKILL.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -name: ln-502-regression-checker -description: Worker that runs existing tests to catch regressions. Auto-detects framework, reports pass/fail. No status changes or task creation. ---- - -# Regression Checker - -Runs the existing test suite to ensure no regressions after implementation changes. - -## Purpose & Scope -- Detect test framework (pytest/jest/vitest/go test/etc.) and test dirs. -- Execute full suite; capture results for Story quality gate. -- Return PASS/FAIL with counts/log excerpts; never modifies Linear or kanban. - -## When to Use -- **Invoked by ln-500-story-quality-gate** Pass 1 (after ln-501) -- Code quality check passed -- Before test planning pipeline (ln-510) - -## Workflow (concise) -1) Auto-discover framework and test locations from repo config/files. -2) **Read `docs/project/runbook.md`** — get exact test commands, Docker setup, environment variables. Use commands from runbook, NOT guessed commands. -3) Build appropriate test command; run with timeout (~5m); capture stdout/stderr. -4) Parse results: passed/failed counts; key failing tests. -5) Output verdict JSON (PASS or FAIL + failures list) and add Linear comment. - -## Critical Rules -- No selective test runs; run full suite. -- Do not fix tests or change status; only report. -- Language preservation in comment (EN/RU). - -## Definition of Done -- Framework detected; command executed. -- Results parsed; verdict produced with failing tests (if any). -- Linear comment posted with summary. - -## Reference Files -- Risk-based limits used downstream: `../ln-510-test-planner/references/risk_based_testing_guide.md` - ---- -**Version:** 3.1.0 (Added mandatory runbook.md reading before test execution) -**Last Updated:** 2026-01-09 diff --git a/.claude/skills/security-auditor/SKILL.md b/.claude/skills/security-auditor/SKILL.md deleted file mode 100644 index 1b05c46..0000000 --- a/.claude/skills/security-auditor/SKILL.md +++ /dev/null @@ -1,152 +0,0 @@ ---- -name: ln-621-security-auditor -description: Security audit worker (L3). Scans codebase for hardcoded secrets, SQL injection, XSS, insecure dependencies, missing input validation. Returns findings with severity (Critical/High/Medium/Low), location, effort, and recommendations. -allowed-tools: Read, Grep, Glob, Bash ---- - -# Security Auditor (L3 Worker) - -Specialized worker auditing security vulnerabilities in codebase. - -## Purpose & Scope - -- **Worker in ln-620 coordinator pipeline** - invoked by ln-620-codebase-auditor -- Audit codebase for **security vulnerabilities** (Category 1: Critical Priority) -- Scan for hardcoded secrets, SQL injection, XSS, insecure dependencies, missing input validation -- Return structured findings to coordinator with severity, location, effort, recommendations -- Calculate compliance score (X/10) for Security category - -## Inputs (from Coordinator) - -**MANDATORY READ:** Load `shared/references/task_delegation_pattern.md#audit-coordinator--worker-contract` for contextStore structure. - -Receives `contextStore` with: `tech_stack`, `best_practices`, `principles`, `codebase_root`. - -## Workflow - -1) **Parse Context:** Extract tech stack, best practices, codebase root from contextStore -2) **Scan Codebase:** Run security checks using Glob/Grep patterns (see Audit Rules below) -3) **Collect Findings:** Record each violation with severity, location (file:line), effort estimate (S/M/L), recommendation -4) **Calculate Score:** Count violations by severity, calculate compliance score (X/10) -5) **Return Results:** Return JSON with category, score, findings to coordinator - -## Audit Rules (Priority: CRITICAL) - -### 1. Hardcoded Secrets -**What:** API keys, passwords, tokens, private keys in source code - -**Detection:** -- Search patterns: `API_KEY = "..."`, `password = "..."`, `token = "..."`, `SECRET = "..."` -- File extensions: `.ts`, `.js`, `.py`, `.go`, `.java`, `.cs` -- Exclude: `.env.example`, `README.md`, test files with mock data - -**Severity:** -- **CRITICAL:** Production credentials (AWS keys, database passwords, API tokens) -- **HIGH:** Development/staging credentials -- **MEDIUM:** Test credentials in non-test files - -**Recommendation:** Move to environment variables (.env), use secret management (Vault, AWS Secrets Manager) - -**Effort:** S (replace hardcoded value with `process.env.VAR_NAME`) - -### 2. SQL Injection Patterns -**What:** String concatenation in SQL queries instead of parameterized queries - -**Detection:** -- Patterns: `query = "SELECT * FROM users WHERE id=" + userId`, `db.execute(f"SELECT * FROM {table}")`, `` `SELECT * FROM ${table}` `` -- Languages: JavaScript, Python, PHP, Java - -**Severity:** -- **CRITICAL:** User input directly concatenated without sanitization -- **HIGH:** Variable concatenation in production code -- **MEDIUM:** Concatenation with internal variables only - -**Recommendation:** Use parameterized queries (prepared statements), ORM query builders - -**Effort:** M (refactor query to use placeholders) - -### 3. XSS Vulnerabilities -**What:** Unsanitized user input rendered in HTML/templates - -**Detection:** -- Patterns: `innerHTML = userInput`, `dangerouslySetInnerHTML={{__html: data}}`, `echo $userInput;` -- Template engines: Check for unescaped output (`{{ var | safe }}`, `<%- var %>`) - -**Severity:** -- **CRITICAL:** User input directly inserted into DOM without sanitization -- **HIGH:** User input with partial sanitization (insufficient escaping) -- **MEDIUM:** Internal data with potential XSS if compromised - -**Recommendation:** Use framework escaping (React auto-escapes, use `textContent`), sanitize with DOMPurify - -**Effort:** S-M (replace `innerHTML` with `textContent` or sanitize) - -### 4. Insecure Dependencies -**What:** Dependencies with known CVEs (Common Vulnerabilities and Exposures) - -**Detection:** -- Run `npm audit` (Node.js), `pip-audit` (Python), `cargo audit` (Rust), `dotnet list package --vulnerable` (.NET) -- Check for outdated critical dependencies - -**Severity:** -- **CRITICAL:** CVE with exploitable vulnerability in production dependencies -- **HIGH:** CVE in dev dependencies or lower severity production CVEs -- **MEDIUM:** Outdated packages without known CVEs but security risk - -**Recommendation:** Update to patched versions, replace unmaintained packages - -**Effort:** S-M (update package.json, test), L (if breaking changes) - -### 5. Missing Input Validation -**What:** Missing validation at system boundaries (API endpoints, user forms, file uploads) - -**Detection:** -- API routes without validation middleware -- Form handlers without input sanitization -- File uploads without type/size checks -- Missing CORS configuration - -**Severity:** -- **CRITICAL:** File upload without validation, authentication bypass potential -- **HIGH:** Missing validation on sensitive endpoints (payment, auth, user data) -- **MEDIUM:** Missing validation on read-only or internal endpoints - -**Recommendation:** Add validation middleware (Joi, Yup, express-validator), implement input sanitization - -**Effort:** M (add validation schema and middleware) - -## Scoring Algorithm - -See `shared/references/audit_scoring.md` for unified formula and score interpretation. - -## Output Format - -**MANDATORY READ:** Load `shared/references/audit_output_schema.md` for JSON structure. - -Return JSON with `category: "Security"` and checks: hardcoded_secrets, sql_injection, xss_vulnerabilities, insecure_dependencies, missing_input_validation. - -## Critical Rules - -- **Do not auto-fix:** Report violations only; coordinator creates task for user to fix -- **Tech stack aware:** Use contextStore to apply framework-specific patterns (e.g., React XSS vs PHP XSS) -- **False positive reduction:** Exclude test files, example configs, documentation -- **Effort realism:** S = <1 hour, M = 1-4 hours, L = >4 hours -- **Location precision:** Always include `file:line` for programmatic navigation - -## Definition of Done - -- contextStore parsed successfully -- All 5 security checks completed (secrets, SQL injection, XSS, deps, validation) -- Findings collected with severity, location, effort, recommendation -- Score calculated using penalty algorithm -- JSON result returned to coordinator - -## Reference Files - -- **Audit scoring formula:** `shared/references/audit_scoring.md` -- **Audit output schema:** `shared/references/audit_output_schema.md` -- Security audit rules: [references/security_rules.md](references/security_rules.md) - ---- -**Version:** 3.0.0 -**Last Updated:** 2025-12-23 diff --git a/.claude/skills/sharp-edges/SKILL.md b/.claude/skills/sharp-edges/SKILL.md deleted file mode 100644 index b5f86df..0000000 --- a/.claude/skills/sharp-edges/SKILL.md +++ /dev/null @@ -1,292 +0,0 @@ ---- -name: sharp-edges -description: "Identifies error-prone APIs, dangerous configurations, and footgun designs that enable security mistakes. Use when reviewing API designs, configuration schemas, cryptographic library ergonomics, or evaluating whether code follows 'secure by default' and 'pit of success' principles. Triggers: footgun, misuse-resistant, secure defaults, API usability, dangerous configuration." -allowed-tools: - - Read - - Grep - - Glob ---- - -# Sharp Edges Analysis - -Evaluates whether APIs, configurations, and interfaces are resistant to developer misuse. Identifies designs where the "easy path" leads to insecurity. - -## When to Use - -- Reviewing API or library design decisions -- Auditing configuration schemas for dangerous options -- Evaluating cryptographic API ergonomics -- Assessing authentication/authorization interfaces -- Reviewing any code that exposes security-relevant choices to developers - -## When NOT to Use - -- Implementation bugs (use standard code review) -- Business logic flaws (use domain-specific analysis) -- Performance optimization (different concern) - -## Core Principle - -**The pit of success**: Secure usage should be the path of least resistance. If developers must understand cryptography, read documentation carefully, or remember special rules to avoid vulnerabilities, the API has failed. - -## Rationalizations to Reject - -| Rationalization | Why It's Wrong | Required Action | -|-----------------|----------------|-----------------| -| "It's documented" | Developers don't read docs under deadline pressure | Make the secure choice the default or only option | -| "Advanced users need flexibility" | Flexibility creates footguns; most "advanced" usage is copy-paste | Provide safe high-level APIs; hide primitives | -| "It's the developer's responsibility" | Blame-shifting; you designed the footgun | Remove the footgun or make it impossible to misuse | -| "Nobody would actually do that" | Developers do everything imaginable under pressure | Assume maximum developer confusion | -| "It's just a configuration option" | Config is code; wrong configs ship to production | Validate configs; reject dangerous combinations | -| "We need backwards compatibility" | Insecure defaults can't be grandfather-claused | Deprecate loudly; force migration | - -## Sharp Edge Categories - -### 1. Algorithm/Mode Selection Footguns - -APIs that let developers choose algorithms invite choosing wrong ones. - -**The JWT Pattern** (canonical example): -- Header specifies algorithm: attacker can set `"alg": "none"` to bypass signatures -- Algorithm confusion: RSA public key used as HMAC secret when switching RS256→HS256 -- Root cause: Letting untrusted input control security-critical decisions - -**Detection patterns:** -- Function parameters like `algorithm`, `mode`, `cipher`, `hash_type` -- Enums/strings selecting cryptographic primitives -- Configuration options for security mechanisms - -**Example - PHP password_hash allowing weak algorithms:** -```php -// DANGEROUS: allows crc32, md5, sha1 -password_hash($password, PASSWORD_DEFAULT); // Good - no choice -hash($algorithm, $password); // BAD: accepts "crc32" -``` - -### 2. Dangerous Defaults - -Defaults that are insecure, or zero/empty values that disable security. - -**The OTP Lifetime Pattern:** -```python -# What happens when lifetime=0? -def verify_otp(code, lifetime=300): # 300 seconds default - if lifetime == 0: - return True # OOPS: 0 means "accept all"? - # Or does it mean "expired immediately"? -``` - -**Detection patterns:** -- Timeouts/lifetimes that accept 0 (infinite? immediate expiry?) -- Empty strings that bypass checks -- Null values that skip validation -- Boolean defaults that disable security features -- Negative values with undefined semantics - -**Questions to ask:** -- What happens with `timeout=0`? `max_attempts=0`? `key=""`? -- Is the default the most secure option? -- Can any default value disable security entirely? - -### 3. Primitive vs. Semantic APIs - -APIs that expose raw bytes instead of meaningful types invite type confusion. - -**The Libsodium vs. Halite Pattern:** - -```php -// Libsodium (primitives): bytes are bytes -sodium_crypto_box($message, $nonce, $keypair); -// Easy to: swap nonce/keypair, reuse nonces, use wrong key type - -// Halite (semantic): types enforce correct usage -Crypto::seal($message, new EncryptionPublicKey($key)); -// Wrong key type = type error, not silent failure -``` - -**Detection patterns:** -- Functions taking `bytes`, `string`, `[]byte` for distinct security concepts -- Parameters that could be swapped without type errors -- Same type used for keys, nonces, ciphertexts, signatures - -**The comparison footgun:** -```go -// Timing-safe comparison looks identical to unsafe -if hmac == expected { } // BAD: timing attack -if hmac.Equal(mac, expected) { } // Good: constant-time -// Same types, different security properties -``` - -### 4. Configuration Cliffs - -One wrong setting creates catastrophic failure, with no warning. - -**Detection patterns:** -- Boolean flags that disable security entirely -- String configs that aren't validated -- Combinations of settings that interact dangerously -- Environment variables that override security settings -- Constructor parameters with sensible defaults but no validation (callers can override with insecure values) - -**Examples:** -```yaml -# One typo = disaster -verify_ssl: fasle # Typo silently accepted as truthy? - -# Magic values -session_timeout: -1 # Does this mean "never expire"? - -# Dangerous combinations accepted silently -auth_required: true -bypass_auth_for_health_checks: true -health_check_path: "/" # Oops -``` - -```php -// Sensible default doesn't protect against bad callers -public function __construct( - public string $hashAlgo = 'sha256', // Good default... - public int $otpLifetime = 120, // ...but accepts md5, 0, etc. -) {} -``` - -See [config-patterns.md](references/config-patterns.md#unvalidated-constructor-parameters) for detailed patterns. - -### 5. Silent Failures - -Errors that don't surface, or success that masks failure. - -**Detection patterns:** -- Functions returning booleans instead of throwing on security failures -- Empty catch blocks around security operations -- Default values substituted on parse errors -- Verification functions that "succeed" on malformed input - -**Examples:** -```python -# Silent bypass -def verify_signature(sig, data, key): - if not key: - return True # No key = skip verification?! - -# Return value ignored -signature.verify(data, sig) # Throws on failure -crypto.verify(data, sig) # Returns False on failure -# Developer forgets to check return value -``` - -### 6. Stringly-Typed Security - -Security-critical values as plain strings enable injection and confusion. - -**Detection patterns:** -- SQL/commands built from string concatenation -- Permissions as comma-separated strings -- Roles/scopes as arbitrary strings instead of enums -- URLs constructed by joining strings - -**The permission accumulation footgun:** -```python -permissions = "read,write" -permissions += ",admin" # Too easy to escalate - -# vs. type-safe -permissions = {Permission.READ, Permission.WRITE} -permissions.add(Permission.ADMIN) # At least it's explicit -``` - -## Analysis Workflow - -### Phase 1: Surface Identification - -1. **Map security-relevant APIs**: authentication, authorization, cryptography, session management, input validation -2. **Identify developer choice points**: Where can developers select algorithms, configure timeouts, choose modes? -3. **Find configuration schemas**: Environment variables, config files, constructor parameters - -### Phase 2: Edge Case Probing - -For each choice point, ask: -- **Zero/empty/null**: What happens with `0`, `""`, `null`, `[]`? -- **Negative values**: What does `-1` mean? Infinite? Error? -- **Type confusion**: Can different security concepts be swapped? -- **Default values**: Is the default secure? Is it documented? -- **Error paths**: What happens on invalid input? Silent acceptance? - -### Phase 3: Threat Modeling - -Consider three adversaries: - -1. **The Scoundrel**: Actively malicious developer or attacker controlling config - - Can they disable security via configuration? - - Can they downgrade algorithms? - - Can they inject malicious values? - -2. **The Lazy Developer**: Copy-pastes examples, skips documentation - - Will the first example they find be secure? - - Is the path of least resistance secure? - - Do error messages guide toward secure usage? - -3. **The Confused Developer**: Misunderstands the API - - Can they swap parameters without type errors? - - Can they use the wrong key/algorithm/mode by accident? - - Are failure modes obvious or silent? - -### Phase 4: Validate Findings - -For each identified sharp edge: - -1. **Reproduce the misuse**: Write minimal code demonstrating the footgun -2. **Verify exploitability**: Does the misuse create a real vulnerability? -3. **Check documentation**: Is the danger documented? (Documentation doesn't excuse bad design, but affects severity) -4. **Test mitigations**: Can the API be used safely with reasonable effort? - -If a finding seems questionable, return to Phase 2 and probe more edge cases. - -## Severity Classification - -| Severity | Criteria | Examples | -|----------|----------|----------| -| Critical | Default or obvious usage is insecure | `verify: false` default; empty password allowed | -| High | Easy misconfiguration breaks security | Algorithm parameter accepts "none" | -| Medium | Unusual but possible misconfiguration | Negative timeout has unexpected meaning | -| Low | Requires deliberate misuse | Obscure parameter combination | - -## References - -**By category:** - -- **Cryptographic APIs**: See [references/crypto-apis.md](references/crypto-apis.md) -- **Configuration Patterns**: See [references/config-patterns.md](references/config-patterns.md) -- **Authentication/Session**: See [references/auth-patterns.md](references/auth-patterns.md) -- **Real-World Case Studies**: See [references/case-studies.md](references/case-studies.md) (OpenSSL, GMP, etc.) - -**By language** (general footguns, not crypto-specific): - -| Language | Guide | -|----------|-------| -| C/C++ | [references/lang-c.md](references/lang-c.md) | -| Go | [references/lang-go.md](references/lang-go.md) | -| Rust | [references/lang-rust.md](references/lang-rust.md) | -| Swift | [references/lang-swift.md](references/lang-swift.md) | -| Java | [references/lang-java.md](references/lang-java.md) | -| Kotlin | [references/lang-kotlin.md](references/lang-kotlin.md) | -| C# | [references/lang-csharp.md](references/lang-csharp.md) | -| PHP | [references/lang-php.md](references/lang-php.md) | -| JavaScript/TypeScript | [references/lang-javascript.md](references/lang-javascript.md) | -| Python | [references/lang-python.md](references/lang-python.md) | -| Ruby | [references/lang-ruby.md](references/lang-ruby.md) | - -See also [references/language-specific.md](references/language-specific.md) for a combined quick reference. - -## Quality Checklist - -Before concluding analysis: - -- [ ] Probed all zero/empty/null edge cases -- [ ] Verified defaults are secure -- [ ] Checked for algorithm/mode selection footguns -- [ ] Tested type confusion between security concepts -- [ ] Considered all three adversary types -- [ ] Verified error paths don't bypass security -- [ ] Checked configuration validation -- [ ] Constructor params validated (not just defaulted) - see [config-patterns.md](references/config-patterns.md#unvalidated-constructor-parameters) diff --git a/.claude/skills/static-analysis/codeql/SKILL.md b/.claude/skills/static-analysis/codeql/SKILL.md deleted file mode 100644 index 7d87d2a..0000000 --- a/.claude/skills/static-analysis/codeql/SKILL.md +++ /dev/null @@ -1,119 +0,0 @@ ---- -name: codeql -description: >- - Runs CodeQL static analysis for security vulnerability detection - using interprocedural data flow and taint tracking. Applicable when - finding vulnerabilities, running a security scan, performing a security - audit, running CodeQL, building a CodeQL database, selecting query - rulesets, creating data extension models, or processing CodeQL SARIF - output. NOT for writing custom QL queries or CI/CD pipeline setup. -allowed-tools: - - Bash - - Read - - Write - - Glob - - Grep - - AskUserQuestion - - Task - - TaskCreate - - TaskList - - TaskUpdate ---- - -# CodeQL Analysis - -Supported languages: Python, JavaScript/TypeScript, Go, Java/Kotlin, C/C++, C#, Ruby, Swift. - -**Skill resources:** Reference files and templates are located at `{baseDir}/references/` and `{baseDir}/workflows/`. Use `{baseDir}` to resolve paths to these files at runtime. - -## Quick Start - -For the common case ("scan this codebase for vulnerabilities"): - -```bash -# 1. Verify CodeQL is installed -command -v codeql >/dev/null 2>&1 && codeql --version || echo "NOT INSTALLED" - -# 2. Check for existing database -ls -dt codeql_*.db 2>/dev/null | head -1 -``` - -Then execute the full pipeline: **build database → create data extensions → run analysis** using the workflows below. - -## When to Use - -- Scanning a codebase for security vulnerabilities with deep data flow analysis -- Building a CodeQL database from source code (with build capability for compiled languages) -- Finding complex vulnerabilities that require interprocedural taint tracking or AST/CFG analysis -- Performing comprehensive security audits with multiple query packs - -## When NOT to Use - -- **Writing custom queries** - Use a dedicated query development skill -- **CI/CD integration** - Use GitHub Actions documentation directly -- **Quick pattern searches** - Use Semgrep or grep for speed -- **No build capability** for compiled languages - Consider Semgrep instead -- **Single-file or lightweight analysis** - Semgrep is faster for simple pattern matching - -## Rationalizations to Reject - -These shortcuts lead to missed findings. Do not accept them: - -- **"security-extended is enough"** - It is the baseline. Always check if Trail of Bits packs and Community Packs are available for the language. They catch categories `security-extended` misses entirely. -- **"The database built, so it's good"** - A database that builds does not mean it extracted well. Always run Step 4 (quality assessment) and check file counts against expected source files. A cached build produces zero useful extraction. -- **"Data extensions aren't needed for standard frameworks"** - Even Django/Spring apps have custom wrappers around ORM calls, request parsing, or shell execution that CodeQL does not model. Skipping the extensions workflow means missing vulnerabilities in project-specific code. -- **"build-mode=none is fine for compiled languages"** - It produces severely incomplete analysis. No interprocedural data flow through compiled code is traced. Only use as an absolute last resort and clearly flag the limitation. -- **"No findings means the code is secure"** - Zero findings can indicate poor database quality, missing models, or wrong query packs. Investigate before reporting clean results. -- **"I'll just run the default suite"** - The default suite varies by how CodeQL is invoked. Always explicitly specify the suite (e.g., `security-extended`) so results are reproducible. - ---- - -## Workflow Selection - -This skill has three workflows: - -| Workflow | Purpose | -|----------|---------| -| [build-database](workflows/build-database.md) | Create CodeQL database using 3 build methods in sequence | -| [create-data-extensions](workflows/create-data-extensions.md) | Detect or generate data extension models for project APIs | -| [run-analysis](workflows/run-analysis.md) | Select rulesets, execute queries, process results | - - -### Auto-Detection Logic - -**If user explicitly specifies** what to do (e.g., "build a database", "run analysis"), execute that workflow. - -**Default pipeline for "test", "scan", "analyze", or similar:** Execute all three workflows sequentially: build → extensions → analysis. The create-data-extensions step is critical for finding vulnerabilities in projects with custom frameworks or annotations that CodeQL doesn't model by default. - -```bash -# Check if database exists -DB=$(ls -dt codeql_*.db 2>/dev/null | head -1) -if [ -n "$DB" ] && codeql resolve database -- "$DB" >/dev/null 2>&1; then - echo "DATABASE EXISTS ($DB) - can run analysis" -else - echo "NO DATABASE - need to build first" -fi -``` - -| Condition | Action | -|-----------|--------| -| No database exists | Execute build → extensions → analysis (full pipeline) | -| Database exists, no extensions | Execute extensions → analysis | -| Database exists, extensions exist | Ask user: run analysis on existing DB, or rebuild? | -| User says "just run analysis" or "skip extensions" | Run analysis only | - - -### Decision Prompt - -If unclear, ask user: - -``` -I can help with CodeQL analysis. What would you like to do? - -1. **Full scan (Recommended)** - Build database, create extensions, then run analysis -2. **Build database** - Create a new CodeQL database from this codebase -3. **Create data extensions** - Generate custom source/sink models for project APIs -4. **Run analysis** - Run security queries on existing database - -[If database exists: "I found an existing database at "] -``` diff --git a/.claude/skills/static-analysis/sarif-parsing/SKILL.md b/.claude/skills/static-analysis/sarif-parsing/SKILL.md deleted file mode 100644 index 577c6dc..0000000 --- a/.claude/skills/static-analysis/sarif-parsing/SKILL.md +++ /dev/null @@ -1,479 +0,0 @@ ---- -name: sarif-parsing -description: Parse, analyze, and process SARIF (Static Analysis Results Interchange Format) files. Use when reading security scan results, aggregating findings from multiple tools, deduplicating alerts, extracting specific vulnerabilities, or integrating SARIF data into CI/CD pipelines. -allowed-tools: - - Bash - - Read - - Glob - - Grep ---- - -# SARIF Parsing Best Practices - -You are a SARIF parsing expert. Your role is to help users effectively read, analyze, and process SARIF files from static analysis tools. - -## When to Use - -Use this skill when: -- Reading or interpreting static analysis scan results in SARIF format -- Aggregating findings from multiple security tools -- Deduplicating or filtering security alerts -- Extracting specific vulnerabilities from SARIF files -- Integrating SARIF data into CI/CD pipelines -- Converting SARIF output to other formats - -## When NOT to Use - -Do NOT use this skill for: -- Running static analysis scans (use CodeQL or Semgrep skills instead) -- Writing CodeQL or Semgrep rules (use their respective skills) -- Analyzing source code directly (SARIF is for processing existing scan results) -- Triaging findings without SARIF input (use variant-analysis or audit skills) - -## SARIF Structure Overview - -SARIF 2.1.0 is the current OASIS standard. Every SARIF file has this hierarchical structure: - -``` -sarifLog -├── version: "2.1.0" -├── $schema: (optional, enables IDE validation) -└── runs[] (array of analysis runs) - ├── tool - │ ├── driver - │ │ ├── name (required) - │ │ ├── version - │ │ └── rules[] (rule definitions) - │ └── extensions[] (plugins) - ├── results[] (findings) - │ ├── ruleId - │ ├── level (error/warning/note) - │ ├── message.text - │ ├── locations[] - │ │ └── physicalLocation - │ │ ├── artifactLocation.uri - │ │ └── region (startLine, startColumn, etc.) - │ ├── fingerprints{} - │ └── partialFingerprints{} - └── artifacts[] (scanned files metadata) -``` - -### Why Fingerprinting Matters - -Without stable fingerprints, you can't track findings across runs: - -- **Baseline comparison**: "Is this a new finding or did we see it before?" -- **Regression detection**: "Did this PR introduce new vulnerabilities?" -- **Suppression**: "Ignore this known false positive in future runs" - -Tools report different paths (`/path/to/project/` vs `/github/workspace/`), so path-based matching fails. Fingerprints hash the *content* (code snippet, rule ID, relative location) to create stable identifiers regardless of environment. - -## Tool Selection Guide - -| Use Case | Tool | Installation | -|----------|------|--------------| -| Quick CLI queries | jq | `brew install jq` / `apt install jq` | -| Python scripting (simple) | pysarif | `pip install pysarif` | -| Python scripting (advanced) | sarif-tools | `pip install sarif-tools` | -| .NET applications | SARIF SDK | NuGet package | -| JavaScript/Node.js | sarif-js | npm package | -| Go applications | garif | `go get github.com/chavacava/garif` | -| Validation | SARIF Validator | sarifweb.azurewebsites.net | - -## Strategy 1: Quick Analysis with jq - -For rapid exploration and one-off queries: - -```bash -# Pretty print the file -jq '.' results.sarif - -# Count total findings -jq '[.runs[].results[]] | length' results.sarif - -# List all rule IDs triggered -jq '[.runs[].results[].ruleId] | unique' results.sarif - -# Extract errors only -jq '.runs[].results[] | select(.level == "error")' results.sarif - -# Get findings with file locations -jq '.runs[].results[] | { - rule: .ruleId, - message: .message.text, - file: .locations[0].physicalLocation.artifactLocation.uri, - line: .locations[0].physicalLocation.region.startLine -}' results.sarif - -# Filter by severity and get count per rule -jq '[.runs[].results[] | select(.level == "error")] | group_by(.ruleId) | map({rule: .[0].ruleId, count: length})' results.sarif - -# Extract findings for a specific file -jq --arg file "src/auth.py" '.runs[].results[] | select(.locations[].physicalLocation.artifactLocation.uri | contains($file))' results.sarif -``` - -## Strategy 2: Python with pysarif - -For programmatic access with full object model: - -```python -from pysarif import load_from_file, save_to_file - -# Load SARIF file -sarif = load_from_file("results.sarif") - -# Iterate through runs and results -for run in sarif.runs: - tool_name = run.tool.driver.name - print(f"Tool: {tool_name}") - - for result in run.results: - print(f" [{result.level}] {result.rule_id}: {result.message.text}") - - if result.locations: - loc = result.locations[0].physical_location - if loc and loc.artifact_location: - print(f" File: {loc.artifact_location.uri}") - if loc.region: - print(f" Line: {loc.region.start_line}") - -# Save modified SARIF -save_to_file(sarif, "modified.sarif") -``` - -## Strategy 3: Python with sarif-tools - -For aggregation, reporting, and CI/CD integration: - -```python -from sarif import loader - -# Load single file -sarif_data = loader.load_sarif_file("results.sarif") - -# Or load multiple files -sarif_set = loader.load_sarif_files(["tool1.sarif", "tool2.sarif"]) - -# Get summary report -report = sarif_data.get_report() - -# Get histogram by severity -errors = report.get_issue_type_histogram_for_severity("error") -warnings = report.get_issue_type_histogram_for_severity("warning") - -# Filter results -high_severity = [r for r in sarif_data.get_results() - if r.get("level") == "error"] -``` - -**sarif-tools CLI commands:** - -```bash -# Summary of findings -sarif summary results.sarif - -# List all results with details -sarif ls results.sarif - -# Get results by severity -sarif ls --level error results.sarif - -# Diff two SARIF files (find new/fixed issues) -sarif diff baseline.sarif current.sarif - -# Convert to other formats -sarif csv results.sarif > results.csv -sarif html results.sarif > report.html -``` - -## Strategy 4: Aggregating Multiple SARIF Files - -When combining results from multiple tools: - -```python -import json -from pathlib import Path - -def aggregate_sarif_files(sarif_paths: list[str]) -> dict: - """Combine multiple SARIF files into one.""" - aggregated = { - "version": "2.1.0", - "$schema": "https://json.schemastore.org/sarif-2.1.0.json", - "runs": [] - } - - for path in sarif_paths: - with open(path) as f: - sarif = json.load(f) - aggregated["runs"].extend(sarif.get("runs", [])) - - return aggregated - -def deduplicate_results(sarif: dict) -> dict: - """Remove duplicate findings based on fingerprints.""" - seen_fingerprints = set() - - for run in sarif["runs"]: - unique_results = [] - for result in run.get("results", []): - # Use partialFingerprints or create key from location - fp = None - if result.get("partialFingerprints"): - fp = tuple(sorted(result["partialFingerprints"].items())) - elif result.get("fingerprints"): - fp = tuple(sorted(result["fingerprints"].items())) - else: - # Fallback: create fingerprint from rule + location - loc = result.get("locations", [{}])[0] - phys = loc.get("physicalLocation", {}) - fp = ( - result.get("ruleId"), - phys.get("artifactLocation", {}).get("uri"), - phys.get("region", {}).get("startLine") - ) - - if fp not in seen_fingerprints: - seen_fingerprints.add(fp) - unique_results.append(result) - - run["results"] = unique_results - - return sarif -``` - -## Strategy 5: Extracting Actionable Data - -```python -import json -from dataclasses import dataclass -from typing import Optional - -@dataclass -class Finding: - rule_id: str - level: str - message: str - file_path: Optional[str] - start_line: Optional[int] - end_line: Optional[int] - fingerprint: Optional[str] - -def extract_findings(sarif_path: str) -> list[Finding]: - """Extract structured findings from SARIF file.""" - with open(sarif_path) as f: - sarif = json.load(f) - - findings = [] - for run in sarif.get("runs", []): - for result in run.get("results", []): - loc = result.get("locations", [{}])[0] - phys = loc.get("physicalLocation", {}) - region = phys.get("region", {}) - - findings.append(Finding( - rule_id=result.get("ruleId", "unknown"), - level=result.get("level", "warning"), - message=result.get("message", {}).get("text", ""), - file_path=phys.get("artifactLocation", {}).get("uri"), - start_line=region.get("startLine"), - end_line=region.get("endLine"), - fingerprint=next(iter(result.get("partialFingerprints", {}).values()), None) - )) - - return findings - -# Filter and prioritize -def prioritize_findings(findings: list[Finding]) -> list[Finding]: - """Sort findings by severity.""" - severity_order = {"error": 0, "warning": 1, "note": 2, "none": 3} - return sorted(findings, key=lambda f: severity_order.get(f.level, 99)) -``` - -## Common Pitfalls and Solutions - -### 1. Path Normalization Issues - -Different tools report paths differently (absolute, relative, URI-encoded): - -```python -from urllib.parse import unquote -from pathlib import Path - -def normalize_path(uri: str, base_path: str = "") -> str: - """Normalize SARIF artifact URI to consistent path.""" - # Remove file:// prefix if present - if uri.startswith("file://"): - uri = uri[7:] - - # URL decode - uri = unquote(uri) - - # Handle relative paths - if not Path(uri).is_absolute() and base_path: - uri = str(Path(base_path) / uri) - - # Normalize separators - return str(Path(uri)) -``` - -### 2. Fingerprint Mismatch Across Runs - -Fingerprints may not match if: -- File paths differ between environments -- Tool versions changed fingerprinting algorithm -- Code was reformatted (changing line numbers) - -**Solution:** Use multiple fingerprint strategies: - -```python -def compute_stable_fingerprint(result: dict, file_content: str = None) -> str: - """Compute environment-independent fingerprint.""" - import hashlib - - components = [ - result.get("ruleId", ""), - result.get("message", {}).get("text", "")[:100], # First 100 chars - ] - - # Add code snippet if available - if file_content and result.get("locations"): - region = result["locations"][0].get("physicalLocation", {}).get("region", {}) - if region.get("startLine"): - lines = file_content.split("\n") - line_idx = region["startLine"] - 1 - if 0 <= line_idx < len(lines): - # Normalize whitespace - components.append(lines[line_idx].strip()) - - return hashlib.sha256("".join(components).encode()).hexdigest()[:16] -``` - -### 3. Missing or Incomplete Data - -SARIF allows many optional fields. Always use defensive access: - -```python -def safe_get_location(result: dict) -> tuple[str, int]: - """Safely extract file and line from result.""" - try: - loc = result.get("locations", [{}])[0] - phys = loc.get("physicalLocation", {}) - file_path = phys.get("artifactLocation", {}).get("uri", "unknown") - line = phys.get("region", {}).get("startLine", 0) - return file_path, line - except (IndexError, KeyError, TypeError): - return "unknown", 0 -``` - -### 4. Large File Performance - -For very large SARIF files (100MB+): - -```python -import ijson # pip install ijson - -def stream_results(sarif_path: str): - """Stream results without loading entire file.""" - with open(sarif_path, "rb") as f: - # Stream through results arrays - for result in ijson.items(f, "runs.item.results.item"): - yield result -``` - -### 5. Schema Validation - -Validate before processing to catch malformed files: - -```bash -# Using ajv-cli -npm install -g ajv-cli -ajv validate -s sarif-schema-2.1.0.json -d results.sarif - -# Using Python jsonschema -pip install jsonschema -``` - -```python -from jsonschema import validate, ValidationError -import json - -def validate_sarif(sarif_path: str, schema_path: str) -> bool: - """Validate SARIF file against schema.""" - with open(sarif_path) as f: - sarif = json.load(f) - with open(schema_path) as f: - schema = json.load(f) - - try: - validate(sarif, schema) - return True - except ValidationError as e: - print(f"Validation error: {e.message}") - return False -``` - -## CI/CD Integration Patterns - -### GitHub Actions - -```yaml -- name: Upload SARIF - uses: github/codeql-action/upload-sarif@v3 - with: - sarif_file: results.sarif - -- name: Check for high severity - run: | - HIGH_COUNT=$(jq '[.runs[].results[] | select(.level == "error")] | length' results.sarif) - if [ "$HIGH_COUNT" -gt 0 ]; then - echo "Found $HIGH_COUNT high severity issues" - exit 1 - fi -``` - -### Fail on New Issues - -```python -from sarif import loader - -def check_for_regressions(baseline: str, current: str) -> int: - """Return count of new issues not in baseline.""" - baseline_data = loader.load_sarif_file(baseline) - current_data = loader.load_sarif_file(current) - - baseline_fps = {get_fingerprint(r) for r in baseline_data.get_results()} - new_issues = [r for r in current_data.get_results() - if get_fingerprint(r) not in baseline_fps] - - return len(new_issues) -``` - -## Key Principles - -1. **Validate first**: Check SARIF structure before processing -2. **Handle optionals**: Many fields are optional; use defensive access -3. **Normalize paths**: Tools report paths differently; normalize early -4. **Fingerprint wisely**: Combine multiple strategies for stable deduplication -5. **Stream large files**: Use ijson or similar for 100MB+ files -6. **Aggregate thoughtfully**: Preserve tool metadata when combining files - -## Skill Resources - -For ready-to-use query templates, see [{baseDir}/resources/jq-queries.md]({baseDir}/resources/jq-queries.md): -- 40+ jq queries for common SARIF operations -- Severity filtering, rule extraction, aggregation patterns - -For Python utilities, see [{baseDir}/resources/sarif_helpers.py]({baseDir}/resources/sarif_helpers.py): -- `normalize_path()` - Handle tool-specific path formats -- `compute_fingerprint()` - Stable fingerprinting ignoring paths -- `deduplicate_results()` - Remove duplicates across runs - -## Reference Links - -- [OASIS SARIF 2.1.0 Specification](https://docs.oasis-open.org/sarif/sarif/v2.1.0/sarif-v2.1.0.html) -- [Microsoft SARIF Tutorials](https://github.com/microsoft/sarif-tutorials) -- [SARIF SDK (.NET)](https://github.com/microsoft/sarif-sdk) -- [sarif-tools (Python)](https://github.com/microsoft/sarif-tools) -- [pysarif (Python)](https://github.com/Kjeld-P/pysarif) -- [GitHub SARIF Support](https://docs.github.com/en/code-security/code-scanning/integrating-with-code-scanning/sarif-support-for-code-scanning) -- [SARIF Validator](https://sarifweb.azurewebsites.net/) diff --git a/.claude/skills/static-analysis/semgrep/SKILL.md b/.claude/skills/static-analysis/semgrep/SKILL.md deleted file mode 100644 index ca9e28f..0000000 --- a/.claude/skills/static-analysis/semgrep/SKILL.md +++ /dev/null @@ -1,417 +0,0 @@ ---- -name: semgrep -description: Run Semgrep static analysis scan on a codebase using parallel subagents. Automatically - detects and uses Semgrep Pro for cross-file analysis when available. Use when asked to scan - code for vulnerabilities, run a security audit with Semgrep, find bugs, or perform - static analysis. Spawns parallel workers for multi-language codebases and triage. -allowed-tools: - - Bash - - Read - - Glob - - Grep - - Write - - Task - - AskUserQuestion - - TaskCreate - - TaskList - - TaskUpdate - - WebFetch ---- - -# Semgrep Security Scan - -Run a complete Semgrep scan with automatic language detection, parallel execution via Task subagents, and parallel triage. Automatically uses Semgrep Pro for cross-file taint analysis when available. - -## Prerequisites - -**Required:** Semgrep CLI - -```bash -semgrep --version -``` - -If not installed, see [Semgrep installation docs](https://semgrep.dev/docs/getting-started/). - -**Optional:** Semgrep Pro (for cross-file analysis and Pro languages) - -```bash -# Check if Semgrep Pro engine is installed -semgrep --pro --validate --config p/default 2>/dev/null && echo "Pro available" || echo "OSS only" - -# If logged in, install/update Pro Engine -semgrep install-semgrep-pro -``` - -Pro enables: cross-file taint tracking, inter-procedural analysis, and additional languages (Apex, C#, Elixir). - -## When to Use - -- Security audit of a codebase -- Finding vulnerabilities before code review -- Scanning for known bug patterns -- First-pass static analysis - -## When NOT to Use - -- Binary analysis → Use binary analysis tools -- Already have Semgrep CI configured → Use existing pipeline -- Need cross-file analysis but no Pro license → Consider CodeQL as alternative -- Creating custom Semgrep rules → Use `semgrep-rule-creator` skill -- Porting existing rules to other languages → Use `semgrep-rule-variant-creator` skill - ---- - -## Orchestration Architecture - -This skill uses **parallel Task subagents** for maximum efficiency: - -``` -┌─────────────────────────────────────────────────────────────────┐ -│ MAIN AGENT │ -│ 1. Detect languages + check Pro availability │ -│ 2. Select rulesets based on detection (ref: rulesets.md) │ -│ 3. Present plan + rulesets, get approval [⛔ HARD GATE] │ -│ 4. Spawn parallel scan Tasks (with approved rulesets) │ -│ 5. Spawn parallel triage Tasks │ -│ 6. Collect and report results │ -└─────────────────────────────────────────────────────────────────┘ - │ Step 4 │ Step 5 - ▼ ▼ -┌─────────────────┐ ┌─────────────────┐ -│ Scan Tasks │ │ Triage Tasks │ -│ (parallel) │ │ (parallel) │ -├─────────────────┤ ├─────────────────┤ -│ Python scanner │ │ Python triager │ -│ JS/TS scanner │ │ JS/TS triager │ -│ Go scanner │ │ Go triager │ -│ Docker scanner │ │ Docker triager │ -└─────────────────┘ └─────────────────┘ -``` - ---- - -## Workflow Enforcement via Task System - -This skill uses the **Task system** to enforce workflow compliance. On invocation, create these tasks: - -``` -TaskCreate: "Detect languages and Pro availability" (Step 1) -TaskCreate: "Select rulesets based on detection" (Step 2) - blockedBy: Step 1 -TaskCreate: "Present plan with rulesets, get approval" (Step 3) - blockedBy: Step 2 -TaskCreate: "Execute scans with approved rulesets" (Step 4) - blockedBy: Step 3 -TaskCreate: "Triage findings" (Step 5) - blockedBy: Step 4 -TaskCreate: "Report results" (Step 6) - blockedBy: Step 5 -``` - -### Mandatory Gates - -| Task | Gate Type | Cannot Proceed Until | -|------|-----------|---------------------| -| Step 3: Get approval | **HARD GATE** | User explicitly approves rulesets + plan | -| Step 5: Triage | **SOFT GATE** | All scan JSON files exist | - -**Step 3 is a HARD GATE**: Mark as `completed` ONLY after user says "yes", "proceed", "approved", or equivalent. - -### Task Flow Example - -``` -1. Create all 6 tasks with dependencies -2. TaskUpdate Step 1 → in_progress, execute detection -3. TaskUpdate Step 1 → completed -4. TaskUpdate Step 2 → in_progress, select rulesets -5. TaskUpdate Step 2 → completed -6. TaskUpdate Step 3 → in_progress, present plan with rulesets -7. STOP: Wait for user response (may modify rulesets) -8. User approves → TaskUpdate Step 3 → completed -9. TaskUpdate Step 4 → in_progress (now unblocked) -... continue workflow -``` - ---- - -## Workflow - -### Step 1: Detect Languages and Pro Availability (Main Agent) - -```bash -# Check if Semgrep Pro is available (non-destructive check) -SEMGREP_PRO=false -if semgrep --pro --validate --config p/default 2>/dev/null; then - SEMGREP_PRO=true - echo "Semgrep Pro: AVAILABLE (cross-file analysis enabled)" -else - echo "Semgrep Pro: NOT AVAILABLE (OSS mode, single-file analysis)" -fi - -# Find languages by file extension -fd -t f -e py -e js -e ts -e jsx -e tsx -e go -e rb -e java -e php -e c -e cpp -e rs | \ - sed 's/.*\.//' | sort | uniq -c | sort -rn - -# Check for frameworks/technologies -ls -la package.json pyproject.toml Gemfile go.mod Cargo.toml pom.xml 2>/dev/null -fd -t f "Dockerfile" "docker-compose" ".tf" "*.yaml" "*.yml" | head -20 -``` - -Map findings to categories: - -| Detection | Category | -|-----------|----------| -| `.py`, `pyproject.toml` | Python | -| `.js`, `.ts`, `package.json` | JavaScript/TypeScript | -| `.go`, `go.mod` | Go | -| `.rb`, `Gemfile` | Ruby | -| `.java`, `pom.xml` | Java | -| `.php` | PHP | -| `.c`, `.cpp` | C/C++ | -| `.rs`, `Cargo.toml` | Rust | -| `Dockerfile` | Docker | -| `.tf` | Terraform | -| k8s manifests | Kubernetes | - -### Step 2: Select Rulesets Based on Detection - -Using the detected languages and frameworks from Step 1, select rulesets by following the **Ruleset Selection Algorithm** in [rulesets.md]({baseDir}/references/rulesets.md). - -The algorithm covers: -1. Security baseline (always included) -2. Language-specific rulesets -3. Framework rulesets (if detected) -4. Infrastructure rulesets -5. **Required** third-party rulesets (Trail of Bits, 0xdea, Decurity - NOT optional) -6. Registry verification - -**Output:** Structured JSON passed to Step 3 for user review: - -```json -{ - "baseline": ["p/security-audit", "p/secrets"], - "python": ["p/python", "p/django"], - "javascript": ["p/javascript", "p/react", "p/nodejs"], - "docker": ["p/dockerfile"], - "third_party": ["https://github.com/trailofbits/semgrep-rules"] -} -``` - -### Step 3: CRITICAL GATE - Present Plan and Get Approval - -> **⛔ MANDATORY CHECKPOINT - DO NOT SKIP** -> -> This step requires explicit user approval before proceeding. -> User may modify rulesets before approving. - -Present plan to user with **explicit ruleset listing**: - -``` -## Semgrep Scan Plan - -**Target:** /path/to/codebase -**Output directory:** ./semgrep-results-001/ -**Engine:** Semgrep Pro (cross-file analysis) | Semgrep OSS (single-file) - -### Detected Languages/Technologies: -- Python (1,234 files) - Django framework detected -- JavaScript (567 files) - React detected -- Dockerfile (3 files) - -### Rulesets to Run: - -**Security Baseline (always included):** -- [x] `p/security-audit` - Comprehensive security rules -- [x] `p/secrets` - Hardcoded credentials, API keys - -**Python (1,234 files):** -- [x] `p/python` - Python security patterns -- [x] `p/django` - Django-specific vulnerabilities - -**JavaScript (567 files):** -- [x] `p/javascript` - JavaScript security patterns -- [x] `p/react` - React-specific issues -- [x] `p/nodejs` - Node.js server-side patterns - -**Docker (3 files):** -- [x] `p/dockerfile` - Dockerfile best practices - -**Third-party (auto-included for detected languages):** -- [x] Trail of Bits rules - https://github.com/trailofbits/semgrep-rules - -**Available but not selected:** -- [ ] `p/owasp-top-ten` - OWASP Top 10 (overlaps with security-audit) - -### Execution Strategy: -- Spawn 3 parallel scan Tasks (Python, JavaScript, Docker) -- Total rulesets: 9 -- [If Pro] Cross-file taint tracking enabled - -**Want to modify rulesets?** Tell me which to add or remove. -**Ready to scan?** Say "proceed" or "yes". -``` - -**⛔ STOP: Await explicit user approval** - -After presenting the plan: - -1. **If user wants to modify rulesets:** - - Add requested rulesets to the appropriate category - - Remove requested rulesets - - Re-present the updated plan - - Return to waiting for approval - -2. **Use AskUserQuestion** if user hasn't responded: - ``` - "I've prepared the scan plan with 9 rulesets (including Trail of Bits). Proceed with scanning?" - Options: ["Yes, run scan", "Modify rulesets first"] - ``` - -3. **Valid approval responses:** - - "yes", "proceed", "approved", "go ahead", "looks good", "run it" - -4. **Mark task completed** only after approval with final rulesets confirmed - -5. **Do NOT treat as approval:** - - User's original request ("scan this codebase") - - Silence / no response - - Questions about the plan - -### Pre-Scan Checklist - -Before marking Step 3 complete, verify: -- [ ] Target directory shown to user -- [ ] Engine type (Pro/OSS) displayed -- [ ] Languages detected and listed -- [ ] **All rulesets explicitly listed with checkboxes** -- [ ] User given opportunity to modify rulesets -- [ ] User explicitly approved (quote their confirmation) -- [ ] **Final ruleset list captured for Step 4** - -### Step 4: Spawn Parallel Scan Tasks - -Create output directory with run number to avoid collisions, then spawn Tasks with **approved rulesets from Step 3**: - -```bash -# Find next available run number -LAST=$(ls -d semgrep-results-[0-9][0-9][0-9] 2>/dev/null | sort | tail -1 | grep -o '[0-9]*$' || true) -NEXT_NUM=$(printf "%03d" $(( ${LAST:-0} + 1 ))) -OUTPUT_DIR="semgrep-results-${NEXT_NUM}" -mkdir -p "$OUTPUT_DIR" -echo "Output directory: $OUTPUT_DIR" -``` - -**Spawn N Tasks in a SINGLE message** (one per language category) using `subagent_type: Bash`. - -Use the scanner task prompt template from [scanner-task-prompt.md]({baseDir}/references/scanner-task-prompt.md). - -**Example - 3 Language Scan (with approved rulesets):** - -Spawn these 3 Tasks in a SINGLE message: - -1. **Task: Python Scanner** - - Approved rulesets: p/python, p/django, p/security-audit, p/secrets, https://github.com/trailofbits/semgrep-rules - - Output: semgrep-results-001/python-*.json - -2. **Task: JavaScript Scanner** - - Approved rulesets: p/javascript, p/react, p/nodejs, p/security-audit, p/secrets, https://github.com/trailofbits/semgrep-rules - - Output: semgrep-results-001/js-*.json - -3. **Task: Docker Scanner** - - Approved rulesets: p/dockerfile - - Output: semgrep-results-001/docker-*.json - -### Step 5: Spawn Parallel Triage Tasks - -After scan Tasks complete, spawn triage Tasks using `subagent_type: general-purpose` (triage requires reading code context, not just running commands). - -Use the triage task prompt template from [triage-task-prompt.md]({baseDir}/references/triage-task-prompt.md). - -### Step 6: Collect Results (Main Agent) - -After all Tasks complete, generate merged SARIF and report: - -**Generate merged SARIF with only triaged true positives:** - -```bash -uv run {baseDir}/scripts/merge_triaged_sarif.py [OUTPUT_DIR] -``` - -This script: -1. Attempts to use [SARIF Multitool](https://www.npmjs.com/package/@microsoft/sarif-multitool) for merging (if `npx` is available) -2. Falls back to pure Python merge if Multitool unavailable -3. Reads all `*-triage.json` files to extract true positive findings -4. Filters merged SARIF to include only triaged true positives -5. Writes output to `[OUTPUT_DIR]/findings-triaged.sarif` - -**Optional: Install SARIF Multitool for better merge quality:** - -```bash -npm install -g @microsoft/sarif-multitool -``` - -**Report to user:** - -``` -## Semgrep Scan Complete - -**Scanned:** 1,804 files -**Rulesets used:** 9 (including Trail of Bits) -**Total raw findings:** 156 -**After triage:** 32 true positives - -### By Severity: -- ERROR: 5 -- WARNING: 18 -- INFO: 9 - -### By Category: -- SQL Injection: 3 -- XSS: 7 -- Hardcoded secrets: 2 -- Insecure configuration: 12 -- Code quality: 8 - -Results written to: -- semgrep-results-001/findings-triaged.sarif (SARIF, true positives only) -- semgrep-results-001/*-triage.json (triage details per language) -- semgrep-results-001/*.json (raw scan results) -- semgrep-results-001/*.sarif (raw SARIF per ruleset) -``` - ---- - -## Common Mistakes - -| Mistake | Correct Approach | -|---------|------------------| -| Running without `--metrics=off` | Always use `--metrics=off` to prevent telemetry | -| Running rulesets sequentially | Run in parallel with `&` and `wait` | -| Not scoping rulesets to languages | Use `--include="*.py"` for language-specific rules | -| Reporting raw findings without triage | Always triage to remove false positives | -| Single-threaded for multi-lang | Spawn parallel Tasks per language | -| Sequential Tasks | Spawn all Tasks in SINGLE message for parallelism | -| Using OSS when Pro is available | Check login status; use `--pro` for deeper analysis | -| Assuming Pro is unavailable | Always check with login detection before scanning | - -## Limitations - -1. **OSS mode:** Cannot track data flow across files (login with `semgrep login` and run `semgrep install-semgrep-pro` to enable) -2. **Pro mode:** Cross-file analysis uses `-j 1` (single job) which is slower per ruleset, but parallel rulesets compensate -3. Triage requires reading code context - parallelized via Tasks -4. Some false positive patterns require human judgment - -## Rationalizations to Reject - -| Shortcut | Why It's Wrong | -|----------|----------------| -| "User asked for scan, that's approval" | Original request ≠ plan approval; user must confirm specific parameters. Present plan, use AskUserQuestion, await explicit "yes" | -| "Step 3 task is blocking, just mark complete" | Lying about task status defeats enforcement. Only mark complete after real approval | -| "I already know what they want" | Assumptions cause scanning wrong directories/rulesets. Present plan with all parameters for verification | -| "Just use default rulesets" | User must see and approve exact rulesets before scan | -| "Add extra rulesets without asking" | Modifying approved list without consent breaks trust | -| "Skip showing ruleset list" | User can't make informed decision without seeing what will run | -| "Third-party rulesets are optional" | Trail of Bits, 0xdea, Decurity rules catch vulnerabilities not in official registry - they are REQUIRED when language matches | -| "Skip triage, report everything" | Floods user with noise; true issues get lost | -| "Run one ruleset at a time" | Wastes time; parallel execution is faster | -| "Use --config auto" | Sends metrics; less control over rulesets | -| "Triage later" | Findings without context are harder to evaluate | -| "One Task at a time" | Defeats parallelism; spawn all Tasks together | -| "Pro is too slow, skip --pro" | Cross-file analysis catches 250% more true positives; worth the time | -| "Don't bother checking for Pro" | Missing Pro = missing critical cross-file vulnerabilities | -| "OSS is good enough" | OSS misses inter-file taint flows; always prefer Pro when available | diff --git a/.claude/skills/story-quality-gate/SKILL.md b/.claude/skills/story-quality-gate/SKILL.md deleted file mode 100644 index 44e90d0..0000000 --- a/.claude/skills/story-quality-gate/SKILL.md +++ /dev/null @@ -1,171 +0,0 @@ ---- -name: ln-500-story-quality-gate -description: "Story-level quality orchestrator with 4-level Gate (PASS/CONCERNS/FAIL/WAIVED) and Quality Score. Pass 1: code quality -> regression -> manual testing. Pass 2: verify tests/coverage -> calculate NFR scores -> mark Story Done. Use when user requests quality gate for Story or when ln-400 delegates quality check." ---- - -# Story Quality Gate - -Two-pass Story review with 4-level Gate verdict, Quality Score calculation, and NFR validation (security, performance, reliability, maintainability). - -## Purpose & Scope -- Pass 1 (after impl tasks Done): run code-quality, lint, regression, and manual testing; if all pass, create/confirm test task; otherwise create targeted fix/refactor tasks and stop. -- Pass 2 (after test task Done): verify tests/coverage/priority limits, calculate Quality Score and NFR validation, close Story to Done or create fix tasks. -- Delegates work to ln-501/ln-502 workers and ln-510-test-planner. - -## 4-Level Gate Model - -| Level | Meaning | Action | -|-------|---------|--------| -| **PASS** | All checks pass, no issues | Story → Done | -| **CONCERNS** | Minor issues, acceptable risk | Story → Done with comment noting concerns | -| **FAIL** | Blocking issues found | Create fix tasks, return to ln-400 | -| **WAIVED** | Issues acknowledged by user | Story → Done with waiver reason documented | - -**Verdict calculation:** `FAIL` if any check fails. `CONCERNS` if minor issues exist. `PASS` if all clean. - -## Quality Score - -Formula: `Quality Score = 100 - (20 × FAIL_count) - (10 × CONCERN_count)` - -| Score Range | Status | Action | -|-------------|--------|--------| -| 90-100 | ✅ Excellent | PASS | -| 70-89 | ⚠️ Acceptable | CONCERNS (proceed with notes) | -| 50-69 | ❌ Below threshold | FAIL (create fix tasks) | -| <50 | 🚨 Critical | FAIL (urgent priority) | - -## NFR Validation - -Evaluate 4 non-functional requirement dimensions: - -| NFR | Checks | Issue Prefix | -|-----|--------|--------------| -| **Security** | Auth, input validation, secrets exposure | SEC- | -| **Performance** | N+1 queries, caching, response times | PERF- | -| **Reliability** | Error handling, retries, timeouts | REL- | -| **Maintainability** | DRY, SOLID, cyclomatic complexity | MNT- | - -Additional prefixes: `TEST-` (coverage gaps), `ARCH-` (architecture issues), `DOC-` (documentation gaps), `DEP-` (Story/Task dependencies), `COV-` (AC coverage quality), `DB-` (database schema), `AC-` (AC validation) - -**NFR verdict per dimension:** PASS / CONCERNS / FAIL - -## When to Use -- Pass 1: all implementation tasks Done; test task missing or not Done. -- Pass 2: test task exists and is Done. -- Explicit `pass` parameter can force 1 or 2; otherwise auto-detect by test task status. - -## Workflow (concise) -- **Phase 1 Discovery:** Auto-discover team/config; select Story; load Story + task metadata (no descriptions), detect test task status. -- **Pass 1 flow (fail fast):** - 1) Invoke ln-501-code-quality-checker. If issues -> create refactor task (Backlog), stop. - 1.5) **Criteria Validation (Story-level checks)** - see `references/criteria_validation.md`: - - Check #1: Story Dependencies (no forward deps within Epic) - if FAIL → create [DEP-] task, stop. - - Check #2: AC-Task Coverage Quality (STRONG/WEAK/MISSING scoring) - if FAIL/CONCERNS → create [BUG-]/[COV-] tasks, stop. - - Check #3: Database Creation Principle (schema scope matches Story) - if FAIL → create [DB-] task, stop. - 2) Run all linters from tech_stack.md. If fail -> create lint-fix task, stop. - 3) Invoke ln-502-regression-checker. If fail -> create regression-fix task, stop. - 4) Invoke ln-510-test-planner (orchestrates: ln-511-test-researcher → ln-512-manual-tester → ln-513-auto-test-planner). If manual testing fails -> create bug-fix task, stop. If all passed -> test task created/updated. - 5) If test task exists and Done, jump to Pass 2; if exists but not Done, report status and stop. -- **Pass 2 flow (after test task Done):** - 1) Load Story/test task; read test plan/results and manual testing comment from Pass 1. - 2) Verify limits and priority: Priority ≤15; E2E 2-5, Integration 0-8, Unit 0-15, total 10-28; tests focus on business logic (no framework/DB/library tests). - 3) Ensure Priority ≤15 scenarios and Story AC are covered by tests; infra/docs updates present. - 4) **Calculate Quality Score and NFR validation** (see formulas above): - - Run NFR checks per dimensions table - - Assign issue prefixes: SEC-, PERF-, REL-, MNT-, TEST-, ARCH-, DOC- - - Calculate Quality Score - 5) **Determine Gate verdict** per 4-Level Gate Model above - -**TodoWrite format (mandatory):** -Add pass steps to todos before starting: -``` -Pass 1: -- Invoke ln-501-code-quality-checker (in_progress) -- Pass 1.5: Criteria Validation (Story deps, AC coverage, DB schema) (pending) -- Run linters from tech_stack.md (pending) -- Invoke ln-502-regression-checker (pending) -- Invoke ln-510-test-planner (research + manual + auto tests) (pending) - -Pass 2: -- Verify test task coverage (in_progress) -- Mark Story Done (pending) -``` -Mark each as in_progress when starting, completed when done. On failure, mark remaining as skipped. - -## Worker Invocation (MANDATORY) - -| Step | Worker | Context | Rationale | -|------|--------|---------|-----------| -| Code Quality | ln-501-code-quality-checker | **Separate** (Task tool) | Independent analysis, focused on DRY/KISS/YAGNI | -| Regression | ln-502-regression-checker | **Shared** (direct Skill tool) | Needs Story context and previous check results | -| Test Planning | ln-510-test-planner | **Shared** (direct Skill tool) | Needs full Gate context for test planning | - -**ln-501 invocation (Separate Context):** -``` -Task(description: "Code quality check via ln-501", - prompt: "Execute ln-501-code-quality-checker. Read skill from ln-501-code-quality-checker/SKILL.md. Story: {storyId}", - subagent_type: "general-purpose") -``` - -**ln-501 result contract (Task tool return):** -Task tool returns worker's final message. Parse for YAML block: -- `verdict: PASS | CONCERNS | ISSUES_FOUND` -- `quality_score: 0-100` -- `issues: [{id, severity, finding, action}]` -- If verdict = ISSUES_FOUND → create refactor task (Backlog), stop Pass 1. - -**ln-502 and ln-510:** Invoke via direct Skill tool — workers see Gate context. - -**Note:** ln-510 orchestrates the full test pipeline (ln-511 research → ln-512 manual → ln-513 auto tests). - -**❌ FORBIDDEN SHORTCUTS (Anti-Patterns):** -- Running `mypy`, `ruff`, `pytest` directly instead of invoking ln-501/ln-502 -- Doing "minimal quality check" (just linters) and skipping ln-510 test planning -- Asking user "Want me to run the full skill?" after doing partial checks -- Marking steps as "completed" in todo without invoking the actual skill -- Any command execution that should be delegated to a worker skill - -**✅ CORRECT BEHAVIOR:** -- Use `Skill(skill: "ln-50X-...")` for EVERY step — NO EXCEPTIONS -- Wait for each skill to complete before proceeding -- If skill fails → create fix task → STOP (fail fast) -- Never bypass skills with "I'll just run the command myself" - -**ZERO TOLERANCE:** If you find yourself running quality commands directly (mypy, ruff, pytest, curl) instead of invoking the appropriate skill, STOP and use Skill tool instead. - -## Critical Rules -- Early-exit: any failure creates a specific task and stops Pass 1/2. -- Single source of truth: rely on Linear metadata for tasks; kanban is updated by workers/ln-400. -- Task creation via skills only (ln-510/ln-301); this skill never edits tasks directly. -- Pass 2 only runs when test task is Done; otherwise return error/status. -- Language preservation in comments (EN/RU). - -## Definition of Done -- Pass 1: ln-501 pass OR refactor task created; linters pass OR lint-fix task created; ln-502 pass OR regression-fix task created; ln-510 pipeline pass (research + manual + auto tests) OR bug-fix task created; test task created/updated; exits. -- Pass 2: test task verified (priority/limits/coverage/infra/docs); Quality Score calculated; NFR validation completed; Gate verdict determined (PASS/CONCERNS/FAIL/WAIVED). -- **Gate output format:** - ```yaml - gate: PASS | CONCERNS | FAIL | WAIVED - quality_score: {0-100} - nfr_validation: - security: PASS | CONCERNS | FAIL - performance: PASS | CONCERNS | FAIL - reliability: PASS | CONCERNS | FAIL - maintainability: PASS | CONCERNS | FAIL - issues: [{id: "SEC-001", severity: high|medium|low, finding: "...", action: "..."}] - ``` -- Story set to Done (PASS/CONCERNS/WAIVED) or fix tasks created (FAIL); comment with gate verdict posted. - -## Reference Files -- **Orchestrator lifecycle:** `shared/references/orchestrator_pattern.md` -- **Task delegation pattern:** `shared/references/task_delegation_pattern.md` -- **AC validation rules:** `shared/references/ac_validation_rules.md` -- Criteria Validation: `references/criteria_validation.md` (Story deps, AC coverage quality, DB schema checks from ln-310) -- Gate levels: `references/gate_levels.md` (detailed scoring rules and thresholds) -- Workers: `../ln-501-code-quality-checker/SKILL.md`, `../ln-502-regression-checker/SKILL.md` -- Test planning orchestrator: `../ln-510-test-planner/SKILL.md` (coordinates ln-511/512/513) -- Tech stack/linters: `docs/project/tech_stack.md` - ---- -**Version:** 6.0.0 (BREAKING: Added Pass 1.5 Criteria Validation with 3 checks from ln-310 - Story dependencies, AC-Task Coverage Quality (STRONG/WEAK/MISSING), Database Creation Principle. New issue prefixes: DEP-, COV-, DB-, AC-. Closes validation-execution gap at Story level per BMAD Method best practices.) -**Last Updated:** 2026-02-03 diff --git a/.claude/skills/storyboard-generator/SKILL.md b/.claude/skills/storyboard-generator/SKILL.md deleted file mode 100755 index 6712c16..0000000 --- a/.claude/skills/storyboard-generator/SKILL.md +++ /dev/null @@ -1,816 +0,0 @@ ---- -name: storyboard-generator -description: 텍스트 기반 인터뷰/기획 문서를 분석하여 IA(메뉴 구조)와 화면 설계 스토리보드 PPTX를 자동 생성합니다. '기획서 생성', '스토리보드 생성', 'IA 생성' 요청 시 활성화됩니다. -allowed-tools: Read, Write, Edit, Glob, Grep, Bash, Task ---- - -# Storyboard Generator Skill - 기획서/스토리보드 자동 생성 - -텍스트 기반 인터뷰 문서나 기획 문서를 분석하여 웹 시스템 기획서(IA + 스토리보드)를 PPTX로 자동 생성하는 스킬입니다. - -## 활성화 트리거 - -다음 키워드가 포함된 요청 시 이 스킬이 활성화됩니다: -- "기획서 생성" -- "스토리보드 생성" -- "IA 생성" -- "화면 설계" -- "웹 기획" - -## Instructions - -### 1단계: 입력 파일 분석 - -사용자가 제공한 파일 경로에서 텍스트 문서를 읽고 분석합니다. - -```bash -# 파일 읽기 -cat [파일경로] -``` - -분석 대상: -- 인터뷰 스크립트 (script.md, interview.txt 등) -- 기획 문서 (planning.md, requirements.txt 등) -- 요구사항 문서 - -### 2단계: 데이터 추출 - -텍스트에서 다음 정보를 자동 추출합니다: - -| 추출 항목 | 예시 | -|-----------|------| -| 프로젝트명 | 방화셔터 견적 시스템 | -| 주요 기능 | 셔터 타입 선택, 견적 계산 | -| 메뉴 구조 | 견적 입력 > 셔터 선택, 규격 입력 | -| 화면 요소 | 입력 폼, 테이블, 버튼 | -| 비즈니스 규칙 | 할인율, 계산 공식 | - -### 3단계: 설정 파일 생성 - -추출된 데이터를 기반으로 storyboard-config.json 파일을 생성합니다. - -```json -{ - "projectName": "방화셔터 견적 시스템", - "company": "SAM", - "author": "IT 혁신팀", - "date": "2025.01.09", - "purpose": "프로젝트 목적 설명", - "features": ["기능 1", "기능 2"], - "effects": [ - { "icon": "⏱️", "title": "시간 단축", "desc": "설명" } - ], - "tocItems": [ - { "num": "01", "title": "프로젝트 개요", "desc": "설명" } - ], - "mainMenus": [ - { "title": "메뉴1", "children": ["서브1", "서브2"] } - ], - "screens": [ - { - "taskName": "화면명", - "route": "/path", - "screenName": "화면 이름", - "screenId": "SCREEN_001", - "descriptions": [ - { "title": "항목1", "content": "설명" } - ] - } - ] -} -``` - -### 4단계: HTML 슬라이드 생성 (권장) - -**HTML 기반 방식**을 사용하면 더 정교한 디자인이 가능합니다. - -```bash -# 1단계: HTML 슬라이드 생성 -node ~/.claude/skills/storyboard-generator/scripts/generate-html-storyboard.js \ - --config [설정파일.json] \ - --output [html_slides 폴더] - -# 2단계: HTML → PPTX 변환 -node ~/.claude/skills/storyboard-generator/scripts/convert-html-to-pptx.js \ - --input [html_slides 폴더] \ - --output [출력파일.pptx] -``` - -### 5단계 (대안): 직접 PPTX 생성 - -간단한 경우 직접 PPTX를 생성할 수도 있습니다. - -```bash -node ~/.claude/skills/storyboard-generator/scripts/generate-storyboard.js \ - --config [설정파일.json] \ - --output [출력파일.pptx] -``` - -### 6단계: 결과 확인 - -생성된 PPTX 파일 정보를 사용자에게 안내합니다. - -## 생성되는 슬라이드 구조 - -| 슬라이드 | 내용 | 템플릿 | -|----------|------|--------| -| 1 | 표지 | 프로젝트명, 버전, 날짜 | -| 2 | Document History | 문서 이력 테이블 | -| 3 | 목차 | 섹션 번호 + 제목 | -| 4 | 프로젝트 개요 | 목적, 주요 기능, 기대 효과 | -| 5 | 메뉴 구조 (IA) | 계층형 다이어그램 | -| 6+ | 화면 스토리보드 | 와이어프레임 + Description | - -## 스토리보드 슬라이드 레이아웃 - -``` -┌─────────────────────────────────────────────────────────┐ -│ Task Name │ [값] │ Ver. │ D1.0 │ Page │ [N] │ │ -│ Route │ [경로] │ Screen Name │ [화면명] │ ID │ [ID] │ -├───────────────────────────────────┬─────────────────────┤ -│ │ Description │ -│ 와이어프레임 영역 (6.8") │─────────────────────│ -│ │ ① 제목 │ -│ ┌─────────────────────────────┐ │ 설명 텍스트 │ -│ │ 사이드바 │ 메인 콘텐츠 │ │─────────────────────│ -│ │ │ │ │ ② 제목 │ -│ │ │ │ │ 설명 텍스트 │ -│ └─────────────────────────────┘ │─────────────────────│ -│ │ ③ 제목 │ -│ │ 설명 텍스트 │ -└───────────────────────────────────┴─────────────────────┘ -``` - -## wireframeElements 가이드라인 (매우 중요) - -### 사이드바 자동 렌더링 -**사이드바는 `mainMenus` 배열을 기반으로 자동 생성됩니다.** -- 현재 화면의 `taskName`과 일치하는 메뉴가 **활성(highlight)** 상태로 표시됩니다 -- wireframeElements에 사이드바 요소를 포함해도 **자동으로 필터링**되어 중복 표시되지 않습니다 -- x좌표 < 1.5인 요소는 사이드바 영역으로 간주되어 제외됩니다 - -### 좌표 체계 (인치 단위) -``` -┌────────────────────────────────────────────────────────┐ -│ 슬라이드 (10" x 5.625") │ -├─────────┬──────────────────────────────────────────────┤ -│ 사이드바 │ 콘텐츠 영역 │ -│ x < 1.5 │ x >= 1.5 │ -│ │ │ -│ (자동 │ wireframeElements 배치 영역 │ -│ 생성) │ │ -│ │ │ -├─────────┴──────────────────────────────────────────────┤ -``` - -### wireframeElements 작성 예시 -```json -{ - "screens": [ - { - "taskName": "견적서 생성", // mainMenus의 title과 일치해야 함 - "wireframeElements": [ - // ❌ 사이드바 요소 - 작성해도 자동 필터링됨 - {"type": "rect", "x": 0.3, "y": 1.3, "w": 1.2, "h": 4, "fill": "f1f5f9"}, - {"type": "rect", "x": 0.35, "y": 1.4, "w": 1.1, "h": 0.3, "text": "규격 입력"}, - - // ✅ 콘텐츠 영역 요소 - 이것만 실제로 렌더링됨 - {"type": "rect", "x": 1.6, "y": 1.3, "w": 5.3, "h": 0.35, "text": "제목"}, - {"type": "rect", "x": 1.6, "y": 1.75, "w": 2.5, "h": 1, "fill": "0d9488"} - ] - } - ] -} -``` - -### wireframeElements 속성 -| 속성 | 타입 | 설명 | 예시 | -|------|------|------|------| -| type | string | 요소 타입 | "rect" | -| x | number | X 좌표 (인치) | 1.6 | -| y | number | Y 좌표 (인치) | 1.3 | -| w | number | 너비 (인치) | 2.5 | -| h | number | 높이 (인치) | 0.8 | -| fill | string | 배경색 (# 없이) | "0d9488" | -| text | string | 텍스트 내용 | "제목" | -| fontSize | number | 폰트 크기 | 11 | -| color | string | 텍스트 색상 | "FFFFFF" | -| bold | boolean | 굵게 | true | -| align | string | 정렬 | "left", "center", "right" | - -### 사이드바 메뉴 설정 -```json -{ - "mainMenus": [ - { "title": "규격 입력", "children": ["개구부 크기", "제작 규격"] }, - { "title": "단가 계산", "children": ["재질 선택", "면적 단가"] }, - { "title": "부대비용", "children": ["모터 선정", "철거비"] }, - { "title": "견적서 생성", "children": ["마진율", "출력"] } - ], - "screens": [ - { - "taskName": "견적서 생성" // ← 이 화면에서 "견적서 생성" 메뉴가 활성화됨 - } - ] -} -``` - -## Description 마커 시스템 (매우 중요) - -### 빨간 번호 마커 -Description 패널의 각 항목 번호(①②③④)가 **빨간색 원형 마커**로 표시됩니다. -- 와이어프레임 영역에 동일한 빨간 번호 마커가 해당 요소 위치에 표시됩니다 -- Description의 설명이 화면의 어떤 요소를 가리키는지 명확하게 매핑됩니다 - -### markerX, markerY 속성 -descriptions 배열의 각 항목에 `markerX`, `markerY`를 추가하여 마커 위치를 지정합니다. - -```json -{ - "descriptions": [ - { - "title": "개구부 입력", - "content": "고객사 제공 문 크기 입력", - "markerX": 1.6, // 마커 X 좌표 (인치) - "markerY": 1.75 // 마커 Y 좌표 (인치) - }, - { - "title": "제작 규격 변환", - "content": "W+100mm, H+600mm 자동 계산", - "markerX": 5.1, - "markerY": 1.75 - } - ] -} -``` - -### descriptions 속성 -| 속성 | 타입 | 필수 | 설명 | 예시 | -|------|------|------|------|------| -| title | string | ✅ | 항목 제목 | "개구부 입력" | -| content | string | ✅ | 설명 내용 | "mm 단위로 입력" | -| markerX | number | ❌ | 마커 X 좌표 (인치) | 1.6 | -| markerY | number | ❌ | 마커 Y 좌표 (인치) | 1.75 | - -### 마커 위치 가이드 -``` -┌──────────────────────────────────────────────┐ -│ 사이드바 │ 콘텐츠 영역 │ -│ │ ①──────────── ②──────────── │ -│ │ │ 개구부 입력 │ │ 제작 규격 │ │ -│ │ └──────────── └──────────── │ -│ │ │ -│ │ ③──────────── ④──────────── │ -│ │ │ 면적 계산 │ │ 할증 적용 │ │ -│ │ └──────────── └──────────── │ -└──────────────────────────────────────────────┘ -``` - -- markerX, markerY가 없으면 기본 위치(좌측 세로 배치)에 마커 표시 -- 좌표는 wireframeElements와 동일한 인치 단위 사용 -- 마커는 z-index가 높아 다른 요소 위에 표시됨 - -## 색상 팔레트 - -```javascript -const colors = { - primary: '0d9488', // Teal (주요 강조) - secondary: '1e293b', // Slate 800 (헤더, 배경) - accent: '95C11F', // 라임 그린 (Description 헤더) - warning: 'f59e0b', // 경고 (유효기간 등) - danger: 'dc2626', // 위험 (필수 항목) - white: 'FFFFFF', - lightGray: 'f1f5f9', // 배경 - darkGray: '64748b', // 보조 텍스트 - black: '1a1a1a', // Description 영역 배경 - wireframeBg: 'f8fafc', // 와이어프레임 배경 - wireframeBorder: 'e2e8f0' // 와이어프레임 테두리 -}; -``` - -## Description 패널 스타일 가이드 (매우 중요) - -### 배경 없는 투명 스타일 -Description 영역의 각 항목은 **배경색 없이** 투명하게 표현해야 합니다. - -```javascript -// ✅ 올바른 Description 항목 스타일 -descriptions.forEach((desc, idx) => { - const y = 1.25 + idx * 0.85; - - // 번호 원 (라임 그린 배경) - slide.addShape('ellipse', { - x: 7.25, y: y, w: 0.25, h: 0.25, - fill: { color: '95C11F' } // 라임 그린 - }); - slide.addText(String(idx + 1), { - x: 7.25, y: y, w: 0.25, h: 0.25, - fontSize: 7, bold: true, color: 'FFFFFF', - align: 'center', valign: 'middle' - }); - - // 제목 - 배경 없음, 흰색 텍스트 - slide.addText(desc.title, { - x: 7.55, y: y, w: 2.1, h: 0.25, - fontSize: 8, bold: true, color: 'FFFFFF' - // fill 속성 없음 = 투명 배경 - }); - - // 설명 - 배경 없음, 회색 텍스트 - slide.addText(desc.content, { - x: 7.25, y: y + 0.28, w: 2.4, h: 0.5, - fontSize: 7, color: '64748b' - // fill 속성 없음 = 투명 배경 - }); -}); - -// ❌ 잘못된 스타일 - 각 항목에 배경색 적용 -slide.addText(desc.title, { - x: 7.55, y: y, w: 2.1, h: 0.25, - fill: { color: '333333' } // 배경색 금지 -}); -``` - -### Description 영역 구조 -``` -┌─────────────────────────┐ -│ Description (헤더) │ ← 라임 그린 배경 (#95C11F) -├─────────────────────────┤ -│ │ -│ ① 제목 │ ← 번호만 원형 배경, 텍스트는 투명 -│ 설명 텍스트 │ ← 배경 없음 -│ │ -│ ② 제목 │ -│ 설명 텍스트 │ -│ │ -│ ③ 제목 │ -│ 설명 텍스트 │ -│ │ -│ ④ 제목 │ -│ 설명 텍스트 │ -│ │ -└─────────────────────────┘ - 전체 배경: #1a1a1a (검정) - 각 항목: 배경 없음 (투명) -``` - -## 테이블 스타일 가이드 (매우 중요) - -### 일관된 테두리 적용 -모든 테이블은 **동일한 테두리 스타일**을 사용해야 합니다. - -```javascript -// ✅ 올바른 테이블 테두리 스타일 -slide.addTable(data, { - x: 0.5, y: 1, w: 9, h: 2, - fontSize: 10, - color: '1e293b', - border: { pt: 0.5, color: '64748b' }, // 일관된 0.5pt, darkGray - colW: [1.2, 0.8, 1.5, 4, 1.5], - align: 'center', - valign: 'middle' -}); - -// ❌ 잘못된 스타일 - 테두리 불일치 -slide.addTable(data, { - border: { pt: 1, color: '000000' } // 다른 두께/색상 사용 금지 -}); -``` - -### 헤더 정보 테이블 (스토리보드 상단) -```javascript -const headerData = [ - ['Task Name', screenInfo.taskName, 'Ver.', 'D1.0', 'Page', screenInfo.page], - ['Route', screenInfo.route, 'Screen Name', screenInfo.screenName, 'Screen ID', screenInfo.screenId] -]; - -slide.addTable(headerData, { - x: 0.2, y: 0.1, w: 9.6, h: 0.5, - fontSize: 8, - color: '1e293b', - border: { pt: 0.5, color: '64748b' }, // 표준 테두리 - fill: { color: 'f1f5f9' }, // 연한 회색 배경 - colW: [0.8, 2.5, 0.8, 2.5, 0.8, 2.2] // 균일한 열 너비 -}); -``` - -### 와이어프레임 내 테이블 형태 요소 -```javascript -// 와이어프레임 내 테이블 스타일 박스 -slide.addShape('rect', { - x: 1.6, y: 2.2, w: 5.3, h: 0.35, - fill: { color: '1e293b' }, - line: { color: 'e2e8f0', width: 0.5 } // 일관된 테두리 -}); - -// 행 구분 -slide.addShape('rect', { - x: 1.6, y: 2.6, w: 5.3, h: 0.35, - fill: { color: 'FFFFFF' }, - line: { color: 'e2e8f0', width: 0.5 } // 동일한 테두리 -}); -``` - -### 테이블 스타일 요약 -| 요소 | 테두리 두께 | 테두리 색상 | 배경색 | -|------|------------|------------|--------| -| 헤더 정보 테이블 | 0.5pt | 64748b | f1f5f9 | -| Document History | 0.5pt | 64748b | FFFFFF | -| 와이어프레임 테이블 | 0.5pt | e2e8f0 | FFFFFF/1e293b | -| IA 메뉴 박스 | 0.5pt | e2e8f0 | f1f5f9 | - -## PptxGenJS 핵심 규칙 - -### 색상 코드 (매우 중요) -```javascript -// ✅ 올바른 사용 - # 없이 -{ color: 'FF0000' } -{ fill: { color: '1e3a5f' } } - -// ❌ 잘못된 사용 - 파일 손상 -{ color: '#FF0000' } -``` - -### 슬라이드 크기 (16:9) -```javascript -pptx.defineLayout({ name: 'CUSTOM_16x9', width: 10, height: 5.625 }); -pptx.layout = 'CUSTOM_16x9'; -``` - -### 요소 추가 예시 -```javascript -// 텍스트 -slide.addText('제목', { - x: 0.5, y: 0.5, w: 9, h: 0.5, - fontSize: 20, bold: true, color: '1e293b' -}); - -// 도형 -slide.addShape('rect', { - x: 0, y: 0, w: 10, h: 0.7, - fill: { color: '1e293b' } -}); - -// 테이블 -slide.addTable(data, { - x: 0.5, y: 1, w: 9, h: 2, - fontSize: 10, - border: { pt: 0.5, color: '64748b' } -}); -``` - -## 입력 파일 형식 지원 - -### Markdown (.md) -```markdown -## 프로젝트명: 방화셔터 견적 시스템 - -### 주요 기능 -1. 셔터 타입 선택 -2. 규격 입력 및 계산 -3. 견적서 생성 -``` - -### 텍스트 (.txt) -``` -=== 프로젝트 개요 === -방화셔터 견적서 자동화 시스템 - -=== 메뉴 구조 === -- 견적 입력 - - 셔터 타입 선택 - - 규격 입력 -``` - -### 인터뷰 스크립트 -``` -**김철수:** 어떤 기능이 필요한가요? -**박지민:** 셔터 종류 선택과 자동 계산이요. -``` - -## 실행 예시 - -### 기본 사용 -```bash -# 사용자 요청 -"C:\project\script.md 기획서 생성해줘" - -# 실행되는 명령 -node ~/.claude/skills/storyboard-generator/scripts/generate-storyboard.js \ - --input "C:\project\script.md" \ - --output "C:\project\pptx\storyboard.pptx" -``` - -### 출력 폴더 지정 -```bash -# 사용자 요청 -"interview.txt를 분석해서 스토리보드 생성, output 폴더에 저장" - -# 실행되는 명령 -node ~/.claude/skills/storyboard-generator/scripts/generate-storyboard.js \ - --input "interview.txt" \ - --output "output/storyboard.pptx" -``` - -## 워크플로우 (HTML 기반 - 권장) - -``` -┌─────────────────┐ -│ 입력 파일 │ script.md, interview.txt -└────────┬────────┘ - │ - ▼ -┌─────────────────┐ -│ 텍스트 분석 │ 섹션 인식, 키워드 추출 -└────────┬────────┘ - │ - ▼ -┌─────────────────┐ -│ 데이터 구조화 │ storyboard-config.json 생성 -└────────┬────────┘ - │ - ▼ -┌─────────────────┐ -│ HTML 슬라이드 │ generate-html-storyboard.js -│ 생성 │ → 각 슬라이드를 HTML로 생성 -└────────┬────────┘ - │ - ▼ -┌─────────────────┐ -│ HTML → PPTX │ convert-html-to-pptx.js -│ 변환 │ → Playwright로 렌더링 후 변환 -└────────┬────────┘ - │ - ▼ -┌─────────────────┐ -│ PPTX 출력 │ 완성된 기획서 -└─────────────────┘ -``` - -### HTML 기반 방식의 장점 - -1. **정교한 디자인**: CSS로 정밀한 레이아웃 제어 가능 -2. **일관된 스타일**: 테두리, 간격, 색상이 CSS로 정확하게 적용 -3. **미리보기**: HTML 파일을 브라우저에서 바로 확인 가능 -4. **디버깅 용이**: 문제 발생 시 HTML 소스 확인으로 빠른 수정 - -## 필수 의존성 - -```bash -# Node.js 패키지 (scripts 폴더에 설치) -cd ~/.claude/skills/storyboard-generator/scripts -npm install pptxgenjs playwright sharp -``` - -## 관련 스킬 - -- **text-analyzer-skill**: 텍스트 구조 분석 -- **proposal-skill**: PDF 기획서 분석 -- **pptx-skill**: HTML → PPTX 변환 -- **design-skill**: 슬라이드 디자인 - -## 대량 견적서 생성 워크플로우 - -### 배치 생성 프로세스 -``` -┌─────────────────┐ -│ 마스터 설정 │ estimate-template.json (공통 설정) -└────────┬────────┘ - │ - ▼ -┌─────────────────┐ -│ 개별 견적 데이터 │ estimates/*.json (프로젝트별 변수) -└────────┬────────┘ - │ - ▼ -┌─────────────────┐ -│ 설정 파일 병합 │ 마스터 + 개별 = 완성된 config -└────────┬────────┘ - │ - ▼ -┌─────────────────┐ -│ HTML 슬라이드 │ 각 견적별 html_slides 폴더 -│ 생성 (배치) │ -└────────┬────────┘ - │ - ▼ -┌─────────────────┐ -│ PPTX 변환 │ 각 견적별 .pptx 파일 -│ (배치) │ -└─────────────────┘ -``` - -### 마스터 템플릿 구조 -```json -{ - "_template": true, - "company": "SAM", - "author": "DX 추진팀", - "mainMenus": [ - { "title": "규격 입력", "children": ["개구부 크기", "제작 규격 계산"] }, - { "title": "단가 계산", "children": ["재질 선택", "두께 선택"] }, - { "title": "부대비용", "children": ["모터 선정", "철거/폐기물"] }, - { "title": "견적서 생성", "children": ["마진율 적용", "PPT 출력"] } - ], - "pricingRules": { - "materials": { - "아연도강판_0.8t": 85000, - "아연도강판_1.0t": 90000, - "스크린_일반": "변동", - "스크린_차열": "일반 × 1.8" - }, - "motors": { - "400형": { "maxWeight": 300, "price": 450000 }, - "600형": { "maxWeight": 500, "price": 600000 }, - "1000형": { "maxWeight": 999, "price": 850000 } - }, - "additionalCosts": { - "철거비": 150000, - "폐기물처리": 50000, - "고소장비": 250000 - }, - "margins": { - "관공서": 0.15, - "일반건설사": 0.20, - "특수": 0.25 - } - } -} -``` - -### 개별 견적 데이터 예시 -```json -{ - "_extends": "estimate-template.json", - "projectName": "○○빌딩 방화셔터 교체공사", - "date": "2025.01.15", - "items": [ - { - "location": "B1F 주차장 입구", - "openingW": 3000, - "openingH": 4000, - "material": "아연도강판_0.8t", - "workflow": "교체" - }, - { - "location": "1F 로비", - "openingW": 2500, - "openingH": 3500, - "material": "스크린_일반", - "workflow": "신축" - } - ] -} -``` - -### 배치 실행 명령 -```bash -# 여러 견적서 일괄 생성 -for config in estimates/*.json; do - name=$(basename "$config" .json) - - # HTML 생성 - node ~/.claude/skills/storyboard-generator/scripts/generate-html-storyboard.js \ - --config "$config" \ - --output "output/${name}_html" - - # PPTX 변환 - node ~/.claude/skills/storyboard-generator/scripts/convert-html-to-pptx.js \ - --input "output/${name}_html" \ - --output "output/${name}.pptx" -done -``` - -## 방화셔터 견적 계산 로직 - -### 규격 변환 공식 -``` -제작 규격 = 개구부 + 여유치 - -┌─────────────────────────────────────────────────┐ -│ 제작 폭(W) = 개구부 폭 + 100mm (레일 설치용) │ -│ 제작 높이(H) = 개구부 높이 + 600mm (권상높이) │ -└─────────────────────────────────────────────────┘ - -예시: - 개구부: 3,000mm × 4,000mm - 제작: 3,100mm × 4,600mm -``` - -### 면적 계산 및 할증 -``` -실제면적 = 제작W × 제작H ÷ 1,000,000 (㎡) - -┌─────────────────────────────────────────────────┐ -│ 최소면적 규칙: 5㎡ 미만 → 5㎡ 강제 적용 │ -│ (제작 공정상 최소 규격 필요) │ -└─────────────────────────────────────────────────┘ - -예시: - 3,100 × 4,600 = 14,260,000 ㎟ = 14.26㎡ - 적용면적 = 14.26㎡ (5㎡ 이상이므로 그대로) -``` - -### 단가표 (기준가) -| 재질 | 두께 | 단가(원/㎡) | 비고 | -|------|------|------------|------| -| 아연도 강판 | 0.8t | 85,000 | 기본 | -| 아연도 강판 | 1.0t | 90,000 | 소방 기준 | -| 스크린 (일반) | - | 변동 | 시세 적용 | -| 스크린 (차열) | - | 일반×1.8 | 차열 등급 | - -### 하중 계산 및 모터 선정 -``` -총 하중 = 적용면적 × 12kg (0.8t 기준) - 적용면적 × 15kg (1.0t 기준) - -┌────────────────────────────────────────────────┐ -│ 모터 선정 기준 │ -│ ~300kg → 400형 (450,000원) │ -│ 300~500kg → 600형 (600,000원) │ -│ 500kg~ → 1000형 (850,000원) │ -└────────────────────────────────────────────────┘ -``` - -### 부대비용 항목 -| 항목 | 금액 | 적용 조건 | -|------|------|----------| -| 철거비 | 150,000원/개소 | 교체공사 시 | -| 폐기물처리 | 50,000원/개소 | 교체공사 시 | -| 고소장비 | 250,000원/일 | 지하/고층 현장 | -| 지방운반비 | 별도 협의 | 수도권 외 | - -### 최종 금액 계산 -``` -원가 합계 = 자재비 + 모터비 + 부대비용 - -┌────────────────────────────────────────────────┐ -│ 마진율 적용 │ -│ 관공서: 15% │ -│ 일반 건설사: 20~25% │ -│ 특수 (원거리/고층): 25~30% │ -└────────────────────────────────────────────────┘ - -최종금액 = 원가 × (1 + 마진율) -절삭금액 = 만원 단위 이하 절삭 (네고 대비) -``` - -## 워크플로우 변형 (신축 vs 교체) - -### 신축공사 워크플로우 -``` -┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ -│ 규격 입력 │ → │ 단가 계산 │ → │ 모터 선정 │ → │ 견적 생성 │ -└──────────┘ └──────────┘ └──────────┘ └──────────┘ - -특징: -- 철거비/폐기물 비용 없음 -- 기존 구조물 확인 불필요 -- 전기 배선 신규 설치 -``` - -### 교체공사 워크플로우 -``` -┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ -│ 규격 입력 │ → │ 단가 계산 │ → │ 모터 선정 │ → │ 부대비용 │ → │ 견적 생성 │ -└──────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────┘ - ↑ - 철거비, 폐기물, - 양중비 추가 - -특징: -- 철거비 150,000원/개소 추가 -- 폐기물처리 50,000원/개소 추가 -- 기존 전기 배선 활용 가능 -- 양중비(고소장비) 현장 상황에 따라 추가 -``` - -### 설정 파일에서 워크플로우 구분 -```json -{ - "screens": [ - { - "taskName": "부대비용", - "conditionalDisplay": { - "show": "workflow === '교체'", - "description": "교체공사 시에만 이 화면 표시" - } - } - ], - "calculations": { - "additionalCosts": { - "철거비": { "condition": "workflow === '교체'", "amount": 150000 }, - "폐기물": { "condition": "workflow === '교체'", "amount": 50000 } - } - } -} -``` - -## 주의사항 - -1. **입력 파일 인코딩**: UTF-8 권장 -2. **출력 폴더**: 존재하지 않으면 자동 생성 -3. **파일명 충돌**: 기존 파일 덮어쓰기 -4. **한글 지원**: 맑은 고딕, Pretendard 폰트 사용 -5. **단가 변동**: 스크린 방화셔터 단가는 시세에 따라 변동 - 외부 단가표 참조 필요 -6. **면책 조항**: 전기 배선 및 상시 전원 공사 별도 문구 필수 삽입 diff --git a/.claude/skills/storyboard-generator/examples/fire-shutter-config.json b/.claude/skills/storyboard-generator/examples/fire-shutter-config.json deleted file mode 100755 index ac46102..0000000 --- a/.claude/skills/storyboard-generator/examples/fire-shutter-config.json +++ /dev/null @@ -1,95 +0,0 @@ -{ - "projectName": "방화셔터 견적 시스템", - "company": "SAM", - "author": "IT 혁신팀", - "date": "2024.10.24", - "purpose": "현장 영업사원의 전화 견적 요청을\nAI 기반으로 자동화하여\n엑셀 정리 → PPT 견적서 생성\n과정을 시스템화", - "features": [ - "셔터 타입 선택 (철재/스크린/하향식)", - "규격 입력 → 면적 자동 계산", - "모터 사양 자동 선택", - "지역/층고별 설치/운반비 가산", - "할인율 적용 및 PPT 자동 생성" - ], - "effects": [ - { "icon": "⏱️", "title": "시간 단축", "desc": "견적서 작성 시간 80% 감소" }, - { "icon": "✅", "title": "정확성 향상", "desc": "계산 오류 제로화" }, - { "icon": "📊", "title": "표준화", "desc": "PPT 양식 통일" } - ], - "tocItems": [ - { "num": "01", "title": "프로젝트 개요", "desc": "시스템 목적 및 범위" }, - { "num": "02", "title": "메뉴 구조 (IA)", "desc": "Information Architecture" }, - { "num": "03", "title": "견적 입력 화면", "desc": "셔터 타입, 규격, 옵션 선택" }, - { "num": "04", "title": "견적 계산 화면", "desc": "단가 계산 및 부가비용" }, - { "num": "05", "title": "견적서 미리보기", "desc": "PPT 양식 프리뷰" }, - { "num": "06", "title": "견적 관리", "desc": "목록 조회 및 이력 관리" } - ], - "mainMenus": [ - { - "title": "견적 입력", - "children": ["셔터 타입 선택", "규격 입력", "옵션 선택"] - }, - { - "title": "견적 계산", - "children": ["단가 계산", "모터 선택", "부가비용"] - }, - { - "title": "견적서 생성", - "children": ["미리보기", "PPT 다운로드", "이메일 전송"] - }, - { - "title": "견적 관리", - "children": ["목록 조회", "이력 관리", "통계"] - } - ], - "screens": [ - { - "taskName": "견적 입력", - "route": "/estimate/input", - "screenName": "견적 입력 화면", - "screenId": "EST_INPUT_001", - "descriptions": [ - { "title": "셔터 타입 선택", "content": "철재(85,000/㎡), 스크린(단가 변동), 하향식 스크린 중 선택" }, - { "title": "규격 입력", "content": "가로(W) × 높이(H) 미터 단위로 입력, 면적 자동 계산" }, - { "title": "수량 입력", "content": "동일 규격 셔터 수량 입력 (1개소 이상)" }, - { "title": "입력 검증", "content": "필수 항목 미입력 시 경고 표시, 다음 단계 진행 불가" } - ] - }, - { - "taskName": "견적 계산", - "route": "/estimate/calculate", - "screenName": "견적 계산 화면", - "screenId": "EST_CALC_001", - "descriptions": [ - { "title": "모터 자동 선택", "content": "셔터 무게 기준 자동 추천, 수동 변경 가능" }, - { "title": "기본 설치비", "content": "1개소당 30만원, 수량에 따라 자동 계산" }, - { "title": "고소작업비", "content": "층고 5m 이상 시 렌탈비 25만원/일 추가" }, - { "title": "운반비", "content": "서울/경기 무료, 지방 거리별 15~30만원 청구" } - ] - }, - { - "taskName": "견적서 생성", - "route": "/estimate/preview", - "screenName": "견적서 미리보기", - "screenId": "EST_PREVIEW_001", - "descriptions": [ - { "title": "PPT 구성", "content": "표지 → 요약 → 세부내역 → 성적서 → 회사소개 (5장)" }, - { "title": "실시간 미리보기", "content": "슬라이드 썸네일 클릭 시 해당 페이지 확대 표시" }, - { "title": "다운로드/전송", "content": "PPT 파일 다운로드 또는 고객사 이메일 직접 전송" }, - { "title": "유효기간 자동 삽입", "content": "제출일 기준 15일 유효기간 빨간색 문구 자동 추가" } - ] - }, - { - "taskName": "견적 관리", - "route": "/estimate/list", - "screenName": "견적 관리 목록", - "screenId": "EST_LIST_001", - "descriptions": [ - { "title": "견적 목록", "content": "프로젝트명, 금액, 상태, 작성일 기준 조회" }, - { "title": "필터/정렬", "content": "상태별 필터, 날짜/금액 정렬 기능" }, - { "title": "통계 대시보드", "content": "전체 건수, 이번 달 건수, 총 금액 실시간 표시" }, - { "title": "할인 권한", "content": "1,000만원 이상 시 5% 할인 가능 (부장 결재 시 추가)" } - ] - } - ] -} diff --git a/.claude/skills/storyboard-generator/scripts/convert-html-to-pptx.js b/.claude/skills/storyboard-generator/scripts/convert-html-to-pptx.js deleted file mode 100755 index 69af4e8..0000000 --- a/.claude/skills/storyboard-generator/scripts/convert-html-to-pptx.js +++ /dev/null @@ -1,122 +0,0 @@ -/** - * HTML 슬라이드를 PPTX로 변환하는 스크립트 - * - * 사용법: - * node convert-html-to-pptx.js --input --output - */ - -const fs = require('fs'); -const path = require('path'); -const PptxGenJS = require('pptxgenjs'); - -// html2pptx.js 모듈 로드 -const pptxSkillPath = path.join(__dirname, '../../pptx-skill/scripts/html2pptx.js'); -let html2pptx; - -if (fs.existsSync(pptxSkillPath)) { - html2pptx = require(pptxSkillPath); -} else { - console.error('❌ html2pptx.js를 찾을 수 없습니다.'); - console.error(' 경로:', pptxSkillPath); - process.exit(1); -} - -async function main() { - const args = process.argv.slice(2); - let inputDir = null; - let outputFile = null; - - for (let i = 0; i < args.length; i++) { - if (args[i] === '--input' && args[i + 1]) { - inputDir = args[i + 1]; - i++; - } else if (args[i] === '--output' && args[i + 1]) { - outputFile = args[i + 1]; - i++; - } - } - - if (!inputDir) { - console.error('Usage: node convert-html-to-pptx.js --input --output '); - process.exit(1); - } - - if (!fs.existsSync(inputDir)) { - console.error(`❌ 입력 폴더를 찾을 수 없습니다: ${inputDir}`); - process.exit(1); - } - - // HTML 파일 목록 가져오기 (이름순 정렬) - const htmlFiles = fs.readdirSync(inputDir) - .filter(f => f.endsWith('.html')) - .sort() - .map(f => path.join(inputDir, f)); - - if (htmlFiles.length === 0) { - console.error('❌ HTML 파일을 찾을 수 없습니다.'); - process.exit(1); - } - - // 출력 파일명 설정 - if (!outputFile) { - outputFile = path.join(path.dirname(inputDir), 'storyboard.pptx'); - } - - console.log('🚀 HTML → PPTX 변환 시작\n'); - console.log(`📂 입력 폴더: ${inputDir}`); - console.log(`📄 HTML 파일: ${htmlFiles.length}개`); - console.log(`📁 출력 파일: ${outputFile}\n`); - - // PptxGenJS 초기화 - const pptx = new PptxGenJS(); - - // 16:9 레이아웃 설정 (720pt x 405pt = 10" x 5.625") - pptx.defineLayout({ name: 'CUSTOM_16x9', width: 10, height: 5.625 }); - pptx.layout = 'CUSTOM_16x9'; - - pptx.author = 'SAM IT Innovation Team'; - pptx.company = 'SAM'; - pptx.subject = 'Web Storyboard'; - - // 각 HTML 파일을 슬라이드로 변환 - for (let i = 0; i < htmlFiles.length; i++) { - const htmlFile = htmlFiles[i]; - const fileName = path.basename(htmlFile); - - try { - console.log(`⏳ 변환 중 (${i + 1}/${htmlFiles.length}): ${fileName}`); - - await html2pptx(htmlFile, pptx); - - console.log(`✅ 완료: ${fileName}`); - } catch (error) { - console.error(`❌ 오류: ${fileName}`); - console.error(` ${error.message}`); - // 오류가 발생해도 계속 진행 - } - } - - // PPTX 파일 저장 - try { - // 출력 폴더 생성 - const outputDir = path.dirname(outputFile); - if (!fs.existsSync(outputDir)) { - fs.mkdirSync(outputDir, { recursive: true }); - } - - await pptx.writeFile(outputFile); - - const stats = fs.statSync(outputFile); - const sizeKB = (stats.size / 1024).toFixed(1); - - console.log('\n🎉 PPTX 생성 완료!'); - console.log(`📁 파일: ${outputFile}`); - console.log(`📊 크기: ${sizeKB} KB`); - console.log(`📄 슬라이드: ${htmlFiles.length}장`); - } catch (error) { - console.error('\n❌ PPTX 저장 실패:', error.message); - process.exit(1); - } -} - -main().catch(console.error); diff --git a/.claude/skills/storyboard-generator/scripts/generate-html-storyboard.js b/.claude/skills/storyboard-generator/scripts/generate-html-storyboard.js deleted file mode 100755 index f5618ce..0000000 --- a/.claude/skills/storyboard-generator/scripts/generate-html-storyboard.js +++ /dev/null @@ -1,1206 +0,0 @@ -/** - * HTML 기반 스토리보드 PPTX 생성기 - * HTML 템플릿을 먼저 생성한 후 PPTX로 변환하는 방식 - * - * 사용법: - * node generate-html-storyboard.js --config <설정파일.json> --output <출력폴더> - */ - -const fs = require('fs'); -const path = require('path'); - -// 색상 정의 -const colors = { - primary: '#0d9488', - secondary: '#1e293b', - accent: '#95C11F', - warning: '#f59e0b', - danger: '#dc2626', - white: '#ffffff', - lightGray: '#f1f5f9', - darkGray: '#64748b', - black: '#1a1a1a', - wireframeBg: '#f8fafc', - wireframeBorder: '#e2e8f0', - tableBorder: '#334155' -}; - -// HTML 이스케이프 -function escapeHtml(text) { - if (!text) return ''; - return String(text) - .replace(/&/g, '&') - .replace(//g, '>') - .replace(/"/g, '"') - .replace(/'/g, '''); -} - -// 공통 스타일 -const commonStyles = ` - * { margin: 0; padding: 0; box-sizing: border-box; } - body { - width: 720pt; - height: 405pt; - font-family: 'Pretendard', 'Malgun Gothic', sans-serif; - background: #ffffff; - } - p, h1, h2, h3 { margin: 0; } -`; - -// ======================================== -// 슬라이드 생성 함수들 -// ======================================== - -function generateCoverSlide(config) { - return ` - - - - - - - -
                                      - -
                                      -

                                      ${escapeHtml(config.projectName || '프로젝트')}

                                      -

                                      웹 기획서 및 스토리보드

                                      -

                                      Version D1.0

                                      -
                                      - - -`; -} - -function generateHistorySlide(config) { - return ` - - - - - - - -
                                      -

                                      Document History

                                      -
                                      -
                                      -
                                      -
                                      -

                                      날짜

                                      -

                                      버전

                                      -

                                      주요 내용

                                      -

                                      상세 내용

                                      -

                                      비고

                                      -
                                      -
                                      -

                                      ${escapeHtml(config.date || '')}

                                      -

                                      D1.0

                                      -

                                      초안 작성

                                      -

                                      ${escapeHtml(config.projectName || '')} 기획서 초안 작성

                                      -

                                      -
                                      -
                                      -

                                      -

                                      -

                                      -

                                      -

                                      -
                                      -
                                      -
                                      - -`; -} - -function generateTOCSlide(config) { - const tocItems = config.tocItems || []; - const itemsHtml = tocItems.map((item, idx) => ` -
                                      -
                                      -

                                      ${escapeHtml(item.num || String(idx + 1).padStart(2, '0'))}

                                      -
                                      -
                                      -

                                      ${escapeHtml(item.title)}

                                      -
                                      -
                                      -

                                      ${escapeHtml(item.desc || '')}

                                      -
                                      -
                                      - `).join(''); - - return ` - - - - - - - -
                                      -

                                      목차 (Table of Contents)

                                      -
                                      -
                                      - ${itemsHtml} -
                                      - -`; -} - -function generateOverviewSlide(config) { - const features = config.features || []; - const effects = config.effects || []; - - const featuresHtml = features.slice(0, 6).map(f => ` -
                                      -

                                      ✓ ${escapeHtml(f)}

                                      -
                                      - `).join(''); - - const effectsHtml = effects.slice(0, 3).map(e => ` -
                                      -

                                      ${escapeHtml(e.icon || '📌')}

                                      -

                                      ${escapeHtml(e.title)}

                                      -

                                      ${escapeHtml(e.desc)}

                                      -
                                      - `).join(''); - - return ` - - - - - - - -
                                      -

                                      01. 프로젝트 개요

                                      -
                                      -
                                      -
                                      -
                                      -

                                      프로젝트 목적

                                      -
                                      -

                                      ${escapeHtml(config.purpose || '업무 프로세스를 시스템화하여 효율성 향상')}

                                      -
                                      -
                                      -
                                      -

                                      주요 기능

                                      - ${featuresHtml} -
                                      -
                                      -
                                      -

                                      기대 효과

                                      -
                                      - ${effectsHtml} -
                                      -
                                      -
                                      - -`; -} - -function generateIASlide(config) { - const menus = config.mainMenus || []; - - const menusHtml = menus.slice(0, 4).map((menu, idx) => ` - - `).join(''); - - const rootName = config.projectName ? - config.projectName.replace(/시스템|프로젝트|기획/g, '').trim() : - '시스템'; - - return ` - - - - - - - -
                                      -

                                      02. 메뉴 구조 (Information Architecture)

                                      -
                                      -
                                      -
                                      -

                                      ${escapeHtml(rootName)}

                                      -
                                      -
                                      -
                                      - -
                                      - -`; -} - -function generateStoryboardSlide(screen, pageNum, config) { - const menuItems = config.mainMenus || []; - const activeMenu = screen.taskName || ''; - - // 사이드바는 항상 mainMenus 기반으로 별도 렌더링 (wireframeElements 유무와 관계없이) - const sidebarHtml = menuItems.slice(0, 4).map(menu => { - const isActive = menu.title === activeMenu; - return ` - - `; - }).join(''); - - const descriptions = screen.descriptions || []; - const descriptionsHtml = descriptions.slice(0, 4).map((desc, idx) => ` -
                                      -
                                      -
                                      -

                                      ${idx + 1}

                                      -
                                      -
                                      -

                                      ${escapeHtml(desc.title)}

                                      -
                                      -
                                      -
                                      -

                                      ${escapeHtml(desc.content)}

                                      -
                                      -
                                      - `).join(''); - - // wireframeElements가 있으면 사이드바 영역 요소 제외하고 콘텐츠만 렌더링 - const hasWireframeElements = screen.wireframeElements && screen.wireframeElements.length > 0; - - // 화면별 콘텐츠 생성 - let contentHtml = ''; - - if (hasWireframeElements) { - // 사이드바 영역(x < 1.5)을 제외한 콘텐츠 요소만 필터링 - contentHtml = generateWireframeContent(screen, config, true); - } else if (screen.contentType === 'form') { - contentHtml = generateFormContent(screen); - } else if (screen.contentType === 'table') { - contentHtml = generateTableContent(screen); - } else if (screen.contentType === 'cards') { - contentHtml = generateCardsContent(screen); - } else { - contentHtml = generateDefaultContent(screen); - } - - return ` - - - - - - - - -
                                      -
                                      -

                                      Task Name

                                      -

                                      ${escapeHtml(screen.taskName || '')}

                                      -

                                      Ver.

                                      -

                                      D1.0

                                      -

                                      Page

                                      -

                                      ${pageNum}

                                      -
                                      -
                                      -

                                      Route

                                      -

                                      ${escapeHtml(screen.route || '')}

                                      -

                                      Screen Name

                                      -

                                      ${escapeHtml(screen.screenName || '')}

                                      -

                                      Screen ID

                                      -

                                      ${escapeHtml(screen.screenId || '')}

                                      -
                                      -
                                      - - -
                                      - -
                                      -
                                      -

                                      ${escapeHtml(config.projectName || '시스템')}

                                      -
                                      -
                                      - -
                                      - ${contentHtml} -
                                      -
                                      -
                                      - - -
                                      -
                                      -

                                      Description

                                      -
                                      - ${descriptionsHtml} -
                                      -
                                      - -`; -} - -// 콘텐츠 타입별 생성 함수 -function generateDefaultContent(screen) { - return ` -

                                      ${escapeHtml(screen.screenName || '화면')}

                                      -
                                      -

                                      콘텐츠 영역

                                      -
                                      - `; -} - -function generateFormContent(screen) { - const fields = screen.fields || []; - const fieldsHtml = fields.map(f => ` -
                                      -
                                      -

                                      ${escapeHtml(f.label)}

                                      -
                                      -
                                      -

                                      ${escapeHtml(f.value || '')}

                                      -
                                      -
                                      - `).join(''); - - return ` -

                                      ${escapeHtml(screen.screenName || '입력 폼')}

                                      - ${fieldsHtml} - `; -} - -function generateTableContent(screen) { - const rows = screen.tableRows || []; - const rowsHtml = rows.map(r => ` -
                                      -

                                      ${escapeHtml(r.label)}

                                      -
                                      -

                                      ${escapeHtml(r.value)}

                                      -
                                      -
                                      - `).join(''); - - return ` -

                                      ${escapeHtml(screen.screenName || '테이블')}

                                      -
                                      - ${rowsHtml} -
                                      - `; -} - -function generateCardsContent(screen) { - const cards = screen.cards || []; - const cardsHtml = cards.map(c => ` -
                                      -
                                      -

                                      ${escapeHtml(c.title)}

                                      -
                                      -
                                      -

                                      ${escapeHtml(c.value)}

                                      -
                                      -
                                      - `).join(''); - - return ` -

                                      ${escapeHtml(screen.screenName || '선택')}

                                      -
                                      - ${cardsHtml} -
                                      - `; -} - -// wireframeElements 기반 콘텐츠 생성 -// excludeSidebar: true면 사이드바 영역(x < 1.5) 요소 제외 -function generateWireframeContent(screen, config, excludeSidebar = false) { - let elements = screen.wireframeElements || []; - const descriptions = screen.descriptions || []; - - // 사이드바 영역 제외 옵션 - if (excludeSidebar) { - // x < 1.5인 요소들은 사이드바 영역이므로 제외 - elements = elements.filter(el => el.x >= 1.5); - } - - // content-area 내에서의 상대 좌표 계산 - // 사이드바 제외 시: x가 1.5부터 시작하므로 baseX를 1.5로 조정 - const baseX = excludeSidebar ? 1.5 : 0.2; - const baseY = 1.25; // 헤더 테이블 아래 - const scaleX = 52; // pt per inch (영역에 맞게 조정) - const scaleY = 52; - - const elementsHtml = elements.map(el => { - const left = (el.x - baseX) * scaleX; - const top = (el.y - baseY) * scaleY; - const width = el.w * scaleX; - const height = el.h * scaleY; - - const bgColor = el.fill ? `#${el.fill}` : 'transparent'; - const textColor = el.color ? `#${el.color}` : colors.secondary; - const fontSize = el.fontSize || 8; - const fontWeight = el.bold ? '700' : '400'; - const textAlign = el.align || 'center'; - - // 텍스트에 줄바꿈 처리 - const textContent = el.text ? el.text.replace(/\n/g, '
                                      ') : ''; - - return ` -
                                      -

                                      ${textContent}

                                      -
                                      - `; - }).join(''); - - // Description 마커 생성 (빨간 번호) - // descriptions에 markerX, markerY가 있으면 해당 위치에 마커 표시 - const markersHtml = descriptions.map((desc, idx) => { - // 마커 위치가 지정되어 있으면 사용, 없으면 기본 위치 계산 - let markerX, markerY; - - if (desc.markerX !== undefined && desc.markerY !== undefined) { - // 명시적으로 지정된 마커 위치 사용 - markerX = (desc.markerX - baseX) * scaleX; - markerY = (desc.markerY - baseY) * scaleY; - } else { - // 기본 위치: 콘텐츠 영역 좌측에 세로로 배치 - markerX = 5 + idx * 0; // 고정 x 위치 - markerY = 10 + idx * 55; // 세로로 간격 - } - - return ` -
                                      -

                                      ${idx + 1}

                                      -
                                      - `; - }).join(''); - - return ` -
                                      - ${elementsHtml} - ${markersHtml} -
                                      - `; -} - -// ======================================== -// 메인 실행 -// ======================================== - -async function main() { - const args = process.argv.slice(2); - let configFile = null; - let outputDir = null; - - for (let i = 0; i < args.length; i++) { - if (args[i] === '--config' && args[i + 1]) { - configFile = args[i + 1]; - i++; - } else if (args[i] === '--output' && args[i + 1]) { - outputDir = args[i + 1]; - i++; - } - } - - if (!configFile) { - console.error('Usage: node generate-html-storyboard.js --config --output '); - process.exit(1); - } - - const config = JSON.parse(fs.readFileSync(configFile, 'utf-8')); - outputDir = outputDir || path.join(path.dirname(configFile), 'html_slides'); - - if (!fs.existsSync(outputDir)) { - fs.mkdirSync(outputDir, { recursive: true }); - } - - console.log('🚀 HTML 스토리보드 생성 시작\n'); - - // 슬라이드 생성 - const slides = []; - - // 1. 표지 - slides.push({ name: 'slide_01_cover.html', content: generateCoverSlide(config) }); - console.log('✅ 슬라이드 1: 표지 생성 완료'); - - // 2. Document History - slides.push({ name: 'slide_02_history.html', content: generateHistorySlide(config) }); - console.log('✅ 슬라이드 2: Document History 생성 완료'); - - // 3. 목차 - slides.push({ name: 'slide_03_toc.html', content: generateTOCSlide(config) }); - console.log('✅ 슬라이드 3: 목차 생성 완료'); - - // 4. 프로젝트 개요 - slides.push({ name: 'slide_04_overview.html', content: generateOverviewSlide(config) }); - console.log('✅ 슬라이드 4: 프로젝트 개요 생성 완료'); - - // 5. IA - slides.push({ name: 'slide_05_ia.html', content: generateIASlide(config) }); - console.log('✅ 슬라이드 5: 메뉴 구조 (IA) 생성 완료'); - - // 6+. 스토리보드 - const screens = config.screens || []; - screens.forEach((screen, idx) => { - const pageNum = idx + 6; - slides.push({ - name: `slide_${String(pageNum).padStart(2, '0')}_${screen.screenId || 'screen'}.html`, - content: generateStoryboardSlide(screen, pageNum, config) - }); - console.log(`✅ 슬라이드 ${pageNum}: ${screen.screenName} 생성 완료`); - }); - - // HTML 파일 저장 - for (const slide of slides) { - const filePath = path.join(outputDir, slide.name); - fs.writeFileSync(filePath, slide.content, 'utf-8'); - } - - console.log(`\n🎉 HTML 슬라이드 생성 완료!`); - console.log(`📁 저장 위치: ${outputDir}`); - console.log(`📊 총 슬라이드: ${slides.length}장`); - console.log(`\n다음 단계: html2pptx.js로 PPTX 변환`); -} - -main().catch(console.error); diff --git a/.claude/skills/storyboard-generator/scripts/generate-storyboard.js b/.claude/skills/storyboard-generator/scripts/generate-storyboard.js deleted file mode 100755 index d805531..0000000 --- a/.claude/skills/storyboard-generator/scripts/generate-storyboard.js +++ /dev/null @@ -1,761 +0,0 @@ -/** - * 스토리보드 PPTX 자동 생성 스크립트 - * 텍스트 기반 인터뷰/기획 문서를 분석하여 IA + 화면 설계 스토리보드 생성 - * - * 사용법: - * node generate-storyboard.js --input <입력파일> --output <출력파일.pptx> - * node generate-storyboard.js --config <설정파일.json> - */ - -const PptxGenJS = require('pptxgenjs'); -const fs = require('fs'); -const path = require('path'); - -// ======================================== -// 색상 팔레트 정의 (# 없이) -// ======================================== -const colors = { - primary: '0d9488', // Teal - secondary: '1e293b', // Slate 800 - accent: '95C11F', // 라임 그린 (Description용) - warning: 'f59e0b', // 경고 - danger: 'dc2626', // 위험 - white: 'FFFFFF', - lightGray: 'f1f5f9', - darkGray: '64748b', - black: '1a1a1a', - wireframeBg: 'f8fafc', - wireframeBorder: 'e2e8f0' -}; - -// ======================================== -// 텍스트 파서 클래스 -// ======================================== -class TextParser { - constructor(content) { - this.content = content; - this.sections = []; - this.metadata = {}; - } - - parse() { - this.extractMetadata(); - this.extractSections(); - this.extractMenuStructure(); - this.extractScreens(); - return this; - } - - extractMetadata() { - // 프로젝트명 추출 - const projectMatch = this.content.match(/프로젝트[명\s]*[::]\s*(.+)/i) || - this.content.match(/##\s*(.+?)\s*(프로젝트|시스템|기획)/i) || - this.content.match(/주제[::]\s*(.+)/i); - - if (projectMatch) { - this.metadata.projectName = projectMatch[1].trim(); - } - - // 날짜 추출 - const dateMatch = this.content.match(/(\d{4})[.\-/년](\d{1,2})[.\-/월](\d{1,2})/); - if (dateMatch) { - this.metadata.date = `${dateMatch[1]}.${dateMatch[2].padStart(2, '0')}.${dateMatch[3].padStart(2, '0')}`; - } else { - const today = new Date(); - this.metadata.date = `${today.getFullYear()}.${String(today.getMonth() + 1).padStart(2, '0')}.${String(today.getDate()).padStart(2, '0')}`; - } - - // 참석자/작성자 추출 - const authorMatch = this.content.match(/참석자[::]\s*(.+)/i) || - this.content.match(/작성자[::]\s*(.+)/i); - if (authorMatch) { - this.metadata.author = authorMatch[1].trim(); - } - } - - extractSections() { - // === 섹션 === 패턴 - const sectionPattern = /===\s*(.+?)\s*===/g; - let match; - while ((match = sectionPattern.exec(this.content)) !== null) { - this.sections.push({ - title: match[1].trim(), - startIndex: match.index - }); - } - - // ## 섹션 패턴 (Markdown) - const mdSectionPattern = /^##\s+(.+)$/gm; - while ((match = mdSectionPattern.exec(this.content)) !== null) { - if (!this.sections.some(s => s.title === match[1].trim())) { - this.sections.push({ - title: match[1].trim(), - startIndex: match.index - }); - } - } - } - - extractMenuStructure() { - this.metadata.menus = []; - - // PPT 구성, 메뉴 구조 등에서 추출 - const menuPatterns = [ - /(\d+)\.\s*\*\*(.+?)\*\*/g, // 1. **메뉴명** - /(\d+)\.\s*(.+?)(?:\n|$)/g, // 1. 메뉴명 - /-\s+(.+?)(?:\n|$)/g // - 메뉴명 - ]; - - // 주요 기능 섹션에서 메뉴 추출 - const funcMatch = this.content.match(/주요\s*기능[^]*?(?=##|===|$)/i); - if (funcMatch) { - const lines = funcMatch[0].split('\n'); - for (const line of lines) { - const itemMatch = line.match(/^\s*[-\d.]+\s*(.+)/); - if (itemMatch && itemMatch[1].trim().length > 0) { - const menuName = itemMatch[1].replace(/\*\*/g, '').trim(); - if (menuName.length < 30) { - this.metadata.menus.push(menuName); - } - } - } - } - } - - extractScreens() { - this.metadata.screens = []; - - // 화면 관련 키워드 추출 - const screenKeywords = ['화면', '페이지', '입력', '조회', '관리', '목록', '상세', '등록', '수정']; - - for (const keyword of screenKeywords) { - const pattern = new RegExp(`(${keyword}[^\\n]{0,20})`, 'gi'); - let match; - while ((match = pattern.exec(this.content)) !== null) { - const screenName = match[1].trim(); - if (!this.metadata.screens.some(s => s.name === screenName) && screenName.length < 30) { - this.metadata.screens.push({ name: screenName }); - } - } - } - } -} - -// ======================================== -// 스토리보드 생성기 클래스 -// ======================================== -class StoryboardGenerator { - constructor(config) { - this.config = config; - this.pptx = new PptxGenJS(); - this.setupPresentation(); - } - - setupPresentation() { - // 레이아웃 설정 (16:9) - this.pptx.defineLayout({ name: 'CUSTOM_16x9', width: 10, height: 5.625 }); - this.pptx.layout = 'CUSTOM_16x9'; - - // 메타데이터 - this.pptx.title = this.config.projectName || '웹 기획서'; - this.pptx.subject = '시스템 기획 및 UI/UX 설계'; - this.pptx.author = this.config.author || 'SAM'; - this.pptx.company = this.config.company || 'SAM'; - } - - // ---------------------------------------- - // 슬라이드 1: 표지 - // ---------------------------------------- - createCoverSlide() { - const slide = this.pptx.addSlide(); - slide.background = { color: colors.secondary }; - - // 상단 악센트 라인 - slide.addShape('rect', { - x: 0, y: 0, w: 10, h: 0.1, - fill: { color: colors.primary } - }); - - // 로고 - slide.addShape('rect', { - x: 0.5, y: 0.4, w: 1.2, h: 0.5, - fill: { color: colors.primary } - }); - slide.addText(this.config.company || 'SAM', { - x: 0.5, y: 0.4, w: 1.2, h: 0.5, - fontSize: 16, bold: true, color: colors.white, - align: 'center', valign: 'middle' - }); - - // 메인 제목 - slide.addText(this.config.projectName || '프로젝트', { - x: 0.5, y: 1.8, w: 9, h: 0.8, - fontSize: 40, bold: true, color: colors.white, - align: 'center' - }); - - // 부제목 - slide.addText('웹 기획서 및 스토리보드', { - x: 0.5, y: 2.6, w: 9, h: 0.5, - fontSize: 22, color: colors.primary, - align: 'center' - }); - - // 버전 정보 - slide.addText('Version D1.0', { - x: 0.5, y: 3.3, w: 9, h: 0.3, - fontSize: 14, color: colors.darkGray, - align: 'center' - }); - - // 하단 정보 - slide.addText(`${this.config.date || new Date().toISOString().split('T')[0].replace(/-/g, '.')} | ${this.config.author || '기획팀'}`, { - x: 0.5, y: 5, w: 9, h: 0.3, - fontSize: 12, color: colors.darkGray, - align: 'center' - }); - - console.log('✅ 슬라이드 1: 표지 생성 완료'); - } - - // ---------------------------------------- - // 슬라이드 2: Document History - // ---------------------------------------- - createHistorySlide() { - const slide = this.pptx.addSlide(); - slide.background = { color: colors.white }; - - // 헤더 - slide.addShape('rect', { - x: 0, y: 0, w: 10, h: 0.7, - fill: { color: colors.secondary } - }); - slide.addText('Document History', { - x: 0.5, y: 0.15, w: 5, h: 0.4, - fontSize: 20, bold: true, color: colors.white - }); - - // 히스토리 테이블 - const historyData = [ - ['날짜', '버전', '주요 내용', '상세 내용', '비고'], - [this.config.date || '', 'D1.0', '초안 작성', `${this.config.projectName || '프로젝트'} 기획서 초안 작성`, ''], - ['', '', '', '', ''], - ['', '', '', '', ''] - ]; - - slide.addTable(historyData, { - x: 0.5, y: 1.2, w: 9, h: 2.5, - fontSize: 10, - color: colors.secondary, - border: { pt: 0.5, color: colors.darkGray }, - colW: [1.2, 0.8, 1.5, 4, 1.5], - align: 'center', - valign: 'middle' - }); - - console.log('✅ 슬라이드 2: Document History 생성 완료'); - } - - // ---------------------------------------- - // 슬라이드 3: 목차 - // ---------------------------------------- - createTOCSlide() { - const slide = this.pptx.addSlide(); - slide.background = { color: colors.white }; - - // 헤더 - slide.addShape('rect', { - x: 0, y: 0, w: 10, h: 0.7, - fill: { color: colors.secondary } - }); - slide.addText('목차 (Table of Contents)', { - x: 0.5, y: 0.15, w: 5, h: 0.4, - fontSize: 20, bold: true, color: colors.white - }); - - const tocItems = this.config.tocItems || [ - { num: '01', title: '프로젝트 개요', desc: '시스템 목적 및 범위' }, - { num: '02', title: '메뉴 구조 (IA)', desc: 'Information Architecture' }, - { num: '03', title: '화면 설계', desc: '상세 스토리보드' } - ]; - - tocItems.forEach((item, idx) => { - const y = 1.1 + idx * 0.7; - - // 번호 박스 - slide.addShape('rect', { - x: 0.8, y: y, w: 0.6, h: 0.5, - fill: { color: colors.primary } - }); - slide.addText(item.num, { - x: 0.8, y: y, w: 0.6, h: 0.5, - fontSize: 14, bold: true, color: colors.white, - align: 'center', valign: 'middle' - }); - - // 제목 - slide.addText(item.title, { - x: 1.6, y: y, w: 3, h: 0.5, - fontSize: 14, bold: true, color: colors.secondary, - valign: 'middle' - }); - - // 설명 - slide.addText(item.desc, { - x: 4.8, y: y, w: 4, h: 0.5, - fontSize: 11, color: colors.darkGray, - valign: 'middle' - }); - }); - - console.log('✅ 슬라이드 3: 목차 생성 완료'); - } - - // ---------------------------------------- - // 슬라이드 4: 프로젝트 개요 - // ---------------------------------------- - createOverviewSlide() { - const slide = this.pptx.addSlide(); - slide.background = { color: colors.white }; - - // 헤더 - slide.addShape('rect', { - x: 0, y: 0, w: 10, h: 0.7, - fill: { color: colors.secondary } - }); - slide.addText('01. 프로젝트 개요', { - x: 0.5, y: 0.15, w: 5, h: 0.4, - fontSize: 20, bold: true, color: colors.white - }); - - // 목적 - slide.addText('프로젝트 목적', { - x: 0.5, y: 1, w: 4, h: 0.4, - fontSize: 14, bold: true, color: colors.secondary - }); - - slide.addShape('rect', { - x: 0.5, y: 1.4, w: 4.3, h: 1.8, - fill: { color: colors.lightGray } - }); - - slide.addText(this.config.purpose || '업무 프로세스를 시스템화하여\n효율성 향상 및 자동화 구현', { - x: 0.7, y: 1.5, w: 4, h: 1.6, - fontSize: 11, valign: 'top' - }); - - // 주요 기능 - slide.addText('주요 기능', { - x: 5.2, y: 1, w: 4, h: 0.4, - fontSize: 14, bold: true, color: colors.secondary - }); - - const features = this.config.features || [ - '데이터 입력 및 관리', - '자동 계산 및 처리', - '보고서 생성', - '이력 관리' - ]; - - features.slice(0, 5).forEach((feat, idx) => { - slide.addText('✓ ' + feat, { - x: 5.2, y: 1.4 + idx * 0.35, w: 4.3, h: 0.35, - fontSize: 10, color: colors.secondary - }); - }); - - // 기대 효과 - slide.addText('기대 효과', { - x: 0.5, y: 3.5, w: 9, h: 0.4, - fontSize: 14, bold: true, color: colors.secondary - }); - - const effects = this.config.effects || [ - { icon: '⏱️', title: '시간 단축', desc: '업무 처리 시간 감소' }, - { icon: '✅', title: '정확성 향상', desc: '오류 최소화' }, - { icon: '📊', title: '표준화', desc: '프로세스 통일' } - ]; - - effects.forEach((eff, idx) => { - const x = 0.5 + idx * 3.1; - slide.addShape('rect', { - x: x, y: 3.9, w: 2.9, h: 1.4, - fill: { color: colors.lightGray }, - line: { color: colors.primary, width: 1 } - }); - slide.addText(eff.icon, { - x: x, y: 4, w: 2.9, h: 0.5, - fontSize: 24, align: 'center' - }); - slide.addText(eff.title, { - x: x, y: 4.5, w: 2.9, h: 0.35, - fontSize: 11, bold: true, color: colors.secondary, - align: 'center' - }); - slide.addText(eff.desc, { - x: x, y: 4.85, w: 2.9, h: 0.35, - fontSize: 9, color: colors.darkGray, - align: 'center' - }); - }); - - console.log('✅ 슬라이드 4: 프로젝트 개요 생성 완료'); - } - - // ---------------------------------------- - // 슬라이드 5: 메뉴 구조 (IA) - // ---------------------------------------- - createIASlide() { - const slide = this.pptx.addSlide(); - slide.background = { color: colors.white }; - - // 헤더 - slide.addShape('rect', { - x: 0, y: 0, w: 10, h: 0.7, - fill: { color: colors.secondary } - }); - slide.addText('02. 메뉴 구조 (Information Architecture)', { - x: 0.5, y: 0.15, w: 6, h: 0.4, - fontSize: 20, bold: true, color: colors.white - }); - - // 루트 노드 - const rootName = this.config.projectName ? - this.config.projectName.replace(/시스템|프로젝트|기획/g, '').trim() : - '시스템'; - - slide.addShape('rect', { - x: 4, y: 1, w: 2, h: 0.6, - fill: { color: colors.secondary } - }); - slide.addText(rootName, { - x: 4, y: 1, w: 2, h: 0.6, - fontSize: 12, bold: true, color: colors.white, - align: 'center', valign: 'middle' - }); - - // 연결선 - slide.addShape('line', { - x: 5, y: 1.6, w: 0, h: 0.4, - line: { color: colors.darkGray, width: 1 } - }); - slide.addShape('line', { - x: 1.5, y: 2, w: 7, h: 0, - line: { color: colors.darkGray, width: 1 } - }); - - // 1차 메뉴 - const level1 = this.config.mainMenus || [ - { title: '메뉴 1', children: ['하위 1-1', '하위 1-2'] }, - { title: '메뉴 2', children: ['하위 2-1', '하위 2-2'] }, - { title: '메뉴 3', children: ['하위 3-1', '하위 3-2'] }, - { title: '메뉴 4', children: ['하위 4-1', '하위 4-2'] } - ]; - - const menuCount = Math.min(level1.length, 4); - const menuWidth = 2.2; - const totalWidth = menuCount * menuWidth + (menuCount - 1) * 0.1; - const startX = (10 - totalWidth) / 2; - - level1.slice(0, 4).forEach((menu, idx) => { - const x = startX + idx * (menuWidth + 0.1); - - // 세로 연결선 - slide.addShape('line', { - x: x + menuWidth / 2, y: 2, w: 0, h: 0.3, - line: { color: colors.darkGray, width: 1 } - }); - - // 1차 메뉴 박스 - slide.addShape('rect', { - x: x, y: 2.3, w: menuWidth, h: 0.5, - fill: { color: colors.primary } - }); - slide.addText(menu.title, { - x: x, y: 2.3, w: menuWidth, h: 0.5, - fontSize: 11, bold: true, color: colors.white, - align: 'center', valign: 'middle' - }); - - // 2차 메뉴 - (menu.children || []).slice(0, 3).forEach((child, cIdx) => { - const y = 3 + cIdx * 0.45; - slide.addShape('rect', { - x: x, y: y, w: menuWidth, h: 0.4, - fill: { color: colors.lightGray }, - line: { color: colors.wireframeBorder, width: 0.5 } - }); - slide.addText(child, { - x: x, y: y, w: menuWidth, h: 0.4, - fontSize: 9, color: colors.secondary, - align: 'center', valign: 'middle' - }); - }); - }); - - console.log('✅ 슬라이드 5: 메뉴 구조 (IA) 생성 완료'); - } - - // ---------------------------------------- - // 스토리보드 슬라이드 (화면 설계) - // ---------------------------------------- - createStoryboardSlide(screenInfo, pageNum) { - const slide = this.pptx.addSlide(); - slide.background = { color: colors.white }; - - // 헤더 정보 테이블 - const headerData = [ - ['Task Name', screenInfo.taskName || '', 'Ver.', 'D1.0', 'Page', String(pageNum)], - ['Route', screenInfo.route || '', 'Screen Name', screenInfo.screenName || '', 'Screen ID', screenInfo.screenId || ''] - ]; - - slide.addTable(headerData, { - x: 0.2, y: 0.1, w: 9.6, h: 0.5, - fontSize: 8, - color: colors.secondary, - border: { pt: 0.5, color: colors.darkGray }, - fill: { color: colors.lightGray }, - colW: [0.8, 2.5, 0.8, 2.5, 0.8, 2.2] - }); - - // 와이어프레임 영역 (좌측) - slide.addShape('rect', { - x: 0.2, y: 0.7, w: 6.8, h: 4.75, - fill: { color: colors.wireframeBg }, - line: { color: colors.wireframeBorder, width: 1 } - }); - - // 와이어프레임 헤더 - slide.addShape('rect', { - x: 0.3, y: 0.8, w: 6.6, h: 0.4, - fill: { color: colors.secondary } - }); - slide.addText(this.config.projectName || '시스템', { - x: 0.4, y: 0.8, w: 2, h: 0.4, - fontSize: 10, bold: true, color: colors.white, - valign: 'middle' - }); - - // 와이어프레임 내용 - if (screenInfo.wireframeElements) { - screenInfo.wireframeElements.forEach(el => { - if (el.type === 'rect') { - slide.addShape('rect', { - x: el.x, y: el.y, w: el.w, h: el.h, - fill: { color: el.fill || colors.white }, - line: { color: colors.wireframeBorder, width: 0.5 } - }); - } - if (el.text) { - slide.addText(el.text, { - x: el.x, y: el.y, w: el.w, h: el.h, - fontSize: el.fontSize || 8, - color: el.color || colors.secondary, - align: el.align || 'center', - valign: 'middle', - bold: el.bold || false - }); - } - }); - } else { - // 기본 와이어프레임 구조 - // 사이드바 - slide.addShape('rect', { - x: 0.3, y: 1.3, w: 1.2, h: 4, - fill: { color: colors.lightGray } - }); - slide.addText('메뉴', { - x: 0.3, y: 1.4, w: 1.2, h: 0.3, - fontSize: 8, color: colors.secondary, align: 'center' - }); - - // 메인 콘텐츠 영역 - slide.addShape('rect', { - x: 1.6, y: 1.3, w: 5.3, h: 0.4, - fill: { color: colors.white } - }); - slide.addText(screenInfo.screenName || '화면 제목', { - x: 1.6, y: 1.3, w: 5.3, h: 0.4, - fontSize: 12, bold: true, color: colors.secondary, align: 'left' - }); - - // 콘텐츠 영역 플레이스홀더 - slide.addShape('rect', { - x: 1.6, y: 1.8, w: 5.3, h: 3.4, - fill: { color: colors.white }, - line: { color: colors.wireframeBorder, width: 0.5, dashType: 'dash' } - }); - slide.addText('콘텐츠 영역', { - x: 1.6, y: 3.2, w: 5.3, h: 0.4, - fontSize: 10, color: colors.darkGray, align: 'center' - }); - } - - // Description 영역 (우측) - slide.addShape('rect', { - x: 7.1, y: 0.7, w: 2.7, h: 4.75, - fill: { color: colors.black } - }); - - // Description 헤더 - slide.addShape('rect', { - x: 7.2, y: 0.8, w: 2.5, h: 0.35, - fill: { color: colors.accent } - }); - slide.addText('Description', { - x: 7.2, y: 0.8, w: 2.5, h: 0.35, - fontSize: 9, bold: true, color: colors.white, - align: 'center', valign: 'middle' - }); - - // Description 항목 - const descriptions = screenInfo.descriptions || [ - { title: '기능 1', content: '기능 설명 1' }, - { title: '기능 2', content: '기능 설명 2' }, - { title: '기능 3', content: '기능 설명 3' } - ]; - - descriptions.slice(0, 5).forEach((desc, idx) => { - const y = 1.25 + idx * 0.85; - - // 번호 원 - slide.addShape('ellipse', { - x: 7.25, y: y, w: 0.25, h: 0.25, - fill: { color: colors.accent } - }); - slide.addText(String(idx + 1), { - x: 7.25, y: y, w: 0.25, h: 0.25, - fontSize: 7, bold: true, color: colors.white, - align: 'center', valign: 'middle' - }); - - // 제목 - slide.addText(desc.title, { - x: 7.55, y: y, w: 2.1, h: 0.25, - fontSize: 8, bold: true, color: colors.white - }); - - // 설명 - slide.addText(desc.content, { - x: 7.25, y: y + 0.28, w: 2.4, h: 0.5, - fontSize: 7, color: colors.darkGray - }); - }); - - console.log(`✅ 슬라이드 ${pageNum}: ${screenInfo.screenName || '화면'} 생성 완료`); - } - - // ---------------------------------------- - // 전체 생성 - // ---------------------------------------- - async generate() { - console.log('🚀 스토리보드 PPTX 생성 시작\n'); - - try { - // 기본 슬라이드 - this.createCoverSlide(); - this.createHistorySlide(); - this.createTOCSlide(); - this.createOverviewSlide(); - this.createIASlide(); - - // 스토리보드 슬라이드 - const screens = this.config.screens || []; - screens.forEach((screen, idx) => { - this.createStoryboardSlide(screen, idx + 6); - }); - - // 파일 저장 - const outputPath = this.config.outputPath || 'storyboard.pptx'; - const outputDir = path.dirname(outputPath); - - if (!fs.existsSync(outputDir)) { - fs.mkdirSync(outputDir, { recursive: true }); - } - - await this.pptx.writeFile({ fileName: outputPath }); - - // 결과 출력 - const stats = fs.statSync(outputPath); - console.log('\n🎉 스토리보드 PPTX 생성 완료!'); - console.log(`📁 저장 위치: ${outputPath}`); - console.log(`📏 파일 크기: ${(stats.size / 1024).toFixed(1)} KB`); - console.log(`📊 총 슬라이드: ${5 + screens.length}장`); - - return outputPath; - } catch (error) { - console.error('❌ 생성 실패:', error.message); - throw error; - } - } -} - -// ======================================== -// CLI 실행 -// ======================================== -async function main() { - const args = process.argv.slice(2); - - // 인자 파싱 - let inputFile = null; - let outputFile = null; - let configFile = null; - - for (let i = 0; i < args.length; i++) { - if (args[i] === '--input' && args[i + 1]) { - inputFile = args[i + 1]; - i++; - } else if (args[i] === '--output' && args[i + 1]) { - outputFile = args[i + 1]; - i++; - } else if (args[i] === '--config' && args[i + 1]) { - configFile = args[i + 1]; - i++; - } - } - - let config = {}; - - // 설정 파일이 있으면 로드 - if (configFile && fs.existsSync(configFile)) { - config = JSON.parse(fs.readFileSync(configFile, 'utf-8')); - console.log(`📄 설정 파일 로드: ${configFile}`); - } - - // 입력 파일 분석 - if (inputFile && fs.existsSync(inputFile)) { - console.log(`📄 입력 파일 분석: ${inputFile}`); - const content = fs.readFileSync(inputFile, 'utf-8'); - const parser = new TextParser(content).parse(); - - // 파싱 결과를 config에 병합 - if (parser.metadata.projectName) { - config.projectName = config.projectName || parser.metadata.projectName; - } - if (parser.metadata.date) { - config.date = config.date || parser.metadata.date; - } - if (parser.metadata.author) { - config.author = config.author || parser.metadata.author; - } - } - - // 출력 파일 설정 - if (outputFile) { - config.outputPath = outputFile; - } else if (!config.outputPath) { - config.outputPath = 'storyboard.pptx'; - } - - // 생성 - const generator = new StoryboardGenerator(config); - await generator.generate(); -} - -// 모듈 내보내기 -module.exports = { TextParser, StoryboardGenerator, colors }; - -// CLI 실행 -if (require.main === module) { - main().catch(console.error); -} diff --git a/.claude/skills/storyboard-generator/scripts/package-lock.json b/.claude/skills/storyboard-generator/scripts/package-lock.json deleted file mode 100755 index 57411a5..0000000 --- a/.claude/skills/storyboard-generator/scripts/package-lock.json +++ /dev/null @@ -1,765 +0,0 @@ -{ - "name": "scripts", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "scripts", - "version": "1.0.0", - "license": "ISC", - "dependencies": { - "playwright": "^1.57.0", - "pptxgenjs": "^4.0.1", - "sharp": "^0.34.5" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz", - "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@img/colour": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", - "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/@img/sharp-darwin-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", - "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.2.4" - } - }, - "node_modules/@img/sharp-darwin-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", - "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.2.4" - } - }, - "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", - "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", - "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", - "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", - "cpu": [ - "arm" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", - "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", - "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", - "cpu": [ - "ppc64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-riscv64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", - "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", - "cpu": [ - "riscv64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", - "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", - "cpu": [ - "s390x" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", - "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", - "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", - "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-linux-arm": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", - "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", - "cpu": [ - "arm" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", - "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-ppc64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", - "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", - "cpu": [ - "ppc64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-ppc64": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-riscv64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", - "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", - "cpu": [ - "riscv64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-riscv64": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-s390x": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", - "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", - "cpu": [ - "s390x" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", - "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.2.4" - } - }, - "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", - "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" - } - }, - "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", - "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.2.4" - } - }, - "node_modules/@img/sharp-wasm32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", - "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", - "cpu": [ - "wasm32" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", - "optional": true, - "dependencies": { - "@emnapi/runtime": "^1.7.0" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", - "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-ia32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", - "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", - "cpu": [ - "ia32" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", - "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@types/node": { - "version": "22.19.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.3.tgz", - "integrity": "sha512-1N9SBnWYOJTrNZCdh/yJE+t910Y128BoyY+zBLWhL3r0TYzlTmFdXrPwHL9DyFZmlEXNQQolTZh3KHV31QDhyA==", - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "license": "MIT" - }, - "node_modules/detect-libc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", - "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "license": "Apache-2.0", - "engines": { - "node": ">=8" - } - }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/https": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/https/-/https-1.0.0.tgz", - "integrity": "sha512-4EC57ddXrkaF0x83Oj8sM6SLQHAWXw90Skqu2M4AEWENZ3F02dFJE/GARA8igO79tcgYqGrD7ae4f5L3um2lgg==", - "license": "ISC" - }, - "node_modules/image-size": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.2.1.tgz", - "integrity": "sha512-rH+46sQJ2dlwfjfhCyNx5thzrv+dtmBIhPHk0zgRUukHzZ/kRueTJXoYYsclBaKcSMBWuGbOFXtioLpzTb5euw==", - "license": "MIT", - "dependencies": { - "queue": "6.0.2" - }, - "bin": { - "image-size": "bin/image-size.js" - }, - "engines": { - "node": ">=16.x" - } - }, - "node_modules/immediate": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", - "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", - "license": "MIT" - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "license": "MIT" - }, - "node_modules/jszip": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", - "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", - "license": "(MIT OR GPL-3.0-or-later)", - "dependencies": { - "lie": "~3.3.0", - "pako": "~1.0.2", - "readable-stream": "~2.3.6", - "setimmediate": "^1.0.5" - } - }, - "node_modules/lie": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", - "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", - "license": "MIT", - "dependencies": { - "immediate": "~3.0.5" - } - }, - "node_modules/pako": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", - "license": "(MIT AND Zlib)" - }, - "node_modules/playwright": { - "version": "1.57.0", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.57.0.tgz", - "integrity": "sha512-ilYQj1s8sr2ppEJ2YVadYBN0Mb3mdo9J0wQ+UuDhzYqURwSoW4n1Xs5vs7ORwgDGmyEh33tRMeS8KhdkMoLXQw==", - "license": "Apache-2.0", - "dependencies": { - "playwright-core": "1.57.0" - }, - "bin": { - "playwright": "cli.js" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "fsevents": "2.3.2" - } - }, - "node_modules/playwright-core": { - "version": "1.57.0", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.57.0.tgz", - "integrity": "sha512-agTcKlMw/mjBWOnD6kFZttAAGHgi/Nw0CZ2o6JqWSbMlI219lAFLZZCyqByTsvVAJq5XA5H8cA6PrvBRpBWEuQ==", - "license": "Apache-2.0", - "bin": { - "playwright-core": "cli.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/pptxgenjs": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pptxgenjs/-/pptxgenjs-4.0.1.tgz", - "integrity": "sha512-TeJISr8wouAuXw4C1F/mC33xbZs/FuEG6nH9FG1Zj+nuPcGMP5YRHl6X+j3HSUnS1f3at6k75ZZXPMZlA5Lj9A==", - "license": "MIT", - "dependencies": { - "@types/node": "^22.8.1", - "https": "^1.0.0", - "image-size": "^1.2.1", - "jszip": "^3.10.1" - } - }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "license": "MIT" - }, - "node_modules/queue": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz", - "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==", - "license": "MIT", - "dependencies": { - "inherits": "~2.0.3" - } - }, - "node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "license": "MIT" - }, - "node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", - "license": "MIT" - }, - "node_modules/sharp": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", - "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", - "hasInstallScript": true, - "license": "Apache-2.0", - "dependencies": { - "@img/colour": "^1.0.0", - "detect-libc": "^2.1.2", - "semver": "^7.7.3" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.34.5", - "@img/sharp-darwin-x64": "0.34.5", - "@img/sharp-libvips-darwin-arm64": "1.2.4", - "@img/sharp-libvips-darwin-x64": "1.2.4", - "@img/sharp-libvips-linux-arm": "1.2.4", - "@img/sharp-libvips-linux-arm64": "1.2.4", - "@img/sharp-libvips-linux-ppc64": "1.2.4", - "@img/sharp-libvips-linux-riscv64": "1.2.4", - "@img/sharp-libvips-linux-s390x": "1.2.4", - "@img/sharp-libvips-linux-x64": "1.2.4", - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", - "@img/sharp-libvips-linuxmusl-x64": "1.2.4", - "@img/sharp-linux-arm": "0.34.5", - "@img/sharp-linux-arm64": "0.34.5", - "@img/sharp-linux-ppc64": "0.34.5", - "@img/sharp-linux-riscv64": "0.34.5", - "@img/sharp-linux-s390x": "0.34.5", - "@img/sharp-linux-x64": "0.34.5", - "@img/sharp-linuxmusl-arm64": "0.34.5", - "@img/sharp-linuxmusl-x64": "0.34.5", - "@img/sharp-wasm32": "0.34.5", - "@img/sharp-win32-arm64": "0.34.5", - "@img/sharp-win32-ia32": "0.34.5", - "@img/sharp-win32-x64": "0.34.5" - } - }, - "node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD", - "optional": true - }, - "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "license": "MIT" - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "license": "MIT" - } - } -} diff --git a/.claude/skills/storyboard-generator/scripts/package.json b/.claude/skills/storyboard-generator/scripts/package.json deleted file mode 100755 index e4c29db..0000000 --- a/.claude/skills/storyboard-generator/scripts/package.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "name": "scripts", - "version": "1.0.0", - "description": "", - "main": "generate-storyboard.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "keywords": [], - "author": "", - "license": "ISC", - "type": "commonjs", - "dependencies": { - "playwright": "^1.57.0", - "pptxgenjs": "^4.0.1", - "sharp": "^0.34.5" - } -} diff --git a/.claude/skills/storyboard-generator/templates/storyboard-slide.html b/.claude/skills/storyboard-generator/templates/storyboard-slide.html deleted file mode 100755 index 3de87ac..0000000 --- a/.claude/skills/storyboard-generator/templates/storyboard-slide.html +++ /dev/null @@ -1,349 +0,0 @@ - - - - - - - - - -
                                      -
                                      -
                                      -

                                      Task Name

                                      -

                                      {{taskName}}

                                      -

                                      Ver.

                                      -

                                      D1.0

                                      -

                                      Page

                                      -

                                      {{page}}

                                      -
                                      -
                                      -

                                      Route

                                      -

                                      {{route}}

                                      -

                                      Screen Name

                                      -

                                      {{screenName}}

                                      -

                                      Screen ID

                                      -

                                      {{screenId}}

                                      -
                                      -
                                      -
                                      - - -
                                      - -
                                      -
                                      -

                                      {{systemName}}

                                      -
                                      -
                                      - - - - -
                                      - {{content}} -
                                      -
                                      -
                                      - - -
                                      -
                                      -

                                      Description

                                      -
                                      - {{#each descriptions}} -
                                      -
                                      -
                                      -

                                      {{index}}

                                      -
                                      -
                                      -

                                      {{title}}

                                      -
                                      -
                                      -
                                      -

                                      {{content}}

                                      -
                                      -
                                      - {{/each}} -
                                      -
                                      - - diff --git a/.claude/skills/system-debug-logger/SKILL.md b/.claude/skills/system-debug-logger/SKILL.md deleted file mode 100755 index 9bb43b9..0000000 --- a/.claude/skills/system-debug-logger/SKILL.md +++ /dev/null @@ -1,171 +0,0 @@ ---- -name: system-debug-logger -description: 애플리케이션에 에러 및 예외를 자동으로 캡처하는 디버그 로깅 기능을 추가합니다. 로깅 추가, 디버그 설정, 에러 추적 요청 시 활성화됩니다. -allowed-tools: Read, Write, Edit, Glob, Grep, Bash ---- - -# System Debug Logger - -애플리케이션에 체계적인 디버그 로깅 기능을 추가하는 스킬입니다. - -## 기능 - -### 로깅 레벨 -- **ERROR**: 오류 및 예외 상황 -- **WARN**: 경고 및 잠재적 문제 -- **INFO**: 일반 정보성 메시지 -- **DEBUG**: 상세 디버깅 정보 -- **TRACE**: 매우 상세한 추적 정보 - -### 캡처 대상 -- 예외 및 에러 -- API 요청/응답 -- 데이터베이스 쿼리 -- 성능 메트릭 -- 사용자 액션 - -### 출력 형식 -- 콘솔 출력 -- 파일 로깅 (일별 롤링) -- JSON 구조화 로그 -- 외부 서비스 전송 (선택) - -## 구현 패턴 - -### JavaScript/Node.js -```javascript -const logger = { - levels: { ERROR: 0, WARN: 1, INFO: 2, DEBUG: 3, TRACE: 4 }, - currentLevel: 2, // INFO - - log(level, message, meta = {}) { - if (this.levels[level] <= this.currentLevel) { - const timestamp = new Date().toISOString(); - const logEntry = { - timestamp, - level, - message, - ...meta - }; - console.log(JSON.stringify(logEntry)); - } - }, - - error(message, error = null) { - this.log('ERROR', message, { - error: error?.message, - stack: error?.stack - }); - }, - - warn(message, meta) { this.log('WARN', message, meta); }, - info(message, meta) { this.log('INFO', message, meta); }, - debug(message, meta) { this.log('DEBUG', message, meta); }, - trace(message, meta) { this.log('TRACE', message, meta); } -}; - -// 전역 에러 핸들러 -process.on('uncaughtException', (error) => { - logger.error('Uncaught Exception', error); - process.exit(1); -}); - -process.on('unhandledRejection', (reason) => { - logger.error('Unhandled Rejection', { reason }); -}); -``` - -### Python -```python -import logging -import json -from datetime import datetime - -class JSONFormatter(logging.Formatter): - def format(self, record): - log_entry = { - 'timestamp': datetime.utcnow().isoformat(), - 'level': record.levelname, - 'message': record.getMessage(), - 'module': record.module, - 'function': record.funcName, - 'line': record.lineno - } - if record.exc_info: - log_entry['exception'] = self.formatException(record.exc_info) - return json.dumps(log_entry) - -# 로거 설정 -logger = logging.getLogger(__name__) -handler = logging.StreamHandler() -handler.setFormatter(JSONFormatter()) -logger.addHandler(handler) -logger.setLevel(logging.DEBUG) - -# 데코레이터로 함수 호출 로깅 -def log_calls(func): - def wrapper(*args, **kwargs): - logger.debug(f'Calling {func.__name__}', - extra={'args': args, 'kwargs': kwargs}) - try: - result = func(*args, **kwargs) - logger.debug(f'{func.__name__} returned', - extra={'result': result}) - return result - except Exception as e: - logger.error(f'{func.__name__} failed', exc_info=True) - raise - return wrapper -``` - -### Express 미들웨어 -```javascript -const requestLogger = (req, res, next) => { - const start = Date.now(); - - res.on('finish', () => { - const duration = Date.now() - start; - logger.info('HTTP Request', { - method: req.method, - url: req.url, - status: res.statusCode, - duration: `${duration}ms`, - userAgent: req.get('User-Agent'), - ip: req.ip - }); - }); - - next(); -}; - -const errorLogger = (err, req, res, next) => { - logger.error('Request Error', { - method: req.method, - url: req.url, - error: err.message, - stack: err.stack - }); - next(err); -}; -``` - -## 로그 파일 구조 - -``` -logs/ -├── app-2024-01-15.log # 일별 로그 -├── error-2024-01-15.log # 에러 전용 -└── debug/ - └── trace-2024-01-15.log # 상세 추적 -``` - -## 사용 예시 - -``` -이 프로젝트에 디버그 로깅을 추가해줘 -API 요청을 로깅하는 미들웨어를 만들어줘 -에러 추적 시스템을 설정해줘 -``` - -## 출처 -Original skill from skills.cokac.com diff --git a/.claude/skills/test-coverage-auditor/SKILL.md b/.claude/skills/test-coverage-auditor/SKILL.md deleted file mode 100644 index 96e2772..0000000 --- a/.claude/skills/test-coverage-auditor/SKILL.md +++ /dev/null @@ -1,223 +0,0 @@ ---- -name: ln-634-test-coverage-auditor -description: Coverage Gaps audit worker (L3). Identifies missing tests for critical paths (Money 20+, Security 20+, Data Integrity 15+, Core Flows 15+). Returns list of untested critical business logic with priority justification. -allowed-tools: Read, Grep, Glob, Bash ---- - -# Coverage Gaps Auditor (L3 Worker) - -Specialized worker identifying missing tests for critical business logic. - -## Purpose & Scope - -- **Worker in ln-630 coordinator pipeline** -- Audit **Coverage Gaps** (Category 4: High Priority) -- Identify untested critical paths -- Classify by category (Money, Security, Data, Core Flows) -- Calculate compliance score (X/10) - -## Inputs (from Coordinator) - -**MANDATORY READ:** Load `shared/references/task_delegation_pattern.md#audit-coordinator--worker-contract` for contextStore structure. - -Receives `contextStore` with: `tech_stack`, `testFilesMetadata`, `codebase_root`. - -**Domain-aware:** Supports `domain_mode` + `current_domain` (see `audit_output_schema.md#domain-aware-worker-output`). - -## Workflow - -1) **Parse context** — extract fields, determine `scan_path` (domain-aware if specified) - ELSE: - scan_path = codebase_root - domain_name = null - ``` - -2) **Identify critical paths in scan_path** (not entire codebase) - - Scan production code in `scan_path` for money/security/data keywords - - All Grep/Glob patterns use `scan_path` (not codebase_root) - - Example: `Grep(pattern="payment|refund|discount", path=scan_path)` - -3) **Check test coverage for each critical path** - - Search ALL test files for coverage (tests may be in different location than production code) - - Match by function name, module name, or test description - -4) **Collect missing tests** - - Tag each finding with `domain: domain_name` (if domain-aware) - -5) **Calculate score** - -6) **Return JSON with domain metadata** - - Include `domain` and `scan_path` fields (if domain-aware) - -## Critical Paths Classification - -### 1. Money Flows (Priority 20+) - -**What:** Any code handling financial transactions - -**Examples:** -- Payment processing (`/payment`, `processPayment()`) -- Discounts/promotions (`calculateDiscount()`, `applyPromoCode()`) -- Tax calculations (`calculateTax()`, `getTaxRate()`) -- Refunds (`processRefund()`, `/refund`) -- Invoices/billing (`generateInvoice()`, `createBill()`) -- Currency conversion (`convertCurrency()`) - -**Min Priority:** 20 - -**Why Critical:** Money loss, fraud, legal compliance - -### 2. Security Flows (Priority 20+) - -**What:** Authentication, authorization, encryption - -**Examples:** -- Login/logout (`/login`, `authenticate()`) -- Token refresh (`/refresh-token`, `refreshAccessToken()`) -- Password reset (`/forgot-password`, `resetPassword()`) -- Permissions/RBAC (`checkPermission()`, `hasRole()`) -- Encryption/hashing (custom crypto logic, NOT bcrypt/argon2) -- API key validation (`validateApiKey()`) - -**Min Priority:** 20 - -**Why Critical:** Security breach, data leak, unauthorized access - -### 3. Data Integrity (Priority 15+) - -**What:** CRUD operations, transactions, validation - -**Examples:** -- Critical CRUD (`createUser()`, `deleteOrder()`, `updateProduct()`) -- Database transactions (`withTransaction()`) -- Data validation (custom validators, NOT framework defaults) -- Data migrations (`runMigration()`) -- Unique constraints (`checkDuplicateEmail()`) - -**Min Priority:** 15 - -**Why Critical:** Data corruption, lost data, inconsistent state - -### 4. Core User Journeys (Priority 15+) - -**What:** Multi-step flows critical to business - -**Examples:** -- Registration → Email verification → Onboarding -- Search → Product details → Add to cart → Checkout -- Upload file → Process → Download result -- Submit form → Approval workflow → Notification - -**Min Priority:** 15 - -**Why Critical:** Broken user flow = lost customers - -## Audit Rules - -### 1. Identify Critical Paths - -**Process:** -- Scan codebase for money-related keywords: `payment`, `refund`, `discount`, `tax`, `price`, `currency` -- Scan for security keywords: `auth`, `login`, `password`, `token`, `permission`, `encrypt` -- Scan for data keywords: `transaction`, `validation`, `migration`, `constraint` -- Scan for user journeys: multi-step flows in routes/controllers - -### 2. Check Test Coverage - -**For each critical path:** -- Search test files for matching test name/description -- If NO test found → add to missing tests list -- If test found but inadequate (only positive, no edge cases) → add to gaps list - -### 3. Categorize Gaps - -**Severity by Priority:** -- **CRITICAL:** Priority 20+ (Money, Security) -- **HIGH:** Priority 15-19 (Data, Core Flows) -- **MEDIUM:** Priority 10-14 (Important but not critical) - -### 4. Provide Justification - -**For each missing test:** -- Explain WHY it's critical (money loss, security breach, etc.) -- Suggest test type (E2E, Integration, Unit) -- Estimate effort (S/M/L) - -## Scoring Algorithm - -See `shared/references/audit_scoring.md` for unified formula and score interpretation. - -**Severity mapping by Priority:** -- Priority 20+ (Money, Security) missing test → CRITICAL -- Priority 15-19 (Data Integrity, Core Flows) missing test → HIGH -- Priority 10-14 (Important) missing test → MEDIUM -- Priority <10 (Nice-to-have) → LOW - -## Output Format - -**Return JSON to coordinator:** -```json -{ - "category": "Coverage Gaps", - "score": 6, - "total_issues": 10, - "critical": 3, - "high": 4, - "medium": 2, - "low": 1, - "checks": [ - {"id": "line_coverage", "name": "Line Coverage", "status": "passed", "details": "85% coverage (threshold: 80%)"}, - {"id": "branch_coverage", "name": "Branch Coverage", "status": "warning", "details": "72% coverage (threshold: 75%)"}, - {"id": "function_coverage", "name": "Function Coverage", "status": "passed", "details": "90% coverage (threshold: 80%)"}, - {"id": "critical_gaps", "name": "Critical Gaps", "status": "failed", "details": "3 Money flows, 2 Security flows untested"} - ], - "domain": "orders", - "scan_path": "src/orders", - "findings": [ - { - "severity": "CRITICAL", - "location": "src/orders/services/order.ts:45", - "issue": "Missing E2E test for applyDiscount() (Priority 25, Money flow)", - "principle": "Coverage Gaps / Money Flow", - "recommendation": "Add E2E test: applyDiscount() with edge cases (negative discount, max discount, currency rounding)", - "effort": "M" - }, - { - "severity": "HIGH", - "location": "src/orders/repositories/order.ts:78", - "issue": "Missing Integration test for orderTransaction() rollback (Priority 18, Data Integrity)", - "principle": "Coverage Gaps / Data Integrity", - "recommendation": "Add Integration test verifying transaction rollback on failure", - "effort": "M" - } - ] -} -``` - -**Note:** `domain` and `scan_path` fields included only when `domain_mode="domain-aware"`. - -## Critical Rules - -- **Domain-aware scanning:** If `domain_mode="domain-aware"`, scan ONLY `scan_path` production code (not entire codebase) -- **Tag findings:** Include `domain` field in each finding when domain-aware -- **Test search scope:** Search ALL test files for coverage (tests may be in different location than production code) -- **Match by name:** Use function name, module name, or test description to match tests to production code - -## Definition of Done - -- contextStore parsed (including domain_mode and current_domain) -- scan_path determined (domain path or codebase root) -- Critical paths identified in scan_path (Money, Security, Data, Core Flows) -- Test coverage checked for each critical path -- Missing tests collected with severity, priority, justification, domain -- Score calculated -- JSON returned to coordinator with domain metadata - -## Reference Files - -- **Audit scoring formula:** `shared/references/audit_scoring.md` -- **Audit output schema:** `shared/references/audit_output_schema.md` - ---- -**Version:** 3.0.0 -**Last Updated:** 2025-12-23 diff --git a/.claude/skills/test-isolation-auditor/SKILL.md b/.claude/skills/test-isolation-auditor/SKILL.md deleted file mode 100644 index 63ffff0..0000000 --- a/.claude/skills/test-isolation-auditor/SKILL.md +++ /dev/null @@ -1,367 +0,0 @@ ---- -name: ln-635-test-isolation-auditor -description: Test Isolation + Anti-Patterns audit worker (L3). Checks isolation (APIs/DB/FS/Time/Random/Network), determinism (flaky, order-dependent), and 6 anti-patterns. -allowed-tools: Read, Grep, Glob, Bash ---- - -# Test Isolation & Anti-Patterns Auditor (L3 Worker) - -Specialized worker auditing test isolation and detecting anti-patterns. - -## Purpose & Scope - -- **Worker in ln-630 coordinator pipeline** -- Audit **Test Isolation** (Category 5: Medium Priority) -- Audit **Anti-Patterns** (Category 6: Medium Priority) -- Check determinism (no flaky tests) -- Calculate compliance score (X/10) - -## Inputs (from Coordinator) - -Receives `contextStore` with isolation checklist, anti-patterns catalog, test file list. - -## Workflow - -1) Parse context -2) Check isolation for 6 categories -3) Check determinism -4) Detect 6 anti-patterns -5) Collect findings -6) Calculate score -7) Return JSON - -## Audit Rules: Test Isolation - -### 1. External APIs - -**Good:** Mocked (jest.mock, sinon, nock) -**Bad:** Real HTTP calls to external APIs - -**Detection:** -- Grep for `axios.get`, `fetch(`, `http.request` without mocks -- Check if test makes actual network calls - -**Severity:** **HIGH** - -**Recommendation:** Mock external APIs with `nock` or `jest.mock` - -**Effort:** M - -### 2. Database - -**Good:** In-memory DB (sqlite :memory:) or mocked -**Bad:** Real database (PostgreSQL, MySQL) - -**Detection:** -- Check DB connection strings (localhost:5432, real DB URL) -- Grep for `beforeAll(async () => { await db.connect() })` without `:memory:` - -**Severity:** **MEDIUM** - -**Recommendation:** Use in-memory DB or mock DB calls - -**Effort:** M-L - -### 3. File System - -**Good:** Mocked (mock-fs, vol) -**Bad:** Real file reads/writes - -**Detection:** -- Grep for `fs.readFile`, `fs.writeFile` without mocks -- Check if test creates/deletes real files - -**Severity:** **MEDIUM** - -**Recommendation:** Mock file system with `mock-fs` - -**Effort:** S-M - -### 4. Time/Date - -**Good:** Mocked (jest.useFakeTimers, sinon.useFakeTimers) -**Bad:** `new Date()`, `Date.now()` without mocks - -**Detection:** -- Grep for `new Date()` in test files without `useFakeTimers` - -**Severity:** **MEDIUM** - -**Recommendation:** Mock time with `jest.useFakeTimers()` - -**Effort:** S - -### 5. Random - -**Good:** Seeded random (Math.seedrandom, fixed seed) -**Bad:** `Math.random()` without seed - -**Detection:** -- Grep for `Math.random()` without seed setup - -**Severity:** **LOW** - -**Recommendation:** Use seeded random for deterministic tests - -**Effort:** S - -### 6. Network - -**Good:** Mocked (supertest for Express, no real ports) -**Bad:** Real network requests (`localhost:3000`, binding to port) - -**Detection:** -- Grep for `app.listen(3000)` in tests -- Check for real HTTP requests - -**Severity:** **MEDIUM** - -**Recommendation:** Use `supertest` (no real port) - -**Effort:** M - -## Audit Rules: Determinism - -### 1. Flaky Tests - -**What:** Tests that pass/fail randomly - -**Detection:** -- Run tests multiple times, check for inconsistent results -- Grep for `setTimeout`, `setInterval` without proper awaits -- Check for race conditions (async operations not awaited) - -**Severity:** **HIGH** - -**Recommendation:** Fix race conditions, use proper async/await - -**Effort:** M-L - -### 2. Time-Dependent Assertions - -**What:** Assertions on current time (`expect(timestamp).toBeCloseTo(Date.now())`) - -**Detection:** -- Grep for `Date.now()`, `new Date()` in assertions - -**Severity:** **MEDIUM** - -**Recommendation:** Mock time - -**Effort:** S - -### 3. Order-Dependent Tests - -**What:** Tests that fail when run in different order - -**Detection:** -- Run tests in random order, check for failures -- Grep for shared mutable state between tests - -**Severity:** **MEDIUM** - -**Recommendation:** Isolate tests, reset state in beforeEach - -**Effort:** M - -### 4. Shared Mutable State - -**What:** Global variables modified across tests - -**Detection:** -- Grep for `let globalVar` at module level -- Check for state shared between tests - -**Severity:** **MEDIUM** - -**Recommendation:** Use `beforeEach` to reset state - -**Effort:** S-M - -## Audit Rules: Anti-Patterns - -### 1. The Liar (Always Passes) - -**What:** Test with no assertions or trivial assertion (`expect().toBeTruthy()`) - -**Detection:** -- Count assertions per test -- If 0 assertions or only `toBeTruthy()` → Liar - -**Severity:** **HIGH** - -**Recommendation:** Add specific assertions or delete test - -**Effort:** S - -**Example:** -- **BAD (Liar):** Test calls `createUser()` but has NO assertions — always passes even if function breaks -- **GOOD:** Test calls `createUser()` and asserts `user.name` equals 'Alice', `user.id` is defined - -### 2. The Giant (>100 lines) - -**What:** Test with >100 lines, testing too many scenarios - -**Detection:** -- Count lines per test -- If >100 lines → Giant - -**Severity:** **MEDIUM** - -**Recommendation:** Split into focused tests (one scenario per test) - -**Effort:** S-M - -### 3. Slow Poke (>5 seconds) - -**What:** Test taking >5 seconds to run - -**Detection:** -- Measure test duration -- If >5s → Slow Poke - -**Severity:** **MEDIUM** - -**Recommendation:** Mock external deps, use in-memory DB, parallelize - -**Effort:** M - -### 4. Conjoined Twins (Unit test without mocks = Integration) - -**What:** Test labeled "Unit" but not mocking dependencies - -**Detection:** -- Check if test name includes "Unit" -- Verify all dependencies are mocked -- If no mocks → actually Integration test - -**Severity:** **LOW** - -**Recommendation:** Either mock dependencies OR rename to Integration test - -**Effort:** S - -### 5. Happy Path Only (No error scenarios) - -**What:** Only testing success cases, ignoring errors - -**Detection:** -- For each function, check if test covers error cases -- If only positive scenarios → Happy Path Only - -**Severity:** **MEDIUM** - -**Recommendation:** Add negative tests (error handling, edge cases) - -**Effort:** M - -**Example:** -- **BAD (Happy Path Only):** Test only checks `login()` with valid credentials, ignores error scenarios -- **GOOD:** Add negative test that verifies `login()` with invalid credentials throws 'Invalid credentials' error - -### 6. Framework Tester (Tests framework behavior) - -**What:** Tests validating Express/Prisma/bcrypt (NOT our code) - -**Detection:** -- Already detected by ln-631-test-business-logic-auditor -- Cross-reference findings - -**Severity:** **MEDIUM** - -**Recommendation:** Delete framework tests - -**Effort:** S - -## Scoring Algorithm - -See `shared/references/audit_scoring.md` for unified formula and score interpretation. - -**Severity mapping:** -- Flaky tests, External API not mocked, The Liar → HIGH -- Real database, File system, Time/Date, Network, The Giant, Happy Path Only → MEDIUM -- Random without seed, Order-dependent, Conjoined Twins → LOW - -## Output Format - -**Return JSON to coordinator (flat findings array):** -```json -{ - "category": "Isolation & Anti-Patterns", - "score": 6, - "total_issues": 18, - "critical": 0, - "high": 5, - "medium": 10, - "low": 3, - "checks": [ - {"id": "api_isolation", "name": "API Isolation", "status": "failed", "details": "2 tests make real HTTP calls"}, - {"id": "db_isolation", "name": "Database Isolation", "status": "warning", "details": "1 test uses real PostgreSQL"}, - {"id": "fs_isolation", "name": "File System Isolation", "status": "passed", "details": "All FS calls mocked"}, - {"id": "time_isolation", "name": "Time Isolation", "status": "passed", "details": "All Date/Time mocked"}, - {"id": "flaky_tests", "name": "Flaky Tests", "status": "failed", "details": "3 race conditions detected"}, - {"id": "anti_patterns", "name": "Anti-Patterns", "status": "warning", "details": "2 Liars, 1 Giant found"} - ], - "findings": [ - { - "severity": "HIGH", - "location": "user.test.ts:45-52", - "issue": "External API not mocked — test makes real HTTP call to https://api.github.com", - "principle": "Test Isolation / External APIs", - "recommendation": "Mock external API with nock or jest.mock", - "effort": "M" - }, - { - "severity": "HIGH", - "location": "async.test.ts:28-35", - "issue": "Flaky test (race condition) — setTimeout without proper await", - "principle": "Determinism / Race Condition", - "recommendation": "Fix race condition with proper async/await", - "effort": "M" - }, - { - "severity": "HIGH", - "location": "user.test.ts:45", - "issue": "Anti-pattern 'The Liar' — test 'createUser works' has no assertions", - "principle": "Anti-Patterns / The Liar", - "recommendation": "Add specific assertions or delete test", - "effort": "S" - }, - { - "severity": "MEDIUM", - "location": "db.test.ts:12", - "issue": "Real database used — test connects to localhost:5432 PostgreSQL", - "principle": "Test Isolation / Database", - "recommendation": "Use in-memory SQLite (:memory:) or mock DB", - "effort": "L" - }, - { - "severity": "MEDIUM", - "location": "order.test.ts:200-350", - "issue": "Anti-pattern 'The Giant' — test 'order flow' is 150 lines (>100)", - "principle": "Anti-Patterns / The Giant", - "recommendation": "Split into focused tests (one scenario per test)", - "effort": "M" - }, - { - "severity": "MEDIUM", - "location": "payment.test.ts", - "issue": "Anti-pattern 'Happy Path Only' — only success scenarios, no error tests", - "principle": "Anti-Patterns / Happy Path Only", - "recommendation": "Add negative tests for error handling", - "effort": "M" - } - ] -} -``` - -**Note:** Findings are flattened into single array. Use `principle` field prefix (Test Isolation / Determinism / Anti-Patterns) to identify issue category. - -## Reference Files - -- **Audit scoring formula:** `shared/references/audit_scoring.md` -- **Audit output schema:** `shared/references/audit_output_schema.md` - ---- -**Version:** 3.0.0 -**Last Updated:** 2025-12-23 diff --git a/.claude/skills/text-analyzer-skill/SKILL.md b/.claude/skills/text-analyzer-skill/SKILL.md deleted file mode 100755 index 842642a..0000000 --- a/.claude/skills/text-analyzer-skill/SKILL.md +++ /dev/null @@ -1,599 +0,0 @@ ---- -name: text-analyzer-skill -description: 자연어 텍스트 파일을 분석하여 PDF 템플릿 구조에 매핑하는 스킬. 섹션 추출, 콘텐츠 분류, 구조화된 데이터 생성 기능 제공. ---- - -# Text Analyzer Skill - 자연어 텍스트 분석 스킬 - -source 폴더의 txt 파일을 분석하여 PDF 샘플과 동일한 형태의 PPTX로 변환하는 핵심 스킬입니다. - -## 기능 개요 - -### 1. 자연어 텍스트 분석 (Natural Language Processing) -텍스트 파일의 구조와 내용을 자동으로 파싱하고 분류 - -### 2. PDF 템플릿 매핑 (Template Mapping) -분석된 내용을 SAM_ERP 스토리보드 구조에 자동 매핑 - -### 3. 구조화된 데이터 생성 (Structured Data Generation) -PPTX 생성에 필요한 슬라이드별 데이터 구조 생성 - -### 4. 콘텐츠 최적화 (Content Optimization) -프레젠테이션에 적합한 형태로 콘텐츠 가공 - -## 핵심 워크플로우 - -### 텍스트 분석 → PDF 구조 매핑 워크플로우 - -```mermaid -graph TD - A[source/*.txt] --> B[텍스트 파싱] - B --> C[섹션 인식] - C --> D[콘텐츠 분류] - D --> E[PDF 템플릿 매핑] - E --> F[PPTX 데이터 생성] - F --> G[슬라이드 변환] -``` - -### 자연어 분석 엔진 - -#### 1. 섹션 인식 패턴 -```javascript -const SECTION_PATTERNS = { - // 프로젝트 메타 정보 - project_meta: /^(프로젝트명|제목|title):\s*(.+)$/im, - date: /^(작성일|날짜|date):\s*(.+)$/im, - company: /^(회사명|회사|company):\s*(.+)$/im, - author: /^(작성자|저자|author):\s*(.+)$/im, - - // 주요 섹션 구분 - overview: /^=+\s*(개요|프로젝트\s*개요|overview)\s*=+$/im, - features: /^=+\s*(기능|주요\s*기능|features)\s*=+$/im, - screens: /^=+\s*(화면|화면\s*구성|screens)\s*=+$/im, - requirements: /^=+\s*(요구사항|기술\s*요구사항|requirements)\s*=+$/im, - flow: /^=+\s*(플로우|사용자\s*플로우|flow)\s*=+$/im, - - // 하위 항목 - numbered_item: /^(\d+)\.\s*(.+)$/, - bullet_item: /^[-*]\s*(.+)$/, - sub_item: /^\s*[-*]\s*(.+)$/ -}; -``` - -#### 2. 콘텐츠 분류 규칙 -```javascript -const CONTENT_CLASSIFIERS = { - // 화면/기능 관련 - screen_indicators: ['화면', '페이지', '뷰', 'screen', 'page', 'view'], - feature_indicators: ['기능', '모듈', '시스템', 'feature', 'module', 'system'], - ui_indicators: ['버튼', '메뉴', '입력', '목록', 'button', 'menu', 'input', 'list'], - - // 비즈니스 로직 - business_indicators: ['관리', '분석', '처리', '생성', 'management', 'analysis'], - data_indicators: ['데이터', '정보', '내역', 'data', 'information'], - - // 기술 요구사항 - tech_indicators: ['시스템', '플랫폼', '기술', 'system', 'platform', 'technology'] -}; -``` - -### PDF 템플릿 매핑 로직 - -#### SAM_ERP 구조 매핑 테이블 -```javascript -const TEMPLATE_MAPPING = { - // 표지 (Cover) - 1페이지 - cover: { - source_fields: ['project_meta', 'date', 'company', 'author'], - template_layout: 'brand_centered', - required: true - }, - - // 문서 히스토리 - 2페이지 - document_history: { - source_fields: ['date', 'author'], - template_layout: 'table_full_width', - auto_generate: true // 자동 생성 - }, - - // 메뉴 구조 - 3페이지 - menu_structure: { - source_fields: ['features', 'screens'], - template_layout: 'hierarchical_diagram', - extraction_method: 'feature_hierarchy' - }, - - // 공통 가이드라인 - 4-8페이지 - common_guidelines: { - source_fields: ['requirements', 'ui_patterns'], - template_layout: 'documentation_style', - default_sections: ['interaction', 'responsive', 'template', 'notifications'] - }, - - // 상세 화면 - 9-N페이지 - detail_screens: { - source_fields: ['screens', 'features', 'flow'], - template_layout: 'wireframe_with_description', - per_screen: true - } -}; -``` - -## 스크립트 구조 - -### 1. text-parser.js -자연어 텍스트 파싱 및 구조 분석 - -```javascript -class TextParser { - constructor() { - this.sections = new Map(); - this.metadata = {}; - } - - async parseFile(filePath) { - const content = await fs.readFile(filePath, 'utf8'); - return this.parseContent(content); - } - - parseContent(content) { - // 1. 메타데이터 추출 - this.extractMetadata(content); - - // 2. 섹션 구분 - this.extractSections(content); - - // 3. 구조화된 데이터 반환 - return { - metadata: this.metadata, - sections: this.sections, - raw_content: content - }; - } - - extractMetadata(content) { - Object.entries(SECTION_PATTERNS).forEach(([key, pattern]) => { - const match = content.match(pattern); - if (match && key.includes('meta') || ['date', 'company', 'author'].includes(key)) { - this.metadata[key] = match[2]?.trim(); - } - }); - } - - extractSections(content) { - const lines = content.split('\n'); - let currentSection = null; - let currentContent = []; - - lines.forEach(line => { - const sectionMatch = this.findSectionHeader(line); - if (sectionMatch) { - // 이전 섹션 저장 - if (currentSection) { - this.sections.set(currentSection, this.processContent(currentContent)); - } - currentSection = sectionMatch; - currentContent = []; - } else if (currentSection) { - currentContent.push(line); - } - }); - - // 마지막 섹션 저장 - if (currentSection) { - this.sections.set(currentSection, this.processContent(currentContent)); - } - } - - findSectionHeader(line) { - for (const [key, pattern] of Object.entries(SECTION_PATTERNS)) { - if (['overview', 'features', 'screens', 'requirements', 'flow'].includes(key)) { - const match = line.match(pattern); - if (match) return key; - } - } - return null; - } - - processContent(lines) { - const processed = { - raw: lines.join('\n'), - items: [], - structure: 'flat' - }; - - lines.forEach(line => { - line = line.trim(); - if (!line) return; - - // 번호 목록 - const numberedMatch = line.match(SECTION_PATTERNS.numbered_item); - if (numberedMatch) { - processed.items.push({ - type: 'numbered', - number: numberedMatch[1], - content: numberedMatch[2], - children: [] - }); - processed.structure = 'numbered'; - return; - } - - // 불릿 목록 - const bulletMatch = line.match(SECTION_PATTERNS.bullet_item); - if (bulletMatch) { - if (processed.items.length > 0 && line.startsWith(' ')) { - // 하위 항목 - const lastItem = processed.items[processed.items.length - 1]; - lastItem.children.push({ - type: 'sub_bullet', - content: bulletMatch[1] - }); - } else { - processed.items.push({ - type: 'bullet', - content: bulletMatch[1], - children: [] - }); - } - processed.structure = 'hierarchical'; - return; - } - - // 일반 텍스트 - if (processed.items.length === 0) { - processed.items.push({ - type: 'paragraph', - content: line - }); - } else { - const lastItem = processed.items[processed.items.length - 1]; - if (lastItem.type === 'paragraph') { - lastItem.content += ' ' + line; - } else { - processed.items.push({ - type: 'paragraph', - content: line - }); - } - } - }); - - return processed; - } -} -``` - -### 2. template-mapper.js -파싱된 데이터를 PDF 템플릿 구조에 매핑 - -```javascript -class TemplateMapper { - constructor() { - this.pdfTemplate = null; - this.mappedData = {}; - } - - async loadPDFTemplate(templatePath) { - const templateContent = await fs.readFile(templatePath, 'utf8'); - this.pdfTemplate = JSON.parse(templateContent); - } - - mapToTemplate(parsedData) { - const mapped = { - metadata: this.generateMetadata(parsedData.metadata), - slides: [] - }; - - // 1. 표지 생성 - mapped.slides.push(this.createCoverSlide(parsedData.metadata)); - - // 2. 문서 히스토리 생성 - mapped.slides.push(this.createDocumentHistorySlide(parsedData.metadata)); - - // 3. 메뉴 구조 생성 - mapped.slides.push(this.createMenuStructureSlide(parsedData.sections)); - - // 4. 공통 가이드라인 생성 - mapped.slides.push(...this.createCommonGuidelines()); - - // 5. 상세 화면 생성 - mapped.slides.push(...this.createDetailScreens(parsedData.sections)); - - return mapped; - } - - createCoverSlide(metadata) { - return { - type: 'cover', - slide_number: 1, - layout: this.pdfTemplate.page_templates.find(t => t.type === 'cover').layout, - content: { - title: metadata.project_meta || '프로젝트 기획서', - subtitle: '시스템 기획 및 설계', - date: this.formatDate(metadata.date), - company: metadata.company || 'Company Name', - author: metadata.author || '작성자' - } - }; - } - - createDocumentHistorySlide(metadata) { - return { - type: 'document_history', - slide_number: 2, - layout: this.pdfTemplate.page_templates.find(t => t.type === 'document_history').layout, - content: { - title: 'Document History', - table: [ - { - date: this.formatDate(metadata.date), - version: 'D1.0', - main_content: '초안 작성', - detailed_content: `${metadata.project_meta || '프로젝트'} 기획서 초안 작성`, - mark: '' - } - ] - } - }; - } - - createMenuStructureSlide(sections) { - const features = sections.get('features') || { items: [] }; - const screens = sections.get('screens') || { items: [] }; - - // 기능과 화면 정보를 계층 구조로 변환 - const menuStructure = this.buildMenuHierarchy(features, screens); - - return { - type: 'menu_structure', - slide_number: 3, - layout: this.pdfTemplate.page_templates.find(t => t.type === 'system_structure').layout, - content: { - title: 'Menu Structure', - structure: menuStructure - } - }; - } - - buildMenuHierarchy(features, screens) { - const structure = { - root: '시스템 구조', - children: [] - }; - - // 주요 기능을 상위 카테고리로 설정 - features.items.forEach(feature => { - if (feature.type === 'numbered' && feature.content) { - const category = { - name: feature.content, - children: [] - }; - - // 하위 항목이 있으면 추가 - if (feature.children && feature.children.length > 0) { - feature.children.forEach(child => { - category.children.push({ - name: child.content, - leaf: true - }); - }); - } - - structure.children.push(category); - } - }); - - // 화면 정보도 추가 - if (screens.items.length > 0) { - const screenCategory = { - name: '화면 구성', - children: [] - }; - - screens.items.forEach(screen => { - if (screen.content) { - screenCategory.children.push({ - name: screen.content, - leaf: true - }); - } - }); - - structure.children.push(screenCategory); - } - - return structure; - } - - createCommonGuidelines() { - // PDF 템플릿의 공통 가이드라인 구조를 사용 - return [ - { - type: 'common_guidelines', - slide_number: 4, - content: { - title: '공통', - sections: ['사용자 인터랙션', '반응형 웹', '화면 템플릿', '알림 시스템'] - } - } - ]; - } - - createDetailScreens(sections) { - const detailSlides = []; - let slideNumber = 9; // 상세 화면은 9페이지부터 시작 - - // 기능별로 상세 화면 생성 - const features = sections.get('features'); - const screens = sections.get('screens'); - const flow = sections.get('flow'); - - if (features && features.items) { - features.items.forEach(feature => { - if (feature.type === 'numbered') { - detailSlides.push(this.createDetailScreenFromFeature(feature, slideNumber)); - slideNumber++; - } - }); - } - - // 명시적인 화면 구성이 있다면 추가 - if (screens && screens.items) { - screens.items.forEach(screen => { - detailSlides.push(this.createDetailScreenFromScreen(screen, slideNumber)); - slideNumber++; - }); - } - - return detailSlides; - } - - createDetailScreenFromFeature(feature, slideNumber) { - return { - type: 'detail_screen', - slide_number: slideNumber, - layout: this.pdfTemplate.page_templates.find(t => t.type === 'detail_screen').layout, - content: { - header_info: { - task_name: feature.content, - version: 'D1.0', - page_number: slideNumber, - route: this.generateRoute(feature.content), - screen_name: feature.content, - screen_id: this.generateScreenId(feature.content) - }, - wireframe: { - type: 'feature_mockup', - description: `${feature.content} 화면 구성`, - elements: this.extractUIElements(feature) - }, - descriptions: this.generateDescriptions(feature) - } - }; - } - - extractUIElements(feature) { - const elements = []; - - // 자연어에서 UI 요소 추출 - if (feature.children && feature.children.length > 0) { - feature.children.forEach((child, index) => { - elements.push(`${index + 1}. ${child.content}`); - }); - } else { - // 기본 UI 요소 추가 - elements.push('1. 헤더 영역'); - elements.push('2. 메인 콘텐츠'); - elements.push('3. 액션 버튼'); - } - - return elements; - } - - generateDescriptions(feature) { - const descriptions = []; - - if (feature.children && feature.children.length > 0) { - feature.children.forEach((child, index) => { - descriptions.push({ - number: index + 1, - title: this.extractTitle(child.content), - description: child.content - }); - }); - } else { - descriptions.push({ - number: 1, - title: feature.content, - description: `${feature.content} 기능 구현 및 사용자 인터랙션 처리` - }); - } - - return descriptions; - } - - extractTitle(content) { - // 콘텐츠에서 제목 추출 (첫 번째 단어 또는 구문) - const words = content.split(' '); - return words.length > 3 ? words.slice(0, 2).join(' ') : words[0]; - } - - generateRoute(screenName) { - return '/' + screenName - .toLowerCase() - .replace(/\s+/g, '_') - .replace(/[^a-z0-9가-힣_]/g, ''); - } - - generateScreenId(screenName) { - return screenName - .toLowerCase() - .replace(/\s+/g, '_') - .replace(/[^a-z0-9가-힣_]/g, ''); - } - - formatDate(dateStr) { - if (!dateStr) { - return new Date().toISOString().split('T')[0].replace(/-/g, '.'); - } - return dateStr.replace(/-/g, '.'); - } - - generateMetadata(metadata) { - return { - title: metadata.project_meta || '프로젝트 기획서', - version: 'D1.0', - created_date: this.formatDate(metadata.date), - author: metadata.author || '작성자', - company: metadata.company || 'Company Name', - source_file: 'source/*.txt' - }; - } -} -``` - -## 사용법 - -### 1. 기본 실행 -```bash -# source 폴더의 txt 파일 분석 및 PPTX 생성 -node .claude/skills/text-analyzer-skill/scripts/txt-to-pptx.js - -# 또는 간단 명령 -npm run txt-to-ppt -``` - -### 2. 특정 파일 지정 -```bash -node txt-to-pptx.js --input source/my_project.txt --output my_presentation.pptx -``` - -### 3. 템플릿 지정 -```bash -node txt-to-pptx.js --template custom_template.json --input source/project.txt -``` - -## 품질 기준 - -### 텍스트 분석 정확도 -- **섹션 인식률**: >90% (명시적 구분자 기준) -- **콘텐츠 분류**: >85% (키워드 매칭 기준) -- **구조 보존**: 원본 계층 구조 유지 - -### PDF 템플릿 매핑 일치도 -- **레이아웃 일관성**: SAM_ERP 스토리보드와 95% 일치 -- **섹션 완성도**: 필수 섹션 100% 포함 -- **콘텐츠 적합성**: 프레젠테이션 형태로 최적화 - -## 확장 기능 - -### 다양한 텍스트 형식 지원 -- Markdown 파일 (.md) -- Word 문서 (.docx) -- 구조화된 JSON (.json) - -### AI 기반 콘텐츠 최적화 -- 자동 요약 기능 -- 키워드 추출 -- 슬라이드 분량 최적화 \ No newline at end of file diff --git a/.claude/skills/text-analyzer-skill/scripts/txt-to-pptx.js b/.claude/skills/text-analyzer-skill/scripts/txt-to-pptx.js deleted file mode 100755 index 6f3fb9e..0000000 --- a/.claude/skills/text-analyzer-skill/scripts/txt-to-pptx.js +++ /dev/null @@ -1,306 +0,0 @@ -/** - * 작동하는 TXT → PPTX 변환기 (컬러 오류 수정 버전) - */ - -const fs = require('fs').promises; -const PptxGenJS = require('pptxgenjs'); - -// 간단한 텍스트 분석 클래스 -class TextAnalyzer { - constructor() { - this.patterns = { - metadata: /^(.+?):[\s]*(.+)$/, - section: /^=== (.+) ===/, - heading: /^[가-힣A-Za-z].+[가-힣A-Za-z\d]$/, // 한글로 시작하고 끝나는 제목 - numbered_item: /^\d+\.\s*(.+)$/, - bullet_item: /^[•\-\*]\s*(.+)$/, - html_tag: /<[^>]+>/g - }; - } - - /** - * TXT 파일 분석 - */ - async analyzeFile(filePath) { - console.log(`📖 텍스트 파일 분석 중: ${filePath}`); - - const content = await fs.readFile(filePath, 'utf8'); - const lines = content.split('\n'); - - const result = { - metadata: {}, - sections: new Map() - }; - - let currentSection = null; - let currentContent = []; - - // 첫 번째 라인을 제목으로 사용 - if (lines.length > 0) { - result.metadata.title = lines[0].trim(); - } - - for (let i = 0; i < lines.length; i++) { - const line = lines[i]; - const trimmedLine = line.trim(); - if (!trimmedLine) continue; - - // HTML 태그 제거 - const cleanLine = trimmedLine.replace(this.patterns.html_tag, ''); - if (!cleanLine.trim()) continue; - - // 메타데이터 파싱 (처음 3줄 이내) - if (i < 3) { - const metaMatch = cleanLine.match(this.patterns.metadata); - if (metaMatch && !currentSection) { - const key = metaMatch[1].toLowerCase().replace(/[^\w]/g, '_'); - result.metadata[key] = metaMatch[2]; - continue; - } - } - - // 섹션 제목 (=== 형태) - const sectionMatch = cleanLine.match(this.patterns.section); - if (sectionMatch) { - // 이전 섹션 저장 - if (currentSection) { - result.sections.set(currentSection, { - title: currentSection, - content: currentContent.join('\n') - }); - } - - currentSection = sectionMatch[1]; - currentContent = []; - continue; - } - - // 한글 제목 패턴 (bullet point나 HTML이 아닌 경우) - if (!cleanLine.startsWith('•') && - !cleanLine.startsWith('-') && - !cleanLine.startsWith('<') && - cleanLine.length > 5 && - cleanLine.length < 100 && - this.patterns.heading.test(cleanLine)) { - - // 이전 섹션 저장 - if (currentSection) { - result.sections.set(currentSection, { - title: currentSection, - content: currentContent.join('\n') - }); - } - - currentSection = cleanLine; - currentContent = []; - continue; - } - - // 현재 섹션에 내용 추가 - if (currentSection) { - currentContent.push(cleanLine); - } else if (!currentSection && i > 2) { - // 섹션이 없으면 "개요"로 시작 - currentSection = "개요"; - currentContent = [cleanLine]; - } - } - - // 마지막 섹션 저장 - if (currentSection) { - result.sections.set(currentSection, { - title: currentSection, - content: currentContent.join('\n') - }); - } - - // 기본값 설정 - if (!result.metadata.title) result.metadata.title = 'TXT 변환 프레젠테이션'; - if (!result.metadata.date) result.metadata.date = new Date().toISOString().split('T')[0]; - if (!result.metadata.company) result.metadata.company = 'Generated by TXT→PPTX'; - - console.log(`✅ 분석 완료: ${result.sections.size}개 섹션 발견`); - return result; - } -} - -// PPTX 생성 클래스 -class PPTXGenerator { - constructor() { - this.pptx = new PptxGenJS(); - this.pptx.layout = 'LAYOUT_16x9'; - this.slideNumber = 1; - } - - /** - * 분석된 데이터로부터 PPTX 생성 - */ - async generateFromAnalysis(analysisResult) { - console.log('📊 PPTX 생성 중...'); - - const { metadata, sections } = analysisResult; - - // 1. 표지 슬라이드 - this.createCoverSlide(metadata); - - // 2. 목차 슬라이드 - this.createTableOfContents(sections); - - // 3. 각 섹션별 슬라이드 - for (const [sectionName, sectionData] of sections) { - this.createSectionSlide(sectionName, sectionData); - } - - return this.pptx; - } - - /** - * 표지 슬라이드 생성 - */ - createCoverSlide(metadata) { - const slide = this.pptx.addSlide(); - - // 배경 색상 - slide.background = { color: '8BC34A' }; - - // 프로젝트 제목 - slide.addText(metadata.title || 'TXT 변환 프레젠테이션', { - x: 1, y: 3, w: 8, h: 1.5, - fontSize: 36, - bold: true, - color: 'FFFFFF', - align: 'center' - }); - - // 부제목 - slide.addText('시스템 기획 및 설계서', { - x: 1, y: 4.8, w: 8, h: 0.8, - fontSize: 20, - color: 'FFFFFF', - align: 'center' - }); - - // 날짜 및 회사정보 - const infoText = `${metadata.date}\n\n${metadata.company}`; - slide.addText(infoText, { - x: 7.5, y: 7, w: 2.5, h: 1.5, - fontSize: 12, - color: 'FFFFFF', - align: 'right' - }); - - console.log(`✅ 슬라이드 ${this.slideNumber}: 표지`); - this.slideNumber++; - } - - /** - * 목차 슬라이드 생성 - */ - createTableOfContents(sections) { - const slide = this.pptx.addSlide(); - - slide.addText('목차', { - x: 0.5, y: 0.5, w: 9, h: 0.8, - fontSize: 24, - bold: true, - color: '2E7D32' - }); - - let yPosition = 1.5; - let index = 1; - - for (const [sectionName] of sections) { - slide.addText(`${index}. ${sectionName}`, { - x: 1, y: yPosition, w: 8, h: 0.6, - fontSize: 16, - color: '333333' - }); - - yPosition += 0.8; - index++; - - if (yPosition > 6) break; // 슬라이드 범위 초과 방지 - } - - console.log(`✅ 슬라이드 ${this.slideNumber}: 목차`); - this.slideNumber++; - } - - /** - * 섹션 슬라이드 생성 - */ - createSectionSlide(sectionName, sectionData) { - const slide = this.pptx.addSlide(); - - // 섹션 제목 - slide.addText(sectionName, { - x: 0.5, y: 0.5, w: 9, h: 0.8, - fontSize: 24, - bold: true, - color: '2E7D32' - }); - - // 내용 처리 (긴 내용은 잘라서 표시) - let content = sectionData.content; - if (content.length > 800) { - content = content.substring(0, 800) + '...'; - } - - slide.addText(content, { - x: 0.5, y: 1.5, w: 9, h: 4.5, - fontSize: 14, - color: '333333', - lineSpacing: 20 - }); - - console.log(`✅ 슬라이드 ${this.slideNumber}: ${sectionName}`); - this.slideNumber++; - } -} - -// 메인 실행 함수 -async function convertTxtToPptx(inputPath, outputPath) { - try { - console.log('🚀 TXT → PPTX 변환 시작'); - console.log(`📄 입력: ${inputPath}`); - console.log(`📊 출력: ${outputPath}`); - - // 1. 텍스트 분석 - const analyzer = new TextAnalyzer(); - const analysisResult = await analyzer.analyzeFile(inputPath); - - // 2. PPTX 생성 - const generator = new PPTXGenerator(); - const pptx = await generator.generateFromAnalysis(analysisResult); - - // 3. 파일 저장 - await pptx.writeFile({ fileName: outputPath }); - console.log('✅ 변환 완료!'); - - } catch (error) { - console.error('❌ 변환 실패:', error.message); - throw error; - } -} - -// 명령행 인수 처리 -async function main() { - const args = process.argv.slice(2); - const inputFlag = args.find(arg => arg.startsWith('--input=')); - const outputFlag = args.find(arg => arg.startsWith('--output=')); - - const inputPath = inputFlag ? inputFlag.split('=')[1] : 'source/sample_project.txt'; - const outputPath = outputFlag ? outputFlag.split('=')[1] : 'pptx/working_test.pptx'; - - // 출력 디렉토리 생성 - await fs.mkdir('pptx', { recursive: true }); - - await convertTxtToPptx(inputPath, outputPath); -} - -// 직접 실행시 -if (require.main === module) { - main().catch(console.error); -} - -module.exports = { convertTxtToPptx, TextAnalyzer, PPTXGenerator }; \ No newline at end of file diff --git a/.claude/skills/uml-generator/SKILL.md b/.claude/skills/uml-generator/SKILL.md deleted file mode 100755 index 6514d61..0000000 --- a/.claude/skills/uml-generator/SKILL.md +++ /dev/null @@ -1,93 +0,0 @@ ---- -name: uml-generator -description: 프로젝트 아키텍처를 분석하여 UML 다이어그램을 자체 완결형 HTML로 생성합니다. UML, 클래스 다이어그램, 아키텍처 시각화 요청 시 활성화됩니다. -allowed-tools: Read, Write, Edit, Glob, Grep, Bash ---- - -# UML Generator - -프로젝트 구조를 분석하여 UML 다이어그램을 생성하는 스킬입니다. - -## 기능 - -### 지원하는 다이어그램 유형 -- **클래스 다이어그램**: 클래스, 인터페이스, 상속/구현 관계 -- **패키지 다이어그램**: 모듈/패키지 간 의존성 -- **시퀀스 다이어그램**: 객체 간 메시지 흐름 -- **컴포넌트 다이어그램**: 시스템 컴포넌트 구조 - -### 분석 대상 -- 클래스 및 인터페이스 정의 -- 상속 및 구현 관계 -- 메서드 시그니처 및 속성 -- 모듈 간 import/export 관계 -- 의존성 주입 패턴 - -### 출력 형식 -- Mermaid.js 기반 인터랙티브 HTML -- 자체 완결형 단일 파일 - -## HTML 템플릿 - -```html - - - - - UML 다이어그램 - - - - - -
                                      -
                                      -

                                      UML 다이어그램

                                      -
                                      -
                                      - -
                                      - -
                                      -

                                      클래스 다이어그램

                                      -
                                      - classDiagram - class ClassName { - +attribute: type - +method(): returnType - } -
                                      -
                                      - - -
                                      -

                                      시퀀스 다이어그램

                                      -
                                      - sequenceDiagram - participant A - participant B - A->>B: message -
                                      -
                                      -
                                      - - - - -``` - -## 사용 예시 - -``` -이 프로젝트의 클래스 다이어그램을 만들어줘 -src 폴더의 UML 다이어그램을 HTML로 생성해줘 -이 코드의 시퀀스 다이어그램을 그려줘 -``` - -## 출처 -Original skill from skills.cokac.com diff --git a/.claude/skills/webapp-testing/SKILL.md b/.claude/skills/webapp-testing/SKILL.md deleted file mode 100644 index 9f755c4..0000000 --- a/.claude/skills/webapp-testing/SKILL.md +++ /dev/null @@ -1,96 +0,0 @@ ---- -name: webapp-testing -description: Toolkit for interacting with and testing local web applications using Playwright. Supports verifying frontend functionality, debugging UI behavior, capturing browser screenshots, and viewing browser logs. -license: Complete terms in LICENSE.txt ---- - -# Web Application Testing - -To test local web applications, write native Python Playwright scripts. - -**Helper Scripts Available**: -- `scripts/with_server.py` - Manages server lifecycle (supports multiple servers) - -**Always run scripts with `--help` first** to see usage. DO NOT read the source until you try running the script first and find that a customized solution is abslutely necessary. These scripts can be very large and thus pollute your context window. They exist to be called directly as black-box scripts rather than ingested into your context window. - -## Decision Tree: Choosing Your Approach - -``` -User task → Is it static HTML? - ├─ Yes → Read HTML file directly to identify selectors - │ ├─ Success → Write Playwright script using selectors - │ └─ Fails/Incomplete → Treat as dynamic (below) - │ - └─ No (dynamic webapp) → Is the server already running? - ├─ No → Run: python scripts/with_server.py --help - │ Then use the helper + write simplified Playwright script - │ - └─ Yes → Reconnaissance-then-action: - 1. Navigate and wait for networkidle - 2. Take screenshot or inspect DOM - 3. Identify selectors from rendered state - 4. Execute actions with discovered selectors -``` - -## Example: Using with_server.py - -To start a server, run `--help` first, then use the helper: - -**Single server:** -```bash -python scripts/with_server.py --server "npm run dev" --port 5173 -- python your_automation.py -``` - -**Multiple servers (e.g., backend + frontend):** -```bash -python scripts/with_server.py \ - --server "cd backend && python server.py" --port 3000 \ - --server "cd frontend && npm run dev" --port 5173 \ - -- python your_automation.py -``` - -To create an automation script, include only Playwright logic (servers are managed automatically): -```python -from playwright.sync_api import sync_playwright - -with sync_playwright() as p: - browser = p.chromium.launch(headless=True) # Always launch chromium in headless mode - page = browser.new_page() - page.goto('http://localhost:5173') # Server already running and ready - page.wait_for_load_state('networkidle') # CRITICAL: Wait for JS to execute - # ... your automation logic - browser.close() -``` - -## Reconnaissance-Then-Action Pattern - -1. **Inspect rendered DOM**: - ```python - page.screenshot(path='/tmp/inspect.png', full_page=True) - content = page.content() - page.locator('button').all() - ``` - -2. **Identify selectors** from inspection results - -3. **Execute actions** using discovered selectors - -## Common Pitfall - -❌ **Don't** inspect the DOM before waiting for `networkidle` on dynamic apps -✅ **Do** wait for `page.wait_for_load_state('networkidle')` before inspection - -## Best Practices - -- **Use bundled scripts as black boxes** - To accomplish a task, consider whether one of the scripts available in `scripts/` can help. These scripts handle common, complex workflows reliably without cluttering the context window. Use `--help` to see usage, then invoke directly. -- Use `sync_playwright()` for synchronous scripts -- Always close the browser when done -- Use descriptive selectors: `text=`, `role=`, CSS selectors, or IDs -- Add appropriate waits: `page.wait_for_selector()` or `page.wait_for_timeout()` - -## Reference Files - -- **examples/** - Examples showing common patterns: - - `element_discovery.py` - Discovering buttons, links, and inputs on a page - - `static_html_automation.py` - Using file:// URLs for local HTML - - `console_logging.py` - Capturing console logs during automation diff --git a/.claude/skills/웹문서/SKILL.md b/.claude/skills/웹문서/SKILL.md deleted file mode 100755 index 80f8ddb..0000000 --- a/.claude/skills/웹문서/SKILL.md +++ /dev/null @@ -1,166 +0,0 @@ ---- -name: 웹문서 -description: SAM 프로젝트의 웹문서(PHP/HTML) 생성 시 적용되는 디자인 표준. 웹페이지, index.php, 보고서 페이지 생성 시 자동 활성화 -allowed-tools: Read, Write, Edit, Glob ---- - -# 웹문서 스타일 가이드 - -이 스킬은 SAM 프로젝트의 웹문서(PHP/HTML) 생성 시 적용되는 디자인 표준입니다. - -## 필수 적용 사항 - -### 1. 상단 홈 버튼 (필수) -모든 웹문서의 헤더에는 상위 index.php로 이동하는 홈 버튼을 포함해야 합니다: - -```html - - 🏠 - -``` - -### 2. 디자인 철학 (Core Philosophy) - -- **주제**: Professional Trust (신뢰 기반의 전문성) -- **분위기**: 깔끔함, 신뢰감, 데이터 중심적인 명확성 -- **핵심 키워드**: Teal, Navy, White, Light Gray, Modern Typography - -### 3. 색상 사양 (Color Palette) - -#### 기본 색상 (Brand Colors) -- **Primary (Teal)**: `#0d9488` (Tailwind: `teal-600`) - 핵심 강조 색상, 활성화 상태 -- **Secondary (Navy/Slate)**: `#1e293b` (Tailwind: `slate-800`) - 제목, 텍스트, 신뢰감을 주는 배경 -- **Accent (Amber)**: `#f59e0b` (Tailwind: `amber-500`) - 차트의 추세선 등 보조 강조 - -#### 배경 및 테두리 -- **Body Background**: `#f3f4f6` (Tailwind: `gray-100`) -- **Container Background**: `#ffffff` (White) -- **Border**: `#e2e8f0` (Tailwind: `slate-200`) - -### 4. 타이포그래피 (Typography) - -- **Font Family**: `'Noto Sans KR', sans-serif` (Google Fonts) -- **Heading Styles**: - - Main Title: `text-2xl font-bold text-slate-800` - - Sub Title: `text-lg font-bold text-slate-700` -- **Body Text**: `text-slate-600 leading-relaxed` - -### 5. UI 컴포넌트 표준 - -#### 상단 네비게이션 (Sticky Header) -```html -
                                      -
                                      -
                                      -
                                      - - 🏠 - -

                                      페이지 제목

                                      -
                                      - 날짜 -
                                      -
                                      -
                                      -``` - -#### 활성화된 탭 -```css -.nav-active { border-bottom: 3px solid #0d9488; color: #0d9488; font-weight: 700; } -``` - -#### 정보 카드 -- 사각 모서리 둥글게: `rounded-xl` -- 가벼운 그림자: `shadow-sm` -- 호버 효과: `hover:shadow-md transition-shadow` -- 왼쪽 테두리 강조: `border-l-4 border-teal-600` - -#### 차트 (Chart.js) -- 스타일: `max-height: 400px` -- 색상: Teal(`rgba(13, 148, 136, 0.2)`) 또는 Blue(`rgba(59, 130, 246, 0.2)`) - -### 6. 코딩 규칙 - -- **CSS Framework**: Tailwind CSS (CDN 방식) -```html - -``` - -- **폰트**: -```html - -``` - -- **SVG 지양**: 유니코드 이모지(🏢, 📈, 💰) 사용 - -- **애니메이션**: fade-in 효과 -```css -.fade-in { - animation: fadeIn 0.5s ease-out forwards; -} -@keyframes fadeIn { - from { opacity: 0; transform: translateY(10px); } - to { opacity: 1; transform: translateY(0); } -} -``` - -- **반응형**: 모바일 우선 + `md:` 접두사 - -- **커스텀 스크롤바**: -```css -.custom-scroll::-webkit-scrollbar { width: 6px; height: 6px; } -.custom-scroll::-webkit-scrollbar-track { background: #f1f5f9; border-radius: 10px; } -.custom-scroll::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 10px; } -``` - -### 7. 기본 HTML 템플릿 - -```html - - - - - - 페이지 제목 - - - - - -
                                      -
                                      -
                                      -
                                      - - 🏠 - -

                                      페이지 제목

                                      -
                                      - 날짜 -
                                      -
                                      -
                                      - -
                                      - -
                                      - -
                                      -
                                      -

                                      © SAM

                                      -
                                      -
                                      - - -``` diff --git a/.gitignore b/.gitignore index 9109c08..f9e7597 100644 --- a/.gitignore +++ b/.gitignore @@ -1,35 +1,19 @@ -# 모든 파일 무시 -* - -# 추적할 파일만 허용 -!.gitignore -!CLAUDE.md - -# .claude 폴더 - 스킬/에이전트는 추적 -!.claude/ -.claude/* -!.claude/skills/ -!.claude/skills/** -.claude/skills/**/node_modules/ -!.claude/agents/ -!.claude/agents/** - -# sam 문서 -!sam/ -sam/* -!sam/docs/ -!sam/docs/** -sam/docs/contracts/docx/backup/ - -# sam 배포/운영 문서 -!sam/deploys/ -!sam/deploys/** -!sam/front/ -!sam/front/** -!sam/projects/ -!sam/projects/** - -# 기타 -sam/sales +# OS/에디터 생성 파일 .DS_Store +Thumbs.db +*.swp +*.swo +*~ + +# 로그/임시 파일 +*.log +*.tmp +*.temp +*.bak +*.cache + +# 노션 내보내기 임시 _to_notion/ + +# 백업 파일 +contracts/docx/backup/ diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 123e5e9..0000000 --- a/CLAUDE.md +++ /dev/null @@ -1,1060 +0,0 @@ -# Claude Code 전역 설정 - -> 이 파일은 모든 프로젝트에 적용되는 전역 규칙입니다. - -## 메모리 - -### sam설명 -SAM 프로젝트의 기술적 개요 문서입니다. 이 문서를 참조하면 SAM 프로젝트가 무엇인지 이해할 수 있습니다. - -**파일 경로**: `/home/aweso/sam/docs/SAM_PROJECT_OVERVIEW_FOR_AI.md` - -**핵심 요약**: -- **회사**: 주일/경동 (블라인드/스크린 제조업체) -- **프로젝트**: SAM (Smart Automation Management) - 차세대 ERP/MES 통합 시스템 -- **기술 스택**: Laravel 12 + HTMX + Tailwind CSS + MySQL 8.0 (PHP 8.4) -- **아키텍처**: Multi-tenant (tenant_id 기반 데이터 격리) -- **레거시**: 5130.co.kr (PHP 기반) → SAM으로 마이그레이션 중 - -**사용자가 'sam설명'이라고 말하면**: -1. 위 경로의 `SAM_PROJECT_OVERVIEW_FOR_AI.md` 파일을 읽어서 전체 내용을 파악하세요 -2. SAM 프로젝트의 비즈니스 도메인, 기술 스택, 현재 작업 현황을 이해한 상태로 작업하세요 - ---- - -## Git 커밋 규칙 (최우선 필수 규칙) - -> **경고: 이 규칙은 절대 누락되어서는 안 됩니다!** -> **기준 문서**: `sam/docs/standards/git-conventions.md` - -### 필수 수행 절차 - -**모든 코드 작업 완료 후 반드시 다음을 수행:** - -1. 변경된 파일이 있는 Git 저장소로 이동 -2. `git status`로 변경사항 확인 -3. `git add <파일들>` 로 스테이징 -4. `git commit -m "type: [scope] 작업내용"` 로 커밋 - -### 커밋 메시지 형식 (필수) - -``` -type: [scope] 작업내용 - -- 세부항목 (생략가능) -- 세부항목 2 - -Issue: URL (생략가능) -``` - -**예시:** -```bash -feat: [calendar] 달력 기능 개선 - -- 클릭시 오류 기능 개선 -- 색상 변경 -``` - -```bash -fix: [auth] 로그인 시 세션 만료 오류 수정 -``` - -### Commit Types - -| Type | 설명 | 예시 | -|------|------|------| -| `feat` | 새로운 기능 추가 | `feat: [file] 파일 업로드 기능 추가` | -| `fix` | 버그 수정 | `fix: [auth] 세션 만료 오류 수정` | -| `chore` | 설정, 빌드 등 변경 | `chore: composer 패키지 업데이트` | -| `refactor` | 프로덕션 코드 리팩토링 | `refactor: [user] 서비스 메서드 분리` | -| `style` | 포맷/코딩 스타일 수정 | `style: Pint 포맷팅 적용` | -| `test` | 테스트 추가/수정 | `test: Product API 테스트 추가` | -| `docs` | 문서 변경 | `docs: API 문서 업데이트` | - -### Claude 서명 제외 (필수) - -``` -❌ Co-Authored-By: Claude — 포함 금지 -❌ 🤖 Generated with Claude Code — 포함 금지 -``` - -- Git hooks로 자동 제거됨 -- 간결하고 명확한 한글 커밋 메시지만 유지 - -### 푸시 정책 - -#### MNG 자동 푸시 (커밋 즉시 배포) - -> **MNG 프로젝트는 커밋 후 자동으로 개발+운영 서버에 배포한다.** 트리거 워드 불필요. - -**커밋 완료 후 자동 실행 절차:** - -1. `git push origin develop` (개발 서버 배포) -2. `git checkout main && git pull origin main` (최신화) -3. `git cherry-pick <방금_커밋_해시>` (운영 반영) -4. `git push origin main` (운영 서버 배포) -5. `git checkout develop` (작업 브랜치 복귀) - -```bash -# MNG 커밋 후 자동 실행 예시 -cd /home/aweso/sam/mng -git push origin develop -git checkout main && git pull origin main -git cherry-pick abc1234 -git push origin main -git checkout develop -``` - -> **충돌 발생 시**: 사용자에게 알리고 해결 지원 (자동 중단하지 않음) -> **여러 커밋 연속 시**: 마지막 커밋 후 한 번만 실행 (중간 커밋에서는 생략 가능) - -#### API / React 푸시 (트리거 워드 기반) - -> API, React는 기존대로 **사용자가 트리거 워드를 말할 때만** 푸시 실행. - -#### 트리거: "개발서버 푸시" - -사용자가 **"개발서버 푸시"**라고 말하면 다음을 자동 실행: - -1. 각 프로젝트(`api`, `mng`, `react`)에서 `git status` 확인 -2. 커밋되지 않은 변경사항이 있으면 커밋 규칙에 따라 커밋 -3. 현재 브랜치가 `develop`인지 확인 (아니면 `develop`으로 전환) -4. `git push origin develop` 실행 (변경사항 있는 프로젝트만) -5. 결과 요약 출력 - -```bash -# 변경사항 있는 프로젝트만 실행 -cd /home/aweso/sam/api && git push origin develop -cd /home/aweso/sam/mng && git push origin develop -cd /home/aweso/sam/react && git push origin develop -``` - -> Jenkins가 Gitea Webhook으로 개발 서버에 자동 배포한다. - -#### 트리거: "운영서버 푸시" - -사용자가 **"운영서버 푸시"**라고 말하면 다음을 자동 실행: - -> **전제**: develop의 변경사항이 개발 서버에서 테스트 완료된 상태 -> **방식**: **cherry-pick** — 이번 세션에서 작업한 커밋만 골라서 main에 반영 (develop 전체 머지 금지) - -1. 각 프로젝트에서 develop에 미푸시 커밋 확인 → 있으면 먼저 `git push origin develop` -2. **체리픽 대상 커밋 식별** — 이번 세션에서 작업한 커밋 해시 목록을 `git log --oneline`으로 확인하여 사용자에게 표시 -3. 사용자에게 체리픽 대상 커밋 목록을 보여주고 **확인** 받기 -4. `git checkout main` (모든 프로젝트 운영 브랜치: `main`) -5. `git pull origin main` — **최신화 (충돌 방지 핵심 단계)** -6. `git cherry-pick ...` — 확인받은 커밋만 순서대로 적용 -7. 충돌 발생 시 → 사용자에게 알리고 해결 지원 -8. `git push origin main` — Jenkins가 운영 서버에 자동 배포 -9. `git checkout develop` — 작업 브랜치로 복귀 -10. 결과 요약 출력 (체리픽한 커밋 목록 포함) - -```bash -# 예시: mng에서 이번 세션 커밋 2개만 체리픽 -cd /home/aweso/sam/mng - -# 1. develop 미푸시 확인 -git push origin develop - -# 2. 체리픽 대상 확인 (사용자 승인) -git log --oneline # → abc1234, def5678 - -# 3. main 전환 및 최신화 -git checkout main && git pull origin main - -# 4. 선택한 커밋만 체리픽 (시간순 — 오래된 것부터) -git cherry-pick abc1234 def5678 - -# 5. 푸시 및 복귀 -git push origin main -git checkout develop -``` - -> **merge와의 차이**: `git merge develop`은 develop의 모든 커밋을 main에 반영하지만, `git cherry-pick`은 지정한 커밋만 반영한다. 다른 개발자의 미검증 커밋이 운영에 올라가는 문제를 방지한다. -> **주의**: cherry-pick 후 develop에 main을 머지하지 않는다. 동일 변경이 이미 develop에 존재하므로 불필요하다. - -#### 운영서버 푸시 대상 프로젝트 (2026-02-27~) - -| 프로젝트 | 개발서버 푸시 | 운영서버 푸시 | 비고 | -|----------|:----------:|:----------:|------| -| **MNG** | ✅ | ✅ | | -| **API** | ✅ | ✅ | 2026-02-27부터 허용 | -| **React** | ✅ | ❌ 중지 | 개발 완료 시점에 배포 예정 | - -> "운영서버 푸시" 트리거 시 **MNG + API**만 cherry-pick → main push 실행. React는 develop push만 수행. - -#### 푸시 대상 자동 판별 - -| 조건 | 동작 | -|------|------| -| 현재 작업 디렉토리가 특정 프로젝트 | 해당 프로젝트만 푸시 | -| `sam/` 루트이거나 명시 없음 | 3개 프로젝트 모두 확인 후 변경사항 있는 것만 푸시 | -| 사용자가 프로젝트 지정 ("api 개발서버 푸시") | 지정된 프로젝트만 푸시 | - -### Claude Code 설정 파일도 커밋 대상 - -다음 파일들이 변경되면 반드시 커밋: - -| 파일/폴더 | 설명 | 커밋 예시 | -|-----------|------|----------| -| `CLAUDE.md` | 프로젝트 설정 | `docs: CLAUDE.md 규칙 업데이트` | -| `claudedocs/` | Claude 관련 문서 | `docs: 기능 분석 문서 추가` | -| `.claude/settings.json` | Claude 설정 | `chore: Claude 설정 변경` | -| `agents/`, `skills/` | 커스텀 에이전트/스킬 | `feat: [claude] 새 스킬 추가` | - -### 커밋 전 체크리스트 - -- [ ] `./vendor/bin/pint` 실행 (코드 포맷팅, 해당 시) -- [ ] `git diff`로 변경사항 검토 -- [ ] 불필요한 파일 제외 (.env, node_modules 등) -- [ ] 변경된 파일이 있는 저장소에서 git add → git commit -- [ ] CLAUDE.md, claudedocs/, agents/, skills/ 변경 확인 → git commit -- [ ] 커밋 메시지: `type: [scope] 한글 작업내용` 형식 준수 -- [ ] Co-Authored-By 서명 미포함 확인 - ---- - -## 주요 프로젝트 경로 - -| 경로 | 설명 | Git 저장소 | -|------|------|-----------| -| `/home/aweso/sam/mng` | 관리자 웹 (Laravel) | 독립 저장소 | -| `/home/aweso/sam/api` | API 서버 (Laravel) | 독립 저장소 | -| `/home/aweso/sam/react` | 프론트엔드 (Next.js) | 독립 저장소 | - -**각 폴더는 독립적인 Git 저장소입니다. 해당 폴더에서 git 명령을 실행해야 합니다.** - -> **서버 경로 참고**: -> - 개발/운영 서버 모두 `/home/webservice/` 하위에 동일한 구조로 배치 -> - 서버: `/home/webservice/api`, `/home/webservice/mng`, `/home/webservice/react`, `/home/webservice/sales` - ---- - -## 서버 접근 정책 (3단계 분류) - -> **2026-02-21 사고 교훈**: Claude가 서버에 SSH로 접속하여 **설정을 변경**한 결과 502 Bad Gateway 발생. -> → 교훈: **읽기는 허용, 변경은 확인 후, 위험 작업은 금지** - -### 핵심 원칙 - -서버 접근을 **3단계**로 분류하여 관리한다: - -| 단계 | 분류 | 정책 | -|------|------|------| -| 🟢 Level 1 | 읽기 전용 (진단/조회) | **자유 허용** — 사용자 요청 시 즉시 실행 | -| 🟡 Level 2 | 경미한 변경 | **사용자 확인 후 실행** — 실행 전 명령어를 보여주고 승인받기 | -| 🔴 Level 3 | 위험한 변경 | **절대 금지** — 안내만 제공 | - -### 🟢 Level 1: 읽기 전용 (자유 허용) - -사용자가 서버 진단/조회를 요청하면 **즉시 SSH 접속하여 실행** 가능: - -``` -✅ ls, cat, head, tail, less — 파일/디렉토리 조회 -✅ ps aux, top, htop — 프로세스 확인 -✅ df -h, free -m — 디스크/메모리 확인 -✅ git status, git log, git diff — Git 상태 조회 -✅ stat, id, whoami — 권한/소유자 확인 -✅ nginx -t, php -v — 설정 검증 (변경 아닌 테스트만) -✅ tail -f /var/log/*.log — 로그 조회 -✅ systemctl status — 서비스 상태 조회 -✅ crontab -l — 크론 작업 조회 -``` - -### 🟡 Level 2: 경미한 변경 (확인 후 실행) - -실행 전 **명령어를 사용자에게 보여주고 승인**받은 후 실행: - -``` -⚠️ git pull — 코드 업데이트 -⚠️ composer install — 의존성 설치 -⚠️ php artisan migrate — DB 마이그레이션 -⚠️ php artisan config:clear, cache:clear — 캐시 초기화 -⚠️ chmod, chown — 파일 권한 변경 (소규모) -⚠️ systemctl restart — 서비스 재시작 -⚠️ npm install — 패키지 설치 -``` - -**실행 절차:** -1. 실행할 명령어를 먼저 사용자에게 표시 -2. 사용자 승인 확인 -3. 승인 후 실행 - -### 🔴 Level 3: 위험한 변경 (절대 금지 — 안내만) - -Claude가 **절대 직접 실행하지 않으며**, 사용자에게 명령어를 안내만 제공: - -``` -❌ rm -rf, 대량 파일 삭제 -❌ nginx/apache 설정 파일 직접 수정 -❌ /etc/ 하위 시스템 설정 변경 -❌ 서버에서 npm run build (메모리 부족 위험) -❌ DB 직접 수정 (DROP, TRUNCATE, 대량 UPDATE/DELETE) -❌ 방화벽(iptables, ufw) 규칙 변경 -❌ 사용자 계정 생성/삭제/비밀번호 변경 -❌ scp, rsync로 대량 파일 전송 -❌ 프로세스 강제 종료 (kill -9) -❌ 서버 재부팅 -``` - -### 서버 접속 정보 - -| 서버 | 호스트 | 계정 | SSH 접근 | 역할 | -|------|--------|------|---------|------| -| 개발 서버 | `114.203.209.83` | `pro`, `hskwon` | **Claude 가능** | 개발/스테이징 + Jenkins CI/CD + Gitea | -| 운영 서버 | (비공개) | 별도 계정 | **Claude 불가** — 개발팀장만 접근 | 정식 서비스 | - -> **참고**: Jenkins(`114.203.209.83:8080`)와 Gitea(`114.203.209.83:3000`)는 개발 서버에서 운영한다. - -> **운영 서버 정책**: Claude는 운영 서버에 SSH 접속할 수 없다. IP 접근이 제한되어 있으며 개발팀장만 접근 가능하다. 운영 배포는 `git push origin main` → Jenkins 자동 배포로만 이루어지며, 운영 서버 상태 확인이 필요하면 사용자(개발팀장)에게 요청한다. - -### 배포 흐름 (Jenkins CI/CD) - -``` -Claude 역할 Jenkins (자동) 운영 서버 -┌───────────────────┐ ┌──────────────────┐ ┌──────────────┐ -│ 코드 작성/수정 │ │ │ │ │ -│ git add / commit │ │ │ │ │ -│ │─push──→ │ Gitea Webhook │ │ │ -│ │(사용자) │ → Jenkins 빌드 │ │ │ -│ │ │ → Lint/Test │ │ │ -│ │ │ → SSH Deploy ────│──→ │ git pull │ -│ │ │ │ │ composer │ -│ 서버 진단 (L1) │ │ │ │ migrate │ -│ 서버 변경 (L2) │─확인──→ │ │ │ │ -│ 위험 작업 (L3) │─안내──→ │ │ │ (팀장 직접) │ -└───────────────────┘ └──────────────────┘ └──────────────┘ -``` - -> **브랜치 전략**: `develop` → 개발 서버 (자동 배포), `main` → 운영 서버 (PR 머지 + 팀장 승인) - -### 체크리스트 (서버 작업 시) - -- [ ] Level 1 (읽기): 사용자 요청 시 즉시 실행 -- [ ] Level 2 (변경): 명령어를 보여주고 승인 후 실행 -- [ ] Level 3 (위험): 절대 직접 실행하지 않고 안내만 제공 -- [ ] 사고 방지: 설정 파일 수정, 서비스 중단 가능성 있는 작업은 Level 3 - ---- - -## React 빌드/배포 정책 (필수 규칙) - -> **경고: React(Next.js) 빌드를 운영 서버에서 직접 실행하지 않는다!** - -### 배경 - -개발 서버(2코어, 3.8GB RAM + Swap 4GB)에서 Jenkins가 React 빌드를 수행한다. -Jenkins 빌드 실패 시 로컬(WSL)에서 빌드 후 결과물을 서버에 배포한다(fallback). - -### 금지 사항 - -``` -❌ 운영 서버에서 npm run build 실행 금지 -❌ 서버 SSH 접속 후 빌드 명령 실행 금지 -❌ Claude가 직접 npm run build 실행 금지 (로컬 포함) -``` - -### 빌드/배포 방법 (Jenkins 자동화) - -``` -Claude 역할 Jenkins (자동) 운영 서버 -┌─────────────────┐ ┌──────────────────┐ ┌──────────────┐ -│ 코드 작성/수정 │ │ Checkout │ │ │ -│ git commit │─push──→ │ Install + Lint │ │ │ -│ │(사용자) │ Build (Next.js) │ │ │ -│ │ │ Package (tar.gz) │──scp→ │ 압축 해제 │ -│ │ │ │ │ node 재시작 │ -└─────────────────┘ └──────────────────┘ └──────────────┘ -``` - -> **Fallback**: Jenkins 빌드 실패(OOM) 시 로컬에서 `react/deploy.sh`로 수동 배포 - -### 빌드가 필요한 상황 - -사용자에게 다음과 같이 안내한다: - -``` -React 코드가 변경되었습니다. git push 후 Jenkins가 자동 배포합니다. -(Jenkins 실패 시 로컬에서 deploy.sh로 수동 배포해주세요.) -``` - ---- - -## 데이터베이스 아키텍처 (필수 규칙) - -> **경고: 이 규칙을 반드시 준수하세요!** - -### 핵심 원칙 - -**모든 데이터베이스 관련 파일은 API 프로젝트에서만 관리합니다.** - -| 항목 | API (`/home/aweso/sam/api`) | MNG (`/home/aweso/sam/mng`) | -|------|----------------------------|----------------------------| -| 마이그레이션 | ✅ 여기에 생성 | ❌ 생성 금지 | -| 시더 | ✅ 여기에 생성 | ⚠️ MNG 전용만 허용 | -| 팩토리 | ✅ 여기에 생성 | ❌ 생성 금지 | - -### 금지 사항 - -``` -❌ /home/aweso/sam/mng/database/migrations/ 에 파일 생성 금지 -❌ MNG에서 테이블 생성/수정 마이그레이션 작성 금지 -``` - -### 허용 사항 - -``` -✅ /home/aweso/sam/api/database/migrations/ 에 모든 마이그레이션 생성 -✅ MNG에서는 MngMenuSeeder 같은 MNG 전용 시더만 허용 -``` - -### 마이그레이션 실행 - -```bash -# 로컬: 마이그레이션은 반드시 API 컨테이너에서 실행 -docker exec sam-api-1 php artisan migrate - -# 개발 서버: Docker 없음, 직접 실행 -cd /home/webservice/api && php artisan migrate - -# 운영 서버: --force 플래그 필수 (production 환경) -cd /home/webservice/api && php artisan migrate --force - -# MNG에서 마이그레이션 실행 금지 (로컬/서버 모두) -``` - -### DB 환경 분리 - -| 환경 | DB명 | 호스트 | 용도 | -|------|------|--------|------| -| 로컬 (Docker) | `samdb` | `sam-mysql-1:3306` | 개발/테스트 | -| 개발 서버 | `samdb` | `localhost` | 스테이징 | -| 운영 서버 | `sam_prod` | `localhost` | 정식 서비스 | -| 통계 DB | `sam_stat` | 동일 서버 | StatMonitorService 전용 | - -> **참고**: `sam_stat`은 API/MNG 모두 `config/database.php`의 별도 connection으로 접속한다. - -### 이유 - -- MNG: 프론트엔드/관리자 화면 담당 (컨트롤러, 뷰, 라우트) -- API: 백엔드/데이터베이스 담당 (마이그레이션, 모델 정의, API) -- 단일 DB를 두 프로젝트가 공유하므로 마이그레이션은 한 곳에서만 관리 - -### 테이블 생성/수정 시 options JSON 컬럼 정책 (필수) - -> **경고: 테이블 생성/수정, 마이그레이션 작성, 모델 생성 시 반드시 아래 정책 문서를 먼저 읽고 준수하세요!** -> **정책 문서**: `/home/aweso/sam/docs/standards/options-column-policy.md` - -**핵심 원칙**: FK/조인키만 전용 컬럼, 나머지 속성은 `options` JSON에 저장 - -| 전용 컬럼 (일반 컬럼) | options JSON | -|---|---| -| FK/조인키 (다른 테이블 참조) | 테넌트별로 다를 수 있는 확장 속성 | -| WHERE/ORDER BY 자주 사용 필드 | 선택적(nullable) 부가 정보 | -| UNIQUE 제약 필드 | 구조가 유동적인 데이터 | -| INDEX 필요한 고빈도 조회 필드 | 드롭다운 선택지 목록 | -| 집계(SUM/AVG) 대상 | 이력/스냅샷성 데이터 | - -**필수 준수 사항**: - -``` -✅ 모든 비즈니스 테이블에 $table->json('options')->nullable() 포함 -✅ 모델 cast: 'options' => 'array' (❌ 'json' 금지) -✅ 모델에 getOption() / setOption() 헬퍼 메서드 추가 -✅ options 키 3개 이상 시 OPTION_* 상수 정의 -✅ options 수정 시 setOption() 사용 (전체 덮어쓰기 금지) -✅ API 응답 노출 시 accessor + $appends 추가 -``` - -**작업 전 체크리스트**: -- [ ] 정책 문서(`docs/standards/options-column-policy.md`) 읽기 -- [ ] 새 필드가 전용 컬럼인지 options인지 판단 (판단 흐름도 참고) -- [ ] 마이그레이션에 `options` JSON 컬럼 포함 확인 -- [ ] 모델에 `'options' => 'array'` cast + 헬퍼 메서드 포함 확인 - ---- - -## 메뉴 관리 규칙 (필수) - -> **경고: 메뉴 시더(Seeder)를 절대 실행하지 마세요!** - -### 배경 - -메뉴 시더 실행 시 부서별 권한 설정(permission_overrides)이 초기화되는 문제가 반복 발생합니다. -메뉴 ID가 변경되면 기존 부서-메뉴 권한 매핑이 깨지기 때문입니다. - -### 금지 사항 - -``` -❌ php artisan db:seed --class=MngMenuSeeder 실행 금지 -❌ php artisan db:seed --class=*MenuSeeder 실행 금지 -❌ 메뉴 시더 파일 생성 금지 -❌ 메뉴 데이터를 일괄 삭제 후 재생성하는 방식 금지 -``` - -### 메뉴 변경 시 올바른 절차 - -메뉴 추가/수정/삭제/이동이 필요할 때는 **사용자에게 수동 실행 안내**를 제공합니다: - -1. **tinker 명령어를 안내** (사용자가 직접 실행) -2. **또는 SQL 쿼리를 안내** (사용자가 phpMyAdmin 등에서 직접 실행) -3. **절대 시더를 만들어 실행하지 않음** - -### 안내 예시 - -``` -메뉴를 추가하려면 아래 명령을 서버에서 실행해 주세요: - -# 개발 서버 -ssh pro@114.203.209.83 "cd /home/webservice/mng && php artisan tinker --execute=\" -App\\Models\\Commons\\Menu::create([ - 'tenant_id' => 1, - 'parent_id' => <부모ID>, - 'name' => '새 메뉴', - 'url' => '/new-menu', - 'icon' => 'icon-name', - 'sort_order' => 1, - 'is_active' => true, -]); -\"" - -# 운영 서버 (동일 경로, 서버 주소만 변경) -ssh <운영계정>@<운영서버IP> "cd /home/webservice/mng && php artisan tinker --execute=\"...동일...\"" -``` - -### 체크리스트 (메뉴 변경 요청 시) - -- [ ] 시더 파일 생성하지 않음 -- [ ] 시더 실행하지 않음 -- [ ] tinker 또는 SQL로 개별 레코드만 수정 -- [ ] 변경 후 부서 권한 설정이 유지되는지 확인 - ---- - -## 실행 환경 (필수 인지) - -> **중요: 로컬 / 개발 서버 / 운영 서버의 환경이 다릅니다!** - -### 환경 비교 (3-Tier) - -| 항목 | 로컬 (WSL) | 개발 서버 | 운영 서버 | -|------|-----------|----------|----------| -| **구성 방식** | Docker 컨테이너 | Bare-metal (네이티브) | Bare-metal (네이티브) | -| **PHP** | 컨테이너 내부 (8.4) | 직접 설치 (8.4) | 직접 설치 (8.4) | -| **MySQL** | 컨테이너 (sam-mysql-1) | 직접 설치 (8.0) | 직접 설치 (8.0) | -| **Nginx** | 컨테이너 (sam-nginx-1) | 직접 설치 | 직접 설치 | -| **명령 실행** | `docker exec` 필요 | 직접 실행 | 직접 실행 | -| **서버 IP** | localhost | `114.203.209.83` | (신규, 미확정) | -| **추가 서비스** | — | Jenkins, Gitea | — | -| **DB** | `samdb` | `samdb` | `sam_prod` | - -> **배경**: 서버는 Docker가 무거워서 PHP, Nginx, MySQL 등을 네이티브로 설치하여 운영한다. - -### 로컬 환경 (Docker) - -PHP, Laravel, Node.js 등이 **Docker 컨테이너 안에** 설치되어 있다. -로컬 PC(WSL)에는 이런 도구들이 없으므로, 반드시 Docker 컨테이너를 통해 실행한다. - -``` -로컬 PC (WSL) -└── Docker - ├── sam-mng-1 ← PHP + Laravel (MNG 앱) - ├── sam-api-1 ← PHP + Laravel (API 앱) - ├── sam-mysql-1 ← MySQL DB - └── sam-nginx-1 ← Nginx 웹서버 -``` - -### 서버 환경 (Bare-metal — 개발/운영 동일 구조) - -서버에는 Docker가 없다. PHP 8.4, Nginx, MySQL 8.0이 직접 설치되어 있다. - -``` -개발 서버 (114.203.209.83) 운영 서버 (신규) -├── Nginx ├── Nginx -├── PHP-FPM (3 pools) ├── PHP-FPM (3 pools) -│ ├── api.sock │ ├── api.sock -│ ├── mng.sock │ ├── mng.sock -│ └── sales.sock │ └── sales.sock -├── MySQL 8.0 (samdb) ├── MySQL 8.0 (sam_prod) -├── Supervisor ├── Supervisor -│ ├── sam-api-worker (x1) │ ├── sam-api-worker (x1) -│ ├── sam-mng-worker (x2) │ ├── sam-mng-worker (x2) -│ └── sam-api-scheduler │ └── sam-api-scheduler -├── Node.js (React SSR :3000) ├── Node.js (React SSR :3000) -├── Jenkins (:8080) │ -├── Gitea (:3000) │ -├── /home/webservice/api ├── /home/webservice/api -├── /home/webservice/mng ├── /home/webservice/mng -├── /home/webservice/react ├── /home/webservice/react -└── /home/webservice/sales └── /home/webservice/sales -``` - -### 도메인 매핑 - -| 서비스 | 로컬 (Docker) | 개발 서버 | 운영 서버 | -|--------|--------------|----------|----------| -| React (사용자) | `dev.sam.kr` | `dev.codebridge-x.com` | `codebridge-x.com` | -| API | `api.sam.kr` | `api.dev.codebridge-x.com` | `api.codebridge-x.com` | -| MNG (관리자) | `mng.sam.kr` | `admin.codebridge-x.com` | `mng.codebridge-x.com` | -| Sales | `sales.sam.kr` | `sales.dev.codebridge-x.com` | `sales.codebridge-x.com` | -| 5130 (레거시) | `5130.sam.kr` | — | — | - -### 명령어 비교 (로컬 vs 개발 vs 운영) - -| 작업 | 로컬 (Docker) | 개발/운영 서버 (네이티브) | -|------|--------------|-------------------------| -| artisan 실행 | `docker exec sam-api-1 php artisan <명령>` | `cd /home/webservice/api && php artisan <명령>` | -| composer 실행 | `docker exec sam-api-1 composer install` | `cd /home/webservice/api && composer install` | -| 마이그레이션 | `docker exec sam-api-1 php artisan migrate` | 개발: `php artisan migrate` / 운영: `php artisan migrate --force` | -| 캐시 클리어 | `docker exec sam-mng-1 php artisan cache:clear` | `cd /home/webservice/mng && php artisan cache:clear` | -| Queue 재시작 | — | `sudo supervisorctl restart sam-api-worker:*` | - -### 로컬 Docker 명령어 패턴 - -```bash -# MNG 앱에서 artisan 명령 실행 -docker exec sam-mng-1 php artisan <명령어> - -# API 앱에서 artisan 명령 실행 -docker exec sam-api-1 php artisan <명령어> - -# 예시: 시더 실행 -docker exec sam-mng-1 php artisan db:seed --class=MngMenuSeeder - -# 예시: 마이그레이션 실행 (API에서만!) -docker exec sam-api-1 php artisan migrate - -# 예시: 캐시 클리어 -docker exec sam-mng-1 php artisan cache:clear -``` - -### 체크리스트 (명령 실행 시) - -- [ ] **로컬**: `php artisan` → `docker exec sam-mng-1 php artisan` 또는 `sam-api-1` 사용 -- [ ] **로컬**: `composer` → `docker exec sam-mng-1 composer` 또는 `sam-api-1` 사용 -- [ ] **서버**: `php artisan`, `composer` 직접 실행 (Docker 없음) -- [ ] **운영 서버 마이그레이션**: `--force` 플래그 필수 -- [ ] **마이그레이션은 반드시 API에서 실행** (로컬: `docker exec sam-api-1`, 서버: 직접) - ---- - -## 공동 개발 워크플로우 (필수) - -> **중요: 코드를 pull 받은 후 반드시 필요한 명령을 실행하세요!** - -### 브랜치 전략 - -| 브랜치 | 배포 대상 | 트리거 워드 | Jenkins 배포 | -|--------|----------|------------|-------------| -| `feature/*` | — | — | — | -| `develop` | 개발 서버 (`dev.codebridge-x.com`) | **"개발서버 푸시"** | Push 시 자동 | -| `main` | 운영 서버 (`codebridge-x.com`) | **"운영서버 푸시"** | Push 시 자동 | - -``` -로컬 작업 → develop push → 개발 서버 테스트 → main merge+push → 운영 서버 - ("개발서버 푸시") ("운영서버 푸시") -``` - -### 브랜치 동기화 규칙 (필수) - -> **cherry-pick 방식에서는 develop→main 자동 동기화가 불필요하다.** -> main에 직접 변경이 발생한 경우에만 develop에 동기화한다. - -| 상황 | 조치 | -|------|------| -| "운영서버 푸시" 완료 후 | 동기화 불필요 (cherry-pick은 develop에 이미 존재하는 커밋) | -| main에 **직접** 변경 발생 시 (hotfix 등) | develop에서 `git merge main` 실행 | -| develop에서 새 작업 시작 전 | `git pull origin develop`으로 최신화 확인 | - -``` -develop ──작업──→ develop push (개발서버) - │ - └── cherry-pick 선택 커밋 ──→ main push (운영서버) -``` - -### 로컬 환경 (Docker) 업데이트 - -```bash -# 1. 코드 받기 (WSL에서 실행) -cd /home/aweso/sam/api -git pull - -cd /home/aweso/sam/mng -git pull - -# 2. 의존성 업데이트 (composer.json 변경 시) -docker exec sam-api-1 composer install -docker exec sam-mng-1 composer install - -# 3. DB 마이그레이션 (API에서만!) -docker exec sam-api-1 php artisan migrate - -# 4. 캐시 클리어 (설정 변경 시) -docker exec sam-api-1 php artisan config:clear -docker exec sam-mng-1 php artisan config:clear -``` - -### 개발 서버 업데이트 (자동) - -> `develop` 브랜치에 Push 시 Gitea Webhook → Jenkins가 자동으로 배포한다. -> 수동 배포가 필요한 경우: - -```bash -# API 프로젝트 -cd /home/webservice/api -git pull origin develop -composer install -php artisan migrate -php artisan config:clear - -# MNG 프로젝트 (마이그레이션 없음) -cd /home/webservice/mng -git pull origin develop -composer install -php artisan config:clear -``` - -### 운영 서버 배포 (Jenkins 자동화) - -> `main` 브랜치에 PR 머지 시 Jenkins가 자동으로 배포한다. -> 수동 배포는 **비상 절차**로만 사용한다. - -```bash -# 비상 수동 배포 (Jenkins 장애 시에만) -# API 프로젝트 -cd /home/webservice/api -git pull origin main -composer install --no-dev --optimize-autoloader -php artisan migrate --force -php artisan config:clear && php artisan cache:clear && php artisan route:cache && php artisan view:cache -sudo supervisorctl restart sam-api-worker:* - -# MNG 프로젝트 -cd /home/webservice/mng -git pull origin master -composer install --no-dev --optimize-autoloader -php artisan config:clear && php artisan cache:clear && php artisan view:cache -sudo supervisorctl restart sam-mng-worker:* -``` - -### 요약 표 - -| 작업 | 로컬 (Docker) | 개발 서버 | 운영 서버 | -|------|--------------|----------|----------| -| 배포 방식 | 수동 | Jenkins 자동 (develop push) | Jenkins 자동 (main push) | -| git pull | WSL에서 직접 | Jenkins 자동 | Jenkins 자동 | -| composer install | `docker exec sam-api-1 composer install` | Jenkins 자동 | `--no-dev --optimize-autoloader` | -| migrate | `docker exec sam-api-1 php artisan migrate` | Jenkins 자동 | `--force` 플래그 포함 | -| config:clear | `docker exec sam-api-1 php artisan config:clear` | Jenkins 자동 | `route:cache` + `view:cache` 포함 | - -### 체크리스트 (pull 후) - -- [ ] API: `git pull` → `composer install` → `php artisan migrate` → `config:clear` -- [ ] MNG: `git pull` → `composer install` → `config:clear` (마이그레이션 없음) -- [ ] 운영 배포: `main`에 PR 머지 → Jenkins 자동 처리 (수동 금지) - ---- - -## 사용 가능한 Agents - -`~/.claude/agents/` 폴더에 있는 에이전트들: - -### 코드 품질 & 개발 - -| Agent | 모델 | 설명 | 출처 | -|-------|------|------|------| -| `code-reviewer` | sonnet | 코드 리뷰 (품질/보안/유지보수성), 메모리 학습 지원 | 공식 문서 패턴 | -| `debugger` | sonnet | 에러/테스트 실패 근본 원인 분석 및 수정 | 공식 문서 패턴 | -| `test-runner` | haiku | 테스트 실행 및 결과 분석/요약 | 커뮤니티 인기 | -| `security-auditor` | sonnet | OWASP Top 10 기반 보안 취약점 감사 | 커뮤니티 인기 | -| `performance-optimizer` | sonnet | N+1 쿼리, 알고리즘, 캐싱 최적화 | 커뮤니티 인기 | -| `refactoring-agent` | sonnet | 코드 구조 개선, SOLID 원칙, DRY 위반 제거 | 커뮤니티 인기 | -| `laravel-expert` | sonnet | Laravel 전문가 (SAM 프로젝트 환경 인지) | 커스텀 | - -### 워크플로우 & 문서 - -| Agent | 모델 | 설명 | 출처 | -|-------|------|------|------| -| `git-manager` | haiku | Git 브랜치/커밋/머지/PR 관리 | 커뮤니티 인기 | -| `doc-writer` | haiku | API 문서, README, 기술 가이드 작성 | 커뮤니티 인기 | -| `research-agent` | sonnet | 웹 리서치 및 자료 조사 | 기존 | -| `organizer-agent` | - | 프로젝트 구조화 및 정리 | 기존 | -| `proposal-agent` | - | 제안서 작성 | 기존 | - ---- - -## 사용 가능한 Skills - -`~/.claude/skills/` 폴더에 있는 스킬들 (슬래시 명령어로 사용): - -### 문서/프레젠테이션 - -| Skill | 설명 | -|-------|------| -| `pptx-skill` | PowerPoint 생성 | -| `ppt-auto-generator` | 마크다운/텍스트에서 PPT 생성 | -| `pdf-template-skill` | PDF 템플릿 분석/생성 | -| `text-analyzer-skill` | 텍스트 분석 및 PDF 구조 매핑 | -| `proposal-skill` | 제안서 생성 | -| `storyboard-generator` | 스토리보드 생성 | -| `design-skill` | 프레젠테이션 HTML 디자인 | - -### 코드 분석/시각화 - -| Skill | 설명 | -|-------|------| -| `code-flow-web-report` | 웹 앱 런타임 흐름 시각화 리포트 | -| `code-flow-web-doc-generator` | 소스 코드 호출/데이터 흐름 다이어그램 HTML 생성 | -| `codebase-analysis-web-report` | 코드베이스 아키텍처 인터랙티브 HTML 리포트 | -| `uml-generator` | UML 다이어그램 생성 | - -### 코드 품질 (levnikolaevich/claude-code-skills) - -| Skill | 설명 | 출처 | -|-------|------|------| -| `code-bug-finder` | 버그 자동 탐지 및 보고서 생성 | 기존 | -| `code-refactoring` | 리팩토링 권장사항/성능 분석/코드 패치 | 기존 | -| `code-commenter` | 소스 코드에 이해하기 쉬운 주석 추가 | 기존 | -| `async-await-keyword-fixer` | JS/TS 누락된 async/await 수정 | 기존 | -| `code-quality-checker` | DRY/KISS/YAGNI 위반 탐지 | levnikolaevich | -| `code-quality-auditor` | 코드 복잡도, 매직넘버 분석 | levnikolaevich | -| `code-principles-auditor` | DRY/KISS/YAGNI, TODO, DI 패턴 검사 | levnikolaevich | -| `dead-code-auditor` | 미사용 코드 탐지 | levnikolaevich | -| `build-auditor` | 컴파일러/타입 에러 검사 | levnikolaevich | -| `concurrency-auditor` | 레이스 컨디션 탐지 | levnikolaevich | -| `layer-boundary-auditor` | 레이어 위반, I/O 격리 검사 | levnikolaevich | -| `observability-auditor` | 로깅, 메트릭 적절성 검사 | levnikolaevich | -| `query-efficiency-auditor` | DB 쿼리 효율성 분석 | levnikolaevich | -| `dependencies-auditor` | 오래된 패키지, CVE 취약점 검사 | levnikolaevich | -| `regression-checker` | 기존 테스트 실행으로 사이드이펙트 탐지 | levnikolaevich | -| `story-quality-gate` | 코드리뷰 + 테스트 2단계 품질 검증 | levnikolaevich | - -### 테스트/커버리지 - -| Skill | 설명 | 출처 | -|-------|------|------| -| `app-comprehensive-test-generator` | 테스트 시나리오 생성/실행, QA 리포트 | 기존 | -| `coverage-improvement-planner` | 테스트 커버리지 분석 및 개선 계획 | 기존 | -| `test-coverage-auditor` | 테스트 커버리지 측정/분석 | levnikolaevich | -| `test-isolation-auditor` | 테스트 독립성/격리 검사 | levnikolaevich | -| `webapp-testing` | Playwright 기반 웹 앱 UI 테스트 | anthropics 공식 | - -### 보안 (Trail of Bits) - -| Skill | 설명 | 출처 | -|-------|------|------| -| `security-auditor` | 시크릿 노출, Injection, XSS 탐지 | levnikolaevich | -| `static-analysis` | CodeQL/Semgrep/SARIF 정적 분석 (3개 하위 스킬) | Trail of Bits | -| `insecure-defaults` | 위험한 기본 설정, 하드코딩 자격증명 탐지 | Trail of Bits | -| `sharp-edges` | 에러 유발 API, 위험한 디자인 패턴 탐지 | Trail of Bits | -| `differential-review` | 보안 중심 코드 변경 리뷰 | Trail of Bits | - -### 디버깅/로깅 - -| Skill | 설명 | -|-------|------| -| `system-debug-logger` | 에러/예외 자동 캡처 디버그 로깅 | -| `node-debug-logging-middleware` | Node.js Express/Koa 디버깅 로그 미들웨어 | - -### 프론트엔드/UI - -| Skill | 설명 | 출처 | -|-------|------|------| -| `frontend-design` | 프론트엔드 디자인 품질 향상 (AI slop 방지) | anthropics 공식 | -| `flutter-ux-hardening` | Flutter 앱 UI/UX 강화 | 기존 | -| `웹문서` | SAM 프로젝트 웹문서 디자인 표준 | 기존 | - -### 유틸리티 - -| Skill | 설명 | -|-------|------| -| `svg-precision` | SVG 결정론적 생성, 검증(validate), PNG 렌더링. 다이어그램/차트/아이콘에 사용 | -| `duplicate-file-cleaner` | 중복 이미지/미디어 파일 정리 | -| `npm-release-manager` | NPM 패키지 배포 자동화 | - -**사용 방법**: `/skill-name` 형식으로 호출 (예: `/code-quality-checker`) - ---- - -## 문서 작성 규칙 (개발팀 협약 - 필수 준수) - -> **경고: 개발자들이 `sam/docs`의 문서 작성 기법을 준용하기로 협약했습니다. 모든 문서 작성 시 반드시 따르세요!** - -### 참조 경로 - -- **인덱스**: `/home/aweso/sam/docs/INDEX.md` (전체 문서 목록 및 폴더 구조) -- **작업 전 확인**: 작업 유형에 맞는 문서를 `INDEX.md`에서 찾아 먼저 읽고 시작 - -### 폴더 선택 기준 (의미 기반 분류) - -| 폴더 | 질문 | 설명 | -|------|------|------| -| `plans/` | "무슨 작업을 할 것인가?" | 임시 개발 계획 (완료 후 삭제) | -| `standards/` | "어떻게 코드를 작성할 것인가?" | 코딩 컨벤션, 스타일 가이드 | -| `architecture/` | "왜 이렇게 설계하는가?" | 시스템 설계, 아키텍처 결정 | -| `rules/` | "무엇이 유효한 데이터인가?" | 비즈니스 규칙, 검증 규칙 | -| `specs/` | "무엇을 구현할 것인가?" | 기술 스펙, DB 스키마 | -| `guides/` | "어떻게 구현할 것인가?" | 단계별 구현 매뉴얼 | -| `features/` | 기능별 상세 | 기능 단위 심층 문서 | -| `changes/` | "무엇이 변경되었는가?" | 완료된 변경 이력 | - -### 파일명 규칙 - -- **일반 문서**: `kebab-case.md` (소문자 + 하이픈) 예: `api-rules.md`, `item-policy.md` -- **변경 이력**: `YYYYMMDD_short_description.md` 예: `20260109_handover_report_api.md` -- **폴더 인덱스**: `README.md` (대문자) -- **크기 목표**: 10KB 이하 -- **새 문서 작성 시**: 반드시 `docs/INDEX.md`에 추가 - -### 문서 구조 템플릿 - -#### 정책/규칙 문서 (`rules/`, `standards/`) - -```markdown -# 제목 - -> **작성일**: YYYY-MM-DD -> **상태**: 설계 확정 - ---- - -## 1. 개요 -### 1.1 목적 -### 1.2 핵심 원칙 - ---- - -## 2. 테이블 구조 (해당 시) -### 2.1 ERD 개요 - ---- - -## N. 비즈니스 규칙 -### N.1 검증 규칙 - ---- - -## N. API 엔드포인트 - ---- - -## 관련 문서 - ---- - -**최종 업데이트**: YYYY-MM-DD -``` - -#### 변경 이력 문서 (`changes/`) - -```markdown -# 변경 내용 요약 - -**날짜:** YYYY-MM-DD -**작업자:** Claude Code - -## 변경 개요 - -## 수정된 파일 -| 파일 | 변경 내용 | -|------|----------| - -## 상세 변경 사항 - -## 테스트 체크리스트 -- [x] 완료 항목 -- [ ] 미완료 항목 - -## 관련 문서 -``` - -### 작성 스타일 규칙 - -| 항목 | 규칙 | -|------|------| -| **언어** | 한글 기본, 코드/경로/기술 식별자만 영어 | -| **어조** | 서술형 ("X를 해야 한다" 아닌 "X 한다") | -| **경고** | `> **경고: ...**` 블록인용 형식 | -| **금지/필수** | `❌` 금지, `✅` 필수 접두사 | -| **우선순위** | `🔴 필수`, `🟡 중요`, `🟢 권장` | -| **섹션 번호** | `## 1.`, `### 1.1` 번호 매기기 | -| **규칙 번호** | R1, R2, R3... 순차 라벨 | -| **코드 블록** | 반드시 언어 지정 (```php, ```bash, ```json, ```sql) | -| **인라인 코드** | 파일 경로, 메서드명, 변수명, 컬럼명에 백틱 | -| **다이어그램** | `┌─┐│└─┘` 박스 문자, `→` 화살표 사용 | -| **구분선** | `---` 주요 섹션 사이마다 | -| **테이블** | API: `| Method | Path | 설명 |`, 필드: `| 필드 | 타입 | 설명 |` | - -### plans/ 워크플로우 - -1. 개발 계획 문서를 `plans/`에 작성 -2. 작업 진행 -3. 완료 후 결과물을 해당 폴더(`features/`, `changes/` 등)에 정리 -4. plan 문서 삭제 - -### 체크리스트 (문서 작성 시) - -- [ ] 적절한 폴더에 배치 (위 폴더 선택 기준 참고) -- [ ] `kebab-case.md` 파일명 사용 -- [ ] 문서 구조 템플릿 준수 -- [ ] 한글 기본, 기술 용어만 영어 -- [ ] 코드 블록에 언어 지정 -- [ ] `docs/INDEX.md`에 새 문서 등록 -- [ ] 10KB 이하 크기 유지 - ---- - -## PPT / 프레젠테이션 제작 규칙 (필수 준수) - -> **경고: 모든 프레젠테이션 및 문서 제작 시 반드시 따르세요!** - -### 회사 정보 - -| 항목 | 값 | -|------|------| -| **공식 회사명** | **(주)코드브릿지엑스** | -| **서비스명** | **SAM** (Smart Automation Management) | -| **푸터 표기 예시** | `SAM 서비스 요금 안내 | (주)코드브릿지엑스` | - -### 금지 사항 - -``` -❌ "주일/경동" — 문서, 슬라이드, 푸터 어디에도 사용 금지 -❌ "주일", "경동" 단독 사용 금지 -❌ 내부 제조사(주일/경동) 이름을 외부 문서에 노출 금지 -``` - -> **배경**: 주일/경동은 SAM을 기반으로 만든 내부 제조업체 이름이며, 대외 문서에 노출되어서는 안 된다. -> 모든 대외 문서의 회사명은 **(주)코드브릿지엑스**를 사용한다. - -### SAM BI (Brand Identity) 이미지 - -**프로젝트 내 경로**: `/home/aweso/sam/docs/assets/bi/` - -| 파일 | 용도 | 배경 | -|------|------|------| -| `sam_bi_black.png` | 밝은 배경 슬라이드 | 투명 배경, 검정 로고 | -| `sam_bi_white.png` | 다크 배경 슬라이드 | 투명 배경, 흰색 로고 | -| `sam_bi_blue.png` | 청색 테마 슬라이드 | 투명 배경, 파란 로고 | -| `sam_bi_green.png` | 녹색 테마 슬라이드 | 녹색 배경, 흰색 로고 | -| `sam_bi_red.png` | 적색/대외비 슬라이드 | 적색 배경, 흰색 로고 | -| `sam_bi_orange.png` | 주황 포인트 슬라이드 | 주황 배경, 흰색 로고 | -| `sam_bi_purple.png` | 보라 테마 슬라이드 | 보라 배경, 흰색 로고 | - -### PPT 슬라이드 제작 시 적용 규칙 - -1. **표지(slide-01)에 BI 로고 필수** — 배경색에 맞는 BI 이미지 사용 -2. **푸터에 회사명**: `(주)코드브릿지엑스` (주일/경동 절대 금지) -3. **BI 로고 + "SAM" 텍스트** 조합 사용 권장 -4. **배경색별 BI 선택**: - - 다크 배경 → `sam_bi_white.png` - - 밝은 배경 → `sam_bi_black.png` - - 테마 컬러 배경 → 해당 색상 BI (green, blue, red 등) - -### 체크리스트 (PPT 제작 시) - -- [ ] 회사명: (주)코드브릿지엑스 사용 -- [ ] "주일/경동" 미포함 확인 -- [ ] 표지에 SAM BI 로고 포함 -- [ ] 푸터에 (주)코드브릿지엑스 표기 -- [ ] 배경색에 맞는 BI 색상 선택 diff --git a/INDEX.md b/INDEX.md index 580176b..f3b933c 100644 --- a/INDEX.md +++ b/INDEX.md @@ -141,6 +141,7 @@ DB 도메인별: | [card-vehicle/README.md](features/card-vehicle/README.md) | 법인카드·차량 | | [settlement/README.md](features/settlement/README.md) | 정산 | | [barobill-kakaotalk/README.md](features/barobill-kakaotalk/README.md) | 바로빌 카카오톡 | +| [quality-management/README.md](features/quality-management/README.md) | 품질관리 (제품검사, 실적신고) | --- diff --git a/brochure/v1/sam-brochure-1page.pptx b/brochure/v1/sam-brochure-1page.pptx index af863d1..5fe19a4 100644 Binary files a/brochure/v1/sam-brochure-1page.pptx and b/brochure/v1/sam-brochure-1page.pptx differ diff --git a/brochure/v1/sam-brochure-2page.pptx b/brochure/v1/sam-brochure-2page.pptx index 26a7357..016084a 100644 Binary files a/brochure/v1/sam-brochure-2page.pptx and b/brochure/v1/sam-brochure-2page.pptx differ diff --git a/brochure/v1/slides/brochure-1page.html b/brochure/v1/slides/brochure-1page.html index 6e54695..8eea8ab 100644 --- a/brochure/v1/slides/brochure-1page.html +++ b/brochure/v1/slides/brochure-1page.html @@ -197,10 +197,12 @@
                                      -

                                      SAM

                                      -

                                      www.sam.it.kr

                                      +

                                      (주)코드브릿지엑스

                                      +

                                      www.codebridge-x.com

                                      +

                                      무료 데모 및 상담

                                      +

                                      contact@codebridge-x.com

                                      diff --git a/brochure/v1/slides/brochure-2page-back.html b/brochure/v1/slides/brochure-2page-back.html index fbd68ab..a285d16 100644 --- a/brochure/v1/slides/brochure-2page-back.html +++ b/brochure/v1/slides/brochure-2page-back.html @@ -209,17 +209,19 @@
                                      +

                                      무료 데모를 신청하세요

                                      귀사에 최적화된 맞춤 데모를 제공합니다

                                      -

                                      www.sam.it.kr

                                      +

                                      contact@codebridge-x.com

                                      +

                                      www.codebridge-x.com

                                      -

                                      SAM — Smart Automation Management

                                      +

                                      (주)코드브릿지엑스 | SAM - Smart Automation Management

                                      \ No newline at end of file diff --git a/brochure/v1/slides/brochure-2page-front.html b/brochure/v1/slides/brochure-2page-front.html index ae770d9..fe6da03 100644 --- a/brochure/v1/slides/brochure-2page-front.html +++ b/brochure/v1/slides/brochure-2page-front.html @@ -110,8 +110,8 @@
                                      -

                                      SAM

                                      -

                                      www.sam.it.kr

                                      +

                                      (주)코드브릿지엑스

                                      +

                                      www.codebridge-x.com

                                      뒷면에서 상세 기능과 가격을 확인하세요

                                      diff --git a/brochure/v2/sam-brochure-v2-dashboard-1page.pptx b/brochure/v2/sam-brochure-v2-dashboard-1page.pptx index 929effc..d5fb878 100644 Binary files a/brochure/v2/sam-brochure-v2-dashboard-1page.pptx and b/brochure/v2/sam-brochure-v2-dashboard-1page.pptx differ diff --git a/brochure/v2/sam-brochure-v2-dashboard-2page.pptx b/brochure/v2/sam-brochure-v2-dashboard-2page.pptx index dd6039a..315707d 100644 Binary files a/brochure/v2/sam-brochure-v2-dashboard-2page.pptx and b/brochure/v2/sam-brochure-v2-dashboard-2page.pptx differ diff --git a/brochure/v2/slides/brochure-dashboard-1page.html b/brochure/v2/slides/brochure-dashboard-1page.html index 8e1c3cb..eca646a 100644 --- a/brochure/v2/slides/brochure-dashboard-1page.html +++ b/brochure/v2/slides/brochure-dashboard-1page.html @@ -248,10 +248,12 @@
                                      -

                                      SAM

                                      -

                                      www.sam.it.kr

                                      +

                                      (주)코드브릿지엑스

                                      +

                                      www.codebridge-x.com

                                      +

                                      무료 데모 신청

                                      +

                                      contact@codebridge-x.com

                                      diff --git a/brochure/v2/slides/brochure-dashboard-back.html b/brochure/v2/slides/brochure-dashboard-back.html index 2f446f0..147b8bf 100644 --- a/brochure/v2/slides/brochure-dashboard-back.html +++ b/brochure/v2/slides/brochure-dashboard-back.html @@ -224,17 +224,19 @@
                                      +

                                      무료 데모를 신청하세요

                                      대표님 전용 대시보드를 직접 체험해 보세요

                                      -

                                      www.sam.it.kr

                                      +

                                      contact@codebridge-x.com

                                      +

                                      www.codebridge-x.com

                                      -

                                      SAM — Smart Automation Management

                                      +

                                      (주)코드브릿지엑스 | SAM - Smart Automation Management

                                      \ No newline at end of file diff --git a/brochure/v2/slides/brochure-dashboard-front.html b/brochure/v2/slides/brochure-dashboard-front.html index ac67529..0df1434 100644 --- a/brochure/v2/slides/brochure-dashboard-front.html +++ b/brochure/v2/slides/brochure-dashboard-front.html @@ -160,8 +160,8 @@
                                      -

                                      SAM

                                      -

                                      www.sam.it.kr

                                      +

                                      (주)코드브릿지엑스

                                      +

                                      www.codebridge-x.com

                                      뒷면에서 상세 기능을 확인하세요

                                      diff --git a/brochure/v3/sam-brochure-v3-dashboard-1page.pptx b/brochure/v3/sam-brochure-v3-dashboard-1page.pptx index 307d933..cbb6084 100644 Binary files a/brochure/v3/sam-brochure-v3-dashboard-1page.pptx and b/brochure/v3/sam-brochure-v3-dashboard-1page.pptx differ diff --git a/brochure/v3/sam-brochure-v3-dashboard-2page.pptx b/brochure/v3/sam-brochure-v3-dashboard-2page.pptx index 0a97e40..cdc1014 100644 Binary files a/brochure/v3/sam-brochure-v3-dashboard-2page.pptx and b/brochure/v3/sam-brochure-v3-dashboard-2page.pptx differ diff --git a/brochure/v3/slides/brochure-dashboard-1page.html b/brochure/v3/slides/brochure-dashboard-1page.html index f6316e2..8547c7d 100644 --- a/brochure/v3/slides/brochure-dashboard-1page.html +++ b/brochure/v3/slides/brochure-dashboard-1page.html @@ -392,10 +392,12 @@
                                      -

                                      SAM

                                      -

                                      www.sam.it.kr

                                      +

                                      (주)코드브릿지엑스

                                      +

                                      www.codebridge-x.com

                                      +

                                      무료 데모 신청

                                      +

                                      contact@codebridge-x.com

                                      diff --git a/brochure/v3/slides/brochure-dashboard-back.html b/brochure/v3/slides/brochure-dashboard-back.html index 95eb751..f199750 100644 --- a/brochure/v3/slides/brochure-dashboard-back.html +++ b/brochure/v3/slides/brochure-dashboard-back.html @@ -354,18 +354,20 @@
                                      +

                                      무료 데모를 신청하세요

                                      대표님 전용 대시보드를 직접 체험

                                      -

                                      www.sam.it.kr

                                      +

                                      contact@codebridge-x.com

                                      +

                                      www.codebridge-x.com

                                      -

                                      SAM — Smart Automation Management

                                      +

                                      (주)코드브릿지엑스 | SAM - Smart Automation Management

                                      \ No newline at end of file diff --git a/brochure/v3/slides/brochure-dashboard-front.html b/brochure/v3/slides/brochure-dashboard-front.html index 5223cb5..b1d8828 100644 --- a/brochure/v3/slides/brochure-dashboard-front.html +++ b/brochure/v3/slides/brochure-dashboard-front.html @@ -250,8 +250,8 @@
                                      -

                                      SAM

                                      -

                                      www.sam.it.kr

                                      +

                                      (주)코드브릿지엑스

                                      +

                                      www.codebridge-x.com

                                      뒷면에서 상세 기능을 확인하세요 ▶

                                      diff --git a/brochure/v4/sam-brochure-v4-dashboard-1page.pptx b/brochure/v4/sam-brochure-v4-dashboard-1page.pptx index 2ec5fe9..747d4f4 100644 Binary files a/brochure/v4/sam-brochure-v4-dashboard-1page.pptx and b/brochure/v4/sam-brochure-v4-dashboard-1page.pptx differ diff --git a/brochure/v4/sam-brochure-v4-dashboard-2page.pptx b/brochure/v4/sam-brochure-v4-dashboard-2page.pptx index 84ae7aa..c56e36e 100644 Binary files a/brochure/v4/sam-brochure-v4-dashboard-2page.pptx and b/brochure/v4/sam-brochure-v4-dashboard-2page.pptx differ diff --git a/brochure/v4/slides/brochure-dashboard-1page.html b/brochure/v4/slides/brochure-dashboard-1page.html index 7920035..8792a24 100644 --- a/brochure/v4/slides/brochure-dashboard-1page.html +++ b/brochure/v4/slides/brochure-dashboard-1page.html @@ -392,10 +392,12 @@
                                      -

                                      SAM

                                      -

                                      www.sam.it.kr

                                      +

                                      (주)코드브릿지엑스

                                      +

                                      www.codebridge-x.com

                                      +

                                      무료 데모 신청

                                      +

                                      contact@codebridge-x.com

                                      diff --git a/brochure/v4/slides/brochure-dashboard-back.html b/brochure/v4/slides/brochure-dashboard-back.html index 3de9eb6..6b64b85 100644 --- a/brochure/v4/slides/brochure-dashboard-back.html +++ b/brochure/v4/slides/brochure-dashboard-back.html @@ -354,18 +354,20 @@
                                      +

                                      무료 데모를 신청하세요

                                      대표님 전용 대시보드를 직접 체험

                                      -

                                      www.sam.it.kr

                                      +

                                      contact@codebridge-x.com

                                      +

                                      www.codebridge-x.com

                                      -

                                      SAM — Smart Automation Management

                                      +

                                      (주)코드브릿지엑스 | SAM - Smart Automation Management

                                      \ No newline at end of file diff --git a/brochure/v4/slides/brochure-dashboard-front.html b/brochure/v4/slides/brochure-dashboard-front.html index 98d48c7..8f4165f 100644 --- a/brochure/v4/slides/brochure-dashboard-front.html +++ b/brochure/v4/slides/brochure-dashboard-front.html @@ -248,8 +248,8 @@
                                      -

                                      SAM

                                      -

                                      www.sam.it.kr

                                      +

                                      (주)코드브릿지엑스

                                      +

                                      www.codebridge-x.com

                                      뒷면에서 상세 기능을 확인하세요 ▶

                                      diff --git a/brochure/v5/sam-brochure-v5-dashboard-1page.pptx b/brochure/v5/sam-brochure-v5-dashboard-1page.pptx index f6b930e..60194bf 100644 Binary files a/brochure/v5/sam-brochure-v5-dashboard-1page.pptx and b/brochure/v5/sam-brochure-v5-dashboard-1page.pptx differ diff --git a/brochure/v5/sam-brochure-v5-dashboard-2page.pptx b/brochure/v5/sam-brochure-v5-dashboard-2page.pptx index e9f9c95..e53d81c 100644 Binary files a/brochure/v5/sam-brochure-v5-dashboard-2page.pptx and b/brochure/v5/sam-brochure-v5-dashboard-2page.pptx differ diff --git a/brochure/v5/slides/brochure-dashboard-1page.html b/brochure/v5/slides/brochure-dashboard-1page.html index 9c1105e..6e8d331 100644 --- a/brochure/v5/slides/brochure-dashboard-1page.html +++ b/brochure/v5/slides/brochure-dashboard-1page.html @@ -306,10 +306,12 @@
                                      -

                                      SAM

                                      -

                                      www.sam.it.kr

                                      +

                                      (주)코드브릿지엑스

                                      +

                                      www.codebridge-x.com

                                      +

                                      무료 데모 신청

                                      +

                                      contact@codebridge-x.com

                                      diff --git a/brochure/v5/slides/brochure-dashboard-back.html b/brochure/v5/slides/brochure-dashboard-back.html index 46012b5..aa9f629 100644 --- a/brochure/v5/slides/brochure-dashboard-back.html +++ b/brochure/v5/slides/brochure-dashboard-back.html @@ -292,18 +292,20 @@
                                      +

                                      무료 데모를 신청하세요

                                      대표님 전용 대시보드를 직접 체험

                                      -

                                      www.sam.it.kr

                                      +

                                      contact@codebridge-x.com

                                      +

                                      www.codebridge-x.com

                                      -

                                      SAM — Smart Automation Management

                                      +

                                      (주)코드브릿지엑스 | SAM - Smart Automation Management

                                      \ No newline at end of file diff --git a/brochure/v5/slides/brochure-dashboard-front.html b/brochure/v5/slides/brochure-dashboard-front.html index 013a249..ba19d78 100644 --- a/brochure/v5/slides/brochure-dashboard-front.html +++ b/brochure/v5/slides/brochure-dashboard-front.html @@ -204,8 +204,8 @@
                                      -

                                      SAM

                                      -

                                      www.sam.it.kr

                                      +

                                      (주)코드브릿지엑스

                                      +

                                      www.codebridge-x.com

                                      뒷면에서 상세 기능을 확인하세요 ▶

                                      diff --git a/brochure/v6/sam-brochure-v6-dashboard-1page.pptx b/brochure/v6/sam-brochure-v6-dashboard-1page.pptx index 0fbb21d..a9fa7c0 100644 Binary files a/brochure/v6/sam-brochure-v6-dashboard-1page.pptx and b/brochure/v6/sam-brochure-v6-dashboard-1page.pptx differ diff --git a/brochure/v6/sam-brochure-v6-dashboard-2page.pptx b/brochure/v6/sam-brochure-v6-dashboard-2page.pptx index 406787c..8c54f02 100644 Binary files a/brochure/v6/sam-brochure-v6-dashboard-2page.pptx and b/brochure/v6/sam-brochure-v6-dashboard-2page.pptx differ diff --git a/brochure/v6/slides/brochure-dashboard-1page.html b/brochure/v6/slides/brochure-dashboard-1page.html index 4afb7db..20ee484 100644 --- a/brochure/v6/slides/brochure-dashboard-1page.html +++ b/brochure/v6/slides/brochure-dashboard-1page.html @@ -359,10 +359,12 @@
                                      -

                                      SAM

                                      -

                                      www.sam.it.kr

                                      +

                                      (주)코드브릿지엑스

                                      +

                                      www.codebridge-x.com

                                      +

                                      무료 데모 신청

                                      +

                                      contact@codebridge-x.com

                                      diff --git a/brochure/v6/slides/brochure-dashboard-back.html b/brochure/v6/slides/brochure-dashboard-back.html index 6cd9c34..792d610 100644 --- a/brochure/v6/slides/brochure-dashboard-back.html +++ b/brochure/v6/slides/brochure-dashboard-back.html @@ -316,18 +316,20 @@
                                      +

                                      무료 데모를 신청하세요

                                      대표님 전용 대시보드를 직접 체험

                                      -

                                      www.sam.it.kr

                                      +

                                      contact@codebridge-x.com

                                      +

                                      www.codebridge-x.com

                                      -

                                      SAM — Smart Automation Management

                                      +

                                      (주)코드브릿지엑스 | SAM - Smart Automation Management

                                      diff --git a/brochure/v6/slides/brochure-dashboard-front.html b/brochure/v6/slides/brochure-dashboard-front.html index d21776b..d25994a 100644 --- a/brochure/v6/slides/brochure-dashboard-front.html +++ b/brochure/v6/slides/brochure-dashboard-front.html @@ -217,8 +217,8 @@
                                      -

                                      SAM

                                      -

                                      www.sam.it.kr

                                      +

                                      (주)코드브릿지엑스

                                      +

                                      www.codebridge-x.com

                                      뒷면에서 상세 기능을 확인하세요 ▶

                                      diff --git a/brochure/v7/sam-brochure-v7-dashboard-1page.pptx b/brochure/v7/sam-brochure-v7-dashboard-1page.pptx index 3fb887d..6f12fd7 100644 Binary files a/brochure/v7/sam-brochure-v7-dashboard-1page.pptx and b/brochure/v7/sam-brochure-v7-dashboard-1page.pptx differ diff --git a/brochure/v7/sam-brochure-v7-dashboard-2page.pptx b/brochure/v7/sam-brochure-v7-dashboard-2page.pptx index 9f37b2f..20a6bd1 100644 Binary files a/brochure/v7/sam-brochure-v7-dashboard-2page.pptx and b/brochure/v7/sam-brochure-v7-dashboard-2page.pptx differ diff --git a/brochure/v7/slides/brochure-dashboard-1page.html b/brochure/v7/slides/brochure-dashboard-1page.html index ccba1ba..4cde3f3 100644 --- a/brochure/v7/slides/brochure-dashboard-1page.html +++ b/brochure/v7/slides/brochure-dashboard-1page.html @@ -363,10 +363,12 @@
                                      -

                                      SAM

                                      -

                                      www.sam.it.kr

                                      +

                                      (주)코드브릿지엑스

                                      +

                                      www.codebridge-x.com

                                      +

                                      무료 데모 신청

                                      +

                                      contact@codebridge-x.com

                                      diff --git a/brochure/v7/slides/brochure-dashboard-back.html b/brochure/v7/slides/brochure-dashboard-back.html index aab44bf..7298655 100644 --- a/brochure/v7/slides/brochure-dashboard-back.html +++ b/brochure/v7/slides/brochure-dashboard-back.html @@ -354,18 +354,20 @@
                                      +

                                      무료 데모를 신청하세요

                                      대표님 전용 대시보드를 직접 체험

                                      -

                                      www.sam.it.kr

                                      +

                                      contact@codebridge-x.com

                                      +

                                      www.codebridge-x.com

                                      -

                                      SAM — Smart Automation Management

                                      +

                                      (주)코드브릿지엑스 | SAM - Smart Automation Management

                                      \ No newline at end of file diff --git a/brochure/v7/slides/brochure-dashboard-front.html b/brochure/v7/slides/brochure-dashboard-front.html index 8d72199..1a4ccdd 100644 --- a/brochure/v7/slides/brochure-dashboard-front.html +++ b/brochure/v7/slides/brochure-dashboard-front.html @@ -266,8 +266,8 @@
                                      -

                                      SAM

                                      -

                                      www.sam.it.kr

                                      +

                                      (주)코드브릿지엑스

                                      +

                                      www.codebridge-x.com

                                      뒷면에서 상세 기능을 확인하세요 ▶

                                      diff --git a/brochure/v8/sam-brochure-v8-dashboard-1page.pptx b/brochure/v8/sam-brochure-v8-dashboard-1page.pptx index 4f20645..4a2c33d 100644 Binary files a/brochure/v8/sam-brochure-v8-dashboard-1page.pptx and b/brochure/v8/sam-brochure-v8-dashboard-1page.pptx differ diff --git a/brochure/v8/sam-brochure-v8-dashboard-2page.pptx b/brochure/v8/sam-brochure-v8-dashboard-2page.pptx index f149d83..ade5f05 100644 Binary files a/brochure/v8/sam-brochure-v8-dashboard-2page.pptx and b/brochure/v8/sam-brochure-v8-dashboard-2page.pptx differ diff --git a/brochure/v8/slides/brochure-dashboard-1page.html b/brochure/v8/slides/brochure-dashboard-1page.html index bbbec34..fbf62ea 100644 --- a/brochure/v8/slides/brochure-dashboard-1page.html +++ b/brochure/v8/slides/brochure-dashboard-1page.html @@ -218,18 +218,20 @@
                                      +

                                      무료 데모를 신청하세요

                                      대표님 전용 대시보드를 직접 체험

                                      -

                                      www.sam.it.kr

                                      +

                                      contact@codebridge-x.com

                                      +

                                      www.codebridge-x.com

                                      -

                                      SAM — Smart Automation Management

                                      +

                                      (주)코드브릿지엑스 | SAM - Smart Automation Management

                                      diff --git a/brochure/v8/slides/brochure-dashboard-back.html b/brochure/v8/slides/brochure-dashboard-back.html index aafa592..28747b2 100644 --- a/brochure/v8/slides/brochure-dashboard-back.html +++ b/brochure/v8/slides/brochure-dashboard-back.html @@ -301,18 +301,20 @@
                                      +

                                      무료 데모를 신청하세요

                                      대표님 전용 대시보드를 직접 체험

                                      -

                                      www.sam.it.kr

                                      +

                                      contact@codebridge-x.com

                                      +

                                      www.codebridge-x.com

                      -

                      SAM — Smart Automation Management

                      +

                      (주)코드브릿지엑스 | SAM - Smart Automation Management

                      \ No newline at end of file diff --git a/brochure/v8/slides/brochure-dashboard-front.html b/brochure/v8/slides/brochure-dashboard-front.html index ff7f738..10ecdb0 100644 --- a/brochure/v8/slides/brochure-dashboard-front.html +++ b/brochure/v8/slides/brochure-dashboard-front.html @@ -268,8 +268,8 @@
                      -

                      SAM

                      -

                      www.sam.it.kr

                      +

                      (주)코드브릿지엑스

                      +

                      www.codebridge-x.com

                      뒷면에서 상세 기능을 확인하세요 ▶

                      diff --git a/brochure/v9/sam-brochure-v9-dashboard-1page.pptx b/brochure/v9/sam-brochure-v9-dashboard-1page.pptx index 1b7de30..9a39ae4 100644 Binary files a/brochure/v9/sam-brochure-v9-dashboard-1page.pptx and b/brochure/v9/sam-brochure-v9-dashboard-1page.pptx differ diff --git a/brochure/v9/sam-brochure-v9-dashboard-2page.pptx b/brochure/v9/sam-brochure-v9-dashboard-2page.pptx index 2a61715..3fd20cc 100644 Binary files a/brochure/v9/sam-brochure-v9-dashboard-2page.pptx and b/brochure/v9/sam-brochure-v9-dashboard-2page.pptx differ diff --git a/brochure/v9/slides/brochure-dashboard-1page.html b/brochure/v9/slides/brochure-dashboard-1page.html index 3b707b6..d228d07 100644 --- a/brochure/v9/slides/brochure-dashboard-1page.html +++ b/brochure/v9/slides/brochure-dashboard-1page.html @@ -250,15 +250,17 @@
                      +

                      무료 데모를 신청하세요

                      대표님 전용 대시보드를 직접 체험

                      -

                      www.sam.it.kr

                      +

                      contact@codebridge-x.com

                      +

                      www.codebridge-x.com

                      -

                      SAM

                      +

                      (주)코드브릿지엑스

                      \ No newline at end of file diff --git a/brochure/v9/slides/brochure-dashboard-back.html b/brochure/v9/slides/brochure-dashboard-back.html index 6bd4f11..46e438c 100644 --- a/brochure/v9/slides/brochure-dashboard-back.html +++ b/brochure/v9/slides/brochure-dashboard-back.html @@ -211,17 +211,19 @@
                      +

                      무료 데모를 신청하세요

                      대표님 전용 대시보드를 직접 체험

                      -

                      www.sam.it.kr

                      +

                      contact@codebridge-x.com

                      +

                      www.codebridge-x.com

                      -

                      SAM

                      +

                      (주)코드브릿지엑스

                      \ No newline at end of file diff --git a/brochure/v9/slides/brochure-dashboard-front.html b/brochure/v9/slides/brochure-dashboard-front.html index 87a7f93..dc52623 100644 --- a/brochure/v9/slides/brochure-dashboard-front.html +++ b/brochure/v9/slides/brochure-dashboard-front.html @@ -172,8 +172,8 @@
                      -

                      SAM

                      -

                      www.sam.it.kr

                      +

                      (주)코드브릿지엑스

                      +

                      www.codebridge-x.com

                      뒷면에서 상세 기능을 확인하세요

                      diff --git a/sam/docs/changes/20260303_gemini_model_upgrade.md b/changes/20260303_gemini_model_upgrade.md similarity index 100% rename from sam/docs/changes/20260303_gemini_model_upgrade.md rename to changes/20260303_gemini_model_upgrade.md diff --git a/sam/docs/changes/20260304_eaccount_infinite_loop_fix.md b/changes/20260304_eaccount_infinite_loop_fix.md similarity index 100% rename from sam/docs/changes/20260304_eaccount_infinite_loop_fix.md rename to changes/20260304_eaccount_infinite_loop_fix.md diff --git a/changes/20260306_purchase_request_payment_method.md b/changes/20260306_purchase_request_payment_method.md new file mode 100644 index 0000000..67308f5 --- /dev/null +++ b/changes/20260306_purchase_request_payment_method.md @@ -0,0 +1,69 @@ +# 품의서 지급방법 UI 개선 + +**날짜:** 2026-03-06 +**작업자:** Claude Code + +## 변경 개요 + +품의서 2종(구매품의서, 비용정산품의서)에 지급방법 선택 기능을 추가/개선하였다. + +## 변경 내용 + +### 1. 구매품의서 (pr_purchase) - 지급방법 추가 + +- 납품 정보(납품업체/납품예정일/납품장소) 아래에 **지급방법 radio** 추가 +- 옵션: `법인카드` / `계좌이체` +- **일괄 선택** 방식 (전체 구매건에 하나의 지급방법) + +### 2. 비용정산품의서 (pr_settlement) - 지급방법 행별 변경 + +- 기존: 테이블 아래에 일괄 radio (법인카드/개인선지출) +- 변경: 각 내역행에 **지급방법 select** 컬럼 추가 +- 테이블 하단에 **지급방법별 합계표** 추가 (법인카드 합계 / 개인선지출 합계) +- 이유: 하나의 정산서에 법인카드/개인선지출 내역이 혼재할 수 있음 + +### 3. 지출품의서 (pr_expense) - 라벨 변경 + +- `사용일자` -> `지출일자` 라벨 변경 (폼 + 조회 화면) + +## 수정된 파일 + +| 파일 | 변경 내용 | +|------|----------| +| `mng/resources/views/approvals/partials/_purchase-request-form.blade.php` | 구매품의서 지급방법 radio 추가, 비용정산품의서 행별 select + 합계표, 지출일자 라벨 변경 | +| `mng/resources/views/approvals/partials/_purchase-request-show.blade.php` | 구매품의서/비용정산품의서 조회 화면 동기화 | + +## Alpine.js 데이터 변경 + +### 구매품의서 + +```javascript +// formData에 추가 +payment_method: initialData?.payment_method || '', + +// getFormData()에 포함 +{ ...base, ..., payment_method: this.formData.payment_method } +``` + +### 비용정산품의서 + +```javascript +// makeItem()에 추가 +payment_method: data?.payment_method || '', + +// computed 속성 추가 +get corporateCardTotal() { /* corporate_card 행만 합산 */ }, +get personalAdvanceTotal() { /* personal_advance 행만 합산 */ }, + +// getFormData() 변경 +// 기존: payment_method: this.formData.payment_method (일괄) +// 변경: 각 item.payment_method (행별) + corporate_card_total, personal_advance_total +``` + +## 관련 문서 + +- [결재 양식 기술 명세](../features/approvals/form-types.md) - 섹션 12, 14 업데이트 + +--- + +**최종 업데이트**: 2026-03-06 diff --git a/sam/docs/contracts/CHANGELOG.md b/contracts/CHANGELOG.md similarity index 100% rename from sam/docs/contracts/CHANGELOG.md rename to contracts/CHANGELOG.md diff --git a/sam/docs/contracts/docx/01_고객_서비스이용계약서_v4_0_전자서명용.docx b/contracts/docx/01_고객_서비스이용계약서_v4_0_전자서명용.docx similarity index 100% rename from sam/docs/contracts/docx/01_고객_서비스이용계약서_v4_0_전자서명용.docx rename to contracts/docx/01_고객_서비스이용계약서_v4_0_전자서명용.docx diff --git a/sam/docs/contracts/docx/비밀유지서약서.docx b/contracts/docx/비밀유지서약서.docx similarity index 100% rename from sam/docs/contracts/docx/비밀유지서약서.docx rename to contracts/docx/비밀유지서약서.docx diff --git a/sam/docs/contracts/docx/영업파트너 위촉계약서(단체용).docx b/contracts/docx/영업파트너 위촉계약서(단체용).docx similarity index 100% rename from sam/docs/contracts/docx/영업파트너 위촉계약서(단체용).docx rename to contracts/docx/영업파트너 위촉계약서(단체용).docx diff --git a/sam/docs/contracts/docx/영업파트너 위촉계약서.docx b/contracts/docx/영업파트너 위촉계약서.docx similarity index 100% rename from sam/docs/contracts/docx/영업파트너 위촉계약서.docx rename to contracts/docx/영업파트너 위촉계약서.docx diff --git a/sam/docs/contracts/markdown/01-service-agreement.md b/contracts/markdown/01-service-agreement.md similarity index 100% rename from sam/docs/contracts/markdown/01-service-agreement.md rename to contracts/markdown/01-service-agreement.md diff --git a/sam/docs/contracts/markdown/02-nda.md b/contracts/markdown/02-nda.md similarity index 100% rename from sam/docs/contracts/markdown/02-nda.md rename to contracts/markdown/02-nda.md diff --git a/sam/docs/contracts/markdown/03-partner-agreement.md b/contracts/markdown/03-partner-agreement.md similarity index 100% rename from sam/docs/contracts/markdown/03-partner-agreement.md rename to contracts/markdown/03-partner-agreement.md diff --git a/sam/docs/contracts/markdown/04-partner-agreement-group.md b/contracts/markdown/04-partner-agreement-group.md similarity index 100% rename from sam/docs/contracts/markdown/04-partner-agreement-group.md rename to contracts/markdown/04-partner-agreement-group.md diff --git a/sam/docs/contracts/revisions.json b/contracts/revisions.json similarity index 100% rename from sam/docs/contracts/revisions.json rename to contracts/revisions.json diff --git a/sam/docs/contracts/scripts/extract_to_markdown.py b/contracts/scripts/extract_to_markdown.py similarity index 100% rename from sam/docs/contracts/scripts/extract_to_markdown.py rename to contracts/scripts/extract_to_markdown.py diff --git a/sam/docs/contracts/scripts/sync_check.py b/contracts/scripts/sync_check.py similarity index 100% rename from sam/docs/contracts/scripts/sync_check.py rename to contracts/scripts/sync_check.py diff --git a/sam/docs/data/interview-master-questions.sql b/data/interview-master-questions.sql similarity index 100% rename from sam/docs/data/interview-master-questions.sql rename to data/interview-master-questions.sql diff --git a/dev/deploys/ops-manual/01-server-overview.md b/dev/deploys/ops-manual/01-server-overview.md index 324c64a..eb6baf7 100644 --- a/dev/deploys/ops-manual/01-server-overview.md +++ b/dev/deploys/ops-manual/01-server-overview.md @@ -218,14 +218,24 @@ ### 서비스 현황 -| 서비스 | 포트 | 상태 | -|--------|------|------| -| Nginx | 80/443 | active | -| Apache | 8080 | active (레거시) | -| MySQL 8.4 | 3306 (localhost) | active | -| Gitea | 3000 | active | -| Next.js (PM2) | 3001 | active | -| fail2ban | - | active | +| 서비스 | 포트 | 상태 | 비고 | +|--------|------|------|------| +| Nginx | 80/443 | active | | +| PHP 8.4 FPM | unix socket | active | SAM 앱 (api, mng, sales) | +| PHP 7.3 FPM | unix socket | active | 5130.codebridge-x.com 레거시 전용 | +| MySQL 8.4 | 3306 (localhost) | active | binlog 7일 보관 | +| Gitea | 3000 | active | | +| Next.js (PM2) | 3001 | active | | +| fail2ban | - | active | | +| Swap | - | active | 4G (/swapfile, fstab 등록) | +| ~~Apache~~ | ~~8080~~ | **disabled** | 2026-03-09 비활성화 (front.5130.co.kr 미사용) | +| ~~PHP 5.6 FPM~~ | - | **disabled** | 2026-03-09 비활성화 (미사용) | + +### 자동 정리 (cron, hskwon) + +| 작업 | 주기 | 설명 | +|------|------|------| +| Gitea repo-archive 캐시 정리 | 매주 일요일 04:00 | 7일 이상 된 캐시 파일 삭제 + 빈 디렉토리 정리 | ### 방화벽 (UFW) 규칙 diff --git a/dev/deploys/ops-manual/02-daily-operations.md b/dev/deploys/ops-manual/02-daily-operations.md index 3c24a43..01a886f 100644 --- a/dev/deploys/ops-manual/02-daily-operations.md +++ b/dev/deploys/ops-manual/02-daily-operations.md @@ -48,6 +48,59 @@ chmod 640 /home/webservice/mng/shared/.env --- +## [개발] sam-dev 리소스 관리 + +sam-dev는 2 vCPU / 3.8GB RAM으로 여러 서비스가 공존하므로 리소스 관리가 중요하다. + +### 주요 리소스 소비자 + +| 프로세스 | 메모리 | CPU (상시) | 비고 | +|----------|--------|-----------|------| +| MySQL | ~1.2G (30%) | 2~5% | | +| Gitea | ~400M (10%) | 1.8% | | +| Next.js (PM2) | ~380M (9.5%) | 0.5% | | +| PHP 8.4 FPM | ~250M (변동) | 요청 시 높음 | max_children=5 | +| PHP 7.3 FPM | ~30M | idle | 5130 레거시 전용 | + +### Swap (4G, 2026-03-09 추가) + +Swap 없이 운영하면 메모리 부족 시 OOM Killer가 프로세스를 즉시 종료한다. + +```bash +# Swap 상태 확인 +swapon --show +free -m + +# /etc/fstab에 등록되어 있으므로 재부팅 후에도 유지됨 +# /swapfile none swap sw 0 0 +``` + +### Gitea repo-archive 캐시 + +Gitea는 Web UI에서 ZIP/TAR.GZ 다운로드 시 `/var/lib/gitea/data/repo-archive/`에 캐시를 생성한다. +기본 설정에서는 만료가 없어 무한 증가하므로, cron으로 7일 이상 된 캐시를 정리한다. + +```bash +# 캐시 크기 확인 +sudo du -sh /var/lib/gitea/data/repo-archive/ + +# 수동 정리 (긴급 시) +sudo rm -rf /var/lib/gitea/data/repo-archive/* + +# 자동 정리: hskwon crontab (매주 일요일 04:00) +# 0 4 * * 0 find /var/lib/gitea/data/repo-archive -type f -mtime +7 -delete +# 0 4 * * 0 find /var/lib/gitea/data/repo-archive -type d -empty -delete +``` + +### 비활성화된 서비스 (2026-03-09) + +| 서비스 | 사유 | 복원 명령 | +|--------|------|----------| +| PHP 5.6 FPM | 미사용 (아무 사이트도 참조 안 함) | `sudo systemctl enable --now php5.6-fpm` | +| Apache | front.5130.co.kr 미사용 | `sudo systemctl enable --now apache2` | + +--- + ## 시스템 리소스 모니터링 양쪽 서버 공통 명령어: diff --git a/dev/deploys/ops-manual/06-database.md b/dev/deploys/ops-manual/06-database.md index 148f917..c31a6da 100644 --- a/dev/deploys/ops-manual/06-database.md +++ b/dev/deploys/ops-manual/06-database.md @@ -92,6 +92,33 @@ gunzip -c /home/hskwon/backups/mysql/gitea_YYYYMMDD_HHMMSS.sql.gz | mysql gitea --- +## [개발] MySQL Binlog 관리 + +sam-dev에서는 리플리케이션/PITR을 사용하지 않으므로 binlog 보관을 최소화한다. + +**설정 파일:** `/etc/mysql/mysql.conf.d/mysqld.cnf` + +```ini +binlog_expire_logs_seconds = 604800 # 7일 보관 (2026-03-09 설정) +max_binlog_size = 100M +``` + +```bash +# binlog 상태 확인 +sudo ls -lh /var/lib/mysql/binlog.0* | wc -l # 파일 수 +sudo du -shc /var/lib/mysql/binlog.0* | tail -1 # 총 크기 + +# 수동 퍼지 (필요 시) +mysql -u pro -p -e "PURGE BINARY LOGS BEFORE DATE_SUB(NOW(), INTERVAL 3 DAY);" + +# binlog 완전 비활성화가 필요하면 (권장하지 않음) +# mysqld.cnf에 skip-log-bin 추가 후 MySQL 재시작 +``` + +> **참고:** 운영서버(sam-prod)의 binlog는 CI/CD 백업에 활용되므로 별도 정책 적용. + +--- + ## Slow Query 분석 (운영) ```bash diff --git a/dev/dev_plans/qms-api-integration-plan.md b/dev/dev_plans/qms-api-integration-plan.md new file mode 100644 index 0000000..c760f0d --- /dev/null +++ b/dev/dev_plans/qms-api-integration-plan.md @@ -0,0 +1,316 @@ +# 품질인정심사(QMS) API 연동 계획 + +> **작성일**: 2026-03-09 +> **상태**: 계획 수립 +> **URL**: `/quality/qms` +> **스토리보드**: 슬라이드 19~20 +> **관련 문서**: `docs/features/quality-management/quality-certification-audit.md` + +--- + +## 1. 현황 분석 + +### 1.1 프론트엔드 현황 + +| 항목 | 상태 | 비고 | +|------|------|------| +| `page.tsx` | ✅ 구현됨 | 14KB, 전체 페이지 레이아웃 | +| `types.ts` | ✅ 구현됨 | 95줄, 타입 정의 완료 | +| `mockData.ts` | ✅ 구현됨 | 543줄, 완전한 목업 데이터 | +| `components/` | ✅ 구현됨 | 12개 컴포넌트 + documents/ 7개 | +| `actions.ts` | ❌ 없음 | API 연동 0% | + +프론트엔드는 UI가 완성되어 있으나 **100% 목업 데이터**로 동작 중. + +### 1.2 백엔드 현황 + +| 영역 | 기존 API | 신규 필요 | +|------|----------|-----------| +| **1일차 (기준/매뉴얼 심사)** | ❌ 없음 | 모델, 마이그레이션, 서비스, 컨트롤러 전체 | +| **2일차 (로트 추적 심사)** | ⚠️ 부분 존재 | 기존 API 조합 + 서류 연결 API 신규 | + +**기존 활용 가능 API:** +- `GET /quality/documents` — 품질관리서 목록 (2일차 1단계) +- `GET /quality/documents/{id}` — 품질관리서 상세 + 수주/개소 (2일차 2단계) +- `GET /quality/performance-reports` — 실적신고 (분기 필터 활용) +- `GET /inspections` — 수입검사/중간검사 성적서 +- 출하/출고/납품 관련 기존 API + +--- + +## 2. 작업 범위 + +### Phase 1: 2일차 (로트 추적 심사) API 연동 + +> **우선순위 높음** — 기존 API 활용 가능하여 빠르게 연동 가능 + +#### 2.1 Frontend — `actions.ts` 생성 + +``` +react/src/app/[locale]/(protected)/quality/qms/actions.ts +``` + +| 액션 | 호출 API | 설명 | +|------|----------|------| +| `getQualityReports()` | `GET /quality/documents` | 품질관리서 목록 (분기 필터) | +| `getReportRoutes(reportId)` | `GET /quality/documents/{id}` | 수주코드 + 개소 목록 | +| `getRouteDocuments(routeId)` | 복합 조회 (아래 참조) | 개소별 관련 서류 8종 | +| `confirmUnitInspection(unitId)` | `PATCH /qms/lot-audit/confirm` | 개소 확인 완료 처리 | + +#### 2.2 관련 서류 조회 로직 + +2일차 3단계 "관련 서류"는 개소(Location)에 연결된 8종 서류를 조합 조회: + +| 서류 타입 | 데이터 소스 | 조회 방식 | +|-----------|-------------|-----------| +| 수입검사 성적서 | `inspections` (type=IQC) | 수주의 BOM 원자재 LOT 추적 | +| 수주서 | `orders` | 수주코드로 직접 조회 | +| 작업일지 | `work_orders` + 작업일지 | 수주 → 작업지시 → 작업일지 | +| 중간검사 성적서 | `inspections` (type=PQC) | 작업지시별 중간검사 | +| 납품확인서 | `shipments` | 출하 → 납품확인서 | +| 출고증 | `shipments` | 출하 → 출고증 | +| 제품검사 성적서 | `quality_document_locations` | 개소별 검사 문서 (EAV) | +| 품질관리서 | `quality_documents` | 품질관리서 원본 | + +#### 2.3 Backend — 신규 API (최소) + +``` +GET /api/v1/qms/lot-audit/reports — 분기별 품질관리서 목록 (전용 뷰) +GET /api/v1/qms/lot-audit/reports/{id} — 수주코드 + 개소 + 완료 상태 +GET /api/v1/qms/lot-audit/routes/{id}/documents — 개소별 8종 서류 조합 조회 +PATCH /api/v1/qms/lot-audit/units/{id}/confirm — 확인 완료 처리 +``` + +> 기존 `quality/documents` API를 래핑하여 QMS 전용 응답 형태로 가공하는 방식 권장. +> 8종 서류 조합 로직이 복잡하므로 **전용 서비스 메서드** 필요. + +#### 2.4 DB 변경 + +| 변경 | 테이블 | 설명 | +|------|--------|------| +| 컬럼 추가 | `quality_document_locations` | `options` JSON에 `lot_audit_confirmed`, `lot_audit_confirmed_at` 추가 | + +> 별도 테이블 없이 기존 개소(Location) 테이블의 `options` 활용 (컬럼 추가 정책 준수) + +--- + +### Phase 2: 1일차 (기준/매뉴얼 심사) 백엔드 구축 + +> **작업량 많음** — 완전 신규 백엔드 구축 필요 + +#### 2.1 DB 설계 (신규 테이블) + +``` +audit_checklists (심사 점검표 마스터) +├── id, tenant_id +├── year, quarter +├── type: 'standard_manual' (1일차) +├── status: draft/in_progress/completed +├── options: JSON +├── created_by, timestamps, soft_delete + +audit_checklist_categories (점검표 카테고리) +├── id, tenant_id +├── checklist_id (FK → audit_checklists) +├── title: '원재료 품질관리 기준' +├── sort_order +├── options: JSON + +audit_checklist_items (점검표 세부 항목) +├── id, tenant_id +├── category_id (FK → audit_checklist_categories) +├── name: '수입검사 기준 확인' +├── description +├── is_completed: boolean +├── completed_at, completed_by +├── sort_order +├── options: JSON + +audit_standard_documents (기준 문서) +├── id, tenant_id +├── checklist_item_id (FK → audit_checklist_items) +├── title, version, date +├── document_id (FK → documents, EAV) +├── options: JSON +``` + +#### 2.2 Backend 구현 + +| 파일 | 역할 | +|------|------| +| `api/app/Models/Qualitys/AuditChecklist.php` | 심사 점검표 모델 | +| `api/app/Models/Qualitys/AuditChecklistCategory.php` | 카테고리 모델 | +| `api/app/Models/Qualitys/AuditChecklistItem.php` | 세부 항목 모델 | +| `api/app/Models/Qualitys/AuditStandardDocument.php` | 기준 문서 모델 | +| `api/app/Services/AuditChecklistService.php` | 서비스 | +| `api/app/Http/Controllers/Api/V1/AuditChecklistController.php` | 컨트롤러 | +| `api/database/migrations/XXXX_create_audit_checklists_table.php` | 마이그레이션 (4테이블) | + +#### 2.3 API 엔드포인트 + +``` +GET /api/v1/qms/checklists — 점검표 목록 (연도/분기 필터) +POST /api/v1/qms/checklists — 점검표 생성 +GET /api/v1/qms/checklists/{id} — 점검표 상세 (카테고리+항목+문서) +PUT /api/v1/qms/checklists/{id} — 점검표 수정 +PATCH /api/v1/qms/checklists/{id}/complete — 점검표 완료 처리 + +PATCH /api/v1/qms/checklist-items/{id}/toggle — 항목 완료/미완료 토글 +GET /api/v1/qms/checklist-items/{id}/documents — 항목별 기준 문서 조회 +POST /api/v1/qms/checklist-items/{id}/documents — 기준 문서 연결 +DELETE /api/v1/qms/checklist-items/{id}/documents/{docId} — 기준 문서 연결 해제 +``` + +#### 2.4 Frontend — actions.ts 확장 + +| 액션 | 설명 | +|------|------| +| `getChecklists(year, quarter)` | 점검표 목록 | +| `getChecklistDetail(id)` | 점검표 상세 (카테고리+항목+문서) | +| `toggleChecklistItem(itemId)` | 항목 완료/미완료 토글 | +| `getCheckItemDocuments(itemId)` | 기준 문서 조회 | +| `confirmCheckItem(itemId)` | 기준/매뉴얼 확인 완료 | + +--- + +### Phase 3: 프론트엔드 목업 → API 전환 + +#### 3.1 page.tsx 수정 + +- `mockData.ts` import 제거 +- `actions.ts` import로 교체 +- `useEffect`에서 API 호출 +- 로딩/에러 상태 추가 + +#### 3.2 컴포넌트 수정 + +| 컴포넌트 | 변경 내용 | +|----------|-----------| +| `ReportList.tsx` | API 데이터 바인딩 | +| `RouteList.tsx` | API 데이터 바인딩 | +| `DocumentList.tsx` | 8종 서류 실제 조회 | +| `InspectionModal.tsx` | 실제 검사 문서 렌더링 | +| `Day1ChecklistPanel.tsx` | API 데이터 바인딩 | +| `Day1DocumentSection.tsx` | 기준 문서 API 조회 | +| `Day1DocumentViewer.tsx` | 실제 파일 미리보기 | +| `AuditProgressBar.tsx` | 실시간 진행률 계산 | +| `Filters.tsx` | 연도/분기 필터 API 연동 | + +#### 3.3 mockData.ts 처리 + +- Phase 3 완료 후 `mockData.ts` 삭제 +- 또는 `USE_MOCK` 플래그 패턴 적용 (개발 편의) + +--- + +## 3. 데이터 매핑 + +### 3.1 InspectionReport ↔ QualityDocument + +| 프론트 (InspectionReport) | 백엔드 (QualityDocument) | +|---------------------------|-------------------------| +| `id` | `quality_documents.id` | +| `code` | `quality_documents.code` (채번) | +| `siteName` | `quality_documents.site_name` | +| `item` | `quality_documents.options.product_type` 또는 인정특성 | +| `routeCount` | `quality_document_orders` COUNT | +| `totalRoutes` | `quality_document_locations` COUNT | +| `quarter` | `performance_reports.year` + `quarter` | +| `year` | `performance_reports.year` | +| `quarterNum` | `performance_reports.quarter` | + +### 3.2 RouteItem ↔ QualityDocumentOrder + +| 프론트 (RouteItem) | 백엔드 (QualityDocumentOrder) | +|--------------------|-------------------------------| +| `id` | `quality_document_orders.id` | +| `code` | `orders.order_code` | +| `date` | `orders.order_date` | +| `site` | `orders.site_name` | +| `locationCount` | `quality_document_locations` COUNT | +| `subItems` | `quality_document_locations` 변환 | + +### 3.3 ChecklistCategory ↔ AuditChecklistCategory + +| 프론트 (ChecklistCategory) | 백엔드 (AuditChecklistCategory) | +|---------------------------|--------------------------------| +| `id` | `audit_checklist_categories.id` | +| `title` | `audit_checklist_categories.title` | +| `subItems` | `audit_checklist_items` 관계 | + +--- + +## 4. 일정 산정 + +| Phase | 작업 내용 | 예상 소요 | +|-------|----------|-----------| +| **Phase 1** | 2일차 API 연동 (기존 API 활용) | | +| ├ 1-1 | Backend: 전용 서비스 + 컨트롤러 + 라우트 | 1일 | +| ├ 1-2 | Backend: 8종 서류 조합 조회 로직 | 1일 | +| ├ 1-3 | Frontend: actions.ts 생성 + 목업 교체 | 1일 | +| └ 1-4 | 테스트 및 디버깅 | 0.5일 | +| **Phase 2** | 1일차 백엔드 구축 (완전 신규) | | +| ├ 2-1 | DB 설계 + 마이그레이션 (4테이블) | 0.5일 | +| ├ 2-2 | 모델 4개 + 관계 설정 | 0.5일 | +| ├ 2-3 | 서비스 + 컨트롤러 + 라우트 | 1일 | +| └ 2-4 | 초기 데이터 시딩 (점검표 마스터) | 0.5일 | +| **Phase 3** | 프론트엔드 전환 | | +| ├ 3-1 | 2일차 컴포넌트 API 바인딩 | 1일 | +| ├ 3-2 | 1일차 컴포넌트 API 바인딩 | 1일 | +| └ 3-3 | 통합 테스트 + mockData 정리 | 0.5일 | + +**총 예상: ~8일** + +--- + +## 5. 의존성 및 리스크 + +### 5.1 의존성 + +| 항목 | 의존 대상 | 상태 | +|------|-----------|------| +| 품질관리서 데이터 | `quality_documents` 실 데이터 | ✅ 운영 중 | +| 실적신고 데이터 | `performance_reports` 실 데이터 | ✅ 운영 중 | +| 수입검사 성적서 | `inspections` (IQC) | ✅ 운영 중 | +| 중간검사 성적서 | `inspections` (PQC) | ⚠️ 구현 중 | +| 작업일지 | `work_orders` 연결 | ✅ 운영 중 | +| 출하/납품 | `shipments` | ✅ 운영 중 | +| 기준 문서 파일 | EAV Document 시스템 | ✅ 운영 중 | + +### 5.2 리스크 + +| 리스크 | 영향 | 완화 방안 | +|--------|------|-----------| +| 8종 서류 추적 로직 복잡 | Phase 1 지연 | 서류별 독립 조회 후 프론트에서 조합 | +| 1일차 점검표 초기 데이터 부재 | Phase 2 테스트 어려움 | 시더로 기본 점검표 생성 | +| 중간검사 미완성 | 2일차 일부 서류 누락 | 빈 상태로 표시, 추후 연동 | + +--- + +## 6. 권장 진행 순서 + +``` +Phase 1 (2일차 API 연동) — 3.5일 + ↓ +Phase 2 (1일차 백엔드 구축) — 2.5일 + ↓ +Phase 3 (프론트엔드 전환) — 2.5일 +``` + +**Phase 1을 먼저 하는 이유:** +- 기존 API 활용으로 빠르게 실 데이터 확인 가능 +- 로트 추적은 실적신고와 직접 연결되어 비즈니스 우선순위 높음 +- Phase 2(1일차)는 독립적인 신규 개발이므로 나중에 진행 가능 + +--- + +## 관련 문서 + +- [품질인정심사 기능 문서](../../features/quality-management/quality-certification-audit.md) +- [제품검사 관리](../../features/quality-management/inspection-management.md) +- [생산실적신고](../../features/quality-management/performance-reports.md) +- [통합 개선 마스터 플랜](./integrated-master-plan.md) + +--- + +**최종 업데이트**: 2026-03-09 diff --git a/dev/guides/LOCAL_SETUP_GUIDE.md b/dev/guides/LOCAL_SETUP_GUIDE.md new file mode 100644 index 0000000..75c4dc9 --- /dev/null +++ b/dev/guides/LOCAL_SETUP_GUIDE.md @@ -0,0 +1,794 @@ +# SAM 로컬 개발 환경 셋팅 가이드 + +> 최종 업데이트: 2026-03-09 + +--- + +## 1. 사전 준비 + +### 필수 소프트웨어 + +| 소프트웨어 | 버전 | 용도 | 설치 | +|-----------|------|------|---------------------------------------------------------------| +| **Docker Desktop** | 최신 | 컨테이너 실행 | [docker.com](https://www.docker.com/products/docker-desktop/) | +| **Git** | 최신 | 버전 관리 | `brew install git` | +| **mkcert** | 최신 | 로컬 SSL 인증서 | `brew install mkcert` | +| **텍스트 에디터** | - | 코드 편집 | JetBrains IDE 권장 | + +> Docker Desktop이 설치되면 PHP, Node.js, MySQL 등은 **별도 설치 불필요** (Docker 컨테이너 내부에서 실행) + +### Git 서버 정보 + +| 항목 | 값 | +|------|-----| +| Git 서버 | Gitea (자체 호스팅) | +| 서버 주소 | `http://114.203.209.83:3000` | +| 조직 | `SamProject` | + +> Gitea 계정이 필요합니다. 팀장에게 계정 생성을 요청하세요. + +--- + +## 2. 저장소 클론 + +### 디렉토리 구조 + +``` +Works/@KD_SAM/SAM/ ← 루트 (원하는 경로에 생성) +├── api/ ← Laravel REST API +├── mng/ ← Laravel 관리자 패널 +├── react/ ← Next.js 프론트엔드 +├── docs/ ← 기술 문서 +├── hotfix/ ← 테스트/QA 문서 +├── docker/ ← Docker 설정 (api 저장소에 포함되지 않음) +├── design/ ← 디자인 시스템 (선택) +└── planning/ ← 기획 문서 (선택) +``` + +### 클론 명령어 + +```bash +# 작업 디렉토리 생성 +mkdir -p ~/Works/@KD_SAM/SAM +cd ~/Works/@KD_SAM/SAM + +# 5개 저장소 클론 +git clone http://114.203.209.83:3000/SamProject/sam-api.git api +git clone http://114.203.209.83:3000/SamProject/sam-manage.git mng +git clone http://114.203.209.83:3000/SamProject/sam-react-prod.git react +git clone http://114.203.209.83:3000/SamProject/sam-docs.git docs +git clone http://114.203.209.83:3000/SamProject/sam-hotfix.git hotfix +``` + +### 기본 브랜치 전환 + +```bash +# api, react는 develop 브랜치에서 작업 +cd api && git checkout develop && cd .. +cd react && git checkout develop && cd .. +``` + +> **중요**: `main` 브랜치에서 직접 작업하지 않습니다. 항상 `develop`에서 작업합니다. + +--- + +## 3. Docker 환경 구성 + +### 3-1. Docker 설정 파일 확인 + +`docker/` 디렉토리에 모든 Docker 설정이 포함되어 있습니다. + +``` +docker/ +├── docker-compose.yml ← 메인 Compose 파일 +├── .env ← Compose 환경변수 +├── api/ +│ ├── Dockerfile ← PHP 8.4 + Nginx +│ ├── nginx.conf +│ ├── supervisord.conf +│ └── uploads.ini +├── mng/ +│ ├── Dockerfile ← PHP 8.4 + Nginx +│ ├── nginx.conf +│ ├── supervisord.conf +│ └── uploads.ini +├── react/ +│ └── Dockerfile ← Node 20 + Chromium (PDF용) +├── mysql/ +│ ├── init.sql ← 초기 DB/유저 생성 +│ └── my.cnf ← MySQL 설정 +├── nginx/ +│ ├── nginx.conf ← 리버스 프록시 설정 +│ └── ssl/ ← SSL 인증서 +└── 5130/ + └── Dockerfile ← 레거시 PHP 7.3 +``` + +### 3-2. 서비스 구성 + +| 서비스 | 이미지 | 내부 포트 | 역할 | +|--------|--------|-----------|------| +| **nginx** | nginx:latest | 80, 443 | 리버스 프록시, SSL 종료 | +| **api** | PHP 8.4-fpm | 9000 | REST API 백엔드 | +| **mng** | PHP 8.4-fpm | 9000 | 관리자 패널 | +| **react** | node:20-alpine | 3000 | Next.js 프론트엔드 | +| **mysql** | mysql:8.0 | 3306 | 데이터베이스 | +| **php73** | PHP 7.3-fpm | 9000 | 레거시 5130 앱 | + +### 3-3. 네트워크 + +모든 컨테이너는 `samnet` 브릿지 네트워크로 연결됩니다. + +``` +[브라우저] → [nginx:443] → api / mng / react (내부 라우팅) + → mysql (컨테이너명: sam-mysql-1) +``` + +--- + +## 4. hosts 파일 설정 + +로컬 도메인을 사용하기 위해 hosts 파일을 수정합니다. + +```bash +sudo nano /etc/hosts +``` + +아래 내용을 추가: + +``` +127.0.0.1 api.sam.kr mng.sam.kr admin.sam.kr dev.sam.kr design.sam.kr plan.sam.kr 5130.sam.kr +127.0.0.1 sam.kr www.sam.kr sales.sam.kr demo.sam.kr +``` + +--- + +## 5. SSL 인증서 설정 + +로컬 HTTPS를 위한 자체 서명 인증서를 생성합니다. + +### 5-1. mkcert 설치 및 초기화 + +```bash +# mkcert 설치 (처음 한 번) +brew install mkcert +mkcert -install # 로컬 CA를 시스템에 등록 +``` + +### 5-2. 와일드카드 인증서 생성 + +```bash +cd docker/nginx/ssl/ + +# *.sam.kr 와일드카드 인증서 생성 +mkcert "*.sam.kr" localhost 127.0.0.1 ::1 + +# 파일명 변경 (nginx.conf에서 참조하는 이름으로) +mv _wildcard.sam.kr+3.pem sam.kr.crt +mv _wildcard.sam.kr+3-key.pem sam.kr.key +``` + +### 5-3. 포트 포워딩 설정 (macOS) + +Docker가 443 포트를 4443으로 매핑하므로, 브라우저에서 표준 443 포트로 접속하려면 포트 포워딩이 필요합니다. + +```bash +# pfctl 규칙 적용 (443 → 4443 포워딩) +sudo pfctl -ef docker/nginx/ssl/pf-sam.conf +``` + +> **참고**: macOS 재부팅 시 이 설정은 초기화되므로 재부팅 후 다시 실행해야 합니다. + +**포트 포워딩 없이 사용하려면** `https://api.sam.kr:4443` 처럼 포트 번호를 직접 지정합니다. + +--- + +## 6. 환경변수 (.env) 설정 + +### 6-1. API (.env) + +```bash +cd api +cp .env.example .env +``` + +`.env` 파일에서 확인/수정할 항목: + +```env +# 앱 키 생성은 Docker 실행 후 컨테이너 내에서 수행 +# docker exec sam-api-1 php artisan key:generate + +APP_NAME="SAM API" +APP_ENV=local +APP_DEBUG=true +APP_URL=https://api.sam.kr/ + +# DB (Docker 환경에서는 docker-compose가 오버라이드) +DB_HOST=127.0.0.1 # 로컬 직접 접속 시 +# DB_HOST=sam-mysql-1 # Docker 환경 (자동 오버라이드) +DB_DATABASE=samdb +DB_USERNAME=samuser +DB_PASSWORD=sampass + +# Swagger +L5_SWAGGER_GENERATE_ALWAYS=true +L5_SWAGGER_CONST_HOST=https://api.sam.kr/ + +# Sanctum 토큰 (분 단위) +SANCTUM_ACCESS_TOKEN_EXPIRATION=120 +SANCTUM_REFRESH_TOKEN_EXPIRATION=10080 + +# 내부 통신 키 (MNG ↔ API) +INTERNAL_EXCHANGE_SECRET= # 팀 내부 문서에서 확인 +``` + +> **API 키, Firebase, AI 서비스 키** 등 민감한 값은 팀 내부 문서(노션)에서 별도 공유합니다. + +### 6-2. MNG (.env) + +```bash +cd mng +cp .env.example .env +``` + +```env +APP_NAME=SAM-MNG +APP_ENV=local +APP_DEBUG=true +APP_URL=https://mng.sam.kr + +DB_HOST=sam-mysql-1 +DB_DATABASE=samdb +DB_USERNAME=samuser +DB_PASSWORD=sampass + +# API 서버 연동 +API_BASE_URL=https://api.sam.kr + +# 내부 통신 키 (API와 동일한 값) +INTERNAL_EXCHANGE_SECRET= # API의 값과 동일하게 설정 +``` + +### 6-3. React (.env.local) + +```bash +cd react +cp .env.example .env.local +``` + +```env +NEXT_PUBLIC_APP_ENV=local +NEXT_PUBLIC_API_URL=https://api.sam.kr +NEXT_PUBLIC_FRONTEND_URL=https://dev.sam.kr +NEXT_PUBLIC_AUTH_MODE=sanctum + +# API Key (서버 사이드 전용 - NEXT_PUBLIC_ 접두사 붙이지 말 것!) +API_KEY= # 팀 내부 문서에서 확인 + +# 개발 도구 +NEXT_PUBLIC_DEV_TOOLBAR_ENABLED=false + +# Puppeteer (PDF 생성 - Docker에서는 자동 설정) +PUPPETEER_EXECUTABLE_PATH=/Applications/Google Chrome.app/Contents/MacOS/Google Chrome +``` + +### 6-4. docs / hotfix + +별도 환경변수 설정 불필요 (순수 마크다운 문서 저장소) + +--- + +## 7. Docker 실행 + +### 7-1. 최초 실행 + +```bash +cd docker + +# 이미지 빌드 + 컨테이너 시작 +docker compose up -d --build +``` + +> 첫 실행 시 이미지 빌드에 5~10분 소요될 수 있습니다. + +### 7-2. 초기 설정 (최초 1회) + +```bash +# API: 앱 키 생성 + 의존성 설치 + 마이그레이션 +docker exec sam-api-1 php artisan key:generate +docker exec sam-api-1 composer install +docker exec sam-api-1 php artisan migrate +docker exec sam-api-1 php artisan l5-swagger:generate + +# MNG: 앱 키 생성 + 의존성 설치 +docker exec sam-mng-1 php artisan key:generate +docker exec sam-mng-1 composer install +docker exec sam-mng-1 npm install +docker exec sam-mng-1 npm run build +``` + +> React는 Docker 컨테이너 시작 시 자동으로 `npm install` + `npm run dev`가 실행됩니다. + +### 7-3. 일상적인 시작/종료 + +```bash +cd docker + +# 시작 +docker compose up -d + +# 종료 +docker compose down + +# 로그 확인 +docker compose logs -f # 전체 +docker compose logs -f api # API만 +docker compose logs -f react # React만 + +# 컨테이너 상태 확인 +docker compose ps +``` + +--- + +## 8. 접속 확인 + +### 8-1. 로컬 도메인 + +| 서비스 | URL | 설명 | +|--------|-----|------| +| **프론트엔드** | `https://dev.sam.kr` | Next.js 사용자 화면 | +| **API 문서** | `https://api.sam.kr/api-docs/index.html` | Swagger UI | +| **관리자 패널** | `https://mng.sam.kr` | MNG 관리자 화면 | +| **관리자 (별칭)** | `https://admin.sam.kr` | MNG와 동일 | +| **디자인 시스템** | `https://design.sam.kr` | 컴포넌트 스토리북 | +| **레거시** | `https://5130.sam.kr` | 기존 PHP 시스템 | + +### 8-2. DB 접속 정보 + +| 항목 | 값 | +|------|-----| +| Host | `127.0.0.1` | +| Port | `3306` | +| Database | `samdb` | +| Username | `samuser` | +| Password | `sampass` | +| Root Password | `root` | +| 레거시 DB | `chandj` | + +> DBeaver, DataGrip, MySQL Workbench 등 원하는 DB 클라이언트로 접속 가능 + +### 8-3. 접속 테스트 + +```bash +# API 응답 확인 +curl -k https://api.sam.kr/api-docs/index.html + +# MySQL 접속 확인 +docker exec sam-mysql-1 mysql -u samuser -psampass -e "SHOW DATABASES;" +``` + +--- + +## 9. 저장소별 상세 정보 + +### 9-1. api/ (REST API 서버) + +| 항목 | 값 | +|------|-----| +| 프레임워크 | Laravel 12 (PHP 8.4) | +| 인증 | Sanctum (토큰 기반) | +| API 문서 | Swagger (l5-swagger) | +| DB | MySQL 8.0 (samdb) | +| 역할 | 모든 비즈니스 로직의 중심, 프론트/관리자 모두 이 API 사용 | + +**주요 명령어:** + +```bash +# 컨테이너 진입 +docker exec -it sam-api-1 bash + +# 마이그레이션 +php artisan migrate +php artisan migrate:status + +# Swagger 재생성 +php artisan l5-swagger:generate + +# 코드 포매터 +./vendor/bin/pint + +# 캐시 초기화 +php artisan cache:clear +php artisan config:clear +php artisan route:clear +``` + +**핵심 디렉토리:** + +``` +api/ +├── app/ +│ ├── Http/Controllers/Api/V1/ ← API 컨트롤러 +│ ├── Http/Requests/ ← FormRequest (검증) +│ ├── Models/ ← Eloquent 모델 +│ ├── Services/ ← 비즈니스 로직 (핵심) +│ ├── Swagger/v1/ ← Swagger 문서 클래스 +│ └── Helpers/ApiResponse.php ← 응답 헬퍼 +├── database/migrations/ ← DB 마이그레이션 +├── routes/api.php ← API 라우트 +└── lang/ko/ ← 한국어 메시지 +``` + +### 9-2. mng/ (관리자 패널) + +| 항목 | 값 | +|------|-----| +| 프레임워크 | Laravel 12 (PHP 8.4) | +| 프론트엔드 | Blade + Tailwind CSS + HTMX + DaisyUI | +| 역할 | 시스템 관리, 메뉴/권한 관리, 테넌트 관리 | + +**주요 명령어:** + +```bash +# 컨테이너 진입 +docker exec -it sam-mng-1 bash + +# 프론트 에셋 빌드 +npm run build # 프로덕션 +npm run dev # 개발 (HMR) + +# 코드 포매터 +./vendor/bin/pint +``` + +**핵심 디렉토리:** + +``` +mng/ +├── app/ +│ ├── Http/Controllers/ ← 웹 컨트롤러 +│ ├── Models/ ← 독립 모델 (API와 별개) +│ └── Services/ ← 비즈니스 로직 +├── resources/views/ ← Blade 템플릿 +├── routes/web.php ← 웹 라우트 +└── public/ ← 정적 파일 +``` + +> **주의**: MNG에서는 DB 마이그레이션을 생성/실행하지 않습니다. DB 변경은 반드시 API 프로젝트에서 수행합니다. + +### 9-3. react/ (Next.js 프론트엔드) + +| 항목 | 값 | +|------|-----| +| 프레임워크 | Next.js 15 (React 19, TypeScript) | +| 스타일링 | Tailwind CSS 4 | +| UI 라이브러리 | shadcn/ui (Radix UI 기반) | +| 상태관리 | Zustand | +| 폼 검증 | Zod + React Hook Form | +| 역할 | 사용자용 ERP 프론트엔드 | + +**주요 명령어:** + +```bash +# React는 Docker 컨테이너가 자동으로 dev 서버를 실행합니다. +# 수동 실행이 필요한 경우: +docker exec -it sam-react-1 sh + +npm run dev # 개발 서버 +npm run build # 프로덕션 빌드 +npm run lint # ESLint +``` + +**핵심 디렉토리:** + +``` +react/ +├── src/ +│ ├── app/ ← Next.js App Router 페이지 +│ ├── components/ +│ │ ├── ui/ ← shadcn/ui 원자 컴포넌트 +│ │ ├── molecules/ ← 조합 컴포넌트 +│ │ ├── organisms/ ← 복합 컴포넌트 +│ │ └── [도메인]/ ← 도메인별 컴포넌트 +│ ├── lib/ ← 유틸리티, API 헬퍼 +│ └── stores/ ← Zustand 스토어 +├── public/ ← 정적 파일 +└── next.config.ts ← Next.js 설정 +``` + +**핵심 규칙:** +- 모든 페이지는 `'use client'` 선언 필수 (폐쇄형 ERP, SSR 불필요) +- API 호출은 Server Action을 통해 수행 (HttpOnly 쿠키 프록시) +- `buildApiUrl()` 유틸리티 필수 사용 + +### 9-4. docs/ (기술 문서) + +| 항목 | 값 | +|------|-----| +| 내용 | 개발 가이드, API 스펙, 기획 문서, 표준 | +| 형식 | Markdown | +| 설치 | 없음 (문서만 관리) | + +``` +docs/ +├── INDEX.md ← 문서 인덱스 (여기부터 시작) +├── dev/ +│ ├── dev_plans/ ← 개발 계획 문서 +│ ├── standards/ ← 코드/아키텍처 표준 +│ ├── guides/ ← 셋업/사용 가이드 +│ ├── changes/ ← 변경 로그 +│ └── deploys/ ← 배포 문서 +├── features/ ← 기능 스펙 +├── frontend/ ← 프론트엔드 API 스펙 +├── rules/ ← 비즈니스 규칙 +└── system/ ← 시스템 아키텍처 +``` + +### 9-5. hotfix/ (테스트/QA) + +| 항목 | 값 | +|------|-----| +| 내용 | E2E 테스트 결과, 버그 리포트, 테스트 케이스 | +| 형식 | Markdown + 스크린샷 | +| 설치 | 없음 (문서만 관리) | + +``` +hotfix/ +├── e2e/ ← E2E 테스트 정의 +├── testcase/ ← 테스트 케이스 문서 +├── reports/ ← 테스트 실행 결과 +├── screenshots/ ← 시각적 테스트 증거 +├── research/ ← 조사 자료 +├── sam.code-workspace ← VS Code 워크스페이스 +├── *-Test-Report_*.md ← 개별 테스트 보고서 +└── Fail-*_*.md ← 실패 케이스 보고서 +``` + +--- + +## 10. 주요 아키텍처 개념 + +### 10-1. 멀티테넌트 + +- 모든 데이터에 `tenant_id` 컬럼이 존재 +- `BelongsToTenant` 글로벌 스코프가 자동으로 테넌트 필터링 +- 하나의 DB에서 여러 회사(테넌트)의 데이터를 격리 + +### 10-2. 데이터 흐름 + +``` +[사용자 브라우저] + │ + ▼ +[Next.js (react/)] ──Server Action──▶ [Laravel API (api/)] ──▶ [MySQL] + │ +[관리자 브라우저] │ + │ │ + ▼ │ +[Laravel MNG (mng/)] ──내부 API 호출──────────┘ +``` + +### 10-3. 인증 방식 + +| 클라이언트 | 인증 방식 | 설명 | +|-----------|----------|------| +| React (웹) | Sanctum Cookie | HttpOnly 쿠키, Server Action 프록시 | +| MNG (관리자) | 세션 + 내부 HMAC | Laravel 세션 + API 내부 통신 키 | +| 외부 연동 | API Key + Bearer | Sanctum 토큰 | + +--- + +## 11. 자주 쓰는 명령어 + +### Docker 관련 + +```bash +# 전체 시작/종료 +cd docker && docker compose up -d +cd docker && docker compose down + +# 컨테이너 재시작 +docker restart sam-api-1 +docker restart sam-react-1 + +# 이미지 재빌드 (Dockerfile 수정 후) +docker compose build --no-cache api +docker compose up -d api + +# 볼륨 포함 완전 초기화 (⚠️ DB 데이터 삭제됨) +docker compose down -v +``` + +### API 개발 + +```bash +docker exec sam-api-1 php artisan migrate # 마이그레이션 +docker exec sam-api-1 php artisan migrate:rollback # 롤백 +docker exec sam-api-1 php artisan l5-swagger:generate # Swagger 재생성 +docker exec sam-api-1 ./vendor/bin/pint # 코드 포매팅 +docker exec sam-api-1 php artisan tinker # REPL +docker exec sam-api-1 php artisan route:list # 라우트 목록 +``` + +### MNG 개발 + +```bash +docker exec sam-mng-1 php artisan tinker +docker exec sam-mng-1 npm run dev # Vite HMR +docker exec sam-mng-1 npm run build # 에셋 빌드 +docker exec sam-mng-1 ./vendor/bin/pint +``` + +### MySQL 접속 + +```bash +# CLI 접속 +docker exec -it sam-mysql-1 mysql -u samuser -psampass samdb + +# 특정 쿼리 실행 +docker exec sam-mysql-1 mysql -u samuser -psampass samdb -e "SHOW TABLES;" +``` + +--- + +## 12. 트러블슈팅 + +### SSL 인증서 오류 (브라우저에서 "안전하지 않음") + +```bash +# mkcert CA가 설치되지 않은 경우 +mkcert -install + +# 인증서 재생성 +cd docker/nginx/ssl +mkcert "*.sam.kr" localhost 127.0.0.1 ::1 +mv _wildcard.sam.kr+3.pem sam.kr.crt +mv _wildcard.sam.kr+3-key.pem sam.kr.key + +# Nginx 재시작 +docker restart sam-nginx-1 +``` + +### 도메인 접속이 안 될 때 + +```bash +# hosts 파일 확인 +cat /etc/hosts | grep sam + +# DNS 캐시 초기화 (macOS) +sudo dscacheutil -flushcache +sudo killall -HUP mDNSResponder + +# Nginx 설정 유효성 확인 +docker exec sam-nginx-1 nginx -t +``` + +### 포트 포워딩 확인 (443 → 4443) + +```bash +# 현재 규칙 확인 +sudo pfctl -s rules | grep 4443 + +# 규칙 재적용 +sudo pfctl -ef docker/nginx/ssl/pf-sam.conf + +# 규칙 비활성화 (필요 시) +sudo pfctl -d +``` + +### DB 접속 오류 + +```bash +# MySQL 컨테이너 상태 확인 +docker compose ps mysql + +# MySQL 로그 확인 +docker compose logs mysql + +# 수동 접속 테스트 +docker exec sam-mysql-1 mysql -u root -proot -e "SELECT 1;" +``` + +### React 빌드/HMR 오류 + +```bash +# node_modules 초기화 +docker exec sam-react-1 rm -rf node_modules .next +docker exec sam-react-1 npm install +docker restart sam-react-1 +``` + +### composer/npm 의존성 문제 + +```bash +# API +docker exec sam-api-1 composer install --no-cache +docker exec sam-api-1 composer dump-autoload + +# MNG +docker exec sam-mng-1 composer install --no-cache +docker exec sam-mng-1 npm ci +``` + +--- + +## 13. Git 워크플로우 + +### 브랜치 전략 + +| 브랜치 | 역할 | 규칙 | +|--------|------|------| +| `main` | 배포용 | squash merge로만 올림, 직접 커밋 금지 | +| `develop` | 일상 작업 | 자유롭게 커밋 | +| `feature/*` | 대형 기능 | 선택적 사용 (1주일+ 작업) | + +### 커밋 메시지 형식 + +``` +type: 간결한 설명 + +type 종류: + feat: 새 기능 + fix: 버그 수정 + refactor: 리팩토링 + docs: 문서 + chore: 설정, 빌드 + style: 코드 포매팅 +``` + +### 일상 워크플로우 + +```bash +# 1. 작업 시작 +cd api +git checkout develop +git pull origin develop + +# 2. 작업 수행 + 커밋 +git add <파일들> +git commit -m "feat: 수주관리 삭제 기능 추가" + +# 3. 푸시 +git push origin develop +``` + +--- + +## 14. 셋팅 체크리스트 + +아래 항목을 순서대로 확인하며 셋팅을 완료합니다. + +- [ ] Docker Desktop 설치 및 실행 +- [ ] Git 설치 및 Gitea 계정 발급 +- [ ] 5개 저장소 클론 완료 +- [ ] `/etc/hosts` 파일 수정 +- [ ] mkcert 설치 + SSL 인증서 생성 +- [ ] pfctl 포트 포워딩 설정 +- [ ] api/.env 설정 (`.env.example` → `.env`) +- [ ] mng/.env 설정 +- [ ] react/.env.local 설정 +- [ ] API Key, HMAC Key 등 민감 값 입력 (팀 공유 문서 참조) +- [ ] `docker compose up -d --build` 실행 +- [ ] 초기 설정 실행 (key:generate, composer install, migrate) +- [ ] `https://api.sam.kr/api-docs/index.html` 접속 확인 +- [ ] `https://mng.sam.kr` 접속 확인 +- [ ] `https://dev.sam.kr` 접속 확인 +- [ ] DB 클라이언트로 `127.0.0.1:3306` 접속 확인 + +--- + +## 15. 참고 문서 + +| 문서 | 경로 | 설명 | +|------|------|------| +| 문서 인덱스 | `docs/INDEX.md` | 전체 문서 목록 | +| API 규칙 | `API_RULES.md` | API 개발 규칙 | +| 개발 명령어 | `DEV_COMMANDS.md` | 자주 쓰는 명령어 모음 | +| 품질 체크리스트 | `QUALITY_CHECKLIST.md` | 코드 품질 체크 항목 | +| 빠른 참조 | `SAM_QUICK_REFERENCE.md` | 핵심 규칙 요약 | +| SSL 가이드 | `docker/nginx/ssl/SSL_SETUP_GUIDE.md` | SSL 상세 설정 | + +--- + +> 문의사항은 팀 슬랙 채널 또는 팀장에게 문의하세요. \ No newline at end of file diff --git a/sam/docs/features/academy/fire-shutter-image-prompts.md b/features/academy/fire-shutter-image-prompts.md similarity index 100% rename from sam/docs/features/academy/fire-shutter-image-prompts.md rename to features/academy/fire-shutter-image-prompts.md diff --git a/sam/docs/features/approvals/README.md b/features/approvals/README.md similarity index 99% rename from sam/docs/features/approvals/README.md rename to features/approvals/README.md index c7b660a..a43521c 100644 --- a/sam/docs/features/approvals/README.md +++ b/features/approvals/README.md @@ -22,6 +22,7 @@ SAM MNG 전자결재 시스템. 기안부터 최종 승인, 반려, 회수, 보 | [workflows.md](workflows.md) | 상세 워크플로우 (승인/반려/회수/보류/전결/복사재기안) | | [api-reference.md](api-reference.md) | API 엔드포인트 명세 | | [ui-screens.md](ui-screens.md) | 화면별 UI 구성 및 동작 | +| [db-changes-and-model-sync.md](db-changes-and-model-sync.md) | DB 변경사항 및 API/MNG 모델 동기화 현황 | ### 1.3 구현 현황 diff --git a/sam/docs/features/approvals/api-reference.md b/features/approvals/api-reference.md similarity index 100% rename from sam/docs/features/approvals/api-reference.md rename to features/approvals/api-reference.md diff --git a/features/approvals/db-changes-and-model-sync.md b/features/approvals/db-changes-and-model-sync.md new file mode 100644 index 0000000..e59cd2b --- /dev/null +++ b/features/approvals/db-changes-and-model-sync.md @@ -0,0 +1,286 @@ +# 결재관리 DB 변경사항 및 API 모델 동기화 현황 + +> **작성일**: 2026-03-09 +> **상태**: 조사 완료 +> **관련**: [README.md](README.md) | [API 명세](api-reference.md) + +--- + +## 1. 개요 + +### 1.1 목적 + +2026-02-27 ~ 2026-03-05 기간에 결재관리 테이블에 대규모 컬럼 추가가 이루어졌다. 이 문서는 변경된 DB 스키마와 API/MNG 프로젝트 간 모델 동기화 상태를 기록한다. + +### 1.2 핵심 발견 + +- 마이그레이션 **15개** 실행 (API 프로젝트에서 관리) +- MNG 모델: ✅ 모든 신규 컬럼 반영 완료 +- API 모델: ❌ **`$fillable`/`$casts` 미반영** — 오류 원인 가능성 + +--- + +## 2. 마이그레이션 변경 타임라인 + +### 2.1 Phase 2 확장 (2026-02-27) + +| 마이그레이션 파일 | 대상 테이블 | 작업 | +|------------------|-----------|------| +| `add_columns_to_approvals_table` | `approvals` | `line_id`, `body`, `is_urgent`, `department_id` 추가 | +| `add_columns_to_approval_steps_table` | `approval_steps` | `approver_name`, `approver_department`, `approver_position` 추가 | +| `add_phase2_columns_to_approval_steps_table` | `approval_steps` | `parallel_group`, `acted_by`, `approval_type` 추가 | +| `add_phase2_columns_to_approvals_table` | `approvals` | `recall_reason`, `parent_doc_id` 추가 | +| `create_approval_delegations_table` | `approval_delegations` | 위임 테이블 신규 생성 | +| `add_linkable_to_approvals_table` | `approvals` | `linkable_type`, `linkable_id` 추가 (다형성) | + +### 2.2 도메인 연동 (2026-02-28) + +| 마이그레이션 파일 | 대상 테이블 | 작업 | +|------------------|-----------|------| +| `add_approval_id_to_leaves_table` | `leaves` | `approval_id` FK 추가 | +| `insert_leave_approval_form` | `approval_forms` | 휴가신청 양식 데이터 등록 | + +### 2.3 양식 확장 (2026-03-03 ~ 03-04) + +| 마이그레이션 파일 | 대상 테이블 | 작업 | +|------------------|-----------|------| +| `insert_attendance_approval_forms` | `approval_forms` | 근태신청, 사유서 양식 등록 | +| `add_body_template_to_approval_forms` | `approval_forms` | `body_template` 컬럼 추가 | +| `insert_expense_approval_form` | `approval_forms` | 지출결의서 양식 + body_template 등록 | +| `update_expense_approval_form_body_template` | `approval_forms` | 지출결의서 body_template 고도화 | + +### 2.4 추적 기능 (2026-03-05) + +| 마이그레이션 파일 | 대상 테이블 | 작업 | +|------------------|-----------|------| +| `add_drafter_read_at_to_approvals_table` | `approvals` | `drafter_read_at` 추가 | +| `add_resubmit_count_to_approvals_table` | `approvals` | `resubmit_count` 추가 | +| `add_rejection_history_to_approvals_table` | `approvals` | `rejection_history` 추가 | + +--- + +## 3. 추가된 컬럼 상세 + +### 3.1 `approvals` 테이블 (11개 컬럼 추가) + +| 컬럼 | 타입 | 기본값 | 추가일 | 용도 | +|------|------|--------|--------|------| +| `line_id` | BIGINT FK NULL | NULL | 02-27 | 결재선 템플릿 참조 | +| `body` | LONGTEXT NULL | NULL | 02-27 | 문서 본문 HTML | +| `is_urgent` | BOOLEAN | false | 02-27 | 긴급 여부 | +| `department_id` | BIGINT NULL | NULL | 02-27 | 기안 부서 | +| `recall_reason` | TEXT NULL | NULL | 02-27 | 회수 사유 | +| `parent_doc_id` | BIGINT FK NULL | NULL | 02-27 | 재기안 원본 문서 | +| `linkable_type` | VARCHAR NULL | NULL | 02-27 | 다형성 모델 타입 | +| `linkable_id` | BIGINT NULL | NULL | 02-27 | 다형성 모델 ID | +| `drafter_read_at` | TIMESTAMP NULL | NULL | 03-05 | 기안자 열람 시각 | +| `resubmit_count` | TINYINT UNSIGNED | 0 | 03-05 | 재상신 횟수 | +| `rejection_history` | JSON NULL | NULL | 03-05 | 반려 이력 배열 | + +### 3.2 `approval_steps` 테이블 (6개 컬럼 추가) + +| 컬럼 | 타입 | 기본값 | 추가일 | 용도 | +|------|------|--------|--------|------| +| `approver_name` | VARCHAR(50) NULL | NULL | 02-27 | 결재자명 스냅샷 | +| `approver_department` | VARCHAR(100) NULL | NULL | 02-27 | 결재자 부서 스냅샷 | +| `approver_position` | VARCHAR(50) NULL | NULL | 02-27 | 결재자 직급 스냅샷 | +| `parallel_group` | INT NULL | NULL | 02-27 | 병렬 결재 그룹 (Phase 3) | +| `acted_by` | BIGINT FK NULL | NULL | 02-27 | 실제 처리자 (대결) | +| `approval_type` | VARCHAR(20) | 'normal' | 02-27 | normal/pre_decided/delegated | + +### 3.3 `approval_forms` 테이블 (1개 컬럼 추가) + +| 컬럼 | 타입 | 기본값 | 추가일 | 용도 | +|------|------|--------|--------|------| +| `body_template` | TEXT NULL | NULL | 03-04 | HTML 양식 렌더링 템플릿 | + +### 3.4 `approval_delegations` 테이블 (신규 생성) + +| 컬럼 | 타입 | 설명 | +|------|------|------| +| `tenant_id` | BIGINT FK | 테넌트 격리 | +| `delegator_id` | BIGINT FK | 위임자 | +| `delegate_id` | BIGINT FK | 대리인 | +| `start_date` | DATE | 위임 시작일 | +| `end_date` | DATE | 위임 종료일 | +| `form_ids` | JSON NULL | 대상 양식 (NULL=전체) | +| `notify_delegator` | BOOLEAN | 대결 시 보고 여부 | +| `is_active` | BOOLEAN | 활성 여부 | +| `reason` | VARCHAR(200) | 위임 사유 | + +--- + +## 4. API/MNG 모델 동기화 현황 + +### 4.1 Approval 모델 비교 + +| 항목 | MNG (`mng/app/Models/Approvals/Approval.php`) | API (`api/app/Models/Tenants/Approval.php`) | +|------|:---:|:---:| +| `line_id` in $fillable | ✅ | ❌ | +| `body` in $fillable | ✅ | ❌ | +| `is_urgent` in $fillable/$casts | ✅ boolean | ❌ | +| `department_id` in $fillable | ✅ | ❌ | +| `recall_reason` in $fillable | ✅ | ❌ | +| `parent_doc_id` in $fillable | ✅ | ❌ | +| `linkable_type/id` in $fillable | ✅ | ✅ | +| `drafter_read_at` in $fillable/$casts | ✅ datetime | ❌ | +| `resubmit_count` in $fillable/$casts | ✅ integer | ❌ | +| `rejection_history` in $fillable/$casts | ✅ array | ❌ | + +### 4.2 ApprovalStep 모델 비교 + +| 항목 | MNG | API | +|------|:---:|:---:| +| `approver_name` in $fillable | ✅ | ❌ | +| `approver_department` in $fillable | ✅ | ❌ | +| `approver_position` in $fillable | ✅ | ❌ | +| `parallel_group` in $fillable | ✅ | ❌ | +| `acted_by` in $fillable | ✅ | ❌ | +| `approval_type` in $fillable | ✅ | ❌ | + +### 4.3 ApprovalForm 모델 비교 + +| 항목 | MNG | API | +|------|:---:|:---:| +| `body_template` in $fillable | ✅ | ❌ | + +### 4.4 ApprovalDelegation 모델 + +| 항목 | MNG | API | +|------|:---:|:---:| +| 모델 파일 존재 | ✅ | ❌ 미생성 | + +--- + +## 5. 오류 영향 분석 + +### 5.1 API 모델 미반영으로 인한 잠재적 오류 + +API 프로젝트의 모델 `$fillable`에 신규 컬럼이 누락되어, API 엔드포인트를 통한 결재 문서 처리 시 다음 오류가 발생할 수 있다: + +| 증상 | 원인 | 영향 범위 | +|------|------|----------| +| `create()`/`update()` 시 신규 필드 저장 안 됨 | `$fillable` 미포함 → mass assignment 차단 | API v1 결재 CRUD | +| JSON 필드(`rejection_history`) 문자열로 반환 | `$casts` 미정의 → 타입 변환 안 됨 | API 응답 파싱 오류 | +| `drafter_read_at` 날짜 비교 실패 | `$casts` datetime 미정의 → Carbon 미변환 | 열람 추적 기능 | +| `is_urgent` 비교 오류 | `$casts` boolean 미정의 → 문자열 비교 | 긴급 필터링 | +| 위임(delegation) 기능 완전 불가 | 모델 자체 미생성 | Phase 3 기능 전체 | + +### 5.2 MNG는 정상 + +MNG 프로젝트의 모델은 모든 신규 컬럼이 `$fillable`, `$casts`, `$attributes`에 반영되어 있으며, `ApprovalService`에서 정상 사용 중이다. + +``` +MNG 정상 동작 확인 기능: +✅ 반려 이력 저장 (rejection_history) +✅ 재상신 횟수 추적 (resubmit_count) +✅ 기안자 열람 추적 (drafter_read_at) +✅ 결재자 스냅샷 저장 (approver_name/department/position) +✅ 전결 처리 (approval_type = pre_decided) +✅ 회수 사유 기록 (recall_reason) +``` + +--- + +## 6. 수정 필요 파일 목록 + +### 6.1 API 모델 업데이트 필요 + +| 파일 | 수정 내용 | +|------|----------| +| `api/app/Models/Tenants/Approval.php` | `$fillable`에 9개 필드, `$casts`에 4개 필드 추가 | +| `api/app/Models/Tenants/ApprovalStep.php` | `$fillable`에 6개 필드 추가 | +| `api/app/Models/Tenants/ApprovalForm.php` | `$fillable`에 `body_template` 추가 | +| `api/app/Models/Tenants/ApprovalDelegation.php` | 모델 파일 신규 생성 | + +### 6.2 Approval.php 수정 상세 + +**`$fillable` 추가 필요:** + +```php +'line_id', +'body', +'is_urgent', +'department_id', +'recall_reason', +'parent_doc_id', +'drafter_read_at', +'resubmit_count', +'rejection_history', +``` + +**`$casts` 추가 필요:** + +```php +'drafter_read_at' => 'datetime', +'resubmit_count' => 'integer', +'rejection_history' => 'array', +'is_urgent' => 'boolean', +``` + +### 6.3 ApprovalStep.php 수정 상세 + +**`$fillable` 추가 필요:** + +```php +'approver_name', +'approver_department', +'approver_position', +'parallel_group', +'acted_by', +'approval_type', +``` + +### 6.4 ApprovalForm.php 수정 상세 + +**`$fillable` 추가 필요:** + +```php +'body_template', +``` + +--- + +## 7. 연관 테이블 참조 변경 + +결재 시스템과 연동된 다른 테이블의 변경사항: + +| 테이블 | 추가 컬럼 | 추가일 | 용도 | +|--------|----------|--------|------| +| `leaves` | `approval_id` (BIGINT FK) | 02-28 | 휴가 ↔ 결재 연동 | +| `purchases` | `approval_id` (BIGINT FK) | (기존) | 구매 ↔ 결재 연동 | + +--- + +## 8. 등록된 결재 양식 (13종) + +2026-02-28 ~ 03-07 기간에 마이그레이션으로 등록된 양식: + +| 코드 | 양식명 | 카테고리 | 등록일 | +|------|--------|---------|--------| +| `leave` | 휴가신청서 | request | 02-28 | +| `attendance_request` | 근태신청서 | request | 03-03 | +| `reason_report` | 사유서 | request | 03-03 | +| `expense` | 지출결의서 | expense | 03-04 | +| `employment_cert` | 재직증명서 | request | 03-05 | +| `career_cert` | 경력증명서 | request | 03-05 | +| `appointment_cert` | 위촉증명서 | request | 03-05 | +| `resignation` | 사직서 | request | 03-06 | +| `seal_usage` | 사용인감계 | request | 03-06 | +| `delegation` | 위임장 | request | 03-06 | +| `board_minutes` | 이사회의사록 | request | 03-06 | +| `quotation` | 견적서 | request | 03-06 | +| `official_letter` | 공문서 | request | 03-07 | + +--- + +## 관련 문서 + +- [결재관리 시스템 개요](README.md) — 아키텍처, 상태 관리, 권한 +- [API 명세](api-reference.md) — 20개 엔드포인트 상세 +- [워크플로우 상세](workflows.md) — 승인/반려/회수/보류/전결 흐름 +- [기획서 원본](../../plans/approval-management-system-plan.md) — Phase 1~4 전체 기획 + +--- + +**최종 업데이트**: 2026-03-09 diff --git a/sam/docs/features/approvals/form-types.md b/features/approvals/form-types.md similarity index 100% rename from sam/docs/features/approvals/form-types.md rename to features/approvals/form-types.md diff --git a/sam/docs/features/approvals/ui-screens.md b/features/approvals/ui-screens.md similarity index 100% rename from sam/docs/features/approvals/ui-screens.md rename to features/approvals/ui-screens.md diff --git a/sam/docs/features/approvals/workflows.md b/features/approvals/workflows.md similarity index 100% rename from sam/docs/features/approvals/workflows.md rename to features/approvals/workflows.md diff --git a/features/barobill-kakaotalk/README.md b/features/barobill-kakaotalk/README.md index 151f1fb..09f1909 100644 --- a/features/barobill-kakaotalk/README.md +++ b/features/barobill-kakaotalk/README.md @@ -1,9 +1,9 @@ # 바로빌 카카오톡 (알림톡/친구톡) 연동 -> **문서 버전**: 1.0 +> **문서 버전**: 1.1 > **작성일**: 2026-02-14 -> **최종 수정**: 2026-02-24 -> **상태**: 운영 중 (전자계약 알림톡 발송 완료) +> **최종 수정**: 2026-02-27 +> **상태**: 운영 중 (알림톡 + SMS + 환경별 분기 완료) > **대상 프로젝트**: MNG --- @@ -25,7 +25,11 @@ | 채널 연동 (바로빌↔카카오) | **완료** (2026-02-20) | 바로빌 관리 URL에서 채널 연동 처리 | | 바로빌 파트너 과금 설정 | **완료** (2026-02-23) | 바로빌 측에서 파트너사 과금 설정 완료 | | 알림톡 템플릿 v1 검수 | **완료** (2026-02-22) | `전자계약_서명요청`, `전자계약_리마인드` 2종 승인 | -| 알림톡 템플릿 v2 검수 | **심사 중** (2026-02-24 접수) | 버튼 URL에 `#{토큰}` 변수 포함 2종 재등록 | +| 알림톡 템플릿 v2 검수 | **완료** (2026-02-25) | 버튼 URL에 `#{토큰}` 변수 포함 3종 승인 | +| 알림톡 `전자계약_완료` | **완료** (2026-02-26) | 서명 완료 알림 발송용 템플릿 승인 | +| 역할 기반 알림 분기 | **완료** (2026-02-26) | 본사=이메일, 상대방=알림톡/SMS | +| 환경별 템플릿 분기 | **완료** (2026-02-27) | `_DEV` 접미사 개발 템플릿 등록 | +| DEV 템플릿 검수 | **심사 중** (2026-02-27 접수) | 개발서버용 3종 (`admin.codebridge-x.com`) | > 상세 등록 가이드: [카카오톡 알림톡 채널 및 템플릿 등록 가이드](../../guides/카카오톡-알림톡-채널-템플릿-등록.md) @@ -310,14 +314,24 @@ $params = [ | 전달 결과 확인 (2단계) | **구현 완료** | 2026-02-24 | | 로그인 페이지 서명 확인 | **성공** | 2026-02-24 | -### 5.3 대기 중인 항목 +### 5.3 완료된 추가 항목 (2026-02-26~27) | 항목 | 상태 | 비고 | |------|------|------| -| 템플릿 v2 승인 | **심사 중** | 버튼 URL에 `#{토큰}` 변수 포함 | -| v2 승인 후 코드 수정 | **대기** | 동적 서명 URL을 버튼에 전달 | -| `전자계약_완료` 템플릿 | **미등록** | 서명 완료 알림 발송용 | -| SMS 대체발송 | **미설정** | 발신번호 등록 필요 | +| 템플릿 v2 승인 | **완료** | 버튼 URL에 `#{토큰}` 변수 포함 3종 승인 | +| `전자계약_완료` 템플릿 | **완료** | 서명 완료 알림 발송 — PDF 다운로드 버튼 | +| 역할 기반 알림 분기 | **완료** | 본사(creator)=이메일, 상대방(counterpart)=알림톡 | +| OTP SMS 발송 | **완료** | 상대방에게 SMS로 인증코드 발송 | +| 환경별 템플릿 분기 | **완료** | `resolveTemplateName()` — `_DEV` 접미사 자동 적용 | +| 서명 PDF 재생성 | **완료** | `downloadDocument()`에서 완료 계약 PDF 자동 재생성 | + +> 상세 가이드: [전자계약 알림톡/SMS 환경별 설정 가이드](./esign-notification-guide.md) + +### 5.4 대기 중인 항목 + +| 항목 | 상태 | 비고 | +|------|------|------| +| DEV 템플릿 검수 | **심사 중** | `admin.codebridge-x.com` 도메인 3종 | | 친구톡 발송 | **대기** | 채널 친구 추가 후 가능 | | 대량 발송 | **대기** | 단건 안정화 후 | @@ -377,37 +391,7 @@ $buttons = [ --- -## 8. API 측 바로빌 연동 (세금계산서) - -MNG의 카카오톡 연동 외에, API(`api/`)에서도 바로빌 서비스를 사용한다: - -### 8.1 바로빌 설정 API - -| HTTP | URI | 설명 | -|------|-----|------| -| GET | `/v1/barobill-settings` | 바로빌 설정 조회 | -| PUT | `/v1/barobill-settings` | 바로빌 설정 저장 (사업자번호, 인증키, 자동발행 등) | -| POST | `/v1/barobill-settings/test-connection` | 연동 테스트 | - -### 8.2 세금계산서 발행 - -`BarobillService`는 세금계산서 발행/취소/국세청 전송 상태 조회도 담당한다: - -| API 메서드 | 설명 | -|-----------|------| -| `issueTaxInvoice()` | 세금계산서 발행 (RegistAndIssueTaxInvoice) | -| `cancelTaxInvoice()` | 세금계산서 취소 | -| `checkNtsSendStatus()` | 국세청 전송 상태 조회 | -| `checkBusinessNumber()` | 사업자번호 휴폐업 조회 | -| `testConnection()` | GetAccessToken으로 연동 테스트 | - -**인증키 보안:** `cert_key`는 Laravel `Crypt` 파사드로 자동 암/복호화 - -→ 상세: [세금계산서 관리](../finance/tax-invoices.md) - ---- - -## 9. 참고 자료 +## 8. 참고 자료 - [바로빌 API 문서](https://dev.barobill.co.kr) - [카카오비즈니스 채널 관리](https://business.kakao.com) @@ -420,6 +404,7 @@ MNG의 카카오톡 연동 외에, API(`api/`)에서도 바로빌 서비스를 | 날짜 | 버전 | 변경 내용 | |------|------|----------| +| 2026-02-27 | 1.1 | 역할 기반 알림, OTP SMS, 환경별 템플릿 분기, 완료 알림톡 추가 | | 2026-02-24 | 1.0 | 전자계약 알림톡 연동 완료, 트러블슈팅 문서화, v2 템플릿 가이드 추가 | | 2026-02-14 | 0.2 | 전자계약(E-Sign) 알림톡 연동 활용 계획 추가 | | 2026-02-14 | 0.1 | 초안 작성 - 코드 구현 완료, 실 서비스 연동 대기 | diff --git a/sam/docs/features/barobill-kakaotalk/esign-notification-guide.md b/features/barobill-kakaotalk/esign-notification-guide.md similarity index 100% rename from sam/docs/features/barobill-kakaotalk/esign-notification-guide.md rename to features/barobill-kakaotalk/esign-notification-guide.md diff --git a/sam/docs/features/business-card-request.md b/features/business-card-request.md similarity index 100% rename from sam/docs/features/business-card-request.md rename to features/business-card-request.md diff --git a/sam/docs/features/credit-evaluation/README.md b/features/credit-evaluation/README.md similarity index 100% rename from sam/docs/features/credit-evaluation/README.md rename to features/credit-evaluation/README.md diff --git a/features/documents/README.md b/features/documents/README.md index 91f3dd9..ab1f4d5 100644 --- a/features/documents/README.md +++ b/features/documents/README.md @@ -111,10 +111,12 @@ DRAFT → PENDING → APPROVED ## 관련 문서 +- [MNG 문서관리 시스템 상세](mng-document-system.md) — MNG 화면 구성, 탭별 기능, 서식 빌더, EAV 저장 패턴 상세 +- [MNG 문서양식관리](mng-document-template.md) — 서식 생성/편집, Legacy/Block Builder, 프리셋, 연결품목 관리 - [DB 스키마 — 문서/전자서명](../../system/database/documents.md) - [게시판 시스템](../boards/README.md) — 유사한 EAV 패턴 적용 - Swagger: `/api-docs` → Documents 섹션 --- -**최종 업데이트**: 2026-02-27 +**최종 업데이트**: 2026-03-06 diff --git a/sam/docs/features/documents/mng-document-system.md b/features/documents/mng-document-system.md similarity index 100% rename from sam/docs/features/documents/mng-document-system.md rename to features/documents/mng-document-system.md diff --git a/sam/docs/features/documents/mng-document-template.md b/features/documents/mng-document-template.md similarity index 100% rename from sam/docs/features/documents/mng-document-template.md rename to features/documents/mng-document-template.md diff --git a/sam/docs/features/planning/README.md b/features/planning/README.md similarity index 100% rename from sam/docs/features/planning/README.md rename to features/planning/README.md diff --git a/sam/docs/features/planning/construction-photos.md b/features/planning/construction-photos.md similarity index 100% rename from sam/docs/features/planning/construction-photos.md rename to features/planning/construction-photos.md diff --git a/sam/docs/features/planning/meeting-minutes.md b/features/planning/meeting-minutes.md similarity index 100% rename from sam/docs/features/planning/meeting-minutes.md rename to features/planning/meeting-minutes.md diff --git a/sam/docs/features/planning/planning-views.md b/features/planning/planning-views.md similarity index 100% rename from sam/docs/features/planning/planning-views.md rename to features/planning/planning-views.md diff --git a/features/quality-management/README.md b/features/quality-management/README.md new file mode 100644 index 0000000..d2a5391 --- /dev/null +++ b/features/quality-management/README.md @@ -0,0 +1,362 @@ +# 품질관리 시스템 + +> **작성일**: 2026-03-09 +> **상태**: 운영 중 + +--- + +## 1. 개요 + +### 1.1 목적 + +SAM 품질관리 시스템은 **방화문 제품검사 → 실적신고 → 건기원 제출**의 전체 흐름을 자동화한다. +건축자재 품질관리 법규(건설기술진흥법)에 따른 제품검사 수행 및 분기별 실적신고를 관리한다. + +### 1.2 역할별 프로세스 플로우 + +> 스토리보드 슬라이드 3 기준 + +``` +매출거래처 견적/수주 담당자 생산 담당자 출고 담당자 품질 담당자 +─────────── ────────────── ────────── ────────── ────────── +주문 견적 작성 생산 부서 할당 출고 대기 제품검사 신청 +(전화/카톡/메일) │ │ │ (거래처 요청) + │ 수주 전환?──No──→ 재고 소요량 출고 완료 │ + │ │ Yes 충분? 검사원 일정 관리 +주문 확인 및 수주서 작성 │ │ +내역 확정 (견적서 선택) 원자재 투입 체크 검사원 현장 방문 + │ │ 및 검사 진행 + 생산지시 생성 공정 작업 진행 │ + │ │ 합격? ──No──→ + 재고 소요량 중간검사 │ Yes + 충분?──No──→ │ 실적 신고 관리 + │ 생산 완료 │ + (자재 담당자 품질 인증 심사 + 입고 등록 + 수입검사) + +* 분할 수주/생산/출고는 1차 이후 반복 가능 +``` + +### 1.3 핵심 흐름 (시스템) + +``` +품질관리서 생성 → 수주 연결 → 개소별 검사 → 검사완료 + ↓ + 실적신고 자동생성 (분기별) + ↓ + 필수정보 확인 → 확정 → 건기원 신고 + ↓ + 품질인정심사 + (기준/매뉴얼 + 로트추적) +``` + +### 1.4 메뉴 구조 + +| 메뉴 | URL | 설명 | 상태 | +|------|-----|------|------| +| 제품검사관리 | `/quality/inspections` | 품질관리서 목록/생성/상세 | 운영 중 | +| 실적신고관리 | `/quality/performance-reports` | 분기별 실적신고 관리 | 운영 중 | +| 품질인정심사 | `/quality/qms` | 기준/매뉴얼 심사 + 로트 추적 심사 | 개발 예정 | + +--- + +## 2. 제품검사 (QualityDocument) + +> 상세 문서: [inspection-management.md](./inspection-management.md) + +### 2.1 품질관리서 생성 + +- **채번**: `KD-QD-YYYYMM-0001` (자동) +- **필수 입력**: 현장명, 접수일, 검사자 +- **선택 입력**: 수주처(client_id), 관련자 정보(options JSON) + +### 2.2 수주 연결 + +- 품질관리서에 수주(Order)를 연결하면 **개소(Location)가 자동 생성** +- 개소 = 수주의 root node (층/부호 단위) +- 시공규격(post_width, post_height)은 발주규격과 다를 수 있음 + +### 2.3 검사 수행 + +각 개소에 대해 **15개 검사항목** + **제품 사진**을 입력: + +| 분류 | 검사항목 | 판정 | +|------|---------|------| +| 외관 | 가공, 봉제, 조립, 차연재, 하부마감 | pass/fail | +| 기능 | 모터, 소재 | pass/fail | +| 치수 | 가로, 세로, 가이드레일 간격, 하부마감 간격 | OK/NG | +| 시험 | 내화, 차연, 개폐, 충격 | pass/fail | + +### 2.4 상태 전이 + +``` +received (접수) → in_progress (검사 중) → completed (검사 완료) +``` + +**개소별 상태 자동 판정**: +- `pending`: 검사 데이터 없음 +- `in_progress`: 일부 항목 입력 또는 사진 미등록 +- `completed`: 15개 항목 전부 + 사진 등록 + +--- + +## 3. 생산실적신고 (PerformanceReport) + +> 상세 문서: [performance-reports.md](./performance-reports.md) + +### 3.1 자동 생성 + +- **트리거**: 품질관리서 검사완료(`complete()`) 시 +- **생성 기준**: 현재 연도 + 분기 (`year`, `quarter`) +- **초기 상태**: `unconfirmed` + +### 3.2 확정 프로세스 + +``` +unconfirmed (미확정) + ↓ confirm() — 필수정보 검증 통과 시 +confirmed (확정) + ↓ distribute() — 건기원 신고 시 (미구현) +reported (신고완료) +``` + +**확정 해제**: `confirmed` → `unconfirmed` (unconfirm) + +### 3.3 필수정보 검증 + +확정 전 4가지 섹션의 필수필드가 모두 입력되어야 함: + +| 섹션 | 필수필드 | +|------|---------| +| 건축공사장 | 현장명, 대지위치, 지번 | +| 자재유통업자 | 업체명, 주소, 대표자, 전화번호 | +| 공사시공자 | 업체명, 주소, 담당자, 연락처 | +| 공사감리자 | 사무소명, 주소, 담당자, 연락처 | + +### 3.4 누락체크 + +- 출고완료(배송 완료)된 수주 중 품질관리서가 미등록된 건 탐지 +- 별도 탭에서 조회 가능 + +### 3.5 건기원 실적신고 비즈니스 컨텍스트 + +**건기원(한국건설기술연구원)** 실적신고는 법적 의무: + +- **주기**: 분기별 (1~3월, 4~6월, 7~9월, 10~12월) +- **대상**: 해당 분기에 검사 완료된 모든 건 +- **내용**: 품질관리서 번호, LOT 번호, 현장 정보, 검사 결과 +- **제출처**: 건기원 온라인 시스템 (수기 입력 또는 엑셀 업로드) + +**SAM 시스템 역할**: +1. 제품검사 완료 시 실적신고 데이터 자동 수집 +2. 필수정보 누락 여부 사전 검증 +3. 확정 후 건기원 제출 양식으로 데이터 정리 +4. (향후) 건기원 시스템 연동 자동 배포 + +--- + +## 4. 데이터 구조 + +### 4.1 테이블 관계 + +``` +quality_documents (품질관리서) +├── quality_document_orders (수주 연결, M:N) +│ └── orders +├── quality_document_locations (개소별 검사) +│ ├── order_items (대표 품목) +│ └── documents (EAV 성적서) +└── performance_reports (실적신고, 1:1) +``` + +### 4.2 주요 테이블 + +| 테이블 | 설명 | 주요 컬럼 | +|--------|------|----------| +| `quality_documents` | 품질관리서 | quality_doc_number, status, client_id, options(JSON) | +| `quality_document_orders` | 품질-수주 연결 | quality_document_id, order_id | +| `quality_document_locations` | 개소별 검사 | inspection_data(JSON), inspection_status, post_width/height | +| `performance_reports` | 실적신고 | year, quarter, confirmation_status, confirmed_date | + +### 4.3 options JSON 구조 (quality_documents) + +```json +{ + "construction_site": { + "name": "현장명", + "land_location": "대지위치", + "lot_no": "지번" + }, + "material_distributor": { + "company": "업체명", + "address": "주소", + "ceo": "대표자", + "tel": "전화번호" + }, + "contractor": { + "company": "업체명", + "address": "주소", + "name": "담당자", + "phone": "연락처" + }, + "supervisor": { + "office": "사무소명", + "address": "주소", + "name": "담당자", + "phone": "연락처" + } +} +``` + +--- + +## 5. API 엔드포인트 + +### 5.1 제품검사 (`/api/v1/quality/documents`) + +| Method | Path | 설명 | +|--------|------|------| +| GET | `/quality/documents` | 목록 (상태/날짜 필터) | +| GET | `/quality/documents/stats` | 상태별 통계 | +| GET | `/quality/documents/calendar` | 캘린더 스케줄 | +| GET | `/quality/documents/available-orders` | 미등록 수주 조회 | +| POST | `/quality/documents` | 생성 | +| GET | `/quality/documents/{id}` | 상세 | +| PUT | `/quality/documents/{id}` | 수정 | +| DELETE | `/quality/documents/{id}` | 삭제 | +| PATCH | `/quality/documents/{id}/complete` | 검사완료 (→ 실적신고 자동생성) | +| POST | `/quality/documents/{id}/orders` | 수주 연결 | +| DELETE | `/quality/documents/{id}/orders/{orderId}` | 수주 해제 | +| POST | `/quality/documents/{id}/locations/{locId}/inspect` | 개소별 검사 저장 | + +### 5.2 실적신고 (`/api/v1/quality/performance-reports`) + +| Method | Path | 설명 | +|--------|------|------| +| GET | `/quality/performance-reports` | 목록 (연도/분기/상태 필터) | +| GET | `/quality/performance-reports/stats` | 확정/미확정 통계 | +| GET | `/quality/performance-reports/missing` | 누락체크 | +| PATCH | `/quality/performance-reports/confirm` | 일괄 확정 | +| PATCH | `/quality/performance-reports/unconfirm` | 확정 해제 | +| PATCH | `/quality/performance-reports/memo` | 메모 일괄 업데이트 | + +--- + +## 6. 프론트엔드 구조 + +### 6.1 페이지 + +``` +react/src/app/[locale]/(protected)/quality/ +├── page.tsx # 대시보드 +├── inspections/ +│ ├── page.tsx # 검사 목록 +│ ├── new/page.tsx # 검사 생성 +│ └── [id]/page.tsx # 검사 상세/수정 +└── performance-reports/ + └── page.tsx # 실적신고 목록 +``` + +### 6.2 컴포넌트 + +``` +react/src/components/quality/ +├── InspectionManagement/ +│ ├── InspectionList.tsx # 검사 목록 +│ ├── InspectionCreate.tsx # 검사 생성 +│ ├── InspectionDetail.tsx # 검사 상세 (수정 포함) +│ ├── OrderSelectModal.tsx # 수주 선택 모달 +│ ├── ProductInspectionInputModal.tsx # 검사 입력 모달 +│ ├── actions.ts # Server Actions +│ ├── types.ts # TypeScript 타입 +│ └── documents/ # 요청서/성적서 문서 +└── PerformanceReportManagement/ + ├── PerformanceReportList.tsx # 실적신고 목록 (2탭) + ├── MemoModal.tsx # 메모 모달 + └── actions.ts # Server Actions +``` + +### 6.3 실적신고 화면 기능 + +**탭 1: 분기별 실적신고** +- 연도/분기 필터 +- 통계: 전체, 확정, 미확정, 총 개소 +- 테이블: 품질관리서번호, 작성일, 현장명, 수주처, 개소수, 필수정보 상태, 확정상태, 확정일, 메모 +- 액션: 선택 확정, 확정 해제, 메모 일괄 작성 + +**탭 2: 누락체크** +- 출고완료 수주 중 품질관리서 미등록 건 조회 +- 빠른 누락 확인으로 법규 준수 지원 + +--- + +## 7. 품질인정심사 (QMS) + +> 상세 문서: [quality-certification-audit.md](./quality-certification-audit.md) + +### 7.1 개요 + +품질 인정 심사 자료를 관리하는 기능. 두 가지 심사 영역으로 구성: + +| 심사 영역 | 설명 | 진행률 추적 | +|----------|------|-----------| +| 기준/매뉴얼 심사 | 품질 기준 문서 및 매뉴얼 점검표 체크 | 완료 항목 / 전체 항목 | +| 로트 추적 심사 | 품질관리서 → 수주코드 → 개소별 제품로트 → 관련 서류 추적 확인 | 확인 개소 / 전체 개소 | + +### 7.2 로트 추적 심사 구조 + +``` +품질관리서 목록 → 수주코드 목록 → 관련 서류 +(1단계 선택) (2단계 선택) (3단계 확인) +``` + +- 해당 분기 실적신고 확정 건 기준 +- 수입검사, 중간검사, 납품확인서, 출고증, 제품검사 성적서, 품질관리서 등 서류 연결 확인 + +### 7.3 구현 상태 + +**개발 예정** — 현재 페이지 구조만 존재 + +--- + +## 8. 미구현 기능 요약 + +| 기능 | 상태 | 설명 | +|------|------|------| +| 배포(distribute) API | 미구현 | 건기원 시스템 연동 자동 배포 | +| 확정건 엑셀 다운로드 | 미구현 | 확정된 실적 건기원 양식 엑셀 내보내기 | +| 품질인정심사 | 미구현 | 기준/매뉴얼 심사 + 로트 추적 심사 | + +--- + +## 9. 스토리보드 참조 + +> **출처**: `SAM_MES_경동기업_품질관리_Storyboard_D1.9_260224` + +| 슬라이드 | 화면 | 기능 영역 | +|---------|------|----------| +| 3 | 프로젝트 진행 플로우차트 | 전체 프로세스 | +| 5~6 | 제품검사 목록 + 캘린더 | 제품검사관리 | +| 7~9 | 제품검사 상세 | 제품검사관리 | +| 10 | 수주 선택 팝업 | 제품검사관리 | +| 11 | 제품검사 팝업 (검사 입력) | 제품검사관리 | +| 12~13 | 제품검사요청서 | 문서 출력 | +| 14~15 | 제품검사성적서 | 문서 출력 | +| 16 | 실적신고 목록 | 실적신고관리 | +| 17 | 메모 팝업 | 실적신고관리 | +| 18 | 누락체크 | 실적신고관리 | +| 19 | 품질인정심사 (기준/매뉴얼) | 품질인정심사 | +| 20 | 품질인정심사 (로트 추적) | 품질인정심사 | + +--- + +## 관련 문서 + +- [DB 스키마 — 생산/품질](../../system/database/production.md) +- [API 규칙](../../dev/standards/api-rules.md) +- [채번 규칙](../../rules/numbering-rules.md) + +--- + +**최종 업데이트**: 2026-03-09 diff --git a/features/quality-management/inspection-management.md b/features/quality-management/inspection-management.md new file mode 100644 index 0000000..f55c560 --- /dev/null +++ b/features/quality-management/inspection-management.md @@ -0,0 +1,317 @@ +# 제품검사 관리 (Inspection Management) + +> **작성일**: 2026-03-09 +> **상태**: 운영 중 +> **URL**: `/quality/inspections` + +--- + +## 1. 개요 + +### 1.1 목적 + +방화문 제품의 현장 출고 전 품질검사를 수행하고 검사성적서를 발행한다. +검사 완료 시 실적신고(PerformanceReport)를 자동 생성한다. + +### 1.2 품질관리서 구조 + +``` +품질관리서 (QualityDocument) +├── 기본정보: 채번, 현장명, 접수일, 검사자, 수주처 +├── 관련자 정보 (options JSON): +│ ├── 건축공사장 정보 +│ ├── 자재유통업자 정보 +│ ├── 공사시공자 정보 +│ └── 공사감리자 정보 +├── 수주 연결 (QualityDocumentOrder, 1:N) +│ └── 개소 (QualityDocumentLocation, 1:N) +│ ├── 시공규격 (post_width, post_height) +│ ├── 검사 데이터 (inspection_data JSON) +│ └── 검사성적서 (Document EAV) +└── 실적신고 (PerformanceReport, 1:1) +``` + +### 1.3 화면 구성 + +**목록 페이지** (슬라이드 5~6): +- 상단: 날짜 필터 (전체/전전월/전월/금월/어제/오늘) + 검색 +- 통계 카드: 접수, 진행중, 완료 건수 +- 테이블: 품질관리서번호, 번호, 수주처, 개소, 실적신고 필수정보, 검사시기, 검사자, 상태, 작성자, 접수일 +- 하단: **캘린더 스케줄** (월간 뷰) + +**캘린더 뷰**: +- 월 단위 표시, 상태 필터(전체/진행중/완료) +- 색상 구분: 완료(초록 배지), 진행중(파란 바) +- 검사 완료 건: `홍길동 - 현장명 / 완료` 형태 +- 검사 진행 건: `홍길동 - 현장명 / 진행중` 형태 (날짜 범위 바) +- 클릭 시 해당 제품검사 상세 화면으로 이동 + +**상세 페이지** (슬라이드 7~9): +- 기본 정보: 품질관리서번호, 현장명, 수주처, 접수일, 담당자, 연락처, 상태, 작성자 +- 관련자 정보 4개 섹션 (실적 신고 시 필수 정보) +- 검사 정보: 검사방문요청일, 검사시작일, 검사종료일, 검사자 +- 현장 주소: 우편번호 찾기 + 상세주소 +- 수주 설정 정보: 수주 선택 버튼 → 수주별 개소 목록 (층수/부호/수주규격/시공규격/변경사유) +- 하단 버튼: 검사제품요청서 보기, 제품검사성적서 보기, **검사 완료**, 수정 + +--- + +## 2. 상태 관리 + +### 2.1 품질관리서 상태 + +| 상태 | 코드 | 조건 | +|------|------|------| +| 접수 | `received` | 생성 직후, 수주 미연결 | +| 진행중 | `in_progress` | 수주 연결됨 또는 일부 검사 진행 | +| 완료 | `completed` | 모든 개소 검사 완료 후 `complete()` 호출 | + +### 2.2 개소별 검사 상태 (자동 판정) + +| 상태 | 코드 | 판정 기준 | +|------|------|----------| +| 대기 | `pending` | 검사 데이터 없음 (15개 항목 0개 + 사진 없음) | +| 진행중 | `in_progress` | 일부 항목 입력 또는 사진 미등록 | +| 완료 | `completed` | 15개 항목 전부 입력 + 사진 1장 이상 | + +### 2.3 상태 자동 재계산 + +개소별 검사 저장 시 → 개소 상태 자동 판정 → 품질관리서 상태 재계산: +- 전부 `pending` → `received` +- 하나라도 `completed` 또는 `in_progress` → `in_progress` +- 전부 `completed` → `in_progress` (수동 `complete()` 필요) + +--- + +## 3. 검사 항목 + +### 3.1 15개 검사 항목 + +| # | 키 | 분류 | 설명 | 판정값 | +|---|-----|------|------|--------| +| 1 | `appearanceProcessing` | 외관 | 가공 상태 | pass/fail | +| 2 | `appearanceSewing` | 외관 | 봉제 상태 | pass/fail | +| 3 | `appearanceAssembly` | 외관 | 조립 상태 | pass/fail | +| 4 | `appearanceSmokeBarrier` | 외관 | 차연재 상태 | pass/fail | +| 5 | `appearanceBottomFinish` | 외관 | 하부마감 상태 | pass/fail | +| 6 | `motor` | 기능 | 모터 작동 | pass/fail | +| 7 | `material` | 기능 | 소재 적합성 | pass/fail | +| 8 | `lengthJudgment` | 치수 | 가로 치수 | OK/NG | +| 9 | `heightJudgment` | 치수 | 세로 치수 | OK/NG | +| 10 | `guideRailGap` | 치수 | 가이드레일 간격 | OK/NG | +| 11 | `bottomFinishGap` | 치수 | 하부마감 간격 | OK/NG | +| 12 | `fireResistanceTest` | 시험 | 내화 시험 | pass/fail | +| 13 | `smokeLeakageTest` | 시험 | 차연 시험 | pass/fail | +| 14 | `openCloseTest` | 시험 | 개폐 시험 | pass/fail | +| 15 | `impactTest` | 시험 | 충격 시험 | pass/fail | + +### 3.2 추가 데이터 + +| 키 | 설명 | 필수 | +|----|------|------| +| `productImages` | 제품 사진 URL 배열 | 완료 판정에 필수 | + +--- + +## 4. 수주 연결 + +### 4.1 수주 선택 + +- `availableOrders()`: 해당 수주처(client_id)의 미등록 수주 조회 +- 모달에서 복수 수주 선택 가능 + +### 4.2 개소 자동생성 + +수주 연결 시 각 수주의 root node(층/부호)마다 개소(Location) 자동생성: + +``` +수주 A (3개 root node) +├── 1F A호 → Location 1 +├── 2F B호 → Location 2 +└── 3F C호 → Location 3 + +수주 B (2개 root node) +├── 지하1F → Location 4 +└── 1F → Location 5 +``` + +### 4.3 개소 데이터 + +| 필드 | 설명 | 출처 | +|------|------|------| +| `order_item_id` | 대표 OrderItem | root node의 첫 번째 품목 | +| `post_width` | 시공 가로 | 발주 규격에서 복사 (수정 가능) | +| `post_height` | 시공 세로 | 발주 규격에서 복사 (수정 가능) | +| `change_reason` | 변경 사유 | 규격 변경 시 입력 | + +--- + +## 5. 문서 자동생성 (EAV) + +### 5.1 제품검사요청서 (슬라이드 12~13) + +- **Template ID**: 66 (제품검사 요청서) +- **트리거**: 품질관리서 생성/수정 시 `syncRequestDocument()` 호출 +- **인쇄용 페이지 형태로 구분**되어 표시 (인쇄, 공유, 닫기 버튼) + +**문서 구성**: + +``` +┌─────────────────────────────────────────┐ +│ 제품검사요청서 │ +│ 문서번호: ABC123 | 작성일자: 2025.11.11 │ +│ │ +│ 승인라인: 작성 → 승인 → 승인 → 승인 │ +│ 홍길동 이름 이름 이름 │ +│ │ +│ ── 기본정보 ── │ +│ 수주처, 수주번호, 담당자, 연락처 │ +│ 현장명, 납품일, 총 개소, 접수일 │ +│ │ +│ ── 입력사항 (실적신고 필수 정보) ── │ +│ 건축공사장: 현장명, 대지위치, 지번 │ +│ 자재유통업자: 회사명, 회사주소, 대표자명, │ +│ 전화번호 │ +│ 공사시공자: 회사명, 회사주소, 성명, 전화번호 │ +│ 공사감리자: 사무소명, 사무소주소, 성명, │ +│ 전화번호 │ +│ │ +│ ── 검사대상 사전 고지 정보 ── │ +│ No. 층수 부호 발주규격(가로/세로) │ +│ 시공후규격(가로/세로) 변경사유 │ +└─────────────────────────────────────────┘ +``` + +**주의 문구** (빨간색): +- 발주 사이즈와 시공 완료된 사이즈가 다를 시 **실질 범위를 넣어야 한다** +- 변경사유를 고지하여야 인정마을을 부착할 수 있다 +- 사전고지를 하지 않음으로 발생하는 문제의 귀책은 신청업체에 있다 + +### 5.2 제품검사성적서 (슬라이드 14~15) + +- 각 개소별 `document_id`로 EAV Document 참조 +- 검사 결과(inspection_data)를 EAV 필드로 저장 +- **개소별 페이지 단위**: 1/50 형태의 페이지 네비게이션 (이전/이동/다음 버튼) + +**문서 구성**: + +``` +┌─────────────────────────────────────────┐ +│ 제품검사성적서 │ +│ 문서번호: ABC123 | 작성일자: 2025.11.11 │ +│ │ +│ 제품명, 제품 LOT NO, 로트크기 │ +│ 제품코드, 검사일자 │ +│ 수주처, 검사자 │ +│ 현장명 │ +│ │ +│ ── 제품 사진 ── │ +│ [IMG] [IMG] │ +│ │ +│ ── 검사 항목 ── │ +│ No. 검사항목 검사기준 검사 특정값 판정│ +│ 1 외모양 │ +│ 가공상태 사용상 해로운 결함이 없을 것 │ +│ 재봉상태 내화심에 의해 견고하게 접합 │ +│ 조립상태 핸드바 견고하게 조립되어야 함 │ +│ 연기차단재 연기차단재 가이드레일 W60, │ +│ 가이드레일 W50 (분체 설치) │ +│ 하단마감재 내부 부재형상 설치 유무 │ +│ 2 모터 인정제품과 동일사양 │ +│ 3 재질 WY-SC780 인쇄상태 확인 │ +│ 4 치수 │ +│ 길이 수주 치수 ± 30mm │ +│ 높이 수주 치수 ± 30mm │ +│ 가이드레일 10 ± 5mm (측정부위 길이 100 이내)│ +│ 간격 가이드레일갑과 하단마감재 25mm 이내│ +│ 5 작동테스트 6mm 관절게이지 관통 여 150mm │ +│ 6 내화시험 25mm 관절게이지 관통 유무 │ +│ 7 차연시험 10초 이상 자속되는 화염 발생 유무 │ +│ 8 개폐시험 전도/개폐 2.5~6.5m/min 등 │ +│ │ +│ 특이사항: │ +│ 종합판정: 합격 │ +│ │ +│ [이전] [1] /50 [이동] [다음] │ +└─────────────────────────────────────────┘ +``` + +--- + +## 6. API + +### 6.1 주요 엔드포인트 + +| Method | Path | 설명 | +|--------|------|------| +| GET | `/quality/documents` | 목록 | +| POST | `/quality/documents` | 생성 | +| GET | `/quality/documents/{id}` | 상세 | +| PUT | `/quality/documents/{id}` | 수정 (개소/규격 포함) | +| PATCH | `/quality/documents/{id}/complete` | 검사완료 | +| POST | `/quality/documents/{id}/orders` | 수주 연결 | +| DELETE | `/quality/documents/{id}/orders/{orderId}` | 수주 해제 | +| POST | `/quality/documents/{id}/locations/{locId}/inspect` | 개소별 검사 저장 | + +### 6.2 검사 저장 요청 예시 + +```json +POST /quality/documents/1/locations/5/inspect +{ + "inspection_data": { + "appearanceProcessing": "pass", + "appearanceSewing": "pass", + "appearanceAssembly": "pass", + "appearanceSmokeBarrier": "pass", + "appearanceBottomFinish": "pass", + "motor": "pass", + "material": "pass", + "lengthJudgment": "OK", + "heightJudgment": "OK", + "guideRailGap": "OK", + "bottomFinishGap": "OK", + "fireResistanceTest": "pass", + "smokeLeakageTest": "pass", + "openCloseTest": "pass", + "impactTest": "pass", + "productImages": ["https://..."] + } +} +``` + +--- + +## 7. 소스 파일 + +### 7.1 Backend + +| 파일 | 역할 | +|------|------| +| `api/app/Models/Qualitys/QualityDocument.php` | 품질관리서 모델 | +| `api/app/Models/Qualitys/QualityDocumentLocation.php` | 개소 모델 | +| `api/app/Models/Qualitys/QualityDocumentOrder.php` | 수주 연결 모델 | +| `api/app/Services/QualityDocumentService.php` | 서비스 (770줄) | +| `api/app/Http/Controllers/Api/V1/QualityDocumentController.php` | 컨트롤러 | + +### 7.2 Frontend + +| 파일 | 역할 | +|------|------| +| `react/src/components/quality/InspectionManagement/InspectionList.tsx` | 목록 | +| `react/src/components/quality/InspectionManagement/InspectionCreate.tsx` | 생성 | +| `react/src/components/quality/InspectionManagement/InspectionDetail.tsx` | 상세/수정 | +| `react/src/components/quality/InspectionManagement/OrderSelectModal.tsx` | 수주 선택 | +| `react/src/components/quality/InspectionManagement/ProductInspectionInputModal.tsx` | 검사 입력 | +| `react/src/components/quality/InspectionManagement/actions.ts` | Server Actions | +| `react/src/components/quality/InspectionManagement/types.ts` | 타입 정의 | + +--- + +## 관련 문서 + +- [품질관리 시스템 개요](./README.md) +- [생산실적신고](./performance-reports.md) + +--- + +**최종 업데이트**: 2026-03-09 diff --git a/features/quality-management/performance-reports.md b/features/quality-management/performance-reports.md new file mode 100644 index 0000000..a34f55d --- /dev/null +++ b/features/quality-management/performance-reports.md @@ -0,0 +1,268 @@ +# 생산실적신고 (Performance Reports) + +> **작성일**: 2026-03-09 +> **상태**: 운영 중 +> **URL**: `/quality/performance-reports` + +--- + +## 1. 개요 + +### 1.1 목적 + +건설기술진흥법에 따라 방화문 제품검사 완료 건에 대해 **분기별 실적신고**를 관리한다. +건기원(한국건설기술연구원) 온라인 시스템에 제출할 데이터를 자동 수집하고, 필수정보 검증 후 확정 처리한다. + +### 1.2 비즈니스 배경 + +- **법적 의무**: 방화문은 건축자재 품질관리 대상으로, 제품검사 후 실적을 건기원에 신고해야 함 +- **신고 주기**: 분기별 (Q1: 1~3월, Q2: 4~6월, Q3: 7~9월, Q4: 10~12월) +- **신고 내용**: 품질관리서 번호, 현장정보, LOT, 규격, 검사결과 +- **제출 방식**: 건기원 온라인 시스템 수기 입력 또는 엑셀 업로드 + +### 1.3 SAM에서의 역할 + +``` +제품검사 완료 → 실적신고 자동생성 → 필수정보 검증 → 확정 → 건기원 제출 + (SAM 자동) (SAM 자동) (담당자) (수동/향후 자동) +``` + +--- + +## 2. 상태 흐름 + +### 2.1 상태 정의 + +| 상태 | 코드 | 설명 | +|------|------|------| +| 미확정 | `unconfirmed` | 자동생성 직후, 필수정보 미완료 가능 | +| 확정 | `confirmed` | 필수정보 완료 후 담당자가 확정 | +| 신고완료 | `reported` | 건기원에 신고 완료 (미구현) | + +### 2.2 상태 전이 + +``` + confirm() +unconfirmed ─────────────────→ confirmed + ↑ │ + │ unconfirm() │ + └──────────────────────────────┘ + │ + distribute() + ↓ + reported (미구현) +``` + +### 2.3 확정 조건 + +확정(`confirm`) 시 **필수정보 4개 섹션** 검증: + +``` +✅ 건축공사장: name, land_location, lot_no +✅ 자재유통업자: company, address, ceo, tel +✅ 공사시공자: company, address, name, phone +✅ 공사감리자: office, address, name, phone +``` + +하나라도 누락되면 확정 불가 → `cannotConfirmWithMissingInfo` 에러 반환 + +--- + +## 3. 자동 생성 로직 + +### 3.1 트리거 + +`QualityDocumentService::complete()` 호출 시: + +```php +PerformanceReport::firstOrCreate( + [ + 'tenant_id' => $tenantId, + 'quality_document_id' => $qualityDocument->id, + ], + [ + 'year' => now()->year, // 현재 연도 + 'quarter' => ceil(now()->month / 3), // 현재 분기 + 'confirmation_status' => 'unconfirmed', + 'created_by' => $userId, + ] +); +``` + +### 3.2 특징 + +- `firstOrCreate`: 동일 품질관리서에 대해 중복 생성 방지 +- 검사완료 시점의 연도/분기로 자동 배정 +- Unique 제약: `(tenant_id, quality_document_id)` + +--- + +## 4. 화면 구성 + +### 4.1 탭 구조 + +| 탭 | 내용 | +|----|------| +| **분기별 실적신고** | 기본 탭. 연도/분기별 실적 목록 | +| **누락체크** | 출고완료 수주 중 품질관리서 미등록 건 | + +### 4.2 통계 카드 + +``` +┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ +│ 전체 │ │ 확정 │ │ 미확정 │ │ 총 개소 │ +│ 12건 │ │ 8건 │ │ 4건 │ │ 48개소 │ +└──────────┘ └──────────┘ └──────────┘ └──────────┘ +``` + +### 4.3 테이블 컬럼 (분기별 실적신고) + +| 컬럼 | 설명 | +|------|------| +| 선택 | 체크박스 (일괄 처리용) | +| 번호 | 순번 | +| 품질관리서번호 | KD-QD-YYYYMM-NNNN | +| 작성일 | 품질관리서 접수일 | +| 현장명 | 시공 현장명 | +| 수주처 | 거래처 | +| 개소 | 검사 개소 수 | +| 필수정보 | "완료" 또는 "N건 누락" | +| 확정상태 | 미확정/확정/신고완료 | +| 확정일 | 확정 처리일 | +| 메모 | 메모 내용 | + +### 4.4 액션 버튼 + +| 버튼 | 조건 | 설명 | +|------|------|------| +| 선택 확정 | 미확정 건 선택 시 | 필수정보 완료된 건만 일괄 확정 | +| 확정 해제 | 확정 건 선택 시 | 확정 → 미확정 되돌리기 | +| 배포 | 확정 건 선택 시 | 건기원 신고 (미구현) | +| 메모 | 건 선택 시 | 메모 일괄 작성 | + +--- + +## 5. 누락체크 (슬라이드 18) + +### 5.1 목적 + +실적신고 기간이 지났지만 확정이 안된 목록을 확인한다. +출고(배송)가 완료되었지만 품질관리서가 등록되지 않은 수주를 탐지한다. + +### 5.2 누락 발생 원인 + +| 원인 | 설명 | +|------|------| +| 품질관리서 발행일 기준 분기 불일치 | 품질관리서가 해당 분기에 포함되어야 하나, 공사 미완료로 다음 분기로 이월 | +| 수주 중복 등록 | 수주통 시 다른 현장명으로 등록되어 제품검사 발행 시 누락 | +| 납품 후 미등록 | 납품 후 제품검사 등록이 누락된 경우 | + +### 5.3 누락체크 목록 표시 + +| 컬럼 | 설명 | +|------|------| +| 품질관리서 번호 | 관련 품질관리서 (있는 경우) | +| 현장명 | 수주 현장명 | +| 수주처 | 거래처 | +| 개소 | 수주 개소 수 | +| 제품검사완료일 | 검사 완료 일자 | +| 메모 | 누락 사유 메모 | + +### 5.4 로직 + +```sql +-- 출고완료 수주 중 quality_document_orders에 미등록된 건 +SELECT orders.* +FROM orders +WHERE delivery_status = 'completed' + AND NOT EXISTS ( + SELECT 1 FROM quality_document_orders + WHERE order_id = orders.id + ) +``` + +--- + +## 6. API + +### 6.1 엔드포인트 + +| Method | Path | 설명 | +|--------|------|------| +| GET | `/quality/performance-reports` | 목록 (year, quarter, status 필터) | +| GET | `/quality/performance-reports/stats` | 확정/미확정 통계 | +| GET | `/quality/performance-reports/missing` | 누락체크 | +| PATCH | `/quality/performance-reports/confirm` | 일괄 확정 | +| PATCH | `/quality/performance-reports/unconfirm` | 확정 해제 | +| PATCH | `/quality/performance-reports/memo` | 메모 일괄 업데이트 | + +### 6.2 요청/응답 예시 + +**목록 조회**: +``` +GET /quality/performance-reports?year=2026&quarter=1&status=unconfirmed +``` + +**일괄 확정**: +```json +PATCH /quality/performance-reports/confirm +{ + "ids": [1, 2, 3] +} +``` + +**응답**: +```json +{ + "success": true, + "message": "message.performance_report.confirmed", + "data": { + "confirmed_count": 2, + "skipped_count": 1, + "skipped_ids": [3] + } +} +``` + +--- + +## 7. 소스 파일 + +### 7.1 Backend + +| 파일 | 역할 | +|------|------| +| `api/app/Models/Qualitys/PerformanceReport.php` | 모델 | +| `api/app/Services/PerformanceReportService.php` | 서비스 (280줄) | +| `api/app/Http/Controllers/Api/V1/PerformanceReportController.php` | 컨트롤러 | +| `api/routes/api/v1/quality.php` | 라우트 | + +### 7.2 Frontend + +| 파일 | 역할 | +|------|------| +| `react/src/app/[locale]/(protected)/quality/performance-reports/page.tsx` | 페이지 | +| `react/src/components/quality/PerformanceReportManagement/PerformanceReportList.tsx` | 목록 컴포넌트 | +| `react/src/components/quality/PerformanceReportManagement/MemoModal.tsx` | 메모 모달 | +| `react/src/components/quality/PerformanceReportManagement/actions.ts` | Server Actions | + +--- + +## 8. 미구현 기능 + +| 기능 | 설명 | 우선순위 | +|------|------|---------| +| 배포(distribute) API | 건기원 시스템 연동 자동 신고 | 중 | +| 엑셀 다운로드 | 확정건 건기원 양식 엑셀 내보내기 | 높음 | +| 분기 마감 알림 | 분기 종료 전 미확정건 알림 | 낮음 | + +--- + +## 관련 문서 + +- [품질관리 시스템 개요](./README.md) +- [제품검사 관리](./inspection-management.md) + +--- + +**최종 업데이트**: 2026-03-09 diff --git a/features/quality-management/quality-certification-audit.md b/features/quality-management/quality-certification-audit.md new file mode 100644 index 0000000..f64936b --- /dev/null +++ b/features/quality-management/quality-certification-audit.md @@ -0,0 +1,169 @@ +# 품질인정심사 (Quality Certification Audit) + +> **작성일**: 2026-03-09 +> **상태**: 개발 예정 +> **URL**: `/quality/qms` +> **스토리보드**: 슬라이드 19~20 + +--- + +## 1. 개요 + +### 1.1 목적 + +품질인정심사 자료를 조회하고 관리한다. 기준/매뉴얼 심사와 로트 추적 심사 두 가지 영역으로 구성된다. + +### 1.2 탭 구조 + +| 탭 | 설명 | 진행률 예시 | +|----|------|-----------| +| 기준/매뉴얼 심사 | 품질 기준 문서 및 매뉴얼 점검 | 2/15 | +| 로트 추적 심사 | 개소별 제품로트 추적 및 서류 확인 | 7/12 | + +**전체 심사 진행률**: 기준/매뉴얼 + 로트추적 합산 (예: 9/27) + +--- + +## 2. 기준/매뉴얼 심사 + +### 2.1 화면 구성 + +``` +┌─────────────────────────────────────────────────────┐ +│ 필터: 연도, 분기(전체/1~4), 검색 │ +├──────────────────┬──────────────────────────────────┤ +│ 점검표 항목 │ 기준 문서화 │ +│ │ │ +│ ▼ 1. 제목 │ 항목명 ─────────── │ +│ ☑ 1. 항목 [완료] │ 소개 ─────────── │ +│ ☑ 2. 항목 [완료] │ │ +│ □ 3. 항목 │ 관련 기준 문서 │ +│ │ 📄 문서명 R025 2025-01-01 │ +│ ▼ 2. 제목 │ 📄 문서명 R025 2025-01-01 │ +│ ☑ 1. 항목 │ │ +│ ☑ 2. 항목 │ 📎 기준/매뉴얼 확인 │ +│ □ 3. 항목 │ │ +│ │ 문서 미리보기 영역 │ +│ 완료 체크 박스 │ (PDF 등) │ +│ □ 완료 │ 파일명: 파일명.pdf 1/1 페이지 │ +└──────────────────┴──────────────────────────────────┘ +``` + +### 2.2 점검표 항목 + +- 계층 구조: 대분류(제목) → 세부 항목 +- 각 항목에 완료/미완료 체크 +- 항목 클릭 시 우측에 해당 항목의 기준 문서 정보 표시 + +### 2.3 기준 문서화 + +| 영역 | 설명 | +|------|------| +| 항목명 및 소개 | 선택한 점검표 항목의 상세 정보 | +| 관련 기준 문서 | 해당 항목에 연결된 기준 문서 목록 (문서명, 문서번호, 날짜) | +| 문서 미리보기 | PDF 등 첨부 문서 미리보기 | +| 완료 체크 박스 | 기본값: 완료 해제 상태 | + +### 2.4 기준/매뉴얼 확인 버튼 + +- 클릭 시 확인 완료 스티커로 변경 +- 전체 항목 확인 완료 시 탭 진행률 갱신 + +--- + +## 3. 로트 추적 심사 + +### 3.1 화면 구성 + +``` +┌───────────────────────────────────────────────────────────┐ +│ 필터: 연도, 분기(전체/1~4), 검색 │ +├─────────────────┬─────────────────┬───────────────────────┤ +│ 품질관리서 목록 │ 수주코드 목록 │ 관련 서류 │ +│ │ │ │ +│ KD-SS-2024- │ KD-SS-240921-19 │ 수입검사 성적서 │ +│ 2025년 3분기 │ 수주: 2024-09-24│ ┌──────────────────┐ │ +│ 현장명 │ 7개소 / 완료 │ │ 전반 수입검사 성적서│ │ +│ 인정특성, 실리카 │ │ │ 절반 수입검사 성적서│ │ +│ 수주코드 2건 │ 개소별 제품로트 │ └──────────────────┘ │ +│ 14개소 │ KD-SS-240921-19 │ │ +│ │ -01 │ 개소별 제품로트 목록 │ +│ KD-SS-2024- │ 보조 │ 수주코드번호 │ +│ 현장명 │ │ 가로, 세로 │ +│ 수주코드 2건 │ KD-SS-240921-19 │ 3건의 서류 │ +│ 7개소 │ -19 [확인] │ │ +│ │ 보조 │ 확인 서류 목록 │ +│ │ │ 수입검사, 일반전표, │ +│ │ │ 중간검사, 납품확인서, │ +│ │ │ 출고증, 제품검사, │ +│ │ │ 검사 성적서, 품질관리서│ +├─────────────────┴─────────────────┤ │ +│ 문서 정보 영역 │ 확인 버튼 │ +│ 품질: 해당 문서 열람/닫힘 토글 │ [확인] [완료] │ +└────────────────────────────────────┴───────────────────────┘ +``` + +### 3.2 3단 드릴다운 구조 + +| 단계 | 영역 | 표시 내용 | +|------|------|---------| +| 1단계 | 품질관리서 목록 | 해당 분기 확정된 품질관리서, 인정특성, 수주코드 건수, 개소수 | +| 2단계 | 수주코드 목록 | 선택한 품질관리서의 수주코드, 수주일, 개소수, 완료 상태 | +| 3단계 | 관련 서류 | 해당 수주코드의 개소별 제품로트, 확인 서류 목록 | + +### 3.3 품질관리서 목록 (1단계) + +- 해당 분기로 실적신고 확정된 품질관리서 +- 표시: 품질관리서 번호, 해당 분기, 현장명, 인정특성, 수주코드 건수, 개소수 +- 클릭 시 2단계(수주코드 목록) 표시 + +### 3.4 수주코드 목록 (2단계) + +- 해당 품질관리서에 연결된 수주코드 목록 +- 품질관리서는 제품검사 시 수주코드 연결 +- 표시: 수주코드 번호, 개소 수, 수주일, 현장, 개소수, 개소별 제품로트 확인 여부 +- 클릭 시 5단계 영역에 해당 수주코드의 관련 서류 표시 + +### 3.5 관련 서류 (3단계) + +개소별 제품로트에 연결된 서류: + +| 서류 종류 | 설명 | +|---------|------| +| 수입검사 성적서 | 원자재 수입검사 결과 | +| 일반전표 | 회계 전표 | +| 중간검사 성적서 | 공정 중간검사 | +| 납품확인서 | 납품 확인 | +| 출고증 | 출고 기록 | +| 제품검사 성적서 | 완제품 검사 | +| 품질관리서 | 품질관리서 원본 | + +### 3.6 확인 프로세스 + +1. 품질관리서 선택 → 수주코드 선택 → 관련 서류 조회 +2. 각 서류를 열람하고 **확인** 버튼 클릭 +3. 모든 서류 확인 완료 시 해당 로트 **완료** 처리 +4. 전체 로트 완료 시 로트 추적 심사 진행률 갱신 + +--- + +## 4. 구현 상태 + +| 기능 | 상태 | 비고 | +|------|------|------| +| 기준/매뉴얼 심사 점검표 | 미구현 | 페이지만 존재 | +| 기준 문서 관리 | 미구현 | | +| 로트 추적 심사 | 미구현 | | +| 서류 연결/확인 | 미구현 | | + +--- + +## 관련 문서 + +- [품질관리 시스템 개요](./README.md) +- [제품검사 관리](./inspection-management.md) +- [생산실적신고](./performance-reports.md) + +--- + +**최종 업데이트**: 2026-03-09 diff --git a/sam/docs/features/rd/README.md b/features/rd/README.md similarity index 100% rename from sam/docs/features/rd/README.md rename to features/rd/README.md diff --git a/sam/docs/features/rd/design-insight.md b/features/rd/design-insight.md similarity index 100% rename from sam/docs/features/rd/design-insight.md rename to features/rd/design-insight.md diff --git a/sam/docs/features/rd/planning-design.md b/features/rd/planning-design.md similarity index 100% rename from sam/docs/features/rd/planning-design.md rename to features/rd/planning-design.md diff --git a/features/rd/sound-logo-studio.md b/features/rd/sound-logo-studio.md new file mode 100644 index 0000000..d21ed27 --- /dev/null +++ b/features/rd/sound-logo-studio.md @@ -0,0 +1,244 @@ +# 사운드 로고 스튜디오 + +> **작성일**: 2026-03-08 +> **상태**: 운영 중 +> **라우트**: `/rd/sound-logo` +> **뷰**: `resources/views/rd/sound-logo/index.blade.php` + +--- + +## 1. 개요 + +사운드 로고 스튜디오는 기업 시그니처 사운드(사운드 로고)를 제작하는 올인원 도구이다. Web Audio API 기반 시퀀서, Gemini AI 어시스트, TTS 음성 오버레이, Lyria RealTime BGM 생성을 하나의 SPA에서 통합 제공한다. + +**핵심 기능:** +- 시퀀서 기반 사운드 로고 수동/프리셋 제작 +- Gemini AI가 프롬프트 기반으로 음표 시퀀스 자동 설계 +- Gemini TTS로 나레이션 음성 생성 (여성/남성/아이, 30종 음성, 속도 조절) +- Lyria RealTime WebSocket으로 AI 배경음악 실시간 생성 +- 시퀀서/BGM 상호 배타적 재생 + TTS 공통 합성 +- WAV 내보내기 + +--- + +## 2. 아키텍처 + +### 2.1 3레이어 오디오 구조 + +``` +┌─────────────────────────────────────────────────────────┐ +│ Layer 1: 사운드 로고 (시퀀서 또는 AI BGM — 상호 배타적) │ +│ ├── A) 시퀀서 (수동 편집 / 프리셋 / AI 생성) │ +│ └── B) AI 배경음악 (Lyria RealTime) │ +├─────────────────────────────────────────────────────────┤ +│ Layer 2: 음성 TTS (Gemini) ─── 양쪽 모드 공통 │ +├─────────────────────────────────────────────────────────┤ +│ DynamicsCompressor (클리핑 방지) → AudioContext.dest │ +└─────────────────────────────────────────────────────────┘ +``` + +- **시퀀서 모드**: 수동/프리셋/AI 생성 음표 + TTS 합성. BGM 제외. +- **BGM 모드**: AI 배경음악 + TTS 합성. 시퀀서 음표 제외. +- 판단 기준: `bgmBuffer` 존재 여부 + +### 2.2 기술 스택 + +| 계층 | 기술 | 설명 | +|------|------|------| +| 프론트엔드 | Blade + Alpine.js | 단일 SPA | +| 오디오 엔진 | Web Audio API | OscillatorNode, BufferSourceNode, DynamicsCompressorNode | +| AI 시퀀서 | Gemini 2.5 Flash | 프롬프트 → JSON 음표 시퀀스 | +| TTS | `gemini-2.5-flash-preview-tts` | 30종 음성, Director's Note 스타일 제어 | +| BGM | Lyria RealTime (WebSocket) | `models/lyria-realtime-exp`, 48kHz 스테레오 PCM | +| 저장 | 없음 (클라이언트 전용) | WAV 내보내기로 결과물 보존 | + +--- + +## 3. 시퀀서 (Layer 1-A) + +### 3.1 입력 모드 + +| 모드 | 설명 | +|------|------| +| **수동** | 음표 그리드에서 직접 추가/삭제/편집 | +| **프리셋** | 사전 정의된 사운드 패턴 선택 (기업 시그널, 알림음 등) | +| **AI 생성** | 프롬프트 입력 → Gemini가 음표 시퀀스 JSON 반환 | + +### 3.2 음표 데이터 구조 + +```json +{ + "type": "note | chord | rest", + "note": "C5", + "chord": ["C4", "E4", "G4"], + "duration": 0.2, + "velocity": 0.8 +} +``` + +### 3.3 신디사이저 설정 + +| 파라미터 | 범위 | 설명 | +|---------|------|------| +| `synth` | sine, triangle, square, sawtooth | 파형 | +| `volume` | 0~1 | 마스터 볼륨 | +| `adsr.attack` | 1~500ms | 어택 | +| `adsr.decay` | 10~1000ms | 디케이 | +| `adsr.sustain` | 0~1.0 | 서스테인 | +| `adsr.release` | 10~3000ms | 릴리즈 | +| `reverb` | 0~1 | 리버브 양 | + +--- + +## 4. 음성 오버레이 — TTS (Layer 2) + +### 4.1 음성 카테고리 + +| 카테고리 | 음성 수 | 주요 음성 | +|---------|---------|----------| +| **여성** | 9종 | Kore(단정), Aoede(산뜻), Leda(따뜻), Zephyr(차분) 등 | +| **남성** | 9종 | Puck(밝음), Charon(깊음), Orus(안정), Fenrir(무게) 등 | +| **아이** | 5종 | Leda 기반(여아), Puck 기반(남아) 등 | + +### 4.2 속도 조절 + +| 단계 | Director's Note | +|------|----------------| +| 1 (매우 느림) | "아주 천천히 또박또박 말해주세요." | +| 2 (느림) | "조금 느린 속도로 말해주세요." | +| 3 (보통) | (지시문 없음) | +| 4 (빠름) | "조금 빠른 속도로 말해주세요." | +| 5 (매우 빠름) | "아주 빠른 속도로 말해주세요." | + +### 4.3 오디오 형식 + +- 출력: `audio/L16;rate=24000` (16-bit PCM, 24kHz, 모노, little-endian) +- 디코딩: `DataView.getInt16(offset, true)` → Float32 변환 + +--- + +## 5. AI 배경음악 — Lyria (Layer 1-B) + +### 5.1 WebSocket 프로토콜 + +``` +브라우저 ──WebSocket──→ Google Lyria RealTime API + (wss://generativelanguage.googleapis.com/ws/...BidiGenerateMusic) +``` + +서버는 API 키만 전달(`GET /lyria-config`), 실제 WebSocket 통신은 브라우저에서 직접 수행한다. + +### 5.2 메시지 흐름 + +``` +1. setup → { setup: { model: "models/lyria-realtime-exp" } } +2. (수신) ← { setupComplete: {} } +3. 프롬프트 → { clientContent: { weightedPrompts: [...] } } +4. 설정 → { musicGenerationConfig: { bpm, density, brightness, scale, temperature } } +5. 재생 → { playbackControl: "PLAY" } +6. (수신 반복) ← { serverContent: { audioChunks: [{ data: "base64..." }] } } +7. 정지 → { playbackControl: "STOP" } +8. (종료) ← WebSocket close +``` + +### 5.3 BGM 파라미터 + +| 파라미터 | 범위 | 설명 | +|---------|------|------| +| `bgmPrompt` | 텍스트 | 음악 분위기 설명 | +| `bgmBpm` | 60~180 | BPM | +| `bgmDensity` | 0~100 | 밀도 (0~1 변환) | +| `bgmBrightness` | 0~100 | 밝기 (0~1 변환) | +| `bgmScale` | C_MAJOR 등 | 음계 | +| `bgmDuration` | 5~60초 | 생성 길이 | + +### 5.4 오디오 형식 + +- 출력: 16-bit PCM, 48kHz, 스테레오, little-endian +- WAV 헤더 감지 시 `decodeAudioData` fallback + +--- + +## 6. API 엔드포인트 + +### 6.1 사운드 로고 (RdController) + +| HTTP | URI | 메서드 | 설명 | +|------|-----|--------|------| +| GET | `/rd/sound-logo` | `soundLogo()` | 스튜디오 페이지 | +| POST | `/rd/sound-logo/generate` | `soundLogoGenerate()` | AI 음표 시퀀스 생성 (Gemini) | +| POST | `/rd/sound-logo/tts` | `soundLogoTts()` | TTS 음성 생성 (Gemini TTS) | +| GET | `/rd/sound-logo/lyria-config` | `soundLogoLyriaConfig()` | Lyria WebSocket 접속 설정 반환 | + +### 6.2 CM송/나레이션 (CmSongController) + +| HTTP | URI | 메서드 | 설명 | +|------|-----|--------|------| +| GET | `/rd/cm-song` | `index()` | 나레이션 목록 | +| GET | `/rd/cm-song/create` | `create()` | 나레이션 제작 | +| POST | `/rd/cm-song` | `store()` | 나레이션 저장 | +| GET | `/rd/cm-song/{id}` | `show()` | 나레이션 상세 | +| DELETE | `/rd/cm-song/{id}` | `destroy()` | 나레이션 삭제 | +| GET | `/rd/cm-song/{id}/download` | `download()` | 음성 파일 다운로드 | +| POST | `/rd/cm-song/generate-lyrics` | `generateLyrics()` | AI 가사 생성 (Gemini) | +| POST | `/rd/cm-song/generate-audio` | `generateAudio()` | TTS 음성 생성 | + +### 6.3 CM송 데이터 모델 + +| 모델 | 테이블 | 설명 | +|------|--------|------| +| `CmSong` | `cm_songs` | 나레이션 (회사명, 업종, 가사, 음성파일) | + +**저장 경로**: `tenant` 디스크 → `cm-songs/{tenant_id}/{filename}.wav` + +--- + +## 7. 오디오 엔진 상세 + +### 7.1 마스터 출력 체인 + +``` +각 소스 (Oscillator / BufferSource) + → GainNode (개별 볼륨) + → DynamicsCompressorNode (마스터 리미터) + → AudioContext.destination +``` + +**컴프레서 설정:** +- threshold: -6dB +- knee: 10dB +- ratio: 12:1 +- attack: 3ms +- release: 150ms + +### 7.2 WAV 내보내기 + +`OfflineAudioContext`로 오프라인 렌더링 후 `bufferToWav()` 변환. +- 샘플레이트: 44,100Hz +- 채널: 2 (스테레오) +- 비트: 16-bit PCM +- 오프라인 컨텍스트에도 동일한 DynamicsCompressor 적용 + +--- + +## 8. 관련 파일 + +| 파일 | 설명 | +|------|------| +| `resources/views/rd/sound-logo/index.blade.php` | SPA 뷰 (Alpine.js, ~2100줄) | +| `app/Http/Controllers/RdController.php` | 사운드 로고 API (4 메서드) | +| `app/Http/Controllers/Rd/CmSongController.php` | CM송/나레이션 CRUD (8 메서드) | +| `app/Models/Rd/CmSong.php` | CM송 모델 | +| `app/Helpers/AiTokenHelper.php` | Gemini 토큰 사용량 추적 | + +--- + +## 관련 문서 + +- [R&D 메뉴 개요](README.md) — R&D 전체 메뉴 구조 +- [AI 분석 리포트](../ai/README.md) — Gemini API 활용 패턴 참고 +- [사운드 로고 생성기 기획서](../../plans/sound-logo-generator-plan.md) — 초기 기획 + +--- + +**최종 업데이트**: 2026-03-08 diff --git a/sam/docs/guides/ai-config-settings.md b/guides/ai-config-settings.md similarity index 100% rename from sam/docs/guides/ai-config-settings.md rename to guides/ai-config-settings.md diff --git a/sam/docs/guides/ai-management.md b/guides/ai-management.md similarity index 100% rename from sam/docs/guides/ai-management.md rename to guides/ai-management.md diff --git a/sam/docs/guides/ai-model-update-workflow.md b/guides/ai-model-update-workflow.md similarity index 100% rename from sam/docs/guides/ai-model-update-workflow.md rename to guides/ai-model-update-workflow.md diff --git a/sam/docs/guides/pptx-generation-guide.md b/guides/pptx-generation-guide.md similarity index 100% rename from sam/docs/guides/pptx-generation-guide.md rename to guides/pptx-generation-guide.md diff --git a/guides/project-launch-roadmap.md b/guides/project-launch-roadmap.md new file mode 100644 index 0000000..c3459fe --- /dev/null +++ b/guides/project-launch-roadmap.md @@ -0,0 +1,639 @@ +# SAM 프로젝트 런칭 로드맵 + +**작성일**: 2025-11-24 +**최종 수정**: 2025-12-02 +**목적**: 프로젝트 전체 방향성 관리 및 런칭 준비 현황 추적 +**대상**: 프로젝트 관리 및 의사결정용 + +--- + +## 1. 프로젝트 현황 개요 + +### 전체 시스템 구성 +``` +SAM (Smart Application Management) +├── api/ - Laravel 12 REST API (독립 모델) +├── mng/ - Plain Laravel 관리자 패널 (독립 모델, 운영 주력) +├── react/ - Next.js 15 사용자 프론트엔드 +├── docs/ - 기술 문서 +├── design/ - 디자인 시스템 (Storybook) +├── planning/ - 기획 문서 +└── docker/ - Docker 개발 환경 +``` + +### 프로젝트 구분 + +| 구분 | 대상 | 설명 | 담당 | +|------|------|------|------| +| **MES (경동기업)** | 경동기업 | 메인 프로젝트, 디자인 시스템 기준 | 디자이너 (기획+디자인) | +| **MES (주일기업)** | 주일기업 | 경동기업 디자인 기반 커스터마이징 | 기획자 | +| **ERP** | SAM 공통 | 공통 모듈 (인사, 회계, 결재 등) | 기획자 | + +### MVP 범위 정의 + +| 구분 | 범위 | 설명 | +|------|------|------| +| **코어 MVP** | MES 핵심 기능 | 견적 → 수주 → 생산 → 출하 흐름 | +| **1차 MVP** | 코어 MVP + 추가 기능 | 품질, 자재, 단가, 회계 등 확장 | + +### 각 시스템 역할 +- **api**: 모든 비즈니스 로직과 데이터 처리의 중심 +- **mng**: Pure Blade + Tailwind 관리자 패널 (운영 환경 주력) +- **react**: 최종 사용자용 인터페이스 +- **design**: 디자인 시스템 및 컴포넌트 문서 + +### 현재 개발 완료율 +- **백엔드 (API)**: 약 70% 완료 + - ✅ 인증/권한, 멀티테넌트, 기준정보 + - ✅ 제품/BOM, 견적/수주, 자재입고/검사 + - 🔄 공정/생산, 단가/원가, 재고관리 + +- **프론트엔드**: 약 50% 완료 + - ✅ Admin 패널 27개 Resources + - 🔄 React 사용자 포털 개발 중 + +--- + +## 2. 팀 구성 및 역할 + +### 팀 역할 분담 + +| 역할 | 담당자 | 주요 업무 | 비고 | +|------|--------|----------|------| +| **디자이너** | 재웅 정 | MES(경동기업) 기획 + 디자인 | 디자인 시스템 기준 | +| **기획자** | 이태화 | ERP 스토리보드, MES(주일기업) 기획, 운영, QA | 기획 완료 시 MES 합류 | +| **Frontend** | - | React 개발 | MES(경동) 우선 | +| **Backend** | hso be | API 서포트, mng 개발, 인프라, 정책/운영 | 전체 기술 지원 | + +### 작업 우선순위 + +**Frontend 우선순위:** +1. **MES (경동기업)** - 디자이너 결과물 즉시 개발 +2. **ERP + MES (주일기업)** - MES 짬/대기 시 병행 + +**Backend 역할:** +- Frontend API 서포트 +- mng (운영 관리자 패널) 개발 +- 인프라 셋팅 +- 정책/운영 관련 일정 체크 + +--- + +## 3. 주요 마일스톤 개요 + +### 📅 마일스톤 타임라인 + +``` +2025년 12월 2026년 1월 2026년 2월 2026년 3월 + | | | | + MS1 MS2 MS3 MS4 +코어 MVP 완료 1차 MVP + 베타 정식 런칭 안정화 완료 +(단위테스트) (통합테스트) +``` + +### 마일스톤 요약 + +| 마일스톤 | 목표 | 기한 | 주요 내용 | +|---------|------|------|----------| +| **MS1** | 코어 MVP 개발 완료 | 2025-12-31 | MES 핵심 기능 + 단위테스트 | +| **MS2** | 1차 MVP + 베타 오픈 | 2026-01-31 | 통합테스트 + 베타 서비스 오픈 | +| **MS3** | 정식 런칭 | 2026-02-28 | 운영 서버 오픈 | +| **MS4** | 안정화 완료 | 2026-03-31 | 고객 성공 사례 확보 | + +--- + +## 4. 기획 및 디자인 일정 + +### 4.1 MES (경동기업) - 디자이너 일정 +**기간**: 2025-11-26 ~ 2025-12-26 (약 21일) + +#### Phase 1: 견적 (11/27 ~ 11/28) +| 화면ID | 화면명 | 유형 | 공수 | 시작일 | 종료일 | +|--------|--------|------|------|--------|--------| +| S2 | 견적 리스트 | LIST | 0.25 | 11-27(목) | 11-27(목) | +| S2-1 | 견적 등록 | FORM | 0.5 | 11-27(목) | 11-27(목) | +| S2-2 | 견적 수정 | FORM | 0.25 | 11-27(목) | 11-28(금) | +| S2-3 | 견적 상세+탭 | DETAIL | 0.5 | 11-28(금) | 11-28(금) | +| S2-4 | 견적서 출력 | PRINT | 0.25 | 11-28(금) | 11-28(금) | + +#### Phase 2: 기준-수식 (11/28 ~ 12/01) +| 화면ID | 화면명 | 유형 | 공수 | 시작일 | 종료일 | +|--------|--------|------|------|--------|--------| +| M12 | 견적수식 리스트 | LIST | 0.1 | 11-28(금) | 11-28(금) | +| M12-1 | 견적수식 등록 | FORM | 0.2 | 12-01(월) | 12-01(월) | + +#### Phase 3: 수주 (12/01 ~ 12/04) +| 화면ID | 화면명 | 유형 | 공수 | 시작일 | 종료일 | +|--------|--------|------|------|--------|--------| +| S3-1 | 수주 리스트 | LIST | 0.25 | 12-01(월) | 12-01(월) | +| S3-2 | 수주 등록 | FORM | 0.5 | 12-01(월) | 12-02(화) | +| S4-3 | 수주 수정 | FORM | 1 | 12-02(화) | 12-03(수) | +| S4-4 | 수주 상세+탭 | DETAIL | 1 | 12-03(수) | 12-04(목) | +| S4-5 | 수주서 발송 | PRINT | 0.5 | 12-04(목) | 12-04(목) | + +#### Phase 4: 생산 (12/04 ~ 12/08) +| 화면ID | 화면명 | 유형 | 공수 | 시작일 | 종료일 | +|--------|--------|------|------|--------|--------| +| P1-3-3 | 작업지시 리스트 | LIST | 0.25 | 12-04(목) | 12-05(금) | +| P1-3-4 | 작업지시 등록 | FORM | 0.5 | 12-05(금) | 12-05(금) | +| P1-3-5 | 작업지시 수정 | FORM | 0.2 | 12-05(금) | 12-05(금) | +| P1-3-6 | 작업지시 상세 | DETAIL | 0.25 | 12-05(금) | 12-08(월) | +| P1-3-7 | 작업실적 입력 | FORM | 0.5 | 12-08(월) | 12-08(월) | +| P1-3-8 | 작업실적 조회 | DETAIL | 0.25 | 12-08(월) | 12-08(월) | + +#### Phase 5: 기준-공정 (12/08 ~ 12/10) +| 화면ID | 화면명 | 유형 | 공수 | 시작일 | 종료일 | +|--------|--------|------|------|--------|--------| +| M5 | 공정 리스트 | LIST | 0.25 | 12-08(월) | 12-08(월) | +| M5-1 | 공정 등록 | FORM | 0.5 | 12-09(화) | 12-09(화) | +| M5-2 | 공정 수정 | FORM | 0.2 | 12-09(화) | 12-09(화) | +| M5-3 | 공정 상세 | DETAIL | 0.5 | 12-10(수) | 12-10(수) | + +#### Phase 6: 출하 (12/10 ~ 12/12) +| 화면ID | 화면명 | 유형 | 공수 | 시작일 | 종료일 | +|--------|--------|------|------|--------|--------| +| S4 | 출하 리스트 | LIST | 0.25 | 12-10(수) | 12-10(수) | +| S4-1 | 출하 등록 | FORM | 0.5 | 12-10(수) | 12-11(목) | +| S4-2 | 배송 조율/관리 | FORM | 0.5 | 12-11(목) | 12-11(목) | +| S4-3 | 상차 체크리스트 | FORM | 0.5 | 12-11(목) | 12-12(금) | +| S4-4 | 출하 수정+탭 | FORM | 0.5 | 12-12(금) | 12-12(금) | + +#### Phase 7: 거래처 (12/15 ~ 12/16) +| 화면ID | 화면명 | 유형 | 공수 | 시작일 | 종료일 | +|--------|--------|------|------|--------|--------| +| S1-1 | 거래처 리스트 | LIST | 0.25 | 12-15(월) | 12-15(월) | +| S1-1 | 거래처 등록 | FORM | 0.5 | 12-15(월) | 12-15(월) | +| S1-2 | 거래처 수정 | FORM | 0.1 | 12-15(월) | 12-16(화) | +| S1-3 | 거래처 상세+탭 | DETAIL | 0.25 | 12-16(화) | 12-16(화) | + +#### Phase 8: 품질 (12/16 ~ 12/19) +| 화면ID | 화면명 | 유형 | 공수 | 시작일 | 종료일 | +|--------|--------|------|------|--------|--------| +| M9 | 검사기준 리스트 | LIST | 0.5 | 12-16(화) | 12-16(화) | +| M9-1 | 검사기준 등록 | FORM | 1 | 12-17(수) | 12-17(수) | +| Q1 | 검사관리 리스트 | LIST | 0.5 | 12-18(목) | 12-18(목) | +| Q1-1 | 검사관리 등록 | FORM | 1 | 12-18(목) | 12-19(금) | +| Q1-2 | 검사관리 상세 | DETAIL | 0.5 | 12-19(금) | 12-19(금) | + +#### Phase 9: 자재 (12/19 ~ 12/24) +| 화면ID | 화면명 | 유형 | 공수 | 시작일 | 종료일 | +|--------|--------|------|------|--------|--------| +| I1 | 재고현황 리스트 | LIST | 0.5 | 12-19(금) | 12-22(월) | +| I1-1 | 재고 상세+탭 | DETAIL | 1 | 12-22(월) | 12-23(화) | +| I2 | 입고 리스트 | LIST | 0.5 | 12-23(화) | 12-23(화) | +| I2-1 | 입고 등록 | FORM | 0.8 | 12-23(화) | 12-24(수) | +| I2-3 | 입고 상세+탭 | DETAIL | 0.5 | 12-24(수) | 12-24(수) | + +#### Phase 10: 단가 (12/24) +| 화면ID | 화면명 | 유형 | 공수 | 시작일 | 종료일 | +|--------|--------|------|------|--------|--------| +| S6 | 단가 리스트 | LIST | 0.25 | 12-24(수) | 12-24(수) | +| S6-1 | 단가 등록 | FORM | 0.25 | 12-24(수) | 12-24(수) | +| S6-2 | 단가 수정 | FORM | 0.25 | 12-24(수) | 12-24(수) | +| S6-3 | 단가 상세+탭 | DETAIL | 0.25 | 12-24(수) | 12-24(수) | + +#### Phase 11: 회계 (12/26) +| 화면ID | 화면명 | 유형 | 공수 | 시작일 | 종료일 | +|--------|--------|------|------|--------|--------| +| A1 | 판매조회 리스트 | LIST | 0.5 | 12-26(금) | 12-26(금) | +| A4 | 수금 리스트 3탭 | LIST | 0.25 | 12-26(금) | 12-26(금) | +| A4-1 | 수금 등록 | FORM | 0.25 | 12-26(금) | 12-26(금) | + +### 4.2 기획자 일정 (ERP + 운영) + +#### 기획 (이태화) +| 구분 | 업무 | 기간 | 일수 | 대상 | +|------|------|------|------|------| +| 스토리보드 | 공통, ERP | 11/26 ~ 12/12 | 13 | SAM | +| 가입 및 로그인 | 스토리보드 | 11/27 | 1 | SAM | +| 인사관리, 전자결재 | 스토리보드 | 11/28 ~ 12/01 | 4 | SAM | +| 회계, 보고서 | 스토리보드 | 12/02 ~ 12/09 | 6 | SAM | +| 고객센터, 게시판 | 스토리보드 | 12/10 ~ 12/12 | 3 | SAM | +| 주일기업 요구사항 정리 | 요구사항 | 12/02 ~ 12/12 | 9 | 주일기업 | +| 스토리보드 - 주일기업 MES | 기획 | 12/15 ~ 12/30 | 12 | 주일기업 | +| 스토리보드 - ERP 2차 | 기획 | 12/31 ~ 01/13 | 12 | SAM | +| 스토리보드 - MES 2차 | 기획 | 01/14 ~ 01/27 | 12 | SAM | + +#### 운영 (hso be) +| 구분 | 업무 | 기간 | 일수 | 대상 | +|------|------|------|------|------| +| 보고서 지표 검토 | 운영 | 11/27 ~ 11/28 | 2 | SAM | +| 주일기업 자료 정리 및 취합 | 운영 | 11/26 ~ 11/28 | 3 | 주일기업 | +| 주일기업 업무 프로세스 인터뷰 | 운영 | 12/01 ~ 12/05 | 5 | 주일기업 | +| 법률 및 정책 검토 | 운영 | 12/08 ~ 12/19 | 10 | SAM | + +--- + +## 5. MS1: 코어 MVP 개발 완료 (2025-12-31) + +**목표**: MES 핵심 기능 개발 완료 + 단위테스트 통과 + +### 코어 MVP 범위 +- **핵심 흐름**: 견적 → 수주 → 생산(작업지시/실적) → 출하 +- **기준정보**: 거래처, 공정, 견적수식 +- **단위테스트**: 커버리지 60% 이상 + +### 완료 기준 +- ✅ 코어 MVP 기능 100% 구현 +- ✅ 단위테스트 커버리지 60% 이상 +- ✅ Swagger 문서화 (코어 MVP 범위) +- ✅ Critical/High 버그 0건 +- ✅ API 평균 응답 속도 < 500ms + +### 주요 산출물 +- [ ] 코어 MVP 소스코드 (api, react) +- [ ] API 문서 (Swagger) +- [ ] 단위테스트 보고서 + +### Week별 작업 + +**Week 1 (12/02-12/08)** +| 팀 | 작업 내용 | +|----|----------| +| 📋 기획 | 회계/보고서 스토리보드 | +| 📋 운영 | 주일기업 업무 프로세스 인터뷰 | +| 🎨 디자인 | 생산, 기준-공정 화면 | +| 🔧 Backend | 공정/단가 체계 완성 | +| 💻 Frontend | React 개발 시작 (12/08~) | + +**Week 2 (12/09-12/15)** +| 팀 | 작업 내용 | +|----|----------| +| 📋 기획 | 고객센터/게시판 스토리보드, 주일기업 MES 스토리보드 시작 | +| 📋 운영 | 법률 및 정책 검토 시작 | +| 🎨 디자인 | 출하, 거래처 화면 | +| 🔧 Backend | 견적서 PDF, 재고 트랜잭션 | +| 💻 Frontend | 견적/수주 화면 개발 | +| 🧪 QA | 단위테스트 시작 (12/10~) | + +**Week 3 (12/16-12/22)** +| 팀 | 작업 내용 | +|----|----------| +| 📋 기획 | 주일기업 MES 스토리보드 진행 | +| 🎨 디자인 | 품질, 자재 화면 | +| 🔧 Backend | API 안정화, 버그 수정 | +| 💻 Frontend | 생산/출하 화면 개발 | +| 🧪 QA | 단위테스트 진행 | + +**Week 4 (12/23-12/31)** +| 팀 | 작업 내용 | +|----|----------| +| 🎨 디자인 | 단가, 회계 화면 (완료) | +| 🔧 Backend | 코어 MVP 마무리 | +| 💻 Frontend | 코어 MVP 화면 완료 | +| 🧪 QA | 단위테스트 완료 | + +### 체크포인트 +- 12/15: 개발 70% 완료, 단위테스트 시작 +- 12/22: 개발 90% 완료 +- 12/29: 코어 MVP 개발 완료 +- 12/31: **MS1 완료** - 단위테스트 통과 + +--- + +## 6. MS2: 1차 MVP + 베타 오픈 (2026-01-31) + +**목표**: 통합테스트 완료 + 1차 MVP 완료 + 베타 서비스 오픈 + +### 1차 MVP 범위 (추가 예정) +- **확장 기능**: 품질, 자재, 단가, 회계 +- **추가 기능**: (1차 MVP 일정에서 별도 정의) + +### 완료 기준 +- ✅ 통합테스트 통과 +- ✅ 베타 서버 구축 완료 +- ✅ 파일럿 고객 온보딩 완료 +- ✅ 주요 시나리오 실전 테스트 완료 + +### 주요 산출물 +- [ ] 1차 MVP 소스코드 +- [ ] 통합테스트 보고서 +- [ ] 베타 서버 환경 + +### Week별 작업 + +**Week 1 (01/01-01/05)** +| 팀 | 작업 내용 | +|----|----------| +| 🧪 QA | 통합테스트 시작 | +| 🔧 Backend | 베타 서버 구축, 도메인/SSL 설정 | +| 📋 기획 | ERP 2차 스토리보드 진행 | + +**Week 2 (01/06-01/12)** +| 팀 | 작업 내용 | +|----|----------| +| 🧪 QA | 통합테스트 진행 | +| 🔧 Backend | 파일럿 고객 데이터 준비 | +| 💻 Frontend | 버그 수정, UI 개선 | +| 📋 기획 | ERP 2차 스토리보드 완료 (01/13) | + +**Week 3 (01/13-01/19)** +| 팀 | 작업 내용 | +|----|----------| +| 🧪 QA | 통합테스트 완료 | +| 운영 | 파일럿 고객 온보딩 (1차) | +| 📋 기획 | MES 2차 스토리보드 시작 (01/14) | + +**Week 4 (01/20-01/31)** +| 팀 | 작업 내용 | +|----|----------| +| 전체 | 실전 테스트, 피드백 수집 | +| 🔧 Backend | 긴급 버그 수정 | +| 📋 기획 | MES 2차 스토리보드 진행 | + +### 베타 고객 프로필 +| 고객사 | 업종 | 주요 사용 기능 | 기대 효과 | +|--------|------|----------------|----------| +| 경동기업 | 제조 | 견적/수주/BOM/생산 | MES 전체 검증 | +| 주일기업 | 제조 | MES 커스터마이징 | 확장성 검증 | + +### 체크포인트 +- 01/05: 베타 서버 오픈 +- 01/13: 통합테스트 완료 +- 01/20: 파일럿 고객 온보딩 완료 +- 01/31: **MS2 완료** - 베타 서비스 오픈 + +--- + +## 7. MS3: 정식 런칭 (2026-02-28) + +**목표**: 운영 서버 오픈 및 본격적인 서비스 시작 + +### 완료 기준 +- ✅ 운영 서버 구축 완료 (이중화) +- ✅ 베타 피드백 반영 완료 +- ✅ 보안 감사 통과 +- ✅ 법적 문서 완비 + +### 주요 산출물 +- [ ] 운영 서버 환경 +- [ ] 보안 감사 보고서 +- [ ] 마케팅 자료 + +### Week별 작업 + +**Week 1-2 (02/01-02/14): 운영 준비** +| 작업 | 내용 | +|------|------| +| 베타 피드백 반영 | UI/UX 개선, 성능 최적화 | +| 운영 서버 구축 | 이중화, 모니터링, 백업 | +| 보안 감사 | 취약점 점검 및 수정 | +| 📋 기획 | MES 2차 스토리보드 완료 (01/27) | + +**Week 3-4 (02/15-02/28): 런칭** +| 작업 | 내용 | +|------|------| +| 정식 오픈 | 운영 서버 오픈 | +| 고객 온보딩 | 초기 고객 온보딩 시작 | + +### 체크포인트 +- 02/14: 운영 준비 완료 +- 02/28: **MS3 완료** - 정식 런칭 + +--- + +## 8. MS4: 안정화 완료 (2026-03-31) + +**목표**: 서비스 안정화 및 초기 고객 성공 사례 확보 + +### 완료 기준 +- ✅ 시스템 가용성 99.5% 이상 +- ✅ 고객 만족도 4.0/5.0 이상 +- ✅ 성공 사례 3건 이상 확보 + +### 주요 작업 +- 런칭 후 긴급 이슈 대응 +- 모니터링 강화 +- 고객 피드백 수집 및 반영 +- 성능 최적화 +- 고객 성공 사례 수집 +- Q2 로드맵 수립 + +### 체크포인트 +- 03/15: 초기 안정화 완료 +- 03/31: **MS4 완료** - 안정화 완료 + +--- + +## 9. 개발 방향성 + +### 기술 아키텍처 방향 +- **Backend**: Laravel 12 + PHP 8.4+ +- **Frontend**: Next.js 15 + React 18 +- **Database**: MySQL 8.0 (멀티테넌트 구조) +- **Auth**: Laravel Sanctum +- **API**: RESTful + Swagger 문서화 +- **Deployment**: Docker + Docker Compose + +### 핵심 개발 원칙 +1. **Service-First**: 모든 비즈니스 로직은 Service 클래스에 +2. **Multi-tenancy**: BelongsToTenant 스코프 필수 적용 +3. **FormRequest**: Controller에서 직접 검증 금지 +4. **API-First**: Backend 완성 후 Frontend 연동 +5. **문서화**: Swagger 100% 완성 목표 + +### 디자인 시스템 전략 +- **MES (경동기업)** 기준으로 디자인 시스템 구성 +- **ERP**는 경동기업 디자인 시스템 기반으로 Frontend가 직접 개발 +- **MES (주일기업)**은 경동기업 디자인 기반 커스터마이징 + +### 품질 기준 +- API Rules 100% 준수 +- Swagger 문서화 완성도 100% +- 테스트 커버리지 60% 이상 +- Pint 코드 포맷팅 통과 +- i18n 메시지 키 사용 + +--- + +## 10. 개발 작업 현황 + +### ✅ 백엔드 완료 항목 + +#### API 공통 기반 +- [x] Exception Handler +- [x] Swagger 설정 (l5-swagger v1) +- [x] API Key 인증 +- [x] Rate Limit, CORS +- [x] 권한 체크 미들웨어 + +#### 인증/보안 +- [x] API Key 모델 및 인증 +- [x] Role-Permission 시스템 +- [x] 멀티테넌트 권한 구조 +- [x] 권한 오버라이드 시스템 + +#### 테넌트 관리 +- [x] BelongsToTenant 글로벌 스코프 +- [x] TenantBootstrap 서비스 +- [x] 테넌트 컨텍스트 주입 +- [x] 테넌트 옵션/설정 관리 + +#### 기준정보/코드 관리 +- [x] Category (3단계 트리) +- [x] CategoryField (동적 필드) +- [x] CategoryTemplate +- [x] Classification (공통 코드) +- [x] CommonCode 관리 + +#### 제품/부품/자재 도메인 +- [x] Product 모델 (67개 모델) +- [x] Part 관리 +- [x] Material 관리 +- [x] ProductComponent (BOM 연결) +- [x] PriceHistory (단가 이력) + +#### BOM (Bill of Materials) +- [x] BomTemplate 관리 +- [x] BomTemplateItem CRUD +- [x] BomCalculationService (가격 계산) +- [x] ModelVersion (버전 관리) +- [x] 재귀 BOM 구조 + +#### 영업 흐름 +- [x] Estimate (견적) - 기본 CRUD +- [x] EstimateItem (견적 라인) +- [x] Order (수주) - 5개 모델 +- [x] OrderItem, OrderHistory +- [x] OrderItemComponent + +#### 자재입고/수입검사 +- [x] MaterialReceipt (자재입고) +- [x] MaterialInspection (수입검사) +- [x] MaterialInspectionItem (검사 항목) + +#### 파일/로그 시스템 +- [x] FileService, FileStorageService +- [x] AuditLogger, AuditLogService +- [x] File 모델 (Polymorphic) + +### 🔄 백엔드 진행 중 (코어 MVP) + +#### 공정/생산 계획 +- [ ] Process Routing (공정 라우팅) +- [ ] Work Order (작업지시) +- [ ] Production Record (생산실적) + +#### 견적서 출력 +- [ ] 견적서 HTML 템플릿 +- [ ] PDF 생성 (DomPDF/Snappy) +- [ ] 견적서 미리보기 API + +### ⏳ 백엔드 예정 (1차 MVP) + +#### 품질/자재/단가/회계 +- [ ] 검사기준, 검사관리 +- [ ] 재고현황, 입고 관리 +- [ ] 단가 정책 로직 +- [ ] 회계 조회/수금 + +### ✅ 프론트엔드 완료 항목 + +#### MNG 패널 (Pure Blade + Tailwind) +- [x] 주요 관리 화면 구현 +- [x] Product, BOM, Material +- [x] Category, Role, Permission +- [x] Department, User, Tenant +- [x] Client, File 관리 + +### 🔄 프론트엔드 진행 중 + +#### React 사용자 포털 +- [ ] 공통 레이아웃 최종 정리 +- [ ] 견적/수주 화면 +- [ ] 생산/출하 화면 +- [ ] 기준정보 관리 UI + +--- + +## 11. 리스크 관리 + +### High Risk +| 리스크 | 영향도 | 완화 방안 | 담당 | +|--------|--------|-----------|------| +| 개발 일정 지연 | High | 주간 진행률 체크, 우선순위 조정 | PM | +| 디자인-개발 병목 | High | Frontend 버퍼 확보, ERP 병행 | Frontend | +| 단가 계산 로직 복잡도 | High | 전문가 리뷰, Week 1 집중 | Backend | + +### Medium Risk +| 리스크 | 영향도 | 완화 방안 | 담당 | +|--------|--------|-----------|------| +| 기획-개발 동기화 | Medium | 주간 싱크업, 스토리보드 우선 리뷰 | PM | +| 통합 테스트 시간 부족 | Medium | 자동화 테스트 확대 | QA | + +--- + +## 12. 핵심 성공 지표 (KPI) + +### 기술 지표 +- [ ] 코어 MVP API 엔드포인트 구현 +- [ ] Swagger 문서 100% 완성 (MVP 범위) +- [ ] 테스트 커버리지 60% 이상 +- [ ] API 평균 응답 속도 < 500ms +- [ ] Critical/High 버그 0건 + +### 품질 지표 +- [ ] Service-First 아키텍처 100% 준수 +- [ ] FormRequest 검증 100% 적용 +- [ ] BelongsToTenant 스코프 100% 적용 +- [ ] Pint 코드 포맷팅 100% 통과 + +### 비즈니스 지표 +- [ ] 베타 고객 2개사 확보 +- [ ] 정식 고객 확보 (런칭 후) +- [ ] 고객 만족도 4.0/5.0 이상 + +--- + +## 13. 담당자 및 연락처 + +| 역할 | 담당자 | 주요 업무 | 비고 | +|------|--------|----------|------| +| 프로젝트 관리 | - | 전체 일정 및 방향성 관리 | PM | +| 디자이너 | 재웅 정 | MES(경동기업) 기획 + 디자인 | 디자인 시스템 기준 | +| 기획자 | 이태화 | ERP/MES 스토리보드, 운영, QA | 기획 완료 시 MES 합류 | +| 백엔드 개발 | hso be | API/mng/인프라/정책 | 기술 총괄 | +| 프론트엔드 개발 | - | React 개발 | MES(경동) 우선 | +| QA | - | 테스트 | 단위/통합 테스트 | + +--- + +## 14. 작업 추적 및 관리 + +### 진행 상황 업데이트 +- **매일**: 각 저장소별 CURRENT_WORKS.md 업데이트 +- **매주**: 주차별 로드맵 진행률 체크 +- **매 2주**: 전체 로드맵 리뷰 및 조정 + +### 관련 문서 +- **개발 세부 계획**: `/claudedocs/SAM_DECEMBER_ROADMAP.md` +- **MES 프로젝트**: `/claudedocs/mes/MES_PROJECT_ROADMAP.md` +- **프로젝트 가이드**: `/CLAUDE.md` +- **빠른 참조**: `/SAM_QUICK_REFERENCE.md` + +--- + +## 15. 다음 단계 (1차 MVP 이후) + +### 1차 MVP 추가 기능 (별도 일정) +- 품질 관리 (검사기준/검사관리) +- 자재 관리 (재고현황/입고) +- 단가 관리 +- 회계 (판매조회/수금) + +### Phase 2: 프론트엔드 고도화 +- React Admin 패널 완전 재구축 +- 사용자 포털 (고객 견적 요청) +- 모바일 대응 +- 실시간 대시보드 + +### Phase 3: 고급 기능 +- 실시간 생산 모니터링 +- IoT 센서 연동 +- AI 기반 수요 예측 + +--- + +**작성**: Claude Code +**최종 업데이트**: 2025-12-02 +**다음 리뷰**: 2025-12-09 (주간 체크) \ No newline at end of file diff --git a/sam/docs/guides/server-how-it-works.md b/guides/server-how-it-works.md similarity index 100% rename from sam/docs/guides/server-how-it-works.md rename to guides/server-how-it-works.md diff --git a/sam/docs/guides/table-design-guide.md b/guides/table-design-guide.md similarity index 100% rename from sam/docs/guides/table-design-guide.md rename to guides/table-design-guide.md diff --git a/plans/SAM_ERP_Storyboard_D1.4.md b/plans/SAM_ERP_Storyboard_D1.4.md new file mode 100644 index 0000000..8b8f4d7 --- /dev/null +++ b/plans/SAM_ERP_Storyboard_D1.4.md @@ -0,0 +1,1150 @@ +# SAM ERP 스토리보드 D1.4 + +> **작성일**: 2026-01-16 +> **버전**: D1.4 +> **상태**: 프론트 작성 +> **문서 ID**: SAM_ERP +> **원본**: `SAM_ERP_Storyboard_D1.4_260116.pdf` (167페이지) + +--- + +## 1. 문서 이력 (Document History) + +| 날짜 | 버전 | 주요 내용 | 상세 내용 | +|------|------|----------|----------| +| 2025-12-01 | D0.6 | 프론트 초안 | PC ERP - 인사관리 & 전자결재 작성 | +| 2025-12-01 | D0.7 | 프론트 작성 | PC ERP - 인사관리 & 전자결재 피드백 반영 | +| 2025-12-16 | D0.8 | 프론트 작성 | PC ERP - 회계 & 보고서 작성. 목록화면 기간 설정 영역 추가, GPS 출퇴근 추가, 급여관리/상세 삭제, 회계관리 추가, 출퇴근관리 추가, 카드/계좌관리 및 보고서 추가 | +| 2025-12-18 | D1.0 | 프론트 작성 | PC ERP - 구독 & 고객센터 작성. 게시판 추가, 악성채권 추심관리 상세 추가, 팝업관리/게시판관리/알림설정 추가, 계정정보/회사정보/구독관리/결제내역/고객센터 추가 | +| 2025-12-22 | D1.1 | 프론트 작성 | 카드 내역 관리 수정 | +| 2025-12-31 | D1.2 | 프론트 작성 | 알림 소리 설정 추가, 접대비 현황 수정 | +| 2026-01-07 | D1.3 | 프론트 작성 | 보고서 정보를 대시보드로 이동, SAM AI 채팅 버튼 추가, 화면 추가 다수, 항목 설정 버튼 추가 | +| 2026-01-16 | D1.4 | 프론트 작성 | 오늘의 이슈/현황판 화면 수정, 현황판 영역 및 3번 추가, 계정과목명 변경 (p99,100,104,106,108,123) | + +--- + +## 2. 메뉴 구조 (Menu Structure) + +``` +SAM +├── 로그인 / 회원가입 +├── 대시보드 +├── MES +│ ├── 판매관리 +│ ├── 구매관리 +│ ├── 발주관리 +│ ├── 공사관리 +│ ├── 생산관리 +│ ├── 품질관리 +│ ├── 자재관리 +│ ├── 장비관리 +│ └── 차량관리 +├── ERP +│ ├── 인사관리 +│ │ ├── 부서관리 +│ │ ├── 사원관리 +│ │ ├── 근태관리 +│ │ └── 휴가관리 +│ ├── 전자결재 +│ │ ├── 기안함 +│ │ ├── 결재함 +│ │ └── 참조함 +│ ├── 게시판 +│ ├── 회계관리 +│ │ ├── 거래처관리 +│ │ ├── 매출관리 +│ │ ├── 매입관리 +│ │ ├── 입금관리 +│ │ ├── 출금관리 +│ │ ├── 어음관리 +│ │ ├── 거래처원장 +│ │ ├── 일일 일보 +│ │ ├── 지출 예상 내역서 +│ │ ├── 미수금 현황 +│ │ ├── 악성채권 추심관리 +│ │ ├── 입출금 계좌 조회 +│ │ └── 카드 내역 관리 +│ ├── 기준정보 +│ │ ├── 직급관리 +│ │ ├── 직책관리 +│ │ ├── 권한관리 +│ │ ├── 근무관리 +│ │ ├── 출퇴근관리 +│ │ ├── 휴가관리 +│ │ ├── 카드관리 +│ │ ├── 계좌관리 +│ │ ├── 팝업관리 +│ │ ├── 게시판관리 +│ │ └── 알림설정 +│ └── 보고서 및 분석 +├── 계정정보 +├── 회사정보 +├── 구독관리 +├── 결제내역 +└── 고객센터 + ├── 공지사항 + ├── 이벤트 + ├── FAQ + └── 1:1 문의 +``` + +--- + +## 3. 공통 요소 + +### 3.1 제스처/인터랙션 + +| Type | 설명 | 적용 | +|------|------|------| +| Tap | 일정영역을 사용자가 터치 | Yes | +| Touch & Hold | 화면을 터치한 후 계속 누르고 있는 상태 | No | +| Double Tap | 일정영역을 두 번 터치 | No | +| Drag & Drop | 터치 혹은 홀드 상태에서 오브젝트를 이동하여 원하는 위치에 배치 | Yes | +| Scroll Up/Down | 위/아래로 스크롤 | Yes | +| Swipe Left/Right | 좌/우로 스와이프 | Yes | +| Pinch Zoom in/out | 오브젝트 또는 화면을 확대/축소 | Yes | + +### 3.2 반응형 웹 브레이크 포인트 + +| 구분 | 크기 | +|------|------| +| 모바일 | < 640px (기본) | +| 태블릿 | 768px ~ 1023px (md) | +| 데스크탑 | 1024px+ (lg) | +| 대형 모니터 | 1280px+ (xl) | + +### 3.3 화면 템플릿 + +- **A**: Status bar - 안테나, 통화, 배터리 등 시스템 OS 관리 영역 +- **B**: Browser 영역 - 브라우저 기능 영역 +- **C**: Title 영역 - 텍스트 또는 기능 버튼, 기본 가운데 정렬 +- **D**: Content 영역 - 컨텐츠 내용 표시, 길어질 경우 스크롤 +- **E**: Browser bar 영역 - 브라우저 유틸 바 영역 +- **F**: Keypad 영역 - 키보드 입력할 때 활성화 + +### 3.4 메시지 유형 + +| Type | 설명 | +|------|------| +| 알림 Alert | 사용자에게 상황을 알려주기 위한 팝업 (확인 버튼) | +| 확인 Alert | 사용자에게 확인이 필요할 경우 제공 (취소/확인 버튼) | +| 토스트 메시지 | 단순 Notify, 2~3초 후 Fade out | + +### 3.5 셀렉트 박스 + +- **기본**: 클릭 시 하단에 종류 목록 표시, 목록 중 하나만 선택 +- **다중 선택**: 복수 선택 가능, 전체 선택/해제 토글, 첫번째 항목명 + 추가 수 표시 +- **검색**: 검색어 입력 후 엔터 또는 검색 아이콘 클릭 시 검색 결과 표시 +- **검색 & 다중 선택**: 검색 + 복수 선택 기능 결합 + +### 3.6 가이드 메시지 + +- 긍정일 경우: 녹색 +- 부정일 경우: 붉은색 +- 입력 필드 하단 또는 Alert에 표시 + +### 3.7 공지 팝업 + +- 대상: 전체 또는 설정 부서 +- 설정 기간동안 대상에게 팝업 표시 +- "1일간 이 창을 열지 않음" 체크박스 (자정 기준) + +--- + +## 4. GNB, LNB, 푸터 (p9) + +### 4.1 GNB (Global Navigation Bar) + +| 번호 | 요소 | 설명 | +|------|------|------| +| 1 | 알림 버튼 | 클릭 시 알림 팝업 표시 | +| 2 | 개인 정보 버튼 | 디폴트 이미지, 이름, 직급 표시. 클릭 시 마이페이지 팝업 | +| 3 | 회사 로고 | 회사정보 화면에서 등록한 로고 표시, 회사 변경 시 해당 로고 변경 | +| 4 | 메뉴 영역 | 하위 메뉴 있을 경우 하단에 표시, 없을 경우 해당 화면으로 이동 | +| 5 | MES 메뉴 영역 | 영업관리, 판매관리, 구매관리 등 MES 메뉴 영역 | +| 6 | 푸터 영역 | 모든 화면 하단 공통 표시 | +| 7 | SAM AI 채팅 버튼 | 클릭 시 SAM AI 채팅 팝업 표시 | + +### 4.2 알림 팝업 (p10) + +- 각 디폴트 썸네일, 종류(공지사항, 안내), 제목/내용, 전송일시 표시 +- 클릭 시 해당 상세 화면으로 이동 +- 최신순 10개까지 표시 +- New 아이콘: 새 알림일 경우 표시, 클릭 시 사라짐 +- 붉은 점 아이콘: 새 알림이 있을 경우 표시, 모두 클릭 시 사라짐 + +### 4.3 마이페이지 팝업 (p11) + +| 번호 | 요소 | 설명 | +|------|------|------| +| 1 | 계정 아이디 | 이메일 표시 | +| 2 | 회사 셀렉트 박스 | 해당 계정이 생성한 회사(테넌트) 목록 표시, 등록순 정렬, 한 회사만 소유 시 숨김 | +| 3 | 로그아웃 버튼 | "정말 로그아웃하시겠습니까?" 확인 Alert | + +--- + +## 5. 운영 (영업) + +### 5.1 가입 및 로그인 플로우 + +``` +영업사원 → 사업자등록번호 입력 → 조회 + ├── 미등록 → 회사정보 등록 → 가입 신청 완료 + └── 등록됨 → 알림 Alert + +관리자(매니저) → 승인/거절 + ├── 승인 → 이메일로 URL 발송 → 약관 동의 → 비밀번호 설정 → SAM 로그인 + └── 거절 → 거절 알림 +``` + +### 5.2 운영 로그인 (p17) + +| 번호 | 요소 | 설명 | +|------|------|------| +| 1 | 아이디 인풋박스 | 테넌트 생성자: 이메일, 사용자: 이메일 또는 아이디 | +| 2 | 비밀번호 인풋박스 | 마지막 글자 제외 마스킹 처리 | +| 2-2 | 열람 버튼 | 열람/숨김 토글, 디폴트 숨김 | +| 3 | 자동 로그인 체크박스 | 체크 시 로그아웃 전까지 세션 유지 | +| 4 | 로그인 버튼 | 유효할 경우 사업자등록번호 조회 화면으로 이동 | + +**아이디 가이드 메시지:** + +| 상황 | 메시지 | +|------|--------| +| 필수 정보 미입력 | "필수 정보입니다." | +| 4글자 미만 | "이메일은 4자 이상 가능합니다." | +| 이메일 형식 유효하지 않음 | "이메일 주소를 다시 확인해주세요." | + +**비밀번호 가이드 메시지:** + +| 상황 | 메시지 | +|------|--------| +| 필수 정보 미입력 | "필수 정보입니다." | +| 8자 미만 | "8자 이상으로 만들어주세요." | +| 영문+숫자+특수문자 조합 아님 | "영문, 숫자, 특수문자를 모두 조합하여 구성해주세요. 단, `' ; -- < ( ) \ /` 보안상 사용 불가" | + +### 5.3 사업자등록번호 조회 (p18) + +| 번호 | 요소 | 설명 | +|------|------|------| +| 1 | 제조 데모 | 클릭 시 제조 데모 화면으로 이동 | +| 2 | 시공 데모 | 클릭 시 시공 데모 화면으로 이동 | +| 3 | 사업자등록번호 인풋박스 | 숫자만 가능, 10자리 | +| 4 | 다음 버튼 | 바로빌 조회 후: 휴폐업 시 알림, 사용가능+등록됨 시 알림, 사용가능+미등록 시 회사정보 등록 이동 | + +### 5.4 회사정보 등록 (p19) + +**회사(테넌트) 상태:** + +| 상태 | 설명 | +|------|------| +| 신청 | 신청 완료 상태 | +| 승인 | 계약 완료 및 계약금 50% 입금, 이메일로 URL 발송, 최초 로그인 시 ERP만 표시 | +| 거절 | 영업사원이 직접 거절 내용 전달 | +| 운영 | 프로그램 설정 완료, 잔금 50% 입금 및 인도, 당월 말일까지 무료, 익월부터 구독료 청구 | +| 만료 | 기간 종료, 종료일~3일동안 연장 결제 없음, 영업사원에게 알림, 서비스에 경고 배너 | +| 해지 대기 | 90일 대기 단계 | +| 해지 | 서비스 해지, 복구 불가 | +| 제재 | 서비스 이용 불가 | +| 탈퇴 | 로그인 불가, 복구 불가 | + +**등록 필드:** +- 회사 로고 (750x250px, 10MB 이하 PNG/JPEG/GIF) +- 회사명, 대표자명, 업태, 업종 +- 주소 (우편번호 찾기) +- 이메일(아이디), 세금계산서 이메일 +- 담당자명, 담당자 연락처 +- 사업자등록증 (파일 첨부) + +### 5.5 가입 신청 완료 (p20) + +- 가입 신청 완료 안내 문구 표시 +- 가입 신청 취소 버튼: "가입 신청 취소 시 등록한 모든 정보가 삭제됩니다." 확인 Alert + +### 5.6 가입 신청 승인 이메일 (p21) + +- 계정 활성화 버튼: 약관 동의 화면으로 이동 +- 지원, 블로그 버튼: 운영 노션 링크로 이동 + +### 5.7 약관 동의 (p22) + +- 필수 약관: 서비스 이용약관, 개인정보 취급방침, 기타 약관 +- 선택 약관: 마케팅 정보 수신 동의 (이메일, SMS) +- "약관에 동의합니다" 버튼: 모든 필수 약관 동의 시 활성화 → 비밀번호 설정 화면 이동 +- "전체 약관에 동의합니다" 버튼: 모든 필수+선택 약관 동의 처리 → 비밀번호 설정 화면 이동 + +### 5.8 비밀번호 설정 (p23) + +- 최소 8자 이상 영문+숫자+특수문자 조합 +- 비밀번호 확인 +- 계정 활성화 버튼: 로그인 화면으로 이동 + +--- + +## 6. GPS 출퇴근 + +### 6.1 출퇴근하기 (p25) + +| 번호 | 요소 | 설명 | +|------|------|------| +| 1 | 출퇴근 버튼 | GPS 출퇴근 사용 시에만 표시, 모바일일 경우에만 활성화 | +| 2 | 출퇴근 허용 반경 | 기준 좌표로부터의 허용 반경을 원형으로 표시 | +| 3 | 현재 위치 버튼 | 현재 위치를 지도 중심으로 표시 | +| 4-6 | 지도 컨트롤 | 확대(+), 축소(-), 슬라이드바 | +| 7 | 개인 정보 영역 | 프로필 이미지, 이름, 부서명, 직급명 | +| 8 | 현재 시각 | HH:MM:SS | +| 9 | 출근하기 버튼 | 위치 미설정 시 알림, 반경 초과 시 알림, 반경 이내 시 출근 기록 저장 | + +### 6.2 출근/퇴근 완료 (p26-27) + +- 출근/퇴근 완료 아이콘 이미지 표시 +- 완료 정보: 시:분:초, 일자(요일) +- 출근/퇴근 좌표의 본사/현장명 표시 +- 확인 버튼: 대시보드로 이동 + +### 6.3 현장등록 - 위치 정보 설정 (p28) + +- 위도/경도 입력 +- 주소 또는 경위도 값으로 설정 +- 각 현장의 GPS 중심값으로 설정 + +--- + +## 7. 대시보드 + +### 7.1 로그인 (p30) + +- 운영 로그인과 동일 구조 +- 로그인 버튼 클릭 시 대시보드 화면으로 이동 + +### 7.2 대시보드 메인 (p31-36) + +#### 7.2.1 오늘의 이슈 (p31) + +| 번호 | 요소 | 설명 | +|------|------|------| +| 1 | 항목 설정 버튼 | 항목 설정_대시보드 팝업 표시 | +| 2 | 오늘의 이슈 영역 | 당일 이슈 발생 시 알림, 즉시 승인/보류 처리 가능 | +| 3 | 필터 셀렉트 박스 | 전체, 수주 성공, 추심 이슈, 적정 재고, 결재 요청, 세금 신고, 신규 업체 등록, 근태, 발주 완료 | +| 4 | 이슈 목록 | 클릭 시 해당 상세 화면으로 이동, 화면 가로 길이에 따라 4/3/2/1열 표시 | +| 5 | 승인/반려 버튼 | 해당 건에 대해 즉시 승인/반려 처리 | +| 6 | 일일 일보 정보 | 현금성 자산 합계, 외국환(USD) 합계, 입금 합계, 출금 합계 | +| 7 | AI 리포트 | 핵심 키워드 강조 표시 (빨간색: 경고, 주황: 주의, 녹색: 긍정, 파랑: 양호) | + +**이슈 케이스:** +- 신규 업체 등록 +- 결근 등 근태 이벤트 +- 재고 미달 알림 +- 채권 추심 등록, 상태 변경 +- 발주, 수주 등록 +- 지출결의서 등 전자결재 상신 +- 세금 신고 알림 + +#### 7.2.2 현황판 (p32) + +- 수주, 채권 추심, 안전 재고, 세금 신고, 신규 업체 등록, 연차, 발주, 결재 요청 +- 경고 상태일 경우 해당 영역에 색상 하이라이트 +- 클릭 시 해당 상세 화면으로 이동 + +#### 7.2.3 당월 예상 지출 내역 (p32-33) + +- 매입, 카드, 발행어음, 총 예상 지출 합계 (전월 대비 %) +- AI 분석 메시지 (예상 지출 증감 원인 분석) + +#### 7.2.4 카드/가지급금 관리 (p33) + +**가지급금 정의:** +- 법인카드(지출결의서) 미정리 +- 접대비 불인정 +- 증빙미비 +- 업무관련성 소명 불가 (주말/심야 카드 사용, 불인정 가맹점) +- 대표자 개인 대여 +- 가지급금 인정이자 4.6% + +| 번호 | 요소 | 설명 | +|------|------|------| +| 1 | 매입 정보 | 클릭 시 당월 매입 상세 팝업 | +| 2 | 카드 정보 | 클릭 시 당월 카드 상세 팝업 | +| 3 | 발행어음 정보 | 클릭 시 당월 발행어음 상세 팝업 | +| 4 | 총 예상 지출 합계 | 클릭 시 당월 지출 예상 상세 팝업 | +| 5 | 가지급금 | 클릭 시 가지급금 상세 팝업 | +| 6 | 법인세 예상 가중 | 클릭 시 법인세 예상 가중 상세 팝업 | +| 7 | 대표자 종합세 예상 가중 | 클릭 시 대표자 종합소득세 예상 가중 상세 팝업 | + +#### 7.2.5 접대비 현황 (p33-34) + +- 매출, 접대비 총 한도, 접대비 잔여한도, 접대비 사용금액 +- AI 분석 메시지 (한도 대비 사용률, 초과 경고, 거래처 정보 누락 등) + +#### 7.2.6 복리후생비 현황 (p34) + +- 당해년도 한도, 기간별 한도/잔여/사용금액 +- AI 분석 (1인당 월 복리후생비, 식대 비과세 한도 초과 등) + +#### 7.2.7 미수금 현황 (p34) + +- 누적 미수금, 당월 미수금 +- 미수금 상위 회사 1, 2위 표시 +- AI 분석 (장기 미수금 경고, 리스크 분산 필요 등) + +#### 7.2.8 채권추심 현황 (p35) + +- 누적 악성채권, 추심중, 법적조치, 회수완료 +- 세금계산서 미발행 건수 +- AI 분석 (지급명령 신청 상태, 대손 처리 검토 등) + +#### 7.2.9 부가세 현황 (p35-36) + +- 매출세액, 매입세액, 예상 납부세액 +- AI 분석 (예상 환급세액/납부세액, 전기 대비 증감 분석) + +#### 7.2.10 캘린더 (p36) + +| 번호 | 요소 | 설명 | +|------|------|------| +| 1 | 이번주 + 좌우 화살표 | 이전월/다음월 스케줄 표시 | +| 2 | 캘린더 탭 | 주, 월 (디폴트: 월) | +| 3 | 부서 필터 | 전체, 부서, 개인 | +| 4 | 업무 필터 (다중선택) | 전체, 일정, 발주, 시공, 수주 성공, 추심 이슈 등 | +| 5 | 일정 영역 | [부서/이름] 제목 형태, 클릭 시 상세 이동 | +| 5-1 | 이슈 영역 | [구분] 제목 형태, 클릭 시 상세 이동 | +| 6 | 일자 영역 | 당일 외곽선 하이라이트, 지난 일자 색상 구분 | +| 7 | +N 버튼 | 해당 일자에 스케줄 2건 초과 시 초과건 숫자 표시 | +| 8 | 일정 목록 영역 | 선택된 일자의 일정 목록 | +| 9 | 일정 등록 버튼 | 일정 상세 팝업 표시 | + +### 7.3 일정 상세 팝업 (p37) + +- 부서 셀렉트 박스 (검색) +- 기간 영역: 기간 설정 달력 팝업 +- 시간 체크박스: 미설정 시 종일, 설정 시 시간 범위 활성화 +- 색상 선택 + +### 7.4 항목 설정_대시보드 팝업 (p38-39) + +**ON/OFF 설정 항목:** +- 오늘의 이슈: 수주, 채권 추심, 안전 재고, 세금 신고, 신규 업체 등록, 연차, 지각, 결근, 발주, 결재 요청 +- 현황판 (오늘의 이슈 항목 정보와 연동) +- 당월 예상 지출 내역, 카드/가지급금 관리, 일일 일보 +- 접대비 현황, 복리후생비 현황, 미수금 현황, 미수금 상위 회사 현황 +- 채권추심 현황, 부가세 현황, 캘린더 + +**접대비 한도 관리:** +- 기간 구분: 연간, 반기, 분기, 월 (총 한도를 분할 계산) +- 기업 구분: 일반법인, 중소기업 + +**복리후생비 한도 관리:** +- 계산 방식: 직원당 정액 금액 방식 / 연봉 총액 X 비율 방식 + +**중소기업 판단 기준표:** + +| 조건 | 기준 | 충족 요건 | +|------|------|----------| +| 매출액 | 업종별 상이 | 업종별 기준 금액 이하 | +| 자산총액 | 5,000억원 | 미만 | +| 독립성 | 소유/경영 | 대기업 계열 아님 | + +**접대비 기본한도:** + +| 판정 | 조건 | 접대비 기본한도 | +|------|------|---------------| +| 중소기업 | 3가지 모두 충족 | 3,600만원 | +| 일반법인 | 하나라도 미충족 | 1,200만원 | + +### 7.5 당월 매입 상세 팝업 (p40) + +- 자재 유형별 구매 비율 차트 (원자재/부자재/포장재) +- 월별 매입 추이 차트 +- 일별 매입 내역 테이블: 매입일, 거래처, 매입금액, 매입유형 +- 필터: 매입유형 (원재료매입, 부재료매입, 상품매입, 외주가공비, 소모품비 등) +- 정렬: 최신순, 등록순, 금액순 + +### 7.6 당월 카드 상세 팝업 (p41) + +- 사용자별 카드 사용 비율 차트 +- 월별 카드 사용 추이 차트 +- 일별 카드 사용 내역: 카드명, 사용자, 사용일시, 가맹점명, 사용금액, 사용유형 +- 미정리 건수 표시 + +### 7.7 당월 발행어음 상세 팝업 (p42) + +- 월별 발행어음 추이 차트 +- 당월 거래처별 발행어음 차트 +- 상태: 보관중, 만기임박(만기일 7일 전), 만기 경과, 결제완료, 부도 + +### 7.8 당월 지출 예상 상세 팝업 (p43) + +- 당월 지출 예상 금액, 전월 대비, 총 계좌 잔액 +- 당월 지출 승인 내역서: 예상 지급일, 항목, 지출금액, 거래처, 계좌 +- 지출 합계, 계좌 잔액, 최종 차액 + +### 7.9 가지급금 상세 팝업 (p44) + +- 가지급금, 인정이자 4.6%, 미설정 건수 +- 내역: 발생일시, 대상, 구분(카드/계좌), 금액, 상태, 내용 +- AI 분류 기준: 미정리, 불인정 가맹점, 접대비 불인정, 주말/심야 카드 사용 + +### 7.10 법인세 예상 가중 상세 팝업 (p45) + +**법인세 과세표준 (2024년 기준):** + +| 과세표준 | 세율 | 누진공제 | +|---------|------|---------| +| 2억원 이하 | 9% | - | +| 2억 초과 ~ 200억 이하 | 19% | 2,000만원 | +| 200억 초과 ~ 3,000억 이하 | 21% | 42,000만원 | +| 3,000억 초과 | 24% | 942,000만원 | + +- 접대비 초과 금액 + 가지급금 인정이자 반영/미반영 비교 +- 과세표준 계산: 당기순이익 + 손금불산입 - 손금산입 + +### 7.11 대표자 종합소득세 예상 가중 상세 팝업 (p46) + +**종합소득세 과세표준 (2024년 기준):** + +| 과세표준 | 세율 | 누진공제 | +|---------|------|---------| +| 1,400만원 이하 | 6% | - | +| 1,400만 초과 ~ 5,000만 이하 | 15% | 126만원 | +| 5,000만 초과 ~ 8,800만 이하 | 24% | 576만원 | +| 8,800만 초과 ~ 1.5억 이하 | 35% | 1,544만원 | +| 1.5억 초과 ~ 3억 이하 | 38% | 1,994만원 | +| 3억 초과 ~ 5억 이하 | 40% | 2,594만원 | +| 5억 초과 ~ 10억 이하 | 42% | 3,594만원 | +| 10억 초과 | 45% | 6,594만원 | + +- 인정이자가 상여로 처리, 종합소득세/지방소득세/4대보험 차액 표시 + +### 7.12 당해 매출 상세 팝업 (p47) + +- 월별 매출 추이 차트, 당해년도 거래처별 매출 차트 +- 매출유형: 제품 매출, 상품 매출, 부품 매출, 용역 매출, 공사 매출, 임대 수익, 기타 매출 + +### 7.13 접대비 상세 팝업 (p48-49) + +**접대비 손금한도 계산:** + +| 법인 유형 | 연간 기본한도 | 월 환산 | +|----------|-------------|--------| +| 일반법인 | 12,000,000원 | 1,000,000원 | +| 중소기업 | 36,000,000원 | 3,000,000원 | + +**수입금액별 추가한도:** + +| 수입금액 구간 | 추가한도 계산식 | +|-------------|--------------| +| 100억원 이하 | 수입금액 x 0.2% | +| 100억 초과 ~ 500억 이하 | 2,000만원 + (수입금액 - 100억) x 0.1% | +| 500억원 초과 | 6,000만원 + (수입금액 - 500억) x 0.03% | + +### 7.14 복리후생비 상세 팝업 (p50-51) + +- 항목별 사용 비율 차트 (식대, 건강검진 등) +- 계산 방식: 직원당 정액 금액 / 연봉 총액 비율 + +**법정 외 복리후생비 예시:** + +| 항목 | 금액(원) | 비고 | +|------|---------|------| +| 식대 (비과세) | 200,000 | 1인당 월 20만원 | +| 교통비/차량유지비 | 100,000 | 1인당 월 10만원 | +| 경조사비 | 50,000 | 1인당 월 5만원 적립 | +| 건강검진비 | 30,000 | 연 1회 기준 월 환산 | +| 교육훈련비 | 80,000 | 1인당 월 8만원 | +| 복지포인트/기타 | 100,000 | 1인당 월 10만원 | + +### 7.15 예상 납부세액 상세 팝업 (p52) + +- 매출세액, 매입세액, 경감/공제세액 +- 세금계산서 미발행/미수취 내역 + +### 7.16 가지급금 인정이자 계산 (p54) + +**계산 공식 (법인세법 기준):** +- 경과일수 = 정산일 - 지급일 +- 일이자율 = 연이자율 / 365 +- 인정이자 = 가지급금 x 일이자율 x 경과일수 +- 정산차액 = 가지급금 총액 - 실사용 총액 + +**계산 예시 (2024년 기준, 인정이자율 4.6%):** + +| 항목 | 금액 | 계산식 | +|------|------|--------| +| 가지급금 잔액 | 15,200,000원 | - | +| 인정이자 | 699,200원 | 잔액 x 0.046 | +| 법인세 추가 (19%) | 132,848원 | 인정이자 x 0.19 | +| 대표자 소득세 추가 (35%) | 244,720원 | 인정이자 x 0.35 | +| 대표자 지방소득세 (10%) | 24,472원 | - | +| 총 세금 부담 | 402,040원 | - | + +--- + +## 8. 인사관리 + +### 8.1 부서관리 (p57-58) + +| 번호 | 요소 | 설명 | +|------|------|------| +| 1 | 전체 선택 체크박스 | 전체 선택/해제 토글 | +| 3 | 추가 버튼 | 선택한 부서의 하위 부서 일괄 생성 (관리 권한 필요) | +| 4 | 삭제 버튼 | 삭제된 부서의 인원은 회사(기본) 인원으로 변경 | +| 5/6 | 축소/확대 버튼 | 하위 부서 숨김/표시 토글 | +| 7 | 추가 버튼 | 부서 추가 팝업 표시 | +| 8 | 수정 버튼 | 부서 수정 팝업 표시 | +| 9 | 삭제 버튼 | 개별 부서 삭제 | + +### 8.2 사원관리 (p59) + +**상단 정보:** +- 재직 인원, 휴직 인원, 퇴직 인원, 평균근속년수 + +| 번호 | 요소 | 설명 | +|------|------|------| +| 1 | 기간 설정 | 입사일 기준, 당해년도/전전월/전월/당월/어제/오늘 | +| 2 | CSV 일괄 등록 | CSV 일괄 등록 화면으로 이동 | +| 3 | 사원 등록 | 사원 상세 화면으로 이동 | +| 4 | 사용자 초대 | 사용자 초대 팝업 표시 | +| 5 | 필터 | 전체, 사용자 아이디 보유/미보유, 재직, 휴직, 퇴직 | +| 6 | 정렬 | 직급순, 입사일순, 부서순, 이름순 | + +**목록 항목:** 사원코드, 부서, 직책, 이름, 직급, 휴대폰, 이메일, 입사일, 상태, 사용자 아이디, 권한 + +### 8.3 사원 상세 (p60-63) + +**사원 정보 (필수):** +- 주민등록번호, 이름, 휴대폰, 이메일 +- 급여계좌 (은행, 계좌, 예금주), 연봉 + +**사용자 정보 (필수):** +- 아이디 (이메일 또는 아이디), 비밀번호 +- 권한 (권한관리 목록), 상태 (정상, 제재, 중지) + +**사원 상세 (선택 - 항목 설정으로 관리):** +- 프로필 사진, 사원코드, 성별, 주소 + +**인사 정보 (선택):** +- 고용 형태: 정규직, 계약직, 파견직, 용역직, 시간제 근로자 +- 직급: 사원, 대리, 과장, 차장, 부장, 이사, 상무, 전무, 부사장, 사장, 회장 +- 상태: 재직, 병가휴직, 육아휴직, 개인사정휴직, 무급휴직, 퇴사, 해고, 권고사직, 계약만료, 정년퇴직 +- 부서 (검색), 직책 +- 출근 위치, 퇴근 위치 (본사, 현장 목록 중 선택) +- 퇴사일, 퇴사 사유 + +### 8.4 사용자 초대 팝업 (p64-65) + +**초대 프로세스:** +1. 초대 이메일 발송 +2. 약관 동의 (아이디를 이메일로 사용) +3. 비밀번호 설정 +4. 로그인 + +- 이메일 주소 기준 사원 정보가 있을 경우 매핑하여 사용자 등록 +- 사용자 아이디는 다른 테넌트와 중복 가능 + +### 8.5 CSV 일괄 등록 (p66-67) + +- 양식 다운로드 → 파일 선택 (CSV 50MB 이하) → 파일 변환 → 정보 등록 영역에 표시 → 체크 항목 등록 + +### 8.6 근태관리 (p68-69) + +- 관리 권한이 있으면 모든 사원 편집 가능, 없으면 본인만 +- 근태관리 자동 설정 시: 모든 사원 정시 출퇴근 기록, 예외사항만 작성 + +**상단 정보:** 정시 출근, 지각, 결근, 휴가 + +**근태 정보 팝업:** +- 사원 (검색 & 다중 선택), 기준일 (다중 선택 가능) +- 출근/퇴근 시간, 야간 연장 시간, 주말 연장 시간 + +### 8.7 휴가관리 (p70-73) + +**탭:** 휴가 사용 현황, 휴가 부여 현황, 휴가 신청 현황 + +**상단 정보:** 휴가 승인 대기, 연차 인원, 경조사 인원, 연간 연차 사용률 + +**휴가 유형:** 연차, 보상, 경조, 보건, 병가, 반차, 회수(차감) + +**휴가 신청:** +- 잔여 일시 >= 신청 일시: 휴가 신청 완료 +- 잔여 일시 < 신청 일시: "휴가 잔여 일시를 초과했습니다." 알림 + +--- + +## 9. 전자결재 + +### 9.1 기안함 (p75) + +**문서 상태:** +- 임시저장: 문서 작성 중 임시저장 +- 진행: 상신 및 결재자 중 일부 승인 +- 완료: 모든 승인 완료 +- 반려: 결재자 중 한 명이 반려 + +| 번호 | 요소 | 설명 | +|------|------|------| +| 1 | 문서 작성 | 문서 작성 화면으로 이동 | +| 2 | 상신 버튼 | 임시저장 상태만 상신 가능 | +| 3 | 삭제 버튼 | 임시저장 상태만 삭제 가능 | + +### 9.2 문서 작성 (p76-81) + +**문서 유형:** + +#### 9.2.1 품의서 (p76-77) + +- 기본 정보: 작성일, 기안자, 문서유형, 문서번호 +- 결재선: 부서/직책/이름 (검색 & 다중 선택) +- 참조: 부서/직책/이름 (검색 & 다중 선택) +- 품의서 정보: 구매처, 구매처 결제일, 제목, 품의 내역, 품의 사유, 예상 비용 +- 녹음 버튼: 음성 인식 → 텍스트 변환 → 인풋박스에 표시 +- 참고 이미지 첨부 + +#### 9.2.2 지출결의서 (p78-79) + +- 기본 정보: 품의서와 동일 +- 지출 정보: 결제일, 지출 요청일 +- 결제 정보: 카드 (등록된 카드 목록), 총 비용 +- 지출결의서 정보: 적요, 금액, 비고 (행 추가 가능) +- 참고 이미지 첨부 + +#### 9.2.3 지출 예상 내역서 (p80-81) + +- 기본 정보: 품의서와 동일 +- 지출 예상 내역서 정보: 제목 +- 목록: 예상 지급일, 항목, 지출금액, 거래처, 계좌 +- 월별 소계, 지출 합계, 계좌 잔액, 최종 차액 + +### 9.3 결재함 (p82) + +**상태:** +- 결재요청: 결재 요청을 받은 상태 +- 예정: 결재 순번에 의한 대기 +- 완료: 승인 완료 +- 반려: 반려 완료 + +### 9.4 문서 상세 팝업 (p83-85) + +**공통 기능:** 복제(새글), 수정(결재선 누구나 가능), 반려/승인(결재선만), 공유(PDF/이메일/팩스/카카오톡), 인쇄 +- 품의서: 구매처 정보, 품의 내역/사유, 예상 비용 +- 지출결의서: 지출 요청일/결제일, 적요/금액/비고, 법인카드, 총 비용 +- 지출예상내역서: 예상 지급일별 항목/지출금액/거래처/계좌 + +### 9.5 참조함 (p86) + +- 열람/미열람 상태 관리 +- 열람 버튼: 일괄 열람 처리 +- 미열람 버튼: 일괄 미열람 처리 + +--- + +## 10. 게시판 + +### 10.1 게시판 목록 (p88) + +- 게시판 탭: 공지사항, 게시판명, ..., 나의 게시글 +- 기준정보 > 게시판관리에서 설정한 게시판 목록 +- 대상(전사, 부서, 팀)에 따라 소속에 맞는 게시판만 표시 +- 게시글: 번호(상단 노출 아이콘 또는 번호), 제목, 작성자, 등록일, 조회수 + +### 10.2 게시글 상세 (p89-91) + +**등록:** +- 게시판 선택, 상단 노출 (최대 5개, 최신순), 댓글 사용/미사용 +- 제목, 내용, 첨부파일 + +**조회:** +- 본인 작성글만 수정/삭제 버튼 표시 +- 댓글 등록/수정/삭제 (본인 댓글만) + +--- + +## 11. 회계관리 + +### 11.1 회계 관리 플로우 + +``` +매출 흐름: + 거래처 선택 → 매출 등록 → 세금계산서 발행 → 입금 등록 + ├── 전액 입금? → 거래처원장 + └── 미입금 → 미수금 현황 → 연체? → 악성 추심 + +매입 흐름: + 거래처 선택 → 매입 등록 → 세금계산서 수취 → 출금 등록 + ├── 전액 출금? → 거래처원장 + └── 미출금 → 미지급 알림 +``` + +### 11.2 거래처관리 (p94-97) + +**목록 필터:** +- 구분: 전체, 매출, 매입, 매입매출 +- 신용등급: AAA ~ D +- 거래등급: A(우수), B(양호), C(보통), D(주의), E(위험) +- 악성채권: 전체, 악성채권, 정상 + +**거래처 상세:** +- 기본 정보: 거래처명, 거래처 코드, 사업자등록번호, 대표자명, 거래처 유형, 업태, 업종, 주소 +- 연락처: 전화번호, 모바일, 팩스, 이메일 +- 담당자 정보: 시스템 관리자, 담당자명, 담당자 전화 +- 회사 정보: 회사 로고 +- 결제일: 매입 결제일(1~31일/말일), 매출 결제일(1~31일/말일) +- 추가 정보: 신용등급, 거래등급, 세금계산서 이메일, 입금계좌 +- 미수금 표시 (읽기 전용) +- 연체 토글 (ON/OFF, 연체일수 표시) +- 미지급 표시 (읽기 전용) +- 악성채권 토글 (ON/OFF) +- 메모 (일시, 작성자, 내용) + +### 11.3 매출관리 (p99-102) + +**매출 등록:** +- 수주 확정 시 자동 등록 (삭제 불가) +- 별도 매출 시 직접 등록 (삭제 가능) + +**매출유형:** 미설정, 제품 매출, 상품 매출, 부품 매출, 용역 매출, 공사 매출, 임대 수익, 기타 매출 + +**매출 상세:** +- 기본 정보: 거래처명, 매출일, 매출번호, 매출유형 +- 품목 정보: 품목명, 수량, 단가, 공급가액, 부가세, 합계, 적요 +- 세금계산서: 발행 토글 (미발행/발행완료) +- 거래명세서: 발행 토글, 조회 버튼, 발행하기 버튼 (거래처 이메일로 자동 발송) + +### 11.4 매입/출금 플로우 (p103) + +``` +직원: 품의서 작성 → 전자결재 상신 → 승인? + ├── 승인 → 지출예상내역서 → 지급일 가능? + │ ├── Yes → 지출결의서 작성 → 전자결재 상신 → 승인? + │ │ ├── 승인 → 매입 자동 등록 → 출금 상세 등록 + │ │ └── 반려 + │ └── No → 예상 지급일 수정 + └── 반려 +``` + +### 11.5 매입관리 (p104-105) + +**매입 등록:** 지출예상내역서 승인 완료 시 자동 등록 (삭제 불가) + +**매입유형:** 미설정, 원재료매입, 부재료매입, 상품매입, 외주가공비, 소모품비, 수선비, 운반비, 사무용품비, 임차료, 수도광열비, 통신비, 차량유지비, 접대비, 보험료, 기타용역비 + +**매입 상세:** +- 근거 문서(품의서/지출결의서), 예상 비용 표시 +- 매입번호: 문서번호 + 넘버링 조합 +- 출금계좌, 거래처, 매입유형 +- 세금계산서 수취 토글 + +### 11.6 입금관리 (p106-107) + +- 기준정보 > 계좌관리에 등록된 계좌의 자동 입금 내역 수집 +- 바로빌 API 연동 시 실시간 조회 + +**입금유형:** 미설정, 매출대금, 선수금, 가수금, 임대수익, 이자수익, 보증금 반환, 차입금, 자본금, 부가세 환급, 기타 + +### 11.7 출금관리 (p108-109) + +- 기준정보 > 계좌관리에 등록된 계좌의 자동 출금 내역 수집 + +**출금유형:** 미설정, 매입대금, 선급금, 가지급금, 임대료, 이자비용, 보증금 지급, 차입금 상환, 배당금 지급, 부가세 납부, 급여, 4대보험, 세금, 공과금, 경비, 기타 + +### 11.8 어음관리 (p110-111) + +**구분:** 수취, 발행 + +**발행 어음 상태:** 보관중, 만기임박(만기일 7일 전), 만기 경과, 결제완료, 부도 +**수취 어음 상태:** 보관중, 만기임박(만기일 7일 전), 추심의뢰, 추심완료, 추심중, 부도 + +**수취 어음:** +- 거래처원장 상세, 일일 일보, 미수금 현황에 반영 +- 미수금에 대한 약정으로만 표시 +- 추심완료되어 입금 시에만 회계에 반영 + +**발행 어음:** +- 지출예상내역서에 반영 +- 지출에 대한 약정으로만 표시 +- 결제완료되어 출금 시에만 회계에 반영 + +**차수 관리:** 총 금액에 대한 차수로 상환 계획 작성 + +### 11.9 거래처원장 (p112-113) + +- 거래처별 기간별 합계 금액 표시 +- 목록: 거래처명, 이월잔액, 매출, 수금, 잔액, 결제일 + +**거래처원장 상세:** +- 이월잔액, 수취 어음 정보, 거래명세서 정보 +- 하위 전체 품목별 판매금액 표시 +- 세금계산서 미발행 시 붉은색 하이라이트 +- 누계 금액 표시 + +### 11.10 일일 일보 (p114) + +- 어음 및 외상매출채권 현황: 수취어음 거래처명, 금액, 발행일, 만기일 +- 현금성 자산: 계좌별 전월 이월, 수입, 지출, 잔액 +- 외국환(USD) 합계, 현금성 자산 합계 + +### 11.11 지출 예상 내역서 (p115-116) + +- 카드 및 승인/반려 확정 목록은 삭제 불가 +- 예상 지급일: 매입 거래처 등록 시 자동 입력 +- 품의서/지출결의서/발행어음 목록 (클릭 시 상세 이동) +- 거래처 월 지출 목록 (클릭 시 거래처원장 상세 이동) +- 전자결재 버튼: 문서 작성_지출 예상 내역서 화면으로 이동 +- 예상 지급일 변경 팝업 + +### 11.12 미수금 현황 (p117) + +- 거래처별 월별 미수금 현황 (매출/입금/어음/누적 미수금) +- 메모 저장 기능 +- 연체 토글 (거래처 상세와 연동) +- 확대/축소 토글 + +### 11.13 악성채권 추심관리 (p118-121) + +**상태:** 추심중, 법적조치, 회수완료, 대손처리 + +**상세:** +- 기본 정보: 거래처 기본 정보 표시 +- 악성채권 등록 ON/OFF +- 담당자 정보, 연락처 정보 +- 필요 서류: 사업자등록증, 세금계산서, 추가 서류 (파일 첨부) +- 악성 채권 정보: 미수금, 상태, 악성채권 발생일/종료일, 연체일수, 본사 담당자, 메모 +- 수취 어음 현황 (어음관리 화면으로 이동) +- 거래처 미수금 현황 (미수금 현황 화면으로 이동) + +### 11.14 입출금 계좌 조회 (p122) + +- 기준정보 > 계좌관리에 등록된 계좌의 자동 입출금 내역 수집 +- 바로빌 API 연동 시 실시간 조회 +- 목록: 은행명, 계좌명, 거래일시, 구분, 적요, 거래처, 입금자/수취인, 입금, 출금, 잔액, 입출금 유형 + +### 11.15 카드 내역 관리 (p123-124) + +- 기준정보 > 카드관리에 등록된 카드의 자동 사용 내역 수집 +- 사용자는 본인 내역 조회 및 사용유형/적요 작성 가능 + +**사용유형:** 미설정, 복리후생비, 접대비, 여비교통비, 차량유지비, 소모품비, 운반비, 통신비, 도서인쇄비, 교육훈련비, 보험료, 광고선전비, 회비, 지급수수료, 세금과공과, 수선비, 임차료, 잡비 + +--- + +## 12. 기준정보 + +### 12.1 직급관리 (p126) + +- 디폴트: 사원, 대리, 과장, 차장, 부장, 이사, 상무, 전무, 부사장, 사장, 회장 +- 추가, 순서 변경 (드래그 & 드랍), 수정, 삭제 +- 사원 설정된 직급은 모두 변경 후 삭제 가능 + +### 12.2 직책관리 (p127) + +- 디폴트: 없음(기본), 팀장, 파트장, 실장, 부서장, 본부장, 센터장, 매니저, 리더 +- 추가, 순서 변경, 수정, 삭제 + +### 12.3 권한관리 (p129-130) + +- 권한 등록/삭제 +- 권한 상세: 권한명, 상태(공개/숨김) +- 메뉴별 권한 설정: 조회, 생성, 수정, 삭제, 승인, 내보내기, 관리 + +### 12.4 근무관리 (p131) + +- 고용 형태: 정규직, 계약직, 파견직, 용역직, 시간제 근로자 +- 주간 근무일: 월~일 체크박스 +- 출근/퇴근 시간 설정 +- 법정 주당 기준 근로시간 (40시간), 주당 연장 근로시간 (12시간) +- 휴게 시작/종료 시간 설정 + +### 12.5 출퇴근관리 (p132) + +**GPS 출퇴근:** +- 사용/미사용 설정 +- 연동 부서 (검색 & 다중 선택) +- 출퇴근 허용 반경: 50M, 100M, 300M, 500M + +**자동 출퇴근:** +- 사용/미사용 설정 +- 연동 부서 + +### 12.6 휴가관리 (p133) + +- 기준: 회계연도 / 입사일 +- 기준일: 월/일 설정 (회계연도 선택 시) + +**기본 연차 설정:** +- 1년간 출근율 80% 이상: 15일 +- 3년 이상 근속 시 2년에 1일 추가 (최대 25일) +- 1년 미만 또는 출근율 80% 미만: 1개월 개근 시 1일씩 (최대 11일) + +### 12.7 카드관리 (p134-135) + +- 카드사 코드, 카드 인증 정보, 비밀번호를 바로빌 API에 전달하여 자동 수집 +- 카드 상세: 카드번호, 카드사, 카드명, 카드 비밀번호 앞 2자리, 유효기간 +- 사용자 정보: 부서/이름/직책 +- 상태: 사용, 정지 (정지 시 자동 조회 중단) + +### 12.8 계좌관리 (p136-138) + +- 계좌 인증 정보, 비밀번호를 바로빌 API에 전달하여 자동 수집 +- 해당 테넌트는 은행에서 빠른 조회 서비스 사전 등록 필수 +- 계좌 상세: 계좌번호, 은행, 계좌명, 계좌 비밀번호, 예금주 +- 상태: 사용, 정지 (정지 시 자동 조회 중지) + +### 12.9 팝업관리 (p139-140) + +- 목록: 대상, 제목, 상태, 작성자, 등록일, 기간 +- 팝업 상세: 대상(전사/부서), 제목, 내용, 기간, 상태(사용함/사용안함) +- 사용함이어도 기간이 아닐 경우 팝업 미노출 + +### 12.10 게시판관리 (p141-142) + +- 모든 테넌트 디폴트: 공지사항, 나의 게시글 (수정/삭제 불가) +- 게시판 등록: 대상(전사/부서), 게시판명, 상태(사용함/사용안함) + +### 12.11 알림설정 (p143-149) + +**전체/개별 ON/OFF 토글** + +**알림 소리 선택:** 기본 알림음, SAM 보이스, ..., 무음 (관리자 등록 음원 목록) +**추가 알림:** 이메일 체크박스 + +**알림 유형:** + +| 카테고리 | 알림 항목 | +|---------|----------| +| 공지 알림 | 공지사항 알림, 이벤트 알림 | +| 거래처 알림 | 일정 알림, 부가세 신고 알림, 종합소득세 신고 알림, 신규 업체 등록 알림, 신용등급 등록 알림 | +| 근태 알림 | 연차 알림, 출근 알림, 지각 알림, 결근 알림 | +| 수주/발주 알림 | 수주 알림, 발주 알림 | +| 전자결재 알림 | 결재요청 알림, 기안>승인 알림, 기안>반려 알림, 기안>완료 알림 | +| 생산 알림 | 안전재고 알림, 생산완료 알림 | + +**항목 설정_알림 팝업:** 개별 알림 ON/OFF 토글 + +--- + +## 13. 보고서 및 분석 (p150-151) + +- TBD (추후 확정) +- 업체별 신용평가 및 보고서 검색 +- 업체별 보고서 및 분석 상세 제공 + +--- + +## 14. 계정정보/회사정보/구독관리/결제내역/고객센터 + +### 14.1 계정정보 (p153) + +- 아이디(이메일), 권한, 상태 +- 비밀번호 변경 버튼 +- 프로필 사진 (250x250px) +- 약관 동의 정보 (동의일시, 철회일시) +- 탈퇴 버튼: 테넌트 마스터가 아닐 경우에만 (모든 테넌트 + SAM 탈퇴) +- 사용중지 버튼: 테넌트 마스터가 아닐 경우에만 (해당 테넌트 사용중지) + +### 14.2 회사정보 (p154-155) + +- 테넌트 마스터에게만 표시 +- 회사 추가 버튼 +- 회사 정보: 운영(영업)에서 입력된 정보 표시, 수정 가능 +- 결제 계좌 정보: SAM 관리자가 등록 (효성 CMS 실물 계약서 기반) + +### 14.3 회사 추가 팝업 (p156) + +- 사업자등록번호 입력 (숫자 10자리) +- 바로빌 조회: 휴폐업 → 알림, 등록됨 → 알림, 미등록 → 매니저에게 알림 발송 + +### 14.4 구독관리 (p157) + +- 테넌트 마스터에게만 표시 +- 구독 정보: 플랜명, 최근/다음 결제일시, 구독금액 +- 사용량: 사용자 수, 저장 공간, AI API 호출 +- 자료 내보내기 버튼 +- 서비스 해지 버튼: "모든 데이터가 삭제되며 복구할 수 없습니다." 확인 Alert + +### 14.5 결제내역 (p158) + +- 테넌트 마스터에게만 표시 +- 목록: 결제일, 구독명, 결제 수단, 구독 기간, 금액, 거래명세서 + +### 14.6 고객센터 - 공지사항 (p159-160) + +- SAM 공지사항 +- 목록: 번호, 제목, 작성자, 등록일, 조회수 +- 상세: 제목, 작성자, 등록일시, 내용, 첨부파일 + +### 14.7 고객센터 - 이벤트 (p161-162) + +- 탭: 진행중인 이벤트, 종료된 이벤트 +- 목록: 번호, 제목, 작성자, 기간, 조회수 + +### 14.8 고객센터 - FAQ (p163) + +- 탭: 전체, 카테고리별 +- 질문 클릭 시 답변 영역 열림/닫힘 토글 + +### 14.9 고객센터 - 1:1 문의 (p164-167) + +- 문의 등록: 상담분류(문의하기/신고하기/건의사항/서비스 오류), 제목, 내용, 첨부파일 +- 문의 상세: 문의 내용 + 답변 내용 +- 수정 버튼: 답변완료 후 비활성화 +- 댓글 등록/조회 + +--- + +## 15. 참조 테이블 + +### 15.1 매출유형 목록 + +| 코드 | 매출유형명 | +|------|----------| +| - | 미설정 | +| 1 | 제품 매출 | +| 2 | 상품 매출 | +| 3 | 부품 매출 | +| 4 | 용역 매출 | +| 5 | 공사 매출 | +| 6 | 임대 수익 | +| 7 | 기타 매출 | + +### 15.2 매입유형 목록 + +| 코드 | 매입유형명 | +|------|----------| +| - | 미설정 | +| 1 | 원재료매입 | +| 2 | 부재료매입 | +| 3 | 상품매입 | +| 4 | 외주가공비 | +| 5 | 소모품비 | +| 6 | 수선비 | +| 7 | 운반비 | +| 8 | 사무용품비 | +| 9 | 임차료 | +| 10 | 수도광열비 | +| 11 | 통신비 | +| 12 | 차량유지비 | +| 13 | 접대비 | +| 14 | 보험료 | +| 15 | 기타용역비 | + +### 15.3 입금유형 목록 + +매출대금, 선수금, 가수금, 임대수익, 이자수익, 보증금 반환, 차입금, 자본금, 부가세 환급, 기타, 미설정 + +### 15.4 출금유형 목록 + +매입대금, 선급금, 가지급금, 임대료, 이자비용, 보증금 지급, 차입금 상환, 배당금 지급, 부가세 납부, 급여, 4대보험, 세금, 공과금, 경비, 기타, 미설정 + +### 15.5 카드 사용유형 목록 + +미설정, 복리후생비, 접대비, 여비교통비, 차량유지비, 소모품비, 운반비, 통신비, 도서인쇄비, 교육훈련비, 보험료, 광고선전비, 회비, 지급수수료, 세금과공과, 수선비, 임차료, 잡비 + +--- + +## 관련 문서 + +- SAM 프로젝트 개요: `/home/aweso/sam/docs/SAM_PROJECT_OVERVIEW_FOR_AI.md` +- 원본 PDF: `/home/aweso/sam/docs/plans/SAM_ERP_Storyboard_D1.4_260116.pdf` + +--- + +**최종 업데이트**: 2026-01-16 (D1.4) diff --git a/plans/SAM_ERP_회계관리_Storyboard_D1.6.md b/plans/SAM_ERP_회계관리_Storyboard_D1.6.md new file mode 100644 index 0000000..1ab7db7 --- /dev/null +++ b/plans/SAM_ERP_회계관리_Storyboard_D1.6.md @@ -0,0 +1,1288 @@ +# SAM ERP 회계관리 스토리보드 D1.6 + +> **작성일**: 2026-02-20 +> **버전**: D1.6 +> **상태**: 프론트 작성 +> **원본**: `SAM_ERP_회계관리_Storyboard_D1.6_260220.pdf` (65페이지) + +--- + +## 문서 이력 + +| 날짜 | 버전 | 주요 내용 | 상세 | +|------|------|----------|------| +| 2026-02-13 | D1.5 | 프론트 작성 | 세금계산서 관리, 계좌 입출금 내역, 계좌 관리, 상품권 관리, 바로빌 연동 수정 및 추가 | +| 2026-02-20 | D1.6 | 프론트 작성 | 거래처 관리(사업자등록증 OCR), 일일일보, 대시보드(생산, 시공), 이관 기초자료, 달력 관리, 즐겨찾기, 신용평가 수정 및 추가 | + +--- + +## 메뉴 구조 + +``` +SAM ERP +├── 로그인 +├── 회원가입 +├── 대시보드 +├── MES +│ ├── 판매관리 +│ ├── 구매관리 +│ ├── 발주관리 +│ ├── 공사관리 +│ ├── 생산관리 +│ ├── 품질관리 +│ ├── 자재관리 +│ ├── 장비관리 +│ └── 차량관리 +├── 인사관리 +├── 전자결재 +├── 게시판 +├── 회계관리 ★ (본 문서 범위) +│ ├── 거래처 관리 +│ ├── 세금계산서 발행 +│ ├── 세금계산서 관리 +│ ├── 계좌 입출금 내역 +│ ├── 카드 사용 내역 +│ ├── 상품권 관리 +│ ├── 일반 전표 입력 +│ └── 일일일보 +├── 기준정보 ★ (본 문서 범위) +│ ├── 바로빌 연동 관리 +│ ├── 계좌 관리 +│ ├── 카드 관리 +│ ├── 달력 관리 +│ └── 이관 기초자료 +├── 보고서 및 분석 +└── 운영 + ├── 회사정보 + ├── 계정정보 + ├── 구독관리 + ├── 결제내역 + └── 고객센터 +``` + +--- + +## 화면 목록 (페이지 인덱스) + +| 페이지 | 경로 | 화면명 | +|--------|------|--------| +| 4 | 공통 | 섹션 구분 | +| 5 | 공통 | 즐겨찾기 | +| 6 | 대시보드 | 섹션 구분 | +| 7 | 대시보드 | 대시보드 (자금 현황, 오늘의 이슈, AI 리포트) | +| 8 | 대시보드 | 대시보드 (매출 현황) | +| 9 | 대시보드 | 대시보드 (매입 현황) | +| 10 | 대시보드 | 대시보드 (생산 현황) | +| 11 | 대시보드 | 대시보드 (시공 현황, 미출고 내역) | +| 12 | 대시보드 | 대시보드 (근태 현황) | +| 13-14 | 대시보드 > 항목 설정 팝업 | 항목 설정_대시보드 팝업 | +| 15 | 회계관리 | 섹션 구분 | +| 16 | 회계관리 > 거래처 관리 | 거래처 관리 (목록) | +| 17-18 | 회계관리 > 거래처 관리 > 거래처 상세 | 거래처 상세 (등록/수정) | +| 19 | 회계관리 > 거래처 관리 > 거래처 상세 | 신용분석 리포트 팝업 | +| 20 | 회계관리 > 세금계산서 발행 | 세금계산서 발행 (목록) | +| 21-22 | 회계관리 > 세금계산서 발행 | 세금계산서 발행_확장 (발행 입력) | +| 23 | 회계관리 > 세금계산서 발행 | 공급자 기초정보 설정 팝업 | +| 24 | 회계관리 > 세금계산서 발행 | 거래처 검색 팝업 | +| 25 | 회계관리 > 세금계산서 관리 | 세금계산서 관리 (매출) | +| 26 | 회계관리 > 세금계산서 관리 | 세금계산서 관리 (매입) | +| 27 | 회계관리 > 세금계산서 관리 | 세금계산서 수기 입력 팝업 | +| 28 | 회계관리 > 세금계산서 관리 | 카드 내역 불러오기 팝업 | +| 29 | 회계관리 > 세금계산서 관리 | 분개 수정 팝업 | +| 30 | 회계관리 > 계좌 입출금 내역 | 계좌 입출금 내역 (목록) | +| 31 | 회계관리 > 계좌 입출금 내역 | 입출금 수기 입력 팝업 | +| 32 | 회계관리 > 카드 사용 내역 | 카드 사용 내역 (목록) | +| 33 | 회계관리 > 카드 사용 내역 | 카드사용 수기 입력 팝업 | +| 34 | 회계관리 > 카드 사용 내역 | 거래 분개 팝업 | +| 35 | 회계관리 > 상품권 관리 | 상품권 관리 (목록) | +| 36 | 회계관리 > 상품권 관리 > 상품권 상세 | 상품권 상세 (등록/수정) | +| 37 | 회계관리 > 일반 전표 입력 | 일반 전표 입력 (목록) | +| 38 | 회계관리 > 일반 전표 입력 | 계정과목 설정 팝업 | +| 39 | 회계관리 > 일반 전표 입력 | 수기 전표 입력 팝업 | +| 40 | 회계관리 > 일반 전표 입력 | 분개 수정 팝업 | +| 41 | 기준정보 | 섹션 구분 | +| 42 | 기준정보 > 바로빌 연동 관리 | 바로빌 연동 관리 | +| 43 | 기준정보 > 바로빌 연동 관리 | 바로빌 로그인 정보 등록 팝업 | +| 44 | 기준정보 > 바로빌 연동 관리 | 바로빌 회원가입 정보 등록 팝업 | +| 45 | 기준정보 > 바로빌 연동 관리 | 은행 빠른조회 서비스 등록 팝업 | +| 46 | 기준정보 > 계좌 관리 | 계좌 관리 (목록) | +| 47 | 기준정보 > 계좌 관리 > 계좌 상세 | 계좌 상세_은행 | +| 48 | 기준정보 > 계좌 관리 > 계좌 상세 | 계좌 상세_대출 | +| 49 | 기준정보 > 계좌 관리 > 계좌 상세 | 계좌 상세_증권 | +| 50 | 기준정보 > 계좌 관리 > 계좌 상세 | 계좌 상세_보험_단체보험 | +| 51 | 기준정보 > 계좌 관리 > 계좌 상세 | 계좌 상세_보험_화재보험 | +| 52 | 기준정보 > 계좌 관리 > 계좌 상세 | 계좌 상세_보험_CEO보험 | +| 53 | 기준정보 > 카드 관리 | 카드 관리 (목록) | +| 54 | 기준정보 > 카드 관리 > 카드 상세 | 카드 상세 | +| 55 | 기준정보 > 달력 관리 | 달력 관리 (달력 뷰) | +| 56 | 기준정보 > 달력 관리 | 달력 관리 (목록 뷰) | +| 57 | 기준정보 > 달력 관리 | 달력 상세 팝업 | +| 58 | 기준정보 > 달력 관리 | 대량 등록 팝업 | +| 59-60 | 기준정보 > 이관 기초자료 | 이관 기초자료 (거래처 탭) | +| 61 | 기준정보 > 이관 기초자료 | 이관 기초자료_거래처 CSV 스펙 | +| 62 | 기준정보 > 이관 기초자료 | 이관 기초자료_거래 내역 CSV 스펙 | +| 63 | 기준정보 > 이관 기초자료 | 이관 기초자료_계좌 내역 CSV 스펙 | +| 64 | 기준정보 > 이관 기초자료 | 이관 기초자료_세금계산서 내역 CSV 스펙 | +| 65 | 기준정보 > 이관 기초자료 | 이관 기초자료_업로드 이력 | + +--- + +## 1. 공통 + +### 1.1 즐겨찾기 (P5) + +**경로**: 공통 (사이드바) + +**기능 설명**: + +| # | 요소 | 설명 | +|---|------|------| +| 1 | 즐겨찾기 버튼 | 메뉴명에 마우스 롤 오버 시 표시. 즐겨찾기 설정 상태일 경우 상시 표시. 클릭: 즐겨찾기 설정/해제 토글. 디폴트: 해제 상태 | +| 2 | 즐겨찾기 폴더 버튼 | 클릭: 즐겨찾기 설정 목록 표시 | + +**사이드바 메뉴 구조** (회계관리 하위): +- 거래처관리 +- 세금계산서발행 +- 세금계산서관리 +- 계좌입출금내역 +- 카드사용내역 +- 상품권관리 +- 일반전표입력 +- 일일일보 + +--- + +## 2. 대시보드 + +### 2.1 대시보드 - 메인 (P7) + +**경로**: 대시보드 +**설명**: 종합 정보를 조회합니다 + +**기능 설명**: + +| # | 요소 | 설명 | +|---|------|------| +| 1 | 항목 설정 버튼 | 클릭: 항목 설정_대시보드 팝업 표시 | +| 2 | 오늘의 이슈 영역 | 당일 이슈 발생 시 알림 처리. 목록 길 경우 영역 내 페이지네이션. 알림 상태에서 즉시 승인/보류 처리 가능 | +| 3 | 필터 셀렉트 박스 | 종류: 전체, 수주 성공, 추심 이슈, 적정 재고, 결재 요청, 세금 신고, 신규 업체 등록, 근태, 발주 완료. 디폴트: 전체. 숫자도 함께 표시 | +| 4 | 이슈 목록 | 클릭: 해당 상세 화면으로 이동. 화면 가로 길이에 따라 4, 3, 2, 1열로 반응형 표시 | +| 5 | 일일일보 영역 | 현금성 자산합계 표시. 클릭: 일일일보 화면으로 이동 | +| 6 | 매출채권 잔액 영역 | 미수금 잔액 합계 표시. 클릭: 미수금 현황 화면으로 이동 | +| 7 | 매입채무 잔액 영역 | = 세금계산서 매입 합계 - 거래처별 일반전표 출금 합계 (또는 거래처별 미지급금 합계) | +| 8 | AI 리포트 | 핵심 키워드 강조 표시 (빨간색: 경고, 주황: 주의, 녹색: 긍정, 파랑: 양호) | + +**이슈 케이스**: +- 신규 업체 등록 +- 재고 미달 알림 +- 채권 추심 등록, 상태 변경 +- 발주, 수주 등록 +- 지출결의서 등 전자결재 상신 +- 세금 신고 알림 등 + +**자금 현황 카드** (예시 데이터): +- 일일일보: 30.5억원 +- 매출채권 잔액: 30.5억원 +- 매입채무 잔액: 30.5억원 +- 운영자금 잔여: 6.2개월 + +**AI 리포트 예시**: +- "어제 3.5억원 출금했습니다. 최근 7일 평균 대비 2배 이상으로 점검이 필요합니다." +- "10.2억원이 입금되었습니다. 대한건설 선수금 입금이 주요 원인입니다." +- "총 현금성 자산이 300.2억원입니다. 월 운영비용 대비 18개월분이 확보되어 안정적입니다." + +--- + +### 2.2 대시보드 - 매출 현황 (P8) + +**기능 설명**: + +| # | 요소 | 설명 | +|---|------|------| +| 1 | 거래처 필터 셀렉트 박스 | 검색 & 다중 선택. 종류: 전체, 매출 거래처명. 디폴트: 전체 | +| 2 | 정렬 셀렉트 박스 | 종류: 최신순, 등록순, 금액 높은순, 금액 낮은순. 디폴트: 최신순 | + +**표시 정보**: +- 누적 매출 금액 +- 목표 대비 달성률 (%) +- 전년 동기 대비 증감률 (%) +- 당월 매출 금액 +- 거래처별 매출 (차트) +- 월별 매출 추이 (1~7월 차트) +- 당월 매출 내역 테이블: No., 매출일, 거래처, 매출금액 + +--- + +### 2.3 대시보드 - 매입 현황 (P9) + +**기능 설명**: + +| # | 요소 | 설명 | +|---|------|------| +| 1 | 거래처 필터 셀렉트 박스 | 검색 & 다중 선택. 종류: 전체, 매입 거래처명. 디폴트: 전체 | +| 2 | 정렬 셀렉트 박스 | 종류: 최신순, 등록순, 금액 높은순, 금액 낮은순. 디폴트: 최신순 | + +**표시 정보**: +- 누적 매입 금액 +- 미결제 금액 +- 전년 동기 대비 증감률 (%) +- 자재 유형별 구매 비율 (파이 차트: 원자재 55%, 부자재 35%, 소모품 10%) +- 월별 매입 추이 차트 +- 당월 매입 내역 테이블: No., 매출일, 거래처, 매입금액 + +--- + +### 2.4 대시보드 - 생산 현황 (P10) + +**기능 설명**: + +| # | 요소 | 설명 | +|---|------|------| +| 1 | 생산 현황 탭 | 종류: 스크린 공정, 슬랫 공정, 절곡 공정. 디폴트: 스크린 공정 | +| 2 | 요약 정보 | 선택한 공정별 요약 정보 표시 (전체 작업, 할일, 작업중, 완료) | +| 3 | 수주 목록 | 클릭: 작업자 화면으로 이동 (해당 수주 작업 선택 상태). 긴급/우선/일반 구분 | +| 4 | 작업자 현황 목록 | 작업자별 당일 생산 현황 표시 (작업중/작업대기 상태, 완료 건수) | +| 5 | 출고 현황 정보 | 클릭: 출고 목록 화면으로 이동 (해당 기간 설정 상태). 예상 출고 7일/30일 이내, 건수 표시 | + +--- + +### 2.5 대시보드 - 시공 현황 (P11) + +**기능 설명**: + +| # | 요소 | 설명 | +|---|------|------| +| 1 | 거래처 필터 셀렉트 박스 | 검색 & 다중 선택. 종류: 전체, 매출 거래처명. 디폴트: 전체 | +| 2 | 정렬 셀렉트 박스 | 종류: 납기일 가까운순, 납기일 먼순, 잔량 많은순, 잔량 적은순. 디폴트: 납기일 가까운순 | +| 3 | 시공 현황별 요약 정보 | 시공 진행(7일 이내), 시공 완료(7일 이내) 건수. 클릭: 시공관리 화면으로 이동 | +| 4 | 시공 상세 목록 | 클릭: 해당 시공 상세 화면으로 이동. 컬럼: No., 로트번호, 현장명, 수주처, 잔량, 납기일(D-N) | + +**미출고 내역 테이블**: 시공진행 상태의 현장명, 로트번호 목록 + +--- + +### 2.6 대시보드 - 근태 현황 (P12) + +**기능 설명**: + +| # | 요소 | 설명 | +|---|------|------| +| 1 | 근태 현황별 요약 정보 | 오늘 출근, 오늘 휴가, 어제 지각, 어제 결근 인원. 클릭: 근태관리 화면으로 이동 | +| 2 | 상태 필터 셀렉트 박스 | 종류: 전체, 출근, 휴가. 디폴트: 전체 | + +**근태 목록 테이블**: No., 부서, 직급, 이름, 상태(출근/휴가) + +--- + +### 2.7 항목 설정_대시보드 팝업 (P13-14) + +**경로**: 대시보드 > 항목 설정_대시보드 팝업 + +#### 2.7.1 접대비 한도 관리 + +| # | 요소 | 설명 | +|---|------|------| +| 1 | 접대비 한도 관리 셀렉트 박스 | 종류: 연간, 반기, 분기, 월. 디폴트: 연간. 선택 값으로 총 한도를 분할 계산 | +| 1-1 | 기업 구분 셀렉트 박스 | 종류: 일반법인, 중소기업. 디폴트: 일반법인 | +| 1-2 | 기업 구분 방법 영역 | 클릭: 확대/축소 토글. 디폴트: 축소 상태 | + +**중소기업 판단 기준표**: + +| 조건 | 기준 | 충족 요건 | +|------|------|----------| +| 매출액 | 업종별 상이 | 업종별 기준 금액 이하 | +| 자산총액 | 5,000억원 | 미만 | +| 독립성 | 소유/경영 | 대기업 계열 아님 | + +> 3가지 조건 모두 충족 시 중소기업 + +**업종별 매출액 기준** (최근 3개년 평균): + +| 업종 분류 | 기준 매출액 | +|-----------|------------| +| 제조업 | 1,500억원 이하 | +| 건설업 | 1,000억원 이하 | +| 운수업 | 1,000억원 이하 | +| 도매업 | 1,000억원 이하 | +| 소매업 | 600억원 이하 | +| 정보통신업 | 600억원 이하 | +| 전문서비스업 | 600억원 이하 | +| 숙박/음식점업 | 400억원 이하 | +| 기타 서비스업 | 400억원 이하 | + +**접대비 기본한도 판정**: + +| 판정 | 조건 | 접대비 기본한도 | +|------|------|----------------| +| 중소기업 | 3가지 모두 충족 | 3,600만원 | +| 일반법인 | 하나라도 미충족 | 1,200만원 | + +#### 2.7.2 복리후생비 한도 관리 + +| # | 요소 | 설명 | +|---|------|------| +| 2 | 복리후생비 한도 관리 셀렉트 박스 | 종류: 연간, 반기, 분기, 월. 디폴트: 연간. 선택 값으로 총 한도를 분할 계산 | +| 3 | 계산 방식 셀렉트 박스 | 종류: 직원당 정액 금액 방식, 연봉 총액 X 비율 방식. 디폴트: 직원당 정액 금액 방식 | +| 4 | 직원당 정액 금액/월 인풋박스 | 직원당 정액 금액 방식일 경우에만 표시 | +| 5 | 비율 인풋박스 | 연봉 총액 X 비율 방식일 경우에만 표시 | +| 6 | 연간 복리후생비 | 계산된 연간 복리후생비 표시 | + +#### 2.7.3 설정 항목 ON/OFF 목록 (P14) + +| 항목 | 기본값 | +|------|--------| +| 접대비 현황 | ON | +| 카드/가지급금 관리 | ON | +| 복리후생비 현황 | ON | +| 미수금 상위 회사 현황 | ON | +| 미수금 현황 | ON | +| 채권추심 현황 | ON | +| 부가세 현황 | ON | +| 캘린더 | ON | +| 매출 현황 | ON | +| 일별 매출 내역 | ON | +| 매입 현황 | ON | +| 일별 매입 현황 | ON | +| 생산 현황 | ON | +| 출고 현황 | ON | +| 미출고 내역 | ON | +| 시공 현황 | ON | +| 근태 현황 | ON | + +--- + +## 3. 회계관리 + +### 3.1 거래처 관리 (P16) + +**경로**: 회계관리 > 거래처 관리 +**설명**: 거래처 정보 및 신용등급을 관리합니다 + +**기능 설명**: + +| # | 요소 | 설명 | +|---|------|------| +| 1 | 거래처 등록 버튼 | 클릭: 거래처 상세_등록 화면으로 이동 | +| 2 | 구분 필터 셀렉트 박스 | 종류: 전체, 매출, 매입, 매입매출. 디폴트: 전체 | +| 3 | 사용 필터 셀렉트 박스 | 종류: 전체, 사용, 미사용. 디폴트: 전체 | +| 4 | 정렬 셀렉트 박스 | 종류: 최신순, 등록순. 디폴트: 최신순 | + +**기간 필터**: 전전월, 어제, 오늘, 전월, 당월, 당해년도 + +**목록 테이블 컬럼**: + +| 컬럼 | 설명 | +|------|------| +| No. | 순번 | +| 구분 | 매출, 매입, 매입매출 | +| 거래처명 | 회사명 | +| 매입 결제일 | 예: 10일 | +| 매출 결제일 | 예: 15일 | +| 신용등급 | 외부 신용평가 (AAA~D) | +| 거래등급 | 자사 기준 (A(우수)~E(위험)) | +| 미수금 | 금액 | +| 악성채권 | 악성채권 여부 | +| 상태 | 사용/미사용 | + +--- + +### 3.2 거래처 상세 (P17-18) + +**경로**: 회계관리 > 거래처 관리 > 거래처 상세 +**설명**: 거래처 상세 정보 및 신용등급을 관리합니다 + +#### 3.2.1 기본 정보 (P17) + +| # | 요소 | 설명 | +|---|------|------| +| 1 | 파일 등록 영역 (사업자등록증 OCR) | 클릭: 파일탐색기 팝업 표시. 사업자등록증 파일 등록 시 관련 정보 자동 입력 처리 | +| 2 | 구분 셀렉트 박스 | 종류: 매출, 매입, 매입매출 | +| 3 | 사용 셀렉트 박스 | 종류: 사용, 미사용 | + +**기본 정보 필드**: + +| 필드 | 설명 | +|------|------| +| 사업자등록증 | 파일 업로드 → OCR 자동 인식 | +| 거래처 코드 | 자동 생성 | +| 사업자등록번호 | OCR 또는 수기 입력 | +| 거래처명 | 회사명 | +| 대표자명 | 대표이사 | +| 업태 | 사업자등록증 업태 | +| 업종 | 사업자등록증 업종 | +| 거래처 유형 | 매출/매입/매입매출 | +| 상태 | 사용/미사용 | + +**연락처 정보 필드**: 주소(우편번호 찾기), 모바일, 전화번호, 팩스, 이메일 + +**담당자 정보 필드**: 담당자명, 담당자 전화 + +#### 3.2.2 신용/거래 정보 (P18) + +| # | 요소 | 설명 | +|---|------|------| +| 1 | 신용정보 보기 버튼 | 클릭: 신용분석 리포트 팝업표시 | +| 2 | 매입 결제일 셀렉트 박스 | 종류: 1일~31일, 말일. 디폴트: 10일. 거래처 유형이 매입 또는 매입매출일 경우 표시 | +| 3 | 매출 결제일 셀렉트 박스 | 종류: 1일~31일, 말일. 디폴트: 15일. 거래처 유형이 매출 또는 매입매출일 경우 표시 | +| 4 | 신용등급 인풋박스 | 외부 신용평가 등급. 예: AAA, AA, A, BBB, BB, B, CCC, CC, C, D | +| 5 | 거래등급 셀렉트 박스 | 종류: A(우수), B(양호), C(보통), D(주의), E(위험). 디폴트: A(우수). 자사 기준 거래처 평가 | +| 6 | 미수금 표시 영역 | 해당 거래처의 현재 미수금 합계 표시. 읽기 전용 | +| 7 | 연체 토글 | ON: 연체 상태, 연체일수 표시. OFF: 정상 상태. 미수금 현황에서 연체 설정과 연동 | +| 8 | 악성채권 토글 | ON: 악성채권으로 등록, 추심관리 목록에 표시. OFF: 정상. 디폴트: OFF | +| 9 | 미지급 표시 영역 | 해당 거래처에 대한 미지급금 합계 표시. 읽기 전용 | + +**추가 필드**: 입금계좌 은행, 계좌, 예금주, 세금계산서 이메일, 메모 + +--- + +### 3.3 신용분석 리포트 팝업 (P19) + +**경로**: 회계관리 > 거래처 관리 > 거래처 상세 > 신용분석 리포트 팝업 +**설명**: 현행화 (기존 화면 유지) + +--- + +### 3.4 세금계산서 발행 (P20-24) + +**경로**: 회계관리 > 세금계산서 발행 +**설명**: 바로빌 API를 통하여 전자세금계산서를 발행하고 관리합니다 + +#### 3.4.1 목록 화면 (P20) + +**상단 요약 카드**: 발행건수, 총 합계금액, 총 공급가액, 총 세액, 발행/전송 건수 + +| # | 요소 | 설명 | +|---|------|------| +| 1 | 공급자 설정 버튼 | 클릭: 공급자 설정 팝업 표시 | +| 2 | 새로 발행 버튼 | 클릭: 전자세금계산서 발행 세부 입력 영역 표시 | +| 3 | 일자 셀렉트 박스 | 종류: 작성일자, 전송일자. 디폴트: 작성일자 | +| 4 | 상태 셀렉트 박스 | 종류: 전체, 작성중, 발행완료, 국세청 전송완료, 취소됨 | +| 5 | 정렬 셀렉트 박스 | 종류: 작성일자, 전송일자, 공급받는자, 합계금액. 디폴트: 작성일자 | +| 6 | 정렬2 셀렉트 박스 | 종류: 내림차순, 오름차순. 디폴트: 내림차순 | +| 7 | 조회 버튼 | 클릭: 조회 결과 표시 | + +**기간 필터**: 1주일, 1개월, 3개월, 날짜 범위 직접 선택 + +**거래처 검색**: 사업자 번호 또는 사업자명 + +**목록 테이블 컬럼**: 발행번호, 공급받는 자, 작성일자, 전송일자, 공급가액, 세액, 합계금액, 상태, 작업 + +#### 3.4.2 발행 세부 입력 (P21-22) + +| # | 요소 | 설명 | +|---|------|------| +| 1 | 검색 버튼 | 클릭: 거래처 검색 팝업표시, 선택 시 공급받는자 목록에 자동 입력 | +| 2 | 품목 추가 버튼 | 클릭: 품목 행 추가 | +| 3 | 전자세금계산서 발행 세부 입력 영역 | - | + +**공급자 영역 필드**: 등록번호, 종사업장, 상호, 대표자, 사업장주소, 업태, 종목, 담당자, 연락처, 이메일 + +**공급받는자 영역 필드**: 등록번호, 종사업장, 상호, 대표자, 사업장주소, 업태, 종목, 담당자, 연락처, 이메일, 세금계산서 수신 이메일 + +**품목 정보 테이블**: 월, 일, 품목, 수량, 단가, 공급가액, 세액, 합계, 과세유형(과세) + +**기타 필드**: 작성일자, 비고, 추가 메모사항 + +#### 3.4.3 공급자 기초정보 설정 팝업 (P23) + +| 필드 | 설명 | +|------|------| +| 사업자번호 | 회사정보 기본값 | +| 상호명 * | 필수 | +| 대표자명 * | 필수 | +| 업태 | - | +| 종목 | - | +| 주소 | - | +| 담당자명 | - | +| 연락처 | - | +| 이메일 | - | + +#### 3.4.4 거래처 검색 팝업 (P24) + +- 거래처명, 사업자번호, 담당자명으로 검색 +- 목록 표시: 거래처명, 사업자번호 + +--- + +### 3.5 세금계산서 관리 (P25-29) + +**경로**: 회계관리 > 세금계산서 관리 +**설명**: 홈택스에 신고된 세금계산서 매입/매출 내역을 조회하고 관리합니다 + +#### 3.5.1 매출 탭 (P25) + +| # | 요소 | 설명 | +|---|------|------| +| 1 | 일자 셀렉트 박스 | 종류: 작성일자, 전송일자. 디폴트: 작성일자 | +| 2 | 세금계산서 관리 탭 | 종류: 매출+수, 매입+수. 디폴트: 매출+수 | +| 3 | 수기 입력 버튼 | 클릭: 세금계산서 수기 입력 팝업표시 | + +**기간 필터**: 1분기, 2분기, 3분기, 4분기, 날짜 범위 직접 선택 + +**상단 요약 카드**: 매출 공급가액, 매출 세액, 매입 과세 공급가액, 매입 면세 공급가액, 매입 세액 + +**기간 요약**: 매출 합계(공급가액 + 세액), 매입 합계(공급가액 + 세액), 예상 부가세 + +**구분 표시**: 수기 세금계산서(색상 표시), 홈택스 연동 세금계산서(색상 표시) + +**목록 테이블 컬럼**: 작성일자, 발급일자, 거래처, 사업자번호(주민번호), 과세형태, 품목, 공급가액, 세액, 합계, 영수청구, 문서형태, 발급형태, 상태, 분개 + +**엑셀 다운로드** 기능 제공 + +#### 3.5.2 매입 탭 (P26) + +매출 탭과 동일 구조. 추가 기능: + +| # | 요소 | 설명 | +|---|------|------| +| 1 | 분개 버튼 | 클릭: 분개 수정 팝업표시. 분개 저장 시 [분개 완료] 버튼으로 변경 | + +#### 3.5.3 세금계산서 수기 입력 팝업 (P27) + +| # | 요소 | 설명 | +|---|------|------| +| 1 | 구분 셀렉트 박스 | 종류: 매출, 매입. 디폴트: 매출 | +| 2 | 카드 내역 불러오기 버튼 | 클릭: 카드 내역 불러오기 팝업 표시. 선택 시 공급자 정보 및 금액 자동 입력, 수정 가능 | +| 3 | 과세유형 셀렉트 박스 | 종류: 과세, 영세, 면세. 디폴트: 과세 | + +**입력 필드**: 구분, 작성일자, 공급자명, 사업자 번호, 품목, 과세유형, 공급가액, 세액, 합계, 비고 + +#### 3.5.4 카드 내역 불러오기 팝업 (P28) + +| # | 요소 | 설명 | +|---|------|------| +| 1 | 카드 내역 목록 표시 | 기간 검색 후 목록 표시 | +| 2 | 선택 버튼 | 클릭: 세금계산서 수기 입력 팝업에 공급자 정보 및 금액 자동 입력 | + +**검색**: 가맹점/승인번호, 기간 검색 + +**목록 컬럼**: 날짜, 가맹점, 금액, 승인번호, 선택 + +#### 3.5.5 분개 수정 팝업 (P29) + +| # | 요소 | 설명 | +|---|------|------| +| 1 | 구분 영역 | 클릭: 차변/대변 토글 | +| 2 | 계정과목 셀렉트 박스 | 검색 가능. 종류: 계정과목 목록 | + +**세금계산서 정보**: 구분(매입/매출), 공급가액, 거래처, 세액 + +**분개 내역**: 구분(차변/대변), 계정과목, 금액, 합계 + +**버튼**: 분개 수정, 취소, 분개 삭제 + +--- + +### 3.6 계좌 입출금 내역 (P30-31) + +**경로**: 회계관리 > 계좌 입출금 내역 +**설명**: 계좌 입출금 내역을 조회하고 관리합니다 + +#### 3.6.1 목록 화면 (P30) + +| # | 요소 | 설명 | +|---|------|------| +| 1 | 저장 버튼 | 클릭: 인풋박스 영역의 변경값 저장 | +| 2 | 입출금 수기 입력 버튼 | 클릭: 입출금 수기 입력_등록 팝업 표시 | +| 3 | 계좌 입출금 내역 목록 | 클릭: 입출금 수기 입력 팝업 표시 | +| 4 | 수정 영역 | 수정된 영역에 하이라이트 표시 | +| 5 | 구분 셀렉트 박스 | 종류: 전체, 은행계좌, 대출계좌, 증권계좌, 보험계좌. 디폴트: 전체 | +| 6 | 금융기관 셀렉트 박스 | 종류: 전체, 금융기관명 목록. 디폴트: 전체 | + +**기간 필터**: 지난달, D-5월, D-4월, D-3월, D-2월, 이번달 + +**상단 요약 카드**: 입금, 출금, 잔액, 계좌 수, 거래 건수 + +**구분 표시**: 수기 계좌(색상 표시), 연동 계좌(색상 표시) + +**목록 테이블 컬럼**: No., 거래일시, 구분, 계좌정보(금융기관+계좌번호), 적요/내용, 입금, 출금, 잔액, 취급점, 상대계좌 예금주명 + +**엑셀 다운로드** 기능 제공 + +#### 3.6.2 입출금 수기 입력 팝업 (P31) + +| # | 요소 | 설명 | +|---|------|------| +| 1 | 계좌 셀렉트 박스 | 종류: 설정된 계좌 목록 | +| 2 | 수정 스티커 | 수정되었을 경우에만 표시 | +| 3 | 원본으로 복원 버튼 | 클릭: 원본 데이터로 변경, 수정 스티커 삭제 | + +**입력 필드**: + +| 필드 | 필수 | 설명 | +|------|------|------| +| 계좌 | * | 계좌 선택 | +| 거래일 | * | 날짜 선택 | +| 거래시간 | - | HH:MM:SS | +| 거래유형 | * | 입금/출금 | +| 금액 | * | 금액 입력 | +| 잔액 | - | 자동계산 | +| 적요 | - | 내용 | +| 상대계좌 예금주명 | - | - | +| 메모 | - | - | +| 취급점 | - | - | + +--- + +### 3.7 카드 사용 내역 (P32-34) + +**경로**: 회계관리 > 카드 사용 내역 +**설명**: 카드 사용 내역을 조회하고 관리합니다 + +#### 3.7.1 목록 화면 (P32) + +| # | 요소 | 설명 | +|---|------|------| +| 1 | 숨김 데이터 보기 버튼 | 클릭: 숨김 처리된 거래 영역 표시/숨김 토글. 디폴트: 숨김 | +| 2 | 저장 버튼 | 표 수정 사항 저장 처리 | +| 3 | 카드사용 수기 입력 버튼 | 클릭: 카드사용 수기 입력 팝업표시 | +| 4 | 카드사 셀렉트 박스 | 종류: 전체, 카드사명 목록. 디폴트: 전체 | +| 5 | 공제 셀렉트 박스 (필터) | 종류: 전체, 공제, 불공제. 디폴트: 전체 | +| 6 | 공제 셀렉트 박스 (행 내) | 종류: 공제, 불공제. 디폴트: 공제 | +| 7 | 텍스트 인풋박스 영역 | 인라인 수정 가능 | +| 8 | 숫자 인풋박스 영역 | 인라인 수정 가능 | +| 9 | 계정과목 셀렉트 박스 | 검색 가능. 종류: 계정과목 목록 | +| 10 | 분개 버튼 | 클릭: 거래 분개 팝업표시 | +| 11 | 숨김 버튼 | 클릭: 숨김 데이터 영역에 추가 | +| 12 | 복원 버튼 | 클릭: 원래 위치로 복원 | + +**기간 필터**: 지난달, D-5월, D-4월, D-3월, D-2월, 이번달 + +**상단 요약 카드**: 사용금액, 공제, 불공제, 등록된 카드 수 + +**구분 표시**: 수기 카드(색상 표시), 연동 카드(색상 표시) + +**목록 테이블 컬럼**: No., 사용일시, 카드사, 카드번호, 카드명, 공제, 사업자번호, 가맹점명/증빙/판매자상호, 내역, 합계금액, 공급가액, 세액, 계정과목, 분개, 숨김 + +**숨김 처리된 거래 영역** (별도 테이블): No., 사용일시, 카드사, 카드번호, 카드명, 사업자번호, 가맹점명, 합계금액, 숨김일시, 복원 + +**엑셀 다운로드** 기능 제공 + +#### 3.7.2 카드사용 수기 입력 팝업 (P33) + +| # | 요소 | 설명 | +|---|------|------| +| 1 | 카드 셀렉트 박스 | 종류: 설정된 카드 목록 (예: 신한카드 123123 카드명) | +| 2 | 공제 셀렉트 박스 | 종류: 공제, 불공제. 디폴트: 공제 | +| 3 | 계정과목 셀렉트 박스 | 검색 가능. 종류: 계정과목 목록 | + +**입력 필드**: + +| 필드 | 필수 | 설명 | +|------|------|------| +| 카드 선택 | * | 카드 셀렉트 | +| 사용일 | * | 날짜 | +| 사용시간 | - | HH:MM:SS | +| 승인유형 | * | 승인/취소 | +| 승인번호 | - | - | +| 가맹점명 | - | - | +| 사업자번호 | - | - | +| 공제여부 | * | 공제/불공제 | +| 계정과목 | - | 선택 | +| 증빙/판매자상호 | - | - | +| 내역 | - | - | +| 공급가액 | * | 금액 | +| 세액 | * | 금액 | +| 메모 | - | - | + +**합계 금액** = 공급가액 + 세액 (자동 계산 표시) + +#### 3.7.3 거래 분개 팝업 (P34) + +| # | 요소 | 설명 | +|---|------|------| +| 1 | 계정과목 셀렉트 박스 | 검색 가능. 종류: 계정과목 목록 | +| 2 | 공제 셀렉트 박스 | 종류: 공제, 불공제. 디폴트: 공제 | +| 3 | 분개 항목 추가 버튼 | 클릭: 분개 항목 영역 추가 | +| 4 | 삭제 버튼 | 클릭: 해당 분개 항목영역 삭제. 1개만 있을 경우 버튼 비활성화 | + +**거래 정보**: 가맹점, 사용일시, 공급가액, 세액, 합계금액 + +**분개 항목 필드**: 계정과목, 공제, 내역, 증빙/판매자상호, 공급가액, 세액, 합계금액, 내역, 메모 + +**분개 합계** 표시 + +--- + +### 3.8 상품권 관리 (P35-36) + +**경로**: 회계관리 > 상품권 관리 +**설명**: 상품권을 등록하고 관리합니다 + +> **상품권 접대비 기준**: +> 1. 50만원 미만: 일반 복리후생비/판촉비. 일반 경비로 처리 가능 +> 2. 50만원 이상: 접대비로 자동 분류. 사용처/수령인 기록 필수. 세법상 접대비 한도 관리 대상 + +#### 3.8.1 목록 화면 (P35) + +| # | 요소 | 설명 | +|---|------|------| +| 1 | 상품권 등록 버튼 | 클릭: 상품권 상세화면으로 이동 | +| 2 | 접대비 셀렉트 박스 | 종류: 전체, 해당, 해당없음. 디폴트: 전체 | +| 3 | 상태 셀렉트 박스 | 종류: 전체, 보유, 사용, 폐기. 디폴트: 전체 | + +**기간 필터**: 지난달, D-5월, D-4월, D-3월, D-2월, 이번달 + +**상단 요약 카드**: 전체 상품권 건수, 보유 상품권(건수/금액), 사용 상품권(건수/금액), 접대비 해당(건수/금액) + +**목록 테이블 컬럼**: No., 일련번호, 상품권명, 액면가, 구입일, 사용일, 접대비, 상태 + +#### 3.8.2 상품권 상세 (P36) + +| # | 요소 | 설명 | +|---|------|------| +| 1 | 액면가 인풋박스 | 50만원 이상 입력 시 필수 정보 표시 (사용처/수령인 등) | +| 2 | 구입처 셀렉트 박스 | 종류: 매입 거래처명 목록 | +| 3 | 구입목적 셀렉트 박스 | 종류: 판촉, 선물, 접대, 기타 | +| 4 | 상태 셀렉트 박스 | 종류: 보유, 사용, 폐기 | + +**기본 정보 필드**: 상품권명, 일련번호, 액면가, 구입처, 구입목적, 구입일, 접대비(해당/해당없음) + +**상품권 정보** (액면가 50만원 이상 필수): + +| 필드 | 설명 | +|------|------| +| 사용처/용도 | 내용 | +| 수령인 | 이름 | +| 수령인 소속 | 회사명 | +| 사용일 | 날짜 | +| 상태 | 보유/사용/폐기 | +| 비고 | - | + +--- + +### 3.9 일반 전표 입력 (P37-40) + +**경로**: 회계관리 > 일반 전표 입력 +**설명**: 계좌입출금내역을 기반으로 분개 전표를 생성합니다 + +#### 3.9.1 목록 화면 (P37) + +| # | 요소 | 설명 | +|---|------|------| +| 1 | 계정과목 설정 버튼 | 클릭: 계정과목 설정 팝업표시 | +| 2 | 수기 전표 입력 버튼 | 클릭: 수기 전표 입력 팝업표시 | +| 3 | 분개 버튼 | 클릭: 분개 수정 팝업표시 | + +**기간 필터**: 지난달, D-5월, D-4월, D-3월, D-2월, 이번달 + +**상단 요약 카드**: 전체 건수, 입금, 출금, 분개완료 건수, 미분개 건수 + +**구분 표시**: 수기 전표(색상 표시), 연동 전표(색상 표시) + +**목록 테이블 컬럼**: 날짜, 적요, 입금, 출금, 잔액, 분개 내역(차변/대변), 분개(차변 합계/대변 합계), 분개 버튼 + +**분개 내역 예시**: +``` +차 현금 6,000 + 대 외상매출금 6,000 + +차 현금 3,000 +차 복리후생비 3,000 + 대 외상매출금 6,000 +``` + +#### 3.9.2 계정과목 설정 팝업 (P38) + +| # | 요소 | 설명 | +|---|------|------| +| 1 | 분류 셀렉트 박스 (추가) | 종류: 자산, 부채, 자본, 수익, 비용. 디폴트: 자산 | +| 2 | 추가 버튼 | 클릭: 계정과목 행 추가 | +| 3 | 분류 셀렉트 박스 (필터) | 종류: 전체, 자산, 부채, 자본, 수익, 비용. 디폴트: 전체 | +| 4 | 상태 버튼 | 클릭: 사용중/미사용 토글 | +| 5 | 삭제 버튼 | 클릭: 해당 계정과목 삭제 처리 | + +**계정과목 추가 필드**: 코드(예: 101), 분류(자산/부채/자본/수익/비용), 계정과목명(예: 현금) + +**목록 테이블 컬럼**: 코드, 계정과목명, 분류, 상태, 작업(삭제) + +**검색**: 코드 또는 이름 검색 + +#### 3.9.3 수기 전표 입력 팝업 (P39) + +| # | 요소 | 설명 | +|---|------|------| +| 1 | 구분 영역 | 클릭: 차변/대변 토글 | +| 2 | 계정과목 셀렉트 박스 | 검색 가능. 종류: 계정과목 목록 | +| 3 | 거래처 셀렉트 박스 | 검색 가능. 종류: 거래처 목록 | +| 4 | 행 추가 버튼 | 클릭: 아래 위치에 행 추가 | + +**거래 정보 필드**: 전표일자 *, 적요, 전표번호(자동생성, 예: JE-20260213-002) + +**분개 내역 필드** (행 단위): 구분(차변/대변), 계정과목, 금액, 거래처, 적요 + +**합계**: 차변 합계, 대변 합계 + +#### 3.9.4 분개 수정 팝업 (P40) + +| # | 요소 | 설명 | +|---|------|------| +| 1 | 차변 합계 표시 | - | +| 2 | 대변 합계 표시 | - | +| 3 | 균형 표시 | 차변=대변이면 "대차 균형", 다르면 "차이: {금액}" 표시 | + +**거래 정보**: 날짜, 금액, 구분(입금/출금), 적요, 계좌, 전표 적요 + +**분개 내역 필드** (행 단위): 구분(차변/대변), 계정과목, 금액, 거래처, 적요 + +**버튼**: 분개 수정, 취소, 분개 삭제, 행추가 + +--- + +## 4. 기준정보 + +### 4.1 바로빌 연동 관리 (P42-45) + +**경로**: 기준정보 > 바로빌 연동 관리 +**설명**: 바로빌 연동 정보를 관리합니다 + +#### 4.1.1 메인 화면 (P42) + +| # | 요소 | 설명 | +|---|------|------| +| 1 | 바로빌 로그인 정보 등록 버튼 | 클릭: 바로빌 로그인 정보 등록 팝업 표시 | +| 2 | 바로빌 회원가입 정보 등록 버튼 | 클릭: 바로빌 회원가입 정보 등록 팝업 표시 | +| 3 | 은행 빠른조회 서비스 등록 버튼 | 클릭: 은행 빠른조회 서비스 등록 팝업 표시 | +| 4 | 계좌 연동 등록 버튼 | 바로빌 연동 정보 없으면 Alert, 있으면 바로빌 계좌 등록 팝업 표시 | +| 5 | 카드 연동 등록 버튼 | 바로빌 연동 정보 없으면 Alert, 있으면 바로빌 카드 등록 팝업 표시 | +| 6 | 공인인증서 등록 버튼 | 바로빌 연동 정보 없으면 Alert, 있으면 바로빌 공인인증서 등록 팝업 표시 | + +**연동 플로우**: + +``` +바로빌 연동 +├── 바로빌 회원인 경우 → 로그인 정보 등록 +├── 바로빌 비회원인 경우 → 회원가입 정보 등록 +├── 계좌 연동 (2단계) +│ ├── 1단계: 각 은행 인터넷뱅킹 접속 → 빠른조회/간편서비스 → 계좌 등록 +│ └── 2단계: 바로빌 연동 계좌 정보 등록 → 은행/계좌번호/비밀번호 → 조회 주기 설정 +├── 카드 연동 → 카드사/아이디/비밀번호 입력 +└── 공인인증서 등록 → 홈택스 세금계산서 연동용 +``` + +#### 4.1.2 바로빌 로그인 정보 등록 팝업 (P43) + +| 필드 | 필수 | 설명 | +|------|------|------| +| 바로빌 아이디 | * | Barobill_id | +| 비밀번호 | * | - | + +**버튼**: 등록하기 (바로빌 로그인 처리), 취소 + +#### 4.1.3 바로빌 회원가입 정보 등록 팝업 (P44) + +| 필드 | 필수 | 설명 | +|------|------|------| +| 사업자등록번호 | * | 123-12-12345 | +| 상호명 | * | 회사명 | +| 대표자명 | * | 홍길동 | +| 업태 | - | - | +| 업종 | - | - | +| 주소 | - | - | +| 바로빌 아이디 | * | Barobill_id | +| 비밀번호 | * | - | +| 담당자명 | - | - | +| 담당자 연락처 | - | - | +| 담당자 이메일 | - | - | + +**버튼**: 등록하기 (바로빌 회원가입 처리), 취소 + +#### 4.1.4 은행 빠른조회 서비스 등록 팝업 (P45) + +| # | 요소 | 설명 | +|---|------|------| +| 1 | 은행 셀렉트 박스 | 종류: 은행 목록. 디폴트: 첫번째 은행 | +| 2 | 구분 셀렉트 박스 | 종류: 기업, 개인. 디폴트: 기업 | +| 3 | 바로가기 버튼 | 클릭: 선택한 은행+구분에 해당하는 URL로 링크 | + +--- + +### 4.2 계좌 관리 (P46-52) + +**경로**: 기준정보 > 계좌 관리 +**설명**: 연동 계좌 및 수기 계좌를 등록하고 관리합니다 + +#### 4.2.1 목록 화면 (P46) + +| # | 요소 | 설명 | +|---|------|------| +| 1 | 수기 계좌 등록 버튼 | 클릭: 계좌 상세_등록 화면으로 이동 | +| 2 | 구분 셀렉트 박스 | 종류: 전체, 은행계좌, 대출계좌, 증권계좌, 보험계좌. 디폴트: 전체 | +| 3 | 금융기관 셀렉트 박스 | 종류: 전체, 금융기관명 목록. 디폴트: 전체 | + +**상단 요약 카드**: 전체 계좌, 국내/외환 계좌, 대출 계좌, 증권 계좌, 보험 계좌 (각 개수) + +**구분 표시**: 수기 계좌(색상 표시), 연동 계좌(색상 표시) + +**목록 테이블 컬럼**: No., 구분, 유형, 금융기관, 계좌번호, 계좌명, 상태(사용/중지) + +#### 4.2.2 계좌 상세_은행 (P47) + +| # | 요소 | 설명 | +|---|------|------| +| 1 | 구분 셀렉트 박스 | 종류: 은행계좌, 대출계좌, 증권계좌, 보험계좌 | +| 2 | 유형 셀렉트 박스 | 은행: 보통예금, 정기예금, 정기적금, 외화예금, 기타 | +| 3 | 사용 셀렉트 박스 | 종류: 사용, 중지 | +| 4 | 계좌 정보 영역 | 은행 계좌일 경우에만 표시 | + +**기본 정보 필드**: 계좌번호, 구분, 유형, 금융기관, 예금주, 계좌명(상품명), 상태 + +**계좌 정보 필드** (은행 전용): 시작일, 만기일, 이율, 계약금액, 이월잔액, 비고 + +#### 4.2.3 계좌 상세_대출 (P48) + +**기본 정보**: 은행과 동일 구조 +**유형**: 시설자금, 운영자금, 기타 + +**대출 정보 필드** (대출 전용): + +| 필드 | 설명 | +|------|------| +| 시작일 | 대출 시작일 | +| 만기일 | 대출 만기일 | +| 이율 | 이자율 (%) | +| 대출금액 | 총 대출 금액 | +| 대출잔액 | 현재 남은 잔액 | +| 상환 방식 | 원리금균등 등 | +| 이자 납입 주기 | 매월 등 | +| 거치 기간 | 개월 수 | +| 월 상환액 | 월 상환 금액 | +| 담보물 | 내용 | +| 비고 | - | + +#### 4.2.4 계좌 상세_증권 (P49) + +**유형**: 직접투자, 펀드, 신탁, 기타 + +**증권 정보 필드** (증권 전용): + +| 필드 | 설명 | +|------|------| +| 시작일 | 투자 시작일 | +| 만기일 | - | +| 수익율 | % | +| 투자금액 | 총 투자 금액 | +| 평가액 | 현재 평가 금액 | +| 비고 | - | + +#### 4.2.5 계좌 상세_보험_단체보험 (P50) + +**유형**: 단체보험 + +**보험 정보 필드**: + +| 필드 | 설명 | +|------|------| +| 시작일 | 계약 시작일 | +| 만기일 | 계약 만기일 | +| 이율 | - | +| 계약금액 | 총 계약 금액 | +| 해약환급금 | 현재 환급금 | +| 납입 주기 | 월납, 분기납, 반기납, 연납, 일시납 | +| 증권번호 | - | +| 가입 인원 | 명 | +| 1인당 보험료 | 금액 | +| 납입 주기당 보험료 | 금액 | +| 비고 | - | + +#### 4.2.6 계좌 상세_보험_화재보험 (P51) + +**유형**: 화재보험 + +**보험 정보 필드**: 단체보험과 유사. 추가 필드: + +| 필드 | 설명 | +|------|------| +| 보험 대상물 | 내용 | +| 대상물 주소 | 주소 | + +#### 4.2.7 계좌 상세_보험_CEO보험 (P52) + +**유형**: CEO 보험 + +**보험 정보 필드**: 단체보험과 유사. 추가 필드: + +| 필드 | 설명 | +|------|------| +| 피보험자 | 이름 (기본정보의 예금주 대신) | +| 수익자 | 이름 | + +--- + +### 4.3 카드 관리 (P53-54) + +**경로**: 기준정보 > 카드 관리 +**설명**: 연동 카드 및 수기 카드를 등록하고 관리합니다 + +#### 4.3.1 목록 화면 (P53) + +| # | 요소 | 설명 | +|---|------|------| +| 1 | 수기 카드 등록 버튼 | 클릭: 카드 상세_등록 화면으로 이동 | +| 2 | 카드사 셀렉트 박스 | 종류: 전체, 카드사명 목록. 디폴트: 전체 | +| 3 | 상태 셀렉트 박스 | 종류: 전체, 사용, 중지. 디폴트: 전체 | + +**상단 요약 카드**: 전체 카드 수, 총 한도, 사용금액, 잔여한도 + +**구분 표시**: 수기 카드(색상 표시), 연동 카드(색상 표시) + +**목록 테이블 컬럼**: No., 카드사, 카드번호, 카드명, 부서, 사용자, 사용현황(금액 + 사용률 %), 상태(사용/중지) + +#### 4.3.2 카드 상세 (P54) + +| # | 요소 | 설명 | +|---|------|------| +| 1 | 카드사 셀렉트 박스 | 종류: 카드사명 목록 | +| 2 | 종류 셀렉트 박스 | 종류: 신용카드, 체크카드 | +| 3 | 결제일 셀렉트 박스 | 종류: 매월 1일, 5일, 10일, 14일, 15일, 20일, 25일, 27일 | +| 4 | 상태 셀렉트 박스 | 종류: 사용, 중지 | +| 5 | 부서 셀렉트 박스 | 검색 가능. 종류: 부서 목록 | +| 6 | 사용자 셀렉트 박스 | 검색 가능. 종류: 선택 부서 사원 목록 | +| 7 | 품의서 작성 버튼 | 클릭: 문서 작성_품의서 화면으로 이동 (품의 사유에 현재 카드사, 카드번호, 카드명 표시) | + +> **선결제 신청 플로우**: 품의서 작성 → 결재선 승인 → 출금 처리 → 장표 등록 → 연동카드일 경우 잔여한도 반영 + +**기본 정보 필드**: 카드명, 종류, 카드사, 카드번호, 유효기간(년도/월), 카드 명의자, CSV + +**사용자 정보 필드**: 부서, 사용자, 직책 + +**한도 정보**: 총 한도, 사용 금액, 잔여한도, 결제일 + +**기타**: 메모, 상태, 선결제 신청, 품의서 작성 안내 + +--- + +### 4.4 달력 관리 (P55-58) + +**경로**: 기준정보 > 달력 관리 +**설명**: 달력을 관리합니다 + +#### 4.4.1 달력 뷰 (P55) + +| # | 요소 | 설명 | +|---|------|------| +| 1 | 달력 탭 | 종류: 달력, 목록. 디폴트: 달력 | +| 2 | 대량 등록 버튼 | 클릭: 대량 등록팝업 표시 | +| 3 | 달력 일정 등록 버튼 | 클릭: 달력 상세_등록팝업 표시 | +| 4 | 월별 달력 영역 | 클릭: 달력 상세 팝업 표시. 연간 달력 표시 (1~12월) | + +**상단 요약**: 등록 건수, 총 휴일 일수, 공휴일 건수 + +#### 4.4.2 목록 뷰 (P56) + +| # | 요소 | 설명 | +|---|------|------| +| 1 | 유형 필터 셀렉트 박스 | 종류: 전체, 공휴일, 임시휴일, 대체휴일, 세무일, 회사일정. 디폴트: 전체 | +| 2 | 달력 목록 | 클릭: 달력 상세 팝업 표시 | + +**목록 테이블 컬럼**: No., 유형, 일정명, 시작일, 종료일, 일수, 반복, 메모 + +#### 4.4.3 달력 상세 팝업 (P57) + +| # | 요소 | 설명 | +|---|------|------| +| 1 | 유형 셀렉트 박스 | 종류: 공휴일, 임시휴일, 대체휴일, 세무일, 회사일정 | +| 2 | 매년 반복 체크박스 | 클릭: 체크 설정/해제 토글. 디폴트: 해제. 체크 설정 시 매년 등록 | + +**입력 필드**: 일정명 *, 유형 *, 기간 *, 메모, 매년 반복 + +**버튼**: 수정, 삭제 + +#### 4.4.4 대량 등록 팝업 (P58) + +**입력 형식**: +- `YYYY-MM-DD 일정명` - 단일 일자 +- `YYYY-MM-DD~YYYY-MM-DD 일정명` - 기간 (일정) +- `YYYY-MM-DD 일정명 [유형]` - 유형 지정 (공휴일/세무일정/회사지정/대체휴일/임시휴일) + +**예시 입력**: +``` +2026-01-01 신정 +2026-01-28~2026-01-30 설날연휴 +2026-03-01 삼일절 +2026-05-05 어린이날 +2026-05-15 부처님오신날 +2026-06-06 현충일 +2026-08-15 광복절 +2026-10-03 개천절 +2026-10-05~2026-10-07 추석연휴 +2026-10-09 한글날 +2026-12-25 크리스마스 +``` + +**버튼**: {N건} 등록, 취소 + +--- + +### 4.5 이관 기초자료 (P59-65) + +**경로**: 기준정보 > 이관 기초자료 +**설명**: 이관 기초자료를 관리합니다 + +#### 4.5.1 메인 화면 (P59-60) + +| # | 요소 | 설명 | +|---|------|------| +| 1 | 이관 기초자료 탭 | 종류: 거래처, 거래 내역, 계좌 내역, 세금계산서 내역, 업로드 이력. 디폴트: 거래처 | +| 2 | 양식 다운로드 버튼 | 클릭: 등록된 양식 CSV 다운로드 | +| 3 | 파일 선택 버튼 | 클릭: 파일 탐색기 팝업. CSV 1개만 등록. 50MB 이하 | +| 4 | 파일 변환 버튼 | 클릭: CSV 데이터를 정보 등록 영역에 변환값 표시 | + +**파일 변환 후 상태** (P60): + +| # | 요소 | 설명 | +|---|------|------| +| 1 | 파일명 버튼 | 클릭: 파일 다운로드 처리 | +| 2 | 전체/개별 체크박스 | 클릭: 체크 설정/해제 토글. 디폴트: 전체 설정 | +| 3 | 등록 버튼 | 파일변환 완료 & 체크 항목 있을 경우만 활성화. 클릭: 확인 Alert → 등록 처리 | +| 4 | 오류 하이라이트 | 오류 사항 색상 표시. 마우스 롤 오버 시 문구 표시 | + +#### 4.5.2 거래처 CSV 스펙 (P61) + +| 컬럼명 | 필수 | 타입 | 최대길이 | 입력형식/규칙 | 예시 | 검증 | +|--------|------|------|----------|--------------|------|------| +| 사업자등록번호 | O | 텍스트 | 12자 | NNN-NN-NNNNN (하이픈유무무관) | 123-45-67890 | 10자리숫자, 하이픈자동처리, 중복검사 | +| 거래처명 | O | 텍스트 | 100자 | 법인명 또는 상호명 | (주)한국건설 | 빈값불가, 앞뒤공백 자동 trim | +| 대표자명 | - | 텍스트 | 50자 | 대표이사 성명 | 김대표 | - | +| 거래처유형 | O | 텍스트 | 10자 | 매출처/매입처/기타 | 매출 | 3가지 값만 허용 (대소문자 무관) | +| 업태 | - | 텍스트 | 50자 | 사업자등록증 업태 | 건설업 | - | +| 업종 | - | 텍스트 | 50자 | 사업자등록증 종목 | 시설공사, 토목공사 | - | +| 우편번호 | - | 텍스트 | 5자 | 5자리 숫자 | 06134 | 숫자 5자리 | +| 주소 | - | 텍스트 | 200자 | 도로명 또는 지번 주소 | 서울시 강남구 테헤란로 123 | - | +| 상세주소 | - | 텍스트 | 100자 | 건물명, 층, 호 | 삼성빌딩 5층 501호 | - | +| 대표전화번호 | - | 텍스트 | 20자 | NNN-NNNN-NNNN | 02-1234-5678 | 하이픈 자동 포맷 | +| 팩스번호 | - | 텍스트 | 20자 | NNN-NNNN-NNNN | 02-1234-5679 | 하이픈 자동 포맷 | +| 담당자명 | - | 텍스트 | 50자 | 실무 담당자 이름 | 홍길동 | - | +| 담당자연락처 | - | 텍스트 | 20자 | 휴대폰 또는 직통번호 | 010-1234-5678 | 하이픈 자동 포맷 | +| 이메일 | - | 텍스트 | 100자 | name@domain.com | hk@hancon.co.kr | 이메일 형식 검증 | + +> 사업자등록번호 중복 상태일 경우 마우스 롤오버 시: "기존 거래처정보가 업데이트됩니다." 문구 표시 + +#### 4.5.3 거래 내역 CSV 스펙 (P62) + +| 컬럼명 | 필수 | 타입 | 최대길이 | 입력형식/규칙 | 예시 | 검증 | +|--------|------|------|----------|--------------|------|------| +| 거래유형 | O | 텍스트 | 10자 | 매출/매입 | 매출 | 2가지 값만 허용 | +| 일자 | O | 날짜 | - | YYYY-MM-DD (엑셀 날짜셀 가능) | 2025-12-31 | - | +| 거래처명 | O | 텍스트 | 100자 | 거래처에 등록된 이름과 매칭 | (주)한국건설 | 미등록 → 오류 | +| 사업자등록번호 | O | 텍스트 | 12자 | 등록된 거래처 자동 매칭 | 123-45-67890 | 미등록 → 오류 | +| 품목명N | - | 텍스트 | 100자 | 품목명 | 품목명 | - | +| 공급가액 | O | 숫자 | - | 양의정수 (콤마/원기호 자동 제거) | 10000000 | 0 이하 불가, 콤마 허용 | +| 부가세 | - | 숫자 | - | 미입력 시 공급가액 x 10% 자동계산 | 1000000 | 음수 불가, 빈값 = 자동계산 | +| 적요 | - | 텍스트 | 200자 | 거래 내용 설명 | 12월 기성금 청구 | - | + +> - 거래처 관리에 해당 거래처 등록 필수 +> - 부가세 미입력 시 자동계산 (공급가액 x 10%), 직접 입력 시 자동계산 무시 +> - 품목명~적요까지 추가 입력 가능 +> - 거래처 유효하지 않을 경우: "등록되지 않은 거래처입니다." 문구 표시 +> - 공급가액/부가세 양수 아닐 경우: "금액은 0보다 커야합니다." 문구 표시 + +#### 4.5.4 계좌 내역 CSV 스펙 (P63) + +| 컬럼명 | 필수 | 타입 | 최대길이 | 입력형식/규칙 | 예시 | 검증 | +|--------|------|------|----------|--------------|------|------| +| 거래일시 | O | 날짜 | - | YYYY-MM-DD HH:MM:SS | 2025-12-31 12:21:12 | - | +| 금융기관명 | O | 텍스트 | 30자 | 정식 은행명 (약어 자동 매칭) | 우리은행 | 등록된 은행 목록과 매칭 | +| 계좌번호 | O | 텍스트 | 30자 | 계좌번호 (하이픈 포함/제외 모두 가능) | 1005-301-123456 | 등록된 계좌와 매칭 | +| 적요 | - | 텍스트 | 200자 | 거래 내용 설명 | 기성금 입금 | - | +| 입금 | - | 숫자 | - | 양의정수 (콤마 자동 제거) | 5000000 | 0 이하 불가 | +| 출금 | - | 숫자 | - | 양의정수 (콤마 자동 제거) | 5000000 | 0 이하 불가 | +| 잔액 | O | 숫자 | - | 해당 거래 후 계좌 잔액 | 25000000 | 잔액 정합성 검증용 | +| 취급점 | - | 텍스트 | 100자 | 증미점 | 증미점 | - | +| 상대계좌예금주명 | - | 텍스트 | 20자 | 예금주명 | (주)한국건설 | - | + +> - 계좌 관리에 해당 계좌 등록 필수 +> - 금융기관명/계좌번호 유효하지 않을 경우: "등록되지 않은 계좌입니다." 문구 표시 +> - 잔액 정합성 유효하지 않을 경우: "잔액이 입출금 누적과 불일치합니다." 문구 표시 +> - 금액 양수 아닐 경우: "금액은 0보다 커야합니다." 문구 표시 + +#### 4.5.5 세금계산서 내역 CSV 스펙 (P64) + +| 컬럼명 | 필수 | 타입 | 최대길이 | 입력형식/규칙 | 예시 | 검증 | +|--------|------|------|----------|--------------|------|------| +| 발행유형 | O | 텍스트 | 10자 | 매출/매입 | 매출 | 2가지 값만 허용 | +| 작성일자 | O | 날짜 | - | 세금계산서 작성일 (발행일과 다를 수 있음) | 2025-12-31 | - | +| 발급일자 | O | 날짜 | - | YYYY-MM-DD | 2025-12-31 | - | +| 거래처 | O | 텍스트 | 100자 | 거래처에 등록된 이름과 매칭 | (주)한국건설 | 미등록 → 오류 | +| 사업자번호 | O | 텍스트 | 12자 | 등록된 거래처 자동 매칭 | 123-45-67890 | 미등록 → 오류 | +| 과세형태 | - | 텍스트 | 10자 | 과세/면세 | 과세 | 미입력 시 "과세" 기본값 | +| 품목 | - | 텍스트 | 200자 | 대표 품목/서비스명 | 시설공사 | - | +| 공급가액 | O | 숫자 | - | 양의정수 | 50000000 | 0 이하 불가 | +| 세액 | - | 숫자 | - | 미입력 시 공급가액 x 10% 자동 | 5000000 | - | +| 합계 | - | 숫자 | - | 합계 | 55000000 | 공급가액 + 세액 검증 | +| 영수청구 | - | 텍스트 | 10자 | 영수/청구 | 청구 | 미입력 시 "청구" 기본값 | + +> - 거래처 유효하지 않을 경우: "등록되지 않은 거래처입니다." 문구 표시 +> - 공급가액/부가세 양수 아닐 경우: "금액은 0보다 커야합니다." 문구 표시 + +#### 4.5.6 업로드 이력 (P65) + +| # | 요소 | 설명 | +|---|------|------| +| 1 | 이관 유형 필터 셀렉트 박스 | 종류: 전체, 거래처, 거래 내역, 계좌 내역, 세금계산서 내역. 디폴트: 전체 | +| 2 | 파일명 버튼 | 클릭: 파일 다운로드 처리 | + +**기간 필터**: 지난달, D-5월, D-4월, D-3월, D-2월, 이번달 + +**목록 테이블 컬럼**: No., 업로드 일시, 이관 유형, 전체(건수), 성공(건수), 파일명, 등록자 + +--- + +## 공통 UI 패턴 정리 + +### 기간 필터 유형 + +| 유형 | 사용 화면 | +|------|----------| +| 전전월/어제/오늘/전월/당월/당해년도 | 거래처 관리 | +| 1주일/1개월/3개월 + 날짜범위 | 세금계산서 발행 | +| 1분기/2분기/3분기/4분기 + 날짜범위 | 세금계산서 관리 | +| 지난달/D-5월~D-2월/이번달 | 계좌 입출금, 카드 사용, 상품권, 일반전표, 계좌관리, 카드관리, 이관기초자료 | + +### 데이터 구분 표시 (색상) + +모든 연동 가능한 화면에서 수기 데이터와 연동 데이터를 색상으로 구분: +- 수기 데이터 (한 색상) +- 연동 데이터 (다른 색상) + +### 엑셀 다운로드 + +다음 화면에서 엑셀 다운로드 기능 제공: +- 세금계산서 관리 +- 계좌 입출금 내역 +- 카드 사용 내역 + +### 분개 관련 + +분개 기능이 있는 화면: +- 세금계산서 관리 (매입) +- 카드 사용 내역 +- 일반 전표 입력 + +분개 공통 요소: +- 차변/대변 토글 +- 계정과목 셀렉트 박스 (검색 가능) +- 대차 균형 표시 +- 분개 수정/삭제 + +--- + +## 외부 연동 + +### 바로빌 API + +| 연동 항목 | 용도 | +|-----------|------| +| 전자세금계산서 발행 | 세금계산서 발행 화면에서 바로빌 API를 통한 발행 | +| 홈택스 세금계산서 조회 | 세금계산서 관리에서 홈택스 신고 내역 조회 | +| 계좌 연동 | 은행 빠른조회 서비스를 통한 실시간 계좌 조회 | +| 카드 연동 | 카드사 연동을 통한 카드 사용 내역 자동 수집 | +| 공인인증서 | 홈택스 세금계산서 연동용 인증서 등록 | + +### 사업자등록증 OCR + +거래처 등록 시 사업자등록증 파일 업로드 → OCR 자동 인식 → 거래처 정보 자동 입력 + +--- + +**최종 업데이트**: 2026-02-23 diff --git a/sam/docs/plans/SAM_General_Rule_Storyboard_D1.0.md b/plans/SAM_General_Rule_Storyboard_D1.0.md similarity index 100% rename from sam/docs/plans/SAM_General_Rule_Storyboard_D1.0.md rename to plans/SAM_General_Rule_Storyboard_D1.0.md diff --git a/sam/docs/plans/ai-quotation-engine-plan.md b/plans/ai-quotation-engine-plan.md similarity index 100% rename from sam/docs/plans/ai-quotation-engine-plan.md rename to plans/ai-quotation-engine-plan.md diff --git a/sam/docs/plans/attendance-management-plan.md b/plans/attendance-management-plan.md similarity index 100% rename from sam/docs/plans/attendance-management-plan.md rename to plans/attendance-management-plan.md diff --git a/sam/docs/plans/block-builder-evolution-plan.md b/plans/block-builder-evolution-plan.md similarity index 100% rename from sam/docs/plans/block-builder-evolution-plan.md rename to plans/block-builder-evolution-plan.md diff --git a/sam/docs/plans/design-insight-menu-plan.md b/plans/design-insight-menu-plan.md similarity index 100% rename from sam/docs/plans/design-insight-menu-plan.md rename to plans/design-insight-menu-plan.md diff --git a/sam/docs/plans/fire-shutter-drawing-generator-plan.md b/plans/fire-shutter-drawing-generator-plan.md similarity index 100% rename from sam/docs/plans/fire-shutter-drawing-generator-plan.md rename to plans/fire-shutter-drawing-generator-plan.md diff --git a/plans/integrated-master-plan.md b/plans/integrated-master-plan.md new file mode 100644 index 0000000..7490860 --- /dev/null +++ b/plans/integrated-master-plan.md @@ -0,0 +1,382 @@ +# 통합 개선 계획 — 제품코드 추적성 + 검사 단위 구조 정비 + +> **작성일**: 2026-02-27 +> **목적**: 두 개선 계획(제품코드 추적성, 검사 단위 구조)을 하나의 순차적 실행 계획으로 통합 +> **상태**: 🔄 Phase 0~3 완료, Phase 4 이후 대기 +> **원본 문서**: +> - [`product-code-traceability-plan.md`](./product-code-traceability-plan.md) (아카이브 참조) +> - [`document-system-improvement-plan.md`](./document-system-improvement-plan.md) (아카이브 참조) +> - [`document-system-improvement-review.md`](./document-system-improvement-review.md) (정책 결정 16건) +> **테스트**: [`integrated-test-scenarios.md`](./integrated-test-scenarios.md) (기능 단위 11개 FU) + +--- + +## 📍 현재 진행 상태 + +| 항목 | 내용 | +|------|------| +| **마지막 완료 작업** | Phase 3 - 절곡 검사 동적 구현 (inspection-config API + 트랜잭션 보강) | +| **다음 작업** | Phase 4 (절곡 재공품 + 결재 워크플로우) — 후순위 | +| **진행률** | 5/7 Phase (Phase 0+1+2A+2B+3 완료) | +| **마지막 업데이트** | 2026-02-27 | + +--- + +## 1. 왜 통합이 필요한가 + +두 계획은 **의존성이 교차**한다: + +- 검사 단위 구조 정비(절곡 동적화)는 `work_order_items.options`에 `product_code`가 있어야 동작 +- `product_code` 전파 버그를 먼저 수정하지 않으면 검사 API(`inspection-config`)가 불완전 +- 별도로 작업하면 순서 혼선, 중복 작업, 회귀 위험 발생 + +**통합 효과**: +- 의존성 순서를 강제하여 작업 꼬임 방지 +- 병렬 가능 작업 식별으로 효율 극대화 +- 진행 상태를 한 곳에서 관리 + +--- + +## 2. 통합 Phase 총괄 + +| Phase | 명칭 | 원본 | 의존성 | 상태 | 상세 | +|:-----:|------|------|--------|:----:|------| +| **0** | 사전 데이터 조사 | product-code P0 | 없음 | ✅ | [Phase 0-1 상세](./integrated-phase-0-1.md) | +| **1** | product_code 전파 버그 수정 | product-code P1 | Phase 0 | ✅ | [Phase 0-1 상세](./integrated-phase-0-1.md) | +| **2A** | 절곡 검사 분석/설계 | document-system P1 | 없음 (**Phase 1과 병렬**) | ✅ | [Phase 2 상세](./integrated-phase-2.md) | +| **2B** | 견적/수주 정합성 + 품질 FK | product-code P2+P3 | Phase 1 | ✅ | [Phase 2 상세](./integrated-phase-2.md) | +| **3** | 절곡 검사 동적 구현 | document-system P2 | Phase 1 + 2A | ✅ | [Phase 3 상세](./integrated-phase-3.md) | +| **4** | 절곡 재공품 + 결재 워크플로우 | document-system P3 | Phase 3 | ⏭️ | 마스터 요약만 | +| **5** | 완제품 마스터 + 출하 연결 | product-code P4 | Phase 2B | ⏭️ | 마스터 요약만 | +| **6** | 3관점 검사 + 수주별 뷰 | document-system P4 | Phase 3 + 기획자 | ⏭️ | 마스터 요약만 | + +--- + +## 3. 의존성 다이어그램 + +``` + ┌─────────────────────────────────────────────┐ + │ 실행 타임라인 │ + └─────────────────────────────────────────────┘ + +Phase 0 ─── Phase 1 ──┬── Phase 2B ──── Phase 5 + (조사) (P/C 수정) │ (견적/품질) (FG 마스터) + │ +Phase 2A ──────────────┼── Phase 3 ──── Phase 4 ──── Phase 6 + (절곡 분석) │ (절곡 구현) (재공품) (3관점) + │ + ※ Phase 1 + 2A 병렬 가능 + ※ Phase 2B + 3 준비 부분 병렬 가능 + ※ Phase 4 + 5 독립 (부분 병렬 가능) + +크리티컬 패스: Phase 0 → 1 → 3 → 4 → 6 +``` + +### 병렬 실행 가능 조합 + +| 조합 | 설명 | 조건 | +|------|------|------| +| Phase 1 + 2A | product_code 수정 + 절곡 분석 동시 진행 | 2A는 코드 변경 없음 (분석만) | +| Phase 2B + 3 시작 | 견적/품질 + 절곡 구현 | Phase 1 완료 필수 | +| Phase 4 + 5 | 절곡 재공품 + FG 마스터 | 각각 Phase 3, 2B 완료 | + +--- + +## 4. 공통 원칙 + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ 🎯 통합 핵심 원칙 │ +├─────────────────────────────────────────────────────────────────┤ +│ 1. 컬럼 추가 정책: FK/조인키만 컬럼, 나머지는 options JSON │ +│ 2. 기존 데이터 보존: 파괴적 변경 없이 점진적 개선 │ +│ 3. 역추적 가능: 어떤 단계에서든 원래 제품코드로 돌아갈 수 있어야 함│ +│ 4. 네이밍 통일: Backend JSON=snake_case, Frontend=camelCase │ +│ 5. 기존 동작 보존: 스크린/슬랫/조인트바 검사는 건드리지 않음 │ +│ 6. TemplateInspectionContent 통합: 신규 개발은 여기서 (C3) │ +│ 7. BendingInspectionContent 레거시 동결: 유지만, 신규 기능 X │ +│ 8. row_index = 개소 통일: 구성품은 field_key 인코딩 (C1) │ +│ 9. EAV + options 병행: 두 데이터 경로 독립 운용 (C2) │ +│ 10. 롤백 = 템플릿 유무: document_template_id NULL → 레거시 (I4) │ +└─────────────────────────────────────────────────────────────────┘ +``` + +### 변경 승인 정책 + +| 분류 | 예시 | 승인 | +|------|------|------| +| ✅ 즉시 가능 | options JSON 필드 추가, React 컴포넌트 내부 리팩토링, 프론트 표시 변경 | 불필요 | +| ⚠️ 컨펌 필요 | 서비스 로직 변경, 마이그레이션, API 엔드포인트 추가, 양식 시더 수정 | **필수** | +| 🔴 금지 | 기존 테이블 컬럼 삭제, 기존 스크린/슬랫 검사 로직 변경 | 별도 협의 | + +--- + +## 5. 핵심 데이터 흐름 (통합 TO-BE) + +``` +견적(quotes) + └─ product_code 컬럼 ✅ (Phase 2B) + └─ calculation_inputs → items[].productCode + │ + ▼ (createFromQuote) +수주(orders) + └─ order_nodes.options → ✅ product_code, product_name + │ + ▼ (createProductionOrder) +작업지시(work_orders) + ├─ work_order_items.options → ✅ product_code (Phase 1 수정) + ├─ inspection-config API → ✅ 공정 자동 판별 + BOM 기반 구성품 (Phase 3) + ├─ TemplateInspectionContent → ✅ 동적 절곡 검사 (Phase 3) + └─ document_data EAV → ✅ C1 field_key 인코딩 + │ + ▼ +품질검사(inspections) + └─ ✅ work_order_id FK (Phase 2B) + │ + ▼ +출하(shipments) + └─ ✅ product_code 포함 (Phase 5) +``` + +--- + +## 6. Phase별 요약 + +### Phase 0: 사전 데이터 조사 ⏳ + +**목표**: 마이그레이션 영향 범위 파악 (읽기 전용, 위험 없음) + +- SQL 4개 실행: order_nodes product_code 보유율, work_order_items source 비율, soft delete 건수, lot_no 중복 +- 결과에 따라 Phase 1 보정 전략 조정 + +→ [상세: integrated-phase-0-1.md](./integrated-phase-0-1.md) + +--- + +### Phase 1: product_code 전파 버그 수정 ⏳ + +**목표**: 모든 work_order_items 생성/수정 경로에서 product_code, product_name 전달 + +- 백엔드 5개 코드 경로 수정 (OrderService, WorkOrderService) +- 기존 데이터 보정 마이그레이션 (스냅샷 백업 후) +- 프론트 WorkerScreen/ProductionDashboard에 제품코드 표시 +- **배포 순서**: 백엔드 → 마이그레이션 → 프론트 + +→ [상세: integrated-phase-0-1.md](./integrated-phase-0-1.md) + +--- + +### Phase 2A: 절곡 검사 분석/설계 ⏳ (**Phase 1과 병렬 가능**) + +**목표**: 절곡 구성품(검사 항목) 정보를 API에서 제공하는 구조 설계 + +- items/BOM 테이블에서 KWE01/KSS01/KSS02 구성품 확인 +- 마감유형(S1/S2/S3)별 차이 분석 +- DEFAULT_GAP_PROFILES 기준치 5130 대조 +- inspection-config 범용 API 설계 + +→ [상세: integrated-phase-2.md](./integrated-phase-2.md) + +--- + +### Phase 2B: 견적/수주 정합성 + 품질 FK ⏳ + +**목표**: quotes.product_code 활용 + inspections ↔ work_orders FK 연결 + +- 견적 저장 시 quotes.product_code 저장 +- inspections 테이블에 work_order_id FK 마이그레이션 +- 기존 데이터 보정 (lot_no 기반 역추적) +- **Phase 2B 내부에서 견적/품질 작업은 병렬 가능** (독립 경로) + +→ [상세: integrated-phase-2.md](./integrated-phase-2.md) + +--- + +### Phase 3: 절곡 검사 동적 구현 ✅ + +**목표**: API 기반 동적 구성품 로딩으로 고정 로직 대체 + +- inspection-config API 구현 (BelongsToTenant 필수) +- TemplateInspectionContent buildBendingProducts → API 연동 +- document_data EAV 저장/복원 검증 (C1 field_key) +- createInspectionDocument 트랜잭션 보강 (I2) +- 레거시(Path A) + 신규(Path B) 독립 동작 확인 + +→ [상세: integrated-phase-3.md](./integrated-phase-3.md) + +--- + +### Phase 4: 절곡 재공품 + 결재 워크플로우 ⏭️ + +**목표**: BendingWip 양식 추가 + 결재 프론트 연동 + +| # | 작업 항목 | 비고 | +|---|----------|------| +| 4.1 | 절곡 재공품 mng 양식 시더 추가 | BendingWipInspectionContent 대응 | +| 4.2 | 결재 워크플로우 프론트 연동 | 작성→검토→승인 3단계 | +| 4.3 | React 기존 하드코딩 컴포넌트 전환 결정 | 프론트 담당자 협의 | + +> 실행 시점에 상세 문서 별도 작성 + +--- + +### Phase 5: 완제품 마스터 + 출하 연결 ⏭️ + +**목표**: FG 품목 등록 + 출하 시 제품코드 포함 + orders.item_id + +| # | 작업 항목 | 비고 | +|---|----------|------| +| 5.1 | 완제품(FG) 품목 자동 등록 방안 설계 | 견적 확정 시 or 수주 확정 시 | +| 5.2 | orders.item_id 설정 | FG 품목 등록 후 가능 | +| 5.3 | shipment_items에 product_code 포함 | 부분 출하 시 개소별 매핑 고려 | +| 5.4 | work_order_items.product_code 컬럼 승격 검토 | 통계 쿼리 성능용 | +| 5.5 | E2E 추적 검증 | 견적→출하→품질 전 구간 | + +> 실행 시점에 상세 문서 별도 작성 + +--- + +### Phase 6: 3관점 검사 + 수주별 뷰 ⏭️ + +**목표**: 구성품별/개소별/수주별 3관점 검사 구조 + 수주별 읽기 전용 뷰 + +| # | 작업 항목 | 비고 | +|---|----------|------| +| 6.1 | 기획자와 3관점 화면 설계 협의 (I3) | 화면 구성·데이터 매핑·UI 설계 | +| 6.2 | 수주별 읽기 전용 뷰 구현 (I7) | 입력=개소별, 출력=수주별 | +| 6.3 | 개소별↔구성품별↔수주별 데이터 매핑 | | +| 6.4 | 5130 recordscreen JSON → EAV 변환 | 이관 설계 | + +> 기획자 협의 후 상세 문서 별도 작성 + +--- + +## 7. 통합 성공 기준 + +### Phase 0-1 (product_code) + +| 기준 | 수치 목표 | +|------|----------| +| WorkerScreen 제품코드 표시 | 100% | +| 신규 작업지시 product_code 포함 | NOT NULL | +| 기존 데이터 보정율 (source_order_item_id 있는 건) | 90% 이상 | +| 기존 기능 회귀 | 에러 0건 | +| API 성능 영향 | 5% 미만 | + +### Phase 2A-2B (분석/견적/품질) + +| 기준 | 수치 목표 | +|------|----------| +| KWE01/KSS01/KSS02 구성품 분석 완료 | 3종 이상 | +| DEFAULT_GAP_PROFILES 5130 대조 | 완료 | +| quotes.product_code 저장 | 정상 동작 | +| inspections.work_order_id FK 보정 정확도 | 95% 이상 | + +### Phase 3 (절곡 동적 구현) + +| 기준 | 수치 목표 | +|------|----------| +| 제품코드별 다른 구성품 표시 | 3종 이상 지원 | +| 마감유형별 구성품 변경 | 정상 동작 | +| 기존 절곡 데이터 호환 (Path A + B) | 100% | +| inspection-config API 응답 시간 | < 200ms | +| 스크린/슬랫 회귀 | 에러 0건 | +| document_data 저장 정합성 | 100% | + +--- + +## 8. 통합 컨펌 대기 목록 + +| # | Phase | 항목 | 변경 내용 | 상태 | +|---|:-----:|------|----------|:----:| +| 1 | 0 | 사전 조사 실행 | SQL 4개 (읽기 전용) | ⚠️ 대기 | +| 2 | 1 | product_code 전파 수정 | 5개 코드 경로 options 복사 변경 | ⚠️ 대기 | +| 3 | 1 | 데이터 보정 마이그레이션 | 기존 work_order_items 역추적 보정 | ⚠️ 대기 | +| 4 | 2A | inspection-config API 설계 | 범용 API 엔드포인트 추가 | ⚠️ 대기 | +| 5 | 2B | inspections.work_order_id FK | 마이그레이션 + 로직 수정 | ⚠️ 대기 | +| 6 | 3 | inspection-config API 구현 | 공정 자동 판별 + BOM 구성품 | ⚠️ 대기 | +| 7 | 5 | 완제품 마스터 자동 등록 | items 테이블에 FG 품목 생성 | ⚠️ 대기 | +| 8 | 6 | 3관점 검사 화면 설계 | 기획자 협의 필요 | ⏭️ | + +--- + +## 9. 롤백 전략 (통합) + +| Phase | 위험도 | 롤백 방법 | +|:-----:|:------:|----------| +| 0 | 없음 | 읽기 전용 | +| 1 (options 추가) | 낮음 | options에서 `product_code`, `product_name` 키 제거 스크립트 | +| 1 (데이터 보정) | 중간 | `work_order_items_backup_product_code` 백업 테이블에서 복원 | +| 2B (inspections FK) | 중간 | `work_order_id` 컬럼 drop 마이그레이션 (down 메서드) | +| 3 (절곡 동적화) | 낮음 | document_template_id NULL → 레거시 컴포넌트 자동 복귀 (I4) | +| 5 (FG 품목) | 높음 | `auto_generated` 플래그 기반 식별 후 삭제 | + +--- + +## 10. 참고 파일 (통합) + +### 백엔드 + +| 파일 | 역할 | 관련 Phase | +|------|------|:----------:| +| `api/app/Services/OrderService.php` | 수주→작업지시 변환 (L1410) | 1 | +| `api/app/Services/WorkOrderService.php` | 작업지시 서비스 (L287, L311, L416) | 1, 3 | +| `api/app/Services/Quote/QuoteService.php` | 견적 서비스 | 2B | +| `api/app/Services/InspectionService.php` | 품질검사 서비스 | 2B | +| `api/app/Services/DocumentService.php` | 문서 CRUD | 3 | + +### 프론트엔드 + +| 파일 | 역할 | 관련 Phase | +|------|------|:----------:| +| `react/.../WorkerScreen/actions.ts` | 작업자 화면 서버 액션 | 1 | +| `react/.../WorkerScreen/index.tsx` | 작업자 화면 메인 | 1 | +| `react/.../documents/TemplateInspectionContent.tsx` | 양식 기반 동적 렌더링 (**통합 방향**) | 3 | +| `react/.../documents/BendingInspectionContent.tsx` | 절곡 레거시 (**동결**) | — | +| `react/.../documents/InspectionReportModal.tsx` | 검사 모달 래퍼 | 3 | + +### 참고 문서 + +| 문서 | 경로 | 용도 | +|------|------|------| +| 원본: 제품코드 추적성 | `docs/plans/product-code-traceability-plan.md` | 상세 코드/쿼리 참조 | +| 원본: 검사 단위 구조 | `docs/plans/document-system-improvement-plan.md` | 상세 설계/정책 참조 | +| 리뷰 정책 결정 | `docs/plans/document-system-improvement-review.md` | 16건 정책 결정 | +| 문서 시스템 마스터 | `docs/plans/document-system-master.md` | 전체 Phase 관리 | +| API 규칙 | `API_RULES.md` | Service-First, FormRequest | +| DB 스키마 | `docs/specs/database-schema.md` | 테이블 구조 | + +--- + +## 11. 변경 이력 + +| 날짜 | 항목 | 변경 내용 | +|------|------|----------| +| 2026-02-27 | 통합 문서 작성 | product-code + document-system 2개 계획을 7 Phase 통합 계획으로 병합 | +| 2026-02-27 | Phase 2A 완료 | 절곡 검사 분석/설계 완료. dynamic_bom 발견, 5130 대조 완료, inspection-config API 재설계 | +| 2026-02-27 | Phase 2B 완료 | 견적 product_code 자동추출, inspections.work_order_id FK, 데이터 보정 25건 | +| 2026-02-27 | Phase 3 완료 | inspection-config API(3.1), TemplateInspectionContent API 연동(3.2), EAV 호환 확인(3.3+3.4), 트랜잭션 보강(3.5) | + +--- + +## 12. 세션 관리 정책 + +### 세션 시작 시 +``` +1. 이 문서(integrated-master-plan.md) 읽기 +2. 진행 상태 테이블 확인 → 마지막 완료 작업 파악 +3. 해당 Phase 상세 문서 읽기 +4. 다음 작업 시작 +``` + +### 작업 중 관리 +- Phase 완료 시 이 문서의 진행 상태 테이블 업데이트 +- 해당 Phase 상세 문서도 업데이트 +- 컨펌 필요 사항 발생 시 컨펌 대기 목록에 추가 + +### 세션 종료 시 +- 변경 이력 섹션에 최종 업데이트 기록 + +--- + +*이 문서는 `product-code-traceability-plan.md`와 `document-system-improvement-plan.md`를 통합한 마스터 계획입니다.* \ No newline at end of file diff --git a/plans/production-deployment-plan.md b/plans/production-deployment-plan.md new file mode 100644 index 0000000..bbcde30 --- /dev/null +++ b/plans/production-deployment-plan.md @@ -0,0 +1,1099 @@ +# SAM 운영 환경 배포 계획서 + +> **작성일**: 2026-02-22 +> **상태**: 계획 수립 +> **대상**: MS3 정식 런칭 (2026-02-28) +> **작성자**: 개발팀 + +--- + +## 1. 개요 + +### 1.1 목적 + +SAM 프로젝트의 MS3(정식 런칭, 2026-02-28)을 위해 개발 환경(`dev.codebridge-x.com`)에서 운영 환경(`codebridge-x.com`)으로의 전환을 체계적으로 수행한다. 수동 배포 방식에서 Jenkins CI/CD 기반 자동화 배포로 전환하여 안정적인 운영 체계를 구축한다. + +### 1.2 핵심 원칙 + +- 🔴 **무중단 전환**: 개발 환경 서비스에 영향 없이 운영 환경을 구축한다 +- 🔴 **롤백 가능**: 모든 배포는 즉시 롤백 가능해야 한다 +- 🔴 **자동화 우선**: 반복 작업은 Jenkins 파이프라인으로 자동화한다 +- 🟡 **점진적 전환**: 한 번에 전환하지 않고 Phase별로 검증한다 + +### 1.3 현재 환경 vs 목표 환경 + +| 항목 | 현재 (개발) | 목표 (운영) | +|------|------------|------------| +| **서버** | 114.203.209.83 (2코어/3.8GB) | 신규 서버 (4코어/8GB 이상) | +| **도메인** | `dev.codebridge-x.com` | `codebridge-x.com` | +| **배포 방식** | 수동 (git pull + SSH) | Jenkins CI/CD 자동화 | +| **SSL** | 자체 서명 인증서 | Let's Encrypt | +| **DB** | `samdb` (개발/운영 공용) | `sam_prod` (운영 전용) | +| **모니터링** | 없음 | 헬스체크 + Slack 알림 | +| **백업** | 수동 | 자동 일일 백업 | + +### 1.4 관련 문서 + +| 문서 | 경로 | +|------|------| +| 런칭 로드맵 | `guides/project-launch-roadmap.md` | +| .env 동기화 | `guides/production-env-sync.md` | +| Docker 환경 스펙 | `specs/docker-setup.md` | +| 보안 정책 | `architecture/security-policy.md` | + +--- + +## 2. 환경 전략 + +### 2.1 3-Tier 환경 분리 + +``` +┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ +│ 로컬 (WSL) │ │ 개발 서버 │ │ 운영 서버 │ +│ Docker 기반 │ │ Bare-metal │ │ Bare-metal │ +│ │ │ │ │ │ +│ dev.sam.kr │────→│ dev.codebridge │────→│ codebridge-x │ +│ (hosts 매핑) │ │ -x.com │ │ .com │ +└─────────────────┘ └─────────────────┘ └─────────────────┘ + 개발/테스트 스테이징/CI/CD 정식 서비스 +``` + +### 2.2 도메인 매핑 + +| 서비스 | 로컬 (WSL Docker) | 개발 서버 | 운영 서버 | +|--------|-------------------|----------|----------| +| **React (사용자)** | `dev.sam.kr` | `dev.codebridge-x.com` | `codebridge-x.com` | +| **API** | `api.sam.kr` | `api.dev.codebridge-x.com` | `api.codebridge-x.com` | +| **MNG (관리자)** | `mng.sam.kr` | `mng.dev.codebridge-x.com` | `mng.codebridge-x.com` | +| **Sales** | `sales.sam.kr` | `sales.dev.codebridge-x.com` | `sales.codebridge-x.com` | +| **5130 (레거시)** | `5130.sam.kr` | - | - | +| **Gitea** | - | `114.203.209.83:3000` | - | + +### 2.3 .env 분기 전략 + +> 상세 동기화 절차는 `guides/production-env-sync.md` 참조 + +| 환경 변수 | 로컬 (Docker) | 개발 서버 | 운영 서버 | +|-----------|--------------|----------|----------| +| `APP_ENV` | `local` | `development` | `production` | +| `APP_DEBUG` | `true` | `true` | `false` | +| `APP_URL` | `https://api.sam.kr` | `https://api.dev.codebridge-x.com` | `https://api.codebridge-x.com` | +| `DB_HOST` | `sam-mysql-1` | `localhost` | `localhost` | +| `DB_DATABASE` | `samdb` | `samdb` | `sam_prod` | +| `LOG_CHANNEL` | `stack` | `stack` | `stack` | +| `LOG_LEVEL` | `debug` | `debug` | `warning` | +| `BAROBILL_TEST_MODE` | `true` | `true` | `false` | + +--- + +## 3. 운영 서버 아키텍처 + +### 3.1 서버 스펙 권장 + +| 항목 | 최소 사양 | 권장 사양 | 사유 | +|------|----------|----------|------| +| **CPU** | 4코어 | 8코어 | PHP-FPM 3풀 + Node.js 동시 운영 | +| **RAM** | 8GB | 16GB | PHP-FPM 풀당 ~1.5GB + MySQL ~2GB | +| **디스크** | 100GB SSD | 200GB SSD | DB + 로그 + 파일 스토리지 | +| **OS** | Ubuntu 22.04 LTS | Ubuntu 24.04 LTS | 장기 지원 | + +> **경고: 현재 개발 서버(2코어/3.8GB)에서는 React 빌드 시 메모리 부족으로 실패한다. 운영 서버는 최소 8GB를 확보해야 한다.** + +### 3.2 Bare-metal 운영 결정 + +운영 서버는 Docker를 사용하지 않고 Bare-metal로 구성한다 (현재 개발 서버와 동일 방식). + +| 항목 | Docker | Bare-metal (선택) | +|------|--------|------------------| +| 리소스 오버헤드 | 15~20% | 없음 | +| 서버 스펙 요구 | 높음 | 낮음 | +| 운영 복잡도 | 중간 | 낮음 | +| 현재 개발 서버 | - | 이미 이 방식 사용 중 | + +### 3.3 서비스 레이아웃 + +``` +┌────────────────────────────────────────────────────────────┐ +│ 운영 서버 │ +│ │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ Nginx (Reverse Proxy + Static Files) │ │ +│ │ :80 → HTTPS redirect │ │ +│ │ :443 → PHP-FPM / Node.js │ │ +│ └──────────┬──────────┬──────────┬────────────────────┘ │ +│ │ │ │ │ +│ ┌──────────┴┐ ┌──────┴──────┐ ┌┴───────────┐ │ +│ │ PHP-FPM │ │ PHP-FPM │ │ PHP-FPM │ │ +│ │ pool: api │ │ pool: mng │ │ pool: sales│ │ +│ │ :9001 │ │ :9002 │ │ :9003 │ │ +│ └───────────┘ └─────────────┘ └────────────┘ │ +│ │ +│ ┌──────────────┐ ┌──────────────────────────────┐ │ +│ │ Node.js │ │ Supervisor │ │ +│ │ (React SSR) │ │ - API Queue Worker (x1) │ │ +│ │ :3000 │ │ - MNG Queue Worker (x2) │ │ +│ └──────────────┘ │ - API Scheduler │ │ +│ └──────────────────────────────┘ │ +│ │ +│ ┌──────────────────────────────────────────────────────┐ │ +│ │ MySQL 8.0 (sam_prod) │ │ +│ │ :3306 (localhost only) │ │ +│ └──────────────────────────────────────────────────────┘ │ +└────────────────────────────────────────────────────────────┘ +``` + +### 3.4 PHP-FPM 풀 설정 + +현재 Docker Supervisor 설정 기반으로 운영 서버 PHP-FPM 풀을 구성한다. + +**API 풀** (`/etc/php/8.4/fpm/pool.d/api.conf`): + +```ini +[api] +user = www-data +group = www-data +listen = /run/php/php8.4-fpm-api.sock +pm = dynamic +pm.max_children = 10 +pm.start_servers = 3 +pm.min_spare_servers = 2 +pm.max_spare_servers = 5 +pm.max_requests = 500 +request_terminate_timeout = 300 +chdir = /home/webservice/api +``` + +**MNG 풀** (`/etc/php/8.4/fpm/pool.d/mng.conf`): + +```ini +[mng] +user = www-data +group = www-data +listen = /run/php/php8.4-fpm-mng.sock +pm = dynamic +pm.max_children = 15 +pm.start_servers = 5 +pm.min_spare_servers = 3 +pm.max_spare_servers = 8 +pm.max_requests = 500 +request_terminate_timeout = 300 +chdir = /home/webservice/mng +``` + +**Sales 풀** (`/etc/php/8.4/fpm/pool.d/sales.conf`): + +```ini +[sales] +user = www-data +group = www-data +listen = /run/php/php8.4-fpm-sales.sock +pm = dynamic +pm.max_children = 5 +pm.start_servers = 2 +pm.min_spare_servers = 1 +pm.max_spare_servers = 3 +pm.max_requests = 500 +chdir = /home/webservice/sales +``` + +### 3.5 Supervisor 프로세스 설정 + +현재 Docker 컨테이너의 `supervisord.conf`를 운영 서버용으로 변환한다. + +**API Queue Worker** (`/etc/supervisor/conf.d/sam-api-worker.conf`): + +```ini +[program:sam-api-worker] +command=php /home/webservice/api/artisan queue:work database --queue=api,default --sleep=3 --tries=3 --timeout=1800 --max-jobs=100 --max-time=3600 +process_name=%(program_name)s_%(process_num)02d +numprocs=1 +directory=/home/webservice/api +autostart=true +autorestart=true +startsecs=5 +startretries=3 +stopwaitsecs=1830 +user=www-data +stdout_logfile=/var/log/sam/api-queue-worker.log +stdout_logfile_maxbytes=5MB +stderr_logfile=/var/log/sam/api-queue-worker-error.log +stderr_logfile_maxbytes=5MB +``` + +**MNG Queue Worker** (`/etc/supervisor/conf.d/sam-mng-worker.conf`): + +```ini +[program:sam-mng-worker] +command=php /home/webservice/mng/artisan queue:work database --queue=mng,default --sleep=3 --tries=1 --timeout=1800 --max-jobs=10 --max-time=3600 +process_name=%(program_name)s_%(process_num)02d +numprocs=2 +directory=/home/webservice/mng +autostart=true +autorestart=true +startsecs=5 +startretries=3 +stopwaitsecs=1830 +user=www-data +stdout_logfile=/var/log/sam/mng-queue-worker.log +stdout_logfile_maxbytes=5MB +stderr_logfile=/var/log/sam/mng-queue-worker-error.log +stderr_logfile_maxbytes=5MB +``` + +**API Scheduler** (`/etc/supervisor/conf.d/sam-api-scheduler.conf`): + +```ini +[program:sam-api-scheduler] +command=bash -c "while true; do php /home/webservice/api/artisan schedule:run --no-interaction; sleep 60; done" +process_name=%(program_name)s +numprocs=1 +directory=/home/webservice/api +autostart=true +autorestart=true +startsecs=0 +user=www-data +stdout_logfile=/var/log/sam/api-scheduler.log +stdout_logfile_maxbytes=5MB +stderr_logfile=/var/log/sam/api-scheduler-error.log +stderr_logfile_maxbytes=5MB +``` + +### 3.6 필수 패키지 설치 + +현재 Docker Dockerfile 기반으로 운영 서버에 설치할 패키지 목록: + +```bash +# PHP 8.4 + 확장 모듈 (API + MNG 공통) +apt install php8.4-fpm php8.4-mysql php8.4-zip php8.4-intl \ + php8.4-xml php8.4-soap php8.4-mbstring php8.4-curl + +# MNG 전용 (GD + LibreOffice + FFmpeg) +apt install php8.4-gd libreoffice-writer-nogui \ + fonts-nanum fonts-nanum-extra ffmpeg + +# Node.js 20 LTS (React SSR) +curl -fsSL https://deb.nodesource.com/setup_20.x | bash - +apt install nodejs + +# 기타 +apt install nginx mysql-server supervisor git unzip +``` + +--- + +## 4. Jenkins CI/CD 파이프라인 + +### 4.1 Jenkins 설치 위치 + +Jenkins는 **개발 서버(114.203.209.83)**에 설치한다. 서버 메모리 한계를 고려하여 Swap을 추가한다. + +```bash +# Swap 4GB 추가 +sudo fallocate -l 4G /swapfile +sudo chmod 600 /swapfile +sudo mkswap /swapfile +sudo swapon /swapfile +echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab + +# Jenkins 설치 +wget -q -O - https://pkg.jenkins.io/debian-stable/jenkins.io-2023.key | sudo apt-key add - +echo "deb https://pkg.jenkins.io/debian-stable binary/" | sudo tee /etc/apt/sources.list.d/jenkins.list +sudo apt update && sudo apt install jenkins +``` + +### 4.2 Gitea Webhook 연동 + +각 저장소에서 Push 이벤트 발생 시 Jenkins 빌드가 자동 트리거된다. + +| 저장소 | Gitea URL | Jenkins Job | +|--------|-----------|-------------| +| sam-api | `http://114.203.209.83:3000/SamProject/sam-api.git` | `sam-api-deploy` | +| sam-manage | `http://114.203.209.83:3000/SamProject/sam-manage.git` | `sam-mng-deploy` | +| sam-react-prod | `http://114.203.209.83:3000/SamProject/sam-react-prod.git` | `sam-react-deploy` | +| sam-sales | `http://114.203.209.83:3000/SamProject/sam-sales.git` | `sam-sales-deploy` | +| sam-docs | `http://114.203.209.83:3000/SamProject/sam-docs.git` | - (배포 없음) | + +### 4.3 브랜치 전략 + +``` +feature/* ──→ develop ──→ main/master + (자동배포) (승인 후 배포) + ↓ ↓ + 개발 서버 운영 서버 +``` + +| 브랜치 | 배포 대상 | 트리거 | 승인 | +|--------|----------|--------|------| +| `develop` | 개발 서버 | Push 자동 | 불필요 | +| `main`/`master` | 운영 서버 | PR 머지 | 팀장 승인 필수 | + +### 4.4 저장소별 Jenkinsfile + +#### sam-api 파이프라인 + +```groovy +pipeline { + agent any + + environment { + DEPLOY_SERVER = credentials('prod-server-ssh') + DEPLOY_PATH = '/home/webservice/api' + } + + stages { + stage('Checkout') { + steps { + checkout scm + } + } + + stage('Lint') { + steps { + sh 'composer install --no-interaction' + sh './vendor/bin/pint --test' + } + } + + stage('Test') { + steps { + sh 'php artisan test --parallel' + } + } + + stage('Deploy') { + when { + branch 'main' + } + steps { + sshagent(['prod-server-ssh']) { + sh """ + ssh -o StrictHostKeyChecking=no ${DEPLOY_SERVER} ' + cd ${DEPLOY_PATH} && + git pull origin main && + composer install --no-dev --optimize-autoloader && + php artisan migrate --force && + php artisan config:clear && + php artisan cache:clear && + php artisan route:cache && + php artisan view:cache && + sudo supervisorctl restart sam-api-worker:* + ' + """ + } + } + } + } + + post { + success { + slackSend channel: '#sam-deploy', + message: "API 배포 성공: ${env.BUILD_URL}" + } + failure { + slackSend channel: '#sam-alerts', + message: "API 배포 실패: ${env.BUILD_URL}" + } + } +} +``` + +#### sam-manage 파이프라인 + +```groovy +pipeline { + agent any + + environment { + DEPLOY_SERVER = credentials('prod-server-ssh') + DEPLOY_PATH = '/home/webservice/mng' + } + + stages { + stage('Checkout') { + steps { + checkout scm + } + } + + stage('Lint') { + steps { + sh 'composer install --no-interaction' + sh './vendor/bin/pint --test' + } + } + + stage('Build Assets') { + steps { + sh 'npm ci && npx tailwindcss -o public/css/app.css --minify' + } + } + + stage('Deploy') { + when { + branch 'master' + } + steps { + sshagent(['prod-server-ssh']) { + sh """ + ssh -o StrictHostKeyChecking=no ${DEPLOY_SERVER} ' + cd ${DEPLOY_PATH} && + git pull origin master && + composer install --no-dev --optimize-autoloader && + php artisan config:clear && + php artisan cache:clear && + php artisan view:cache && + sudo supervisorctl restart sam-mng-worker:* + ' + """ + } + } + } + } + + post { + success { + slackSend channel: '#sam-deploy', + message: "MNG 배포 성공: ${env.BUILD_URL}" + } + failure { + slackSend channel: '#sam-alerts', + message: "MNG 배포 실패: ${env.BUILD_URL}" + } + } +} +``` + +> **참고**: MNG는 마이그레이션을 실행하지 않는다. 모든 마이그레이션은 API에서만 실행한다. + +#### sam-react-prod 파이프라인 + +```groovy +pipeline { + agent any + + environment { + DEPLOY_SERVER = credentials('prod-server-ssh') + DEPLOY_PATH = '/home/webservice/react' + BUILD_FILE = 'next-standalone.tar.gz' + } + + stages { + stage('Checkout') { + steps { + checkout scm + } + } + + stage('Install') { + steps { + sh 'npm ci' + } + } + + stage('Lint') { + steps { + sh 'npm run lint' + } + } + + stage('Build') { + steps { + sh ''' + # .env.local 백업 (.env.production 으로 빌드) + [ -f .env.local ] && mv .env.local .env.local.bak + + npm run build + + # .env.local 복원 + [ -f .env.local.bak ] && mv .env.local.bak .env.local + + # standalone 빌드 확인 + test -f .next/standalone/server.js + ''' + } + } + + stage('Package') { + steps { + sh """ + rm -f ${BUILD_FILE} + COPYFILE_DISABLE=1 tar -czf ${BUILD_FILE} \ + .next/standalone \ + .next/static \ + public + """ + } + } + + stage('Deploy') { + when { + branch 'master' + } + steps { + sshagent(['prod-server-ssh']) { + sh """ + scp ${BUILD_FILE} ${DEPLOY_SERVER}:${DEPLOY_PATH}/ + ssh -o StrictHostKeyChecking=no ${DEPLOY_SERVER} ' + cd ${DEPLOY_PATH} && + lsof -ti:3000 | xargs kill 2>/dev/null || true && + sleep 2 && + rm -rf .next.bak && + mv .next .next.bak 2>/dev/null || true && + tar xzf ${BUILD_FILE} && + cp -r .next/static .next/standalone/.next/static && + cp -r public .next/standalone/public && + cp .env.production .next/standalone/.env.production 2>/dev/null || true && + cd .next/standalone && + PORT=3000 HOSTNAME=0.0.0.0 nohup node server.js > /tmp/sam-react.log 2>&1 & && + sleep 3 && + cd ${DEPLOY_PATH} && + rm -f ${BUILD_FILE} && + rm -rf .next.bak + ' + """ + } + } + } + } + + post { + success { + slackSend channel: '#sam-deploy', + message: "React 배포 성공: ${env.BUILD_URL}" + } + failure { + slackSend channel: '#sam-alerts', + message: "React 배포 실패: ${env.BUILD_URL}" + } + } +} +``` + +> **경고: React 빌드는 Jenkins 서버(Swap 추가 후)에서 수행한다. Jenkins 서버에서도 메모리 부족 시 로컬(WSL)에서 빌드 후 `deploy.sh`로 배포한다.** + +#### sam-sales 파이프라인 (간소화) + +```groovy +pipeline { + agent any + + stages { + stage('Deploy') { + when { + branch 'main' + } + steps { + sshagent(['prod-server-ssh']) { + sh """ + ssh -o StrictHostKeyChecking=no ${DEPLOY_SERVER} ' + cd /home/webservice/sales && + git pull origin main && + composer install --no-dev --optimize-autoloader && + php artisan config:clear && + php artisan cache:clear + ' + """ + } + } + } + } +} +``` + +--- + +## 5. 데이터베이스 전략 + +### 5.1 개발/운영 DB 물리 분리 + +| 항목 | 개발 DB | 운영 DB | +|------|---------|---------| +| **DB명** | `samdb` | `sam_prod` | +| **위치** | 개발 서버 (114.203.209.83) | 운영 서버 | +| **접속** | `samuser`/`sampass` | 별도 운영 계정 | +| **용도** | 개발/테스트 | 정식 서비스 | + +### 5.2 마이그레이션 규칙 + +> **경고: 모든 마이그레이션은 API 프로젝트(`/home/webservice/api`)에서만 실행한다. MNG에서 마이그레이션 실행 금지.** + +```bash +# 운영 서버 마이그레이션 (API에서만) +cd /home/webservice/api +php artisan migrate --force +``` + +### 5.3 초기 데이터 마이그레이션 절차 + +```bash +# 1. 개발 DB 덤프 (구조 + 필수 데이터) +mysqldump -u samuser -p samdb \ + --single-transaction \ + --routines \ + --triggers \ + --add-drop-table \ + > sam_initial_dump.sql + +# 2. 운영 서버로 전송 +scp sam_initial_dump.sql user@prod-server:/tmp/ + +# 3. 운영 DB에 복원 +mysql -u sam_prod_user -p sam_prod < /tmp/sam_initial_dump.sql + +# 4. 운영 전용 설정 적용 +mysql -u sam_prod_user -p sam_prod << 'EOF' +-- 바로빌 운영 모드 전환 +UPDATE barobill_configs SET is_active = 0 WHERE environment = 'test'; +UPDATE barobill_configs SET is_active = 1 WHERE environment = 'production'; +UPDATE barobill_members SET server_mode = 'production'; + +-- 테스트 데이터 정리 (필요 시) +-- DELETE FROM ... WHERE is_test = 1; +EOF +``` + +### 5.4 백업 체계 + +| 항목 | 주기 | 보관 기간 | 방법 | +|------|------|----------|------| +| **전체 백업** | 매일 03:00 | 30일 | `mysqldump --single-transaction` | +| **증분 백업** | 매 6시간 | 7일 | `mysqlbinlog` | +| **배포 전 스냅샷** | 배포 시 | 다음 배포까지 | Jenkins 파이프라인 내 자동 실행 | + +**자동 백업 스크립트** (`/etc/cron.d/sam-backup`): + +```bash +# 매일 03:00 전체 백업 +0 3 * * * root /home/webservice/scripts/db-backup.sh >> /var/log/sam/backup.log 2>&1 +``` + +```bash +#!/bin/bash +# /home/webservice/scripts/db-backup.sh +BACKUP_DIR="/home/webservice/backups/db" +DATE=$(date +%Y%m%d_%H%M%S) +KEEP_DAYS=30 + +mkdir -p ${BACKUP_DIR} + +mysqldump -u sam_prod_user --single-transaction --routines --triggers sam_prod \ + | gzip > ${BACKUP_DIR}/sam_prod_${DATE}.sql.gz + +# 30일 이상 된 백업 삭제 +find ${BACKUP_DIR} -name "*.sql.gz" -mtime +${KEEP_DAYS} -delete + +# Slack 알림 +curl -X POST -H 'Content-type: application/json' \ + --data "{\"text\":\"DB 백업 완료: sam_prod_${DATE}.sql.gz\"}" \ + ${SLACK_WEBHOOK_URL} +``` + +--- + +## 6. SSL/도메인 설정 + +### 6.1 Let's Encrypt 인증서 발급 + +```bash +# Certbot 설치 +apt install certbot python3-certbot-nginx + +# 인증서 발급 (4개 도메인) +certbot --nginx -d codebridge-x.com \ + -d api.codebridge-x.com \ + -d mng.codebridge-x.com \ + -d sales.codebridge-x.com + +# 자동 갱신 확인 +certbot renew --dry-run + +# 자동 갱신 cron (이미 certbot이 자동 설정) +# /etc/cron.d/certbot +``` + +### 6.2 Nginx 운영 설정 + +현재 Docker Nginx 설정(`docker/nginx/nginx.conf`)을 기반으로 운영 서버용으로 변환한다. + +핵심 변경 사항: + +| 항목 | 개발 (Docker) | 운영 (Bare-metal) | +|------|--------------|------------------| +| upstream | `proxy_pass http://react:3000` | `proxy_pass http://127.0.0.1:3000` | +| PHP-FPM | `fastcgi_pass api:9000` | `fastcgi_pass unix:/run/php/php8.4-fpm-api.sock` | +| SSL | 자체 서명 | Let's Encrypt | +| 도메인 | `*.sam.kr` | `*.codebridge-x.com` | + +**보안 헤더** (개발 서버 Sales 설정 기반): + +```nginx +# 공통 보안 헤더 +add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; +add_header X-Frame-Options "SAMEORIGIN" always; +add_header X-Content-Type-Options "nosniff" always; +add_header X-XSS-Protection "1; mode=block" always; + +# 보안: 악의적 경로 패턴 차단 +if ($request_uri ~* "(\.\.\/|\.\.\\|etc\/passwd|\.env|\.git|\.htaccess|\.sql|@fs\/)") { + return 403; +} + +# 보안: 의심스러운 User-Agent 차단 +if ($http_user_agent ~* "(sqlmap|nikto|nmap|masscan|metasploit|nessus)") { + return 403; +} +``` + +--- + +## 7. 모니터링 및 로깅 + +### 7.1 로그 집중화 + +``` +/var/log/sam/ +├── api-laravel.log # API Laravel 로그 (심볼릭 링크) +├── mng-laravel.log # MNG Laravel 로그 (심볼릭 링크) +├── api-queue-worker.log # API Queue Worker +├── api-queue-worker-error.log +├── mng-queue-worker.log # MNG Queue Worker +├── mng-queue-worker-error.log +├── api-scheduler.log # API Scheduler +├── react.log # React SSR 로그 +├── backup.log # DB 백업 로그 +└── healthcheck.log # 헬스체크 로그 +``` + +```bash +# 심볼릭 링크 설정 +ln -sf /home/webservice/api/storage/logs/laravel.log /var/log/sam/api-laravel.log +ln -sf /home/webservice/mng/storage/logs/laravel.log /var/log/sam/mng-laravel.log +``` + +### 7.2 헬스체크 스크립트 + +**5분 주기 실행** (`/etc/cron.d/sam-healthcheck`): + +```bash +*/5 * * * * root /home/webservice/scripts/healthcheck.sh >> /var/log/sam/healthcheck.log 2>&1 +``` + +```bash +#!/bin/bash +# /home/webservice/scripts/healthcheck.sh + +SLACK_WEBHOOK="${SLACK_WEBHOOK_URL}" +SERVICES=( + "https://codebridge-x.com|React" + "https://api.codebridge-x.com/up|API" + "https://mng.codebridge-x.com|MNG" + "https://sales.codebridge-x.com|Sales" +) + +for service in "${SERVICES[@]}"; do + IFS='|' read -r url name <<< "$service" + HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 "$url") + + if [ "$HTTP_CODE" -ne 200 ] && [ "$HTTP_CODE" -ne 302 ]; then + echo "[$(date)] ALERT: ${name} DOWN (HTTP ${HTTP_CODE})" + curl -X POST -H 'Content-type: application/json' \ + --data "{\"text\":\"${name} 서비스 다운! HTTP ${HTTP_CODE} - ${url}\"}" \ + "$SLACK_WEBHOOK" + fi +done + +# MySQL 체크 +if ! mysqladmin ping -u sam_prod_user --silent 2>/dev/null; then + echo "[$(date)] ALERT: MySQL DOWN" + curl -X POST -H 'Content-type: application/json' \ + --data '{"text":"MySQL 서비스 다운!"}' \ + "$SLACK_WEBHOOK" +fi + +# 디스크 사용량 체크 (90% 이상 경고) +DISK_USAGE=$(df / | tail -1 | awk '{print $5}' | tr -d '%') +if [ "$DISK_USAGE" -ge 90 ]; then + curl -X POST -H 'Content-type: application/json' \ + --data "{\"text\":\"디스크 사용량 경고: ${DISK_USAGE}%\"}" \ + "$SLACK_WEBHOOK" +fi +``` + +### 7.3 Slack 채널 구성 + +| 채널 | 용도 | 알림 내용 | +|------|------|----------| +| `#sam-deploy` | 배포 알림 | 배포 성공/실패 결과 | +| `#sam-alerts` | 장애 알림 | 서비스 다운, 디스크 부족, DB 연결 실패 | +| `#sam-errors` | 에러 로그 | Laravel 500 에러, Queue 실패 | + +--- + +## 8. 롤백 전략 + +### 8.1 API/MNG 롤백 + +```bash +# 1. 이전 커밋으로 코드 복원 +cd /home/webservice/api +git log --oneline -5 # 이전 커밋 확인 +git checkout <이전_커밋_해시> + +# 2. 의존성 복원 +composer install --no-dev --optimize-autoloader + +# 3. 캐시 초기화 +php artisan config:clear +php artisan cache:clear +php artisan route:cache + +# 4. Queue Worker 재시작 +sudo supervisorctl restart sam-api-worker:* +``` + +### 8.2 React 롤백 + +```bash +# .next.bak 이 남아있는 경우 (배포 직후) +cd /home/webservice/react +lsof -ti:3000 | xargs kill 2>/dev/null || true +rm -rf .next +mv .next.bak .next +cd .next/standalone +PORT=3000 HOSTNAME=0.0.0.0 nohup node server.js > /tmp/sam-react.log 2>&1 & +``` + +### 8.3 DB 롤백 + +| 우선순위 | 방법 | 설명 | +|---------|------|------| +| **1순위** | 코드 롤백 | 마이그레이션 문제가 아니면 코드만 롤백 | +| **2순위** | `migrate:rollback` | 마지막 마이그레이션 배치 되돌리기 | +| **3순위** | 스냅샷 복원 | 배포 전 자동 스냅샷에서 복원 | + +```bash +# 마이그레이션 롤백 +cd /home/webservice/api +php artisan migrate:rollback --step=1 + +# 스냅샷 복원 (최후의 수단) +mysql -u sam_prod_user -p sam_prod < /home/webservice/backups/db/pre-deploy-snapshot.sql.gz +``` + +--- + +## 9. 보안 강화 + +### 9.1 방화벽 (UFW) + +```bash +# 기본 정책 +ufw default deny incoming +ufw default allow outgoing + +# 허용 포트 +ufw allow 22/tcp # SSH +ufw allow 80/tcp # HTTP +ufw allow 443/tcp # HTTPS + +# MySQL은 localhost만 (외부 차단) +# 기본 deny에 의해 자동 차단됨 + +# 활성화 +ufw enable +``` + +### 9.2 SSH 보안 + +```bash +# /etc/ssh/sshd_config +PermitRootLogin no +PasswordAuthentication no +PubkeyAuthentication yes +MaxAuthTries 3 +AllowUsers deploy +``` + +### 9.3 fail2ban + +```bash +apt install fail2ban + +# /etc/fail2ban/jail.local +[sshd] +enabled = true +port = 22 +maxretry = 5 +bantime = 3600 + +[nginx-http-auth] +enabled = true + +[nginx-limit-req] +enabled = true +``` + +### 9.4 운영 .env 관리 규칙 + +``` +❌ .env 파일을 Git에 커밋 금지 +❌ .env 파일을 Slack/메신저로 공유 금지 +✅ 서버에서 직접 편집 (vi /home/webservice/api/.env) +✅ 변경 시 팀 채널에 "어떤 키를 변경했는지"만 공유 +``` + +### 9.5 보안 체크리스트 + +| # | 항목 | 확인 | +|---|------|------| +| 1 | `APP_DEBUG=false` | [ ] | +| 2 | `APP_ENV=production` | [ ] | +| 3 | `APP_KEY` 운영 전용 키 생성 | [ ] | +| 4 | DB 비밀번호 강력한 값으로 변경 | [ ] | +| 5 | MySQL 외부 접속 차단 (bind-address=127.0.0.1) | [ ] | +| 6 | UFW 방화벽 활성화 | [ ] | +| 7 | SSH 키 인증만 허용 (비밀번호 금지) | [ ] | +| 8 | fail2ban 설치 및 활성화 | [ ] | +| 9 | Nginx 보안 헤더 적용 (HSTS, X-Frame 등) | [ ] | +| 10 | Nginx 악의적 경로/UA 차단 규칙 적용 | [ ] | +| 11 | SSL 인증서 발급 및 자동 갱신 설정 | [ ] | +| 12 | `.env` 파일 권한 600 설정 | [ ] | +| 13 | `storage/`, `bootstrap/cache/` 권한 확인 | [ ] | +| 14 | phpMyAdmin 운영 서버에 설치하지 않음 | [ ] | +| 15 | Sanctum 토큰 만료 시간 설정 확인 | [ ] | +| 16 | `LOG_SLACK_WEBHOOK_URL` 설정 (에러 알림) | [ ] | + +--- + +## 10. 단계별 마이그레이션 체크리스트 + +### 10.1 Phase 1: 인프라 구축 (1주) + +| # | 작업 | 담당 | 확인 | +|---|------|------|------| +| 1 | 운영 서버 호스팅 계약 및 OS 설치 | 팀장 | [ ] | +| 2 | 기본 패키지 설치 (Nginx, PHP 8.4, MySQL 8.0, Node.js 20) | 팀장 | [ ] | +| 3 | PHP 확장 모듈 설치 (zip, intl, xml, soap, gd 등) | 팀장 | [ ] | +| 4 | LibreOffice, FFmpeg 설치 (MNG용) | 팀장 | [ ] | +| 5 | Supervisor 설치 및 설정 | 팀장 | [ ] | +| 6 | MySQL `sam_prod` 데이터베이스 생성 | 팀장 | [ ] | +| 7 | MySQL 운영 계정 생성 (외부 접속 차단) | 팀장 | [ ] | +| 8 | UFW 방화벽 설정 (22, 80, 443만 허용) | 팀장 | [ ] | +| 9 | SSH 키 인증 설정 (비밀번호 로그인 차단) | 팀장 | [ ] | +| 10 | fail2ban 설치 | 팀장 | [ ] | +| 11 | DNS 레코드 추가 (A 레코드 4개) | 팀장 | [ ] | +| 12 | Let's Encrypt SSL 인증서 발급 | 팀장 | [ ] | +| 13 | Nginx 운영 설정 배포 (4개 도메인) | 팀장 | [ ] | +| 14 | PHP-FPM 3개 풀 설정 (api, mng, sales) | 팀장 | [ ] | +| 15 | 로그 디렉토리 생성 (`/var/log/sam/`) | 팀장 | [ ] | +| 16 | 백업 스크립트 설치 및 cron 등록 | 팀장 | [ ] | + +### 10.2 Phase 2: CI/CD 파이프라인 구축 (1주) + +| # | 작업 | 담당 | 확인 | +|---|------|------|------| +| 1 | 개발 서버 Swap 4GB 추가 | 팀장 | [ ] | +| 2 | Jenkins 설치 및 초기 설정 | 팀장 | [ ] | +| 3 | Gitea → Jenkins Webhook 연동 (4개 저장소) | 팀장 | [ ] | +| 4 | Jenkins SSH Credential 등록 (운영 서버) | 팀장 | [ ] | +| 5 | sam-api Jenkinsfile 작성 및 테스트 | 팀장 | [ ] | +| 6 | sam-manage Jenkinsfile 작성 및 테스트 | 팀장 | [ ] | +| 7 | sam-react-prod Jenkinsfile 작성 및 테스트 | 팀장 | [ ] | +| 8 | sam-sales Jenkinsfile 작성 및 테스트 | 팀장 | [ ] | +| 9 | Slack Webhook 연동 (배포/장애 알림) | 팀장 | [ ] | +| 10 | 헬스체크 스크립트 설치 및 cron 등록 | 팀장 | [ ] | +| 11 | develop → 개발 서버 자동 배포 테스트 | 팀장 | [ ] | + +### 10.3 Phase 3: 스테이징 배포 (3일) + +| # | 작업 | 담당 | 확인 | +|---|------|------|------| +| 1 | 프로젝트 소스 코드 클론 (4개 저장소) | 팀장 | [ ] | +| 2 | 운영 `.env` 파일 생성 (API, MNG, Sales, React) | 팀장 | [ ] | +| 3 | `composer install` (API, MNG, Sales) | 팀장 | [ ] | +| 4 | 개발 DB → 운영 DB 데이터 마이그레이션 | 팀장 | [ ] | +| 5 | `php artisan migrate --force` (API에서만) | 팀장 | [ ] | +| 6 | 바로빌 운영 설정 전환 (DB + .env) | 팀장 | [ ] | +| 7 | Google 서비스 어카운트 파일 배치 | 팀장 | [ ] | +| 8 | React 빌드 및 배포 (standalone) | 팀장 | [ ] | +| 9 | Supervisor 프로세스 시작 | 팀장 | [ ] | +| 10 | 전체 서비스 기동 확인 | 전원 | [ ] | +| 11 | 기능 테스트 (로그인, 견적, 세금계산서 등) | 전원 | [ ] | +| 12 | 외부 서비스 연동 확인 (바로빌, FCM, Gemini) | 전원 | [ ] | +| 13 | 성능 기본 테스트 (응답 속도 < 500ms) | 팀장 | [ ] | + +### 10.4 Phase 4: 운영 전환 (1일) + +| # | 작업 | 담당 | 확인 | +|---|------|------|------| +| 1 | 전환 일시 공지 (사용자/팀) | 팀장 | [ ] | +| 2 | 개발 DB 최종 덤프 → 운영 DB 동기화 | 팀장 | [ ] | +| 3 | DNS 최종 전환 (운영 서버 IP로 변경) | 팀장 | [ ] | +| 4 | SSL 인증서 최종 확인 | 팀장 | [ ] | +| 5 | 운영 환경 최종 기동 | 팀장 | [ ] | +| 6 | Jenkins 운영 파이프라인 활성화 | 팀장 | [ ] | +| 7 | 모니터링/헬스체크 최종 확인 | 팀장 | [ ] | +| 8 | 사용자 접속 안내 (URL 변경) | 팀장 | [ ] | +| 9 | 2시간 집중 모니터링 | 전원 | [ ] | +| 10 | 전환 완료 공지 | 팀장 | [ ] | + +**운영 전환 후 검증 체크리스트:** + +``` +□ React 메인 페이지 로딩 확인 +□ 로그인/로그아웃 정상 +□ MNG 관리자 화면 접속 확인 +□ API 엔드포인트 응답 확인 (/up) +□ 파일 업로드/다운로드 정상 +□ 바로빌 세금계산서 발행 테스트 +□ FCM 푸시 알림 전송 확인 +□ Queue Worker 정상 동작 (failed_jobs 확인) +□ Scheduler 정상 동작 +□ Slack 알림 수신 확인 +``` + +--- + +## 11. 일정 요약 + +``` +Week 1 (02/22~02/28) Week 2 (03/01~03/07) Week 3 (03/08~03/14) + │ │ │ + Phase 1 Phase 2 Phase 3 → Phase 4 + 인프라 구축 CI/CD 구축 스테이징 운영 전환 + ├─ 서버 셋업 ├─ Jenkins 설치 ├─ 데이터 ├─ DNS 전환 + ├─ 패키지 설치 ├─ Webhook 연동 │ 마이그 ├─ 모니터링 + ├─ 방화벽/SSL ├─ 파이프라인 작성 │ 레이션 └─ 전환 공지 + └─ Nginx 설정 └─ Slack 연동 └─ 기능 + 테스트 +``` + +> **참고**: MS3 목표일(2026-02-28)은 Phase 1 완료 시점이다. 실제 운영 전환은 Week 3에 수행한다. 필요 시 Phase 1~2를 병렬로 진행하여 일정을 단축할 수 있다. + +--- + +## 12. 위험 요소 및 완화 방안 + +| # | 위험 요소 | 영향도 | 발생 확률 | 완화 방안 | +|---|----------|--------|----------|----------| +| 1 | **RAM 부족으로 React 빌드 실패** | 🔴 높음 | 중간 | Jenkins 서버 Swap 추가, 폴백으로 로컬 빌드 사용 | +| 2 | **DNS 전파 지연** | 🟡 중간 | 높음 | TTL 사전 단축 (300초), 전환 24시간 전 TTL 변경 | +| 3 | **바로빌 운영 전환 실패** | 🔴 높음 | 낮음 | DB + .env 동시 전환, 즉시 롤백 절차 준비 (`production-env-sync.md` 참조) | +| 4 | **운영 서버 호스팅 지연** | 🔴 높음 | 낮음 | 대안 호스팅 사전 조사, 최소 1주 여유 확보 | +| 5 | **Jenkins 메모리 부족** | 🟡 중간 | 중간 | Swap 4GB 추가, 동시 빌드 제한 (1개), 빌드 후 workspace 정리 | +| 6 | **마이그레이션 충돌** | 🟡 중간 | 낮음 | 배포 전 DB 스냅샷, `migrate:rollback` 준비 | +| 7 | **Google 서비스 어카운트 경로 불일치** | 🟡 중간 | 중간 | 운영 서버 경로 통일, .env 교차 검증 | + +--- + +## 관련 문서 + +- [런칭 로드맵](../guides/project-launch-roadmap.md) +- [.env 동기화 절차](../guides/production-env-sync.md) +- [Docker 환경 스펙](../specs/docker-setup.md) +- [보안 정책](../architecture/security-policy.md) +- [시스템 아키텍처](../architecture/system-overview.md) + +--- + +**최종 업데이트**: 2026-02-22 diff --git a/sam/docs/plans/sound-logo-generator-plan.md b/plans/sound-logo-generator-plan.md similarity index 100% rename from sam/docs/plans/sound-logo-generator-plan.md rename to plans/sound-logo-generator-plan.md diff --git a/projects/index_projects.md b/projects/index_projects.md index b08bbb9..7ce5106 100644 --- a/projects/index_projects.md +++ b/projects/index_projects.md @@ -1,7 +1,7 @@ # 프로젝트 문서 인덱스 > SAM 시스템 개발 프로젝트별 문서 모음 -> **최종 업데이트**: 2026-02-12 +> **최종 업데이트**: 2026-03-08 --- @@ -18,6 +18,8 @@ | [auto-login](#auto-login---자동-로그인) | ⚪ 대기 | 자동 로그인 기능 | | [migration-5130-mng](#migration-5130-mng---5130--mng-마이그레이션) | 🟡 진행중 | 5130 → mng 통합 마이그레이션 | | [e-sign](#e-sign---전자계약-서명) | 🟢 v1.0 구현 완료 | 전자계약 서명 솔루션 (SAM E-Sign) | +| [org-chart](#org-chart---조직도-관리) | 🟢 v1.0 구현 완료 | 트리형 조직도 관리 (드래그앤드롭, 숨기기) | +| [planning-design](#planning-design---기획디자인-스토리보드-에디터) | 🟢 v1.2 운영 중 | 브라우저 블록 에디터 (Notion/Figma 스타일) | --- @@ -204,6 +206,53 @@ --- +### org-chart - 조직도 관리 + +**경로**: `docs/projects/org-chart/` +**상태**: 🟢 v1.0 구현 완료 (2026-03-06) +**목표**: 테넌트별 조직 구조를 시각적으로 관리하는 트리형 조직도 + +**핵심 문서**: +- [README.md](./org-chart/README.md) - 기술 문서 (아키텍처, API, DB, 프론트엔드 상세) + +**구현 범위**: + +| 영역 | 수량 | +|------|------| +| MNG 컨트롤러 메서드 | 7개 (RdController) | +| API 엔드포인트 | 6개 (조회, 배치, 해제, 순서변경, 숨기기) | +| DB 마이그레이션 | 1개 (departments.options JSON 추가) | +| 뷰 | 1개 (Alpine.js + SortableJS) | + +**기술 스택**: Alpine.js + SortableJS + 수동 DOM 렌더링 (x-for 미사용) + +--- + +### planning-design - 기획디자인 스토리보드 에디터 + +**경로**: `docs/projects/planning-design/` +**상태**: 🟢 v1.2 운영 중 (고도화 진행중) +**목표**: 브라우저 내 Notion/Figma 스타일 블록 에디터로 ERP 화면 기획서 작성 + +**핵심 문서**: +- [README.md](./planning-design/README.md) - 프로젝트 개요 및 구현 이력 + +**구현 범위**: + +| 영역 | 수량 | +|------|------| +| 블록 유형 | 15종 (텍스트/레이아웃/UI모형/미디어/체크리스트) | +| 편집 기능 | 올가미 선택, Undo/Redo, 복사/잘라내기, 서식 | +| 서식 시스템 | 글자색/배경색/크기/굵기/기울임/정렬/z-index | +| 작업 영역 | 사이드바/Description 접기/펼치기, 캔버스 폭 자동 확장 | +| 출력 | HTML 내보내기 + A4 인쇄 (좌표 기반 WYSIWYG) | + +**기술 스택**: Alpine.js + localStorage (서버 API 없음) + +**기술 스펙**: [features/rd/planning-design.md](../features/rd/planning-design.md) + +--- + ## 디렉토리 구조 ``` @@ -232,7 +281,9 @@ docs/projects/ ├── mng-mobile-responsive/ # 모바일 반응형 ├── auto-login/ # 자동 로그인 ├── migration-5130-mng/ # 5130→mng 마이그레이션 -└── e-sign/ # 전자계약 서명 (SAM E-Sign) +├── e-sign/ # 전자계약 서명 (SAM E-Sign) +├── org-chart/ # 조직도 관리 +└── planning-design/ # 기획디자인 스토리보드 에디터 ``` --- @@ -240,7 +291,7 @@ docs/projects/ ## 관련 문서 - [docs/INDEX.md](../INDEX.md) - 전체 문서 인덱스 -- [docs/dev_plans/index_plans.md](../plans/index_plans.md) - 기획 문서 인덱스 +- [docs/plans/index_plans.md](../plans/index_plans.md) - 기획 문서 인덱스 - [docs/guides/PROJECT_DEVELOPMENT_POLICY.md](../guides/PROJECT_DEVELOPMENT_POLICY.md) - 공통 개발 정책 - [CURRENT_WORKS.md](../../CURRENT_WORKS.md) - 현재 작업 diff --git a/sam/docs/projects/org-chart/README.md b/projects/org-chart/README.md similarity index 100% rename from sam/docs/projects/org-chart/README.md rename to projects/org-chart/README.md diff --git a/sam/docs/projects/planning-design/README.md b/projects/planning-design/README.md similarity index 100% rename from sam/docs/projects/planning-design/README.md rename to projects/planning-design/README.md diff --git a/rules/billing-policy.md b/rules/billing-policy.md index 5c2dce0..56a5966 100644 --- a/rules/billing-policy.md +++ b/rules/billing-policy.md @@ -54,8 +54,7 @@ SAM 프로젝트의 과금정책 중 **본사 지출 원가**, **마진 구조** |--------|--------|------| | 계좌조회 | 10,000원 | 고객 부담 | | 카드내역 | 10,000원 | 고객 부담 | -| 홈택스 매입 | 33,000원 (VAT 포함) | 코드브릿지엑스 지원 → 본사 부담 (무료) | -| 홈택스 매출 | 33,000원 (VAT 포함) | 코드브릿지엑스 지원 → 본사 부담 (무료) | +| 홈택스 매입/매출 | 33,000원 (VAT 포함) | 코드브릿지엑스 지원 → 본사 부담 (무료) | > **참고**: 계좌조회/카드내역 월정액은 고객에게 전가한다. 홈택스는 본사가 흡수한다. diff --git a/rules/customer-pricing.md b/rules/customer-pricing.md index e233fa6..60a9ff2 100644 --- a/rules/customer-pricing.md +++ b/rules/customer-pricing.md @@ -92,7 +92,7 @@ SAM 서비스 도입 시 고객에게 안내하는 요금 체계를 정리한다 |--------|---------|---------|---------|----------| | 계좌조회 | 월정액 10,000원 | 1계좌 | 추가 1계좌당 10,000원 | 고객 | | 카드내역 | 월정액 10,000원 | 5장 | 추가 1장당 5,000원 | 고객 | -| 홈택스 매입/매출 | 월 33,000원 × 2 | - | - | 본사 (무료) | +| 홈택스 매입/매출 | 월 33,000원 | - | - | 본사 (무료) | | 세금계산서 발행 | 건별 | 100건 | 추가 50건당 5,000원 | 고객 | > **과금 계산 예시**: diff --git a/rules/slides/billing-policy/slide-03.html b/rules/slides/billing-policy/slide-03.html index c32f344..b1e7126 100644 --- a/rules/slides/billing-policy/slide-03.html +++ b/rules/slides/billing-policy/slide-03.html @@ -50,18 +50,12 @@

                      10,000원

                      고객 부담 (전가)

                      - -
                      -

                      홈택스 매입

                      + +
                      +

                      홈택스 매입/매출

                      33,000원

                      본사 흡수 (코드브릿지엑스 → 무료)

                      - -
                      -

                      홈택스 매출

                      -

                      33,000원

                      -

                      본사 흡수 (무료)

                      -
                      diff --git a/rules/slides/customer-pricing/slide-06.html b/rules/slides/customer-pricing/slide-06.html index d50a3e8..e3c9461 100644 --- a/rules/slides/customer-pricing/slide-06.html +++ b/rules/slides/customer-pricing/slide-06.html @@ -67,7 +67,7 @@

                      홈택스 매입/매출

                      -

                      월 33,000원 x 2

                      +

                      월 33,000원

                      -

                      -

                      diff --git a/sam/docs/rules/slides/usage-plan/SAM_활용방안.pptx b/rules/slides/usage-plan/SAM_활용방안.pptx similarity index 100% rename from sam/docs/rules/slides/usage-plan/SAM_활용방안.pptx rename to rules/slides/usage-plan/SAM_활용방안.pptx diff --git a/sam/docs/rules/slides/usage-plan/convert.cjs b/rules/slides/usage-plan/convert.cjs similarity index 100% rename from sam/docs/rules/slides/usage-plan/convert.cjs rename to rules/slides/usage-plan/convert.cjs diff --git a/sam/docs/rules/slides/usage-plan/slide-01.html b/rules/slides/usage-plan/slide-01.html similarity index 100% rename from sam/docs/rules/slides/usage-plan/slide-01.html rename to rules/slides/usage-plan/slide-01.html diff --git a/sam/docs/rules/slides/usage-plan/slide-02.html b/rules/slides/usage-plan/slide-02.html similarity index 100% rename from sam/docs/rules/slides/usage-plan/slide-02.html rename to rules/slides/usage-plan/slide-02.html diff --git a/sam/docs/rules/slides/usage-plan/slide-03.html b/rules/slides/usage-plan/slide-03.html similarity index 100% rename from sam/docs/rules/slides/usage-plan/slide-03.html rename to rules/slides/usage-plan/slide-03.html diff --git a/sam/docs/rules/slides/usage-plan/slide-04.html b/rules/slides/usage-plan/slide-04.html similarity index 100% rename from sam/docs/rules/slides/usage-plan/slide-04.html rename to rules/slides/usage-plan/slide-04.html diff --git a/sam/docs/rules/slides/usage-plan/slide-05.html b/rules/slides/usage-plan/slide-05.html similarity index 100% rename from sam/docs/rules/slides/usage-plan/slide-05.html rename to rules/slides/usage-plan/slide-05.html diff --git a/sam/docs/rules/slides/usage-plan/slide-06.html b/rules/slides/usage-plan/slide-06.html similarity index 100% rename from sam/docs/rules/slides/usage-plan/slide-06.html rename to rules/slides/usage-plan/slide-06.html diff --git a/sam/docs/rules/slides/usage-plan/slide-07.html b/rules/slides/usage-plan/slide-07.html similarity index 100% rename from sam/docs/rules/slides/usage-plan/slide-07.html rename to rules/slides/usage-plan/slide-07.html diff --git a/sam/coocon/쿠콘_나이스평가정보_API_개발가이드.md b/sam/coocon/쿠콘_나이스평가정보_API_개발가이드.md deleted file mode 100644 index 1c354a6..0000000 --- a/sam/coocon/쿠콘_나이스평가정보_API_개발가이드.md +++ /dev/null @@ -1,685 +0,0 @@ -# 개발가이드 나이스평가정보연계 API - -> Ver 1.0.0 -> 최종수정일: 2024-06-28 - ---- - -## 개정 내역 - -| 개정버전 | 개정일자 | 개정내용 | -|--------|----------|--------| -| 1.0.0 | 2024.06.28 | 초안작성 | - ---- - -## 목차 - -1. [개요](#1-개요) -2. [용어정의](#2-용어정의) -3. [개발절차](#3-개발절차) -4. [API 인증키](#4-api-인증키) -5. [개발가이드](#5-개발가이드) -6. [개발 유의사항](#6-개발-유의사항) -7. [결과코드](#참조-1-결과코드) - ---- - -## 1. 개요 - -### 1.1 설명 - -나이스평가 정보 연동하여 업무를 처리합니다. - -### 1.2 기본정보 - -| 항목 | 값 | -|------|-----| -| Protocol | HTTPS | -| 데이터형식 | JSON | -| Network | 인터넷망 | -| 데이터 제공방식 | 실시간 | -| Content-Type | application/json | - -### 1.3 API 목록 - -| API 명 | 환경 | URL | 설명 | -|--------|------|-----|------| -| [기업]지표-주요경영지표 (OA07) | 개발 | https://dev2.coocon.co.kr:8443/sol/gateway/oapi_relay.jsp | 업무구분 API_ID(OA07) | -| | 운영 | https://sgw.coocon.co.kr/sol/gateway/oapi_relay.jsp | | -| [기업]개요-기본정보 (OA08) | 개발 | https://dev2.coocon.co.kr:8443/sol/gateway/oapi_relay.jsp | 업무구분 API_ID(OA08) | -| | 운영 | https://sgw.coocon.co.kr/sol/gateway/oapi_relay.jsp | | -| [기업]검색-통합기업검색 (OA09) | 개발 | https://dev2.coocon.co.kr:8443/sol/gateway/oapi_relay.jsp | 업무구분 API_ID(OA09) | -| | 운영 | https://sgw.coocon.co.kr/sol/gateway/oapi_relay.jsp | | -| [기업]등급-기업평가등급 (OA10) | 개발 | https://dev2.coocon.co.kr:8443/sol/gateway/oapi_relay.jsp | 업무구분 API_ID(OA10) | -| | 운영 | https://sgw.coocon.co.kr/sol/gateway/oapi_relay.jsp | | -| [기업]등급-WATCH 등급 (OA11) | 개발 | https://dev2.coocon.co.kr:8443/sol/gateway/oapi_relay.jsp | 업무구분 API_ID(OA11) | -| | 운영 | https://sgw.coocon.co.kr/sol/gateway/oapi_relay.jsp | | -| [기업]신용-신용요약정보 (OA12) | 개발 | https://dev2.coocon.co.kr:8443/sol/gateway/oapi_relay.jsp | 업무구분 API_ID(OA12) | -| | 운영 | https://sgw.coocon.co.kr/sol/gateway/oapi_relay.jsp | | -| [기업]신용-단기연체정보 (한국신용정보원) (OA13) | 개발 | https://dev2.coocon.co.kr:8443/sol/gateway/oapi_relay.jsp | 업무구분 API_ID(OA13) | -| | 운영 | https://sgw.coocon.co.kr/sol/gateway/oapi_relay.jsp | | -| [기업]신용-신용도 판단정보(공공정보 포함) (한국신용정보원) (OA14) | 개발 | https://dev2.coocon.co.kr:8443/sol/gateway/oapi_relay.jsp | 업무구분 API_ID(OA14) | -| | 운영 | https://sgw.coocon.co.kr/sol/gateway/oapi_relay.jsp | | -| [기업]신용-신용도-판단정보 (신용정보사) (OA15) | 개발 | https://dev2.coocon.co.kr:8443/sol/gateway/oapi_relay.jsp | 업무구분 API_ID(OA15) | -| | 운영 | https://sgw.coocon.co.kr/sol/gateway/oapi_relay.jsp | | -| [기업]신용-당좌거래정지 정보 (금융결제원) (OA16) | 개발 | https://dev2.coocon.co.kr:8443/sol/gateway/oapi_relay.jsp | 업무구분 API_ID(OA16) | -| | 운영 | https://sgw.coocon.co.kr/sol/gateway/oapi_relay.jsp | | -| [기업]신용-법정관리/워크아웃정보 (OA17) | 개발 | https://dev2.coocon.co.kr:8443/sol/gateway/oapi_relay.jsp | 업무구분 API_ID(OA17) | -| | 운영 | https://sgw.coocon.co.kr/sol/gateway/oapi_relay.jsp | | - -### 1.4 서버 접속 정보 - -> 전용선 및 VPN 이용고객은 아래 서버정보 참고 - -| 구분 | IP | PORT | -|------|-----|------| -| 개발 | #1 : 183.111.160.137 | 8443 | -| 운영 | #1 : 112.175.51.59 | 443 | - -### 1.5 API 구성/흐름도 - -``` -Application COOCON API Server OpenAPI Provider - │ │ │ - │ 1. INPUT 데이터 조립 │ │ - │ - API 인증키 │ │ - │ - 사업자정보 │ │ - │ │ │ - ├──────── 2. Request ──────────>│ │ - │ │ INPUT 데이터 수신 및 재조립 │ - │ │ [쿠콘 API GW] │ - │ │ │ - │ ├────── 3. Call ──────────────>│ - │ │ [나이스평가정보 API] │ - │ │ │ - │ │<───── 4. OUTPUT 데이터 수신 ──│ - │ │ │ - │<──────── 5. Response ─────────│ │ - │ │ │ - │ OUTPUT 데이터 추출 │ │ - │ - 결과코드/메시지 │ │ - │ - 출력값 │ │ -``` - -**흐름도 설명:** -1. Application에서 INPUT 데이터를 조립하여 [나이스 API]를 호출합니다. -2. INPUT 데이터가 API 서버로 전송되고, 쿠콘 API GW에서 INPUT 데이터를 수신합니다. -3. 쿠콘 API GW에서 수신된 INPUT 데이터를 오픈 API 포맷에 맞게 조립하고, 오픈 API 제공자에게 요청 후에 응답을 수신합니다. -4. Application에 수신받은 응답데이터를 전달합니다. -5. Application에서 필요한 데이터 추출 및 파싱을 합니다. - -### 1.6 API 인터페이스 구조 - -``` -S/W 개발사 Application 서버 COOCON API 서버 -┌─────────────────────────────────┐ ┌─────────────────┐ -│ ┌────────┐ ┌────────┐ │ │ │ -│ │ 공통 │ │ INPUT │ │ Request │ OUTPUT │ -│ │ 입력 │ -> │ │ ────────────────>│ │ -│ │ 항목 │ │ │ <────────────────│ │ -│ └────────┘ └────────┘ │ Response │ │ -└─────────────────────────────────┘ └─────────────────┘ -``` - ---- - -## 2. 용어정의 - -| 용어 | 정의 | -|------|------| -| 이용기업 | API 상품을 구매하여 Application을 개발하는 회사 (S/W 개발사) | -| Application | 이용기업에서 API를 적용하려는 Application | -| COOCON API Server | COOCON에서 제공하는 API가 실행되는 서버 | -| API 인증키 | COOCON에서 제공하는 API를 호출한 이용 기관을 인증하는 고유값. 이 때 COOCON에 등록된 이용 기관의 고유 IP에서 요청(Request)된 API 호출만 정상 처리됩니다. | - ---- - -## 3. 개발절차 - -``` -┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ -│ 1 │ │ 2 │ │ 3 │ │ 4 │ -│ API │ -> │ 개발 │ -> │ 테스트 │ -> │ 운영 │ -│ 인증키 │ │ │ │ │ │ 적용 │ -└─────────┘ └─────────┘ └─────────┘ └─────────┘ -``` - ---- - -## 4. API 인증키 - -### 4.1 발급 - -API 인증키 발급 과정은 다음과 같습니다. - -1. 구매신청 담당자에게 "API 인증키 신청서"가 이메일로 전달됩니다. -2. API 이용기업 담당자는 "API 인증키 신청서" 양식을 작성하여 이메일로 답장을 보냅니다. -3. COOCON 기술지원 담당자는 신청내용 검토 후 인증키를 이메일로 발송합니다. - -### 4.2 개발 - -1. API를 이용하여 Application 개발을 합니다. -2. 자세한 내용은 "5 개발 가이드"를 참조해 주세요. - -### 4.3 테스트 - -개발 혹은 테스트 시스템에서 API 호출 테스트를 진행합니다. - -1. 개발용 API를 이용해서 테스트해야 합니다. - -**[주의사항]** - -1. 개발 혹은 테스트 시스템에서 대량거래 혹은 부하테스트 같이 짧은 시간 내에 너무 많은 거래를 테스트하면 경우에 따라서 정보제공 기관(금융사 등)에서 테스트 거래를 차단할 수 있으니 주의하시기 바랍니다. -2. 테스트 시스템에서 확인한 금융 정보는 정보제공 기관(금융사) 시스템에 따라 정확하지 않을 수 있습니다. -3. 만일 운영 API를 이용하여 테스트를 하게 되면 수수료가 발생하니 유의하시기 바랍니다. - -### 4.4 운영적용 - -개발된 Application이 운영으로 이관되면 반드시 "운영 API 인증키"로 변경해 주어야 정상 거래가 가능합니다. - ---- - -## 5. 개발가이드 - -### 5.1 공통 입력 항목 - -| 공통 입력 ID | 공통 입력 명 | 타입 | 최대길이 | 필수여부 | 비고 | -|-------------|-------------|------|---------|---------|------| -| API_KEY | 인증키 | String | 20 | 필수 | 이용기업 인증키 값 (이용기업 별로 발급됨) | -| API_ID | 업무코드 | String | 4 | 필수 | 나이스 연동: 1.3 API 목록 참조 | -| TR_SEQ | 거래일련번호 | String | 20 | 옵션 | 거래추적 용도 번호 | - ---- - -### 5.2 [기업] 신용-신용요약정보 (OA12) - -#### [INPUT] - -| 개별 입력 ID | 개별 입력 명 | 타입 | 최대길이 | 필수여부 | 비고 | -|-------------|-------------|------|---------|---------|------| -| Companykey | 사업자정보 | String | 13 | 필수 | 조회할 기업의 사업자번호, 법인번호, 업체 코드 중 한 개만 입력 | - -#### [OUTPUT] - -| 출력 항목 | 반복여부 | 출력 항목 명 | 타입 | 최대길이 | 설명 | -|---------|---------|------------|------|---------|------| -| RSLT_CD | - | 결과코드 | String | 8 | 조회 결과 코드값, 정상 00000000 | -| RSLT_MSG | - | 결과메세지 | String | 255 | 조회 결과 메시지 | -| TR_SEQ | - | 거래일련번호 | String | 20 | 거래 추적용도 번호 | -| RSLT_DATA | - | 결과데이터 | Object | - | 나이스 결과데이터 | -| request | | 요청한 값 | Object | - | 단기연체정보 요청항목 | -| requestKey | | 요청 KEY | String | 13 | 사용자가요청한 companyKey | -| requestKeyType | | 요청한 KEY 타입 | String | 6 | BIZNO: 사업자번호, CRPNO: 법인번호, UPCHECD: 업체코드 | -| data | | 기업요약 신용정보 결과 | Object | | | -| listCount | | list 건수 | integer | | | -| creditSummaryList | | 기업신용 요약정보 | Array | | | -| shorttermOverdueCnt | ● | 단기연체정보(한국신용정보원) 건수 | integer | | | -| negativeInfoBbCnt | ● | 신용도판단정보(한국신용정보원) 건수 | integer | | | -| negativeInfoPbCnt | ● | 공공정보 건수 | integer | | | -| negativeInfoCbCnt | ● | 신용도판단정보(신용정보사) 건수 | integer | | | -| suspensionInfoCnt | ● | 당좌거래정지정보 건수 | integer | | | -| workoutCnt | ● | 법정관리/워크아웃정보 건수 | integer | | | - ---- - -### 5.3 [기업]신용-단기연체정보 (한국신용정보원) (OA13) - -#### [INPUT] - -| 개별 입력 ID | 개별 입력 명 | 타입 | 최대길이 | 필수여부 | 비고 | -|-------------|-------------|------|---------|---------|------| -| Companykey | 사업자정보 | String | 13 | 필수 | 조회대상 사업자번호, 법인번호, 업체코드 중 한개만 입력 | -| reqDate | 기준일자 | String | 8 | 옵션 | 기본값(현재날짜(YYYYMMDD) 미입력시 최신일자 기준으로 조회) | - -#### [OUTPUT] - -| 출력 항목 | 반복여부 | 출력 항목 명 | 타입 | 최대길이 | 설명 | -|---------|---------|------------|------|---------|------| -| RSLT_CD | - | 결과코드 | String | 8 | 조회 결과 코드값, 정상 00000000 | -| RSLT_MSG | - | 결과 메시지 | String | 255 | 조회 결과 메시지 | -| TR_SEQ | - | 거래일련번호 | String | 20 | 거래 추적용도 번호 | -| RSLT_DATA | - | 결과데이터 | Object | - | 나이스 결과데이터 | -| request | | 요청한 값 | Object | - | 단기연체정보 요청항목 | -| requestKey | | 요청 KEY | String | 13 | 사용자가요청한 companyKey | -| requestKeyType | | 요청 KEY 타입 | String | 6 | BIZNO: 사업자번호, CRPNO: 법인번호, UPCHECD: 업체코드 | -| reqDate | | 기준일자 | String | 8 | | -| data | | | Object | | 기업신용 단기연체 결과 | -| listCount | | 단기연체정보 건수 | Integer | | | -| creditShorttermOverdueList | | 단기연체정보 | Array | | | -| count | ● | 단기연체정보건수 | Integer | | | -| rcno | ● | 차주대상번호 | String | | | -| rcnoKind | ● | 차주대상구분 | String | | 2: 사업자등록번호, 3: 법인등록번호 | -| name | ● | 기관명 | String | | | -| reqdate | ● | 요청일자 | String | | yyyymmdd | -| basedate | ● | 기준일자 | String | | yyyymmdd | -| organnizationsList | ● | 단기연체정보 기관별 정보 | Array | | | -| orgcd | ● | 한국신용정보원 | String | | | -| orgname | ● | 한국신용정보원기관명 | String | | | -| orgnameEng | ● | 한국신용정보원기관영문명 | String | | | -| principlaOduYn | ● | 연체금액여부(계정과목코드 6909) | String | | | -| accountSubjectsList | ● | 단기연체정보 계정과목별정보 | Array | | | -| acccd | ● | 계정과목코드 | String | | | -| accname | ● | 계정과목명 | String | | | -| kindcode1 | ● | 계정과목대과목코드 | String | | | -| kind1 | ● | 계정과목대과목명 | String | | | -| kindcode2 | ● | 계정과목중과목코드 | String | | | -| kind2 | ● | 계정과목중과목명 | String | | | -| categorycd | ● | 한국신용정보원만기코드 | String | | | -| category | ● | 한국신용정보원만기코드명 | String | | | -| accamt | ● | 거래금액 | String | | | - ---- - -### 5.4 [기업]신용도 판단정보(공공정보 포함)(한국 신용정보원) (OA14) - -#### [INPUT] - -| 개별 입력 ID | 개별 입력 명 | 타입 | 최대길이 | 필수여부 | 비고 | -|-------------|-------------|------|---------|---------|------| -| Companykey | 사업자정보 | String | 13 | 필수 | 조회할 기업의 사업자번호, 법인번호, 업체코드 중 한개만 입력 | - -#### [OUTPUT] - -| 출력 항목 | 반복여부 | 출력 항목 명 | 타입 | 최대길이 | 설명 | -|---------|---------|------------|------|---------|------| -| RSLT_CD | - | 결과코드 | String | 8 | 조회 결과 코드값, 정상 00000000 | -| RSLT_MSG | - | 결과메세지 | String | 255 | 조회 결과 메시지 | -| TR_SEQ | - | 거래일련번호 | String | 20 | 거래 추적용도 번호 | -| RSLT_DATA | - | 결과데이터 | Object | - | 나이스 결과데이터 | -| request | | 요청한 값 | Object | - | 단기연체정보 요청항목 | -| requestKey | | 요청 KEY | String | 13 | 사용자가요청한 companyKey | -| requestKeyType | | 요청한 KEY 타입 | String | 6 | BIZNO: 사업자번호, CRPNO: 법인번호, UPCHECD: 업체코드 | -| data | | | Object | | 신용도판단정보 결과 | -| listCount | | list 건수 | Integer | | | -| creditNegativeInfoList | | 신용도판단정보 | Array | | | -| totaloccCnt | ● | 발생 총 건수 | integer | | | -| bbCnt | ● | 채무불이행건수 | integer | | | -| fdCnt | ● | 금용질서문란 건수 | String | | | -| pbCnt | ● | 공공기록정보건수 | integer | | | -| sbCnt | ● | 특수기록정보건수 | integer | | | -| totalrelCnt | ● | 해제 총건수 | integer | | | -| bbrelCnt | ● | 채무불이행 해제건수 | integer | | | -| fdrelCnt | ● | 금융질서문란 해제건수 | integer | | | -| pbrelCnt | ● | 공공기록정보 해제건수 | integer | | | -| sbrelCnt | ● | 특수기록정보 해제건수 | integer | | | -| negativeInfoDetailList | ● | 신용도판단정보 item | Array | | | -| typecode | ● | 유형구분코드 | String | | BB: 신용도판단정보(채무불이행), FD: 금융질서문란, PB: 공공정보, SB: 특수기록정보 | -| typename | ● | 용도판단정보 | String | | (한국 신용정보원), 신용도판단정보(공공기록정보) 등 | -| gubn | ● | 구분 | String | | | -| delayamt | ● | 연체금액 | String | | | -| causecode | ● | 등록코드 | String | | | -| causename | ● | 등록사유명 | String | | | -| causenameEng | ● | 등록사유영문명 | String | | | -| causecont | ● | 등록사유설명 | String | | | -| causedetail | ● | 등록사유상세설명 | String | | | -| regamt | ● | 등록금액 | String | | | -| regdate | ● | 등록일자 | String | | yyyymmdd | -| relscode | ● | 해제코드 | String | | | -| reldate | ● | 해재일자 | String | | yyyymmdd | -| orgname | ● | 발생기관명 | String | | | -| orgnameEng | ● | 발생기관영문명 | String | | | -| brcname | ● | 발생지점명 | String | | | -| occdate | ● | 발생일자 | String | | yyyymmdd | - ---- - -### 5.5 [기업]신용-신용도 판단정보(신용정보사) (OA15) - -#### [INPUT] - -| 개별 입력 ID | 개별 입력 명 | 타입 | 최대길이 | 필수여부 | 비고 | -|-------------|-------------|------|---------|---------|------| -| Companykey | 사업자정보 | String | 13 | 필수 | 조회할 기업의 사업자번호, 법인번호, 업체코드 중 한개만 입력 | - -#### [OUTPUT] - -| 출력 항목 | 반복여부 | 출력 항목 명 | 타입 | 최대길이 | 설명 | -|---------|---------|------------|------|---------|------| -| RSLT_CD | - | 결과코드 | String | 8 | 조회 결과 코드값, 정상 00000000 | -| RSLT_MSG | - | 결과메세지 | String | 255 | 조회 결과 메시지 | -| TR_SEQ | - | 거래일련번호 | String | 20 | 거래 추적용도 번호 | -| RSLT_DATA | - | 결과데이터 | Object | - | 나이스 결과데이터 | -| request | | 요청한 값 | Object | - | 단기연체정보 요청항목 | -| requestKey | | 요청 KEY | String | 13 | 사용자가요청한 companyKey | -| requestKeyType | | 요청한 KEY 타입 | String | 6 | BIZNO: 사업자번호, CRPNO: 법인번호, UPCHECD: 업체코드 | -| data | | | Object | | 기업 신용 신용도판단 정보(신용정보사) 결과 | -| listCount | | list 건수 | Integer | | | -| creditNegativeInfoCdList | | | Array | | 기업 신용 신용도판단 정보(신용정보사) | -| typecode | ● | 유형구분코드 | String | | CB: 신용도판단정보(신용정보사) | -| typename | ● | 유형구분명 | String | | (예: 신용도판단정보(신용정보사)) | -| causecode | ● | 등록코드 | String | | | -| causename | ● | 등록사유 명 | String | | | -| causenameEng | ● | 등록사유영문명 | String | | | -| causecont | ● | 등록사유설명 | String | | | -| causedetail | ● | 등록사유상세설명 | String | | | -| regamt | ● | 등록금액 | String | | | -| regdate | ● | 등록일자 | String | | yyyymmdd | -| relscode | ● | 해제코드 | String | | | -| relsdate | ● | 해제일자 | String | | yyyymmdd | -| relsname | ● | 해제 명 | String | | | -| orgname | ● | 발생기관명 | String | | | -| orgnameEng | ● | 발생기관영문명 | String | | | -| brcname | ● | 발생지점명 | String | | | -| occdate | ● | 발생일자 | String | | yyyymmdd | - ---- - -### 5.6 [기업]신용-당좌거래정지정보(금융결제원) (OA16) - -#### [INPUT] - -| 개별 입력 ID | 개별 입력 명 | 타입 | 최대길이 | 필수여부 | 비고 | -|-------------|-------------|------|---------|---------|------| -| Companykey | 사업자정보 | String | 13 | 필수 | 조회할 기업의 사업자번호, 법인번호, 업체코드 중 한개만 입력 | - -#### [OUTPUT] - -| 출력 항목 | 반복여부 | 출력 항목 명 | 타입 | 최대길이 | 설명 | -|---------|---------|------------|------|---------|------| -| RSLT_CD | - | 결과코드 | String | 8 | 조회 결과 코드값, 정상 00000000 | -| RSLT_MSG | - | 결과메세지 | String | 255 | 조회 결과 메시지 | -| TR_SEQ | - | 거래일련번호 | String | 20 | 거래 추적용도 번호 | -| RSLT_DATA | - | 결과데이터 | Object | - | 나이스 결과데이터 | -| request | | 요청한 값 | Object | - | 단기연체정보 요청항목 | -| requestKey | | 요청 KEY | String | 13 | 사용자가요청한 companyKey | -| requestKeyType | | 요청한 KEY 타입 | String | 6 | BIZNO: 사업자번호, CRPNO: 법인번호, UPCHECD: 업체코드 | -| data | | | Object | | 기업 신용 당좌거래정지정보 결과 | -| listCount | | list 건수 | Integer | | | -| creditSuspensionInfoList | | | Array | | 기업 신용 당좌거래정 지정보 | -| datSeq | ● | 데이터일련번호 | Integer | | | -| changeHouse | ● | 교환소 | String | 20 | | -| koreantrnm | ● | 업체 명 | String | 70 | | -| korrenprnm | ● | 대표자명 | String | 30 | | -| bizno | ● | 사업자등록번호 | String | 10 | | -| regno | ● | 주민등록번호 | String | 13 | | -| address | ● | 주소 | String | 100 | | -| occdate | ● | 발행일 | String | 8 | yyyymmdd | -| relsdate | ● | 해제일 | String | 8 | yyyymmdd | - ---- - -### 5.7 [기업]신용-법정관리/워크아웃정보 (OA17) - -#### [INPUT] - -| 개별 입력 ID | 개별 입력 명 | 타입 | 최대길이 | 필수여부 | 비고 | -|-------------|-------------|------|---------|---------|------| -| Companykey | 사업자정보 | String | 13 | 필수 | 조회할 기업의 사업자번호, 법인번호, 업체코드 중 한개만 입력 | -| pageNo | 페이지 번호 | String | | | | -| pageSize | 페이지 사이즈 | String | | | | - -#### [OUTPUT] - -| 출력 항목 | 반복여부 | 출력 항목 명 | 타입 | 최대길이 | 설명 | -|---------|---------|------------|------|---------|------| -| RSLT_CD | - | 결과코드 | String | 8 | 조회 결과 코드값, 정상 00000000 | -| RSLT_MSG | - | 결과메세지 | String | 255 | 조회 결과 메시지 | -| TR_SEQ | - | 거래일련번호 | String | 20 | 거래 추적용도 번호 | -| RSLT_DATA | - | 결과데이터 | Object | - | 나이스 결과데이터 | -| request | | 요청한 값 | Object | - | 단기연체정보 요청항목 | -| requestKey | | 요청 KEY | String | 13 | 사용자가요청한 companyKey | -| requestKeyType | | 요청 KEY 타입 | String | 6 | BIZNO: 사업자번호, CRPNO: 법인번호, UPCHECD: 업체코드 | -| pageNo | | | integer | | | -| pageSize | | | integer | | | -| data | | | Object | | 법정관리/워크아웃정보 결과 | -| listCount | | list 건수 | Integer | | | -| totalCount | | 총 건수 | integer | | | -| creditworkoutList | | 기업 신용 법정관리/워크아웃정보 | Array | | | -| upchecd | ● | 업체코드 | String | 6 | | -| lglmgmtRldDate | ● | 법정관리관례일자 | String | 8 | | -| hngno | ● | 사건번호 | String | 14 | | -| lwccd | ● | 법원코드 | String | 3 | | -| lwccd | ● | 법원 명 | String | 100 | | -| crgJudgDeptnm | ● | 담당판사부서명 | String | 30 | | -| crgJudgNam | ● | 담당판사 명 | String | 100 | | -| korentrnm | ● | 업체 명 | String | 100 | | -| lglmgmtdivcd | ● | 법정관리유형코드 | String | 2 | | -| lglmgmtdivnm | ● | 법정관리유형명 | String | 150 | | - ---- - -### 5.8 소스코딩 예제 - -#### 1. [기업]지표-주요경영지표 (OA07) - -**[입력값]** -```json -{ - "API_KEY": "업체 인증키", - "API_ID": "OA07", - "TR_SEQ": "123456789", - "Companykey": "2178149522", - "tpCd": "01", - "fatpCd": "0" -} -``` - -#### 2. [기업]개요-기본정보 (OA08) - -**[입력값]** -```json -{ - "API_KEY": "업체 인증키", - "API_ID": "OA08", - "TR_SEQ": "123456789", - "Companykey": "2178149522", - "idscdcg": "09" -} -``` - -#### 3. [검색]통합기업검색 (OA09) - -**[입력값]** -```json -{ - "API_KEY": "업체 인증키", - "API_ID": "OA09", - "TR_SEQ": "123456789", - "pageNo": "1", - "pageSize": "10", - "keyword": "삼성전자", - "upchecd": "380725" -} -``` - -#### 4. [기업]등급-기업평가등급 (OA10) - -**[입력값]** -```json -{ - "API_KEY": "업체 인증키", - "API_ID": "OA10", - "TR_SEQ": "123456789", - "Companykey": "2178149522", - "pageNo": "1", - "pageSize": "10", - "startDate": "20220101", - "endDate": "20240101" -} -``` - -#### 5. [기업]등급-WATCH 등급 (OA11) - -**[입력값]** -```json -{ - "API_KEY": "업체 인증키", - "API_ID": "OA11", - "TR_SEQ": "123456789", - "Companykey": "2178149522", - "addWatchRsn": "1", - "pageNo": "1", - "pageSize": "10", - "startDate": "20220101", - "endDate": "20240101" -} -``` - -#### 6. [기업]신용-신용요약정보 (OA12) - -**[입력값]** -```json -{ - "API_KEY": "업체 인증키", - "API_ID": "OA12", - "TR_SEQ": "123456789", - "Companykey": "2178149522" -} -``` - -#### 7. [기업]신용-단기연체정보(한국신용정보원) (OA13) - -**[입력값]** -```json -{ - "API_KEY": "업체 인증키", - "API_ID": "OA13", - "TR_SEQ": "123456789", - "Companykey": "2178149522", - "reqDate": "20220101" -} -``` - -#### 8. [기업]신용도-판단정보(공공정보 포함)(한국 신용정보원) (OA14) - -**[입력값]** -```json -{ - "API_KEY": "업체 인증키", - "API_ID": "OA14", - "TR_SEQ": "123456789", - "Companykey": "2178149522" -} -``` - -#### 9. [기업]신용-신용도 판단정보(신용정보사) (OA15) - -**[입력값]** -```json -{ - "API_KEY": "업체 인증키", - "API_ID": "OA15", - "TR_SEQ": "123456789", - "Companykey": "2178149522" -} -``` - -#### 10. [기업]신용-당좌거래정지정보(금융결제원) (OA16) - -**[입력값]** -```json -{ - "API_KEY": "업체 인증키", - "API_ID": "OA16", - "TR_SEQ": "123456789", - "Companykey": "2178149522" -} -``` - -#### 11. [기업]신용-법정관리/워크아웃정보 (OA17) - -**[입력값]** -```json -{ - "API_KEY": "업체 인증키", - "API_ID": "OA17", - "TR_SEQ": "123456789", - "Companykey": "5148145785", - "pageNo": "1", - "pageSize": "10" -} -``` - ---- - -## 6. 개발 유의사항 - -API를 호출하실 경우에는 자바스크립트, VB 스크립트 등의 클라이언트 사이트 스크립트 언어가 아닌 **서버 사이드 스크립트 언어(JSP, PHP, ASP 등)**로 호출하여 주셔야 정상적인 거래가 가능합니다. - -### 서버 사이드 스크립트 사용 JSP 페이지 샘플 - -```jsp -<%@ page contentType="text/html; charset=utf-8"%> -<%@ page import="java.net.*"%> -<%@ page import = "java.io.*" %> -<%@ page import = "java.net.URLConnection" %> -<%@ page import = "javax.net.ssl.HttpsURLConnection" %> -<% -response.setHeader("Cache-Control","no-store"); // HTTP 1.1 -response.setHeader("Pragma","no-cache"); // HTTP 1.0 -response.setDateHeader("Expires", 0); - -String url = "https://dev2.coocon.co.kr:8443/sol/gateway/oapi_relay.jsp"; -byte[] resMessage = null; -HttpsURLConnection conn; - -try { - conn = (HttpsURLConnection) new URL(url).openConnection(); - conn.setDoInput(true); - conn.setDoOutput(true); - conn.setRequestMethod("POST"); - conn.setRequestProperty("Content-Type","application/json"); - conn.setUseCaches(false); - OutputStreamWriter os = new OutputStreamWriter(conn.getOutputStream()); - - JSONObject inputObj = new JSONObject(); - inputObj.put("API_KEY","발급인증키"); - inputObj.put("API_ID","업무코드"); - inputObj.put("Companykey","사업자정보"); - os.write(inputObj.toString()); - os.flush(); - os.close(); - - DataInputStream in = new DataInputStream(conn.getInputStream()); - ByteArrayOutputStream bout = new ByteArrayOutputStream(); - int bcount = 0; - byte[] buf = new byte[2048]; - while (true) { - int n = in.read(buf); - if (n == -1) break; - bout.write(buf, 0, n); - } - bout.flush(); - resMessage = bout.toByteArray(); - conn.disconnect(); -} -catch (MalformedURLException e) { - System.out.println("MalformedURLException"); -} -catch (IOException e) { - e.printStackTrace(); -} - -//결과처리 -String temp = new String(resMessage, "UTF-8"); -temp = temp.replaceAll("\r\n",""); -temp = temp.replaceAll("\r",""); -temp = temp.replaceAll("\n",""); -out.println(temp.trim()); -%> -``` - ---- - -## [참조 1] 결과코드 - -| Code | 내용 | -|------|------| -| 00000000 | 정상처리되었습니다. | -| GWS06001 | 수신자료 포맷 오류. (REQ_DATA 미입력 오류로 입력값중에 개별입력값 확인 필요) | -| GWS06002 | 수신자료 변환 오류. (입력값 JSON 형식인지 확인필요) | -| GWS06003 | 잘못된 전문번호 입니다. (API ID 오입력으로 API ID 값 확인 필요) | -| GWS06004 | 응답시간을 초과하였습니다. (업무 담당자에게 문의) | -| GWS06005 | 결과처리중 오류가 발생하였습니다. (업무 담당자에게 문의) | -| GWS06006 | 정의되지 않은 응답오류입니다. (업무 담당자에게 문의) | -| GWS06013 | 거래고유번호(TR_SEQ)이 미입력되었습니다. | -| GWS06020 | 서비스가능한 시간대가 아닙니다. | -| GWS09979 | 내부 서버오류 (업무 담당자에게 문의) | -| GWS09984 | 잘못된 인증키 입니다. (API_KEY 값 오입력으로 API KEY 값 확인 필요) | -| GWS09985 | 보안키(API_KEY)값이 미입력되었습니다. | -| GWS09989 | 허용된 아이피가 아닙니다 (접속아이피 확인필요) | -| GWS09990 | 사용가능 아이피가 등록되지 않았습니다. | -| GWS09993 | 서비스 설정값이 잘못되었습니다. (업무 담당자에게 문의) | -| GWS09998 | 인증과정중 알수없는 오류가 발생했습니다. | -| OAPI0005 | Companykey 미입력 되었습니다. | diff --git a/sam/docker/api/opcache.ini b/sam/docker/api/opcache.ini deleted file mode 100644 index 137d2c0..0000000 --- a/sam/docker/api/opcache.ini +++ /dev/null @@ -1,33 +0,0 @@ -; OPcache 설정 (성능 향상) -; PHP OPcode 캐시를 활성화하여 애플리케이션 성능을 크게 향상시킵니다 - -[opcache] -; OPcache 활성화 -opcache.enable=1 - -; CLI 환경에서도 OPcache 활성화 (개발 환경) -opcache.enable_cli=1 - -; OPcache 메모리 사용량 (MB) -; 256MB 권장 (프로젝트 크기에 따라 조정) - 개발 환경 성능 향상 -opcache.memory_consumption=256 - -; 내부 문자열 버퍼 크기 (MB) -opcache.interned_strings_buffer=16 - -; 최대 가속화 파일 수 -opcache.max_accelerated_files=20000 - -; 타임스탬프 검증 활성화 (개발 환경) -; 프로덕션에서는 0으로 설정하여 성능 최적화 -opcache.validate_timestamps=1 - -; 재검증 주기 (초) - 개발 환경 성능 향상을 위해 단축 -; validate_timestamps가 1일 때만 사용됨 -opcache.revalidate_freq=1 - -; 빠른 종료 활성화 -opcache.fast_shutdown=1 - -; 최적화 레벨 (0-7, 높을수록 느리지만 더 최적화됨) -opcache.optimization_level=0x7FFFBFFF diff --git a/sam/docker/api/supervisord.conf b/sam/docker/api/supervisord.conf deleted file mode 100755 index be1dada..0000000 --- a/sam/docker/api/supervisord.conf +++ /dev/null @@ -1,36 +0,0 @@ -[supervisord] -nodaemon=true - -[program:php-fpm] -command=/usr/local/sbin/php-fpm - -[program:nginx] -command=nginx -g "daemon off;" - -[program:queue-worker] -command=php /var/www/api/artisan queue:work database --queue=api,default --sleep=3 --tries=3 --timeout=1800 --max-jobs=100 --max-time=3600 -process_name=%(program_name)s_%(process_num)02d -numprocs=1 -directory=/var/www/api -autostart=true -autorestart=true -startsecs=5 -startretries=3 -stopwaitsecs=1830 -stdout_logfile=/var/www/api/storage/logs/queue-worker.log -stdout_logfile_maxbytes=5MB -stderr_logfile=/var/www/api/storage/logs/queue-worker-error.log -stderr_logfile_maxbytes=5MB - -[program:scheduler] -command=bash -c "while true; do php /var/www/api/artisan schedule:run --no-interaction; sleep 60; done" -process_name=%(program_name)s -numprocs=1 -directory=/var/www/api -autostart=true -autorestart=true -startsecs=0 -stdout_logfile=/var/www/api/storage/logs/scheduler.log -stdout_logfile_maxbytes=5MB -stderr_logfile=/var/www/api/storage/logs/scheduler-error.log -stderr_logfile_maxbytes=5MB \ No newline at end of file diff --git a/sam/docker/docker-compose.yml b/sam/docker/docker-compose.yml deleted file mode 100644 index e89b1e8..0000000 --- a/sam/docker/docker-compose.yml +++ /dev/null @@ -1,225 +0,0 @@ -services: - nginx: - image: nginx:latest - ports: - - "80:80" - - "443:443" - volumes: - - /home/aweso/sam/api:/var/www/api - - /home/aweso/sam/admin:/var/www/admin - - /home/aweso/sam/mng:/var/www/mng - - /home/aweso/sam/5130:/var/www/5130 - - /home/aweso/sam/docker/nginx/nginx.conf:/etc/nginx/nginx.conf:ro - - /home/aweso/sam/docker/nginx/ssl:/etc/nginx/ssl:ro - command: > - sh -c "rm -f /etc/nginx/conf.d/default.conf && nginx -g 'daemon off;'" - depends_on: - - api - - admin - - mng - - react - - design - - sales - - php73 - networks: - - samnet - - api: - build: - context: . - dockerfile: /home/aweso/sam/docker/api/Dockerfile - volumes: - - /home/aweso/sam/api:/var/www/api - - api_vendor:/var/www/api/vendor - - api_node_modules:/var/www/api/node_modules - - /home/aweso/sam/docker/api/nginx.conf:/etc/nginx/conf.d/default.conf - - /home/aweso/sam/docker/api/supervisord.conf:/etc/supervisor/conf.d/supervisord.conf - - /home/aweso/sam/docker/api/uploads.ini:/usr/local/etc/php/conf.d/uploads.ini - - /home/aweso/sam/docker/api/opcache.ini:/usr/local/etc/php/conf.d/opcache.ini - - /home/aweso/sam/docker/mysql/client-skip-ssl.cnf:/etc/mysql/conf.d/disable-ssl.cnf:ro - environment: - - DB_HOST=sam-mysql-1 - - DB_PORT=3306 - - DB_DATABASE=samdb - - DB_USERNAME=samuser - - DB_PASSWORD=sampass - networks: - - samnet - working_dir: /var/www/api - - admin: - build: - context: . - dockerfile: /home/aweso/sam/docker/admin/Dockerfile - volumes: - - /home/aweso/sam/admin:/var/www/admin - - admin_vendor:/var/www/admin/vendor - - admin_node_modules:/var/www/admin/node_modules - - /home/aweso/sam/docker/admin/nginx.conf:/etc/nginx/conf.d/default.conf - - /home/aweso/sam/docker/admin/supervisord.conf:/etc/supervisor/conf.d/supervisord.conf - - /home/aweso/sam/docker/admin/uploads.ini:/usr/local/etc/php/conf.d/uploads.ini - environment: - - DB_HOST=sam-mysql-1 - - DB_PORT=3306 - - DB_DATABASE=samdb - - DB_USERNAME=samuser - - DB_PASSWORD=sampass - networks: - - samnet - working_dir: /var/www/admin - - mng: - build: - context: . - dockerfile: mng/Dockerfile - volumes: - - ../mng:/var/www/mng - - mng_vendor:/var/www/mng/vendor - - mng_node_modules:/var/www/mng/node_modules - - ./mng/nginx.conf:/etc/nginx/conf.d/default.conf - - ./mng/supervisord.conf:/etc/supervisor/conf.d/supervisord.conf - - ./mng/opcache.ini:/usr/local/etc/php/conf.d/opcache.ini - - ./mng/uploads.ini:/usr/local/etc/php/conf.d/uploads.ini - # - ./mng/www.conf:/usr/local/etc/php-fpm.d/www.conf - - ../api/storage/logs:/var/www/api/storage/logs:ro - - ./mysql/client-skip-ssl.cnf:/etc/mysql/conf.d/disable-ssl.cnf:ro - - ../sales/apikey:/var/www/sales/apikey:ro # Google 서비스 계정 파일 접근용 - - ../sales:/var/www/sales-docs:ro # 영업 PPTX 문서 접근용 - # shared-storage 제거됨 → mng/storage/app/tenants 로 이동 (2026-02-23) - - ../docs:/var/www/docs:ro # SAM 프로젝트 문서 (RAG 검색용) - environment: - - DB_HOST=sam-mysql-1 - - DB_PORT=3306 - - DB_DATABASE=samdb - - DB_USERNAME=samuser - - DB_PASSWORD=sampass - networks: - - samnet - working_dir: /var/www/mng - - react: - build: - context: /home/aweso/sam - dockerfile: docker/react/Dockerfile - volumes: - - /home/aweso/sam/react:/app - - /app/node_modules - - /app/.next - environment: - - NEXT_PUBLIC_API_URL=https://api.sam.kr - - NEXT_PUBLIC_ADMIN_URL=https://admin.sam.kr - - NEXT_PUBLIC_API_KEY=42Jfwc6EaRQ04GNRmLR5kzJp5UudSOzGGqjmdk1a - - NEXT_PUBLIC_APP_NAME=SAM - - NODE_ENV=development - - NODE_TLS_REJECT_UNAUTHORIZED=0 - extra_hosts: - - "api.sam.kr:host-gateway" - networks: - - samnet - working_dir: /app - - design: - build: - context: /home/aweso/sam - dockerfile: docker/design/Dockerfile - volumes: - - /home/aweso/sam/design:/app - - /app/node_modules - environment: - - NODE_ENV=development - networks: - - samnet - working_dir: /app - - sales: - build: - context: . - dockerfile: /home/aweso/sam/docker/sales/Dockerfile - volumes: - - /home/aweso/sam/sales:/var/www/sales - - sales_vendor:/var/www/sales/vendor - - sales_node_modules:/var/www/sales/node_modules - - /home/aweso/sam/docker/sales/nginx.conf:/etc/nginx/conf.d/default.conf - - /home/aweso/sam/docker/sales/supervisord.conf:/etc/supervisor/conf.d/supervisord.conf - - /home/aweso/sam/docker/sales/uploads.ini:/usr/local/etc/php/conf.d/uploads.ini - environment: - - TZ=Asia/Seoul - networks: - - samnet - working_dir: /var/www/sales - - php73: - build: - context: . - dockerfile: /home/aweso/sam/docker/5130/Dockerfile - volumes: - - /home/aweso/sam/5130:/var/www/5130 - - php73_vendor:/var/www/5130/vendor - - php73_node_modules:/var/www/5130/node_modules - - /home/aweso/sam/docker/5130/nginx.conf:/etc/nginx/conf.d/default.conf - - /home/aweso/sam/docker/5130/supervisord.conf:/etc/supervisor/conf.d/supervisord.conf - - /home/aweso/sam/docker/5130/uploads.ini:/usr/local/etc/php/conf.d/uploads.ini - environment: - - DB_HOST=sam-mysql-1 - - DB_PORT=3306 - - DB_DATABASE=chandj - - DB_USERNAME=root - - DB_PASSWORD=root - - TZ=Asia/Seoul - networks: - - samnet - working_dir: /var/www/5130 - - mysql: - image: mysql:8.0 - restart: always - environment: - MYSQL_DATABASE: samdb - MYSQL_USER: samuser - MYSQL_PASSWORD: sampass - MYSQL_ROOT_PASSWORD: root - TZ: Asia/Seoul - command: --sql-mode="NO_ENGINE_SUBSTITUTION" --default-time-zone="+09:00" --default-authentication-plugin=mysql_native_password - volumes: - - db_data:/var/lib/mysql - - /home/aweso/sam/docker/mysql/init.sql:/docker-entrypoint-initdb.d/01-init.sql - # - /home/aweso/sam/chandj_dump.sql:/docker-entrypoint-initdb.d/02-chandj-dump.sql - ports: - - "3306:3306" - networks: - - samnet - - phpmyadmin: - image: phpmyadmin:latest - restart: always - ports: - - "8080:80" - environment: - - PMA_ARBITRARY=1 - - PMA_HOST=mysql - - PMA_PORT=3306 - - PMA_USER=root - - PMA_PASSWORD=root - - TZ=Asia/Seoul - depends_on: - - mysql - networks: - - samnet - -volumes: - db_data: - # 의존성 디렉토리 분리 (성능 향상) - api_vendor: - api_node_modules: - mng_vendor: - mng_node_modules: - admin_vendor: - admin_node_modules: - sales_vendor: - sales_node_modules: - php73_vendor: - php73_node_modules: - -networks: - samnet: - driver: bridge diff --git a/sam/docker/mng/Dockerfile b/sam/docker/mng/Dockerfile deleted file mode 100755 index f19b269..0000000 --- a/sam/docker/mng/Dockerfile +++ /dev/null @@ -1,43 +0,0 @@ -FROM php:8.4-fpm - -# 필수 패키지/확장 설치 -RUN apt-get update && apt-get install -y \ - git \ - unzip \ - libzip-dev \ - libicu-dev \ - libxml2-dev \ - libpng-dev \ - libfreetype-dev \ - libjpeg62-turbo-dev \ - nginx \ - supervisor \ - libreoffice-writer-nogui \ - fonts-nanum fonts-nanum-extra \ - ffmpeg \ - wget \ - && docker-php-ext-configure gd --with-freetype --with-jpeg \ - && docker-php-ext-install zip mysqli pdo pdo_mysql intl soap gd \ - # Pretendard 폰트 설치 (Word→PDF 변환 시 한글 폰트 지원) - && mkdir -p /usr/share/fonts/truetype/pretendard \ - && wget -q "https://github.com/orioncactus/pretendard/releases/download/v1.3.9/Pretendard-1.3.9.zip" -O /tmp/pretendard.zip \ - && unzip -jo /tmp/pretendard.zip "*/Pretendard-*.otf" -d /usr/share/fonts/truetype/pretendard/ \ - && rm -f /tmp/pretendard.zip \ - && fc-cache -f - -# Composer 설치 -COPY --from=composer:2 /usr/bin/composer /usr/bin/composer - -# 타임존 설정 -RUN echo "date.timezone=Asia/Seoul" > /usr/local/etc/php/conf.d/timezone.ini - -# 포트 개방 -EXPOSE 80 - -# supervisor로 nginx+php-fpm 동시 기동 -CMD ["/usr/bin/supervisord"] - -# entrypoint.sh 복사 및 권한 -COPY ./mng/entrypoint.sh /entrypoint.sh -RUN chmod +x /entrypoint.sh -ENTRYPOINT ["/entrypoint.sh"] \ No newline at end of file diff --git a/sam/docker/mng/entrypoint.sh b/sam/docker/mng/entrypoint.sh deleted file mode 100755 index f5dcbc9..0000000 --- a/sam/docker/mng/entrypoint.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash - -# 0. Nginx 기본 사이트 설정 비활성화 (충돌 방지) -rm -f /etc/nginx/sites-enabled/default - -# 1. 퍼미션 설정 (mng) -chown -R www-data:www-data /var/www/mng/storage /var/www/mng/bootstrap/cache -chmod -R 775 /var/www/mng/storage /var/www/mng/bootstrap/cache - -# 2. tenant storage 퍼미션 (명함/신분증/통장/게시판 첨부파일 등) -mkdir -p /var/www/mng/storage/app/tenants -chown -R www-data:www-data /var/www/mng/storage/app/tenants -chmod -R 775 /var/www/mng/storage/app/tenants - -# 3. storage:link (실패해도 무시) -cd /var/www/mng && php artisan storage:link || true - -# 4. supervisor 실행(nginx+php-fpm) -exec /usr/bin/supervisord \ No newline at end of file diff --git a/sam/docker/mng/nginx.conf b/sam/docker/mng/nginx.conf deleted file mode 100755 index 32136d4..0000000 --- a/sam/docker/mng/nginx.conf +++ /dev/null @@ -1,32 +0,0 @@ -server { - listen 80; - server_name _; - - root /var/www/mng/public; - index index.php index.html; - - access_log /var/log/nginx/mng_access.log; - error_log /var/log/nginx/mng_error.log; - - # 심볼릭 링크 허용 - disable_symlinks off; - - # tenant-storage 정적 파일 서빙 - location /tenant-storage/ { - alias /var/www/mng/storage/app/tenants/; - expires 7d; - add_header Cache-Control "public, immutable"; - } - - location / { - try_files $uri $uri/ /index.php?$query_string; - } - - location ~ \.php$ { - fastcgi_pass 127.0.0.1:9000; - fastcgi_index index.php; - fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; - include fastcgi_params; - fastcgi_read_timeout 300s; - } -} \ No newline at end of file diff --git a/sam/docker/mng/supervisord.conf b/sam/docker/mng/supervisord.conf deleted file mode 100755 index acdf92b..0000000 --- a/sam/docker/mng/supervisord.conf +++ /dev/null @@ -1,23 +0,0 @@ -[supervisord] -nodaemon=true - -[program:php-fpm] -command=/usr/local/sbin/php-fpm - -[program:nginx] -command=nginx -g "daemon off;" - -[program:queue-worker] -command=php /var/www/mng/artisan queue:work database --queue=mng,default --sleep=3 --tries=1 --timeout=1800 --max-jobs=10 --max-time=3600 -process_name=%(program_name)s_%(process_num)02d -numprocs=2 -directory=/var/www/mng -autostart=true -autorestart=true -startsecs=5 -startretries=3 -stopwaitsecs=1830 -stdout_logfile=/var/www/mng/storage/logs/queue-worker.log -stdout_logfile_maxbytes=5MB -stderr_logfile=/var/www/mng/storage/logs/queue-worker-error.log -stderr_logfile_maxbytes=5MB \ No newline at end of file diff --git a/sam/docs/CLAUDE.md b/sam/docs/CLAUDE.md deleted file mode 100644 index cce4943..0000000 --- a/sam/docs/CLAUDE.md +++ /dev/null @@ -1,876 +0,0 @@ -# Claude Code 전역 설정 - -> 이 파일은 모든 프로젝트에 적용되는 전역 규칙입니다. - -## 메모리 - -### sam설명 -SAM 프로젝트의 기술적 개요 문서입니다. 이 문서를 참조하면 SAM 프로젝트가 무엇인지 이해할 수 있습니다. - -**파일 경로**: `/home/aweso/sam/docs/SAM_PROJECT_OVERVIEW_FOR_AI.md` - -**핵심 요약**: -- **회사**: 주일/경동 (블라인드/스크린 제조업체) -- **프로젝트**: SAM (Smart Automation Management) - 차세대 ERP/MES 통합 시스템 -- **기술 스택**: Laravel 12 + HTMX + Tailwind CSS + MySQL 8.0 (PHP 8.4) -- **아키텍처**: Multi-tenant (tenant_id 기반 데이터 격리) -- **레거시**: 5130.co.kr (PHP 기반) → SAM으로 마이그레이션 중 - -**사용자가 'sam설명'이라고 말하면**: -1. 위 경로의 `SAM_PROJECT_OVERVIEW_FOR_AI.md` 파일을 읽어서 전체 내용을 파악하세요 -2. SAM 프로젝트의 비즈니스 도메인, 기술 스택, 현재 작업 현황을 이해한 상태로 작업하세요 - ---- - -## Git 커밋 규칙 (최우선 필수 규칙) - -> **경고: 이 규칙은 절대 누락되어서는 안 됩니다!** -> **기준 문서**: `sam/docs/standards/git-conventions.md` - -### 필수 수행 절차 - -**모든 코드 작업 완료 후 반드시 다음을 수행:** - -1. 변경된 파일이 있는 Git 저장소로 이동 -2. `git status`로 변경사항 확인 -3. `git add <파일들>` 로 스테이징 -4. `git commit -m "type: [scope] 작업내용"` 로 커밋 - -### 커밋 메시지 형식 (필수) - -``` -type: [scope] 작업내용 - -- 세부항목 (생략가능) -- 세부항목 2 - -Issue: URL (생략가능) -``` - -**예시:** -```bash -feat: [calendar] 달력 기능 개선 - -- 클릭시 오류 기능 개선 -- 색상 변경 -``` - -```bash -fix: [auth] 로그인 시 세션 만료 오류 수정 -``` - -### Commit Types - -| Type | 설명 | 예시 | -|------|------|------| -| `feat` | 새로운 기능 추가 | `feat: [file] 파일 업로드 기능 추가` | -| `fix` | 버그 수정 | `fix: [auth] 세션 만료 오류 수정` | -| `chore` | 설정, 빌드 등 변경 | `chore: composer 패키지 업데이트` | -| `refactor` | 프로덕션 코드 리팩토링 | `refactor: [user] 서비스 메서드 분리` | -| `style` | 포맷/코딩 스타일 수정 | `style: Pint 포맷팅 적용` | -| `test` | 테스트 추가/수정 | `test: Product API 테스트 추가` | -| `docs` | 문서 변경 | `docs: API 문서 업데이트` | - -### Claude 서명 제외 (필수) - -``` -❌ Co-Authored-By: Claude — 포함 금지 -❌ 🤖 Generated with Claude Code — 포함 금지 -``` - -- Git hooks로 자동 제거됨 -- 간결하고 명확한 한글 커밋 메시지만 유지 - -### 푸시 정책 - -- **사용자가 수동으로 푸시 진행** -- 자동 푸시 하지 않음 -- 커밋 후 푸시 여부를 묻지 않음 - -### Claude Code 설정 파일도 커밋 대상 - -다음 파일들이 변경되면 반드시 커밋: - -| 파일/폴더 | 설명 | 커밋 예시 | -|-----------|------|----------| -| `CLAUDE.md` | 프로젝트 설정 | `docs: CLAUDE.md 규칙 업데이트` | -| `claudedocs/` | Claude 관련 문서 | `docs: 기능 분석 문서 추가` | -| `.claude/settings.json` | Claude 설정 | `chore: Claude 설정 변경` | -| `agents/`, `skills/` | 커스텀 에이전트/스킬 | `feat: [claude] 새 스킬 추가` | - -### 커밋 전 체크리스트 - -- [ ] `./vendor/bin/pint` 실행 (코드 포맷팅, 해당 시) -- [ ] `git diff`로 변경사항 검토 -- [ ] 불필요한 파일 제외 (.env, node_modules 등) -- [ ] 변경된 파일이 있는 저장소에서 git add → git commit -- [ ] CLAUDE.md, claudedocs/, agents/, skills/ 변경 확인 → git commit -- [ ] 커밋 메시지: `type: [scope] 한글 작업내용` 형식 준수 -- [ ] Co-Authored-By 서명 미포함 확인 - ---- - -## 주요 프로젝트 경로 - -| 경로 | 설명 | Git 저장소 | -|------|------|-----------| -| `/home/aweso/sam/mng` | 관리자 웹 (Laravel) | 독립 저장소 | -| `/home/aweso/sam/api` | API 서버 (Laravel) | 독립 저장소 | -| `/home/aweso/sam/react` | 프론트엔드 (Next.js) | 독립 저장소 | - -**각 폴더는 독립적인 Git 저장소입니다. 해당 폴더에서 git 명령을 실행해야 합니다.** - -> **서버 경로 참고**: -> - 개발/운영 서버 모두 `/home/webservice/` 하위에 동일한 구조로 배치 -> - 서버: `/home/webservice/api`, `/home/webservice/mng`, `/home/webservice/react`, `/home/webservice/sales` - ---- - -## 서버 직접 접근 금지 (최우선 필수 규칙) - -> **경고: 운영/개발 서버에 SSH로 직접 접속하여 파일을 수정하거나 명령을 실행하지 마세요!** -> **2026-02-21 사고**: Claude가 서버에 SSH로 직접 접속하여 설정을 변경한 결과 502 Bad Gateway 발생. 개발팀장이 복구함. - -### 핵심 원칙 - -서버는 **개발팀장이 관리**한다. Claude는 서버에 절대 직접 접근하지 않는다. - -### 금지 사항 - -``` -❌ ssh pro@114.203.209.83 ... 로 서버 접속 금지 -❌ ssh hskwon@114.203.209.83 ... 로 서버 접속 금지 -❌ 서버에서 파일 수정, 프로세스 종료/시작, 설정 변경 금지 -❌ 서버에서 npm run build, npm start, node server.js 등 실행 금지 -❌ 서버에서 git pull, composer install, php artisan 등 실행 금지 -❌ scp, rsync로 서버에 파일 직접 전송 금지 -``` - -### 허용 사항 - -``` -✅ 로컬에서 코드 작성 및 수정 -✅ 로컬에서 git add → git commit -✅ 사용자에게 git push 안내 (사용자가 수동으로 실행) -✅ 사용자에게 서버 배포 절차 안내 (사용자가 수동으로 실행) -``` - -### 서버 접속 정보 - -| 서버 | 호스트 | 계정 | 역할 | -|------|--------|------|------| -| 개발 서버 | `114.203.209.83` | `pro`, `hskwon` | 개발/스테이징 + Jenkins CI/CD + Gitea | -| 운영 서버 | (신규, 미확정) | 별도 계정 | 정식 서비스 | - -> **참고**: Jenkins(`114.203.209.83:8080`)와 Gitea(`114.203.209.83:3000`)는 개발 서버에서 운영한다. - -### 배포 흐름 (Jenkins CI/CD) - -``` -Claude 역할 Jenkins (자동) 운영 서버 -┌───────────────────┐ ┌──────────────────┐ ┌──────────────┐ -│ 코드 작성/수정 │ │ │ │ │ -│ git add / commit │ │ │ │ │ -│ │─push──→ │ Gitea Webhook │ │ │ -│ │(사용자) │ → Jenkins 빌드 │ │ │ -│ │ │ → Lint/Test │ │ │ -│ │ │ → SSH Deploy ────│──→ │ git pull │ -│ │ │ │ │ composer │ -│ │ │ │ │ migrate │ -└───────────────────┘ └──────────────────┘ └──────────────┘ -``` - -> **브랜치 전략**: `develop` → 개발 서버 (자동 배포), `main`/`master` → 운영 서버 (PR 머지 + 팀장 승인) - -### 서버 작업이 필요한 경우 - -사용자에게 명령어를 안내만 한다: - -``` -서버에서 다음 명령을 실행해주세요: -cd /home/webservice/api && git pull && composer install && php artisan migrate -``` - -### 체크리스트 (모든 작업 시) - -- [ ] SSH 명령 사용하지 않음 -- [ ] 서버 파일 직접 수정하지 않음 -- [ ] 배포가 필요하면 사용자에게 안내만 제공 -- [ ] git push까지만 Claude 역할 - ---- - -## React 빌드/배포 정책 (필수 규칙) - -> **경고: React(Next.js) 빌드를 운영 서버에서 직접 실행하지 않는다!** - -### 배경 - -개발 서버(2코어, 3.8GB RAM + Swap 4GB)에서 Jenkins가 React 빌드를 수행한다. -Jenkins 빌드 실패 시 로컬(WSL)에서 빌드 후 결과물을 서버에 배포한다(fallback). - -### 금지 사항 - -``` -❌ 운영 서버에서 npm run build 실행 금지 -❌ 서버 SSH 접속 후 빌드 명령 실행 금지 -❌ Claude가 직접 npm run build 실행 금지 (로컬 포함) -``` - -### 빌드/배포 방법 (Jenkins 자동화) - -``` -Claude 역할 Jenkins (자동) 운영 서버 -┌─────────────────┐ ┌──────────────────┐ ┌──────────────┐ -│ 코드 작성/수정 │ │ Checkout │ │ │ -│ git commit │─push──→ │ Install + Lint │ │ │ -│ │(사용자) │ Build (Next.js) │ │ │ -│ │ │ Package (tar.gz) │──scp→ │ 압축 해제 │ -│ │ │ │ │ node 재시작 │ -└─────────────────┘ └──────────────────┘ └──────────────┘ -``` - -> **Fallback**: Jenkins 빌드 실패(OOM) 시 로컬에서 `react/deploy.sh`로 수동 배포 - -### 빌드가 필요한 상황 - -사용자에게 다음과 같이 안내한다: - -``` -React 코드가 변경되었습니다. git push 후 Jenkins가 자동 배포합니다. -(Jenkins 실패 시 로컬에서 deploy.sh로 수동 배포해주세요.) -``` - ---- - -## 데이터베이스 아키텍처 (필수 규칙) - -> **경고: 이 규칙을 반드시 준수하세요!** - -### 핵심 원칙 - -**모든 데이터베이스 관련 파일은 API 프로젝트에서만 관리합니다.** - -| 항목 | API (`/home/aweso/sam/api`) | MNG (`/home/aweso/sam/mng`) | -|------|----------------------------|----------------------------| -| 마이그레이션 | ✅ 여기에 생성 | ❌ 생성 금지 | -| 시더 | ✅ 여기에 생성 | ⚠️ MNG 전용만 허용 | -| 팩토리 | ✅ 여기에 생성 | ❌ 생성 금지 | - -### 금지 사항 - -``` -❌ /home/aweso/sam/mng/database/migrations/ 에 파일 생성 금지 -❌ MNG에서 테이블 생성/수정 마이그레이션 작성 금지 -``` - -### 허용 사항 - -``` -✅ /home/aweso/sam/api/database/migrations/ 에 모든 마이그레이션 생성 -✅ MNG에서는 MngMenuSeeder 같은 MNG 전용 시더만 허용 -``` - -### 마이그레이션 실행 - -```bash -# 로컬: 마이그레이션은 반드시 API 컨테이너에서 실행 -docker exec sam-api-1 php artisan migrate - -# 개발 서버: Docker 없음, 직접 실행 -cd /home/webservice/api && php artisan migrate - -# 운영 서버: --force 플래그 필수 (production 환경) -cd /home/webservice/api && php artisan migrate --force - -# MNG에서 마이그레이션 실행 금지 (로컬/서버 모두) -``` - -### DB 환경 분리 - -| 환경 | DB명 | 호스트 | 용도 | -|------|------|--------|------| -| 로컬 (Docker) | `samdb` | `sam-mysql-1:3306` | 개발/테스트 | -| 개발 서버 | `samdb` | `localhost` | 스테이징 | -| 운영 서버 | `sam_prod` | `localhost` | 정식 서비스 | -| 통계 DB | `sam_stat` | 동일 서버 | StatMonitorService 전용 | - -> **참고**: `sam_stat`은 API/MNG 모두 `config/database.php`의 별도 connection으로 접속한다. - -### 이유 - -- MNG: 프론트엔드/관리자 화면 담당 (컨트롤러, 뷰, 라우트) -- API: 백엔드/데이터베이스 담당 (마이그레이션, 모델 정의, API) -- 단일 DB를 두 프로젝트가 공유하므로 마이그레이션은 한 곳에서만 관리 - ---- - -## 메뉴 관리 규칙 (필수) - -> **경고: 메뉴 시더(Seeder)를 절대 실행하지 마세요!** - -### 배경 - -메뉴 시더 실행 시 부서별 권한 설정(permission_overrides)이 초기화되는 문제가 반복 발생합니다. -메뉴 ID가 변경되면 기존 부서-메뉴 권한 매핑이 깨지기 때문입니다. - -### 금지 사항 - -``` -❌ php artisan db:seed --class=MngMenuSeeder 실행 금지 -❌ php artisan db:seed --class=*MenuSeeder 실행 금지 -❌ 메뉴 시더 파일 생성 금지 -❌ 메뉴 데이터를 일괄 삭제 후 재생성하는 방식 금지 -``` - -### 메뉴 변경 시 올바른 절차 - -메뉴 추가/수정/삭제/이동이 필요할 때는 **사용자에게 수동 실행 안내**를 제공합니다: - -1. **tinker 명령어를 안내** (사용자가 직접 실행) -2. **또는 SQL 쿼리를 안내** (사용자가 phpMyAdmin 등에서 직접 실행) -3. **절대 시더를 만들어 실행하지 않음** - -### 안내 예시 - -``` -메뉴를 추가하려면 아래 명령을 서버에서 실행해 주세요: - -# 개발 서버 -ssh pro@114.203.209.83 "cd /home/webservice/mng && php artisan tinker --execute=\" -App\\Models\\Commons\\Menu::create([ - 'tenant_id' => 1, - 'parent_id' => <부모ID>, - 'name' => '새 메뉴', - 'url' => '/new-menu', - 'icon' => 'icon-name', - 'sort_order' => 1, - 'is_active' => true, -]); -\"" - -# 운영 서버 (동일 경로, 서버 주소만 변경) -ssh <운영계정>@<운영서버IP> "cd /home/webservice/mng && php artisan tinker --execute=\"...동일...\"" -``` - -### 체크리스트 (메뉴 변경 요청 시) - -- [ ] 시더 파일 생성하지 않음 -- [ ] 시더 실행하지 않음 -- [ ] tinker 또는 SQL로 개별 레코드만 수정 -- [ ] 변경 후 부서 권한 설정이 유지되는지 확인 - ---- - -## 실행 환경 (필수 인지) - -> **중요: 로컬 / 개발 서버 / 운영 서버의 환경이 다릅니다!** - -### 환경 비교 (3-Tier) - -| 항목 | 로컬 (WSL) | 개발 서버 | 운영 서버 | -|------|-----------|----------|----------| -| **구성 방식** | Docker 컨테이너 | Bare-metal (네이티브) | Bare-metal (네이티브) | -| **PHP** | 컨테이너 내부 (8.4) | 직접 설치 (8.4) | 직접 설치 (8.4) | -| **MySQL** | 컨테이너 (sam-mysql-1) | 직접 설치 (8.0) | 직접 설치 (8.0) | -| **Nginx** | 컨테이너 (sam-nginx-1) | 직접 설치 | 직접 설치 | -| **명령 실행** | `docker exec` 필요 | 직접 실행 | 직접 실행 | -| **서버 IP** | localhost | `114.203.209.83` | (신규, 미확정) | -| **추가 서비스** | — | Jenkins, Gitea | — | -| **DB** | `samdb` | `samdb` | `sam_prod` | - -> **배경**: 서버는 Docker가 무거워서 PHP, Nginx, MySQL 등을 네이티브로 설치하여 운영한다. - -### 로컬 환경 (Docker) - -PHP, Laravel, Node.js 등이 **Docker 컨테이너 안에** 설치되어 있다. -로컬 PC(WSL)에는 이런 도구들이 없으므로, 반드시 Docker 컨테이너를 통해 실행한다. - -``` -로컬 PC (WSL) -└── Docker - ├── sam-mng-1 ← PHP + Laravel (MNG 앱) - ├── sam-api-1 ← PHP + Laravel (API 앱) - ├── sam-mysql-1 ← MySQL DB - └── sam-nginx-1 ← Nginx 웹서버 -``` - -### 서버 환경 (Bare-metal — 개발/운영 동일 구조) - -서버에는 Docker가 없다. PHP 8.4, Nginx, MySQL 8.0이 직접 설치되어 있다. - -``` -개발 서버 (114.203.209.83) 운영 서버 (신규) -├── Nginx ├── Nginx -├── PHP-FPM (3 pools) ├── PHP-FPM (3 pools) -│ ├── api.sock │ ├── api.sock -│ ├── mng.sock │ ├── mng.sock -│ └── sales.sock │ └── sales.sock -├── MySQL 8.0 (samdb) ├── MySQL 8.0 (sam_prod) -├── Supervisor ├── Supervisor -│ ├── sam-api-worker (x1) │ ├── sam-api-worker (x1) -│ ├── sam-mng-worker (x2) │ ├── sam-mng-worker (x2) -│ └── sam-api-scheduler │ └── sam-api-scheduler -├── Node.js (React SSR :3000) ├── Node.js (React SSR :3000) -├── Jenkins (:8080) │ -├── Gitea (:3000) │ -├── /home/webservice/api ├── /home/webservice/api -├── /home/webservice/mng ├── /home/webservice/mng -├── /home/webservice/react ├── /home/webservice/react -└── /home/webservice/sales └── /home/webservice/sales -``` - -### 도메인 매핑 - -| 서비스 | 로컬 (Docker) | 개발 서버 | 운영 서버 | -|--------|--------------|----------|----------| -| React (사용자) | `dev.sam.kr` | `dev.codebridge-x.com` | `codebridge-x.com` | -| API | `api.sam.kr` | `api.dev.codebridge-x.com` | `api.codebridge-x.com` | -| MNG (관리자) | `mng.sam.kr` | `admin.codebridge-x.com` | `mng.codebridge-x.com` | -| Sales | `sales.sam.kr` | `sales.dev.codebridge-x.com` | `sales.codebridge-x.com` | -| 5130 (레거시) | `5130.sam.kr` | — | — | - -### 명령어 비교 (로컬 vs 개발 vs 운영) - -| 작업 | 로컬 (Docker) | 개발/운영 서버 (네이티브) | -|------|--------------|-------------------------| -| artisan 실행 | `docker exec sam-api-1 php artisan <명령>` | `cd /home/webservice/api && php artisan <명령>` | -| composer 실행 | `docker exec sam-api-1 composer install` | `cd /home/webservice/api && composer install` | -| 마이그레이션 | `docker exec sam-api-1 php artisan migrate` | 개발: `php artisan migrate` / 운영: `php artisan migrate --force` | -| 캐시 클리어 | `docker exec sam-mng-1 php artisan cache:clear` | `cd /home/webservice/mng && php artisan cache:clear` | -| Queue 재시작 | — | `sudo supervisorctl restart sam-api-worker:*` | - -### 로컬 Docker 명령어 패턴 - -```bash -# MNG 앱에서 artisan 명령 실행 -docker exec sam-mng-1 php artisan <명령어> - -# API 앱에서 artisan 명령 실행 -docker exec sam-api-1 php artisan <명령어> - -# 예시: 시더 실행 -docker exec sam-mng-1 php artisan db:seed --class=MngMenuSeeder - -# 예시: 마이그레이션 실행 (API에서만!) -docker exec sam-api-1 php artisan migrate - -# 예시: 캐시 클리어 -docker exec sam-mng-1 php artisan cache:clear -``` - -### 체크리스트 (명령 실행 시) - -- [ ] **로컬**: `php artisan` → `docker exec sam-mng-1 php artisan` 또는 `sam-api-1` 사용 -- [ ] **로컬**: `composer` → `docker exec sam-mng-1 composer` 또는 `sam-api-1` 사용 -- [ ] **서버**: `php artisan`, `composer` 직접 실행 (Docker 없음) -- [ ] **운영 서버 마이그레이션**: `--force` 플래그 필수 -- [ ] **마이그레이션은 반드시 API에서 실행** (로컬: `docker exec sam-api-1`, 서버: 직접) - ---- - -## 공동 개발 워크플로우 (필수) - -> **중요: 코드를 pull 받은 후 반드시 필요한 명령을 실행하세요!** - -### 브랜치 전략 - -| 브랜치 | 배포 대상 | 트리거 | 승인 | -|--------|----------|--------|------| -| `feature/*` | — | — | — | -| `develop` | 개발 서버 (`dev.codebridge-x.com`) | Push 시 자동 배포 | 불필요 | -| `main`/`master` | 운영 서버 (`codebridge-x.com`) | PR 머지 시 Jenkins 배포 | 팀장 승인 필수 | - -``` -feature/* ──→ develop ──→ main/master - (push) (PR merge) - ↓ ↓ - 개발 서버 운영 서버 - (자동 배포) (Jenkins CI/CD) -``` - -### 로컬 환경 (Docker) 업데이트 - -```bash -# 1. 코드 받기 (WSL에서 실행) -cd /home/aweso/sam/api -git pull - -cd /home/aweso/sam/mng -git pull - -# 2. 의존성 업데이트 (composer.json 변경 시) -docker exec sam-api-1 composer install -docker exec sam-mng-1 composer install - -# 3. DB 마이그레이션 (API에서만!) -docker exec sam-api-1 php artisan migrate - -# 4. 캐시 클리어 (설정 변경 시) -docker exec sam-api-1 php artisan config:clear -docker exec sam-mng-1 php artisan config:clear -``` - -### 개발 서버 업데이트 (자동) - -> `develop` 브랜치에 Push 시 Gitea Webhook → Jenkins가 자동으로 배포한다. -> 수동 배포가 필요한 경우: - -```bash -# API 프로젝트 -cd /home/webservice/api -git pull origin develop -composer install -php artisan migrate -php artisan config:clear - -# MNG 프로젝트 (마이그레이션 없음) -cd /home/webservice/mng -git pull origin develop -composer install -php artisan config:clear -``` - -### 운영 서버 배포 (Jenkins 자동화) - -> `main`/`master` 브랜치에 PR 머지 시 Jenkins가 자동으로 배포한다. -> 수동 배포는 **비상 절차**로만 사용한다. - -```bash -# 비상 수동 배포 (Jenkins 장애 시에만) -# API 프로젝트 -cd /home/webservice/api -git pull origin main -composer install --no-dev --optimize-autoloader -php artisan migrate --force -php artisan config:clear && php artisan cache:clear && php artisan route:cache && php artisan view:cache -sudo supervisorctl restart sam-api-worker:* - -# MNG 프로젝트 -cd /home/webservice/mng -git pull origin master -composer install --no-dev --optimize-autoloader -php artisan config:clear && php artisan cache:clear && php artisan view:cache -sudo supervisorctl restart sam-mng-worker:* -``` - -### 요약 표 - -| 작업 | 로컬 (Docker) | 개발 서버 | 운영 서버 | -|------|--------------|----------|----------| -| 배포 방식 | 수동 | Jenkins 자동 (develop push) | Jenkins 자동 (main/master PR) | -| git pull | WSL에서 직접 | Jenkins 자동 | Jenkins 자동 | -| composer install | `docker exec sam-api-1 composer install` | Jenkins 자동 | `--no-dev --optimize-autoloader` | -| migrate | `docker exec sam-api-1 php artisan migrate` | Jenkins 자동 | `--force` 플래그 포함 | -| config:clear | `docker exec sam-api-1 php artisan config:clear` | Jenkins 자동 | `route:cache` + `view:cache` 포함 | - -### 체크리스트 (pull 후) - -- [ ] API: `git pull` → `composer install` → `php artisan migrate` → `config:clear` -- [ ] MNG: `git pull` → `composer install` → `config:clear` (마이그레이션 없음) -- [ ] 운영 배포: `main`/`master`에 PR 머지 → Jenkins 자동 처리 (수동 금지) - ---- - -## 사용 가능한 Agents - -`~/.claude/agents/` 폴더에 있는 에이전트들: - -### 코드 품질 & 개발 - -| Agent | 모델 | 설명 | 출처 | -|-------|------|------|------| -| `code-reviewer` | sonnet | 코드 리뷰 (품질/보안/유지보수성), 메모리 학습 지원 | 공식 문서 패턴 | -| `debugger` | sonnet | 에러/테스트 실패 근본 원인 분석 및 수정 | 공식 문서 패턴 | -| `test-runner` | haiku | 테스트 실행 및 결과 분석/요약 | 커뮤니티 인기 | -| `security-auditor` | sonnet | OWASP Top 10 기반 보안 취약점 감사 | 커뮤니티 인기 | -| `performance-optimizer` | sonnet | N+1 쿼리, 알고리즘, 캐싱 최적화 | 커뮤니티 인기 | -| `refactoring-agent` | sonnet | 코드 구조 개선, SOLID 원칙, DRY 위반 제거 | 커뮤니티 인기 | -| `laravel-expert` | sonnet | Laravel 전문가 (SAM 프로젝트 환경 인지) | 커스텀 | - -### 워크플로우 & 문서 - -| Agent | 모델 | 설명 | 출처 | -|-------|------|------|------| -| `git-manager` | haiku | Git 브랜치/커밋/머지/PR 관리 | 커뮤니티 인기 | -| `doc-writer` | haiku | API 문서, README, 기술 가이드 작성 | 커뮤니티 인기 | -| `research-agent` | sonnet | 웹 리서치 및 자료 조사 | 기존 | -| `organizer-agent` | - | 프로젝트 구조화 및 정리 | 기존 | -| `proposal-agent` | - | 제안서 작성 | 기존 | - ---- - -## 사용 가능한 Skills - -`~/.claude/skills/` 폴더에 있는 스킬들 (슬래시 명령어로 사용): - -### 문서/프레젠테이션 - -| Skill | 설명 | -|-------|------| -| `pptx-skill` | PowerPoint 생성 | -| `ppt-auto-generator` | 마크다운/텍스트에서 PPT 생성 | -| `pdf-template-skill` | PDF 템플릿 분석/생성 | -| `text-analyzer-skill` | 텍스트 분석 및 PDF 구조 매핑 | -| `proposal-skill` | 제안서 생성 | -| `storyboard-generator` | 스토리보드 생성 | -| `design-skill` | 프레젠테이션 HTML 디자인 | - -### 코드 분석/시각화 - -| Skill | 설명 | -|-------|------| -| `code-flow-web-report` | 웹 앱 런타임 흐름 시각화 리포트 | -| `code-flow-web-doc-generator` | 소스 코드 호출/데이터 흐름 다이어그램 HTML 생성 | -| `codebase-analysis-web-report` | 코드베이스 아키텍처 인터랙티브 HTML 리포트 | -| `uml-generator` | UML 다이어그램 생성 | - -### 코드 품질 (levnikolaevich/claude-code-skills) - -| Skill | 설명 | 출처 | -|-------|------|------| -| `code-bug-finder` | 버그 자동 탐지 및 보고서 생성 | 기존 | -| `code-refactoring` | 리팩토링 권장사항/성능 분석/코드 패치 | 기존 | -| `code-commenter` | 소스 코드에 이해하기 쉬운 주석 추가 | 기존 | -| `async-await-keyword-fixer` | JS/TS 누락된 async/await 수정 | 기존 | -| `code-quality-checker` | DRY/KISS/YAGNI 위반 탐지 | levnikolaevich | -| `code-quality-auditor` | 코드 복잡도, 매직넘버 분석 | levnikolaevich | -| `code-principles-auditor` | DRY/KISS/YAGNI, TODO, DI 패턴 검사 | levnikolaevich | -| `dead-code-auditor` | 미사용 코드 탐지 | levnikolaevich | -| `build-auditor` | 컴파일러/타입 에러 검사 | levnikolaevich | -| `concurrency-auditor` | 레이스 컨디션 탐지 | levnikolaevich | -| `layer-boundary-auditor` | 레이어 위반, I/O 격리 검사 | levnikolaevich | -| `observability-auditor` | 로깅, 메트릭 적절성 검사 | levnikolaevich | -| `query-efficiency-auditor` | DB 쿼리 효율성 분석 | levnikolaevich | -| `dependencies-auditor` | 오래된 패키지, CVE 취약점 검사 | levnikolaevich | -| `regression-checker` | 기존 테스트 실행으로 사이드이펙트 탐지 | levnikolaevich | -| `story-quality-gate` | 코드리뷰 + 테스트 2단계 품질 검증 | levnikolaevich | - -### 테스트/커버리지 - -| Skill | 설명 | 출처 | -|-------|------|------| -| `app-comprehensive-test-generator` | 테스트 시나리오 생성/실행, QA 리포트 | 기존 | -| `coverage-improvement-planner` | 테스트 커버리지 분석 및 개선 계획 | 기존 | -| `test-coverage-auditor` | 테스트 커버리지 측정/분석 | levnikolaevich | -| `test-isolation-auditor` | 테스트 독립성/격리 검사 | levnikolaevich | -| `webapp-testing` | Playwright 기반 웹 앱 UI 테스트 | anthropics 공식 | - -### 보안 (Trail of Bits) - -| Skill | 설명 | 출처 | -|-------|------|------| -| `security-auditor` | 시크릿 노출, Injection, XSS 탐지 | levnikolaevich | -| `static-analysis` | CodeQL/Semgrep/SARIF 정적 분석 (3개 하위 스킬) | Trail of Bits | -| `insecure-defaults` | 위험한 기본 설정, 하드코딩 자격증명 탐지 | Trail of Bits | -| `sharp-edges` | 에러 유발 API, 위험한 디자인 패턴 탐지 | Trail of Bits | -| `differential-review` | 보안 중심 코드 변경 리뷰 | Trail of Bits | - -### 디버깅/로깅 - -| Skill | 설명 | -|-------|------| -| `system-debug-logger` | 에러/예외 자동 캡처 디버그 로깅 | -| `node-debug-logging-middleware` | Node.js Express/Koa 디버깅 로그 미들웨어 | - -### 프론트엔드/UI - -| Skill | 설명 | 출처 | -|-------|------|------| -| `frontend-design` | 프론트엔드 디자인 품질 향상 (AI slop 방지) | anthropics 공식 | -| `flutter-ux-hardening` | Flutter 앱 UI/UX 강화 | 기존 | -| `웹문서` | SAM 프로젝트 웹문서 디자인 표준 | 기존 | - -### 유틸리티 - -| Skill | 설명 | -|-------|------| -| `duplicate-file-cleaner` | 중복 이미지/미디어 파일 정리 | -| `npm-release-manager` | NPM 패키지 배포 자동화 | - -**사용 방법**: `/skill-name` 형식으로 호출 (예: `/code-quality-checker`) - ---- - -## 문서 작성 규칙 (개발팀 협약 - 필수 준수) - -> **경고: 개발자들이 `sam/docs`의 문서 작성 기법을 준용하기로 협약했습니다. 모든 문서 작성 시 반드시 따르세요!** - -### 참조 경로 - -- **인덱스**: `/home/aweso/sam/docs/INDEX.md` (전체 문서 목록 및 폴더 구조) -- **작업 전 확인**: 작업 유형에 맞는 문서를 `INDEX.md`에서 찾아 먼저 읽고 시작 - -### 폴더 선택 기준 (의미 기반 분류) - -| 폴더 | 질문 | 설명 | -|------|------|------| -| `plans/` | "무슨 작업을 할 것인가?" | 임시 개발 계획 (완료 후 삭제) | -| `standards/` | "어떻게 코드를 작성할 것인가?" | 코딩 컨벤션, 스타일 가이드 | -| `architecture/` | "왜 이렇게 설계하는가?" | 시스템 설계, 아키텍처 결정 | -| `rules/` | "무엇이 유효한 데이터인가?" | 비즈니스 규칙, 검증 규칙 | -| `specs/` | "무엇을 구현할 것인가?" | 기술 스펙, DB 스키마 | -| `guides/` | "어떻게 구현할 것인가?" | 단계별 구현 매뉴얼 | -| `features/` | 기능별 상세 | 기능 단위 심층 문서 | -| `changes/` | "무엇이 변경되었는가?" | 완료된 변경 이력 | - -### 파일명 규칙 - -- **일반 문서**: `kebab-case.md` (소문자 + 하이픈) 예: `api-rules.md`, `item-policy.md` -- **변경 이력**: `YYYYMMDD_short_description.md` 예: `20260109_handover_report_api.md` -- **폴더 인덱스**: `README.md` (대문자) -- **크기 목표**: 10KB 이하 -- **새 문서 작성 시**: 반드시 `docs/INDEX.md`에 추가 - -### 문서 구조 템플릿 - -#### 정책/규칙 문서 (`rules/`, `standards/`) - -```markdown -# 제목 - -> **작성일**: YYYY-MM-DD -> **상태**: 설계 확정 - ---- - -## 1. 개요 -### 1.1 목적 -### 1.2 핵심 원칙 - ---- - -## 2. 테이블 구조 (해당 시) -### 2.1 ERD 개요 - ---- - -## N. 비즈니스 규칙 -### N.1 검증 규칙 - ---- - -## N. API 엔드포인트 - ---- - -## 관련 문서 - ---- - -**최종 업데이트**: YYYY-MM-DD -``` - -#### 변경 이력 문서 (`changes/`) - -```markdown -# 변경 내용 요약 - -**날짜:** YYYY-MM-DD -**작업자:** Claude Code - -## 변경 개요 - -## 수정된 파일 -| 파일 | 변경 내용 | -|------|----------| - -## 상세 변경 사항 - -## 테스트 체크리스트 -- [x] 완료 항목 -- [ ] 미완료 항목 - -## 관련 문서 -``` - -### 작성 스타일 규칙 - -| 항목 | 규칙 | -|------|------| -| **언어** | 한글 기본, 코드/경로/기술 식별자만 영어 | -| **어조** | 서술형 ("X를 해야 한다" 아닌 "X 한다") | -| **경고** | `> **경고: ...**` 블록인용 형식 | -| **금지/필수** | `❌` 금지, `✅` 필수 접두사 | -| **우선순위** | `🔴 필수`, `🟡 중요`, `🟢 권장` | -| **섹션 번호** | `## 1.`, `### 1.1` 번호 매기기 | -| **규칙 번호** | R1, R2, R3... 순차 라벨 | -| **코드 블록** | 반드시 언어 지정 (```php, ```bash, ```json, ```sql) | -| **인라인 코드** | 파일 경로, 메서드명, 변수명, 컬럼명에 백틱 | -| **다이어그램** | `┌─┐│└─┘` 박스 문자, `→` 화살표 사용 | -| **구분선** | `---` 주요 섹션 사이마다 | -| **테이블** | API: `| Method | Path | 설명 |`, 필드: `| 필드 | 타입 | 설명 |` | - -### plans/ 워크플로우 - -1. 개발 계획 문서를 `plans/`에 작성 -2. 작업 진행 -3. 완료 후 결과물을 해당 폴더(`features/`, `changes/` 등)에 정리 -4. plan 문서 삭제 - -### 체크리스트 (문서 작성 시) - -- [ ] 적절한 폴더에 배치 (위 폴더 선택 기준 참고) -- [ ] `kebab-case.md` 파일명 사용 -- [ ] 문서 구조 템플릿 준수 -- [ ] 한글 기본, 기술 용어만 영어 -- [ ] 코드 블록에 언어 지정 -- [ ] `docs/INDEX.md`에 새 문서 등록 -- [ ] 10KB 이하 크기 유지 - ---- - -## PPT / 프레젠테이션 제작 규칙 (필수 준수) - -> **경고: 모든 프레젠테이션 및 문서 제작 시 반드시 따르세요!** - -### 회사 정보 - -| 항목 | 값 | -|------|------| -| **공식 회사명** | **(주)코드브릿지엑스** | -| **서비스명** | **SAM** (Smart Automation Management) | -| **푸터 표기 예시** | `SAM 서비스 요금 안내 | (주)코드브릿지엑스` | - -### 금지 사항 - -``` -❌ "주일/경동" — 문서, 슬라이드, 푸터 어디에도 사용 금지 -❌ "주일", "경동" 단독 사용 금지 -❌ 내부 제조사(주일/경동) 이름을 외부 문서에 노출 금지 -``` - -> **배경**: 주일/경동은 SAM을 기반으로 만든 내부 제조업체 이름이며, 대외 문서에 노출되어서는 안 된다. -> 모든 대외 문서의 회사명은 **(주)코드브릿지엑스**를 사용한다. - -### SAM BI (Brand Identity) 이미지 - -**프로젝트 내 경로**: `/home/aweso/sam/docs/assets/bi/` - -| 파일 | 용도 | 배경 | -|------|------|------| -| `sam_bi_black.png` | 밝은 배경 슬라이드 | 투명 배경, 검정 로고 | -| `sam_bi_white.png` | 다크 배경 슬라이드 | 투명 배경, 흰색 로고 | -| `sam_bi_blue.png` | 청색 테마 슬라이드 | 투명 배경, 파란 로고 | -| `sam_bi_green.png` | 녹색 테마 슬라이드 | 녹색 배경, 흰색 로고 | -| `sam_bi_red.png` | 적색/대외비 슬라이드 | 적색 배경, 흰색 로고 | -| `sam_bi_orange.png` | 주황 포인트 슬라이드 | 주황 배경, 흰색 로고 | -| `sam_bi_purple.png` | 보라 테마 슬라이드 | 보라 배경, 흰색 로고 | - -### PPT 슬라이드 제작 시 적용 규칙 - -1. **표지(slide-01)에 BI 로고 필수** — 배경색에 맞는 BI 이미지 사용 -2. **푸터에 회사명**: `(주)코드브릿지엑스` (주일/경동 절대 금지) -3. **BI 로고 + "SAM" 텍스트** 조합 사용 권장 -4. **배경색별 BI 선택**: - - 다크 배경 → `sam_bi_white.png` - - 밝은 배경 → `sam_bi_black.png` - - 테마 컬러 배경 → 해당 색상 BI (green, blue, red 등) - -### 체크리스트 (PPT 제작 시) - -- [ ] 회사명: (주)코드브릿지엑스 사용 -- [ ] "주일/경동" 미포함 확인 -- [ ] 표지에 SAM BI 로고 포함 -- [ ] 푸터에 (주)코드브릿지엑스 표기 -- [ ] 배경색에 맞는 BI 색상 선택 diff --git a/sam/docs/INDEX.md b/sam/docs/INDEX.md deleted file mode 100644 index 3bee9ea..0000000 --- a/sam/docs/INDEX.md +++ /dev/null @@ -1,420 +0,0 @@ -# SAM 프로젝트 문서 인덱스 - -> **Claude Code 작업 전 필수 확인** — 작업 유형에 맞는 문서를 먼저 읽고 시작하세요. -> **최종 갱신**: 2026-03-08 - ---- - -## 🎯 작업별 필수 문서 - -| 작업 유형 | 필수 문서 | 용도 | -|----------|----------|------| -| **API 개발** | `standards/api-rules.md` | Service-First, FormRequest, i18n 규칙 | -| **DB 변경** | `system/database/README.md` | 테이블 구조, 관계, 컬럼 규칙 | -| **새 기능 구현** | `system/overview.md` | 전체 아키텍처 이해 | -| **보안 관련** | `system/security-policy.md` | 인증/인가, 보안 규칙 | -| **Git 커밋** | `standards/git-conventions.md` | 커밋 메시지, 브랜치 전략 | -| **품질 검증** | `standards/quality-checklist.md` | 코드 품질 체크리스트 | -| **Swagger 작성** | `guides/swagger-guide.md` | API 문서 작성 방법 | -| **품목관리** | `rules/item-policy.md` | 품목 정책 (유형, 예약어, API 규칙) | -| **단가관리** | `rules/pricing-policy.md` | 원가/판매가 계산, 리비전 관리 | -| **견적관리** | `features/quotes/README.md` | 견적 시스템, BOM 계산, 10단계 로직 | -| **결재관리** | `features/approvals/README.md` | 결재 시스템 (워크플로우, API, UI) | -| **운영 배포** | `plans/production-deployment-plan.md` | 운영 환경 배포 계획 | -| **서버 운영** | `deploys/ops-manual/README.md` | 서버 운영 매뉴얼 | -| **MES 개발** | `projects/mes/README.md` | MES 프로젝트 개요 | - ---- - -## 📁 폴더 구조 - -``` -docs/ -├── system/ # 시스템 현황 — 아키텍처, DB 스키마, 인프라 (architecture/ + specs/ 통합) -├── standards/ # 개발 표준 — "어떻게 코드를 작성할 것인가" -├── rules/ # 비즈니스 규칙 — "무엇이 유효한 데이터인가" -├── features/ # 기능별 상세 — 도메인별 기능 문서 -├── guides/ # 구현 가이드 — "어떻게 구현할 것인가" -├── quickstart/ # 빠른 시작 — 핵심 요약, 명령어 -├── plans/ # 작업 추적 — 예정 → 진행 → 완료 → archive/ -├── projects/ # 프로젝트 자료 — 프로젝트성 분석, 설계, 참고 -├── deploys/ # 운영 매뉴얼 — 서버 운영, 배포 -├── changes/ # 변경 이력 -├── data/ # 데이터 분석 -├── history/ # 히스토리 기록 -├── api/ # API 통합 문서 -├── requests/ # 요청/기획 문서 -└── assets/ # BI 등 정적 자산 -``` - ---- - -## 📚 폴더별 문서 목록 - -### system/ — 시스템 현황 -> 아키텍처, DB 스키마, 기술 스펙, 인프라 (기존 architecture/ + specs/ 통합) - -| 문서 | 설명 | -|------|------| -| [overview.md](system/overview.md) | 전체 시스템 아키텍처 (api/react/mng 구조, 기술 스택) | -| [api-structure.md](system/api-structure.md) | API 서버 구조 (~1,027 엔드포인트, 18 도메인) | -| [react-structure.md](system/react-structure.md) | React 프론트엔드 구조 (249 페이지, 612 컴포넌트) | -| [mng-structure.md](system/mng-structure.md) | MNG 관리자 패널 구조 (171 컨트롤러, 436 뷰) | -| [docker-setup.md](system/docker-setup.md) | Docker 환경 + CI/CD (7 서비스, Jenkins) | -| [database/README.md](system/database/README.md) | DB 스키마 인덱스 (220 모델, 32 도메인, 459 마이그레이션) | - -**DB 도메인별 스키마:** - -| 문서 | 포함 도메인 | -|------|-----------| -| [database/tenants.md](system/database/tenants.md) | 테넌트, 사용자, 권한 (63 모델) | -| [database/products.md](system/database/products.md) | 제품, 품목, 설계 (21 모델) | -| [database/sales.md](system/database/sales.md) | 영업, 수주, 견적 (18 모델) | -| [database/production.md](system/database/production.md) | 생산, 시공, 자재, 품질 (20 모델) | -| [database/finance.md](system/database/finance.md) | 재무, 회계 | -| [database/hr.md](system/database/hr.md) | 인사, 면접 | -| [database/documents.md](system/database/documents.md) | 문서, 전자서명 (19 모델) | -| [database/commons.md](system/database/commons.md) | 공통, 게시판, 감사 (17 모델) | -| [database/stats.md](system/database/stats.md) | 통계 (21 모델, sam_stat DB) | -| [database/codebridge-separation.md](system/database/codebridge-separation.md) | codebridge DB 분리 (MNG 전용 118 테이블, 78 모델) | - -**이관 완료 (architecture/ + specs/ → system/):** - -| 문서 | 설명 | -|------|------| -| [security-policy.md](system/security-policy.md) | 보안 정책 (다층 방어, Sanctum, RBAC) | -| [scaling-roadmap.md](system/scaling-roadmap.md) | 10K 테넌트 스케일링 로드맵 | -| [ai-automation-vision.md](system/ai-automation-vision.md) | SAM AI 자동화 비전 및 장기 로드맵 | -| [board-system-spec.md](system/board-system-spec.md) | 게시판 시스템 설계 스펙 | -| [item-master-integration.md](system/item-master-integration.md) | 품목 마스터 통합 설계 | -| [remote-work-setup.md](system/remote-work-setup.md) | 원격 개발 설정 (DEPRECATED) | -| [erp-analysis/](system/erp-analysis/) | ERP 스토리보드 분석 (9개 파일) | - ---- - -### standards/ — 개발 표준 -> 코딩 컨벤션, 스타일 가이드, 품질 기준 - -| 문서 | 설명 | 필수 확인 시점 | -|------|------|--------------| -| [api-rules.md](standards/api-rules.md) | API 개발 규칙 (Service-First, FormRequest, i18n) | API 개발 전 | -| [git-conventions.md](standards/git-conventions.md) | Git 커밋 메시지, 브랜치 전략 | 커밋 전 | -| [quality-checklist.md](standards/quality-checklist.md) | 코드 품질 체크리스트 | PR 전 | -| [pagination-policy.md](standards/pagination-policy.md) | 페이지네이션 표준 | 목록 API 구현 시 | -| [options-column-policy.md](standards/options-column-policy.md) | JSON options 컬럼 표준 정책 (마이그레이션, 모델, 쿼리) | 테이블 생성/확장 시 | - ---- - -### rules/ — 비즈니스 규칙 -> 도메인 로직, 검증 규칙, 정책 - -| 문서 | 설명 | 필수 확인 시점 | -|------|------|--------------| -| [README.md](rules/README.md) | 비즈니스 규칙 개요 | 도메인 로직 구현 전 | -| [item-policy.md](rules/item-policy.md) | 품목 정책 (유형, 예약어, API 규칙) | 품목 관련 작업 전 | -| [pricing-policy.md](rules/pricing-policy.md) | 단가 정책 (원가/판매가, 리비전) | 단가 관련 작업 전 | -| [customer-pricing.md](rules/customer-pricing.md) | 고객 안내용 서비스 요금표 | 고객 요금 안내 시 | -| [partner-commission.md](rules/partner-commission.md) | 영업파트너 수당 체계 및 정산 | 수당/정산 관련 작업 전 | -| [billing-policy.md](rules/billing-policy.md) | 내부용 원가/마진/코드참조 (CONFIDENTIAL) | 과금 코드 개발 전 | -| [client-policy.md](rules/client-policy.md) | 고객사 관리 정책 | 고객 관련 작업 전 | -| [attendance-api.md](rules/attendance-api.md) | 근태 API 규칙 | 근태 관련 작업 전 | -| [department-tree-api.md](rules/department-tree-api.md) | 부서 트리 API 규칙 | 부서 관련 작업 전 | -| [employee-api.md](rules/employee-api.md) | 직원 API 규칙 | 직원 관련 작업 전 | -| [numbering-rules.md](rules/numbering-rules.md) | 채번규칙 (패턴 기반 자동 번호 생성) | 채번 로직 수정 전 | - ---- - -### features/ — 기능별 문서 -> 도메인별 기능 상세 (기능 설명 + 엔드포인트 경로 + Swagger 참조) - -| 문서 | 설명 | -|------|------| -| [quotes/README.md](features/quotes/README.md) | 견적 시스템 (BOM 계산, 10단계 로직) | -| [boards/README.md](features/boards/README.md) | 게시판 시스템 구현 | -| [boards/mng-implementation.md](features/boards/mng-implementation.md) | MNG 게시판 구현 상세 | -| [hr/attendance-management-spec.md](features/hr/attendance-management-spec.md) | 근태관리 기획서 | -| [hr/hr-api-analysis.md](features/hr/hr-api-analysis.md) | HR API 분석 (근태/직원/부서) | -| [barobill-kakaotalk/README.md](features/barobill-kakaotalk/README.md) | 바로빌 카카오톡 + 세금계산서 연동 | -| ~~business-card-request.md~~ | 명함신청 관리 (DB 마이그레이션만 존재, 문서 미작성) | -| [sales/README.md](features/sales/README.md) | 영업 관리 (면접 시나리오 포함) | -| [crm/README.md](features/crm/README.md) | CRM (거래처, 미수금, 미지급금) | -| [finance/README.md](features/finance/README.md) | 재무 관리 (14개 하위 문서) | -| [card-vehicle/README.md](features/card-vehicle/README.md) | 법인카드·차량 관리 | -| [settlement/README.md](features/settlement/README.md) | 정산 관리 | -| [esign/README.md](features/esign/README.md) | 전자서명 (계약·OTP·PDF 합성) | -| [documents/README.md](features/documents/README.md) | 문서관리 (EAV 기반 서식·결재) | -| [ai/README.md](features/ai/README.md) | AI 분석 리포트 (Gemini 연동) | -| [equipment/README.md](features/equipment/README.md) | 설비관리 (MNG 전용) | -| [approvals/README.md](features/approvals/README.md) | 결재관리 시스템 (순차결재, 보류/전결/참조/복사재기안) | -| [planning/README.md](features/planning/README.md) | 주일기업 기획 (견적/프로젝트/사진대지/회의록/AI) | -| [rd/README.md](features/rd/README.md) | R&D (조직도/AI견적/기획디자인/디자인 인사이트/사운드 로고/CM송) | -| [rd/planning-design.md](features/rd/planning-design.md) | 기획디자인 스토리보드 에디터 기술 명세 | -| [rd/design-insight.md](features/rd/design-insight.md) | 디자인 인사이트 UI/UX 연구 도구 (100종 패턴, AI 프롬프트) | -| [rd/sound-logo-studio.md](features/rd/sound-logo-studio.md) | 사운드 로고 스튜디오 (시퀀서 + Gemini TTS + Lyria BGM 합성) | - ---- - -### guides/ — 구현 가이드 -> 특정 기능 구현을 위한 단계별 매뉴얼 - -| 문서 | 설명 | 필수 확인 시점 | -|------|------|--------------| -| [swagger-guide.md](guides/swagger-guide.md) | Swagger API 문서 작성법 | API 문서 작성 전 | -| [file-storage-guide.md](guides/file-storage-guide.md) | 파일 업로드/다운로드 구현 | 파일 기능 구현 전 | -| [item-management-migration.md](guides/item-management-migration.md) | Item 시스템 전환 가이드 | 마이그레이션 작업 전 | -| [project-launch-roadmap.md](guides/project-launch-roadmap.md) | 런칭 준비 현황 | 런칭 관련 작업 시 | -| [production-env-sync.md](guides/production-env-sync.md) | 운영 전환 시 .env 동기화 | 테스트→운영 전환 시 | -| [server-how-it-works.md](guides/server-how-it-works.md) | 서버 동작 원리 | 신규 합류 시 | -| [nginx-fastcgi-guide.md](guides/nginx-fastcgi-guide.md) | Nginx & FastCGI 가이드 | 서버 이해 시 | -| [php-fpm-guide.md](guides/php-fpm-guide.md) | PHP-FPM 가이드 | 서버 이해 시 | -| [jenkins-setup-guide.md](guides/jenkins-setup-guide.md) | Jenkins CI/CD 셋업 | Jenkins 설치/설정 시 | -| [auto-login-guide.md](guides/auto-login-guide.md) | MNG→DEV 자동 로그인 | 자동 로그인 구현 시 | -| [erp-api-list.md](guides/erp-api-list.md) | ERP API 목록 (List vs Detail 구분) | 프론트 API 연동 시 | -| [erp-api-detail.md](guides/erp-api-detail.md) | ERP API 상세 스펙 | 프론트 API 연동 시 | -| [table-design-guide.md](guides/table-design-guide.md) | 테이블 설계 가이드 (비전문가용, options JSON 패턴) | 테이블 구조 이해 시 | -| [item-master-guide.md](guides/item-master-guide.md) | 품목기준관리 페이지-섹션-필드 구조 | 품목 UI 구현 시 | -| [item-master-items-api.md](guides/item-master-items-api.md) | ItemMaster & Items API 문서 | 품목 API 연동 시 | -| [ai-management.md](guides/ai-management.md) | AI 관리 종합 가이드 (아키텍처, 버전 이력, 온보딩) | AI 관련 작업 시 | -| [ai-model-update-workflow.md](guides/ai-model-update-workflow.md) | AI 모델 업데이트 표준 절차 (7단계) | AI 모델 변경 시 | -| [ai-config-settings.md](guides/ai-config-settings.md) | AI 설정 기술문서 (DB 구조, 메서드) | AI 설정 구현 시 | - ---- - -### quickstart/ — 빠른 시작 -> 핵심 규칙 요약, 자주 쓰는 명령어 - -| 문서 | 설명 | -|------|------| -| [quick-start.md](quickstart/quick-start.md) | 프로젝트 핵심 규칙 요약 | -| [dev-commands.md](quickstart/dev-commands.md) | 일상 개발 명령어 모음 | - ---- - -### plans/ — 작업 추적 -> 예정 → 진행 → 완료 → archive/ (이미 정리 완료, 현행 유지) - -| 문서 | 설명 | -|------|------| -| [index_plans.md](plans/index_plans.md) | 계획 인덱스 (ACTIVE + PLANNED) | -| [GUIDE.md](plans/GUIDE.md) | 계획 문서 작성 가이드 | -| [fire-shutter-drawing-generator-plan.md](plans/fire-shutter-drawing-generator-plan.md) | 방화셔터 도면생성 기능 기획서 (가이드레일 단면도 + 셔터박스 + 3D 렌더링) | - ---- - -### projects/ — 프로젝트 자료 -> 프로젝트성 분석, 설계, 참고 자료 (지속 보관) - -| 프로젝트 | 문서 | 설명 | -|---------|------|------| -| [index_projects.md](projects/index_projects.md) | 프로젝트 인덱스 | | -| **MES** | [README.md](projects/mes/README.md) | MES 프로젝트 개요 | -| **MES** | [MES_PROJECT_ROADMAP.md](projects/mes/MES_PROJECT_ROADMAP.md) | 개발 로드맵 | -| **5130 이관** | [MASTER_PLAN.md](projects/5130-migration/MASTER_PLAN.md) | 레거시 이관 마스터 플랜 | -| **API 연동** | [MASTER_PLAN.md](projects/api-integration/MASTER_PLAN.md) | React↔API 연동 | -| **Legacy** | [draw-module.md](projects/legacy-5130/draw-module.md) | 레거시 드로우 모듈 | -| **견적** | [quotation/](projects/quotation/) | 견적 프로젝트 자료 | -| **전자서명** | [e-sign/](projects/e-sign/) | 전자서명 프로젝트 자료 | -| **조직도** | [org-chart/README.md](projects/org-chart/README.md) | 조직도 관리 (트리형, 드래그앤드롭, 숨기기) | - ---- - -### deploys/ — 운영 매뉴얼 -> 서버 운영, 배포 (현행 유지) - -| 문서 | 설명 | -|------|------| -| [ops-manual/README.md](deploys/ops-manual/README.md) | 서버 운영 매뉴얼 (11부 구성) | - ---- - -### changes/ — 변경 이력 -> 파일명 형식: `YYYYMMDD_description.md` - -| 문서 | 설명 | -|------|------| -| [20260304_eaccount_infinite_loop_fix.md](changes/20260304_eaccount_infinite_loop_fix.md) | 계좌 입출금내역 부분 월 조회 시 무한루프 크래시 수정 | -| [20260306_purchase_request_payment_method.md](changes/20260306_purchase_request_payment_method.md) | 품의서 지급방법 UI 개선 (구매품의서 추가, 비용정산품의서 행별 변경) | - ---- - -### data/ — 데이터 분석 - -| 문서 | 설명 | -|------|------| -| [analysis/item-db-analysis.md](data/analysis/item-db-analysis.md) | Item DB/API 분석 최종본 | -| [analysis/bom-item-mapping-analysis.md](data/analysis/bom-item-mapping-analysis.md) | BOM-품목 매핑 분석 | - -### contracts/ - 전자계약서 버전 관리 -> DOCX 배포본 + Markdown 추적본 + 자동화 스크립트 - -| 문서 | 설명 | -|------|------| -| [CHANGELOG.md](contracts/CHANGELOG.md) | 전체 개정이력 | -| [revisions.json](contracts/revisions.json) | 개정 데이터 | -| [docx/](contracts/docx/) | DOCX 배포본 (전자서명용 4종, 바로 사용 가능) | -| [markdown/](contracts/markdown/) | Markdown 추적본 (Git diff용 4종) | -| [scripts/extract_to_markdown.py](contracts/scripts/extract_to_markdown.py) | DOCX → Markdown 추출 | -| [scripts/sync_check.py](contracts/scripts/sync_check.py) | DOCX ↔ Markdown 동기화 검증 | - -### plans/ - 개발 계획 -> 임시 개발 계획 문서 (작업 완료 후 정리 → 삭제) - -| 문서 | 설명 | -|------|------| -| [SAM_ERP_Storyboard_D1.4_260116.md](plans/SAM_ERP_Storyboard_D1.4_260116.md) | ERP 전체 스토리보드 D1.4 (167p PDF → 마크다운 변환, 14개 섹션 146개 화면) | -| [SAM_ERP_Storyboard_D1.4.md](plans/SAM_ERP_Storyboard_D1.4.md) | ERP 스토리보드 D1.4 AI 최적화 버전 (구조화된 한글 마크다운, 15개 섹션) | -| [SAM_ERP_회계관리_Storyboard_D1.6.md](plans/SAM_ERP_회계관리_Storyboard_D1.6.md) | ERP 회계관리 스토리보드 D1.6 (65p PDF → 마크다운 변환) | -| [SAM_General_Rule_Storyboard_D1.0.md](plans/SAM_General_Rule_Storyboard_D1.0.md) | General Rule 스토리보드 D1.0 (43p PDF → 마크다운 변환, UIUX 공통 규칙) | -| [production-deployment-plan.md](plans/production-deployment-plan.md) | 운영 환경 배포 계획 (CI/CD, 서버 아키텍처) | -| [attendance-management-plan.md](plans/attendance-management-plan.md) | 근태현황 개발 계획 (Phase 1~2, HTMX 기반) | -| [leave-management-plan.md](plans/leave-management-plan.md) | 휴가관리 모듈 개발 계획 (연차 발생/신청/승인/정책) | -| [approval-management-system-plan.md](plans/approval-management-system-plan.md) | 결재관리 시스템 기획서 (전자결재 전체 설계: 기안~회수, DB 설계, 17개 양식, 4 Phase) | -| [block-builder-evolution-plan.md](plans/block-builder-evolution-plan.md) | 양식 디자이너(Block Builder) 고도화 계획 (6 Phase: 렌더러→결재→동적테이블→수식→인쇄→Legacy 대체) | -| [design-insight-menu-plan.md](plans/design-insight-menu-plan.md) | UI/UX 디자인 인사이트 연구 메뉴 기획서 (레퍼런스 수집, 화면 분석, 패턴 라이브러리, Before/After) | -| [sound-logo-generator-plan.md](plans/sound-logo-generator-plan.md) | 사운드 로고 생성기 기획서 (Web Audio + Gemini AI 어시스트 + Lyria 자동 생성) | - -### features/ - 기능별 문서 - -| 문서 | 설명 | -|------|------| -| [barobill-kakaotalk/README.md](features/barobill-kakaotalk/README.md) | 바로빌 카카오톡 (알림톡/친구톡) 연동 | -| [boards/README.md](features/boards/README.md) | 게시판 시스템 구현 | -| [boards/mng-implementation.md](features/boards/mng-implementation.md) | MNG 게시판 구현 상세 | -| [hr/attendance-management-spec.md](features/hr/attendance-management-spec.md) | 근태관리 기획서 (화면/데이터/비즈니스규칙/API) | -| [hr/hr-api-analysis.md](features/hr/hr-api-analysis.md) | HR API 분석 (근태/직원/부서) | -| [quotes/README.md](features/quotes/README.md) | 견적 시스템 분석 (BOM 계산, 10단계 로직) | -| [business-card-request.md](features/business-card-request.md) | 명함신청 관리 (3단계 워크플로우: 요청→제작의뢰→처리완료) | -| [academy/fire-shutter-image-prompts.md](features/academy/fire-shutter-image-prompts.md) | 방화셔터 백과사전 이미지 생성 프롬프트 (Gemini용) | -| [approvals/README.md](features/approvals/README.md) | 결재관리 시스템 개요 (아키텍처, DB, 상태관리, 권한) | -| [approvals/workflows.md](features/approvals/workflows.md) | 결재관리 워크플로우 상세 (승인/반려/회수/보류/전결/복사재기안) | -| [approvals/api-reference.md](features/approvals/api-reference.md) | 결재관리 API 명세 (20개 엔드포인트) | -| [approvals/ui-screens.md](features/approvals/ui-screens.md) | 결재관리 UI 화면 구성 (Blade + HTMX) | - -### projects/ - 프로젝트별 문서 - -| 프로젝트 | 문서 | 설명 | -|---------|------|------| -| **MES** | [README.md](projects/mes/README.md) | MES 프로젝트 개요 | -| **MES** | [MES_PROJECT_ROADMAP.md](projects/mes/MES_PROJECT_ROADMAP.md) | 개발 로드맵 | -| **Legacy** | [draw-module.md](projects/legacy-5130/draw-module.md) | 레거시 드로우 모듈 | -| **조직도** | [README.md](projects/org-chart/README.md) | 조직도 관리 (Alpine.js + SortableJS) | - -### history/ - 히스토리 - -| 기간 | 문서 | -|------|------| -| **2025-11** | [item-master-gap-analysis.md](history/2025-11/item-master-gap-analysis.md), [item-master-spec.md](history/2025-11/item-master-spec.md), [front-requests/](history/2025-11/front-requests/), [item-master-archived/](history/2025-11/item-master-archived/) | -| **2025-09** | [checkpoint.md](history/2025-09/checkpoint.md), [database-schema.md](history/2025-09/database-schema.md) | -| **Roadmaps** | [december-2025.md](history/roadmaps/december-2025.md) | - ---- - -### 서브프로젝트 문서 - -각 서브프로젝트는 독립적인 `docs/` 디렉토리를 가집니다. - -| 프로젝트 | 문서 경로 | 설명 | -|---------|----------|------| -| **API** | [api/docs/](../api/docs/) | REST API 프로젝트 | -| **MNG** | [mng/docs/](../mng/docs/) | Plain Laravel 관리자 | -| **React** | [react/docs/](../react/docs/) | Next.js 프론트엔드 | - ---- - -## 📝 문서 작성 규칙 - -### 폴더 선택 기준 - -| 질문 | 폴더 | -|------|------| -| "시스템이 현재 어떤 상태인가?" | `system/` | -| "어떻게 코드를 작성할 것인가?" | `standards/` | -| "무엇이 유효한 데이터인가?" | `rules/` | -| "이 기능은 어떻게 동작하는가?" | `features/` | -| "어떻게 구현할 것인가?" | `guides/` | -| "무슨 작업을 할 것인가?" | `plans/` | -| "프로젝트 자료를 보관하고 싶다" | `projects/` | -| "무엇이 변경되었는가?" | `changes/` | - -### 파일명 규칙 - -| 유형 | 규칙 | 예시 | -|------|------|------| -| 기술 문서 (코드 참조) | 영문 kebab-case | `api-rules.md`, `database-schema.md` | -| 업무/비즈니스 문서 | 한글 허용 | `영업파트너가이드북.md`, `수당지급.md` | -| 변경 이력 | `YYYYMMDD_description.md` | `20260205_sus_inspection_template.md` | -| 폴더 인덱스 | `README.md` (대문자) | `features/finance/README.md` | -| **혼용 금지** | 한글+영문 섞지 않음 | ❌ `영업partner가이드.md` | - -### 크기 제한 -- **목표**: 10KB 이하 -- 초과 시 도메인별로 분할 - -### 문서 구조 템플릿 - -#### 정책/규칙 문서 (`rules/`, `standards/`) - -```markdown -# 제목 - -> **작성일**: YYYY-MM-DD -> **상태**: 설계 확정 - ---- - -## 1. 개요 -## 2. 핵심 원칙 -## 3. 상세 규칙 -## 4. API 엔드포인트 (해당 시) -## 관련 문서 - ---- - -**최종 업데이트**: YYYY-MM-DD -``` - -#### 변경 이력 문서 (`changes/`) - -```markdown -# 변경 내용 요약 - -**날짜:** YYYY-MM-DD - -## 변경 개요 -## 수정된 파일 -## 상세 변경 사항 -## 테스트 체크리스트 -``` - -### 작성 스타일 - -| 항목 | 규칙 | -|------|------| -| **언어** | 한글 기본, 코드/경로/기술 식별자만 영어 | -| **어조** | 서술형 ("X 한다") | -| **경고** | `> **경고: ...**` 블록인용 | -| **금지/필수** | `❌` 금지, `✅` 필수 | -| **우선순위** | `🔴 필수`, `🟡 중요`, `🟢 권장` | -| **코드 블록** | 반드시 언어 지정 (```php, ```bash 등) | -| **인라인 코드** | 파일 경로, 메서드명, 변수명에 백틱 | -| **구분선** | `---` 주요 섹션 사이 | - -### 새 문서 작성 시 체크리스트 -- [ ] 적절한 폴더에 배치 -- [ ] 파일명 규칙 준수 -- [ ] 문서 구조 템플릿 준수 -- [ ] 이 INDEX.md에 등록 - ---- - -## 🔄 문서 정비 진행 현황 - -> 참조: [docs-comprehensive-update-plan.md](plans/docs-comprehensive-update-plan.md) - -| Phase | 작업 | 상태 | -|-------|------|------| -| **Phase 0** | 문서 정책 재정립 | ✅ 완료 | -| **Phase 1** | 시스템 현황 문서화 (DB, API, React, MNG, Docker) | ✅ 완료 (14개 문서) | -| **Phase 2** | 기존 문서 정비 (architecture/+specs/ → system/ 이관) | ⏳ 대기 | -| **Phase 3** | 신규 도메인 기능 문서 작성 | ⏳ 대기 | -| **Phase 4** | 최종 검증 및 INDEX 갱신 | ⏳ 대기 | \ No newline at end of file diff --git a/sam/docs/assets/bi/sam_bi_black.png b/sam/docs/assets/bi/sam_bi_black.png deleted file mode 100755 index e83d284..0000000 Binary files a/sam/docs/assets/bi/sam_bi_black.png and /dev/null differ diff --git a/sam/docs/assets/bi/sam_bi_blue.png b/sam/docs/assets/bi/sam_bi_blue.png deleted file mode 100755 index a5cde92..0000000 Binary files a/sam/docs/assets/bi/sam_bi_blue.png and /dev/null differ diff --git a/sam/docs/assets/bi/sam_bi_green.png b/sam/docs/assets/bi/sam_bi_green.png deleted file mode 100755 index ff0afc0..0000000 Binary files a/sam/docs/assets/bi/sam_bi_green.png and /dev/null differ diff --git a/sam/docs/assets/bi/sam_bi_orange.png b/sam/docs/assets/bi/sam_bi_orange.png deleted file mode 100755 index 85aceb3..0000000 Binary files a/sam/docs/assets/bi/sam_bi_orange.png and /dev/null differ diff --git a/sam/docs/assets/bi/sam_bi_purple.png b/sam/docs/assets/bi/sam_bi_purple.png deleted file mode 100755 index 7b9dc0c..0000000 Binary files a/sam/docs/assets/bi/sam_bi_purple.png and /dev/null differ diff --git a/sam/docs/assets/bi/sam_bi_red.png b/sam/docs/assets/bi/sam_bi_red.png deleted file mode 100755 index 93d566f..0000000 Binary files a/sam/docs/assets/bi/sam_bi_red.png and /dev/null differ diff --git a/sam/docs/assets/bi/sam_bi_white.png b/sam/docs/assets/bi/sam_bi_white.png deleted file mode 100755 index 34a5a8c..0000000 Binary files a/sam/docs/assets/bi/sam_bi_white.png and /dev/null differ diff --git a/sam/docs/brochure/README.md b/sam/docs/brochure/README.md deleted file mode 100644 index 9fc78d2..0000000 --- a/sam/docs/brochure/README.md +++ /dev/null @@ -1,370 +0,0 @@ -# SAM 브로셔 버전 관리 - -> **작성일**: 2026-03-01 -> **상태**: 운영 중 - ---- - -## 1. 개요 - -SAM CEO Dashboard 및 ERP/MES 영업 브로셔의 버전별 디자인 변천을 기록한다. -모든 브로셔는 세로형(9:16) HTML로 작성하며, `pptx-skill`(html2pptx.js)로 PPTX 변환한다. - ---- - -## 2. 버전 요약 - -| 버전 | 대상 | 테마 | 배경색 | 주 액센트 | 비고 | -|------|------|------|--------|-----------|------| -| **v1** | 전체 고객 | 다크 | `#0F2439` | `#10B981` (에메랄드) | SAM ERP/MES 범용 | -| **v2** | 경영진 | 다크 | `#0B1929` | `#0EA5E9` (스카이블루) | CEO Dashboard 초판 | -| **v3** | 경영진 | 다크 | `#0B1929` | `#0EA5E9` (스카이블루) | v2 개선, Before/After 추가 | -| **v4** | 경영진 | 라이트 | `#F8FAFC` | `#0EA5E9` (스카이블루) | v3의 밝은 배경 변환 | -| **v5** | 경영진 | 프리미엄 그래디언트 | `#0F172A→#312E81` | `#FBBF24` (골드) | 글래스모피즘 + 골드 | -| **v6** | 경영진 | 코퍼레이트 블루 | `#FFFFFF` | `#2563EB` (블루) | 대기업/공공기관 스타일 | -| **v7** | 경영진 | 웜 그레이 + 틸 | `#FAFAF9` | `#0D9488` (틸) | IT/SaaS 스타일 | -| **v8** | 경영진 | 투톤 스플릿 | `#1E293B` / `#FFFFFF` | `#F97316` (오렌지) | 금융/컨설팅 스타일 | -| **v9** | 경영진 | 미니멀 화이트 | `#FFFFFF` | `#6366F1` (인디고) | Apple/디자인 에이전시 | - ---- - -## 3. 버전별 상세 - -### 3.1 v1 — SAM ERP/MES 범용 브로셔 - -**컨셉**: 중소 제조업 대상 SAM 플랫폼 전체 기능 소개 - -| 항목 | 값 | -|------|------| -| 배경 | `#0F2439` (네이비) | -| 주 액센트 | `#10B981` (에메랄드 그린) | -| 보조 액센트 | `#2E86AB`, `#8B5CF6`, `#E86F2C` | -| 카드 스타일 | 반투명 배경 + 컬러 보더 | -| BI 로고 | `sam_bi_white.png` | - -**앞면 구성**: -- 히어로: "중소 제조업을 위한 ERP/MES 통합 플랫폼" -- 고민 포인트: Excel 과의존, 실시간 가시성, 품질관리, 높은 ERP 비용 -- 효과 지표: 시간 절감 80%, 납기 준수 95%, 추적성 100%, 인사/회계 무료 -- 기술 태그: 클라우드, 모바일 대응, 멀티테넌트 - -**뒷면 구성**: -- 8대 핵심 모듈 (01~08 번호 뱃지): 품목/BOM, 견적/수주, 생산/MES, 출하, 품질, 자재, 인사/회계, 대시보드 -- 확장 기능: 전자서명, 알림톡, AI Lab, QR -- 가격표: 기본 2,000만원 + 월 50만원 -- 도입 프로세스: 인터뷰 → 개발 → 이관 → 교육 - ---- - -### 3.2 v2 — CEO Dashboard 초판 - -**컨셉**: 경영진 타겟, 대시보드 중심 소개. 문제→해결 스토리텔링 - -| 항목 | 값 | -|------|------| -| 배경 | `#0B1929` (짙은 네이비) | -| 주 액센트 | `#0EA5E9` (스카이블루) | -| 보조 액센트 | `#10B981`, `#8B5CF6`, `#F59E0B`, `#EF4444`, `#EC4899` | -| 카드 스타일 | 반투명 다크 카드 | -| BI 로고 | `sam_bi_white.png` | - -**앞면 구성**: -- 히어로: "대표님, 지금 우리 회사 어떻게 돌아가고 있나요?" -- 시간대별 고민: 오전 9시(매출 보고 대기), 오후 2시(수주 취합), 오후 5시(결재 서류) -- 대시보드 Mock UI: KPI 카드 4개 + 매출 추이 차트 + 조직 성과 바 차트 -- 약속 박스: "실시간 KPI 파악" - -**뒷면 구성**: -- 대시보드 7대 기능 (01~07 뱃지) -- 역할별 맞춤 화면: CEO, 관리자, 운영자, 영업자 -- SAM 플랫폼 연동: 견적/수주, 생산, 품질, 재고, 인사/회계 -- 가격표 + 도입 프로세스 - -**v1 대비 차이**: -- 타겟이 전체→경영진으로 좁혀짐 -- 타임라인 기반 문제 제시 (시간대별 고민) -- 대시보드 UI Mock 삽입 -- 역할별 화면 분리 소개 - ---- - -### 3.3 v3 — CEO Dashboard 개선판 (다크) - -**컨셉**: v2 기반 개선. Before/After 인포그래픽 추가, SVG 아이콘 강화 - -| 항목 | 값 | -|------|------| -| 배경 | `#0B1929` (짙은 네이비) | -| 주 액센트 | `#0EA5E9` (스카이블루) | -| 카드 스타일 | 반투명 다크 + 컬러 보더 | -| BI 로고 | `sam_bi_white.png` | - -**v2 대비 개선사항**: -- 1page 통합본 추가 -- Before/After 비교 인포그래픽 도입 -- 핵심 가치 3카드: 즉시 현황 파악, 데이터로 판단, 모바일 승인 -- SVG 인라인 아이콘 전면 적용 (외부 이미지 의존 제거) -- PPTX 텍스트 줄바꿈 방지 패턴 적용 (`white-space: nowrap`, 개별 `

                      ` 분리) - ---- - -### 3.4 v4 — CEO Dashboard 라이트 버전 - -**컨셉**: v3와 동일 콘텐츠, 밝은 배경으로 색상 전환 - -| 항목 | 값 | -|------|------| -| 배경 | `#F8FAFC` (밝은 슬레이트) | -| 주 액센트 | `#0EA5E9` (스카이블루) | -| 제목 텍스트 | `#0F172A` | -| 본문 텍스트 | `#475569` | -| 보조 텍스트 | `#94A3B8` | -| 카드 스타일 | 화이트 + `box-shadow` + `#E2E8F0` 보더 | -| BI 로고 | `sam_bi_black.png` | - -**v3 → v4 색상 변환 규칙**: - -| 요소 | v3 (다크) | v4 (라이트) | -|------|-----------|------------| -| 배경 | `#0B1929` | `#F8FAFC` | -| 카드 | `#111D2E` 반투명 | `#FFFFFF` + shadow | -| 제목 | `#FFFFFF` | `#0F172A` | -| 본문 | `rgba(255,255,255,0.55)` | `#475569` | -| 보조 | `rgba(255,255,255,0.3)` | `#94A3B8` | -| 구분선 | `rgba(14,165,233,0.15)` | `#E2E8F0` | -| 기술 태그 | `rgba(255,255,255,0.03)` | `#F1F5F9` | -| 액센트 | 동일 유지 | 동일 유지 | - ---- - -### 3.5 v5 — Premium Executive Gradient - -**컨셉**: 고급 프리미엄 감성. 네이비→인디고 그래디언트 + 골드 액센트 + 글래스모피즘 - -| 항목 | 값 | -|------|------| -| 배경 | `#0F172A → #1E1B4B → #312E81` (175deg 그래디언트) | -| 주 액센트 | `#FBBF24` (골드/앰버) | -| 보조 액센트 | `#10B981`, `#8B5CF6`, `#EF4444`, `#F59E0B`, `#EC4899` | -| 카드 스타일 | 글래스모피즘 (`rgba(255,255,255,0.05)` + `rgba(255,255,255,0.1)` border) | -| BI 로고 | `sam_bi_white.png` | -| 뱃지 | "EXECUTIVE EDITION" 골드 뱃지 | - -**앞면 구성**: -- 히어로: "대표님의 시간은 보고를 기다리는 데 쓰여선 안 됩니다." -- 잃어버린 시간 카드: 1~2일(매출), 반나절(수주), 30분(결재) — 빨간 톤 -- 골드 전환 구분선: "SAM 도입 후" -- 대시보드 Mock: 골드 차트 라인, 글래스모피즘 카드 -- 약속 박스: 골드 테두리 - -**뒷면 구성**: -- 7대 기능 리스트 (글래스모피즘 카드) -- 역할별 맞춤 화면: CEO(골드), 관리자(그린), 운영자(앰버), 영업자(퍼플) -- 가격표: 골드 강조 -- 도입 프로세스: 골드 화살표 연결 - -**기술 특이사항**: -- body CSS gradient → PPTX 미지원 → Sharp로 PNG 사전 렌더링 -- HTML body는 `#1A1640`(단색 fallback), convert 스크립트에서 `slide.background`로 그래디언트 PNG 덮어쓰기 -- 구분선 gradient도 solid rgba로 변환 (PPTX 호환) - ---- - -### 3.6 v6 — Corporate Blue & White - -**컨셉**: 대기업/공공기관 프레젠테이션 스타일. 정돈된 블루 헤더 바 + 순백색 본문 - -| 항목 | 값 | -|------|------| -| 배경 | `#FFFFFF` (순백) | -| 헤더 바 | `#1E40AF` (로열 블루) | -| 주 액센트 | `#2563EB` (블루) | -| 보조 배경 | `#EFF6FF` (블루-50), `#DBEAFE` (블루-100) | -| 카드 스타일 | 화이트 + `#DBEAFE` 보더 | -| BI 로고 | `sam_bi_white.png` (헤더), `sam_bi_black.png` (본문) | - -**특징**: -- 풀 폭 블루 헤더 바에 흰색 BI 로고 + 뱃지 배치 -- 섹션 라벨은 블루-50 배경 + 로열블루 텍스트 뱃지 -- 대시보드 Mock UI: `#DBEAFE` 보더 카드 -- CTA: 블루 배경 풀 폭 바 - ---- - -### 3.7 v7 — Warm Gray + Teal - -**컨셉**: IT/SaaS 기업 스타일. 따뜻한 그레이 배경 + 틸(Teal) 액센트 - -| 항목 | 값 | -|------|------| -| 배경 | `#FAFAF9` (웜 그레이) | -| 주 액센트 | `#0D9488` (틸) | -| 보조 액센트 | `#10B981`, `#8B5CF6`, `#EF4444`, `#F59E0B`, `#EC4899` | -| 카드 스타일 | 화이트 + `#E7E5E4` 보더 + 미세 그림자 | -| 구분선 | `#D6D3D1` | -| BI 로고 | `sam_bi_black.png` | - -**특징**: -- 부드러운 웜 톤 배경으로 눈의 피로 감소 -- 틸 계열 SVG 아이콘 전면 적용 -- 가벼운 카드 스타일로 정보 구분 -- 타임라인 인포그래픽: 시간대별 고민 (9AM/2PM/5PM) - ---- - -### 3.8 v8 — Two-Tone Navy/White Split - -**컨셉**: 금융/컨설팅 프레젠테이션 스타일. 다크 상단 + 화이트 하단 투톤 분할 - -| 항목 | 값 | -|------|------| -| 상단 배경 | `#1E293B` (슬레이트-800) | -| 하단 배경 | `#FFFFFF` (순백) | -| 주 액센트 | `#F97316` (오렌지) | -| 보조 액센트 | `#FB923C` (오렌지-400) | -| 카드 (다크) | `rgba(255,255,255,0.08)` + `rgba(255,255,255,0.12)` 보더 | -| 카드 (라이트) | 화이트 + `#E2E8F0` 보더 + 컬러 좌측 보더 | -| BI 로고 | `sam_bi_white.png` (다크), `sam_bi_black.png` (라이트) | - -**특징**: -- 앞면 상단: 다크 존에 KPI 카드 + 히어로 메시지 -- 앞면 하단: 화이트 존에 가치 카드 + 기능 그리드 -- 기능 카드에 컬러 좌측 보더 포인트 (오렌지/그린/퍼플/레드) -- 다크 CTA 박스: 하단 풀 폭 다크 배경 + 오렌지 액센트 - ---- - -### 3.9 v9 — Minimal White + Indigo - -**컨셉**: Apple/디자인 에이전시 스타일. 극도의 미니멀리즘 + 인디고 수직 액센트 라인 - -| 항목 | 값 | -|------|------| -| 배경 | `#FFFFFF` (순백) | -| 주 액센트 | `#6366F1` (인디고) | -| 카드 배경 | `#F8FAFC` (거의 투명한 그레이) | -| 카드 보더 | `#F1F5F9` | -| 본문 텍스트 | `#334155` (슬레이트-700) | -| 보조 텍스트 | `#94A3B8` | -| BI 로고 | `sam_bi_black.png` | - -**특징**: -- 좌측 3pt 인디고 수직 액센트 라인 (풀 하이트) -- 타이포그래피 중심 레이아웃 (아이콘 최소화) -- 거의 보이지 않는 카드 구분 (barely-there cards) -- 기능은 텍스트 로우 형태 (인디고 불릿 도트만) -- 가격/프로세스도 텍스트 기반 미니멀 표현 - ---- - -## 4. 폴더 구조 - -``` -docs/brochure/ -├── README.md ← 이 파일 -├── v1/ -│ ├── slides/ -│ │ ├── brochure-2page-front.html -│ │ ├── brochure-2page-back.html -│ │ └── brochure-1page.html -│ ├── convert-1page.cjs -│ ├── convert-2page.cjs -│ ├── sam-brochure-1page.pptx -│ └── sam-brochure-2page.pptx -├── v2/ -│ ├── slides/ -│ │ ├── brochure-dashboard-front.html -│ │ ├── brochure-dashboard-back.html -│ │ └── brochure-dashboard-1page.html -│ ├── convert-1page.cjs -│ ├── convert-2page.cjs -│ ├── sam-brochure-v2-dashboard-1page.pptx -│ └── sam-brochure-v2-dashboard-2page.pptx -├── v3/ -│ ├── slides/ -│ │ ├── brochure-dashboard-front.html -│ │ ├── brochure-dashboard-back.html -│ │ └── brochure-dashboard-1page.html -│ ├── convert-1page.cjs -│ ├── convert-2page.cjs -│ ├── sam-brochure-v3-dashboard-1page.pptx -│ └── sam-brochure-v3-dashboard-2page.pptx -├── v4/ -│ ├── slides/ -│ │ ├── brochure-dashboard-front.html -│ │ ├── brochure-dashboard-back.html -│ │ └── brochure-dashboard-1page.html -│ ├── convert-1page.cjs -│ ├── convert-2page.cjs -│ ├── sam-brochure-v4-dashboard-1page.pptx -│ └── sam-brochure-v4-dashboard-2page.pptx -├── v5/ -│ ├── slides/ -│ │ ├── brochure-dashboard-front.html -│ │ ├── brochure-dashboard-back.html -│ │ └── brochure-dashboard-1page.html -│ ├── convert-1page.cjs ← Sharp 그래디언트 배경 생성 포함 -│ ├── convert-2page.cjs ← Sharp 그래디언트 배경 생성 포함 -│ ├── sam-brochure-v5-dashboard-1page.pptx -│ └── sam-brochure-v5-dashboard-2page.pptx -├── v6/ ← Corporate Blue & White -│ ├── slides/ (front, back, 1page) -│ ├── convert-1page.cjs -│ ├── convert-2page.cjs -│ ├── sam-brochure-v6-dashboard-1page.pptx -│ └── sam-brochure-v6-dashboard-2page.pptx -├── v7/ ← Warm Gray + Teal -│ ├── slides/ (front, back, 1page) -│ ├── convert-1page.cjs -│ ├── convert-2page.cjs -│ ├── sam-brochure-v7-dashboard-1page.pptx -│ └── sam-brochure-v7-dashboard-2page.pptx -├── v8/ ← Two-Tone Navy/White Split -│ ├── slides/ (front, back, 1page) -│ ├── convert-1page.cjs -│ ├── convert-2page.cjs -│ ├── sam-brochure-v8-dashboard-1page.pptx -│ └── sam-brochure-v8-dashboard-2page.pptx -└── v9/ ← Minimal White + Indigo - ├── slides/ (front, back, 1page) - ├── convert-1page.cjs - ├── convert-2page.cjs - ├── sam-brochure-v9-dashboard-1page.pptx - └── sam-brochure-v9-dashboard-2page.pptx -``` - ---- - -## 5. PPTX 변환 방법 - -```bash -# 각 버전 폴더에서 실행 -cd docs/brochure/v5 -node convert-1page.cjs # 1페이지 통합본 -node convert-2page.cjs # 앞면+뒷면 2페이지 -``` - ---- - -## 6. PPTX 변환 시 주의사항 - -### 6.1 텍스트 줄바꿈 방지 - -- 단일행 `

                      ` 태그에 `white-space: nowrap` 필수 -- `
                      ` 멀티라인 `

                      `는 개별 `

                      ` 태그로 분리 -- `` 포함 텍스트도 개별 `

                      ` 처리 (PPTX 폰트 폭 차이 보정) - -### 6.2 CSS gradient 미지원 - -- html2pptx.js는 CSS `linear-gradient`를 지원하지 않음 -- body gradient → Sharp로 PNG 사전 렌더링 후 `slide.background`에 설정 -- 구분선 gradient → solid `rgba()` 색상으로 변환 - -### 6.3 SVG 처리 - -- 인라인 SVG는 html2pptx가 자동으로 PNG 래스터화 -- SVG 내부 fill 색상은 배경에 맞게 조정 필요 - ---- - -**최종 업데이트**: 2026-03-01 (v6~v9 추가) diff --git a/sam/docs/brochure/v1/convert-1page.cjs b/sam/docs/brochure/v1/convert-1page.cjs deleted file mode 100644 index 5252ffa..0000000 --- a/sam/docs/brochure/v1/convert-1page.cjs +++ /dev/null @@ -1,28 +0,0 @@ -const path = require('path'); -module.paths.unshift(path.join(require('os').homedir(), '.claude/skills/pptx-skill/scripts/node_modules')); - -const PptxGenJS = require('pptxgenjs'); -const html2pptx = require(path.join(require('os').homedir(), '.claude/skills/pptx-skill/scripts/html2pptx.js')); - -async function main() { - const pres = new PptxGenJS(); - - // 9:16 세로형 (Portrait) - pres.defineLayout({ name: 'PORTRAIT_9x16', width: 5.625, height: 10 }); - pres.layout = 'PORTRAIT_9x16'; - - const htmlFile = path.join(__dirname, 'slides', 'brochure-1page.html'); - console.log('Converting 1-page brochure...'); - - try { - await html2pptx(htmlFile, pres); - } catch (err) { - console.error(`Error: ${err.message}`); - } - - const outputPath = path.join(__dirname, 'sam-brochure-1page.pptx'); - await pres.writeFile({ fileName: outputPath }); - console.log(`\nPPTX created: ${outputPath}`); -} - -main().catch(console.error); diff --git a/sam/docs/brochure/v1/convert-2page.cjs b/sam/docs/brochure/v1/convert-2page.cjs deleted file mode 100644 index d3590f5..0000000 --- a/sam/docs/brochure/v1/convert-2page.cjs +++ /dev/null @@ -1,32 +0,0 @@ -const path = require('path'); -module.paths.unshift(path.join(require('os').homedir(), '.claude/skills/pptx-skill/scripts/node_modules')); - -const PptxGenJS = require('pptxgenjs'); -const html2pptx = require(path.join(require('os').homedir(), '.claude/skills/pptx-skill/scripts/html2pptx.js')); - -async function main() { - const pres = new PptxGenJS(); - - // 9:16 세로형 (Portrait) - pres.defineLayout({ name: 'PORTRAIT_9x16', width: 5.625, height: 10 }); - pres.layout = 'PORTRAIT_9x16'; - - const slidesDir = path.join(__dirname, 'slides'); - const slides = ['brochure-2page-front.html', 'brochure-2page-back.html']; - - for (const file of slides) { - const htmlFile = path.join(slidesDir, file); - console.log(`Converting ${file} ...`); - try { - await html2pptx(htmlFile, pres); - } catch (err) { - console.error(`Error on ${file}: ${err.message}`); - } - } - - const outputPath = path.join(__dirname, 'sam-brochure-2page.pptx'); - await pres.writeFile({ fileName: outputPath }); - console.log(`\nPPTX created: ${outputPath}`); -} - -main().catch(console.error); diff --git a/sam/docs/brochure/v1/sam-brochure-1page.pptx b/sam/docs/brochure/v1/sam-brochure-1page.pptx deleted file mode 100644 index 5fe19a4..0000000 Binary files a/sam/docs/brochure/v1/sam-brochure-1page.pptx and /dev/null differ diff --git a/sam/docs/brochure/v1/sam-brochure-2page.pptx b/sam/docs/brochure/v1/sam-brochure-2page.pptx deleted file mode 100644 index 016084a..0000000 Binary files a/sam/docs/brochure/v1/sam-brochure-2page.pptx and /dev/null differ diff --git a/sam/docs/brochure/v1/slides/brochure-1page.html b/sam/docs/brochure/v1/slides/brochure-1page.html deleted file mode 100644 index 8eea8ab..0000000 --- a/sam/docs/brochure/v1/slides/brochure-1page.html +++ /dev/null @@ -1,210 +0,0 @@ - - - - - - - - -

                      - -
                      -

                      PRODUCT BROCHURE 2026

                      -
                      - - -
                      -

                      SMART AUTOMATION MANAGEMENT

                      -

                      중소 제조업을 위한
                      ERP/MES 통합 플랫폼

                      -

                      품목관리, 견적, 수주, 생산, 출하, 품질, 인사/회계까지
                      제조업의 모든 업무를 하나의 시스템으로 통합합니다.

                      -
                      - - -
                      - - -
                      -

                      현재 업무 과제

                      -
                      -
                      -

                      Excel 수작업

                      -

                      오류 잦음, 시간 낭비

                      -
                      -
                      -

                      현황 파악 불가

                      -

                      생산/재고 실시간 X

                      -
                      -
                      -

                      ERP 도입비 부담

                      -

                      수천만~수억원

                      -
                      -
                      -
                      - - -
                      -

                      SAM 핵심 기능

                      -
                      - -
                      -
                      -
                      -
                      -

                      01

                      -
                      -

                      견적/수주 자동화

                      -
                      -

                      BOM 전개, 단가 적용, PDF 견적서 자동 생성

                      -
                      -
                      -
                      -
                      -

                      02

                      -
                      -

                      생산관리 (MES)

                      -
                      -

                      바코드/QR 공정추적, 실시간 현황 대시보드

                      -
                      -
                      - -
                      -
                      -
                      -
                      -

                      03

                      -
                      -

                      품질/검사 관리

                      -
                      -

                      수입/공정/출하 3단계 검사, 인증 자동 알림

                      -
                      -
                      -
                      -
                      -

                      04

                      -
                      -

                      자재/재고 추적

                      -
                      -

                      안전재고, LOT 추적, 바코드 입출고 관리

                      -
                      -
                      - -
                      -
                      -
                      -
                      -

                      05

                      -
                      -

                      인사/회계 (무료)

                      -
                      -

                      근태, 급여, 매입매출, 세금계산서 자동 발행

                      -
                      -
                      -
                      -
                      -

                      06

                      -
                      -

                      경영 대시보드

                      -
                      -

                      수주/생산/매출/품질 KPI 실시간 모니터링

                      -
                      -
                      -
                      -
                      - - -
                      -
                      -

                      전자서명

                      -
                      -
                      -

                      카카오 알림톡

                      -
                      -
                      -

                      AI 실험실

                      -
                      -
                      -

                      바로빌 연동

                      -
                      -
                      - - -
                      - - -
                      - -
                      -

                      도입 기대 효과

                      -
                      -
                      -
                      -

                      80%

                      -

                      업무 시간 단축

                      -
                      -
                      -

                      95%

                      -

                      납기 준수율

                      -
                      -
                      -
                      -
                      -

                      100%

                      -

                      이력 추적성

                      -
                      -
                      -

                      Free

                      -

                      인사/회계 포함

                      -
                      -
                      -
                      -
                      - -
                      -

                      투자 비용

                      -
                      -

                      제조업 기본 패키지

                      -

                      2,000만원

                      -

                      + 월 50만원 (유지보수)

                      -
                      -

                      품목-견적-수주-생산-출하
                      인사/회계 무료 포함

                      -
                      -
                      -
                      - - -
                      -
                      -

                      클라우드 기반 (설치 불필요)

                      -
                      -
                      -

                      모바일 대응

                      -
                      -
                      -

                      Multi-tenant

                      -
                      -
                      - - -
                      -
                      -
                      -

                      (주)코드브릿지엑스

                      -

                      www.codebridge-x.com

                      -
                      -
                      -

                      무료 데모 및 상담

                      -

                      contact@codebridge-x.com

                      -
                      -
                      -
                      - - \ No newline at end of file diff --git a/sam/docs/brochure/v1/slides/brochure-2page-back.html b/sam/docs/brochure/v1/slides/brochure-2page-back.html deleted file mode 100644 index a285d16..0000000 --- a/sam/docs/brochure/v1/slides/brochure-2page-back.html +++ /dev/null @@ -1,227 +0,0 @@ - - - - - - - - -
                      - -
                      -

                      FEATURES & PRICING

                      -
                      - - -
                      -

                      SAM 핵심 모듈

                      -
                      - -
                      -
                      -

                      01

                      -
                      -

                      품목/BOM 관리

                      -

                      품목 마스터, 다단계 BOM 전개, 단가 관리

                      -
                      - -
                      -
                      -

                      02

                      -
                      -

                      견적/수주 자동화

                      -

                      견적서 자동 생성, 수주 전환, PDF 출력

                      -
                      - -
                      -
                      -

                      03

                      -
                      -

                      생산관리 (MES)

                      -

                      작업지시, 바코드/QR 공정추적, 실시간 현황

                      -
                      - -
                      -
                      -

                      04

                      -
                      -

                      출하/물류 관리

                      -

                      출하 지시, 거래명세서, 배송 추적

                      -
                      - -
                      -
                      -

                      05

                      -
                      -

                      품질/검사 관리

                      -

                      수입/공정/출하 검사, 인증 만료 자동 알림

                      -
                      - -
                      -
                      -

                      06

                      -
                      -

                      자재/재고 관리

                      -

                      안전재고, 입출고, LOT 추적, 바코드 관리

                      -
                      - -
                      -
                      -

                      07

                      -
                      -

                      인사/회계 (무료)

                      -

                      근태, 급여, 매입매출, 세금계산서 자동 발행

                      -
                      - -
                      -
                      -

                      08

                      -
                      -

                      경영 대시보드

                      -

                      수주/생산/매출/품질 KPI 실시간 모니터링

                      -
                      -
                      -
                      - - -
                      - - -
                      -

                      확장 기능

                      -
                      -
                      -

                      전자서명

                      -

                      계약/확인서

                      -
                      -
                      -

                      알림톡

                      -

                      카카오 자동발송

                      -
                      -
                      -

                      AI 실험실

                      -

                      음성요약/문서분류

                      -
                      -
                      -

                      QR 코드

                      -

                      설비/장비 점검

                      -
                      -
                      -
                      - - -
                      - - -
                      -

                      투자 비용

                      -
                      - -
                      -
                      -

                      제조업 기본 패키지

                      -

                      2,000만원

                      -

                      + 월 50만원 (유지보수)

                      -
                      -
                      -

                      품목 - 견적 - 수주 - 생산 - 출하
                      인사/회계 무료 포함

                      -
                      -
                      - -
                      -
                      -

                      추가 옵션 (선택)

                      -
                      -
                      -

                      생산공정 추가

                      -

                      +500만원

                      -
                      -
                      -

                      품질관리(인정검사)

                      -

                      +2,000만원

                      -
                      -
                      -

                      사진등록/챗봇/녹음

                      -

                      월 10~20만원

                      -
                      -
                      -
                      -
                      -
                      -
                      - - -
                      - - -
                      -

                      도입 프로세스

                      -
                      -
                      -

                      Step 1

                      -

                      1~2주

                      -

                      현장 인터뷰

                      -
                      -
                      -

                      Step 2

                      -

                      2~4주

                      -

                      맞춤 개발

                      -
                      -
                      -

                      Step 3

                      -

                      1~2주

                      -

                      데이터 이관

                      -
                      -
                      -

                      Step 4

                      -

                      1~2주

                      -

                      교육/안정화

                      -
                      -
                      -
                      - - -
                      -
                      -

                      바로빌 API

                      -

                      세금계산서 자동

                      -
                      -
                      -

                      카카오 알림톡

                      -

                      점검/납기 알림

                      -
                      -
                      -

                      이카운트 연동

                      -

                      기존 ERP 동기화

                      -
                      -
                      - - -
                      -
                      -
                      -

                      무료 데모를 신청하세요

                      -

                      귀사에 최적화된 맞춤 데모를 제공합니다

                      -
                      -
                      -

                      contact@codebridge-x.com

                      -

                      www.codebridge-x.com

                      -
                      -
                      -
                      - - -
                      -

                      (주)코드브릿지엑스 | SAM - Smart Automation Management

                      -
                      - - \ No newline at end of file diff --git a/sam/docs/brochure/v1/slides/brochure-2page-front.html b/sam/docs/brochure/v1/slides/brochure-2page-front.html deleted file mode 100644 index fe6da03..0000000 --- a/sam/docs/brochure/v1/slides/brochure-2page-front.html +++ /dev/null @@ -1,122 +0,0 @@ - - - - - - - - -
                      - -
                      -

                      PRODUCT BROCHURE 2026

                      -
                      - - -
                      -

                      SMART AUTOMATION MANAGEMENT

                      -

                      중소 제조업을 위한
                      ERP/MES 통합 플랫폼

                      -

                      품목관리, 견적, 수주, 생산, 출하, 품질, 인사/회계까지
                      제조업의 모든 업무를 하나의 시스템으로 통합합니다.

                      -
                      - - -
                      - - -
                      -

                      이런 고민이 있으신가요?

                      -
                      -
                      -
                      -

                      Excel 견적서, 수기 전표로 업무 시간 낭비가 심하다

                      -
                      -
                      -
                      -

                      생산 현황을 실시간으로 파악할 수 없다

                      -
                      -
                      -
                      -

                      품질/검사 기록이 체계적으로 관리되지 않는다

                      -
                      -
                      -
                      -

                      ERP 도입비가 수천만원~수억원으로 부담된다

                      -
                      -
                      -
                      - - -
                      -

                      SAM이 해결합니다

                      -

                      - SAM은 중소 제조업에 특화된 클라우드 ERP/MES 통합 플랫폼입니다. - 품목/BOM 관리, 견적 자동화, 바코드 생산추적, 품질검사, 인사/회계까지 - 별도 설치 없이 웹 브라우저만으로 모든 업무를 통합 관리합니다. -

                      -
                      - - -
                      - - -
                      -

                      도입 기대 효과

                      -
                      -
                      -

                      80%

                      -

                      업무 시간 단축

                      -
                      -
                      -

                      95%

                      -

                      납기 준수율

                      -
                      -
                      -

                      100%

                      -

                      이력 추적성

                      -
                      -
                      -

                      Free

                      -

                      인사/회계 포함

                      -
                      -
                      -
                      - - -
                      -
                      -

                      클라우드 기반

                      -

                      설치 불필요

                      -
                      -
                      -

                      모바일 대응

                      -

                      현장 태블릿/폰

                      -
                      -
                      -

                      Multi-tenant

                      -

                      데이터 완전 격리

                      -
                      -
                      - - -
                      -
                      -
                      -

                      (주)코드브릿지엑스

                      -

                      www.codebridge-x.com

                      -
                      -
                      -

                      뒷면에서 상세 기능과 가격을 확인하세요

                      -
                      -
                      -
                      - - \ No newline at end of file diff --git a/sam/docs/brochure/v2/convert-1page.cjs b/sam/docs/brochure/v2/convert-1page.cjs deleted file mode 100644 index d11c9ee..0000000 --- a/sam/docs/brochure/v2/convert-1page.cjs +++ /dev/null @@ -1,28 +0,0 @@ -const path = require('path'); -module.paths.unshift(path.join(require('os').homedir(), '.claude/skills/pptx-skill/scripts/node_modules')); - -const PptxGenJS = require('pptxgenjs'); -const html2pptx = require(path.join(require('os').homedir(), '.claude/skills/pptx-skill/scripts/html2pptx.js')); - -async function main() { - const pres = new PptxGenJS(); - - // 9:16 세로형 (Portrait) - pres.defineLayout({ name: 'PORTRAIT_9x16', width: 5.625, height: 10 }); - pres.layout = 'PORTRAIT_9x16'; - - const htmlFile = path.join(__dirname, 'slides', 'brochure-dashboard-1page.html'); - console.log('Converting CEO Dashboard 1-page brochure...'); - - try { - await html2pptx(htmlFile, pres); - } catch (err) { - console.error(`Error: ${err.message}`); - } - - const outputPath = path.join(__dirname, 'sam-brochure-v2-dashboard-1page.pptx'); - await pres.writeFile({ fileName: outputPath }); - console.log(`\nPPTX created: ${outputPath}`); -} - -main().catch(console.error); diff --git a/sam/docs/brochure/v2/convert-2page.cjs b/sam/docs/brochure/v2/convert-2page.cjs deleted file mode 100644 index 7d4aed0..0000000 --- a/sam/docs/brochure/v2/convert-2page.cjs +++ /dev/null @@ -1,32 +0,0 @@ -const path = require('path'); -module.paths.unshift(path.join(require('os').homedir(), '.claude/skills/pptx-skill/scripts/node_modules')); - -const PptxGenJS = require('pptxgenjs'); -const html2pptx = require(path.join(require('os').homedir(), '.claude/skills/pptx-skill/scripts/html2pptx.js')); - -async function main() { - const pres = new PptxGenJS(); - - // 9:16 세로형 (Portrait) - pres.defineLayout({ name: 'PORTRAIT_9x16', width: 5.625, height: 10 }); - pres.layout = 'PORTRAIT_9x16'; - - const slidesDir = path.join(__dirname, 'slides'); - const slides = ['brochure-dashboard-front.html', 'brochure-dashboard-back.html']; - - for (const file of slides) { - const htmlFile = path.join(slidesDir, file); - console.log(`Converting ${file} ...`); - try { - await html2pptx(htmlFile, pres); - } catch (err) { - console.error(`Error on ${file}: ${err.message}`); - } - } - - const outputPath = path.join(__dirname, 'sam-brochure-v2-dashboard-2page.pptx'); - await pres.writeFile({ fileName: outputPath }); - console.log(`\nPPTX created: ${outputPath}`); -} - -main().catch(console.error); diff --git a/sam/docs/brochure/v2/sam-brochure-v2-dashboard-1page.pptx b/sam/docs/brochure/v2/sam-brochure-v2-dashboard-1page.pptx deleted file mode 100644 index d5fb878..0000000 Binary files a/sam/docs/brochure/v2/sam-brochure-v2-dashboard-1page.pptx and /dev/null differ diff --git a/sam/docs/brochure/v2/sam-brochure-v2-dashboard-2page.pptx b/sam/docs/brochure/v2/sam-brochure-v2-dashboard-2page.pptx deleted file mode 100644 index 315707d..0000000 Binary files a/sam/docs/brochure/v2/sam-brochure-v2-dashboard-2page.pptx and /dev/null differ diff --git a/sam/docs/brochure/v2/slides/brochure-dashboard-1page.html b/sam/docs/brochure/v2/slides/brochure-dashboard-1page.html deleted file mode 100644 index eca646a..0000000 --- a/sam/docs/brochure/v2/slides/brochure-dashboard-1page.html +++ /dev/null @@ -1,261 +0,0 @@ - - - - - - - - -
                      - -
                      -

                      CEO DASHBOARD EDITION 2026

                      -
                      - - -
                      -

                      EXECUTIVE DASHBOARD

                      -

                      대표님, 지금 우리 회사
                      어떻게 돌아가고 있나요?

                      -

                      보고를 기다리지 마세요. SAM 대시보드 하나면
                      매출, 수주, 조직 실적, 승인 대기까지 한눈에 파악합니다.

                      -
                      - - -
                      - - -
                      - -
                      -
                      -
                      -
                      -

                      SAM CEO Dashboard

                      -
                      - -
                      -
                      -

                      월 매출

                      -

                      5.2억

                      -

                      +15.3%

                      -
                      -
                      -

                      수주 잔량

                      -

                      127건

                      -

                      +8건

                      -
                      -
                      -

                      납기 준수율

                      -

                      96%

                      -

                      목표 초과

                      -
                      -
                      -

                      승인 대기

                      -

                      5건

                      -

                      즉시 처리

                      -
                      -
                      - -
                      - -
                      -

                      월별 매출 추이

                      -
                      -
                      -
                      -
                      -
                      -
                      -
                      -
                      -
                      -
                      -
                      -
                      -
                      -
                      -
                      - -
                      -

                      조직별 실적

                      -
                      -
                      -
                      -

                      영업1팀

                      -
                      -
                      -
                      -
                      -
                      -
                      -

                      영업2팀

                      -
                      -
                      -
                      -
                      -
                      -
                      -

                      생산팀

                      -
                      -
                      -
                      -
                      -
                      -
                      -

                      품질팀

                      -
                      -
                      -
                      -
                      -
                      -
                      -
                      -
                      - - -
                      -

                      대표님이 얻는 것

                      -
                      -
                      -
                      -
                      -

                      1

                      -
                      -

                      즉시 현황 파악

                      -
                      -

                      보고를 기다릴 필요 없이
                      로그인만으로 전사 현황 확인

                      -
                      -
                      -
                      -
                      -

                      2

                      -
                      -

                      데이터 기반 의사결정

                      -
                      -

                      감이 아닌 숫자로 판단
                      매출 추이, KPI, 팀 성과 비교

                      -
                      -
                      -
                      -
                      -

                      3

                      -
                      -

                      빠른 승인/결재

                      -
                      -

                      대기 건수를 실시간 알림
                      모바일에서도 즉시 승인 처리

                      -
                      -
                      -
                      - - -
                      - - -
                      -

                      대시보드 핵심 기능

                      -
                      -
                      -
                      -
                      -

                      실시간 매출/수주 KPI

                      -
                      -
                      -
                      -

                      조직 계층별 실적 트리

                      -
                      -
                      -
                      -
                      -
                      -

                      역할별 수당 현황

                      -
                      -
                      -
                      -

                      미승인 건수 실시간 알림

                      -
                      -
                      -
                      -
                      -
                      -

                      기간별 트렌드 분석

                      -
                      -
                      -
                      -

                      수익 시뮬레이터

                      -
                      -
                      -
                      -
                      - - -
                      - - -
                      - -
                      -

                      BEFORE

                      -
                      -
                      -

                      "매출 얼마야?" → 보고 대기 1~2일

                      -

                      "수주 현황?" → Excel 취합 반나절

                      -

                      "승인할 것 있어?" → 서류 뒤지기

                      -

                      "팀별 실적?" → 각 팀장 개별 보고

                      -
                      -
                      -
                      - -
                      -

                      AFTER (SAM)

                      -
                      -
                      -

                      로그인 → 3초만에 전사 현황

                      -

                      클릭 한 번 → 실시간 수주 데이터

                      -

                      빨간 뱃지 → 즉시 승인 처리

                      -

                      트리 구조 → 전 조직 실적 한눈에

                      -
                      -
                      -
                      -
                      - - -
                      -
                      -

                      PC + 모바일

                      -
                      -
                      -

                      실시간 업데이트

                      -
                      -
                      -

                      역할별 권한

                      -
                      -
                      -

                      클라우드 기반

                      -
                      -
                      -

                      데이터 암호화

                      -
                      -
                      - - -
                      -
                      -
                      -

                      (주)코드브릿지엑스

                      -

                      www.codebridge-x.com

                      -
                      -
                      -

                      무료 데모 신청

                      -

                      contact@codebridge-x.com

                      -
                      -
                      -
                      - - \ No newline at end of file diff --git a/sam/docs/brochure/v2/slides/brochure-dashboard-back.html b/sam/docs/brochure/v2/slides/brochure-dashboard-back.html deleted file mode 100644 index 147b8bf..0000000 --- a/sam/docs/brochure/v2/slides/brochure-dashboard-back.html +++ /dev/null @@ -1,242 +0,0 @@ - - - - - - - - -
                      - -
                      -

                      DASHBOARD FEATURES & PRICING

                      -
                      - - -
                      -

                      대시보드 핵심 기능

                      -
                      - -
                      -
                      -

                      01

                      -
                      -

                      실시간 KPI 카드

                      -

                      월 매출, 수주 잔량, 납기 준수율, 승인 대기 한눈에

                      -
                      - -
                      -
                      -

                      02

                      -
                      -

                      조직 실적 트리

                      -

                      계층 구조로 각 팀/개인 실적 펼쳐보기

                      -
                      - -
                      -
                      -

                      03

                      -
                      -

                      역할별 수당 현황

                      -

                      판매자/관리자/협업자 수당 배분 실시간 확인

                      -
                      - -
                      -
                      -

                      04

                      -
                      -

                      승인 대기 알림

                      -

                      가입/지급 승인 미처리 건수 빨간 뱃지로 강조

                      -
                      - -
                      -
                      -

                      05

                      -
                      -

                      기간별 트렌드

                      -

                      당월/분기/연간 매출 추이 차트, 성장률 비교

                      -
                      - -
                      -
                      -

                      06

                      -
                      -

                      수익 시뮬레이터

                      -

                      가상 시나리오로 수당/마진 사전 계산

                      -
                      - -
                      -
                      -

                      07

                      -
                      -

                      모바일 대응

                      -

                      이동중에도 스마트폰으로 KPI 확인 및 승인

                      -
                      -
                      -
                      - - -
                      - - -
                      -

                      역할별 맞춤 화면

                      -
                      -
                      -

                      CEO

                      -

                      전사 KPI

                      -

                      매출/수주/조직 총괄

                      -
                      -
                      -

                      관리자

                      -

                      팀 실적 관리

                      -

                      하위 조직 성과 추적

                      -
                      -
                      -

                      운영자

                      -

                      인력/승인 관리

                      -

                      가입/지급 승인 처리

                      -
                      -
                      -

                      영업자

                      -

                      내 실적 조회

                      -

                      계약/수당 현황 확인

                      -
                      -
                      -
                      - - -
                      - - -
                      -

                      대시보드 + SAM 통합 플랫폼

                      -
                      -
                      -

                      견적/수주

                      -
                      -
                      -

                      생산 (MES)

                      -
                      -
                      -

                      품질/검사

                      -
                      -
                      -

                      재고/자재

                      -
                      -
                      -

                      인사/회계

                      -
                      -
                      -

                      대시보드에 표시되는 모든 데이터는 SAM ERP/MES 실시간 데이터 기반

                      -
                      - - -
                      - - -
                      -

                      투자 비용

                      -
                      - -
                      -
                      -

                      대시보드 포함 기본 패키지

                      -

                      2,000만원

                      -

                      + 월 50만원 (유지보수)

                      -
                      -
                      -

                      CEO 대시보드 + 견적/수주 + 생산
                      인사/회계 무료 포함

                      -
                      -
                      - -
                      -
                      -

                      추가 옵션 (선택)

                      -
                      -
                      -

                      생산공정 관리

                      -

                      +500만원

                      -
                      -
                      -

                      품질관리(인정검사)

                      -

                      +2,000만원

                      -
                      -
                      -

                      AI 견적 자동 생성

                      -

                      월 10~20만원

                      -
                      -
                      -
                      -
                      -
                      -
                      - - -
                      - - -
                      -

                      도입 프로세스

                      -
                      -
                      -

                      1

                      -

                      1~2주

                      -

                      현장 인터뷰

                      -
                      -
                      -

                      -
                      -
                      -

                      2

                      -

                      2~4주

                      -

                      맞춤 개발

                      -
                      -
                      -

                      -
                      -
                      -

                      3

                      -

                      1~2주

                      -

                      데이터 이관

                      -
                      -
                      -

                      -
                      -
                      -

                      4

                      -

                      1~2주

                      -

                      교육/안정화

                      -
                      -
                      -
                      - - -
                      -
                      -
                      -

                      무료 데모를 신청하세요

                      -

                      대표님 전용 대시보드를 직접 체험해 보세요

                      -
                      -
                      -

                      contact@codebridge-x.com

                      -

                      www.codebridge-x.com

                      -
                      -
                      -
                      - - -
                      -

                      (주)코드브릿지엑스 | SAM - Smart Automation Management

                      -
                      - - \ No newline at end of file diff --git a/sam/docs/brochure/v2/slides/brochure-dashboard-front.html b/sam/docs/brochure/v2/slides/brochure-dashboard-front.html deleted file mode 100644 index 0df1434..0000000 --- a/sam/docs/brochure/v2/slides/brochure-dashboard-front.html +++ /dev/null @@ -1,172 +0,0 @@ - - - - - - - - -
                      - -
                      -

                      CEO DASHBOARD EDITION

                      -
                      - - -
                      -

                      EXECUTIVE DASHBOARD

                      -

                      대표님, 지금 우리 회사
                      어떻게 돌아가고 있나요?

                      -

                      매출이 얼마인지, 수주가 밀려있는지, 승인할 건이 있는지
                      더 이상 보고를 기다리지 마세요.

                      -
                      - - -
                      - - -
                      -

                      대표님의 하루

                      -
                      -
                      -

                      AM 9:00

                      -

                      "어제 매출 얼마야?" → 팀장 보고 대기중...

                      -
                      -
                      -

                      PM 2:00

                      -

                      "수주 밀린 거 없어?" → Excel 취합중...

                      -
                      -
                      -

                      PM 5:00

                      -

                      "결재할 것 좀 정리해줘" → 서류 찾는중...

                      -
                      -
                      -
                      - - -
                      -

                      -

                      SAM으로 바꾸면

                      -
                      - - -
                      - -
                      -
                      -
                      -
                      -

                      SAM CEO Dashboard ― 로그인 후 3초

                      -
                      - -
                      -
                      -

                      월 매출

                      -

                      5.2억

                      -

                      ▲ 15.3%

                      -
                      -
                      -

                      누적 수주

                      -

                      127건

                      -

                      ▲ 8건

                      -
                      -
                      -

                      납기 준수율

                      -

                      96%

                      -

                      목표 달성

                      -
                      -
                      -

                      승인 대기

                      -

                      5건

                      -

                      즉시 처리

                      -
                      -
                      - -
                      -
                      -

                      월별 매출 추이

                      -
                      -
                      -
                      -
                      -
                      -
                      -
                      -
                      -
                      -
                      -
                      -
                      -
                      -
                      -

                      조직별 실적

                      -
                      -
                      -
                      -

                      영업1팀

                      -
                      -
                      -
                      -
                      -
                      -
                      -

                      영업2팀

                      -
                      -
                      -
                      -
                      -
                      -
                      -

                      생산팀

                      -
                      -
                      -
                      -
                      -
                      -
                      -
                      -
                      - - -
                      -

                      SAM 대시보드가 드리는 약속

                      -

                      - 로그인 한 번이면 전사 매출, 수주, 조직 실적, 승인 대기 건수를 - 한눈에 파악합니다. 보고를 기다리는 시간을 제로로 만들어 드립니다. -

                      -
                      - - -
                      -
                      -

                      클라우드 기반 (설치 불필요)

                      -
                      -
                      -

                      모바일 대응

                      -
                      -
                      -

                      역할별 권한 분리

                      -
                      -
                      - - -
                      -
                      -
                      -

                      (주)코드브릿지엑스

                      -

                      www.codebridge-x.com

                      -
                      -
                      -

                      뒷면에서 상세 기능을 확인하세요

                      -
                      -
                      -
                      - - \ No newline at end of file diff --git a/sam/docs/brochure/v3/convert-1page.cjs b/sam/docs/brochure/v3/convert-1page.cjs deleted file mode 100644 index 38f401a..0000000 --- a/sam/docs/brochure/v3/convert-1page.cjs +++ /dev/null @@ -1,27 +0,0 @@ -const path = require('path'); -module.paths.unshift(path.join(require('os').homedir(), '.claude/skills/pptx-skill/scripts/node_modules')); - -const PptxGenJS = require('pptxgenjs'); -const html2pptx = require(path.join(require('os').homedir(), '.claude/skills/pptx-skill/scripts/html2pptx.js')); - -async function main() { - const pres = new PptxGenJS(); - - pres.defineLayout({ name: 'PORTRAIT_9x16', width: 5.625, height: 10 }); - pres.layout = 'PORTRAIT_9x16'; - - const htmlFile = path.join(__dirname, 'slides', 'brochure-dashboard-1page.html'); - console.log('Converting CEO Dashboard v3 1-page brochure...'); - - try { - await html2pptx(htmlFile, pres); - } catch (err) { - console.error(`Error: ${err.message}`); - } - - const outputPath = path.join(__dirname, 'sam-brochure-v3-dashboard-1page.pptx'); - await pres.writeFile({ fileName: outputPath }); - console.log(`\nPPTX created: ${outputPath}`); -} - -main().catch(console.error); diff --git a/sam/docs/brochure/v3/convert-2page.cjs b/sam/docs/brochure/v3/convert-2page.cjs deleted file mode 100644 index ee5132c..0000000 --- a/sam/docs/brochure/v3/convert-2page.cjs +++ /dev/null @@ -1,31 +0,0 @@ -const path = require('path'); -module.paths.unshift(path.join(require('os').homedir(), '.claude/skills/pptx-skill/scripts/node_modules')); - -const PptxGenJS = require('pptxgenjs'); -const html2pptx = require(path.join(require('os').homedir(), '.claude/skills/pptx-skill/scripts/html2pptx.js')); - -async function main() { - const pres = new PptxGenJS(); - - pres.defineLayout({ name: 'PORTRAIT_9x16', width: 5.625, height: 10 }); - pres.layout = 'PORTRAIT_9x16'; - - const slidesDir = path.join(__dirname, 'slides'); - const slides = ['brochure-dashboard-front.html', 'brochure-dashboard-back.html']; - - for (const file of slides) { - const htmlFile = path.join(slidesDir, file); - console.log(`Converting ${file} ...`); - try { - await html2pptx(htmlFile, pres); - } catch (err) { - console.error(`Error on ${file}: ${err.message}`); - } - } - - const outputPath = path.join(__dirname, 'sam-brochure-v3-dashboard-2page.pptx'); - await pres.writeFile({ fileName: outputPath }); - console.log(`\nPPTX created: ${outputPath}`); -} - -main().catch(console.error); diff --git a/sam/docs/brochure/v3/sam-brochure-v3-dashboard-1page.pptx b/sam/docs/brochure/v3/sam-brochure-v3-dashboard-1page.pptx deleted file mode 100644 index cbb6084..0000000 Binary files a/sam/docs/brochure/v3/sam-brochure-v3-dashboard-1page.pptx and /dev/null differ diff --git a/sam/docs/brochure/v3/sam-brochure-v3-dashboard-2page.pptx b/sam/docs/brochure/v3/sam-brochure-v3-dashboard-2page.pptx deleted file mode 100644 index cdc1014..0000000 Binary files a/sam/docs/brochure/v3/sam-brochure-v3-dashboard-2page.pptx and /dev/null differ diff --git a/sam/docs/brochure/v3/slides/brochure-dashboard-1page.html b/sam/docs/brochure/v3/slides/brochure-dashboard-1page.html deleted file mode 100644 index 8547c7d..0000000 --- a/sam/docs/brochure/v3/slides/brochure-dashboard-1page.html +++ /dev/null @@ -1,405 +0,0 @@ - - - - - - - - -
                      - -
                      -

                      CEO DASHBOARD v3

                      -
                      - - -
                      -
                      -

                      EXECUTIVE DASHBOARD

                      -

                      대표님, 우리 회사
                      지금 어떤 상태인가요?

                      -

                      보고 대기 없이, 로그인 한 번이면
                      전사 현황이 한눈에 들어옵니다.

                      -
                      - -
                      - - - - - - - - - - - - - - - - - - - - - - - -
                      -
                      - - -
                      - - -
                      - -
                      -
                      -
                      -
                      -

                      SAM CEO Dashboard

                      -
                      - -
                      - -
                      - - - - - - -

                      5.2억

                      -

                      ▲ 15.3%

                      -

                      월 매출

                      -
                      - -
                      - - - - -

                      127건

                      -

                      ▲ 8건

                      -

                      수주 잔량

                      -
                      - -
                      - - - - 96 - -

                      96%

                      -

                      목표 달성

                      -

                      납기 준수율

                      -
                      - -
                      - - - - - -

                      5건

                      -

                      즉시 처리

                      -

                      승인 대기

                      -
                      -
                      - -
                      - -
                      -

                      월별 매출 추이

                      - - - - - - - - - - - - - - - - - - - -
                      - -
                      - - - - - - - - - - - - -
                      -
                      -
                      -

                      영업1팀 38%

                      -
                      -
                      -
                      -

                      영업2팀 25%

                      -
                      -
                      -
                      -

                      생산팀 22%

                      -
                      -
                      -
                      -

                      품질팀 15%

                      -
                      -
                      -
                      -
                      -
                      - - -
                      -

                      대표님이 얻는 것

                      -
                      - -
                      - - - - - - - -

                      즉시 현황 파악

                      -

                      로그인 3초면
                      전사 현황 확인

                      -
                      - -
                      - - - - - - - - - - - - -

                      데이터로 판단

                      -

                      감이 아닌 숫자로
                      KPI/팀 성과 비교

                      -
                      - -
                      - - - - - - - - -

                      모바일 승인

                      -

                      이동중에도 즉시
                      결재/승인 처리

                      -
                      -
                      -
                      - - -
                      - - -
                      -

                      대시보드 핵심 기능

                      -
                      -
                      - -
                      - - - - - -

                      실시간 매출/수주 KPI

                      -
                      - -
                      - - - - - - - - - - - -

                      조직 계층별 실적 트리

                      -
                      -
                      -
                      - -
                      - - - - -

                      역할별 수당 현황

                      -
                      - -
                      - - - - 5 - - -

                      미승인 실시간 알림

                      -
                      -
                      -
                      - -
                      - - - - -

                      기간별 트렌드 분석

                      -
                      - -
                      - - - - - - - - -

                      수익 시뮬레이터

                      -
                      -
                      -
                      -
                      - - -
                      - - -
                      - -
                      -
                      - - - - - -

                      BEFORE

                      -
                      -

                      매출? → 보고 대기 1~2일

                      -

                      수주? → Excel 취합 반나절

                      -

                      승인? → 서류 찾기 30분

                      -

                      실적? → 각 팀장 개별 보고

                      -
                      - -
                      - - - - -
                      - -
                      -
                      - - - - -

                      AFTER (SAM)

                      -
                      -

                      로그인 → 3초 전사 현황

                      -

                      클릭 → 실시간 수주 데이터

                      -

                      뱃지 → 즉시 승인 처리

                      -

                      트리 → 전 조직 한눈에

                      -
                      -
                      - - -
                      -
                      - - - - -

                      실시간 업데이트

                      -
                      -
                      - - - - -

                      PC + 모바일

                      -
                      -
                      - - - - - -

                      역할별 권한

                      -
                      -
                      - - - - - -

                      데이터 암호화

                      -
                      -
                      - - - - - -

                      클라우드

                      -
                      -
                      - - -
                      -
                      -
                      -

                      (주)코드브릿지엑스

                      -

                      www.codebridge-x.com

                      -
                      -
                      -

                      무료 데모 신청

                      -

                      contact@codebridge-x.com

                      -
                      -
                      -
                      - - \ No newline at end of file diff --git a/sam/docs/brochure/v3/slides/brochure-dashboard-back.html b/sam/docs/brochure/v3/slides/brochure-dashboard-back.html deleted file mode 100644 index f199750..0000000 --- a/sam/docs/brochure/v3/slides/brochure-dashboard-back.html +++ /dev/null @@ -1,373 +0,0 @@ - - - - - - - - -
                      - -
                      -

                      FEATURES & PRICING

                      -
                      - - -
                      -

                      대시보드 핵심 기능

                      -
                      - -
                      - - - - - -
                      -

                      실시간 KPI 카드

                      -
                      -

                      매출, 수주, 납기율, 승인 대기

                      -
                      - -
                      - - - - - - - - - - - - - - - -
                      -

                      조직 실적 트리

                      -
                      -

                      계층별 팀/개인 실적 펼쳐보기

                      -
                      - -
                      - - - - -
                      -

                      역할별 수당 현황

                      -
                      -

                      판매자/관리자/협업자 배분 확인

                      -
                      - -
                      - - - - ! - - -
                      -

                      승인 대기 알림

                      -
                      -

                      가입/지급 미처리 빨간 뱃지

                      -
                      - -
                      - - - - -
                      -

                      기간별 트렌드

                      -
                      -

                      당월/분기/연간 추이 차트

                      -
                      - -
                      - - - - - - - - -
                      -

                      수익 시뮬레이터

                      -
                      -

                      가상 시나리오 수당/마진 계산

                      -
                      - -
                      - - - - - - - - -
                      -

                      모바일 대응

                      -
                      -

                      스마트폰으로 KPI 확인/승인

                      -
                      -
                      -
                      - - -
                      - - -
                      -

                      역할별 맞춤 화면

                      -
                      - -
                      - - - - - -

                      CEO

                      -

                      전사 KPI 총괄

                      -
                      - -
                      - - - - - - -

                      관리자

                      -

                      팀 실적 관리

                      -
                      - -
                      - - - - - - - - -

                      운영자

                      -

                      인력/승인 관리

                      -
                      - -
                      - - - - - - - -

                      영업자

                      -

                      내 실적 조회

                      -
                      -
                      -
                      - - -
                      - - -
                      -

                      대시보드 + SAM ERP/MES 통합

                      -
                      -
                      - - - - - - -

                      견적/수주

                      -
                      -
                      - - - - - - -

                      생산 MES

                      -
                      -
                      - - - - - -

                      품질/검사

                      -
                      -
                      - - - - - -

                      재고/자재

                      -
                      -
                      - - - - -

                      인사/회계

                      -
                      -
                      -

                      대시보드의 모든 데이터는 SAM ERP/MES 실시간 데이터 기반

                      -
                      - - -
                      - - -
                      -

                      투자 비용

                      -
                      - -
                      -
                      -
                      - - - - -

                      대시보드 포함 기본 패키지

                      -
                      -

                      2,000만원

                      -

                      + 월 50만원 (유지보수)

                      -
                      -
                      -

                      CEO 대시보드 + 견적/수주 + 생산
                      인사/회계 무료 포함

                      -
                      -
                      - -
                      -
                      -
                      - - - - - -

                      추가 옵션 (선택)

                      -
                      -
                      -
                      -

                      생산공정 관리

                      -

                      +500만원

                      -
                      -
                      -

                      품질관리(인정검사)

                      -

                      +2,000만원

                      -
                      -
                      -

                      AI 견적 자동 생성

                      -

                      월 10~20만원

                      -
                      -
                      -
                      -
                      -
                      -
                      - - -
                      - - -
                      -

                      도입 프로세스

                      -
                      -
                      - - - - - -

                      1~2주

                      -

                      현장 인터뷰

                      -
                      - - - -
                      - - - - - - - -

                      2~4주

                      -

                      맞춤 개발

                      -
                      - - - -
                      - - - - - -

                      1~2주

                      -

                      데이터 이관

                      -
                      - - - -
                      - - - - -

                      1~2주

                      -

                      교육/안정화

                      -
                      -
                      -
                      - - -
                      -
                      -
                      - - - - -
                      -

                      무료 데모를 신청하세요

                      -

                      대표님 전용 대시보드를 직접 체험

                      -
                      -
                      -
                      -

                      contact@codebridge-x.com

                      -

                      www.codebridge-x.com

                      -
                      -
                      -
                      - - -
                      -

                      (주)코드브릿지엑스 | SAM - Smart Automation Management

                      -
                      - - \ No newline at end of file diff --git a/sam/docs/brochure/v3/slides/brochure-dashboard-front.html b/sam/docs/brochure/v3/slides/brochure-dashboard-front.html deleted file mode 100644 index b1d8828..0000000 --- a/sam/docs/brochure/v3/slides/brochure-dashboard-front.html +++ /dev/null @@ -1,262 +0,0 @@ - - - - - - - - -
                      - -
                      -

                      CEO DASHBOARD v3

                      -
                      - - -
                      -
                      -

                      EXECUTIVE DASHBOARD

                      -

                      대표님, 우리 회사
                      지금 어떤 상태인가요?

                      -

                      매출, 수주, 조직 실적, 승인 대기
                      더 이상 보고를 기다리지 마세요.

                      -
                      - -
                      - - - - - - - - - - - - - - - - - - - - 5 - - - - - - - - - - - -
                      -
                      - - -
                      - - -
                      -

                      대표님의 하루

                      -
                      - -
                      - - - - - 9AM - -
                      -

                      "어제 매출 얼마야?" → 팀장 보고 대기중...

                      -
                      -
                      - -
                      - - - - - 2PM - -
                      -

                      "수주 밀린 거 없어?" → Excel 취합중...

                      -
                      -
                      - -
                      - - - - - 5PM - -
                      -

                      "결재할 것 정리해줘" → 서류 찾는중...

                      -
                      -
                      -
                      -
                      - - -
                      - - - -

                      SAM 도입 후

                      -
                      - - -
                      - -
                      -
                      -
                      -
                      -

                      SAM CEO Dashboard ― 로그인 후 3초

                      -
                      - -
                      -
                      - - - - - -

                      5.2억

                      -

                      ▲ 15.3%

                      -

                      월 매출

                      -
                      -
                      - - - - -

                      127건

                      -

                      ▲ 8건

                      -

                      누적 수주

                      -
                      -
                      - - - - -

                      96%

                      -

                      목표 달성

                      -

                      납기 준수율

                      -
                      -
                      - - - - - -

                      5건

                      -

                      즉시 처리

                      -

                      승인 대기

                      -
                      -
                      - -
                      -
                      -

                      월별 매출 추이

                      - - - - - - - - - - - - - -
                      -
                      - - - - - - - -
                      -
                      -
                      -

                      영업1팀

                      -
                      -
                      -
                      -

                      영업2팀

                      -
                      -
                      -
                      -

                      생산팀

                      -
                      -
                      -
                      -

                      품질팀

                      -
                      -
                      -
                      -
                      -
                      - - -
                      -
                      - - - - -
                      -

                      SAM 대시보드가 드리는 약속

                      -

                      로그인 한 번이면 전사 매출, 수주, 승인 대기를 한눈에.
                      보고를 기다리는 시간을 제로로 만들어 드립니다.

                      -
                      -
                      -
                      - - -
                      -
                      - -

                      클라우드 기반

                      -
                      -
                      - -

                      PC + 모바일

                      -
                      -
                      - -

                      역할별 권한

                      -
                      -
                      - - -
                      -
                      -
                      -

                      (주)코드브릿지엑스

                      -

                      www.codebridge-x.com

                      -
                      -
                      -

                      뒷면에서 상세 기능을 확인하세요 ▶

                      -
                      -
                      -
                      - - \ No newline at end of file diff --git a/sam/docs/brochure/v4/convert-1page.cjs b/sam/docs/brochure/v4/convert-1page.cjs deleted file mode 100644 index e218f62..0000000 --- a/sam/docs/brochure/v4/convert-1page.cjs +++ /dev/null @@ -1,27 +0,0 @@ -const path = require('path'); -module.paths.unshift(path.join(require('os').homedir(), '.claude/skills/pptx-skill/scripts/node_modules')); - -const PptxGenJS = require('pptxgenjs'); -const html2pptx = require(path.join(require('os').homedir(), '.claude/skills/pptx-skill/scripts/html2pptx.js')); - -async function main() { - const pres = new PptxGenJS(); - - pres.defineLayout({ name: 'PORTRAIT_9x16', width: 5.625, height: 10 }); - pres.layout = 'PORTRAIT_9x16'; - - const htmlFile = path.join(__dirname, 'slides', 'brochure-dashboard-1page.html'); - console.log('Converting CEO Dashboard v4 (Light) 1-page brochure...'); - - try { - await html2pptx(htmlFile, pres); - } catch (err) { - console.error(`Error: ${err.message}`); - } - - const outputPath = path.join(__dirname, 'sam-brochure-v4-dashboard-1page.pptx'); - await pres.writeFile({ fileName: outputPath }); - console.log(`\nPPTX created: ${outputPath}`); -} - -main().catch(console.error); diff --git a/sam/docs/brochure/v4/convert-2page.cjs b/sam/docs/brochure/v4/convert-2page.cjs deleted file mode 100644 index f8c691d..0000000 --- a/sam/docs/brochure/v4/convert-2page.cjs +++ /dev/null @@ -1,31 +0,0 @@ -const path = require('path'); -module.paths.unshift(path.join(require('os').homedir(), '.claude/skills/pptx-skill/scripts/node_modules')); - -const PptxGenJS = require('pptxgenjs'); -const html2pptx = require(path.join(require('os').homedir(), '.claude/skills/pptx-skill/scripts/html2pptx.js')); - -async function main() { - const pres = new PptxGenJS(); - - pres.defineLayout({ name: 'PORTRAIT_9x16', width: 5.625, height: 10 }); - pres.layout = 'PORTRAIT_9x16'; - - const slidesDir = path.join(__dirname, 'slides'); - const slides = ['brochure-dashboard-front.html', 'brochure-dashboard-back.html']; - - for (const file of slides) { - const htmlFile = path.join(slidesDir, file); - console.log(`Converting ${file} ...`); - try { - await html2pptx(htmlFile, pres); - } catch (err) { - console.error(`Error on ${file}: ${err.message}`); - } - } - - const outputPath = path.join(__dirname, 'sam-brochure-v4-dashboard-2page.pptx'); - await pres.writeFile({ fileName: outputPath }); - console.log(`\nPPTX created: ${outputPath}`); -} - -main().catch(console.error); diff --git a/sam/docs/brochure/v4/sam-brochure-v4-dashboard-1page.pptx b/sam/docs/brochure/v4/sam-brochure-v4-dashboard-1page.pptx deleted file mode 100644 index 747d4f4..0000000 Binary files a/sam/docs/brochure/v4/sam-brochure-v4-dashboard-1page.pptx and /dev/null differ diff --git a/sam/docs/brochure/v4/sam-brochure-v4-dashboard-2page.pptx b/sam/docs/brochure/v4/sam-brochure-v4-dashboard-2page.pptx deleted file mode 100644 index c56e36e..0000000 Binary files a/sam/docs/brochure/v4/sam-brochure-v4-dashboard-2page.pptx and /dev/null differ diff --git a/sam/docs/brochure/v4/slides/brochure-dashboard-1page.html b/sam/docs/brochure/v4/slides/brochure-dashboard-1page.html deleted file mode 100644 index 8792a24..0000000 --- a/sam/docs/brochure/v4/slides/brochure-dashboard-1page.html +++ /dev/null @@ -1,405 +0,0 @@ - - - - - - - - -
                      - -
                      -

                      CEO DASHBOARD v4

                      -
                      - - -
                      -
                      -

                      EXECUTIVE DASHBOARD

                      -

                      대표님, 우리 회사
                      지금 어떤 상태인가요?

                      -

                      보고 대기 없이, 로그인 한 번이면
                      전사 현황이 한눈에 들어옵니다.

                      -
                      - -
                      - - - - - - - - - - - - - - - - - - - - - - - -
                      -
                      - - -
                      - - -
                      - -
                      -
                      -
                      -
                      -

                      SAM CEO Dashboard

                      -
                      - -
                      - -
                      - - - - - - -

                      5.2억

                      -

                      ▲ 15.3%

                      -

                      월 매출

                      -
                      - -
                      - - - - -

                      127건

                      -

                      ▲ 8건

                      -

                      수주 잔량

                      -
                      - -
                      - - - - 96 - -

                      96%

                      -

                      목표 달성

                      -

                      납기 준수율

                      -
                      - -
                      - - - - - -

                      5건

                      -

                      즉시 처리

                      -

                      승인 대기

                      -
                      -
                      - -
                      - -
                      -

                      월별 매출 추이

                      - - - - - - - - - - - - - - - - - - - -
                      - -
                      - - - - - - - - - - - - -
                      -
                      -
                      -

                      영업1팀 38%

                      -
                      -
                      -
                      -

                      영업2팀 25%

                      -
                      -
                      -
                      -

                      생산팀 22%

                      -
                      -
                      -
                      -

                      품질팀 15%

                      -
                      -
                      -
                      -
                      -
                      - - -
                      -

                      대표님이 얻는 것

                      -
                      - -
                      - - - - - - - -

                      즉시 현황 파악

                      -

                      로그인 3초면
                      전사 현황 확인

                      -
                      - -
                      - - - - - - - - - - - - -

                      데이터로 판단

                      -

                      감이 아닌 숫자로
                      KPI/팀 성과 비교

                      -
                      - -
                      - - - - - - - - -

                      모바일 승인

                      -

                      이동중에도 즉시
                      결재/승인 처리

                      -
                      -
                      -
                      - - -
                      - - -
                      -

                      대시보드 핵심 기능

                      -
                      -
                      - -
                      - - - - - -

                      실시간 매출/수주 KPI

                      -
                      - -
                      - - - - - - - - - - - -

                      조직 계층별 실적 트리

                      -
                      -
                      -
                      - -
                      - - - - -

                      역할별 수당 현황

                      -
                      - -
                      - - - - 5 - - -

                      미승인 실시간 알림

                      -
                      -
                      -
                      - -
                      - - - - -

                      기간별 트렌드 분석

                      -
                      - -
                      - - - - - - - - -

                      수익 시뮬레이터

                      -
                      -
                      -
                      -
                      - - -
                      - - -
                      - -
                      -
                      - - - - - -

                      BEFORE

                      -
                      -

                      매출? → 보고 대기 1~2일

                      -

                      수주? → Excel 취합 반나절

                      -

                      승인? → 서류 찾기 30분

                      -

                      실적? → 각 팀장 개별 보고

                      -
                      - -
                      - - - - -
                      - -
                      -
                      - - - - -

                      AFTER (SAM)

                      -
                      -

                      로그인 → 3초 전사 현황

                      -

                      클릭 → 실시간 수주 데이터

                      -

                      뱃지 → 즉시 승인 처리

                      -

                      트리 → 전 조직 한눈에

                      -
                      -
                      - - -
                      -
                      - - - - -

                      실시간 업데이트

                      -
                      -
                      - - - - -

                      PC + 모바일

                      -
                      -
                      - - - - - -

                      역할별 권한

                      -
                      -
                      - - - - - -

                      데이터 암호화

                      -
                      -
                      - - - - - -

                      클라우드

                      -
                      -
                      - - -
                      -
                      -
                      -

                      (주)코드브릿지엑스

                      -

                      www.codebridge-x.com

                      -
                      -
                      -

                      무료 데모 신청

                      -

                      contact@codebridge-x.com

                      -
                      -
                      -
                      - - \ No newline at end of file diff --git a/sam/docs/brochure/v4/slides/brochure-dashboard-back.html b/sam/docs/brochure/v4/slides/brochure-dashboard-back.html deleted file mode 100644 index 6b64b85..0000000 --- a/sam/docs/brochure/v4/slides/brochure-dashboard-back.html +++ /dev/null @@ -1,373 +0,0 @@ - - - - - - - - -
                      - -
                      -

                      FEATURES & PRICING

                      -
                      - - -
                      -

                      대시보드 핵심 기능

                      -
                      - -
                      - - - - - -
                      -

                      실시간 KPI 카드

                      -
                      -

                      매출, 수주, 납기율, 승인 대기

                      -
                      - -
                      - - - - - - - - - - - - - - - -
                      -

                      조직 실적 트리

                      -
                      -

                      계층별 팀/개인 실적 펼쳐보기

                      -
                      - -
                      - - - - -
                      -

                      역할별 수당 현황

                      -
                      -

                      판매자/관리자/협업자 배분 확인

                      -
                      - -
                      - - - - ! - - -
                      -

                      승인 대기 알림

                      -
                      -

                      가입/지급 미처리 빨간 뱃지

                      -
                      - -
                      - - - - -
                      -

                      기간별 트렌드

                      -
                      -

                      당월/분기/연간 추이 차트

                      -
                      - -
                      - - - - - - - - -
                      -

                      수익 시뮬레이터

                      -
                      -

                      가상 시나리오 수당/마진 계산

                      -
                      - -
                      - - - - - - - - -
                      -

                      모바일 대응

                      -
                      -

                      스마트폰으로 KPI 확인/승인

                      -
                      -
                      -
                      - - -
                      - - -
                      -

                      역할별 맞춤 화면

                      -
                      - -
                      - - - - - -

                      CEO

                      -

                      전사 KPI 총괄

                      -
                      - -
                      - - - - - - -

                      관리자

                      -

                      팀 실적 관리

                      -
                      - -
                      - - - - - - - - -

                      운영자

                      -

                      인력/승인 관리

                      -
                      - -
                      - - - - - - - -

                      영업자

                      -

                      내 실적 조회

                      -
                      -
                      -
                      - - -
                      - - -
                      -

                      대시보드 + SAM ERP/MES 통합

                      -
                      -
                      - - - - - - -

                      견적/수주

                      -
                      -
                      - - - - - - -

                      생산 MES

                      -
                      -
                      - - - - - -

                      품질/검사

                      -
                      -
                      - - - - - -

                      재고/자재

                      -
                      -
                      - - - - -

                      인사/회계

                      -
                      -
                      -

                      대시보드의 모든 데이터는 SAM ERP/MES 실시간 데이터 기반

                      -
                      - - -
                      - - -
                      -

                      투자 비용

                      -
                      - -
                      -
                      -
                      - - - - -

                      대시보드 포함 기본 패키지

                      -
                      -

                      2,000만원

                      -

                      + 월 50만원 (유지보수)

                      -
                      -
                      -

                      CEO 대시보드 + 견적/수주 + 생산
                      인사/회계 무료 포함

                      -
                      -
                      - -
                      -
                      -
                      - - - - - -

                      추가 옵션 (선택)

                      -
                      -
                      -
                      -

                      생산공정 관리

                      -

                      +500만원

                      -
                      -
                      -

                      품질관리(인정검사)

                      -

                      +2,000만원

                      -
                      -
                      -

                      AI 견적 자동 생성

                      -

                      월 10~20만원

                      -
                      -
                      -
                      -
                      -
                      -
                      - - -
                      - - -
                      -

                      도입 프로세스

                      -
                      -
                      - - - - - -

                      1~2주

                      -

                      현장 인터뷰

                      -
                      - - - -
                      - - - - - - - -

                      2~4주

                      -

                      맞춤 개발

                      -
                      - - - -
                      - - - - - -

                      1~2주

                      -

                      데이터 이관

                      -
                      - - - -
                      - - - - -

                      1~2주

                      -

                      교육/안정화

                      -
                      -
                      -
                      - - -
                      -
                      -
                      - - - - -
                      -

                      무료 데모를 신청하세요

                      -

                      대표님 전용 대시보드를 직접 체험

                      -
                      -
                      -
                      -

                      contact@codebridge-x.com

                      -

                      www.codebridge-x.com

                      -
                      -
                      -
                      - - -
                      -

                      (주)코드브릿지엑스 | SAM - Smart Automation Management

                      -
                      - - \ No newline at end of file diff --git a/sam/docs/brochure/v4/slides/brochure-dashboard-front.html b/sam/docs/brochure/v4/slides/brochure-dashboard-front.html deleted file mode 100644 index 8f4165f..0000000 --- a/sam/docs/brochure/v4/slides/brochure-dashboard-front.html +++ /dev/null @@ -1,260 +0,0 @@ - - - - - - - - -
                      - -
                      -

                      CEO DASHBOARD v4

                      -
                      - - -
                      -
                      -

                      EXECUTIVE DASHBOARD

                      -

                      대표님, 우리 회사
                      지금 어떤 상태인가요?

                      -

                      매출, 수주, 조직 실적, 승인 대기
                      더 이상 보고를 기다리지 마세요.

                      -
                      - -
                      - - - - - - - - - - - - - - - - - - 5 - - - - - - - - - - - -
                      -
                      - - -
                      - - -
                      -

                      대표님의 하루

                      -
                      - -
                      - - - - - 9AM - -
                      -

                      "어제 매출 얼마야?" → 팀장 보고 대기중...

                      -
                      -
                      - -
                      - - - - - 2PM - -
                      -

                      "수주 밀린 거 없어?" → Excel 취합중...

                      -
                      -
                      - -
                      - - - - - 5PM - -
                      -

                      "결재할 것 정리해줘" → 서류 찾는중...

                      -
                      -
                      -
                      -
                      - - -
                      - - - -

                      SAM 도입 후

                      -
                      - - -
                      - -
                      -
                      -
                      -
                      -

                      SAM CEO Dashboard ― 로그인 후 3초

                      -
                      - -
                      -
                      - - - - - -

                      5.2억

                      -

                      ▲ 15.3%

                      -

                      월 매출

                      -
                      -
                      - - - - -

                      127건

                      -

                      ▲ 8건

                      -

                      누적 수주

                      -
                      -
                      - - - - -

                      96%

                      -

                      목표 달성

                      -

                      납기 준수율

                      -
                      -
                      - - - - - -

                      5건

                      -

                      즉시 처리

                      -

                      승인 대기

                      -
                      -
                      - -
                      -
                      -

                      월별 매출 추이

                      - - - - - - - - - - - - - -
                      -
                      - - - - - - - -
                      -
                      -
                      -

                      영업1팀

                      -
                      -
                      -
                      -

                      영업2팀

                      -
                      -
                      -
                      -

                      생산팀

                      -
                      -
                      -
                      -

                      품질팀

                      -
                      -
                      -
                      -
                      -
                      - - -
                      -
                      - - - - -
                      -

                      SAM 대시보드가 드리는 약속

                      -

                      로그인 한 번이면 전사 매출, 수주, 승인 대기를 한눈에.
                      보고를 기다리는 시간을 제로로 만들어 드립니다.

                      -
                      -
                      -
                      - - -
                      -
                      - -

                      클라우드 기반

                      -
                      -
                      - -

                      PC + 모바일

                      -
                      -
                      - -

                      역할별 권한

                      -
                      -
                      - - -
                      -
                      -
                      -

                      (주)코드브릿지엑스

                      -

                      www.codebridge-x.com

                      -
                      -
                      -

                      뒷면에서 상세 기능을 확인하세요 ▶

                      -
                      -
                      -
                      - - \ No newline at end of file diff --git a/sam/docs/brochure/v5/convert-1page.cjs b/sam/docs/brochure/v5/convert-1page.cjs deleted file mode 100644 index 840ccc9..0000000 --- a/sam/docs/brochure/v5/convert-1page.cjs +++ /dev/null @@ -1,52 +0,0 @@ -const path = require('path'); -module.paths.unshift(path.join(require('os').homedir(), '.claude/skills/pptx-skill/scripts/node_modules')); - -const PptxGenJS = require('pptxgenjs'); -const html2pptx = require(path.join(require('os').homedir(), '.claude/skills/pptx-skill/scripts/html2pptx.js')); -const sharp = require('sharp'); - -async function generateGradientBg() { - const svgGradient = ` - - - - - - - - - `; - const buf = await sharp(Buffer.from(svgGradient)).png().toBuffer(); - return buf.toString('base64'); -} - -async function main() { - const pres = new PptxGenJS(); - - pres.defineLayout({ name: 'PORTRAIT_9x16', width: 5.625, height: 10 }); - pres.layout = 'PORTRAIT_9x16'; - - // Pre-generate gradient background PNG - console.log('Generating gradient background...'); - const bgBase64 = await generateGradientBg(); - - const htmlFile = path.join(__dirname, 'slides', 'brochure-dashboard-1page.html'); - console.log('Converting CEO Dashboard v5 (Premium Gradient) 1-page brochure...'); - - try { - await html2pptx(htmlFile, pres); - } catch (err) { - console.error(`Error: ${err.message}`); - } - - // Set gradient background on each slide - for (const slide of pres.slides) { - slide.background = { data: `image/png;base64,${bgBase64}` }; - } - - const outputPath = path.join(__dirname, 'sam-brochure-v5-dashboard-1page.pptx'); - await pres.writeFile({ fileName: outputPath }); - console.log(`\nPPTX created: ${outputPath}`); -} - -main().catch(console.error); diff --git a/sam/docs/brochure/v5/convert-2page.cjs b/sam/docs/brochure/v5/convert-2page.cjs deleted file mode 100644 index 7aa1fd0..0000000 --- a/sam/docs/brochure/v5/convert-2page.cjs +++ /dev/null @@ -1,56 +0,0 @@ -const path = require('path'); -module.paths.unshift(path.join(require('os').homedir(), '.claude/skills/pptx-skill/scripts/node_modules')); - -const PptxGenJS = require('pptxgenjs'); -const html2pptx = require(path.join(require('os').homedir(), '.claude/skills/pptx-skill/scripts/html2pptx.js')); -const sharp = require('sharp'); - -async function generateGradientBg() { - const svgGradient = ` - - - - - - - - - `; - const buf = await sharp(Buffer.from(svgGradient)).png().toBuffer(); - return buf.toString('base64'); -} - -async function main() { - const pres = new PptxGenJS(); - - pres.defineLayout({ name: 'PORTRAIT_9x16', width: 5.625, height: 10 }); - pres.layout = 'PORTRAIT_9x16'; - - // Pre-generate gradient background PNG - console.log('Generating gradient background...'); - const bgBase64 = await generateGradientBg(); - - const slidesDir = path.join(__dirname, 'slides'); - const slides = ['brochure-dashboard-front.html', 'brochure-dashboard-back.html']; - - for (const file of slides) { - const htmlFile = path.join(slidesDir, file); - console.log(`Converting ${file} ...`); - try { - await html2pptx(htmlFile, pres); - } catch (err) { - console.error(`Error on ${file}: ${err.message}`); - } - } - - // Set gradient background on each slide - for (const slide of pres.slides) { - slide.background = { data: `image/png;base64,${bgBase64}` }; - } - - const outputPath = path.join(__dirname, 'sam-brochure-v5-dashboard-2page.pptx'); - await pres.writeFile({ fileName: outputPath }); - console.log(`\nPPTX created: ${outputPath}`); -} - -main().catch(console.error); diff --git a/sam/docs/brochure/v5/sam-brochure-v5-dashboard-1page.pptx b/sam/docs/brochure/v5/sam-brochure-v5-dashboard-1page.pptx deleted file mode 100644 index 60194bf..0000000 Binary files a/sam/docs/brochure/v5/sam-brochure-v5-dashboard-1page.pptx and /dev/null differ diff --git a/sam/docs/brochure/v5/sam-brochure-v5-dashboard-2page.pptx b/sam/docs/brochure/v5/sam-brochure-v5-dashboard-2page.pptx deleted file mode 100644 index e53d81c..0000000 Binary files a/sam/docs/brochure/v5/sam-brochure-v5-dashboard-2page.pptx and /dev/null differ diff --git a/sam/docs/brochure/v5/slides/brochure-dashboard-1page.html b/sam/docs/brochure/v5/slides/brochure-dashboard-1page.html deleted file mode 100644 index 6e8d331..0000000 --- a/sam/docs/brochure/v5/slides/brochure-dashboard-1page.html +++ /dev/null @@ -1,319 +0,0 @@ - - - - - - - - -
                      - -
                      -
                      -

                      EXECUTIVE EDITION

                      -
                      -
                      - - -
                      -
                      -

                      CEO DASHBOARD

                      -

                      대표님의 시간은
                      보고를 기다리는 데
                      쓰여선 안 됩니다.

                      -

                      로그인 3초. 매출, 수주, 승인까지
                      모든 경영 현황이 한 화면에.

                      -
                      - -
                      - - - - - - - - - - - - - - - - -
                      -
                      - - -
                      -
                      -
                      -

                      SAM 도입 후

                      -
                      -
                      -
                      - - -
                      - -
                      -
                      -
                      -
                      -

                      SAM CEO Dashboard

                      -
                      - -
                      -
                      -

                      5.2억

                      -

                      ▲ 15.3%

                      -

                      월 매출

                      -
                      -
                      -

                      127건

                      -

                      ▲ 8건

                      -

                      누적 수주

                      -
                      -
                      -

                      96%

                      -

                      목표 달성

                      -

                      납기 준수율

                      -
                      -
                      -

                      5건

                      -

                      즉시 처리

                      -

                      승인 대기

                      -
                      -
                      - -
                      -
                      -

                      월별 매출 추이

                      - - - - - - - - - - - - - -
                      -
                      - - - - - - -
                      -
                      -
                      -

                      영업1팀

                      -
                      -
                      -
                      -

                      영업2팀

                      -
                      -
                      -
                      -

                      생산팀

                      -
                      -
                      -
                      -
                      -
                      - - -
                      -

                      대표님이 얻는 것

                      -
                      -
                      - - - - - -

                      즉시 현황 파악

                      -

                      로그인 3초면 전사 확인

                      -
                      -
                      - - - - - - - - - -

                      데이터로 판단

                      -

                      감이 아닌 KPI 비교

                      -
                      -
                      - - - - -

                      모바일 승인

                      -

                      이동중 즉시 결재

                      -
                      -
                      -
                      - - -
                      - - -
                      -

                      대시보드 핵심 기능

                      -
                      -
                      -
                      - - - - - -

                      실시간 매출/수주 KPI

                      -
                      -
                      - - - - - - - - -

                      조직 계층별 실적 트리

                      -
                      -
                      -
                      -
                      - - - - -

                      역할별 수당 현황

                      -
                      -
                      - - - - 5 - -

                      미승인 실시간 알림

                      -
                      -
                      -
                      -
                      - - - - -

                      기간별 트렌드 분석

                      -
                      -
                      - - - - - - - -

                      수익 시뮬레이터

                      -
                      -
                      -
                      -
                      - - -
                      - - -
                      - -
                      -
                      - - - - - -

                      BEFORE

                      -
                      -

                      매출? → 보고 대기 1~2일

                      -

                      수주? → Excel 취합 반나절

                      -

                      승인? → 서류 찾기 30분

                      -

                      실적? → 각 팀장 개별 보고

                      -
                      - -
                      - - - - -
                      - -
                      -
                      - - - - -

                      AFTER (SAM)

                      -
                      -

                      로그인 → 3초 전사 현황

                      -

                      클릭 → 실시간 수주 데이터

                      -

                      뱃지 → 즉시 승인 처리

                      -

                      트리 → 전 조직 한눈에

                      -
                      -
                      - - -
                      -
                      - -

                      클라우드

                      -
                      -
                      - -

                      PC + 모바일

                      -
                      -
                      - -

                      역할별 권한

                      -
                      -
                      - -

                      데이터 암호화

                      -
                      -
                      - - -
                      -
                      -
                      -

                      (주)코드브릿지엑스

                      -

                      www.codebridge-x.com

                      -
                      -
                      -

                      무료 데모 신청

                      -

                      contact@codebridge-x.com

                      -
                      -
                      -
                      - - \ No newline at end of file diff --git a/sam/docs/brochure/v5/slides/brochure-dashboard-back.html b/sam/docs/brochure/v5/slides/brochure-dashboard-back.html deleted file mode 100644 index aa9f629..0000000 --- a/sam/docs/brochure/v5/slides/brochure-dashboard-back.html +++ /dev/null @@ -1,311 +0,0 @@ - - - - - - - - -
                      - -
                      -

                      FEATURES & PRICING

                      -
                      - - -
                      -

                      대시보드 핵심 기능

                      -
                      - -
                      - - - - - -
                      -

                      실시간 KPI 카드

                      -
                      -

                      매출, 수주, 납기율, 승인 대기

                      -
                      - -
                      - - - - - - - - - - - -
                      -

                      조직 실적 트리

                      -
                      -

                      계층별 팀/개인 실적 펼쳐보기

                      -
                      - -
                      - - - - -
                      -

                      역할별 수당 현황

                      -
                      -

                      판매자/관리자/협업자 배분 확인

                      -
                      - -
                      - - - - ! - - -
                      -

                      승인 대기 알림

                      -
                      -

                      가입/지급 미처리 빨간 뱃지

                      -
                      - -
                      - - - - -
                      -

                      기간별 트렌드

                      -
                      -

                      당월/분기/연간 추이 차트

                      -
                      - -
                      - - - - - - - - -
                      -

                      수익 시뮬레이터

                      -
                      -

                      가상 시나리오 수당/마진 계산

                      -
                      - -
                      - - - - - - - - -
                      -

                      모바일 대응

                      -
                      -

                      스마트폰으로 KPI 확인/승인

                      -
                      -
                      -
                      - - -
                      - - -
                      -

                      역할별 맞춤 화면

                      -
                      -
                      - - - - - -

                      CEO

                      -

                      전사 KPI 총괄

                      -
                      -
                      - - - - - - -

                      관리자

                      -

                      팀 실적 관리

                      -
                      -
                      - - - - - - - - -

                      운영자

                      -

                      인력/승인 관리

                      -
                      -
                      - - - - - - - -

                      영업자

                      -

                      내 실적 조회

                      -
                      -
                      -
                      - - -
                      - - -
                      -

                      투자 비용

                      -
                      -
                      -
                      -
                      - - - - -

                      대시보드 포함 기본 패키지

                      -
                      -

                      2,000만원

                      -

                      + 월 50만원 (유지보수)

                      -
                      -
                      -

                      CEO 대시보드 + 견적/수주 + 생산
                      인사/회계 무료 포함

                      -
                      -
                      -
                      -
                      -
                      - - - - - -

                      추가 옵션 (선택)

                      -
                      -
                      -
                      -

                      생산공정 관리

                      -

                      +500만원

                      -
                      -
                      -

                      품질관리(인정검사)

                      -

                      +2,000만원

                      -
                      -
                      -

                      AI 견적 자동 생성

                      -

                      월 10~20만원

                      -
                      -
                      -
                      -
                      -
                      -
                      - - -
                      - - -
                      -

                      도입 프로세스

                      -
                      -
                      - - - - - -

                      1~2주

                      -

                      현장 인터뷰

                      -
                      - - - -
                      - - - - - - - -

                      2~4주

                      -

                      맞춤 개발

                      -
                      - - - -
                      - - - - - -

                      1~2주

                      -

                      데이터 이관

                      -
                      - - - -
                      - - - - -

                      1~2주

                      -

                      교육/안정화

                      -
                      -
                      -
                      - - -
                      -
                      -
                      - - - - -
                      -

                      무료 데모를 신청하세요

                      -

                      대표님 전용 대시보드를 직접 체험

                      -
                      -
                      -
                      -

                      contact@codebridge-x.com

                      -

                      www.codebridge-x.com

                      -
                      -
                      -
                      - - -
                      -

                      (주)코드브릿지엑스 | SAM - Smart Automation Management

                      -
                      - - \ No newline at end of file diff --git a/sam/docs/brochure/v5/slides/brochure-dashboard-front.html b/sam/docs/brochure/v5/slides/brochure-dashboard-front.html deleted file mode 100644 index ba19d78..0000000 --- a/sam/docs/brochure/v5/slides/brochure-dashboard-front.html +++ /dev/null @@ -1,216 +0,0 @@ - - - - - - - - -
                      - -
                      -
                      -

                      EXECUTIVE EDITION

                      -
                      -
                      - - -
                      -

                      CEO DASHBOARD

                      -

                      대표님의 시간은
                      보고를 기다리는 데
                      쓰여선 안 됩니다.

                      -

                      로그인 3초. 매출, 수주, 승인 대기까지
                      모든 경영 현황이 한 화면에.

                      -
                      - - -
                      -
                      -
                      - - - - -

                      1~2일

                      -

                      매출 보고 대기

                      -
                      -
                      - - - - - - -

                      반나절

                      -

                      Excel 수주 취합

                      -
                      -
                      - - - - -

                      30분

                      -

                      결재 서류 찾기

                      -
                      -
                      -

                      매일 반복되는 비효율, SAM이 제로로 만듭니다.

                      -
                      - - -
                      -
                      -
                      -

                      SAM 도입 후

                      -
                      -
                      -
                      - - -
                      - -
                      -
                      -
                      -
                      -

                      SAM CEO Dashboard

                      -
                      - -
                      -
                      - - - - - -

                      5.2억

                      -

                      ▲ 15.3%

                      -

                      월 매출

                      -
                      -
                      - - - - -

                      127건

                      -

                      ▲ 8건

                      -

                      누적 수주

                      -
                      -
                      - - - - -

                      96%

                      -

                      목표 달성

                      -

                      납기 준수율

                      -
                      -
                      - - - - - -

                      5건

                      -

                      즉시 처리

                      -

                      승인 대기

                      -
                      -
                      - -
                      -
                      -

                      월별 매출 추이

                      - - - - - - - - - - - - - -
                      -
                      - - - - - - - -
                      -
                      -
                      -

                      영업1팀

                      -
                      -
                      -
                      -

                      영업2팀

                      -
                      -
                      -
                      -

                      생산팀

                      -
                      -
                      -
                      -

                      품질팀

                      -
                      -
                      -
                      -
                      -
                      - - -
                      -
                      - - - - -
                      -

                      대표님께 드리는 약속

                      -

                      보고를 기다리는 시간을 제로로.
                      의사결정에만 집중하실 수 있도록.

                      -
                      -
                      -
                      - - -
                      -
                      - -

                      클라우드 기반

                      -
                      -
                      - -

                      PC + 모바일

                      -
                      -
                      - -

                      역할별 권한

                      -
                      -
                      - - -
                      -
                      -
                      -

                      (주)코드브릿지엑스

                      -

                      www.codebridge-x.com

                      -
                      -
                      -

                      뒷면에서 상세 기능을 확인하세요 ▶

                      -
                      -
                      -
                      - - \ No newline at end of file diff --git a/sam/docs/brochure/v6/convert-1page.cjs b/sam/docs/brochure/v6/convert-1page.cjs deleted file mode 100644 index 99a9664..0000000 --- a/sam/docs/brochure/v6/convert-1page.cjs +++ /dev/null @@ -1,27 +0,0 @@ -const path = require('path'); -module.paths.unshift(path.join(require('os').homedir(), '.claude/skills/pptx-skill/scripts/node_modules')); - -const PptxGenJS = require('pptxgenjs'); -const html2pptx = require(path.join(require('os').homedir(), '.claude/skills/pptx-skill/scripts/html2pptx.js')); - -async function main() { - const pres = new PptxGenJS(); - - pres.defineLayout({ name: 'PORTRAIT_9x16', width: 5.625, height: 10 }); - pres.layout = 'PORTRAIT_9x16'; - - const htmlFile = path.join(__dirname, 'slides', 'brochure-dashboard-1page.html'); - console.log('Converting CEO Dashboard v6 (Corporate Blue & White) 1-page brochure...'); - - try { - await html2pptx(htmlFile, pres); - } catch (err) { - console.error(`Error: ${err.message}`); - } - - const outputPath = path.join(__dirname, 'sam-brochure-v6-dashboard-1page.pptx'); - await pres.writeFile({ fileName: outputPath }); - console.log(`\nPPTX created: ${outputPath}`); -} - -main().catch(console.error); diff --git a/sam/docs/brochure/v6/convert-2page.cjs b/sam/docs/brochure/v6/convert-2page.cjs deleted file mode 100644 index 604c5be..0000000 --- a/sam/docs/brochure/v6/convert-2page.cjs +++ /dev/null @@ -1,31 +0,0 @@ -const path = require('path'); -module.paths.unshift(path.join(require('os').homedir(), '.claude/skills/pptx-skill/scripts/node_modules')); - -const PptxGenJS = require('pptxgenjs'); -const html2pptx = require(path.join(require('os').homedir(), '.claude/skills/pptx-skill/scripts/html2pptx.js')); - -async function main() { - const pres = new PptxGenJS(); - - pres.defineLayout({ name: 'PORTRAIT_9x16', width: 5.625, height: 10 }); - pres.layout = 'PORTRAIT_9x16'; - - const slidesDir = path.join(__dirname, 'slides'); - const slides = ['brochure-dashboard-front.html', 'brochure-dashboard-back.html']; - - for (const file of slides) { - const htmlFile = path.join(slidesDir, file); - console.log(`Converting ${file} ...`); - try { - await html2pptx(htmlFile, pres); - } catch (err) { - console.error(`Error on ${file}: ${err.message}`); - } - } - - const outputPath = path.join(__dirname, 'sam-brochure-v6-dashboard-2page.pptx'); - await pres.writeFile({ fileName: outputPath }); - console.log(`\nPPTX created: ${outputPath}`); -} - -main().catch(console.error); diff --git a/sam/docs/brochure/v6/sam-brochure-v6-dashboard-1page.pptx b/sam/docs/brochure/v6/sam-brochure-v6-dashboard-1page.pptx deleted file mode 100644 index a9fa7c0..0000000 Binary files a/sam/docs/brochure/v6/sam-brochure-v6-dashboard-1page.pptx and /dev/null differ diff --git a/sam/docs/brochure/v6/sam-brochure-v6-dashboard-2page.pptx b/sam/docs/brochure/v6/sam-brochure-v6-dashboard-2page.pptx deleted file mode 100644 index 8c54f02..0000000 Binary files a/sam/docs/brochure/v6/sam-brochure-v6-dashboard-2page.pptx and /dev/null differ diff --git a/sam/docs/brochure/v6/slides/brochure-dashboard-1page.html b/sam/docs/brochure/v6/slides/brochure-dashboard-1page.html deleted file mode 100644 index 20ee484..0000000 --- a/sam/docs/brochure/v6/slides/brochure-dashboard-1page.html +++ /dev/null @@ -1,374 +0,0 @@ - - - - - - - - -
                      - -
                      -
                      -

                      CEO DASHBOARD

                      -
                      -
                      - - -
                      - - -
                      -
                      -

                      EXECUTIVE DASHBOARD

                      -

                      대표님, 우리 회사
                      지금 어떤 상태인가요?

                      -

                      보고 대기 없이, 로그인 한 번이면

                      -

                      전사 현황이 한눈에 들어옵니다.

                      -
                      -
                      - - - - - - - - - - - - - - - - - - - -
                      -
                      - - -
                      - - -
                      - -
                      -
                      -
                      -
                      -

                      SAM CEO Dashboard

                      -
                      - -
                      -
                      - - - - - - -

                      5.2억

                      -

                      ▲ 15.3%

                      -

                      월 매출

                      -
                      -
                      - - - - -

                      127건

                      -

                      ▲ 8건

                      -

                      수주 잔량

                      -
                      -
                      - - - - 96 - -

                      96%

                      -

                      목표 달성

                      -

                      납기 준수율

                      -
                      -
                      - - - - - -

                      5건

                      -

                      즉시 처리

                      -

                      승인 대기

                      -
                      -
                      - -
                      -
                      -

                      월별 매출 추이

                      - - - - - - - - - -
                      -
                      - - - - - - - -
                      -
                      -
                      -

                      영업1팀 38%

                      -
                      -
                      -
                      -

                      영업2팀 25%

                      -
                      -
                      -
                      -

                      생산팀 22%

                      -
                      -
                      -
                      -

                      품질팀 15%

                      -
                      -
                      -
                      -
                      -
                      - - -
                      -
                      -

                      대표님이 얻는 것

                      -
                      -
                      -
                      - - - - - - -

                      즉시 현황 파악

                      -

                      로그인 3초면
                      전사 현황 확인

                      -
                      -
                      - - - - - - - - - - -

                      데이터로 판단

                      -

                      감이 아닌 숫자로
                      KPI/팀 성과 비교

                      -
                      -
                      - - - - - - -

                      모바일 승인

                      -

                      이동중에도 즉시
                      결재/승인 처리

                      -
                      -
                      -
                      - - -
                      - - -
                      -
                      -

                      대시보드 핵심 기능

                      -
                      -
                      -
                      -
                      - - - - - -

                      실시간 매출/수주 KPI

                      -
                      -
                      - - - - - - - - - - - -

                      조직 계층별 실적 트리

                      -
                      -
                      -
                      -
                      - - - - -

                      역할별 수당 현황

                      -
                      -
                      - - - - 5 - - -

                      미승인 실시간 알림

                      -
                      -
                      -
                      -
                      - - - - -

                      기간별 트렌드 분석

                      -
                      -
                      - - - - - - - - -

                      수익 시뮬레이터

                      -
                      -
                      -
                      -
                      - - -
                      - - -
                      -
                      -
                      - - - - - -

                      BEFORE

                      -
                      -

                      매출? → 보고 대기 1~2일

                      -

                      수주? → Excel 취합 반나절

                      -

                      승인? → 서류 찾기 30분

                      -

                      실적? → 각 팀장 개별 보고

                      -
                      -
                      - - - - -
                      -
                      -
                      - - - - -

                      AFTER (SAM)

                      -
                      -

                      로그인 → 3초 전사 현황

                      -

                      클릭 → 실시간 수주 데이터

                      -

                      뱃지 → 즉시 승인 처리

                      -

                      트리 → 전 조직 한눈에

                      -
                      -
                      - - -
                      -
                      - - - - -

                      실시간 업데이트

                      -
                      -
                      - - - - -

                      PC + 모바일

                      -
                      -
                      - - - - - -

                      역할별 권한

                      -
                      -
                      - - - - - -

                      데이터 암호화

                      -
                      -
                      - - - - - -

                      클라우드

                      -
                      -
                      - - -
                      -
                      -
                      -

                      (주)코드브릿지엑스

                      -

                      www.codebridge-x.com

                      -
                      -
                      -

                      무료 데모 신청

                      -

                      contact@codebridge-x.com

                      -
                      -
                      -
                      - -
                      - - \ No newline at end of file diff --git a/sam/docs/brochure/v6/slides/brochure-dashboard-back.html b/sam/docs/brochure/v6/slides/brochure-dashboard-back.html deleted file mode 100644 index 792d610..0000000 --- a/sam/docs/brochure/v6/slides/brochure-dashboard-back.html +++ /dev/null @@ -1,337 +0,0 @@ - - - - - - - - -
                      - -
                      -
                      -

                      FEATURES & PRICING

                      -
                      -
                      - - -
                      - - -
                      -
                      -

                      대시보드 핵심 기능

                      -
                      -
                      - -
                      - - - - - -
                      -

                      실시간 KPI 카드

                      -
                      -

                      매출, 수주, 납기율, 승인 대기

                      -
                      - -
                      - - - - - - - - - - - - - - - -
                      -

                      조직 실적 트리

                      -
                      -

                      계층별 팀/개인 실적 펼쳐보기

                      -
                      - -
                      - - - - -
                      -

                      역할별 수당 현황

                      -
                      -

                      판매자/관리자/협업자 배분 확인

                      -
                      - -
                      - - - - ! - - -
                      -

                      승인 대기 알림

                      -
                      -

                      가입/지급 미처리 빨간 뱃지

                      -
                      - -
                      - - - - -
                      -

                      기간별 트렌드

                      -
                      -

                      당월/분기/연간 추이 차트

                      -
                      - -
                      - - - - - - - - -
                      -

                      수익 시뮬레이터

                      -
                      -

                      가상 시나리오 수당/마진 계산

                      -
                      - -
                      - - - - - - - - -
                      -

                      모바일 대응

                      -
                      -

                      스마트폰으로 KPI 확인/승인

                      -
                      -
                      -
                      - - -
                      - - -
                      -
                      -

                      역할별 맞춤 화면

                      -
                      -
                      - -
                      - - - - - -

                      CEO

                      -

                      전사 KPI 총괄

                      -
                      - -
                      - - - - - - -

                      관리자

                      -

                      팀 실적 관리

                      -
                      - -
                      - - - - - - - - -

                      운영자

                      -

                      인력/승인 관리

                      -
                      - -
                      - - - - - - - -

                      영업자

                      -

                      내 실적 조회

                      -
                      -
                      -
                      - - -
                      - - -
                      -
                      -

                      투자 비용

                      -
                      -
                      - -
                      -
                      -
                      - - - - -

                      대시보드 포함 기본 패키지

                      -
                      -

                      2,000만원

                      -

                      + 월 50만원 (유지보수)

                      -
                      -
                      -

                      CEO 대시보드 + 견적/수주 + 생산

                      -

                      인사/회계 무료 포함

                      -
                      -
                      - -
                      -
                      -
                      - - - - - -

                      추가 옵션 (선택)

                      -
                      -
                      -
                      -

                      생산공정 관리

                      -

                      +500만원

                      -
                      -
                      -

                      품질관리(인정검사)

                      -

                      +2,000만원

                      -
                      -
                      -

                      AI 견적 자동 생성

                      -

                      월 10~20만원

                      -
                      -
                      -
                      -
                      -
                      -
                      - - -
                      - - -
                      -
                      -

                      도입 프로세스

                      -
                      -
                      -
                      - - - - - -

                      1~2주

                      -

                      현장 인터뷰

                      -
                      - - - -
                      - - - - - - - -

                      2~4주

                      -

                      맞춤 개발

                      -
                      - - - -
                      - - - - - -

                      1~2주

                      -

                      데이터 이관

                      -
                      - - - -
                      - - - - -

                      1~2주

                      -

                      교육/안정화

                      -
                      -
                      -
                      - - -
                      -
                      -
                      - - - - -
                      -

                      무료 데모를 신청하세요

                      -

                      대표님 전용 대시보드를 직접 체험

                      -
                      -
                      -
                      -

                      contact@codebridge-x.com

                      -

                      www.codebridge-x.com

                      -
                      -
                      -
                      - - -
                      -

                      (주)코드브릿지엑스 | SAM - Smart Automation Management

                      -
                      - -
                      - - \ No newline at end of file diff --git a/sam/docs/brochure/v6/slides/brochure-dashboard-front.html b/sam/docs/brochure/v6/slides/brochure-dashboard-front.html deleted file mode 100644 index d25994a..0000000 --- a/sam/docs/brochure/v6/slides/brochure-dashboard-front.html +++ /dev/null @@ -1,231 +0,0 @@ - - - - - - - - -
                      - -
                      -
                      -

                      CEO DASHBOARD

                      -
                      -
                      - - -
                      - - -
                      -
                      -

                      EXECUTIVE DASHBOARD

                      -

                      대표님, 우리 회사
                      지금 어떤 상태인가요?

                      -

                      매출, 수주, 조직 실적, 승인 대기

                      -

                      더 이상 보고를 기다리지 마세요.

                      -
                      - -
                      - - - - - - - - - - - - - 5 - - - - -
                      -
                      - - -
                      - - -
                      - -
                      -
                      -
                      -
                      -

                      SAM CEO Dashboard ― 로그인 후 3초

                      -
                      - -
                      -
                      - - - - - -

                      5.2억

                      -

                      ▲ 15.3%

                      -

                      월 매출

                      -
                      -
                      - - - - -

                      127건

                      -

                      ▲ 8건

                      -

                      누적 수주

                      -
                      -
                      - - - - -

                      96%

                      -

                      목표 달성

                      -

                      납기 준수율

                      -
                      -
                      - - - - - -

                      5건

                      -

                      즉시 처리

                      -

                      승인 대기

                      -
                      -
                      - -
                      -
                      -

                      월별 매출 추이

                      - - - - - - - -
                      -
                      - - - - - - - -
                      -
                      -
                      -

                      영업1팀

                      -
                      -
                      -
                      -

                      영업2팀

                      -
                      -
                      -
                      -

                      생산팀

                      -
                      -
                      -
                      -

                      품질팀

                      -
                      -
                      -
                      -
                      -
                      - - -
                      -
                      -

                      대표님이 얻는 것

                      -
                      -
                      - -
                      - - - - - - -

                      즉시 현황 파악

                      -

                      로그인 3초면
                      전사 현황 확인

                      -
                      - -
                      - - - - - - - - - - -

                      데이터로 판단

                      -

                      감이 아닌 숫자로
                      KPI/팀 성과 비교

                      -
                      - -
                      - - - - - - -

                      모바일 승인

                      -

                      이동중에도 즉시
                      결재/승인 처리

                      -
                      -
                      -
                      - - -
                      -
                      - -

                      클라우드 기반

                      -
                      -
                      - -

                      PC + 모바일

                      -
                      -
                      - -

                      역할별 권한

                      -
                      -
                      - - -
                      -
                      -
                      -

                      (주)코드브릿지엑스

                      -

                      www.codebridge-x.com

                      -
                      -
                      -

                      뒷면에서 상세 기능을 확인하세요 ▶

                      -
                      -
                      -
                      - -
                      - - \ No newline at end of file diff --git a/sam/docs/brochure/v7/convert-1page.cjs b/sam/docs/brochure/v7/convert-1page.cjs deleted file mode 100644 index 723bfce..0000000 --- a/sam/docs/brochure/v7/convert-1page.cjs +++ /dev/null @@ -1,27 +0,0 @@ -const path = require('path'); -module.paths.unshift(path.join(require('os').homedir(), '.claude/skills/pptx-skill/scripts/node_modules')); - -const PptxGenJS = require('pptxgenjs'); -const html2pptx = require(path.join(require('os').homedir(), '.claude/skills/pptx-skill/scripts/html2pptx.js')); - -async function main() { - const pres = new PptxGenJS(); - - pres.defineLayout({ name: 'PORTRAIT_9x16', width: 5.625, height: 10 }); - pres.layout = 'PORTRAIT_9x16'; - - const htmlFile = path.join(__dirname, 'slides', 'brochure-dashboard-1page.html'); - console.log('Converting CEO Dashboard v7 (Warm Gray + Teal) 1-page brochure...'); - - try { - await html2pptx(htmlFile, pres); - } catch (err) { - console.error(`Error: ${err.message}`); - } - - const outputPath = path.join(__dirname, 'sam-brochure-v7-dashboard-1page.pptx'); - await pres.writeFile({ fileName: outputPath }); - console.log(`\nPPTX created: ${outputPath}`); -} - -main().catch(console.error); diff --git a/sam/docs/brochure/v7/convert-2page.cjs b/sam/docs/brochure/v7/convert-2page.cjs deleted file mode 100644 index bf9d100..0000000 --- a/sam/docs/brochure/v7/convert-2page.cjs +++ /dev/null @@ -1,31 +0,0 @@ -const path = require('path'); -module.paths.unshift(path.join(require('os').homedir(), '.claude/skills/pptx-skill/scripts/node_modules')); - -const PptxGenJS = require('pptxgenjs'); -const html2pptx = require(path.join(require('os').homedir(), '.claude/skills/pptx-skill/scripts/html2pptx.js')); - -async function main() { - const pres = new PptxGenJS(); - - pres.defineLayout({ name: 'PORTRAIT_9x16', width: 5.625, height: 10 }); - pres.layout = 'PORTRAIT_9x16'; - - const slidesDir = path.join(__dirname, 'slides'); - const slides = ['brochure-dashboard-front.html', 'brochure-dashboard-back.html']; - - for (const file of slides) { - const htmlFile = path.join(slidesDir, file); - console.log(`Converting ${file} ...`); - try { - await html2pptx(htmlFile, pres); - } catch (err) { - console.error(`Error on ${file}: ${err.message}`); - } - } - - const outputPath = path.join(__dirname, 'sam-brochure-v7-dashboard-2page.pptx'); - await pres.writeFile({ fileName: outputPath }); - console.log(`\nPPTX created: ${outputPath}`); -} - -main().catch(console.error); diff --git a/sam/docs/brochure/v7/sam-brochure-v7-dashboard-1page.pptx b/sam/docs/brochure/v7/sam-brochure-v7-dashboard-1page.pptx deleted file mode 100644 index 6f12fd7..0000000 Binary files a/sam/docs/brochure/v7/sam-brochure-v7-dashboard-1page.pptx and /dev/null differ diff --git a/sam/docs/brochure/v7/sam-brochure-v7-dashboard-2page.pptx b/sam/docs/brochure/v7/sam-brochure-v7-dashboard-2page.pptx deleted file mode 100644 index 20a6bd1..0000000 Binary files a/sam/docs/brochure/v7/sam-brochure-v7-dashboard-2page.pptx and /dev/null differ diff --git a/sam/docs/brochure/v7/slides/brochure-dashboard-1page.html b/sam/docs/brochure/v7/slides/brochure-dashboard-1page.html deleted file mode 100644 index 4cde3f3..0000000 --- a/sam/docs/brochure/v7/slides/brochure-dashboard-1page.html +++ /dev/null @@ -1,376 +0,0 @@ - - - - - - - - -
                      - -
                      -

                      CEO DASHBOARD v7

                      -
                      - - -
                      -
                      -

                      EXECUTIVE DASHBOARD

                      -

                      대표님, 우리 회사
                      지금 어떤 상태인가요?

                      -

                      보고 대기 없이, 로그인 한 번이면
                      전사 현황이 한눈에 들어옵니다.

                      -
                      - -
                      - - - - - - - - - - - - - - - - - - - - - - - -
                      -
                      - - -
                      - - -
                      - -
                      -
                      -
                      -
                      -

                      SAM CEO Dashboard

                      -
                      - -
                      - -
                      - - - - - - -

                      5.2억

                      -

                      ▲ 15.3%

                      -

                      월 매출

                      -
                      - -
                      - - - - -

                      127건

                      -

                      ▲ 8건

                      -

                      수주 잔량

                      -
                      - -
                      - - - - 96 - -

                      96%

                      -

                      목표 달성

                      -

                      납기 준수율

                      -
                      - -
                      - - - - - -

                      5건

                      -

                      즉시 처리

                      -

                      승인 대기

                      -
                      -
                      - -
                      - -
                      -

                      월별 매출 추이

                      - - - - - - - - - -
                      - -
                      - - - - - - - -
                      -
                      -
                      -

                      영업1팀 38%

                      -
                      -
                      -
                      -

                      영업2팀 25%

                      -
                      -
                      -
                      -

                      생산팀 22%

                      -
                      -
                      -
                      -

                      품질팀 15%

                      -
                      -
                      -
                      -
                      -
                      - - -
                      -

                      대표님이 얻는 것

                      -
                      -
                      - - - - - - -

                      즉시 현황 파악

                      -

                      로그인 3초면
                      전사 현황 확인

                      -
                      -
                      - - - - - - - - - - -

                      데이터로 판단

                      -

                      감이 아닌 숫자로
                      KPI/팀 성과 비교

                      -
                      -
                      - - - - - - -

                      모바일 승인

                      -

                      이동중에도 즉시
                      결재/승인 처리

                      -
                      -
                      -
                      - - -
                      - - -
                      -

                      대시보드 핵심 기능

                      -
                      -
                      -
                      - - - - - -

                      실시간 매출/수주 KPI

                      -
                      -
                      - - - - - - - - - - - -

                      조직 계층별 실적 트리

                      -
                      -
                      -
                      -
                      - - - - -

                      역할별 수당 현황

                      -
                      -
                      - - - - 5 - - -

                      미승인 실시간 알림

                      -
                      -
                      -
                      -
                      - - - - -

                      기간별 트렌드 분석

                      -
                      -
                      - - - - - - - - -

                      수익 시뮬레이터

                      -
                      -
                      -
                      -
                      - - -
                      - - -
                      - -
                      -
                      - - - - - -

                      BEFORE

                      -
                      -

                      매출? 보고 대기 1~2일

                      -

                      수주? Excel 취합 반나절

                      -

                      승인? 서류 찾기 30분

                      -

                      실적? 각 팀장 개별 보고

                      -
                      - -
                      - - - - -
                      - -
                      -
                      - - - - -

                      AFTER (SAM)

                      -
                      -

                      로그인 3초 전사 현황

                      -

                      클릭 실시간 수주 데이터

                      -

                      뱃지 즉시 승인 처리

                      -

                      트리 전 조직 한눈에

                      -
                      -
                      - - -
                      -
                      - - - - -

                      실시간 업데이트

                      -
                      -
                      - - - - -

                      PC + 모바일

                      -
                      -
                      - - - - - -

                      역할별 권한

                      -
                      -
                      - - - - - -

                      데이터 암호화

                      -
                      -
                      - - - - - -

                      클라우드

                      -
                      -
                      - - -
                      -
                      -
                      -

                      (주)코드브릿지엑스

                      -

                      www.codebridge-x.com

                      -
                      -
                      -

                      무료 데모 신청

                      -

                      contact@codebridge-x.com

                      -
                      -
                      -
                      - - \ No newline at end of file diff --git a/sam/docs/brochure/v7/slides/brochure-dashboard-back.html b/sam/docs/brochure/v7/slides/brochure-dashboard-back.html deleted file mode 100644 index 7298655..0000000 --- a/sam/docs/brochure/v7/slides/brochure-dashboard-back.html +++ /dev/null @@ -1,373 +0,0 @@ - - - - - - - - -
                      - -
                      -

                      FEATURES & PRICING

                      -
                      - - -
                      -

                      대시보드 핵심 기능

                      -
                      - -
                      - - - - - -
                      -

                      실시간 KPI 카드

                      -
                      -

                      매출, 수주, 납기율, 승인 대기

                      -
                      - -
                      - - - - - - - - - - - - - - - -
                      -

                      조직 실적 트리

                      -
                      -

                      계층별 팀/개인 실적 펼쳐보기

                      -
                      - -
                      - - - - -
                      -

                      역할별 수당 현황

                      -
                      -

                      판매자/관리자/협업자 배분 확인

                      -
                      - -
                      - - - - ! - - -
                      -

                      승인 대기 알림

                      -
                      -

                      가입/지급 미처리 빨간 뱃지

                      -
                      - -
                      - - - - -
                      -

                      기간별 트렌드

                      -
                      -

                      당월/분기/연간 추이 차트

                      -
                      - -
                      - - - - - - - - -
                      -

                      수익 시뮬레이터

                      -
                      -

                      가상 시나리오 수당/마진 계산

                      -
                      - -
                      - - - - - - - - -
                      -

                      모바일 대응

                      -
                      -

                      스마트폰으로 KPI 확인/승인

                      -
                      -
                      -
                      - - -
                      - - -
                      -

                      역할별 맞춤 화면

                      -
                      - -
                      - - - - - -

                      CEO

                      -

                      전사 KPI 총괄

                      -
                      - -
                      - - - - - - -

                      관리자

                      -

                      팀 실적 관리

                      -
                      - -
                      - - - - - - - - -

                      운영자

                      -

                      인력/승인 관리

                      -
                      - -
                      - - - - - - - -

                      영업자

                      -

                      내 실적 조회

                      -
                      -
                      -
                      - - -
                      - - -
                      -

                      대시보드 + SAM ERP/MES 통합

                      -
                      -
                      - - - - - - -

                      견적/수주

                      -
                      -
                      - - - - - - -

                      생산 MES

                      -
                      -
                      - - - - - -

                      품질/검사

                      -
                      -
                      - - - - - -

                      재고/자재

                      -
                      -
                      - - - - -

                      인사/회계

                      -
                      -
                      -

                      대시보드의 모든 데이터는 SAM ERP/MES 실시간 데이터 기반

                      -
                      - - -
                      - - -
                      -

                      투자 비용

                      -
                      - -
                      -
                      -
                      - - - - -

                      대시보드 포함 기본 패키지

                      -
                      -

                      2,000만원

                      -

                      + 월 50만원 (유지보수)

                      -
                      -
                      -

                      CEO 대시보드 + 견적/수주 + 생산
                      인사/회계 무료 포함

                      -
                      -
                      - -
                      -
                      -
                      - - - - - -

                      추가 옵션 (선택)

                      -
                      -
                      -
                      -

                      생산공정 관리

                      -

                      +500만원

                      -
                      -
                      -

                      품질관리(인정검사)

                      -

                      +2,000만원

                      -
                      -
                      -

                      AI 견적 자동 생성

                      -

                      월 10~20만원

                      -
                      -
                      -
                      -
                      -
                      -
                      - - -
                      - - -
                      -

                      도입 프로세스

                      -
                      -
                      - - - - - -

                      1~2주

                      -

                      현장 인터뷰

                      -
                      - - - -
                      - - - - - - - -

                      2~4주

                      -

                      맞춤 개발

                      -
                      - - - -
                      - - - - - -

                      1~2주

                      -

                      데이터 이관

                      -
                      - - - -
                      - - - - -

                      1~2주

                      -

                      교육/안정화

                      -
                      -
                      -
                      - - -
                      -
                      -
                      - - - - -
                      -

                      무료 데모를 신청하세요

                      -

                      대표님 전용 대시보드를 직접 체험

                      -
                      -
                      -
                      -

                      contact@codebridge-x.com

                      -

                      www.codebridge-x.com

                      -
                      -
                      -
                      - - -
                      -

                      (주)코드브릿지엑스 | SAM - Smart Automation Management

                      -
                      - - \ No newline at end of file diff --git a/sam/docs/brochure/v7/slides/brochure-dashboard-front.html b/sam/docs/brochure/v7/slides/brochure-dashboard-front.html deleted file mode 100644 index 1a4ccdd..0000000 --- a/sam/docs/brochure/v7/slides/brochure-dashboard-front.html +++ /dev/null @@ -1,278 +0,0 @@ - - - - - - - - -
                      - -
                      -

                      CEO DASHBOARD v7

                      -
                      - - -
                      -
                      -

                      EXECUTIVE DASHBOARD

                      -

                      대표님, 우리 회사
                      지금 어떤 상태인가요?

                      -

                      매출, 수주, 조직 실적, 승인 대기
                      더 이상 보고를 기다리지 마세요.

                      -
                      - -
                      - - - - - - - - - - - - - - - - - - 5 - - - - - -
                      -
                      - - -
                      - - -
                      -

                      대표님의 하루

                      -
                      - -
                      - - - - - 9AM - -
                      -

                      "어제 매출 얼마야?" 팀장 보고 대기중...

                      -
                      -
                      - -
                      - - - - - 2PM - -
                      -

                      "수주 밀린 거 없어?" Excel 취합중...

                      -
                      -
                      - -
                      - - - - - 5PM - -
                      -

                      "결재할 것 정리해줘" 서류 찾는중...

                      -
                      -
                      -
                      -
                      - - -
                      - - - -

                      SAM 도입 후

                      -
                      - - -
                      - -
                      -
                      -
                      -
                      -

                      SAM CEO Dashboard --- 로그인 후 3초

                      -
                      - -
                      -
                      - - - - - -

                      5.2억

                      -

                      ▲ 15.3%

                      -

                      월 매출

                      -
                      -
                      - - - - -

                      127건

                      -

                      ▲ 8건

                      -

                      누적 수주

                      -
                      -
                      - - - - -

                      96%

                      -

                      목표 달성

                      -

                      납기 준수율

                      -
                      -
                      - - - - - -

                      5건

                      -

                      즉시 처리

                      -

                      승인 대기

                      -
                      -
                      - -
                      -
                      -

                      월별 매출 추이

                      - - - - - - - -
                      -
                      - - - - - - - -
                      -
                      -
                      -

                      영업1팀

                      -
                      -
                      -
                      -

                      영업2팀

                      -
                      -
                      -
                      -

                      생산팀

                      -
                      -
                      -
                      -

                      품질팀

                      -
                      -
                      -
                      -
                      -
                      - - -
                      -

                      대표님이 얻는 것

                      -
                      - -
                      - - - - - - -

                      즉시 현황 파악

                      -

                      로그인 3초면
                      전사 현황 확인

                      -
                      - -
                      - - - - - - - - - - -

                      데이터로 판단

                      -

                      감이 아닌 숫자로
                      KPI/팀 성과 비교

                      -
                      - -
                      - - - - - - -

                      모바일 승인

                      -

                      이동중에도 즉시
                      결재/승인 처리

                      -
                      -
                      -
                      - - -
                      -
                      - -

                      클라우드 기반

                      -
                      -
                      - -

                      PC + 모바일

                      -
                      -
                      - -

                      역할별 권한

                      -
                      -
                      - - -
                      -
                      -
                      -

                      (주)코드브릿지엑스

                      -

                      www.codebridge-x.com

                      -
                      -
                      -

                      뒷면에서 상세 기능을 확인하세요 ▶

                      -
                      -
                      -
                      - - \ No newline at end of file diff --git a/sam/docs/brochure/v8/convert-1page.cjs b/sam/docs/brochure/v8/convert-1page.cjs deleted file mode 100644 index aa5af35..0000000 --- a/sam/docs/brochure/v8/convert-1page.cjs +++ /dev/null @@ -1,27 +0,0 @@ -const path = require('path'); -module.paths.unshift(path.join(require('os').homedir(), '.claude/skills/pptx-skill/scripts/node_modules')); - -const PptxGenJS = require('pptxgenjs'); -const html2pptx = require(path.join(require('os').homedir(), '.claude/skills/pptx-skill/scripts/html2pptx.js')); - -async function main() { - const pres = new PptxGenJS(); - - pres.defineLayout({ name: 'PORTRAIT_9x16', width: 5.625, height: 10 }); - pres.layout = 'PORTRAIT_9x16'; - - const htmlFile = path.join(__dirname, 'slides', 'brochure-dashboard-1page.html'); - console.log('Converting CEO Dashboard v8 (Two-Tone Split) 1-page brochure...'); - - try { - await html2pptx(htmlFile, pres); - } catch (err) { - console.error(`Error: ${err.message}`); - } - - const outputPath = path.join(__dirname, 'sam-brochure-v8-dashboard-1page.pptx'); - await pres.writeFile({ fileName: outputPath }); - console.log(`\nPPTX created: ${outputPath}`); -} - -main().catch(console.error); diff --git a/sam/docs/brochure/v8/convert-2page.cjs b/sam/docs/brochure/v8/convert-2page.cjs deleted file mode 100644 index 1b887e1..0000000 --- a/sam/docs/brochure/v8/convert-2page.cjs +++ /dev/null @@ -1,31 +0,0 @@ -const path = require('path'); -module.paths.unshift(path.join(require('os').homedir(), '.claude/skills/pptx-skill/scripts/node_modules')); - -const PptxGenJS = require('pptxgenjs'); -const html2pptx = require(path.join(require('os').homedir(), '.claude/skills/pptx-skill/scripts/html2pptx.js')); - -async function main() { - const pres = new PptxGenJS(); - - pres.defineLayout({ name: 'PORTRAIT_9x16', width: 5.625, height: 10 }); - pres.layout = 'PORTRAIT_9x16'; - - const slidesDir = path.join(__dirname, 'slides'); - const slides = ['brochure-dashboard-front.html', 'brochure-dashboard-back.html']; - - for (const file of slides) { - const htmlFile = path.join(slidesDir, file); - console.log(`Converting ${file} ...`); - try { - await html2pptx(htmlFile, pres); - } catch (err) { - console.error(`Error on ${file}: ${err.message}`); - } - } - - const outputPath = path.join(__dirname, 'sam-brochure-v8-dashboard-2page.pptx'); - await pres.writeFile({ fileName: outputPath }); - console.log(`\nPPTX created: ${outputPath}`); -} - -main().catch(console.error); diff --git a/sam/docs/brochure/v8/sam-brochure-v8-dashboard-1page.pptx b/sam/docs/brochure/v8/sam-brochure-v8-dashboard-1page.pptx deleted file mode 100644 index 4a2c33d..0000000 Binary files a/sam/docs/brochure/v8/sam-brochure-v8-dashboard-1page.pptx and /dev/null differ diff --git a/sam/docs/brochure/v8/sam-brochure-v8-dashboard-2page.pptx b/sam/docs/brochure/v8/sam-brochure-v8-dashboard-2page.pptx deleted file mode 100644 index ade5f05..0000000 Binary files a/sam/docs/brochure/v8/sam-brochure-v8-dashboard-2page.pptx and /dev/null differ diff --git a/sam/docs/brochure/v8/slides/brochure-dashboard-1page.html b/sam/docs/brochure/v8/slides/brochure-dashboard-1page.html deleted file mode 100644 index fbf62ea..0000000 --- a/sam/docs/brochure/v8/slides/brochure-dashboard-1page.html +++ /dev/null @@ -1,238 +0,0 @@ - - - - - - - - -
                      - -
                      - -
                      -
                      -

                      EXECUTIVE DASHBOARD

                      -
                      -
                      - - -
                      -

                      대표님, 우리 회사

                      -

                      지금 어떤 상태인가요?

                      -

                      보고 대기 없이, 로그인 한 번이면 전사 현황이 한눈에.

                      -
                      - - -
                      -
                      - - - - - -

                      5.2억

                      -

                      ▲ 15.3%

                      -

                      월 매출

                      -
                      -
                      - - - - -

                      127건

                      -

                      ▲ 8건

                      -

                      누적 수주

                      -
                      -
                      - - - - -

                      96%

                      -

                      목표 달성

                      -

                      납기 준수율

                      -
                      -
                      - - - - - -

                      5건

                      -

                      즉시 처리

                      -

                      승인 대기

                      -
                      -
                      -
                      - - -
                      - - -
                      -

                      대표님이 얻는 것

                      -
                      -
                      -

                      즉시 현황 파악

                      -

                      로그인 3초면

                      -

                      전사 현황 확인

                      -
                      -
                      -

                      데이터로 판단

                      -

                      감이 아닌 숫자로

                      -

                      KPI/팀 성과 비교

                      -
                      -
                      -

                      모바일 승인

                      -

                      이동중에도 즉시

                      -

                      결재/승인 처리

                      -
                      -
                      -
                      - - -
                      - - -
                      -

                      대시보드 핵심 기능

                      -
                      -
                      - - - - - -

                      실시간 KPI 카드

                      -

                      매출, 수주, 납기율, 승인

                      -
                      -
                      - - - - - - - - -

                      조직 실적 트리

                      -

                      계층별 팀/개인 실적

                      -
                      -
                      - - - - -

                      역할별 수당 현황

                      -

                      판매자/관리자/협업자

                      -
                      -
                      - - - - ! - -

                      승인 대기 알림

                      -

                      미처리 빨간 뱃지

                      -
                      -
                      - - - - -

                      기간별 트렌드

                      -

                      당월/분기/연간 추이

                      -
                      -
                      - - - - - - - -

                      수익 시뮬레이터

                      -

                      가상 시나리오 계산

                      -
                      -
                      - - - - - - - - -

                      모바일 대응

                      -

                      스마트폰 KPI/승인

                      -
                      -
                      -
                      - - -
                      - - -
                      -

                      투자 비용

                      -
                      -
                      -

                      기본 패키지

                      -

                      2,000만원

                      -

                      + 월 50만원 (유지보수)

                      -
                      -
                      -

                      추가 옵션 (선택)

                      -
                      -

                      생산공정 관리

                      -

                      +500만원

                      -
                      -
                      -

                      품질관리(인정검사)

                      -

                      +2,000만원

                      -
                      -
                      -

                      AI 견적 자동 생성

                      -

                      월 10~20만원

                      -
                      -
                      -
                      -
                      - - -
                      -
                      -
                      - - - - -
                      -

                      무료 데모를 신청하세요

                      -

                      대표님 전용 대시보드를 직접 체험

                      -
                      -
                      -
                      -

                      contact@codebridge-x.com

                      -

                      www.codebridge-x.com

                      -
                      -
                      -
                      - - -
                      -

                      (주)코드브릿지엑스 | SAM - Smart Automation Management

                      -
                      -
                      - - \ No newline at end of file diff --git a/sam/docs/brochure/v8/slides/brochure-dashboard-back.html b/sam/docs/brochure/v8/slides/brochure-dashboard-back.html deleted file mode 100644 index 28747b2..0000000 --- a/sam/docs/brochure/v8/slides/brochure-dashboard-back.html +++ /dev/null @@ -1,320 +0,0 @@ - - - - - - - - -
                      - -
                      -
                      -

                      FEATURES & PRICING

                      -
                      -
                      - - -
                      -

                      대시보드 핵심 기능

                      -
                      - -
                      - - - - - -
                      -

                      실시간 KPI 카드

                      -
                      -

                      매출, 수주, 납기율, 승인 대기

                      -
                      - -
                      - - - - - - - - - - - -
                      -

                      조직 실적 트리

                      -
                      -

                      계층별 팀/개인 실적 펼쳐보기

                      -
                      - -
                      - - - - -
                      -

                      역할별 수당 현황

                      -
                      -

                      판매자/관리자/협업자 배분 확인

                      -
                      - -
                      - - - - ! - - -
                      -

                      승인 대기 알림

                      -
                      -

                      가입/지급 미처리 빨간 뱃지

                      -
                      - -
                      - - - - -
                      -

                      기간별 트렌드

                      -
                      -

                      당월/분기/연간 추이 차트

                      -
                      - -
                      - - - - - - - - -
                      -

                      수익 시뮬레이터

                      -
                      -

                      가상 시나리오 수당/마진 계산

                      -
                      - -
                      - - - - - - - - -
                      -

                      모바일 대응

                      -
                      -

                      스마트폰으로 KPI 확인/승인

                      -
                      -
                      -
                      - - -
                      - - -
                      -

                      역할별 맞춤 화면

                      -
                      - -
                      - - - - - -

                      CEO

                      -

                      전사 KPI 총괄

                      -
                      - -
                      - - - - - - -

                      관리자

                      -

                      팀 실적 관리

                      -
                      - -
                      - - - - - - - - -

                      운영자

                      -

                      인력/승인 관리

                      -
                      - -
                      - - - - - - - -

                      영업자

                      -

                      내 실적 조회

                      -
                      -
                      -
                      - - -
                      - - -
                      -

                      투자 비용

                      -
                      - -
                      -
                      -
                      - - - - -

                      대시보드 포함 기본 패키지

                      -
                      -

                      2,000만원

                      -

                      + 월 50만원 (유지보수)

                      -
                      -
                      -

                      CEO 대시보드 + 견적/수주 + 생산

                      -

                      인사/회계 무료 포함

                      -
                      -
                      - -
                      -
                      -
                      - - - - - -

                      추가 옵션 (선택)

                      -
                      -
                      -
                      -

                      생산공정 관리

                      -

                      +500만원

                      -
                      -
                      -

                      품질관리(인정검사)

                      -

                      +2,000만원

                      -
                      -
                      -

                      AI 견적 자동 생성

                      -

                      월 10~20만원

                      -
                      -
                      -
                      -
                      -
                      -
                      - - -
                      - - -
                      -

                      도입 프로세스

                      -
                      -
                      - - - - - -

                      1~2주

                      -

                      현장 인터뷰

                      -
                      - - - -
                      - - - - - - - -

                      2~4주

                      -

                      맞춤 개발

                      -
                      - - - -
                      - - - - - -

                      1~2주

                      -

                      데이터 이관

                      -
                      - - - -
                      - - - - -

                      1~2주

                      -

                      교육/안정화

                      -
                      -
                      -
                      - - -
                      -
                      -
                      - - - - -
                      -

                      무료 데모를 신청하세요

                      -

                      대표님 전용 대시보드를 직접 체험

                      -
                      -
                      -
                      -

                      contact@codebridge-x.com

                      -

                      www.codebridge-x.com

                      -
                      -
                      -
                      - - -
                      -

                      (주)코드브릿지엑스 | SAM - Smart Automation Management

                      -
                      - - \ No newline at end of file diff --git a/sam/docs/brochure/v8/slides/brochure-dashboard-front.html b/sam/docs/brochure/v8/slides/brochure-dashboard-front.html deleted file mode 100644 index 10ecdb0..0000000 --- a/sam/docs/brochure/v8/slides/brochure-dashboard-front.html +++ /dev/null @@ -1,281 +0,0 @@ - - - - - - - - -
                      - -
                      - -
                      -
                      -

                      EXECUTIVE DASHBOARD

                      -
                      -
                      - - -
                      -

                      대표님, 우리 회사

                      -

                      지금 어떤 상태인가요?

                      -

                      매출, 수주, 조직 실적, 승인 대기

                      -

                      더 이상 보고를 기다리지 마세요.

                      -
                      - - -
                      - -
                      - - - - - -

                      5.2억

                      -

                      ▲ 15.3%

                      -

                      월 매출

                      -
                      - -
                      - - - - -

                      127건

                      -

                      ▲ 8건

                      -

                      누적 수주

                      -
                      - -
                      - - - - -

                      96%

                      -

                      목표 달성

                      -

                      납기 준수율

                      -
                      - -
                      - - - - - -

                      5건

                      -

                      즉시 처리

                      -

                      승인 대기

                      -
                      -
                      -
                      - - -
                      - - -
                      -

                      대표님이 얻는 것

                      -
                      - -
                      - - - - - - -

                      즉시 현황 파악

                      -

                      로그인 3초면

                      -

                      전사 현황 확인

                      -
                      - -
                      - - - - - - - - - -

                      데이터로 판단

                      -

                      감이 아닌 숫자로

                      -

                      KPI/팀 성과 비교

                      -
                      - -
                      - - - - - - -

                      모바일 승인

                      -

                      이동중에도 즉시

                      -

                      결재/승인 처리

                      -
                      -
                      -
                      - - -
                      - - -
                      - -
                      -
                      - - - - - -

                      BEFORE

                      -
                      -

                      매출? → 보고 대기 1~2일

                      -

                      수주? → Excel 취합 반나절

                      -

                      승인? → 서류 찾기 30분

                      -

                      실적? → 각 팀장 개별 보고

                      -
                      - -
                      - - - - -
                      - -
                      -
                      - - - - -

                      AFTER (SAM)

                      -
                      -

                      로그인 3초 → 전사 현황

                      -

                      클릭 한 번 → 실시간 수주

                      -

                      뱃지 터치 → 즉시 승인

                      -

                      트리 펼침 → 전 조직 한눈에

                      -
                      -
                      - - -
                      - - -
                      -

                      대시보드 핵심 기능

                      -
                      -
                      -
                      - - - - - -

                      실시간 매출/수주 KPI

                      -
                      -
                      - - - - - - - - - - - -

                      조직 계층별 실적 트리

                      -
                      -
                      -
                      -
                      - - - - -

                      역할별 수당 현황

                      -
                      -
                      - - - - 5 - - -

                      미승인 실시간 알림

                      -
                      -
                      -
                      -
                      - - - - -

                      기간별 트렌드 분석

                      -
                      -
                      - - - - - - - - -

                      수익 시뮬레이터

                      -
                      -
                      -
                      -
                      - - -
                      -
                      - -

                      클라우드 기반

                      -
                      -
                      - -

                      PC + 모바일

                      -
                      -
                      - -

                      역할별 권한

                      -
                      -
                      - -

                      데이터 암호화

                      -
                      -
                      - - -
                      -
                      -
                      -

                      (주)코드브릿지엑스

                      -

                      www.codebridge-x.com

                      -
                      -
                      -

                      뒷면에서 상세 기능을 확인하세요 ▶

                      -
                      -
                      -
                      -
                      - - \ No newline at end of file diff --git a/sam/docs/brochure/v9/convert-1page.cjs b/sam/docs/brochure/v9/convert-1page.cjs deleted file mode 100644 index 9846c6f..0000000 --- a/sam/docs/brochure/v9/convert-1page.cjs +++ /dev/null @@ -1,27 +0,0 @@ -const path = require('path'); -module.paths.unshift(path.join(require('os').homedir(), '.claude/skills/pptx-skill/scripts/node_modules')); - -const PptxGenJS = require('pptxgenjs'); -const html2pptx = require(path.join(require('os').homedir(), '.claude/skills/pptx-skill/scripts/html2pptx.js')); - -async function main() { - const pres = new PptxGenJS(); - - pres.defineLayout({ name: 'PORTRAIT_9x16', width: 5.625, height: 10 }); - pres.layout = 'PORTRAIT_9x16'; - - const htmlFile = path.join(__dirname, 'slides', 'brochure-dashboard-1page.html'); - console.log('Converting CEO Dashboard v9 (Minimal White + Indigo) 1-page brochure...'); - - try { - await html2pptx(htmlFile, pres); - } catch (err) { - console.error(`Error: ${err.message}`); - } - - const outputPath = path.join(__dirname, 'sam-brochure-v9-dashboard-1page.pptx'); - await pres.writeFile({ fileName: outputPath }); - console.log(`\nPPTX created: ${outputPath}`); -} - -main().catch(console.error); diff --git a/sam/docs/brochure/v9/convert-2page.cjs b/sam/docs/brochure/v9/convert-2page.cjs deleted file mode 100644 index a635175..0000000 --- a/sam/docs/brochure/v9/convert-2page.cjs +++ /dev/null @@ -1,31 +0,0 @@ -const path = require('path'); -module.paths.unshift(path.join(require('os').homedir(), '.claude/skills/pptx-skill/scripts/node_modules')); - -const PptxGenJS = require('pptxgenjs'); -const html2pptx = require(path.join(require('os').homedir(), '.claude/skills/pptx-skill/scripts/html2pptx.js')); - -async function main() { - const pres = new PptxGenJS(); - - pres.defineLayout({ name: 'PORTRAIT_9x16', width: 5.625, height: 10 }); - pres.layout = 'PORTRAIT_9x16'; - - const slidesDir = path.join(__dirname, 'slides'); - const slides = ['brochure-dashboard-front.html', 'brochure-dashboard-back.html']; - - for (const file of slides) { - const htmlFile = path.join(slidesDir, file); - console.log(`Converting ${file} ...`); - try { - await html2pptx(htmlFile, pres); - } catch (err) { - console.error(`Error on ${file}: ${err.message}`); - } - } - - const outputPath = path.join(__dirname, 'sam-brochure-v9-dashboard-2page.pptx'); - await pres.writeFile({ fileName: outputPath }); - console.log(`\nPPTX created: ${outputPath}`); -} - -main().catch(console.error); diff --git a/sam/docs/brochure/v9/sam-brochure-v9-dashboard-1page.pptx b/sam/docs/brochure/v9/sam-brochure-v9-dashboard-1page.pptx deleted file mode 100644 index 9a39ae4..0000000 Binary files a/sam/docs/brochure/v9/sam-brochure-v9-dashboard-1page.pptx and /dev/null differ diff --git a/sam/docs/brochure/v9/sam-brochure-v9-dashboard-2page.pptx b/sam/docs/brochure/v9/sam-brochure-v9-dashboard-2page.pptx deleted file mode 100644 index 3fd20cc..0000000 Binary files a/sam/docs/brochure/v9/sam-brochure-v9-dashboard-2page.pptx and /dev/null differ diff --git a/sam/docs/brochure/v9/slides/brochure-dashboard-1page.html b/sam/docs/brochure/v9/slides/brochure-dashboard-1page.html deleted file mode 100644 index d228d07..0000000 --- a/sam/docs/brochure/v9/slides/brochure-dashboard-1page.html +++ /dev/null @@ -1,266 +0,0 @@ - - - - - - - - -
                      - - -
                      - -
                      -

                      v9

                      -
                      - - -
                      -

                      EXECUTIVE DASHBOARD

                      -

                      대표님, 우리 회사

                      -

                      지금 어떤 상태인가요?

                      -

                      보고 대기 없이, 로그인 한 번이면 전사 현황이 한눈에.

                      -
                      - - -
                      - - -
                      - -
                      -
                      -
                      -
                      -

                      SAM CEO Dashboard

                      -
                      - -
                      -
                      -

                      5.2억

                      -

                      +15.3%

                      -

                      월 매출

                      -
                      -
                      -

                      127건

                      -

                      +8건

                      -

                      누적 수주

                      -
                      -
                      -

                      96%

                      -

                      목표 달성

                      -

                      납기 준수율

                      -
                      -
                      -

                      5건

                      -

                      즉시 처리

                      -

                      승인 대기

                      -
                      -
                      - -
                      -
                      -

                      월별 매출 추이

                      - - - - - -
                      -
                      - - - - - - - -
                      -
                      -
                      -

                      영업1팀

                      -
                      -
                      -
                      -

                      영업2팀

                      -
                      -
                      -
                      -

                      생산팀

                      -
                      -
                      -
                      -

                      품질팀

                      -
                      -
                      -
                      -
                      -
                      - - -
                      -
                      - - - - -

                      즉시 현황 파악

                      -

                      3초면 전사 현황

                      -
                      -
                      - - - - - -

                      데이터로 판단

                      -

                      KPI/팀 성과 비교

                      -
                      -
                      - - - - -

                      모바일 승인

                      -

                      즉시 결재 처리

                      -
                      -
                      - - -
                      - - -
                      -

                      핵심 기능

                      -
                      -
                      -
                      -

                      실시간 KPI 카드

                      -
                      -
                      -
                      -

                      조직 실적 트리

                      -
                      -
                      -
                      -

                      역할별 수당 현황

                      -
                      -
                      -
                      -

                      승인 대기 알림

                      -
                      -
                      -
                      -

                      기간별 트렌드

                      -
                      -
                      -
                      -

                      수익 시뮬레이터

                      -
                      -
                      -
                      -

                      모바일 대응

                      -
                      -
                      -
                      - - -
                      - - -
                      -

                      역할별 맞춤 화면

                      -
                      -
                      -

                      CEO

                      -

                      전사 KPI

                      -
                      -
                      -

                      관리자

                      -

                      팀 실적

                      -
                      -
                      -

                      운영자

                      -

                      인력/승인

                      -
                      -
                      -

                      영업자

                      -

                      내 실적

                      -
                      -
                      -
                      - - -
                      - - -
                      -

                      투자 비용

                      -
                      -

                      2,000만원

                      -

                      + 월 50만원 (유지보수)

                      -
                      -

                      CEO 대시보드 + 견적/수주 + 생산 + 인사/회계 포함

                      -
                      - - -
                      -

                      도입 프로세스

                      -
                      -
                      -

                      01

                      -

                      현장 인터뷰

                      -
                      - - - -
                      -

                      02

                      -

                      맞춤 개발

                      -
                      - - - -
                      -

                      03

                      -

                      데이터 이관

                      -
                      - - - -
                      -

                      04

                      -

                      교육/안정화

                      -
                      -
                      -
                      - - -
                      -
                      -
                      -

                      무료 데모를 신청하세요

                      -

                      대표님 전용 대시보드를 직접 체험

                      -
                      -
                      -

                      contact@codebridge-x.com

                      -

                      www.codebridge-x.com

                      -
                      -
                      -
                      -
                      -

                      (주)코드브릿지엑스

                      -
                      - - \ No newline at end of file diff --git a/sam/docs/brochure/v9/slides/brochure-dashboard-back.html b/sam/docs/brochure/v9/slides/brochure-dashboard-back.html deleted file mode 100644 index 46e438c..0000000 --- a/sam/docs/brochure/v9/slides/brochure-dashboard-back.html +++ /dev/null @@ -1,229 +0,0 @@ - - - - - - - - -
                      - - -
                      - -
                      -

                      FEATURES & PRICING

                      -
                      - - -
                      -

                      대시보드 핵심 기능

                      -
                      - -
                      -
                      -

                      실시간 KPI 카드

                      -

                      매출, 수주, 납기율, 승인 대기

                      -
                      - -
                      -
                      -

                      조직 실적 트리

                      -

                      계층별 팀/개인 실적 펼쳐보기

                      -
                      - -
                      -
                      -

                      역할별 수당 현황

                      -

                      판매자/관리자/협업자 배분 확인

                      -
                      - -
                      -
                      -

                      승인 대기 알림

                      -

                      가입/지급 미처리 빨간 뱃지

                      -
                      - -
                      -
                      -

                      기간별 트렌드

                      -

                      당월/분기/연간 추이 차트

                      -
                      - -
                      -
                      -

                      수익 시뮬레이터

                      -

                      가상 시나리오 수당/마진 계산

                      -
                      - -
                      -
                      -

                      모바일 대응

                      -

                      스마트폰으로 KPI 확인/승인

                      -
                      -
                      -
                      - - -
                      - - -
                      -

                      역할별 맞춤 화면

                      -
                      - -
                      - - - - - -

                      CEO

                      -

                      전사 KPI 총괄

                      -
                      - -
                      - - - - - - -

                      관리자

                      -

                      팀 실적 관리

                      -
                      - -
                      - - - - - - - - -

                      운영자

                      -

                      인력/승인 관리

                      -
                      - -
                      - - - - - - - -

                      영업자

                      -

                      내 실적 조회

                      -
                      -
                      -
                      - - -
                      - - -
                      -

                      투자 비용

                      -
                      - -
                      -

                      대시보드 포함 기본 패키지

                      -

                      2,000만원

                      -

                      + 월 50만원 (유지보수)

                      -
                      -

                      CEO 대시보드 + 견적/수주 + 생산

                      -

                      인사/회계 무료 포함

                      -
                      -
                      - -
                      -

                      추가 옵션 (선택)

                      -
                      -
                      -

                      생산공정 관리

                      -

                      +500만원

                      -
                      -
                      -

                      품질관리(인정검사)

                      -

                      +2,000만원

                      -
                      -
                      -

                      AI 견적 자동 생성

                      -

                      월 10~20만원

                      -
                      -
                      -
                      -
                      -
                      - - -
                      - - -
                      -

                      도입 프로세스

                      -
                      -
                      -

                      01

                      -

                      현장 인터뷰

                      -

                      1~2주

                      -
                      - - - -
                      -

                      02

                      -

                      맞춤 개발

                      -

                      2~4주

                      -
                      - - - -
                      -

                      03

                      -

                      데이터 이관

                      -

                      1~2주

                      -
                      - - - -
                      -

                      04

                      -

                      교육/안정화

                      -

                      1~2주

                      -
                      -
                      -
                      - - -
                      -
                      -
                      -

                      무료 데모를 신청하세요

                      -

                      대표님 전용 대시보드를 직접 체험

                      -
                      -
                      -

                      contact@codebridge-x.com

                      -

                      www.codebridge-x.com

                      -
                      -
                      -
                      - - -
                      -

                      (주)코드브릿지엑스

                      -
                      - - \ No newline at end of file diff --git a/sam/docs/brochure/v9/slides/brochure-dashboard-front.html b/sam/docs/brochure/v9/slides/brochure-dashboard-front.html deleted file mode 100644 index dc52623..0000000 --- a/sam/docs/brochure/v9/slides/brochure-dashboard-front.html +++ /dev/null @@ -1,181 +0,0 @@ - - - - - - - - -
                      - - -
                      - -
                      -

                      v9

                      -
                      - - -
                      -

                      EXECUTIVE DASHBOARD

                      -

                      대표님, 우리 회사

                      -

                      지금 어떤 상태인가요?

                      -

                      매출, 수주, 조직 실적, 승인 대기

                      -

                      더 이상 보고를 기다리지 마세요.

                      -
                      - - -
                      - - -
                      - -
                      -
                      -
                      -
                      -

                      SAM CEO Dashboard

                      -
                      - -
                      - -
                      - - - - - -

                      5.2억

                      -

                      +15.3%

                      -

                      월 매출

                      -
                      - -
                      - - - - -

                      127건

                      -

                      +8건

                      -

                      누적 수주

                      -
                      - -
                      - - - - -

                      96%

                      -

                      목표 달성

                      -

                      납기 준수율

                      -
                      - -
                      - - - - - -

                      5건

                      -

                      즉시 처리

                      -

                      승인 대기

                      -
                      -
                      - -
                      - -
                      -

                      월별 매출 추이

                      - - - - - - -
                      - -
                      - - - - - - - -
                      -
                      -
                      -

                      영업1팀

                      -
                      -
                      -
                      -

                      영업2팀

                      -
                      -
                      -
                      -

                      생산팀

                      -
                      -
                      -
                      -

                      품질팀

                      -
                      -
                      -
                      -
                      -
                      - - -
                      - -
                      - - - - -

                      즉시 현황 파악

                      -

                      로그인 3초면

                      -

                      전사 현황 확인

                      -
                      - -
                      - - - - - -

                      데이터로 판단

                      -

                      감이 아닌 숫자로

                      -

                      KPI/팀 성과 비교

                      -
                      - -
                      - - - - -

                      모바일 승인

                      -

                      이동중에도 즉시

                      -

                      결재/승인 처리

                      -
                      -
                      - - -
                      -
                      -

                      (주)코드브릿지엑스

                      -

                      www.codebridge-x.com

                      -
                      -

                      뒷면에서 상세 기능을 확인하세요

                      -
                      - - \ No newline at end of file diff --git a/sam/docs/features/barobill-kakaotalk/README.md b/sam/docs/features/barobill-kakaotalk/README.md deleted file mode 100644 index 09f1909..0000000 --- a/sam/docs/features/barobill-kakaotalk/README.md +++ /dev/null @@ -1,410 +0,0 @@ -# 바로빌 카카오톡 (알림톡/친구톡) 연동 - -> **문서 버전**: 1.1 -> **작성일**: 2026-02-14 -> **최종 수정**: 2026-02-27 -> **상태**: 운영 중 (알림톡 + SMS + 환경별 분기 완료) -> **대상 프로젝트**: MNG - ---- - -## 1. 개요 - -### 1.1 목적 - -바로빌(Barobill) 플랫폼의 카카오톡 알림톡/친구톡 API를 SAM에 연동하여, -고객사에 카카오톡 메시지를 자동 또는 수동으로 발송하는 기능을 제공한다. - -### 1.2 사전 요구사항 - -| 항목 | 상태 | 설명 | -|------|------|------| -| 법인 명의 휴대폰 준비 | **완료** | 카카오톡 채널 가입에 법인 명의 번호 사용 | -| 카카오톡 채널 개설 | **완료** (2026-02-20) | 채널 ID: `@codebridge`, 채널명: (주)코드브릿지엑스 | -| 바로빌 카카오톡 서비스 신청 | **완료** (2026-02-20) | 바로빌 관리자 페이지에서 카카오톡 서비스 활성화 | -| 채널 연동 (바로빌↔카카오) | **완료** (2026-02-20) | 바로빌 관리 URL에서 채널 연동 처리 | -| 바로빌 파트너 과금 설정 | **완료** (2026-02-23) | 바로빌 측에서 파트너사 과금 설정 완료 | -| 알림톡 템플릿 v1 검수 | **완료** (2026-02-22) | `전자계약_서명요청`, `전자계약_리마인드` 2종 승인 | -| 알림톡 템플릿 v2 검수 | **완료** (2026-02-25) | 버튼 URL에 `#{토큰}` 변수 포함 3종 승인 | -| 알림톡 `전자계약_완료` | **완료** (2026-02-26) | 서명 완료 알림 발송용 템플릿 승인 | -| 역할 기반 알림 분기 | **완료** (2026-02-26) | 본사=이메일, 상대방=알림톡/SMS | -| 환경별 템플릿 분기 | **완료** (2026-02-27) | `_DEV` 접미사 개발 템플릿 등록 | -| DEV 템플릿 검수 | **심사 중** (2026-02-27 접수) | 개발서버용 3종 (`admin.codebridge-x.com`) | - -> 상세 등록 가이드: [카카오톡 알림톡 채널 및 템플릿 등록 가이드](../../guides/카카오톡-알림톡-채널-템플릿-등록.md) - -### 1.3 알림톡 vs 친구톡 - -| 구분 | 알림톡 | 친구톡 | -|------|--------|--------| -| **용도** | 정보성 메시지 (주문확인, 배송안내 등) | 광고성 메시지 (프로모션, 이벤트 등) | -| **수신 대상** | 모든 카카오톡 사용자 | 채널 친구 추가한 사용자만 | -| **템플릿** | 필수 (카카오 사전 검수) | 불필요 (자유 형식) | -| **광고 표시** | 불가 | 필수 (`(광고)` 표기) | -| **이미지 첨부** | 불가 | 가능 (이미지/와이드 이미지) | -| **비용** | 건당 약 8~9원 | 건당 약 15~20원 | -| **SMS 대체발송** | 설정 가능 | 설정 가능 | - ---- - -## 2. 아키텍처 - -### 2.1 시스템 구조 - -``` -SAM MNG (브라우저) - │ - ├─ [페이지] /barobill/kakaotalk/* ← Blade 뷰 - │ KakaotalkController (페이지 렌더링) - │ - ├─ [API] /api/admin/barobill/kakaotalk/* ← AJAX 호출 - │ BarobillKakaotalkController - │ - └─ [전자계약] /esign/* ← 자동 발송 - EsignApiController::sendAlimtalk() - │ - └─ BarobillService (SOAP 클라이언트) - │ - └─ 바로빌 KAKAOTALK.asmx (WSDL) - │ - └─ 카카오톡 서버 -``` - -### 2.2 바로빌 SOAP API 엔드포인트 - -| 환경 | WSDL URL | -|------|----------| -| **테스트** | `https://testws.baroservice.com/KAKAOTALK.asmx?WSDL` | -| **운영** | `https://ws.baroservice.com/KAKAOTALK.asmx?WSDL` | - ---- - -## 3. 전자계약 알림톡 연동 (핵심) - -### 3.1 발송 흐름 - -``` -전자계약 생성 (E-Sign) - │ - ├─ [1단계] EsignApiController::sendAlimtalk() - │ │ - │ ├─ 채널 ID 조회 (getKakaotalkChannelId) - │ ├─ 템플릿 본문 + 버튼 조회 (getTemplateData) - │ ├─ 변수 치환 (#{이름}, #{계약명}, #{기한}) - │ └─ SendATKakaotalkEx 호출 - │ - ├─ [2단계] 바로빌 접수 → SendKey 반환 - │ - ├─ [3단계] 3초 대기 후 GetSendKakaotalk으로 전달 결과 확인 - │ │ - │ ├─ ResultCode = 1 → 성공 - │ └─ ResultCode != 1 → 실패 (에러 반환) - │ - └─ [이메일 폴백] 알림톡 실패 시 이메일로 자동 전환 -``` - -### 3.2 등록된 템플릿 (v1 — 현재 운영) - -**`전자계약_서명요청`** - -``` - 안녕하세요, #{이름}님. - 전자계약 서명 요청이 도착했습니다. - - ■ 계약명: #{계약명} - ■ 서명 기한: #{기한} - - 아래 버튼을 눌러 계약서를 확인하고 서명해 주세요. -``` - -- 버튼: `계약서 확인하기` (WL) -- Url1/Url2: `https://mng.codebridge-x.com` - -**`전자계약_리마인드`** - -``` -안녕하세요, #{이름}님. -아직 서명이 완료되지 않은 전자계약이 있습니다. - - ■ 계약명: #{계약명} - ■ 서명 기한: #{기한} - - 기한 내에 서명을 완료해 주세요. -``` - -- 버튼: `계약서 확인하기` (WL) -- Url1/Url2: `https://mng.codebridge-x.com` - -### 3.3 등록 예정 템플릿 (v2 — 심사 중) - -> **2026-02-24 재등록**: 버튼 URL에 `#{토큰}` 변수를 포함하여 동적 서명 URL 지원 - -- Url1/Url2: `https://mng.codebridge-x.com/esign/sign/#{토큰}` - -v2 승인 후 코드 변경 필요: -- `EsignApiController::sendAlimtalk()`에서 동적 `$signUrl`을 버튼 URL로 전달 -- 현재 코드의 등록된 URL 그대로 사용 → 동적 URL 사용으로 전환 - -### 3.4 임시 우회: 로그인 페이지 서명 확인 - -v1 템플릿의 버튼 URL이 대시보드(`https://mng.codebridge-x.com`)로 고정되어 있어, -로그인 페이지에 전화번호 기반 서명 확인 기능을 추가하였다. - -``` -알림톡 버튼 클릭 → https://mng.codebridge-x.com → 로그인 페이지 - │ - └─ "전자계약 서명하기" 섹션 - │ - ├─ 전화번호 입력 - ├─ POST /esign/verify-phone - └─ 대기 중인 계약 조회 → /esign/sign/{token} 리다이렉트 -``` - -- 라우트: `POST /esign/verify-phone` -- 컨트롤러: `EsignPublicController::verifyPhone()` -- v2 템플릿 승인 후에도 유지 (비로그인 사용자 대응) - -### 3.5 관련 파일 - -| 파일 | 역할 | -|------|------| -| `app/Http/Controllers/ESign/EsignApiController.php` | `sendAlimtalk()`, `getTemplateData()` | -| `app/Http/Controllers/ESign/EsignPublicController.php` | `verifyPhone()` 전화번호 확인 | -| `app/Services/Barobill/BarobillService.php` | SOAP 클라이언트, `sendATKakaotalkEx()` | -| `resources/views/auth/login.blade.php` | 로그인 페이지 서명 확인 UI | -| `routes/web.php` | `/esign/verify-phone` 라우트 | - ---- - -## 4. 트러블슈팅 (실전 경험) - -> **경고: 아래 내용은 실제 연동 과정에서 발견한 핵심 이슈다. 반드시 숙지할 것.** - -### 4.1 바로빌 API 응답 구조 - -바로빌 SOAP 응답은 `stdClass` 객체로 반환된다. 배열이 아니므로 주의: - -```php -// ❌ 잘못된 접근 -$channels = $result['data']; // 배열이 아님 - -// ✅ 올바른 접근 -$data = $result['data']; // stdClass -$channels = is_array($data->KakaotalkChannel) - ? $data->KakaotalkChannel - : [$data->KakaotalkChannel]; // 1건이면 객체, N건이면 배열 -``` - -### 4.2 SendKey vs ResultCode (2단계 검증 필수) - -> **핵심**: 바로빌이 SendKey를 반환해도 **실제 카카오톡 전달이 실패할 수 있다.** - -``` -[1단계] SendATKakaotalkEx 호출 - → SendKey 반환 (예: BB_6648603713_AT_3044107_260224) - → 이것은 "접수 성공"이지 "전달 성공"이 아님! - -[2단계] 3초 후 GetSendKakaotalk(SendKey) 호출 - → ResultCode = 1: 전달 성공 ✅ - → ResultCode = 4: 템플릿 데이터 일치 오류 ❌ - → ResultCode != 1: 기타 실패 ❌ -``` - -```php -// 반드시 2단계 검증 필요 -if ($result['success'] && is_string($result['data'])) { - $sendKey = $result['data']; - sleep(3); // 카카오톡 전달 대기 - $sendResult = $barobill->getSendKakaotalk($member->biz_no, $sendKey); - $resultCode = $sendResult['data']->ResultCode ?? null; - if ($resultCode != 1) { - // 실패 처리! - } -} -``` - -### 4.3 템플릿 URL 정확 일치 규칙 - -> **핵심**: 버튼 URL은 등록된 템플릿의 URL과 **정확히 일치**해야 한다. 1글자라도 다르면 실패. - -| 등록된 URL | 전송 시 URL | 결과 | -|------------|------------|------| -| `https://mng.codebridge-x.com` | `https://mng.codebridge-x.com` | ResultCode=1 (성공) | -| `https://mng.codebridge-x.com` | `https://mng.codebridge-x.com/esign/sign/xxx` | ResultCode=4 (실패) | -| `https://mng.codebridge-x.com` | `https://mng.codebridge-x.com?sign=xxx` | ResultCode=4 (실패) | -| `https://mng.codebridge-x.com` | `https://mng.codebridge-x.com#sign=xxx` | ResultCode=4 (실패) | - -- 경로 추가: 실패 -- 쿼리 파라미터 추가: 실패 -- URL 프래그먼트(#) 추가: 실패 -- **동적 URL을 사용하려면 템플릿에 `#{변수}` 포함하여 재등록 필요** - -### 4.4 SmsReply 오류 (-31325) - -`SmsReply` 파라미터가 `'S'`(대체발송 사용)인데 `SmsSenderNum`이 비어있으면 `-31325` 오류 발생. - -```php -// ❌ 오류 발생 -'SmsReply' => empty($smsMessage) ? 'N' : 'S', // SmsSenderNum이 비어도 S로 설정 - -// ✅ 수정 -'SmsReply' => (empty($smsMessage) || empty($smsSenderNum)) ? 'N' : 'S', -``` - -### 4.5 SOAP 파라미터 구조 - -바로빌 SOAP API의 파라미터 구조에 주의: - -```php -// 올바른 구조 -$params = [ - 'CorpNum' => $bizNo, // 사업자번호 (하이픈 포함: 123-45-67890) - 'SenderID' => $barobillId, // 바로빌 계정 ID - 'YellowId' => $channelId, // 카카오 채널 ID (@codebridge) - 'TemplateName' => '전자계약_서명요청', - 'SendDT' => '', // 즉시발송: 빈 문자열 - 'SmsReply' => 'N', // SMS 발신번호 없으면 반드시 'N' - 'SmsSenderNum' => '', - 'KakaotalkMessage' => [ - 'ReceiverName' => $name, - 'ReceiverNum' => $phone, // 하이픈 없이: 01012345678 - 'Title' => '', - 'Message' => $message, // 템플릿 변수 치환 완료된 본문 - 'SmsMessage' => '', - 'SmsSubject' => '', - 'Buttons' => ['KakaotalkButton' => $buttons], // 버튼 배열 - ], -]; -``` - -### 4.6 에러 코드 정리 - -| 코드 | 메시지 | 원인 | 해결 | -|------|--------|------|------| -| 1 | 성공 | 정상 전달 | - | -| 4 | 템플릿 데이터 일치 오류 | 본문/버튼 URL이 등록 템플릿과 불일치 | 등록된 템플릿과 동일하게 전송 | -| -31325 | 대체문자 유형 오류 | SmsReply=S인데 SmsSenderNum 비어있음 | SmsReply를 N으로 설정 | -| 음수값 | 바로빌 API 오류 | 파라미터 오류 또는 서비스 미설정 | 바로빌 에러코드 문서 참조 | - ---- - -## 5. 구현 현황 - -### 5.1 완료된 항목 - -| 구분 | 파일 | 설명 | -|------|------|------| -| SOAP 서비스 | `app/Services/Barobill/BarobillService.php` | kakaotalk SOAP 클라이언트 + 15개 API 메서드 | -| 전자계약 알림톡 | `app/Http/Controllers/ESign/EsignApiController.php` | `sendAlimtalk()`, `getTemplateData()` | -| 서명 확인 | `app/Http/Controllers/ESign/EsignPublicController.php` | `verifyPhone()` 전화번호 기반 서명 확인 | -| API 컨트롤러 | `app/Http/Controllers/Api/Admin/Barobill/BarobillKakaotalkController.php` | 15개 API 엔드포인트 | -| 페이지 컨트롤러 | `app/Http/Controllers/Barobill/KakaotalkController.php` | 6개 관리 페이지 | -| 로그인 페이지 | `resources/views/auth/login.blade.php` | 전자계약 서명하기 섹션 | -| 라우트 | `routes/web.php` | `/esign/verify-phone`, `/barobill/kakaotalk/*` | -| 메뉴 등록 | DB (menus 테이블) | 로컬/서버 모두 등록 완료 | - -### 5.2 검증 완료 항목 - -| 항목 | 결과 | 날짜 | -|------|------|------| -| 채널 API 호출 | **성공** | 2026-02-22 | -| 템플릿 조회 | **성공** | 2026-02-22 | -| 알림톡 발송 (본문) | **성공** (ResultCode=1) | 2026-02-24 | -| 알림톡 버튼 URL | **성공** (등록된 URL 사용 시) | 2026-02-24 | -| 전달 결과 확인 (2단계) | **구현 완료** | 2026-02-24 | -| 로그인 페이지 서명 확인 | **성공** | 2026-02-24 | - -### 5.3 완료된 추가 항목 (2026-02-26~27) - -| 항목 | 상태 | 비고 | -|------|------|------| -| 템플릿 v2 승인 | **완료** | 버튼 URL에 `#{토큰}` 변수 포함 3종 승인 | -| `전자계약_완료` 템플릿 | **완료** | 서명 완료 알림 발송 — PDF 다운로드 버튼 | -| 역할 기반 알림 분기 | **완료** | 본사(creator)=이메일, 상대방(counterpart)=알림톡 | -| OTP SMS 발송 | **완료** | 상대방에게 SMS로 인증코드 발송 | -| 환경별 템플릿 분기 | **완료** | `resolveTemplateName()` — `_DEV` 접미사 자동 적용 | -| 서명 PDF 재생성 | **완료** | `downloadDocument()`에서 완료 계약 PDF 자동 재생성 | - -> 상세 가이드: [전자계약 알림톡/SMS 환경별 설정 가이드](./esign-notification-guide.md) - -### 5.4 대기 중인 항목 - -| 항목 | 상태 | 비고 | -|------|------|------| -| DEV 템플릿 검수 | **심사 중** | `admin.codebridge-x.com` 도메인 3종 | -| 친구톡 발송 | **대기** | 채널 친구 추가 후 가능 | -| 대량 발송 | **대기** | 단건 안정화 후 | - ---- - -## 6. v2 템플릿 승인 후 코드 변경 가이드 - -### 6.1 변경 대상 - -`EsignApiController::sendAlimtalk()` (약 1059~1063행) - -### 6.2 현재 코드 (v1) - -```php -// 등록된 버튼 URL을 그대로 사용 (동적 URL 사용 시 템플릿 불일치 오류) -$buttons = ! empty($templateButtons) ? $templateButtons : [ - ['Name' => '계약서 확인하기', 'ButtonType' => 'WL', - 'Url1' => 'https://mng.codebridge-x.com', 'Url2' => 'https://mng.codebridge-x.com'], -]; -``` - -### 6.3 변경 코드 (v2 승인 후) - -```php -// v2 템플릿: 버튼 URL에 동적 서명 URL 사용 -$buttons = [ - ['Name' => '계약서 확인하기', 'ButtonType' => 'WL', - 'Url1' => $signUrl, 'Url2' => $signUrl], -]; -``` - -- `$signUrl`은 1033행에서 이미 생성됨: `config('app.url').'/esign/sign/'.$signer->access_token` -- `getTemplateData()`에서 등록된 버튼 조회는 더 이상 필요 없음 (제거 가능) - ---- - -## 7. API 메서드 목록 - -### 7.1 BarobillService 카카오톡 메서드 - -| 메서드 | SOAP Action | 설명 | -|--------|-------------|------| -| `getKakaotalkChannels` | `GetKakaotalkChannels` | 채널 목록 조회 | -| `getKakaotalkChannelManagementUrl` | `GetKakaotalkChannelManagementURL` | 채널 관리 URL | -| `getKakaotalkTemplates` | `GetKakaotalkTemplates` | 템플릿 목록 조회 | -| `getKakaotalkTemplateManagementUrl` | `GetKakaotalkTemplateManagementURL` | 템플릿 관리 URL | -| `sendATKakaotalk` | `SendATKakaotalk` | 알림톡 단건 발송 | -| `sendATKakaotalkEx` | `SendATKakaotalkEx` | 알림톡 단건 발송 (버튼 포함) | -| `sendATKakaotalks` | `SendATKakaotalks` | 알림톡 대량 발송 | -| `sendFTKakaotalk` | `SendFTKakaotalk` | 친구톡 텍스트 단건 | -| `sendFTKakaotalks` | `SendFTKakaotalks` | 친구톡 텍스트 대량 | -| `sendFIKakaotalk` | `SendFIKakaotalk` | 친구톡 이미지 | -| `sendFWKakaotalk` | `SendFWKakaotalk` | 친구톡 와이드 이미지 | -| `getSendKakaotalk` | `GetSendKakaotalk` | 전송 결과 단건 조회 | -| `getSendKakaotalks` | `GetSendKakaotalks` | 전송 결과 다건 조회 | -| `cancelReservedKakaotalk` | `CancelReservedKakaotalk` | 예약 전송 취소 | - ---- - -## 8. 참고 자료 - -- [바로빌 API 문서](https://dev.barobill.co.kr) -- [카카오비즈니스 채널 관리](https://business.kakao.com) -- [카카오 알림톡 가이드](https://kakaobusiness.gitbook.io) -- 바로빌 템플릿 관리: 로그인 후 `https://www.barobill.co.kr` → 카카오톡 템플릿 관리 - ---- - -## 변경 이력 - -| 날짜 | 버전 | 변경 내용 | -|------|------|----------| -| 2026-02-27 | 1.1 | 역할 기반 알림, OTP SMS, 환경별 템플릿 분기, 완료 알림톡 추가 | -| 2026-02-24 | 1.0 | 전자계약 알림톡 연동 완료, 트러블슈팅 문서화, v2 템플릿 가이드 추가 | -| 2026-02-14 | 0.2 | 전자계약(E-Sign) 알림톡 연동 활용 계획 추가 | -| 2026-02-14 | 0.1 | 초안 작성 - 코드 구현 완료, 실 서비스 연동 대기 | diff --git a/sam/docs/features/documents/README.md b/sam/docs/features/documents/README.md deleted file mode 100644 index ab1f4d5..0000000 --- a/sam/docs/features/documents/README.md +++ /dev/null @@ -1,122 +0,0 @@ -# 문서관리 시스템 (Document Management) - -> **상태**: API 완전 구현 -> **최종 갱신**: 2026-02-27 - ---- - -## 1. 개요 - -EAV(Entity-Attribute-Value) 패턴 기반의 동적 문서 관리 시스템. 문서 서식(Template)을 정의하면 해당 서식에 따라 문서를 생성·결재·관리할 수 있다. 제품 검사(FQC), 공정 검사 등 다양한 문서 유형을 하나의 시스템으로 처리한다. - -**핵심 기능:** -- 문서 서식(Template) 관리: 결재선, 기본필드, 섹션, 컬럼 정의 -- EAV 기반 동적 데이터 저장 (section_id + column_id + row_index + field_key) -- 결재 워크플로우: 작성 → 검토 → 승인 (다단계) -- FQC(제품검사) 일괄 생성 및 진행 현황 -- 첨부파일 관리 (서명, 이미지, 참조 문서) - ---- - -## 2. 모델 - -### 서식 (Template) 계층 - -| 모델 | 설명 | -|------|------| -| `DocumentTemplate` | 서식 마스터 (이름, 카테고리, 회사 정보, 활성 여부) | -| `DocumentTemplateApprovalLine` | 결재선 (이름, 부서, 역할, 순서) | -| `DocumentTemplateBasicField` | 기본 필드 (라벨, 유형, 기본값) | -| `DocumentTemplateSection` | 섹션 (제목, 이미지, 순서) | -| `DocumentTemplateSectionField` | 섹션 필드 (field_key, 유형, 옵션, 필수 여부) | -| `DocumentTemplateColumn` | 컬럼 (라벨, 너비, 유형, 하위 라벨) | -| `DocumentTemplateLink` | 서식 간 연결 | - -### 문서 (Document) 계층 - -| 모델 | 설명 | Traits | -|------|------|--------| -| `Document` | 문서 인스턴스 (서식 기반, 상태, 연결 대상) | BelongsToTenant, Auditable, SoftDeletes | -| `DocumentApproval` | 결재 기록 (단계, 역할, 상태, 코멘트) | BelongsToTenant | -| `DocumentData` | EAV 데이터 (section + column + row + field_key → value) | BelongsToTenant | -| `DocumentAttachment` | 첨부파일 (유형: general, signature, image, reference) | BelongsToTenant | - -**문서 상태 흐름:** -``` -DRAFT → PENDING → APPROVED - → REJECTED → DRAFT (재작성) - → CANCELLED -``` - -**컬럼 유형:** text, check, complex, select, measurement - ---- - -## 3. 서비스 - -| 서비스 | 주요 메서드 | -|--------|-----------| -| `DocumentService` | list, show, create, update, destroy, submit, approve, reject, cancel, bulkCreateFqc, fqcStatus, resolve, upsert, formatTemplateForReact | -| `DocumentTemplateService` | list, show | - ---- - -## 4. API 엔드포인트 - -### 서식 조회 (읽기 전용) - -| HTTP | URI | 설명 | -|------|-----|------| -| GET | `/v1/document-templates` | 서식 목록 | -| GET | `/v1/document-templates/{id}` | 서식 상세 (필드·컬럼·섹션 포함) | - -### 문서 CRUD + 워크플로우 - -| HTTP | URI | 설명 | -|------|-----|------| -| GET | `/v1/documents` | 문서 목록 (필터: status, template_id, 날짜, 검색) | -| POST | `/v1/documents` | 문서 생성 | -| GET | `/v1/documents/{id}` | 문서 상세 | -| PATCH | `/v1/documents/{id}` | 문서 수정 | -| DELETE | `/v1/documents/{id}` | 문서 삭제 | -| POST | `/v1/documents/{id}/submit` | 결재 요청 | -| POST | `/v1/documents/{id}/approve` | 승인 | -| POST | `/v1/documents/{id}/reject` | 반려 | -| POST | `/v1/documents/{id}/cancel` | 취소/회수 | - -### 특수 기능 - -| HTTP | URI | 설명 | -|------|-----|------| -| POST | `/v1/documents/bulk-create-fqc` | FQC 일괄 생성 | -| GET | `/v1/documents/fqc-status` | FQC 진행 현황 | -| GET | `/v1/documents/resolve` | 카테고리+item_id로 문서 조회 | -| POST | `/v1/documents/upsert` | 생성 또는 업데이트 | - ---- - -## 5. FormRequest - -| Request | 주요 검증 | -|---------|----------| -| `StoreRequest` | template_id (필수, exists), title, approvers[], data[] (EAV), attachments[] | -| `UpdateRequest` | title, data[] (EAV), attachments[] | -| `IndexRequest` | status, template_id, search, 날짜 범위, 정렬 | -| `BulkCreateFqcRequest` | order_id, template_id, item_count | -| `ResolveRequest` | category, item_id | -| `ApproveRequest` | comment (선택) | -| `RejectRequest` | comment (필수) | - ---- - -## 관련 문서 - -- [MNG 문서관리 시스템 상세](mng-document-system.md) — MNG 화면 구성, 탭별 기능, 서식 빌더, EAV 저장 패턴 상세 -- [MNG 문서양식관리](mng-document-template.md) — 서식 생성/편집, Legacy/Block Builder, 프리셋, 연결품목 관리 -- [DB 스키마 — 문서/전자서명](../../system/database/documents.md) -- [게시판 시스템](../boards/README.md) — 유사한 EAV 패턴 적용 -- Swagger: `/api-docs` → Documents 섹션 - ---- - -**최종 업데이트**: 2026-03-06 diff --git a/sam/docs/projects/e-sign/esign-storyboard.pptx b/sam/docs/projects/e-sign/esign-storyboard.pptx deleted file mode 100644 index 07c3b5d..0000000 Binary files a/sam/docs/projects/e-sign/esign-storyboard.pptx and /dev/null differ diff --git a/sam/docs/projects/index_projects.md b/sam/docs/projects/index_projects.md deleted file mode 100644 index 7ce5106..0000000 --- a/sam/docs/projects/index_projects.md +++ /dev/null @@ -1,304 +0,0 @@ -# 프로젝트 문서 인덱스 - -> SAM 시스템 개발 프로젝트별 문서 모음 -> **최종 업데이트**: 2026-03-08 - ---- - -## 프로젝트 현황 요약 - -| 프로젝트 | 상태 | 설명 | -|---------|------|------| -| [mes](#mes---meserp-프로젝트) | 🟡 진행중 | 차세대 MES/ERP 기능 개발 | -| [quotation](#quotation---견적-기능) | 🟢 Phase 3 완료 | 5130 견적 → SAM 이관 | -| [api-integration](#api-integration---react--api-연동) | 🟡 진행중 | React ↔ API 연동 | -| [5130-migration](#5130-migration---품목-마이그레이션) | 🟡 Phase 1 진행중 | 5130 품목 데이터 마이그레이션 | -| [legacy-5130](#legacy-5130---레거시-분석) | 📚 참조용 | 5130 레거시 모듈 분석 | -| [mng-mobile-responsive](#mng-mobile-responsive---모바일-반응형) | 🟡 진행중 | mng 모바일 반응형 개선 | -| [auto-login](#auto-login---자동-로그인) | ⚪ 대기 | 자동 로그인 기능 | -| [migration-5130-mng](#migration-5130-mng---5130--mng-마이그레이션) | 🟡 진행중 | 5130 → mng 통합 마이그레이션 | -| [e-sign](#e-sign---전자계약-서명) | 🟢 v1.0 구현 완료 | 전자계약 서명 솔루션 (SAM E-Sign) | -| [org-chart](#org-chart---조직도-관리) | 🟢 v1.0 구현 완료 | 트리형 조직도 관리 (드래그앤드롭, 숨기기) | -| [planning-design](#planning-design---기획디자인-스토리보드-에디터) | 🟢 v1.2 운영 중 | 브라우저 블록 에디터 (Notion/Figma 스타일) | - ---- - -## 프로젝트 상세 - -### mes - MES/ERP 프로젝트 - -**경로**: `docs/projects/mes/` -**상태**: 🟡 Phase 0 (베이스라인 분석) 30% 완료 -**목표**: SAM 시스템의 차세대 MES/ERP 기능 개발 - -**핵심 문서**: -- [README.md](./mes/README.md) - 프로젝트 개요 및 문서 안내 -- [MES_PROGRESS_TRACKER.md](./mes/MES_PROGRESS_TRACKER.md) - 진행 상황 추적 -- [MES_PROJECT_ROADMAP.md](./mes/MES_PROJECT_ROADMAP.md) - 전체 로드맵 - -**분석 결과**: -- `00_baseline/` - Phase 0 분석 결과 - - [PHASE_0_FINAL_REPORT.md](./mes/00_baseline/PHASE_0_FINAL_REPORT.md) - - [BACKEND_DEVELOPMENT_ROADMAP_V2.md](./mes/00_baseline/BACKEND_DEVELOPMENT_ROADMAP_V2.md) - - `docs_breakdown/` - 문서 분석 (7개) - -**v2 분석**: -- `v2-analysis/` - MES v2 화면 분석 - - `quote-analysis/` - 견적 분석 - - `order-analysis/` - 주문 분석 - - `production-analysis/` - 생산 분석 - - `customer-analysis/` - 거래처 분석 - - `site-analysis/` - 현장 분석 - - `price-analysis/` - 단가 분석 - - `master-data-analysis/` - 기준정보 분석 - - `production-userflow/` - 생산 유저플로우 - ---- - -### quotation - 견적 기능 - -**경로**: `docs/projects/quotation/` -**상태**: 🟢 Phase 3 완료 (2025-12-19) -**목표**: 5130 레거시 견적 기능을 SAM 시스템으로 이관 - -**핵심 문서**: -- [MASTER_PLAN.md](./quotation/MASTER_PLAN.md) - 마스터 플랜 -- [PROGRESS.md](./quotation/PROGRESS.md) - 진행 현황 - -**Phase 문서**: -| Phase | 상태 | 경로 | -|-------|------|------| -| 1. 5130 분석 | ✅ 완료 | `phase-1-5130-analysis/` | -| 2. mng 분석 | ✅ 완료 | `phase-2-mng-analysis/` | -| 3. 구현 | ✅ 완료 | `phase-3-implementation/` | -| 4. API 개발 | ⚪ 대기 | `phase-4-api/` | - -**참조 자료**: -- `screenshots/` - MES 프로토타입 화면 캡쳐 (7개) - ---- - -### api-integration - React ↔ API 연동 - -**경로**: `docs/projects/api-integration/` -**상태**: 🟡 Phase 4 진행중 -**목표**: React(dev.sam.kr)와 API(api.sam.kr) 완벽 연동 - -**핵심 문서**: -- [MASTER_PLAN.md](./api-integration/MASTER_PLAN.md) - 마스터 플랜 -- [PROGRESS.md](./api-integration/PROGRESS.md) - 진행 현황 -- [WORKFLOW.md](./api-integration/WORKFLOW.md) - 작업 프로세스 - -**Phase 문서**: -| Phase | 상태 | 경로 | -|-------|------|------| -| 1. 테이블 통합 | 🟢 완료(스킵) | `phase-1-table-migration/` | -| 2. 메뉴 추출 | 🟡 진행중 | `phase-2-menu-extraction/` | -| 3. API 매핑 | 🟡 진행중 | `phase-3-api-mapping/` | -| 4. 연동+검증 | 🟡 진행중 | `phase-4-integration/` | - -**TC 파일**: `phase-4-integration/tc/` - 기능별 테스트 케이스 JSON (17개) - ---- - -### 5130-migration - 품목 마이그레이션 - -**경로**: `docs/projects/5130-migration/` -**상태**: 🟡 Phase 1 진행중 -**목표**: 5130 품목(부품, 자재, BOM) 데이터를 SAM DB로 이전 - -**핵심 문서**: -- [MASTER_PLAN.md](./5130-migration/MASTER_PLAN.md) - 마스터 플랜 -- [PROGRESS.md](./5130-migration/PROGRESS.md) - 진행 현황 - -**Phase 문서**: -| Phase | 상태 | 경로 | -|-------|------|------| -| 1. 소스 분석 | 🟡 진행중 | `phase-1-source-analysis/` | -| 2. 타겟 분석 | ⚪ 대기 | `phase-2-target-analysis/` | -| 3. 매핑 설계 | ⚪ 대기 | `phase-3-mapping/` | - ---- - -### legacy-5130 - 레거시 분석 - -**경로**: `docs/projects/legacy-5130/` -**상태**: 📚 참조용 문서 -**용도**: 5130 레거시 시스템 모듈별 분석 - -**모듈별 분석 문서**: -| 문서 | 내용 | -|------|------| -| [00_OVERVIEW.md](./legacy-5130/00_OVERVIEW.md) | 시스템 개요 | -| [01_MATERIAL.md](./legacy-5130/01_MATERIAL.md) | 자재 관리 | -| [02_PRODUCT.md](./legacy-5130/02_PRODUCT.md) | 제품 관리 | -| [03_ESTIMATE.md](./legacy-5130/03_ESTIMATE.md) | 견적 관리 | -| [04_PRODUCTION.md](./legacy-5130/04_PRODUCTION.md) | 생산 관리 | -| [05_SHIPPING.md](./legacy-5130/05_SHIPPING.md) | 출하 관리 | -| [06_QUALITY.md](./legacy-5130/06_QUALITY.md) | 품질 관리 | -| [07_ACCOUNTING.md](./legacy-5130/07_ACCOUNTING.md) | 회계 관리 | -| [08_SAM_COMPARISON.md](./legacy-5130/08_SAM_COMPARISON.md) | SAM 비교 분석 | -| [draw-module.md](./legacy-5130/draw-module.md) | 도면 모듈 | - ---- - -### mng-mobile-responsive - 모바일 반응형 - -**경로**: `docs/projects/mng-mobile-responsive/` -**상태**: 🟡 진행중 -**목표**: mng 관리자 패널 모바일 반응형 개선 - -**문서**: -- [01-analysis.md](./mng-mobile-responsive/01-analysis.md) - 분석 -- [02-implementation-plan.md](./mng-mobile-responsive/02-implementation-plan.md) - 구현 계획 -- [06-excluded-menus.md](./mng-mobile-responsive/06-excluded-menus.md) - 제외 메뉴 -- [PROGRESS.md](./mng-mobile-responsive/PROGRESS.md) - 진행 현황 - ---- - -### auto-login - 자동 로그인 - -**경로**: `docs/projects/auto-login/` -**상태**: ⚪ 대기 -**목표**: 자동 로그인 기능 구현 - -**문서**: -- [PROGRESS.md](./auto-login/PROGRESS.md) - 진행 현황 - ---- - -### migration-5130-mng - 5130 → mng 마이그레이션 - -**경로**: `docs/projects/migration-5130-mng/` -**상태**: 🟡 진행중 -**목표**: 5130 기능을 mng로 통합 마이그레이션 - -**문서**: -- [MIGRATION_TRACKER.md](./migration-5130-mng/MIGRATION_TRACKER.md) - 마이그레이션 추적 - ---- - -### e-sign - 전자계약 서명 - -**경로**: `docs/projects/e-sign/` -**상태**: 🟢 v1.0 구현 완료 (2026-02-12) -**목표**: 모두싸인과 유사한 간편 전자계약 서명 솔루션 자체 구축 - -**핵심 문서**: -- [technical-design.md](./e-sign/technical-design.md) - 기술 설계 문서 -- [implementation-guide.md](./e-sign/implementation-guide.md) - 구현 가이드 - -**구현 범위**: -| 영역 | 수량 | -|------|------| -| DB 마이그레이션 | 4개 (esign_contracts, esign_signers, esign_sign_fields, esign_audit_logs) | -| API 모델 | 4개 | -| API 서비스 | 4개 | -| API 컨트롤러 | 2개 (16 엔드포인트) | -| MNG 컨트롤러 | 2개 (8 화면) | -| MNG 뷰 | 8개 (React 하이브리드) | - -**기술 스택**: Laravel 11 + React 18 + SignaturePad + PDF.js - -**참고 자료**: -- `esign-storyboard.pptx` - 화면 스토리보드 -- `storyboard-config.json` - 스토리보드 설정 - ---- - -### org-chart - 조직도 관리 - -**경로**: `docs/projects/org-chart/` -**상태**: 🟢 v1.0 구현 완료 (2026-03-06) -**목표**: 테넌트별 조직 구조를 시각적으로 관리하는 트리형 조직도 - -**핵심 문서**: -- [README.md](./org-chart/README.md) - 기술 문서 (아키텍처, API, DB, 프론트엔드 상세) - -**구현 범위**: - -| 영역 | 수량 | -|------|------| -| MNG 컨트롤러 메서드 | 7개 (RdController) | -| API 엔드포인트 | 6개 (조회, 배치, 해제, 순서변경, 숨기기) | -| DB 마이그레이션 | 1개 (departments.options JSON 추가) | -| 뷰 | 1개 (Alpine.js + SortableJS) | - -**기술 스택**: Alpine.js + SortableJS + 수동 DOM 렌더링 (x-for 미사용) - ---- - -### planning-design - 기획디자인 스토리보드 에디터 - -**경로**: `docs/projects/planning-design/` -**상태**: 🟢 v1.2 운영 중 (고도화 진행중) -**목표**: 브라우저 내 Notion/Figma 스타일 블록 에디터로 ERP 화면 기획서 작성 - -**핵심 문서**: -- [README.md](./planning-design/README.md) - 프로젝트 개요 및 구현 이력 - -**구현 범위**: - -| 영역 | 수량 | -|------|------| -| 블록 유형 | 15종 (텍스트/레이아웃/UI모형/미디어/체크리스트) | -| 편집 기능 | 올가미 선택, Undo/Redo, 복사/잘라내기, 서식 | -| 서식 시스템 | 글자색/배경색/크기/굵기/기울임/정렬/z-index | -| 작업 영역 | 사이드바/Description 접기/펼치기, 캔버스 폭 자동 확장 | -| 출력 | HTML 내보내기 + A4 인쇄 (좌표 기반 WYSIWYG) | - -**기술 스택**: Alpine.js + localStorage (서버 API 없음) - -**기술 스펙**: [features/rd/planning-design.md](../features/rd/planning-design.md) - ---- - -## 디렉토리 구조 - -``` -docs/projects/ -├── INDEX.md # 이 파일 -├── mes/ # MES/ERP 프로젝트 (핵심) -│ ├── 00_baseline/ # Phase 0 분석 -│ ├── v1-analysis/ # v1 분석 -│ ├── v2-analysis/ # v2 화면 분석 -│ └── phases/ # Phase별 진행 -├── quotation/ # 견적 기능 -│ ├── phase-1-5130-analysis/ -│ ├── phase-2-mng-analysis/ -│ ├── phase-3-implementation/ -│ └── screenshots/ -├── api-integration/ # React ↔ API 연동 -│ ├── phase-1-table-migration/ -│ ├── phase-2-menu-extraction/ -│ ├── phase-3-api-mapping/ -│ └── phase-4-integration/tc/ -├── 5130-migration/ # 품목 마이그레이션 -│ ├── phase-1-source-analysis/ -│ ├── phase-2-target-analysis/ -│ └── phase-3-mapping/ -├── legacy-5130/ # 레거시 분석 (참조용) -├── mng-mobile-responsive/ # 모바일 반응형 -├── auto-login/ # 자동 로그인 -├── migration-5130-mng/ # 5130→mng 마이그레이션 -├── e-sign/ # 전자계약 서명 (SAM E-Sign) -├── org-chart/ # 조직도 관리 -└── planning-design/ # 기획디자인 스토리보드 에디터 -``` - ---- - -## 관련 문서 - -- [docs/INDEX.md](../INDEX.md) - 전체 문서 인덱스 -- [docs/plans/index_plans.md](../plans/index_plans.md) - 기획 문서 인덱스 -- [docs/guides/PROJECT_DEVELOPMENT_POLICY.md](../guides/PROJECT_DEVELOPMENT_POLICY.md) - 공통 개발 정책 -- [CURRENT_WORKS.md](../../CURRENT_WORKS.md) - 현재 작업 - ---- - -**범례**: -- 🟢 완료 -- 🟡 진행중 -- ⚪ 대기 -- 📚 참조용 \ No newline at end of file diff --git a/sam/docs/rules/billing-policy.md b/sam/docs/rules/billing-policy.md deleted file mode 100644 index 56a5966..0000000 --- a/sam/docs/rules/billing-policy.md +++ /dev/null @@ -1,189 +0,0 @@ -# SAM 과금정책 — 내부용 (CONFIDENTIAL) - -> **작성일**: 2026-02-21 -> **상태**: 설계 확정 -> **열람 범위**: 내부 개발팀/관리층 전용 — 대외 공유 금지 - ---- - -## 문서 분리 안내 - -과금정책이 대상 독자별 3개 문서로 분리되었다. - -| 문서 | 대상 | 내용 | -|------|------|------| -| [고객 요금 안내](customer-pricing.md) | 고객 | 서비스 가격표, 과금 기준 | -| [영업파트너 수당 체계](partner-commission.md) | 영업파트너 | 수당률, 정산 프로세스 | -| **본 문서** (billing-policy.md) | 내부 개발팀/관리층 | 본사 원가, 마진율, 코드 참조 | - ---- - -## 1. 개요 - -### 1.1 목적 - -SAM 프로젝트의 과금정책 중 **본사 지출 원가**, **마진 구조**, **코드 참조**를 정리한다. -고객 공개용 요금과 영업파트너 수당 체계는 별도 문서로 분리되었다. - -### 1.2 적용범위 - -- 외부 서비스 이용 비용 (바로빌, Google, Anthropic) -- 마진 구조 및 가격 책정 배경 -- 코드 참조 (모델, 서비스, DB 테이블) - -### 1.3 용어 정의 - -| 용어 | 설명 | -|------|------| -| **개발비** | 서비스 도입 시 1회 납부하는 초기 비용 | -| **구독료** | 서비스 유지를 위한 월 정기 비용 | -| **토큰** | AI가 언어를 처리하는 최소 단위 (한글 ~1.5자 = 1토큰) | -| **MRR** | Monthly Recurring Revenue (월간 반복 매출) | - ---- - -## 2. 본사 지출 과금정책 (Company → Service Provider) - -본사가 외부 서비스 제공자에게 납입하는 비용이다. - -### 2.1 바로빌 API 비용 - -#### 월정액 서비스 - -| 서비스 | 월정액 | 비고 | -|--------|--------|------| -| 계좌조회 | 10,000원 | 고객 부담 | -| 카드내역 | 10,000원 | 고객 부담 | -| 홈택스 매입/매출 | 33,000원 (VAT 포함) | 코드브릿지엑스 지원 → 본사 부담 (무료) | - -> **참고**: 계좌조회/카드내역 월정액은 고객에게 전가한다. 홈택스는 본사가 흡수한다. - -#### 건별 과금 - -| 서비스 | 단가 | 비고 | -|--------|------|------| -| 전자세금계산서 발행 | 100원/건 | 원가 기준 | - -### 2.2 AI/클라우드 서비스 비용 - -#### 토큰 기반 과금 (LLM) - -| 제공자 | 모델 | 입력 단가 (USD/1M) | 출력 단가 (USD/1M) | 입력 (KRW/1M) | 출력 (KRW/1M) | -|--------|------|------------------:|------------------:|--------------:|--------------:| -| Google | Gemini 2.0 Flash | $0.10 | $0.40 | 140원 | 560원 | -| Anthropic | Claude 3 Haiku | $0.25 | $1.25 | 350원 | 1,750원 | - -#### 시간/작업 기반 과금 - -| 서비스 | 단가 (USD) | 단위 | KRW 환산 | -|--------|----------:|------|----------:| -| Google STT | $0.009 | 15초당 | 12.6원/15초 (약 50원/분) | -| Google GCS | $0.005 | 1,000건당 | 7원/1,000건 | -| Google FCM | 무료 | - | - | - -#### 환율 기준 - -- 기본 환율: **1,400원/USD** -- `ai_pricing_configs` 테이블에서 동적 관리 -- 캐시 TTL: 1시간 - -### 2.3 본사 지출 월간 예상 (테넌트 1개 기준) - -| 항목 | 최소 | 최대 | 비고 | -|------|-----:|-----:|------| -| 바로빌 홈택스 | 0원 | 0원 | 코드브릿지엑스 지원 | -| 세금계산서 원가 | ~5,000원 | ~10,000원 | 50~100건 기준 | -| AI 토큰 (Gemini) | ~100원 | ~5,000원 | 사용량 비례 | -| GCS/STT | ~50원 | ~2,000원 | 사용량 비례 | - ---- - -## 3. 고객 안내용 과금정책 - -> 고객 요금 상세는 **[고객 요금 안내](customer-pricing.md)** 문서를 참조한다. - ---- - -## 4. 내부 정산 정책 - -### 4.1 영업 수당 체계 - -> 수당률 및 정산 프로세스는 **[영업파트너 수당 체계](partner-commission.md)** 문서를 참조한다. - -### 4.2 마진 구조 - -- **회사 마진**: 개발비의 약 **67~75%** (가입 유형에 따라 변동) - - 개인 가입: 개발비의 약 72~75% (수당 25~28% 차감 후) - - 단체 가입: 개발비의 약 67% (수당 33% 차감 후) - -### 4.3 가격 책정 배경 - -- 내부 총 개발비 산정: **약 7,600만원** -- 구성: 개발비 2,000만원 + 장기 구독(약 7년) 및 금융 비용(4.6%) -- 중소기업 초기 투자 부담 분산 구조 - ---- - -## 5. 코드 참조 (개발자용) - -### 5.1 바로빌 과금 - -| 파일 | 내용 | -|------|------| -| `mng/app/Models/Barobill/BarobillSubscription.php` | `DEFAULT_MONTHLY_FEES` 월정액 상수 | -| `mng/app/Models/Barobill/BarobillBillingRecord.php` | `USAGE_UNIT_PRICES` 건별 단가 상수 | -| `mng/app/Models/Barobill/BarobillPricingPolicy.php` | 과금 정책 모델 + `calculateBilling()` | -| `mng/app/Services/Barobill/BarobillBillingService.php` | 월정액/건별 과금 처리 서비스 | -| `mng/app/Services/Barobill/BarobillUsageService.php` | 사용량 집계 및 과금 계산 | -| `mng/database/seeders/BarobillPricingPolicySeeder.php` | 과금 정책 시더 (5개 정책) | - -### 5.2 AI 가격/토큰 - -| 파일 | 내용 | -|------|------| -| `api/app/Models/Tenants/AiPricingConfig.php` | AI 단가 모델 + `getActivePricing()` | -| `api/app/Models/Tenants/AiTokenUsage.php` | 토큰 사용량 기록 모델 | -| `api/app/Services/AiReportService.php` | 토큰 비용 계산 로직 (`saveTokenUsage()`) | -| `api/database/migrations/2026_02_09_*_ai_pricing_configs.php` | AI 단가 테이블 + 시드 데이터 | -| `api/database/migrations/2026_02_07_*_ai_token_usages.php` | 토큰 사용량 테이블 | - -### 5.3 정산 관련 - -| 파일 | 내용 | -|------|------| -| `mng` 또는 `api` 정산 컨트롤러/서비스 | 영업수수료, 구독료, 컨설팅비 정산 | - -### 5.4 DB 테이블 참조 - -| 테이블 | 설명 | -|--------|------| -| `barobill_subscriptions` | 바로빌 월정액 구독 현황 | -| `barobill_billing_records` | 바로빌 월별 과금 내역 | -| `barobill_pricing_policies` | 바로빌 과금 정책 (무료 제공량, 추가 단가) | -| `ai_pricing_configs` | AI 제공자별 단가 설정 | -| `ai_token_usages` | AI 토큰 사용량 기록 | -| `ai_voice_recordings` | AI 음성 녹음 (STT 비용 발생) | -| `sales_commissions` | 영업수수료 정산 | - ---- - -## 6. 관련 문서 - -- [고객 요금 안내](customer-pricing.md) - 서비스 가격표 (고객 공개용) -- [영업파트너 수당 체계](partner-commission.md) - 수당률 및 정산 (파트너용) -- [단가 정책 (품목)](pricing-policy.md) - 품목 단가/원가 계산 -- [영업수수료정산](../features/settlement/sales-commissions.md) -- [구독료정산](../features/settlement/subscriptions.md) -- [컨설팅비용정산](../features/settlement/consulting-fees.md) -- [고객사정산](../features/settlement/customer-settlements.md) -- `sales/price/수당지급체계.md` - 수당 지급 체계 상세 -- `sales/policy/SAM_영업정책문서.md` - 영업 정책 상세 -- `sales/price/ref/토큰정책.md` - AI 토큰 고객 안내 가이드 - ---- - -> **공통 안내**: 본 문서에 명시된 모든 개발비 및 구독료는 **부가가치세(VAT) 별도** 금액이다. - ---- - -**최종 업데이트**: 2026-02-21 diff --git a/sam/docs/rules/billing-policy.pptx b/sam/docs/rules/billing-policy.pptx deleted file mode 100644 index 6a3f307..0000000 Binary files a/sam/docs/rules/billing-policy.pptx and /dev/null differ diff --git a/sam/docs/rules/customer-pricing.md b/sam/docs/rules/customer-pricing.md deleted file mode 100644 index 60a9ff2..0000000 --- a/sam/docs/rules/customer-pricing.md +++ /dev/null @@ -1,116 +0,0 @@ -# SAM 서비스 요금 안내 - -> **작성일**: 2026-02-21 -> **상태**: 설계 확정 - ---- - -## 1. 개요 - -### 1.1 목적 - -SAM 서비스 도입 시 고객에게 안내하는 요금 체계를 정리한다. - -### 1.2 용어 정의 - -| 용어 | 설명 | -|------|------| -| **개발비** | 서비스 도입 시 1회 납부하는 초기 비용 | -| **구독료** | 서비스 유지를 위한 월 정기 비용 | -| **토큰** | AI가 언어를 처리하는 최소 단위 (한글 ~1.5자 = 1토큰) | - ---- - -## 2. 기본 서비스 요금 - -### 2.1 제조업 기본 패키지 - -- **포함**: 품목관리 → 견적 → 수주 → 생산 → 출하 (ERP 인사/회계 무료 포함) -- **개발비**: 2,000만원 (VAT 별도) -- **구독료**: 50만원/월 (VAT 별도) - -### 2.2 개별 모듈 - -| 모듈명 | 개발비 (VAT 별도) | 구독료/월 (VAT 별도) | -|--------|------------------:|-----------------:| -| QR코드 관리 | 1,020만원 | 5만원 | -| 사진/출하 관리 | 1,920만원 | 10만원 | -| 검사/토큰 적용 | 1,020만원 | 5만원 | -| 이카운트 연동 | 1,920만원 | 10만원 | - -### 2.3 통합 패키지 - -| 패키지명 | 개발비 (VAT 별도) | 구독료/월 (VAT 별도) | -|---------|------------------:|-----------------:| -| 공사관리 패키지 | 4,000만원 | 20만원 | -| 공정/정부지원사업 | 8,000만원 | 40만원 | - ---- - -## 3. 추가 옵션 요금 - -| 옵션명 | 개발비 추가 (VAT 별도) | 구독료 추가/월 (VAT 별도) | -|--------|---------------------:|----------------------:| -| 생산공정 1개 추가 | 500만원 | 10만원 | -| 품질관리 (인정검사) | 2,000만원 | 50만원 | -| 사진 등록 | - | 10만원 | -| 챗봇/녹음/업무일지 | - | 각 20만원 | -| 연구소 연구노트 | - | 5만원 | -| 장비점검, 사무소 정비 | - | 5만원 | - -> **참고**: 품질관리(인정검사)에는 '장비점검, 사무소 정비' 기능이 기본 포함된다. - ---- - -## 4. 사용량 기반 추가 과금 - -기본 제공 한도 초과 시 실비 과금한다. - -| 항목 | 기본 제공 | 추가 과금 기준 | -|------|----------|--------------| -| 파일 저장 공간 | 100GB | 100GB당 **10만원/월** | -| AI 토큰 | 월 100만 토큰 | 1,000토큰 단위 실비 과금 | - -### AI 토큰 사용량 체감 (100만 토큰 기준) - -| 활용 시나리오 | 예상 처리량 | -|-------------|-----------| -| 음성 회의 요약 | 약 520분 (8.6시간) | -| 문서 자료 정리 (A4) | 약 300~400매 | -| 이메일/노트 분류 | 약 1,500~2,000건 | - -- 미사용 잔여 토큰은 이월되지 않는다 (매월 1일 갱신) -- 기본 제공량 80%, 100% 소진 시 자동 알림 발송 - ---- - -## 5. 바로빌 부가 서비스 요금 - -고객이 선택적으로 이용하는 바로빌 연동 서비스이다. - -| 서비스 | 과금 방식 | 기본 제공 | 추가 과금 | 부담 주체 | -|--------|---------|---------|---------|----------| -| 계좌조회 | 월정액 10,000원 | 1계좌 | 추가 1계좌당 10,000원 | 고객 | -| 카드내역 | 월정액 10,000원 | 5장 | 추가 1장당 5,000원 | 고객 | -| 홈택스 매입/매출 | 월 33,000원 | - | - | 본사 (무료) | -| 세금계산서 발행 | 건별 | 100건 | 추가 50건당 5,000원 | 고객 | - -> **과금 계산 예시**: -> - 법인카드 8장 등록 → (8-5) × 5,000 = 15,000원 추가 -> - 세금계산서 151건 → ceil((151-100)/50) × 5,000 = 10,000원 추가 - ---- - -## 6. 관련 문서 - -- [내부 과금정책](billing-policy.md) - 본사 지출 원가 및 코드 참조 (내부용) -- [영업파트너 수당 체계](partner-commission.md) - 수당률 및 정산 프로세스 (파트너용) -- [단가 정책 (품목)](pricing-policy.md) - 품목 단가/원가 계산 - ---- - -> **공통 안내**: 본 문서에 명시된 모든 개발비 및 구독료는 **부가가치세(VAT) 별도** 금액이다. - ---- - -**최종 업데이트**: 2026-02-21 diff --git a/sam/docs/rules/customer-pricing.pptx b/sam/docs/rules/customer-pricing.pptx deleted file mode 100644 index f96693b..0000000 Binary files a/sam/docs/rules/customer-pricing.pptx and /dev/null differ diff --git a/sam/docs/rules/partner-commission.md b/sam/docs/rules/partner-commission.md deleted file mode 100644 index 7917fe1..0000000 --- a/sam/docs/rules/partner-commission.md +++ /dev/null @@ -1,114 +0,0 @@ -# SAM 영업파트너 수당 체계 - -> **작성일**: 2026-02-21 -> **상태**: 설계 확정 - ---- - -## 1. 개요 - -### 1.1 목적 - -영업파트너가 알아야 할 수당 체계 및 정산 프로세스를 안내한다. - -### 1.2 기본 원칙 - -- 수당은 **개발비에 대해서만** 지급한다 -- 구독료는 플랫폼 유지보수 및 클라우드 인프라 비용으로 활용되며 수당 대상이 아니다 -- 기준금액은 개발비의 50%이며, 2단계(1차/2차) 분할 지급한다 - ---- - -## 2. 가입 유형별 수당률 - -| 가입 유형 | 파트너/단체 수당 | 매니저 수당 | 협업지원금(유치자) | **본사 총 지급률** | -|-----------|:--------------:|:----------:|:-----------------:|:----------------:| -| **개인 가입** | 20% | 5% | 3% | **28%** | -| **단체 가입** | 30% | 0% | 3% | **33%** | - -- **협업지원금**: 유치자(parent)가 자신이 유치한 하위 파트너의 매출에서 3%를 수령하는 내부 제도 -- 1단계까지만 적용 (유치자 → 하위 파트너, 그 이상 상위로는 올라가지 않음) -- 단체 가입 시 매니저 수당은 0% -- 단체 가입 시 수수료율은 개발비의 33% (VAT 별도) - ---- - -## 3. 수당 산정 예시 - -### 3.1 기준금액 100만원 기준 - -| 가입 유형 | 파트너/단체 | 매니저 | 협업지원금 | 본사 총 지급 | -|-----------|----------:|-------:|----------:|------------:| -| 개인 (유치자 있음) | 200,000원 | 50,000원 | 30,000원 | **280,000원 (28%)** | -| 개인 (유치자 없음) | 200,000원 | 50,000원 | 0원 | 250,000원 (25%) | -| 단체 | 300,000원 | 0원 | 30,000원 | **330,000원 (33%)** | - -### 3.2 제조업 기본 패키지 (개발비 2,000만원) 기준 - -**개인 가입:** - -| 항목 | 금액 | -|------|-----:| -| 판매자(파트너) 수당 | 400만원 (20%) | -| 매니저 수당 | 100만원 (5%) | -| 협업지원금(유치자) | 60만원 (3%) | - -**단체 가입:** - -| 항목 | 금액 | -|------|-----:| -| 단체 수당 | 600만원 (30%) | -| 매니저 수당 | 0원 (0%) | -| 협업지원금(유치자) | 60만원 (3%) | - ---- - -## 4. 수당 지급 프로세스 - -``` -┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ -│ 1. 계약 및 입금 │ → │ 2. 수당 확정 │ → │ 3. 지급 │ -│ │ │ │ │ │ -│ 개발비 전액 │ │ 매월 정산일에 │ │ 파트너 등록 │ -│ 완납 확인 │ │ 건별 수당 확정 │ │ 계좌로 지급 │ -└─────────────────┘ └─────────────────┘ └─────────────────┘ -``` - -- **지급 시점**: 개발비 입금 완료 확인 후 익월 정산일 - ---- - -## 5. 추가 옵션별 수당 참고 - -개발비가 있는 옵션만 수당 대상이다. 서비스 요금 상세는 [고객 요금 안내](customer-pricing.md)를 참조한다. - -| 옵션명 | 개발비 (VAT 별도) | 판매자 수당 (20%) | -|--------|------------------:|------------------:| -| 생산공정 1개 추가 | 500만원 | 100만원 | -| 품질관리 (인정검사) | 2,000만원 | 400만원 | - -> **참고**: 구독료만 있는 옵션(사진 등록, 챗봇/녹음/업무일지 등)은 수당 대상이 아니다. - ---- - -## 6. 기타 정책 - -- **계약 유지**: 장기 구독을 전제로 한 초기 비용 할인 정책이므로, 중도 해지 시 별도의 위약금 규정이 적용될 수 있다 -- **구독료**: 플랫폼 유지보수 및 클라우드 인프라 비용으로 활용되며, 수당 지급 대상이 아니다 - ---- - -## 7. 관련 문서 - -- [고객 요금 안내](customer-pricing.md) - 서비스 요금 상세 (고객용) -- [내부 과금정책](billing-policy.md) - 본사 지출 원가 및 코드 참조 (내부용) -- `sales/price/수당지급체계.md` - 수당 지급 체계 원본 -- `sales/policy/SAM_영업정책문서.md` - 영업 정책 상세 - ---- - -> **공통 안내**: 본 문서에 명시된 모든 개발비 및 구독료는 **부가가치세(VAT) 별도** 금액이다. - ---- - -**최종 업데이트**: 2026-02-21 diff --git a/sam/docs/rules/partner-commission.pptx b/sam/docs/rules/partner-commission.pptx deleted file mode 100644 index ff69d4a..0000000 Binary files a/sam/docs/rules/partner-commission.pptx and /dev/null differ diff --git a/sam/docs/rules/slides/billing-policy/slide-01.html b/sam/docs/rules/slides/billing-policy/slide-01.html deleted file mode 100644 index 8d81f7c..0000000 --- a/sam/docs/rules/slides/billing-policy/slide-01.html +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - - - -
                      -
                      - -

                      SAM

                      -
                      -
                      -

                      CONFIDENTIAL

                      -
                      -
                      - - -
                      -

                      INTERNAL USE ONLY

                      -

                      SAM 과금정책

                      -

                      본사 지출 원가 · 마진 구조 · 코드 참조

                      -
                      - - -
                      -
                      -

                      COST PER TENANT

                      -

                      ~1만원/월

                      -

                      테넌트당 월 원가

                      -
                      -
                      -

                      OPERATING MARGIN

                      -

                      67~75%

                      -

                      영업이익률

                      -
                      -
                      -

                      SCALE ADVANTAGE

                      -

                      MC → 0

                      -

                      스케일 시 한계비용

                      -
                      -
                      - - -
                      -
                      -

                      SCOPE

                      -

                      내부 개발팀 / 관리층

                      -
                      -
                      -

                      DATE

                      -

                      2026. 02

                      -
                      -
                      -

                      WARNING

                      -

                      대외 공유 금지

                      -
                      -
                      - - diff --git a/sam/docs/rules/slides/billing-policy/slide-02.html b/sam/docs/rules/slides/billing-policy/slide-02.html deleted file mode 100644 index 863fc46..0000000 --- a/sam/docs/rules/slides/billing-policy/slide-02.html +++ /dev/null @@ -1,91 +0,0 @@ - - - - - - - - -
                      -
                      -
                      -

                      OVERVIEW

                      -
                      -

                      과금정책 3분할 체계

                      -
                      -
                      -

                      CONFIDENTIAL

                      -
                      -
                      - - -
                      - -
                      -
                      -

                      FOR CUSTOMER

                      -
                      -

                      고객용

                      -
                      -

                      customer-pricing.md

                      -
                      -

                      서비스 가격표, 과금 기준

                      -
                      -

                      고객에게 직접 전달 가능

                      -
                      -
                      - - -
                      -
                      -

                      FOR PARTNER

                      -
                      -

                      파트너용

                      -
                      -

                      partner-commission.md

                      -
                      -

                      수당률, 정산 프로세스

                      -
                      -

                      영업파트너 제공용

                      -
                      -
                      - - -
                      -
                      -

                      INTERNAL ONLY

                      -
                      -

                      내부용 (본 문서)

                      -
                      -

                      billing-policy.md

                      -
                      -

                      원가, 마진, 코드참조

                      -
                      -

                      대외 공유 금지

                      -
                      -
                      -
                      - - -
                      -

                      본 문서는 내부 개발팀/관리층 전용입니다. 고객 및 파트너에게 공유하지 마세요.

                      -
                      - - -
                      -

                      SAM 내부 과금정책 | CONFIDENTIAL

                      -

                      02

                      -
                      - - diff --git a/sam/docs/rules/slides/billing-policy/slide-03.html b/sam/docs/rules/slides/billing-policy/slide-03.html deleted file mode 100644 index b1e7126..0000000 --- a/sam/docs/rules/slides/billing-policy/slide-03.html +++ /dev/null @@ -1,103 +0,0 @@ - - - - - - - - -
                      -
                      -
                      -

                      COST

                      -
                      -

                      바로빌 API 원가 분석

                      -
                      -

                      03

                      -
                      - - -
                      - -
                      -

                      월정액 서비스

                      - -
                      -

                      서비스

                      -

                      월정액

                      -

                      처리 방식

                      -
                      - -
                      -

                      계좌조회

                      -

                      10,000원

                      -

                      고객 부담 (전가)

                      -
                      - -
                      -

                      카드내역

                      -

                      10,000원

                      -

                      고객 부담 (전가)

                      -
                      - -
                      -

                      홈택스 매입/매출

                      -

                      33,000원

                      -

                      본사 흡수 (코드브릿지엑스 → 무료)

                      -
                      -
                      - - -
                      - -
                      -

                      건별 과금

                      - -
                      -

                      서비스

                      -

                      원가

                      -
                      -
                      -

                      세금계산서 발행

                      -

                      100원/건

                      -
                      - -
                      -

                      고객 과금: 5,000원/50건

                      -

                      마진: 4,900원 (98%)

                      -
                      -
                      - - -
                      -

                      핵심 요약

                      -
                      -
                      -

                      계좌/카드 → 고객 전가, 홈택스 → 코드브릿지엑스 지원으로 본사 원가 0원

                      -
                      -
                      -
                      -

                      본사 실질 부담: 세금계산서 원가만 (5,000~10,000원/월)

                      -
                      -
                      -
                      -
                      - - -
                      -

                      SAM 내부 과금정책 | CONFIDENTIAL

                      -

                      03

                      -
                      - - diff --git a/sam/docs/rules/slides/billing-policy/slide-04.html b/sam/docs/rules/slides/billing-policy/slide-04.html deleted file mode 100644 index 89ea645..0000000 --- a/sam/docs/rules/slides/billing-policy/slide-04.html +++ /dev/null @@ -1,142 +0,0 @@ - - - - - - - - -
                      -
                      -
                      -

                      COST

                      -
                      -

                      AI / 클라우드 원가 분석

                      -
                      -

                      04

                      -
                      - - -
                      - -
                      -
                      -

                      토큰 기반 과금 (LLM)

                      -
                      -

                      환율 1,400원/USD

                      -
                      -
                      - - -
                      -
                      -

                      Google Gemini 2.0 Flash

                      -
                      -
                      -
                      -

                      입력 /1M 토큰

                      -

                      $0.10 → 140원

                      -
                      -
                      -

                      출력 /1M 토큰

                      -

                      $0.40 → 560원

                      -
                      -
                      -
                      -

                      초저비용 AI — 100만 토큰 처리해도 700원

                      -
                      -
                      - - -
                      -
                      -

                      Anthropic Claude 3 Haiku

                      -
                      -
                      -
                      -

                      입력 /1M 토큰

                      -

                      $0.25 → 350원

                      -
                      -
                      -

                      출력 /1M 토큰

                      -

                      $1.25 → 1,750원

                      -
                      -
                      -
                      -

                      고품질 AI — 100만 토큰에도 약 2,100원

                      -
                      -
                      -
                      - - -
                      -

                      시간/작업 기반 과금

                      - - -
                      -
                      -

                      Google STT

                      -

                      $0.009 / 15초

                      -
                      -
                      -

                      ~50원/분

                      -

                      1시간 녹음 처리 = 3,000원

                      -
                      -
                      - - -
                      -
                      -

                      Google GCS

                      -

                      $0.005 / 1,000건

                      -
                      -
                      -

                      7원/1,000건

                      -

                      사실상 무료 수준

                      -
                      -
                      - - -
                      -
                      -

                      Google FCM (푸시알림)

                      -

                      무제한

                      -
                      -
                      -

                      무료

                      -

                      완전 무료

                      -
                      -
                      - - -
                      -

                      핵심 인사이트

                      -

                      LLM 토큰 비용은 건당 1원 미만 수준. 클라우드 인프라 비용도 무시 가능한 수준으로, AI 기능이 수익 마진에 미치는 영향이 극히 작음.

                      -
                      -
                      -
                      - - -
                      -

                      ai_pricing_configs 테이블에서 동적 관리

                      -

                      캐시 TTL 1시간

                      -
                      - - -
                      -

                      SAM 내부 과금정책 | CONFIDENTIAL

                      -

                      04

                      -
                      - - diff --git a/sam/docs/rules/slides/billing-policy/slide-05.html b/sam/docs/rules/slides/billing-policy/slide-05.html deleted file mode 100644 index 377697e..0000000 --- a/sam/docs/rules/slides/billing-policy/slide-05.html +++ /dev/null @@ -1,115 +0,0 @@ - - - - - - - - -
                      -
                      -
                      -

                      MARGIN

                      -
                      -

                      마진 구조 + 가격 책정 배경

                      -
                      -

                      05

                      -
                      - - -
                      - -
                      -
                      -

                      MARGIN STRUCTURE

                      -

                      회사 영업이익률

                      -

                      67~75%

                      -
                      -
                      -
                      -

                      개인 가입

                      -

                      72~75% (수당 25~28%)

                      -
                      -
                      -

                      단체 가입

                      -

                      ~67% (수당 33%)

                      -
                      -
                      -
                      -

                      SaaS 업계 평균 마진 60% 대비 높은 수익성

                      -
                      -
                      - - -
                      -
                      -

                      PRICE BACKGROUND

                      -

                      내부 총 개발비 산정

                      -

                      7,600만원

                      -
                      -
                      -
                      -
                      -

                      개발비 2,000만원 + 장기 구독 약 7년

                      -
                      -
                      -
                      -

                      금융 비용 4.6% 포함

                      -
                      -
                      -
                      -

                      중소기업 초기 투자 부담 분산 구조

                      -
                      -
                      -
                      -
                      - - -
                      -

                      본사 지출 월간 예상 (테넌트 1개)

                      -
                      -
                      -

                      바로빌 홈택스

                      -

                      0원

                      -
                      -
                      -

                      세금계산서

                      -

                      5,000~10,000원

                      -
                      -
                      -

                      AI 토큰

                      -

                      100~5,000원

                      -
                      -
                      -

                      GCS/STT

                      -

                      50~2,000원

                      -
                      -
                      -
                      -

                      합계 최대

                      -

                      ~17,000원/월

                      -
                      -
                      -
                      -

                      구독료 50만원 대비 원가율 3.4% — 스케일 시 한계비용 0에 수렴

                      -
                      -
                      - - -
                      -

                      SAM 내부 과금정책 | CONFIDENTIAL

                      -

                      05

                      -
                      - - diff --git a/sam/docs/rules/slides/billing-policy/slide-06.html b/sam/docs/rules/slides/billing-policy/slide-06.html deleted file mode 100644 index 923547d..0000000 --- a/sam/docs/rules/slides/billing-policy/slide-06.html +++ /dev/null @@ -1,116 +0,0 @@ - - - - - - - - -
                      -
                      -
                      -

                      CODE REF

                      -
                      -

                      코드 참조 — 소스 파일

                      -
                      -

                      06

                      -
                      - - -
                      - -
                      -
                      -
                      -

                      바로빌 과금

                      -
                      -

                      6개 파일

                      -
                      -
                      - -
                      -
                      -

                      BarobillSubscription.php

                      -

                      DEFAULT_MONTHLY_FEES 상수

                      -
                      -
                      -

                      BarobillBillingRecord.php

                      -

                      USAGE_UNIT_PRICES 상수

                      -
                      -
                      -

                      BarobillPricingPolicy.php

                      -

                      calculateBilling() 메서드

                      -
                      -
                      -

                      BarobillBillingService.php

                      -

                      월정액/건별 과금 처리

                      -
                      -
                      -

                      BarobillUsageService.php

                      -

                      사용량 집계

                      -
                      -
                      -

                      BarobillPricingPolicySeeder.php

                      -

                      5개 정책 시더

                      -
                      -
                      -
                      - - -
                      -
                      -
                      -

                      AI 가격/토큰

                      -
                      -

                      5개 파일

                      -
                      -
                      - -
                      -
                      -

                      AiPricingConfig.php

                      -

                      getActivePricing()

                      -
                      -
                      -

                      AiTokenUsage.php

                      -

                      토큰 사용량 기록

                      -
                      -
                      -

                      AiReportService.php

                      -

                      saveTokenUsage()

                      -
                      -
                      -

                      ai_pricing_configs 마이그레이션

                      -

                      AI 단가 테이블

                      -
                      -
                      -

                      ai_token_usages 마이그레이션

                      -

                      토큰 사용량 테이블

                      -
                      -
                      - - -
                      -

                      모든 과금 상수와 정책은 소스 코드에 정의되며, DB 시더/마이그레이션으로 초기 데이터가 관리됩니다.

                      -
                      -
                      -
                      - - -
                      -

                      SAM 내부 과금정책 | CONFIDENTIAL

                      -

                      06

                      -
                      - - diff --git a/sam/docs/rules/slides/billing-policy/slide-07.html b/sam/docs/rules/slides/billing-policy/slide-07.html deleted file mode 100644 index 7a5a772..0000000 --- a/sam/docs/rules/slides/billing-policy/slide-07.html +++ /dev/null @@ -1,128 +0,0 @@ - - - - - - - - -
                      -
                      -
                      -

                      CODE REF

                      -
                      -

                      코드 참조 — DB 테이블

                      -
                      -

                      07

                      -
                      - - -
                      - -
                      -

                      테이블

                      -

                      카테고리

                      -

                      설명

                      -
                      - - -
                      -

                      barobill_subscriptions

                      -
                      -
                      -

                      바로빌

                      -
                      -
                      -

                      바로빌 월정액 구독 현황

                      -
                      - - -
                      -

                      barobill_billing_records

                      -
                      -
                      -

                      바로빌

                      -
                      -
                      -

                      바로빌 월별 과금 내역

                      -
                      - - -
                      -

                      barobill_pricing_policies

                      -
                      -
                      -

                      바로빌

                      -
                      -
                      -

                      과금 정책 (무료 제공량, 추가 단가)

                      -
                      - - -
                      -

                      ai_pricing_configs

                      -
                      -
                      -

                      AI

                      -
                      -
                      -

                      AI 제공자별 단가 설정

                      -
                      - - -
                      -

                      ai_token_usages

                      -
                      -
                      -

                      AI

                      -
                      -
                      -

                      AI 토큰 사용량 기록

                      -
                      - - -
                      -

                      ai_voice_recordings

                      -
                      -
                      -

                      AI

                      -
                      -
                      -

                      AI 음성 녹음 (STT 비용 발생)

                      -
                      - - -
                      -

                      sales_commissions

                      -
                      -
                      -

                      영업

                      -
                      -
                      -

                      영업수수료 정산

                      -
                      -
                      - - -
                      -

                      모든 테이블은 tenant_id 기반 Multi-tenant 구조이며, API 프로젝트(/home/aweso/sam/api)에서 마이그레이션을 관리합니다.

                      -
                      - - -
                      -

                      SAM 내부 과금정책 | CONFIDENTIAL

                      -

                      07

                      -
                      - - diff --git a/sam/docs/rules/slides/customer-pricing/slide-01.html b/sam/docs/rules/slides/customer-pricing/slide-01.html deleted file mode 100644 index e20a544..0000000 --- a/sam/docs/rules/slides/customer-pricing/slide-01.html +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - - - -
                      -
                      - -

                      SAM

                      -
                      -
                      -

                      SERVICE PRICING 2026

                      -
                      -
                      - - -
                      -

                      SMART AUTOMATION MANAGEMENT

                      -

                      SAM 서비스 요금 안내

                      -

                      제조 현장의 디지털 전환, SAM 하나로 완성합니다

                      -
                      - - -
                      -
                      -

                      80% UP

                      -

                      업무 자동화율 향상

                      -
                      -
                      -

                      30% DOWN

                      -

                      인건비 절감 효과

                      -
                      -
                      -

                      100%

                      -

                      실시간 현황 파악

                      -
                      -
                      - - -
                      -
                      -

                      COMPANY

                      -

                      (주)코드브릿지엑스

                      -
                      -
                      -

                      DATE

                      -

                      2026. 02

                      -
                      -
                      -

                      NOTE

                      -

                      VAT 별도

                      -
                      -
                      -

                      01 / 07

                      -
                      -
                      - - \ No newline at end of file diff --git a/sam/docs/rules/slides/customer-pricing/slide-02.html b/sam/docs/rules/slides/customer-pricing/slide-02.html deleted file mode 100644 index 7a6d142..0000000 --- a/sam/docs/rules/slides/customer-pricing/slide-02.html +++ /dev/null @@ -1,82 +0,0 @@ - - - - - - - - -
                      - -
                      -
                      -

                      CONTENTS

                      -
                      -

                      목차

                      -

                      SAM 서비스의 투명한 요금 체계를 한눈에 확인하세요

                      -
                      - - -
                      - -
                      -
                      -

                      01

                      -
                      -
                      -

                      기본 서비스 요금

                      -

                      제조업 패키지, 개별 모듈, 통합 패키지

                      -
                      -
                      - -
                      -
                      -

                      02

                      -
                      -
                      -

                      추가 옵션 요금

                      -

                      생산공정 추가, 품질관리, 챗봇, 연구노트

                      -
                      -
                      - -
                      -
                      -

                      03

                      -
                      -
                      -

                      사용량 기반 과금

                      -

                      파일 저장 공간, AI 토큰 사용량

                      -
                      -
                      - -
                      -
                      -

                      04

                      -
                      -
                      -

                      바로빌 부가서비스

                      -

                      계좌조회, 카드내역, 세금계산서 발행

                      -
                      -
                      -
                      -
                      - - -
                      -

                      SAM 서비스 요금 안내 | (주)코드브릿지엑스

                      -

                      02 / 07

                      -

                      모든 금액 VAT 별도

                      -
                      - - \ No newline at end of file diff --git a/sam/docs/rules/slides/customer-pricing/slide-03.html b/sam/docs/rules/slides/customer-pricing/slide-03.html deleted file mode 100644 index 925e39a..0000000 --- a/sam/docs/rules/slides/customer-pricing/slide-03.html +++ /dev/null @@ -1,131 +0,0 @@ - - - - - - - - -
                      -
                      -
                      -

                      SECTION 01

                      -
                      -

                      기본 서비스 요금

                      -
                      -

                      03 / 07

                      -
                      - - -
                      - - -
                      -
                      -
                      -
                      -

                      BEST SELLER

                      -
                      -
                      -

                      BASIC

                      -
                      -
                      -

                      제조업 기본 패키지

                      -

                      품목관리 → 견적 → 수주 → 생산 → 출하

                      -
                      -

                      ERP 인사/회계 무료 포함

                      -
                      -
                      -
                      -
                      -
                      -

                      개발비 (1회)

                      -

                      2,000만원

                      -
                      -
                      -

                      구독료 (월)

                      -

                      50만원

                      -
                      -
                      -
                      -

                      하루 약 1.6만원으로 제조 ERP 완성

                      -
                      -
                      -
                      - - -
                      - -

                      개별 모듈

                      - -
                      -

                      모듈명

                      -

                      개발비

                      -

                      구독/월

                      -
                      - -
                      -

                      QR코드 관리

                      -

                      1,020만원

                      -

                      5만원

                      -
                      - -
                      -

                      사진/출하 관리

                      -

                      1,920만원

                      -

                      10만원

                      -
                      - -
                      -

                      검사/토큰 적용

                      -

                      1,020만원

                      -

                      5만원

                      -
                      - -
                      -

                      이카운트 연동

                      -

                      1,920만원

                      -

                      10만원

                      -
                      - - -

                      통합 패키지

                      - -
                      -

                      패키지명

                      -

                      개발비

                      -

                      구독/월

                      -
                      - -
                      -

                      공사관리 패키지

                      -

                      4,000만원

                      -

                      20만원

                      -
                      - -
                      -

                      공정/정부지원사업

                      -

                      8,000만원

                      -

                      40만원

                      -
                      -
                      -
                      - - -
                      -

                      SAM 서비스 요금 안내 | (주)코드브릿지엑스

                      -

                      모든 금액 VAT 별도

                      -
                      - - \ No newline at end of file diff --git a/sam/docs/rules/slides/customer-pricing/slide-04.html b/sam/docs/rules/slides/customer-pricing/slide-04.html deleted file mode 100644 index 154d62c..0000000 --- a/sam/docs/rules/slides/customer-pricing/slide-04.html +++ /dev/null @@ -1,163 +0,0 @@ - - - - - - - - -
                      -
                      -
                      -

                      SECTION 02

                      -
                      -

                      추가 옵션 요금

                      -
                      -

                      04 / 07

                      -
                      - - -
                      - -
                      - -
                      -
                      -

                      생산공정 1개 추가

                      -

                      생산 라인별 개별 공정 확장

                      -
                      -
                      -
                      -

                      개발비

                      -

                      500만원

                      -
                      -
                      -

                      구독/월

                      -

                      10만원

                      -
                      -
                      -
                      - - -
                      -
                      -
                      -

                      품질관리 (인정검사)

                      -
                      -
                      -

                      품질 인증 필수!

                      -
                      -

                      ISO 인증 대응 필수 모듈

                      -
                      -
                      -
                      -

                      개발비

                      -

                      2,000만원

                      -
                      -
                      -

                      구독/월

                      -

                      50만원

                      -
                      -
                      -
                      - - -
                      -
                      -

                      사진 등록

                      -

                      제품/공정 사진 관리 기능

                      -
                      -
                      -
                      -

                      개발비

                      -

                      -

                      -
                      -
                      -

                      구독/월

                      -

                      10만원

                      -
                      -
                      -
                      -
                      - - -
                      - -
                      -
                      -

                      챗봇 / 녹음 / 업무일지

                      -

                      AI 기반 업무 보조 도구 3종

                      -
                      -
                      -
                      -

                      개발비

                      -

                      -

                      -
                      -
                      -

                      구독/월

                      -

                      각 20만원

                      -
                      -
                      -
                      - - -
                      -
                      -

                      연구소 연구노트

                      -

                      R&D 기록 및 지식재산 관리

                      -
                      -
                      -
                      -

                      개발비

                      -

                      -

                      -
                      -
                      -

                      구독/월

                      -

                      5만원

                      -
                      -
                      -
                      - - -
                      -
                      -

                      장비점검 / 사무소 정비

                      -

                      설비 유지보수 이력 관리

                      -
                      -
                      -
                      -

                      개발비

                      -

                      -

                      -
                      -
                      -

                      구독/월

                      -

                      5만원

                      -
                      -
                      -
                      -
                      -
                      - - -
                      -

                      참고 품질관리(인정검사)에는 장비점검/사무소 정비 기능이 기본 포함됩니다.

                      -
                      - - -
                      -

                      SAM 서비스 요금 안내 | (주)코드브릿지엑스

                      -

                      모든 금액 VAT 별도

                      -
                      - - \ No newline at end of file diff --git a/sam/docs/rules/slides/customer-pricing/slide-05.html b/sam/docs/rules/slides/customer-pricing/slide-05.html deleted file mode 100644 index 03b55af..0000000 --- a/sam/docs/rules/slides/customer-pricing/slide-05.html +++ /dev/null @@ -1,124 +0,0 @@ - - - - - - - - -
                      -
                      -
                      -

                      SECTION 03

                      -
                      -

                      사용량 기반 추가 과금

                      -
                      -

                      05 / 07

                      -
                      - - -
                      - - -
                      - -
                      -
                      -
                      -

                      F

                      -
                      -

                      파일 저장 공간

                      -
                      -
                      -

                      기본 제공

                      -

                      100GB

                      -
                      -
                      -
                      -

                      초과 시

                      -

                      100GB당 10만원/월

                      -
                      -
                      - - -
                      -
                      -
                      -

                      AI

                      -
                      -

                      AI 토큰

                      -
                      -
                      -

                      기본 제공

                      -

                      100만 토큰/월

                      -
                      -
                      -
                      -

                      초과 시

                      -

                      실비 정산

                      -
                      -
                      -
                      - - -
                      -

                      AI 100만 토큰, 이만큼 쓸 수 있습니다

                      -

                      대부분의 중소 제조업에서 충분한 사용량

                      - - -
                      -
                      -
                      -

                      음성 회의 요약

                      -

                      하루 30분씩 매일 회의해도 한 달 충분

                      -
                      -

                      520

                      -
                      -
                      - - -
                      -
                      -
                      -

                      문서 자동 정리

                      -

                      매일 15장씩 처리해도 여유

                      -
                      -

                      300~400매(A4)

                      -
                      -
                      - - -
                      -
                      -
                      -

                      이메일/노트 자동 분류

                      -

                      하루 75건 자동 분류 가능

                      -
                      -

                      2,000

                      -
                      -
                      - -
                      -

                      미사용 토큰 이월 불가 | 80% / 100% 소진 시 자동 알림

                      -
                      -
                      -
                      - - -
                      -

                      SAM 서비스 요금 안내 | (주)코드브릿지엑스

                      -

                      모든 금액 VAT 별도

                      -
                      - - \ No newline at end of file diff --git a/sam/docs/rules/slides/customer-pricing/slide-06.html b/sam/docs/rules/slides/customer-pricing/slide-06.html deleted file mode 100644 index e3c9461..0000000 --- a/sam/docs/rules/slides/customer-pricing/slide-06.html +++ /dev/null @@ -1,117 +0,0 @@ - - - - - - - - -
                      -
                      -
                      -

                      SECTION 04

                      -
                      -

                      바로빌 부가서비스

                      -
                      -

                      06 / 07

                      -
                      - - -
                      - -
                      -

                      서비스

                      -

                      과금 방식

                      -

                      기본 제공

                      -

                      추가 과금

                      -

                      부담

                      -
                      - - -
                      -

                      계좌조회

                      -

                      월정액 10,000원

                      -

                      1계좌

                      -

                      추가 1계좌당 10,000원

                      -
                      -
                      -

                      고객

                      -
                      -
                      -
                      - - -
                      -

                      카드내역

                      -

                      월정액 10,000원

                      -

                      5장

                      -

                      추가 1장당 5,000원

                      -
                      -
                      -

                      고객

                      -
                      -
                      -
                      - - -
                      -

                      홈택스 매입/매출

                      -

                      월 33,000원

                      -

                      -

                      -

                      -

                      -
                      -
                      -

                      SAM 무료!

                      -
                      -
                      -
                      - - -
                      -

                      세금계산서 발행

                      -

                      건별

                      -

                      100건

                      -

                      추가 50건당 5,000원

                      -
                      -
                      -

                      고객

                      -
                      -
                      -
                      -
                      - - -
                      -

                      과금 계산 예시

                      -
                      - -
                      -

                      법인카드 8장 등록 시

                      -

                      (8 - 5) x 5,000 = 15,000원 추가

                      -
                      - -
                      -

                      세금계산서 151건 발행 시

                      -

                      ceil((151-100)/50) x 5,000 = 10,000원 추가

                      -
                      -
                      -
                      - - -
                      -

                      SAM 서비스 요금 안내 | (주)코드브릿지엑스

                      -

                      모든 금액 VAT 별도

                      -
                      - - \ No newline at end of file diff --git a/sam/docs/rules/slides/customer-pricing/slide-07.html b/sam/docs/rules/slides/customer-pricing/slide-07.html deleted file mode 100644 index b7ebd6e..0000000 --- a/sam/docs/rules/slides/customer-pricing/slide-07.html +++ /dev/null @@ -1,65 +0,0 @@ - - - - - - - - -
                      - -

                      SAM

                      -
                      - - -
                      -

                      지금 SAM과 함께 시작하세요

                      -

                      제조 현장의 모든 흐름을 하나의 플랫폼에서 관리합니다

                      -
                      -
                      -

                      ERP + MES 통합

                      -
                      -
                      -

                      AI 기반 업무 자동화

                      -
                      -
                      -

                      실시간 현황 대시보드

                      -
                      -
                      -
                      - - -
                      -
                      -

                      COMPANY

                      -

                      (주)코드브릿지엑스

                      -
                      -
                      -

                      SYSTEM

                      -

                      Smart Automation Management

                      -
                      -
                      -

                      CONTACT

                      -

                      담당 영업파트너 또는 본사

                      -
                      -
                      -
                      -

                      모든 금액 VAT 별도

                      -
                      -

                      07 / 07

                      -
                      -
                      - - \ No newline at end of file diff --git a/sam/docs/rules/slides/partner-commission/slide-01.html b/sam/docs/rules/slides/partner-commission/slide-01.html deleted file mode 100644 index 1a7624d..0000000 --- a/sam/docs/rules/slides/partner-commission/slide-01.html +++ /dev/null @@ -1,72 +0,0 @@ - - - - - - - - -
                      -
                      - -

                      SAM

                      -
                      -
                      -

                      PARTNER COMMISSION GUIDE

                      -
                      -
                      - - -
                      -

                      SALES PARTNER COMMISSION

                      -

                      SAM 영업파트너
                      수당 체계

                      -

                      Smart Automation Management

                      -

                      업계 최고 수준 수당률, 함께 성장하는 파트너십

                      -
                      - - -
                      -
                      -

                      33%

                      -

                      최대 수당률

                      -
                      -
                      -

                      660만원

                      -

                      개발비 2,000만원 기준

                      -
                      -
                      -

                      무제한

                      -

                      누적 파트너 수익

                      -
                      -
                      - - -
                      -
                      -

                      COMPANY

                      -

                      (주)코드브릿지엑스

                      -
                      -
                      -

                      DATE

                      -

                      2026. 02

                      -
                      -
                      -

                      NOTE

                      -

                      VAT 별도

                      -
                      -
                      - - \ No newline at end of file diff --git a/sam/docs/rules/slides/partner-commission/slide-02.html b/sam/docs/rules/slides/partner-commission/slide-02.html deleted file mode 100644 index b7ce596..0000000 --- a/sam/docs/rules/slides/partner-commission/slide-02.html +++ /dev/null @@ -1,91 +0,0 @@ - - - - - - - - -
                      -
                      -
                      -

                      SECTION 01

                      -
                      -

                      수당 지급 3대 원칙

                      -
                      -

                      02

                      -
                      - - -
                      - -
                      -
                      -

                      1

                      -
                      -

                      개발비 기반

                      -

                      수당은 개발비에 대해서만 지급합니다.

                      -

                      구독료는 인프라 비용으로 활용되며 수당 대상이 아닙니다.

                      -
                      -
                      -

                      개발비 = 수당 대상

                      -
                      -
                      -
                      - - -
                      -
                      -

                      2

                      -
                      -

                      기준금액 50% 적용

                      -

                      개발비의 50%를 기준금액으로 산정합니다.

                      -

                      기준금액에 수당률을 적용하여 2단계 분할 지급합니다.

                      -
                      -
                      -

                      2단계 분할 지급

                      -
                      -
                      -
                      - - -
                      -
                      -

                      3

                      -
                      -

                      투명한 정산

                      -

                      매월 정산일에 건별 수당 확정합니다.

                      -

                      익월 파트너 등록 계좌로 입금합니다.

                      -
                      -
                      -

                      매월 정산 + 익월 지급

                      -
                      -
                      -
                      -
                      - - -
                      -

                      명확한 기준, 신뢰할 수 있는 파트너십

                      -
                      - - -
                      -

                      SAM 영업파트너 수당 체계 | (주)코드브릿지엑스

                      -

                      02

                      -

                      VAT 별도

                      -
                      - - \ No newline at end of file diff --git a/sam/docs/rules/slides/partner-commission/slide-03.html b/sam/docs/rules/slides/partner-commission/slide-03.html deleted file mode 100644 index ce86831..0000000 --- a/sam/docs/rules/slides/partner-commission/slide-03.html +++ /dev/null @@ -1,118 +0,0 @@ - - - - - - - - -
                      -
                      -
                      -

                      SECTION 02

                      -
                      -

                      가입 유형별 수당률

                      -
                      -

                      03

                      -
                      - - -
                      - -
                      -
                      -

                      개인 가입

                      -
                      -

                      총 28%

                      -
                      -
                      - - -
                      -

                      28%

                      -
                      - - -
                      -
                      -

                      파트너 수당

                      -

                      20%

                      -
                      -
                      -

                      매니저 수당

                      -

                      5%

                      -
                      -
                      -

                      협업지원금(유치자)

                      -

                      3%

                      -
                      -
                      - -
                      -

                      매니저와 유치자를 통한 추가 수익 구조

                      -
                      -
                      - - -
                      -
                      -

                      단체 가입

                      -
                      -

                      총 33%

                      -
                      -
                      - - -
                      -

                      33%

                      -
                      - - -
                      -
                      -

                      단체 수당

                      -

                      30%

                      -
                      -
                      -

                      매니저

                      -

                      0%

                      -
                      -
                      -

                      협업지원금(유치자)

                      -

                      3%

                      -
                      -
                      - -
                      -

                      단체 가입 시 최고 수당률!

                      -
                      -
                      -
                      - - -
                      -

                      유치자 수당은 하위 파트너 1단계까지 적용

                      -

                      |

                      -

                      단체 가입 시 수수료율 개발비의 33% (VAT 별도)

                      -
                      - - -
                      -

                      SAM 영업파트너 수당 체계 | (주)코드브릿지엑스

                      -

                      03

                      -

                      VAT 별도

                      -
                      - - \ No newline at end of file diff --git a/sam/docs/rules/slides/partner-commission/slide-04.html b/sam/docs/rules/slides/partner-commission/slide-04.html deleted file mode 100644 index 72c5b0f..0000000 --- a/sam/docs/rules/slides/partner-commission/slide-04.html +++ /dev/null @@ -1,180 +0,0 @@ - - - - - - - - -
                      -
                      -
                      -

                      SECTION 03

                      -
                      -

                      수당 산정 시뮬레이션

                      -
                      -

                      04

                      -
                      - - -
                      -

                      기준금액 100만원 기준

                      - - -
                      -
                      -

                      가입 유형

                      -
                      -
                      -

                      파트너/단체

                      -
                      -
                      -

                      매니저

                      -
                      -
                      -

                      협업지원금

                      -
                      -
                      -

                      총 지급

                      -
                      -
                      - - -
                      -
                      -

                      개인 (유치자O)

                      -
                      -
                      -

                      200,000원

                      -
                      -
                      -

                      50,000원

                      -
                      -
                      -

                      30,000원

                      -
                      -
                      -

                      280,000원 (28%)

                      -
                      -
                      - - -
                      -
                      -

                      개인 (유치자X)

                      -
                      -
                      -

                      200,000원

                      -
                      -
                      -

                      50,000원

                      -
                      -
                      -

                      0원

                      -
                      -
                      -

                      250,000원 (25%)

                      -
                      -
                      - - -
                      -
                      -

                      단체

                      -
                      -
                      -

                      300,000원

                      -
                      -
                      -

                      0원

                      -
                      -
                      -

                      30,000원

                      -
                      -
                      -

                      330,000원 (33%)

                      -
                      -
                      -
                      - - -
                      -
                      -

                      제조업 기본 패키지 (개발비 2,000만원) 실제 수당

                      -
                      -

                      실전 시뮬레이션

                      -
                      -
                      - -
                      - -
                      -

                      개인 가입

                      -
                      -

                      판매자(파트너)

                      -

                      400만원

                      -
                      -
                      -

                      매니저

                      -

                      100만원

                      -
                      -
                      -

                      협업지원금

                      -

                      60만원

                      -
                      -
                      -
                      -

                      총 560만원 수익!

                      -
                      -
                      -
                      - - -
                      -
                      -

                      단체 가입

                      -
                      -

                      BEST

                      -
                      -
                      -
                      -

                      단체

                      -

                      600만원

                      -
                      -
                      -

                      매니저

                      -

                      0원

                      -
                      -
                      -

                      협업지원금

                      -

                      60만원

                      -
                      -
                      -
                      -

                      총 660만원 수익!

                      -
                      -
                      -
                      -
                      -
                      - - -
                      -

                      SAM 영업파트너 수당 체계 | (주)코드브릿지엑스

                      -

                      04

                      -

                      VAT 별도

                      -
                      - - \ No newline at end of file diff --git a/sam/docs/rules/slides/partner-commission/slide-05.html b/sam/docs/rules/slides/partner-commission/slide-05.html deleted file mode 100644 index de4844e..0000000 --- a/sam/docs/rules/slides/partner-commission/slide-05.html +++ /dev/null @@ -1,129 +0,0 @@ - - - - - - - - -
                      -
                      -
                      -

                      SECTION 04

                      -
                      -

                      수당 지급 프로세스

                      -
                      -

                      05

                      -
                      - - -
                      - -
                      -
                      -

                      1

                      -
                      -

                      계약 및 입금

                      -

                      개발비 전액 완납 확인

                      -
                      - - -
                      -

                      -
                      - - -
                      -
                      -

                      2

                      -
                      -

                      수당 확정

                      -

                      매월 정산일 건별 수당 확정

                      -
                      - - -
                      -

                      -
                      - - -
                      -
                      -

                      3

                      -
                      -

                      지급

                      -

                      파트너 등록 계좌로 입금

                      -
                      -
                      - - -
                      -
                      -

                      추가 옵션 수당 참고

                      -
                      -

                      개발비가 있는 옵션만 수당 대상

                      -
                      -
                      - -
                      - -
                      -

                      생산공정 추가

                      -
                      -

                      개발비

                      -

                      500만원

                      -
                      -
                      -

                      판매자 수당

                      -

                      100만원

                      -
                      -
                      - - -
                      -

                      품질관리 인정검사

                      -
                      -

                      개발비

                      -

                      2,000만원

                      -
                      -
                      -

                      판매자 수당

                      -

                      400만원

                      -
                      -
                      - - -
                      -

                      구독료만 있는 옵션

                      -
                      -

                      개발비

                      -

                      없음

                      -
                      -
                      -

                      판매자 수당

                      -

                      비대상

                      -
                      -
                      -
                      -
                      - - -
                      -

                      SAM 영업파트너 수당 체계 | (주)코드브릿지엑스

                      -

                      05

                      -

                      VAT 별도

                      -
                      - - \ No newline at end of file diff --git a/sam/docs/rules/slides/partner-commission/slide-06.html b/sam/docs/rules/slides/partner-commission/slide-06.html deleted file mode 100644 index 4c69874..0000000 --- a/sam/docs/rules/slides/partner-commission/slide-06.html +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - - - -
                      -
                      - -

                      SAM

                      -
                      -

                      06

                      -
                      - - -
                      -

                      PARTNERSHIP

                      -

                      지금, SAM 파트너로
                      함께하세요

                      -

                      제조업 디지털 전환 시장, 폭발적 성장 중

                      -

                      대한민국 제조업 디지털 전환, 당신이 이끄는 비즈니스 기회

                      -
                      - - -
                      -
                      -

                      33%

                      -

                      최대 수당률

                      -
                      -
                      -

                      660만원

                      -

                      건당 최대 수익

                      -
                      -
                      -

                      무제한

                      -

                      누적 수익 상한

                      -
                      -
                      - - -
                      -
                      -
                      -

                      COMPANY

                      -

                      (주)코드브릿지엑스

                      -
                      -
                      -

                      SYSTEM

                      -

                      Smart Automation Management

                      -
                      -
                      -

                      CONTACT

                      -

                      본사 정산팀

                      -
                      -
                      -

                      모든 금액 VAT 별도

                      -
                      - - \ No newline at end of file diff --git a/sam/docs/system/ai-automation-vision.md b/system/ai-automation-vision.md similarity index 100% rename from sam/docs/system/ai-automation-vision.md rename to system/ai-automation-vision.md diff --git a/system/database/codebridge-separation.md b/system/database/codebridge-separation.md new file mode 100644 index 0000000..9022cbe --- /dev/null +++ b/system/database/codebridge-separation.md @@ -0,0 +1,443 @@ +# codebridge DB 분리 + +> **작성일**: 2026-03-07 +> **상태**: 로컬/개발 서버 적용 완료, **운영 서버 코드 revert 상태 — DB 선행 작업 필요** +> **최종 수정**: 2026-03-09 — API 사용 테이블 점검, 로컬/개발 samdb 삭제 완료, 운영 코드 revert + +--- + +## 1. 개요 + +### 1.1 목적 + +SAM 프로젝트의 DB를 **서비스용**과 **내부 관리용**으로 분리한다. + +- **samdb**: React 서비스가 사용하는 테이블 (수주, 견적, 생산, 거래처 등) +- **codebridge**: MNG(관리자 패널)에서만 사용하는 코드브릿지엑스 내부 관리 테이블 + +### 1.2 핵심 원칙 + +- codebridge DB에 테이블을 복사한 후, samdb에서 해당 테이블을 **삭제**하여 실질적 분리 +- MNG 모델에 `$connection = 'codebridge'`를 설정하여 읽기 대상 DB만 변경 +- React/API 서비스에는 **영향 없음** +- 개발 서버: samdb에서 59개 테이블 삭제 완료 (백업: `/home/pro/backup/sam_backup_20260309.sql.gz`) + +### 1.3 분리 기준 + +| 분류 | 대상 DB | 기준 | +|------|---------|------| +| React 서비스 테이블 | samdb (유지) | React 프론트엔드 또는 API에서 사용 | +| MNG 전용 테이블 | codebridge (이동) | MNG에서만 사용, React/API 미참조 | +| 공통 테이블 | samdb (유지) | 양쪽 모두 사용 (users, tenants 등) | +| **API 사용 테이블** | **samdb (유지 필수)** | **API에 모델/서비스/컨트롤러 존재 — 이동 시 데이터 불일치 발생** | + +> **경고: API에서 모델/서비스/컨트롤러로 참조하는 테이블을 codebridge로 이동하면, API는 samdb에 쓰고 MNG는 codebridge에서 읽게 되어 데이터 불일치가 발생한다. 절대 이동 금지.** + +--- + +## 2. codebridge 테이블 목록 (59개) + +> 2026-03-09 점검: API 프로젝트 전체 코드 조사를 통해 API에서 사용하는 22개 테이블을 제외함. 제외된 테이블은 [3절](#3-api-사용-테이블--samdb-유지-필수-22개) 참조. +> Equipment 하위 테이블 4개 추가 (FK 의존성으로 equipments와 동일 DB 필수). + +### Admin (9) + +| 테이블 | 설명 | +|--------|------| +| `admin_api_flows` | API 플로우 정의 | +| `admin_api_flow_runs` | API 플로우 실행 이력 | +| `admin_pm_daily_logs` | PM 일일 로그 | +| `admin_pm_daily_log_entries` | PM 일일 로그 항목 | +| `admin_pm_issues` | PM 이슈 | +| `admin_pm_projects` | PM 프로젝트 | +| `admin_pm_tasks` | PM 태스크 | +| `admin_roadmap_milestones` | 로드맵 마일스톤 | +| `admin_roadmap_plans` | 로드맵 계획 | + +### DevTools (5) + +| 테이블 | 설명 | 비고 | +|--------|------|------| +| `admin_api_bookmarks` | API 북마크 | 기존명 `api_bookmarks` | +| `admin_api_deprecations` | API 지원종료 관리 | 기존명 `api_deprecations` | +| `admin_api_environments` | API 환경 설정 | 기존명 `api_environments` | +| `admin_api_histories` | API 호출 이력 | 기존명 `api_histories` | +| `admin_api_templates` | API 템플릿 | 기존명 `api_templates` | + +### Sales (17) + +| 테이블 | 설명 | +|--------|------| +| `sales_partners` | 영업 파트너 | +| `sales_managers` | 영업 담당자 | +| `sales_manager_documents` | 영업 담당자 문서 | +| `sales_commissions` | 영업 수당 | +| `sales_commission_details` | 영업 수당 상세 | +| `sales_consultations` | 영업 상담 | +| `sales_contract_products` | 계약 제품 | +| `sales_products` | 영업 제품 | +| `sales_product_categories` | 영업 제품 카테고리 | +| `sales_prospects` | 영업 전망 | +| `sales_prospect_consultations` | 전망 상담 | +| `sales_prospect_products` | 전망 제품 | +| `sales_prospect_scenarios` | 전망 시나리오 | +| `sales_records` | 영업 실적 | +| `sales_scenario_checklists` | 시나리오 체크리스트 | +| `sales_tenant_managements` | 테넌트 영업 관리 | +| `tenant_prospects` | 테넌트 전망 | + +### Finance (9) + +| 테이블 | 설명 | +|--------|------| +| `condolence_expenses` | 경조사비 | +| `consulting_fees` | 컨설팅비 | +| `corporate_cards` | 법인카드 | +| `corporate_card_prepayments` | 법인카드 선결제 | +| `customer_settlements` | 고객 정산 | +| `daily_fund_memos` | 일일 자금 메모 | +| `daily_fund_transactions` | 일일 자금 거래 | +| `incomes` | 수입 | +| `vat_records` | 부가세 기록 | + +### ESign (2) + +| 테이블 | 설명 | +|--------|------| +| `esign_field_templates` | 전자서명 필드 템플릿 | +| `esign_field_template_items` | 전자서명 필드 항목 | + +> esign_contracts, esign_audit_logs, esign_sign_fields, esign_signers는 API에서 전자계약 기능으로 사용 중 → samdb 유지 + +### Equipment (6) + +| 테이블 | 설명 | +|--------|------| +| `equipments` | 설비 | +| `equipment_processes` | 설비 공정 | +| `equipment_inspections` | 설비 점검 (FK → equipments) | +| `equipment_inspection_details` | 설비 점검 상세 (FK → equipment_inspections) | +| `equipment_inspection_templates` | 설비 점검 템플릿 (FK → equipments) | +| `equipment_repairs` | 설비 수리 (FK → equipments) | + +> Equipment 하위 4개 테이블은 `equipments`에 FK 의존하므로 반드시 동일 DB에 있어야 한다. + +### HR (1) + +| 테이블 | 설명 | +|--------|------| +| `business_income_payments` | 사업소득 지급 | + +> income_tax_brackets는 API IncomeTaxBracketSeeder에서 초기 데이터 관리 → samdb 유지 + +### System (1) + +| 테이블 | 설명 | +|--------|------| +| `ai_configs` | AI 설정 | + +> ai_pricing_configs, ai_token_usages는 API 모델에서 직접 사용 → samdb 유지 + +### 기타 (9) + +| 테이블 | 설명 | 비고 | +|--------|------|------| +| `biz_cert` | 사업자등록증 | 문서 기존명 `biz_certs` → 실제 테이블명 (단수) | +| `cm_songs` | R&D 곡 관리 | | +| `construction_site_photos` | 시공 현장 사진 | | +| `construction_site_photo_rows` | 시공 사진 행 | | +| `admin_meeting_logs` | 회의 로그 | 문서 기존명 `meeting_logs` → 실제 테이블명 | +| `meeting_minutes` | 회의록 | | +| `meeting_minute_segments` | 회의록 세그먼트 | | +| `interview_knowledges` | 면접 지식 | | +| `sales_records` | 매출 기록 | | + +--- + +## 3. API 사용 테이블 — samdb 유지 필수 (22개) + +> **경고: 아래 테이블은 API 프로젝트에서 모델/서비스/컨트롤러/시더로 직접 참조한다. 절대 codebridge로 이동 금지.** +> +> **2026-03-09 점검**: sam/api 프로젝트 전체 코드 (모델, 컨트롤러, 서비스, 라우트, 시더) 조사 완료. + +### Barobill (12) — 전체 samdb 유지 + +| 테이블 | API 사용처 | 사유 | +|--------|-----------|------| +| `barobill_billing_records` | BarobillBillingService | 과금 기록 CRUD | +| `barobill_members` | BarobillUsageService | 회원사 사용량 집계 | +| `barobill_monthly_summaries` | BarobillBillingService | 월별 집계 갱신 | +| `barobill_pricing_policies` | BarobillUsageService | 과금 계산 | +| `bank_sync_statuses` | BankSyncStatus 모델 | 동기화 상태 추적 | +| `bank_transactions` | BankTransactionController | 은행 거래 조회/분개 | +| `bank_transaction_overrides` | BankTransactionOverride 모델 | 거래 재정의 | +| `bank_transaction_splits` | BankTransactionController | 은행 거래 분개 | +| `card_transaction_amount_logs` | CardTransactionAmountLog 모델 | 금액 수정 이력 + FK → card_transactions | +| `card_transaction_hides` | CardTransactionHide 모델 | 거래 숨김 + FK → card_transactions | +| `hometax_invoices` | BarobillUsageService | 세금계산서 사용량 집계 | +| `hometax_invoice_journals` | HometaxInvoiceJournal 모델 | 세금계산서 분개 + FK → hometax_invoices | + +> **핵심**: API의 BarobillController, BarobillSettingController, BarobillService, EntertainmentService가 바로빌 테이블을 직접 참조. `barobill_card_transactions` (samdb 유지)와 FK로 연결된 자식 테이블도 분리 불가. + +### ESign (4) — API 전자계약 기능 + +| 테이블 | API 사용처 | 사유 | +|--------|-----------|------| +| `esign_contracts` | EsignContractController, EsignService | 전자계약 CRUD | +| `esign_audit_logs` | EsignService | 감사 추적 기록 | +| `esign_sign_fields` | EsignService | 서명 위치 데이터 | +| `esign_signers` | EsignService | 서명자 정보/인증 | + +### Audit (2) — API 전사 감사 시스템 + +| 테이블 | API 사용처 | 사유 | +|--------|-----------|------| +| `audit_logs` | AuditLog 모델, AuditLogService, AuditRollbackService | 전사 DML 감사 | +| `trigger_audit_logs` | TriggerAuditLog 모델, TriggerAuditLogController, RegenerateAuditTriggers 명령 | DB 트리거 감사 + 파티셔닝 관리 | + +### DevTools (1) + +| 테이블 | API 사용처 | 사유 | +|--------|-----------|------| +| `api_request_logs` | ApiRequestLog 모델, SystemStatService | API 통계 집계 | + +### System (2) + +| 테이블 | API 사용처 | 사유 | +|--------|-----------|------| +| `ai_pricing_configs` | AiPricingConfig 모델 | AI 서비스 비용 계산 (캐시 기반) | +| `ai_token_usages` | AiTokenUsage 모델 | 멀티테넌트 AI 토큰 사용량 추적 | + +### HR (1) + +| 테이블 | API 사용처 | 사유 | +|--------|-----------|------| +| `income_tax_brackets` | IncomeTaxBracketSeeder | 소득세 구간 초기 데이터 관리 | + +--- + +## 4. 적용 현황 + +### 4.1 환경별 상태 + +| 환경 | codebridge DB | 테이블 복사 | samdb 삭제 | .env 설정 | MNG 코드 | 상태 | +|------|:---:|:---:|:---:|:---:|:---:|------| +| **로컬 Docker** | O | 100개 | **58개 삭제** | O | O (develop) | ✅ 정상 작동, samdb 265개 | +| **개발 서버** | O | 101개 | **63개 삭제** | O | O (develop) | ✅ 정상 작동, samdb 265개 | +| **운영 서버** | **X** | **X** | **X** | **X** | **revert됨** | ⚠️ DB 선행 작업 후 코드 재배포 필요 | + +> **2026-03-09 작업 내역**: +> - API 사용 테이블 22개: codebridge 이동 대상에서 제외 → samdb 유지 +> - `finance_*` 17개 + `barobill_companies` 1개: codebridge에 없는 유령 테이블 → samdb에서만 삭제 +> - Equipment 하위 4개 테이블: FK 의존성으로 codebridge 이동 대상에 추가 (55→59개) +> - **개발 서버 samdb에서 63개 테이블 DROP 완료** (59개 + DevTools 실제 테이블명 4개 추가분) +> - **로컬 samdb에서 58개 테이블 DROP 완료** → 로컬/개발 265개로 동기화 +> - 로컬에 `quality_documents` 등 4개 테이블 구조 동기화 (개발서버에서 복사) +> - 백업: `/home/pro/backup/sam_backup_20260309.sql.gz` (6.3MB) +> +> **테이블명 불일치 발견 (수정 완료)**: +> - `api_bookmarks` → 실제: `admin_api_bookmarks` +> - `meeting_logs` → 실제: `admin_meeting_logs` +> - `biz_certs` → 실제: `biz_cert` (단수형) +> - DevTools 4개: `api_deprecations` → `admin_api_deprecations`, `api_environments` → `admin_api_environments`, `api_histories` → `admin_api_histories`, `api_templates` → `admin_api_templates` +> +> **운영 서버 revert 사유 (2026-03-09)**: +> - MNG main에 codebridge 코드 2건 cherry-pick → Jenkins 배포됨 (빌드 #456, #457) +> - 운영 서버에 codebridge DB가 없는 상태에서 코드 배포 → **59개 모델 사용 페이지 오류 발생 위험** +> - kent가 main에서 revert 2건 push → 운영 서버 정상 복구 +> - **교훈: 운영 서버는 반드시 DB 선행 작업(1~2단계) 완료 후 코드 배포(3단계)** + +### 4.2 코드 변경 사항 + +**config/database.php** — `codebridge` connection 추가: + +```php +'codebridge' => [ + 'driver' => 'mysql', + 'host' => env('CODEBRIDGE_DB_HOST', env('DB_HOST', '127.0.0.1')), + 'port' => env('CODEBRIDGE_DB_PORT', env('DB_PORT', '3306')), + 'database' => env('CODEBRIDGE_DB_DATABASE', 'codebridge'), + 'username' => env('CODEBRIDGE_DB_USERNAME', env('DB_USERNAME')), + 'password' => env('CODEBRIDGE_DB_PASSWORD', env('DB_PASSWORD')), + // ... (mysql 기본 설정과 동일) +], +``` + +**.env** — 추가 설정: + +``` +CODEBRIDGE_DB_DATABASE=codebridge +``` + +**MNG 모델** — `$connection` 속성 추가 (codebridge 59개만): + +```php +class SalesPartner extends Model +{ + protected $connection = 'codebridge'; // 추가 + protected $table = 'sales_partners'; + // ... +} +``` + +> **주의**: API 사용 테이블 22개에 해당하는 MNG 모델은 `$connection = 'codebridge'`를 설정하지 않는다. 기본 samdb connection을 사용해야 API와 동일한 데이터를 참조한다. + +### 4.3 samdb 테이블 삭제 절차 (개발 서버 완료) + +> Sales 테이블 그룹은 FK 상호 참조가 있어 `FOREIGN_KEY_CHECKS = 0`으로 일괄 삭제. + +```sql +-- FK 체크 비활성화 (Sales, Equipment 등 FK 체인 테이블) +SET FOREIGN_KEY_CHECKS = 0; + +-- 59개 테이블 DROP (codebridge에 복제 완료 확인 후) +DROP TABLE IF EXISTS admin_api_flows, admin_api_flow_runs, ...; + +SET FOREIGN_KEY_CHECKS = 1; +``` + +> **롤백**: 백업에서 특정 테이블만 복원 가능 +> ```bash +> gunzip < /home/pro/backup/sam_backup_20260309.sql.gz | mysql -u codebridge -p sam +> ``` + +--- + +## 5. 운영 서버 적용 절차 (미완료) + +> **전제**: 운영 서버 SSH 접근 + DB root 권한 필요 +> **현재 상태**: 운영 서버 main 코드는 revert 상태 (codebridge 코드 없음). DB 작업 완료 후 코드 재배포 필요. +> **⚠️ 교훈**: 2026-03-09에 DB 없이 코드만 배포하여 장애 위험 발생 → **반드시 DB 선행 후 코드 배포** + +### 순서 (반드시 1 → 2 → 3 → 4 → 5 순서로) + +**1단계: 운영 sam DB 백업** + +```bash +# 운영 서버 접속 후 +mysqldump -u codebridge -p'[운영PW]' sam --single-transaction > ~/backup/sam_backup_$(date +%Y%m%d).sql +gzip ~/backup/sam_backup_$(date +%Y%m%d).sql +``` + +**2단계: codebridge DB 생성 + 59개 테이블 복사** + +```bash +# DB 생성 +mysql -u root -p -e "CREATE DATABASE IF NOT EXISTS codebridge CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;" + +# DB 계정 권한 부여 +mysql -u root -p -e "GRANT ALL PRIVILEGES ON codebridge.* TO 'codebridge'@'localhost'; FLUSH PRIVILEGES;" + +# sam에서 59개 테이블 구조+데이터 복사 +mysqldump -u codebridge -p sam \ + admin_api_flows admin_api_flow_runs \ + admin_pm_daily_logs admin_pm_daily_log_entries admin_pm_issues admin_pm_projects admin_pm_tasks \ + admin_roadmap_milestones admin_roadmap_plans \ + admin_api_bookmarks admin_api_deprecations admin_api_environments admin_api_histories admin_api_templates \ + sales_partners sales_managers sales_manager_documents sales_commissions sales_commission_details \ + sales_consultations sales_contract_products sales_products sales_product_categories \ + sales_prospects sales_prospect_consultations sales_prospect_products sales_prospect_scenarios \ + sales_records sales_scenario_checklists sales_tenant_managements tenant_prospects \ + condolence_expenses consulting_fees corporate_cards corporate_card_prepayments \ + customer_settlements daily_fund_memos daily_fund_transactions incomes vat_records \ + esign_field_templates esign_field_template_items \ + equipments equipment_process equipment_inspections equipment_inspection_details \ + equipment_inspection_templates equipment_repairs \ + business_income_payments ai_configs \ + biz_cert cm_songs construction_site_photos construction_site_photo_rows \ + admin_meeting_logs meeting_minutes meeting_minute_segments \ + interview_knowledge \ + | mysql -u codebridge -p codebridge +``` + +**3단계: .env 설정** + +```bash +echo 'CODEBRIDGE_DB_DATABASE=codebridge' >> /home/webservice/mng/.env +cd /home/webservice/mng && php artisan config:clear +``` + +**4단계: MNG 코드 재배포 (main cherry-pick)** + +> develop에 codebridge 코드가 있으므로, revert 커밋 이후 develop의 최신 커밋을 cherry-pick. +> 또는 develop의 해당 커밋을 다시 cherry-pick하여 main에 push. + +```bash +# 로컬에서 실행 +cd /home/aweso/sam/mng +git checkout main && git pull origin main +git cherry-pick +git push origin main +git checkout develop +``` + +**5단계: 동작 확인 + samdb 테이블 삭제 (선택)** + +MNG 관리자 페이지에서 영업관리, 설비, 재무 등 주요 메뉴 동작 확인 후, 문제없으면 sam DB에서 59개 테이블 삭제. + +```sql +SET FOREIGN_KEY_CHECKS = 0; +DROP TABLE IF EXISTS + admin_api_flows, admin_api_flow_runs, + admin_pm_daily_logs, admin_pm_daily_log_entries, admin_pm_issues, admin_pm_projects, admin_pm_tasks, + admin_roadmap_milestones, admin_roadmap_plans, + admin_api_bookmarks, admin_api_deprecations, admin_api_environments, admin_api_histories, admin_api_templates, + sales_partners, sales_managers, sales_manager_documents, sales_commissions, sales_commission_details, + sales_consultations, sales_contract_products, sales_products, sales_product_categories, + sales_prospects, sales_prospect_consultations, sales_prospect_products, sales_prospect_scenarios, + sales_records, sales_scenario_checklists, sales_tenant_managements, tenant_prospects, + condolence_expenses, consulting_fees, corporate_cards, corporate_card_prepayments, + customer_settlements, daily_fund_memos, daily_fund_transactions, incomes, vat_records, + esign_field_templates, esign_field_template_items, + equipments, equipment_process, equipment_inspections, equipment_inspection_details, + equipment_inspection_templates, equipment_repairs, + business_income_payments, ai_configs, + biz_cert, cm_songs, construction_site_photos, construction_site_photo_rows, + admin_meeting_logs, meeting_minutes, meeting_minute_segments, + interview_knowledge; +SET FOREIGN_KEY_CHECKS = 1; +``` + +> **⚠️ 핵심 주의사항**: +> - 반드시 **1→2→3→4** 순서 (DB 먼저, 코드 나중) +> - 4단계(코드 배포) 전에 3단계(.env)까지 완료되어야 함 +> - 5단계(samdb 삭제)는 4단계 동작 확인 후 선택적 수행 + +--- + +## 6. 아키텍처 다이어그램 + +``` + React (사용자) + | + API 서버 (Laravel) + | + ┌─────┴─────┐ + | | + samdb sam_stat + (서비스 DB) (통계 DB) + | + | (공통 + API 사용 테이블: users, tenants, barobill_*, esign_*, audit_* 등) + | + MNG (관리자) + | + ┌─────┴─────┐ + | | + samdb codebridge + (공통 참조) (MNG 전용 59개) +``` + +- **React → API → samdb**: 서비스 트래픽 (수주, 견적, 생산, 바로빌, 전자서명 등) +- **MNG → samdb**: 공통 테이블 (users, tenants, menus) + API 사용 테이블 22개 참조 +- **MNG → codebridge**: MNG 전용 데이터 (영업관리, 재무, 설비, PM 도구 등) + +--- + +## 관련 문서 + +- [database/README.md](README.md) — DB 스키마 전체 현황 +- [codebridge-db-separation-plan.md](/home/aweso/sam/docs/plans/codebridge-db-separation-plan.md) — 분리 작업 계획서 (plans/) + +--- + +**최종 업데이트**: 2026-03-09 (운영 revert 반영, 적용 절차 5단계로 개정) diff --git a/system/server-access-management.md b/system/server-access-management.md index 67da35a..b32b531 100644 --- a/system/server-access-management.md +++ b/system/server-access-management.md @@ -5,7 +5,7 @@ SAM 시스템의 서버 및 데이터베이스 접근 권한을 관리합니다. 서버 관리자는 1명이며, 외부 인원에게는 임시로 접근을 허용하고 작업 완료 후 차단합니다. -**최종 업데이트:** 2026-03-07 +**최종 업데이트:** 2026-03-09 --- @@ -22,19 +22,21 @@ SAM 시스템의 서버 및 데이터베이스 접근 권한을 관리합니다. | 구분 | 호스트 | SSH alias | 용도 | |------|--------|-----------|------| -| 운영 (prod) | 211.117.60.189 | `sam-prod` | API + React 운영 배포 | -| 개발 (dev) | - | `sam-dev` | 개발 환경 | -| CI/CD | 114.203.209.83 | `sam-cicd` | Jenkins + React 개발 배포 + DB 백업 | +| 운영 (prod) | 211.117.60.189 | `sam-prod` | API + React + MNG 운영 배포 | +| CI/CD | 110.10.147.46 | `sam-cicd` | Jenkins + Gitea + Grafana + Prometheus | +| 개발 (dev) | 114.203.209.83 | `sam-dev` | API + React + MNG 개발 환경 + Gitea + MySQL | + +> **참고:** 상세 서버 구성은 `docs/dev/deploys/ops-manual/01-server-overview.md` 참조 ### 서버 시간대 -| 설정 | prod | cicd | -|------|------|------| +| 설정 | prod | dev | +|------|------|-----| | OS timezone | Asia/Seoul (KST, +0900) | Asia/Seoul (KST, +0900) | | NTP 동기화 | 활성 | 활성 | -| PHP CLI (`/etc/php/8.4/cli/php.ini`) | Asia/Seoul | PHP 미설치 | -| PHP FPM (`/etc/php/8.4/fpm/php.ini`) | Asia/Seoul | - | -| Laravel `app.timezone` | Asia/Seoul | - | +| PHP CLI (`/etc/php/8.4/cli/php.ini`) | Asia/Seoul | Asia/Seoul | +| PHP FPM (`/etc/php/8.4/fpm/php.ini`) | Asia/Seoul | Asia/Seoul | +| Laravel `app.timezone` | Asia/Seoul | Asia/Seoul | > **주의:** PHP timezone이 UTC면 스케줄러 실행 시간이 9시간 어긋남. 반드시 Asia/Seoul로 설정할 것. @@ -54,7 +56,6 @@ SAM 시스템의 서버 및 데이터베이스 접근 권한을 관리합니다. | 계정 | 이름 | 용도 | 상태 | |------|------|------|------| | `hskwon` | 권혁성 | 서버 관리자 | 활성 | -| `pro` | 김보곤 | 서브 관리자 임시 접근용 | 평상시 **잠금** | ### sam-dev (개발서버) @@ -90,39 +91,6 @@ sudo passwd -S pro --- -## 공동 관리 구조 (cicd 서버) - -인력 변동에 대비하여 cicd 서버는 공동 관리 구조로 운영합니다. - -### develop 그룹 - -```bash -# 그룹 구성원 -$ getent group develop -develop:x:1004:hskwon,pro - -# 디렉토리 구조 -/data/ -├── backups/mysql/ # DB 백업 파일 (14일 보관) -└── scripts/ # 운영 스크립트 + .sam_backup.cnf -``` - -### 권한 설정 - -- `/data/` 전체: `develop` 그룹 소유, **setgid** 적용 -- setgid(`chmod g+s`)로 하위에 생성되는 파일/폴더도 자동으로 `develop` 그룹 상속 -- 그룹 권한 = 소유자 권한 (`chmod g=u`) -- 누가 작업하든 다른 관리자가 접근/수정 가능 - -```bash -# 권한 재설정 필요 시 -sudo chown -R hskwon:develop /data -sudo chmod -R g=u /data -sudo chmod g+s /data /data/backups /data/backups/mysql /data/scripts -``` - ---- - ## DB 계정 현황 ### sam-prod (운영서버 MySQL 8.4) @@ -130,17 +98,17 @@ sudo chmod g+s /data /data/backups /data/backups/mysql /data/scripts | 계정 | 인증 플러그인 | 접근 DB | 용도 | |------|-------------|---------|------| | `codebridge@localhost` | caching_sha2_password | sam, sam_stage, sam_stat, codebridge | 애플리케이션 (.env) | -| `hskwon@localhost` | caching_sha2_password | 전역 관리자 + sam | 서버 관리자 | -| `pro@localhost` | caching_sha2_password | sam, sam_stage, sam_stat, codebridge | 서브 관리자용 | +| `hskwon@localhost` | auth_socket | 전역 관리자 (WITH GRANT OPTION) | 서버 관리자 | +| `root@localhost` | auth_socket | 전역 | 시스템 (sudo mysql) | +| `sam_backup@110.10.147.46` | caching_sha2_password | SELECT, LOCK TABLES (sam, sam_stat) | CI/CD 백업 | ### sam-cicd (CI/CD 서버 MySQL 8.4) | 계정 | 인증 플러그인 | 접근 DB | 용도 | |------|-------------|---------|------| | `hskwon@localhost` | caching_sha2_password | 전역 관리자 | 서버 관리자 | -| `pro@localhost` | caching_sha2_password | sam, sam_stage, sam_stat, codebridge, gitea | 서브 관리자용 | -> **참고:** cicd 서버에는 `codebridge` DB 계정이 없음 (애플리케이션이 다른 계정 사용) +> **참고:** cicd 서버의 MySQL은 Gitea DB + 백업 저장 용도 ### sam-dev (개발서버 MySQL 8.4) @@ -184,41 +152,16 @@ FLUSH PRIVILEGES; --- -## DB 리플리케이션 - -운영 DB(prod)에서 cicd 서버로 실시간 리플리케이션이 구성되어 있습니다. - -| 항목 | 값 | -|------|---| -| Source (마스터) | sam-prod (211.117.60.189) | -| Replica (슬레이브) | sam-cicd | -| 리플리케이션 사용자 | `sam_backup` | -| 대상 DB | **sam, sam_stat, codebridge** | -| 지연 | 0초 (실시간) | - -```sql --- cicd에서 리플리케이션 상태 확인 -SHOW REPLICA STATUS\G - --- 확인 항목 --- Replica_IO_Running: Yes --- Replica_SQL_Running: Yes --- Seconds_Behind_Source: 0 -``` - ---- - ## DB 백업 -리플리케이션과 별도로, cicd 서버에서 매일 03:00에 mysqldump 백업을 수행합니다. +sam-cicd 서버에서 prod DB를 원격 mysqldump로 백업합니다. ### 백업 설정 | 항목 | 값 | |------|---| -| 스크립트 | `/data/scripts/backup-db.sh` | -| 인증 파일 | `/data/scripts/.sam_backup.cnf` | -| 백업 경로 | `/data/backups/mysql/` | +| 스크립트 | `/home/hskwon/scripts/backup-db.sh` | +| 백업 경로 | `/home/hskwon/backups/mysql/` | | 실행 시간 | 매일 03:00 (cron, hskwon 사용자) | | 보관 기간 | 14일 | @@ -234,11 +177,14 @@ SHOW REPLICA STATUS\G ### 백업 확인 ```bash +# cicd 서버에서 확인 +ssh sam-cicd + # 최근 백업 파일 확인 -ls -lt /data/backups/mysql/ | head -10 +ls -lt ~/backups/mysql/ | head -10 # 백업 로그 확인 -tail /data/backups/mysql/backup.log +tail ~/backups/mysql/backup.log ``` --- @@ -247,6 +193,8 @@ tail /data/backups/mysql/backup.log ### Jenkins 배포 흐름 +Jenkins는 sam-cicd(110.10.147.46)에서 운영됩니다. + | 프로젝트 | main | develop | |----------|------|---------| | **api** | Jenkins (Stage + Production) | post-update hook (Jenkins 미관여) | @@ -254,6 +202,7 @@ tail /data/backups/mysql/backup.log - 배포 SSH 계정: `hskwon` (deploy-ssh-key 크레덴셜) - Slack 알림: `#deploy_api`, `#deploy_react` +- Jenkins 웹 UI: https://ci.sam.it.kr --- @@ -263,20 +212,19 @@ tail /data/backups/mysql/backup.log 2. **임시 접근만 허용** -- 서브 관리자는 필요 시에만 OS/DB 계정 해제 3. **작업 완료 후 즉시 잠금** -- 해제 상태로 방치하지 않음 4. **DB 접근은 SSH 터널 경유** -- 외부에서 MySQL 직접 접근 불가 (localhost 바인딩) -5. **공동 관리 대비** -- cicd `/data/`는 develop 그룹으로 관리, 인력 변동 대비 -6. **이중 백업** -- 리플리케이션(실시간) + mysqldump(매일 03:00) 병행 +5. **이중 백업** -- cicd에서 mysqldump(매일 03:00) 수행 --- ## 참고 +- 서버 운영 매뉴얼: [../dev/deploys/ops-manual/README.md](../dev/deploys/ops-manual/README.md) - API 보안 가이드: [security-policy.md](./security-policy.md) - Docker 설정: [docker-setup.md](./docker-setup.md) -- 원격 근무 설정: [remote-work-setup.md](./remote-work-setup.md) - Jenkins 설정: [../dev/guides/jenkins-setup-guide.md](../dev/guides/jenkins-setup-guide.md) --- **작성일:** 2026-03-05 -**버전:** 1.1 +**버전:** 1.2 **담당자:** SAM Infrastructure Team \ No newline at end of file