🏢 Stripe 기술 스택 분석

33 min read
striperubygojavareactawsmongodbpostgresqlkafkapaymentsfintechdistributed-systemsapi-first

스트라이프가 전 세계 수백만 비즈니스의 결제를 처리하는 기술 스택 심층 분석 - Ruby, Go, Java로 구축한 금융 인프라의 미래

🏢 Stripe 기술 스택 분석

개발자를 위한 결제 인프라를 구축하며 금융의 미래를 만드는 Stripe의 기술 스택을 심층 분석합니다.
"Increase the GDP of the internet" - 인터넷 경제의 기반을 만드는 기술


📊 회사 개요

서비스 규모

  • 연간 결제 처리량: $817B+ (2023)
  • 비즈니스 고객: 수백만 개
  • 서비스 국가: 45+ 개국
  • API 요청: 연간 5,000억+ 건
  • 가용성: 99.999% (Five nines)

엔지니어링 조직

  • 엔지니어: 2,500명+ (전체 직원 8,000+)
  • 문화: "Move fast with stable infrastructure"
  • 원격 우선: 전 세계 분산 팀
  • 기술 철학: API 우선, 개발자 경험 중심

기술적 도전과제

  1. 금융 규제 준수: 각국 규제 대응
  2. 보안: 민감한 금융 데이터 보호
  3. 신뢰성: 다운타임 제로 목표
  4. 확장성: 폭발적 성장 대응

🏗️ 아키텍처 Overview

시스템 다이어그램

┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ API │────►│ Gateway │────►│ Payment │ │ Clients │ │ (Kong) │ │ Engine │ └─────────────┘ └─────────────┘ └─────────────┘ │ │ │ │ ┌─────▼─────┐ ┌─────▼─────┐ │ │Rate Limiter│ │Risk Engine │ │ │ (Redis) │ │ (ML) │ │ └───────────┘ └───────────┘ │ │ │ └────────────────────┤ │ ┌─────▼─────┐ ┌─────▼─────┐ │ Ledger DB │ │ Analytics │ │(PostgreSQL)│ │ (Presto) │ └───────────┘ └───────────┘ 

핵심 설계 원칙

  1. API-First: 모든 기능은 API로
  2. Idempotency: 멱등성 보장
  3. Eventual Consistency: 최종 일관성
  4. Defense in Depth: 다층 보안
  5. Observability: 완전한 가시성

기술 진화

  • 2010-2012: Ruby 모놀리스
  • 2013-2015: 서비스 분리 시작
  • 2016-2018: Go 도입, 마이크로서비스
  • 2019-2021: 글로벌 확장, ML 플랫폼
  • 2022-현재: 임베디드 금융, 암호화폐

🧩 기술 스택 상세

Backend 기술

결제 처리 엔진

# Stripe의 Ruby 기반 결제 처리 module Stripe class PaymentProcessor include Retriable include Idempotent def process_payment(payment_intent) # 멱등성 키 확인 idempotency_key = payment_intent.idempotency_key if result = IdempotencyStore.get(idempotency_key) return result end # 트랜잭션 시작 transaction do # 1. 유효성 검증 validate_payment_intent(payment_intent) # 2. 위험 평가 risk_score = RiskEngine.evaluate(payment_intent) if risk_score.high_risk? return handle_high_risk_payment(payment_intent, risk_score) end # 3. 결제 수단 확인 payment_method = PaymentMethod.find(payment_intent.payment_method_id) verify_payment_method(payment_method) # 4. 잔액 확인 및 보류 amount = payment_intent.amount currency = payment_intent.currency hold = create_balance_hold( payment_method: payment_method, amount: amount, currency: currency ) # 5. 네트워크 요청 (카드사, 은행 등) network_response = process_with_network( payment_method: payment_method, amount: amount, metadata: payment_intent.metadata ) # 6. 결과 처리 case network_response.status when :approved complete_payment(payment_intent, hold, network_response) when :declined release_hold(hold) handle_decline(payment_intent, network_response) when :requires_action handle_3ds_authentication(payment_intent, network_response) else raise UnexpectedNetworkResponse, network_response end end rescue => e # 에러 처리 및 복구 handle_payment_error(payment_intent, e) ensure # 멱등성 결과 저장 IdempotencyStore.set(idempotency_key, result, expires_in: 24.hours) end private def complete_payment(payment_intent, hold, network_response) # 원장에 기록 ledger_entry = Ledger.record( type: :payment, debit_account: payment_intent.customer_account, credit_account: payment_intent.merchant_account, amount: payment_intent.amount, currency: payment_intent.currency, reference: network_response.authorization_code ) # 보류 해제 및 실제 이체 transfer = Transfer.create!( from_hold: hold, to_account: payment_intent.merchant_account, ledger_entry: ledger_entry ) # 이벤트 발행 Events.publish( 'payment.succeeded', payment_intent: payment_intent, transfer: transfer, timestamp: Time.now ) # Webhook 트리거 WebhookDispatcher.dispatch_async( event_type: 'payment_intent.succeeded', account: payment_intent.merchant_account, data: payment_intent.to_webhook_payload ) payment_intent.update!(status: :succeeded) end end end 

Go 기반 고성능 서비스

// Stripe의 Go 기반 리스크 엔진 package risk import ( "context" "time" "github.com/stripe/stripe-go/v72" "github.com/stripe/veneur/trace" ) type RiskEngine struct { mlModels map[string]MLModel ruleEngine *RuleEngine dataStore DataStore cache Cache } type RiskAssessment struct { Score float64 Reasons []string Action RiskAction RequiredActions []string } func (re *RiskEngine) Evaluate(ctx context.Context, payment *Payment) (*RiskAssessment, error) { span := trace.StartSpan("risk.evaluate") defer span.End() // 동시에 여러 신호 수집 signals := make(chan Signal, 10) errors := make(chan error, 10) go re.collectDeviceSignals(ctx, payment, signals, errors) go re.collectBehaviorSignals(ctx, payment, signals, errors) go re.collectNetworkSignals(ctx, payment, signals, errors) go re.collectHistoricalSignals(ctx, payment, signals, errors) // 타임아웃 설정 timeout := time.After(100 * time.Millisecond) collectedSignals := []Signal{} for i := 0; i < 4; i++ { select { case signal := <-signals: collectedSignals = append(collectedSignals, signal) case err := <-errors: // 에러는 로그하지만 계속 진행 span.LogFields(trace.Error(err)) case <-timeout: // 타임아웃 시 수집된 것만으로 진행 break } } // ML 모델 예측 mlScore := re.runMLModels(ctx, payment, collectedSignals) // 규칙 엔진 실행 ruleResults := re.ruleEngine.Evaluate(payment, collectedSignals) // 최종 점수 계산 assessment := re.calculateFinalAssessment(mlScore, ruleResults) // 결과 저장 (비동기) go re.storeAssessment(ctx, payment.ID, assessment) return assessment, nil } func (re *RiskEngine) runMLModels(ctx context.Context, payment *Payment, signals []Signal) float64 { features := re.extractFeatures(payment, signals) // 앙상블 모델 실행 predictions := make([]float64, 0, len(re.mlModels)) for name, model := range re.mlModels { pred, err := model.Predict(features) if err != nil { continue } predictions = append(predictions, pred) } // 가중 평균 return re.weightedAverage(predictions) } // 실시간 사기 탐지 규칙 func (re *RiskEngine) detectVelocityAbuse(customerID string) bool { // 슬라이딩 윈도우로 속도 제한 체크 key := fmt.Sprintf("velocity:%s", customerID) windows := []struct { duration time.Duration limit int }{ {time.Minute, 5}, // 분당 5건 {time.Hour, 20}, // 시간당 20건 {24 * time.Hour, 100}, // 일당 100건 } for _, window := range windows { count, err := re.cache.IncrementWindow(key, window.duration) if err != nil { return true // 에러 시 안전하게 차단 } if count > window.limit { return true } } return false } 

원장 시스템 (Double-Entry Bookkeeping)

// Stripe의 Java 기반 원장 시스템 @Service @Transactional public class LedgerService { private final LedgerRepository ledgerRepository; private final AccountRepository accountRepository; private final AuditLogger auditLogger; public LedgerEntry recordTransaction(TransactionRequest request) { // 원자적 트랜잭션 보장 return transactionTemplate.execute(status -> { // 1. 계정 잠금 (데드락 방지를 위해 ID 순서대로) List<Account> accounts = lockAccountsInOrder( request.getDebitAccountId(), request.getCreditAccountId() ); Account debitAccount = accounts.get(0); Account creditAccount = accounts.get(1); // 2. 잔액 검증 if (!debitAccount.hasSufficientBalance(request.getAmount())) { throw new InsufficientFundsException( debitAccount.getId(), request.getAmount() ); } // 3. 원장 항목 생성 (불변) LedgerEntry entry = LedgerEntry.builder() .id(UUID.randomUUID()) .transactionId(request.getTransactionId()) .debitAccountId(debitAccount.getId()) .creditAccountId(creditAccount.getId()) .amount(request.getAmount()) .currency(request.getCurrency()) .description(request.getDescription()) .metadata(request.getMetadata()) .timestamp(Instant.now()) .build(); // 4. 잔액 업데이트 debitAccount.debit(request.getAmount()); creditAccount.credit(request.getAmount()); // 5. 영속화 ledgerRepository.save(entry); accountRepository.saveAll(Arrays.asList(debitAccount, creditAccount)); // 6. 감사 로그 auditLogger.log(AuditEvent.builder() .type(AuditEventType.LEDGER_ENTRY_CREATED) .entityId(entry.getId()) .entityType("LedgerEntry") .userId(request.getInitiatorId()) .changes(entry.toMap()) .timestamp(Instant.now()) .build() ); // 7. 이벤트 발행 publishLedgerEvent(entry); return entry; }); } // 일일 정산 및 검증 @Scheduled(cron = "0 0 2 * * *") // 매일 오전 2시 public void runDailyReconciliation() { LocalDate yesterday = LocalDate.now().minusDays(1); // 모든 계정의 데빗/크레딧 합계 계산 BigDecimal totalDebits = ledgerRepository .sumDebitsForDate(yesterday); BigDecimal totalCredits = ledgerRepository .sumCreditsForDate(yesterday); // 복식부기 원칙: 데빗 = 크레딧 if (!totalDebits.equals(totalCredits)) { alertOps( "Ledger imbalance detected", Map.of( "date", yesterday, "debits", totalDebits, "credits", totalCredits, "difference", totalDebits.subtract(totalCredits) ) ); } // 각 계정별 잔액 검증 reconcileAccountBalances(yesterday); } } 

Frontend 기술

React 기반 대시보드

// Stripe Dashboard 컴포넌트 import React, { useEffect, useState } from "react"; import { useStripe } from "@stripe/react-stripe-js"; import { LineChart, BarChart } from "recharts"; interface DashboardProps { accountId: string; } export const StripeDashboard: React.FC<DashboardProps> = ({ accountId }) => { const stripe = useStripe(); const [metrics, setMetrics] = useState<DashboardMetrics>(); const [timeRange, setTimeRange] = useState<TimeRange>("7d"); useEffect(() => { loadDashboardData(); }, [accountId, timeRange]); const loadDashboardData = async () => { // 병렬로 여러 메트릭 로드 const [revenue, transactions, customers, disputes, payouts] = await Promise.all([ fetchRevenueMetrics(timeRange), fetchTransactionMetrics(timeRange), fetchCustomerMetrics(timeRange), fetchDisputeMetrics(timeRange), fetchPayoutSchedule(), ]); setMetrics({ revenue, transactions, customers, disputes, payouts, }); }; return ( <div className="stripe-dashboard"> <Header> <h1>Dashboard</h1> <TimeRangeSelector value={timeRange} onChange={setTimeRange} /> </Header> <MetricsGrid> <MetricCard title="Gross Volume" value={formatCurrency(metrics?.revenue.total)} change={metrics?.revenue.changePercent} sparkline={metrics?.revenue.daily} /> <MetricCard title="Successful Payments" value={metrics?.transactions.successful.toLocaleString()} subtitle={`${metrics?.transactions.successRate}% success rate`} /> <MetricCard title="New Customers" value={metrics?.customers.new.toLocaleString()} change={metrics?.customers.changePercent} /> <MetricCard title="Disputes" value={metrics?.disputes.active} subtitle={`${formatCurrency(metrics?.disputes.amount)} at risk`} variant={metrics?.disputes.active > 0 ? "warning" : "default"} /> </MetricsGrid> <ChartsSection> <RevenueChart data={metrics?.revenue.hourly} /> <PaymentMethodBreakdown data={metrics?.transactions.byMethod} /> <GeographicDistribution data={metrics?.revenue.byCountry} /> </ChartsSection> <PayoutSchedule payouts={metrics?.payouts} /> <RecentActivity /> </div> ); }; // 실시간 활동 피드 const RecentActivity: React.FC = () => { const [activities, setActivities] = useState<Activity[]>([]); useEffect(() => { // WebSocket 연결로 실시간 업데이트 const ws = new WebSocket("wss://dashboard.stripe.com/activity"); ws.onmessage = (event) => { const activity = JSON.parse(event.data); setActivities((prev) => [activity, ...prev].slice(0, 50)); }; return () => ws.close(); }, []); return ( <ActivityFeed> {activities.map((activity) => ( <ActivityItem key={activity.id}> <ActivityIcon type={activity.type} /> <ActivityContent> <ActivityDescription>{activity.description}</ActivityDescription> <ActivityTime> {formatRelativeTime(activity.timestamp)} </ActivityTime> </ActivityContent> <ActivityAmount> {activity.amount && formatCurrency(activity.amount)} </ActivityAmount> </ActivityItem> ))} </ActivityFeed> ); }; // Stripe Elements 통합 export const CheckoutForm: React.FC = () => { const stripe = useStripe(); const elements = useElements(); const [error, setError] = useState<string>(); const [processing, setProcessing] = useState(false); const handleSubmit = async (event: FormEvent) => { event.preventDefault(); if (!stripe || !elements) return; setProcessing(true); // 결제 수단 생성 const { error: methodError, paymentMethod } = await stripe.createPaymentMethod({ type: "card", card: elements.getElement(CardElement)!, billing_details: { name: formData.name, email: formData.email, address: formData.address, }, }); if (methodError) { setError(methodError.message); setProcessing(false); return; } // 서버로 결제 요청 const response = await fetch("/api/create-payment-intent", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ payment_method_id: paymentMethod.id, amount: calculateTotal(), currency: "usd", }), }); const result = await response.json(); if (result.requires_action) { // 3D Secure 인증 필요 const { error: confirmError } = await stripe.confirmCardPayment( result.payment_intent_client_secret ); if (confirmError) { setError(confirmError.message); } } setProcessing(false); }; return ( <form onSubmit={handleSubmit}> <CardElement options={{ style: stripeElementStyles, hidePostalCode: true, }} /> {error && <ErrorMessage>{error}</ErrorMessage>} <Button type="submit" disabled={!stripe || processing}> {processing ? <Spinner /> : `Pay ${formatCurrency(total)}`} </Button> </form> ); }; 

인프라 및 신뢰성

분산 시스템 패턴

# Stripe의 분산 시스템 관리 import asyncio from typing import List, Optional import consul import aioredis class DistributedLock: """분산 잠금 구현""" def __init__(self, redis_client: aioredis.Redis): self.redis = redis_client async def acquire( self, key: str, ttl: int = 30, retry_times: int = 10, retry_delay: float = 0.1 ) -> Optional[str]: """ Redlock 알고리즘 구현 """ token = str(uuid.uuid4()) for _ in range(retry_times): # SET NX EX 원자적 실행 acquired = await self.redis.set( key, token, expire=ttl, exist=False ) if acquired: return token await asyncio.sleep(retry_delay) return None async def release(self, key: str, token: str) -> bool: """ Lua 스크립트로 안전한 해제 """ lua_script = """ if redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("del", KEYS[1]) else return 0 end """ result = await self.redis.eval(lua_script, [key], [token]) return bool(result) class CircuitBreaker: """서킷 브레이커 패턴""" def __init__( self, failure_threshold: int = 5, recovery_timeout: int = 60, expected_exception: type = Exception ): self.failure_threshold = failure_threshold self.recovery_timeout = recovery_timeout self.expected_exception = expected_exception self.failure_count = 0 self.last_failure_time = None self.state = 'closed' # closed, open, half-open async def call(self, func, *args, **kwargs): if self.state == 'open': if self._should_attempt_reset(): self.state = 'half-open' else: raise CircuitOpenError("Circuit breaker is open") try: result = await func(*args, **kwargs) self._on_success() return result except self.expected_exception as e: self._on_failure() raise def _on_success(self): self.failure_count = 0 self.state = 'closed' def _on_failure(self): self.failure_count += 1 self.last_failure_time = time.time() if self.failure_count >= self.failure_threshold: self.state = 'open' class RateLimiter: """토큰 버킷 기반 rate limiter""" def __init__(self, rate: int, burst: int): self.rate = rate # tokens per second self.burst = burst # max tokens self.tokens = burst self.last_update = time.time() self.lock = asyncio.Lock() async def acquire(self, tokens: int = 1) -> bool: async with self.lock: now = time.time() elapsed = now - self.last_update self.last_update = now # 토큰 리필 self.tokens = min( self.burst, self.tokens + elapsed * self.rate ) if self.tokens >= tokens: self.tokens -= tokens return True return False 

보안 및 컴플라이언스

PCI 컴플라이언스

// Rust로 구현된 카드 번호 토큰화 use aes_gcm::{Aes256Gcm, Key, Nonce}; use ring::rand::{SecureRandom, SystemRandom}; pub struct CardTokenizer { encryption_key: Key<Aes256Gcm>, token_vault: TokenVault, hsm_client: HsmClient, } impl CardTokenizer { pub fn tokenize_card(&self, card_number: &str) -> Result<String, TokenizationError> { // 1. 카드 번호 검증 if !self.validate_card_number(card_number) { return Err(TokenizationError::InvalidCardNumber); } // 2. 형식 정규화 let normalized = card_number.chars() .filter(|c| c.is_digit(10)) .collect::<String>(); // 3. 토큰 생성 let token = self.generate_token(&normalized); // 4. HSM에서 암호화 let encrypted = self.hsm_client.encrypt( normalized.as_bytes(), &self.encryption_key )?; // 5. 토큰 볼트에 저장 self.token_vault.store( &token, &encrypted, TokenMetadata { created_at: Utc::now(), last_four: &normalized[normalized.len()-4..], card_brand: detect_card_brand(&normalized), expiry_date: None, // 저장하지 않음 } )?; // 6. 감사 로그 audit_log!("card_tokenized", { "token": &token, "brand": detect_card_brand(&normalized), "timestamp": Utc::now(), }); Ok(token) } fn validate_card_number(&self, number: &str) -> bool { // Luhn 알고리즘 let digits: Vec<u8> = number.chars() .filter_map(|c| c.to_digit(10).map(|d| d as u8)) .collect(); if digits.len() < 13 || digits.len() > 19 { return false; } let checksum: u8 = digits.iter() .rev() .enumerate() .map(|(i, &d)| { if i % 2 == 1 { let doubled = d * 2; if doubled > 9 { doubled - 9 } else { doubled } } else { d } }) .sum(); checksum % 10 == 0 } fn generate_token(&self, seed: &str) -> String { // 형식 보존 암호화 (Format Preserving Encryption) let mut token = String::with_capacity(16); token.push_str("tok_"); // 안전한 난수 생성 let rng = SystemRandom::new(); let mut bytes = [0u8; 12]; rng.fill(&mut bytes).unwrap(); // Base62 인코딩 token.push_str(&base62::encode(&bytes)); token } } 

기술 스택 요약:

  • Backend: Ruby, Go, Java, Rust
  • Frontend: React, TypeScript
  • 데이터베이스: PostgreSQL, MongoDB, Redis
  • 메시지 큐: Kafka, NSQ
  • 인프라: AWS, 자체 데이터센터
  • 모니터링: Veneur, Datadog
  • 보안: HSM, Vault

💡 핵심 기술 인사이트

1. API 설계의 교과서

  • RESTful하면서도 실용적
  • 뛰어난 개발자 경험
  • 포괄적인 문서화

2. 금융 시스템의 신뢰성

  • 복식부기 원장
  • 완벽한 감사 추적
  • 멱등성 보장

3. 글로벌 규모의 보안

  • PCI DSS Level 1
  • 엔드투엔드 암호화
  • 실시간 사기 탐지

📈 성과 & 지표

기술적 성과

  • 가용성: 99.999% (연간 5분 다운타임)
  • API 응답시간: P50 < 100ms
  • 처리 용량: 초당 65,000+ 요청
  • 글로벌 지연시간: < 150ms
  • 보안 사고: 0 (설립 이후)

비즈니스 영향

  • 결제 처리량: 연간 $817B+
  • 개발자 만족도: 96%
  • 통합 시간: 평균 10줄 코드
  • 수익 증가: 고객사 평균 6.7%

🎓 Stripe에서 배울 점

✅ 적용 가능한 패턴

  1. API 우선 설계: 모든 기능을 API로
  2. 멱등성: 분산 시스템 필수
  3. 점진적 마이그레이션: 모놀리스→마이크로서비스
  4. 개발자 경험: 문서와 도구의 중요성
  5. 감사 추적: 모든 변경사항 기록

❌ 주의사항

  1. 규제 복잡성: 각국 금융 법규
  2. 보안 요구사항: 극도로 높은 기준
  3. 레거시 시스템: 금융권 통합 어려움
  4. 다운타임 불가: 24/7 운영 부담

📚 추천 리소스


🔮 미래 전망

현재 집중 분야

  1. Stripe Treasury: 은행 서비스 API
  2. Stripe Capital: 대출 서비스
  3. Stripe Climate: 탄소 제거 투자
  4. Crypto: 암호화폐 결제
  5. Embedded Finance: 금융 인프라화

기술 투자 영역

  • 기계학습: 더 정교한 사기 탐지
  • 블록체인: 국경 간 결제
  • 실시간 결제: 즉시 정산
  • 개발자 도구: 더 나은 DX
  • 글로벌 확장: 더 많은 국가

"우리는 인터넷의 결제 인프라를 구축하여, 모든 비즈니스가 글로벌하게 성장할 수 있도록 돕고 있습니다."

  • Stripe Engineering

마지막 업데이트: 2025-01-28

Found this helpful? Share it with others!
Tweet

🔗 Related Content

You might also be interested in these articles

🏢 company

🏢 Airbnb 기술 스택 분석

에어비앤비가 전 세계 400만 개의 숙소를 연결하는 글로벌 마켓플레이스 기술 스택 심층 분석 - Ruby on Rails에서 Service-Oriented Architecture로의 진화

24 min read
airbnb, ruby+12
Read more
🏢 company

🏢 Canva 기술 스택 분석

캔바가 1억 명 이상의 사용자에게 쉽고 강력한 디자인 도구를 제공하는 기술 스택 심층 분석 - Java, TypeScript, AWS로 구축한 대규모 디자인 플랫폼

32 min read
canva, java+10
Read more
🏢 company

🏢 Netflix 기술 스택 분석

넷플릭스가 전 세계 2.4억 사용자에게 스트리밍 서비스를 제공하는 기술 스택 심층 분석 - 마이크로서비스, AWS, Chaos Engineering의 교과서

9 min read
netflix, microservices+11
Read more
🏢 company

🏢 Uber 기술 스택 분석

우버가 매일 2,500만 건의 라이드를 실시간으로 매칭하는 기술 스택 심층 분석 - Go, Java, Node.js로 구축한 글로벌 실시간 마켓플레이스

15 min read
uber, microservices+12
Read more
🏗️ stack

🏢 Enterprise Microservices Stack

대규모 트래픽과 복잡한 비즈니스 로직을 위한 마이크로서비스 아키텍처 - Go, gRPC, Kubernetes로 구축하는 확장 가능한 시스템

14 min read
go, grpc+13
Read more
🏢 company

🏢 카카오 기술 스택 분석

카카오가 5천만 한국인의 일상을 연결하는 기술 스택 심층 분석 - Java, Kotlin, MySQL로 구축한 국민 메신저부터 슈퍼앱까지

32 min read
kakao, java+9
Read more

Found this helpful?

Help us improve this content by contributing on GitHub or sharing your feedback with the community.