S9 · REF제 5 장 대응 — 돈

플레인텍스트 회계

Ledger / hledger / Beancount

개인 재정 전체를 사람이 읽는 텍스트 파일 하나에 복식부기로 기장하고, 잔액·보고서는 전부 그 파일에서 계산해 내는 오픈소스 계보다. 거래=불변 텍스트 이벤트 로그, 잔액=파생 계산 — 우리가 사건과 곡선에 적용한 "기록이 원천, 상태는 파생"을 회계 영역에서 20년 넘게 실전 검증해 온 살아 있는 표본이다.

무엇인가

플레인텍스트 회계(plain text accounting, PTA)는 회계 장부를 데이터베이스가 아니라 평문 텍스트 파일(저널)로 두는 소프트웨어 계열의 총칭이다. 계보는 셋이 축이다:

도구만든 이특징
LedgerJohn Wiegley · 2003원조. C++ CLI. "장부는 텍스트, 도구는 읽기 전용 계산기"라는 문법을 세움
hledgerSimon Michael · 2007Haskell 재구현. 문서·웹 UI·기간 반복 거래와 --forecast(미래 투영)가 강함
BeancountMartin BlaisPython. 문법이 더 엄격(계정 사전 open 선언, 잔액 단언 강제 지향). 웹 프런트엔드 Fava 와 짝

세 도구가 공유하는 규칙은 네 개로 요약된다.

  1. 저널은 append-only 에 가까운 이벤트 로그다. 거래(transaction) 하나가 한 줄기 기록 — 날짜·서술·그리고 둘 이상의 분개(posting)로 이뤄진다. 파일은 시간순으로 자라고, 과거 거래는 고치기보다 정정 거래를 덧붙이는 쪽이 문화다. 텍스트이므로 git 에 넣으면 장부 자체의 이력(누가 언제 무엇을 고쳤나)까지 남는다.
  2. 복식부기 — 모든 거래는 0 으로 맞아야 한다. 분개들의 합이 0 이 아니면 도구가 거래를 거부한다. 돈은 사라지거나 생겨나지 않고 계정에서 계정으로 이동할 뿐이라는 500년 된 불변식을 파서 수준에서 강제하는 것이다.
  3. 계정은 트리다. 최상위는 관례상 다섯 — Assets(자산) · Liabilities(부채) · Equity(자본) · Income(수입) · Expenses(지출) — 이고 그 아래를 Assets:은행:적금처럼 콜론으로 파 내려간다.
  4. 잔액은 저장하지 않는다. 어떤 계정의 현재 잔액도, 순자산도, 월별 지출 보고서도 파일 어디에도 적혀 있지 않다. 전부 balance·register 같은 명령이 로그를 처음부터 재생해 파생 계산한다. 다만 파생값을 현실과 대조하는 장치는 있다 — 잔액 단언(balance assertion). "이 날짜에 이 계정은 정확히 얼마여야 한다"를 적어 두면, 계산 결과가 어긋날 때 도구가 오류를 낸다. 기록 누락을 잡는 체크포인트다.

거래 하나의 생김새는 이렇다(hledger/Ledger 문법):

2026-07-11 서점 · 책 구입
    Expenses:책            18,000 KRW
    Assets:체크카드       -18,000 KRW

커뮤니티 허브 plaintextaccounting.org 가 도구·관행·비교표를 모아 둔다. 수십 개의 파생 도구가 있지만 저널 문법은 대체로 상호 호환된다 — 데이터가 특정 앱이 아니라 사용자의 파일에 산다는 것이 이 계보의 정체성이다.

우리 모델과의 접점

우리 개념 모델 제6장의 돈 4역과 PTA 의 어휘는 층이 정확히 맞물린다:

PTA우리 개념비고
저널(거래 로그)사건 + 이동(Flow)의 기록둘 다 "기록이 진실의 원천"(원칙 1·7)
계정(Assets 하위 트리)지갑(Pocket)돈의 거처. 자산 계정 간 이체가 순자산을 안 바꾸는 것도 동일 — "주머닛돈이 쌈짓돈"은 복식부기의 자산 내부 이동 그 자체다
파생 잔액 · 보고서스톡(순자산) · 곡선둘 다 저장하지 않고 로그 재생으로 계산
잔액 단언NetWorthSnapshot파생값을 현실에 정박시키는 체크포인트 — 아래 생각할 거리 3
기간 반복 거래 · --forecast반복 이동 = 계단미래 곡선을 반복 규칙에서 투영하는 발상이 동형
Expenses/Income 카테고리 트리(등가물 없음 — 의도적)어긋남 — 아래 참조

어긋남이 우리 설계를 더 선명하게 보여 주는 두 지점:

  • 카테고리 트리 vs 인생 노드. PTA 는 지출의 의미를 Expenses:식비:외식 같은 임의 분류 트리에 담는다. 우리는 그 자리를 거부했다 — 배정(Allocation)은 임의 카테고리가 아니라 실제 인생 노드(프로젝트·사건·목표·대상)에 돈을 붙이고, 일회 이동은 반드시 사건에 앵커된다. PTA 의 장부는 돈의 세계 안에서 완결되지만, 우리 장부의 좌표계는 삶 그 자체다.
  • 거처와 임무의 직교. PTA 에서 예산(임무)을 표현하려면 가상 하위 계정 같은 트릭으로 거처 트리 하나에 임무까지 욱여넣게 된다. 우리는 지갑(거처)과 배정(임무)을 처음부터 다른 두 축으로 분리했다(2026-07-10, 여비 금융상품 확장) — 돈이 어디 놓여 있느냐와 무슨 일을 맡았느냐는 다른 두 사실이다.
v2 Transaction 의 성숙한 참조 모델. 오늘의 이동(Flow)은 계단·점프라는 모델이고, 언젠가 실거래 수준의 실측이 필요해지는 순간이 온다 — Flow 의 실측화, v2 Transaction. 그때 스키마를 발명할 필요가 없다. 거래=불변 로그, 분개 합=0, 잔액=파생, 단언=검증이라는 PTA 의 골격은 20년 이상 실사용으로 다듬어진 오픈소스 정답지다. 이벤트 소싱이 "append-only 기록, 파생 상태"의 공학판이라면 PTA 는 그 회계판이고, 개인이 평생 규모의 로그를 실제로 유지할 수 있음을 보여 준 유일한 실증이기도 하다.

생각할 거리

  1. 실측의 해상도는 어디서 멈추나. PTA 사용자는 커피 한 잔까지 기장하지만, 그 규율은 소수만 유지한다. v2 Transaction 이 전량 기장을 요구하면 감정 부채의 함정에, 은행 연동 자동 수집으로 때우면 자동 수집의 배신에 빠진다. "일회 이동은 사건에 앵커된다"는 제약은 거래가 월 수백 건이 되어도 유지 가능한가 — 아니면 사건이 될 자격이 있는 이동배경 거래의 이층 구조가 필요한가.
  2. 복식부기를 어디까지 채택하나. 지갑 간 내부 이동은 이미 복식이다(출처 지갑 −, 도착 지갑 +). 외부 유출입까지 0 합을 강제하려면 상대 계정(수입원·지출처)이 필요한데, 그것은 우리가 거부한 카테고리 트리를 뒷문으로 다시 들이는 일이 될 수 있다. 확장 가드레일의 일곱 질문(원칙 10)에 "복식의 상대 계정"을 통과시켜 볼 것 — 페이로드 없는 새 조인은 만들지 않는다(원칙 3)와 어떻게 화해하나.
  3. 스냅샷 = 잔액 단언이라는 재해석. NetWorthSnapshot 을 "시작점"으로만 읽으면 스냅샷 이후의 기록 누락이 조용히 곡선을 왜곡한다. PTA 처럼 스냅샷을 파생값과 대조되는 단언으로 재해석하면 — 계산된 잔액과 새 스냅샷이 어긋날 때 그 차액이 "기록되지 않은 이동"으로 드러난다. 이 어긋남을 오류로 보여 줄 것인가, 인터뷰 엔진의 질문 소재("이 석 달 사이 무슨 일이 있었나요")로 쓸 것인가.
  4. 장부의 소유권과 산출 포맷. PTA 의 데이터는 앱이 죽어도 읽히는 텍스트 파일로 사용자 곁에 남는다 — 회사 수명<기록 수명에 대한 가장 오래된 답이자 local-first의 선행 사례다. 우리 백업(zip)·책자(md) 산출에 여비 데이터를 PTA 호환 저널로 내보내는 선택지를 더하면, 원칙 9(기록의 소유)가 돈 영역에서도 "들고 나가서 다른 도구로 계산 가능"까지 도달한다.
  5. 미래 기장과 시나리오 겹. hledger 의 반복 거래와 --forecast 는 우리 곡선의 계단과 동형이지만, PTA 에는 "미래 여러 벌"이 없다 — 사용자들은 저널 파일을 git 브랜치로 갈라 시나리오를 흉내 낸다(Git 브랜칭 모델). 우리는 겹(overlay)을 1급 개념으로 두었으므로(원칙 5), 시나리오별 배정·이동의 델타를 "브랜치된 저널의 diff"로 사고하면 승격(③결정) 시 무엇을 확정 세계로 옮겨야 하는지가 정확히 델타로 정의된다.

더 찾아보기

내 생각

이 메모는 지도 페이지의 같은 항목과 공유되며, 이 기기(브라우저)에 저장됩니다.