- 루트 문서 30개를 도메인별 하위 폴더로 이동 - accounting/, architecture/, dev/, guides/, security/ 등 카테고리 분류 - archive/ 폴더에 QA 스크린샷 이동 - _index.md 문서 맵 업데이트 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
14 KiB
14 KiB
문서 시스템 통합 계획
작성일: 2025-01-21 상태: 계획 수립
1. 현황 분석
1.1 발견된 문서 컴포넌트 (17개)
| 카테고리 | 문서 수 | 파일 위치 |
|---|---|---|
| 결재함 (Approval) | 3개 | src/components/approval/DocumentDetail/ |
| 견적/발주 (Quotes) | 2개 | src/components/quotes/ |
| 수주/거래 (Orders) | 3개 | src/components/orders/documents/ |
| 건축사업 (Construction) | 3개 | src/components/business/construction/*/modals/ |
| 품질관리 (QMS) | 6개 | src/app/.../quality/qms/components/documents/ |
1.2 문서 상세 목록
결재함 (approval/)
| 문서 | 파일 | 라인 |
|---|---|---|
| 품의서 | ProposalDocument.tsx | 114 |
| 지출결의서 | ExpenseReportDocument.tsx | 135 |
| 지출예상내역서 | ExpenseEstimateDocument.tsx | 127 |
견적/발주 (quotes/)
| 문서 | 파일 | 라인 |
|---|---|---|
| 견적서 | QuoteDocument.tsx | 464 |
| 발주서 | PurchaseOrderDocument.tsx | 425 |
수주/거래 (orders/documents/)
| 문서 | 파일 | 라인 |
|---|---|---|
| 계약서 | ContractDocument.tsx | 236 |
| 거래명세서 | TransactionDocument.tsx | 202 |
| 발주서 | PurchaseOrderDocument.tsx | 202 |
건축사업 (construction/*/modals/)
| 문서 | 파일 | 라인 |
|---|---|---|
| 계약서 | ContractDocumentModal.tsx | 104 |
| 인수인계보고서 | HandoverReportDocumentModal.tsx | 308 |
| 발주서 | OrderDocumentModal.tsx | 354 |
품질관리 (quality/qms/components/documents/)
| 문서 | 파일 | 라인 |
|---|---|---|
| 제품검사성적서 | ProductInspectionDocument.tsx | 289 |
| 스크린중간검사 | ScreenInspectionDocument.tsx | 309 |
| 슬랫중간검사 | SlatInspectionDocument.tsx | 286 |
| 절곡중간검사 | BendingInspectionDocument.tsx | 348 |
| 금속프레임검사 | JointbarInspectionDocument.tsx | 298 |
| 수입검사성적서 | ImportInspectionDocument.tsx | 418 |
1.3 기능 현황
| 컴포넌트 | 줌 | 드래그 | 인쇄 | 다운로드 | 수정 | 삭제 | 상신 |
|---|---|---|---|---|---|---|---|
| InspectionModal (QMS) | ✅ | ✅ | ✅ | UI만 | - | - | - |
| ContractDocumentModal | - | - | ✅ | - | ✅ | - | ✅ |
| OrderDocumentModal | - | - | ✅ | - | ✅ | ✅ | - |
| HandoverReportModal | - | - | ✅ | - | ✅ | ✅ | - |
| 결재함 문서 3개 | - | - | - | - | - | - | - |
| 견적/발주 문서 | - | - | @media print | - | - | - | - |
1.4 문제점
- 중복 코드: 모달 래퍼, 버튼, 프린트 로직이 각 파일에 반복
- 기능 불일치: 줌 기능이 QMS에만 있음, 다른 문서에는 없음
- 스타일 불일치: 버튼 위치, 스타일이 문서마다 다름
- 유지보수 어려움: 17개 파일을 개별 관리
2. 설계 방향
2.1 핵심 원칙
- Config + 프리셋 패턴: 프로젝트의 IntegratedDetailTemplate과 일관성 유지
- Shell/Content 분리: 뷰어 기능과 문서 내용 완전 분리
- 블록 기반 렌더링: 향후 문서 빌더 대비
- 점진적 마이그레이션: 기존 컴포넌트 유지하면서 전환
2.2 아키텍처 개요
┌─────────────────────────────────────────────────────────┐
│ DocumentViewer (Shell) │
│ - 줌/드래그 기능 │
│ - 액션 버튼 (인쇄, 다운로드, 수정, 삭제, 상신) │
├─────────────────────────────────────────────────────────┤
│ DocumentRenderer (Content) │
│ ├── 정적 모드: 기존 컴포넌트 (component prop) │
│ └── 동적 모드: 블록 배열 렌더링 (blocks prop) - 빌더용 │
└─────────────────────────────────────────────────────────┘
3. 상세 설계
3.1 프리셋 시스템
// src/components/document-system/presets/index.ts
export const DOCUMENT_PRESETS = {
// QMS 검사 문서용
inspection: {
features: { zoom: true, drag: true, print: true, download: true },
actions: ['print', 'download'],
},
// 건설 프로젝트용 (CRUD)
construction: {
features: { zoom: true, drag: true, print: true, download: false },
actions: ['edit', 'delete', 'print'],
},
// 결재 문서용
approval: {
features: { zoom: true, drag: true, print: true, download: false },
actions: ['edit', 'submit', 'print'],
},
// 조회 전용
readonly: {
features: { zoom: true, drag: true, print: true, download: false },
actions: ['print'],
},
};
3.2 Config 인터페이스
// src/components/document-system/types.ts
interface DocumentConfig {
// 메타 정보
type: string;
title: string;
preset?: keyof typeof DOCUMENT_PRESETS;
// 뷰어 설정 (Shell) - 프리셋 오버라이드 가능
features?: {
zoom?: boolean;
drag?: boolean;
print?: boolean;
download?: boolean;
};
actions?: ActionType[];
// 콘텐츠 설정 (Content) - 둘 중 하나 사용
component?: React.ComponentType<any>; // Phase 1: 정적 모드
blocks?: DocumentBlock[]; // Phase 2: 동적 모드 (빌더)
}
type ActionType = 'print' | 'download' | 'edit' | 'delete' | 'submit';
3.3 블록 시스템 (문서 빌더 대비)
// src/components/document-system/types.ts
type DocumentBlock =
| HeaderBlock
| InfoTableBlock
| ItemTableBlock
| ApprovalLineBlock
| SignatureBlock
| TextSectionBlock
| ImageGridBlock
| CustomBlock;
interface HeaderBlock {
type: 'header';
title: string;
logo?: boolean;
showApprovalLine?: boolean;
approvalPositions?: string[]; // ['작성', '검토', '승인']
}
interface InfoTableBlock {
type: 'info-table';
columns: 2 | 3 | 4;
fields: {
label: string;
key: string;
colSpan?: number;
}[];
}
interface ItemTableBlock {
type: 'item-table';
columns: {
key: string;
label: string;
width?: string;
align?: 'left' | 'center' | 'right';
}[];
showTotal?: boolean;
totalFields?: string[];
}
interface ApprovalLineBlock {
type: 'approval-line';
positions: string[]; // ['담당', '부서장', '결재']
layout: 'horizontal' | 'vertical';
}
interface SignatureBlock {
type: 'signature';
positions: { label: string; name?: string }[];
}
interface TextSectionBlock {
type: 'text-section';
title?: string;
content: string;
style?: 'normal' | 'highlight' | 'note';
}
interface ImageGridBlock {
type: 'image-grid';
columns: 2 | 3 | 4;
images: { src: string; caption?: string }[];
}
interface CustomBlock {
type: 'custom';
componentKey: string; // 블록 레지스트리에서 찾음
props?: Record<string, any>;
}
3.4 DocumentViewer Props
interface DocumentViewerProps {
// Config 기반 (권장)
config?: DocumentConfig;
// 또는 개별 props (하위 호환)
title?: string;
preset?: keyof typeof DOCUMENT_PRESETS;
// 데이터
data?: any;
// 액션 핸들러
onPrint?: () => void;
onDownload?: () => void;
onEdit?: () => void;
onDelete?: () => void;
onSubmit?: () => void;
// 모달 제어
open?: boolean;
onOpenChange?: (open: boolean) => void;
// 정적 모드용
children?: React.ReactNode;
}
4. 폴더 구조
src/components/document-system/
├── index.ts # 공개 API
├── types.ts # 타입 정의
│
├── viewer/ # 뷰어 (Shell)
│ ├── DocumentViewer.tsx # 메인 컴포넌트
│ ├── DocumentToolbar.tsx # 툴바 (줌 + 액션 버튼)
│ ├── DocumentContent.tsx # 콘텐츠 영역 (줌/드래그)
│ └── hooks/
│ ├── useZoom.ts # 줌 로직
│ ├── useDrag.ts # 드래그 로직
│ └── usePrint.ts # 인쇄 로직
│
├── renderer/ # 렌더러 (Content)
│ ├── DocumentRenderer.tsx # 정적/동적 분기
│ ├── StaticRenderer.tsx # 정적 모드 (컴포넌트 렌더링)
│ └── BlockRenderer.tsx # 동적 모드 (블록 렌더링)
│
├── blocks/ # 블록 컴포넌트들
│ ├── index.ts # 블록 레지스트리
│ ├── HeaderBlock.tsx
│ ├── InfoTableBlock.tsx
│ ├── ItemTableBlock.tsx
│ ├── ApprovalLineBlock.tsx
│ ├── SignatureBlock.tsx
│ ├── TextSectionBlock.tsx
│ └── ImageGridBlock.tsx
│
├── presets/ # 프리셋 정의
│ └── index.ts
│
└── configs/ # 문서별 Config (정적)
├── index.ts # Config 레지스트리
├── qms/
│ ├── importInspection.config.ts
│ ├── productInspection.config.ts
│ └── ...
├── construction/
│ ├── contract.config.ts
│ ├── handoverReport.config.ts
│ └── order.config.ts
├── approval/
│ ├── proposal.config.ts
│ ├── expenseReport.config.ts
│ └── expenseEstimate.config.ts
└── orders/
├── contract.config.ts
├── transaction.config.ts
└── purchaseOrder.config.ts
5. 사용 예시
5.1 Phase 1: 기존 컴포넌트 사용 (정적 모드)
// configs/qms/importInspection.config.ts
import { ImportInspectionDocument } from '@/app/.../quality/qms/components/documents';
export const importInspectionConfig: DocumentConfig = {
type: 'import-inspection',
title: '수입검사 성적서',
preset: 'inspection',
component: ImportInspectionDocument,
};
// 페이지에서 사용
import { DocumentViewer } from '@/components/document-system';
import { importInspectionConfig } from '@/components/document-system/configs';
<DocumentViewer
config={importInspectionConfig}
data={inspectionData}
open={isOpen}
onOpenChange={setIsOpen}
/>
5.2 Phase 2: 블록 기반 (동적 모드 - 빌더용)
// DB에서 가져온 템플릿
const customTemplate: DocumentConfig = {
type: 'custom-report',
title: '커스텀 검사 보고서',
preset: 'inspection',
blocks: [
{
type: 'header',
title: '커스텀 검사 보고서',
logo: true,
showApprovalLine: true,
approvalPositions: ['작성', '검토', '승인']
},
{
type: 'info-table',
columns: 2,
fields: [
{ label: '품명', key: 'productName' },
{ label: '규격', key: 'specification' },
{ label: '검사일', key: 'inspectionDate' },
{ label: '검사자', key: 'inspector' },
]
},
{
type: 'item-table',
columns: [
{ key: 'no', label: 'No', width: '50px', align: 'center' },
{ key: 'item', label: '검사항목', align: 'left' },
{ key: 'standard', label: '기준', align: 'center' },
{ key: 'result', label: '결과', align: 'center' },
{ key: 'judgment', label: '판정', width: '80px', align: 'center' },
],
showTotal: false
},
{
type: 'text-section',
title: '특기사항',
content: '',
style: 'note'
},
{
type: 'signature',
positions: [
{ label: '검사자', name: '' },
{ label: '확인자', name: '' },
]
},
],
};
<DocumentViewer config={customTemplate} data={reportData} />
6. 마이그레이션 전략
Phase 1: 공통 시스템 구축 (1주)
- document-system 폴더 구조 생성
- types.ts 작성
- DocumentViewer (Shell) 구현 - InspectionModal 기반
- useZoom, useDrag 훅 추출
- 프리셋 정의
Phase 2: QMS 문서 마이그레이션 (3일)
- QMS 문서 6개 Config 작성
- InspectionModal → DocumentViewer 전환
- 기존 문서 컴포넌트는 그대로 유지 (component prop)
Phase 3: 건설/결재함 문서 마이그레이션 (3일)
- 건설사업 문서 3개 Config 작성
- 결재함 문서 3개 Config 작성
- 견적/발주 문서 5개 Config 작성
Phase 4: 블록 시스템 구축 (1주)
- 기본 블록 컴포넌트 구현 (Header, InfoTable, ItemTable 등)
- BlockRenderer 구현
- 블록 레지스트리 구축
Phase 5: 문서 빌더 준비 (향후)
- 블록 편집 UI
- 템플릿 저장/불러오기 API
- 미리보기 기능
7. 예상 효과
| 항목 | Before | After |
|---|---|---|
| 총 코드량 | ~4,100줄 (17개 파일) | ~2,500줄 + 공통 시스템 |
| 새 문서 추가 | 200~400줄 | Config만 작성 (20~50줄) |
| 줌 기능 | QMS만 | 모든 문서 |
| UI 일관성 | 불일치 | 통일 |
| 빌더 확장성 | 불가능 | 블록 기반 지원 |
8. 참고: 기존 InspectionModal 구조
현재 줌/드래그 기능이 구현된 InspectionModal.tsx (587줄) 참고:
// 줌 레벨
const ZOOM_LEVELS = [50, 75, 100, 125, 150, 200];
const MIN_ZOOM = 50;
const MAX_ZOOM = 200;
// 상태
const [zoom, setZoom] = useState(100);
const [isDragging, setIsDragging] = useState(false);
const [position, setPosition] = useState({ x: 0, y: 0 });
// 핵심 함수
handleZoomIn() // 확대
handleZoomOut() // 축소
handleZoomReset() // 맞춤 (100%)
handleMouseDown/Move/Up() // 드래그
handleTouchStart/Move/End() // 터치 드래그
이 로직을 useZoom, useDrag 훅으로 추출하여 재사용.