QuickBooks에서 Beancount로 마이그레이션 플레이북
1단계: QuickBooks에서 데이터 내보내기
5년치 데이터를 마이그레이션하는 작업은 모든 QuickBooks 기록을 사용 가능한 형식으로 내보내는 것부터 시작됩니다. QuickBooks Desktop과 QuickBooks Online은 서로 다른 내보내기 옵션을 제공합니다:
1.1 QuickBooks Desktop – 내보내기 옵션
IIF (Intuit Interchange Format): QuickBooks Desktop은 목록(계정과목표, 고객, 공급업체 등)을 .IIF 텍스트 파일로 내보낼 수 있습니다. QuickBooks Desktop에서 파일 → 유틸리티 → 내보내기 → IIF로 목록 내보내기로 이동한 다음 필요한 목록(예: 계정과목표, 고객, 공급업체)을 선택하세요. 이렇게 하면 계정 이름, 유형 및 목록 데이터가 포함된 텍스트 파일 이 생성됩니다. IIF는 독점적이지만 일반 텍스트 형식이므로 분석하기가 비교적 쉽습니다. 이를 사용하여 계정과목표와 연락처 목록을 캡처하여 Beancount에서 참조용으로 사용하세요.
CSV를 통한 총계정원장/분개장: 거래 데이터의 경우 QuickBooks Desktop은 원클릭 전체 내보내기 기능을 제공하지 않지만, 보고서를 사용할 수 있습니다. 권장되는 방법은 원하는 기간 동안의 분개장(모든 거래)을 내보내는 것입니다. QuickBooks Desktop에서 보고서 → 회계 및 세금 → 분개장을 열고, 날짜를 가장 빠른 거래부터 오늘까지로 설정한 다음, 내보내기 → Excel을 클릭하세요. 보고서 머리글/바닥글과 빈 열을 제거한 후 결과를 CSV로 저장하세요. 숫자 데이터가 깨끗한지 확인하세요: 센트 포함(예: 3이 아닌 3.00), 불필요한 따옴표 없음, CSV에 통화 기호나 이중 음수 부호 없음. CSV에는 Date, Trans #, Name, Account, Memo, Debit, Credit, Balance와 같은 열이 있어야 합니다(또는 보고서 형식에 따라 단일 Amount 열).
팁: QuickBooks Desktop 2015+ 버전에서는 찾기 대화상자를 통해서도 거래를 내보낼 수 있습니다. 편집 → 찾기 → 고급을 사용하고, 날짜 범위를 5년으로 설정한 다음, 결과를 CSV로 내보내세요. 경고: 일부 버전에서는 내보내기가 32,768줄로 제한됩니다. 데이터가 매우 큰 경우 잘림을 방지하기 위해 연도별(또는 더 작은 단위)로 내보낸 다음 나중에 결합하세요. 중복을 방지하기 위해 날짜 범위가 겹치지 않도록 하세요.
기타 형식 (QBO/QFX/QIF): QuickBooks Desktop은 .QBO (Web Connect) 또는 .QFX/.OFX 파일을 통해 은행 거래를 가져올 수 있지만, QuickBooks에서 내보내는 데는 일반적이지 않습니다. 목표가 은행 거래만 추출하는 것이라면 이미 은행에서 QBO/OFX 형식으로 가지고 있을 수 있습니다. 하지만 전체 원장 내보내기를 위해서는 IIF와 CSV를 사용하는 것이 좋습니다. QuickBooks Desktop은 타사 도구 없이는 QIF(Quicken Interchange Format)로 직접 내보낼 수 없습니다. 만약 QIF를 얻을 방법을 찾았다면, 일부 원장 도구(구 버전 Ledger 2.x)는 QIF를 읽을 수 있었지만, 저희 파이프라인에서는 CSV로 작업하는 것이 더 낫다는 점을 유의하세요.
1.2 QuickBooks Online – 내보내기 옵션
내장된 Excel/CSV 내보내기: QuickBooks Online (QBO)은 데이터 내보내기 도구를 제공합니다. 설정 ⚙ → 도구 → 데이터 내보내기로 이동하세요. 내보내기 대화상자에서 보고서 탭을 사용하여 데이터(예: 총계정원장 또는 거래 목록)를 선택하고 목록 탭에서 목록(계정과목표 등)을 선택한 후, 모든 날짜를 선택하고 Excel로 내보내세요. QuickBooks Online은 선택한 보고서 및 목록(예: 손익계산서, 재무상태표, 총계정원장, 고객, 공급업체, 계정과목표 등)에 대한 여러 Excel 파일이 포함된 ZIP 파일을 다운로드합니다. 그런 다음 이 Excel 파일들을 처리하기 위해 CSV로 변환할 수 있습니다.
거래 세부 정보 보고서: QBO의 기본 내보내기에 단일 총계정원장 파일이 포함되지 않은 경우, 수동으로 상세 보고서를 실행할 수 있습니다:
- 보고서로 이동하여 계정별 거래 세부 정보(또는 일부 QBO 버전에서는 총계정원장)를 찾습니다.
- 보고 기간을 전체 5년 범위로 설정합니다.
- 보고서 옵션에서 그룹화 기준 = 없음으로 설정합니다(소계 없이 개별 거래를 나열하기 위해).
- 열을 사용자 정의하여 최소한 다음을 포함하도록 합니다: 날짜, 거래 유형, 번호, 이름(수취인/고객), 메모/설명, 계정, 차변, 대변(또는 단일 금액 열), 그리고 잔액. 사용했다면 클래스나 위치도 포함합니다.
- 보고서를 실행한 다음 Excel로 내보내기를 합니다.
이렇게 하면 모든 거래의 상세 원장을 얻을 수 있습니다. 이것을 CSV로 저장하세요. 각 줄은 거래의 한 분개(posting)를 나타냅니다. 나중에 변환을 위해 거래별로 줄을 그룹화해야 합니다.
계정과목표 및 기타 목록: QuickBooks Online은 회계 → 계정과목표 → 일괄 작업 → Excel로 내보내기를 통해 계정과목표를 내보낼 수 있습니다. 계정 이름과 유형을 얻기 위해 이 작업을 수행하세요. 마찬가지로, 메타데이터로 이름을 가져오려면 고객, 공급업체 등을 내보내세요.
QuickBooks Online API (선택 사항): 프로그래밍 방식의 접근을 위해 Intuit는 QBO 데이터용 REST API를 제공합니다. 고급 사용자는 QuickBooks Online 앱을 생성하고(개발자 계정 필요) API를 사용하여 JSON 형식으로 데이터를 가져올 수 있습니다. 예 를 들어, Account 엔드포인트를 쿼리하여 계정과목표를, JournalEntry 또는 GeneralLedger 보고서 엔드포인트를 쿼리하여 거래를 가져올 수 있습니다. API를 래핑하는 python-quickbooks와 같은 Python SDK가 있습니다. 그러나 API를 사용하는 것은 OAuth 인증을 포함하며, 자동화를 선호하지 않는 한 일회성 마이그레이션에는 과도한 작업입니다. 대부분의 경우, CSV/Excel로 수동 내보내기하는 것이 더 간단하고 오류가 적습니다.
2단계: 데이터 변환 및 정리
QuickBooks 데이터를 CSV(및/또는 IIF)로 얻었다면, 다음 단계는 이를 Beancount의 일반 텍스트 원장 형식으로 변환하는 것입니다. 여기에는 내보낸 파일을 파싱하고, QuickBooks 계정을 Beancount 계정과목표에 매핑하며, 거래를 Beancount 구문으로 형식화하는 작업이 포함됩니다.
2.1 Python으로 QuickBooks 내보내기 파일 파싱하기
Python을 사용하면 변환의 정확성과 재현성을 보장할 수 있습니다. 두 가지 주요 작업인 계정과목표 가져오기와 거래 변환을 위한 스크립트 개요를 설명 하겠습니다.
계정 가져오기 및 매핑: 거래를 추가하기 전에 Beancount에서 계정을 설정하는 것이 중요합니다. QuickBooks 계정에는 유형(은행, 매출채권, 비용 등)이 있으며, 이를 Beancount의 계층 구조(자산, 부채, 수익, 비용 등)에 매핑할 것입니다. 예를 들어, 다음과 같은 매핑을 사용할 수 있습니다:
# QuickBooks 계정 유형을 Beancount 루트 카테고리에 매핑
AccountTypeMap = {
'BANK': 'Assets',
'CCARD': 'Liabilities',
'AR': 'Assets', # 매출채권을 자산으로
'AP': 'Liabilities', # 매입채무를 부채로
'FIXASSET': 'Assets',
'OASSET': 'Assets', # 기타 자산
'OCASSET': 'Assets', # 기타 유동 자산
'LTLIAB': 'Liabilities', # 장기 부채
'OCLIAB': 'Liabilities', # 기타 유동 부채
'EQUITY': 'Equity',
'INC': 'Income',
'EXP': 'Expenses',
'EXINC': 'Income', # 기타 수익
'EXEXP': 'Expenses', # 기타 비용
}
QuickBooks Desktop IIF 내보내기 파일 또는 QBO의 계정 목록 CSV를 사용하여 각 계정의 이름과 유형을 검색합니다. 그런 다음:
-
Beancount 계정 이름 생성: QuickBooks는 때때로 계정 이름에 콜론(
:)을 사용하여 하위 계정을 나타냅니다(예: "Current Assets:Checking"). Beancount도 계층 구조에 동일한 콜론 표기법을 사용합니다. 종종 이름을 직접 재사용할 수 있습니다. QuickBooks 계정 이름이 카테고리로 시작하지 않으면 매핑된 카테고리를 앞에 붙입니다. 예를 들어, 유형이BANK이고 이름이 "Checking"인 QuickBooks 계정은 Beancount에서Assets:Checking이 됩니다.EXP(비용) 계정 "Meals"는Expenses:Meals이 됩니다. -
유효한 이름 지정 보장: Beancount를 혼란스럽게 할 수 있는 문자를 제거하거나 대체합니다. QuickBooks는 이름에
&나/와 같은 문자를 허용합니다. 특수 문자를 제거하거나 대체하는 것이 좋습니다(예:&를and로 바꾸고, 슬래시나 공백 제거). 또한, 변환 후 모든 계정 이름이 고유한지 확인하세요. QuickBooks는 다른 상위 계정 아래에 동일한 하위 계정 이름을 허용했을 수 있지만, Beancount에서는 전체 이름(상위 포함)이 고유해야 합니다. 필요한 경우 이름을 바꾸거나 구분자를 추가하여 구별하세요. -
계정 개설(open) 구문 출력: Beancount에서는 사용되는 모든 계정이
open지시어로 개설되어야 합니다. 첫 거래 이전 날짜를 선택할 수 있습니다(예: 2019-2023 데이터를 마이그레이션하는 경우 모든 개설에2018-12-31또는 더 이른 날짜 사용). 스크립트는 다음과 같은 줄을 작성합니다:2018-12-31 open Assets:Checking USD2018-12-31 open Expenses:Meals USD각 계정에 대해 위와 같이 작성합니다(USD가 주 통화라고 가정). 각 계정에 적합한 통화를 사용하세요(아래 다중 통화 참고).
거래 변환: 가장 큰 과제는 QuickBooks 거래 내보내기 파일(CSV)을 Beancount 항목으로 변환하는 것입니다. 각 QuickBooks 거래(송장, 청구서, 수표, 분개 등)는 여러 개의 분개(줄)를 가질 수 있으며, 이를 하나의 Beancount 거래로 모아야 합니다.
Python의 CSV 리더를 사용하여 내보낸 줄을 반복하고 분개를 누적할 것입니다:
import csv
from collections import defaultdict
# QuickBooks 분개장 CSV에서 모든 줄 읽기
rows = []
with open('quickbooks_exported_journal.csv', 'r', encoding='utf-8') as f:
reader = csv.DictReader(f)
for line in reader:
rows.append(line)
# 거래별로 줄 그룹화('Trans #'가 거래를 식별한다고 가정)
transactions = defaultdict(list)
for line in rows:
trans_id = line.get('Trans #') or line.get('Transaction ID') or line.get('Num')
transactions[trans_id].append(line)
이제 transactions는 각 키가 거래 ID/번호이고 값이 해당 거래의 분개 목록인 딕셔너리가 되었습니다. 다음으로, 각 그룹을 Beancount로 변환합니다:
def format_date(qb_date):
# QuickBooks 날짜는 "12/31/2019"와 같을 수 있음
m, d, y = qb_date.split('/')
return f"{y}-{int(m):02d}-{int(d):02d}"
output_lines = []
for trans_id, splits in transactions.items():
# 필요한 경우 줄 순서대로 분개 정렬 (보통 순서대로 나옴)
splits = sorted(splits, key=lambda x: x.get('Line') or 0)
first = splits[0]
date = format_date(first['Date'])
payee = first.get('Name', "").strip()
memo = first.get('Memo', "").strip()
# 거래 헤더
output_lines.append(f"{date} * \"{payee}\" \"{memo}\"")
if first.get('Num'): # 참조 번호가 있으면 포함
output_lines.append(f" number: \"{first['Num']}\"")
# 각 분개/포스팅 반복
for split in splits:
acct_name = split['Account'].strip()
# QuickBooks 계정 이름을 Beancount 계정으로 매핑 (이전 매핑 사용)
beancount_acct = account_map.get(acct_name, acct_name)
# 부호가 있는 금액 결정:
amount = split.get('Amount') or ""
debit = split.get('Debit') or ""
credit = split.get('Credit') or ""
if amount:
# 일부 내보내기 파일은 단일 Amount 열을 가짐 (크레딧은 음수)
amt_str = amount
else:
# 별도의 차변/대변 열이 있는 경우
amt_str = debit if debit else f"-{credit}"
# 안전을 위해 숫자의 쉼표 제거
amt_str = amt_str.replace(",", "")
# 통화 추가
currency = split.get('Currency') or "USD"
amt_str = f"{amt_str} {currency}"
# 분개에 대한 메모/설명
line_memo = split.get('Memo', "").strip()
comment = f" ; {line_memo}" if line_memo else ""
output_lines.append(f" {beancount_acct:<40} {amt_str}{comment}")
# 거래 끝 – 빈 줄
output_lines.append("")
이 스크립트 로직은 다음을 수행합니다:
-
날짜를 Beancount를 위해 YYYY-MM-DD 형식으로 바꿉니다.
-
수취인(Name)과 메모를 거래 설명으로 사용합니다. 예를 들어:
2020-05-01 * "ACME Corp" "Invoice payment"(수취인이 없는 경우 QuickBooks 거래 유형을 사용하거나 수취인을 빈 따옴표로 남길 수 있습니다). -
참조 번호(수표 번호, 송장 번호 등)가 있는 경우
number메타데이터를 추가합니다. -
각 분개 줄을 반복합니다:
account_map딕셔너리(계정과목표 단계에서 채워짐)를 사용하여 QuickBooks 계정 이름을 Beancount 계정으로 매핑합니다.- 금액을 결정합니다. 내보내기 파일에 따라 단일 Amount 열(양수/음수 값) 또는 별도의 차변 및 대변 열이 있을 수 있습니다. 위 코드는 두 경우 모두 처리합니다. Beancount에서는 포스팅당 부호가 있는 단일 숫자를 사용하므로 대변이 음수 금액으로 표시되도록 합니다.
- 통화를 첨부합니다(다른 통화 열이 없는 한 USD로 가정).
- 계정, 금액 및 줄 메모가 있는 주석과 함께 Beancount 포스팅 줄을 작성합니다. 예를 들어:
Assets:Checking 500.00 USD ; DepositIncome:Sales -500.00 USD ; Deposit이는 $500 입금(수익에서 예금으로)을 반영합니다.
-
모든 분개를 나열한 후 빈 줄로 거래를 구분합니다.
다중 통화 처리: QuickBooks 데이터에 여러 통화가 포함된 경우, 각 포스팅에 통화 코드를 포함하세요(위에서 보인 바와 같이). 외화로 된 계정은 해당 통화로 개설되었는지 확인하세요. 예를 들어, EUR 은행 계좌가 있다면 open Assets:Bank:Checking EUR를 출력하고 해당 계좌의 거래는 EUR를 사용할 것입니다. Beancount는 다중 통화 원장을 지원하며 암묵적인 환산을 추적하지만, 보고서에서 기준 통화로의 환산을 원한다면 환율에 대한 가격 항목을 추가해야 할 수도 있습니다. Beancount 파일 상단에 주 운영 통화를 선언하는 것도 권장됩니다(예: option "operating_currency" "USD").
변환 실행: Python 스크립트(예: qb_to_beancount.py)를 저장하고 내보낸 파일에 대해 실행하세요. 모든 계정과 거래가 포함된 .beancount 파일을 생성해야 합니다.