-
[Database] 반정규화(Denormalization)의 개념과 적용업무 자동화/Database 2025. 11. 10. 17:12

반정규화란?
정규화는 데이터 중복을 최소화하고 이상 현상(삽입·수정·삭제 문제) 을 막기 위해 테이블을 쪼개는 과정이다.
하지만, 실제 서비스 환경에서는 “너무 잘게 쪼개진 구조”가 성능과 개발 효율을 떨어뜨리는 경우가 있다.반정규화(Denormalization) 는
이미 정규화된 스키마에서 의도적으로 중복이나 결합된 구조를 허용하여
조회 성능, 단순한 쿼리, 운영 편의성을 얻는 설계 기법을 말한다.❗ “비정규화(아무 생각 없이 막 설계)”와 다르다.
반정규화는 정규화 원칙을 이해한 상태에서, 트레이드오프를 알고 일부를 깨는 것이다.반정규화의 긍정적 효과
1) 조인(Join) 감소 → 조회 성능 향상
- 여러 테이블을 매번 조인해야 했던 조회를 한 테이블만 읽거나, 조인 수를 줄여 처리할 수 있다.
- 대량 트래픽, 조회 위주 시스템에서 체감 성능 향상 효과가 크다.
2) 쿼리 구조 단순화
- FROM A JOIN B JOIN C ... 형태의 복잡한 SQL 대신 단순 SELECT 한 번으로 적용 가능
- 개발 난이도 감소
- 버그 발생 가능성 감소
- 분석/리포트 쿼리 작성 편의성 증가
3) 캐시/리포트 역할
- 자주 사용하는 집계 정보나 요약 정보를 테이블에 함께 두면 서비스 API, 대시보드, 리포트가 빠르게 응답 가능
- 별도 실시간 집계 연산을 줄여 인프라 비용 절감에 기여
반정규화의 부정적 효과
1) 데이터 중복 증가
- 동일한 데이터가 여러 테이블 또는 여러 행에 흩어져 저장
- 예) 고객 이름, 상품명, 코드명 등
2) 일관성(Consistency) 유지 비용 증가
- 한 곳이 바뀌면, 같이 바뀌어야 하는 곳이 여러 군데 생김
- 제때 동기화되지 않으면:
- 화면마다 다른 값이 보이는 “데이터 불일치” 문제 발생
3) 수정/삭제 로직 복잡화
- UPDATE/DELETE 시
→ 관련 테이블들을 함께 갱신해야 함 - 트리거, 배치, 애플리케이션 로직 등으로 보완 필요
- 관리 복잡도 상승
- 잘못 설계하면 정규화보다 더 큰 유지보수 지옥 가능
반정규화가 필요한 대표 상황
아래와 같은 경우에는 반정규화를 “진지하게 검토”할 만하다.
- 조인 테이블 수가 과도하고, 쿼리 수행 시간이 문제될 때
- 실시간 API에서 4~5개 이상 테이블 조인이 상시 발생
- 인덱스를 최적화해도 지연이 심한 경우
- 읽기(Read) 위주 시스템
- 조회 트래픽이 90% 이상
- 데이터 변경은 상대적으로 적고, 조회 응답 속도가 핵심 가치일 때
- 예: 뉴스 포털, 상품 카탈로그, 대시보드, 모니터링 시스템
- 대용량 데이터 & 실시간 분석 요구
- 매 요청마다 대량 스캔 + 집계가 부담스러운 상황
- 사전 집계 테이블, 요약 컬럼, 캐시 역할의 반정규화 구조 적합
- 분산 DB / 샤딩 환경
- 서로 다른 노드에 있는 테이블 간 조인이 비효율적일 때
- 필요한 정보 일부를 같은 샤드(또는 같은 테이블)에 중복 저장해서,
네트워크 코스트와 복잡한 분산 조인을 줄이는 전략
- 지나친 정규화로 업무/개발 생산성이 떨어질 때
- “단순 화면 하나 띄우는데 테이블 7개를 봐야 하는” 상황
- 실무 개발자와 운영자가 구조를 이해하기 어려운 경우
- 이 때, 업무 단위로 적절히 묶은 반정규화 테이블을 제공하는 것이 전체 생산성을 높일 수 있음
예시 1~4
예시 1. 주문 조회 화면 성능 개선
(정규화 구조)
- 고객(Customer): CustomerID, Name, Grade, ...
- 상품(Product): ProductID, ProductName, Price, ...
- 주문상세(OrderDetail): OrderID, ProductID, Qty, ...
- 주문(Order): OrderID, CustomerID, OrderDate, ...
주문 이력 화면에서 아래 정보가 필요하다고 할 경우:
- 주문일자, 고객명, 고객등급, 상품명, 수량, 금액
매번 다음과 같은 조인이 필요하다:
SELECT ... FROM Order O JOIN Customer C ON O.CustomerID = C.CustomerID JOIN OrderDetail OD ON O.OrderID = OD.OrderID JOIN Product P ON OD.ProductID = P.ProductID(반정규화 적용)
- Order 또는 OrderDetail에 다음 컬럼 추가:
- CustomerName
- CustomerGrade
- ProductName
- (필요시 당시 판매가 등을 함께 저장)
장점
- 조회 시 조인 축소 → 주문 리스트 다건 조회 성능 개선
- 읽기 위주(이력 조회) 화면에서 매우 유용
단점 / 주의
- 고객 이름/등급이 바뀌면 과거 주문에 반영할지 말지 정책 필요
- “주문 시점 기준 값 고정”이라면 오히려 반정규화가 더 적합한 설계가 될 수 있음
예시 2. 게시글의 댓글 수 / 좋아요 수
(정규화 구조)
- Post(PostID, Title, Content, ...)
- Comment(CommentID, PostID, ...)
- Like(UserID, PostID, ...)
매번 화면에 표시하기 위해:
SELECT P.Title, (SELECT COUNT(*) FROM Comment WHERE PostID = P.PostID) AS CommentCount, (SELECT COUNT(*) FROM Like WHERE PostID = P.PostID) AS LikeCount FROM Post P→ 트래픽이 많으면 부담.
(반정규화 적용)
- Post 테이블에 CommentCount, LikeCount 컬럼 추가
- 댓글 작성/삭제, 좋아요 변경 시 해당 컬럼 +1/-1 업데이트
장점
- 조회 시 COUNT 연산 불필요 → 매우 빠른 응답
- 실무에서 가장 흔한 반정규화 패턴
단점
- 카운트 값 불일치 가능성
- 해결: 트랜잭션 처리, 주기적 재계산 배치, 장애 시 동기화 로직 등
예시 3. 통계/리포트용 집계 테이블
(정규화 구조)
- 모든 거래를 Transaction 테이블에만 저장
- 매일 리포트 요청 시, 수천만 건을 GROUP BY 해서 계산
(반정규화 적용)
- DailySalesSummary 테이블 생성:
- SaleDate, StoreID, TotalAmount, OrderCount, ...
- 배치 작업으로 매일/매시간 집계 값 미리 저장
장점
- 리포트 조회 속도 극적으로 개선
- 운영 DB 부하 감소
단점
- 원본과 요약 테이블 간 싱크 관리 필요
- 잘못된 집계가 들어가면 잘못된 리포트가 그대로 노출
예시 4. 코드명/설명 중복 저장
(정규화 구조)
- CodeMaster(Code, Name)
- User(UserID, StatusCode(FK))
화면마다 조인으로 코드명을 가져와야 한다.
(반정규화 적용 예)
- User 테이블에 StatusName을 함께 저장
- 또는, 조회 전용 View/Materialized View로 합쳐 제공
사용 배경
- 외부 시스템 연동, 로그 적재, DW로의 덤프 등에서
- “그 시점의 코드명 그대로” 보존하고 싶은 경우
'업무 자동화 > Database' 카테고리의 다른 글
[Database] SQL의 정규표현식: 정의, 핵심 패턴 표, DBMS별 사용법, 이메일·전화번호·주소 예시 (0) 2025.11.12 [Database] SQL Window 함수 개념과 사용 예시 (0) 2025.11.11 [Database] 정규화(1NF~BCNF) 개념과 예시 (0) 2025.11.08 [Database] 데이터 모델링의 특징 및 유의사항 (0) 2025.11.07 [Database] 데이터베이스 정의와 유형 (0) 2025.11.05