DB 읽기 비용 최적화 방향 통합
ADR-360 DB 읽기 비용 최적화 방향 통합
1. Metadata
- ADR ID: ADR-360
- Status: draft
- Date: 2026-03-19
- Owner: YSY
- Related ADRs: ADR-180, ADR-220, ADR-270, ADR-290, ADR-295, ADR-350
- Supersedes: 없음
- Superseded By: 없음
2. Context
- 현재 DB 비용 최적화 과제는 파트너 운영 화면 한 곳의 문제로 끝나지 않는다.
- 비용에 직접 영향을 주는 항목은
쿼리 횟수만이 아니라RLS 스캔 범위,반환 row 수,payload 크기,polling 주기,같은 세션 내 중복 조회까지 포함한다. - 현재 읽기 패턴은 다음처럼 성격이 다른 데이터를 동일한 방식으로 다루는 경향이 있다.
- 슬롯 발행/종료에 따라 자주 바뀌는 실시간 운영 상태
- 최근 목록만 필요하지만 전체 이력을 읽는 운영 목록
- 전일 또는 기간 기준으로 닫힌 집계 데이터
- 파트너 전환과 무관한 정적 카탈로그
- 다른 조회에 종속되지 않아도 되는 보조 메타데이터
- 이 상태에서는 화면 하나를 빠르게 만드는 단기 수정은 가능해도, 기능이 늘수록 읽기 비용과 응답 지연이 함께 커진다.
- 따라서 이번 결정의 범위는
특정 쿼리 튜닝이 아니라DB 비용 최적화의 기준 방향을 정하는 것이다.
3. Domain Decision
- DB 읽기 비용 최적화는
화면이 아니라read class기준으로 설계한다. - 기본 read class는 다음 5가지로 고정한다.
live current: 지금 또는 오늘 기준으로 계속 변할 수 있는 상태bounded operational list: 최근/활성 범위만 필요한 운영 목록historical snapshot: 전일 또는 기간 기준으로 닫힌 집계static catalog: 세션 단위 재사용 가능한 저변화 데이터auxiliary metadata: 필요한 시점에만 읽으면 되는 보조 정보- 비용 최적화의 우선순위는
총 쿼리 수 절감이 아니라활성 세션당 반복적으로 터지는 hot path 절감에 둔다. - 같은 도메인 데이터라도 read class가 다르면 별도 조회 계약과 캐시 규칙을 사용한다.
현황이라는 용어는live current에만 사용하고, 전일 또는 기간 기준 데이터에는 사용하지 않는다.- 전일 스냅샷과 기간 추이는
historical snapshot으로 분류하며, 실시간 현황과 같은 갱신 기대치를 갖지 않는다.
4. Product Decision
- 파트너 대시보드와 운영 화면은
오늘/현재와전일/기간을 명시적으로 분리한다. - 대시보드의
현황은 오늘 기준 또는 현재 기준 데이터만 의미한다. - 전일 마감 데이터는 별도
전일 요약,전일 마감,기준일 요약과 같이 닫힌 데이터임이 드러나는 이름으로 노출한다. 기간추이와업종 분석은 어제까지 확정된 데이터를 기본 SoT로 사용한다.- 파트너 운영 화면의 기본 진입은
요약 확인을 우선하고, 과거 이력과 편집 상세는 명시적 진입 시점에만 연다. - 사용자가 한 세션 안에서 같은 파트너를 반복 방문할 때 같은 정적 데이터와 닫힌 집계를 매번 처음부터 다시 읽지 않는다.
5. UX Decision
- 실시간 운영 상태는 mutation 결과와 앱 로컬 상태를 우선 반영하고, 필요한 경우에만 서버 재검증을 수행한다.
- 닫힌 집계 데이터는 기준일 라벨과 함께 캐시를 적극 허용한다.
- 사용자가 인지하는
새로고침 필요성은 실제 변동 가능성이 있는 영역에만 남긴다. - 파트너 전환과 무관한 정적 카탈로그는 화면 재진입마다 skeleton을 다시 만들지 않는다.
- 긴 이력 목록은 기본 로드 범위를 제한하고, 더보기 또는 페이지네이션으로 확장한다.
- 주기적 polling은 기본 전략이 아니라 보조 전략으로 사용한다.
6. Tech Decision
6.1 기본 원칙
live current는 전체 재조회보다write result patch + 선택적 재검증을 우선한다.bounded operational list는 기본 조회 범위를 명시적으로 제한한다.historical snapshot은 날짜 키 기반 캐시를 허용한다.static catalog는 파트너 선택과 분리된 세션 캐시를 기본으로 한다.auxiliary metadata는 다른 hot query의 종속 effect로 읽지 않는다.
6.2 대시보드 방향
현황카드는 오늘/현재 기준 데이터만 사용한다.기간추이와업종 분석은partner_metrics_daily또는 동등한 일별 snapshot SoT를 사용한다.- 전일 snapshot이 필요하면
historical snapshot으로 노출하고,현황과 같은 영역으로 섞지 않는다. 기간추이와업종 분석은partnerId + range + toDate기준으로 앱 캐시를 허용한다.- snapshot 응답에는 기준일과
updated_at또는 동등한 freshness 필드를 포함한다.
6.3 파트너 운영 방향
- 슬롯 목록 기본 조회는
active + recent closed범위로 제한한다. - entitlement, QR 상태, 파트너 기본 상태는 하나의 요약 read model로 합칠 수 있으면 합친다.
- mutation 성공 후 전체 entitlement 재조회는 기본 전략으로 사용하지 않는다.
subscription_plans,subscription_addons는 세션 범위 캐시로 올린다.- 편집용 상세 필드와 이미지 배열은 hot path에서 분리한다.
6.4 사용자 탐색 방향
public_get_nearby_slots같은 대량 hot read는 polling 주기, 위치 변화, 화면 focus를 함께 고려한 적응형 전략으로 줄인다.- 즐겨찾기 파트너 메타데이터 조회는 슬롯 새로고침 주기에 종속시키지 않는다.
- 위치 기반 탐색은
반경,정렬,필드 집합,재호출 조건을 함께 최적화한다.
6.5 캐시 및 무효화 규칙
live current는 아래 이벤트에서 우선 무효화하거나 patch한다.- 슬롯 발행
- 슬롯 종료
- 구독 변경
- 파트너 전환
- 수동 새로고침
historical snapshot은 아래 경우에만 재조회한다.- 날짜 경계가 바뀐 경우
- 기준일이 바뀐 경우
- 수동 새로고침
is_stale또는 동등한 stale 표시가 있는 경우static catalog는 파트너 전환만으로 무효화하지 않는다.auxiliary metadata는 해당 화면 또는 모달 진입 시점에만 읽는다.
6.6 비채택 방향
- 근거 없이 모든 문제를 초대형 단일 RPC로 몰아넣지 않는다.
- 정확한 invalidation 규칙 없이 전면적 polling 확대를 채택하지 않는다.
- 서버 SoT를 흐리는 장기 로컬 캐시를 기본 전략으로 사용하지 않는다.
7. Ops Decision
- 비용 최적화의 기준 지표는
월 청구액만이 아니라active session당 DB invocation,row 수,payload 크기,P50/P95 응답시간을 함께 본다. - 화면별 read budget을 문서와 QA 기준으로 관리한다.
- 우선순위는 아래 순서로 둔다.
- 사용자 근처 슬롯 탐색
- 파트너 운영 hot path
- 즐겨찾기/보조 메타데이터
- 대시보드 historical cache
- 신규 read model이나 snapshot 도입 전후로 호출 수와 payload 절감 효과를 비교한다.
- 비용 문제와 성능 문제는 같은 운영 항목으로 추적한다.
8. Implementation Contract
8.1 Read Class Contract
live current는 오늘/현재 상태를 의미한다.historical snapshot은 전일 또는 기간 기준 닫힌 데이터를 의미한다.- 같은 화면 안에서도 두 계약을 혼용할 수 있지만, 명칭과 캐시 정책은 분리해야 한다.
8.2 Dashboard Contract
현황은 전일 snapshot을 직접 의미하지 않는다.- 전일 snapshot을 보여줄 경우
기준일을 함께 노출해야 한다. 기간추이와업종 분석은 앱 캐시를 허용한다.- snapshot 응답은 최소
metric_date와 freshness 필드를 포함해야 한다.
8.3 Partner Ops Contract
- 슬롯 목록 기본 로드는 전체 이력이 아니라 제한된 범위를 사용한다.
- mutation RPC는 가능하면 화면에 필요한 최소 summary를 함께 반환한다.
- 정적 카탈로그는 파트너 전환 때 재조회하지 않는다.
8.4 User Explore Contract
- 근처 슬롯 조회는 기본적으로 앱 foreground, 위치 변화, 수동 refresh 조건을 함께 본다.
- polling은 비용 예산 안에서만 허용한다.
- 즐겨찾기 메타데이터는 슬롯 목록 refresh effect에 직접 묶지 않는다.
8.5 Observability Contract
- 최적화 전후 비교가 가능하도록 화면 단위 호출 수와 payload를 기록할 수 있어야 한다.
- cache hit/miss와 재조회 트리거는 QA에서 설명 가능해야 한다.
9. Alternatives
대안 A. 화면별로 개별 최적화만 반복
- 장점: 당장 손보는 화면 하나는 빨리 개선할 수 있다.
- 단점: 동일한 실수를 다른 화면에서 반복하게 된다.
- 판단: 응급 처치로는 가능하지만 기준 아키텍처로 채택하지 않는다.
대안 B. DB read를 전면 polling + 앱 캐시로 흡수
- 장점: 구현이 단순해 보일 수 있다.
- 단점: 장기 비용과 stale state 리스크가 함께 커진다.
- 판단: 비용 최적화 방향으로 채택하지 않는다.
대안 C. 모든 read를 통합 RPC 하나로 수렴
- 장점: 왕복 횟수는 줄일 수 있다.
- 단점: 변경 결합도가 높아지고 hot/cold 분리 이점이 줄어든다.
- 판단: 필요한 영역에서 제한적으로만 검토한다.
10. Consequences / Impact
긍정 영향:
- 최적화 우선순위를 쿼리 수가 아니라 실제 hot path 비용 기준으로 정렬할 수 있다.
- 대시보드와 운영 화면에서
실시간과닫힌 집계의 의미가 명확해진다. - 동일 세션 내 중복 조회와 과도한 polling을 줄일 기준이 생긴다.
부정 영향 및 리스크:
- read class 분리 초기에 앱 상태 관리와 invalidation 규칙이 더 복잡해질 수 있다.
- 기존 ADR과 화면 문구 중 일부는 후속 정리가 필요하다.
- 계측 없이 캐시만 먼저 도입하면 잘못된 최적화가 될 수 있다.
11. Scope
- 파트너 대시보드
- 파트너 운영 및 편집
- 사용자 근처 슬롯 탐색
- 즐겨찾기 파트너 메타데이터
- 구독 카탈로그 및 entitlement 조회
12. Validation
-
live current와historical snapshot의 용어가 UI와 문서에서 일관된다. - 대시보드
현황용어가 전일 snapshot과 분리된다. -
subscription_plans,subscription_addons가 파트너 전환 시 재조회되지 않는다. - 슬롯 목록 기본 조회 범위가 제한된다.
- 근처 슬롯 polling 정책의 비용 예산이 문서화된다.
- 화면별 read budget과 관측 항목이 QA 또는 운영 기준으로 연결된다.
13. Rollout / Migration
- 1차는 계측과 budget 정의를 먼저 적용한다.
- 2차는 파트너 운영 hot path와 정적 카탈로그 캐시를 정리한다.
- 3차는 대시보드
현황/전일/기간추이의미 분리를 반영한다. - 4차는 사용자 근처 슬롯과 즐겨찾기 메타데이터 조회를 줄인다.
14. Open Questions
- 사용자 근처 슬롯은 polling보다 이벤트 기반 무효화로 어느 정도까지 대체할 수 있는가.
- 파트너 운영 summary는 view가 적절한가, RPC가 적절한가.
- historical snapshot 캐시를 세션 캐시로 충분히 볼지, AsyncStorage까지 내릴지 추가 판단이 필요한가.