Files
sam-react-prod/claudedocs/guides/[PLAN-2025-01-21] document-system-integration.md
유병철 f3b07ac875 chore(WEB): claudedocs 디렉토리 도메인별 재구조화
- 루트 문서 30개를 도메인별 하위 폴더로 이동
- accounting/, architecture/, dev/, guides/, security/ 등 카테고리 분류
- archive/ 폴더에 QA 스크린샷 이동
- _index.md 문서 맵 업데이트

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 09:35:22 +09:00

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 문제점

  1. 중복 코드: 모달 래퍼, 버튼, 프린트 로직이 각 파일에 반복
  2. 기능 불일치: 줌 기능이 QMS에만 있음, 다른 문서에는 없음
  3. 스타일 불일치: 버튼 위치, 스타일이 문서마다 다름
  4. 유지보수 어려움: 17개 파일을 개별 관리

2. 설계 방향

2.1 핵심 원칙

  1. Config + 프리셋 패턴: 프로젝트의 IntegratedDetailTemplate과 일관성 유지
  2. Shell/Content 분리: 뷰어 기능과 문서 내용 완전 분리
  3. 블록 기반 렌더링: 향후 문서 빌더 대비
  4. 점진적 마이그레이션: 기존 컴포넌트 유지하면서 전환

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 훅으로 추출하여 재사용.