--- id: frontend-print-stylesheet title: Print Stylesheet — @page / 인쇄 / PDF category: Coding status: draft source_trust_level: B verification_status: conceptual created_at: 2026-05-09 updated_at: 2026-05-09 tags: [frontend, css, print, vibe-coding] tech_stack: { language: "CSS", applicable_to: ["Frontend"] } applied_in: [] aliases: [@page, @media print, page-break, print preview, paged.js, PDF generation] --- # Print Stylesheet > Web → 인쇄 / PDF. **`@media print` + `@page`**. Header / footer 반복, page break, 흑백, link URL 표시. Receipt / report / 영수증 / book. ## 📖 핵심 개념 - @media print: 인쇄용 styling. - @page: 페이지 size / margin. - page-break: 강제 break. - print preview: 디버깅. ## 💻 코드 패턴 ### 기본 ```css @media print { /* Hide UI */ .header, .nav, .footer, .ads, .no-print { display: none !important; } body { font-size: 12pt; line-height: 1.5; color: black; background: white; } /* Link URL 표시 */ a[href]::after { content: " (" attr(href) ")"; font-size: 10pt; color: #555; } /* Internal link 제외 */ a[href^="#"]::after, a[href^="javascript:"]::after { content: ""; } } ``` ### @page ```css @page { size: A4 portrait; margin: 2cm; } @page :first { margin-top: 4cm; } @page :left { margin-left: 3cm; } @page :right { margin-right: 3cm; } /* Named page */ @page chapter { margin: 3cm; } .chapter-page { page: chapter; } ``` ### Page break ```css .page-break-before { page-break-before: always; break-before: page; } .page-break-after { page-break-after: always; break-after: page; } .no-break { page-break-inside: avoid; break-inside: avoid; } /* Modern syntax */ .chapter { break-before: page; } .figure { break-inside: avoid; } ``` ```html
Chapter 1

Content...

Chapter 2
``` ### Avoid orphan / widow ```css p { orphans: 3; /* 페이지 끝 최소 3 line */ widows: 3; /* 페이지 시작 최소 3 line */ } ``` → 한 줄 만 페이지 끝 / 시작 안 됨. ### Header / footer (running headers) ```css @page { @top-center { content: "My Document"; } @bottom-center { content: counter(page) " / " counter(pages); } @top-right { content: string(chapter-title); } } h1.chapter { string-set: chapter-title content(); } ``` → 매 page header 자동. ⚠️ Browser 지원 부분적. Paged.js / Prince XML 사용 가능. ### Counter (page number) ```css @page { @bottom-right { content: counter(page) " / " counter(pages); font-size: 9pt; } } ``` ### Image / table breaks ```css img, table, figure { page-break-inside: avoid; break-inside: avoid; } /* Heading 가 단독 페이지 끝 X */ h1, h2, h3 { break-after: avoid; page-break-after: avoid; } ``` ### Receipts / invoices ```css @page { size: 80mm auto; /* thermal printer */ margin: 0; } @media print { body { font-family: monospace; font-size: 10pt; margin: 0; padding: 5mm; } .item-row { display: flex; justify-content: space-between; } .total { font-weight: bold; border-top: 1px dashed black; padding-top: 5mm; } } ``` ### 흑백 (cost saving) ```css @media print { * { color: black !important; background: white !important; box-shadow: none !important; } /* 단 logo / chart 등 색 유지 */ .logo, .chart { color: revert !important; background: revert !important; } } ``` ### 색 강제 (cost OK) ```css @media print { .alert { -webkit-print-color-adjust: exact; print-color-adjust: exact; /* Modern */ background: red !important; color: white !important; } } ``` ### Print button ```html ``` ```ts window.print(); // 인쇄 dialog 열기 window.addEventListener('beforeprint', () => { // Modify before print }); window.addEventListener('afterprint', () => { // 후처리 }); ``` ### Print preview (dev) ``` Chrome: File → Print (Cmd+P) → "Save as PDF" 또는 DevTools → 3-dot → More tools → Rendering → Emulate CSS media → print ``` → 인쇄 dialog 열어서 preview. ### Paged.js (advanced) ```bash yarn add pagedjs ``` ```ts import { Previewer } from 'pagedjs'; const previewer = new Previewer(); previewer.preview(); ``` → Browser 의 paged media 부족 = polyfill. CSS 표준 paged feature 더 많이 지원. ### PDF generation (server) ```ts // Puppeteer import puppeteer from 'puppeteer'; const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.goto('https://example.com/report?id=42'); await page.emulateMediaType('print'); const pdf = await page.pdf({ format: 'A4', printBackground: true, margin: { top: '2cm', bottom: '2cm', left: '2cm', right: '2cm' }, displayHeaderFooter: true, headerTemplate: '
My Report
', footerTemplate: '
/
', }); await browser.close(); fs.writeFileSync('report.pdf', pdf); ``` → HTML + CSS print → PDF. ### Receipt 인쇄 (browser → POS printer) ``` 대부분 = browser 의 일반 print dialog → 사용자가 thermal printer 선택. 또는 ESC/POS 직접 (Bluetooth / serial / USB). ``` ### CSS 실험 — bookmarks (PDF 안) ```css h1 { bookmark-level: 1; bookmark-label: content(); } h2 { bookmark-level: 2; } ``` → PDF 의 navigation pane. ### Page size 표준 ``` A4: 210mm × 297mm (most international) US Letter: 8.5in × 11in Receipt: 80mm wide Legal: 8.5in × 14in A5: 148mm × 210mm ``` ```css @page { size: letter; } @page { size: 80mm 200mm; } /* custom */ ``` ### Orientation ```css @page { size: landscape; } /* Specific page */ @page wide { size: A3 landscape; } .wide-table { page: wide; } ``` ### Test ```ts test('print stylesheet hides nav', () => { document.body.classList.add('print-preview'); // 또는 @media print 검증 // Puppeteer test await page.emulateMediaType('print'); const navVisible = await page.evaluate(() => { return getComputedStyle(document.querySelector('nav')).display !== 'none'; }); expect(navVisible).toBe(false); }); ``` ### React component ```tsx function ReportPage() { return ( <>
{/* Print content */}

Report

...
); } ``` ```css @media print { .no-print { display: none; } } ``` ## 🤔 의사결정 기준 | 작업 | 추천 | |---|---| | 일반 web print | @media print + @page | | PDF download | Puppeteer / Playwright server | | Receipt | 80mm @page + monospace | | Book / chapter | Paged.js / Prince XML | | 단순 export | window.print() + CSS | | Server batch PDF | Puppeteer + queue | ## ❌ 안티패턴 - **Print stylesheet 무**: 사용자 인쇄 시 깨짐. - **`!important` 없는 background hide**: 옛 browser 가 무시. - **`page-break-inside: auto` 큰 image**: 잘림. - **Widows / orphans 무시**: 작은 element 1줄 단독. - **Color 인쇄 강제 (사용자 흑백 원함)**: print-color-adjust: economy. - **JS 가 print 시 작동 가정**: 일부 browser 에서 X. - **Print preview 없이 prod**: 디자인 깨짐 모름. ## 🤖 LLM 활용 힌트 - @media print 항상 + .no-print class. - Server PDF = Puppeteer. - 책 / 복잡 = Paged.js. - Print preview 가 진짜 test. ## 🔗 관련 문서 - [[Frontend_CSS_Modern_Features]] - [[Frontend_A11y_Testing]] - [[Frontend_Image_Optimization]]