시스템아키텍처추천
기술 스타트업의 확장 가능한 아키텍처: 작게 시작해서 크게 성장하는 개발 설계
스타트업이 오버엔지니어링을 피하면서도 확장 가능한 시스템을 구축하는 아키텍처 설계 전략과 단계별 진화 방법.
서론: 오버엔지니어링의 함정
"우리는 유니콘이 될 거니까 처음부터 마이크로서비스로 가자!"
6개월 후, 이 스타트업은 복잡한 시스템 유지보수에 지쳐 폐업했습니다.
스타트업 아키텍처의 딜레마:
- 너무 단순하면? → 성장 시 전면 재개발
- 너무 복잡하면? → 개발 속도 저하, 비용 폭증
- 정답은? → 진화 가능한 아키텍처
실제 통계:
- 스타트업의 90%는 예상 트래픽의 10%도 못 받음
- 하지만 성공한 10%는 100배 이상 성장
- 재개발로 인한 평균 손실: 6개월, 10억원
스타트업 성장 단계별 아키텍처 진화
Stage 1: MVP (0-1,000 사용자)
목표: 빠른 개발과 검증
├── 사용자: 0-1,000명
├── 트래픽: 100 req/min
├── 데이터: < 1GB
└── 팀 규모: 1-3명
아키텍처:
┌─────────────────────┐
│ Monolithic App │
│ (Next.js + API) │
└──────────┬──────────┘
│
┌──────▼──────┐
│ PostgreSQL │
│ (Supabase)│
└─────────────┘
기술 스택:
├── Frontend: Next.js
├── Backend: Next.js API Routes
├── Database: PostgreSQL (Supabase)
├── Hosting: Vercel
└── 비용: 월 0-10만원
코드 구조 예시:
// pages/api/users/[id].ts - 단순한 모놀리식 API
export default async function handler(req, res) {
const { id } = req.query
if (req.method === 'GET') {
const user = await db.user.findUnique({ where: { id } })
return res.json(user)
}
if (req.method === 'PUT') {
const user = await db.user.update({
where: { id },
data: req.body
})
return res.json(user)
}
}
Stage 2: Product-Market Fit (1,000-10,000 사용자)
목표: 안정성과 성능 개선
├── 사용자: 1,000-10,000명
├── 트래픽: 1,000 req/min
├── 데이터: 1-100GB
└── 팀 규모: 3-10명
아키텍처:
┌──────────────┐ ┌──────────────┐
│ Frontend │────▶│ Backend │
│ (Next.js) │ │ (Node.js) │
└──────────────┘ └──────┬───────┘
│
┌──────────▼──────────┐
│ │
┌────▼────┐ ┌─────▼────┐
│Primary DB│────────▶│ Redis │
│(PostgreSQL) │ Cache │
└─────────┘ └──────────┘
기술 스택:
├── Frontend: Next.js (Vercel)
├── Backend: Express.js (AWS EC2)
├── Database: PostgreSQL (RDS)
├── Cache: Redis
├── CDN: CloudFront
└── 비용: 월 50-200만원
개선된 코드 구조:
// services/userService.ts - 서비스 레이어 분리
class UserService {
async getUser(id: string) {
// 캐시 먼저 확인
const cached = await redis.get(`user:${id}`)
if (cached) return JSON.parse(cached)
// DB 조회
const user = await db.user.findUnique({ where: { id } })
// 캐시 저장
await redis.setex(`user:${id}`, 3600, JSON.stringify(user))
return user
}
async updateUser(id: string, data: any) {
const user = await db.user.update({ where: { id }, data })
// 캐시 무효화
await redis.del(`user:${id}`)
return user
}
}
Stage 3: 스케일업 (10,000-100,000 사용자)
목표: 수평 확장과 고가용성
├── 사용자: 10,000-100,000명
├── 트래픽: 10,000 req/min
├── 데이터: 100GB-1TB
└── 팀 규모: 10-30명
아키텍처:
┌─────────────┐
│Load Balancer│
└──────┬──────┘
│
┌───────────┼───────────┐
│ │ │
┌───▼───┐ ┌────▼───┐ ┌─────▼───┐
│ API │ │ API │ │ API │
│Server1│ │ Server2│ │ Server3 │
└───┬───┘ └────┬───┘ └─────┬───┘
│ │ │
└──────────┼────────────┘
│
┌──────────▼──────────┐
│ Message Queue │
│ (RabbitMQ/SQS) │
└──────────┬──────────┘
│
┌──────────▼──────────┐
│ Worker Nodes │
└─────────────────────┘
데이터 레이어:
├── Primary DB: PostgreSQL (Master)
├── Read Replicas: 2-3개
├── Cache: Redis Cluster
├── Search: Elasticsearch
└── File Storage: S3
비용: 월 500-2,000만원
마이크로서비스 준비:
// 모듈별 분리 시작
// apps/user-service/index.ts
class UserAPI {
@Post('/users')
@RateLimit(100)
async createUser(@Body() data: CreateUserDto) {
// 사용자 생성 로직
const user = await this.userService.create(data)
// 이벤트 발행
await this.eventBus.publish('user.created', user)
return user
}
}
// apps/notification-service/index.ts
class NotificationWorker {
@Subscribe('user.created')
async handleUserCreated(user: User) {
// 환영 이메일 발송
await this.emailService.sendWelcome(user)
// 푸시 알림
await this.pushService.notify(user)
}
}
Stage 4: 하이퍼스케일 (100,000+ 사용자)
목표: 글로벌 확장과 최적화
├── 사용자: 100,000+명
├── 트래픽: 100,000+ req/min
├── 데이터: 1TB+
└── 팀 규모: 30+명
마이크로서비스 아키텍처:
┌────────────────────────────────────────┐
│ API Gateway │
│ (Kong/AWS API GW) │
└────┬──────┬──────┬──────┬──────┬──────┘
│ │ │ │ │
┌────▼──┐┌──▼──┐┌──▼──┐┌──▼──┐┌──▼──┐
│ User ││Auth ││Order││Pay ││Noti │
│Service││Svc ││ Svc ││ Svc ││ Svc │
└───┬───┘└──┬──┘└──┬──┘└──┬──┘└──┬──┘
│ │ │ │ │
└───────┼──────┼──────┼──────┘
│ │ │
┌─────▼──────▼──────▼─────┐
│ Event Stream │
│ (Kafka/Kinesis) │
└─────────────────────────┘
데이터 아키텍처:
├── OLTP: PostgreSQL Sharding
├── OLAP: Data Warehouse (Redshift)
├── NoSQL: DynamoDB/MongoDB
├── Cache: Redis Cluster
├── CDN: Multi-Region CloudFront
└── ML Platform: SageMaker
비용: 월 5,000만원+
확장 가능한 아키텍처 설계 원칙
1. Database-First Design
-- 처음부터 확장을 고려한 스키마 설계
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
-- 샤딩 키 준비
shard_key INT GENERATED ALWAYS AS (hashtext(id::text) % 100) STORED,
email VARCHAR(255) UNIQUE,
created_at TIMESTAMPTZ DEFAULT NOW(),
-- 인덱스 전략
INDEX idx_shard_key (shard_key),
INDEX idx_created_at (created_at)
) PARTITION BY RANGE (created_at);
-- 파티셔닝 준비
CREATE TABLE users_2024_01 PARTITION OF users
FOR VALUES FROM ('2024-01-01') TO ('2024-02-01');
2. Stateless 설계
// ❌ 나쁜 예: 서버에 상태 저장
class BadService {
private cache = new Map() // 서버 메모리에 저장
async getUser(id: string) {
if (this.cache.has(id)) {
return this.cache.get(id)
}
// ...
}
}
// ✅ 좋은 예: 외부 저장소 사용
class GoodService {
async getUser(id: string) {
// Redis 같은 외부 캐시 사용
const cached = await redis.get(`user:${id}`)
if (cached) return JSON.parse(cached)
// ...
}
}
3. API 버전 관리
// API 버저닝 전략
app.use('/api/v1', v1Routes)
app.use('/api/v2', v2Routes)
// GraphQL의 경우 - 필드 deprecation
type User {
id: ID!
name: String!
username: String @deprecated(reason: "Use 'name' instead")
}
// REST API 응답 버전 관리
interface ApiResponse<T> {
version: string
data: T
meta: {
timestamp: number
deprecations?: string[]
}
}
4. 이벤트 기반 아키텍처
// 이벤트 드리븐 설계로 느슨한 결합
class OrderService {
async createOrder(data: CreateOrderDto) {
// 1. 주문 생성
const order = await this.orderRepo.create(data)
// 2. 이벤트 발행 (다른 서비스들이 구독)
await this.eventBus.publish('order.created', {
orderId: order.id,
userId: order.userId,
amount: order.amount,
items: order.items
})
// 3. 동기 처리 불필요
return order
}
}
// 각 서비스는 독립적으로 이벤트 처리
class InventoryService {
@Subscribe('order.created')
async handleOrderCreated(event: OrderCreatedEvent) {
await this.updateInventory(event.items)
}
}
class NotificationService {
@Subscribe('order.created')
async handleOrderCreated(event: OrderCreatedEvent) {
await this.sendOrderConfirmation(event.userId)
}
}
기술 부채 관리 전략
기술 부채 quadrant
긴급도 높음 ↑
│
🔥 A │ B 📋
즉시 │ 계획
수정 │ 수정
─────────┼─────────
📝 C │ D 🗑️
문서화│ 무시
│
└→ 영향도 높음
A (긴급+중요): 보안 취약점, 데이터 손실 위험
B (중요): 성능 병목, 확장성 제한
C (기록): 코드 스멜, 리팩토링 필요
D (무시): 사용하지 않는 코드, 미미한 최적화
리팩토링 타이밍
const refactoringTriggers = {
// 즉시 리팩토링
immediate: [
"보안 취약점 발견",
"데이터 정합성 문제",
"서비스 다운 위험"
],
// 다음 스프린트
nextSprint: [
"코드 중복 30% 이상",
"응답 시간 2초 초과",
"테스트 커버리지 50% 미만"
],
// 분기별 정리
quarterly: [
"사용하지 않는 의존성",
"레거시 API 제거",
"문서 업데이트"
]
}
실제 사례: 성공과 실패
성공 사례: 배달 스타트업 D사
2020년 (MVP):
├── 아키텍처: Laravel 모놀리스
├── 인프라: 단일 서버
├── 비용: 월 20만원
└── 일 주문: 100건
2021년 (성장기):
├── 아키텍처: API/Frontend 분리
├── 인프라: AWS ECS + RDS
├── 비용: 월 500만원
└── 일 주문: 5,000건
2022년 (확장기):
├── 아키텍처: 부분 마이크로서비스
│ ├── 주문 서비스
│ ├── 배달 서비스
│ └── 결제 서비스
├── 인프라: K8s + Multi-AZ
├── 비용: 월 3,000만원
└── 일 주문: 50,000건
2024년 (성숙기):
├── 아키텍처: 완전 마이크로서비스
├── 인프라: Multi-Region
├── 비용: 월 2억원
└── 일 주문: 500,000건
성공 요인:
✅ 단계적 진화
✅ 데이터 중심 의사결정
✅ 기술 부채 관리
실패 사례: 소셜 스타트업 S사
실패 과정:
1. 시작부터 과도한 설계
- 10명 사용자에 K8s 도입
- 5개 마이크로서비스 분리
- 3개 프로그래밍 언어 혼용
2. 결과
- 개발 속도 90% 감소
- 월 운영비 1,000만원
- 6개월 후 자금 소진
교훈:
"Premature optimization is
the root of all evil" - Donald Knuth
모니터링과 관찰성(Observability)
필수 모니터링 지표
# 모니터링 스택
metrics:
application:
- response_time_p99
- error_rate
- throughput
infrastructure:
- cpu_usage
- memory_usage
- disk_io
business:
- daily_active_users
- conversion_rate
- revenue_per_user
# 알림 설정
alerts:
- name: high_error_rate
condition: error_rate > 1%
action: page_oncall
- name: slow_response
condition: p99_latency > 2s
action: slack_notification
- name: low_disk_space
condition: disk_usage > 80%
action: auto_scale
로깅 전략
// 구조화된 로깅
class Logger {
info(message: string, context: any) {
console.log(JSON.stringify({
timestamp: new Date().toISOString(),
level: 'info',
message,
...context,
// 추적을 위한 correlation ID
correlationId: getCorrelationId(),
// 서비스 정보
service: process.env.SERVICE_NAME,
version: process.env.VERSION
}))
}
}
// 사용 예시
logger.info('Order created', {
orderId: order.id,
userId: user.id,
amount: order.amount,
// 성능 추적
duration: Date.now() - startTime
})
비용 최적화 전략
클라우드 비용 절감
const costOptimization = {
// 1. 적절한 인스턴스 타입 선택
compute: {
development: "t3.micro", // 개발: 월 $10
staging: "t3.small", // 스테이징: 월 $20
production: "c5.xlarge", // 운영: 월 $150
// Spot Instance 활용 (70% 절감)
batch: "spot.c5.xlarge"
},
// 2. 자동 스케일링
autoScaling: {
minInstances: 2,
maxInstances: 10,
targetCPU: 70,
scaleDownDelay: 300 // 5분
},
// 3. 예약 인스턴스 (1년 약정 시 40% 절감)
reservedInstances: {
type: "c5.xlarge",
term: "1year",
payment: "partial_upfront"
},
// 4. 데이터 수명 주기 관리
storage: {
hot: "gp3", // 자주 액세스
warm: "gp2", // 가끔 액세스
cold: "glacier" // 아카이브
}
}
데이터베이스 최적화
-- 인덱스 최적화
CREATE INDEX CONCURRENTLY idx_users_email
ON users(email)
WHERE deleted_at IS NULL;
-- 파티셔닝으로 성능 개선
CREATE TABLE orders_2024_01
PARTITION OF orders
FOR VALUES FROM ('2024-01-01') TO ('2024-02-01');
-- 오래된 데이터 아카이빙
INSERT INTO orders_archive
SELECT * FROM orders
WHERE created_at < NOW() - INTERVAL '1 year';
DELETE FROM orders
WHERE created_at < NOW() - INTERVAL '1 year';
마이그레이션 전략
모놀리스 → 마이크로서비스
Step 1: Strangler Fig Pattern
┌─────────────────────────┐
│ Monolith │
│ ┌─────────────────┐ │
│ │ User Module │────┼──→ User Service
│ └─────────────────┘ │ (신규)
│ ┌─────────────────┐ │
│ │ Order Module │ │
│ └─────────────────┘ │
└─────────────────────────┘
Step 2: 점진적 분리
- API Gateway 도입
- 신규 기능은 별도 서비스로
- 기존 모듈 순차 이전
Step 3: 데이터 분리
- 서비스별 DB 분리
- Event Sourcing 도입
- SAGA 패턴으로 트랜잭션 관리
체크리스트: 확장 가능한 아키텍처
설계 단계
- 도메인 경계 명확히 정의
- 데이터 모델 확장성 고려
- API 버전 관리 전략
- 캐싱 전략 수립
- 모니터링 계획
구현 단계
- 12-Factor App 원칙 준수
- 환경별 설정 분리
- 헬스체크 엔드포인트
- 그레이스풀 셧다운
- 로깅 표준화
운영 단계
- CI/CD 파이프라인
- 블루-그린 배포
- 롤백 계획
- 재해 복구 계획
- 비용 모니터링
결론: 진화하는 아키텍처
성공하는 스타트업의 아키텍처는 예측이 아닌 진화의 결과입니다.
핵심 원칙:
- Start Simple: 복잡성은 나중에
- Measure Everything: 데이터 기반 결정
- Evolve Gradually: 한 번에 하나씩
- Plan for Change: 변경은 필연
"The best architecture is the one that lets you defer decisions" - Robert C. Martin
오늘의 최선이 내일의 레거시가 될 수 있습니다. 하지만 그것이 두려워 시작조차 하지 않는다면, 내일은 오지 않을 것입니다.
지금 당장 필요한 것부터 시작하세요. 성장의 신호가 보이면 그때 진화하세요.
인테그래빗의 확장 가능한 아키텍처 설계 서비스
인테그래빗은 MVP부터 유니콘까지, 각 성장 단계에 최적화된 아키텍처 진화를 지원합니다.
- 아키텍처 컨설팅: 현재 상태 진단과 로드맵
- 마이그레이션: 무중단 전환 지원
- 성능 최적화: 비용 50% 절감
[아키텍처 리뷰 신청] [성공 사례 보기]