# 리포트 컴포넌트화 (Phase 3 확장) — 실행 계획서

> **작성일**: 2026-03-10 | **최종 업데이트**: 2026-03-10 (v5)
> **목표**: 리포트 디자이너에서 만든 리포트를 화면관리의 V2 컴포넌트(`v2-report-viewer`)에서 **reportId를 직접 지정**하여 배치하고, 인라인 또는 모달로 렌더링하는 것.

### 참조 문서

| 순서 | 문서 | 경로 | 반영 내용 |
|------|------|------|----------|
| 1 | V2 컴포넌트 분석 가이드 | `docs/V2_컴포넌트_분석_가이드.md` | 파일 구조, `v2-` 접두사, Definition 네이밍 |
| 2 | V2 컴포넌트 연동 가이드 | `docs/V2_컴포넌트_연동_가이드.md` | ScreenContext, V2 이벤트 시스템, formData 공유 |
| 3 | 화면개발 표준 가이드 | `docs/screen-implementation-guide/화면개발_표준_가이드.md` | V2 컴포넌트 목록, screen_layouts_v2 저장 구조 |
| 4 | CLAUDE.md | `CLAUDE.md` | 네이밍 규칙, 표준 파일 구조, 코딩 규칙 |

---

## 완성 후 동작 플로우

### 플로우 A: 관리자가 화면에 리포트를 배치하는 과정 (설정 시점)

```
[1] 관리자가 화면 디자이너에 접속
    URL: /admin/screenMng/screenMngList
    → 화면 목록에서 "견적 관리" 화면을 더블클릭
    → 화면 디자이너 진입 (URL 변화 없음, ScreenDesigner.tsx 렌더링)

[2] 좌측 컴포넌트 패널에서 "리포트 뷰어" (v2-report-viewer)를 캔버스에 드래그&드롭
    ┌──────────────────────────────────────────────────────────────────┐
    │  [좌측 패널]          [캔버스]                    [우측 패널]     │
    │                                                                  │
    │  ▸ 입력              ┌────────────────────────┐                  │
    │  ▸ 버튼              │  v2-input: 주문번호     │                  │
    │  ▸ 테이블            ├────────────────────────┤                  │
    │  ▸ 표시              │  v2-table-list          │                  │
    │    ├ 리포트 뷰어 ◀   │  (주문 목록 테이블)      │                  │
    │    ├ 텍스트           ├────────────────────────┤                  │
    │    └ ...             │  ┌──────────────────┐  │                  │
    │                      │  │ 📄 리포트 (뷰어)  │  │  ← 방금 배치     │
    │                      │  │                  │  │                  │
    │                      │  └──────────────────┘  │                  │
    │                      └────────────────────────┘                  │
    └──────────────────────────────────────────────────────────────────┘

[3] 배치한 리포트 뷰어 컴포넌트를 클릭 → 우측 설정 패널이 열림
    ┌─────────────────────────────────────────────┐
    │  리포트 뷰어 설정                            │
    │                                             │
    │  컴포넌트 제목                               │
    │  [견적서 미리보기]  ___________________      │
    │                                             │
    │  리포트 선택                                 │
    │  ┌─────────────────────────────────────┐    │
    │  │ 리포트를 선택해주세요        [선택] │    │
    │  └─────────────────────────────────────┘    │
    │                                             │
    │  표시 모드                                   │
    │  [모달 (클릭 시 팝업)                  ▼]    │
    │                                             │
    │  파라미터 매핑                               │
    │  매핑 없음 — 폼 데이터가 자동 주입됩니다     │
    │                                   [+ 추가]  │
    └─────────────────────────────────────────────┘

[4] "선택" 버튼 클릭 → 리포트 선택 모달이 열림
    ┌──────────────────────────────────────────────────────┐
    │  리포트 선택                                    [×]  │
    │  ┌──────────────────────────────────────────────┐    │
    │  │ 🔍 견적...                                   │    │
    │  └──────────────────────────────────────────────┘    │
    │                                                      │
    │  ┌──────┬──────────────┬──────────┬─────────────┐    │
    │  │  ID  │ 리포트명      │ 유형     │ 사용여부     │    │
    │  ├──────┼──────────────┼──────────┼─────────────┤    │
    │  │  12  │ ▶ 견적서 ◀   │ 견적     │ Y           │ ← 클릭!
    │  │  15  │ 발주서        │ 발주     │ Y           │    │
    │  │  18  │ 검수 보고서   │ 검사     │ Y           │    │
    │  └──────┴──────────────┴──────────┴─────────────┘    │
    │                                                      │
    │  * 리포트 관리에서 만든 리포트 목록이 표시됩니다       │
    │  * 리포트 관리 URL: /admin/screenMng/reportList       │
    └──────────────────────────────────────────────────────┘

[5] "견적서" 행 클릭 → 모달 닫히고 설정 패널에 선택 결과 표시
    ┌─────────────────────────────────────────────┐
    │  리포트 뷰어 설정                            │
    │                                             │
    │  컴포넌트 제목                               │
    │  [견적서 미리보기]  ___________________      │
    │                                             │
    │  리포트 선택                                 │
    │  ┌─────────────────────────────────────┐    │
    │  │ 📄 견적서              [변경] [×]   │    │
    │  │    ID: 12 | 유형: 견적              │    │
    │  └─────────────────────────────────────┘    │
    │                                             │
    │  표시 모드                                   │
    │  [인라인 (화면 내 직접 표시)            ▼]    │  ← "인라인"으로 변경
    │                                             │
    │  파라미터 매핑                               │
    │  ┌─────────────────────────────────────┐    │
    │  │ $1  ←  order_no               [×]  │    │  ← 매핑 추가
    │  │                        [+ 추가]     │    │
    │  └─────────────────────────────────────┘    │
    │  쿼리의 $1에 폼 데이터의 order_no 값 전달    │
    └─────────────────────────────────────────────┘

[6] 화면 저장 → screen_layouts_v2 테이블에 JSONB로 저장됨
    저장되는 componentConfig:
    {
      "title": "견적서 미리보기",
      "reportId": 12,
      "reportName": "견적서",
      "displayMode": "inline",
      "paramMappings": [{ "param": "$1", "formField": "order_no" }]
    }
```

---

### 플로우 B: 사용자가 화면에서 리포트를 보는 과정 (실행 시점 — 인라인 모드)

```
[1] 사용자가 "견적 관리" 화면에 접속
    URL: /screens/45?menuObjid=789
    → DynamicComponentRenderer가 screen_layouts_v2에서 레이아웃 로드
    → v2-report-viewer 컴포넌트 렌더링 시작

[2] 화면 초기 상태 (formData에 order_no 없음)
    ┌──────────────────────────────────────────────────────────────┐
    │  견적 관리                                                    │
    │  ┌────────────────────────────────────────────────────────┐  │
    │  │  주문번호: [_______________]  [조회]                    │  │
    │  ├────────────────────────────────────────────────────────┤  │
    │  │  주문 목록 테이블                                       │  │
    │  │  ┌──────┬──────────┬──────────┬──────────┐             │  │
    │  │  │ 번호 │ 주문번호  │ 고객명    │ 금액     │             │  │
    │  │  │      │          │          │          │             │  │
    │  │  │  (데이터 없음)                         │             │  │
    │  │  └──────┴──────────┴──────────┴──────────┘             │  │
    │  ├────────────────────────────────────────────────────────┤  │
    │  │  견적서 미리보기              [↻ 새로고침] [↗ 전체보기]  │  │
    │  │  ┌──────────────────────────────────────────────────┐  │  │
    │  │  │                                                  │  │  │
    │  │  │   파라미터가 없어 리포트를 표시할 수 없습니다      │  │  │
    │  │  │                                                  │  │  │
    │  │  └──────────────────────────────────────────────────┘  │  │
    │  └────────────────────────────────────────────────────────┘  │
    └──────────────────────────────────────────────────────────────┘

[3] 사용자가 주문번호 입력 후 조회 → 테이블에 데이터 표시 → 행 선택
    → formData가 변경됨: { order_no: "ORD-2026-001", ... }
    → v2-report-viewer가 ScreenContext.formData 변경 감지
    → buildContextParams: { "$1": "ORD-2026-001" }
    → useReportExecution 훅이 즉시 쿼리 실행
    → reportApi.executeQuery(12, queryId, { "$1": "ORD-2026-001" })

[4] 리포트가 인라인으로 렌더링됨 (축소 표시)
    ┌──────────────────────────────────────────────────────────────┐
    │  견적 관리                                                    │
    │  ┌────────────────────────────────────────────────────────┐  │
    │  │  주문번호: [ORD-2026-001]  [조회]                       │  │
    │  ├────────────────────────────────────────────────────────┤  │
    │  │  주문 목록 테이블                                       │  │
    │  │  ┌──────┬──────────────┬──────────┬──────────┐         │  │
    │  │  │ 번호 │ 주문번호      │ 고객명    │ 금액     │         │  │
    │  │  │  1   │ ORD-2026-001 │ (주)가나  │ 1,500만  │ ← 선택  │  │
    │  │  │  2   │ ORD-2026-002 │ (주)다라  │ 800만    │         │  │
    │  │  └──────┴──────────────┴──────────┴──────────┘         │  │
    │  ├────────────────────────────────────────────────────────┤  │
    │  │  견적서 미리보기              [↻ 새로고침] [↗ 전체보기]  │  │
    │  │  ┌──────────────────────────────────────────────────┐  │  │
    │  │  │  ╔══════════════════════════════════════════╗     │  │  │
    │  │  │  ║          견   적   서                    ║     │  │  │
    │  │  │  ║──────────────────────────────────────────║     │  │  │
    │  │  │  ║  수신: (주)가나                          ║     │  │  │
    │  │  │  ║  주문번호: ORD-2026-001                  ║     │  │  │
    │  │  │  ║  ┌────┬──────────┬────┬──────────┐      ║     │  │  │
    │  │  │  ║  │ No │ 품목명    │ 수량│ 단가     │      ║     │  │  │
    │  │  │  ║  │ 1  │ 부품A    │ 100│ 50,000   │      ║     │  │  │
    │  │  │  ║  │ 2  │ 부품B    │ 50 │ 100,000  │      ║     │  │  │
    │  │  │  ║  └────┴──────────┴────┴──────────┘      ║     │  │  │
    │  │  │  ║  합계: 15,000,000원                     ║     │  │  │
    │  │  │  ╚══════════════════════════════════════════╝     │  │  │
    │  │  └──────────────────────────────────────────────────┘  │  │
    │  │  * 컴포넌트 크기에 맞게 축소(scale) 렌더링              │  │
    │  │  * 클릭하면 전체 보기 모달 열림                          │  │
    │  └────────────────────────────────────────────────────────┘  │
    └──────────────────────────────────────────────────────────────┘

[5] "전체보기" 버튼 또는 인라인 리포트 클릭 → 모달로 전체 크기 표시
    ┌──────────────────────────────────────────────────────────────┐
    │  견적서 — ORD-2026-001                        [PDF] [×]      │
    │  ┌──────────────────────────────────────────────────────┐    │
    │  │                                                      │    │
    │  │              (A4 크기 리포트 전체 표시)                │    │
    │  │              ReportListPreviewModal 사용              │    │
    │  │              (기존 모달 그대로)                        │    │
    │  │                                                      │    │
    │  └──────────────────────────────────────────────────────┘    │
    │  페이지: [< 1 / 1 >]                                        │
    └──────────────────────────────────────────────────────────────┘
```

---

### 플로우 C: 모달 모드로 설정한 경우 (실행 시점 — 모달 모드)

```
[1] 관리자가 설정 패널에서 displayMode = "모달" 선택 + reportId = 12 (견적서) 설정

[2] 사용자가 화면 접속 시 → 리포트 이름 + "보기" 버튼만 표시
    ┌────────────────────────────────────────────────────────┐
    │  견적서 미리보기                                        │
    │  ┌──────────────────────────────────────────────────┐  │
    │  │  📄 견적서                            [보기]     │  │
    │  └──────────────────────────────────────────────────┘  │
    └────────────────────────────────────────────────────────┘

[3] "보기" 버튼 클릭 → ReportListPreviewModal 모달 열림 (기존과 동일)
    → formData의 order_no 값이 $1 파라미터로 자동 바인딩
    → 모달 안에서 리포트 렌더링 + PDF 다운로드 가능
```

---

### 플로우 D: reportId 미지정 시 (하위 호환 — 기존 menuObjid 기반)

```
[1] 관리자가 설정 패널에서 reportId를 선택하지 않음 (또는 선택 해제)

[2] 사용자가 /screens/45?menuObjid=789 로 접속
    → menuObjid=789에 연결된 리포트 목록 자동 조회
    → 기존과 동일하게 리포트 버튼 목록 표시

    ┌────────────────────────────────────────────────────────┐
    │  리포트                                                │
    │  ┌──────────────────────────────────────────────────┐  │
    │  │  📄 견적서                                       │  │
    │  │  📄 발주서                                       │  │
    │  │  📄 거래명세서                                   │  │
    │  └──────────────────────────────────────────────────┘  │
    └────────────────────────────────────────────────────────┘

[3] 버튼 클릭 → 해당 리포트의 ReportListPreviewModal 모달 열림
    (현재 동작과 100% 동일)
```

---

### 데이터 흐름 요약

```
┌─────────────────┐     ┌──────────────────┐     ┌─────────────────────┐
│  화면 디자이너    │     │  화면 뷰어        │     │  백엔드 API          │
│  (설정 시점)     │     │  (실행 시점)      │     │                     │
├─────────────────┤     ├──────────────────┤     ├─────────────────────┤
│                 │     │                  │     │                     │
│ ConfigPanel     │     │ ReportViewer     │     │ GET /admin/reports  │
│   ↓ reportId    │ 저장 │ Component        │     │   → 리포트 목록     │
│   ↓ displayMode │ ──→ │   ↓              │     │                     │
│   ↓ paramMappings    │   ↓ config 로드    │     │ GET /admin/reports  │
│                 │     │   ↓              │     │     /:reportId      │
│ ReportSelect    │     │ reportId 있음?   │     │   → 리포트 상세     │
│ Modal           │     │   ├─ Yes         │     │                     │
│   ↓             │     │   │  displayMode?│     │ POST /admin/reports │
│ reportApi       │     │   │  ├─ inline   │     │   /:reportId/queries│
│ .getReports()   │     │   │  │  → Inline │     │   /:queryId/execute │
│                 │     │   │  │  Renderer │ ──→ │   → 쿼리 실행       │
│                 │     │   │  └─ modal    │     │   params: { $1: ... }│
│                 │     │   │     → 버튼   │     │                     │
│                 │     │   │     → 클릭시 │     │                     │
│                 │     │   │     모달     │     │                     │
│                 │     │   │              │     │                     │
│                 │     │   └─ No (fallback)     │ GET /admin/reports  │
│                 │     │      → menuObjid │     │   /by-menu/:objid   │
│                 │     │      기반 목록    │     │   → 메뉴별 리포트   │
│                 │     │                  │     │                     │
│                 │     │ formData 변경    │     │                     │
│                 │     │   → 즉시 재실행   │     │                     │
└─────────────────┘     └──────────────────┘     └─────────────────────┘
```

---

### 관련 URL 정리

| 화면 | URL | 역할 |
|------|-----|------|
| 화면 목록 (관리자) | `/admin/screenMng/screenMngList` | 화면 목록 + 더블클릭 시 디자이너 진입 |
| 화면 디자이너 (관리자) | (URL 변화 없음, 같은 페이지 내 ScreenDesigner 렌더링) | 컴포넌트 배치 + 설정 |
| 리포트 관리 (관리자) | `/admin/screenMng/reportList` | 리포트 CRUD + 디자이너 진입 |
| 리포트 디자이너 (관리자) | `/admin/screenMng/reportList/designer/{reportId}` | SQL + 레이아웃 + 바인딩 설정 |
| 화면 뷰어 (사용자) | `/screens/{screenId}?menuObjid={menuObjid}` | 실제 화면 사용 |
| POP 화면 뷰어 (사용자) | `/pop/screens/{screenId}` | POP 화면 사용 |

---

## 확정된 결정 사항

| # | 결정 항목 | 확정 내용 |
|---|----------|----------|
| 1 | **리포트 선택 방식** | **reportId 직접 지정** — 설정 패널에서 "리포트 선택" 버튼 → 리포트 목록 모달 → 선택하면 reportId 저장. menuObjid 기반은 fallback으로 유지 |
| 2 | **표시 모드** | **모달 + 인라인 선택 가능** — 설정에서 displayMode 선택 (modal / inline) |
| 3 | **파라미터 바인딩** | **단순 키만** — `formData['order_no']` 같은 1단계 키만 지원 (현재 방식 유지) |
| 4 | **자동 갱신** | **즉시 자동 갱신** — formData 변경 시 즉시 리포트 재실행 |
| 5 | **버그 수정 범위** | **screens + pop/screens 모두 수정** — menuObjid 미전달 버그 |

---

## 1. 현재 코드 현황

### v2-report-viewer 현재 파일 구조

```
frontend/lib/registry/components/v2-report-viewer/
├── index.ts                        # V2ReportViewerDefinition (28줄)
├── types.ts                        # ReportViewerConfig, ReportParamMapping (18줄)
├── ReportViewerComponent.tsx       # 메인 컴포넌트 (133줄)
├── ReportViewerConfigPanel.tsx     # 설정 패널 (115줄)
└── ReportViewerRenderer.tsx        # ComponentRegistry 등록 (12줄)
```

### 현재 문제점

| 문제 | 원인 |
|------|------|
| reportId를 직접 지정할 수 없음 | 설정 패널에 리포트 선택 UI 없음 |
| menuObjid 없으면 아무것도 안 보임 | reportId fallback 없음 |
| 인라인 렌더링 불가 | 모달만 지원 |
| formData 변경 시 자동 갱신 없음 | 감지 로직 없음 |
| `/screens/` 페이지에서 menuObjid 전달 안 됨 | ScreenContextProvider에 props 미전달 |

---

## 2. 구현 단계 (7 Steps)

### Step 1: menuObjid 미전달 버그 수정 [난이도: 낮음]

**수정 파일 (2개):**

| 파일 | 현재 | 변경 |
|------|------|------|
| `frontend/app/(main)/screens/[screenId]/page.tsx` | `<ScreenContextProvider>` (props 없음, 1377행) | Wrapper에서 `useSearchParams`로 `menuObjid` 파싱 후 전달 |
| `frontend/app/(pop)/pop/screens/[screenId]/page.tsx` | `<ScreenContextProvider>` (props 없음, 348행) | 동일 |

**검증:**
- [ ] `/screens/{screenId}?menuObjid=123` 접속 → `screenContext.menuObjid === 123` 확인

---

### Step 2: ReportViewerConfig 타입 확장 [난이도: 낮음]

**수정 파일:** `frontend/lib/registry/components/v2-report-viewer/types.ts`

```typescript
export interface ReportViewerConfig extends ComponentConfig {
  title?: string;
  paramMappings?: ReportParamMapping[];

  reportId?: number;                       // 리포트 목록 모달에서 선택한 리포트 ID
  reportName?: string;                     // 선택한 리포트명 (설정 패널 표시용)
  displayMode?: "modal" | "inline";        // 기본: "modal"
}
```

---

### Step 3: ConfigPanel에 리포트 선택 UI 추가 [난이도: 중간]

**수정 파일:** `frontend/lib/registry/components/v2-report-viewer/ReportViewerConfigPanel.tsx`

**추가되는 UI 섹션:**
1. 리포트 선택 영역 (선택 버튼 → 리포트 목록 모달 → 선택 결과 표시 + 해제 버튼)
2. 표시 모드 Select (모달 / 인라인)

**리포트 선택 모달:** `reportApi.getReports({ limit: 100, useYn: 'Y' })` → 검색 + 테이블 → 행 클릭 시 선택 완료.

---

### Step 4: ReportInlineRenderer + useReportExecution 추출 [난이도: 높음]

**신규 파일 (2개):**

| 파일 | 역할 | 위치 근거 |
|------|------|----------|
| `frontend/hooks/useReportExecution.ts` | 리포트 로드 + 쿼리 실행 공용 훅 (~120줄) | CLAUDE.md 네이밍 규칙: 훅은 `frontend/hooks/`에 배치. `ReportListPreviewModal`과 `ReportInlineRenderer` 양쪽에서 공유하므로 특정 컴포넌트 폴더가 아닌 공용 위치가 적합 |
| `frontend/components/report/ReportInlineRenderer.tsx` | 모달 없이 인라인 렌더링 (~200줄) | `ReportListPreviewModal`과 동일 레벨에 배치. v2-report-viewer 전용이 아니라 향후 다른 컨텍스트(예: 대시보드 위젯)에서도 재사용 가능한 범용 렌더러이므로 `components/report/`가 적합 |

**useReportExecution:** `ReportListPreviewModal`에서 리포트 로드 + 쿼리 실행 로직을 추출한 공용 훅.

**ReportInlineRenderer:** `useReportExecution` 훅 사용 + ResizeObserver로 scale 축소 렌더링 + 첫 페이지만 표시.

---

### Step 5: ReportViewerComponent에 reportId 직접 지정 + displayMode 분기 [난이도: 중간]

**수정 파일:** `frontend/lib/registry/components/v2-report-viewer/ReportViewerComponent.tsx`

**렌더링 분기:**

```
config.reportId 있음:
  ├─ displayMode === "inline" → ReportInlineRenderer + 헤더(제목, 새로고침, 전체보기)
  └─ displayMode === "modal"  → 리포트명 + "보기" 버튼 → 클릭 시 모달

config.reportId 없음 (fallback):
  → menuObjid 기반 리포트 목록 (기존 동작 100% 유지)
```

---

### Step 6: formData 변경 시 즉시 자동 갱신 [난이도: 중간]

**수정 파일:** `ReportViewerComponent.tsx`

`ScreenContext.formData` 변경 감지 → `contextParams` 재계산 → `refreshKey` 증가 → `ReportInlineRenderer`에 전달하여 즉시 재실행.

---

### Step 7: 통합 테스트 + 가이드 문서 업데이트 [난이도: 낮음]

**검증 시나리오:** 플로우 A~D 전체 검증 + `npx tsc --noEmit` 오류 없음.

**가이드 문서 업데이트:**
- [ ] `docs/V2_컴포넌트_분석_가이드.md` — V2 컴포넌트 목록에 `v2-report-viewer` 추가 (18개 → 19개)
- [ ] `docs/V2_컴포넌트_연동_가이드.md` — 6.1 연동 능력 매트릭스에 `v2-report-viewer` 행 추가
- [ ] `docs/screen-implementation-guide/화면개발_표준_가이드.md` — V2 컴포넌트 목록에 `v2-report-viewer` 추가 (23개 → 24개)

---

## 3. 파일 변경 요약

### 수정 파일 (5개)

| 파일 | Step | 핵심 변경 |
|------|------|-----------|
| `frontend/app/(main)/screens/[screenId]/page.tsx` | 1 | ScreenContextProvider에 menuObjid 전달 |
| `frontend/app/(pop)/pop/screens/[screenId]/page.tsx` | 1 | 동일 |
| `frontend/lib/registry/components/v2-report-viewer/types.ts` | 2 | `reportId`, `reportName`, `displayMode` 필드 추가 |
| `frontend/lib/registry/components/v2-report-viewer/ReportViewerConfigPanel.tsx` | 3 | 리포트 선택 모달 + displayMode Select |
| `frontend/lib/registry/components/v2-report-viewer/ReportViewerComponent.tsx` | 5,6 | reportId 분기 + displayMode 분기 + 자동 갱신 |

### 신규 파일 (2개)

| 파일 | Step | 역할 |
|------|------|------|
| `frontend/hooks/useReportExecution.ts` | 4 | 리포트 로드 + 쿼리 실행 공용 훅 |
| `frontend/components/report/ReportInlineRenderer.tsx` | 4 | 모달 없이 인라인 리포트 렌더링 |

### 수정 대상 (리팩토링, 선택적)

| 파일 | Step | 변경 |
|------|------|------|
| `frontend/components/report/ReportListPreviewModal.tsx` | 4 | 내부 로드/실행 로직을 `useReportExecution`으로 교체 (기능 변경 없음) |

### 가이드 문서 업데이트 (3개)

| 파일 | Step | 변경 |
|------|------|------|
| `docs/V2_컴포넌트_분석_가이드.md` | 7 | V2 컴포넌트 목록에 `v2-report-viewer` 추가 |
| `docs/V2_컴포넌트_연동_가이드.md` | 7 | 연동 능력 매트릭스에 `v2-report-viewer` 행 추가 |
| `docs/screen-implementation-guide/화면개발_표준_가이드.md` | 7 | V2 컴포넌트 목록에 `v2-report-viewer` 추가 |

---

## 4. 구현 순서 및 의존성

```
Step 1  menuObjid 버그 수정 ─────────────────── (독립)
  ↓
Step 2  types.ts 확장 ───────────────────────── (Step 3, 5의 기반)
  ↓
Step 3  ConfigPanel 리포트 선택 UI ──────────── (Step 2 의존)
  ↓
Step 4  useReportExecution + ReportInlineRenderer (가장 큰 작업)
  ↓
Step 5  ReportViewerComponent 분기 렌더링 ───── (Step 2, 4 의존)
  ↓
Step 6  즉시 자동 갱신 ──────────────────────── (Step 5 의존)
  ↓
Step 7  통합 테스트 + 가이드 문서 업데이트
```

**병렬 가능:**
- Step 1 + Step 2: 동시 진행 가능
- Step 3 + Step 4: Step 2 완료 후 동시 진행 가능

---

## 5. V2 이벤트 시스템과의 관계

`V2_컴포넌트_연동_가이드.md`에서 정의한 V2 표준 이벤트 시스템(`V2_EVENTS`, `dispatchV2Event`, `subscribeV2Event`)과의 관계를 정리합니다.

### 현재 v2-report-viewer가 사용하지 않는 이유

| V2 이벤트 | 사용 여부 | 이유 |
|-----------|----------|------|
| `tableListDataChange` | 구독 안 함 | 리포트 뷰어는 테이블 데이터 변경이 아닌 `ScreenContext.formData`를 통해 파라미터를 받음 |
| `beforeFormSave` / `afterFormSave` | 구독 안 함 | 리포트 뷰어는 데이터를 저장하지 않음 (읽기 전용 표시 컴포넌트) |
| `refreshTable` | 구독 안 함 | 리포트 갱신은 `refreshKey` prop으로 처리. 테이블 갱신 이벤트와는 무관 |
| `componentDataTransfer` | 구독 안 함 | 리포트 뷰어는 DataReceivable이 아님 (데이터를 수신하여 편집하는 컴포넌트가 아님) |

### formData 공유 방식

`v2-report-viewer`는 `ScreenContext`의 `formData`를 통해 다른 컴포넌트와 통신합니다:

```
v2-input (order_no 입력)
  → ScreenContext.formData 업데이트
    → v2-report-viewer가 formData 변경 감지
      → buildContextParams로 쿼리 파라미터 생성
        → useReportExecution으로 쿼리 실행
```

이 방식은 `V2_컴포넌트_연동_가이드.md` 4.3절 ScreenContext의 `formData` 공유 패턴과 일치합니다.

### 향후 확장 시 이벤트 도입 가능성

리포트 실행 완료 후 다른 컴포넌트에 알림이 필요한 경우(예: 리포트 로드 완료 시 집계 위젯 갱신), V2 이벤트를 추가할 수 있습니다. 현재 Phase에서는 불필요합니다.

---

## 6. 충돌 사전 검사 대상

구현 시작 전 아래 이름들이 현재 코드베이스에 **0건**인지 Grep 확인 필수:

```
ReportInlineRenderer, useReportExecution,
ReportSelectModal, displayMode (v2-report-viewer 내),
reportName (v2-report-viewer/types.ts 내)
```

---

## 7. 주의사항

1. **하위 호환 필수**: 모든 신규 필드는 optional. `reportId` 없으면 기존 menuObjid 기반 동작 그대로 유지.
2. **reportId 타입**: `ReportMaster.report_id`는 `string`이지만 실제 값은 숫자 문자열. API 호출 시 `String(reportId)`로 변환.
3. **멀티테넌시**: `reportApi.getReports()` 호출 시 백엔드에서 자동으로 company_code 필터링됨.
4. **디자인 모드 보호**: `isDesignMode`일 때 API 호출, 자동 갱신 모두 스킵.
5. **ReportListPreviewModal 수정 최소화**: 기존 모달은 그대로 유지. 공통 로직만 훅으로 추출.
6. **인라인 렌더링 스케일**: `ResizeObserver`로 컨테이너 크기 감지 → `transform: scale(containerWidth / canvasWidth)`.
7. **V2 컴포넌트 규칙 준수**: `v2-` 접두사, `V2ReportViewerDefinition` 네이밍, `screen_layouts_v2` JSONB 저장.
8. **각 Step 완료 시 필수**: `cd frontend && npx tsc --noEmit`

---

## 8. 핵심 원칙

| 역할 | 담당 |
|------|------|
| SQL 작성, 컴포넌트 레이아웃, queryId+field 연결, 숫자 포맷/합계 | **리포트 디자이너** (기존, 수정 없음) |
| 어떤 리포트를 보여줄지 (reportId), 언제 실행할지 (자동 갱신), 어디에 표시할지 (displayMode) | **화면관리 v2-report-viewer** (이번 구현) |

리포트 디자이너의 코드는 이번 작업에서 **수정하지 않는다**.

---

## 9. 연동 능력 매트릭스 (Step 7에서 가이드 문서에 추가할 내용)

| 컴포넌트 | 이벤트 발행 | 이벤트 구독 | DataProvider | DataReceiver | Context 사용 |
|----------|:-----------:|:-----------:|:------------:|:------------:|:------------:|
| `v2-report-viewer` | - | - | - | - | Screen (formData, menuObjid) |

| 소스 컴포넌트 | 타겟 컴포넌트 | 연동 방식 | 용도 |
|--------------|--------------|----------|------|
| `v2-input` / `v2-table-list` | `v2-report-viewer` | ScreenContext.formData | 파라미터 바인딩 |
| `v2-report-viewer` | `ReportListPreviewModal` | props (report, contextParams) | 전체 보기 모달 |
