✍️ 데이터 정합성을 위한 정규화, 개발 및 운영상의 편의성 & 효율성을 위한 비정규화
모든 결정자 중에 후보키가 아닌 것을 제거하는 것
엄격한 제3정규형이라고도 하며, 그래서 그런지 제3정규형과 비슷해보여 헷갈렸음
아래는 제3정규형에는 위배되지 않으나, 보이스/코드 정규형에 위배되는 예시
Schedule 테이블 속성: 강의실, 타임, 과목, 강사진
수업 정책:
1) 강의실은 타임에 따라 다른 과목이 배정된다
2) 같은 과목이더라도, 강의실+타임에 따라 다른 강사진이 배정될 수 있다
3) ✋ 강사진은 항상 같은 강의실에만 배정된다
'과목'과 '강사진'은 후보키(강의실+타임)에 의해 결정되고 있으나,
'강사진'에 의해서 '강의실' 속성이 결정되고 있다는 걸 알 수 있음. 이때, '강사진'은 여러 레코드에 중복으로 나타날 수 있으므로, 후보키가 아님.
이러한 경우, 강사진-강의실 정보를 담고 있는 개별 릴레이션으로 분리할 수 있음
보통 데이터베이스 설계 및 검토 과정에서 정규화 공식을 하나하나 대입하지는 않았던 것 같다. 정규화 규칙들을 직접 대입시키기 보다는, 도메인/도메인을 구성 하는 요소/메타데이터인지 세부 데이터인지 등에 따라 계속 분리해나가고 검토하는 과정에서 중복이 발생하는지 + 기능 개발시 데이터를 어떻게 가져올지를 고려하며 수정하다보면 정합성이 지켜지는 설계가 나왔던 것 같다.
인턴 근무 시절, 이전에 동아리 프로젝트나 수업시간에서는 생각해보지 못했던 정규화 과정을 진행했었다. 유저 테이블에 대한 내용이다.
실제 여러 서비스들을 보면, 회원가입 때 필수 입력값과 선택 입력값이 있다. 그리고 회원가입때는 없지만, 이후 로그인-마이페이지 등에서 새롭게 입력할 수 있는 회원 정보가 있을 수 있다. 이런 상황들에 따라, '회원'에 대한 정보를 담는 users 테이블에는 아래와 같은 속성들로 구성될 수 있다.
필수 입력: 유저 id(pk), 이메일, 비밀 번호, 마케팅 수신 여부, 가입 채널, 회원 등급, 닉네임, 회원 상태(활성/휴먼/탈퇴 등)
null값 허용: 성별, 추천인, 연락처, 주소, 프로필 이미지, 국가 코드, 우편 번호, 상세 주소, 생년월일, 타임존, 언어 등
간단한 사이드 프로젝트를 진행할때는 유저 테이블에 null값으로 들어가게 될 속성들이 많은 경우가 없었다. 기본적인 유저 정보(user_id, email, password, rank, status) 정도였다.
연락처나 성별 등 null 값을 허용하는 속성이 있었지만, 1) user_id와 1:1로 결정되는 데이터였음 2) 이런 옵션 속성 개수 자체가 매우 적었음
의 이유로 users 테이블과의 분리를 크게 생각 안 했었다.
하지만 실무 프로젝트에서는
user_id와 1:1로 정해지는 데이터긴 하나, null값으로 설정되야 하는 속성이 많았고, 소셜 링크처럼 1:N으로 계속 확장되어야 하는 속성들도 여럿 있었다.
처음에는 한 명의 회원에 대한 기본 정보에 다 해당 된다고 생각해서, 이걸 어떤식으로 나눠야 할지 감이 안 왔다. 하지만 공부하고, 동료 & 시니어 개발자분들과 논의하며, 테이블을 분리해가는 방향성에 대해 차근차근 경험해 갈 수 있었다🙂
user_id와 1:N 관계를 가지는 속성(ex. 소셜 링크, 연락처 등)
: user_social_profiles 테이블 등으로 분리 시키는 편이 확실하다. 보통 유저에 대한 핵심 정보를 담은 users 테이블은 유저 한 명당, 고유한 레코드를 가진다. 이에 따라, 1:N을 표현하려면 social_link1, social_link2 등 불필요한 속성들이 계속 늘어난다.
따라서, 소셜 링크를 입력한 유저들에 한해서만 레코드를 추가하고, 소셜 링크 타입/소셜 링크 url 등 관련 속성들이 더 필요할 때 확장하기 쉽도록 별도 릴레이션(테이블)로 분리하는 것이 좋다
user_id와 1:1 관계를 가지지만 null값이 허용되는 속성
: 이런 옵션 속성이 몇 개 안된다면, 테이블를 엄격하게 분리하는 것이 조회 성능이나 개발/운영의 편리성 측면에서 오히려 안 좋을 수 있다. 하지만, 어느정도 서비스 규모가 생기면 회원과 관련된 정보가 많아질 수 밖에 없다. 이때는 회원 정보 중에서도 어떤 정보에 속하냐에 따라서 테이블을 분리할 수 있다.
예를 들어,
users - 유저id, 이메일, 비밀번호, 등급, 회원 상태(활성/휴먼/탈퇴)
user_profile - 프로필 이미지, 성별, 닉네임, 전화번호
user_social_links - 소셜 링크 타입, 소셜 링크 url, 공개 여부
user_settings - 언어, 타임존, UI 테마
user_security - 유저 id, 토큰
등으로
주로 어떤 속성들끼리 함께 조회, 생성, 수정, 삭제 될 것 인지
어떤 종류의 데이터를 나타내는 것인지 등을 고려하여 '회원 기본 정보'라는 큰 카테코리를 세분화 할 수 있다.
이런 정규화 과정을 통해, 불필요하게 users 테이블이 비대해지는 문제를 방지할 수 있다.
또한, 현재는 단순히 '마케팅 수신 여부'만 수집중이나, 추후 마케팅 채널, 동의 일자 등 관련 속성이 확장될 수 있을 것 같은 속성은 미리 분리해 둘 수도 있다.
user_marketing_consent - 수신 동의 여부, 채널, 동의 일자
정규화에 반대되는 과정입니다. 즉, 정규화가 데이터 일관성 & 무결성을 위해 릴레이션을 분리해간다면, 반정규화는 개발/운영상의 단순함과 시스템 효율성 향상을 위해 데이터 중복, 통합 등을 적용하는 것이다.