Files
sam-docs/dev/guides/performance-report-excel-export.md
김보곤 007e8a3ed3 docs: [quality] 실적신고 엑셀 Export 구현 가이드 추가
- dev/guides/performance-report-excel-export.md 신규 작성
- 품질관리 README 미구현 항목 → 구현 완료로 업데이트
- INDEX.md에 새 문서 등록
2026-03-17 16:55:19 +09:00

9.3 KiB
Raw Blame History

실적신고 확정건 엑셀 Export 구현 가이드

작성일: 2026-03-17 상태: 운영 중


1. 개요

1.1 목적

품질관리 > 실적신고관리에서 확정건 엑셀다운로드 버튼 클릭 시, 건기원 제출 양식(품질인정자재등의 판매실적 대장)에 맞는 xlsx 파일을 생성하여 다운로드한다.

1.2 왜 PhpSpreadsheet 직접 사용인가

기존 ExportService(Maatwebsite/Excel)는 단순 테이블 Export에 적합하지만, 건기원 양식은 다음 요구사항이 있어 PhpSpreadsheet를 직접 사용한다:

  • 다중 헤더 행 (제목, 부제목, 카테고리 헤더, 컬럼 헤더)
  • 복잡한 셀 병합 (같은 품질관리서의 여러 개소 → 문서 수준 컬럼 병합)
  • 카테고리별 배경색 (5개 카테고리, 각기 다른 색상)
  • 회사 정보 섹션 (Tenant 모델 참조)
  • 27개 컬럼 (A~AA)

2. 아키텍처

2.1 구성 요소

┌─────────────────────────────────────────────────────────┐
│ React (PerformanceReportList.tsx)                        │
│   └── handleExcelDownload()                             │
│        └── exportConfirmedExcel() [Server Action]       │
│             └── fetch(API, Accept: xlsx)                │
└────────────────────┬────────────────────────────────────┘
                     │ GET /api/v1/quality/performance-reports/export-excel
                     ▼
┌─────────────────────────────────────────────────────────┐
│ API (PerformanceReportController)                        │
│   └── exportExcel()                                     │
│        └── PerformanceReportService.exportConfirmed()   │
│             └── PerformanceReportExcelService.generate() │
│                  ├── getConfirmedReports()               │
│                  ├── setColumnWidths()                   │
│                  ├── writeTitle()                        │
│                  ├── writeCompanyInfo()                  │
│                  ├── writeCategoryHeaders()              │
│                  ├── writeColumnHeaders()                │
│                  └── writeDataRows() + merge logic       │
│                       └── StreamedResponse (xlsx)        │
└─────────────────────────────────────────────────────────┘

2.2 파일 구조

프로젝트 파일 역할
API app/Services/PerformanceReportExcelService.php 엑셀 생성 전담 서비스
API app/Services/PerformanceReportService.php exportConfirmed() 메서드
API app/Http/Controllers/Api/V1/PerformanceReportController.php exportExcel() 액션
API routes/api/v1/quality.php GET 라우트
React components/quality/PerformanceReportManagement/actions.ts exportConfirmedExcel() 서버 액션
React components/quality/PerformanceReportManagement/PerformanceReportList.tsx handleExcelDownload()

3. 엑셀 양식 구조

3.1 시트 레이아웃

Row 1   : [A1:AA1 병합] 제목 — "품질인정자재등의 판매실적 제출서식" (돋움 24pt bold)
Row 2   : 빈 행
Row 3   : [A3:AA3 병합] 부제목 — "품질인정자재등의 판매실적 대장(2026년 1분기)" (돋움 18pt)
Row 4   : 빈 행
Row 5~9 : 회사 정보 (Tenant 모델)
Row 10  : 빈 행
Row 11  : 카테고리 헤더 (5개 카테고리, 각각 배경색)
Row 12  : 컬럼 헤더 (27개)
Row 13+ : 데이터 행

3.2 카테고리 헤더 (Row 11)

범위 카테고리 배경색
A~L 건축자재내역 #DAEEF3 (연한 파랑)
M~O 건축공사장 #E2EFDA (연한 초록)
P~S 공사감리자 #FCE4D6 (연한 주황)
T~W 공사시공자 #EDEDED (연한 회색)
X~AA 자재유통업자 #FFF2CC (연한 노랑)

3.3 컬럼 헤더 (Row 12) — 27개

컬럼 헤더 데이터 소스 병합
A 일련번호 순번 (auto) O
B 품질관리서번호 quality_doc_number O
C 작성일 received_date O
D 인정품목 미확정 → '' O
E 규격(품명) orderItem.item_name O
F 규격(종류) orderItem.specification O
G 제품검사일 options.inspection.end_date O
H 내화성능시간 미확정 → '' O
I 사용부위 미확정 → '' O
J 로트번호 미확정 → '' X
K 규격(치수) post_width × post_height X
L 수량 orderItem.quantity X
M 공사명칭 options.construction_site.name O
N 소재지 options.construction_site.land_location O
O 번지 options.construction_site.lot_number O
P 사무소명 options.supervisor.office O
Q 사무소주소 options.supervisor.address O
R 성명 options.supervisor.name O
S 연락처 options.supervisor.phone O
T 업체명 options.contractor.company O
U 업체주소 options.contractor.address O
V 성명 options.contractor.name O
W 연락처 options.contractor.phone O
X 업체명 options.material_distributor.company O
Y 업체주소 options.material_distributor.address O
Z 대표자명 options.material_distributor.ceo O
AA 연락처 options.material_distributor.phone O

병합 O: 같은 품질관리서의 여러 개소 → 첫 행에만 기록, 나머지 행 병합 병합 X: 개소별로 다른 데이터 → 매 행마다 기록

3.4 셀 병합 로직

하나의 PerformanceReportQualityDocument → 여러 QualityDocumentLocation

// 같은 품질관리서에 개소가 3개인 경우:
// Row 13: 일련번호=1, 품관번호, 작성일, ... (문서 데이터) + 개소1 데이터
// Row 14:                                                    + 개소2 데이터
// Row 15:                                                    + 개소3 데이터
// → A13:A15, B13:B15, C13:C15, ... 병합 (J, K, L 제외)

4. 미확정 필드 (추후 확장 포인트)

4개 필드가 현재 빈 문자열을 반환하며, 별도 메서드로 분리되어 있어 추후 데이터 매핑만 추가하면 된다:

메서드 컬럼 설명
getProductCategory($location) D 인정품목
getFireResistanceTime($location) H 내화성능시간
getUsagePart($location) I 사용부위
getLotNumber($location) J 로트번호

이 4개 필드의 데이터 소스가 확정되면 해당 메서드 내부만 수정하면 된다. 참고: project_excel_export_pending_fields.md (메모리)


5. API 엔드포인트

GET /api/v1/quality/performance-reports/export-excel
파라미터 타입 필수 설명
year int O 연도 (기본: 현재 연도)
quarter int O 분기 (기본: 현재 분기)

응답: StreamedResponse (xlsx 파일)

Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
Content-Disposition: attachment; filename*=UTF-8''{회사명}_품질인정자재등의_판매실적_대장_{year}년_{quarter}분기.xlsx

6. 프론트엔드 패턴

6.1 Server Action (Blob 다운로드)

기존 급여관리 exportPayrollExcel 패턴을 재사용:

// actions.ts
export async function exportConfirmedExcel(params) {
  const cookieStore = await cookies();
  const token = cookieStore.get('access_token')?.value;
  const response = await fetch(url, {
    headers: {
      Accept: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
      Authorization: `Bearer ${token}`,
      'X-API-KEY': process.env.API_KEY,
    },
  });
  const blob = await response.blob();
  return { success: true, data: blob, filename };
}

6.2 컴포넌트 (다운로드 트리거)

// PerformanceReportList.tsx
const handleExcelDownload = useCallback(async () => {
  const result = await exportConfirmedExcel({ year, quarter });
  if (result.success && result.data) {
    const url = URL.createObjectURL(result.data);
    const a = document.createElement('a');
    a.href = url;
    a.download = result.filename;
    a.click();
    URL.revokeObjectURL(url);
  }
}, [year, quarter]);

7. 의존성

패키지 버전 프로젝트 용도
phpoffice/phpspreadsheet ^1.30 API xlsx 생성

--ignore-platform-reqs로 설치됨 (Docker 컨테이너에 ext-gd 미설치). xlsx 생성에는 gd 불필요. 개발 서버에는 gd 확장이 설치되어 있어 문제 없음.


관련 문서


최종 업데이트: 2026-03-17