Data Architecture

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

한국과 일본을 한 시스템에서 운영하기 위한 구조입니다. 물건 프로파일이 규칙을 갖고, 요금은 행으로 서고, 원장 위에 분석층이 따로 섭니다.

Principle

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

나라별 차이는 조건문이 아니라 물건 프로파일에 담깁니다. 물건마다 아래 아홉 개를 지정하면 규칙이 따라옵니다. 그래서 한국과 일본이 한 시스템·한 원장에서 돌아갑니다.

프로파일한국일본
청구 주기캘린더월사이클 — 입주일 기준 익월 같은 날의 전날까지
장기/단기 판정계약 개월수사이클 6개 이상 = 장기. 최초 1회만 판정하고 재계약해도 재판정하지 않습니다
일할 분모그 달 실일수장기 = 실일수 / 단기 마지막 잔여 = 28일 고정
청구 시점매월 납부기한일입주달 = 당월 일할 + 익월분 동시 / 이후 전월 1일
재계약 수수료없음(협의)신규 임대료의 10% · 장기만 (면제 협상 가능)
법정 신고임대차 신고 대상해당 없음 — 국내 제도라 일본 계약에는 이 단계가 아예 생기지 않습니다
입주 가능 시점정비 리드타임 D+n퇴실 +7일
신규 모집 시작만기 D-60퇴실 예정일 1개월 전
중도퇴거 위약금없음장기 = 2사이클 통지 / 이내 퇴거 시 2사이클분, 단기 = 잔여 전액

사이클이 가장 큰 차이입니다. 캘린더월과 다른 축이라 화면이 아니라 계산 엔진이 갈립니다. 물건 프로파일을 보지 않고 구현하면 두 나라 중 하나가 반드시 틀립니다.

Tables

테이블 관계

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

법인 › 물건 › 호실통화·세금 체계가 법인에서 따라오고, 물건이 프로파일 9종을 갖고, 호실이 요금·원가·공과금·상태를 갖습니다.
고객 › 문의(리드) › 계약리드 1 : 0..1 계약. 계약에는 임차인(1:1)과 수신인(1:N)이 붙습니다 — 계약자와 받는 사람이 다른 경우가 실제로 있습니다.
이벤트 3종상태이력(트랙·전환율의 근거, 무효 플래그 있음) · 대화로그(채널이 갈려도 스레드는 하나) · 투어(같은 사람이 두 번 오면 2행).
호실가격이력계약 스냅샷의 근거. 요금표를 바꿔도 이미 맺은 계약은 그대로입니다.
거래처유입경로의 하위값이 아니라 별도 마스터 + FK. 표기가 흔들려도 집계가 쪼개지지 않습니다.
수입지출 항목 › 거래계정과목을 데이터로 정의합니다. 새 항목이 필요하면 개발이 아니라 행을 추가합니다.

v5

요금은 호실의 «컬럼»이 아니라 «행»입니다

구간이 물건마다 다르고 개수도 다릅니다 — 국내 2구간, 일본 3구간. 컬럼으로 두면 구간이 하나 늘 때마다 컬럼을 추가해야 하고 물건별로 다른 개수를 담을 수 없습니다.

마스터물건프로파일 9종
1 : N기간 구간구간명 · 최소/최대 · 적용 요금 · 보증금 규칙 · 납부 방식
1 : N호실 요금호실 × 구간 · 금액 · 평당가 · 적용 시작/종료일
스냅샷계약계약 시점 요금으로 묶임
구간기간보증금납부
초단기3개월 이하없음일시불(전체 선납)
단기3개월 초과 ~ 6개월 미만없음3개월치 선납 후 월납
장기6개월 이상월세 1개월분보증금 + 월세 선납 후 월납

일본 기준 예시입니다. 경계 규칙은 «최소 이상 ~ 최대 미만» 하나로 고정했습니다 — 포함 여부를 구간마다 정하게 두면 «6개월»이 어디 속하는지 아무도 확신하지 못합니다. 값은 물건 마스터가 원장이고, 숫자가 바뀌어도 코드는 고치지 않습니다.

호실 테이블에 요금 컬럼을 만들지 않습니다. 만들면 물건이 늘 때마다 마이그레이션이 필요합니다. 일본은 가격이 2단인데 납부 방식이 3단이라, 컬럼 방식으로는 세 번째 경계를 담을 자리가 아예 없었습니다.

Ledger

원장 스키마 — 12개 도메인

PostgreSQL 마이그레이션 29개로 서 있습니다. 운영 시스템이 쓰는 테이블과 분석이 읽는 테이블이 같은 원장을 봅니다.

asset자산물건 · 객실 · 운영계약 · 물건별 시간대
catalog상품상품 · 공고 · 가격 · 단가 예외
crm수요문의 · 리드 · 투어 · 입주신청 · 거래처
leasing계약계약 · 중개수수료 정책 · 법정 신고
inventory재고점유 · 선점 · 차단 · 점유 편차
billing정산청구 · 수납 · 보증금 · 예약금
facility시설정비 티켓 · 법정점검 · 입퇴실 점검
party주체고객 · 조직 · 주소
metrics지표지표 정의 버전(불변)
sharing공유외부 공유 계약 · 항목 정의
security보안접근 · 감사
platform공통공통 타입 · 통화 코드

Policies

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

스키마만으로는 장부가 되지 않습니다. 아래가 코드가 아니라 정책 문서로 잠겨 있습니다.

이벤트 불변현재 상태와 append-only 이벤트가 함께 삽니다. 이벤트 행은 수정·삭제하지 않고, 집합체 시퀀스로 순서를 보장합니다.
점유 기록 근거점유 행마다 OBSERVED(실측)와 DERIVED_FROM_CONTRACT(계약 파생)를 구분합니다. 가동률은 계약이 아니라 실제 점유일에서 나옵니다.
개인정보 봉투 암호화고객 식별정보는 봉투 암호화로 저장하고 검색은 blind index로 합니다. 원문을 인덱스에 노출하지 않습니다.
보존과 파티셔닝이벤트 테이블은 파티션 + 보존 정책을 갖고, 변경 전파는 outbox CDC로 나갑니다.
지표 정의 불변가동률·평균임대료의 정의가 승인된 버전으로 고정됩니다. 바꾸려면 새 버전을 만들어야 하고 과거 숫자는 그대로 재현됩니다.
다통화 · 물건별 시간대금액은 통화 코드를 함께 들고 다니고 시간대는 물건마다 IANA 값으로 검증됩니다. 통화가 다르면 합계를 합치지 않습니다.

통화는 법인에서 따라옵니다. 물건과 거래가 각자 정하면 반드시 어긋납니다. 화면 합계도 ¥55,196,240 · 79,909만원처럼 나눠 냅니다 — 환산해서 한 줄로 만들면 그 환율이 곧 근거 없는 숫자가 됩니다.

Analytics

원장 위에 분석층이 따로 섭니다

지표를 화면에서 계산하지 않습니다. 계보가 있고 각 단계에 데이터 테스트가 걸립니다.

원장PostgreSQLasset · crm · leasing · inventory · billing
stagingstg_*자산 · 계약 · 재고 · 정산 · 숙박 · 지표정의
corefct_unit_day
fct_lease_term_day
객실 1일 = 1행 · 계약기간 1일 = 1행
martmart_occupancy_daily
mart_amr_monthly
가동률 · 평균임대료

데이터 테스트 8종 — 가동률 범위 검증 · 차단 객실 점유 금지 · 평균임대료 순서 · 시간대 유효성 외.

Accounting

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

원상회복비·위약금·재계약 수수료·카드 수수료·공과금·조정금… 항목은 계속 늘어납니다. 그때마다 개발하지 않도록 항목을 데이터로 정의합니다.

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

구분은 청구 · 수납 · 지출 · 조정이고, 청구 − 수납 = 미수금입니다. 임대비용을 계약에서 계산해 보여주는 파생값으로 두면 상태(예정·청구·완납·연체·면제)를 담을 수 없습니다. 그래서 청구 회차를 테이블로 승격하는 것이 온라인 결제의 선행 작업입니다.