Data Architecture

나라 차이는 조건문이 아니라
데이터에 담습니다

한국과 일본을 한 시스템에서 운영할 수 있게 설계했습니다. 지금 실제로 돌고 있는 것은 일본 7물건 98실이고, 한국 트랙은 정책 23항목 중 다수가 미정이라 아직 열지 않았습니다. 물건 프로파일이 규칙을 갖고, 청구액은 확정 규칙을 넣은 엔진이 계산합니다.

Principle

코드에 «일본이면…» 분기를 두지 않습니다

나라별 차이는 조건문이 아니라 물건 프로파일에 담깁니다. 물건마다 아래를 지정하면 규칙이 따라옵니다. 지금 채워져 돌고 있는 것은 일본 쪽이고, 한국 트랙은 운영 정책 23항목 중 다수가 미정이라 아직 값을 넣지 않았습니다.

프로파일한국일본
청구 주기미정 — 정책 협의 중캘린더월 — 매월 1일~말일이 1개월. 첫·마지막 달만 그 달 실일수를 분모로 일할 (JC-01)
일할 분모미정 — 정책 협의 중첫 달과 마지막 달만 그 달 실일수. 중간 달은 온전한 1개월 (JC-01)
금액 반올림미정 — 정책 협의 중100엔 단위 올림 (JC-02)
장기/단기 판정미정 — 정책 협의 중총 일수 31~180일 = 단기 / 181일 이상 = 장기. 30일 이하는 취급하지 않습니다 (JC-03)
연장 처리미정 — 정책 협의 중모든 연장은 신규 계약으로 다시 판정합니다 (JC-04)
보증금미정 — 정책 협의 중장기 = 월세 1개월분 / 단기 = 없음 (JC-05)
청구 시점미정 — 정책 협의 중입주월 = 당월 일할 + 익월분 동시 청구. 이후 매월 첫 영업일에 익월분 (JC-06)
갱신료미정 — 정책 협의 중직전 계약과 신규 계약이 둘 다 장기일 때만 신규 임대료의 10% (JC-07)
관리수수료미정 — 정책 협의 중위탁운영 = 월임대료 × 5% × 1.1 / 마스터리스 = 월수입 − 원가(고정비)
원상회복미정 — 정책 협의 중청소 2,000엔/㎡ + 에어컨 25,000엔/대
중도퇴거미정 — 정책 협의 중계약 화면에서 중도퇴거로 표시합니다. 위약금 계산 규칙은 아직 확정되지 않았습니다

규칙은 문서로 확정한 뒤 엔진에 박았습니다. 위 일본 규칙은 JC-01~JC-07로 번호를 붙여 고정했고, 청구 엔진 회귀 테스트 47건이 이 규칙들을 잠그고 있습니다. 한국 값은 아직 비어 있습니다 — 채우기 전에 열면 두 나라 중 하나가 반드시 틀립니다.

Tables

테이블 관계

마스터 · 원장 · 기록을 구분합니다. 원장은 사실을 쌓고, 기록은 그 사실이 언제 왜 바뀌었는지를 남깁니다.

법인 › 물건 › 호실통화·세금 체계가 법인에서 따라오고, 물건이 프로파일을 갖고, 호실이 요금·모집가·원가·면적/타입·상태를 갖습니다. 현재 물건 7 · 호실 98(운영 중 77 · 운영 미개시 21).
계약 › 청구 회차 › 수납계약 51건(입주중 44 · 퇴거 6 · 입력중 1). 활성 계약 44건의 회차·월별 발생을 확정 규칙 엔진이 전부 계산하고(차단 0), 수납은 회계 원장 1,385행과 대사합니다.
audit_log모든 쓰기를 화면·SQL 구분 없이 트리거로 기록합니다. «변경 이력» 화면이 이 테이블을 그대로 봅니다.
청구서 스냅샷발행하면 번호가 붙고 내용이 snapshot에 굳습니다. DB 트리거가 발행 후 변경·삭제를 막습니다.
호실 마스터 — 편집 화면은 아직 없습니다요금·면적·원가·에어컨은 아직 앱에서 고칠 수 없고 시트에서 수정합니다. 계약 «수정» 화면도 아직 없습니다(등록·퇴거는 있음).
수입지출 항목 › 거래계정과목을 데이터로 정의합니다. 새 항목이 필요하면 개발이 아니라 항목 정의에 행을 추가합니다.

Pricing

구간은 총 일수로 갈립니다

요금은 호실의 «컬럼»이 아니라 «행»으로 섭니다. 구간을 컬럼으로 두면 구간이 하나 늘 때마다 컬럼을 추가해야 하고, 물건마다 다른 개수를 담을 수 없습니다. 일본 구간은 단기·장기 둘이고, 판정 기준은 개월수가 아니라 총 일수입니다.

마스터물건 7마스터리스 2물건 42실(東新宿 32 · 南大井 10) · 위탁운영 5물건 56실
→
1 : N호실 98임대료 · 모집가 · 원가 · 면적/타입 · 상태 (운영 중 77 · 운영 미개시 21)
→
판정계약 51총 일수로 단기·장기 자동 판정 (JC-03) · 중도퇴거 표시
→
산출청구 · 청구서확정 규칙으로 회차·월별 발생 계산 → 발행 시 스냅샷
구간기간보증금납부
단기총 31 ~ 180일없음 (JC-05)입주월 = 당월 일할 + 익월분 동시. 이후 매월 첫 영업일에 익월분 (JC-06)
장기총 181일 이상월세 1개월분 (JC-05)입주월 = 당월 일할 + 익월분 동시. 이후 매월 첫 영업일에 익월분 (JC-06)

일본 기준입니다. 30일 이하는 취급하지 않습니다(JC-03). 모든 연장은 신규 계약으로 다시 판정하므로(JC-04) 구간도 그때 다시 결정됩니다. 금액은 100엔 단위로 올립니다(JC-02). 갱신료는 직전·신규가 둘 다 장기일 때만 신규 임대료의 10%입니다(JC-07).

호실 테이블에 요금 컬럼을 만들지 않습니다. 만들면 물건이 늘 때마다 마이그레이션이 필요합니다. 다만 요금·면적·원가·에어컨 마스터는 아직 앱에서 편집할 수 없습니다 — 현재는 시트에서 수정하고 있고, 마스터 편집 화면은 아직 없습니다.

Ledger

원장 스키마 — 화면이 붙어 있는 도메인

PostgreSQL 마이그레이션 20개로 서 있습니다. 모든 쓰기는 audit_log 트리거를 지나 같은 원장에 남습니다. 이 원장은 운영 콘솔(freymadmin.llnpco.dev, 사이트 전체 인증)에만 붙어 있고, 지금 보고 계신 소개 사이트(freym.llnpco.dev)는 공개면이라 실데이터를 담지 않습니다.

asset자산물건 7 · 호실 98 · 운영계약(마스터리스 / 위탁운영). 화면 = 호실 현황
leasing계약계약 51건 · 장단기 판정 · 중도퇴거. 화면 = 계약(등록·퇴거). 수정 화면은 아직 없습니다
billing정산청구 회차 · 월별 발생 · 수납 · 보증금 · 청구서. 화면 = 청구 / 수납·미수 / 청구서
security감사접근 · audit_log. 화면 = 변경 이력
party주체입주자 · 수신인 — 계약자와 받는 사람이 다른 경우가 실제로 있습니다
platform공통공통 타입 · 통화 코드 · 물건별 입금계좌(日本橋 · 九条는 각각 별도 신탁계좌)
아직 화면이 없는 것호실·건물 마스터 편집 · 계약 수정 · 조정금 처리(JX-03)는 아직 없습니다.
이 시스템에 없는 것문의·투어 관리, 계약 단계 보드, 부동산 공개 페이지, 경영 대시보드는 이 원장의 범위가 아닙니다.

Policies

원장을 원장답게 만드는 여섯 가지

스키마만으로는 장부가 되지 않습니다. 아래가 구현에 들어간 설계 원칙입니다.

원장은 append-only수정은 superseded_by로 덧쓰고 물리 삭제하지 않습니다. 퇴거한 계약도 목록에서 사라지지 않습니다(현재 퇴거 6건).
모든 쓰기는 남습니다화면에서 고치든 SQL로 고치든 audit_log에 트리거로 기록됩니다. 시트에는 변경 이력이 아예 없었습니다.
발행한 청구서는 불변발행하면 번호가 붙고 내용이 snapshot에 굳습니다. DB 트리거가 이후의 변경·삭제를 막습니다.
날짜는 UTC 자정 기준기준 시각을 하나로 통일했습니다. 시트 계산이 TODAY()에 의존해 열 때마다 값이 달라지던 문제의 반대편입니다.
가동률 정의 고정«운영 미개시» 21실을 분모에서 제외하고 운영 중 77실만으로 계산합니다 — 현재 57.1%. 98실 전부를 분모로 쓰지 않습니다.
시크릿은 저장소에 두지 않습니다자격증명은 코드·저장소에 넣지 않고 환경변수로 주입합니다.

통화는 법인에서 따라옵니다. 물건과 거래가 각자 정하면 반드시 어긋납니다. 금액은 통화 코드를 함께 들고 다니고, 통화가 다르면 합계를 합치지 않습니다 — 환산해서 한 줄로 만들면 그 환율이 곧 근거 없는 숫자가 됩니다.

Verification

시트 원장을 그대로 재현했는가

구글 스프레드시트 1개(18탭 · 77,000셀 · 49,400수식)로 운영하던 것을 이 원장으로 이관했습니다. 시트는 아직 정본이고 전환 시점은 협의 중입니다. 금액은 화면에서 계산하지 않고, 확정 규칙을 넣은 엔진이 산출합니다 — 시트 값을 옮겨 적지 않습니다.

규칙JC-01 ~ JC-07캘린더월 · 100엔 올림 · 단기/장기 · 재판정 · 보증금 · 청구 시점 · 갱신료
→
엔진청구 엔진활성 계약 44건 전부 계산 · 차단 0
→
원장회차 · 월별 발생수납은 회계 원장 1,385행과 대사
→
발행청구서 스냅샷번호가 붙고 내용이 굳음 · DB 트리거가 변경·삭제 차단

청구 엔진 회귀 테스트 47건. 시트 대조 — 월별 발생 533개월 중 완전일치 494건(93%) · 단수차 36건 · 실제 차이 3건. 청구 이벤트 337건 중 완전일치 303건(90%). 대조에 쓴 시트 사본 원장행 31,920.

그래서 원장을 이렇게 세웠습니다. 시트에서 실측으로 확인한 문제는 넷이었습니다 — ①청구한 사실이 기록되지 않고 계산이 TODAY()에 의존해 열 때마다 값이 달라짐 ②수납을 입주자 «이름 문자열»로 조인해 실제 ¥58,777,660 중 14.2%만 매칭 ③「호실 정보」를 참조하는 수식이 워크북 전체에 0개(요금 마스터 미연결) ④변경 이력 없음. append-only 원장 · audit_log 트리거 · 청구서 스냅샷 · 확정 규칙 엔진은 각각 이 넷에 대응합니다.

Accounting

정산 항목은 개발하지 않고 추가합니다

원상회복비(청소 2,000엔/㎡ + 에어컨 25,000엔/대) · 갱신료 · 관리수수료 · 공과금… 항목은 계속 늘어납니다. 그때마다 개발하지 않도록 항목을 데이터로 정의합니다. 조정금 처리(JX-03)는 이번 범위에서 제외했습니다 — 아직 없습니다.

행을 추가합니다새 항목이 필요하면 항목 정의에 행을 추가합니다. 화면·코드는 손대지 않습니다.
귀속 축이 경계를 정합니다계약·호실·물건에 붙으면 이 시스템, 법인 전체(급여·사회보험)는 회계 시스템 소관입니다. 사람이 매번 판단하지 않습니다.
출처를 구분합니다청구는 확정 규칙 엔진이 계약에서 자동 산출하고, 일회성은 수기로 넣습니다. 출처가 없으면 «이 금액 왜 있지»를 나중에 추적할 수 없습니다.
코드는 고정, 표시명만 다국어청소비 / クリーニング / Cleaning 이 세 항목이 되면 집계가 쪼개집니다.

구분은 청구 · 수납 · 지출이고, 청구 − 수납 = 미수금입니다. 청구 회차는 이미 테이블로 서 있고, 확정 규칙이 활성 계약 44건 전부를 계산합니다(차단 0). 시트에서는 수납이 입주자 이름 문자열로 조인돼 ¥58,777,660 중 14.2%만 매칭됐고, 그래서 나오던 「차액」 ¥72,758,400은 미수금이 아니라 허수였습니다. 온라인 결제는 아직 없습니다.