# print-email — 인쇄와 이메일 브리프가 인쇄 가능한 문서(출력·PDF 저장·서명용 서류)나 이메일(뉴스레터·트랜잭션 메일·초대장)을 명시할 때만 연다. 둘 중 하나만 필요하면 해당 절만 읽는다. 이 문서는 `SKILL.md` 4-5(서류로서의 완성)의 인쇄 규칙을 일반화한다. 4-5는 프리셋이 서류·원장·콘솔 계열(장부, 시간표, 관리 화면)일 때만 도는 좁은 규칙이고, 여기서는 **그 밖의 어떤 화면이든 브리프가 인쇄를 요구하면** 적용할 수 있게 넓힌다. 4-5의 규칙(모달이 열려 있으면 그 서류만, `print-color-adjust: exact`)과 모순되지 않으며, 겹치는 부분은 새로 쓰지 않고 그대로 가져와 쓴다. ## 이 문서를 읽는 법 | 상황 | 읽을 곳 | |---|---| | 페이지를 인쇄·PDF 저장 가능하게 만든다 | §1 인쇄 | | 인쇄 결과가 맞는지 확인한다 | §2 인쇄 검증 | | 뉴스레터·트랜잭션 메일·초대장을 만든다 | §3 이메일 | | 이메일 클라이언트에서 실제로 확인한다 | §4 이메일 검증 | | 출시 전 한 번에 훑는다 | §5 체크리스트 | --- ## 0. 언제 여는가 - 브리프에 "인쇄", "PDF로 저장", "출력해서 제출", "서명 후 인쇄" 같은 말이 있으면 §1·§2. - 브리프에 "뉴스레터", "이메일로 보낸다", "가입 확인 메일", "영수증 메일", "초대 메일" 같은 말이 있으면 §3·§4. - 둘 다 아니면 이 문서는 읽지 않는다. 모든 웹 페이지를 기본적으로 인쇄·이메일 대응시키지 않는다 — 그것은 이 문서가 조건부로 존재하는 이유 자체를 없앤다. --- ## 1. 인쇄 언제: 브리프가 인쇄 가능한 문서를 요구할 때. 프리셋이 서류·원장·콘솔이면 `SKILL.md` 4-5를 먼저 따르고, 이 절은 그 규칙이 없는 일반 페이지(랜딩, 포트폴리오, 안내문 등)에 인쇄 대응을 추가할 때 쓴다. ### 1-1. `@media print` 뼈대 **프로젝트 계약**: 인쇄가 요구되면 `@media print` 블록으로 화면과 서류를 분리한다. 화면에서 필요한 앱 크롬(내비, 툴바, 플로팅 버튼, 사이드바)은 인쇄에서 의미가 없다 — 사용자는 종이 위에서 그것을 누를 수 없다. ```css @media print { header.app-nav, [role="toolbar"], .floating-action, .cookie-banner, button:not(.print-keep) { display: none !important; } } ``` **프로젝트 계약**: 모달이 열린 채로 인쇄가 시작되면 배경의 앱 화면이 아니라 **그 모달 내용만** 서류가 되어야 한다. `SKILL.md` 4-5의 기존 규칙을 그대로 쓴다. ```css @media print { body:has(dialog[open]) > *:not(:has(dialog[open])) { display: none !important; } } ``` **관찰 후보(시작값)**: `@page`의 여백은 브라우저 기본값에서 시작한다. 제본이나 클립이 필요한 서류만 여백을 늘린다. 정확히 몇 cm가 맞는지는 프로젝트마다 다르므로 숫자를 단정하지 않고, 실제 출력물로 확인한다(§2). ```css @page { margin: 2cm 1.5cm; /* 제본 쪽에 더 필요하면 margin-left 만 늘린다 — 실측 조정 */ } ``` ### 1-2. 콘텐츠를 서류에 맞게 바꾼다 **프로젝트 계약**: 카드·표 행·영수증 구획처럼 한 덩어리로 읽혀야 하는 블록은 페이지 경계에서 잘리지 않게 한다. ```css @media print { .card, tr, .receipt-line, figure { break-inside: avoid; } h2, h3 { break-after: avoid; /* 제목만 페이지 끝에 남고 본문이 다음 장으로 넘어가는 것을 막는다 */ } } ``` **프로젝트 계약**: 상태색처럼 의미를 나르는 색(경고·성공·실패 배지 등)만 `print-color-adjust: exact`로 보존한다. 나머지는 브라우저의 기본 인쇄 설정(그레이스케일·절약 모드 포함)에 맡긴다. 전체를 강제 컬러로 찍으면 사용자의 잉크 절약 설정을 무시하게 된다. ```css @media print { .status-badge, .status-dot { print-color-adjust: exact; -webkit-print-color-adjust: exact; } } ``` **하드 게이트**: 화면에서 잘라 보여주던 것(짧은 링크 텍스트, 접힌 섹션)은 종이 위에서 클릭이나 hover로 나머지에 닿을 수 없다. [typography.md](typography.md) §13의 "잘린 텍스트 — 전체 값에 도달하는 수단" 원칙과 같은 이유로, 인쇄에서는 그 값 자체를 펼쳐 보여줘야 정보 손실이 없다. ```css @media print { a[href^="http"]:not([href*="#"])::after { content: " (" attr(href) ")"; word-break: break-all; } } ``` 닫힌 `
`의 내용은 브라우저가 내부 슬롯으로 숨기므로, 자식 요소에 `display`를 주는 CSS로는 확실히 펼쳐지지 않는다. 인쇄 직전에 `open`을 켜고 끝나면 되돌린다. ```js let reopened = []; addEventListener('beforeprint', () => { reopened = [...document.querySelectorAll('details:not([open])')]; reopened.forEach((d) => { d.open = true; }); }); addEventListener('afterprint', () => { reopened.forEach((d) => { d.open = false; }); reopened = []; }); ``` ### 1-3. 서류의 신원 — 머리말·바닥말·출력일 **관찰 후보**: 여러 장으로 인쇄될 문서(보고서, 명세서)는 페이지 번호와 문서 제목을 각 장에 남긴다. 독자가 순서를 잃어버리지 않게 하는 목적이므로, 한 장짜리 인쇄물(영수증, 초대장)에는 강제하지 않는다. ```css @media print { @page { @bottom-right { content: counter(page) " / " counter(pages); } } .print-footer::after { content: "출력일 " attr(data-print-date); } } ``` `@page` 안의 여백 상자(`@bottom-right` 등)는 브라우저 지원이 고르지 않으므로 점진적 향상으로 둔다 — 지원하지 않는 브라우저에서는 페이지 번호가 빠질 뿐이다. 페이지 번호가 계약상 반드시 필요하면 인쇄 미리보기에서 대상 브라우저로 확인하고, 안 되면 PDF 생성 도구 쪽에서 넣는다. `attr(data-print-date)`는 CSS만으로 오늘 날짜를 넣을 수 없으므로, 인쇄 전에 JS로 `document.querySelector('.print-footer').dataset.printDate = new Date().toLocaleDateString('ko-KR')` 같은 식으로 채워 둔다. --- ## 2. 인쇄 검증 인쇄는 화면 렌더링과 다른 엔진 경로를 타기 때문에, 화면에서 괜찮아 보인다고 인쇄도 괜찮다고 결론 내리지 않는다. 1. **인쇄 미리보기를 캡처한다.** 브라우저의 인쇄 미리보기(Ctrl/Cmd+P)를 열어 각 페이지를 실제로 넘겨 본다. 브라우저 자동화 도구가 있으면 인쇄 미디어를 에뮬레이트해 스크린샷으로 남긴다 — 쓸 수 있는 도구는 [harness.md](harness.md)를 따른다. 2. **PDF로 저장해 연다.** "PDF로 저장"으로 실제 파일을 만들고 별도 뷰어로 열어, 브라우저 미리보기와 최종 PDF가 다르게 나오는 경우(특히 폰트·배경색)가 없는지 확인한다. 3. **경계에서 확인할 것**: 카드·표·영수증 구획이 페이지 경계에서 잘리지 않는지, 앱 크롬이 실제로 사라졌는지, 모달만 인쇄될 상황에서 배경 화면이 섞여 나오지 않는지, 링크 URL과 펼친 `details`가 실제로 종이에 보이는지, 상태색이 흑백 인쇄에서도 배지 모양(테두리·아이콘)만으로 구분되는지(색만으로 구분되면 흑백에서 정보가 사라진다). 4. **확인하지 못했으면 "미검증"이라고 적는다.** 소스만 보고 통과로 추정하지 않는다 — `audit-gate.md`의 미검증 관례를 그대로 따른다. --- ## 3. 이메일 언제: 브리프가 뉴스레터, 트랜잭션 메일(가입 확인·영수증·비밀번호 재설정), 초대장처럼 **이메일 클라이언트 안에서 열리는 HTML**을 명시할 때. 웹사이트에 "문의하기 → 메일 발송" 버튼이 있는 정도는 해당하지 않는다. 이메일 클라이언트는 웹 브라우저가 아니다. **관찰 후보 — 이 문서에서 실측하지 않았다.** 일부 클라이언트는 데스크톱 문서 편집기의 렌더링 경로를 그대로 쓰거나, 외부 스타일시트·최신 레이아웃 CSS를 지원하지 않거나 부분적으로만 지원한다는 관찰이 있다. 정확히 어떤 클라이언트가 무엇을 지원하는지는 이 문서에서 확인하지 않았으므로 클라이언트별 지원 수치를 단정하지 않는다. 아래는 그런 제약을 가정했을 때 안전한 기본값이며, 실제 확인은 §4를 따른다. ### 3-1. 레이아웃 **프로젝트 계약**: 폭은 600px 안팎의 단일 컬럼을 기본값으로 한다. 화면이 넓어도 이메일 본문 폭을 늘리지 않는다 — 클라이언트의 미리보기 창, 모바일 메일 앱 폭이 그보다 좁은 경우가 흔하다[SKILL-IMPECCABLE]. **프로젝트 계약**: 레이아웃은 표(``) 기반으로 짜고, 스타일은 `