NRR 숫자 하나만 강조된 분기 리포트나 투자 자료는 흔하다. 그런데 NRR이 같아도 속사정은 전혀 다를 수 있다.
GRR(총 매출 유지율)은 기존 고객에게서 매출이 얼마나 빠져나갔는지만 보는 지표다. NRR(순 매출 유지율)은 여기에 업셀 같은 확장 매출을 더한 지표다. NRR은 GRR에 확장 매출 비율을 더한 값이라 항상 GRR보다 크거나 같고, 둘의 차이가 곧 확장 매출의 크기다. NRR만 보면 소수 대형 고객의 확장이 다수 고객의 이탈을 가릴 수 있다. 그래서 두 숫자는 항상 같이 봐야 한다.
매출 유지율은 기존 고객만 떼어 보는 지표다
매출 유지율은 기간 시작 시점에 이미 돈을 내던 고객들이 기간이 끝날 때 얼마를 내고 있는지를 비율로 나타낸 값이다. 구독 매출에는 신규 고객 유입과 기존 고객 변화가 섞여 있다. 그래서 전체 매출 성장률만으로는 기존 고객 기반이 건강한지 알 수 없다.
예를 들어 연 매출이 20% 늘었는데 신규 영업으로 40%를 채우고 기존 고객에서 20%가 빠진 상황일 수 있다. 이 회사는 신규 영업이 멈추는 순간 매출이 줄기 시작한다. 이런 상태는 전체 성장률에는 드러나지 않고 유지율에서만 보인다.
글에서 쓰는 용어부터 정리한다. MRR과 ARR은 구독 계약에서 매달, 매년 반복해서 들어오는 매출이다. 이탈(churn)은 고객이 해지해서 사라진 매출이고, 다운그레이드(contraction)는 고객은 남았지만 좌석 축소나 하위 플랜 전환으로 줄어든 매출이다. 확장(expansion)은 업셀, 크로스셀, 좌석 추가, 사용량 증가로 기존 고객에게서 늘어난 매출이다. 코호트는 같은 기준 시점으로 묶은 고객 집단이며, 이 글에서는 기간 시작 시점에 유료였던 고객을 뜻한다.
GRR은 새어 나간 양만 잰다
GRR(Gross Revenue Retention)은 이탈과 다운그레이드로 잃은 매출만 반영한 유지율이다.
GRR = (시작 MRR - 이탈 - 다운그레이드) / 시작 MRR
업셀, 크로스셀 같은 확장 매출은 분자에 넣지 않는다. 가격 인상으로 늘어난 매출도 같은 취급이다. 그래서 GRR은 100%를 넘을 수 없다. 기존 고객 기반이라는 통에서 물이 얼마나 새는지만 본다.
이 숫자는 제품 가치와 고객 성공 활동(온보딩, 지원, 계약 갱신)이 제대로 돌아가는지 보여 주는 건강 지표다. ChartMogul은 같은 지표를 Gross MRR Retention이라고 부르고, 고객을 현재 가격이나 계약 금액 그대로 얼마나 붙잡고 있는지를 나타내는 지표로 설명한다.
NRR은 확장 매출까지 더한다
NRR(Net Revenue Retention)은 GRR 계산식의 분자에 확장 매출을 더한 값이다. NDR(Net Dollar Retention)이라고도 부른다.
NRR = (시작 MRR - 이탈 - 다운그레이드 + 확장) / 시작 MRR
신규 고객 매출은 여기서도 빠진다. NRR이 100%를 넘으면 신규 영업이 하나도 없어도 기존 고객만으로 매출이 커진다는 뜻이다.
GRR이 건강 지표라면 NRR은 성장 지표다. 고객을 지키는 힘에 더해 좌석, 사용량 과금, 상위 플랜 같은 확장 모델이 얼마나 작동하는지가 함께 반영된다. 과금 모델에 따라 경향도 갈린다. 클라우드나 API처럼 사용량 기반으로 과금하는 SaaS는 NRR이 100%를 크게 넘기 쉽고, 좌석 기반 과금은 상대적으로 완만한 편이다.
두 지표가 공유하는 계산 규칙
두 지표 모두 기간 시작 시점의 고객 코호트만 추적한다. 기간 중에 새로 가입한 고객은 분자에도 분모에도 넣지 않는다. 이 고객들은 다음 기간 코호트에서 다룬다.
기준 기간도 반드시 적어야 한다. 연간 계약의 갱신 주기를 한 바퀴 다 포함하도록 12개월 기준을 많이 쓰고, 월간 값을 쓰는 곳도 있다. 월간 NRR 101%와 연간 NRR 101%는 전혀 다른 수준이다. 그래서 기간이 빠진 유지율 숫자는 비교 자료로 쓰지 않는다.
가상 수치로 직접 계산해 보기
아래는 설명을 위해 만든 가상 수치다. 연초 ARR이 10억 원인 B2B SaaS가 있다고 하자. 1년 동안 기존 고객에게서 일어난 변화는 다음과 같다.
- 해지로 인한 이탈: 8천만 원
- 좌석 축소 등 다운그레이드: 2천만 원
- 업셀과 추가 좌석: 1억 8천만 원
시작 ARR = 10.0억 이탈 = 0.8억 다운그레이드 = 0.2억 확장 = 1.8억 GRR = (10.0 - 0.8 - 0.2) / 10.0 = 9.0 / 10.0 = 90% NRR = (10.0 - 0.8 - 0.2 + 1.8) / 10.0 = 10.8 / 10.0 = 108% 확장 비율 = 1.8 / 10.0 = 18% NRR = GRR + 확장 비율 = 90% + 18% = 108%
올해 신규 고객 매출이 얼마였든 이 계산에는 들어가지 않는다. NRR과 GRR의 차이 18%p가 그대로 확장 매출의 몫이라는 점도 확인할 수 있다.
NRR 하나만 보면 놓치는 것
역시 가상의 두 회사를 놓고 보자. 둘 다 NRR이 108%다.
- A사: GRR 95%, 확장 13%
- B사: GRR 80%, 확장 28%
A사는 고객이 거의 떠나지 않고 꾸준히 조금씩 늘어난다. B사는 매년 시작 매출의 20%를 잃는데, 일부 대형 고객의 큰 확장이 그 구멍을 메우고 있을 수 있다. 대형 고객의 확장이 한계에 다다르면 B사의 NRR은 GRR 쪽으로 빠르게 내려갈 수 있다. NRR만 보면 두 회사는 똑같아 보이고, GRR을 옆에 놓아야 차이가 보인다.
매출 기준 지표와 고객 수 기준 지표도 다르게 움직인다. 소액 고객이 많이 떠나도 GRR은 크게 움직이지 않을 수 있다. 반대로 대형 고객 한 곳이 떠나면 GRR이 크게 떨어진다. 그래서 고객 수 기준 유지율(Logo Retention)도 함께 보는 것을 권한다.
계산에서 자주 틀리는 지점
공식은 단순하지만 실제 데이터로 뽑으면 아래 지점에서 숫자가 잘 틀어진다. 집계 쿼리를 짜기 전에 아래 항목부터 확인해 두면 시행착오가 줄어든다.
1. 신규 고객을 분자에 넣는 실수
기간 말 전체 MRR을 분자로 쓰면 신규 고객 매출이 섞인다.
분자는 반드시 시작 코호트에 속한 고객의 기간 말 매출만 더해야 한다. 이렇게 하지 않으면 NRR이 성장률과 비슷한 숫자가 되어 버린다.
2. 가격 인상을 확장으로 계산
정가 인상으로 늘어난 매출을 확장에 넣으면 NRR이 단가 효과만큼 높아진다.
고객이 더 쓰거나 더 산 것이 아니라 단가만 오른 경우다. 가격 인상분을 NRR에 포함할지는 회사마다 정의가 다르다. 어느 쪽이든 가격 인상분은 별도 항목으로 집계해 두고, 포함한 값과 제외한 값을 함께 보고하는 것을 권한다.
3. 계약 기준과 청구 기준 혼용
계약 기준 ARR과 실제 인보이스 금액을 섞으면 숫자가 튄다.
연간 선불 계약은 청구가 한 달에 몰리므로, 인보이스 기준으로 보면 특정 달에 매출이 튀고 나머지 달은 비어 보인다. 유지율은 계약 기준 MRR(연간 계약은 12로 나눈 값)로 통일한다.
4. 무료 플랜과 체험을 코호트에 포함
무료 사용자나 체험 고객이 코호트에 들어가면 지표가 왜곡된다.
시작 MRR이 0인 고객이 유료로 전환하면 그 매출이 확장처럼 잡혀 NRR이 올라간다. 코호트는 기간 시작 시점에 MRR이 0보다 큰 유료 고객으로만 잡는다.
5. 연간 계약 이탈이 늦게 드러나는 문제
연간 계약 고객은 이미 떠날 마음을 먹었어도 갱신 시점까지는 MRR이 그대로 남는다.
그래서 월간 GRR은 실제 이탈보다 늦게 반응한다. 갱신 예정 매출 대비 실제 갱신 매출 비율(갱신율)을 월별로 따로 보면 이 지연을 보완할 수 있다.
6. 고객 규모별로 나누지 않음
전체 코호트 평균 하나만 보면 SMB와 엔터프라이즈의 서로 다른 움직임이 섞여 상쇄된다.
SMB는 이탈이 잦고 엔터프라이즈는 확장이 큰 구조라면, 평균만으로는 어느 쪽에 문제가 있는지 알 수 없다. 최소한 고객 규모 구간별로 GRR과 NRR을 따로 낸다.
7. 통화와 환율 영향
외화 계약이 있으면 환율 변동만으로 확장이나 다운그레이드가 생긴 것처럼 보인다.
기간 시작 시점 환율 같은 고정 환율로 환산해서 비교하면 이 효과를 걷어낼 수 있다.
SQL로 같은 데이터에서 함께 뽑기
GRR과 NRR을 따로따로 다른 리포트에서 뽑으면 기준이 어긋나기 쉽다. 같은 데이터에서 이탈, 다운그레이드, 확장을 각각 분리해 집계해 두면 두 지표를 한 번에 낼 수 있다.
순서는 이렇게 잡는다. 구독 이벤트 테이블(시작, 증감, 해지)에서 고객별 월말 MRR 스냅샷을 만든다. 그다음 기간 시작 월과 끝 월의 스냅샷을 고객 단위로 조인한다. 아래 예시는 설명용 가상 스키마이고, PostgreSQL 문법 기준이다.
-- 가상 스키마: 고객별 월별 MRR 스냅샷 (month = 해당 월의 값, 계약 기준, 고정 환율 환산)
-- mrr_snapshot(customer_id, month date, mrr numeric, segment text)
WITH base AS ( -- 기간 시작 시점의 유료 고객 코호트
SELECT customer_id, segment, mrr AS start_mrr
FROM mrr_snapshot
WHERE month = DATE '2025-01-01'
AND mrr > 0
),
last AS ( -- 12개월 뒤 같은 고객들의 MRR
SELECT customer_id, mrr AS end_mrr
FROM mrr_snapshot
WHERE month = DATE '2026-01-01'
),
moved AS (
SELECT b.segment, b.start_mrr,
COALESCE(l.end_mrr, 0) AS end_mrr
FROM base b
LEFT JOIN last l USING (customer_id) -- 신규 고객은 base에 없으므로 자동 제외
)
SELECT
segment,
SUM(start_mrr) AS start_mrr,
SUM(CASE WHEN end_mrr = 0 THEN start_mrr ELSE 0 END) AS churn,
SUM(CASE WHEN end_mrr > 0 AND end_mrr < start_mrr
THEN start_mrr - end_mrr ELSE 0 END) AS contraction,
SUM(CASE WHEN end_mrr > start_mrr
THEN end_mrr - start_mrr ELSE 0 END) AS expansion,
ROUND(100.0 * SUM(LEAST(end_mrr, start_mrr)) / SUM(start_mrr), 1) AS grr_pct,
ROUND(100.0 * SUM(end_mrr) / SUM(start_mrr), 1) AS nrr_pct
FROM moved
GROUP BY ROLLUP (segment);
핵심은 LEAST(end_mrr, start_mrr)다. 고객별로 기간 말 매출이 시작 매출을 넘는 부분을 잘라 내면 확장이 빠진 값이 되고, 이 값의 합이 GRR의 분자다. NRR의 분자는 코호트 고객의 기간 말 매출을 그대로 더한 값이다. ROLLUP을 걸어 두면 고객 규모별 값과 전체 합계가 한 번에 나온다. 합계 행은 segment가 NULL로 표시되므로, segment 값이 비어 있는 고객이 있다면 GROUPING(segment)로 합계 행을 구분한다.
이 방식에는 한계가 있다. 시작과 끝 두 시점만 비교하므로 고객 한 명 안에서 일어난 움직임이 상쇄된다. 예를 들어 한 상품은 줄이고 다른 상품을 늘린 고객은 순증분만 확장으로 잡힌다. 기간 중 해지했다가 다시 돌아온 고객도 유지된 것으로 보인다. 가격 인상분을 따로 떼어 내야 하거나 이런 움직임까지 구분해야 한다면, 스냅샷 대신 이벤트 테이블에서 코호트 고객의 이벤트를 유형별(해지, 축소, 확장, 가격 변경)로 합산하는 방식으로 바꾼다.
지표별로 손댈 수 있는 레버
두 지표는 개선하는 방법도 다르다. GRR이 낮으면 고객을 지키는 쪽을 먼저 손본다. 첫 가치를 경험하기 전에 떠나는 고객을 줄이는 온보딩이 출발점이다. 로그인과 핵심 기능 사용량으로 헬스스코어를 만들어 위험 고객을 미리 찾고, 연간 계약은 갱신 90일 전쯤 먼저 접촉하면 갱신 시점에야 이탈이 드러나는 문제를 앞당길 수 있다. 다운그레이드할 때 이유를 남기게 하면 제품 문제와 가격 문제를 가려낼 수 있다. 카드 만료나 한도 초과로 실패한 결제는 재시도와 고객 알림으로 회수하는 절차(dunning)를 두면 비자발적 이탈을 줄일 수 있다.
GRR이 충분히 높은데 NRR이 100% 근처에 머문다면 확장 구조를 본다. 사용량이나 좌석이 자연스럽게 늘어나는 구조가 과금 모델에 들어 있는지, 한도 도달이나 고급 기능 필요 같은 시점에 상위 플랜을 제안하는지, 기본 플랜과 별도로 살 수 있는 애드온이 있는지를 점검한다. 고객 성공팀이 어떤 신호에서 어떤 제안을 할지 플레이북으로 정리해 두는 것도 여기에 속한다.
함께 보는 관련 지표
Logo Retention은 매출이 아니라 고객 수 기준 유지율이라 소액 고객 이탈을 잡아낸다. CAC 회수기간은 고객 한 명을 데려오는 데 쓴 비용을 몇 개월 만에 회수하는지 나타내고, LTV는 고객 한 명이 관계가 끝날 때까지 가져다주는 총매출 또는 이익의 추정값이다. 유지율이 높을수록 LTV는 커진다.
정리
GRR은 기존 고객 기반이 얼마나 새는지, NRR은 그 위에서 확장이 얼마나 일어나는지를 말한다. 리포트에는 두 숫자를 항상 쌍으로 넣고, 기준 기간과 고객 규모별 값을 함께 적는 것을 권한다. 어느 수준이 좋은지는 업종, 과금 모델, 고객 규모에 따라 다르므로 다른 회사 숫자보다 자기 코호트의 추세를 먼저 본다. 데이터 팀이라면 이탈, 다운그레이드, 확장을 처음부터 분리해서 쌓아 두는 것이 두 지표를 같은 기준으로 뽑는 가장 확실한 방법이다.