원클릭으로
data-export-pattern
PDF(인쇄 레이아웃) 및 Excel(xlsx 동적 로딩) 파일 내보내기 구현 및 갱신 패턴 가이드
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
PDF(인쇄 레이아웃) 및 Excel(xlsx 동적 로딩) 파일 내보내기 구현 및 갱신 패턴 가이드
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
프로젝트 구조와 네이밍 컨벤션에 맞게 새 React 컴포넌트를 추가하는 가이드
Gemini 3.1 Flash Lite API를 사용한 계기판 OCR 및 증빙 서류 AI 판별 연동 패턴 가이드
Sentry에 잡힌 환경/브라우저/외부 의존성 발 노이즈 에러를 필터링한다. 사용자가 "Sentry 노이즈", "Sentry 에러 필터", "이 에러 무시", "Sentry에 자꾸 뜨는 X 막아줘" 등을 요청할 때 발동한다.
React 단위 테스트(Vitest) 및 E2E 테스트(Playwright) 작성 컨벤션과 Mocking 가이드
Cloud Functions, 프론트엔드 빌드 및 배포 시 발생하는 다양한 오류(Node 버전, 의존성 충돌, 소스맵)의 해결 패턴을 모아둔 가이드.
functions/src/ 디렉터리에 새 Cloud Function을 추가하고 index.ts에 등록하는 패턴 가이드
| name | data-export-pattern |
| description | PDF(인쇄 레이아웃) 및 Excel(xlsx 동적 로딩) 파일 내보내기 구현 및 갱신 패턴 가이드 |
이 가이드는 차량운행일지 앱에서 표 형태의 통계/기록 데이터를 PDF(브라우저 인쇄 형태) 및 Excel(xlsx) 형식으로 파일 다운로드하는 통합 개발 스킬 패턴을 설명합니다.
브라우저의 인쇄 기능(window.print())을 활용해 A4 가로(Landscape)에 딱 맞춰 테이블을 인쇄/저장하는 HTML/CSS 기반 PDF 생성 패턴을 따릅니다.
src/lib/pdfStyles.ts: 공통 폰트 설정, CSS 및 데이터 포맷팅 유틸 (formatDate, formatNumber)src/lib/{Domain}PdfExport.ts: 도메인별 전용 PDF 파일 (예: dailyLogPdfExport.ts, maintenancePdfExport.ts 등)ROWS_PER_PAGE)를 설정합니다. (보통 19 ~ 25행 권장) )으로 채웁니다.// 1. 데이터 인터페이스 정의
interface PdfEntry {
date: string;
vehicleName: string;
purpose: string;
distance: number;
}
// 2. 메인 다운로드 함수
export function downloadVehiclePdf(
records: PdfEntry[],
options: { orgName?: string; approvalLine?: { title: string }[] } = {}
) {
if (!records || records.length === 0) return;
// 데이터 정렬 & 페이지 분할
const sorted = [...records].sort((a, b) => a.date.localeCompare(b.date));
const pages: PdfEntry[][] = [];
for (let i = 0; i < sorted.length; i += ROWS_PER_PAGE) {
pages.push(sorted.slice(i, i + ROWS_PER_PAGE));
}
// HTML 조립 및 새 윈도우 인쇄
const htmlContent = buildPdfHtml(pages, options);
const printWindow = window.open('', '_blank', 'width=1100,height=800');
if (!printWindow) {
alert("팝업이 차단되었습니다. 팝업 허용 후 다시 시도해 주세요.");
return;
}
printWindow.document.write(htmlContent);
printWindow.document.close();
printWindow.onload = () => {
setTimeout(() => { printWindow.print(); }, 300);
};
}
엑셀 저장 기능은 번들 크기 최소화를 위해 xlsx 패키지를 전역적으로 가져오지 않고, 다운로드 함수 호출 시점에만 비동기 동적 임포트(Dynamic Import) 해야 합니다.
src/lib/excelExport.ts 하나의 공용 파일 내에 함수를 추가하여 관리합니다.export async function downloadDriveLogsExcel(
records: any[],
filename = '운행일지',
{ onError }: { onError?: (msg: string) => void } = {}
) {
// 1단계: 데이터 체크
if (!records || records.length === 0) {
onError?.('다운로드할 데이터가 없습니다.');
return;
}
// 2단계: xlsx 동적 임포트 (중요!)
const XLSX = await import('xlsx');
// 3단계: 사용자 친화적인 한글 헤더 매핑
const rows = records.map(rec => ({
'일자': rec.date,
'차량번호': rec.vehicleNumber,
'운행전km': rec.startKm,
'운행후km': rec.endKm,
'목적': rec.purpose
}));
// 4단계: 워크시트 생성 및 열 너비(wch) 지정
const ws = XLSX.utils.json_to_sheet(rows);
ws['!cols'] = [
{ wch: 12 }, // 일자
{ wch: 15 }, // 차량번호
{ wch: 12 }, // 운행전km
{ wch: 12 }, // 운행후km
{ wch: 25 } // 목적
];
// 5단계: 워크북 생성 및 파일 쓰기
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, '기록');
XLSX.writeFile(wb, `${filename}.xlsx`);
}
window.open이나 파일 저장이 차단되는 상황을 대비해 onError 콜백을 넘겨 UI에서 useToast 또는 Toast 팝업을 통해 대응해야 합니다.dateUtils.ts 또는 pdfStyles의 formatDate를 호출해 일관성 있는 날짜 표기 형식(YYYY-MM-DD HH:mm)을 갖춰야 합니다.