300문제 세트 자동 생성! API 오류로 250문제에서 중단되자, 체크포인트 메커니즘을 구현한 이야기
요약
GenAI를 활용하여 자격증 문제 세트를 자동 생성하는 과정에서 API 오류로 250문제까지의 작업이 중단되는 경험을 했다. 이 글은 단순한 메모리 기반 처리가 아닌, '체크포인트 메커니즘'을 구현하여 대규모 배치 처리의 안정성을 확보하고 재개 가능성을 높인 과정을 설명한다.
핵심 포인트
- 장시간 API 호출 시 데이터 손실 방지
- 체크포인트는 중간 결과물을 파일에 저장하는 방식
- JSONL 포맷은 단계별 추가(append)에 용이함
- 대규모 배치 처리의 안정성 확보가 핵심
최근 어떤 자격증 시험을 공부하면서 공식 문제집만으로는 부족하다고 느꼈다. 그래서 직접 만들어 보기로 했다. GenAI를 사용하면 유사 문제를 얼마든지 만들 수 있는 시대가 되었으니까.
곧바로 시험의 시라버스(syllabus)를 입력하여, 장(chapter)별로 문제와 해설을 300문제 분량으로 생성하는 스크립트를 작성했다. python generate_questions.py를 실행하고 기다리기만 하면 된다. 프로그레스바가 빠르게 늘어나는 것을 보며 '정말 편리한 시대가 되었구나'라고 생각했다.
실행 시작 후 75분이 지났다. 프로그레스바는 8할을 넘은 정도였다. 슬슬 끝날까 싶어 콘솔을 들여다보았더니, 보고 싶지 않은 빨간 글자가 표시되어 있었다.
requests.exceptions.ConnectionError: ('Connection aborted.', ConnectionResetError(104, 'Connection reset by peer'))
API 호출 오류 자체는 흔한 일이다. 문제는 그 이후였다. 결과물이 출력되어야 할 디렉토리를 보니 파일이 텅 비어 있었다.
……응?
맞다. 250문제 가까이까지 생성했을 데이터가 어디에도 없다. 75분이라는 시간과 상당한 양의 API 토큰이 순식간에 사라졌다. 이건 심각하다.
원인: 너무 단순했던 전체 일괄 처리
원인은 완전히 내 설계 실수였다. 구현이 너무 단순했다.
작성한 코드는 대략 이런 느낌이었다.
- 빈 리스트
all_questions = []를 준비한다. for루프를 이용해 300번 GenAI의 API를 호출한다.- 생성된 문제를
all_questions.append(new_question)으로 리스트에 추가한다. - 루프가 모두 끝나면,
all_questions의 내용을 모아서 JSON 파일로 작성한다.
이 설계에서 문제인 부분은 무엇일까? 중간에 단 한 번이라도 처리가 꼬이면, 메모리에 있던 all_questions의 내용 전체가 사라진다는 것이다.
외부 API를 호출하는 장시간 배치 처리(batch processing)에서 이 구현 방식은 치명적이었다. 네트워크는 변덕스럽고, API 서버 역시 가끔 컨디션이 안 좋을 수 있다. 그런 '중단'을 전혀 예상하지 못한 것이 패인이었다.
수정: 적절한 지점에서 저장하고 재개하기
이대로는 생성이 끝날 때까지 신에게 기도하는 게임이 될 것이다. 엔지니어로서 그건 있을 수 없다.
그래서 '체크포인트 메커니즘(checkpoint mechanism)'을 구현하기로 했다.
아이디어는 간단하다. '적절한 지점까지 처리가 진행되면, 그 시점의 결과물을 파일에 저장해 두는 것'이다. 그리고 만약 스크립트가 중단되어도, 다음에 실행했을 때 '어디까지 끝났었는지'를 파일에서 읽어와서 이어서 재개할 수 있도록 하는 것이다.
구체적으로는, 장별 문제 생성이 완료될 때마다 결과를 파일에 추가(append)하는 방식으로 했다. 파일 형식으로는 JSONL (JSON Lines)이 이런 용도에 편리하다. 한 줄이 하나의 독립적인 JSON 객체가 되어 있어, 추가가 매우 쉽다.
수정된 코드는 대략 이런 느낌이다.
import os
import json
...
이 코드의 핵심 포인트는 세 가지다.
- 시작 시 진행 상황 확인: 스크립트가 시작되면, 먼저
checkpoint_file이 존재하는지 체크한다. 있다면 내용을 읽어와서 어느 장(chapter)이 완료되었는지completed_chapters셋에 기록한다. - 처리된 작업 건너뛰기:
for루프 안에서 처리 대상인 장이completed_chapters에 포함되어 있다면, API를 호출하지 않고 건너뛴다. - 진행 상황 영속화: 하나의 장 처리가 끝날 때마다, `open(...,
이 '체크포인트를 설정하여 중간에 재개할 수 있게 한다'는 생각은 AI 콘텐츠 생성뿐만 아니라 대규모 데이터 처리, 웹 스크레이핑, 인프라 프로비저닝(Terraform 등도 내부적으로 state 파일을 이용해 비슷한 작업을 합니다) 등 여러 상황에서 응용될 수 있습니다.
개인 개발 툴 같은 경우는 '작동하기만 하면 된다'고 생각하기 쉽지만, 이런 작은 노력을 추가하는 것만으로 운영 안정성이 엄청나게 높아집니다. 75분을 낭비한 것은 아팠지만, 덕분에 앞으로의 개발에서 같은 실수를 반복하지 않을 수 있을 겁니다. 좋은 학습 비용이었다고 생각하기로 했습니다.
저는 소규모 Python 시스템(거래 봇, RAG API, 스케줄링 자동화 등)을 구축하고 운영하며 발생하는 모든 문제를 기록합니다.
제공업체에 구애받지 않는 RAG Q&A API가 유용하다면, GitHub의 MIT 라이선스로 공개했습니다: rag-faq-api. 이 API는 API 키 없이도(오프라인 스텁 LLM + 해싱 임베더) 작동하며 전체 테스트 스위트를 통과하고, 환경 변수 하나로 Claude / Gemini / OpenAI를 전환할 수 있으며, 청크링 스윕을 통해 검색 품질 측정 도구(Hit@k / MRR / Recall@k)를 제공합니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기