← → 이동 · F 전체화면 · N 노트 · M 목차 · E ERD · 점선 용어 클릭
데이터베이스 · 팀 프로젝트

사망자 명의 금융거래
신속차단 시스템 DB 설계

금융위원회 2026년 9월 11일 시행 제도의 데이터 모델링

이서현 · 이채원

데이터베이스
배경

2개월의 사각지대

사망신고를 해도 은행이 그 사실을 알기까지 최대 두 달이 걸렸습니다.

기존에는 행정안전부의 사망자 정보가 월 1회, 그것도 일부 금융회사에만 제공됐습니다. 사망신고 기한 1개월과 합쳐지면 공백이 두 달까지 벌어집니다.

그 사이에

사망자 명의 계좌에서 인출과 이체가 계속 가능합니다. 상속인 간 분쟁, 명의도용 범죄의 통로가 됩니다.

월 1회 → 매일
정보 제공 주기
4,890개사
정보를 받는 전 금융권
신고 다음 날
차단 개시 시점
A
요구사항 도출

제도 문구를 DB 요구사항으로

보도자료의 각 문장이 어떤 테이블과 제약으로 바뀌었는지 대응시켰습니다.

제도 내용DB에서 표현해야 할 것
매일 1회 사망자 정보 수신배치 수신 이력 — 파일 단위 + 레코드 단위
사망신고 다음 날부터 차단차단개시일시 = 사망일이 아니라 신고일 + 1일
입금 등 필수 거래만 허용거래유형 × 방향을 데이터로 관리하는 규칙 테이블
치료비·장례비 예외 인출신청–서류–승인–지급 상태 흐름 + 지정 수취처
자동이체 중단약정과 처리결과를 분리, 실패사유코드 기록
처리 이력의 전자적 기록·관리감사로그 — 사망정보 조회 이력까지 포함
A
범위 정의

어디까지 구현했는가

차단이라는 하나의 흐름을 끝까지 구현하는 데 집중했습니다.

범위 내

은행 1개사 · 입출금이 자유로운 예금 1종

사망정보 수신 → 고객·계좌 상태 반영 → 거래 차단 판정 → 예외 인출 → 감사·집계

범위 외 — 제외 사유
카드·보험·증권업권별 원장 구조가 상이
상속 분할·상속세금융회사 시스템 밖의 업무
대출 원장차단 대상 유형으로만 존재
공동망 고정길이 전문참가기관 대상 비공개 규격
실제 행안부 API실습 환경에서 접근 불가
A
설계 근거

법령에서 모델을 도출했다

제도 문서만 보고 만든 것이 아니라, 근거 법령의 조문을 확인해 다섯 축으로 정리했습니다. 조문을 클릭하면 요지가 열립니다.

축근거 법령모델에 반영한 것
기준정보가족관계등록법 제84조·제85조 death_dt와 death_report_dt를 분리하고, 차단 기준을 신고일+1일로 확정
개념·논리민법 제998조의2 · 제1000~1003조 예외인출 엔터티 도출, 신청인 자격 도메인을 법정 상속순위로 제한
물리 스키마전자금융거래법 제22조 · 개인정보보호법 제24조의2 법정 필수 컬럼 배치, 보존연한 확정, 해시와 암호문 분리 저장
업무 규칙금융실명법 제3조 · 제4조 규칙 테이블 분리, 오매칭 권리구제 이력, 수취처 직수령 금지
검증·정보계전자금융감독규정 제13조 접근 감사로그에 조회 주체·목적·성공여부를 강제 저장
ⓘ 국가법령정보센터 현행본에서 조문을 직접 확인해 반영했습니다
A
전체 구조

MySQL 서버 1대 · 데이터베이스 5개 · 테이블 30개

실제 은행은 역할에 따라 시스템을 나눕니다. 그 구조를 하나의 MySQL 서버 안에서 데이터베이스(스키마)를 나누는 방식으로 재현했습니다.

외부

행정안전부
한국신용정보원

→
대외계 · ext_link · 3개

ext_death_file · ext_death_record · ext_inquiry_log

받은 데이터를 가공하지 않고 보관

→
계정계 · core_bank · 15개

cust · acct · acct_txn · death_block · block_rule · exc_* · payee_registry

잔액과 거래를 실제로 움직이는 원장

고객 접점

창구 · ATM
모바일 · 오픈뱅킹

→
채널계 · channel · 3개

chnl_request · chnl_response · chnl_block_decision

요청을 받아 계정계에 전달하고 결과를 반환

→
정보계 · info_mart · 4개

audit_death_access · stat_block_daily · stat_exception_daily · stat_mismatch

영업 종료 후 일배치로 집계. 개인정보 미보관

공통 · comm_code · 5개

code_group code_detail rsp_code org_code biz_calendar — 위 네 계층이 모두 참조합니다. 코드명과 응답 메시지가 여기 한 곳에만 존재합니다.

ⓘ 계층 내부는 FK로 묶고, 계층 사이는 값만 전달합니다 — 자세한 내용은 슬라이드 13
A
전체 흐름

데이터가 쌓이는 순서

사망신고부터 집계까지, 각 단계에서 어느 테이블에 행이 생기는지가 곧 그 테이블이 존재하는 이유입니다.

일어나는 일행이 생기는 곳
1새벽 1시, 사망자 파일 수신 대외ext_death_file
대외ext_death_record
2해시로 고객 매칭 대외ext_death_record
match_status_cd 갱신
3차단 생성
신고일+1일 00:00 산출
계정death_block
계정death_block_hist
계정cust_status_hist
계정acct_status_hist
4사망정보를 조회했다는 사실 정보audit_death_access
5ATM 출금 시도 → 차단 채널chnl_request
채널chnl_block_decision
채널chnl_response
일어나는 일행이 생기는 곳
6타행에서 입금 → 허용 계정acct_txn
계정acct balance_amt 갱신
7자동이체일 도래 → 실패 계정auto_transfer_result
8유가족이 장례비 신청
서류 → 2단계 승인 → 이체
계정exc_request → 계정exc_document
→ 계정exc_approval → 계정exc_payout
+ 계정acct_txn
9오매칭 판명 → 차단 해제 계정death_block 갱신
계정death_block_hist
계정cust_status_hist
10영업 종료 후 야간 집계 정보stat_block_daily
정보stat_exception_daily
정보stat_mismatch
읽기만 하는 테이블

계정block_rule 계정payee_registry 공통code_detail 공통rsp_code 공통org_code 공통biz_calendar

A
핵심 영역

거래 한 건이 처리되는 순서

출금 요청이 들어오면 세 곳을 읽어 판정하고, 네 곳에 기록합니다.

읽는 것 — 판정에 필요한 3곳
계정ACCT cust_no 예금주
balance_amt 잔액
계정DEATH_BLOCK block_status_cd 차단중인가
block_start_dtm 지났는가
계정BLOCK_RULE allow_yn 이 유형은 허용인가
순서가 중요하다

차단이 없으면 규칙은 조회하지 않습니다.

쓰는 것 — 결과가 남는 4곳
채널CHNL_BLOCK_DECISION 항상 · 허용도 기록
계정ACCT_TXN 허용일 때만 기표
계정ACCT.balance_amt 허용일 때만 갱신
정보AUDIT_DEATH_ACCESS 항상 · 조회 사실 기록
차단돼도 기록은 남는다

원장에는 없지만 판정이력과 감사로그에는 남습니다. 원장과 시도 이력을 분리한 이유입니다.

A
전체 ERD

30개 테이블 · 102개 FK 컬럼

아래를 클릭하거나 E 키를 누르면 전체 화면 ERD 탐색기가 열립니다. 스키마별로 보거나, FK가 어디서 왔는지 추적할 수 있습니다.

🗂️
ERD 탐색기 열기
스키마 필터 · 컬럼 검색 · FK 출처 추적
30
테이블
322
컬럼
102
FK 컬럼
5
스키마
A
설계 결정 ①

차단 기준일은 사망일이 아니다

사망신고 기한은 1개월입니다. 사망일로 차단하면 아직 신고되지 않은 사망을 은행이 알 수 없어 제도가 성립하지 않습니다.

구분값
사망일자2026-09-10
사망 신고일자2026-09-12
차단개시일시2026-09-13 00:00:00
얻은 것

어떤 경로로 데이터가 들어와도 틀린 차단 시각이 저장될 수 없습니다.

-- 애플리케이션이 아니라 DB가 강제한다
CONSTRAINT ck_block_start CHECK (
  block_start_dtm =
    TIMESTAMP(DATE_ADD(
      death_report_dt,
      INTERVAL 1 DAY))
)
A
설계 결정 ②

업무 규칙을 코드가 아닌 데이터로  [ block_rule 테이블 조회 예시 ]

아래 표는 컬럼 목록이 아니라 block_rule 테이블에 실제로 들어 있는 7개 행입니다. 거래유형 코드를 클릭하면 설명이 열립니다.

거래유형방향허용예외인출응답코드
DEP 입금들어옴허용——
TRI 이체입금들어옴허용——
WDR 출금나감차단가능D001
TRO 이체출금나감차단가능D001
ATO 자동이체나감차단불가D002
CLS 계좌해지나감차단불가D003
LON 대출실행나감차단불가D004
ⓘ 거래유형 txn_type_cd · 방향 dr_cr_cd · 허용 allow_yn · 예외 exception_possible_yn · 응답 reject_rsp_cd
핵심

이 표는 문서가 아니라 테이블입니다. 규정이 바뀌면 코드를 고치고 재배포하는 대신 UPDATE 한 줄로 끝납니다.

적용 기간을 갖는다

규칙을 수정하지 않고 종료일을 끊고 새 행을 추가합니다. 과거 판정의 근거가 보존됩니다.

응답코드는 어디에 있나

코드 자체는 comm_code.rsp_code 한 곳에만 있고, 쓰는 쪽은 코드 값만 갖고 FK로 참조합니다. 참조하는 컬럼이 7개입니다.

모순도 DB가 막는다

CHECK 제약 하나로 "허용인데 거절코드가 있는" 모순된 규칙이 저장되지 않습니다.

B
예외 인출

치료비·장례비는 어떻게 처리하나

상태와 테이블 이름을 클릭하면 각 단계의 의미와 테이블 속성을 볼 수 있습니다.

REQ접수
→
DOC서류검토
→
APV승인
→
PAY이체실행
→
CMP완료
/
REJ반려
테이블역할
계정EXC_REQUEST신청 1건의 전체 상태
계정EXC_DOCUMENT증빙서류 N건과 검증결과
계정EXC_APPROVAL차수별 승인 이력
계정EXC_PAYOUT실제 지급 1건
계정PAYEE_REGISTRY사전 등록된 병원·장례식장
ⓘ 테이블 이름을 클릭하면 속성과 역할이 열립니다
제도의 핵심

돈은 유가족에게 가지 않는다

승인되어도 병원·장례식장 계좌로 직접 이체됩니다. 본인 현금 인출이 아닙니다.

이 원칙을 애플리케이션 코드가 아니라 스키마로 강제한 것이 다음 장입니다.

B
설계 결정 ③

규칙을 코드가 아니라 DB에 새긴다

지켜야 할 정책은 하나입니다. 예외인출 대금은 등록된 병원·장례식장으로만 나간다. 이걸 어디서 막느냐가 문제입니다.

보통은 이렇게 막는다

애플리케이션 코드에서

if (!payeeRepo.exists(payeeId)) {
    throw new Exception("미등록 수취처");
}

문제는 이 코드를 거치지 않는 경로가 존재한다는 것입니다.

DB에 들어오는 경로는 하나가 아니다
창구 애플리케이션검증 코드 있음
야간 배치 프로그램다른 팀이 만듦
운영자의 DB 콘솔코드를 안 거침
데이터 이관 스크립트일회성이라 검증 생략

코드로 막으면 첫 번째 경로만 지켜집니다.

우리가 택한 방법

DB가 직접 거절하게 한다

payee_id BIGINT NOT NULL,

CONSTRAINT fk_expay_payee
  FOREIGN KEY (payee_id)
  REFERENCES payee_registry (payee_id)

NOT NULL — 비워 둘 수 없다
FOREIGN KEY — 등록된 수취처가 아니면 값이 될 수 없다

두 제약이 함께 있어야 합니다. NULL은 FK 검사를 통과하기 때문입니다.

결과

어느 경로로 들어오든 DB 엔진이 트랜잭션을 거절합니다. 상속인 개인계좌 번호가 들어갈 자리 자체가 스키마에 없습니다.

ERROR 1452: a foreign key constraint fails
(`exc_payout`, CONSTRAINT `fk_expay_payee`)

B
설계 결정 ④

FK를 걸지 않은 것도 설계다

전체 68개 FOREIGN KEY 중, 아래 8개 컬럼만 의도적으로 FK 없이 두었습니다.

관계FK를 걸지 않은 이유
EXT_DEATH_RECORD → CUST대외계 원천을 재적재·정리할 때 계정계가 막히면 안 됨
EXT_DEATH_RECORD → DEATH_BLOCK동일
CHNL_REQUEST.acct_no존재하지 않는 계좌번호로 들어온 요청도 기록되어야 함 — FK를 걸면 공격 시도를 추적할 수 없음
AUDIT_DEATH_ACCESS.target_cust_no감사로그는 대상이 사라져도 남아야 함
CHNL_BLOCK_DECISION.applied_rule_id규칙이 폐기돼도 과거 판정 이력은 보존되어야 함
BLOCK_RULE → ACCT_TXN판정 시점의 참조. 규칙 폐기 후에도 과거 거래는 존재
B
설계 결정 ⑤

이력은 왜 9999-12-31로 닫는가

고객 상태가 바뀔 때마다 기존 행을 남기고 새 행을 추가하는 SCD Type 2 방식을 썼습니다.

valid_to_dtm DATETIME(3) NOT NULL
  DEFAULT '9999-12-31 23:59:59.999',

UNIQUE KEY uk_cust_hist_current
  (cust_no, valid_to_dtm)

현재 유효한 행은 종료일시를 센티널 값으로 채웁니다.

NULL로 두면 안 되는 이유

MySQL의 UNIQUE는 NULL을 서로 다른 값으로 취급해 중복을 허용합니다. NULL이면 "현재 행"이 여러 개여도 막히지 않습니다.

센티널을 쓰면

고객당 현재 행이 정확히 1건임을 DB가 보장합니다. 배치가 두 번 돌아도 안전합니다.

다만 close_dt처럼 "구간의 끝"이 아니라 "사건의 발생 여부"를 뜻하는 컬럼은 NULL이 맞습니다. 센티널을 넣으면 평균 존속기간 같은 집계가 왜곡됩니다.

B
정규화

우리 모델은 어디까지 정규화됐나

30개 테이블 전부 제3정규형을 만족합니다. 단계별로 무엇을 제거했는지 점검했습니다.

단계제거 대상우리 모델에서 실제로 한 일
1NF다중값 · 반복 속성 증빙서류를 EXC_DOCUMENT로, 승인을 EXC_APPROVAL로 분리해 한 건당 한 행으로
2NF부분 함수 종속 복합키는 CODE_DETAIL(group_cd, code_cd) 하나뿐이며, code_nm은 두 컬럼이 모두 있어야 결정됨
3NF이행 함수 종속 기관명·코드명·응답메시지를 원장에 두지 않고 코드만 저장해 조인. 코드 테이블 5종이 이 역할
BCNF후보키가 아닌 결정자 점검 결과 별도 분해가 필요한 사례는 확인되지 않음
정규화가 만들어낸 테이블

30개 중 12개가 정규화 과정에서 분리된 것입니다. 이력 3종, 예외인출 4종, 코드 5종.

되돌린 것

3개 컬럼만 의도적 반정규화로 예외 처리했습니다. 근거와 대가는 다음 장에서.

B
설계 결정 ⑥

법령이 컬럼과 제약이 되다

규제 요건을 문서로만 정리하지 않고 실제 스키마에 반영했습니다.

법령 요건반영이유
개인정보보호법
제24조의2
rrn_hash CHAR(64)
rrn_enc VARBINARY(128)
검색용 해시와 저장용 암호문을 분리. 해시는 되돌릴 수 없어 매칭만 가능
전자금융거래법
제22조
terminal_id 추가 거래 추적·오류 정정에 필요한 법정 기록 항목
신용정보법
제20조의2
cust.close_dt 상거래관계 종료일. 보존기간 5년의 기산점
채택하지 않은 것

연 단위 파티셔닝은 포기했습니다

보존연한 관리에는 유리하지만, MySQL InnoDB는 파티션 테이블에 FK를 걸 수 없습니다. 실제로 시도하면 ERROR 1506이 납니다. 67개 FK로 얻은 참조 무결성을 포기할 수 없어 아카이브 테이블 이관 방식으로 대체했습니다.

B
정규화

의도적 반정규화 3건

이유는 두 가지뿐입니다. 조회 성능과 판정 시점의 값 고정.

컬럼이유왜 중복을 허용했나지불한 대가
ACCT
.balance_amt
성능 거래원장 합계로 구할 수 있지만, 20년치가 쌓이면 잔액 조회 한 번에 수만 행을 훑어야 함 잔액–원장 대사 쿼리가 필요해짐
ACCT_TXN
.balance_after_amt
시점 그 거래 직후의 잔액을 재계산 없이 답하기 위해. 오픈뱅킹 거래내역조회 API 응답 필드와도 일치 원장 수정 시 재계산 필요
DEATH_BLOCK
.death_report_dt
시점 원천이 정정·재전송돼도 판정에 실제로 쓴 값은 바뀌면 안 됨 원천과 불일치 가능 → 정합성 점검
오해하기 쉬운 것

보안이나 교차검증이 목적이 아닙니다. 중복을 만들었기 때문에 어긋날 위험이 생겼고, 그래서 대사 쿼리가 필요해진 것입니다.

판단 기준 3가지 — ① 조회가 쓰기보다 훨씬 잦은가 ② 재계산 비용이 데이터 증가에 비례하는가 ③ 어긋남을 검증할 방법이 있는가. 셋을 모두 만족할 때만 허용했습니다.

반정규화가 아닌 것

payee_id가 EXC_REQUEST와 EXC_PAYOUT 양쪽에 있는 것은 의미가 다른 별개 컬럼입니다. 신청 시 지정한 곳과 실제 지급한 곳. 이건 교차검증이 목적이 맞습니다.

B
시연 ①

경계값 — 같은 계좌, 같은 금액, 1분 차이

차단개시일시를 사이에 두고 동일한 출금 요청이 정반대로 처리됩니다.

요청일시시점판정응답메시지
2026-09-12 23:59:00차단개시 전허용0000정상 처리되었습니다
2026-09-13 00:00:01차단개시 후차단D001사망자 명의 계좌로 출금 거래가 제한됩니다

규칙 테이블을 조인한 판정 시뮬레이션 — IF문 없이 SQL만으로 판정합니다.

고객거래유형거래일시판정
C0000000002DEP 입금09-14 10:00허용 — 규칙상 필수거래
C0000000002ATO 자동이체09-15 03:00차단 — D002
C0000000002CLS 계좌해지09-15 09:30차단 — D003
B
시연 ②

제약조건이 실제로 막는다

미등록 수취처, 즉 상속인 개인계좌로 예외인출을 지급하려고 시도했습니다.

INSERT INTO exc_payout (exc_request_id, payee_id, ...) VALUES (1, 9999, ...);

ERROR 1452 (23000): Cannot add or update a child row:
  a foreign key constraint fails (`core_bank`.`exc_payout`,
  CONSTRAINT `fk_expay_payee` FOREIGN KEY (`payee_id`)
  REFERENCES `payee_registry` (`payee_id`))
오류번호

1452

FK 위반 — 부모 테이블에 없는 값

오류번호

1062

UNIQUE 위반 — 중복된 값

오류번호

3819

CHECK 위반 — 조건 불충족

총 12개 위반 시나리오를 검증했습니다. 차단 기준일 오류, 이중 지급, 이력 현재행 2건 등이 모두 DB 단계에서 거부됩니다.

B
시연 ③

무결성 검증 — 무엇을 확인했나

설계한 규칙이 데이터에서 실제로 지켜지는지 네 가지를 쿼리로 확인했습니다.

검증확인하는 것결과
잔액 = 원장 합계 반정규화한 balance_amt가 거래원장 입출금 합계와 같은가 5개 계좌 전부 일치
이력 현재행 고객마다 "지금 유효한 상태" 행이 정확히 1건인가
2건이면 현재 상태 조회에 답이 두 개가 됨
5명 전부 1건
유효구간 연속성 앞 행의 종료와 뒤 행의 시작이 정확히 맞물리는가
틈이 있으면 그 시점 상태를 알 수 없고, 겹치면 상태가 두 개
단절·중복 없음
왜 이 검증이 중요한가

"임의의 시점 T에 이 고객이 차단 상태였는가"에 답이 정확히 하나 나와야 차단의 정당성을 증명할 수 있습니다.

샘플 데이터 5명

김정상(생존) · 박순자(차단, 예외인출 2건) · 이영희 ×2(동명이인) · 최말순(오차단 후 해제)

A · B
마무리

한계와 다음 단계

설계의 전제

행안부 제공 정보에 사망신고일자가 포함된다고 가정했습니다. 실제 규격을 확인할 수 없었고, 이 가정이 틀리면 제도 자체가 성립하지 않습니다.

매칭의 한계

해시 기반 매칭은 동명이인을 구분하지만, 원천에 주민번호가 없으면 성명·생년월일로 다건매칭이 발생합니다. 자동 차단하지 않고 수작업 확인 대상으로 분리했습니다.

다음 단계

① 배치 프로시저 — 사망정보 매칭에서 차단 생성까지
② 판정 로직을 Java 서비스 계층으로 이관
③ 로그 보존기간 확정 (전자금융거래법 제22조)

Java 이관을 고려한 설계

판정 로직을 프로시저에 두껍게 넣지 않았습니다. 규칙은 테이블에, 조합과 분기는 서비스 계층에 두는 구조입니다.

A · B
감사합니다

질문

30
테이블
68
FOREIGN KEY
42
CHECK
17
UNIQUE

이서현, 이채원
배경 · 범위 · 아키텍처 · 차단 기준일 · 규칙 설계 · 예외인출 · 무결성 · 시연

데이터베이스
부록 A1

테이블별 정규화 점검

위반했다면 이런 모습우리 모델제거한 종속
exc_request.documents
= '가족관계증명서, 영수증'
EXC_DOCUMENT 분리 1NF · 한 칸에 여러 값. 서류별 검증결과를 기록할 수 없음
exc_request.approver1_id
exc_request.approver2_id
EXC_APPROVAL 분리 1NF · 반복 속성. 3차 승인이 생기면 구조 변경 필요
acct_txn.counter_org_nm
(상대 기관명)
ORG_CODE 조인 3NF · 기관명은 거래가 아니라 기관코드가 결정
acct_txn.rsp_message
(응답 메시지)
RSP_CODE 조인 3NF · 메시지는 응답코드가 결정
cust.cust_status_nm
('사망')
CODE_DETAIL 조인 3NF · 상태명은 상태코드가 결정
cust.cust_status_cd를
UPDATE로 덮어씀
CUST_STATUS_HIST 정규화는 아니지만 시점 정보 손실 방지
확인 방법

각 테이블의 비주키 속성이 기본키 전체에만 종속되는지 확인했습니다. 코드명·기관명처럼 다른 속성이 결정하는 값은 전부 참조 테이블로 뺐습니다.

부록
부록 A2

자료형 선택 근거

용도선택이유
금액DECIMAL(18,2)FLOAT/DOUBLE은 이진 부동소수점이라 십진 소수를 정확히 표현하지 못해 잔액 대사가 어긋남
일시DATETIME(3)TIMESTAMP는 2038년 한계가 있고 세션 타임존에 따라 값이 변환됨
계좌·고객번호VARCHAR앞자리 0을 보존해야 하고 산술 연산을 하지 않음
Y/N 여부CHAR(1)TINYINT(1)은 드라이버 설정에 따라 Boolean/Integer로 달라짐
코드값CHAR(4) + 코드표ENUM은 값을 추가할 때마다 ALTER TABLE이 필요
해시CHAR(64)SHA-256 hex 고정 64자. 주민번호 평문을 저장하지 않음
대리키BIGINTUNSIGNED는 JDBC에서 BigInteger로 매핑되어 다루기 번거로움
부록
부록 A3

코드표에 외래키를 걸기까지

문제

기본키가 두 컬럼이라 참조할 수 없다

코드표 CODE_DETAIL의 기본키는 (group_cd, code_cd) 두 컬럼입니다. 10이라는 코드값이 고객상태 그룹에도, 예외인출사유 그룹에도 있어서 코드값만으로는 유일하지 않기 때문입니다.

그런데 CUST 테이블에는 cust_status_cd만 있고 group_cd가 없습니다. 외래키는 부모의 기본키 전체를 가리켜야 하므로 걸 수가 없습니다.

생각해 본 대안

① 코드 그룹마다 테이블을 따로 만든다 → 테이블이 24개 늘어남
② CHECK로만 검증한다 → 코드가 추가될 때마다 제약을 고쳐야 함

해결

고정값 컬럼을 하나 붙인다

cust_status_cd  CHAR(4),
cust_status_grp CHAR(10)
  GENERATED ALWAYS AS ('CUST_ST') STORED,

FOREIGN KEY (cust_status_grp, cust_status_cd)
  REFERENCES code_detail (group_cd, code_cd)

cust_status_grp는 항상 'CUST_ST'가 되는 컬럼입니다. MySQL이 자동으로 채우므로 INSERT에 쓰지 않습니다. 이 컬럼 덕분에 두 컬럼짜리 외래키가 성립합니다.

결과

고객상태 컬럼에 예외인출 상태값 REQ를 넣으면 거부됩니다. 존재하는 코드인지뿐 아니라 이 컬럼에 맞는 그룹의 코드인지까지 막습니다.

비용 — 생성컬럼 35개 추가, FK마다 인덱스 자동 생성, CASCADE 사용 불가

부록
부록 A4

인덱스는 왜 필요한가

책 뒤의 찾아보기와 같습니다. 원하는 내용을 처음부터 넘겨보지 않고 바로 찾아가게 해 줍니다.

없으면

거래 100만 건에서 특정 계좌의 거래를 찾으려면 100만 건을 전부 읽어야 합니다. 이걸 풀 스캔이라고 합니다.

있으면

정렬된 색인을 따라가 수십 번의 비교로 찾습니다.

공짜가 아니다

저장 공간을 더 쓰고, INSERT할 때마다 색인도 갱신해야 해서 쓰기가 느려집니다. 그래서 자주 쓰는 조회에만 만듭니다.

PK · UNIQUE · FK 컬럼은 MySQL이 자동으로 인덱스를 만들어 줍니다. 아래는 그 외에 직접 추가한 것입니다.

인덱스이 조회를 위해
ACCT_TXN (acct_no, txn_dtm)계좌별 기간 거래내역 — 가장 빈번
DEATH_BLOCK (block_start_dtm)일별 신규 차단 집계
CUST_STATUS_HIST
(cust_no, valid_from_dtm)
특정 시점의 고객 상태 조회
EXT_DEATH_RECORD (rrn_hash)일배치 사망자 매칭
순서가 중요하다

(acct_no, txn_dtm)은 "계좌로 먼저 좁히고 그 안에서 기간으로 좁힌다"는 뜻입니다. 순서를 바꾸면 계좌별 조회에는 잘 듣지 않습니다.

실습 데이터가 소량이라 EXPLAIN 측정은 유의미하지 않아 설계 근거만 제시합니다.

부록
부록 A5

법령 매핑 ① 기준정보 · 예외인출

조문핵심 내용모델 반영
가족관계등록법 제84조사망 사실을 안 날부터 1개월 이내 신고사망일과 신고일이 최대 1개월 벌어짐 → 컬럼 분리
가족관계등록법 제85조동거하는 친족이 신고의무자신고 주체와 예외인출 신청인이 다를 수 있음을 전제
민법 제997조상속은 사망으로 개시사망 시점부터 계좌 자산은 상속재산 → 동결 원칙
민법 제998조의2상속비용은 상속재산 중에서 지급예외인출 제도의 법적 근거. exc_reason_cd 도메인
민법 제1000~1003조직계비속 → 직계존속 → 형제자매, 배우자는 공동상속applicant_rel_cd를 법정 순위로 제한
부록
부록 A6

법령 매핑 ② 보존 · 보안 · 실명

조문핵심 내용모델 반영
전자금융거래법 제22조거래기록 생성·보존 의무일시·금액·방향·채널·단말·상대방 컬럼 배치
시행령 제12조1만원 초과 5년, 이하 1년보존정책 확정. 아카이브 이관 기준
개인정보보호법 제24조의2주민번호 암호화 보관 의무rrn_hash(검색) + rrn_enc(저장) 분리
신용정보법 제20조의2상거래 종료 후 최장 5년 내 삭제·분리close_dt로 기산점 확보
금융실명법 제3조실명거래 의무, 실명계좌 자산은 명의자 소유로 추정오매칭 시 권리구제 → 해제 + SCD 이력
금융실명법 제4조명의인 동의 없는 제3자 제공 금지상속인 직수령 차단 → payee_registry FK
부록
부록 A7

금융권 공통 설계 체크리스트

금융 도메인 데이터 모델이 일반적으로 갖춰야 할 항목과 본 모델의 충족 현황입니다.

점검 항목본 모델
모든 테이블에 감사 컬럼(등록·수정 일시와 사용자)이 있는가충족
거래내역이 UPDATE·DELETE 없이 INSERT만 되는 구조인가충족
고유식별정보가 _hash / _enc로 구분되어 있는가충족
고객·계좌에 거래종료일이 있어 보존기간을 계산할 수 있는가충족
거래내역에 채널·단말·현금여부·상대방 정보가 있는가충족
마스터 변경 시 이력 테이블이 있는가충족
직원 권한과 고객정보 접근로그 구조가 있는가충족
코드성 값이 ENUM이 아닌 공통코드 테이블로 관리되는가충족
실명확인·고객확인(CDD) 정보가 기본정보와 분리된 이력 구조인가범위 외
부록
부록 A8

동명이인과 오매칭 처리

20 매칭성공해시 일치 → 자동 차단
30 미매칭당행 고객 아님 → 조치 없음
40 다건매칭성명·생년월일 복수 일치 → 수작업 확인
자동 차단하지 않는 이유

잘못 차단하면 생존 고객의 금융거래가 막힙니다. 다건매칭은 match_status_cd = '40'으로 분리해 담당자가 확인합니다.

해제 프로세스

DEATH_BLOCK의 상태를 해제로 바꾸고 DEATH_BLOCK_HIST에 사유와 조치자를 남깁니다.

샘플 데이터의 최말순이 이 경로를 밟습니다. 09-13 차단 → 09-15 해제 → 이후 정상 출금.

부록
ERD 탐색기
FK 컬럼을 클릭하면 부모 테이블로 이동 · ESC 닫기
테이블 이름을 클릭하면
참조 관계가 여기에 표시됩니다.

FK 컬럼은 파란색이며, 클릭하면 값이 어디서 왔는지
부모 테이블로 이동합니다.

슬라이드 목차 — 클릭하면 이동합니다 (M 닫기)