# Stripe가 원하는 것은 숫자 하나뿐

> Clean Markdown view of GeekNews topic #32032. Use the original source for factual precision when an external source URL is present.

## Metadata

- GeekNews HTML: [https://news.hada.io/topic?id=32032](https://news.hada.io/topic?id=32032)
- GeekNews Markdown: [https://news.hada.io/topic/32032.md](https://news.hada.io/topic/32032.md)
- Type: GN+
- Author: [neo](https://news.hada.io/@neo)
- Published: 2026-08-01T18:01:28+09:00
- Updated: 2026-08-01T18:01:28+09:00
- Original source: [blog.exe.dev](https://blog.exe.dev/billable-facts)
- Points: 1
- Comments: 1

## Topic Body

- exe는 제품 로직 곳곳에서 결제 API를 직접 호출하는 대신, 상태 변화를 **청구 가능 사실(billable facts)** 로 기록하고 확정된 상태를 Stripe와 조정함
- 기존 구조에서는 데이터베이스 트랜잭션과 결제 API 호출이 얽혀 **부분 실패**, 비정상 구독 상태, 결제 거절 같은 예외가 제품 흐름까지 흔들었음
- 팀 좌석이 추가되면 상태를 **dirty**로 표시하고, 후속 워커가 비즈니스 규칙에 따라 수량을 계산해 변경된 경우에만 Stripe 구독 수량을 갱신함
- 결제를 분리하면 **신규 팀원 온보딩**이 결제 코드에 의존하지 않고, 좌석 계산 규칙을 바꿔도 초대와 가입 흐름에 영향을 주지 않음
- 같은 조정 구조를 활성 VM과 디스크 사용량 같은 **종량제 청구**와 iOS 인앱 구매에도 적용해, 제품 이벤트는 유지하면서 결제 제공자별 연동만 바꿀 수 있음

---

### 제품 흐름에서 결제 로직 분리하기
- 결제 로직이 일반 비즈니스 로직과 뒤섞이면 청구가 필요한 모든 핵심 경로에 관련 코드가 퍼지고, 가격 구조도 취약해져 변경하기 어려움
- exe는 한 사람이 결제 지식을 독점하지 않고 누구나 관련 코드를 변경할 수 있도록 하되, 복잡한 예외는 전문 담당자가 처리하는 방식을 지향함
- 초기 팀 좌석 청구는 초대 수락부터 결제까지 **하나의 큰 흐름**으로 묶여 있었음
  - 사용자가 초대를 수락하고 계정을 확인한 뒤 팀에 가입함
  - 공유 VM 접근 권한과 요금제에 따른 컴퓨팅 자원을 할당받음
  - 이 과정에서 결제 API까지 호출함
- 이렇게 데이터베이스 변경과 외부 API 호출을 결합하면 한쪽만 성공하는 **부분 실패**가 발생할 수 있음
  - 팀 구독 상태가 비정상적일 수 있음
  - 추가 좌석에 대한 결제가 거절될 수 있음
  - 예외가 쌓일수록 전체 구조가 취약해짐

### 청구 가능 사실과 사후 조정
- **청구 가능 사실**은 특정 상태가 바뀌었음을 나타내는 원자적 작업임
  - 제품 로직을 먼저 실행해 자원의 새 상태를 확정함
  - 이후 확정된 사실을 바탕으로 결제 제공자의 상태를 조정함
  - Stripe에는 그 상태에 도달한 과정이 아니라 **최종 수량**만 필요함
- ## 팀 좌석 조정
  - 초대가 수락되면 팀 좌석 상태를 **dirty**로 표시함
  - 후속 워커가 dirty 상태를 감지하고 비즈니스 규칙에 따라 좌석 증감분을 계산함
  - 수량이 달라진 경우에만 Stripe의 구독 수량을 갱신함
  - 팀원 추가와 결제 코드가 분리되므로 초대 흐름을 다시 작성해도 청구가 함께 깨지지 않으며, 좌석 계산 방식도 독립적으로 바꿀 수 있음
- ## 종량제 청구와 인앱 구매
  - 같은 조정 과정은 모든 **종량제 청구**에 적용됨
    - 시스템이 활성 VM과 디스크 사용량에 관한 사실을 기록함
    - 계량 워커가 이를 결제 제공자의 상태와 조정함
    - 새로운 청구 방식을 추가해도 사실은 그대로 유지되고, 각 API와 조정하는 방법만 달라짐
  - iOS 앱 역시 누군가 인앱 구매로 구독했다는 사실만 전달하고, 실제 결제 상태는 이후 조정함
  - 결제 구조가 다른 구성원도 다룰 수 있는 영역이 되었으며, 초대 흐름 같은 제품 기능 변경이 청구 시스템을 손상시킬 가능성도 낮아짐

## Comments



### Comment 62710

- Author: neo
- Created: 2026-08-01T18:01:29+09:00
- Points: 1

###### [Lobste.rs 의견들](https://lobste.rs/s/ri8bav/stripe_just_wants_number) 
- 글의 핵심인 **변경분을 비동기로 감지해 처리하는 방식**은 결합도를 낮추고 부수 효과를 구현하기에 좋음  
  하지만 LLM은 코드가 곳곳으로 퍼지는 현상을 막기보다 가속하는 도구라서 아키텍처상 부채가 되기 쉬움. 이해하는 속도보다 빠르게 생성되는 코드를 어떻게 검토할 수 있는지도 의문인데, Exe가 코드 검토조차 하지 않는다는 대목은 더욱 두려움
  - 원래도 진지한 적이 별로 없던 기술 업계가 지금은 **유난히 비진지한 시기**를 지나고 있음을 기억해야 함
  - 청구 시스템을 다루는 입장에서 꽤 무서운 접근임. 청구 플랫폼은 여러 **경계가 명확한 컨텍스트**로 구성되는데, LLM은 도메인 경계를 잘 지키지 못하므로 큰 고통을 부를 가능성이 큼
  - “LLM을 써도”가 아니라 “**특히 LLM을 쓰면**” 코드가 더 퍼지는 것 아닌가 싶었음
  - 그 인용문은 Orwell이 아니라 **Upton Sinclair**의 말로 알고 있음

- 이 아키텍처가 흥미롭기는 하지만, 글 앞부분에서 제시한 문제를 어떻게 해결하는지는 불분명함. 결제가 거절되면 모든 리소스의 결제를 보장하기보다 미결제 리소스를 먼저 제공하게 될 것으로 보임  
  청구 작업자가 `declined` 사실을 발행해 리소스를 회수할 수도 있지만, 깔끔한 단방향 흐름과 비교하면 순환 구조가 됨. Exe처럼 월별로 청구하는 컴퓨팅 서비스에는 잘 맞겠지만, 물리 장비를 배송하거나 다른 서비스의 좌석을 재판매하는 사업에서는 받아들이기 어려운 절충안임
  - 제품이 추상적이고 대체 가능한 **소프트웨어 중심 청구 방식**임. 사용량 기반 청구는 과금 이벤트가 발생했다는 분석 데이터를 토대로 하고, 청구 시스템이 이를 집계해 일관된 청구 내역을 만드는 편이 좋음. 과금 이벤트는 여러 곳에서 생길 수 있으므로 제품 코드 밖으로 분리하는 것도 건전함  
    다만 API 호출이나 데이터베이스 트랜잭션 실패, 비정상적인 구독 상태, 좌석 결제 거절을 어떻게 처리하는지는 직접 답하지 않음. 리팩터링으로 분석 수집이 깨지거나, 특정 행을 더 이상 변경 상태로 표시하지 않거나, 새 경로에서 변경 표시를 빠뜨리는 문제도 그대로 남음  
    좌석·무료 할당량 제한, 최대 지출액 설정, 선불 잔액 차감처럼 제품 내부에 존재하는 **청구 관련 상태**도 해결하지 못함. 제품 서비스가 이벤트를 발행하고 청구 서비스가 준수 상태를 다시 제품에 반영할 수 있지만, 그러면 상태를 가진 분산 시스템에 두 행위자가 생김

- Stripe가 숫자 하나만 원하는 것처럼 보이지만, 일정 규모에서는 결제의 **세부 항목**까지 제공하면 교환 수수료를 절감하고 승인율을 높일 수 있음

- Exe가 **코드 검토를 하지 않는 방식**은 신선함. 그렇다면 릴리스와 테스트는 어떤 방식으로 운영하는지 궁금함
