견적서나 주문서, 증명서처럼 종이로 뽑히는 화면을 만들면 결국 같은 요청이 온다. 페이지마다 위에 회사 이름을 넣어 달라, 아래에 "2 / 5" 같은 쪽 번호를 넣어 달라.
웹 페이지는 원래 쪽이라는 개념이 없어서, 이걸 처리하는 방법은 오랫동안 둘 중 하나였다. 머리글을 position: fixed로 띄워서 인쇄할 때 매 쪽에 반복되게 하거나, 아예 서버에서 PDF 라이브러리로 문서를 만들어 내려주거나. 앞쪽은 쪽 번호를 못 넣고, 뒤쪽은 화면용 HTML과 인쇄용 문서를 따로 관리하게 된다.
CSS에는 이걸 위한 문법이 따로 있다. @page 안에 @top-left, @bottom-center 같은 페이지 여백 상자를 두면, 인쇄된 쪽의 여백 자리에 글자를 넣을 수 있다. 크롬이 2024년 11월 131에서 이걸 넣었다. 이 글은 크롬 154에서 PDF로 뽑아 확인한 결과와, 아직 크롬 계열에서만 된다는 점을 어떻게 다뤄야 하는지에 대한 것이다.
먼저 지원 범위: Baseline 인 것은 @page 의 절반이다
@page를 "2024년 12월 Baseline"으로 소개하는 글이 있는데, 둘을 나눠 봐야 한다. web-platform-dx의 Baseline 자료(webstatus.dev)에서 2024-12-11에 Baseline이 된 항목은 Page setup, 즉 @page의 size와 margin이다. 사파리 18.2가 마지막으로 들어오면서 채워졌다. 여백 상자는 이 항목에 들어 있지 않고, MDN 호환성 데이터(browser-compat-data 8.1.4, 2026-10-01)로 보면 이렇다.
| 기능 | 크롬·엣지 | 파이어폭스 | 사파리 |
|---|---|---|---|
@page + size·margin | 15 | 95 | 18.2 |
이름 붙인 페이지 (page 속성) | 85 | 110 | 지원 |
여백 상자 @top-left 등 16종 | 131 | 미지원 | 미지원 |
string-set / string() | 미지원 | 미지원 | 미지원 |
여백 상자는 지금 크롬·엣지·삼성 인터넷 같은 크로미움 계열에서만 동작한다. 그러니 이건 "어디서나 쓰는 기능"이 아니라 "크롬에서 인쇄하면 머리글과 쪽 번호가 붙고, 다른 브라우저에서는 그것만 빠지는" 기능으로 써야 한다. 다행히 빠질 때 망가지는 것은 없다. 모르는 규칙은 무시되고 본문은 그대로 인쇄된다. 이 점은 뒤에서 다시 다룬다.
쪽 번호와 머리글 넣기
가상의 견적서 페이지에 이렇게 넣었다.
@page {
size: A4;
margin: 22mm 16mm 20mm;
@top-left {
content: "A업체 견적서";
font: 600 10pt sans-serif;
color: #1a4fa0;
border-bottom: 1px solid #1a4fa0;
vertical-align: bottom;
}
@bottom-center {
content: counter(page) " / " counter(pages);
font: 9pt sans-serif;
}
@bottom-right {
content: "출력일 2026-10-07";
font: 8pt sans-serif;
color: #999;
}
}
여백 상자는 이름 그대로 페이지 여백 안에 놓인다. margin을 22mm로 잡았으면 위쪽 상자의 높이가 22mm다. 위아래에 각각 왼쪽·가운데·오른쪽 셋, 좌우에 각각 위·가운데·아래 셋, 네 모서리까지 모두 16개 자리가 있다.
counter(page)는 현재 쪽, counter(pages)는 전체 쪽 수다. 따로 카운터를 선언할 필요가 없다. 상자 안에는 font, color, border, vertical-align이 그대로 먹었다. 위 예시의 파란 밑줄은 머리글 상자에 준 border-bottom이다.
첫 쪽·짝수 쪽·특정 구간만 다르게
첫 쪽에는 문서 제목이 크게 있으니 머리글을 빼고 싶을 때가 많다. :first로 첫 쪽만 고른다. 양면 인쇄용으로 왼쪽·오른쪽 쪽을 다르게 하려면 :left, :right를 쓴다.
@page :first {
@top-left { content: none; }
}
@page :left { @bottom-left { content: "왼쪽 페이지"; } }
@page :right { @bottom-left { content: "오른쪽 페이지"; } }
뽑아 보니 1쪽이 오른쪽, 2쪽이 왼쪽으로 번갈아 붙었다. 책처럼 첫 쪽을 오른쪽에서 시작하는 규칙이다.
견적서 뒤에 열이 많은 단가표가 붙는다면 그 부분만 가로로 뽑을 수 있다. 이건 여백 상자가 아니라 이름 붙인 페이지 기능이고, 크롬·파이어폭스·사파리 모두 지원한다.
@page wide {
size: A4 landscape;
@top-left { content: "부록 — 가로 표"; }
}
.wide { page: wide; }
.wide 구간이 시작되면 새 쪽으로 넘어가고, 그 쪽만 A4 가로로 나온다. 위 그림의 4쪽이 그렇게 나왔고, 머리글도 이름 붙인 페이지에 준 문구로 바뀌었다. PDF의 쪽 크기를 직접 읽어 보면 4쪽만 841.92 × 594.96pt, 나머지는 594.96 × 841.92pt였다.
position: fixed 로 하던 방법과 비교
기존 방법을 같은 조건으로 뽑아 봤다.
@media print {
header { position: fixed; top: 0; left: 0; right: 0; }
footer { position: fixed; bottom: 0; left: 0; right: 0; }
footer::after { content: "쪽 " counter(page); }
}
차이가 둘 있었다.
- 본문이 머리글 밑에 깔린다. 2쪽은 "품목 줄 41"부터 시작해야 하는데 첫 줄이 42로 보인다. 41번 줄은 머리글 뒤에 가려졌다.
fixed요소는 본문 흐름과 상관없이 쪽마다 같은 자리에 그려질 뿐이라, 본문이 그 자리를 비켜 주지 않는다. - 쪽 번호가 0이다. 일반 요소 안에서
counter(page)는 값이 없다. 쪽 번호는 여백 상자 안에서만 의미가 있다.
여백 상자는 본문 영역 바깥, 여백 안에 놓이기 때문에 겹칠 일이 없다. 본문은 @page의 margin이 남긴 자리 안에서만 흐른다.
브라우저 기본 머리글·바닥글과 겹칠 때
크롬 인쇄 대화상자에는 "머리글과 바닥글" 옵션이 있다. 켜면 위에 날짜와 문서 제목, 아래에 주소와 쪽 번호가 찍힌다. 여백 상자를 넣은 문서에서 이 옵션을 켜면 어떻게 되는지 쟀다. 헤드리스 크롬에서 같은 옵션(displayHeaderFooter)을 켜고 PDF를 뽑았다.
| 문서 상태 | 위쪽 | 아래쪽 |
|---|---|---|
| 여백 상자 없음 | 브라우저 날짜·제목 | 브라우저 주소·쪽 번호 |
아래쪽에만 @bottom-center | 브라우저 날짜·제목 | 내 쪽 번호만 |
| 위·아래 모두 있음 | 내 머리글만 | 내 바닥글만 |
:first에서 위쪽 상자를 content: none | 브라우저 날짜·제목이 다시 나옴 | 내 바닥글 |
:first에서 위쪽 상자를 content: "" | 비어 있음 | 내 바닥글 |
위쪽과 아래쪽이 따로 판단됐다. 그 쪽의 위 여백에 내 상자가 하나라도 있으면 브라우저 머리글이 빠지고, 하나도 없으면 브라우저 머리글이 들어온다. 그래서 첫 쪽 머리글을 none으로 지웠더니, 사용자가 옵션을 켜 둔 경우 그 자리에 브라우저의 날짜와 제목이 대신 찍혔다. 첫 쪽 위를 정말 비우고 싶다면 빈 문자열을 준다.
@page :first {
@top-left { content: ""; } /* none 이 아니라 빈 문자열 */
}
none은 상자를 아예 만들지 않고, ""는 내용이 빈 상자를 만든다. 브라우저 입장에서는 뒤쪽이 "작성자가 이 자리를 쓰고 있다"로 보인다.
장 제목을 머리글에 넣는 string-set 은 아직 안 된다
명세(CSS Generated Content for Paged Media)에는 본문의 제목을 읽어 머리글에 넣는 문법도 있다.
h2 { string-set: chapter content(); }
@page {
@top-right { content: string(chapter); }
}
크롬 154에서 CSS.supports('string-set', 'chapter content()')는 false였고, 뽑은 PDF의 오른쪽 위 상자도 비어 있었다. MDN 호환성 데이터에는 이 속성 항목 자체가 없다. 장마다 머리글이 바뀌어야 한다면 지금은 장마다 이름 붙인 페이지를 하나씩 만들어 머리글 문구를 따로 주는 수밖에 없다.
다른 브라우저에서는 어떻게 되나
여백 상자를 모르는 브라우저에서는 @top-left 같은 규칙만 무시된다. size와 margin은 모든 주요 브라우저가 이해하니, 쪽 크기와 여백은 어디서나 같고 머리글·쪽 번호만 빠진다. 그래서 이렇게 나눠 생각하면 된다.
- 머리글·쪽 번호가 있으면 좋은 문서 — 화면에서 인쇄 버튼을 눌러 뽑는 견적서, 주문 내역 같은 것. 여백 상자를 그냥 넣는다. 크롬 사용자는 쪽 번호가 붙은 문서를 받고, 나머지는 지금과 같다.
- 머리글·쪽 번호가 반드시 있어야 하는 문서 — 제출용 증명서, 계약서처럼 쪽 번호가 빠지면 안 되는 것. 브라우저 인쇄에 맡기지 말고 서버에서 PDF로 만들어 내려주는 편이 맞다. 크롬 헤드리스로 PDF를 만들고 있다면 같은 CSS가 그대로 먹으니 인쇄용 HTML 하나로 화면 인쇄와 PDF를 같이 쓸 수 있다.
파이어폭스와 사파리에서 직접 뽑아 보지는 못했다. 위 표의 지원 여부는 MDN 호환성 데이터 기준이다.
정리
- 2024년 12월 Baseline이 된 것은
@page의size·margin이다. 머리글·쪽 번호를 넣는 여백 상자는 크롬 131부터 크로미움 계열에서만 된다. @bottom-center { content: counter(page) " / " counter(pages); }한 줄이면 쪽 번호가 붙는다. 일반 요소 안의counter(page)는 0이다.position: fixed머리글은 다음 쪽부터 본문 첫 줄을 가렸다. 여백 상자는 여백 안에 놓여 겹치지 않는다.- 첫 쪽은
:first, 양면은:left/:right, 특정 구간의 가로 방향은 이름 붙인 페이지로 고른다. - 브라우저 기본 머리글은 위·아래를 따로 판단한다. 첫 쪽 위를 비울 때는
none이 아니라""를 준다. - 모르는 브라우저에서는 머리글·쪽 번호만 빠지고 본문은 그대로다.