배포 — 서버를 세상에 공개하기¶
지금까지 만든 것과 남은 것¶
7장에서 API를 만들고, 8장에서 인증을 추가했습니다.
하지만 지금까지 서버는 내 컴퓨터 안에서만 실행되고 있습니다.
지금: 클라이언트 → localhost:8000 (내 컴퓨터에서만 접근 가능)
목표: 클라이언트 → https://myapp.com (누구나 접근 가능)
이 과정을 배포(Deployment)라고 합니다.
개발 환경 vs 운영 환경¶
같은 코드라도 동작하는 환경이 다릅니다.
| 구분 | 개발(Development) | 운영(Production) |
|---|---|---|
| 실행 위치 | 내 컴퓨터 | 클라우드 서버 |
| 주소 | localhost:8000 |
https://myapp.com |
| DB | 로컬 파일 | 별도 DB 서버 |
| 디버그 모드 | 켜짐 (상세 오류) | 꺼짐 (보안) |
| SECRET_KEY | 아무 값이나 | 강력한 무작위 값 |
| 로그 | 터미널 출력 | 파일/모니터링 서비스 |
운영 환경에서 디버그 모드를 켜두면 오류 메시지에서 코드 내부가 노출될 수 있습니다.
클라우드 서비스란?¶
서버 컴퓨터를 직접 사지 않고, 빌려 쓰는 방식입니다.
전통적인 방식:
서버 컴퓨터 구매 → 데이터센터에 설치 → 직접 유지보수
클라우드:
AWS, GCP, Azure, Cloudflare 등에서 서버를 시간/월 단위로 임대
역할별 추천 서비스:
| 서비스 | 분류 | 특징 |
|---|---|---|
| Vercel | 프론트엔드 / 서버리스 API | GitHub 연결 자동 배포, 글로벌 CDN 기본 제공 |
| Supabase | 데이터베이스 | PostgreSQL + REST API, 인증·스토리지 포함 |
| AWS EC2 / ECS | 백엔드 서버 | 세밀한 제어, 기업 운영 환경 표준 |
| AWS Lambda | 백엔드 서버리스 | 이벤트 기반 실행, 트래픽 비례 과금 |
| Cloudflare Workers | 백엔드 서버리스 | 엣지 배포, 전 세계 저지연 응답 |
환경 변수 (Environment Variables)¶
배포 전에 반드시 알아야 할 개념입니다.
# 절대 하면 안 되는 것 — 코드에 비밀 값 하드코딩
SECRET_KEY = "my-super-secret-key"
DATABASE_URL = "postgresql://user:password@host/db"
# 올바른 방법 — 환경 변수에서 읽기
import os
SECRET_KEY = os.getenv("SECRET_KEY")
DATABASE_URL = os.getenv("DATABASE_URL")
환경 변수는 서버에서 설정하고, 코드는 읽기만 합니다.
개발 환경: .env 파일에 작성 (.gitignore에 추가)
운영 환경: 클라우드 서비스 설정 패널에서 입력
.env 파일¶
# .env (절대 git에 커밋하지 말 것)
SECRET_KEY=f3a1b2c4d5e6f7a8b9c0d1e2f3a4b5c6
DATABASE_URL=sqlite:///app.db
DEBUG=false
# python-dotenv로 .env 읽기
from dotenv import load_dotenv
import os
load_dotenv()
SECRET_KEY = os.getenv("SECRET_KEY")
if not SECRET_KEY:
raise RuntimeError("SECRET_KEY 환경 변수가 설정되지 않았습니다")
# .gitignore
.env
*.db
__pycache__/
프로젝트 구조 정리¶
배포 전에 프로젝트 구조를 다음처럼 정리합니다.
myapp/
├── main.py ← FastAPI 앱
├── database.py ← DB 연결
├── auth.py ← 인증 로직
├── requirements.txt ← 의존성 목록
├── Procfile ← 실행 명령
├── .env ← 환경 변수 (git 제외)
└── .gitignore
requirements.txt¶
배포 서버에서 어떤 패키지를 설치해야 하는지 명시합니다.
pip freeze > requirements.txt
# requirements.txt
fastapi==0.111.0
uvicorn==0.29.0
python-jose[cryptography]==3.3.0
passlib[bcrypt]==1.7.4
python-dotenv==1.0.1
시작 명령 설정¶
클라우드 서비스마다 서버를 시작하는 방법을 알려줘야 합니다.
web: uvicorn main:app --host 0.0.0.0 --port $PORT
--host 0.0.0.0: 외부에서 접근 허용
--port $PORT: 클라우드가 지정하는 포트 사용 (고정 8000이 아님)
배포 방식별 예시¶
Vercel (프론트엔드 / 서버리스 API)¶
1. vercel.com 접속 → GitHub 연결
2. 저장소 선택 → 프레임워크 자동 감지
3. Environment Variables에 SECRET_KEY 등 입력
4. Deploy
Python FastAPI는 vercel.json으로 서버리스 함수로 배포 가능합니다.
{
"builds": [{ "src": "main.py", "use": "@vercel/python" }],
"routes": [{ "src": "/(.*)", "dest": "main.py" }]
}
AWS Lambda (서버리스 백엔드)¶
FastAPI를 Lambda에 올릴 때는 Mangum 어댑터를 사용합니다.
# main.py
from fastapi import FastAPI
from mangum import Mangum
app = FastAPI()
handler = Mangum(app) # Lambda 핸들러
# 배포 — AWS SAM 또는 Serverless Framework 사용
sam build && sam deploy --guided
Cloudflare Workers (엣지 서버리스 백엔드)¶
Python보다 JavaScript/TypeScript에 최적화되어 있습니다.
Python 백엔드는 Workers보다 Lambda 또는 EC2/ECS가 더 적합합니다.
# Wrangler CLI로 배포
npx wrangler deploy
AWS EC2 (전통적 백엔드 서버)¶
# EC2 인스턴스에 SSH 접속 후
pip install -r requirements.txt
uvicorn main:app --host 0.0.0.0 --port 8000 &
Nginx를 앞단에 두고 프록시하는 방식이 일반적입니다.
환경 변수 설정 (서비스 공통)¶
Vercel: 프로젝트 → Settings → Environment Variables
AWS Lambda: 함수 → Configuration → Environment variables
Cloudflare: wrangler.toml 또는 대시보드 → Workers → Settings
AWS EC2: /etc/environment 또는 systemd unit 파일
SQLite의 한계 — 운영 환경에서는 PostgreSQL¶
SQLite는 파일 기반 데이터베이스입니다.
클라우드 서버는 재배포 시 파일 시스템이 초기화됨
→ SQLite 파일이 사라짐 → 데이터 소실
운영 환경에서는 PostgreSQL 같은 독립 DB 서버를 사용합니다.
Supabase를 사용하면 PostgreSQL을 별도 서버 없이 빠르게 연결할 수 있습니다.
프로젝트 생성 후 대시보드에서 DATABASE_URL을 복사해 환경 변수로 설정합니다.
# database.py — 환경 변수로 DB URL 읽기
import os
import psycopg2
DATABASE_URL = os.getenv("DATABASE_URL")
def get_db():
conn = psycopg2.connect(DATABASE_URL)
return conn
HTTPS — 암호화된 연결¶
운영 서버는 반드시 HTTPS를 사용해야 합니다.
HTTP: 데이터가 평문으로 전송 → 중간에서 도청 가능
HTTPS: TLS 암호화 → 도청 불가
특히 비밀번호, JWT 토큰이 HTTP로 전송되면 네트워크 중간에서 탈취될 수 있습니다.
Vercel, Cloudflare Workers, AWS Lambda(API Gateway) 모두 자동으로 HTTPS를 설정합니다.
EC2 직접 배포 시에는 Let's Encrypt(Certbot)으로 TLS 인증서를 별도로 발급해야 합니다.
CI/CD — 코드를 올리면 자동 배포¶
CI/CD: git push하면 자동으로 테스트 → 빌드 → 배포가 실행됩니다.
개발자 GitHub 클라우드 서비스
│ │ │
│ git push ──▶ │ │
│ │ Webhook ───▶ │
│ │ │ 자동 빌드
│ │ │ 자동 배포
Vercel, AWS CodePipeline, Cloudflare Workers 모두 main 브랜치 push 시
자동 재배포를 지원합니다.
배포 체크리스트¶
□ .env가 .gitignore에 포함되어 있는가
□ SECRET_KEY가 강력한 무작위 값인가 (코드에 하드코딩 아닌가)
□ 디버그 모드가 꺼져 있는가
□ requirements.txt가 최신 상태인가
□ 운영 DB가 PostgreSQL인가 (SQLite 아님)
□ HTTPS가 활성화되어 있는가
□ 로그를 확인할 수 있는가
실습 미션¶
미션 1: 환경 변수 적용¶
1. python-dotenv를 설치하고 .env 파일을 만드세요.
2. SECRET_KEY를 환경 변수로 옮기고 os.getenv()로 읽으세요.
3. .env를 .gitignore에 추가하세요.
4. SECRET_KEY 없이 서버를 시작하면 오류가 나도록 처리하세요.
미션 2: requirements.txt 작성¶
1. pip freeze > requirements.txt 를 실행하세요.
2. 새 가상 환경에서 pip install -r requirements.txt 로 복원하세요.
3. 서버가 정상 실행되는지 확인하세요.
미션 3: 클라우드 배포¶
1. GitHub 저장소에 코드를 올리세요.
2. Vercel 또는 AWS Lambda 중 하나를 선택해 배포하세요.
- Vercel: vercel.json 추가 후 vercel deploy
- Lambda: Mangum 추가 후 SAM 또는 Serverless Framework로 배포
3. 환경 변수를 서비스 대시보드에서 설정하세요.
4. 배포된 URL로 POST /auth/signup을 호출해 보세요.
미션 4 (심화): Supabase PostgreSQL 연동¶
1. supabase.com에서 프로젝트를 생성하세요.
2. 대시보드 → Settings → Database에서 DATABASE_URL을 복사하세요.
3. SQLite 대신 PostgreSQL을 사용하도록 database.py를 수정하세요.
4. DATABASE_URL을 클라우드 환경 변수로 설정 후 재배포하세요.
5. 재배포 후에도 데이터가 유지되는지 확인하세요.
핵심 요약¶
| 개념 | 설명 |
|---|---|
| 배포 | 로컬 서버를 외부에서 접근 가능하게 옮기는 과정 |
| 환경 변수 | 비밀 값을 코드 밖에서 관리하는 방법 |
| requirements.txt | 패키지 의존성 목록 — 동일한 환경 재현 |
| 시작 명령 | 서버 시작 명령 — 서비스마다 설정 방식이 다름 |
| PostgreSQL | 운영 환경에서 SQLite 대신 사용하는 DB |
| HTTPS | 데이터 암호화 — 운영 환경 필수 |
| CI/CD | push 시 자동 빌드·배포 파이프라인 |
코드는 GitHub에서 누구나 볼 수 있습니다.
비밀 값은 절대 코드에 넣지 말고, 환경 변수로 분리하세요.