이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.
- Reporting, Search, Recommendation은 같은 source event를 보더라도 왜 다른 정합성 기준을 갖는가?
- 검색 색인과 추천 feature가 늦거나 누락됐을 때 API 장애와 어떻게 구분하는가?
- 취소, 삭제, 품절, 환불 같은 상태 변화는 downstream별로 어떻게 전달해야 하는가?
- 백엔드 개발자는 각 downstream 소비자에게 어떤 key, freshness, 재처리 기준을 제공해야 하는가?
개요
리포트, 검색, 추천은 같은 운영 데이터를 사용하지만 같은 방식으로 믿지 않는다.
리포트는 집계 기준과 금액 정확성이 중요하다.
검색은 색인 freshness와 삭제 반영이 중요하다.
추천은 feature 생성 시점, 학습 데이터 누락, late event 처리가 중요하다.
이 문서는 백엔드 서비스가 만든 event와 DB 변경이 세 downstream 흐름에서 어떻게 다르게 소비되는지 정리한다.
세 소비자의 차이
| 소비자 | 주 관심사 | 허용 가능한 지연 | 가장 위험한 실패 |
|---|---|---|---|
| Reporting | metric definition, 집계 정확성 | 분 단위에서 시간 단위 | 금액 합계와 count가 틀림 |
| Search | 색인 freshness, 삭제 반영 | 초 단위에서 분 단위 | 삭제/품절 상품이 노출됨 |
| Recommendation | feature completeness, 학습 품질 | 시간 단위에서 일 단위 | 잘못된 feature로 추천 품질 저하 |
세 소비자를 하나의 “데이터 파이프라인”으로 묶으면 장애 해석이 흐려진다.
Reporting 연동
리포트는 보통 event를 fact table로 바꾼 뒤 집계한다.
중요한 질문은 다음과 같다.
- event 하나가 fact row 하나인가?
- 취소와 환불은 음수 fact인가, 별도 상태 변경인가?
- 집계 기준 시간은 order created time인가, paid time인가?
- 정산 기준 금액과 사용자 화면 금액이 같은가?
- late event를 어느 partition에 반영하는가?
SELECT order_date,
count(*) AS paid_orders,
sum(paid_amount) AS paid_amount
FROM fact_order_paid
WHERE order_date = current_date
GROUP BY order_date;백엔드가 eventTime의 의미를 명확히 하지 않으면 이 쿼리의 기준일이 흔들린다.
Search 연동
검색 색인은 현재 사용자가 볼 수 있는 상태를 빠르게 반영해야 한다.
검색에는 다음 event가 중요하다.
- 생성: 상품, 게시글, 주문 검색 대상이 새로 생김.
- 수정: 제목, 가격, 재고, 공개 상태가 바뀜.
- 삭제: 더 이상 노출되면 안 됨.
- 권한 변경: 특정 사용자나 조직만 볼 수 있음.
검색 색인은 집계 mart와 달리 “삭제 누락”이 특히 위험하다.
사용자가 삭제한 상품이나 비공개 글이 계속 노출되면 freshness 문제를 넘어 제품 장애가 된다.
Recommendation 연동
추천은 실시간 화면과 다르게 지연을 어느 정도 허용할 수 있다.
하지만 feature 의미가 틀리면 조용히 품질이 나빠진다.
추천 feature에는 다음 계약이 필요하다.
- userId, itemId 같은 join key.
- 클릭, 장바구니, 구매 같은 event 의미.
- bot, test, admin traffic 제외 기준.
- 취소, 환불, 반품이 positive signal을 되돌리는 방식.
- 학습 데이터에 포함할 기간과 late event 처리 기준.
추천 장애는 API 500처럼 드러나지 않는다.
그래서 feature freshness, null ratio, distribution drift를 따로 본다.
Downstream별 계약 예시
event: ProductPriceChanged v1
key: productId
event_time: price change committed time
reporting:
freshness: 30 minutes
use: price history mart
replay: last 180 days
search:
freshness: 2 minutes
use: product search index
delete_policy: hide unavailable product immediately
recommendation:
freshness: 6 hours
use: item price feature
late_event_policy: include until daily feature cutoff같은 event라도 downstream마다 기대하는 지연과 복구 방식이 다르다.
취소와 삭제의 중요성
백엔드 서비스는 생성 event만 잘 보내고 취소/삭제 event를 빠뜨리기 쉽다.
그 결과는 downstream마다 다르게 나타난다.
- 리포트: 취소된 매출이 계속 집계된다.
- 검색: 삭제된 상품이 검색 결과에 남는다.
- 추천: 품절 상품이 계속 추천된다.
- 품질 rule: source count와 target count가 맞아도 의미가 틀린다.
데이터 파이프라인에서 “사라짐”은 “없음”으로 자연스럽게 전달되지 않는다.
명시적인 상태 변화 event가 필요하다.
장애 구분
API는 정상인데 검색 결과가 늦다면 API 장애가 아니다.
다음 순서로 분리한다.
- source DB에는 변경이 commit됐는가?
- outbox 또는 CDC에는 event가 기록됐는가?
- broker publish lag가 있는가?
- consumer lag 또는 DLQ가 있는가?
- target index, mart, feature store에 반영됐는가?
- target 품질 rule은 통과했는가?
이 순서를 기록해 두면 “백엔드 문제인지 데이터 문제인지” 논쟁이 줄어든다.
운영 지표
reporting_freshness_lag: 리포트 mart 최신 event와 현재 시각 차이.search_index_lag: source 변경부터 색인 반영까지의 시간.recommendation_feature_age: feature가 생성된 지 얼마나 지났는지.delete_event_miss_count: 삭제/비공개 event가 target에 반영되지 않은 횟수.late_event_ratio: cutoff 이후 들어온 event 비율.
세 downstream의 지표를 같은 dashboard에 두되 severity 기준은 다르게 둔다.
위험 신호!
- 모든 downstream에 같은 freshness SLA를 약속한다.
- 생성 event는 있지만 취소, 삭제, 품절 event가 없다.
- 검색 색인 실패를 API 성공률만 보고 정상으로 판단한다.
- 추천 feature null ratio와 drift를 보지 않는다.
- 데이터 팀이 event_time, published_at, indexed_at을 구분하지 못한다.
확인 질문
확인 질문
- 리포트, 검색, 추천의 freshness 요구가 서로 다른 이유는 무엇인가?
- 삭제 event가 빠지면 검색과 추천에서는 각각 어떤 장애가 생기는가?
- API 정상과 downstream 정상은 어떤 지표로 분리해 확인할 수 있는가?
- 추천 feature의 품질은 왜 단순 count보다 distribution drift로도 봐야 하는가?
참고 문서
- Apache Kafka Documentation
- Spring for Apache Kafka Reference
- Elasticsearch Documentation
- Martin Kleppmann, Designing Data-Intensive Applications