본문 바로가기

카테고리 없음

Django 앱 AWS Elastic Beanstalk에 단계별로 배포하기

반응형

복잡한 인프라도 한 걸음씩: PostgreSQL, Redis, WebSocket을 안전하게 연동하는 전략


🎯 들어가며

로컬 환경에서 개발한 Django 애플리케이션을 클라우드에 배포할 때, 많은 개발자들이 고민에 빠집니다. 데이터베이스는 PostgreSQL을 써야 하고, 실시간 기능을 위해 Redis와 WebSocket도 필요하고, 파일 저장을 위해 S3도 연동해야 하죠. "이걸 한 번에 다 설정하면 안 될까?" 하는 생각이 들 수 있지만, 실제로는 이것이 배포 실패의 가장 큰 원인입니다.

이 글에서는 복잡한 Django 앱을 AWS Elastic Beanstalk(EB)에 안전하게 배포하는 단계별 전략을 소개합니다. 각 단계는 독립적으로 검증 가능하며, 문제가 발생했을 때 쉽게 롤백할 수 있도록 설계되었습니다.

Elastic Beanstalk Deploy Command


🤔 왜 단계별 배포가 필요한가?

❌ 한 번에 모든 것을 배포하면 생기는 문제들

  • 어디서 문제가 발생했는지 파악하기 어려움
  • 설정 오류가 누적되어 디버깅 시간 증가
  • 롤백 시 모든 설정을 되돌려야 함
  • 팀원들과 협업 시 어떤 부분이 문제인지 공유 어려움

✅ 단계별 배포의 장점

  • 각 단계마다 명확한 검증 포인트 존재
  • 문제 발생 시 원인 파악이 쉬움
  • 이전 단계로 쉽게 롤백 가능
  • 비용을 점진적으로 증가시킬 수 있음
  • 학습 곡선이 완만함

🗺️ 5단계 배포 로드맵

전체 배포 과정을 5단계로 나누어, 각 단계마다 새로운 기능을 추가하는 방식으로 진행합니다. 각 단계는 이전 단계가 완전히 검증된 후에만 진행합니다.


Phase 1: 기본 HTTP 앱 배포 ("일단 띄우기")

🎯 목표

가장 단순한 형태로 Django 앱이 클라우드에서 실행되는지 확인

🛠️ 사용 기술

  • Django + Gunicorn (HTTP 서버)
  • SQLite (로컬 데이터베이스)
  • WhiteNoise (정적 파일 서빙)
  • 단일 EC2 인스턴스 (t3.micro)

⏱️ 예상 소요 시간

2-3일

💰 월 예상 비용

$0 (AWS 프리티어 적용 가능)

✅ 검증 포인트

  • 웹 페이지가 정상적으로 뜨는가?
  • Admin 페이지 접속이 가능한가?
  • 기본 CRUD 작업이 동작하는가?
  • 에러 로그가 깨끗한가?

💻 핵심 명령어

# EB 애플리케이션 초기화
eb init

# 개발 환경 생성 (단일 인스턴스)
eb create django-app-dev --single

# 환경 변수 설정
eb setenv SECRET_KEY="your-secret-key" DEBUG=False

# 배포
eb deploy

# 브라우저로 확인
eb open

Phase 2: S3 정적 파일 연동

🎯 목표

파일 저장소를 로컬에서 클라우드로 분리

🤔 왜 DB나 WebSocket보다 먼저?

  • S3 설정이 상대적으로 단순함
  • 문제 발생 시 WhiteNoise로 쉽게 롤백 가능
  • 다중 인스턴스 확장의 첫 번째 단계
  • 데이터 손실 위험이 없음

📋 필요한 설정

  • S3 버킷 생성 및 권한 설정
  • django-storages 라이브러리 설치
  • Django settings에 S3 백엔드 설정
  • collectstatic이 S3에 업로드되는지 확인

⏱️ 예상 소요 시간

1-2일

💰 추가 비용

월 $0.20 (정적 파일 50MB 기준)

💻 설정 예시

# config/settings/eb_dev.py
AWS_STORAGE_BUCKET_NAME = "your-bucket-name"
AWS_S3_CUSTOM_DOMAIN = f"{AWS_STORAGE_BUCKET_NAME}.s3.ap-northeast-2.amazonaws.com"
STATIC_URL = f"https://{AWS_S3_CUSTOM_DOMAIN}/static/"

STORAGES = {
    "staticfiles": {
        "BACKEND": "storages.backends.s3.S3Storage",
        "OPTIONS": {
            "bucket_name": AWS_STORAGE_BUCKET_NAME,
            "location": "static",
        },
    },
}

Phase 3: RDS PostgreSQL 연동

🎯 목표

영구적이고 확장 가능한 데이터베이스 구축

⚠️ SQLite의 한계

  • 인스턴스 재시작 시 데이터 손실 위험
  • 다중 인스턴스 확장 불가능
  • 동시 접속 처리 제한적
  • 프로덕션 환경에 부적합

🔧 핵심 작업

1. RDS 인스턴스 생성 (EB 외부에서)

aws rds create-db-instance \
  --db-instance-identifier django-app-db \
  --engine postgres \
  --db-instance-class db.t3.micro \
  --master-username dbadmin \
  --master-user-password [secure-password] \
  --allocated-storage 20

2. Security Group 설정

  • EB 인스턴스 → RDS (포트 5432) 허용

3. 데이터 마이그레이션

# SQLite 데이터 백업
python manage.py dumpdata > backup.json

# PostgreSQL로 마이그레이션
python manage.py migrate

# 데이터 복원
python manage.py loaddata backup.json

4. 환경 변수 설정

eb setenv DATABASE_URL="postgresql://user:pass@endpoint:5432/dbname"

⏱️ 예상 소요 시간

3-5일 (데이터 마이그레이션 포함)

💰 추가 비용

월 $15-20 (db.t3.micro)


Phase 4: Redis 캐싱 및 세션 관리

🎯 목표

성능 향상 및 Celery 백그라운드 작업 준비

📈 단계별 적용 전략 (위험도 낮은 순서)

1단계: Django 캐시로만 사용

  • 기존 기능에 영향 없음
  • 성능 향상 효과 측정 가능

2단계: 세션 백엔드로 확장

  • 다중 인스턴스에서 세션 공유
  • 로그인 상태 유지 개선

3단계: Celery 추가 (선택사항)

  • 이메일 발송, 리포트 생성 등 비동기 작업
  • 별도 워커 인스턴스 필요

💻 설정 예시

# ElastiCache Redis 생성
aws elasticache create-cache-cluster \
  --cache-cluster-id django-redis \
  --cache-node-type cache.t3.micro \
  --engine redis \
  --num-cache-nodes 1

# 환경 변수 설정
eb setenv \
  REDIS_URL="redis://endpoint:6379/0" \
  CELERY_BROKER_URL="redis://endpoint:6379/1"
# config/settings/eb_dev.py
CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.redis.RedisCache",
        "LOCATION": os.environ.get("REDIS_URL"),
    }
}

SESSION_ENGINE = "django.contrib.sessions.backends.cache"
SESSION_CACHE_ALIAS = "default"

⏱️ 예상 소요 시간

2-3일

💰 추가 비용

월 $15 (cache.t3.micro)


Phase 5: WebSocket 실시간 통신 (Daphne + Channels)

🎯 목표

실시간 채팅, 알림 등 양방향 통신 구현

⚠️ 가장 복잡한 단계!

🔧 필요한 인프라 변경사항

  • Load Balancer를 Application LB로 변경
  • WebSocket 경로(/ws/) 라우팅 설정
  • Daphne ASGI 서버 추가
  • Redis를 Channel Layer로 사용
  • Sticky Session 설정

💻 Procfile 설정

web: gunicorn config.wsgi:application --bind :8000
websocket: daphne -b 0.0.0.0 -p 8001 config.asgi:application

📝 nginx 설정

# .platform/nginx/conf.d/websocket.conf
upstream daphne {
    server 127.0.0.1:8001;
}

location /ws/ {
    proxy_pass http://daphne;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
}

⏱️ 예상 소요 시간

5-7일 (테스트 및 디버깅 포함)

💰 추가 비용

$0 (설정만 변경, 인스턴스는 동일)


🛡️ 각 단계별 안전장치

1. 환경 분리 전략

각 단계를 별도 환경에서 테스트한 후 프로덕션에 적용하세요.

# 개발 환경 (실험용)
eb create django-app-dev --single

# 스테이징 환경 (최종 검증용)
eb create django-app-staging --single

# 프로덕션 환경 (실제 서비스)
eb create django-app-prod --elb-type application

2. 설정 파일 분리

환경별로 다른 설정을 사용하여 혼란을 방지합니다.

config/settings/
├── base.py          # 공통 설정
├── local.py         # 로컬 개발
├── eb_dev.py        # EB 개발 환경
├── eb_staging.py    # EB 스테이징
└── eb_prod.py       # EB 프로덕션

3. Git 태그를 이용한 롤백 계획

각 단계가 완료될 때마다 Git 태그를 생성하여 쉽게 되돌릴 수 있습니다.

# 각 단계 완료 시 태그 생성
git tag -a phase1-basic -m "Phase 1: Basic deployment"
git tag -a phase2-s3 -m "Phase 2: S3 static files"
git tag -a phase3-rds -m "Phase 3: PostgreSQL"

# 문제 발생 시 이전 버전으로 롤백
git checkout phase2-s3
eb deploy

4. 체크리스트 활용

각 단계마다 검증 체크리스트를 만들어 누락을 방지하세요.

□ 로컬 환경에서 테스트 완료
□ 환경 변수 설정 확인
□ 배포 성공 확인
□ 에러 로그 점검
□ 기능 테스트 완료
□ Git 태그 생성

⚠️ 흔한 실수와 예방법

❌ 실수 1: 한 번에 여러 변경사항 배포

나쁜 예: S3 + RDS + Redis를 동시에 설정
→ 문제 발생 시 어디가 원인인지 파악 어려움

좋은 예: S3만 먼저 → 검증 완료 → 다음 단계
→ 문제를 단계별로 격리하여 빠른 해결 가능

❌ 실수 2: 환경 변수 누락

배포 전 항상 환경 변수를 확인하세요.

eb printenv | grep -E "SECRET_KEY|DATABASE_URL|REDIS_URL|AWS_"

❌ 실수 3: 로컬 테스트 생략

클라우드에 배포하기 전에 로컬에서 eb_dev.py 설정을 테스트하세요.

DJANGO_SETTINGS_MODULE=config.settings.eb_dev python manage.py check

❌ 실수 4: 백업 없이 진행

각 단계 전에 백업을 생성하세요.

# 데이터베이스 백업
python manage.py dumpdata > backup_$(date +%Y%m%d).json

# Git 태그 백업
git tag phase-X-backup

📅 현실적인 배포 타임라인

전체 과정을 2-3주에 걸쳐 진행하는 것을 권장합니다. 서두르면 오히려 시간이 더 걸립니다.

Week 1: Phase 1 (기본 배포)

Day 1-2: 로컬에서 EB 설정 파일 준비

  • eb init, eb create 명령어 실행
  • 환경 변수 설정

Day 3-4: 배포 및 디버깅

  • eb deploy 실행
  • 에러 로그 확인 및 수정 반복

Day 5: 검증 완료

  • ✅ 웹 페이지 접속 확인
  • ✅ Admin 기능 동작 확인
  • ✅ Git 태그 생성

Week 2: Phase 2 (S3) + Phase 3 (RDS)

Day 1-2: S3 설정

  • S3 버킷 생성 및 권한 설정
  • Django settings 변경
  • collectstatic 테스트

Day 3-5: RDS 설정

  • RDS 인스턴스 생성
  • 데이터 마이그레이션
  • 배포 및 검증

Week 3: Phase 4 (Redis) + Phase 5 (WebSocket, 선택)

Day 1-3: Redis 설정

  • ElastiCache 생성
  • 단계별 적용 (캐시 → 세션 → Celery)

Day 4-7: WebSocket 설정 (필요한 경우)

  • Daphne 및 Channels 설정
  • Load Balancer 변경
  • 실시간 기능 테스트

💰 예상 비용 정리

각 단계별로 비용이 증가하므로, 필요한 단계까지만 진행하면 됩니다.

Phase 내용 월 비용
Phase 1 기본 (EC2 t3.micro) $0 (프리티어)
Phase 2 S3 정적 파일 +$0.20
Phase 3 RDS PostgreSQL +$15
Phase 4 ElastiCache Redis +$15
Phase 5 WebSocket (설정만) +$0

총 예상 비용: 월 $30-35 (모든 단계 완료 시)


🎓 마치며: 단순함의 힘

이 글에서 소개한 5단계 배포 전략의 핵심은 "점진적 복잡도 증가"입니다. 처음부터 모든 기능을 갖춘 완벽한 인프라를 구축하려고 하면, 설정 오류와 디버깅에 막대한 시간을 소비하게 됩니다. 대신 가장 단순한 형태로 시작하여, 각 단계를 완전히 검증한 후 다음으로 넘어가는 것이 결과적으로 더 빠르고 안전합니다.

특히 초기 서비스 개발 시에는 다양한 기술과 자원을 연동하기보다는 최소한의 기능과 자원으로 운용하는 것이 추후 유지보수에도 유리합니다.

SQLite와 로컬 스토리지만으로도 서비스를 시작할 수 있습니다. 실제 사용자가 늘어나고, 성능 이슈가 발생하기 시작할 때 PostgreSQL이나 Redis를 추가해도 늦지 않습니다. 이러한 "필요에 따른 확장" 접근법은 다음과 같은 이점을 제공합니다:

  • 초기 개발 및 배포 속도가 빠릅니다
  • 문제 발생 시 디버깅 범위가 명확합니다
  • 비용을 실제 필요에 따라 증가시킬 수 있습니다
  • 팀원들의 학습 곡선이 완만합니다
  • 불필요한 복잡성을 피할 수 있습니다

저의 경험상, 완벽한 인프라를 처음부터 갖추려다 결국 서비스 출시가 지연된 프로젝트들을 많이 봤습니다. 반면 단순하게 시작해서 필요에 따라 점진적으로 개선한 프로젝트들은 빠르게 시장에 진입하고, 실제 사용자 피드백을 바탕으로 의미 있는 개선을 이뤄냈습니다.

"처음부터 완벽하게"보다 "작게 시작해서 점진적으로 개선"하는 것이 성공적인 배포의 핵심입니다.

여러분의 Django 앱이 AWS 클라우드에서 안정적으로 운영되길 바랍니다. 배포 과정에서 어려움이 있다면 각 단계의 체크리스트와 롤백 계획을 활용하세요. 그리고 무엇보다, 서두르지 마세요. 한 걸음씩 확실하게 나아가는 것이 결국 가장 빠른 길입니다.


📚 참고 자료


행복한 배포 되세요! 🚀