ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [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 시
      → 관련 테이블들을 함께 갱신해야 함
    • 트리거, 배치, 애플리케이션 로직 등으로 보완 필요
      • 관리 복잡도 상승
      • 잘못 설계하면 정규화보다 더 큰 유지보수 지옥 가능

     

    반정규화가 필요한 대표 상황

    아래와 같은 경우에는 반정규화를 “진지하게 검토”할 만하다.

    1. 조인 테이블 수가 과도하고, 쿼리 수행 시간이 문제될 때
      • 실시간 API에서 4~5개 이상 테이블 조인이 상시 발생
      • 인덱스를 최적화해도 지연이 심한 경우
    2. 읽기(Read) 위주 시스템
      • 조회 트래픽이 90% 이상
      • 데이터 변경은 상대적으로 적고, 조회 응답 속도가 핵심 가치일 때
      • 예: 뉴스 포털, 상품 카탈로그, 대시보드, 모니터링 시스템
    3. 대용량 데이터 & 실시간 분석 요구
      • 매 요청마다 대량 스캔 + 집계가 부담스러운 상황
      • 사전 집계 테이블, 요약 컬럼, 캐시 역할의 반정규화 구조 적합
    4. 분산 DB / 샤딩 환경
      • 서로 다른 노드에 있는 테이블 간 조인이 비효율적일 때
      • 필요한 정보 일부를 같은 샤드(또는 같은 테이블)에 중복 저장해서,
        네트워크 코스트와 복잡한 분산 조인을 줄이는 전략
    5. 지나친 정규화로 업무/개발 생산성이 떨어질 때
      • “단순 화면 하나 띄우는데 테이블 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로의 덤프 등에서
    • “그 시점의 코드명 그대로” 보존하고 싶은 경우

     

Designed by Tistory.