[전문가 칼럼] 관계형 데이터 모델링 중 식별자에 대한 이야기

1. 데이터 모델링의 정석과 현장의 데이터 모델러

필자는 데이터 모델링 강의도 진행하지만, 현장에서 모델러로서 활동하기도 합니다. 강의할 때 수강생에게 이야기 하는 것은 데이터 모델링의 정석을 이야기 합니다. 그리고 현장에서는 많은 부분 수강생들에게 이야기 한 것과 반대의 모델링을 할 때도 많습니다.  왜 이런 일이 발생할까요?  뭐가 맞는 것일까요? 강의장에서 데이터 모델링의 정석을 이야기 하는 저와 현장에서의 데이터 모델러로서의 저는 다른 사람일까요? 아니면 제가 이중 인격이라는 고차원적 기술을 구사하는 어마무시한 사람일까요? 이와 관련해서 재미있는 경험을 한 적이 있었습니다. 감리사 자격증을 보유하고 계신 분이 수강생으로 들어오신 적이 있었습니다. 그리고 6개월 후 제가 수행하고 있는 프로젝트에 감리 담당자로 만났습니다. 제가 만들어 놓은 모델을 보더니 한 말씀 하시더군요.  “강의장에서 말씀 하신 것과 너무 다릅니다. 강의장에서는 이렇게 말씀하지 않으셨잖아요?”  웃으며 한 말씀 하시더군요. 강의장에서는 데이터 모델링의 정석을 이야기 하고, 현장에서는 강의장에서 했던 이야기와 다른 데이터 모델링을 했다는 이야기 이겠지요. 그리고는 “현장에서는 당신도 어쩔 수 없네.” 라고 속으로 생각하는 것 같았습니다. 그리고는 감리사님은 회심의 한 방을 날린 듯 뿌듯해 하고 있었습니다.  

그때 전 평상시에 제가 생각하고 있던 부분을 조용히 이야기 했습니다. 교육장에서는 모델링의 정석을 강의해야 하지만, 현장에서는 현장 상황에 맞춰 모델링을 진행해야 합니다. 프로젝트에 가 보면 프로젝트마다 해당 사업에서 추구하는 목적은 다 다릅니다. 데이터 모델을 완벽하게 만드는 것이 차세대의 목적이면 모델링의 정석에 맞춰 데이터 구조를 설계해야 합니다. 하지만 어떤 프로젝트는 다양한 시스템에 대한 통합이 목적인 경우가 있습니다. 그럴 때는 중요한 키를 담당하는 기준정보 엔터티는 통합을 시키고, 업무를 담당하는 하위 엔터티들은 가급적 ASIS 형태를 취하기 위한 노력을 해야 합니다. 모 기관에서는 15개 업무를 하나의 시스템으로 통합하는 것이 목적이었습니다. 그 때 키 엔터티(기준정보 엔터티) 부터 시작해서 업무 영역의 모든 엔터티까지 멋지게 데이터 모델을 설계했다면 아마도 그 사업은 종료될 수 없었을 것입니다. 데이터 모델이야 멋지게 설계되었겠지만, ASIS와 너무 다른 엔터티로 인해 개발자들의 투입 공수는 엄청나게 늘어나야 했을 겁니다. 개발 투입 공수는 그렇다 치더라도 데이터 전환은 거의 불가능한 수준이 아닐까 생각합니다.

가끔 보면 한결 같이 올 바른 데이터 모델링만 고집하시는 분을 종종 보게 됩니다. 데이터 모델러로서 굉장히 위험한 사상을 가진 분이라 생각됩니다. 데이터 모델러는 프로젝트 상황에 맞춰 해당 프로젝트에 맞는 모델링을 해줘야 한다는 것이 저의 입장입니다. 궁극적으로는 데이터 품질 측면에서 완벽함을 추구하는 것이 맞지만, 이상과 현실을 구분할 수 있는 능력이 있어야 합니다.  그렇다 하더라도 모델링 정석을 알지 못한 채 무조건적으로 현장에만 맞추는 모델링과는 다른 이야기 입니다. 감리사님도 얘기를 다 듣고 나서 제 생각에 동의했던 기억이 납니다. 


2. 엔터티 분류

식별자를 이야기 하기 전에 먼저 엔터티 분류에 대한 이해가 필요 합니다. 각 엔터티의 분류에 따라 식별자를 다르게 만들어 줘야 하기 때문입니다. 엔터티를 분류하는 기준은 굉장히 많이 존재하는데요. 필자가 가장 중요하게 생각하는 분류는 키(기준정보) 엔터티, 메인 엔터티, 액션 엔터티로 나누는 것입니다.

2.1. 키엔터티

부모가 없는 엔터티 입니다. 신과 사람을 구분하는 것과 같습니다. 신은 부모가 없습니다. 하지만 인간은 부모가 있죠. 부모를 모를 수는 있어도 부모가 없을 수는 없습니다. 키 엔터티는 부모가 없습니다. 모범답안의 고객, 차량, 제조및수입사, 부품 엔터티가 여기에 속합니다.

2.2. 메인엔터티

부모는 있으나, 하위로 무수히 많은 자손을 낳는 엔터티입니다. 맨 아래 부분의 모범답안에서 매매, 사고현황이 메인 엔터티 입니다. 실습문제의 요구사항이 A4용지 1페이지가 안되는 아주 간단하여 하위엔터티로 자식을 많이 낳지 않아서 그렇지, 실무에서는 무수히 많은 자식을 낳게 될 겁니다. 매매 엔터티는 고객이 차량을 매매하였다는 사연에 의해 발생한 엔터티 입니다. 키 엔터티로 분류되는 고객 엔터티는 C0001을 홍길동으로 하기로 하였다. 이렇게 사연 없이 정의되지만, 메인 엔터티는 부모가 있기에 부모가 수행했던 행위에 의해 도출되는 엔터티 이죠. 부모가 했던 행위를 “사연”이라고 표현하였습니다. 메인엔터티와 액션엔터티는 사연이 존재하는 엔터티들입니다. 고객이 상품을 가입하면 신용카드회사에서 카드엔터티, 은행의 계좌엔터티, 보험회사에서 보험계약엔터티가 도출됩니다. 쇼핑몰에서 고객이 상품을 주문하면 발생되는 주문엔터티 역시 메인 엔터티들의 대표적인 사례입니다.

2.3. 액션엔터티

위에서 이야기 하였듯이 액션엔터티와 메인엔터티는 사연들이 있습니다. 즉 부모가 어떠한 행위에 의해 발생하는 행위 엔터티들입니다. 메인 엔터티는 많은 자손을 낳지만, 액션 엔터티는 그렇지 않습니다. 모범답안에서 매매차량, 수리부품현황 엔터티가 이에 속할 수 있습니다.  현장에서는 내역, 현황, 이력 등의 엔터티가 대표적인 액션 엔터티 입니다.


3. 식별자

데이터 모델링은 다양한 단계가 있습니다.  주제영역 식별 🡪 핵심엔터티 도출 🡪 관계 🡪 속성정의 🡪 식별자 확정 🡪 정규화 🡪 이력관리 🡪 논리 물리 변환 🡪 반정규화 등의 단계가 있습니다. 이중에서 가장 중요한 것을 꼽으라 한다면 전 항상 “식별자 확정”을 이야기 합니다. 혹자는 정규화라고 하는 분들도 많이 있겠지만, 저는 식별자라 생각합니다. 식별자는 엔터티의 정체성을 지정하는 속성입니다. SQL을 구현할 때 조인 연결고리 칼럼으로 활용되기에 매우 중요한 역할을 합니다. 하지만 현장에 나가 ERD를 볼 때 식별자 설계가 잘못되어 데이터의 모순과 잘못된 데이터가 입력되어 있는 경우를 너무도 많이 봐 왔습니다. 데이터 품질의 직접적인 영향을 미치는 식별자는 데이터 모델링 절차에 있어 가장 중요한 단계라 생각합니다. 지금부터 식별자 확정 절차에 대해 이야기 해 보겠습니다.

식별자는 물리데이터모델로 가서는 Primary Key로 구성되는 것이며, 논리데이터모델에서는 식별자라 부릅니다. 식별자는 무결성에서 엔터티 무결성으로 다루어 집니다. 각각의 무결성은 아래와 같습니다.

① 엔터티 무결성 : 엔터티는 행을 유일하게 구분할 수 있어야 한다.
② 도메인 무결성 : 열의 값은 비슷한 형식이어야 한다.
③ 참조 무결성 : Primary Forein Key 제약 조건
④ 사용자 정의 무결성 : 단가 X 수량 = 금액 등과 같은 업무 규칙에 의해 정의

또한 관계형 데이터 모델링 이론에서 데이터 구조의 정의에서 다시 다루어 집니다.  데이터 구조의 정의는 아래와 같습니다.

①열은 유일한 이름을 가져야 한다. 
②열은 하나의 값을 가져야 합니다. 🡨 1정규형
③열의 값은 비슷한 형식이어야 한다. 🡨 도메인 무결성
④행은 유일하게 구분할 수 있어야 한다. 🡨 엔터티 무결성
⑤행과 열의 순서는 무의미 하다.

식별자는 다음과 같은 조건을 지켜야 합니다. 

① Not Null
② Unique
③ 최소 속성으로 구성

식별자의 종류는 실질식별자, 본질식별자, 인조식별자, 후보식별자, 대체식별자 이렇게 나누는 것이 일반적입니다. 더 다양하게 분류하는 경우도 있지만, 데이터진흥원에서 발간한 데이터카이텍쳐 전문가 가이드에서 위와 같이 분류되어 있기에 필자 역시 위와 같이 분류해 보겠습니다.

3.1. 실질식별자

실질 식별자는 물리 모델에서 Primary Key로 만들어지며, 논리데이터 모델에서 엔터티 내에 식별자로 분류된 속성 입니다. 아래 모범 답안의 고객 엔터티의 고객번호, 차량 엔터티의 차량번호, 제조및수입사의 회사번호, 매매의 매매번호, 매매차량의 매매번호+매매차량번호, 사고현황의 사고차량번호+사고일자, 수리부품현황의 사고차량번호+사고일자+수리부품번호, 부품의 부품번호 입니다. 즉 실제 식별자로 분류된 것이 실질 식별자 입니다.

3.2. 본질식별자

본질식별자는 부모로부터 물려 받은 식별자 입니다. 아래 모범답안의 사고현황 엔터티의 본질식별자는 사고차량번호 입니다. 사고현황 엔터티는 부모에게 물려받은 본질 식별자인 차량번호를 식별자로 사용하고 있으며, 이 관계를 “식별자 관계”라 합니다. 

3.3. 인조식별자

부모로부터 물려 받은 식별자를 자신의 일반속성으로 활용할 수 있습니다. 이때 자신의 식별자는 아무 의미없는 번호를 만들어 사용합니다. 이를 인조식별자라 합니다. 매매 엔터티의 매매번호가 인조식별자 입니다. 이를 “비식별자관계”라 합니다. 식별자 관계와 비 식별자 관계의 장.단점은 식별자 상속과 단절의 원리에서 설명 하겠습니다. 참고로 매매엔터티의 본질식별자는 부모로부터 물려받은 매매고객번호 속성입니다.

3.4. 후보식별자

 후보 식별자는 말 그대로 식별자로 사용할 수 있는 후보 속성들입니다.

3.5. 대체식별자

 후보 식별자로 올라 갔다가 식별자로 선정되지 않는 속성들입니다. 대체 식별자도 행을 유일하게 식별할 수 있는 속성입니다. 고객 엔터티의 주민번호 속성이 대표적인 예입니다.

3.6. 식별자 상속과 단절의 원리

사고현황은 부모로부터 물려받은 본질식별자를 자신의 식별자로 활용하였습니다. 자식으로 내려갈수록 식별자 개수가 증가하는 단점이 있으며, 장점으로는 자식인 수리부품현황이 부모인 사고현황을 경유하지 않고, 할아버지 엔터티인 차량과 직접 조인이 가능한 것이 장점입니다.  반대로 매매 엔터티는 부모에게 물려받은 본질식별자인 매매고객번호를 일반 속성으로 사용했습니다. 따라서 매매차량인 자식에게 매매고객번호를 물려주지 않습니다. 장점으로는 매매 및 매매차량 엔터티의 식별자 개수는 줄어들어 단순성은 확보했지만, 매매차량은 고객 엔터티와 조인하기 위해서는 매매 엔터티를 반드시 경유해야 하는 단점이 존재합니다.

3.7. 식별자 확정

키 엔터티의 식별자 확정

 키 엔터티의 식별자는 인조식별자냐? 본질식별자냐?를 구분할 필요가 없습니다. 이유는 부모가 없는 엔터티 이기 때문 입니다. 따라서 ~~~번호 등으로 해서 하나의 속성으로 식별자를 만들어 주면 됩니다. 가끔 키 엔터티의 식별자가 인조식별자인지, 본질식별자인지 구분하고자 하는 분이 있는데요. 키 엔터티의 식별자는 인조 또는 본질의 구분 자체가 무의미 합니다. 부모가 없기에 부모로부터 물려 받을 식별자가 없기에 그렇습니다.

메인 엔터티의 식별자 확정

 원칙적으로는 본질 식별자를 활용해야 합니다. 하지만 자손을 여러 단계로 발생시키는 엔터티 라면 본질식별자를 식별자로 활용하였을 경우 자손의 식별자 속성 개수가 많아지는 단점이 있습니다. 따라서 자손을 많이 낳는다면 인조식별자를 사용하는 것이 좋습니다. 예를 든다면 신용카드 회사에서 카드 엔터티의 카드번호, 은행에서 계좌의 계좌번호, 보험회사에서 계약의 계약번호, 유통회사에서 주문의 주문번호가 대표적인 인조식별자 입니다. 이렇게 하면 식별자 개수가 적어집니다. 모델의 단순성 측면에서 유리하죠. SQL 활용 시 하위 테이블과 조인 시 조인 연결고리 칼럼이 적어져서 사용이 편리해 지는 장점이 있습니다. 단점이라면 인조식별자를 사용한 엔터티의 자식 엔터티는 자신의 할아버지를 직접 조인할 수 없습니다.

액션 엔터티의 식별자 확정

자신의 조상을 직접 접근(조인)이 가능한 본질식별자를 식별자로 활용해야 합니다. 매매엔터티에서의 매매번호는 인조식별자이지만, 매매차량 엔터티에서의 매매번호는 부모를 찾아가기 위한 중요한 속성으로 본질식별자에 해당 합니다. 

3.8. 인조식별자를 활용할 경우 주의할 점

 인조식별자를 사용할 경우 식별자 속성의 개수는 하나로 해야 합니다. 인조식별자를 활용하는 이유는 자손이 할아버지를 직접 조인이 불가능한 단점에도 불구하고 식별자 개수를 줄이기 위해 사용합니다. 인조식별자와 본질식별자를 함께 식별자로 사용하는 경우가 있는데, 이는 식별자 개수가 오히려 증가하는 현상이 발생합니다. 따라서 인조식별자를 사용했을 경우는 반드시 하나의 속성으로 Unique를 보장해야 하며, 다른 속성과 함께 식별자로 구성하는 것은 피해야 할 방법입니다.

모든 엔터티의 식별자를 인조식별자로 활용하는 곳을 볼 수 있습니다. 이런 모델을 볼 때면 너무도 행복해 집니다. 이렇게 설계하신 분들 때문에 아직도 우리가 모델러라는 타이틀을 달고 밥먹고 살 수 있기 때문입니다. 좀 돌려서 이야기 했는데요. 직접적으로 말씀드리자면, 치명적인 실수 입니다. 한 단계만 건너서 조인을 하려 한다면 불필요한 조상 엔터티를 모두 경유해야 합니다. SQL을 작성할 때 매우 어렵기도 할 뿐 아니라 속도 측면에서도 매우 불리합니다. 이를 피하기 위해 관계의 반정규화를 엄청나게 해야 합니다. 모든 하위 엔터티에 고객번호, 차량번호 속성을 달고 다닙니다. 중복이며, 관계의 반정규화 입니다. 고객번호 속성에 저장된 값과, 관계를 따라 올라 갔을 때의 고객번호가 다를 수 있다는 것입니다. 식별자 설계만 정상적으로 했다면 구지 이러지 않아도 되었을 텐데, 식별자 설계는 대충 해 놓고, 이를 해결하기 위해 반정규화를 엄청나게 하는 사례입니다. 현장에서는 너무도 많이 볼 수 있는 사례 중 하나 입니다.


[실습문제]

우리회사는 중고차 매매하는 회사로서,  중고차 매매관리시스템을 만들고자 한다.

우리는 고객에게 차량을 사기도 하고 판매하기도 한다.  또한 한 번의 매매에 다수의 차량을 거래한다. 하지만 판매와 구입을 하나의 매매에서 발생할 수는 없다.  우리는 판매 또는 매입한 고객을 관리한다.  고객번호, 이름, 연락처, 주소이며,  법인일 경우 대표자, 대표자연락처를 관리한다. 

우리는 차량의 매매에 대해, 매매번호, 매매고객, 매매구분(구입, 판매), 매매일자, 매매총금액, 차량별매매금액을 관리한다.  

차량은 차량번호, 모델명, 최초차량가액, 출고일자 관리한다.  차량의 모델을 별도로 관리하지 않고, 모델명으로만 관리한다.  차량의 종류는 승합차, 승용차, 화물차가 있으며, 승용차의 경우에는 사고현황을 관리한다.  사고일자, 사고장소, 수리비용을 관리해야 하며, 수리 부품도 상세 관리해야 한다.  수리 부품은 정품으로 수리를 할 수도 있으며, 정품이 아닌 비품으로도 수리할 수 있다. 비품일 경우 제조사명을 반드시 관리 해야 한다. 수리 부품은 부품명을 관리해야 한다. 수리한 부품은 부품수량을 관리한다.

차량은 국산차량과 수입차량이 있으며, 국산차량일 경우는 제조사를 수입차일 경우는 수입회사를 관리해야 한다.  제조사는 제조사번호, 제조사명, 대표공장위치를 관리하며,  수입회사일 경우는 회사번호, 회사명, 설립일자, 전년도매출액, 대표자명, 종업원수를 관리한다.


[모범답안]


**해당 콘텐츠는 저작권법에 의해 보호받는 저작물입니다.
**해당 콘텐츠는 사전 동의없이 2차 가공 및 영리적인 이용을 금하고 있습니다.