목차
1. Celery란?
Celery는 Python에서 가장 널리 사용되는 분산 작업 큐, Distributed Task Queue 프레임워크입니다. 웹 애플리케이션에서 시간이 오래 걸리는 작업을 즉시 처리하지 않고, 별도의 Worker 프로세스에게 맡겨 백그라운드에서 실행하도록 도와줍니다.
공식 문서에서도 Celery를 “실시간 처리에 초점을 둔 Task Queue이며, 스케줄링도 지원하는 분산 시스템”으로 설명합니다.
예를 들어 다음과 같은 작업에 Celery를 사용할 수 있습니다.
회원가입 이메일 발송
대용량 엑셀 파일 생성
이미지 리사이징
PDF 생성
외부 API 호출
크롤링 작업
AI 모델 추론
정기 배치 작업
로그 분석
알림 발송즉, Celery는 웹 서버 옆에서 묵묵히 일하는 “백그라운드 작업 공장”입니다. 웹 서버가 손님을 응대하는 카운터라면, Celery Worker는 주방 안쪽에서 오래 걸리는 요리를 처리하는 셰프에 가깝습니다.
2. Celery가 필요한 이유
일반적인 웹 요청은 빠르게 응답해야 합니다.
예를 들어 사용자가 회원가입 버튼을 눌렀을 때 서버가 다음 작업을 모두 직접 처리한다고 가정해 보겠습니다.
1. 사용자 정보 저장
2. 이메일 인증 메일 발송
3. 가입 쿠폰 생성
4. 추천 시스템 업데이트
5. 관리자 알림 발송이 모든 작업을 한 번에 처리하면 사용자는 응답을 오래 기다려야 합니다. 이때 Celery를 사용하면 핵심 작업만 즉시 처리하고, 나머지는 Worker에게 넘길 수 있습니다.
웹 서버:
사용자 정보 저장 후 즉시 응답
Celery Worker:
이메일 발송
쿠폰 생성
알림 발송
로그 처리이렇게 하면 서비스 응답 속도, 안정성, 확장성이 크게 좋아집니다.
3. Celery의 핵심 구조
Celery는 보통 다음 구성 요소로 동작합니다.
Client / Web App
↓
Message Broker
↓
Celery Worker
↓
Result Backend3.1 Client 또는 Producer
작업을 요청하는 애플리케이션입니다. Django, Flask, FastAPI, 일반 Python 스크립트 등이 될 수 있습니다.
예를 들어 FastAPI 서버에서 “이메일 보내기 작업을 실행해줘”라고 Celery에 요청할 수 있습니다.
3.2 Broker
Broker는 작업 메시지를 임시로 저장하고 Worker에게 전달하는 중간 큐입니다.
대표적으로 다음 도구를 사용합니다.
Redis
RabbitMQ
Amazon SQSCelery 공식 문서에서는 RabbitMQ와 Redis 조합이 흔히 사용되며, Redis는 빠른 메시지 처리에 적합하고 RabbitMQ는 메시지 브로커로 널리 사용된다고 설명합니다.
실무에서는 간단한 프로젝트나 내부 시스템에서는 Redis를 많이 사용하고, 메시지 안정성과 라우팅이 중요한 시스템에서는 RabbitMQ를 선택하는 경우가 많습니다.
3.3 Worker
Worker는 실제 작업을 수행하는 프로세스입니다.
celery -A app worker --loglevel=infoWorker는 Broker에서 작업을 가져와 실행합니다.
예를 들어 다음 작업을 처리할 수 있습니다.
send_email()
generate_report()
resize_image()
sync_external_api()Worker는 여러 대로 확장할 수 있습니다.
Worker 1
Worker 2
Worker 3
Worker 4작업량이 많아지면 Worker 수를 늘려 처리량을 높일 수 있습니다.
3.4 Result Backend
Result Backend는 작업 실행 결과를 저장하는 공간입니다.
예를 들어 다음 정보를 저장합니다.
작업 성공 여부
작업 결과값
실패 에러 메시지
작업 상태Redis, Database, RPC Backend 등을 사용할 수 있습니다. 단순히 작업만 실행하고 결과가 필요 없다면 Result Backend를 사용하지 않을 수도 있습니다.
4. Celery 설치 방법
4.1 Python 가상환경 생성
mkdir celery-demo
cd celery-demo
python -m venv venv
source venv/bin/activateWindows에서는 다음 명령을 사용합니다.
venv\Scripts\activate4.2 Celery 설치
pip install celeryRedis를 Broker로 사용할 경우 다음 패키지도 설치합니다.
pip install redis또는 한 번에 설치할 수 있습니다.
pip install "celery[redis]"Celery 공식 Getting Started 문서에서도 Celery 설치 후 Broker 선택, 애플리케이션 생성, Worker 실행, Task 호출 순서로 기본 흐름을 안내합니다.
4.3 Redis 실행
Docker를 사용하면 Redis를 쉽게 실행할 수 있습니다.
docker run -d \
--name redis \
-p 6379:6379 \
redis:7Redis 실행 확인:
docker ps5. 가장 간단한 Celery 예제
5.1 프로젝트 구조
celery-demo/
├── tasks.py
└── run_task.py5.2 tasks.py 작성
from celery import Celery
app = Celery(
"tasks",
broker="redis://localhost:6379/0",
backend="redis://localhost:6379/1"
)
@app.task
def add(x, y):
return x + y여기서 중요한 부분은 다음과 같습니다.
broker="redis://localhost:6379/0"작업 메시지를 Redis 0번 DB에 저장합니다.
backend="redis://localhost:6379/1"작업 결과를 Redis 1번 DB에 저장합니다.
5.3 Worker 실행
터미널에서 다음 명령을 실행합니다.
celery -A tasks worker --loglevel=info정상 실행되면 Worker가 작업을 기다리는 상태가 됩니다.
5.4 작업 호출
새 터미널을 열고 Python Shell을 실행합니다.
python다음 코드를 입력합니다.
from tasks import add
result = add.delay(10, 20)
print(result.id)
print(result.get(timeout=10))결과:
30delay()는 Celery 작업을 비동기로 요청하는 가장 기본적인 방법입니다. 즉시 결과를 계산하는 것이 아니라 Broker에 작업을 등록하고 Worker가 처리하도록 넘깁니다.
6. delay()와 apply_async() 차이
Celery 작업 호출에는 대표적으로 두 가지 방식이 있습니다.
6.1 delay()
가장 간단한 비동기 실행 방식입니다.
send_email.delay("user@example.com")내부적으로는 apply_async()의 축약형입니다.
6.2 apply_async()
더 세밀한 제어가 필요할 때 사용합니다.
send_email.apply_async(
args=["user@example.com"],
countdown=10
)10초 뒤 실행:
task.apply_async(countdown=10)특정 시간에 실행:
from datetime import datetime, timedelta
task.apply_async(
eta=datetime.utcnow() + timedelta(minutes=5)
)Queue 지정:
task.apply_async(queue="email")7. 실무형 프로젝트 구조
간단한 예제에서는 tasks.py 하나만 사용해도 됩니다. 하지만 실무에서는 구조를 분리하는 것이 좋습니다.
my_project/
├── app/
│ ├── __init__.py
│ ├── celery_app.py
│ ├── tasks/
│ │ ├── __init__.py
│ │ ├── email_tasks.py
│ │ └── report_tasks.py
│ └── main.py
├── requirements.txt
└── docker-compose.yml8. Celery 설정 분리하기
app/celery_app.py
from celery import Celery
celery_app = Celery(
"my_project",
broker="redis://localhost:6379/0",
backend="redis://localhost:6379/1",
include=[
"app.tasks.email_tasks",
"app.tasks.report_tasks"
]
)
celery_app.conf.update(
task_serializer="json",
result_serializer="json",
accept_content=["json"],
timezone="Asia/Seoul",
enable_utc=False,
)app/tasks/email_tasks.py
import time
from app.celery_app import celery_app
@celery_app.task
def send_email_task(email: str, subject: str):
time.sleep(3)
print(f"Send email to {email}: {subject}")
return {
"email": email,
"status": "sent"
}app/tasks/report_tasks.py
import time
from app.celery_app import celery_app
@celery_app.task
def generate_report_task(user_id: int):
time.sleep(5)
return {
"user_id": user_id,
"file_path": f"/reports/{user_id}.pdf"
}Worker 실행
celery -A app.celery_app.celery_app worker --loglevel=info9. FastAPI와 Celery 연동 예제
Celery는 Flask, Django, FastAPI 같은 웹 프레임워크와 함께 많이 사용됩니다. Flask 공식 문서에서도 Celery를 단순 백그라운드 작업부터 복잡한 다단계 프로그램과 스케줄 작업까지 사용할 수 있는 강력한 Task Queue로 소개합니다.
FastAPI 예제는 다음과 같습니다.
app/main.py
from fastapi import FastAPI
from app.tasks.email_tasks import send_email_task
from app.tasks.report_tasks import generate_report_task
from app.celery_app import celery_app
api = FastAPI()
@api.post("/emails")
def send_email(email: str, subject: str):
task = send_email_task.delay(email, subject)
return {
"message": "Email task submitted",
"task_id": task.id
}
@api.post("/reports/{user_id}")
def create_report(user_id: int):
task = generate_report_task.delay(user_id)
return {
"message": "Report generation started",
"task_id": task.id
}
@api.get("/tasks/{task_id}")
def get_task_status(task_id: str):
result = celery_app.AsyncResult(task_id)
return {
"task_id": task_id,
"status": result.status,
"result": result.result if result.ready() else None
}FastAPI 실행:
uvicorn app.main:api --reloadCelery Worker 실행:
celery -A app.celery_app.celery_app worker --loglevel=info10. Docker Compose로 Celery 구축하기
실무에서는 Redis, API 서버, Celery Worker를 함께 구성하는 경우가 많습니다.
docker-compose.yml
version: "3.9"
services:
redis:
image: redis:7
container_name: celery-redis
ports:
- "6379:6379"
api:
build: .
container_name: celery-api
command: uvicorn app.main:api --host 0.0.0.0 --port 8000
volumes:
- .:/code
working_dir: /code
ports:
- "8000:8000"
depends_on:
- redis
worker:
build: .
container_name: celery-worker
command: celery -A app.celery_app.celery_app worker --loglevel=info
volumes:
- .:/code
working_dir: /code
depends_on:
- redisDockerfile
FROM python:3.12-slim
WORKDIR /code
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .requirements.txt
fastapi
uvicorn
celery[redis]
redis실행:
docker compose up --build11. Celery Beat로 정기 작업 실행하기
Celery는 단순 비동기 작업뿐 아니라 스케줄링도 지원합니다. 정기적으로 실행해야 하는 작업에는 Celery Beat를 사용합니다.
예시:
매일 새벽 1시 로그 정리
매시간 뉴스 수집
5분마다 외부 API 동기화
매주 월요일 리포트 생성celery_app.py에 Beat 설정 추가
from celery.schedules import crontab
celery_app.conf.beat_schedule = {
"run-every-minute": {
"task": "app.tasks.report_tasks.generate_report_task",
"schedule": 60.0,
"args": (1,)
},
"run-daily-report": {
"task": "app.tasks.report_tasks.generate_report_task",
"schedule": crontab(hour=1, minute=0),
"args": (100,)
},
}Beat 실행:
celery -A app.celery_app.celery_app beat --loglevel=infoWorker와 Beat를 함께 실행해야 합니다.
celery -A app.celery_app.celery_app worker --loglevel=info
celery -A app.celery_app.celery_app beat --loglevel=info12. Queue 분리 전략
작업 종류에 따라 Queue를 분리하면 운영이 쉬워집니다.
email queue
report queue
ai queue
crawler queue예를 들어 이메일 작업과 AI 추론 작업을 같은 Worker에서 처리하면, 무거운 AI 작업 때문에 이메일 발송이 지연될 수 있습니다.
작업별 Queue 지정
send_email_task.apply_async(
args=["user@example.com", "Welcome"],
queue="email"
)
generate_report_task.apply_async(
args=[10],
queue="report"
)Queue별 Worker 실행
celery -A app.celery_app.celery_app worker \
-Q email \
--loglevel=infocelery -A app.celery_app.celery_app worker \
-Q report \
--loglevel=info이렇게 하면 작업 성격에 따라 Worker를 독립적으로 운영할 수 있습니다.
13. Retry 처리
외부 API 호출, 이메일 발송, 파일 업로드 같은 작업은 실패할 수 있습니다. Celery에서는 Retry를 통해 자동 재시도를 구현할 수 있습니다.
from app.celery_app import celery_app
@celery_app.task(
bind=True,
max_retries=3,
default_retry_delay=10
)
def call_external_api(self, url: str):
try:
# 외부 API 호출 로직
raise Exception("Temporary API error")
except Exception as exc:
raise self.retry(exc=exc)설명:
max_retries=3
최대 3번 재시도
default_retry_delay=10
10초 후 재시도
bind=True
Task 자기 자신 self에 접근 가능실무에서는 Retry 설정이 매우 중요합니다. 특히 외부 API, 네트워크, 결제, 메일 발송처럼 실패 가능성이 있는 작업은 반드시 재시도 전략을 설계해야 합니다.
14. Timeout 설정
작업이 무한정 실행되면 Worker 자원을 잡아먹을 수 있습니다. 따라서 Time Limit을 설정하는 것이 좋습니다.
@celery_app.task(
time_limit=300,
soft_time_limit=240
)
def long_running_task():
passsoft_time_limit
작업에 종료 신호를 보냄
time_limit
강제 종료15. 작업 상태 확인
Celery 작업은 다음과 같은 상태를 가질 수 있습니다.
PENDING
STARTED
SUCCESS
FAILURE
RETRY
REVOKED상태 확인 예제:
result = celery_app.AsyncResult(task_id)
print(result.status)
print(result.ready())
print(result.successful())
print(result.result)API로 제공하면 프론트엔드에서 작업 진행 상태를 조회할 수 있습니다.
POST /reports/1
→ task_id 반환
GET /tasks/{task_id}
→ 현재 상태 조회16. Flower로 Celery 모니터링하기
Flower는 Celery 작업을 웹 UI로 모니터링할 수 있는 도구입니다.
설치:
pip install flower실행:
celery -A app.celery_app.celery_app flower접속:
http://localhost:5555Flower에서 확인할 수 있는 정보:
Worker 상태
Task 성공 / 실패 내역
실행 중인 작업
작업 소요 시간
Queue 상태
Retry 정보운영 환경에서는 Flower에 인증을 붙이는 것이 좋습니다.
celery -A app.celery_app.celery_app flower \
--basic_auth=admin:password17. Celery와 Redis 사용 시 주의점
Redis는 빠르고 설정이 간단하지만 모든 상황에 만능은 아닙니다.
주의할 점:
Redis 장애 시 작업 유실 가능성 검토 필요
메모리 사용량 모니터링 필요
대량 작업 적재 시 메모리 압박 발생 가능
중요 작업은 ACK, Retry, Visibility Timeout 고려 필요
결과 Backend를 무기한 보관하지 않도록 설정 필요결과 만료 시간 설정:
celery_app.conf.result_expires = 36001시간 후 결과를 만료시킵니다.
18. RabbitMQ를 사용하는 경우
RabbitMQ는 메시지 브로커로서 기능이 풍부합니다.
장점:
안정적인 메시지 브로커
라우팅 기능 우수
Exchange / Queue / Routing Key 구조 제공
복잡한 메시징 패턴에 적합RabbitMQ Broker URL 예시:
broker="amqp://guest:guest@localhost:5672//"Docker 실행:
docker run -d \
--name rabbitmq \
-p 5672:5672 \
-p 15672:15672 \
rabbitmq:3-management관리 콘솔:
http://localhost:15672기본 계정:
guest / guest19. Celery 실무 활용 시나리오
19.1 이메일 발송 시스템
회원가입
비밀번호 재설정
결제 완료 알림
공지 메일 발송웹 요청에서 직접 SMTP를 호출하지 않고 Celery로 넘기면 응답 속도가 좋아집니다.
19.2 대용량 파일 생성
엑셀 다운로드
PDF 보고서 생성
CSV Export
정산 파일 생성사용자는 요청만 보내고, 서버는 작업 ID를 반환합니다. 작업이 완료되면 다운로드 링크를 제공할 수 있습니다.
19.3 크롤링 / 뉴스 수집
RSS 수집
외부 뉴스 API 호출
본문 파싱
요약 생성
DB 저장뉴스 수집 시스템에서는 Celery Beat와 Worker 조합이 매우 유용합니다.
Celery Beat:
매 5분마다 수집 작업 생성
Celery Worker:
뉴스 수집, 중복 검사, 저장, 요약 처리19.4 AI / RAG 시스템
AI 서비스에서도 Celery는 자주 사용됩니다.
문서 임베딩
벡터 DB 저장
대용량 PDF 파싱
LLM 요약
외부 지식 동기화
비동기 질의 처리특히 RAG 시스템에서는 문서 업로드 후 다음 작업을 백그라운드로 넘기는 구조가 자연스럽습니다.
파일 업로드
→ Celery 작업 생성
→ PDF 파싱
→ Chunk 분리
→ Embedding 생성
→ Vector DB 저장
→ 완료 상태 업데이트20. 운영 환경에서 고려해야 할 설정
20.1 Worker 동시성
celery -A app.celery_app.celery_app worker \
--concurrency=4 \
--loglevel=infoCPU 작업이 많다면 CPU 코어 수를 고려해야 합니다. I/O 작업이 많다면 더 많은 동시성을 줄 수 있습니다.
20.2 Prefetch 설정
Worker가 한 번에 너무 많은 작업을 가져가면 특정 Worker에 작업이 몰릴 수 있습니다.
celery_app.conf.worker_prefetch_multiplier = 1무거운 작업이 많은 경우 유용합니다.
20.3 작업 ACK 설정
작업이 완료된 뒤 ACK를 보내도록 설정할 수 있습니다.
celery_app.conf.task_acks_late = TrueWorker가 작업 도중 죽었을 때 작업을 다시 처리하게 만들 수 있습니다.
20.4 결과 저장 비활성화
결과가 필요 없는 작업은 Backend 저장을 끄는 것이 좋습니다.
@celery_app.task(ignore_result=True)
def send_log():
pass21. Celery 사용 시 흔한 문제
21.1 Task가 등록되지 않음
증상:
Received unregistered task원인:
include 설정 누락
import 경로 오류
Worker 재시작 누락
패키지 구조 문제해결:
include=[
"app.tasks.email_tasks",
"app.tasks.report_tasks"
]21.2 Redis 연결 실패
증상:
Error 111 connecting to localhost:6379. Connection refused.확인:
docker ps
redis-cli ping정상 응답:
PONGDocker 환경에서는 localhost 대신 서비스명을 사용해야 합니다.
broker="redis://redis:6379/0"21.3 작업 결과가 안 나옴
원인:
backend 미설정
ignore_result=True
작업 실패
Result Backend 연결 오류확인:
result.status
result.result21.4 Worker가 작업을 처리하지 않음
확인할 것:
Worker 실행 여부
Broker 연결 여부
Queue 이름 일치 여부
Task import 여부
로그 에러 여부22. Celery vs APScheduler vs RQ
| 구분 | Celery | APScheduler | RQ |
|---|---|---|---|
| 주요 목적 | 분산 작업 큐 | 스케줄러 | Redis 기반 간단 작업 큐 |
| Broker | Redis, RabbitMQ 등 | 필요 없음 | Redis |
| Worker 분산 | 강함 | 약함 | 보통 |
| 정기 작업 | Celery Beat | 강함 | 별도 구성 필요 |
| 난이도 | 중간 | 쉬움 | 쉬움 |
| 실무 확장성 | 높음 | 제한적 | 중간 |
간단한 스케줄만 필요하다면 APScheduler도 충분합니다. 하지만 대량 비동기 작업, Worker 확장, Retry, Queue 분리, 모니터링이 필요하다면 Celery가 더 적합합니다.
23. Celery를 도입하면 좋은 경우
Celery는 다음 상황에서 특히 유용합니다.
요청 시간이 오래 걸리는 작업이 있다
외부 API 호출이 많다
이메일 / 알림 발송이 많다
파일 생성 작업이 있다
정기 배치 작업이 필요하다
작업 실패 시 재시도가 필요하다
Worker를 여러 대로 확장해야 한다
AI, RAG, 크롤링, 로그 처리 작업이 있다반대로 다음 경우에는 과할 수 있습니다.
단순 CRUD 서비스
작업량이 매우 적은 서비스
서버 1대에서 간단한 스케줄만 필요한 경우
운영 복잡도를 감당하기 어려운 경우24. 실무 권장 아키텍처
소규모 서비스:
FastAPI / Django
+ Redis
+ Celery Worker
+ Celery Beat중규모 서비스:
API Server
+ Redis or RabbitMQ
+ Worker Group
+ Flower
+ Database
+ Object Storage대규모 서비스:
API Gateway
+ Multiple Queues
+ RabbitMQ / Redis Cluster / SQS
+ Worker Auto Scaling
+ Observability Stack
+ Dead Letter Queue
+ Retry Policy
+ Result Store25. 결론
Celery는 Python 백엔드 개발에서 비동기 작업과 분산 처리를 구현할 때 매우 강력한 도구입니다.
웹 서버가 모든 일을 직접 처리하면 응답 속도와 안정성이 떨어질 수 있습니다. Celery를 도입하면 오래 걸리는 작업을 Worker에게 위임하고, 웹 서버는 빠르게 응답할 수 있습니다.
핵심은 다음과 같습니다.
Celery는 Python의 대표적인 분산 작업 큐이다.
Redis나 RabbitMQ 같은 Broker가 필요하다.
Worker가 실제 작업을 수행한다.
Result Backend를 통해 작업 결과를 조회할 수 있다.
Celery Beat로 정기 작업을 실행할 수 있다.
Retry, Queue 분리, Timeout 설정이 실무 운영의 핵심이다.Celery는 단순한 백그라운드 작업 도구를 넘어, 서비스 내부의 작업 흐름을 분리하고 확장 가능한 구조로 만드는 비동기 처리 엔진입니다.
이메일 발송, 파일 생성, 뉴스 수집, AI 문서 처리, RAG 파이프라인, 배치 작업까지 Python 기반 서비스에서 “오래 걸리는 일”이 등장한다면 Celery는 매우 좋은 선택지가 될 수 있습니다.
