BackgroundTasks를 활용한 경량 오프로딩
FastAPI에 내장된 BackgroundTasks를 사용해 응답을 차단하지 않고 실행 후 잊는 부수 효과를 처리합니다.
BackgroundTasks를 활용한 경량 오프로딩은(는) CoddyKit의 무료 FastAPI Backend Development Bootcamp 강의입니다. 이것은 4개 중 1번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 FastAPI Backend Development Bootcamp 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. FastAPI Backend Development Bootcamp 강의에는 총 4개의 강의가 포함되어 있습니다.
이 강의의 일부는 아직 번역되지 않았으며 영어로 표시됩니다.
Why Offload Work?
When a client sends a request, they wait for the response. If your endpoint also sends a welcome email, writes an audit log, or warms a cache, the user is stuck waiting for work they don't care about.
Fire-and-forget side effects are tasks that should run after the response is sent, without blocking it:
- Sending notification emails
- Writing analytics or audit logs
- Invalidating or warming caches
- Cleaning up temporary files
FastAPI ships a built-in tool for exactly this: BackgroundTasks.
Declaring BackgroundTasks
To use it, add a parameter typed as BackgroundTasks to your path operation function. FastAPI sees the type annotation and injects an instance for you, just like any other dependency.
You then register work with .add_task(func, *args, **kwargs). The function is not called immediately, it is queued to run once the response has been returned.
from fastapi import BackgroundTasks, FastAPI
app = FastAPI()
def write_log(message: str) -> None:
with open("log.txt", mode="a") as f:
f.write(message + "\n")
@app.post("/signup")
async def signup(email: str, tasks: BackgroundTasks):
tasks.add_task(write_log, f"signup: {email}")
return {"status": "accepted"}The Execution Order
The critical detail: background tasks run after the response is sent to the client, but still within the same server process.
- The endpoint returns its
dictorResponse. - FastAPI flushes the response over the network.
- Only then does it execute each queued task, in the order they were added.
So the user gets an instant 202-style reply while the email or log happens behind the scenes.
Passing Arguments to a Task
Arguments you pass to add_task are stored and forwarded when the task finally runs. Positional and keyword arguments both work.
This pattern keeps the side-effect logic in a plain function that is easy to unit-test in isolation, completely independent of FastAPI.
from fastapi import BackgroundTasks, FastAPI
app = FastAPI()
def send_email(to: str, subject: str, body: str) -> None:
# imagine an SMTP client here
print(f"Sending to {to}: {subject}")
@app.post("/orders")
async def create_order(email: str, tasks: BackgroundTasks):
order_id = 1234
tasks.add_task(
send_email,
to=email,
subject="Order confirmed",
body=f"Your order {order_id} is on the way!",
)
return {"order_id": order_id}Sync vs Async Task Functions
A task function can be either a normal def or an async def.
- An async task is awaited directly on the event loop.
- A regular
deftask is run in a thread pool so it doesn't block the loop.
Rule of thumb: if your side effect does blocking I/O (file writes, a synchronous DB driver), a plain def is fine, FastAPI offloads it to a thread. Use async def only when you genuinely await async I/O.
async def notify_async(user_id: int) -> None:
# awaits an async HTTP client, for example
await some_async_push(user_id)
def notify_sync(user_id: int) -> None:
# blocking call, run in a threadpool by FastAPI
requests_post(user_id)Adding Multiple Tasks
You can call add_task as many times as you like. Tasks run sequentially in the exact order added, each completing before the next begins.
Because they run one after another, a slow task delays the ones queued behind it, but never the HTTP response itself.
from fastapi import BackgroundTasks, FastAPI
app = FastAPI()
@app.post("/publish")
async def publish(post_id: int, tasks: BackgroundTasks):
tasks.add_task(reindex_search, post_id)
tasks.add_task(invalidate_cache, post_id)
tasks.add_task(notify_followers, post_id)
return {"published": post_id}Using BackgroundTasks in Dependencies
A powerful trick: a dependency can also declare a BackgroundTasks parameter and queue tasks. FastAPI merges everything into one shared task set for that request.
This lets cross-cutting concerns, like audit logging, live in a reusable dependency instead of being copy-pasted into every endpoint.
from fastapi import BackgroundTasks, Depends, FastAPI
app = FastAPI()
def audit(action: str, tasks: BackgroundTasks):
tasks.add_task(write_audit_row, action)
return action
@app.delete("/items/{item_id}")
async def delete_item(item_id: int, action=Depends(audit)):
return {"deleted": item_id}A Plain-Python Task Queue Mental Model
Under the hood, BackgroundTasks is little more than a list of callables that get run after the response. You can model the idea in pure Python to build intuition.
The snippet below is standalone, no FastAPI needed, showing the add-then-run-later pattern.
class TaskList:
def __init__(self):
self.tasks = []
def add_task(self, func, *args, **kwargs):
self.tasks.append((func, args, kwargs))
def run_all(self):
for func, args, kwargs in self.tasks:
func(*args, **kwargs)
def log(msg):
print("LOG:", msg)
q = TaskList()
q.add_task(log, "user signed up")
q.add_task(log, "email queued")
print("response sent")
q.run_all()Error Handling Inside Tasks
Because a task runs after the response, you can no longer turn its failure into an HTTP error, the client already got a 200.
An unhandled exception in a background task is logged by the server but is invisible to the client. Always wrap risky work in try/except and decide on retries or a dead-letter strategy yourself.
def send_receipt(order_id: int) -> None:
try:
deliver_email(order_id)
except Exception as exc:
# the client already has its 200, so log and recover here
logger.exception("receipt failed for %s: %s", order_id, exc)
schedule_retry(order_id)The Big Limitation: Same Process
BackgroundTasks runs in the same worker process as your app. That brings real constraints:
- Heavy CPU work still consumes that worker's resources.
- If the process crashes or is redeployed, queued tasks are lost, there is no persistence.
- Tasks don't survive across multiple machines or scale horizontally.
It is perfect for lightweight, best-effort side effects, but not for reliable, long-running, or distributed jobs.
When to Reach for Celery Instead
Choose BackgroundTasks when the work is short, non-critical, and OK to lose on a crash, sending an email, bumping a counter, deleting a temp file.
Reach for Celery or another distributed queue (RQ, Dramatiq, Arq) when you need:
- Durability, jobs survive restarts via a broker like Redis/RabbitMQ.
- Retries, scheduling, and rate limiting.
- Horizontal scaling across dedicated worker machines.
- Heavy CPU jobs that would otherwise starve your web workers.
Quick Check
Test your understanding of when BackgroundTasks is the right tool.
Recap
Key takeaways:
- Add a
BackgroundTasksparameter and calladd_task(func, *args, **kwargs)to defer side effects. - Tasks run after the response, sequentially, in the same worker process.
- Sync
deftasks run in a thread pool;async deftasks run on the event loop. - Dependencies can queue tasks too, great for cross-cutting concerns like auditing.
- No persistence: failures are invisible to the client and tasks die with the process.
- Use it for lightweight, best-effort work; choose Celery for durable, retryable, distributed, or CPU-heavy jobs.
자주 묻는 질문
“BackgroundTasks를 활용한 경량 오프로딩” 강의는 무료인가요?
네 — “BackgroundTasks를 활용한 경량 오프로딩” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 FastAPI Backend Development Bootcamp 강의 전체를 잠금 해제할 수 있습니다. FastAPI Backend Development Bootcamp 강의에는 총 4개의 강의가 포함되어 있습니다.
“BackgroundTasks를 활용한 경량 오프로딩”에서 뭘 배우나요?
FastAPI에 내장된 BackgroundTasks를 사용해 응답을 차단하지 않고 실행 후 잊는 부수 효과를 처리합니다. 브라우저에서 직접 실행하는 실습 코드로 FastAPI Backend Development Bootcamp을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
FastAPI Backend Development Bootcamp을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 FastAPI Backend Development Bootcamp은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 1번째 강의입니다.
“BackgroundTasks를 활용한 경량 오프로딩” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 FastAPI Backend Development Bootcamp 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 FastAPI Backend Development Bootcamp 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.
이 강의의 모든 강의
- BackgroundTasks를 활용한 경량 오프로딩
- Celery 작업자를 FastAPI 앱에 연결
- 재시도, 멱등성 및 배달 불가 메시지 처리
- Celery Beat를 활용한 예약 및 주기적 작업