Files
sam-docs/dev/dev_plans/document-snapshot-architecture-plan.md
권혁성 5bfc89afa7 docs: [제품검사] FQC 문서 시스템 계획 + 스냅샷 Lazy Snapshot 반영
- fqc-document-system-plan.md: FormRequest 상태 수정, Phase 2.4 Lazy Snapshot 확정, 참고 파일 추가
- document-snapshot-architecture-plan.md: Lazy Snapshot 캡처 원칙 추가
- server-access-management.md 신규
- README.md 수정

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 21:09:57 +09:00

16 KiB

문서 스냅샷 아키텍처 계획

작성일: 2026-03-06 목적: 문서 보기/인쇄 시 HTML 스냅샷 기반 출력으로 전환 (B안 + 구조화 데이터 병행) 상태: 코드 완료 (검증 대기) 영향 범위: API(저장), React(캡처/전송), MNG(출력)


현재 진행 상태

항목 내용
마지막 완료 작업 Phase 2 전면 보정: API 누락 수정, 오프스크린 렌더링 적용, readOnly 자동 캡처 제거
다음 작업 Phase 4: 브라우저 검증 + 기존 partial 정리
진행률 13/13 (100% 코드 완료, 검증 대기)
마지막 업데이트 2026-03-06

1. 개요

1.1 배경

현재 MNG 문서 보기(show.blade.php)는 문서 양식별로 전용 blade partial을 만들어 렌더링한다:

  • bending-inspection-data.blade.php (절곡 중간검사)
  • bending-worklog.blade.php (절곡 작업일지)

이 방식의 문제:

  1. 확장 불가: 회사마다 다양한 양식이 존재 → 양식마다 blade 파일 생성 불가
  2. 스냅샷 미보장: 하드코딩된 제품 목록/도면치수가 정책 변경 시 과거 문서를 깨뜨림
  3. 이중 렌더링: React와 MNG에서 동일 문서를 각각 렌더링 → 불일치 발생

1.2 목표 아키텍처

[React] 문서 저장 시
├── 구조화 데이터 저장 (기존 유지)
│   ├── document_data (EAV 플랫)
│   └── work_order_items.options.inspection_data (JSON 스냅샷)
└── rendered_html 저장 (신규)
    └── React가 렌더링한 HTML을 캡처 → documents.rendered_html에 저장

[MNG] 문서 보기 시
├── rendered_html 있으면 → 그대로 출력 (렌더링 로직 0)
└── rendered_html 없으면 → 기존 동적 렌더링 fallback

1.3 핵심 원칙

1. 하나의 view 파일로 모든 문서를 보기 (문서 양식별 blade 파일 금지)
2. rendered_html이 있으면 무조건 그것을 사용 (완전한 스냅샷)
3. 구조화 데이터는 편집/검색/통계용으로 병행 유지
4. React에서만 문서 렌더링 책임 → MNG는 출력만 담당
5. Lazy Snapshot: 조회 시 rendered_html 없으면 자동 캡처 → 저장 (점진적 스냅샷 전환)

1.4 변경 승인 정책

분류 예시 승인
즉시 가능 blade 템플릿 수정, 기존 partial 정리 불필요
컨펌 필요 API 저장 로직 변경, React 저장 흐름 변경 필수
금지 documents 테이블 구조 변경 (이미 rendered_html 존재) 불필요

2. 현황 분석

2.1 DB 현황

documents 테이블에 이미 rendered_html (LONGTEXT, nullable) 컬럼이 존재:

  • 마이그레이션: api/database/migrations/2026_02_28_100001_add_block_data_to_documents.php
  • 현재 값: 모든 문서에서 NULL (사용 안 됨)
  • DB 변경 불필요

2.2 React 현황 (구현 완료)

캡처 원칙 A: 입력 시 저장 (Active Capture)

입력 화면에서 저장할 때 해당 데이터의 "문서 뷰"를 캡처. 보기(readOnly)에서는 캡처하지 않음.

Save Path 파일 방식 캡처 대상
작업일지 저장 WorkLogModal.tsx contentWrapperRef.innerHTML 작업일지 문서 뷰
검사성적서 저장 (edit) InspectionReportModal.tsx contentWrapperRef.innerHTML 검사 성적서 문서 뷰
수입검사 저장 ImportInspectionInputModal.tsx 오프스크린 렌더링 (captureRenderedHtml) 수입검사 성적서 문서 (ImportInspectionDocument)
WorkerScreen 인라인 검사 저장 index.tsx 미캡처 (데이터만 저장) 성적서 모달에서 저장 시 캡처

WorkerScreen 인라인 저장: 검사 입력 시점에 성적서 문서가 렌더링되지 않으므로 rendered_html 미포함. 이후 InspectionReportModal을 edit 모드로 열어 저장하면 캡처됨. 향후 오프스크린 렌더링으로 확장 가능 (템플릿 로딩 등 async 의존성 해결 필요).

캡처 원칙 B: 조회 시 자동 캡처 (Lazy Snapshot)

문서 조회(view/readOnly) 시 rendered_html이 없으면 자동 캡처하여 백그라운드 저장.

문서 View 시
├── rendered_html 있음 → 그대로 표시 (기존)
└── rendered_html 없음 → 동적 렌더링 완료 후 캡처 → API로 rendered_html 저장
                          (다음 조회부터는 스냅샷 사용)

적용 대상:

  • readonly 문서 (제품검사 요청서 등 — 입력 없이 자동 생성되는 문서)
  • 마이그레이션 이전 기존 데이터 (rendered_html이 NULL인 과거 문서)
  • WorkerScreen 인라인 저장 후 아직 모달에서 저장하지 않은 문서

구현 방식:

// 문서 표시 컴포넌트에서 (DocumentViewer, Modal 등)
useEffect(() => {
  if (document && !document.rendered_html && isContentRendered) {
    const html = contentWrapperRef.current?.innerHTML
              || await captureRenderedHtml(DocumentComponent, props);
    patchDocumentRenderedHtml(document.id, html); // 백그라운드 저장
  }
}, [document, isContentRendered]);

고려사항:

  • 사용자 UX 영향 없음 (백그라운드 비동기 저장)
  • 조회 권한만 있는 사용자도 트리거 가능해야 함
  • 동시 접속 시 중복 저장 가능 → 같은 HTML이므로 실질적 문제 없음
  • 캡처 타이밍: template 로드 + 데이터 바인딩 완료 후 (isContentRendered 판단 필요)

2.3 API 현황 (구현 완료)

  • Document 모델 $fillablerendered_html 포함
  • DocumentService store/update에서 rendered_html 저장
  • DocumentService upsert에서 rendered_html 전달 (수입검사 경로)
  • StoreRequest/UpdateRequestrendered_html nullable string 검증
  • UpsertRequestrendered_html nullable string 검증

2.4 MNG 현황 (구현 완료)

  • show.blade.php: rendered_html 우선 출력, 없으면 기존 동적 렌더링 fallback
  • print.blade.php: 동일 패턴 적용
  • 전용 partial 파일 (삭제 대기):
    • partials/bending-inspection-data.blade.php
    • partials/bending-worklog.blade.php

3. 작업 범위

Phase 0: 사전 정리

# 작업 항목 상태 비고
0.1 API Document 모델 $fillable 확인 및 rendered_html 추가
0.2 기존 절곡 전용 partial 파일 정리 방침 결정 rendered_html 전환 후 삭제

Phase 1: API - rendered_html 저장 지원

# 작업 항목 상태 비고
1.1 Document 모델 $fillable에 rendered_html 추가
1.2 DocumentService store/update에서 rendered_html 저장
1.3 StoreRequest/UpdateRequest에 rendered_html 검증 추가 nullable, string
1.4 WorkOrderService inspection/worklog에 rendered_html 전달 create + update 모두

Phase 2: React - HTML 캡처 및 전송

# 작업 항목 상태 비고
2.1 오프스크린 렌더링 유틸리티 생성 captureRenderedHtml()flushSync + createRoot
2.2 InspectionReportModal 저장 시 rendered_html 포함 전송 contentWrapperRef.innerHTML 캡처
2.3 작업일지 저장 시 rendered_html 포함 전송 contentWrapperRef.innerHTML 캡처
2.4 ImportInspectionInputModal 수입검사 저장 시 rendered_html 오프스크린 성적서 문서 렌더링
2.5 ReceivingManagement/actions saveInspectionData 파라미터 추가 rendered_html → /documents/upsert 전달
2.6 API UpsertRequest에 rendered_html 검증 추가 nullable string

Phase 3: MNG - 스냅샷 출력

# 작업 항목 상태 비고
3.1 show.blade.php에 rendered_html 우선 출력 로직 추가
3.2 기존 전용 partial 파일 fallback으로 유지 (과도기)
3.3 print.blade.php에도 rendered_html 출력 적용 스냅샷 우선, 레거시 fallback

Phase 4: 검증 및 정리

# 작업 항목 상태 비고
4.1 브라우저 검증 (MNG 보기/인쇄)
4.2 기존 전용 partial 파일 삭제 rendered_html 전환 완료 후

4. 상세 작업 내용

4.1 HTML 캡처 방식 (Phase 2.1)

React에서 문서 컨텐츠 영역의 DOM을 캡처할 때 고려사항:

방법 A: innerHTML 직접 추출 + CSS 인라인화

// 문서 컨텐츠 영역에 ref 부여
const contentRef = useRef<HTMLDivElement>(null);

// 저장 시 HTML 추출
const captureHtml = () => {
  const el = contentRef.current;
  if (!el) return '';

  // Tailwind 클래스 → 인라인 스타일 변환 (또는 스타일시트 포함)
  // 방법 1: 계산된 스타일을 인라인으로
  // 방법 2: 필요한 Tailwind CSS를 <style> 태그로 포함
  return el.innerHTML;
};

방법 B: 자체 CSS 포함 완전한 HTML 조각

<div class="document-snapshot">
  <style>/* 필요한 스타일만 추출 */</style>
  <!-- 문서 컨텐츠 -->
</div>

권장: 방법 B — MNG에서 Tailwind가 로드되어 있으므로, Tailwind 클래스를 그대로 사용하되 MNG에 없는 커스텀 스타일만 <style> 태그로 포함. MNG도 Tailwind를 사용하므로 대부분의 클래스가 호환됨.

4.2 MNG 출력 구조 (Phase 3.1)

{{-- show.blade.php --}}
@if($document->rendered_html)
    {{-- 스냅샷 모드: React가 생성한 HTML 그대로 출력 --}}
    <div class="document-snapshot-container">
        {!! $document->rendered_html !!}
    </div>
@else
    {{-- 동적 모드: 기존 템플릿 기반 렌더링 (fallback) --}}
    @include('documents.partials.dynamic-render')
@endif

4.3 스타일 호환성 전략

항목 React (Tailwind) MNG (Tailwind) 호환성
기본 클래스 px-2, py-1, border 등 동일 완전 호환
반응형 sm:, md:, lg: inline style 정책 주의 필요
커스텀 컴포넌트 shadcn/ui 없음 <style> 포함 필요

결론: React에서 문서 영역은 순수 HTML+Tailwind로 렌더링 (shadcn 컴포넌트 미사용) → MNG 호환성 높음. 단, @media 쿼리나 MNG에서 빌드되지 않은 Tailwind 클래스가 있을 수 있으므로 필요 시 인라인 스타일 변환.

4.4 XSS 보안 고려

{!! !!} (unescaped output) 사용 시 XSS 위험:

  • rendered_html자체 시스템(React)에서만 생성 → 외부 입력 아님
  • API 저장 시 sanitize 처리 권장 (script 태그, on* 이벤트 제거)
  • 또는 CSP(Content Security Policy)로 인라인 스크립트 차단

5. 참고 파일

React (수정 대상)

  • react/src/components/production/WorkOrders/documents/InspectionReportModal.tsx — 저장 흐름 (contentWrapperRef)
  • react/src/components/production/WorkOrders/documents/BendingInspectionContent.tsx — 검사 문서 렌더링
  • react/src/components/production/WorkOrders/documents/BendingWorkLogContent.tsx — 작업일지 렌더링
  • react/src/components/production/WorkOrders/documents/bending/ — 절곡 섹션 컴포넌트들
  • react/src/components/production/WorkerScreen/WorkLogModal.tsx — 작업일지 저장 시 캡처
  • react/src/components/material/ReceivingManagement/ImportInspectionInputModal.tsx — 수입검사 저장 시 오프스크린 캡처
  • react/src/components/material/ReceivingManagement/actions.ts — saveInspectionData rendered_html 전달
  • react/src/lib/utils/capture-rendered-html.tsx — 오프스크린 렌더링 유틸리티 (신규)

API (수정 대상)

  • api/app/Models/Documents/Document.php — $fillable
  • api/app/Services/DocumentService.php — store/update/upsert
  • api/app/Http/Requests/Documents/StoreRequest.php — 검증
  • api/app/Http/Requests/Documents/UpdateRequest.php — 검증
  • api/app/Http/Requests/Document/UpsertRequest.php — 검증 (수입검사 경로)

MNG (수정 대상)

  • mng/resources/views/documents/show.blade.php — 메인 보기
  • mng/resources/views/documents/print.blade.php — 인쇄
  • mng/resources/views/documents/partials/bending-inspection-data.blade.php — 삭제 예정
  • mng/resources/views/documents/partials/bending-worklog.blade.php — 삭제 예정
  • mng/app/Http/Controllers/DocumentController.php — show()

DB

  • api/database/migrations/2026_02_28_100001_add_block_data_to_documents.php — rendered_html 컬럼 (이미 존재)

문서

  • docs/features/documents/README.md — 문서관리 시스템
  • docs/system/database/documents.md — DB 스키마

6. 의존성 및 순서

Phase 0 (사전 정리)
    ↓
Phase 1 (API: rendered_html 저장)
    ↓
Phase 2 (React: HTML 캡처 + 전송)  ← Phase 1 완료 필요
    ↓
Phase 3 (MNG: 스냅샷 출력)  ← Phase 2 완료 후 데이터 존재
    ↓
Phase 4 (검증 + 정리)  ← 모든 Phase 완료 후

Phase 1과 Phase 3의 MNG 코드 수정은 병렬 가능 (fallback 유지). 단, 실제 데이터가 있어야 검증 가능하므로 Phase 2 완료 후 통합 검증.


7. 리스크 및 대응

리스크 영향 대응
HTML 용량 문서당 50-200KB, 수만 건 시 GB LONGTEXT 이미 사용, 필요 시 gzip 압축
Tailwind 클래스 불일치 MNG에서 일부 스타일 깨짐 인라인 스타일 변환 또는 MNG Tailwind 빌드에 포함
XSS rendered_html에 악성 코드 API에서 sanitize, CSP 정책
과도기 fallback rendered_html 없는 기존 문서 기존 동적 렌더링 유지
React 미경유 문서 MNG에서만 생성된 문서 MNG 저장 시에도 rendered_html 생성 검토

8. 변경 이력

날짜 항목 변경 내용 파일 승인
2026-03-06 - 계획 문서 작성 - -
2026-03-06 Phase 0~3 Phase 0~3 전체 구현 완료 API/React/MNG 다수
2026-03-06 Phase 2.4~2.6 모든 저장 경로에 rendered_html 추가 InspectionReportModal, ImportInspectionInputModal, actions.ts
2026-03-06 Phase 2 보정 API UpsertRequest rendered_html 누락 수정, DocumentService upsert() rendered_html 전달 추가 UpsertRequest.php, DocumentService.php
2026-03-06 Phase 2 보정 ImportInspection: 입력폼 캡처 → 오프스크린 성적서 렌더링으로 변경 ImportInspectionInputModal.tsx, capture-rendered-html.tsx
2026-03-06 Phase 2 보정 InspectionReportModal readOnly 자동 캡처 useEffect 제거 InspectionReportModal.tsx
2026-03-06 원칙 확장 Lazy Snapshot 패턴 추가 — 조회 시 rendered_html 없으면 자동 캡처/저장 아키텍처 원칙 -

9. 검증 결과

작업 완료 후 이 섹션에 검증 결과 추가

9.1 성공 기준

기준 달성 비고
React 저장 시 rendered_html이 documents 테이블에 저장
mng.sam.kr/documents/36 에서 rendered_html로 문서 출력
mng.sam.kr/documents/39 에서 rendered_html로 문서 출력
rendered_html 없는 기존 문서가 기존대로 렌더링
인쇄 시에도 스냅샷 기반 출력
양식별 전용 blade 파일 없이 동작

10. 자기완결성 점검 결과

10.1 체크리스트 검증

# 검증 항목 상태 비고
1 작업 목적이 명확한가? 스냅샷 기반 문서 출력
2 성공 기준이 정의되어 있는가? 9.1 참조
3 작업 범위가 구체적인가? 5 Phase, 15개 작업
4 의존성이 명시되어 있는가? 6. 의존성 참조
5 참고 파일 경로가 정확한가? 5. 참고 파일
6 단계별 절차가 실행 가능한가? 3. 작업 범위
7 검증 방법이 명시되어 있는가? 9.1 성공 기준
8 모호한 표현이 없는가?

10.2 새 세션 시뮬레이션 테스트

질문 답변 가능 참조 섹션
Q1. 이 작업의 목적은 무엇인가? 1.1 배경
Q2. 어디서부터 시작해야 하는가? Phase 0 → 1 → 2 → 3
Q3. 어떤 파일을 수정해야 하는가? 5. 참고 파일
Q4. 작업 완료 확인 방법은? 9.1 성공 기준
Q5. 막혔을 때 참고 문서는? 5. 참고 파일, docs/

이 문서는 /plan 스킬로 생성되었습니다.