PetClinic 3-Tier on AWS2026
샘플 앱을 띄우는 것과 서비스로 운영하는 것은 다릅니다. 외부 트래픽을 어디서 걸러낼지, 서버가 죽으면 어떻게 대체할지, 배포 중에도 서비스가 멈추지 않게 하려면 어떻게 할지를 계층별로 나눠 설계했습니다.

초점
어려운 건 리소스를 만드는 일이 아니라, 각 계층이 서로를 믿는 범위를 좁게 유지하는 일이었습니다.
핵심 질문: 서버가 바뀌고 트래픽이 몰려도, 누가 어디까지 접근할 수 있는지가 그대로일까?
네 가지에 집중했습니다.
계층마다 문을 좁게
보안 그룹은 이전 계층의 보안 그룹만 소스로 받습니다. 외부 ALB는 CloudFront 전용 프리픽스 리스트의 443만 열고, DB는 인터넷 경로가 없는 서브넷에 둡니다.
엣지에서 먼저 거르기
WAF가 SQL 인젝션과 공통 취약 패턴을 막고, 한 IP의 과도한 요청에는 429 안내 페이지를 돌려줍니다. 정적 파일은 CloudFront가 캐시합니다.
서버는 갈아 끼운다
서버를 고치지 않고 새 이미지로 교체합니다. 교체 중에도 절반은 살아 있고, 새 서버는 60초 워밍업을 거칩니다.
비밀번호를 사람이 모르게
DB 자격 증명은 Secrets Manager가 30일마다 교체합니다. 애플리케이션은 VPC 엔드포인트로 가져옵니다.
구성
네트워크
- VPC 10.0.0.0/16, 서브넷 8개
- public · web · was · db × AZ 2개
- Regional NAT
- VPC 플로우 로그
애플리케이션
- 외부 ALB와 내부 ALB
- Apache ASG (2–6대)
- Spring PetClinic ASG (2–6대)
- Docker 컨테이너
데이터
- RDS MySQL 8.4 Multi-AZ
- gp3, 저장 시 암호화
- Secrets Manager 로테이션
- 향상된 모니터링
엣지 · 운영
- Route 53, ACM, CloudFront
- WAF 두 개 (CloudFront, ALB)
- CloudWatch, SNS, Lambda
- GuardDuty, CloudTrail, IAM
배포
코드 한 줄을 고치면 GitHub Actions가 이미지를 빌드해 ECR에 올립니다. 장기 액세스 키 대신 OIDC 롤을 씁니다.
새 이미지는 Instance Refresh로 반영합니다. 한 번에 절반만 교체하고, 새 서버가 정상이 되면 나머지를 바꿉니다. 다이어그램의 노드를 누르면 펼쳐집니다.
시스템
요청은 Route 53에서 CloudFront와 WAF를 지나 외부 ALB로 들어옵니다. Apache가 받아 내부 ALB를 거쳐 Spring 서버로 넘기고, 서버는 RDS에 접속합니다.
계층마다 서브넷과 보안 그룹을 분리했습니다. 다이어그램은 영어입니다.
운영
ALB 5xx, CPU, RDS CPU 알람은 SNS와 Lambda를 거쳐 Discord로 갑니다. 서버가 새로 뜨는 이벤트와 GuardDuty 탐지, WAF 차단도 같은 채널로 옵니다.
권한역할별 IAM 그룹 4개(네트워크, DB·보안, 엣지·보안, 운영)로 나눴습니다. 사람마다 만질 수 있는 범위가 다릅니다.
코드로 남기기AWS 리소스는 프로젝트 종료와 함께 모두 삭제했습니다. 삭제 전 구성을 CloudTrail 기록에서 복원해 Terraform 모듈 9개로 정리했고, plan으로 검증했습니다.
시연 영상
실제로 동작하는 모습을 다섯 장면으로 담았습니다. WAS 롤링 배포, WAS 부하 테스트, Rate limit, SQL 인젝션 차단, GuardDuty 탐지 순서입니다.