Celery는 비동기 작업 처리를 지원하는 강력한 프레임워크입니다. @shared_task 데코레이터는 작업(Task)을 정의하는 데 사용되며, 특히 옵션인 bind, autoretry_for, retry_backoff, max_retries를 활용하면 작업의 안정성과 자동화된 오류 처리를 크게 향상시킬 수 있습니다.

이번 글에서는 각 옵션의 동작 방식과 활용법을 살펴보며, 자주 발생하는 혼동과 함께 이를 해결하는 최선의 방법을 소개합니다.


1. bind=True

정의

bind=True는 현재 작업(Task)을 첫 번째 매개변수로 전달하여 작업 내부에서 self를 사용할 수 있도록 합니다. 이를 통해 작업의 상태, 메서드, 속성 등에 접근할 수 있습니다.

주요 기능

  • 작업 상태에 접근 가능: 작업 ID, 요청 정보 등에 접근하여 상태를 확인하거나 로깅할 수 있습니다.
  • 명시적 재시도 로직: self.retry() 메서드를 통해 재시도 로직을 수동으로 구현할 수 있습니다.

예시

@shared_task(bind=True)
def my_task(self, some_arg):
    print(f"Task ID: {self.request.id}")  # 작업 ID 출력
    self.retry()  # 작업 재시도

2. autoretry_for=(ExceptionType, ...)

정의

지정된 예외 유형이 발생했을 때, Celery가 작업을 자동으로 재시도(retry)하도록 설정합니다. 개발자가 self.retry()를 명시적으로 호출하지 않아도 예외를 처리하고 재시도를 자동화합니다.

주의점

  • autoretry_for를 사용하는 경우: 재시도가 자동으로 이루어지므로 self.retry()와 중복되지 않도록 사용해야 합니다.
  • 혼용 시 문제점: autoretry_forself.retry()를 동시에 사용하면 동일한 예외에 대해 중복 재시도가 발생할 수 있습니다.

예시

권장 방식: autoretry_for만 사용하는 경우
import requests

@shared_task(bind=True, autoretry_for=(requests.RequestException,), retry_backoff=True)
def my_task(self, url):
    response = requests.get(url)
    response.raise_for_status()  # 상태 코드가 200이 아니면 예외 발생
특정 조건에서만 명시적으로 재시도 (self.retry())를 사용하는 경우
import requests

@shared_task(bind=True, retry_backoff=True, max_retries=5)
def my_task(self, url):
    try:
        response = requests.get(url)
        response.raise_for_status()
    except requests.RequestException as e:
        print(f"Retrying due to error: {e}")
        self.retry(exc=e)  # 명시적 재시도

3. retry_backoff=True

정의

작업 재시도 간격을 점진적으로 증가시키는 지수 백오프(Exponential Backoff)를 활성화합니다. 첫 번째 재시도는 즉시, 이후 재시도는 1초, 2초, 4초...와 같이 간격이 증가합니다.

주요 기능

  • 서버 부하를 줄이고 네트워크 장애를 효율적으로 처리.
  • 백오프 시간은 Celery의 기본 설정을 통해 커스터마이징 가능.

예시

@shared_task(bind=True, autoretry_for=(requests.RequestException,), retry_backoff=True)
def my_task(self):
    # 첫 번째 재시도는 1초 후, 두 번째 재시도는 2초 후...
    raise requests.RequestException("Simulated failure")

4. max_retries

정의

작업의 최대 재시도 횟수를 제한합니다. 작업이 지정된 횟수만큼 재시도한 후에도 성공하지 못하면, 작업은 실패로 기록됩니다.

주요 기능

  • 무한 재시도를 방지하여 서버 자원 소모를 제한.
  • 실패 조건에 따라 작업을 기록하거나 다른 로직을 실행 가능.

예시

@shared_task(bind=True, autoretry_for=(requests.RequestException,), retry_backoff=True, max_retries=5)
def my_task(self):
    raise requests.RequestException("Simulated failure")

5. 혼용 시 주의사항: autoretry_for vs self.retry()

올바른 사용 가이드

  1. autoretry_for 사용 시: 자동 재시도가 설정되므로, self.retry()를 명시적으로 호출하지 않아도 됩니다. 단순히 특정 예외에 대해 작업을 재시도할 때 간결한 코드를 작성할 수 있습니다.
  2. self.retry() 사용 시: 재시도 전 추가 작업(예: 로그 작성, 특정 조건 검사)이 필요할 때 사용합니다. autoretry_for와 중복되지 않도록 주의하세요.

6. 옵션 요약

옵션 설명
bind=True 작업에서 self를 통해 작업 상태와 메서드에 접근 가능.
autoretry_for 특정 예외가 발생했을 때 작업을 자동으로 재시도.
retry_backoff 재시도 간격을 점진적으로 증가시키는 지수 백오프(Exponential Backoff) 활성화.
max_retries 최대 재시도 횟수를 제한하여 작업 실패 조건 정의.
Understanding Celery @shared_task Options

7. 결론

Celery의 @shared_task 옵션은 작업 실패를 효과적으로 처리하고 안정성을 높이는 데 유용합니다.

  • autoretry_for를 사용하는 경우: 재시도 로직이 자동화되므로, self.retry()와 중복되지 않도록 주의하세요.
  • 조건부 로직이나 추가 작업이 필요하다면 self.retry()를 활용할 수 있습니다.

Celery를 활용한 작업을 안정적으로 구현하려면 이러한 옵션을 조합하여 상황에 맞게 최적화된 코드를 작성해보세요! 😊