저는 같은 배치 작업을 두 번 실행했습니다. 물론 의도한 바는 아니었습니다. Adobe Stock에 40개의 파일을 푸시하던 도중에 네트워크 타임아웃이 발생했고, 저는 문제가 해결되었다고 생각하여 전체 과정을 다시 실행했습니다. 그 결과 23개의 파일이 두 번 업로드되었습니다. Adobe의 중복 감지 기능이 대부분을 잡아냈지만, 검토 대기열 처리가 오래 걸렸고, 여러 플랫폼에 걸쳐 깔끔한 제출 기록을 만들려고 할 때 원치 않는 계정 노이즈가 발생했습니다.
그날 밤 저는 upload_tracker.py를 작성했습니다. 이 스크립트는 제 파이프라인에서 가장 화려하지 않은 부분이며, 만약 두 가지만 유지할 수 있다면 제가 지킬 스크립트입니다. 이것이 무엇을 하는지, 그리고 이것이 해결하는 문제가 얼마나 심각한 문제인지 설명드리겠습니다.
중복 업로드가 사소한 실수가 아닌 이유
모든 스톡 플랫폼은 중복 제출물을 다르게 처리하지만, 어느 곳도 잘 처리하지 못합니다. Adobe Stock은 검토 중에 중복을 거부하며, 같은 콘텐츠의 반복적인 거부는 계정에 품질 문제를 표시할 수 있습니다. Vecteezy의 검토 프로세스 역시 카탈로그에 이미 존재하는 콘텐츠를 반송시킵니다. Dreamstime은 업로드(ingest) 시 중복을 플래그합니다. 세 곳 모두에서 CSV 메타데이터는 플랫폼이 기대하는 것과 정확히 일치해야 합니다. Adobe는 5개 열 헤더, Vecteezy는 4개 열 헤더, Dreamstime은 15개 열 헤더를 원합니다. make_metadata.py가 이 부분을 처리합니다. 하지만 같은 JPG 파일이 두 업로드 세션에 나타날 때, 어떤 메타데이터 스크립트도 사용자 자신으로부터 당신을 구할 수는 없습니다.
진짜 함정은 부분적인 실패입니다. 배치 작업은 중간에 실패합니다. 2GB 서버, 느린 연결, 플랫폼의 일시적 오류 등 무엇이든 첫 번째 실행을 40개 파일 중 17번째 파일에서 멈추게 할 수 있습니다. 만약 당신의 복구 계획이 '다시 실행'이라면, 당신은 아직 발견하지 못한 버그를 가지고 있는 것입니다.
데이터베이스가 아닌 원장(Ledger)
upload_tracker.py는 파이썬 표준 라이브러리만을 사용합니다. 설치할 것도 없고, 의존성도 없고, 데이터베이스 서버도 없습니다. 이것은 일반 텍스트 원장 파일(ledger file)을 유지하며 세 가지 질문에 답합니다:
python upload_tracker.py pending items.txt # 아직 완료되지 않은 항목 확인
python upload_tracker.py done "asset-001.jpg"
python upload_tracker.py status
제가 지킨 설계 규칙은 다음과 같습니다: 트래커는 사용자가 _시작한 것_이 아니라 _완료한 것_을 기록합니다. 파일은 업로드 단계가 확인해야만 완료로 표시됩니다. 만약 실행 도중에 파일이 죽더라도, 그 파일은 여전히 보류(pending) 상태이며 다음 실행에서 정확히 한 번만 가져와 처리됩니다. 항목들을 낙관적으로(optimistically) 완료 처리하는 방식 때문에 제가 만든 트래커의 첫 번째 버전에서는 테스트 배치가 중복 게시된 적이 있습니다. 해결책은 순서 지정이라는 단 하나의 줄이었습니다: 확인 후 기록합니다.
이는 매우 중요합니다. 왜냐하면 트래커는 가장 약한 순간만큼만 신뢰할 수 있고, 그 가장 약한 순간은 항상 충돌(crash) 구간이기 때문입니다. 실패-후-기록(Fail-then-record) 방식에는 데이터를 잃을 만한 충돌 구간이 없습니다. 기록-후-실패(Record-then-fail) 방식은 중복을 생성하는 충돌 구간을 가집니다.
배치 흐름에 통합되는 방법
제 폴더 구조는 고정되어 있습니다: 원본 출력물은 assets/, 제출 준비 파일과 메타데이터 CSV는 output/, 도구(tools)는 scripts/에 위치합니다. 배치 실행 과정은 다음과 같습니다:
assets/에 에셋을 생성합니다 (에셋별로 하나의 파일, 항목 간에는 gc.collect()를 수행하는데, 이는 2 GB의 RAM이 병렬 이미지 작업에서 용서하지 못하기 때문입니다).
- 각 플랫폼에 대해 폴더를 대상으로
make_metadata.py를 실행합니다.
- 아직 게시되지 않은 파일 목록을 얻기 위해
upload_tracker.py pending을 실행합니다.
- 보류된 목록만, 플랫폼별로 업로드합니다.
- 업로드가 확인될 때마다 각 파일을 완료(done) 처리합니다.
3단계가 핵심입니다. 재실행은 이제 구조적으로멱등성(idempotent)을 갖게 되었습니다. 저는 이 배치를 의도적으로 다섯 번이나 다시 실행했으며, 첫 번째 완전한 실행 이후에는 매번 보류 목록이 비어 있었습니다.
솔직히 한 가지 제한 사항이 있습니다: 트래커는 플랫폼 자체에 대해서는 알지 못합니다. Adobe에 쿼리하여
나는 SQLite를 고려했다. 잠금(locking) 기능이 있는 JSON 상태 파일도 고려했다. 둘 다 괜찮다. 하지만 1 vCPU, 2 GB RAM 사양의 박스에서는 가장 단순한 것이 오류가 날 염려가 없는 것이 최선이다. 만약 원장(ledger)에 문제가 생기더라도, 나는 그것을 읽고 손으로 고칠 수 있으며 내가 어떤 상태인지 정확히 알 수 있다. status는 카운트와 목록을 출력한다. 이것이 전체 인터페이스다. 실패한 배치 이후 새벽 1시에 상태 저장소(state store)를 디버깅하는 것은 내가 두 번쯤 겪고 싶지 않은 문제였다.
같은 철학은 파이프라인의 나머지 부분에도 적용된다. make_metadata.py는 각 플랫폼이 요구하는 정확한 헤더만 작성하고 그 외에는 아무것도 작성하지 않는다. 왜냐하면 세 플랫폼 모두 잘못된 헤더를 즉시 거부하기 때문이다. pack_product.py는 파일을 zip으로 압축하고 Pillow를 사용해 1280×720 커버와 600×600 썸네일을 렌더링하며, 이는 이 키트에서 유일한 이미지 의존성이다. 모든 것이 동일한 작은 서버에서 한 번에 하나의 프로세스로 실행되며, 추측하는 대신 ru_maxrss로 최대 RSS를 측정한다.
과거의 나에게 전하고 싶은 말
실패 모드는 업로드 자체가 아니었다. 재실행이 안전하다고 가정하는 것이 문제였다. 두 번 이상 실행해야 하는 모든 배치 파이프라인은 시도 횟수가 아닌 완료 기록을 남기는 원장(ledger)이 필요하며, 그것은 디스크에 있을 수 있는 가장 지루한 파일이어야 한다. 첫 부분적 실패가 발생하기 전에 구축해야 하며, 그 이후가 아니다. 왜냐하면 첫 부분적 실패는 보장되어 있으며, 이로 인해 생성되는 중복물들은 판매하는 모든 플랫폼에서 검토 시간과 계정 평판을 소모시키기 때문이다.
이 트래커(tracker)는 파이프라인 스타터 키트의 네 가지 스크립트 중 하나이며, 단일 코어, 2 GB 서버에서 39개의 제품과 856 MB의 출력을 패키징했던 것과 동일한 도구들이다. 만약 유사한 환경에서 스티커 및 에셋 제품을 구축하고 있다면, ai sticker factory playbook가 이 CSV 파일들의 생성 측면과 그 배경에 있는 플랫폼 규칙을 포함하여 전체 파이프라인을 안내한다. 이것은 무엇이 작동했는지에 대한 문서이며, 무엇이 될 것이라는 약속이 아니다.
ai sticker factory playbook 받기
디지털 제품을 만드신가요? 디지털 제품 판매자를 위한 무료 가이드 및 키트.