본문으로 건너뛰기

스크립트 가능한 워크플로우

Beancount와 Fava를 사용하여 스크립트 가능한 워크플로우를 통해 재무 작업을 자동화하고 맞춤형 보고서를 생성하는 방법을 알아보세요. 이 가이드는 초보자 수준의 자동화부터 고급 플러그인 개발까지 다룹니다.

Beancount(일반 텍스트 복식 부기 도구)와 Fava(그 웹 인터페이스)는 확장성과 스크립트 가능성이 매우 높습니다. 이들의 설계 덕분에 Python 스크립트를 작성하여 재무 작업을 자동화하고, 맞춤형 보고서를 생성하며, 알림을 설정할 수 있습니다. 한 사용자의 말을 빌리자면, "저는 제 데이터가 그렇게 편리한 형식으로 있다는 점과 원하는 만큼 자동화할 수 있다는 점을 정말 좋아합니다. 디스크에 있는 파일만큼 좋은 API는 없습니다; 통합하기 쉽습니다." 이 가이드는 초보자 수준의 자동화부터 고급 Fava 플러그인까지 스크립트 가능한 워크플로우를 만드는 방법을 안내합니다.

라이브 예제 원장을 살펴보세요:

새 탭에서 예제 원장 열기

bea 명령줄로 시작하기

Python을 작성하기 전에 bea가 이미 작업을 수행하는지 확인하세요. 원장을 검증하고, BQL 쿼리를 실행하며, 네 가지 재무 보고서를 생성하고 은행 내역을 가져오며, 전역 --json 옵션은 각각을 셸이 jq로 파이프할 수 있는 구문 분석 가능한 봉투로 변환합니다. 종료 코드는 예약된 작업이 분기하는 계약이므로 cron이나 CI에는 로더 스크립트가 전혀 필요하지 않습니다. 타겟 해석, 봉투 및 종료 코드 분기에 대해서는 bea로 부기 자동화를 참조하고, CLI가 제공하지 않는 맞춤 계산이 필요할 때 여기로 돌아오세요.

시작하기: Python 스크립트로 Beancount 실행

구체적인 작업에 들어가기 전에 스크립팅 패키지를 설치하세요(pip install beancount beanquery beangulp). Beancount는 Python으로 작성되었기 때문에 자체 스크립트에서 라이브러리로 사용할 수 있습니다. 아래 스크립트는 Beancount 3.2.3, beanquery 0.2.0, beangulp 0.2.0으로 실행되었습니다. 일반적인 접근 방식은 다음과 같습니다:

  • Beancount 원장 로드: Beancount의 로더를 사용하여 .beancount 파일을 Python 객체로 구문 분석합니다. 예를 들어:

    from beancount import loader
    entries, errors, options = loader.load_file("myledger.beancount")
    if errors:
        for error in errors:
            print(error)
        raise SystemExit(1)

    로더는 항목과 오류를 함께 반환합니다. 불균형하거나 유효하지 않은 파일도 항목을 반환하므로 errors를 확인하고 데이터를 신뢰하기 전에 중지하세요. 모든 계정, 거래, 잔액이 이제 코드에서 접근 가능합니다.

  • Beancount 쿼리 언어(BQL) 활용: 수동으로 반복하는 대신 데이터에 SQL과 유사한 쿼리를 실행할 수 있습니다. 쿼리는 별도의 beanquery 패키지에 있습니다. Beancount 3.2.3에는 beancount.query 모듈이 없습니다. 예를 들어 월별 총 지출을 얻으려면 로드된 항목을 연결하고 쿼리를 직접 실행하세요:

    import beanquery
     
    conn = beanquery.connect("beancount:", entries=entries, errors=errors, options=options)
    cur = conn.execute(
        "SELECT year, month, sum(position) WHERE account ~ 'Expenses' GROUP BY year, month"
    )
    for row in cur.fetchall():
        print(row)

    이는 데이터를 집계하기 위해 beanquery를 사용합니다. bean-query 명령 뒤에 있는 동일한 엔진이지만 여기서는 스크립트에서 직접 호출합니다. 이렇게 하면 루프에서 외부 명령을 셸 호출하는 것을 피할 수 있습니다.

  • 프로젝트 구조 설정: 원장 옆에 스크립트를 구성하세요. 일반적인 레이아웃은 임포터(외부 데이터 가져오기/구문 분석용), 보고서 또는 쿼리(분석 스크립트용), 문서(다운로드한 명세서 저장) 디렉토리를 두는 것입니다. 예를 들어, 한 사용자는 다음을 유지합니다:

    • importers/ – 맞춤 Python 가져오기 스크립트(테스트 포함),
    • queries/ – 보고서 생성 스크립트(python3 queries/...로 실행 가능),
    • documents/ – 계정별로 정리된 다운로드된 은행 CSV/PDF.

    이 설정을 통해 스크립트를 수동으로 실행하거나(cron 또는 작업 실행기를 통해) 예약하여 워크플로우를 자동화할 수 있습니다.

조정 작업 자동화

조정이란 원장이 외부 기록(은행 명세서, 신용 카드 보고서 등)과 일치하는지 확인하는 것을 의미합니다. Beancount의 일반 텍스트 원장과 Python API를 사용하면 이 프로세스의 많은 부분을 자동화할 수 있습니다.

거래 가져오기 및 일치시키기(초보자)

초보자에게 권장되는 접근 방식은 별도의 beangulp 패키지의 임포터를 사용하는 것입니다. Beancount 3.2.3에는 beancount.ingest 모듈과 bean-extract 명령이 없습니다. 특정 형식(CSV, OFX, PDF 등)을 구문 분석하고 거래를 생성하기 위해 beangulp.Importer를 상속하는 작은 Python 클래스를 작성합니다. 그런 다음 소유한 짧은 인제스트 스크립트로 이를 구동합니다:

  • 은행의 CSV 형식에 맞는 임포터(identify(), account(), extract() 메서드를 가진 Python 클래스)를 작성합니다.
  • 임포터를 등록하는 인제스트 스크립트를 추가합니다. 스크립트 자체는 identify, extract, archive 명령을 제공합니다. 예를 들어, 한 워크플로우는 ~/Downloads의 모든 파일에 대해 extract를 실행하고 임시 파일에 거래를 출력합니다.
  • 수동으로 검토하고 임시 파일의 거래를 메인 원장에 복사한 다음 bean-check를 실행하여 잔액이 조정되는지 확인합니다.

최소 예: date,description,amount 열이 있는 statement.csv를 이 임포터(checking_importer.py)로 구문 분석합니다:

import csv
import datetime
from beancount.core import data
from beancount.core.amount import Amount
from beancount.core.number import D
import beangulp
 
 
class CheckingImporter(beangulp.Importer):
    def identify(self, filepath: str) -> bool:
        return filepath.endswith("statement.csv")
 
    def account(self, filepath: str) -> str:
        return "Assets:Bank:Checking"
 
    def extract(self, filepath: str, existing):
        entries = []
        with open(filepath, newline="") as f:
            for row in csv.DictReader(f):
                date = datetime.date.fromisoformat(row["date"])
                amount = Amount(D(row["amount"]), "USD")
                meta = data.new_metadata(filepath, 0)
                entries.append(
                    data.Transaction(
                        meta, date, "*", None, row["description"],
                        data.EMPTY_SET, data.EMPTY_SET, [
                            data.Posting("Expenses:Food:Groceries", amount,
                                         None, None, None, None),
                            data.Posting("Assets:Bank:Checking",
                                         Amount(-amount.number, "USD"),
                                         None, None, None, None),
                        ]))
        return entries

인제스트 스크립트(ingest.py)는 이를 연결합니다:

from checking_importer import CheckingImporter
from beangulp import Ingest
 
ingest = Ingest([CheckingImporter()])
 
if __name__ == "__main__":
    ingest()

다운로드한 파일에 대해 실행합니다. 로컬 CSV에는 자격 증명이 필요하지 않습니다:

python ingest.py identify statement.csv
python ingest.py extract statement.csv -o new.beancount

identify는 파일에 대해 checking_importer.CheckingImporter를 보고합니다. extract는 거래를 Beancount 형식으로 작성합니다:

2024-01-08 * "Grocery Store"
  Expenses:Food:Groceries   120.00 USD
  Assets:Bank:Checking     -120.00 USD

new.beancount를 검토하고 항목을 메인 원장에 복사한 다음 bean-check를 실행하세요.

일회성 작업에는 임포터를 건너뛰세요

단일 명세서를 변환하기 위해 임포터를 작성할 필요는 없습니다. 파일을 CSV to Beancount 변환기에 붙여넣거나, .ofx, .qfx.qif 다운로드에는 OFX & QIF to Beancount를 사용하세요. 둘 다 브라우저에서 완전히 실행되므로 명세서가 기기를 떠나지 않습니다.

이 프로세스는 여전히 검토 단계를 포함하지만, 구문 분석 및 형식 지정 작업의 대부분은 자동화됩니다. 임포터 스크립트는 카테고리를 자동 할당하고 차이를 잡기 위해 잔액 어서션(예상 잔액 명세)을 설정할 수도 있습니다. 예를 들어, 가져온 후 2025-04-30 balance Assets:Bank:Checking 1234.56 USD와 같은 줄이 있을 수 있으며 이는 마감 잔액을 어서션합니다. bean-check를 실행하면 Beancount가 이러한 모든 잔액 어서션이 올바른지 검증하고, 누락되거나 중복된 거래가 있으면 오류를 표시합니다. 이는 모범 사례입니다: 각 명세서 기간에 대해 잔액 어서션을 자동 생성하여 컴퓨터가 미조정 차이를 찾아내도록 하세요.

맞춤 조정 스크립트(중급)

더 많은 제어를 위해 은행의 거래 목록(CSV 또는 API를 통한)을 원장 항목과 비교하는 맞춤 Python 스크립트를 작성할 수 있습니다:

  1. 외부 데이터 읽기: Python의 csv 모듈(또는 Pandas)을 사용하여 은행의 CSV 파일을 구문 분석합니다. 데이터를 날짜, 금액, 설명이 각각 있는 거래 목록으로 정규화합니다.
  2. 원장 거래 로드: 앞서 보여준 대로 loader.load_file을 사용하여 모든 원장 항목을 가져옵니다. 이 목록을 관심 계정(예: 당좌 계정) 및 명세서 기간으로 필터링합니다.
  3. 비교 및 불일치 찾기:
  • 각 외부 거래에 대해 원장에 동일한 항목이 존재하는지 확인합니다(날짜와 금액, 설명으로 일치). 없으면 "새로운" 것으로 표시하고 검토를 위해 Beancount 형식의 거래로 출력할 수 있습니다.
  • 반대로 해당 계정의 원장 항목 중 외부 소스에 없는 항목을 식별합니다 – 이는 입력 오류 또는 은행에서 아직 결제되지 않은 거래일 수 있습니다.
  1. 결과 출력: 보고서를 인쇄하거나 누락된 거래가 있는 새 .beancount 스니펫을 생성합니다.

예를 들어, 커뮤니티 스크립트 **reconcile.py**는 정확히 이 작업을 수행합니다: Beancount 파일과 입력 CSV가 주어지면 가져와야 할 새 거래 목록과 입력에 없는 기존 원장 포스팅(잘못 분류되었을 가능성이 있는)을 인쇄합니다. 이러한 스크립트를 사용하면 월간 조정은 스크립트를 실행하고 제안된 거래를 원장에 추가하는 것만큼 간단해집니다. 한 Beancount 사용자는 _"매월 모든 계정에 대해 조정 프로세스를 수행"_하고 증가하는 Python 코드 모음을 사용하여 가져오기 및 조정의 수동 작업을 대부분 제거한다고 말합니다.

팁: 조정 중 정확성을 위해 Beancount의 도구를 활용하세요:

  • 앞서 언급한 잔액 어서션을 사용하여 계정 잔액에 대한 자동 검사를 수행하세요.
  • 원하는 경우 pad 지시어를 사용하여 작은 반올림 차이에 대한 균형 잡기 항목을 자동 삽입할 수 있습니다(주의해서 사용).
  • 임포터 또는 조정 로직에 대한 단위 테스트를 작성하세요(Beancount는 테스트 헬퍼를 제공). 예를 들어, 한 워크플로우는 샘플 CSV를 가져와 예상 거래로 실패하는 테스트를 작성한 다음 모든 테스트가 통과할 때까지 임포터를 구현하는 것이었습니다. 이는 다양한 경우에 대해 가져오기 스크립트가 올바르게 작동하도록 보장합니다.

맞춤 보고서 및 요약 생성

Fava는 많은 표준 보고서(손익계산서, 대차대조표 등)를 제공하지만, 스크립트를 사용하여 맞춤 보고서를 만들 수 있습니다. 이는 단순한 콘솔 출력부터 풍부한 형식의 파일이나 차트까지 다양합니다.

보고서용 데이터 쿼리(초보자)

기본 수준에서 Beancount 쿼리 언어(BQL)를 사용하여 요약 데이터를 얻고 인쇄하거나 저장할 수 있습니다. 예를 들어:

  • 현금 흐름 요약: 쿼리를 사용하여 순 현금 흐름을 계산합니다. "현금 흐름"은 특정 기간 동안 특정 계정의 잔액 변화로 정의될 수 있습니다. BQL을 사용하면 다음과 같이 할 수 있습니다:

    SELECT year, month, sum(position)
    WHERE account ~ 'Income' OR account ~ 'Expenses'
    GROUP BY year, month

    이는 모든 수입 및 지출 포스팅을 월별로 순액화합니다. 정규식을 가진 ~로 필터링하세요: LIKE는 beanquery 0.2.0에서 구문 오류입니다. 포스팅은 amount가 아닌 position을 가집니다. 각 행은 하나의 Inventory를 가지므로 각 통화는 변환되지 않고 별도로 나열됩니다. 수입은 음수로, 지출은 양수로 나옵니다. 이 결과를 bean-query CLI 또는 앞서 보여준 beanquery Python API를 통해 실행하고 결과를 형식화할 수 있습니다.

  • 카테고리별 지출 보고서: 카테고리별 총 지출을 쿼리합니다:

    SELECT account, sum(position)
    WHERE account ~ 'Expenses'
    GROUP BY account
    ORDER BY sum(position) ASC

    이는 카테고리별 지출 테이블을 제공합니다. 각 합계는 원래 통화의 Inventory입니다. 집계를 round()로 감싸지 마세요: round(inventory, int) 함수가 없으므로 round(sum(position), 2)는 컴파일되지 않습니다. 스크립트에서 여러 쿼리를 실행하고 결과를 텍스트, CSV 또는 추가 처리를 위해 JSON으로 출력할 수 있습니다.

한 사용자는 Fava나 스크립트로 재무 데이터를 분석하는 것이 _"사소한 일"_이며 Python 스크립트 하나로 Query Language를 통해 Beancount에서 데이터를 가져와 Pandas DataFrame에 넣어 맞춤 보고서를 준비한다고 말합니다. 예를 들어, 쿼리로 월별 합계를 가져온 다음 Pandas/Matplotlib를 사용하여 시간 경과에 따른 현금 흐름 차트를 그릴 수 있습니다. BQL과 데이터 과학 라이브러리의 조합으로 Fava가 기본적으로 제공하는 것 이상의 보고서를 만들 수 있습니다.

고급 보고(차트, 성과 등)

더 고급 요구 사항의 경우 스크립트가 투자 성과와 같은 지표를 계산하거나 시각적 출력을 만들 수 있습니다:

  • 투자 성과(IRR/XIRR): 원장에 모든 현금 흐름(매수, 매도, 배당금)이 포함되어 있으므로 포트폴리오 수익률을 계산할 수 있습니다. 예를 들어, 투자 계정의 거래를 필터링한 다음 내부 수익률을 계산하는 스크립트를 작성할 수 있습니다. 현금 흐름 데이터가 주어지면 IRR을 계산하는 라이브러리(또는 공식)가 있습니다. 일부 커뮤니티 개발 Fava 확장(예: PortfolioSummary 또는 fava_investor)은 정확히 이 작업을 수행하여 투자 포트폴리오의 IRR 및 기타 지표를 계산합니다. 스크립트로는 기여/인출 시리즈와 종료 가치에 IRR 함수(NumPy 또는 자체)를 사용할 수 있습니다.

  • 다중 기간 또는 맞춤 지표: 매월 저축률(소득 대비 저축 비율) 보고서를 원하십니까? Python 스크립트가 원장을 로드하고 모든 수입 계정과 모든 지출 계정을 합산한 다음 저축 = 수입 - 지출 및 백분율을 계산할 수 있습니다. 이는 멋진 표를 출력하거나 기록용 HTML/Markdown 보고서를 생성할 수도 있습니다.

  • 시각화: Fava 외부에서 차트를 생성할 수 있습니다. 예를 들어, 스크립트에서 matplotlib 또는 altair를 사용하여 원장 데이터로 시간 경과에 따른 순자산 차트를 만들 수 있습니다. 원장에 모든 과거 잔액이 있으므로(또는 항목을 반복하여 누적할 수 있으므로) 시계열 플롯을 생성할 수 있습니다. 이러한 차트를 이미지나 인터랙티브 HTML로 저장하세요. (앱 내 시각화를 선호하면 아래 Fava 확장 섹션을 참조하여 Fava 내에서 차트를 추가하세요.)

출력 옵션: 보고서를 전달하는 방법을 결정하세요:

  • 일회성 분석의 경우 화면에 인쇄하거나 CSV/Excel 파일로 저장하는 것으로 충분할 수 있습니다.
  • 대시보드의 경우 데이터로 HTML 파일을 생성하는 것을 고려하세요(템플릿 라이브러리 Jinja2 또는 간단한 Markdown 작성 사용) 브라우저에서 열 수 있습니다.
  • 탐구보다 자동화에 더 적합하지만 대화형 보고 환경을 위해 Jupyter Notebook과 통합할 수도 있습니다.

원장에서 알림 트리거

스크립트 가능한 워크플로우의 또 다른 강력한 사용법은 재무 데이터의 조건에 따라 알림을 설정하는 것입니다. 원장이 정기적으로 업데이트되고(예정된 청구서나 예산과 같은 미래 날짜 항목 포함 가능) 있으므로 스크립트로 스캔하여 중요한 이벤트를 알림받을 수 있습니다.

낮은 계정 잔액 경고

초과 인출을 피하거나 최소 잔액을 유지하기 위해 계정(예: 당좌 또는 저축)이 임계값 아래로 떨어지면 알림을 원할 수 있습니다. 구현 방법은 다음과 같습니다:

  1. 현재 잔액 확인: 로더를 통해 entries를 로드한 후 관심 계정의 최신 잔액을 계산합니다. 포스팅을 집계하거나 쿼리를 사용하여 이를 수행할 수 있습니다. 예를 들어, 특정 계정의 잔액에 대한 BQL 쿼리를 사용하세요:

    SELECT sum(position) WHERE account = 'Assets:Bank:Checking'

    이는 해당 계정의 현재 잔액(모든 포스팅의 합)을 반환합니다. 대안으로 Beancount의 내부 함수를 사용하여 대차대조표를 만들 수 있습니다. 예를 들어:

    from beancount.core import realization
    tree = realization.realize(entries)
    acct = realization.get_or_create(tree, "Assets:Bank:Checking")
    balance = acct.balance  # an Inventory of commodities

    항목만 전달하세요: 두 번째 매개변수는 옵션 맵이 아닌 min_accounts입니다. 그런 다음 숫자 값을 추출하세요(예: balance.get_currency_units('USD')는 USD의 Decimal 금액을 반환). 쿼리 집계처럼 잔액은 각 통화를 별도로 유지합니다. 그러나 대부분의 경우 쿼리를 사용하는 것이 더 간단합니다.

  2. 임계값 확인: 잔액을 사전 정의된 한도와 비교합니다. 아래로 떨어지면 알림을 트리거합니다.

  3. 알림 트리거: 콘솔에 경고를 인쇄하는 것만큼 간단할 수 있지만 실제 알림의 경우 이메일이나 푸시 알림을 보낼 수 있습니다. 이메일(smtplib를 통해) 또는 IFTTT나 Slack의 웹훅 API와 같은 서비스와 통합하여 알림을 푸시할 수 있습니다. 예를 들어:

    if balance < 1000:
        send_email("Low balance alert", f"Account XYZ balance is {balance}")

    (이메일 서버 세부 정보로 send_email을 구현하세요.)

이 스크립트를 매일 실행하면(cron 작업 또는 Windows 작업 스케줄러를 통해) 사전 경고를 받을 수 있습니다. 원장을 사용하므로 방금 추가한 모든 거래를 고려할 수 있습니다.

다가오는 지불 마감일

Beancount를 사용하여 청구서나 마감일을 추적하는 경우 미래 지불을 표시하고 스크립트가 알림을 보내도록 할 수 있습니다. Beancount에서 다가오는 의무를 나타내는 두 가지 방법:

  • 이벤트: Beancount는 임의 날짜 메모를 위한 event 지시어를 지원합니다. 예를 들어:

    2025-05-10 event "BillDue" "Mortgage payment due"

    이는 잔액에 영향을 미치지 않지만 날짜와 레이블을 기록합니다. 스크립트는 entries에서 Event.type == "BillDue"(또는 선택한 맞춤 유형)인 Event 항목을 스캔하고 날짜가 오늘부터 7일 이내인지 확인할 수 있습니다. 그렇다면 알림을 트리거합니다(이메일, 알림 또는 팝업).

  • 미래 거래: 일부 사용자는 예정된 지불과 같은 미래 날짜 거래를 입력합니다. 이는 날짜가 지나기 전까지 잔액에 표시되지 않습니다(미래 날짜로 보고서를 실행하지 않는 한). 스크립트는 가까운 미래의 거래를 찾아 나열할 수 있습니다.

이를 사용하여 실행 시 곧 만기가 되는 작업이나 청구서 목록을 출력하는 "tickler" 스크립트를 만들 수 있습니다. Google 캘린더나 작업 관리자와 같은 API와 통합하여 거기에 알림을 자동 생성할 수 있습니다.

이상 탐지

알려진 임계값이나 날짜 외에도 비정상 패턴에 대한 맞춤 알림을 스크립트할 수 있습니다. 예를 들어, 일반적으로 월별 지출이 발생하지 않았거나(청구서를 잊어버렸을 수 있음) 카테고리의 지출이 이번 달 비정상적으로 높은 경우 스크립트가 이를 플래그할 수 있습니다. 이는 일반적으로 최근 데이터를 쿼리하고 기록과 비교하는 것을 포함합니다(고급 주제일 수 있음 – 통계 또는 ML 사용 가능).

실제로 많은 사용자가 조정에 의존하여 이상을 발견합니다(예상치 못한 거래). 은행 알림(각 거래에 대한 이메일 등)을 받는 경우 스크립트로 이를 구문 분석하여 Beancount에 자동으로 추가하거나 최소한 기록되었는지 확인할 수 있습니다. 한 열성 사용자는 은행에서 거래 알림 이메일을 보내도록 설정하고 이를 구문 분석하여 원장에 자동으로 추가할 계획을 세웠습니다. 이러한 이벤트 중심 알림은 기록되지 않은 거래가 없도록 보장할 수 있습니다.

Fava를 맞춤 플러그인 및 뷰로 확장

Fava는 확장 시스템을 통해 이미 스크립트 가능합니다. 자동화나 보고서를 웹 인터페이스에 직접 통합하려면 Python으로 Fava 확장(플러그인)을 작성할 수 있습니다.

Fava 확장 작동 방식: 확장은 fava.ext.FavaExtensionBase를 상속하는 클래스를 정의하는 Python 모듈입니다. Beancount 파일에서 맞춤 옵션으로 등록합니다. 예를 들어, myextension.pyMyAlerts(FavaExtensionBase) 클래스가 있으면 원장에 다음을 추가하여 활성화할 수 있습니다:

1970-01-01 custom "fava-extension" "myextension"

Fava가 로드될 때 해당 모듈을 가져와 MyAlerts 클래스를 초기화합니다.

확장은 여러 작업을 수행할 수 있습니다:

  • : Fava의 수명 주기 이벤트에 연결할 수 있습니다. 예를 들어, after_load_file()은 원장이 로드된 후 호출됩니다. 이를 사용하여 검사를 실행하거나 데이터를 미리 계산할 수 있습니다. Fava 내에서 낮은 잔액 검사를 구현하려면 after_load_file이 계정 잔액을 반복하고 경고를 저장할 수 있습니다(UI에 표시하려면 FavaAPIError를 발생시키거나 Javascript를 사용하여 알림을 표시하는 등 더 많은 작업이 필요할 수 있음).
  • 맞춤 보고서/페이지: 확장 클래스가 report_title 속성을 설정하면 Fava가 사이드바에 새 페이지를 추가합니다. 그런 다음 페이지 콘텐츠에 대한 템플릿(HTML/Jinja2)을 제공합니다. 이는 Fava가 기본적으로 제공하지 않는 대시보드나 요약과 같은 완전히 새로운 뷰를 만드는 방법입니다. 확장은 필요한 데이터를 수집할 수 있으며(self.ledger에 모든 항목, 잔액 등이 있음) 템플릿을 렌더링할 수 있습니다.

예를 들어, Fava의 내장 portfolio_list 확장은 포트폴리오 포지션을 나열하는 페이지를 추가합니다. 커뮤니티 확장은 더 나아갑니다:

  • 대시보드: fava-dashboards 플러그인은 맞춤 차트와 패널(Apache ECharts와 같은 라이브러리 사용)을 정의할 수 있습니다. 실행할 쿼리의 YAML 구성을 읽고 Beancount를 통해 실행하며 Fava에서 동적 대시보드 페이지를 생성합니다. 본질적으로 Beancount 데이터와 JavaScript 차트 라이브러리를 연결하여 인터랙티브 시각화를 생성합니다.
  • 포트폴리오 분석: PortfolioSummary 확장(사용자 기여)은 투자 요약(계정 그룹화, IRR 계산 등)을 계산하고 Fava UI에 표시합니다.
  • 거래 검토: 또 다른 확장인 fava-review는 시간 경과에 따른 거래 검토(예: 영수증을 놓치지 않았는지 확인)를 돕습니다.

직접 간단한 확장을 만들려면 FavaExtensionBase를 상속하는 것부터 시작하세요. 예를 들어, 페이지를 추가하는 최소 확장은 다음과 같을 수 있습니다:

from fava.ext import FavaExtensionBase
 
class HelloReport(FavaExtensionBase):
    report_title = "Hello World"
 
    def __init__(self, ledger, config):
        super().__init__(ledger, config)
        # any initialization, perhaps parse config if provided
 
    def after_load_file(self):
        # (optional) run after ledger is loaded
        print("Ledger loaded with", len(self.ledger.entries), "entries")

이를 hello.py에 배치하고 원장에 custom "fava-extension" "hello"를 추가하면 Fava에 "Hello World" 페이지가 표시됩니다(페이지 콘텐츠를 정의하려면 templates 하위 폴더에 HelloReport.html 템플릿 파일도 필요하지만, 확장이 훅만 사용하는 경우에는 필요 없음). 템플릿은 확장 클래스에 첨부한 데이터를 사용할 수 있습니다. Fava는 Jinja2 템플릿을 사용하므로 해당 템플릿에서 데이터를 HTML 테이블이나 차트로 렌더링할 수 있습니다.

참고: Fava의 확장 시스템은 강력하지만 "불안정"(변경 가능)으로 간주됩니다. 맞춤 페이지를 만들려면 웹 개발(HTML/JS)에 대한 일부 친숙함이 필요합니다. 목표가 스크립트나 분석 실행이라면 외부 스크립트로 유지하는 것이 더 쉬울 수 있습니다. 워크플로우에 맞춤형 앱 내 경험을 원할 때 Fava 확장을 사용하세요.

타사 API 및 데이터 통합

스크립트 가능한 워크플로우의 장점 중 하나는 외부 데이터를 가져올 수 있다는 것입니다. 일반적인 통합은 다음과 같습니다:

  • 환율 및 상품: Beancount는 보고서를 결정적으로 유지하기 위해 설계상 가격을 자동으로 가져오지 않지만, 요율을 제공하기 위해 가격 지시어를 제공합니다. 이러한 가격 가져오기를 자동화할 수 있습니다. 예를 들어, 스크립트가 API(Yahoo Finance, Alpha Vantage 등)에 최신 환율이나 주가를 쿼리하고 원장에 가격 항목을 추가할 수 있습니다:

    2025-04-30 price BTC 30000 USD
    2025-04-30 price EUR 1.10 USD

    bean-price(이제 Beancount 우산 아래의 외부 도구)와 같은 도구가 일일 호가를 가져와 Beancount 형식으로 출력합니다. bean-price를 매일 밤 실행하여 prices.beancount include 파일을 업데이트하도록 예약할 수 있습니다. 또는 Python을 사용하세요: 예를 들어 requests 라이브러리로 API를 호출합니다. Beancount의 문서는 공개 거래 자산의 경우 "가격을 다운로드하고 지시어를 작성하는 코드를 호출"할 수 있다고 제안합니다. 즉, 수동으로 하는 대신 스크립트가 조회를 수행하고 price 줄을 삽입하게 하세요.

  • 주식 포트폴리오 데이터: 환율과 유사하게 API와 통합하여 상세한 주식 데이터나 배당금을 가져올 수 있습니다. 예를 들어, Yahoo Finance API(또는 yfinance와 같은 커뮤니티 라이브러리)는 티커의 과거 데이터를 검색할 수 있습니다. 스크립트는 보유한 각 주식의 월별 가격 기록으로 원장을 업데이트하여 정확한 과거 시장 가치 보고서를 가능하게 합니다. 일부 맞춤 확장(예: fava_investor)은 표시를 위해 가격 데이터를 즉시 가져오지만, 가장 간단한 방법은 정기적으로 가격을 원장에 가져오는 것입니다.

  • 은행 API(Open Banking/Plaid): CSV 다운로드 대신 API를 사용하여 거래를 자동으로 가져올 수 있습니다. Plaid와 같은 서비스는 은행 계정을 집계하고 거래에 대한 프로그래밍 방식 액세스를 제공합니다. 고급 설정에서는 Python 스크립트가 Plaid의 API를 사용하여 매일 새 거래를 가져와 파일에 저장하거나(또는 직접 원장으로 가져오기) 할 수 있습니다. 한 파워 유저는 Plaid가 가져오기 파이프라인에 공급되어 원장을 거의 자동으로 만드는 시스템을 구축했습니다. 그들은 "Plaid API에 가입하고 로컬에서 동일하게 수행하는 것을 막는 것은 없습니다"라고 말합니다 – 즉, 은행 데이터를 얻기 위한 로컬 스크립트를 작성한 다음 Beancount 임포터 로직을 사용하여 원장 항목으로 구문 분석할 수 있습니다. 일부 지역에서는 은행에서 제공하는 오픈 뱅킹 API를 유사하게 사용할 수 있습니다.

  • 기타 API: 예산 도구 통합(Beancount의 실제와 비교할 계획 예산 내보내기) 또는 OCR API를 사용하여 영수증을 읽고 거래와 자동 일치시킬 수 있습니다. 스크립트는 Python 생태계에 대한 전체 액세스 권한이 있으므로 이메일 서비스(알림 전송용)부터 Google Sheets(월 재무 지표로 시트 업데이트) 및 메시징 앱(텔레그램 봇으로 요약 보고서 보내기)까지 모든 것을 통합할 수 있습니다.

타사 API를 사용할 때 자격 증명을 보호(API 키에 환경 변수 또는 구성 파일 사용)하고 스크립트에서 오류(네트워크 문제, API 중단)를 적절히 처리하는 것을 기억하세요. 데이터를 캐시하는 것이 좋은 경우가 많습니다(예: 동일한 과거 환율을 반복적으로 요청하지 않도록 가져온 환율 저장).

모듈식, 유지 관리 가능한 스크립트를 위한 모범 사례

스크립트 가능한 워크플로우를 구축하면서 코드를 체계적이고 견고하게 유지하세요:

  • 모듈성: 서로 다른 관심사를 서로 다른 스크립트나 모듈로 분리하세요. 예를 들어 "데이터 가져오기/조정"과 "보고서 생성" 및 "알림"에 대한 별도 스크립트를 두세요. ledger_import.py, ledger_reports.py 등과 같은 모듈로 원장용 소형 Python 패키지를 만들 수도 있습니다. 이렇게 하면 각 부분을 이해하고 테스트하기가 더 쉬워집니다.

  • 구성: 하드 코딩 값을 피하세요. 계정 이름, 임계값, API 키, 날짜 범위 등에 구성 파일이나 스크립트 상단의 변수를 사용하세요. 이렇게 하면 코드를 깊이 수정하지 않고 조정하기 쉽습니다. 예를 들어, 상단에 LOW_BALANCE_THRESHOLDS = {"Assets:Bank:Checking": 500, "Assets:Savings": 1000}을 정의하고 알림 스크립트가 이 사전을 반복하도록 할 수 있습니다.

  • 테스트: 재무 자동화를 임무 중요 코드로 취급하세요 – 실제로 그렇습니다! 복잡한 로직에 대한 테스트를 작성하세요. Beancount는 내부적으로 임포터 테스트에 사용되는 일부 테스트 헬퍼를 제공하며 원장 입력을 시뮬레이션하는 데 활용할 수 있습니다. 화려한 프레임워크가 없어도 더미 CSV와 예상 출력 거래를 두고 가져오기 스크립트가 올바른 항목을 생성하는지 어서션할 수 있습니다. pytest를 사용하는 경우 이러한 테스트를 쉽게 통합할 수 있습니다(Alex Watt가 just test 명령으로 pytest를 래핑한 것처럼).

  • 버전 관리: 원장과 스크립트를 버전 관리(git) 아래에 두세요. 이는 백업과 기록을 제공할 뿐만 아니라 통제된 방식으로 변경을 장려합니다. "금융 스크립트"의 릴리스를 태그하거나 디버깅 시 차이점을 검토할 수 있습니다. 일부 사용자는 재무 기록을 Git에서 추적하여 시간 경과에 따른 변화를 봅니다. 그러나 민감한 데이터(원시 명세서 파일 또는 API 키)는 리포지토리에서 무시하도록 주의하세요.

  • 문서화: 미래의 자신을 위해 맞춤 워크플로우를 문서화하세요. 환경 설정 방법, 각 스크립트 실행 방법 및 수행 작업을 설명하는 리포지토리의 README는 몇 달이 지난 후에 매우 유용할 것입니다. 또한 특히 명백하지 않은 회계 로직이나 API 상호 작용에 코드에 주석을 달아야 합니다.

  • Fava 플러그인 유지 관리: Fava 확장을 작성하는 경우 간단하게 유지하세요. Fava는 변경될 수 있으므로 대상이 지정된 기능을 가진 더 작은 확장이 업데이트하기 더 쉽습니다. 너무 많은 로직을 복제하지 마세요 – 원장 변경에 민감할 수 있는 계산을 하드코딩하는 대신 가능할 때마다 Beancount의 쿼리 엔진이나 기존 헬퍼 함수를 사용하세요.

  • 보안: 스크립트가 민감한 데이터를 처리하고 외부 서비스에 연결할 수 있으므로 주의하세요. API 키를 노출하지 말고 안전한 시스템에서 자동화를 실행하는 것을 고려하세요. 호스팅된 솔루션이나 클라우드(GitHub Actions 예약 또는 서버에서 Fava 실행)를 사용하는 경우 원장 데이터가 저장 시 암호화되어 있고 개인 정보 보호 영향에 대해 편안한지 확인하세요.

이러한 관행을 따르면 재무(및 도구 자체)가 진화해도 워크플로우가 안정적으로 유지됩니다. 최소한의 조정으로 해마다 재사용할 수 있는 스크립트를 원합니다.

결론

Beancount와 Fava는 기술에 능숙한 사용자가 개인 재무 추적을 완전히 맞춤화할 수 있는 강력하고 유연한 플랫폼을 제공합니다. Python 스크립트를 작성하면 명세서 조정과 같은 지루한 작업을 자동화하고, 필요에 맞는 풍부한 보고서를 생성하며, 시기적절한 알림으로 재무를 관리할 수 있습니다. 기본 쿼리와 CSV 가져오기부터 본격적인 Fava 플러그인과 외부 API 통합까지 다양한 예를 다뤄습니다. 구현할 때 간단하게 시작하고 점진적으로 구축하세요. 몇 개의 작은 자동화 스크립트만으로도 많은 시간을 절약하고 정확성을 크게 향상시킬 수 있습니다. 모든 것이 일반 텍스트와 Python이므로 완전한 통제권을 가지고 있음을 기억하세요 – 재무 시스템은 특정 요구 사항에 맞춰 함께 성장합니다. 즐거운 스크립팅 되세요!

출처: 위 기법은 Beancount 문서 및 커뮤니티 경험에서 가져왔습니다. 추가 자료는 Beancount 공식 문서, 커뮤니티 가이드 및 블로그, 유용한 플러그인과 도구에 대한 링크가 있는 Awesome Beancount 리포지토리를 참조하세요.

출처: https://beancount.io/ko/docs/Solutions/scriptable-workflows