📋 전체 학습 목차 — 이 글 하나로 클라우드/컨테이너 완전 정복
🐳 Chapter 01
Docker란? — 컨테이너 기초 완전 이해
"내 컴퓨터에서는 되는데..." 이 말이 사라지는 날, Docker를 배운 날입니다
🚢 Docker를 배 여행으로 이해해봅시다
옛날에는 물건을 배에 실을 때 크기/모양이 제각각이라 매번 다르게 포장해야 했습니다. 그러다 표준화된 컨테이너(container)가 등장하면서 어떤 배에든, 어떤 항구에서든 같은 방식으로 처리할 수 있게 됐죠. Docker도 똑같습니다.
😫 Docker 없는 세상
• 개발: Mac에서 Node.js 18 사용
• 서버: Ubuntu에 Node.js 16 설치됨
• 결과: "내 PC에서는 됐는데?!"
• 팀원 A: Python 3.9
• 팀원 B: Python 3.11
• 결과: 라이브러리 충돌 지옥
😊 Docker 있는 세상
• 모든 환경을 컨테이너로 패키징
• "이 컨테이너 이미지 실행해"
• 결과: 어디서나 동일하게 실행!
• Mac, Windows, Linux 무관
• 개발 = 테스트 = 운영 환경 동일
🔑 핵심 개념: 이미지 vs 컨테이너
변경 불가능
Docker Hub에 공유
여러 개 동시 실행 가능
삭제하면 데이터 사라짐
컨테이너 삭제 후에도 유지
DB 데이터 보관에 필수
💻 Docker 핵심 명령어 완전 정복
# ━━━ 이미지 관리 ━━━
docker pull nginx:latest # Docker Hub에서 이미지 다운로드
docker images # 로컬 이미지 목록 보기
docker rmi nginx:latest # 이미지 삭제
docker build -t myapp:1.0 . # Dockerfile로 이미지 빌드
# ━━━ 컨테이너 실행 ━━━
docker run nginx # 기본 실행 (포그라운드)
docker run -d nginx # 백그라운드(detached) 실행
docker run -d -p 8080:80 nginx # 포트 매핑 (호스트:컨테이너)
docker run -d -p 3000:3000 -e NODE_ENV=production # 환경변수 설정
-v /data:/app/data # 볼륨 마운트
--name myapp # 컨테이너 이름 지정
--restart unless-stopped # 자동 재시작
myapp:1.0
# ━━━ 컨테이너 관리 ━━━
docker ps # 실행 중인 컨테이너 목록
docker ps -a # 모든 컨테이너 (정지 포함)
docker stop myapp # 컨테이너 정지
docker start myapp # 컨테이너 시작
docker rm myapp # 컨테이너 삭제
docker logs -f myapp # 실시간 로그 확인
docker exec -it myapp bash # 실행 중인 컨테이너 내부 접속
docker stats # 컨테이너 리소스 사용량 모니터링
# ━━━ 이미지 배포 ━━━
docker tag myapp:1.0 myrepo/myapp:1.0
docker push myrepo/myapp:1.0 # Docker Hub에 업로드
# ━━━ 정리 명령어 ━━━
docker system prune -a # 미사용 이미지/컨테이너 전체 삭제
📝 Chapter 02
Dockerfile 작성 완전 가이드 — 프로덕션급 이미지 만들기
"Dockerfile은 서버 구축 매뉴얼을 코드로 만든 것입니다"
📋 Dockerfile 명령어 완전 정복
| 명령어 | 역할 | 예시 |
|---|---|---|
| FROM | 베이스 이미지 지정 | FROM node:20-alpine |
| WORKDIR | 작업 디렉토리 설정 | WORKDIR /app |
| COPY | 호스트 → 컨테이너 파일 복사 | COPY . . |
| RUN | 빌드 시 명령어 실행 | RUN npm ci --only=production |
| ENV | 환경변수 설정 | ENV NODE_ENV=production |
| EXPOSE | 포트 문서화 (실제 개방 아님) | EXPOSE 3000 |
| CMD | 컨테이너 시작 시 기본 명령 | CMD ["node", "dist/main.js"] |
| ENTRYPOINT | 항상 실행되는 명령 (CMD와 결합) | ENTRYPOINT ["docker-entrypoint.sh"] |
| ARG | 빌드 시 인자 (런타임엔 없음) | ARG NODE_VERSION=20 |
| USER | 실행 사용자 (보안상 non-root) | USER node |
💻 프로덕션급 NestJS Dockerfile — 멀티스테이지 빌드
# ━━━ Stage 1: Builder (빌드 전용) ━━━
FROM node:20-alpine AS builder
WORKDIR /app
# package 파일만 먼저 복사 → 의존성 캐싱 최적화
# 소스코드가 바뀌어도 의존성이 안 바뀌면 npm install 캐시 사용!
COPY package*.json ./
RUN npm ci # npm install보다 빠르고 안전 (lock 파일 기반)
COPY . .
RUN npm run build # TypeScript → JavaScript 빌드
# ━━━ Stage 2: Runner (실행 전용) ━━━
FROM node:20-alpine AS runner
# 보안: 비루트 사용자로 실행
RUN addgroup -g 1001 -S nodejs && adduser -S nestjs -u 1001
WORKDIR /app
# 빌드 결과물만 복사 (node_modules 개발 의존성 제외)
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package*.json ./
RUN npm ci --only=production && npm cache clean --force
USER nestjs
EXPOSE 3000
# 헬스체크: 컨테이너 정상 여부 확인
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 CMD wget --quiet --tries=1 --spider http://localhost:3000/health || exit 1
CMD ["node", "dist/main.js"]
# 멀티스테이지 빌드 효과:
# 일반 빌드: ~800MB (빌드 도구 + 개발 의존성 포함)
# 멀티스테이지: ~150MB (5배 이상 작아짐!)
🚫 .dockerignore — 이것도 꼭 만드세요!
# .dockerignore — 빌드 컨텍스트에서 제외
node_modules # 가장 중요! 로컬 node_modules 제외
.git # git 이력 제외
.env # 환경변수 파일 절대 포함하지 말 것!
.env.*
dist # 빌드 결과물 (컨테이너 내에서 빌드)
coverage # 테스트 커버리지
*.log
*.md
.DS_Store
docker-compose*.yml # 컴포즈 파일 제외
🔗 Chapter 03
Docker Compose — 멀티 컨테이너 서비스 한 번에 관리
"앱 서버 + DB + 캐시 + 메시지큐를 명령어 하나로 실행합니다"
🍱 Docker Compose 개념 — 도시락 세트로 이해하기
혼자 먹는 도시락(단일 컨테이너)이 아니라, 밥+반찬+국+음료가 세트(멀티 컨테이너)로 구성된 도시락이 Docker Compose입니다. 각 메뉴는 독립적이지만 하나의 세트로 묶여 함께 서비스됩니다.
# docker-compose.yml — 실전 NestJS 풀스택 구성
version: '3.9'
services:
# ─── 앱 서버 ───
api:
build:
context: .
dockerfile: Dockerfile
target: runner # 멀티스테이지에서 runner 스테이지만 빌드
container_name: nestjs-api
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- DATABASE_URL=postgresql://user:pass@postgres:5432/mydb
- REDIS_URL=redis://redis:6379
depends_on:
postgres:
condition: service_healthy # DB 준비 후 시작
redis:
condition: service_healthy
restart: unless-stopped
networks:
- app-network
# ─── PostgreSQL ───
postgres:
image: postgres:15-alpine
container_name: postgres-db
environment:
POSTGRES_DB: mydb
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
volumes:
- postgres-data:/var/lib/postgresql/data # 데이터 영구 저장
- ./init.sql:/docker-entrypoint-initdb.d/init.sql # 초기 SQL 실행
healthcheck:
test: ["CMD-SHELL", "pg_isready -U user -d mydb"]
interval: 10s
timeout: 5s
retries: 5
networks:
- app-network
# ─── Redis ───
redis:
image: redis:7-alpine
container_name: redis-cache
command: redis-server --requirepass mypassword --maxmemory 256mb --maxmemory-policy allkeys-lru
volumes:
- redis-data:/data
healthcheck:
test: ["CMD", "redis-cli", "--no-auth-warning", "-a", "mypassword", "ping"]
interval: 10s
networks:
- app-network
# ─── Nginx (리버스 프록시) ───
nginx:
image: nginx:alpine
container_name: nginx-proxy
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./ssl:/etc/ssl/certs:ro
depends_on:
- api
networks:
- app-network
volumes:
postgres-data:
redis-data:
networks:
app-network:
driver: bridge
docker compose up -d # 전체 서비스 백그라운드 시작
docker compose up -d --build # 이미지 재빌드 후 시작
docker compose down # 전체 서비스 중지 + 컨테이너 삭제
docker compose down -v # 볼륨까지 삭제 (DB 데이터 초기화)
docker compose logs -f api # api 서비스 실시간 로그
docker compose ps # 서비스 상태 확인
docker compose exec api bash # api 컨테이너 내부 접속
docker compose scale api=3 # api 서비스 3개로 스케일업
⚙️ Chapter 04
Kubernetes 기초 — 컨테이너 오케스트레이션의 표준
"Docker가 악기라면, Kubernetes는 오케스트라 지휘자입니다"
🎭 Kubernetes가 필요한 이유
😰 Docker만 쓸 때 문제점
• 컨테이너 하나 죽으면 수동으로 재시작
• 트래픽 몰릴 때 수동으로 컨테이너 추가
• 서버 여러 대에 배포 어떻게?
• 롤링 배포 구현 어떻게?
• 컨테이너 간 로드밸런싱은?
😊 Kubernetes 해결책
• 컨테이너 죽으면 자동 재시작 ✅
• HPA로 트래픽 따라 자동 스케일링 ✅
• 노드 클러스터에 자동 배포 ✅
• 롤링 업데이트/롤백 기본 제공 ✅
• 내장 로드밸런서 + 서비스 디스커버리 ✅
🧩 Kubernetes 핵심 오브젝트 완전 이해
🏗️ Chapter 05
Kubernetes 심화 — 실전 Manifest 작성과 배포 전략
"YAML 하나로 프로덕션 서비스를 완전히 제어합니다"
💻 실전 Kubernetes Manifest — NestJS 앱 전체 배포
# ━━━ 1. Namespace 분리 ━━━
apiVersion: v1
kind: Namespace
metadata:
name: production
---
# ━━━ 2. Secret (민감 정보) ━━━
apiVersion: v1
kind: Secret
metadata:
name: app-secrets
namespace: production
type: Opaque
stringData: # stringData는 자동으로 base64 인코딩
DATABASE_URL: postgresql://user:pass@postgres-svc:5432/mydb
JWT_SECRET: my-super-secret-jwt-key-here
REDIS_URL: redis://:password@redis-svc:6379
---
# ━━━ 3. ConfigMap (비민감 설정) ━━━
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
namespace: production
data:
NODE_ENV: production
PORT: "3000"
LOG_LEVEL: info
---
# ━━━ 4. Deployment (핵심!) ━━━
apiVersion: apps/v1
kind: Deployment
metadata:
name: nestjs-api
namespace: production
spec:
replicas: 3 # Pod 3개 항상 유지
selector:
matchLabels:
app: nestjs-api
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1 # 배포 중 최대 1개 동시 중지
maxSurge: 1 # 최대 1개 추가 생성
template:
metadata:
labels:
app: nestjs-api
spec:
containers:
- name: nestjs-api
image: myrepo/nestjs-api:v1.2.3
ports:
- containerPort: 3000
envFrom:
- secretRef:
name: app-secrets
- configMapRef:
name: app-config
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe: # 살아있는지 체크 (실패 시 재시작)
httpGet:
path: /health
port: 3000
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe: # 트래픽 받을 준비됐는지 체크
httpGet:
path: /health/ready
port: 3000
initialDelaySeconds: 5
periodSeconds: 5
---
# ━━━ 5. Service (내부 통신) ━━━
apiVersion: v1
kind: Service
metadata:
name: nestjs-api-svc
namespace: production
spec:
selector:
app: nestjs-api
ports:
- port: 80
targetPort: 3000
type: ClusterIP
---
# ━━━ 6. HPA (자동 스케일링) ━━━
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: nestjs-api-hpa
namespace: production
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: nestjs-api
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70 # CPU 70% 넘으면 Pod 추가
---
# ━━━ 7. Ingress (외부 트래픽) ━━━
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: api-ingress
namespace: production
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
tls:
- hosts:
- api.myapp.com
secretName: api-tls
rules:
- host: api.myapp.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: nestjs-api-svc
port:
number: 80
🔧 kubectl 핵심 명령어 레퍼런스
# 기본 조회
kubectl get pods -n production # Pod 목록
kubectl get pods -n production -w # 실시간 상태 감시
kubectl get all -n production # 모든 리소스 조회
kubectl describe pod nestjs-api-xxx # Pod 상세 정보 (오류 디버깅)
kubectl logs -f nestjs-api-xxx # Pod 로그 실시간
kubectl exec -it nestjs-api-xxx -- bash # Pod 내부 접속
# 배포
kubectl apply -f ./k8s/ # 디렉토리 내 모든 yaml 적용
kubectl set image deployment/nestjs-api nestjs-api=myrepo/nestjs-api:v1.3.0
kubectl rollout status deployment/nestjs-api # 배포 진행 상황
kubectl rollout history deployment/nestjs-api # 배포 이력
kubectl rollout undo deployment/nestjs-api # 이전 버전으로 롤백!
kubectl scale deployment nestjs-api --replicas=5 # 수동 스케일링
☁️ Chapter 06
AWS 핵심 서비스 완전 정복 — 백엔드 개발자 필수 서비스
"AWS를 모르면 2024년 백엔드 개발자가 아닙니다"
🗺️ AWS 전체 서비스 지도 — 백엔드 필수 서비스
| 카테고리 | 서비스 | 역할 | 대응 솔루션 |
|---|---|---|---|
| 컴퓨팅 | EC2 | 가상 서버 (VM) | GCP Compute Engine |
| ECS/EKS | 컨테이너 오케스트레이션 | GKE, Azure AKS | |
| Lambda | 서버리스 함수 | Cloud Functions | |
| 데이터베이스 | RDS | 관리형 관계형 DB | Cloud SQL |
| DynamoDB | NoSQL (서버리스) | Firestore, CosmosDB | |
| 스토리지 | S3 | 객체 스토리지 (파일) | GCS, Azure Blob |
| EFS | NFS (공유 파일시스템) | Filestore | |
| 네트워크 | VPC | 가상 사설 네트워크 | GCP VPC |
| ALB/NLB | 로드 밸런서 | Cloud Load Balancing | |
| 캐시/메시지 | ElastiCache | 관리형 Redis/Memcached | Cloud Memorystore |
| SQS/SNS | 메시지 큐/알림 | Pub/Sub | |
| 보안/인증 | IAM | 권한 관리 | IAM (GCP) |
💻 EC2 + NestJS 배포 실전 가이드
# ━━━ EC2 초기 설정 스크립트 (User Data) ━━━
#!/bin/bash
set -e
# 시스템 업데이트
apt-get update && apt-get upgrade -y
# Docker 설치
curl -fsSL https://get.docker.com | sh
usermod -aG docker ubuntu
# Docker Compose 설치
curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
# CloudWatch Agent 설치 (로그/메트릭 수집)
wget https://s3.amazonaws.com/amazoncloudwatch-agent/ubuntu/amd64/latest/amazon-cloudwatch-agent.deb
dpkg -i amazon-cloudwatch-agent.deb
# Nginx 설치 (리버스 프록시)
apt-get install -y nginx certbot python3-certbot-nginx
# 방화벽 설정
ufw allow 22 # SSH
ufw allow 80 # HTTP
ufw allow 443 # HTTPS
ufw enable
# Nginx 설정 (리버스 프록시)
cat > /etc/nginx/sites-available/api.myapp.com << 'EOF'
server {
server_name api.myapp.com;
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
}
# 파일 업로드 크기 제한
client_max_body_size 50M;
}
EOF
ln -s /etc/nginx/sites-available/api.myapp.com /etc/nginx/sites-enabled/
nginx -t && systemctl reload nginx
# Let's Encrypt SSL 인증서 자동 발급
certbot --nginx -d api.myapp.com --non-interactive --agree-tos -m admin@myapp.com
🗄️ Chapter 07
AWS RDS, ElastiCache, S3 — 데이터 관리 완전 정복
"직접 설치보다 관리형 서비스가 훨씬 편합니다. 실수도 줄어듭니다"
🗄️ AWS RDS vs 직접 설치 비교
😰 EC2에 직접 DB 설치
• 백업: 직접 cron job 설정
• 업그레이드: 직접 진행 (다운타임)
• 장애 복구: 직접 처리
• 모니터링: 별도 구성
• 멀티 AZ: 직접 구성 (매우 복잡)
😊 AWS RDS 사용
• 백업: 자동 (최대 35일 보관) ✅
• 업그레이드: 클릭 한 번 ✅
• 자동 장애 조치 (Multi-AZ) ✅
• CloudWatch 통합 모니터링 ✅
• Read Replica 클릭으로 추가 ✅
# NestJS RDS 연결 설정 (TypeORM)
TypeOrmModule.forRootAsync({
useFactory: () => ({
type: 'postgres',
host: process.env.DB_HOST, # RDS 엔드포인트
port: +process.env.DB_PORT || 5432,
username: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
ssl: process.env.NODE_ENV === 'production'
? { rejectUnauthorized: false } # RDS SSL 연결
: false,
entities: [__dirname + '/**/*.entity{.ts,.js}'],
migrations: [__dirname + '/migrations/**/*{.ts,.js}'],
synchronize: process.env.NODE_ENV !== 'production', # 운영에서 false!
logging: process.env.NODE_ENV !== 'production',
# 커넥션 풀 설정 (RDS 동시 연결 제한 주의)
extra: {
max: 10, # 최대 연결 수
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 2000,
},
}),
})
📁 S3 파일 업로드 — NestJS 실전 구현
// S3 파일 업로드 서비스 (NestJS)
@Injectable()
export class S3Service {
private s3: S3Client;
constructor() {
this.s3 = new S3Client({
region: process.env.AWS_REGION,
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
},
});
}
async uploadFile(file: Express.Multer.File, folder: string = 'uploads') {
const ext = path.extname(file.originalname);
const key = `${folder}/${Date.now()}-${uuidv4()}${ext}`;
const command = new PutObjectCommand({
Bucket: process.env.AWS_S3_BUCKET,
Key: key,
Body: file.buffer,
ContentType: file.mimetype,
// ACL: 'public-read', // 퍼블릭으로 공개 (CloudFront 사용 시 불필요)
Metadata: {
originalName: file.originalname,
uploadedBy: 'api-server',
},
});
await this.s3.send(command);
// CloudFront CDN URL 반환 (직접 S3 URL 노출 비권장)
return {
key,
url: `https://${process.env.CLOUDFRONT_DOMAIN}/${key}`,
};
}
// Presigned URL — 클라이언트가 S3에 직접 업로드
async getPresignedUploadUrl(fileName: string, contentType: string) {
const key = `uploads/${Date.now()}-${uuidv4()}-${fileName}`;
const command = new PutObjectCommand({
Bucket: process.env.AWS_S3_BUCKET,
Key: key,
ContentType: contentType,
});
const presignedUrl = await getSignedUrl(this.s3, command, { expiresIn: 300 }); // 5분
return { key, presignedUrl };
}
}
🔄 Chapter 08
CI/CD 파이프라인 완전 구축 — 자동화된 배포의 모든 것
"코드 push 하나로 운영 서버까지 자동 배포되는 세상을 만들어봅시다"
🔄 CI/CD 개념 완전 이해
🔨 CI (Continuous Integration)
코드 변경 시 자동으로:
1. 코드 체크아웃
2. 의존성 설치
3. 린트/포맷 검사
4. 단위 테스트 실행
5. 통합 테스트 실행
6. 보안 취약점 스캔
7. Docker 이미지 빌드 및 푸시
🚀 CD (Continuous Deployment)
CI 완료 후 자동으로:
1. 스테이징 환경 배포
2. E2E 테스트 실행
3. 승인 단계 (선택)
4. 운영 환경 배포
5. 배포 상태 알림 (Slack)
6. 이상 감지 시 자동 롤백
💻 GitHub Actions — 실전 CI/CD 파이프라인
# .github/workflows/deploy.yml name: CI/CD Pipeline on: push: branches: [main, develop] pull_request: branches: [main, develop] env: REGISTRY: ghcr.io IMAGE_NAME: your-org/your-app jobs: # CI: 테스트 + 빌드 ci: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Setup Node.js 20 uses: actions/setup-node@v4 with: node-version: '20' cache: 'npm' - name: Install run: npm ci - name: Lint run: npm run lint - name: Test (with coverage) run: npm run test:cov - name: Build run: npm run build - name: Docker Build and Push if: github.event_name != 'pull_request' run: | echo "LOGIN_TOKEN" | docker login ghcr.io -u actor --password-stdin docker build -t ghcr.io/your-org/your-app:SHA . docker push ghcr.io/your-org/your-app:SHA # 스테이징 자동 배포 deploy-staging: needs: ci if: github.ref == 'refs/heads/develop' runs-on: ubuntu-latest environment: staging steps: - name: SSH Deploy to Staging uses: appleboy/ssh-action@v1 with: host: STAGING_HOST username: ubuntu key: SSH_PRIVATE_KEY script: | cd /opt/myapp docker compose pull docker compose up -d --no-deps api docker system prune -f # 운영 배포 (main 브랜치) deploy-production: needs: ci if: github.ref == 'refs/heads/main' runs-on: ubuntu-latest environment: production steps: - name: Deploy to EKS run: | aws eks update-kubeconfig --region ap-northeast-2 --name prod-cluster kubectl set image deployment/nestjs-api nestjs-api=IMAGE:SHA -n production kubectl rollout status deployment/nestjs-api -n production - name: Notify Slack on result if: always() run: | curl -X POST SLACK_WEBHOOK -d '{"text":"Production deploy finished"}'
📦 Chapter 09
Infrastructure as Code — Terraform으로 인프라 코드화
"인프라도 코드다. 버전 관리하고, 리뷰하고, 자동화한다"
🏗️ Terraform 기초 — AWS 인프라를 코드로
Terraform은 HashiCorp의 IaC(Infrastructure as Code) 도구입니다. AWS 콘솔에서 클릭클릭 하던 것을 코드로 선언하면, Terraform이 알아서 인프라를 만들어줍니다.
# main.tf — NestJS 운영 인프라 전체 정의 terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 5.0" } } backend "s3" { bucket = "myapp-terraform-state" key = "production/terraform.tfstate" region = "ap-northeast-2" } } provider "aws" { region = var.aws_region } # RDS PostgreSQL resource "aws_db_instance" "postgres" { identifier = "myapp-postgres" engine = "postgres" engine_version = "15.4" instance_class = "db.t3.medium" allocated_storage = 20 storage_encrypted = true db_name = var.db_name username = var.db_username password = var.db_password multi_az = true deletion_protection = true backup_retention_period = 7 skip_final_snapshot = false tags = { Name = "myapp-postgres", Env = "production" } } # ElastiCache Redis resource "aws_elasticache_replication_group" "redis" { replication_group_id = "myapp-redis" description = "MyApp Redis Cluster" node_type = "cache.t3.micro" num_cache_clusters = 2 automatic_failover_enabled = true at_rest_encryption_enabled = true transit_encryption_enabled = true } # Terraform 명령어 # terraform init → 초기화 # terraform plan → 변경 미리보기 (dry-run) # terraform apply → 실제 인프라 생성/변경 # terraform destroy → 전체 인프라 삭제
🔒 Chapter 10
클라우드 보안 완전 정복 — 해킹당하지 않는 인프라
"클라우드 보안 실수 하나가 수백만 원의 요금 폭탄이 될 수 있습니다"
🛡️ AWS IAM 최소 권한 원칙
IAM(Identity and Access Management)은 AWS 서비스 접근 권한을 관리합니다. 최소 권한 원칙(Principle of Least Privilege): 필요한 권한만 부여합니다.
# IAM Policy 예시 — S3 특정 버킷 읽기/쓰기만 허용 { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:GetObject", "s3:PutObject", "s3:DeleteObject" ], "Resource": "arn:aws:s3:::myapp-uploads/*" }, { "Effect": "Allow", "Action": "s3:ListBucket", "Resource": "arn:aws:s3:::myapp-uploads" } ] } # 나쁜 예 (절대 하지 말 것!) # "Action": "*", "Resource": "*" → 모든 서비스 모든 작업 허용! # EC2 인스턴스에 IAM Role 적용 (Access Key 사용 X) resource "aws_iam_role" "ec2_role" { name = "ec2-app-role" assume_role_policy = jsonencode({ Statement = [{ Action = "sts:AssumeRole" Effect = "Allow" Principal = { Service = "ec2.amazonaws.com" } }] }) } # EC2에 Role 적용 시 코드에 AccessKey 하드코딩 불필요! # AWS SDK가 자동으로 인스턴스 메타데이터에서 인증 처리
🚨 절대 하지 말아야 할 보안 실수 TOP 5
❌ S3 버킷을 퍼블릭으로 설정 (개인정보 유출)
❌ 보안그룹 0.0.0.0/0 인바운드 전체 허용
❌ root 계정 직접 사용 (IAM 사용자 생성 후 사용)
❌ MFA 미설정 (Multi-Factor Authentication 필수)
📊 Chapter 11
모니터링 완전 정복 — Prometheus + Grafana
"장애는 반드시 납니다. 언제, 어디서, 왜 났는지 알아야 합니다"
📊 모니터링 스택 구성
// NestJS Prometheus 메트릭 노출 // npm install @willsoto/nestjs-prometheus prom-client @Module({ imports: [ PrometheusModule.register({ defaultMetrics: { enabled: true }, path: '/metrics', }), ], }) // 커스텀 메트릭 정의 @Injectable() export class AppMetrics { private httpRequestsTotal: Counter; private httpRequestDuration: Histogram; constructor() { this.httpRequestsTotal = new Counter({ name: 'http_requests_total', help: 'Total HTTP requests', labelNames: ['method', 'path', 'status'], }); this.httpRequestDuration = new Histogram({ name: 'http_request_duration_seconds', help: 'HTTP request duration in seconds', labelNames: ['method', 'path'], buckets: [0.1, 0.3, 0.5, 1, 3, 5, 10], }); } recordRequest(method: string, path: string, status: number, duration: number) { this.httpRequestsTotal.labels(method, path, String(status)).inc(); this.httpRequestDuration.labels(method, path).observe(duration); } }
🔔 Grafana 핵심 알람 설정 가이드
| 메트릭 | 경고 임계값 | 위험 임계값 | 조치 |
|---|---|---|---|
| CPU 사용률 | 70% | 90% | 스케일 업/아웃 |
| 메모리 사용률 | 75% | 90% | 메모리 누수 확인 |
| API 응답시간 | 500ms | 2s | 쿼리/캐시 최적화 |
| 에러율 | 1% | 5% | 로그 분석 |
| DB 연결 수 | 80% | 95% | 풀 크기 조정 |
💰 Chapter 12
클라우드 비용 최적화 — 돈 아끼는 기술
"AWS 요금 폭탄 맞아본 사람만 이 챕터의 소중함을 압니다"
💰 AWS 비용 절약 10가지 전략
• Spot Instance: 경매 방식, 최대 90% 할인 (중단 가능)
• Savings Plans: 유연한 약정 할인
• Auto Scaling: 트래픽 없을 때 자동 축소
• 올바른 인스턴스 타입: CPU 최적화 vs 메모리 최적화
• RDS 스냅샷 관리: 불필요한 스냅샷 삭제
• CloudFront: 엣지 캐싱으로 S3 요청 감소
• 데이터 전송 최소화: 같은 리전 서비스 사용
• 압축: gzip으로 데이터 전송량 감소
⚠️ 비용 폭탄 방지 필수 설정
2. Cost Explorer: 매주 비용 리뷰 습관화
3. 미사용 리소스 정리: 쓰지 않는 EC2, EIP, NAT Gateway 삭제
4. 개발 환경 자동 종료: 업무 시간 외 자동 중지 스케줄러
5. CloudTrail: 누가 어떤 리소스 만들었는지 추적
🌐 Chapter 13
서버리스 vs 컨테이너 — 언제 무엇을 선택할까?
"모든 문제에 맞는 도구가 있습니다"
⚡ Lambda(서버리스) vs ECS(컨테이너) 완전 비교
| 구분 | Lambda (서버리스) | ECS/EKS (컨테이너) |
|---|---|---|
| 과금 | 요청당 과금 (0원도 가능) | 실행 시간 과금 (항상 비용) |
| 시작 시간 | Cold Start 100ms~수초 | 항상 빠름 (이미 실행 중) |
| 실행 시간 | 최대 15분 | 제한 없음 |
| 스케일링 | 자동 (동시 10,000개) | 수동/자동 (HPA) |
| 상태 관리 | Stateless 필수 | Stateful 가능 |
| 배포 복잡도 | 낮음 | 높음 |
| 적합한 워크로드 | 이벤트 기반, 배치, 가끔 실행 | 항상 실행, 실시간 처리 |
| 대표 사용처 | 이미지 리사이징, 알림, API 백엔드 | 웹 API, 마이크로서비스 |
// Lambda 핸들러 — NestJS to Serverless // npm install @vendia/serverless-express import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import serverlessExpress from '@vendia/serverless-express'; let cachedServer: any; async function bootstrap() { if (!cachedServer) { const app = await NestFactory.create(AppModule); await app.init(); const expressApp = app.getHttpAdapter().getInstance(); cachedServer = serverlessExpress({ app: expressApp }); } return cachedServer; } // Lambda 핸들러 export const handler = async (event: any, context: any) => { const server = await bootstrap(); return server(event, context); }; // Cold Start 최소화 팁: // 1. Provisioned Concurrency 설정 (항상 워밍업) // 2. 번들 크기 최소화 (트리쉐이킹) // 3. 불필요한 의존성 제거
🎯 Chapter 14
면접 Q&A + 실무 체크리스트
"이 챕터를 다 읽으면 클라우드/컨테이너 면접이 두렵지 않습니다"
💬 면접 단골 질문 완전 정리
Readiness Probe: 트래픽 받을 준비가 됐는지 확인. 실패하면 Service에서 Pod 제외 (트래픽 차단). 시작 시 초기화 완료 확인에 사용.
2) ALB + Auto Scaling Group: 로드밸런서 + 자동 스케일링
3) RDS Multi-AZ: 자동 페일오버
4) ElastiCache 클러스터 모드: Redis 다중화
5) Route 53 헬스체크: DNS 레벨 페일오버
✅ 클라우드/컨테이너 실무 체크리스트
🐳 Docker 체크리스트
☐ alpine 이미지 사용
☐ .dockerignore 작성
☐ non-root 사용자 실행
☐ HEALTHCHECK 설정
⚙️ K8s 체크리스트
☐ Liveness/Readiness Probe
☐ HPA 자동 스케일링
☐ Secret으로 민감정보 관리
☐ Namespace로 환경 분리
☁️ AWS 체크리스트
☐ RDS Multi-AZ + 자동 백업
☐ S3 버전 관리 + 암호화
☐ CloudWatch 알람 설정
☐ AWS Budgets 예산 알림
🔄 CI/CD 체크리스트
☐ 보안 취약점 스캔 (npm audit)
☐ 이미지 취약점 스캔 (Trivy)
☐ 롤백 전략 수립
☐ Slack/이메일 알림 설정
BackendDevGuide0012 완성!
클라우드와 컨테이너 완전 정복 A-Z
Docker부터 Kubernetes, AWS, CI/CD, IaC, 모니터링, 보안까지
현업 백엔드 개발자가 알아야 할 클라우드/인프라 모든 것을 담았습니다 💪