PDF 페이지 번호 삽입 완벽 가이드 2026

PDF 페이지 번호 삽입: 기초부터 고급까지

간단한 숫자 표시부터 복잡한 형식(로마자, 부분 번호 지정, 위치 커스터마이징)까지 모든 페이지 번호 삽입 방법을 상세히 설명합니다.

1. 온라인 도구 (무료, 설치 불필요)

도구비용시간(100MB)기능추천
PDFKit무료3~5초숫자 페이지 번호개인용
ILovePDF월 $65초위치 선택, 형식소기업
SmallPDF월 $66초기본 번호 삽입개인
Sejda무료/월 $104초고급 포맷팅전문가
PDF2Go무료/월 $74초형식 선택개인

2. 데스크톱 소프트웨어

  • Adobe Acrobat Pro ($9.99/월) – 완벽한 페이지 번호, 배치 처리 가능
  • Preview (macOS) (무료) – 기본 기능만 지원
  • PDF-XChange Editor ($75) – 좌표 지정 가능
  • Foxit PhantomPDF (월 $9.99) – 고급 포맷팅

3. Python 라이브러리 (개발자용)

3.1 PyPDF2 + reportlab (기본)

from PyPDF2 import PdfReader, PdfWriter
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import letter
from io import BytesIO

def add_page_numbers_basic(input_pdf, output_pdf):
    """기본 페이지 번호 삽입 (하단 중앙)"""
    
    reader = PdfReader(input_pdf)
    writer = PdfWriter()
    
    for page_num, page in enumerate(reader.pages, start=1):
        # 페이지 번호 생성
        packet = BytesIO()
        can = canvas.Canvas(packet, pagesize=letter)
        can.setFont("Helvetica", 12)
        can.drawString(280, 30, str(page_num))  # 하단 중앙 (x=280, y=30)
        can.save()
        
        packet.seek(0)
        page_number_pdf = PdfReader(packet)
        page.merge_page(page_number_pdf.pages[0])
        
        writer.add_page(page)
    
    with open(output_pdf, "wb") as f:
        writer.write(f)
    
    print(f"✓ 페이지 번호 추가 완료: {output_pdf}")

# 사용
add_page_numbers_basic("input.pdf", "numbered.pdf")

3.2 pikepdf (고급)

import pikepdf
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import A4
from io import BytesIO

def add_page_numbers_advanced(input_pdf, output_pdf, 
                              position="bottom-center", 
                              format="numeric", 
                              start_page=1):
    """고급 페이지 번호 삽입"""
    
    with pikepdf.open(input_pdf) as pdf:
        width = float(pdf.pages[0].MediaBox[2])
        height = float(pdf.pages[0].MediaBox[3])
        
        for page_num, page in enumerate(pdf.pages, start=start_page):
            # 페이지 번호 형식 결정
            if format == "numeric":
                text = str(page_num)
            elif format == "roman_lower":
                text = to_roman(page_num).lower()
            elif format == "roman_upper":
                text = to_roman(page_num)
            elif format == "custom":
                text = f"Page {page_num}"
            
            # 위치 결정
            if position == "bottom-center":
                x, y = width / 2, 30
            elif position == "bottom-right":
                x, y = width - 50, 30
            elif position == "bottom-left":
                x, y = 50, 30
            elif position == "top-right":
                x, y = width - 50, height - 40
            else:
                x, y = width / 2, 30
            
            # 페이지 번호 생성
            packet = BytesIO()
            can = canvas.Canvas(packet, pagesize=(width, height))
            can.setFont("Helvetica", 11)
            can.drawString(x, y, text)
            can.save()
            
            packet.seek(0)
            number_pdf = PdfReader(packet)
            number_page = number_pdf.pages[0]
            
            # 병합
            page.Contents = pikepdf.Array([page.Contents, number_page.Contents])
    
    pdf.save(output_pdf)
    print(f"✓ 고급 페이지 번호 추가: {output_pdf}")

def to_roman(num):
    """숫자를 로마자로 변환"""
    val = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1]
    syms = ["M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"]
    roman_num = ""
    i = 0
    while num > 0:
        for _ in range(num // val[i]):
            roman_num += syms[i]
            num -= val[i]
        i += 1
    return roman_num

# 사용
add_page_numbers_advanced(
    "input.pdf", 
    "numbered.pdf",
    position="bottom-right",
    format="roman_lower",
    start_page=1
)

3.3 reportlab으로 새 PDF 생성 (가장 유연)

from reportlab.lib.pagesizes import A4
from reportlab.pdfgen import canvas
from reportlab.lib import colors
from PyPDF2 import PdfReader, PdfWriter

def add_page_numbers_flexible(input_pdf, output_pdf):
    """완전 커스터마이징 페이지 번호"""
    
    reader = PdfReader(input_pdf)
    writer = PdfWriter()
    
    for page_num, page in enumerate(reader.pages, start=1):
        width = float(page.MediaBox[2])
        height = float(page.MediaBox[3])
        
        # 페이지 번호 생성 (고급 포맷팅)
        packet = BytesIO()
        can = canvas.Canvas(packet, pagesize=(width, height))
        
        # 배경 (선택사항)
        can.setFillColor(colors.white)
        can.rect(width - 80, 20, 60, 20, fill=1)
        
        # 테두리
        can.setStrokeColor(colors.grey)
        can.rect(width - 80, 20, 60, 20)
        
        # 페이지 번호 텍스트
        can.setFont("Courier", 10)
        can.setFillColor(colors.black)
        can.drawString(width - 70, 28, f"p. {page_num}")
        
        # 페이지 번호와 총 페이지 수
        can.setFont("Helvetica", 9)
        can.drawString(50, height - 30, f"© 2024 | Page {page_num} of {len(reader.pages)}")
        
        can.save()
        
        packet.seek(0)
        page_number_pdf = PdfReader(packet)
        page.merge_page(page_number_pdf.pages[0])
        
        writer.add_page(page)
    
    with open(output_pdf, "wb") as f:
        writer.write(f)

add_page_numbers_flexible("input.pdf", "numbered.pdf")

4. 부분 페이지 번호 (특정 범위만)

def add_page_numbers_selective(input_pdf, output_pdf, 
                               start_numbering=1, 
                               skip_first_n=0,
                               skip_last_n=0):
    """특정 페이지만 번호 삽입"""
    
    reader = PdfReader(input_pdf)
    writer = PdfWriter()
    total_pages = len(reader.pages)
    
    for idx, page in enumerate(reader.pages):
        # 처음 skip_first_n 페이지 건너뛰기
        if idx < skip_first_n:
            writer.add_page(page)
            continue
        
        # 마지막 skip_last_n 페이지 건너뛰기
        if idx >= total_pages - skip_last_n:
            writer.add_page(page)
            continue
        
        # 페이지 번호 지정
        page_num = start_numbering + (idx - skip_first_n)
        
        # 페이지 번호 추가
        packet = BytesIO()
        can = canvas.Canvas(packet, pagesize=(595, 842))
        can.setFont("Helvetica", 12)
        can.drawString(280, 30, str(page_num))
        can.save()
        
        packet.seek(0)
        page_number_pdf = PdfReader(packet)
        page.merge_page(page_number_pdf.pages[0])
        
        writer.add_page(page)
    
    with open(output_pdf, "wb") as f:
        writer.write(f)
    
    print(f"✓ 부분 페이지 번호 완료: {output_pdf}")

# 사용: 첫 3페이지 제외, 마지막 2페이지 제외
add_page_numbers_selective(
    "input.pdf", 
    "numbered.pdf",
    start_numbering=1,
    skip_first_n=3,  # 커버 페이지 등
    skip_last_n=2    # 부록 등
)

5. 페이지 번호 형식 옵션

형식예시코드
숫자1, 2, 3, ...str(page_num)
로마자 (소문자)i, ii, iii, ...to_roman(page_num).lower()
로마자 (대문자)I, II, III, ...to_roman(page_num)
알파벳 (소문자)a, b, c, ...chr(96 + page_num)
알파벳 (대문자)A, B, C, ...chr(64 + page_num)
접두사 포함Page 1, Page 2, ...f"Page {page_num}"
범위 표시1/100, 2/100, ...f"{page_num}/{total}"
섹션 번호1-1, 1-2, 2-1, ...f"{section}-{page_num}"

6. 배치 처리 (대량 파일)

from pathlib import Path
from concurrent.futures import ThreadPoolExecutor

def batch_add_page_numbers(input_dir, output_dir):
    """폴더 내 모든 PDF에 페이지 번호 추가"""
    
    Path(output_dir).mkdir(exist_ok=True)
    pdf_files = list(Path(input_dir).glob("*.pdf"))
    
    def process_file(pdf_file):
        output_file = Path(output_dir) / pdf_file.name
        add_page_numbers_basic(str(pdf_file), str(output_file))
        return pdf_file.name
    
    # 병렬 처리 (4 스레드)
    with ThreadPoolExecutor(max_workers=4) as executor:
        results = list(executor.map(process_file, pdf_files))
    
    print(f"✓ {len(results)}개 파일 처리 완료")

# 사용
batch_add_page_numbers("/home/user/pdfs/input", "/home/user/pdfs/output")

7. Ghostscript를 이용한 페이지 번호 삽입

import subprocess
import os

def add_page_numbers_ghostscript(input_pdf, output_pdf):
    """Ghostscript를 이용한 페이지 번호 추가 (매우 빠름)"""
    
    # 임시 PostScript 파일 생성
    ps_code = """
    %!PS-Adobe-3.0
    
    /PageNumbers {
        /Helvetica findfont 12 scalefont setfont
        0.2 setgray
        10 20 moveto
        (Page: ) show
        /PageNumber pdfmark
    } def
    
    /EndPage {
        PageNumbers
    } def
    """
    
    temp_ps = "/tmp/page_numbers.ps"
    with open(temp_ps, "w") as f:
        f.write(ps_code)
    
    # Ghostscript 실행
    subprocess.run([
        "gs",
        "-sDEVICE=pdfwrite",
        "-dNOPAUSE",
        "-dBATCH",
        f"-sOutputFile={output_pdf}",
        temp_ps,
        input_pdf
    ], check=True)
    
    os.remove(temp_ps)
    print(f"✓ Ghostscript로 페이지 번호 추가: {output_pdf}")

# 주의: Ghostscript 필요 (apt-get install ghostscript)
# add_page_numbers_ghostscript("input.pdf", "numbered.pdf")

8. 클라우드 자동화 (AWS Lambda)

import boto3
import json
from io import BytesIO
from PyPDF2 import PdfReader, PdfWriter

s3 = boto3.client('s3')

def lambda_handler(event, context):
    """Lambda에서 페이지 번호 삽입"""
    
    try:
        # S3에서 PDF 다운로드
        bucket = event['Records'][0]['s3']['bucket']['name']
        key = event['Records'][0]['s3']['object']['key']
        
        obj = s3.get_object(Bucket=bucket, Key=key)
        pdf_content = obj['Body'].read()
        
        # 페이지 번호 추가
        reader = PdfReader(BytesIO(pdf_content))
        writer = PdfWriter()
        
        from reportlab.pdfgen import canvas
        
        for page_num, page in enumerate(reader.pages, start=1):
            packet = BytesIO()
            can = canvas.Canvas(packet, pagesize=(595, 842))
            can.setFont("Helvetica", 12)
            can.drawString(280, 30, str(page_num))
            can.save()
            
            packet.seek(0)
            page_number_pdf = PdfReader(packet)
            page.merge_page(page_number_pdf.pages[0])
            
            writer.add_page(page)
        
        # S3에 저장
        output = BytesIO()
        writer.write(output)
        output.seek(0)
        
        output_key = f"numbered/{key}"
        s3.put_object(
            Bucket=bucket,
            Key=output_key,
            Body=output.getvalue()
        )
        
        return {
            "statusCode": 200,
            "body": json.dumps(f"Success: {output_key}")
        }
    
    except Exception as e:
        return {
            "statusCode": 500,
            "body": json.dumps(f"Error: {str(e)}")
        }

9. 성능 비교

방법시간(100MB)메모리비용추천
PDFKit 온라인3~5초-무료개인
PyPDF22.5초150 MB무료개발자
pikepdf1.8초80 MB무료고성능
Adobe Acrobat5초450 MB$9.99/월전문가
Ghostscript2초60 MB무료배치
AWS Lambda1.5초자동$0.0001대규모

10. 추천 선택

  • 개인 (1~5개/월): PDFKit 온라인
  • 중소기업 (50~200개/월): PyPDF2 배치 + Cron
  • 대기업 (>500개/월): AWS Lambda
  • 높은 품질 필요: Adobe Acrobat
  • 매우 빠른 처리: Ghostscript CLI

11. FAQ

  • 가장 빠른 방법? Ghostscript (2초) > pikepdf (1.8초) > AWS Lambda (1.5초)
  • 가장 저렴한 방법? 무료 오픈소스: PyPDF2, pikepdf, Ghostscript
  • 가장 유연한 방법? reportlab (완전 커스터마이징)
  • 로마자 지원? 모든 Python 라이브러리 가능 (변환 함수 필요)
  • 부분 페이지만? skip_first_n, skip_last_n 파라미터 사용

12. 관련 자료

댓글

이 블로그의 인기 게시물

르무통 신발 단점: 구매 전에 꼭 알아야 할 정보

국민연금 예상수령액 조회 방법 및 계산법

국민연금 납부증명서 발급 방법 쉽게 정리