본문으로 건너뛰기

클라우드 아키텍처 (CA)

갱신: 2026-08-17

이 문서는 2026-08-17 시점에 세운 목표 구조다. 실제로 적용된 구성이 아니다. 지금 구성은 클라우드 아키텍처 본문에 있다.

화면과 애플리케이션의 책임은 각각 정보 아키텍처와 애플리케이션 아키텍처에 있다. 여기서는 서비스가 어느 네트워크 경계에 놓이고 외부와 어떤 길로 통신하는지만 다룬다.

전체 구성​

ALB와 NAT Gateway는 모두 public subnet을 사용하지만 서로 연결되지는 않는다. ALB는 인터넷 요청을 받아 private app subnet의 API·realtime target으로 보낸다. NAT Gateway는 private app subnet에서 시작한 외부 요청만 인터넷으로 내보낸다. api·realtime·ai ECS task는 private app subnet, RDS와 Redis는 private data subnet에 둔다.

SQS는 VPC 밖에서 실행 단위 사이의 event를 전달한다. audio-assembly Lambda는 meeting-end를 받아 S3의 audio segment를 합치고 audio-assembled를 다시 발행한다. RDS와 Redis에는 연결하지 않는다. GitHub Actions·IAM·ECR·ECS는 서비스 트래픽과 별개의 배포 control plane으로 표시했다. IAM은 네트워크 통로가 아니라 배포와 runtime 권한을 제한한다.

private app subnet에서 S3로 가는 요청은 Gateway Endpoint를 사용한다. 외부 API로 나가는 요청은 같은 가용 영역의 NAT Gateway를 거친다. 아래에서는 이 포함관계와 연결을 배포·데이터·운영 관점으로 나누어 검토한다.

AWS 안팎의 경계​

전체 구성 그림에서 Internet Gateway 나 NAT Gateway A 를 누르면 AWS 안팎을 넘는 경로만 남는다.

사용자 요청은 두 길로 들어온다. 일반 REST 요청은 Cloudflare를 거쳐 ALB의 API 대상 그룹으로 향한다. 회의와 채팅의 장기 연결은 realtime 호스트를 통해 ALB에 직접 닿는다. 웹 정적 자원은 Vercel이 제공한다.

VPC 안은 public, private app, private data 세 층으로 나눈다. ALB와 NAT Gateway만 public subnet에 둔다. ECS task는 app subnet, RDS와 Redis는 data subnet에 둔다. data subnet의 경로표에는 인터넷 기본 경로를 만들지 않는다.

S3, SQS, ECR, Secrets Manager, CloudWatch Logs, SES, IAM은 특정 subnet에 넣는 자원이 아니라 AWS Region 서비스다. Cloudflare, Vercel, GitHub Actions와 외부 SaaS는 AWS 경계 밖에 있다.

Region, VPC, 가용 영역의 포함관계​

전체 구성 그림의 Region → VPC → 가용 영역 → 서브넷 중첩이 이 표와 같다.

범위배치할 자원
RegionS3, SQS, ECR, Secrets Manager, CloudWatch Logs, SES
VPCIGW, 경로표, 보안 그룹, S3 Gateway Endpoint, ECS cluster
두 가용 영역ALB
가용 영역별 subnetNAT Gateway, ECS 태스크, RDS 인스턴스, Redis 노드
subnet groupRDS와 cache가 사용할 가용 영역 목록. 실행 자원은 아니다

각 app subnet은 같은 가용 영역의 NAT Gateway를 사용한다. 경로표를 가용 영역별로 나누지 않으면 NAT 장애가 반대편 app subnet까지 번지고 cross-AZ 비용도 생긴다. S3 요청은 인터넷으로 내보내지 않고 app subnet의 Gateway Endpoint로 보낸다.

RDS Multi-AZ는 두 data subnet에 writer와 standby를 두는 구성이다. 읽기 부하를 나누는 read replica는 포함하지 않는다.

애플리케이션이 실행되는 자리​

전체 구성 그림에서 api task A 를 누르면 그 task 가 닿는 자리만 남는다.

ECS service는 api, realtime, ai 세 개다. ALB 대상 그룹에는 api와 realtime만 등록한다. ai는 API와 realtime의 보안 그룹에서 들어오는 내부 요청만 받는다. 오디오 조립은 상시 실행하는 ECS service가 아니라 SQS 사건으로 시작하는 Lambda 함수가 맡는다.

실행 단위확장 기준배포할 때 지킬 점
apiCPU와 요청 수짧은 요청을 마친 뒤 태스크 종료
realtime활성 회의 수종료할 회의를 고를 수 없어 자동 축소를 사용하지 않음
ai동시 스트림 수외부 진입 경로 없음
조립 함수SQS 사건 수와 예약 동시성VPC 밖에서 실행

회의 연결을 끊지 않는 배포​

전체 구성 그림에서 Amazon ECS 를 누르면 롤링 교체가 닿는 대상이 보인다.

회의는 최대 5시간 이어질 수 있다. 태스크 종료 유예를 길게 잡는 것만으로 배포를 해결할 수 없다. realtime이 SIGTERM을 받으면 메모리에 남은 오디오를 마지막 세그먼트로 S3에 올리고 세션을 INTERRUPTED로 닫는다.

브라우저는 새 realtime 태스크에 연결해 새 run을 연다. 이전 run의 길이를 새 run 오프셋에 더하면 배포 전후 전사 좌표가 한 회의 안에서 이어진다. 계획된 태스크 종료에서는 오디오 유실 0을 목표로 하지만 장애 주입으로 검증한 기록은 없다.

데이터베이스 변경은 애플리케이션 배포와 분리해 expand-then-contract 순서로 진행한다. 이미 실행한 migration을 되돌리지 않고 문제가 생기면 후속 migration으로 수정한다.

데이터가 머무는 곳​

저장소소유자와 연결 범위
RDS PostgreSQLheymoa는 server, heymoa_ai는 AI가 소유한다. 데이터베이스 간 직접 질의 금지
ElastiCache RedisAPI와 realtime이 티켓과 fan-out 상태에 사용
S3realtime 세그먼트와 조립된 합본 저장
SQS서로 다른 실행 단위 사이의 작업 전달

조립 함수는 VPC 밖에서 S3와 SQS만 사용한다. RDS, Redis, 외부 업체에 연결할 이유가 없다. 두 app subnet의 경로표에 S3 Gateway Endpoint를 모두 연결해야 한다. 한쪽 연결이 빠져도 기능은 동작하지만 해당 subnet의 S3 트래픽이 NAT를 지나 비용과 장애 범위가 달라진다.

S3 세그먼트는 합본을 만든 뒤 삭제한다. 삭제 유예 기간은 정하지 않았다. Opus 합본 보존 기간도 법률 검토가 필요하다. 브라우저부터 Opus를 사용하므로 손실 압축 전 PCM 원본은 저장하지 않는다.

운영 접근과 관측​

아래 외부 진입과 외부 연결 그림에서 운영자 나 ECS 애플리케이션 서비스 를 누르면 운영 접근 경로만 남는다.

운영 접근을 위해 공개 SSH 포트를 열지 않는다. SSM Session Manager와 ECS Exec을 사용한다. IAM은 실행 주체가 호출할 수 있는 AWS API를 제한하고, 보안 그룹은 통신할 수 있는 상대를 제한한다.

애플리케이션 metric과 trace는 OTLP로 Grafana Cloud와 Langfuse에 보낸다. 부팅 실패와 프로세스 비정상 종료는 CloudWatch Logs에 남긴다. 외부 관측 서비스로 나가는 트래픽은 NAT를 지난다.

외부 진입과 외부 연결​

외부에서 허용할 경로​

요청허용 방식
사용자 → Cloudflare → VercelCloudflare proxy
사용자 → Cloudflare → ALB → apialb-sg에서 api-sg로 전달
사용자 → ALB → realtimealb-sg에서 rt-sg로 전달
pyannote → ALB → apiwebhook 서명 검증
운영자 → SSM / ECS Exec수신 포트를 열지 않음

realtime 호스트는 DNS only로 운영한다. 이 때문에 ALB ingress를 Cloudflare 주소 대역만으로 제한할 수 없다. realtime의 인증은 일회용 티켓과 애플리케이션 검증에 의존한다.

외부로 나가는 경로​

출발대상네트워크 경로예상량
realtimeS3Gateway Endpoint월 약 330 GB 계산값
realtimeSonioxNAT월 약 330 GB 계산값
apipyannoteNAT회의당 요청 1회, 오디오는 presigned URL로 전달
aiOpenAI, Linear, GitHubNAT채팅과 분석 호출
조립 함수S3, SQSAWS Region 내부회의당 합본 1개
apiSES, Google OAuth2NAT로그인과 메일 발송
전체 서비스Grafana Cloud, LangfuseNATmetric과 trace
ECS 태스크ECR, Secrets Manager, CloudWatch LogsNAT배포와 확장 시

월 330 GB는 Opus 32 kbps와 기존 이용량 가정으로 계산한 값이다. PCM 384 kbps 설계 계산의 약 12분의 1이다.

보안 그룹 관계​

internet -> alb-sg -> api-sg -> ai-sg
-> rt-sg -> ai-sg
ai-sg -> api-sg

api-sg -> rds-sg, redis-sg
rt-sg -> rds-sg, redis-sg
ai-sg -> rds-sg

AI 보안 그룹은 API와 realtime 보안 그룹에서만 요청을 받는다. data subnet에는 인터넷 기본 경로가 없다. 조립 함수는 VPC 밖에 있으므로 RDS 보안 그룹의 허용 대상으로 지정할 수 없다.

IAM 역할​

역할필요한 권한
apiSQS publish·consume, SES, Secrets Manager, 합본 presigned URL 서명
realtimeS3 PutObject, Secrets Manager
조립 함수S3 read·write, SQS consume·publish, CloudWatch Logs
aiSecrets Manager, SQS consume

조립 함수 역할에는 Secrets Manager 권한이 없다. S3 byte를 직접 읽고 쓰는 실행 단위는 realtime과 조립 함수다. API는 합본을 내려받을 presigned URL만 서명한다.

기존 환경에서 바꿀 부분​

현재 환경목표 구조
기존 VPC와 private subnetVPC는 재사용하고 기존 private subnet을 app 층으로 사용, data subnet 추가
EC2와 nginxECS service와 ALB로 교체
Single-AZ RDS같은 인스턴스를 Multi-AZ로 전환
연결되지 않은 S3 Gateway Endpoint두 app 경로표에 연결
SSH 운영 접속SSM Session Manager로 전환
Secrets Manager, SES, Cloudflare, Vercel유지