OLTP vs 리포팅 스키마: 비정규화할까, 요약 테이블을 추가할까?
OLTP와 리포팅 스키마 선택은 대시보드 속도와 데이터 정확성에 영향을 줍니다. 언제 비정규화하고 요약 테이블을 추가하거나 리포팅 뷰를 분리해야 하는지 알아보세요.

왜 OLTP와 리포팅은 스키마를 서로 다른 방향으로 끌어당기는가
OLTP(온라인 트랜잭션 처리)는 애플리케이션이 매일 수행하는 작업입니다: 빠르고 안전해야 하는 작은 작업들이 많습니다. 주문 생성, 상태 업데이트, 결제 추가, 로그 기록 등. 데이터베이스는 빠른 삽입과 업데이트, 외래키 같은 엄격한 규칙, 소수의 행만 건드리는 간단한 쿼리에 최적화됩니다.
리포팅은 다른 일입니다. 대시보드나 BI 스타일 화면은 종종 많은 행을 스캔하고, 그룹화하고, 기간을 비교해야 합니다. “이 고객 하나를 보여줘” 대신에 “주별·지역별·상품 카테고리별 수익을 필터와 함께 보여줘”라는 요청을 합니다. 이는 넓은 읽기, 집계, 여러 테이블 간의 조인, 반복되는 계산을 의미합니다.
이것이 OLTP 대 리포팅 스키마 결정의 핵심 긴장입니다: 쓰기를 깔끔하고 일관되게 만드는 구조(정규화된 테이블, 많은 관계)는 대규모 분석에서는 느리거나 비용이 많이 드는 구조가 되는 경우가 많습니다.
초기에는 단일 스키마가 둘 다 충족할 수 있습니다. 하지만 데이터가 커지면 보통 다음과 같은 트레이드오프를 느끼게 됩니다:
- 트랜잭션 화면은 빠르지만 대시보드는 매달 느려진다.
- “간단한 차트”가 여러 조인을 포함한 복잡한 쿼리가 된다.
- 같은 지표가 여러 곳에서 계산되어 값이 달라진다.
- 새로운 필터를 추가하면 위험한 쿼리 변경이 필요해진다.
그래서 팀들은 보통 하나(또는 그 이상)의 전술을 선택합니다: 자주 쓰이는 필드를 비정규화하거나, 반복되는 합계를 위한 요약 테이블을 추가하거나, 리포팅 뷰(때로는 별도의 리포팅 스키마)를 만들어 OLTP 성능을 보호하면서 수치를 일관되게 유지합니다.
트랜잭션 화면과 BI 화면에서 무엇이 달라지는가
트랜잭션 화면과 BI 화면은 같은 비즈니스 사실을 보여줄 수 있지만 데이터베이스에 요구하는 방식은 정반대일 때가 많습니다. 그 긴장이 OLTP 대 리포팅 스키마 결정의 핵심입니다.
트랜잭션 화면에서는 대부분의 요청이 소수의 행만 건드립니다. 사용자가 주문을 생성하거나 고객을 수정하거나 결제를 환불하거나 상태를 변경합니다. 데이터베이스는 작은 삽입과 업데이트로 바쁘고, 각각을 빠르고 안전하게 확정해야 합니다.
BI 화면은 다릅니다. 읽기가 쓰기보다 훨씬 많습니다. 단일 대시보드 뷰가 몇 주치 데이터를 스캔하고 그룹화하며 정렬하고 여러 방식으로 필터링할 수 있습니다. 이러한 쿼리는 종종 열이 많고 여러 비즈니스 영역의 데이터를 동시에 가져옵니다.
쿼리가 어떻게 달라지는가
OLTP에서는 정규화된 테이블과 깔끔한 관계가 친구입니다. 데이터를 일관되게 유지하고 중복을 피하며 한 곳에서 사실을 업데이트합니다.
BI에서는 조인이 병목이 될 수 있습니다. 대시보드는 사람들이 필터로 많이 쓰는 필드(날짜, 지역, 상품 카테고리, 담당자)를 이미 포함한 넓은 테이블이 더 잘 작동합니다. 이렇게 하면 읽기 시점의 조인 작업을 줄여 쿼리를 단순하게 할 수 있습니다.
차이를 빠르게 파악하는 방법:
- 트랜잭션 화면: 많은 작은 쓰기, 빠른 포인트 읽기
- BI 화면: 요청은 적지만 그룹화와 필터링이 많은 무거운 읽기
- OLTP 데이터: 일관성을 보호하기 위해 정규화됨
- BI 데이터: 조인과 스캔을 줄이기 위해 종종 재구성됨
동시성 및 신선도
OLTP는 업데이트를 위한 높은 동시성을 필요로 합니다. 장시간 실행되는 리포팅 쿼리가 큰 범위를 스캔하면 업데이트를 차단하거나 느리게 할 수 있습니다.
신선도 기대치도 달라집니다. 일부 대시보드는 거의 실시간에 가까워야 합니다(예: 지원 대기열). 다른 대시보드는 시간 단위나 일 단위(재무, 실적)로 충분합니다. 스케줄로 새로고침할 수 있다면 요약 테이블, 물리화된 뷰, 별도의 리포팅 스키마를 사용할 자유를 얻습니다.
AppMaster로 화면을 구축한다면 초기에 계획하는 것이 도움이 됩니다: 트랜잭션 모델은 깔끔하게 유지하고, 리포팅 데이터는 대시보드 필터와 집계에 맞게 별도로 형태를 잡으세요.
리포팅을 위해 조정해야 한다는 신호
앱의 일상 트랜잭션은 빠른데 대시보드가 느리다면, 전형적인 OLTP 대 리포팅 스키마 분리가 나타난 것입니다. 트랜잭션 화면은 소수의 행을 빠르게 건드리는 반면 BI 스타일 화면은 많은 행을 스캔하고 그룹화하며 동일한 계산을 여러 번 반복합니다.
간단한 신호는 시간입니다: 개발에서는 괜찮던 대시보드 쿼리가 운영에서는 느려지거나 피크 시 타임아웃이 발생합니다. 리포팅 워크로드는 앱 트래픽이 비슷할 때도 데이터베이스 CPU가 ‘스파이크’로 나타납니다. 이는 보통 데이터베이스가 더 많은 사용자를 서빙해서가 아니라 큰 테이블을 조인하고 집계하기 위해 열심히 일하고 있다는 신호입니다.
가장 흔한 신호는 다음과 같습니다:
- 대시보드가 하나의 질문을 답하기 위해 여러 테이블을 많이 조인한다.
- 같은 계산(수익, 활성 사용자, 평균 처리 시간)이 여러 차트와 페이지에서 반복된다.
- 사용자들이 일/주/월 단위로 같은 합계를 계속 요구하고, 각 요청이 또 다른 무거운 쿼리를 트리거한다.
- BI 쿼리가 일반 사용자가 레코드를 생성하거나 편집할 때 느려지거나 타임아웃된다.
- OLTP 트래픽과 쓰기량은 안정적인데 데이터베이스 CPU가 지속적으로 상승한다.
실용적인 예: 영업팀이 담당자별·월별로 주문을 그룹화하고 지역·상품·채널로 필터하는 ‘실적’ 화면을 연다고 합시다. 필터를 바꿀 때마다 동일한 합계가 다시 계산되는 다중 조인 쿼리가 재실행된다면, 매번 전체 비용을 지불하고 있는 셈입니다.
AppMaster 같은 플랫폼으로 내부 도구를 만들면, 리포팅 페이지가 반응성을 유지하기 위해 복잡한 백엔드 로직이 필요해지는 시점에서 이러한 문제가 드러납니다. 이 지점에서 비정규화, 요약 테이블 또는 별도의 리포팅 뷰가 '있으면 좋은 것'에서 대시보드를 빠르고 수치 일관성을 유지하는 데 필수로 바뀝니다.
비정규화가 적절한 경우
비정규화는 리포팅 요구가 예측 가능할 때 합리적입니다. 동일한 소수의 대시보드 질문이 매주 반복되고 자주 바뀌지 않는다면, 각 차트가 여러 테이블을 조합해 답을 만들게 하지 말고 데이터 구조를 질문에 맞게 만드는 것이 더 낫습니다.
OLTP는 깔끔하고 업데이트 친화적인 테이블을 필요로 하고, BI 스타일은 적은 조인으로 빠른 읽기를 필요로 한다는 것이 전형적인 전환점입니다. 분석에서는 몇몇 필드를 복사하는 것이 매번 다섯 개 테이블을 조인하는 것보다 비용이 적을 수 있습니다.
비정규화는 속도와 쿼리 단순화를 확실히 가져오고 쓰기 경로를 안전하게 유지할 수 있을 때 적용하세요. 핵심은 복제된 필드를 파생 데이터로 취급하는 것입니다. 사용자가 편집할 수 있는 또 다른 장소로 만들면 안 됩니다. 진실의 단일 소스를 유지하고, 모든 복사본은 코드나 통제된 프로세스로 업데이트하세요.
좋은 후보 필드는 다음과 같습니다:
- 대시보드에서 자주 읽히지만 거의 수정되지 않는 필드(고객 이름, 상품 카테고리)
- 반복적으로 조인하기에 비용이 큰 경우(다대다 관계, 깊은 체인)
- 빠르게 필터링하고 그룹화하는 데 필요한 경우(지역, 팀, 요금제)
- 신뢰할 수 있는 테이블에서 복사할 수 있어 검증이 쉬운 경우(임의 텍스트 아님)
소유권이 중요합니다. 누군가(또는 어떤 작업)가 복제본의 일관성을 유지할 책임이 있어야 하고, 소스가 바뀔 때 어떤 규칙을 적용할지 명확히 해야 합니다.
예: 영업 대시보드가 담당자와 지역별로 주문을 그룹화한다고 합시다. 매번 Orders -> Customers -> Regions를 조인하는 대신 주문 생성 시 region_id를 주문에 저장할 수 있습니다. 고객이 나중에 지역을 옮기면 규칙을 “과거 주문은 원래 지역을 유지한다” 또는 “밤사이에 이전 주문을 백필(backfill) 한다” 중 하나로 정하세요. 하나를 선택하고 문서화하며 강제합니다.
AppMaster와 PostgreSQL을 사용하면 이러한 종류의 비정규화 필드를 Data Designer에서 모델링하기가 쉽습니다. 단, 누가 쓸 수 있고 일관되게 업데이트할지 잠궈야 합니다.
피해야 할 비정규화 함정
비정규화는 BI 화면을 빠르게 만들 수 있지만, 쉽게 “진실이 두 개”가 생기는 길이기도 합니다. 가장 흔한 실패는 같은 사실을 여러 곳에 반복하고, 값이 다를 때 어느 필드가 우선인지 명확히 하지 않는 것입니다. order_total과 라인 아이템을 둘 다 저장하면 order_total이 계산된 것인지, 사용자가 입력한 것인지, 결제 제공자에서 복사한 것인지 규칙이 필요합니다.
또 다른 함정은 자주 변경되는 필드를 비정규화하는 것입니다. 고객 상태, 계정 소유자, 상품 카테고리, 지역 할당은 시간이 지나면서 변하는 경향이 있습니다. 이런 값을 편의상 여러 테이블에 복사하면, 변경할 때마다 정리 작업이 되고 누락된 업데이트는 대시보드의 잘못된 슬라이스로 드러납니다.
OLTP 경로에서 매우 넓은 테이블을 조심하세요. 트랜잭션 화면을 구동하는 동일한 테이블에 많은 비정규화 컬럼을 추가하면 쓰기가 느려지고 락 시간이 늘어나며 단순한 업데이트가 불필요하게 무거워집니다. 이벤트, 주문 라인, 지원 메시지 같은 고빈도 테이블에서 특히 고통스럽습니다.
문서화는 대부분의 팀이 예상하는 것보다 더 중요합니다. 유지 계획 없는 비정규화 컬럼은 시한폭탄입니다: 사람들이 리포트에서 읽고 신뢰하지만 워크플로 변경 후 업데이트가 중단된 사실을 모를 수 있습니다.
실용적인 예: rep_name을 모든 order에 추가했다고 합시다. 담당자 이름이 변경되거나 재배정되면 지난 분기의 숫자가 두 이름으로 분리될 수 있습니다. 표시용 이름이 정말 필요하다면 안정적인 rep_id를 저장하고 리포팅 뷰에서 이름을 해결(resolve)하거나 rep_name_at_sale처럼 의도적으로 스냅샷했음을 나타내는 라벨을 고려하세요.
비정규화하기 전에 다음을 확인하세요:
- 반복되는 값마다 진실의 소스를 정의하고 문서화하세요.
- 변경 가능한 텍스트 필드보다 안정적인 ID를 선호하세요.
- 현재 상태 리포팅이 필요한지 시점 스냅샷이 필요한지 결정하세요.
- 트리거, 작업, 워크플로 단계 등 명확한 유지 메커니즘과 소유자를 추가하세요.
- 불일치를 모니터링할 수 있는 간단한 대조 쿼리를 추가해 오류가 빨리 드러나게 하세요.
AppMaster과 PostgreSQL을 사용하면 유지 관리를 Business Process 단계에 연결해 누군가 "기억할 때" 업데이트하는 일이 아니라 일관되게 발생하도록 하는 것이 도움이 됩니다.
요약/집계 테이블을 추가해야 할 때
요약 테이블은 BI 스타일 화면이 동일한 합계를 반복해서 필요로 할 때 유용합니다: 일별 가입, 요금제별 수익, 활성 사용자, 환불, 처리된 티켓 수 등과 같은 KPI들.
반복성이 좋은 신호입니다. 여러 대시보드 카드가 거의 동일한 GROUP BY를 실행한다면 데이터베이스는 같은 작업을 계속 반복합니다. 이는 보통 1,000행에서는 괜찮지만 1,000만 행에서는 고통스럽습니다. OLTP 대 리포팅 스키마 논의에서 이 시점은 인덱스를 조정하는 것을 멈추고 사전 계산(precompute)을 시작하는 순간입니다.
또한 예측 가능한 속도가 필요할 때 요약 테이블을 추가합니다. 차트는 몇 초 내에 로드되어야 합니다. 요약 테이블은 비싼 스캔을 작은 조회로 바꿉니다.
요약 테이블이 도움이 되는 전형적 트리거는 다음과 같습니다:
- 대시보드가 여러 화면이나 필터에서 동일한 GROUP BY를 반복한다.
- 일/주/월 단위의 시간 버킷과 상위 N 리스트를 자주 조회한다.
- 기본 테이블이 추가 중심(append-heavy)이다(이벤트, 트랜잭션, 로그).
- 이해관계자가 알려진 기준 시점의 안정적 KPI 수치를 기대한다(예: "자정 기준").
새로 고침 전략도 결정의 절반입니다. 신선도 요구에 따라 몇 가지 현실적 옵션이 있습니다:
- 예측 가능한 워크로드를 위한 정기 새로고침(5분마다, 시간 단위, 야간)
- 실시간에 가깝게 필요하면 주요 동작 후 이벤트 기반 갱신(새 주문, 구독 변경)
- 하이브리드: 정기 배치로 백필하고 소규모 증분 업데이트 수행
테이블은 집중되고 단순해야 합니다: 그레인이 분명해야 합니다(예: 날짜·요금제별 한 행), 컬럼은 차트가 직접 읽는 메트릭(합계, 카운트 등)이어야 합니다. AppMaster로 구축하면 집계를 PostgreSQL에 저장하고 Business Process로 스케줄이나 이벤트 후 갱신하는 패턴이 깔끔하게 맞습니다.
요약 테이블 설계 단계별 가이드
요약 테이블은 OLTP 대 리포팅 스키마에서 의도적인 타협입니다: 트랜잭션용 상세 원본을 유지하고, 공통 대시보드 질문에 빠르게 답하는 작은 테이블을 추가합니다.
1) 먼저 그레인(행의 의미)을 정하세요
한 행이 무엇을 의미하는지부터 결정하세요. 틀리면 이후 모든 메트릭 설명이 어려워집니다. 일반적인 그레인은 고객별 일간, 주문별, 담당자별 일간 등입니다.
그레인을 테스트하는 간단한 방법: 한 행을 고유하게 식별할 수 있는가? 확실하지 않다면 그레인이 모호한 상태입니다.
2) 원시 데이터가 아니라 질문에 맞춰 테이블을 설계하세요
대시보드가 실제로 보여주는 수치 5~10개를 적고, 필요한 것만 저장하세요: 합계와 카운트가 보통 우선이며, 범위가 필요하면 min/max를 추가합니다. '유일 고객 수'를 보여야 한다면 정확한 distinct가 필요한지(비용 높음) 근사값으로 충분한지 결정하고 문서화하세요.
실용적 단계:
- 5-10개의 대시보드 질문을 적는다(예: "담당자별 일별 매출")
- 대부분을 한 행으로 답할 그레인을 정한다
- 컬럼은 집계(합계, 카운트, min, max, 필요 시 distinct)로만 정의한다
- 필터(날짜, 담당자_id, 고객_id)에 맞춘 키와 인덱스를 추가한다
- 환불, 수정, 취소 같은 지연 도착 데이터는 어떻게 처리할지 정의한다
3) 신뢰할 수 있는 새로고침 방법을 선택하세요
배치 새로고침(야간, 시간 단위)이 가장 이해하기 쉽습니다. 증분 새로고침은 빠르지만 '무엇이 변경되었나' 로직이 필요합니다. 트리거 스타일 업데이트는 거의 실시간이 될 수 있지만 통제하지 않으면 쓰기 성능에 위험을 더할 수 있습니다.
AppMaster로 구축하면 보통 일정 작업이 Business Process를 실행해 어제와 오늘을 재계산하고 오래된 날은 고정하는 패턴을 사용합니다.
4) 대조 검사(reconciliation) 추가
요약 테이블에 의존하기 전에 원시 테이블과 비교하는 몇 가지 기본 검사를 추가하세요:
- 특정 기간 총합이 허용 오차 내에서 일치하는지
- 같은 필터에 대해 카운트(주문, 사용자, 티켓)가 일치하는지
- 몇몇 엔터티(한 담당자, 한 고객)를 끝에서 끝까지 샘플 체크
- 누락 날짜(갭)나 중복(같은 키가 두 번) 감지
검사가 실패하면 더 많은 메트릭을 추가하기 전에 로직을 고치세요. 빠른 대시보드지만 틀리면 느린 것보다 나쁩니다.
별도의 리포팅 뷰와 스키마: 무엇을 해결하나
OLTP 테이블을 깔끔하게 유지하는 것은 주로 정확성에 관한 것입니다. 명확한 규칙, 강한 제약, 잘못된 데이터를 만들기 어렵게 하는 구조를 원합니다. 리포팅 화면은 다른 것을 원합니다: 조인이 적고 친숙한 이름, 바로 읽을 수 있는 메트릭. 이 불일치 때문에 팀들은 핵심 테이블을 바꾸지 않고 리포팅 계층을 추가하는 경우가 많습니다.
리포팅 뷰(또는 별도 리포팅 스키마)는 번역 계층처럼 작동합니다. 앱은 정규화된 테이블에 계속 쓰고, BI 스타일 화면은 "월별", "지역별", "상위 10개 상품" 같은 질문에 맞춰 설계된 객체를 읽습니다. 트랜잭션 로직을 깨지 않고 OLTP 대 리포팅 스키마의 긴장을 해소하는 가장 단순한 방법입니다.
뷰 대 물리적 복사본
논리적 뷰는 데이터량이 적당하고 쿼리가 예측 가능할 때 좋습니다. 단일 진실 소스를 유지하고 대시보드 쿼리에서 중복 로직을 줄입니다.
물리화된 복사(물리화된 뷰, 요약 테이블, 복제된 테이블)는 리포팅 부하가 많고 계산이 비싸거나 피크 시간대에 안정적인 성능이 필요할 때 타당합니다.
간단한 선택 기준:
- 가독성과 일관된 정의가 주 목적이면 논리적 뷰 사용
- 대시보드가 느리거나 핵심 쓰기와 경쟁하면 물리적 복사 사용
- 명확한 경계와 소유권을 원하면 별도 리포팅 스키마 사용
- 리포팅이 쓰기 지연에 영향을 주면 리플리카나 별도 DB 사용
리포팅이 쓰기와 경쟁할 때
대시보드가 넓은 스캔이나 큰 조인을 실행하면 같은 DB에서 트랜잭션을 차단하거나 느리게 할 수 있습니다. 리플리카나 별도 리포팅 DB는 쓰기 경로를 보호합니다. 리포트 쪽에서 뷰를 만들어 정의를 일관되게 유지할 수 있습니다.
예: 지원팀 대시보드가 "SLA 상태별 열린 티켓"을 몇 초마다 보여줘야 하고 OLTP 시스템은 티켓을 계속 업데이트한다면, 리플리카에 리포팅 뷰(또는 미리 계산된 상태 카운트)를 두면 티켓 업데이트를 느리게 하지 않고 대시보드를 빠르게 유지할 수 있습니다. AppMaster 프로젝트에서는 이 패턴이 트랜잭션 데이터 모델을 깔끔하게 유지하면서 리포팅 친화적인 객체를 화면에 제공하는 데 도움이 됩니다.
현실적인 예: 영업 실적 대시보드 만들기
사업부에서 일별 수익, 일별 환불, 지난 30일의 "상위 상품" 리스트를 보여주는 Sales 대시보드를 요청한다고 합시다. 트랜잭션 화면에서 OLTP DB는 정규화되어 있고 주문, 결제, 환불, 라인 아이템이 각각 따로 존재합니다. 이는 정확성과 업데이트에는 좋지만 대시보드는 이제 많은 행을 스캔하고 조인한 뒤 일별로 그룹화해야 합니다.
첫날에는 신중한 쿼리, 적절한 인덱스, 몇 가지 작은 조정으로 허용 가능한 속도를 얻을 수 있습니다. 하지만 볼륨이 커지면 OLTP 대 리포팅 스키마 트레이드오프를 하기 시작합니다.
옵션 A: 빠른 필터링을 위한 비정규화
대시보드가 주로 필터링과 슬라이싱(지역, 영업사원, 채널별)이라면 가벼운 비정규화가 도움이 됩니다. 예를 들어 주문(또는 라인 아이템) 행에 몇몇 안정적인 필드를 복사해 쿼리가 추가 조인 없이 필터링하도록 합니다.
좋은 후보는 구매 시점에 거의 변하지 않는 필드(상품 카테고리, 판매 지역)입니다. 정규화된 테이블에 진실을 두되, BI 스타일 화면을 빠르게 하는 "쿼리 친화적" 복사본을 저장합니다.
옵션 B: 차트와 랭킹을 위한 일별 요약 테이블
대시보드가 차트와 상위 목록에 무겁다면 요약 테이블이 보통 승리합니다. daily_sales 같은 일일 팩트 테이블을 만들고 date, gross_revenue, refunds, net_revenue, orders_count 같은 컬럼을 둡니다. "상위 상품"은 daily_product_sales처럼 날짜와 product_id로 키된 테이블을 추가합니다.
신선도와 비용이 선택에 미치는 영향:
- 거의 실시간(분 단위)이 필요하면: 비정규화하고 라이브 쿼리 또는 요약을 매우 자주 갱신
- 시간 단위 또는 야간 업데이트로 충분하면: 요약이 쿼리 시간을 크게 줄임
- 트래픽이 높은 대시보드: 요약은 OLTP 테이블의 부하를 줄임
- 환불, 부분 결제 같은 복잡한 비즈니스 규칙: 요약은 결과를 일관되게 하고 테스트하기 쉽게 함
AppMaster 같은 도구에서는 깔끔한 트랜잭션 데이터 모델과 별도의 스케줄 프로세스로 요약 테이블을 채우는 패턴이 잘 맞습니다.
느린 대시보드와 잘못된 수치를 초래하는 흔한 실수
가장 흔한 실패 패턴은 OLTP 쓰기와 BI 읽기를 같은 테이블에서 혼합하고 몇 개의 인덱스로 모든 문제가 해결될 거라 가정하는 것입니다. 대시보드는 많은 행을 스캔하고 그룹화하고 정렬합니다. 이는 주문을 저장하거나 티켓을 업데이트하는 것과 다른 작업입니다. 하나의 스키마로 둘 다 제공하려 하면 트랜잭션이 느려지거나 대시보드가 타임아웃됩니다.
또 다른 문제는 비싼 작업을 숨기는 "멋진" 리포팅 뷰입니다. 뷰는 쿼리를 단순해 보이게 만들지만 데이터베이스는 여전히 매번 조인과 필터, 계산을 수행해야 합니다. 몇 주 후 누군가가 "한 필드만 추가"하면서 또 다른 조인을 넣으면 대시보드는 하룻밤 사이에 느려질 수 있습니다. 뷰는 수행 작업량을 바꾸지 않고 숨기기만 했습니다.
요약 테이블은 속도를 해결하지만 드리프트 위험을 만듭니다. 집계를 스케줄로 재생성하면 뒤처질 수 있고, 증분 업데이트는 누락된 작업이나 버그로 인해 합계가 며칠 동안 틀릴 수 있습니다. 그래서 팀들은 대시보드와 트랜잭션 화면 간의 "수치 불일치"에 놀라게 됩니다.
지표 정의 변경은 가장 큰 혼란을 일으킵니다. "수익"이 처음에는 결제된 송장으로 시작했다가 나중에 환불을 뺀 값이 되고, 또 나중에는 "인식된 수익"이 될 수 있습니다. 로직을 버전 관리 없이 덮어쓰면 지난달 차트가 바뀌고 아무도 대시보드를 신뢰하지 않게 됩니다.
대부분의 문제를 방지하는 실용적 가이드라인:
- 가능하면 무거운 대시보드 쿼리를 쓰기 중심 트랜잭션 경로와 분리하세요(별도의 리포팅 테이블이라도 좋습니다).
- 뷰를 코드로 취급하세요: 변경을 리뷰하고 성능을 테스트하며 어떤 조인을 하는지 문서화하세요.
- 요약 테이블의 신선도 검사(마지막 업데이트 시간, 행 수, 정합 총계)를 추가하고 깨지면 알람을 보내세요.
- 주요 지표는 버전 관리하고, 과거 보고를 위해 이전 정의를 유지하세요.
AppMaster와 PostgreSQL로 BI 스타일 화면을 만들고 있다면 이 규칙들은 더 중요합니다. 빠르게 반복하기 쉽지만 속도가 아무리 빨라도 수치가 정확해야 합니다.
스키마를 변경하기 전에 확인할 빠른 체크리스트
테이블을 건드리기 전에 대시보드가 실제로 무엇을 하는지 적으세요. 상위 대시보드 쿼리(약 10개)를 선택하고 각 쿼리가 얼마나 자주 실행되는지 적습니다: 페이지 로드마다, 매분, 또는 누군가 필터를 클릭할 때만. 하루에 500번 실행되는 쿼리는 일주일에 두 번 실행되는 쿼리와 다른 접근이 필요합니다.
다음으로 수학적 정합성을 점검하세요. 어떤 메트릭이 가법(additive)인지, 어떤 것이 특별한 로직이 필요한지 표시하세요. 수익, 수량, 통화 수는 보통 가법입니다. 전환율, 평균 주문 금액, 유일 고객 수는 그렇지 않습니다. 이 한 단계가 가장 흔한 보고 실수(빠른 대시보드지만 틀린 수치)를 예방합니다.
이제 쿼리 유형별로 설계를 선택하세요. OLTP 대 리포팅 스키마 결정에 하나의 전역 답이 필요한 건 아닙니다. 접근 패턴에 맞게 선택하세요:
- 화면이 몇몇 필드를 빠르게 필요로 하고 규칙이 단순하면 비정규화
- 동일한 그룹화를 반복하면 요약 테이블
- 로직이 복잡하거나 트랜잭션 쓰기와 명확한 경계를 원하면 별도 리포팅 뷰나 스키마
각 메트릭에 대해 '충분히 신선한' 기준을 정하고, 하나의 간단한 검증 규칙을 세우세요. 예: "대시보드의 일별 주문 수는 해당 날짜의 주문 테이블 카운트와 0.5% 이내 일치해야 한다" 또는 "총 수익은 게시 상태의 송장으로 대조해야 한다".
마지막으로 소유권을 합의하세요. 스키마 변경을 승인하고 지표 정의를 소유하는 사람(또는 소규모 그룹)을 지정하세요. AppMaster에서 구축한다면 이런 정의를 데이터 모델과 Business Process 옆에 캡처해 동일한 로직이 화면과 리포트 전반에 일관되게 사용되도록 하세요.
다음 단계: 경로를 선택하고 안전하게 구현하세요
OLTP 대 리포팅 스키마 결정은 리디자인 프로젝트가 아니라 성능 버그처럼 다루세요. 측정으로 시작하세요. 가장 느린 2-3개의 대시보드 쿼리를 찾아 얼마나 자주 실행되는지 기록하고 쿼리의 형태(큰 조인, 시간 필터, 상위 N, 반복되는 합계)를 캡처하세요.
사용자에게 보이는 문제를 해결하는 가장 작은 변경을 선택하세요. 대시보드가 한 조인 때문에 느리면 표적 비정규화나 계산 컬럼 하나면 충분할 수 있습니다. 동일한 합계가 반복 계산돼 비용이 크다면 작은 요약 테이블이면 됩니다. BI 화면이 계속 늘어나 트랜잭션과 경쟁하면 별도 리포팅 뷰나 스키마로 위험을 줄이세요.
숫자의 신뢰를 유지하는 안전한 구현 흐름:
- 대시보드 목표(기간, 그룹화, 새로고침 필요성)와 수용 기준(예: 로드 2초 이하)을 정의
- 한 번에 하나의 변경만 적용(비정규화 필드 하나, 요약 테이블 하나, 리포팅 뷰 하나)
- 고정 테스트 창(어제, 최근 7일, 지난 전체 달)에 대해 OLTP 소스와 합계를 검증
- 점진적으로 롤아웃하고 일주일간 성능과 정확성 관찰
- "쿼리 시간"과 "행 수"에 대한 알람을 추가해 조용한 드리프트를 포착
AppMaster로 화면을 구축한다면 OLTP 엔티티(트랜잭션 화면과 편집에 사용되는 것)와 리포팅 엔티티(읽기 최적화된 모델로 BI 스타일 페이지를 구동하는 것)를 깔끔하게 분리해 설계하세요. 웹 UI 빌더에서 실제 필터와 날짜 범위를 사용해 BI 화면을 프로토타입으로 만들어 사용자가 실제로 클릭하는 것에 따라 데이터 모델을 조정하세요.
일주일간 실제 사용한 후 다음 조치를 결정하세요. 빠른 수정으로 해결되면 계속 반복하세요. 합계 계산이 여전히 비용이 크면 명확한 새로고침 계획이 있는 요약 테이블에 투자하세요. 리포팅이 핵심이고 무거워지면 리포팅 워크로드를 별도 저장소로 옮기는 것을 고려하되 OLTP는 빠르고 안전한 쓰기에 집중하게 하세요.


