# Retry 설정으로 재시도 동작 제어하기 ```{admonition} 이 문서에서 배울 내용 :class: tip - `@retry` 데코레이터로 함수에 자동 재시도를 적용하는 방법 - `configure_retry`로 전역 재시도 설정을 변경하는 방법 - 설정 우선순위와 `reset_retry_config`를 통한 초기화 ``` PyQQQ SDK의 `retry` 모듈은 네트워크 오류, API 일시 장애 등 일시적 실패 상황에서 함수를 자동으로 재시도하는 기능을 제공합니다. `@retry` 데코레이터로 개별 함수에 재시도를 적용하고, `configure_retry`로 프로젝트 전체의 재시도 동작을 일괄 제어할 수 있습니다. ## @retry 데코레이터 기본 사용법 `@retry` 데코레이터를 함수에 적용하면, 지정한 예외가 발생했을 때 자동으로 재시도합니다. ```python from pyqqq.utils.retry import retry @retry(Exception, total_tries=3, delay=1, backoff=2) def fetch_price(ticker: str): """주가를 조회합니다. 실패 시 최대 3회 시도합니다.""" response = requests.get(f"https://api.example.com/price/{ticker}") response.raise_for_status() return response.json() ``` **파라미터 설명** | 파라미터 | 기본값 | 설명 | |---|---|---| | `exceptions` | (필수) | 재시도를 트리거할 예외 클래스 또는 튜플 | | `total_tries` | `5` | 최초 시도를 포함한 총 시도 횟수 | | `delay` | `0.5` | 첫 번째 재시도까지의 대기 시간(초) | | `backoff` | `2` | 재시도마다 대기 시간에 곱해지는 배수 | | `silently` | `False` | `True`면 재시도 시 경고 로그를 출력하지 않음 | **backoff 동작 예시** `delay=1, backoff=2, total_tries=4`인 경우 대기 시간은 다음과 같이 증가합니다: ``` 1차 실패 → 1초 대기 → 2차 시도 2차 실패 → 2초 대기 → 3차 시도 3차 실패 → 4초 대기 → 4차 시도 4차 실패 → 예외 발생 ``` ## 특정 예외만 재시도하기 튜플을 사용하면 여러 예외 타입에 대해 재시도할 수 있습니다. 지정하지 않은 예외는 즉시 전파됩니다. ```python from pyqqq.utils.retry import retry import requests @retry((requests.ConnectionError, requests.Timeout), total_tries=5, delay=2, backoff=2) def call_api(): """네트워크 오류와 타임아웃만 재시도합니다.""" resp = requests.get("https://api.example.com/data", timeout=5) resp.raise_for_status() return resp.json() ``` ## configure_retry로 전역 설정 변경하기 프로젝트 전체에서 `@retry`가 적용된 모든 함수의 재시도 동작을 한 번에 변경하려면 `configure_retry`를 사용합니다. 전역 설정은 **데코레이터에 지정된 인자보다 우선 적용**됩니다. ```python from pyqqq.utils.retry import configure_retry # 모든 @retry 함수에 대해 총 시도 횟수를 10회, 초기 대기 시간을 2초로 변경 configure_retry(total_tries=10, delay=2) ``` **RetryConfig 필드** | 필드 | 타입 | 설명 | |---|---|---| | `total_tries` | `int` | 시도할 총 횟수 | | `delay` | `float` | 재시도 간 초기 지연 시간(초) | | `backoff` | `float` | 백오프 배수 | | `silently` | `bool` | `True`면 재시도 로그를 숨김 | ```{important} `configure_retry`에 전달된 인자만 전역 설정에 반영되며, 전달하지 않은 인자는 기존 값을 유지합니다. ``` ### 설정 우선순위 `@retry` 데코레이터가 실행될 때, 각 파라미터는 다음 순서로 결정됩니다: 1. **전역 설정 (`configure_retry`)** — 값이 설정되어 있으면 우선 사용 2. **데코레이터 인자** — 전역 설정이 `None`인 필드에 대해 적용 ```python from pyqqq.utils.retry import retry, configure_retry configure_retry(total_tries=10) @retry(Exception, total_tries=3, delay=1, backoff=2) def my_func(): raise Exception("fail") # total_tries → 10 (전역 설정 우선) # delay → 1 (전역 설정 없음, 데코레이터 인자 사용) # backoff → 2 (전역 설정 없음, 데코레이터 인자 사용) ``` ### 전역 설정 초기화 `reset_retry_config`를 호출하면 전역 설정을 모두 초기 상태(`None`)로 되돌립니다. 초기화 후에는 `@retry` 데코레이터에 지정된 기본값이 그대로 적용됩니다. ```python from pyqqq.utils.retry import configure_retry, reset_retry_config configure_retry(total_tries=10, delay=5) # 전역 설정 초기화 reset_retry_config() # 이후 @retry 데코레이터는 각자 지정한 기본값으로 동작합니다. ``` ## 활용 사례 **전략 진입점에서 전역 설정 적용하기** 전략 스크립트의 시작 부분에서 `configure_retry`를 호출하면, 이후 모든 API 호출 함수의 재시도 동작을 일괄 제어할 수 있습니다. ```python from pyqqq.utils.retry import configure_retry, retry # 전략 시작 시 전역 retry 설정 configure_retry(total_tries=10, delay=1, backoff=2, silently=False) @retry(Exception) def get_market_data(): ... @retry(Exception) def submit_order(ticker, qty, price): ... ``` **운영 환경에 따라 설정 분리하기** 개발 환경에서는 빠른 실패 확인을 위해 적은 재시도 횟수를, 운영 환경에서는 안정성을 위해 많은 재시도 횟수를 설정할 수 있습니다. ```python import os from pyqqq.utils.retry import configure_retry if os.getenv("ENV") == "production": configure_retry(total_tries=15, delay=2, backoff=2, silently=True) else: configure_retry(total_tries=2, delay=0.1, backoff=1) ``` **로그 숨기기** 재시도 경고 로그가 과도하게 출력되는 경우, `silently=True`로 설정하면 재시도 과정의 로그를 숨길 수 있습니다. 단, 최종 실패 시에는 항상 에러 로그가 출력됩니다. ```python from pyqqq.utils.retry import configure_retry configure_retry(silently=True) ``` ```{admonition} 다음 단계 :class: tip - [retry API 레퍼런스](../reference/utils/retry) — `RetryConfig`, `configure_retry`, `reset_retry_config`, `retry`의 전체 시그니처와 파라미터 설명 ```