Files
sam-react-prod/src/lib/print-utils.ts
유병철 a2c3e4c41e refactor(WEB): 프론트엔드 대규모 코드 정리 및 리팩토링
- 미사용 코드 삭제: ThemeContext, itemStore, utils/date.ts, utils/formatAmount.ts
- 유틸리티 이동: date, formatAmount → src/lib/utils/ (중앙 집중화)
- 다수 page.tsx 클라이언트 컴포넌트 패턴 통일
- DateRangeSelector 리팩토링 및 date-range-picker UI 컴포넌트 추가
- ThemeSelect/themeStore Zustand 직접 연동으로 전환
- 건설/회계/영업/품목/출하 등 전반적 컴포넌트 개선
- UniversalListPage, IntegratedListTemplateV2 타입 확장
- 프론트엔드 종합 리뷰 문서 및 개선 체크리스트 추가

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 16:30:07 +09:00

193 lines
4.9 KiB
TypeScript

/**
* 인쇄 유틸리티 함수
* 특정 요소만 인쇄하기 위한 헬퍼 함수들
*/
import { sanitizeHTMLForPrint } from '@/lib/sanitize';
import { toast } from 'sonner';
interface PrintOptions {
/** 문서 제목 (브라우저 인쇄 다이얼로그에 표시) */
title?: string;
/** 추가 CSS 스타일 */
styles?: string;
/** 인쇄 후 창 닫기 여부 */
closeAfterPrint?: boolean;
}
/**
* 특정 요소의 내용만 인쇄합니다.
* @param elementOrSelector - 인쇄할 요소 또는 CSS 선택자
* @param options - 인쇄 옵션
*/
export function printElement(
elementOrSelector: HTMLElement | string,
options: PrintOptions = {}
): void {
const {
title = '문서 인쇄',
styles = '',
closeAfterPrint = true,
} = options;
// 요소 찾기
const element =
typeof elementOrSelector === 'string'
? document.querySelector(elementOrSelector)
: elementOrSelector;
if (!element) {
console.error('인쇄할 요소를 찾을 수 없습니다:', elementOrSelector);
return;
}
// 인쇄용 새 창 열기
const printWindow = window.open('', '_blank', 'width=800,height=600');
if (!printWindow) {
console.error('팝업 창을 열 수 없습니다. 팝업 차단을 확인해주세요.');
toast.error('인쇄 창을 열 수 없습니다. 팝업 차단을 해제해주세요.');
return;
}
// 현재 페이지의 스타일시트 수집
const styleSheets = Array.from(document.styleSheets)
.map((styleSheet) => {
try {
if (styleSheet.href) {
return `<link rel="stylesheet" href="${styleSheet.href}">`;
}
if (styleSheet.cssRules) {
const rules = Array.from(styleSheet.cssRules)
.map((rule) => rule.cssText)
.join('\n');
return `<style>${rules}</style>`;
}
} catch (e) {
// CORS 에러 등으로 접근 불가한 스타일시트는 무시
if (styleSheet.href) {
return `<link rel="stylesheet" href="${styleSheet.href}">`;
}
}
return '';
})
.join('\n');
// 기본 인쇄 스타일
const defaultPrintStyles = `
<style>
@media print {
@page {
size: A4 portrait;
margin: 10mm;
}
* {
-webkit-print-color-adjust: exact !important;
print-color-adjust: exact !important;
color-adjust: exact !important;
}
body {
margin: 0;
padding: 0;
background: white !important;
}
.print-container {
width: 100%;
max-width: 190mm;
margin: 0 auto;
padding: 0;
background: white;
}
/* 그림자, 라운드 제거 */
.print-container * {
box-shadow: none !important;
}
/* 테이블 스타일 */
table {
border-collapse: collapse !important;
page-break-inside: avoid;
}
th, td {
border: 1px solid #000 !important;
}
}
/* 화면 표시용 스타일 */
@media screen {
body {
margin: 0;
padding: 20px;
background: #f5f5f5;
}
.print-container {
max-width: 210mm;
margin: 0 auto;
padding: 20mm;
background: white;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
}
${styles}
</style>
`;
// 인쇄할 내용 복제 및 정리
const contentClone = element.cloneNode(true) as HTMLElement;
// print-hidden 요소 제거
contentClone.querySelectorAll('.print-hidden').forEach((el) => el.remove());
// 불필요한 클래스 및 스타일 정리
contentClone.classList.remove('print-area');
contentClone.style.cssText = '';
// 내부 wrapper의 스타일 정리
const innerWrapper = contentClone.querySelector(':scope > div');
if (innerWrapper instanceof HTMLElement) {
innerWrapper.style.cssText = 'max-width: none; margin: 0; padding: 0; box-shadow: none; border-radius: 0;';
}
// HTML 작성
printWindow.document.write(`
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>${title}</title>
${styleSheets}
${defaultPrintStyles}
</head>
<body>
<div class="print-container">
${sanitizeHTMLForPrint(contentClone.innerHTML)}
</div>
<script>
window.onload = function() {
setTimeout(function() {
window.print();
${closeAfterPrint ? 'window.close();' : ''}
}, 250);
};
</script>
</body>
</html>
`);
printWindow.document.close();
}
/**
* .print-area 클래스를 가진 요소를 인쇄합니다.
* @param options - 인쇄 옵션
*/
export function printArea(options: PrintOptions = {}): void {
printElement('.print-area', options);
}