데이터베이스 정규화(1NF~3NF)가 필요한 이유와 실례

많은 데이터베이스 설계자들과 개발자들은 시스템의 안정성을 높이기 위해 데이터베이스 정규화를 어떻게 효율적으로 적용할 것인지 깊게 고민합니다.

관계형 데이터베이스 디자인에서 가장 핵심이 되는 1NF, 2NF, 3NF 과정을 제대로 이해하고 실무에 적용한다면, 데이터의 중복을 제거하고 이상 현상을 근본적으로 방지할 수 있습니다.

그래서 오늘은 실제 시스템 유지보수 및 개발 경험을 바탕으로, 1NF부터 3NF까지의 데이터베이스 정규화가 필요한 이유와 구체적인 실례를 통해 개념을 쉽게 정리해 보도록 하겠습니다.


1. 데이터베이스 정규화와 이상 현상의 이해

우리가 일상적으로 구축하는 수많은 애플리케이션 뒤에는 데이터를 안전하게 저장하는 테이블 구조가 존재합니다. 초기 설계 단계에서 무작정 하나의 큰 테이블에 모든 정보를 밀어 넣으면 당장은 조인(Join) 연산이 없어 조회가 편해 보일 수 있습니다.

하지만 시간이 지나고 데이터 양이 늘어날수록 심각한 데이터 불일치와 오류가 발생하게 됩니다. 이러한 구조적 결함을 해결하고 데이터를 논리적 단위로 쪼개어 최적화하는 일련의 과정을 데이터베이스 정규화라고 부릅니다.

정규화 과정을 거치지 않은 비정규 테이블에서는 데이터의 삽입, 수정, 삭제 과정에서 크게 세 가지의 이상 현상(Anomaly)이 발생하게 됩니다.

  • 삽입 이상: 새 데이터를 추가할 때 원하지 않는 불필요한 정보까지 억지로 함께 입력해야 하거나, 특정 데이터가 없으면 삽입이 불가능한 현상입니다.
  • 갱신 이상: 중복된 데이터 중 일부만 수정되어 같은 정보 내에서 데이터 불일치가 일어나는 현상입니다.
  • 삭제 이상: 특정 정보를 삭제할 때 그와 연관된 유용한 다른 정보까지 의도치 않게 함께 유실되는 현상입니다.

이러한 문제들은 단순히 저장 공간을 낭비하는 것에 그치지 않고 비즈니스 로직 전체의 신뢰성을 무너뜨립니다. 따라서 우리는 단계별 정규화 규범인 1NF, 2NF, 3NF를 차례대로 적용하여 테이블을 견고하게 설계해야 합니다.


2. 제1정규형(1NF): 원자값의 확보와 실례

제1정규형(1NF)의 정의는 매우 직관적입니다. 테이블의 모든 도메인이 단일값, 즉 '원자값(Atomic Value)'으로만 이루어져야 한다는 규칙입니다. 하나의 컬럼이나 셀에 여러 개의 값이 쉼표나 공백으로 구분되어 무더기로 들어가 있다면, 이는 제1정규형을 위반한 구조입니다.

예를 들어, 쇼핑몰의 '주문' 테이블이 다음과 같이 설계되어 있다고 가정해 보겠습니다.

주문번호 (PK) 고객ID 주문상품
1001 userA 모니터, 마우스, 키보드
1002 userB 노트북

위의 테이블에서 '주문상품' 컬럼은 하나의 셀에 세 개의 상품명이 동시에 들어가 있어 원자성을 상실했습니다. 이 상태에서는 '마우스'를 주문한 고객만 조회하고 싶을 때 SQL 쿼리문에서 LIKE '%마우스%'와 같은 무거운 문자열 검색을 강제하게 되며, 인덱스를 타지 못해 조회 성능이 극도로 저하됩니다.


이를 제1정규형에 맞게 변경하려면 각 행마다 하나의 값만 매핑되도록 분리해야 합니다. 다음과 같이 데이터를 행 단위로 쪼개어 모든 속성이 원자값을 갖도록 수정하는 것이 1NF의 올바른 실례입니다.

주문번호 (PK) 주문상품 (PK) 고객ID
1001 모니터 userA
1001 마우스 userA
1001 키보드 userA
1002 노트북 userB


3. 제2정규형(2NF): 부분 함수 종속성 제거와 나의 실무 경험

제2정규형(2NF)은 우선 제1정규형을 만족한 상태에서, 기본키(PK)가 복합키(2개 이상의 컬럼으로 구성)로 구성되어 있을 때 적용됩니다. 핵심 규칙은 기본키의 일부분에만 종속되는 '부분 함수 종속성(Partial Functional Dependency)'을 완전히 제거하고, 모든 비기본키 컬럼이 기본키 전체에 완전히 종속되도록 분리하는 것입니다.

개인적으로 과거에 이커머스 솔루션의 주문 및 배송 시스템을 유지보수하던 시절, 정규화가 제대로 되지 않은 거대한 복합키 테이블 때문에 큰 곤혹을 치른 적이 있습니다. 당시 테이블은 {주문번호, 상품명}이 복합 기본키로 지정되어 있었고, 그 옆에 '제조사'와 '제조사전화번호' 컬럼이 함께 묶여 있는 구조였습니다.

이 구조의 문제는 '제조사' 정보가 {주문번호, 상품명} 전체에 종속되는 것이 아니라, 오직 기본키의 일부분인 '상품명'에만 종속된다는 점이었습니다. 이로 인해 다음과 같은 심각한 **이상 현상**들을 직접 겪었습니다.


"당시 특정 제조사의 전화번호가 변경되었을 때, 과거에 누적된 수만 건의 주문 내역 속 제조사 전화번호 컬럼을 전부 찾아 동시에 업데이트(UPDATE)해야만 했습니다. 이 과정에서 서버 부하가 급증하여 시스템이 느려졌고, 일부 행이 누락되어 동일한 제조사의 전화번호가 주문 건마다 다르게 노출되는 데이터 불일치(갱신 이상) 현상이 발생했습니다. 2NF의 중요성을 뼈저리게 깨달은 순간이었습니다."


이 문제를 해결하려면 부분 함수 종속 관계에 있는 속성들을 별도의 테이블로 떼어내야 합니다. {주문번호, 상품명}을 갖는 '주문상세 테이블'과 {상품명, 제조사, 제조사전화번호}를 갖는 '상품 제조사 테이블'로 명확히 분리함으로써 데이터 구조를 정형화하고 문제를 깔끔하게 해결할 수 있었습니다.


4. 제3정규형(3NF): 이행적 함수 종속성 해결과 구조 개선

제3정규형(3NF)은 제2정규형을 만족한 상태에서, 비기본키 컬럼들 간에 발생하는 '이행적 함수 종속성(Transitive Functional Dependency)'을 제거하는 과정입니다.

이행적 종속이란 A가 B를 결정하고, B가 C를 결정하는 관계(A → B, B → C)가 성립하여 결과적으로 A가 C를 결정하게 되는 형태를 말합니다. 주식별자가 아닌 일반 컬럼이 다른 일반 컬럼을 결정하는 구조를 쪼개는 것이 주된 목적입니다.

이해를 돕기 위해 임직원의 부서 배치를 관리하는 사원 테이블을 예로 들어보겠습니다.

사원번호 (PK) 사원명 부서코드 부서명
E001 장상균 D01 시스템유지보수팀
E002 김철수 D02 품질보증팀

이 테이블의 기본키는 '사원번호'입니다. 사원번호를 알면 '부서코드'를 알 수 있고(사원번호 → 부서코드), 부서코드를 알면 '부서명'을 알 수 있습니다(부서코드 → 부서명). 즉, 사원번호 → 부서코드 → 부서명이라는 이행적 함수 종속성이 성립합니다.

여기서 만약 신설 부서 '개발혁신팀(D03)'이 생겼지만 아직 소속된 사원이 한 명도 없다면, 이 부서 정보는 테이블에 삽입할 수 없습니다(삽입 이상). 사원번호가 필수이기 때문입니다.

또한, '품질보증팀'의 유일한 사원인 김철수가 퇴사하여 해당 행을 삭제하면 D02 부서 정보 자체가 아예 세상에서 사라져 버립니다(삭제 이상).

이러한 모순을 해결하는 3NF의 조치 방식은 종속 관계의 중심이 되는 주체를 별도 테이블로 독립시키는 것입니다. 즉, 사원 정보와 부서코드를 보관하는 '사원 테이블'과 부서코드 및 부서명을 매핑하는 '부서 테이블'로 이원화하는 것입니다. 이렇게 분리하면 사원이 없는 상태에서도 부서 코드를 자유롭게 추가 및 관리할 수 있게 됩니다.


5. 결론

지금까지 데이터베이스 정규화의 핵심 이론인 1NF, 2NF, 3NF의 개념과 필요성, 그리고 이를 통해 해결할 수 있는 다양한 이상 현상에 대해 심도 있게 살펴보았습니다.

정규화는 단순히 이론 교육을 위한 학술적 개념이 아니라, 실제 프로덕션 환경에서 대규모 트랜잭션을 안전하게 처리하고 데이터 무결성을 유지하기 위한 최고의 안전장치입니다.

다만, 실무에서는 무조건적인 고차 정규화(3NF나 BCNF 이상)가 항상 정답은 아닐 수 있다는 점도 기억해야 합니다. 과도한 정규화는 테이블의 개수를 지나치게 늘려 조인(Join) 연산의 횟수를 증가시키고, 이는 오히려 조회 성능을 떨어뜨리는 원인이 되기도 합니다.

따라서 비즈니스의 특성과 쿼리 패턴을 면밀히 분석하여, 정규화와 적절한 반정규화(De-normalization) 사이에서 최적의 균형점을 찾아가는 유연한 안목이 가장 중요합니다.

Post a Comment

다음 이전