콘텐츠로 이동

배포 — 서버를 세상에 공개하기

지금까지 만든 것과 남은 것

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에서 누구나 볼 수 있습니다.
비밀 값은 절대 코드에 넣지 말고, 환경 변수로 분리하세요.