#자동화#엣지케이스#폴백 전략#복구 설계#운영

자동화 엣지케이스 5가지 — 완전 자동이 아니라 우아한 폴백이 답이다

👁 3 조회
자동화 엣지케이스 5가지 — 완전 자동이 아니라 우아한 폴백이 답이다 핵심 개념을 담은 커버 이미지
자동화 엣지케이스 5가지 — 완전 자동이 아니라 우아한 폴백이 답이다 핵심 개념을 담은 커버 이미지

지난 4개월간 블로그 자동발행과 유튜브 쇼츠 파이프라인을 돌리면서 자동화가 깨진 횟수를 세어봤어요. 총 127번 실행 중 38번 실패. 실패율 29.9%입니다. 자동화 도구는 '완전 자동'이라 홍보하지만 실무에선 30% 실패가 정상이에요. 저는 이게 오류가 아니라 설계 문제라고 생각합니다. 1인 운영자가 해야 할 일은 수동 처리가 아니라 감시와 자동 복구 구조를 만드는 거예요.

세션 만료는 예외가 아니라 전제다

티스토리 교차발행 자동화를 만들 때 제일 먼저 깨진 게 세션이었어요. 로그인 후 3일은 잘 돌다가 4일 째 아침에 보면 "로그인이 필요합니다" 에러. 6월 둘째 주에 로그인 상태 유지를 활성화했더니 일주일은 버텼는데, 열흘 뒤 또 만료됐습니다.

세션 만료를 예외 케이스로 보면 매번 수동 로그인하게 돼요. 저는 이걸 전제로 바꿨어요. 세션은 언젠가 만료된다고 가정하고, 게시 전에 세션 검증 단계를 넣었습니다. 실제 게시 페이지를 headless로 로드해서 로그인 리다이렉트가 뜨면 즉시 텔레그램 알림을 보내요. 만료됐을 때 pending 큐에 보존하고 재로그인 후 자동 재발행되거든요.

인증 갱신은 사람이 아니라 크론이 해야 한다

Google Cloud TTS를 쓰는 유튜브 쇼츠 파이프라인에서 7월 초에 인증 토큰 만료 에러가 났어요. 서비스 계정 키는 영구 유효한데 왜 만료되냐고 따졌더니, 제가 쓴 라이브러리가 토큰을 메모리에만 캐싱하고 있었더라고요. 프로세스가 재시작되면 다시 인증 요청을 보내야 하는데, 네트워크가 순간 끊기면 인증 실패로 이어졌습니다.

인증을 프로세스 시작 시 1회만 하는 건 위험해요. 저는 4시간마다 도는 크론을 추가했어요. 세션이 살아있으면 keep-alive 요청을 보내서 idle timeout을 리셋하고, 토큰이 1시간 내 만료 예정이면 미리 갱신합니다. 사람이 에러를 보고 수동으로 재인증하는 게 아니라, 크론이 만료 전에 알아서 갱신하는 구조죠.

타임아웃 재시도는 횟수가 아니라 전략이다

타임아웃 재시도는 횟수가 아니라 전략이다
타임아웃 재시도는 횟수가 아니라 전략이다

AI 이미지 생성 API를 쓰는 쇼츠 배경 제작에서 타임아웃이 자주 났어요. Gemini API는 응답 시간이 5초에서 40초까지 들쭉날쭉했거든요. 처음엔 타임아웃을 60초로 늘렸는데, 네트워크가 느린 날엔 그것도 부족했어요. 재시도를 3번으로 늘렸지만 3번 다 타임아웃 나면 결국 실패예요.

재시도 횟수만 늘리는 건 미봉책이에요. 저는 3단계 폴백 전략을 넣었습니다:

  1. 1차 실패: 10초 대기 후 재시도
  2. 2차 실패: 30초 대기 + 타임아웃을 90초로 늘림
  3. 3차 실패: 안전 추상 배경(로컬 그라데이션 이미지)으로 폴백

이미지 품질이 약간 떨어져도 영상은 만들어지거든요. 이 전략을 넣고 나서 API 타임아웃으로 인한 전체 파이프라인 중단은 사라졌어요.

네트워크 재시도 없이 API 호출하지 마라

Reddit RSS 수집 스크립트가 6월 말에 이틀 연속 실패했어요. 로그를 보니 "ECONNRESET" 에러. 네트워크가 순간 끊긴 거죠. 제 코드엔 재시도 로직이 없었어요. fetch 한 번 실패하면 그냥 throw 하고 끝이었습니다.

외부 API 호출엔 재시도가 필수예요. 저는 모든 HTTP 요청에 지수 백오프 재시도를 넣었어요:

async function fetchWithRetry(url, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await fetch(url);
    } catch (error) {
      if (i === maxRetries - 1) throw error;
      await sleep(Math.pow(2, i) * 1000); // 1초, 2초, 4초
    }
  }
}

Reddit 수집은 10개 서브레딧을 순회하는데, 하나가 실패해도 나머지 9개는 수집되게 했어요. 부분 데이터라도 보존하는 게 전체 실패보다 낫죠.

사용 한도 초과는 큐로 우회한다

사용 한도 초과는 큐로 우회한다
사용 한도 초과는 큐로 우회한다

Google TTS API를 처음 쓸 때 일일 무료 한도가 백만 글자인 줄 알았는데, 실제론 분당 요청 수 제한이 있었어요. 쇼츠 5개를 동시에 만들려다가 "429 Too Many Requests" 에러를 받았죠. 제 첫 반응은 유료 플랜으로 올리는 거였는데, 한 달에 $50 내면서 하루 쇼츠 3개 만드는 건 비효율적이었어요.

사용 한도는 돈으로 해결하는 게 아니라 큐로 우회하는 거예요. 저는 TTS 요청을 순차 큐에 넣었어요. 한 번에 1개씩만 처리하고, 요청 사이에 2초 간격을 뒀습니다. 5개 쇼츠를 만들 때 동시 처리는 25초 걸리다 실패했는데, 순차 처리는 45초 걸려도 대부분 안정적으로 동작해요.

자동화는 완전 자동이 목표가 아니다

"자동화하면 손 안 대도 된다"는 환상이 있어요. 저도 그랬죠. 근데 4개월 운영하면서 깨달은 건, 자동화의 목표는 완전 자동이 아니라 우아한 폴백이라는 거예요. 제가 적용한 5가지 폴백 전략은 이래요:

  • 세션 만료: 검증 → 알림 + pending 큐 보존 → 재로그인 후 자동 재발행
  • 인증 갱신: 4시간 크론 → keep-alive + 1시간 전 미리 갱신
  • 타임아웃: 3단계 폴백 → 대기 늘림 → 안전 배경 전환
  • 네트워크 끊김: 지수 백오프 재시도 → 부분 데이터 보존
  • 사용 한도 초과: 순차 큐 → 요청 간격 2초

이 5가지를 넣으니 실패율이 29.9%에서 3.1%로 떨어졌어요. 자동화 도구가 깨지는 건 당연합니다. 엣지케이스를 설계 전제로 받아들이고, 실패했을 때 우아하게 복구하는 구조를 만드세요. 그게 진짜 자동화예요.

자주 묻는 질문

Q. 재시도 로직을 모든 API 호출에 넣으면 코드가 복잡해지지 않나요?

A. 재시도를 래퍼 함수로 만들면 됩니다. 위 코드처럼 fetchWithRetry 유틸 함수를 만들어서 모든 외부 API 호출에 쓰면 10줄로 전체 안정성이 올라가요.

Q. 세션 만료 알림을 받으면 결국 수동으로 로그인해야 하는 거 아닌가요?

A. 맞아요. 하지만 차이는 게시물이 보존되느냐 사라지느냐예요. 알림 없이 실패하면 게시물이 날아가지만, pending 큐에 보존하면 재로그인 후 자동으로 다시 돌아갑니다.

Q. 폴백 품질이 떨어지면 차라리 실패하는 게 낫지 않나요?

A. 폴백 품질 기준을 정하면 돼요. 저는 안전 배경 이미지가 원본 AI 이미지 대비 80% 품질은 유지한다고 보고, 그 정도면 게시 가치가 있다고 판단했습니다.

이 글이 도움이 됐다면 공유해 주세요
X 공유

관련 글