들어가며: 왜 모든 테넌트에 별도 AWS 계정을 쓸까?
SaaS 플랫폼이 성장하면서 가장 먼저 부딪히는 고민은 **테넌트 격리(Isolation)**입니다. 초기에는 하나의 AWS 계정 안에서 IAM 정책과 데이터 파티셔닝으로 멀티 테넌시를 구성하는 게 일반적이에요. 하지만 테넌트 수가 수백, 수천 개로 늘어나면 다음과 같은 문제가 발생합니다.
- 블래스트 반경(Blast Radius): 하나의 설정 실수나 취약점이 여러 테넌트를 동시에 노출시킴
- 서비스 한도(Quota): 단일 계정의 Lambda 동시 실행 한도, API Rate Limit 등을 모든 테넌트가 공유
- 운영 복잡도: '이 리소스가 누구의 것인지' 추적하기 어려움
- 비용 투명성 부족: 공유 인프라 비용을 테넌트별로 정확히 분배하기 어려움
ProGlove는 이 문제를 **'테넌트마다 별도 AWS 계정을 부여'**하는 방식으로 해결했습니다. 현재 약 6,000개 계정 중 50%(약 3,000개)가 활성화되어 있고, 40개 마이크로서비스가 각 계정에 배포되어 총 120,000개 이상의 서비스 인스턴스, 약 100만 개의 Lambda 함수가 운영 중입니다. 운영 인원은 고작 3명이에요.
🧠 핵심 인사이트: '운영팀 규모를 늘리지 않고 테넌트 수를 늘린다'는 역설을 가능하게 만든 건 플랫폼 자동화입니다. 복잡성을 애플리케이션 코드에서 플랫폼 엔지니어링으로 이동시킨 거죠.
이 글은 AWS Architecture Blog에 게시된 6000 AWS accounts, three people, one platform: Lessons learned를 바탕으로, 국내 SaaS 기업이 참고할 만한 실무 인사이트를 정리했습니다.

계정-테넌트 모델의 실제 구현: CI/CD와 관찰 가능성
CI/CD: 한 번의 파이프라인으로 수천 개 계정 배포
가장 큰 난관은 배포 자동화입니다. 하나의 계정에 배포하는 것과 수천 개 계정에 동시에 배포하는 것은 차원이 다른 문제예요. ProGlove는 AWS CodePipeline + CloudFormation StackSets 조합으로 해결했습니다.
# 예시: StackSets를 이용한 대규모 병렬 배포 구성 (실제 ProGlove 구성 단순화)
AWSTemplateFormatVersion: '2010-09-09'
Description: '테넌트 계정에 공통 마이크로서비스 배포를 위한 StackSet'
Parameters:
TenantAccountIds:
Type: CommaDelimitedList
Description: 배포 대상 테넌트 계정 ID 목록
Default: "111111111111,222222222222,333333333333"
Resources:
# 각 테넌트 계정에 동일한 Lambda 함수 배포
TenantServiceStackSet:
Type: AWS::CloudFormation::StackSet
Properties:
StackSetName: !Sub "${AWS::StackName}-tenant-service"
TemplateBody: |
AWSTemplateFormatVersion: '2010-09-09'
Resources:
TenantFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Sub "tenant-${AWS::AccountId}-processor"
Runtime: python3.12
Handler: index.handler
Code:
S3Bucket: my-artifact-bucket
S3Key: tenant-service.zip
Role: !GetAtt TenantExecutionRole.Arn
TenantExecutionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
# 모든 테넌트 계정에 배포 (조직 단위 또는 계정 ID 목록)
DeploymentTargets:
Accounts: !Ref TenantAccountIds
Regions:
- ap-northeast-2
# 중앙 계정에서 StackSet의 관리 역할 수행
PermissionModel: SERVICE_MANAGED
AutoDeployment:
Enabled: true
RetainStacksOnAccountRemoval: false
⚠️ 주의할 점: StackSets는 강력하지만 부분 롤아웃 실패 시 롤백 전략을 명확히 정의해야 합니다. ProGlove는 실패한 계정만 재시도하는 커스텀 재시도 로직을 Step Functions로 구현했다고 해요.
관찰 가능성(Observability): 중앙 집중 vs. 계정 격리
모니터링은 또 다른 난제입니다. 각 계정이 독립적으로 CloudWatch 로그와 메트릭을 생성하면, 운영자는 수천 개의 대시보드를 일일이 볼 수 없어요. ProGlove의 접근법:
- 중앙 집중식 관찰 가능성 플랫폼 도입: 모든 테넌트 계정의 텔레메트리(로그, 메트릭)를 중앙 계정의 서드파티 도구(예: Datadog, Grafana)로 전달
- 태깅 전략 통일: 모든 리소스에
TenantId,Environment,ServiceName태그를 강제하여 필터링과 상관관계 분석 용이 - 알람 중복 방지: 각 계정에 동일한 알람을 복제하지 말고, 스트리밍과 집계(Aggregation) 방식을 사용
# 예시: 중앙 관찰 가능성 계정으로 메트릭 전송 (Python + boto3)
import boto3
import json
from datetime import datetime
def put_metric_to_central_account(tenant_account_id, metric_name, value, unit="Count"):
"""
각 테넌트 계정에서 중앙 모니터링 계정으로 커스텀 메트릭 전송
- tenant_account_id: 소스 계정 ID (태깅용)
- metric_name: 예: 'TenantRequestLatency'
- value: 측정값
"""
# 중앙 계정의 IAM 역할을 AssumeRole
sts_client = boto3.client('sts')
assumed_role = sts_client.assume_role(
RoleArn='arn:aws:iam::CENTRAL_ACCOUNT_ID:role/MetricForwarderRole',
RoleSessionName='TenantMetricForwarder'
)
cloudwatch = boto3.client(
'cloudwatch',
aws_access_key_id=assumed_role['Credentials']['AccessKeyId'],
aws_secret_access_key=assumed_role['Credentials']['SecretAccessKey'],
aws_session_token=assumed_role['Credentials']['SessionToken']
)
cloudwatch.put_metric_data(
Namespace='ProGlove/TenantMetrics',
MetricData=[
{
'MetricName': metric_name,
'Value': value,
'Unit': unit,
'Timestamp': datetime.utcnow(),
'Dimensions': [
{'Name': 'TenantAccountId', 'Value': tenant_account_id}
]
}
]
)

계정-테넌트 모델의 그림자: 비용과 한계
비용: '무료'인 줄 알았던 서비스가 3,000달러로
계정당 약간의 고정 비용이 있는 서비스는 테넌트 수가 많아지면 기하급수적으로 늘어납니다. 예:
| 서비스 | 단일 계정 비용 | 1,000개 계정 비용 (월) | 비고 |
|---|---|---|---|
| EC2 t4g.nano (최소) | ~$3 | $3,000 | 사용량과 무관 |
| RDS db.t4g.micro (최소) | ~$15 | $15,000 | 사용량과 무관 |
| Lambda (요청당) | $0.20/백만 요청 | $200 (1000계정 * 1000요청/일) | 실제 사용량 기반 |
| DynamoDB (온디맨드) | $1.25/백만 WRU | $1,250 (1000계정 * 1000WRU/일) | 실제 사용량 기반 |
💡 실무 팁: '서버리스'라고 안심하지 마세요. Lambda와 DynamoDB는 사용량 기반이지만, CloudWatch 메트릭 수집 비용은 계정당 고정 비용으로 발생합니다. ProGlove는 모든 메트릭을 수집하지 않고 꼭 필요한 것만 필터링하여 비용을 최적화했다고 해요.
한계와 주의사항
- AWS 서비스 한도(Quota) 분산: 각 계정이 독립적이므로 한도 초과가 특정 테넌트에 국한되지만, 전체 한도 사용량을 한눈에 파악하기 어려움
- 자격 증명 관리 복잡성: 수천 개의 IAM 역할과 교차 계정 신뢰 정책을 안전하게 관리해야 함. 장기 크리덴셜 사용은 절대 금물
- 퇴역(Decommission) 자동화의 어려움: 계정 생성은 완전 자동화가 가능하지만, 계정 폐쇄는 데이터 백업, 규정 준수 확인 등 수동 프로세스가 필요
- 조직 단위(OU) 구조 설계: AWS Organizations에서 OU를 어떻게 구성하느냐에 따라 SCP(서비스 제어 정책) 적용 범위가 달라짐. 초기에 잘 설계해야 함
국내 SaaS 환경에서의 적용 맥락
국내 SI/클라우드 환경에서는 다음과 같은 점을 고려해야 합니다.
- 계정당 최소 비용 부담: 국내 스타트업의 경우 초기에는 공유 계정 모델로 시작하고, 테넌트당 매출이 일정 수준 이상일 때 계정 분리를 검토하는 것이 현실적입니다.
- 규제 준수: 금융, 의료 등 규제 산업에서는 계정 분리가 오히려 컴플라이언스 증명을 단순화할 수 있습니다. 각 테넌트 계정에 별도의 감사 로그를 구성할 수 있기 때문이에요.
- 운영 인력 규모: ProGlove 사례는 '3명'이라는 극단적인 효율성을 보여주지만, 국내에서는 최소 5~10명의 플랫폼 엔지니어링 팀이 필요할 수 있습니다. 초기 투자 비용을 감안해야 해요.
함께 보면 좋은 글
- PyTorch 분산 학습 완벽 가이드: Point-to-Point과 Collective Operations 실전 코드 - 분산 시스템에서의 통신 패턴 이해
- Netflix가 JDK 벡터 API로 추천 시스템을 최적화한 실제 사례 - 대규모 시스템 최적화 접근법 비교

결론: 운영팀을 키우지 않고 테넌트를 늘리는 법
ProGlove의 사례가 주는 가장 큰 교훈은 **'복잡성의 이동(Shift Complexity)'**입니다. 전통적인 접근법은 각 마이크로서비스 개발자가 멀티테넌시를 고려해야 했지만, 계정-테넌트 모델에서는 플랫폼 팀이 그 복잡성을 흡수합니다.
다음 단계 학습 방향:
- AWS Organizations + SCP 심화 학습: OU 설계, 태그 정책, 백업 정책 등
- CloudFormation StackSets + Terraform: 대규모 인프라 자동화 도구 비교
- Observability as Code: 중앙 집중식 모니터링 파이프라인 구축 (OpenTelemetry, Grafana)
📌 최종 조언: 이 모델은 '모든 SaaS에 정답'이 아닙니다. 테넌트당 매출이 높고, 보안/규제 요구사항이 엄격하며, 운영 자동화에 투자할 여력이 있는 기업에 적합합니다. 처음부터 6,000개를 목표로 하지 말고, 10개, 100개 단위로 점진적으로 확장하며 플랫폼 자동화 역량을 키워보세요.