안녕하세요! 이번 포스팅에서는 비동기 작업 처리와 배포 전략을 중점적으로 살펴보겠습니다. 현대적인 웹 애플리케이션은 단순 동기 REST API만으로는 부족할 때가 많습니다. 예를 들어 이미지 처리, 이메일 발송, 데이터 분석 등 시간이 오래 걸리는 작업은 비동기로 처리해야 서버의 응답성을 유지할 수 있습니다. 또한, 애플리케이션을 확장하기 위해선 Docker 컨테이너 기반으로 배포하는 전략이 필수에 가까워졌습니다.
이번 장에서는 Celery + Redis 조합을 통해 비동기 작업을 구현하고, Docker 및 Docker Compose를 이용해 FastAPI 애플리케이션을 손쉽게 배포·관리하는 방법을 자세히 알아보겠습니다.
7.1. 비동기 작업의 필요성
7.1.1. 언제 비동기 처리가 필요한가?
- 장시간 연산: 대용량 데이터 처리, AI 모델 예측, 영상 인코딩 등 한 번의 요청에 몇 초 이상 걸리는 작업
- 네트워크 지연: 외부 API나 파일 업로드 같은 네트워크 의존 작업
- 고객 경험 향상: REST API를 빠르게 응답시키고, 실제 처리는 백그라운드에서 진행해 사용자가 “대기”를 최소화
7.1.2. Python에서의 비동기 옵션
- Async/Await: FastAPI 자체가 비동기 I/O를 지원하지만, 대규모 백그라운드 작업은 추가적인 아키텍처가 필요
- Celery: 메시지 큐(예: Redis, RabbitMQ)와 연동해 분산 작업을 수행하는 대표적인 Python 프레임워크
- RQ, Huey 등 경량 작업 큐: 규모가 작거나 단순한 경우에 사용
이번 강의에서는 가장 많이 사용되는 Celery를 중점적으로 다룹니다.
7.2. Celery와 Redis를 활용한 비동기 처리
7.2.1. Celery 개요
- Celery는 작업(Tasks)을 큐(Queue)에 넣고, 여러 워커(Worker)가 이를 소비(Consume)하여 처리하는 아키텍처를 제공합니다.
- Broker(ex. Redis, RabbitMQ)가 작업 큐를 관리하고, 필요에 따라 여러 개의 워커 프로세스를 띄워 병렬 작업을 수행할 수 있습니다.
- 성능, 확장성, 유연성 면에서 널리 검증된 솔루션이며, FastAPI와도 쉽게 연동할 수 있습니다.
7.2.2. Redis 설정
- Redis는 메모리 기반 저장소로, Celery에서 가장 흔히 쓰이는 브로커 중 하나입니다.
- 설치 후, redis:// 형태의 URL로 접근할 수 있습니다. (예: redis://localhost:6379/0)
7.2.3. Poetry로 Celery & Redis 설치
poetry add celery redis
TIP: 필요하다면 Celery 플러그인을 추가(celery[s3], celery[redis] 등)하여 기능을 확장할 수 있습니다.
7.2.4. Celery 설정 (celery_app.py 예시)
# app/celery_app.py
import os
from celery import Celery
REDIS_URL = os.getenv("REDIS_URL", "redis://localhost:6379/0")
celery_app = Celery(
"worker",
broker=REDIS_URL,
backend=REDIS_URL, # 결과 저장소도 Redis 사용
)
celery_app.conf.update(
result_expires=3600, # 작업 결과 만료 시간 (예시)
task_serializer="json",
accept_content=["json"],
result_serializer="json",
timezone="Asia/Seoul",
enable_utc=True,
)
- broker와 backend를 동일하게 REDIS_URL로 설정하면, 작업 큐와 결과를 모두 Redis에서 관리
- celery_app 인스턴스 하나를 전역적으로 생성해 필요한 모듈에서 가져다 씁니다.
7.2.5. 작업 정의 및 호출
# app/tasks.py
from app.celery_app import celery_app
import time
@celery_app.task
def long_running_task(param: int) -> str:
# 예: 10초간 대기 후 결과 반환
time.sleep(10)
return f"Processed {param} successfully!"
- @celery_app.task 데코레이터를 사용해 함수를 Celery 작업으로 등록
- 실행은 다음과 같이 진행됩니다:
from app.tasks import long_running_task result = long_running_task.delay(42) # 즉시 반환, 비동기로 처리 # result.get() 을 호출하면 blocking 모드로 실제 결과를 기다릴 수 있음
7.2.6. Celery 워커 실행
celery -A app.celery_app.celery_app worker --loglevel=info
- -A 옵션으로 Celery 앱(여기서는 celery_app)을 지정
- 워커는 별도 프로세스로 실행되며, Redis에 들어오는 작업을 계속 모니터링합니다.
7.3. FastAPI와 Celery 연동 예시
7.3.1. API에서 비동기 작업 트리거
# app/api/v1/endpoints/long_task.py
from fastapi import APIRouter
from app.tasks import long_running_task
router = APIRouter()
@router.post("/start-task")
def start_long_task(param: int):
task_result = long_running_task.delay(param)
return {"task_id": task_result.id}
- 사용자가 /start-task 엔드포인트를 호출하면, Celery 작업이 큐에 들어가고 즉시 응답
- 비동기로 작업이 처리되는 동안 API 서버는 다른 요청을 처리할 수 있습니다.
7.3.2. 작업 상태 조회
@router.get("/task-status/{task_id}")
def get_task_status(task_id: str):
from app.celery_app import celery_app
task_result = celery_app.AsyncResult(task_id)
return {
"task_id": task_id,
"status": task_result.status,
"result": task_result.result if task_result.ready() else None
}
- Celery는 작업의 상태(PENDING, STARTED, SUCCESS, FAILURE 등)와 결과를 추적 가능
- 클라이언트는 주기적으로 이 엔드포인트를 조회하거나, 이벤트(웹소켓 등)를 통해 실시간 상태 업데이트를 받을 수 있습니다.
7.4. Docker를 활용한 배포
7.4.1. Docker의 장점
- 환경 일관성: 모든 종속 라이브러리와 실행 환경을 컨테이너 이미지에 명시하므로, “내 컴퓨터에서만 돌아가는” 문제를 방지합니다.
- 확장성: 동일 이미지를 여러 서버(혹은 컨테이너 오케스트레이션 툴)에서 실행해 트래픽을 쉽게 분산할 수 있습니다.
- 표준화: DevOps 파이프라인(CI/CD)에서 Docker는 사실상 표준으로 자리잡았습니다.
7.4.2. Dockerfile 작성 예시
# Dockerfile
FROM python:3.10-slim
WORKDIR /app
# Poetry 설치
RUN pip install --upgrade pip && pip install poetry
# pyproject.toml, poetry.lock 복사
COPY pyproject.toml poetry.lock /app/
# 라이브러리 설치
RUN poetry install --no-dev --no-interaction --no-ansi
# 소스 코드 복사
COPY . /app
# FastAPI 실행 (개발용: --reload 제외, 실제 배포는 --reload 빼는 게 일반적)
CMD ["poetry", "run", "uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"]
- Python 공식 이미지(python:3.10-slim)를 베이스로 사용
- Poetry를 설치하고, pyproject.toml 기반으로 라이브러리를 설치
- uvicorn으로 FastAPI 앱을 기동
TIP: 프로덕션 환경용 Dockerfile은 캐싱 전략, 멀티 스테이지 빌드 등을 적용해 더 최적화할 수 있습니다.
7.4.3. Docker Compose 예시
# docker-compose.yml
version: '3.8'
services:
web:
build: .
container_name: fastapi-app
ports:
- "80:80"
depends_on:
- redis
worker:
build: .
container_name: celery-worker
command: poetry run celery -A app.celery_app.celery_app worker --loglevel=info
depends_on:
- redis
redis:
image: redis:6.2
container_name: redis-broker
ports:
- "6379:6379"
- web: FastAPI 앱 컨테이너
- worker: Celery 워커 컨테이너
- redis: Redis 컨테이너 (브로커 및 결과 저장소)
- docker-compose up --build로 모든 서비스를 동시에 빌드 & 실행
7.5. 확장 시나리오: AWS, GCP 배포
7.5.1. AWS ECS / EKS
- AWS ECS(Elastic Container Service) 또는 AWS EKS(Elastic Kubernetes Service)를 사용해 Docker 이미지를 클러스터에 배포
- AWS Fargate를 통해 서버리스 방식으로 컨테이너를 동적으로 스케일링 가능
- Elastic Load Balancer(ALB) + Auto Scaling 정책을 설정해 트래픽 증가에 대응
7.5.2. GCP Cloud Run / GKE
- Google Cloud Run: 컨테이너 이미지만 업로드하면, 서버리스 방식으로 HTTP 엔드포인트 제공
- GKE(Google Kubernetes Engine)는 Kubernetes 오케스트레이션 환경을 제공, 대규모 트래픽을 다루는 데 유리
- 각각의 파드(Pod)나 인스턴스에 Celery 워커를 띄워 분산 처리
7.5.3. CI/CD 연동
- GitHub Actions, GitLab CI, Jenkins 등으로 Docker 빌드 → 테스트 → 푸시 → 배포 자동화
- Infra as Code(Terraform, AWS CDK, Pulumi)로 인프라 설정을 버전 관리하면 안정성과 재현성을 극대화
7.6. 실제 활용 사례
7.6.1. 이미지/영상 처리 서비스
- 업로드된 파일은 Celery 비동기 작업으로 넘겨 썸네일 생성 또는 영상 인코딩 작업을 처리
- UI는 즉시 업로드 성공 응답을 받고, 배경에서 변환이 완료되면 별도 알림(이메일, Push, WebSocket 등)을 전송
- Docker Compose 또는 Kubernetes 환경에서 워커 노드를 확장해 대규모 파일 처리를 분산
7.6.2. 대규모 로그 분석
- 요청마다 로그를 DB나 메시지 큐에 저장한 뒤, Celery 작업에서 실시간 분석 또는 통계 집계 수행
- Redis 대신 RabbitMQ나 SQS 같은 다른 브로커로 교체할 수도 있음
- Docker 기반으로 워커들을 스케일 아웃하여 처리량을 늘릴 수 있음
7.6.3. 뉴스레터 발송 시스템
- 여러 만 명에게 동시에 이메일을 발송해야 하는 경우, 동기 방식은 타임아웃/지연 발생
- Celery 워커가 이메일 발송 작업을 병렬로 처리해 빠른 대량 발송 가능
- Docker Swarm이나 Kubernetes로 배포해 배포 자동화 및 스케일링 관리
모범 사례
- 비동기 작업과 DB 트랜잭션 분리: 요청-응답 트랜잭션에서 무거운 로직은 Celery로 넘기고, API는 빠르게 응답
- Idempotent 작업: 비동기 작업은 재시도(Retry) 시에도 중복 문제가 없어야 함
- 모니터링: Celery 작업 대기열, 성공/실패 건수를 Grafana, Prometheus 등으로 확인
- Docker 멀티 스테이지 빌드: 빌드 속도 단축 및 이미지 크기 최적화
- 시크릿 관리: API 키, DB 비밀번호 등 민감 정보는 .env 파일 또는 Docker Secret, Vault 등을 사용해 안전하게 보관
7.7. 마무리
이번 7장에서는 비동기 작업(Celery)과 Docker 기반 배포에 대해 알아보았습니다.
- 비동기 작업이 필요한 이유와 Celery + Redis를 사용해 장시간 작업을 백그라운드에서 처리하는 방법
- Dockerfile, Docker Compose를 통해 FastAPI 앱, Celery 워커, Redis를 한꺼번에 컨테이너로 띄우고 관리하는 과정
- AWS나 GCP와 같은 클라우드 환경에서 확장성을 극대화하는 방법과 CI/CD 연동 개념
다음 8장에서는 예시로 종합 프로젝트 실습(간단한 블로그/게시판 API 등)을 통해 여기까지 배운 내용들을 실제로 융합하여 구현해볼 예정입니다. 비동기 작업, 인증, 배포 전략까지 종합해보면, 프로덕션급 백엔드 시스템 구축 흐름을 한 번에 이해할 수 있을 것입니다. 부디 이번 장의 내용이 실무 개발과 DevOps 업무에 도움이 되길 바랍니다!
'소프트웨어 개발 > 백엔드' 카테고리의 다른 글
📚[FastAPI] 9장. Google 로그인 연동: OAuth2 Social Login 전략과 실습 적용 (0) | 2025.01.26 |
---|---|
📚[FastAPI] 8장. 종합 프로젝트 실습: 간단한 블로그 API 구현하기 (0) | 2025.01.26 |
📚[FastAPI] 6장. 테스트 및 디버깅: Pytest 활용과 품질 보증 (0) | 2025.01.26 |
📚[FastAPI] 5장. 인증 및 권한 관리: JWT를 활용한 보안 강화 (0) | 2025.01.26 |
📚[FastAPI] 4장. API 설계 및 구현: RESTful 엔드포인트와 Pydantic 스키마 (0) | 2025.01.26 |