운세 생성 API에 타임아웃과 재시도 로직을 추가해 안정성 개선
목차
외부 API 호출 시 타임아웃과 재시도 로직을 추가했다. 단순해 보이는 변경이지만, 프로덕션 안정성 관점에서는 꽤 중요한 결정이었다.
외부 API 호출의 불안정성을 감안한 방어 로직
운이 필요한 기능(fortune)을 생성하는 과정에서 OpenAI API를 호출하는데, 네트워크 지연이나 서드파티 서버 응답 지연으로 인한 타임아웃이 발생하곤 했다. 처음엔 "API가 느릴 수도 있지" 하고 넘어갔지만, 이게 누적되면서 사용자가 체감하는 응답 지연이 눈에 띄게 늘어났다. 특히 특정 시간대에 OpenAI 서비스가 느려지면 우리 서비스도 함께 느려지는 악순환이 반복됐다.
단순히 타임아웃을 늘리는 건 근본 해결이 아니다. 90초로 설정한 이유는 사용자가 체감하기에 합리적인 대기 시간(보통 1분 30초 정도)이면서도, 진짜 문제가 있는 상황(네트워크 단절, API 완전 다운 등)을 너무 오래 기다리지 않도록 하기 위한 균형이다. 이보다 길면 사용자는 "혹시 버그인가" 하고 새로고침을 반복할 가능성이 높아진다.
재시도 로직의 필요성과 설계 원칙
단일 시도로 끝내면 일시적 네트워크 지연이나 API 과부하로 실패한 요청이 그냥 떨어진다. 하지만 대부분의 경우 몇 초 후 재시도하면 성공한다. 그래서 최대 3회 재시도를 추가했다.
// 개념적 흐름 (실제 구현은 다를 수 있음)
const MAX_RETRIES = 3;
const TIMEOUT_MS = 90000;
async function callOpenAIWithRetry(prompt) {
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
try {
return await callOpenAI(prompt, { timeout: TIMEOUT_MS });
} catch (error) {
if (attempt === MAX_RETRIES) throw error;
// 지수 백오프: 1초, 2초, 4초 식으로 대기
await sleep(Math.pow(2, attempt - 1) * 1000);
}
}
}
여기서 중요한 건 재시도 간에 대기 시간(backoff)을 두는 것이다. 바로 다시 시도하면 같은 원인(API 과부하)에서 또 실패할 가능성이 크다. 지수 백오프(exponential backoff) 패턴을 쓰면 첫 재시도는 1초, 두 번째는 2초 정도로 기다렸다가 다시 시도한다. 이렇게 하면 서버 부하를 덜 주면서도 성공 가능성을 높인다.
팀 관점에서의 고려사항
이 수정이 병합될 때 팀과 나눈 포인트는 다음과 같았다:
- 사용자 경험: 타임아웃이 짧으면 정상 요청도 자주 실패하고, 길면 사용자가 "버그인가" 하고 새로고침을 반복한다. 90초는 이 둘의 균형이다.
- API 비용: 재시도가 많으면 같은 fortune을 여러 번 생성하려다가 토큰을 낭비할 수 있다. 3회는 "충분히 재시도하되 과하지 않은" 수준이다.
- 모니터링: 어느 시점에 재시도가 자주 발생하는지 추적해야 OpenAI 서비스 품질 문제를 조기에 발견할 수 있다.
다음 단계와 배운 점
이후 로그를 보니 실제로 전체 요청의 5~8% 정도가 첫 시도에서 실패했지만, 재시도로 대부분 성공했다. 이는 설정이 적절했음을 보여준다.
비슷한 외부 API 호출(결제, 인증, 데이터 조회 등)이 있다면 이 패턴을 일관되게 적용할 필요가 있다. 각 API마다 특성이 다르므로 (결제 API는 재시도가 위험할 수 있음) 맥락에 맞게 조정해야 하지만, 일단 "무작정 기다리거나 즉시 포기하지 말고, 적절한 재시도 로직을 갖춘다"는 원칙은 견고한 API 통합의 기본이다.
🛒 이 글과 어울리는 추천 상품
*위 링크는 쿠팡파트너스 활동의 일환이며, 일정액의 수수료를 제공받을 수 있습니다.
댓글 0
첫 댓글 달아줘.