[외주] 현금 승계 PDF로 만들기 - 4/8

https://wo-dbs.tistory.com/392

 

[외주] 클라이언트 앞에서 PT를 진행하다! - 4/8

오늘은 지금까지 정리한 요구사항과 그에 맞춰 개발한 결과물을 직접 보여드리는 날이었다. 평소와 다르게 이번에는 사장님뿐 아니라 총무님, 원장님 앞에서도 시연과 개발 현황을 함께 설명해

wo-dbs.tistory.com

 

위 글에서 정리했듯이, 미팅에서 받은 피드백 중 하나는 현금 승계 내역을 프로그램에서 바로 PDF로 출력할 수 있으면 좋겠다는 것이었다.

 

기존에는 총무님께서 현금 승계 내용을 종이 양식에 맞춰 따로 정리하고 출력해서 사용하고 있었다. 그런데 프로그램 안에 이미 같은 데이터를 입력해두고도, 다시 한 번 출력용 문서를 따로 만들어야 한다면 작업을 두 번 하게 된다. 그래서 이번에는 입력한 데이터를 그대로 출력 가능한 형태로 만들고, 그 상태에서 PDF까지 바로 뽑을 수 있도록 기능을 추가해보았다.

 

현금 승계의 원본

현금 승계는 실제로 다음과 같은 종이 양식 기반으로 관리되고 있었다.

이 문서를 보면서 내가 생각한 핵심은 단순했다. 이미 프로그램에 입력한 데이터가 있다면, 그 데이터를 다시 손으로 옮기지 않고 그대로 출력 문서로 만들 수 있어야 한다는 점이다. 즉 별도의 출력용 데이터를 다시 만드는 것이 아니라

  • 청구 기간
  • 호수
  • 구분
  • 기간
  • 금액
  • 사용량
  • 계좌번호
  • 비고

같은 입력값을 그대로 표 형태로 정리해서 출력하면 되는 구조라고 보았다.

 

기존 기능 상태

즉 데이터 입력과 조회는 가능했지만, 이를 출력용 문서로 바로 전환하는 단계는 아직 없었다. 그래서 이번 작업에서는 기존 입력 흐름은 유지하면서, 같은 데이터를 출력 전용 레이아웃으로 한 번 더 렌더링하고 PDF로 연결하는 방식을 추가하게 되었다

 

최종 UI

최종적으로는 다음과 같이 현금 승계 관리 화면에서 바로 PDF 출력을 할 수 있는 형태로 구성했다.

화면상에서는 다크 테마를 유지하고, 우측 상단에 PDF 출력 버튼을 두었다. 사용자는 평소에는 기존 관리 화면처럼 데이터를 입력하고 수정하면 되고, 출력이 필요할 때는 버튼만 눌러 브라우저의 인쇄 기능을 통해 PDF로 저장할 수 있도록 만들었다.

 

어떻게 만들었는가

구현 방식은 생각보다 단순하게 가져갔다. 핵심은 화면에 보이는 UI와 출력용 UI를 분리하는 것이었다.

 

1. 숨겨진 프린트용 HTML을 따로 두었다

먼저 페이지 안에 프린트 전용 HTML div를 하나 따로 만들었다. 이 div는 화면에는 보이지 않지만, DOM에는 항상 존재하도록 두었다.

<div id="cash-succession-print" style={{ display: "none" }}>
  {/* 흰 배경 + 테이블 형태의 순수 HTML */}
</div>

여기에는 실제 화면에 보이는 다크 테마 컴포넌트가 아니라, 흰 배경 + 테이블 중심의 출력용 레이아웃을 별도로 렌더링했다.

 

즉 사용자가 보는 관리 화면과, 프린터가 읽어야 하는 문서 화면을 분리한 것이다. 이렇게 한 이유는 출력물은 결국 종이에 가깝게 보여야 했기 때문이다. 화면용 UI를 그대로 인쇄하면 색상이나 간격, 테두리, 폰트가 출력 문서로는 애매할 수 있어서 아예 프린트 전용 구조를 따로 두는 편이 더 낫다고 판단했다.

 

 

2. PDF 출력 버튼에서 print 함수를 실행했다

버튼을 누르면 printCashSuccessions() 함수가 실행되도록 했다. 흐름은 다음과 같다.

  1. 숨겨진 프린트용 div를 찾는다.
  2. 새 브라우저 창을 연다.
  3. 새 창에 출력 전용 CSS를 주입한다.
  4. 숨겨진 div의 내용을 새 창 body에 복사한다.
  5. window.print()를 실행한다.

코드 흐름만 보면 이런 식이다.

function printCashSuccessions() {
  const printDiv = document.getElementById("cash-succession-print");
  const win = window.open("", "_blank");

  win.document.head.innerHTML = `... <style> 프린트용 CSS </style>`;
  win.document.body.innerHTML = printDiv.innerHTML;
  win.print();
}

즉 브라우저의 기본 인쇄 기능을 그대로 활용하되, 현재 앱 전체를 인쇄하는 것이 아니라 출력용으로 준비한 HTML만 별도 창에서 인쇄하도록 만든 것이다.

 

왜 이런 방식을 선택했는가

처음에는 더 단순하게 window.print()와 @media print를 이용해서 해결하려고 했다. 즉 현재 페이지 안에서 필요한 부분만 보이게 하고 나머지는 숨기는 전형적인 방식이다.

그런데 여기서 문제가 있었다. Next.js에서는 앱 전체가 보통 __next 루트 아래에 렌더링되는데, 프린트 시 CSS로 화면의 다른 요소를 숨기려다 보니 출력하려는 div까지 같이 영향을 받는 문제가 생겼다. 결국 “현재 화면 안에서 일부만 예쁘게 인쇄”하는 방식은 구조적으로 제어가 까다로웠다. 그래서 방향을 바꿨다. 현재 페이지를 억지로 인쇄 대상으로 쓰는 대신, 새 창에 출력용 HTML만 따로 넣어서 인쇄하는 방식으로 바꾼 것이다.

 

이 방식의 장점은 명확했다.

  • Next.js 앱 구조에 영향을 받지 않는다.
  • 출력할 내용만 따로 격리할 수 있다.
  • 흰 배경, 테두리, 폰트 크기 같은 프린트용 스타일을 자유롭게 줄 수 있다.
  • 브라우저의 PDF로 저장 기능과 자연스럽게 연결된다.

결국 이 기능은 “PDF 생성 라이브러리를 붙였다”기보다, 브라우저의 인쇄 기능을 활용하되 출력 대상을 별도로 구성한 것에 가깝다.

 

결과는 다음과 같이 잘 나온다 ㅎㅎ