보고서

보고서 자동화

대시보드는 담당자가 보고, 보고서는 경영진과 고객사가 봅니다. 결재 라인에 올라가는 것은 결국 PDF 한 장입니다. 그래서 에너지 시스템에는 거의 항상 정기 보고서 자동 생성이 요구사항으로 들어옵니다.


1. 보고서 종류

주기대상주요 내용
일간현장 담당자전일 사용량, 피크, 알람
주간팀장전주 대비, 공정별 순위
월간경영진요금, 절감 실적, 탄소
연간경영진, 대외목표 달성률, ESG 지표
수시요청자임의 기간

2. 처리 흐름

스케줄 트리거
 → 기간 계산 (period_start, period_end)
 → 데이터 조회 (집계 테이블)
 → HTML 렌더링 (템플릿)
 → PDF 변환
 → 파일 저장 (로컬 / S3 / MinIO)
 → report 테이블 기록 (status=completed)
 → 알림 (선택)

생성 시작 시 status=generating으로 먼저 기록하고, 실패하면 failed와 오류 메시지를 남깁니다. 화면에서 재생성 버튼을 제공합니다.

3. 기간 계산

보고서에서 가장 많이 틀리는 부분입니다.

유형실행 예기간
PREVIOUS_DAY매일 07:00전일 00:00 ~ 당일 00:00
PREVIOUS_WEEK매주 월 07:00전주 월 00:00 ~ 이번주 월 00:00
PREVIOUS_MONTH매월 1일 07:00전월 1일 ~ 당월 1일
PREVIOUS_YEAR매년 1월 2일전년 1월 1일 ~ 당해 1월 1일

규칙:

  1. 종료 시각은 미포함(exclusive) 으로 통일합니다. [start, end)
  2. 타임존을 고정합니다 (Asia/Seoul)
  3. 하루 시작이 06:00인 현장이면 period_end_hour 같은 설정으로 컷 시각을 조정합니다
  4. 실행 시각은 집계 배치가 끝난 이후로 잡습니다. 00:05에 돌리면 전날 집계가 아직 안 끝났을 수 있습니다

4. 스케줄 관리

report_schedule
- schedule_type: DAILY / WEEKLY / MONTHLY / YEARLY
- hour, minute
- day_of_week (1=월 ~ 7=일)
- day_of_month
- month_of_year
- is_active
  • 스케줄은 DB에 두고, 스케줄러가 주기적으로 동기화합니다. 화면에서 바꾸면 재배포 없이 반영됩니다
  • 요일 체계가 라이브러리마다 다릅니다. DB는 1=월요일, APScheduler는 0=월요일, cron은 0=일요일. 변환 함수를 한 곳에 두세요
  • day_of_month=31이면 짧은 달에 실행이 안 됩니다. 말일 옵션(L)을 별도로 두는 것이 안전합니다
  • 서버를 여러 대 띄우면 같은 보고서가 중복 생성됩니다. 분산 락이나 단일 스케줄러 인스턴스를 보장해야 합니다

5. 템플릿 구성

월간 보고서의 일반적인 구성입니다.

  1. 요약: 총 사용량, 요금, 탄소, 전월/전년 동월 대비 증감률
  2. 사용량: 에너지원별, 사업장별, 공정별 사용량과 순위
  3. 피크와 품질: 최대수요, 부하율, 역률
  4. 절감 성과: 기준선 대비 절감 kWh, 금액, 누적 실적
  5. 탄소: Scope 1/2 배출량, 원단위
  6. 알람과 이슈: 주요 알람, 통신 장애, 결측률

숫자 포맷은 템플릿 필터로 통일합니다 (천 단위 구분, 소수 1자리, 퍼센트).

6. HTML → PDF

도구언어특징
WeasyPrintPythonCSS Paged Media 지원, 서버 친화적
Puppeteer / PlaywrightNode, Python브라우저 렌더링, 차트 JS 그대로
wkhtmltopdf바이너리오래됨, 유지보수 중단
@react-pdf/rendererReact컴포넌트로 PDF 작성
  • WeasyPrint는 JavaScript를 실행하지 않습니다. 차트는 서버에서 이미지(SVG/PNG)로 만들어 넣어야 합니다 (matplotlib, 서버 사이드 차트)
  • 한글 폰트를 서버에 설치하고 CSS @font-face로 지정하지 않으면 글자가 깨집니다
  • A4 기준 @page { size: A4; margin: 15mm; }, 표가 페이지를 넘을 때 thead 반복과 page-break-inside: avoid를 지정합니다

7. 저장

  • 파일 경로 규칙: reports/{company}/{yyyy}/{mm}/{report_id}.pdf
  • 스토리지는 전략 패턴으로 추상화합니다 (LocalStorage / S3Storage). 개발은 로컬, 운영은 S3/MinIO
  • DB에는 파일 경로, 포맷, 크기, 생성 시각만 저장하고 파일 자체는 넣지 않습니다
  • 다운로드는 API를 거치거나 presigned URL을 짧은 만료로 발급합니다

8. 재생성 정책

결측이 나중에 채워지거나 기준선이 확정되면 보고서 숫자가 바뀝니다.

  • 재생성 시 기존 파일을 덮어쓸지, 버전을 남길지 결정
  • 이미 외부로 발송된 보고서는 버전을 남기는 쪽이 안전
  • 보고서에 "생성 시각"과 "데이터 기준 시각"을 표기

9. 체크리스트

  1. 기간 경계가 exclusive인가
  2. 집계 배치 완료 후 실행되는가
  3. 요일/말일 변환이 맞는가
  4. 다중 인스턴스에서 중복 생성되지 않는가
  5. 한글 폰트가 PDF에 포함되는가
  6. 실패 시 재시도와 알림이 있는가
Previous
알람 설계