🏢 토스 기술 스택 분석
토스가 한국 금융을 혁신하며 유니콘으로 성장한 기술 스택 심층 분석 - Node.js, React Native, Kotlin으로 만든 모바일 금융 플랫폼
🏢 토스 기술 스택 분석
간편송금으로 시작해 종합 금융 플랫폼으로 성장한 토스의 기술 스택을 심층 분석합니다.
"금융의 모든 것, 토스에서" - 기술로 금융을 재정의하는 혁신
📊 회사 개요
서비스 규모
- 월간 활성 사용자: 1,400만+ 명
- 월간 거래: 2억+ 건
- 금융 서비스: 40+ 개
- 연간 거래액: 70조원+
- 직원: 1,800명+
엔지니어링 조직
- 개발자: 600명+ (전체의 35%+)
- 문화: "Move Fast, Think Deep"
- 조직: 목적 조직 (Purpose Organization)
- 기술 철학: 사용자 중심, 데이터 기반
기술적 도전과제
- 금융 규제 준수: 엄격한 보안/컴플라이언스
- 대규모 트래픽: 초당 수만 건 거래
- 무중단 서비스: 24/7 가용성
- 빠른 성장: 매년 2배 이상 성장
🏗️ 아키텍처 Overview
시스템 다이어그램
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│Mobile Client│────►│ API │────►│ Service │
│(React Native) │ Gateway │ │ Mesh │
└─────────────┘ └─────────────┘ └─────────────┘
│ │ │
│ ┌─────▼─────┐ ┌─────▼─────┐
│ │ Auth │ │ Payment │
│ │ Service │ │ Core │
│ └───────────┘ └───────────┘
│ │ │
└────────────────────┤ │
┌─────▼─────┐ ┌─────▼─────┐
│ Event │ │ Data │
│ Stream │ │ Platform │
└───────────┘ └───────────┘
핵심 설계 원칙
- Security First: 보안이 최우선
- Mobile First: 모바일 중심 설계
- Microservices: 서비스별 독립 배포
- Event Driven: 이벤트 기반 아키텍처
- Data Driven: 모든 결정은 데이터로
기술 진화
- 2015-2016: 간편송금 MVP
- 2017-2018: 마이크로서비스 전환
- 2019-2020: 토스뱅크, 토스증권
- 2021-2022: 토스페이먼츠 분사
- 2023-현재: 슈퍼앱화, 해외 진출
🧩 기술 스택 상세
Backend 기술
Node.js 기반 마이크로서비스
// 토스의 결제 처리 서비스
import { Injectable } from "@nestjs/common";
import { EventEmitter2 } from "@nestjs/event-emitter";
import { InjectRepository } from "@nestjs/typeorm";
import { DataSource, EntityManager } from "typeorm";
@Injectable()
export class PaymentService {
constructor(
@InjectRepository(Payment)
private paymentRepository: Repository<Payment>,
private eventEmitter: EventEmitter2,
private dataSource: DataSource,
private fraudDetectionService: FraudDetectionService,
private notificationService: NotificationService,
) {}
async processPayment(request: PaymentRequest): Promise<PaymentResult> {
// 분산 트랜잭션 시작
const queryRunner = this.dataSource.createQueryRunner();
await queryRunner.connect();
await queryRunner.startTransaction("SERIALIZABLE");
try {
// 1. 멱등성 체크
const existingPayment = await this.checkIdempotency(
request.idempotencyKey,
);
if (existingPayment) {
return existingPayment;
}
// 2. 사기 탐지
const fraudCheck = await this.fraudDetectionService.analyze({
userId: request.userId,
amount: request.amount,
recipient: request.recipient,
deviceInfo: request.deviceInfo,
behaviorPattern: await this.getUserBehaviorPattern(request.userId),
});
if (fraudCheck.isHighRisk) {
await this.handleHighRiskTransaction(request, fraudCheck);
throw new PaymentSecurityException("Security check required");
}
// 3. 계좌 잔액 확인 및 보류
const account = await queryRunner.manager.findOne(Account, {
where: { userId: request.userId },
lock: { mode: "pessimistic_write" },
});
if (account.balance.lessThan(request.amount)) {
throw new InsufficientBalanceException();
}
// 4. 결제 생성
const payment = await queryRunner.manager.save(Payment, {
id: generateUUID(),
userId: request.userId,
amount: request.amount,
currency: request.currency,
recipient: request.recipient,
status: PaymentStatus.PENDING,
idempotencyKey: request.idempotencyKey,
metadata: request.metadata,
});
// 5. 잔액 차감
account.balance = account.balance.minus(request.amount);
await queryRunner.manager.save(account);
// 6. 외부 결제 처리
const externalResult = await this.processExternalPayment(payment);
// 7. 결제 완료 처리
payment.status = PaymentStatus.COMPLETED;
payment.externalId = externalResult.transactionId;
payment.completedAt = new Date();
await queryRunner.manager.save(payment);
// 8. 트랜잭션 커밋
await queryRunner.commitTransaction();
// 9. 이벤트 발행
await this.eventEmitter.emitAsync("payment.completed", {
paymentId: payment.id,
userId: payment.userId,
amount: payment.amount,
timestamp: payment.completedAt,
});
// 10. 알림 발송
await this.notificationService.sendPaymentNotification(payment);
return {
paymentId: payment.id,
status: payment.status,
amount: payment.amount,
completedAt: payment.completedAt,
};
} catch (error) {
await queryRunner.rollbackTransaction();
// 보상 트랜잭션
await this.compensateFailedPayment(request, error);
throw error;
} finally {
await queryRunner.release();
}
}
// 실시간 사기 탐지
private async getUserBehaviorPattern(
userId: string,
): Promise<BehaviorPattern> {
const recentTransactions = await this.paymentRepository.find({
where: { userId },
order: { createdAt: "DESC" },
take: 100,
});
return {
averageAmount: this.calculateAverage(recentTransactions),
frequency: this.calculateFrequency(recentTransactions),
commonRecipients: this.extractCommonRecipients(recentTransactions),
unusualPatterns: await this.detectAnomalies(recentTransactions),
};
}
// 분산 락을 이용한 동시성 제어
async transferMoney(
fromUserId: string,
toUserId: string,
amount: Money,
): Promise<TransferResult> {
// Redis 분산 락 획득
const lockKeys = [`account:${fromUserId}`, `account:${toUserId}`].sort(); // 데드락 방지를 위한 정렬
const locks = await Promise.all(
lockKeys.map((key) => this.redisLock.acquire(key, 5000)),
);
try {
// 트랜잭션 실행
return await this.executeTransfer(fromUserId, toUserId, amount);
} finally {
// 락 해제
await Promise.all(locks.map((lock) => lock.release()));
}
}
}
// 이벤트 기반 데이터 동기화
@Injectable()
export class DataSyncService {
constructor(
private readonly kafkaProducer: KafkaProducer,
private readonly elasticsearchService: ElasticsearchService,
) {}
@OnEvent("payment.completed")
async handlePaymentCompleted(event: PaymentCompletedEvent) {
// 1. 데이터 웨어하우스 동기화
await this.kafkaProducer.send({
topic: "payment-events",
messages: [
{
key: event.paymentId,
value: JSON.stringify({
eventType: "PAYMENT_COMPLETED",
timestamp: event.timestamp,
data: event,
}),
},
],
});
// 2. 검색 인덱스 업데이트
await this.elasticsearchService.index({
index: "payments",
id: event.paymentId,
body: {
userId: event.userId,
amount: event.amount,
timestamp: event.timestamp,
searchableText: this.generateSearchableText(event),
},
});
// 3. 실시간 대시보드 업데이트
await this.updateRealtimeDashboard(event);
}
}
Kotlin 기반 금융 코어
// 토스뱅크 계좌 시스템
@Service
@Transactional
class AccountService(
private val accountRepository: AccountRepository,
private val transactionRepository: TransactionRepository,
private val eventPublisher: ApplicationEventPublisher,
private val auditService: AuditService
) {
suspend fun createAccount(request: CreateAccountRequest): Account {
// 본인 인증 확인
val identity = verifyIdentity(request.userId)
require(identity.isVerified) { "Identity verification required" }
// 중복 계좌 체크
val existingAccount = accountRepository.findByUserId(request.userId)
if (existingAccount != null) {
throw DuplicateAccountException("Account already exists")
}
// 계좌 생성
val account = Account(
id = generateAccountId(),
userId = request.userId,
accountNumber = generateAccountNumber(),
balance = Money.ZERO,
currency = Currency.KRW,
status = AccountStatus.ACTIVE,
type = request.accountType,
createdAt = Instant.now()
)
// 저장
val savedAccount = accountRepository.save(account)
// 이벤트 발행
eventPublisher.publishEvent(
AccountCreatedEvent(
accountId = savedAccount.id,
userId = savedAccount.userId,
accountNumber = savedAccount.accountNumber
)
)
// 감사 로그
auditService.log(
AuditLog(
action = "ACCOUNT_CREATED",
entityId = savedAccount.id,
userId = request.userId,
details = mapOf(
"accountType" to request.accountType,
"accountNumber" to savedAccount.accountNumber
)
)
)
return savedAccount
}
suspend fun deposit(accountId: String, amount: Money): Transaction {
return withTimeout(5000) { // 5초 타임아웃
val account = accountRepository.findByIdWithLock(accountId)
?: throw AccountNotFoundException(accountId)
// 입금 한도 체크
val dailyDeposit = transactionRepository
.sumDailyDeposits(accountId, LocalDate.now())
if (dailyDeposit + amount > DAILY_DEPOSIT_LIMIT) {
throw DepositLimitExceededException(
"Daily deposit limit exceeded"
)
}
// 잔액 업데이트
val previousBalance = account.balance
account.balance = account.balance + amount
accountRepository.save(account)
// 거래 내역 생성
val transaction = Transaction(
id = generateTransactionId(),
accountId = accountId,
type = TransactionType.DEPOSIT,
amount = amount,
balanceBefore = previousBalance,
balanceAfter = account.balance,
description = "Deposit",
status = TransactionStatus.COMPLETED,
createdAt = Instant.now()
)
transactionRepository.save(transaction)
// 실시간 알림
sendDepositNotification(account.userId, amount)
transaction
}
}
// 이체 트랜잭션
@Transactional(isolation = Isolation.SERIALIZABLE)
suspend fun transfer(
fromAccountId: String,
toAccountId: String,
amount: Money,
description: String? = null
): TransferResult = coroutineScope {
// 계좌 조회 (순서 정렬로 데드락 방지)
val accountIds = listOf(fromAccountId, toAccountId).sorted()
val accounts = accountRepository.findByIdsWithLock(accountIds)
val fromAccount = accounts.find { it.id == fromAccountId }
?: throw AccountNotFoundException(fromAccountId)
val toAccount = accounts.find { it.id == toAccountId }
?: throw AccountNotFoundException(toAccountId)
// 잔액 확인
if (fromAccount.balance < amount) {
throw InsufficientBalanceException(
"Insufficient balance: ${fromAccount.balance}"
)
}
// 이체 한도 확인
checkTransferLimit(fromAccount, amount)
// 잔액 이동
fromAccount.balance -= amount
toAccount.balance += amount
// 저장
accountRepository.saveAll(listOf(fromAccount, toAccount))
// 거래 내역 생성 (병렬 처리)
val (withdrawalTx, depositTx) = awaitAll(
async {
createTransaction(
fromAccount,
TransactionType.WITHDRAWAL,
-amount,
description ?: "Transfer to ${toAccount.accountNumber}"
)
},
async {
createTransaction(
toAccount,
TransactionType.DEPOSIT,
amount,
description ?: "Transfer from ${fromAccount.accountNumber}"
)
}
)
TransferResult(
transferId = generateTransferId(),
fromTransaction = withdrawalTx,
toTransaction = depositTx,
amount = amount,
completedAt = Instant.now()
)
}
}
// 실시간 거래 모니터링
@Component
class TransactionMonitor(
private val redisTemplate: RedisTemplate<String, String>,
private val alertService: AlertService
) {
private val suspiciousPatterns = listOf(
// 연속된 소액 이체
Pattern { transactions ->
transactions.size >= 10 &&
transactions.all { it.amount < Money(10000, Currency.KRW) } &&
transactions.distinctBy { it.toAccountId }.size == 1
},
// 새벽 시간 대량 이체
Pattern { transactions ->
val nightTransactions = transactions.filter {
val hour = it.createdAt.atZone(ZoneId.of("Asia/Seoul")).hour
hour in 0..6
}
nightTransactions.sumOf { it.amount } > Money(10000000, Currency.KRW)
},
// 급격한 패턴 변화
Pattern { transactions ->
val recent = transactions.take(10)
val historical = transactions.drop(10).take(90)
val recentAvg = recent.map { it.amount }.average()
val historicalAvg = historical.map { it.amount }.average()
recentAvg > historicalAvg * 10
}
)
@EventListener
fun onTransaction(event: TransactionEvent) {
runBlocking {
// 최근 거래 내역 조회
val recentTransactions = getRecentTransactions(event.userId, 100)
// 패턴 분석
val detectedPatterns = suspiciousPatterns
.filter { pattern -> pattern.matches(recentTransactions) }
if (detectedPatterns.isNotEmpty()) {
// 위험도 계산
val riskScore = calculateRiskScore(detectedPatterns, recentTransactions)
if (riskScore > 0.7) {
// 거래 차단
blockUserTransactions(event.userId)
// 알림 발송
alertService.sendHighRiskAlert(
userId = event.userId,
riskScore = riskScore,
patterns = detectedPatterns
)
}
}
}
}
}
Mobile 기술 (React Native)
크로스 플랫폼 앱
// 토스 React Native 앱
import React, { useEffect, useState } from "react";
import {
View,
Text,
TouchableOpacity,
Animated,
PanResponder,
} from "react-native";
import BiometricAuth from "react-native-biometrics";
import { useRecoilState } from "recoil";
// 생체 인증 Hook
export const useBiometricAuth = () => {
const [isAuthenticated, setIsAuthenticated] = useRecoilState(authState);
const authenticate = async (): Promise<boolean> => {
try {
const { available, biometryType } =
await BiometricAuth.isSensorAvailable();
if (!available) {
throw new Error("Biometric authentication not available");
}
const { success, signature } = await BiometricAuth.createSignature({
promptMessage: "토스 앱 잠금해제",
cancelButtonText: "취소",
});
if (success) {
// 서버 검증
const verified = await verifyBiometricSignature(signature);
setIsAuthenticated(verified);
return verified;
}
return false;
} catch (error) {
console.error("Biometric auth failed:", error);
return false;
}
};
return { authenticate, isAuthenticated };
};
// 송금 화면 컴포넌트
export const TransferScreen: React.FC = () => {
const [amount, setAmount] = useState("");
const [recipient, setRecipient] = useState<User | null>(null);
const slideAnim = useRef(new Animated.Value(0)).current;
const { authenticate } = useBiometricAuth();
// 스와이프하여 송금 제스처
const panResponder = useRef(
PanResponder.create({
onStartShouldSetPanResponder: () => true,
onPanResponderMove: (_, gestureState) => {
if (gestureState.dx > 0) {
slideAnim.setValue(gestureState.dx);
}
},
onPanResponderRelease: (_, gestureState) => {
if (gestureState.dx > 200) {
// 송금 실행
executeTransfer();
} else {
// 원위치로
Animated.spring(slideAnim, {
toValue: 0,
useNativeDriver: true,
}).start();
}
},
})
).current;
const executeTransfer = async () => {
try {
// 생체 인증
const authenticated = await authenticate();
if (!authenticated) {
showError("인증에 실패했습니다");
return;
}
// 송금 API 호출
const result = await transferAPI.send({
toUserId: recipient.id,
amount: parseAmount(amount),
currency: "KRW",
});
// 성공 애니메이션
await showSuccessAnimation();
// 결과 화면으로 이동
navigation.navigate("TransferComplete", { result });
} catch (error) {
handleTransferError(error);
}
};
return (
<SafeAreaView style={styles.container}>
<TransferHeader recipient={recipient} />
<AmountInput value={amount} onChange={setAmount} currency="KRW" />
<View style={styles.swipeContainer}>
<Animated.View
{...panResponder.panHandlers}
style={[
styles.swipeButton,
{
transform: [{ translateX: slideAnim }],
},
]}
>
<Text style={styles.swipeText}>밀어서 송금</Text>
</Animated.View>
</View>
<RecentTransfers onSelectRecipient={setRecipient} />
</SafeAreaView>
);
};
// 실시간 자산 현황
export const AssetDashboard: React.FC = () => {
const [assets, setAssets] = useState<Asset[]>([]);
const [totalValue, setTotalValue] = useState(0);
useEffect(() => {
// WebSocket 연결로 실시간 업데이트
const ws = new WebSocket("wss://api.toss.im/assets/stream");
ws.onmessage = (event) => {
const update = JSON.parse(event.data);
switch (update.type) {
case "BALANCE_CHANGE":
updateAssetBalance(update.assetId, update.newBalance);
break;
case "PRICE_CHANGE":
updateAssetPrice(update.assetId, update.newPrice);
break;
case "NEW_TRANSACTION":
addTransaction(update.transaction);
break;
}
};
return () => ws.close();
}, []);
return (
<ScrollView style={styles.dashboard}>
<TotalAssetCard value={totalValue} />
<SectionTitle>내 자산</SectionTitle>
{assets.map((asset) => (
<AssetCard
key={asset.id}
asset={asset}
onPress={() => navigateToAssetDetail(asset)}
/>
))}
<SectionTitle>투자 상품</SectionTitle>
<InvestmentProducts />
<InsightCard
title="이번 달 소비 분석"
insight={generateSpendingInsight()}
/>
</ScrollView>
);
};
// 보안 모듈
class SecurityModule {
private static instance: SecurityModule;
private jailbreakDetected = false;
private appTampered = false;
static getInstance(): SecurityModule {
if (!this.instance) {
this.instance = new SecurityModule();
}
return this.instance;
}
async initialize() {
// 탈옥/루팅 탐지
this.jailbreakDetected = await this.detectJailbreak();
// 앱 무결성 검증
this.appTampered = await this.verifyAppIntegrity();
// 보안 위협 감지 시 종료
if (this.jailbreakDetected || this.appTampered) {
Alert.alert("보안 경고", "보안 위험이 감지되어 앱을 종료합니다.", [
{ text: "확인", onPress: () => RNExitApp.exitApp() },
]);
}
// SSL Pinning 설정
await this.setupSSLPinning();
// 화면 캡처 방지
this.preventScreenCapture();
}
private async detectJailbreak(): Promise<boolean> {
// iOS
if (Platform.OS === "ios") {
const suspiciousPaths = [
"/Applications/Cydia.app",
"/Library/MobileSubstrate/MobileSubstrate.dylib",
"/bin/bash",
"/usr/sbin/sshd",
"/etc/apt",
];
for (const path of suspiciousPaths) {
if (await RNFS.exists(path)) {
return true;
}
}
}
// Android
if (Platform.OS === "android") {
const rootBinaries = [
"/system/app/Superuser.apk",
"/sbin/su",
"/system/bin/su",
"/system/xbin/su",
"/data/local/xbin/su",
];
for (const binary of rootBinaries) {
if (await RNFS.exists(binary)) {
return true;
}
}
}
return false;
}
}
데이터 플랫폼
실시간 분석 시스템
# 토스 데이터 파이프라인
from pyspark.sql import SparkSession
from pyspark.sql.functions import *
from pyspark.sql.types import *
import mlflow
class TossDataPipeline:
def __init__(self):
self.spark = SparkSession.builder \
.appName("TossRealtimeAnalytics") \
.config("spark.sql.adaptive.enabled", "true") \
.config("spark.sql.adaptive.coalescePartitions.enabled", "true") \
.getOrCreate()
def process_transaction_stream(self):
# Kafka 스트림 읽기
transaction_stream = self.spark \
.readStream \
.format("kafka") \
.option("kafka.bootstrap.servers", "kafka-cluster:9092") \
.option("subscribe", "transactions") \
.option("startingOffsets", "latest") \
.load()
# 스키마 정의
transaction_schema = StructType([
StructField("transaction_id", StringType()),
StructField("user_id", StringType()),
StructField("amount", DoubleType()),
StructField("currency", StringType()),
StructField("type", StringType()),
StructField("timestamp", TimestampType()),
StructField("merchant_id", StringType()),
StructField("category", StringType())
])
# 파싱 및 변환
parsed_stream = transaction_stream \
.select(from_json(col("value").cast("string"), transaction_schema).alias("data")) \
.select("data.*") \
.withWatermark("timestamp", "10 minutes")
# 실시간 집계
# 1. 사용자별 지출 패턴
user_spending = parsed_stream \
.groupBy(
window("timestamp", "1 hour"),
"user_id",
"category"
) \
.agg(
sum("amount").alias("total_spent"),
count("transaction_id").alias("transaction_count"),
avg("amount").alias("avg_transaction")
)
# 2. 이상 거래 탐지
anomaly_detection = parsed_stream \
.groupBy(
window("timestamp", "5 minutes"),
"user_id"
) \
.agg(
sum("amount").alias("total_amount"),
count("transaction_id").alias("count"),
stddev("amount").alias("amount_stddev")
) \
.filter(
(col("total_amount") > 10000000) | # 1천만원 이상
(col("count") > 50) | # 5분간 50건 이상
(col("amount_stddev") > 1000000) # 표준편차 100만원 이상
)
# 결과 저장
# 1. 실시간 대시보드용 Redis
user_spending.writeStream \
.foreachBatch(lambda df, epoch_id: self.write_to_redis(df, "user_spending")) \
.outputMode("update") \
.start()
# 2. 이상 거래 알림
anomaly_detection.writeStream \
.foreachBatch(lambda df, epoch_id: self.send_anomaly_alerts(df)) \
.outputMode("update") \
.start()
# 3. 데이터 웨어하우스 저장
parsed_stream.writeStream \
.format("delta") \
.option("checkpointLocation", "/checkpoints/transactions") \
.option("path", "/data/warehouse/transactions") \
.partitionBy("timestamp") \
.start()
self.spark.streams.awaitAnyTermination()
def train_fraud_detection_model(self):
# 학습 데이터 로드
training_data = self.spark.read \
.format("delta") \
.load("/data/warehouse/transactions") \
.filter(col("label").isNotNull())
# 특징 엔지니어링
feature_data = training_data \
.withColumn("hour_of_day", hour("timestamp")) \
.withColumn("day_of_week", dayofweek("timestamp")) \
.withColumn("amount_log", log(col("amount") + 1)) \
.withColumn("user_avg_amount",
avg("amount").over(Window.partitionBy("user_id"))) \
.withColumn("amount_deviation",
col("amount") - col("user_avg_amount"))
# ML 파이프라인
from pyspark.ml import Pipeline
from pyspark.ml.feature import VectorAssembler, StandardScaler
from pyspark.ml.classification import GBTClassifier
assembler = VectorAssembler(
inputCols=[
"amount_log", "hour_of_day", "day_of_week",
"amount_deviation", "transaction_count_1h"
],
outputCol="features"
)
scaler = StandardScaler(
inputCol="features",
outputCol="scaled_features"
)
gbt = GBTClassifier(
featuresCol="scaled_features",
labelCol="is_fraud",
maxDepth=5,
maxIter=100
)
pipeline = Pipeline(stages=[assembler, scaler, gbt])
# 모델 학습
model = pipeline.fit(feature_data)
# MLflow로 모델 저장
with mlflow.start_run():
mlflow.spark.log_model(model, "fraud_detection_model")
mlflow.log_metric("auc", 0.95)
mlflow.log_metric("precision", 0.92)
mlflow.log_metric("recall", 0.89)
return model
기술 스택 요약:
- Backend: Node.js (TypeScript), Kotlin, Python
- Frontend: React Native, React
- 데이터베이스: PostgreSQL, MongoDB, Redis
- 메시지큐: Kafka, RabbitMQ
- 인프라: AWS, Kubernetes
- 데이터: Spark, Airflow, Delta Lake
- 모니터링: Datadog, Sentry
- 보안: Vault, AWS KMS
💡 핵심 기술 인사이트
1. 모바일 퍼스트 전략
- React Native로 빠른 개발
- 네이티브 수준 성능
- 일관된 사용자 경험
2. 마이크로서비스 아키텍처
- 서비스별 독립 배포
- 장애 격리
- 빠른 개발 속도
3. 실시간 데이터 처리
- 스트리밍 아키텍처
- 실시간 이상 탐지
- 즉각적인 인사이트
📈 성과 & 지표
기술적 성과
- 앱 구동 시간: < 1초
- API 응답시간: P50 < 100ms
- 시스템 가용성: 99.99%
- 일일 배포: 50+ 회
- 장애 복구시간: < 5분
비즈니스 영향
- MAU: 1,400만+ (한국 경제활동인구 50%+)
- 누적 송금액: 180조원+
- 기업 가치: 8조원+ (유니콘)
- 금융상품 가입자: 500만+
🎓 토스에서 배울 점
✅ 적용 가능한 패턴
- 간단한 UX: 복잡한 금융을 쉽게
- 빠른 반복: 주간 단위 릴리즈
- 데이터 기반: A/B 테스트 문화
- 보안 우선: 제로 트러스트
- 목적 조직: 자율과 책임
❌ 주의사항
- 규제 대응: 금융 규제 변화
- 보안 요구사항: 극도로 높은 기준
- 확장성: 급성장에 따른 부담
- 기술 부채: 빠른 성장의 부작용
📚 추천 리소스
🔮 미래 전망
현재 집중 분야
- 토스뱅크: 인터넷 전문은행
- 토스증권: 모바일 투자
- 토스페이: B2B 결제
- 해외 진출: 베트남, 일본
- Web3: 블록체인, 암호화폐
기술 투자 영역
- AI/ML: 개인화 금융 서비스
- 블록체인: DeFi, CBDC
- 오픈뱅킹: API 플랫폼
- 빅데이터: 신용평가 고도화
- 클라우드: 멀티 클라우드
"누구나 쉽게 금융 서비스를 이용할 수 있는 세상을 만들어갑니다."
- 토스 기술 조직
마지막 업데이트: 2025-01-28