Stan 기술블로그

리플레이 디스크를 GA4 3년치로 다시 쟀다

리플레이 용량 실측 편은 “리플레이 1개 평균 213KB, 디스크 여유 66GB — 걱정할 단계 아님”으로 끝났다. 개발자 몇 명이 쓰는 테스트 트래픽 얘기였다. 운영 트래픽이 들어오면 어떻게 될까. 레거시 앱의 GA4에서 3년치 세션을 뽑아 다시 쟀더니, 처음 감으로 잡아 둔 알람 기준이 세 번 뒤집혔다.

(사내 인프라 특성상 서비스 이름·서버 주소는 일반화했다.)


정해야 할 값은 셋

레거시 웹앱의 프런트를 React로 바꾸면서 Sentry Session Replay를 켜기로 했다. self-hosted라 디스크가 곧 쿼터다. 정해야 할 값은 셋이었다.

  • 리플레이 샘플링: replaysOnErrorSampleRate(에러가 난 세션만 녹화), replaysSessionSampleRate(아무 세션이나 무작위 녹화)
  • 90일 보관할 때 디스크 크기
  • 디스크 알람을 울릴 기준

처음 초안에는 “5GB 넘으면 알람” 정도로 적어 뒀다. 근거는 없었다.

근거는 레거시 앱의 GA4에서 가져오기로 했다. 프런트만 바꾸는 것이니 새 React 앱은 같은 사용자와 같은 트래픽을 그대로 물려받는다. 레거시의 세션 수가 React 배포 첫날의 가장 정확한 예측치다. 리플레이는 세션 단위로 녹화되니 이용자 수가 아니라 세션 수를 본다.

3년치 세션 — 평시는 잔잔하고, 신고철은 3.8배

연도 총 세션 일평균
2023 약 335만 약 9,190
2024 약 367만 약 10,020
2025 약 332만 약 9,100
2026 (8월 중순까지) 약 176만 약 7,800

평시는 하루 9,000~10,000세션으로 3년 내내 비슷했다. 트래픽이 늘어 디스크가 서서히 차오를 걱정은 없다는 뜻이다.

문제는 세무 신고철이다. 2025년 가장 붐빈 달은 1,043,569세션, 하루 약 34,800으로 연평균의 약 3.8배였다. 한산한 달은 하루 2,830 정도이고, 마감일 당일에는 그 달 평균의 1.5~2배(하루 5~7만)도 가능하다. “평균의 두 배쯤 잡아 두면 되겠지”라는 어림으로는 놓치는 크기다.

GA4에서 하나 헤맨 점이 있다. 탐색(Explore) 화면은 데이터 보존 기간(기본 2개월, 최대 14개월) 때문에 작년 신고철을 보여 주지 못한다. 몇 년 전 수치는 보고서 → 획득 → 트래픽 획득에서 봐야 한다. 탐색에서 안 보인다고 “데이터가 없다”고 결론 내리면 안 된다.

리플레이 한 개는 얼마인가

테스트 서버에서 세 번 쟀다(재는 방법은 실측 편에 있다).

날짜 개수 총량 1개 평균
7월 27일 51 10.9MB 약 213KB
8월 13일 349 59MB 약 177KB
9월 8일 658 147MB 약 229KB

세션이 길수록 커진다. 처음에 가정한 250KB가 타당했다. 다만 에러 세션 하나에 에러가 평균 2.4건 붙어 있어서, 세션당으로 보면 리플레이 229KB에 에러 43KB를 더한 272KB가 더 정확하다.

계산식은 이렇다. 전제는 에러 세션만 녹화(onError 1.0, session 0)하고, 에러율은 GA에 없으니 5%로 가정했다.

하루 저장량 = 하루 세션 수 × 에러율 × 샘플링율 × 리플레이 1개 크기

첫 번째 — “5GB 알람”

8월 중순 첫 계산(리플레이 250KB 가정)부터 초안이 무너졌다.

  • 평시: 하루 1만 세션 × 5% × 250KB ≈ 하루 125MB
  • 가장 붐빈 달: 하루 34,800 × 5% × 250KB ≈ 하루 435MB → 그 한 달만 약 13GB

한 달 치만으로 13GB가 쌓이는데 5GB에서 알람이 울리면 경보가 아니라 소음이다. 기준을 25~30GB로 올렸다. 이때 계산한 최악의 90일은 약 20GB였다.

두 번째 — 최악은 20GB가 아니라 27GB

20GB는 90일 동안 신고철이 한 번 들어온다고 가정한 값이었다. 그런데 세무 일정은 1~2월 연말정산, 1월 부가세, 3월 법인세로 몰려 있다. 1~3월이라는 90일 창 하나에 성수기가 두 번 들어온다.

9월에 실측 단가로 다시 계산했다(평시 하루 9,100세션, 229KB 기준).

90일 동안 리플레이가 차지하는 공간 에러율 5%
평시 약 9.4GB
성수기 한 번 약 18.2GB
성수기 두 번 겹침 약 27GB

에러율이 10%면 전부 두 배다. 그리고 신고철에는 부하 때문에 타임아웃·5xx가 늘어 에러율 자체가 같이 오를 가능성이 크다. 세션이 3.8배인데 에러율까지 곱해지니, 고정 5%는 낙관적이다. 그래서 디스크 크기는 에러율 10%로 잡고, 5%를 운영 목표로 두자고 정리했다. 너무 작게 잡은 대가(신고철에 디스크가 가득 참)가 너무 크게 잡은 대가(디스크가 남음)보다 훨씬 크다.

세 번째 — “얼마 썼나”가 아니라 “얼마 남았나”

리플레이만 보다가 서버 전체를 다시 계산해 보니, 알람 기준 자체가 틀린 방식이었다. Sentry는 리플레이 말고도 OS, 컨테이너 이미지, Kafka, 에러, 트레이스가 디스크를 쓴다.

서버 전체 (GB) 평시 성수기 한 번 성수기 두 번
OS·이미지·Kafka 29.8 32.7 36.7
리플레이 90일 9.4 18.2 27.0
에러 90일 1.2 2.4 3.6
합계 (트레이싱 끔) 40.4 53.3 67.3
합계 (트레이싱 켬) 43.9 59.8 77.3

평시에도 40GB를 쓴다. “25~30GB 넘으면 알람”이면 첫날부터 계속 울린다. 기준을 쓴 양이 아니라 남은 양으로 바꿨다. 남은 공간 30GB에서 경고, 15GB에서 위험.

그리고 이 표가 더 큰 사실을 보여 줬다. 지금 서버는 98GB 디스크에 여유가 63GB다. 성수기 두 번이 겹치면 67~77GB가 필요하다. 지금 서버로는 운영 트래픽을 못 버틴다. 지금 넉넉해 보이는 건 아직 운영 트래픽이 안 들어와서일 뿐이다. 새 서버 디스크는 최악에 여유 30%를 더한 150GB를 권고했다.

아직 모르는 것

  • 에러율. 5%는 가정이다. 실측하려면 분모인 세션(릴리즈 헬스) 데이터가 필요한데, React 앱 프로젝트의 세션 데이터가 8월 21일 이후 끊겨 있다. 원인을 찾기 전에는 실측할 수 없다.
  • 웹과 모바일이 섞였을 가능성. 이 GA4 속성에는 모바일 앱 채널로 보이는 항목도 있다. 리플레이는 웹 대상이니 웹 세션만 걸러야 정확하다. 다만 섞였다면 숫자가 실제보다 크게 잡힌 쪽이라, 사이징에는 여유를 두는 방향의 오차다.

마치며

이 숫자를 가지고 무엇을 받고, 언제 지우고, 어떻게 지켜볼지 정한 이야기는 보존 정책 편에 이어서 적었다.

진짜로 배운 것

  1. 시즌이 있는 서비스는 평균이 아니라 최악의 보관 기간 창으로 잰다. 보관이 90일이면 성수기 짐은 석 달 동안 남는다. 그 창에 성수기가 몇 번 들어오는지까지 센다.
  2. 알람은 “얼마 썼나”가 아니라 “얼마 남았나”로 건다. 시스템이 원래 쓰는 고정비가 있으면 사용량 기준은 첫날부터 소음이 된다.
  3. 근거에 결함이 있으면, 그 결함이 어느 방향으로 틀리게 하는지를 같이 적는다. 웹·모바일이 섞인 GA4 숫자는 불완전했지만 크게 잡히는 쪽이라, 5GB 기준을 뒤집기에는 충분했다.
  4. 가정은 가정이라고 남겨 둔다. 에러율 5%는 실측으로 바꿀 자리다. 그 자리가 비어 있다는 걸(세션 데이터 끊김) 찾은 것도 이번 조사의 결과다.

self-hosted Sentry 카테고리의 글