Python에서 콜백(callback) 함수는 비동기 작업이 완료된 후 호출되는 함수입니다. 콜백 함수를 사용하는 주된 이유는 비동기 작업이 완료될 때까지 기다리지 않고 다른 작업을 수행할 수 있도록 하기 위함입니다. 이번 글에서는 콜백 함수의 개념과 활용 방법에 대해 설명하겠습니다.
콜백 함수는 다른 함수의 인수로 전달되어, 특정 이벤트나 작업이 완료되었을 때 호출되는 함수입니다. 비동기 작업에서 많이 사용됩니다. 예를 들어, 네트워크 요청이 완료되었을 때 콜백 함수를 호출하여 응답 데이터를 처리할 수 있습니다.
아래 예시는 aiohttp 라이브러리를 사용하여 비동기 HTTP 요청을 수행하고, 요청이 완료된 후 콜백 함수를 호출하는 예제입니다.
import aiohttp
import asyncio
async def get_data(callback):
async with aiohttp.ClientSession() as session:
async with session.get('https://jsonplaceholder.typicode.com/posts/1') as response:
data = await response.json()
callback(data)
def print_data(data):
print(data)
async def main():
await get_data(print_data)
asyncio.run(main())
위 코드에서 get_data 함수는 비동기적으로 데이터를 가져오고, 데이터가 준비되면 callback 함수를 호출합니다. print_data 함수가 콜백 함수로 사용되어 데이터를 출력합니다.
콜백 지옥(callback hell)은 콜백 함수를 연속적으로 사용할 때 발생하는 문제입니다. 이는 코드의 가독성과 유지보수성을 떨어뜨립니다. 예를 들어, 아래와 같은 코드가 있습니다.
def step1(callback):
# 비동기 작업 수행
callback('step1 result')
def step2(callback):
# 비동기 작업 수행
callback('step2 result')
def step3(callback):
# 비동기 작업 수행
callback('step3 result')
step1(lambda result1: step2(lambda result2: step3(lambda result3: print(result3))))
위 코드는 비동기 작업을 연속적으로 처리하기 위해 콜백 함수를 중첩해서 사용하고 있습니다. 이는 매우 복잡하고 가독성이 떨어집니다.
콜백 지옥을 해결하기 위해 콜백 함수를 분리하여 가독성을 높일 수 있습니다.
def step1(callback):
callback('step1 result')
def step2(callback):
callback('step2 result')
def step3(callback):
callback('step3 result')
def step3_callback(result3):
print(result3)
def step2_callback(result2):
step3(step3_callback)
def step1_callback(result1):
step2(step2_callback)
step1(step1_callback)
위 코드는 콜백 함수를 각각의 단계로 분리하여 호출하는 방식으로, 코드의 가독성과 유지보수성을 높였습니다.
FastAPI를 사용하여 비동기 HTTP 요청을 처리하고, 콜백 함수를 사용하는 API 서버를 구현할 수 있습니다.
from fastapi import FastAPI
import aiohttp
import asyncio
app = FastAPI()
async def fetch_data(callback):
async with aiohttp.ClientSession() as session:
async with session.get('https://jsonplaceholder.typicode.com/posts/1') as response:
data = await response.json()
callback(data)
def process_data(data):
print(data)
@app.get("/data")
async def get_data():
await fetch_data(process_data)
return {"status": "Data processed"}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="127.0.0.1", port=8000)
위 예제에서 fetch_data 함수는 비동기적으로 데이터를 가져오고, 데이터가 준비되면 process_data 콜백 함수를 호출합니다. /data 엔드포인트에서 이를 호출하여 클라이언트에 응답합니다.
이와 같이, 콜백 함수를 사용하면 비동기 작업이 완료된 후 원하는 동작을 수행할 수 있으며, Python에서도 비동기 처리를 통해 효율적으로 고성능 서버를 구축할 수 있습니다.