와이프도 나도 DB 1차, 2차 정규화도 모르고 개발을 했다.
어제 와이프님께서 DB자격증 시험을 신청 하셨다고 해서 같이 공부하면서 정리한 내용을 올립니다. 물론 저도 '정규화'에 대한 이론을 대충 훓어서 시험을 본 후 실무에 적용하는데는 몇년이 걸린 것 같습니다.ㅜㅜ
DB이론은 실무가 먼저 있고 실무의 패턴을 이론으로 만든 것이기 때문에 이론부터 접근하려고 하면 실무가 잘 안풀리는 경향이 있습니다. 결국은 이론대로 가지만 실무의 출발은 이론대로 시작하게 되지는 않는 것 같습니다.
1차 정규화
1차 정규화는 다음과 같이 컬럼에 값이 두개가 들어있으면 1차 정규화 위반입니다. 경험이 조금 있으면 이 원칙에 대해 잘 몰라도 위반 할 일이 별로 없습니다. 다음 게시판에서 usre_id가 2개가 들어있는 레코드가 있는데 이것이 1차 정규화 위반입니다. 설계 없이 급하게 만들어야 하는 경우가 아니라면 이럴일은 잘 없지요.
Board
id | title | contents | user_id |
1 | 연애인 000 스캔들 | 어쩌구~ | kyeongrok, areum |
2 | 운동선수 000 홈런 | 저쩌구~ | areum |
1차 정규화: 속성이 원자값(Atomic Value)을 갖도록 함. "기본키" 보유.
user_id라고 되어 있는데 kyeongrok, areum은 테이블로 분리가 되지 않았고 기본키도 가지고 있지 않습니다. 그리고 id가 한 컬럼에 두개가 들어가는 상황도 현대의 rdb에서는 잘 발생하지 않지요. varchar타입으로 ,로 구분해서 넣는 것은 사실 String 1개 이기 때문입니다. 개념상 문제가 있다는 것입니다.
1차는 잘 발생하지 않으니 2차로 바로 넘어가보겠습니다.
2차 정규화 전
2차 정규화는 '부분 함수 종속성'이 있으면 2차 정규화 위반입니다. 꽤 자주 일어납니다. 역시나 게시판 입니다. 다음과 같이 제목(title), 내용(contents), 글쓴이(user_name), 이메일(user_email) 등이 들어가 있는 게시판을 머릿속에 떠올려 볼 수 있습니다. 하지만 그림을 그려놓고 보니 문제가 좀 있습니다.
Board
id | title | contents | user_id | user_name | user_email | password |
1 | 연애인 000 스캔들 | 어쩌구~ | kyeongrok | 김경록 | kyeongrok1@gmail.com | 1123 |
2 | 운동선수 000 홈런 | 저쩌구~ | kyeongrok | 김경록 | kyeongrok1@gmail.com | 1123 |
3 | 000 고액 트레이드 | 비싸고~ | areum | 이아름 | areum1@gmail.com | 1234 |
4 | 개발자 연봉 00만원 | 이제는~ | kyeongrok | 김경록 | kyeongrok1@gmail.com | 1123 |
... | ... | ... | ... | ... | ... | ... |
이름 중복, 이메일도 중복, password도 중복됩니다. 여기에서 user 'kyeongrok'이 password를 1123에서 4456으로 변경 한다면 위 테이블 기준 1, 2, 4번 3개 레코드의 password컬럼을 바꿔주어야 합니다. 글을 400개 썼다면 400군데를 바꿔줘야 하겠지요.
2차 정규화 : 1차 정규화를 완료한 후 수행됩니다. 2차 정규화의 핵심은 **부분 함수적 종속성(Partial Functional Dependency)**을 제거하는 것입니다.
'김경록'이라는 값이 들어있는 속성인 'user_name'(이름)은 게시판의 글 번호(id)의 종속적인 속성이 아닙니다. 게시판의 '게시글'과 '사용자'는 분리되어야 합니다. 왜냐하면 위와 같이 반복되기 때문입니다. 이름 뿐만 아니라 이메일 password도 마찬가지로 원자값이 아니기 때문에 2차 정규화를 해줍니다. 2차 정규화 결과는 다음과 같습니다.
2차 정규화 후
User와 Content로 분리 합니다. 그리고 기본키(Primary Key)를 만들어 줍니다. User라는 테이블로 분리를 해서 name이라는 속성은 원자값만 들어가게 되었습니다. 물론 동명이인이 있어서 같은 이름이 들어갈 수 있지만 id가 다르기 때문에 원자값이 됩니다.
User
id(pk) | name | english_name | password | |
1 | 김경록 | kyeongrok | kyeongrok1@gmail.com | 1123 |
2 | 이아름 | areum | areum1@gmail.com | 1234 |
... | ... | ... | ... |
Content
id(pk) | title | contents | user_id |
1 | 연애인 000 스캔들 | 어쩌구~ | 1 |
2 | 운동선수 000 홈런 | 저쩌구~ | 1 |
3 | 000 고액 트레이드 | 비싸고~ | 2 |
4 | 개발자 연봉 00만원 | 이제는~ | 1 |
이 구조에서 User는 여러개의 '글(Content)'을 쓸 수 있습니다. 그래서 User(1) - Content(N)또는 Content(N) - User(1)로 표현 할 수 있습니다.
여기에서 '식별관계'라는 말이 있는데 Content는 User의 Primary Key인 user_id를 Foreign Key로 가지고 있지만 primary key는 따로 따서 쓰고 있기 때문에 비식별관계라고 할 수 있습니다. 글(Content)은 user_id가 필요한 관계가 되었습니다.
용어의 어려움
식별 관계와 비식별관계는 자식 테이블의 기본키가 부모 테이블의 기본 키에 의존하는 관계입니다. 여기에서 자식은 Content이고 부모는 User가 되겠네요.
DB에서 부모자식은 상대적인 개념 입니다. 실무 관점에서는 User가 왜 Content의 '부모'인지도 좀 헷갈립니다. 만드는 앱에 따라서는 User보다 Content가 더 중요하고 선행되는 엔티티 일 수 있습니다. DB에서의 부모와 자식은 Foreign Key를 가지고 있으면 자식, 제공하면 부모 이렇게 생각해야 합니다.