본문 바로가기
AI 리더의 시대

중복을 줄이는 실전 DB 설계 전략

by woojoon 2025. 12. 14.
반응형

실전 DB 설계: 중복 제거가 답이다 관련 이미지

 

 

데이터베이스 설계에서 가장 흔히 저지르는 실수는 데이터 중복입니다. 같은 정보가 여러 곳에 저장되면 당장은 편리해 보일 수 있습니다. 하지만 시간이 지나면 수정할 때 일부만 바뀌고, 삭제할 때 의도치 않은 정보까지 사라지며, 저장 공간이 낭비되는 심각한 문제가 발생합니다. 이런 문제들을 '이상 현상(Anomaly)'이라고 하며, 데이터베이스의 신뢰성을 근본적으로 흔듭니다.

데이터 중복을 제거하는 체계적인 방법이 바로 정규화(Normalization)입니다. 정규화는 테이블을 더 작고 연관성 있는 단위로 분리하여 중복을 최소화하는 과정입니다. 1970년대 에드거 코드(Edgar Codd)가 제안한 이후, 정규화는 관계형 데이터베이스 설계의 핵심 원칙으로 자리 잡았습니다. 수십 년이 지난 지금도 그 중요성은 변함이 없습니다.

이 글에서는 데이터 중복이 왜 문제인지, 정규화를 통해 어떻게 해결하는지, 그리고 실전에서 어떻게 적용하는지 상세히 다루겠습니다. 제1 정규형부터 제3 정규형까지 단계별로 살펴보고, 실제 테이블 설계 예시와 함께 정규화 과정을 직접 따라 해볼 수 있도록 구성했습니다. 이 글을 끝까지 읽으면 중복 없는 견고한 데이터베이스를 설계할 수 있는 역량을 갖추게 될 것입니다.

데이터 중복이 부르는 세 가지 문제

데이터 중복은 세 가지 심각한 문제를 일으킵니다. 이를 이상 현상(Anomaly)이라고 부르며, 삽입 이상, 갱신 이상, 삭제 이상으로 구분됩니다. 이 문제들을 이해하면 왜 중복 제거가 필수인지 명확해집니다.

삽입 이상(Insertion Anomaly)은 새로운 데이터를 추가할 때 불필요한 정보까지 함께 입력해야 하는 문제입니다. 예를 들어, 학생과 수강 과목이 한 테이블에 있다고 가정해봅시다. 새로운 과목을 개설하려면 반드시 그 과목을 수강하는 학생이 있어야만 입력할 수 있습니다. 아직 수강생이 없는 신규 과목은 등록 자체가 불가능해지는 것입니다. 이처럼 논리적으로 독립적인 정보가 다른 정보에 종속되어 있으면 삽입 이상이 발생합니다.

갱신 이상(Update Anomaly)은 데이터를 수정할 때 일부만 변경되는 문제입니다. 만약 한 교수가 100개의 수업을 담당하고, 각 수업 레코드에 교수의 연락처가 중복 저장되어 있다면 어떻게 될까요? 교수의 연락처가 바뀌면 100개의 레코드를 모두 수정해야 합니다. 만약 99개만 수정하고 1개를 놓쳤다면, 같은 교수의 연락처가 서로 다르게 저장되는 데이터 불일치가 발생합니다. 이것이 갱신 이상입니다.

삭제 이상(Deletion Anomaly)은 데이터를 삭제할 때 의도치 않은 정보까지 함께 사라지는 문제입니다. 학생과 수강 과목이 한 테이블에 있을 때, 어떤 과목의 마지막 수강생이 수강을 취소하면 해당 과목의 정보 자체가 사라집니다. 과목명, 담당 교수, 강의실 정보 등 유지해야 할 정보까지 함께 삭제되는 것입니다. 이는 데이터 손실로 이어집니다.

이 세 가지 이상 현상의 근본 원인은 하나입니다. 서로 다른 성격의 정보가 한 테이블에 뒤섞여 있기 때문입니다. 학생 정보, 과목 정보, 수강 정보는 각각 독립적인 개념인데, 이를 하나의 테이블에 담으면 문제가 생깁니다. 정규화는 이렇게 뒤섞인 정보를 분리하여 각각의 독립적인 테이블로 만드는 과정입니다.

중복 데이터는 저장 공간도 낭비합니다. 같은 정보를 여러 번 저장하면 그만큼 디스크 용량이 소모됩니다. 수백만 건의 데이터를 다루는 대규모 서비스에서는 이 낭비가 상당한 비용 증가로 이어집니다. 또한 중복된 데이터를 처리하느라 쿼리 성능도 저하됩니다.

정규화 핵심, 1NF부터 3NF까지

정규화는 단계별로 진행됩니다. 각 단계를 정규형(Normal Form)이라고 하며, 숫자가 높아질수록 더 엄격한 조건을 만족합니다. 실무에서는 보통 제3정규형(3NF)까지 적용합니다. 각 단계를 구체적인 예시와 함께 살펴보겠습니다.

제1 정규형(1NF)은 모든 속성이 원자값(Atomic Value)만 가져야 한다는 조건입니다. 원자값이란 더 이상 분리할 수 없는 단일 값을 의미합니다. 예를 들어, '취미' 열에 "독서, 영화, 운동"처럼 여러 값을 콤마로 구분해서 저장하면 1NF를 위반한 것입니다. 이 경우 취미별로 행을 분리하거나, 취미 테이블을 별도로 만들어야 합니다. 또한 '전화번호 1', '전화번호 2', '전화번호 3'처럼 반복되는 열도 1NF 위반입니다. 이런 구조는 전화번호가 4개인 경우를 처리할 수 없습니다.

제2 정규형(2NF)은 1NF를 만족하면서 부분 함수 종속을 제거해야 합니다. 부분 함수 종속이란 복합 기본 키의 일부에만 종속되는 속성이 있는 경우입니다. 예를 들어, 주문상세 테이블에 (주문번호, 상품번호)가 복합 기본 키이고, 여기에 상품명이 포함되어 있다면 문제입니다. 상품명은 상품번호만으로 결정되지, 주문번호와는 무관합니다. 이런 경우 상품명은 상품 테이블로 분리해야 합니다. 2NF는 복합 키를 사용하는 테이블에서 주로 적용됩니다.

제3 정규형(3NF)은 2NF를 만족하면서 이행 함수 종속을 제거해야 합니다. 이행 함수 종속이란 A→B, B→C일 때 A→C가 성립하는 경우입니다. 쉽게 말해, 기본 키가 아닌 속성이 다른 속성을 결정하는 관계입니다. 예를 들어, 직원 테이블에 직원번호, 부서코드, 부서명이 있다고 합시다. 직원번호→부서코드, 부서코드→부서명이므로 직원번호→부서명이 성립합니다. 하지만 부서명은 부서코드에 종속되므로, 부서명은 부서 테이블로 분리해야 합니다.

정규화 과정을 실제 예시로 따라가 봅시다. 비정규화된 주문 테이블이 있습니다: 주문번호, 고객명, 고객주소, 상품명, 상품가격, 수량. 먼저 1NF를 적용합니다. 만약 상품명에 여러 상품이 콤마로 들어있다면 행을 분리합니다. 다음 2NF를 적용합니다. 상품명과 상품가격은 상품에 종속되므로 상품 테이블로 분리합니다. 마지막 3NF를 적용합니다. 고객주소는 고객에 종속되므로 고객 테이블로 분리합니다. 결과적으로 고객, 상품, 주문, 주문상세 네 개의 테이블이 만들어집니다.

정규화 테이블 설계 실전 사례

이론을 알았으니 실전에 적용해 봅시다. 온라인 쇼핑몰 데이터베이스를 설계한다고 가정합니다. 비정규화된 상태에서 시작하여 단계별로 정규화를 적용하는 과정을 보여드리겠습니다.

처음에 모든 정보를 하나의 테이블에 담았다고 가정합니다. 주문 테이블: 주문번호, 주문일자, 고객번호, 고객명, 고객이메일, 고객등급, 등급할인율, 상품번호, 상품명, 카테고리, 가격, 수량. 이 테이블에는 수많은 중복이 존재합니다. 같은 고객이 여러 번 주문하면 고객 정보가 반복됩니다. 같은 상품이 여러 주문에 포함되면 상품 정보가 반복됩니다.

먼저 고객 정보를 분리합니다. 고객 테이블: 고객번호(PK), 고객명, 고객이메일, 고객등급. 여기서 고객등급에 따른 할인율도 함께 저장하고 싶을 수 있습니다. 하지만 등급할인율은 고객등급에 종속되므로 3NF 위반입니다. 따라서 등급 테이블을 별도로 만듭니다. 등급 테이블: 등급코드(PK), 등급명, 할인율. 고객 테이블은 등급코드를 외래 키로 참조합니다.

상품 정보도 분리합니다. 상품 테이블: 상품번호(PK), 상품명, 카테고리코드(FK), 가격. 카테고리 정보도 별도 테이블로 관리하면 카테고리 이름이 변경될 때 한 곳만 수정하면 됩니다. 카테고리 테이블: 카테고리코드(PK), 카테고리명.

주문 정보는 주문과 주문상세로 분리합니다. 주문 테이블: 주문번호(PK), 주문일자, 고객번호(FK). 주문상세 테이블: 주문번호(FK), 상품번호(FK), 수량, 주문단가. 여기서 주문단가를 별도로 저장하는 이유가 있습니다. 상품 가격은 시간에 따라 변할 수 있지만, 주문 당시의 가격은 보존되어야 하기 때문입니다. 이는 비정규화가 아니라 비즈니스 요구사항을 반영한 설계입니다.

최종적으로 6개의 테이블이 만들어졌습니다: 고객, 등급, 상품, 카테고리, 주문, 주문상세. 각 테이블은 하나의 개념만 담고 있으며, 외래 키를 통해 서로 연결됩니다. 데이터 중복이 최소화되었고, 이상 현상이 발생하지 않습니다.

정규화의 한계와 비정규화 선택

정규화가 항상 정답은 아닙니다. 과도한 정규화는 오히려 성능 문제를 일으킬 수 있습니다. 테이블이 너무 많이 분리되면 데이터를 조회할 때마다 여러 테이블을 JOIN 해야 합니다. JOIN 연산은 비용이 크기 때문에, 자주 조회되는 데이터에 대해 복잡한 JOIN을 반복하면 응답 시간이 길어집니다.

비정규화(Denormalization)는 성능 향상을 위해 의도적으로 중복을 허용하는 기법입니다. 예를 들어, 주문 조회 시 항상 고객명이 필요하다면, 주문 테이블에 고객명을 중복 저장하여 JOIN을 피할 수 있습니다. 물론 고객명이 변경되면 주문 테이블도 함께 수정해야 하는 부담이 생깁니다. 하지만 읽기 연산이 쓰기 연산보다 훨씬 많은 서비스에서는 충분히 가치 있는 트레이드오프입니다.

언제 비정규화를 고려해야 할까요? 첫째, 특정 조회 쿼리가 매우 자주 실행되고 성능이 중요할 때입니다. 둘째, JOIN 대상 테이블의 데이터가 거의 변경되지 않을 때입니다. 셋째, 실시간 응답이 필요한 OLTP 시스템보다 분석용 OLAP 시스템에서 더 적극적으로 고려합니다.

비정규화는 정규화된 설계를 먼저 완성한 후 적용해야 합니다. 처음부터 비정규화하면 나중에 데이터 무결성 문제가 발생했을 때 원인을 찾기 어렵습니다. 또한 비정규화 결정은 실제 성능 측정 데이터를 바탕으로 해야 합니다. 추측이 아닌 증거에 기반한 최적화가 중요합니다.

NoSQL 데이터베이스에서는 비정규화가 더 일반적입니다. MongoDB 같은 문서형 데이터베이스는 관련 데이터를 하나의 문서에 중첩(Embedded) 저장하는 것을 권장합니다. 이는 JOIN이 없는 NoSQL의 특성에 맞춘 설계입니다. 따라서 사용하는 데이터베이스의 특성에 맞는 설계 원칙을 적용해야 합니다.

결론적으로, 중복 제거는 데이터베이스 설계의 기본 원칙이지만 유일한 원칙은 아닙니다. 정규화로 견고한 기반을 만들고, 필요한 곳에 선택적으로 비정규화를 적용하는 균형 잡힌 접근이 최선입니다. 데이터 무결성과 성능 사이에서 서비스의 특성에 맞는 최적점을 찾는 것이 숙련된 DB 설계자의 역량입니다. 이 글에서 배운 정규화 원칙을 바탕으로 여러분의 데이터베이스를 점검해 보세요. 숨어 있는 중복을 찾아 제거하면, 더 안정적이고 유지보수하기 쉬운 시스템을 만들 수 있을 것입니다.

반응형