하나의 프롬프트로 39개의 완성된 제품을 만들다: 실제로 문제가 된 것들
요약
AI 콘텐츠 업로드 과정에서 기술적 실패를 경험하며, 성공적인 자동화 파이프라인 구축의 중요성을 깨달았습니다. 특히 플랫폼별 고유한 메타데이터 사양 준수와 중복 제출 방지를 위한 자체 레저 시스템 구축에 초점을 맞췄습니다.
핵심 포인트
- 플랫폼별로 정확히 다른 CSV 헤더를 사용해야 합니다.
- 자동화 파이프라인은 반드시 자체 메모리(레저)를 가져야 합니다.
- 단일 코어 환경에서 프로세스를 순차적으로 처리하는 규율을 지켜야 합니다.
지난 3월에 Adobe Stock에 40개 파일을 밤새 업로드했습니다. 일어나 보니 거절 화면과 함께 좋지 않은 기분이었습니다. 아트 자체는 괜찮았습니다. 문제는 CSV 파일이었습니다. 헤더 열(header column) 하나가 잘못되자, Adobe의 uploader는 이미지를 검토하기도 전에 파일을 거부합니다. 그날 아침에 저는 프롬프트 엔진은 쉬운 부분이고, 제품이 실제로 만들어지는 것은 그 이후라는 것을 깨달았습니다.
무엇이 문제였는지, 무엇으로 해결했는지, 그리고 그 배경 수치들을 소개합니다.
메타데이터 헤더는 제안이 아니라 관문이다
Adobe의 업로드 CSV 파일은 고정된 5개 열(column) 헤더를 가지고 있습니다. Vecteezy는 4개 열을 요구하고, Dreamstime은 15개를 요구합니다. 만약 이들 중 어느 곳에 잘못된 형태의 파일을 건네주면, 검토 확인이 즉시 실패하며 부분 점수나 '어느 정도 비슷함' 같은 것은 없습니다. 저는 헤더가 한 열 부족했다는 이유로 전체 배치를 날렸습니다.
해결책은 지루했습니다: 플랫폼이 요구하는 대로 헤더를 작성하고 그 외에는 아무것도 넣지 않는 것이었습니다. 제 make_metadata.py 스크립트는 이제 하나의 폴더에 있는 파일들로부터 세 가지 형식(format)을 모두 생성하며, --platform adobe, --platform vecteezy, 또는 --platform dreamstime 플래그를 사용합니다. 임시 제목으로 파일명을 title-case 처리하지만, 제출하기 전에는 항상 실제 제목과 키워드를 담은 titles.json 파일을 전달합니다. asset-017.jpg와 같은 파일명으로는 사람 검토자를 통과할 수 없습니다.
만약 이 시스템을 직접 구축한다면, 하나의 범용 CSV를 만들려고 시도하지 마십시오. 플랫폼별로 정확한 사양에 맞춰 각각의 파일을 만들고, 업로드 전에 열(column) 개수를 검증해야 합니다. 세 가지 형식, 세 가지 헤더, 그리고 제로 임프로비제이션입니다.
중복 업로드는 플래그를 받기 가장 빠른 방법이다
두 번째 실패는 전적으로 저의 잘못이었습니다. 네트워크 문제로 인해 업로드 배치를 다시 실행했고, 12개 항목을 두 번 게시했습니다. 중복 제출은 계정이 플래그(flagged)를 받을 수 있는 종류의 일이며, 저는 이미 어떤 것이 나갔는지 기록이 없었습니다.
이제 모든 배치(batch)는 레저 스크립트인 upload_tracker.py를 거칩니다. 이것은 세 가지 명령어(pending: 완료되지 않은 항목 표시, done: 항목 마킹, status: 개요 제공)로 구성된 일반 텍스트 레저입니다. 파이썬 표준 라이브러리만 사용하며 별도의 설치가 필요 없습니다. 이 레저는 무언가를 제출하기 전에 확인되기 때문에 재실행(re-run)해도 중복 포스팅되지 않습니다.
이 교훈은 일반화할 수 있습니다: 외부 계정에 접근하는 모든 자동화 파이프라인에는 자체 메모리가 필요합니다. 새벽 1시에 본인의 기억에 의존하지 마십시오.
1 vCPU, 2 GB 서버가 가르쳐준 규율
전체 파이프라인은 GPU 없이 RAM 2GB를 가진 단일 코어 박스에서 실행됩니다. 이 제약 조건이 모든 것을 형성했습니다. 작동하게 만든 습관들은 다음과 같습니다:
- 한 번에 하나의 프로세스만 처리합니다. 한 코어에서 이미지 작업을 병렬로 분산(fanning out)하는 것은 단순히 시스템을 과부하(thrashes)시킵니다.
- 각 에셋(asset)마다
gc.collect()를 실행하여 39개 항목을 처리하는 동안 메모리가 누적되는 것을 방지합니다. - 평균 RSS가 아닌,
resource.getrusage(RUSAGE_SELF).ru_maxrss를 사용하여 최대 RSS(Peak RSS)를 측정했습니다. 평균값은 아무런 유용한 정보도 주지 못했지만, 최대값은 렌더링이 언제 OOM(Out Of Memory)이 될지 정확히 알려주었습니다. - 무거운 렌더링 단계에는 서브프로세스당 하나의 파일을 사용합니다. 그래야 메모리가 실제로 OS로 반환됩니다.
- 미리보기는 60 dpi에서 검사하고, 최종본은 300 dpi에서 렌더링했습니다. 한때 저는 전체 저녁 시간을 소비하여 디버깅했는데, 그 원인은 제가 검사용 사본을 풀 해상도로 렌더링했기 때문에 발생한 메모리 급증이었습니다.
이 중 어느 것도 기발한 것은 아닙니다. 단지 작은 서버가 강요하는 것일 뿐이며, 솔직히 말해 이 제약 조건 덕분에 파이프라인은 더 복잡하지 않고 오히려 단순해졌습니다.
콘텐츠를 제품으로 바꾸는 과정 자체가 한 단계입니다
저는 실제 콘텐츠로 가득 찬 마크다운(Markdown) 문서들을 가지고 있었지만, 그것들을 제품처럼 보이게 만들 방법이 없었습니다.
패키징 역시 마찬가지였습니다. 커버가 없는 단순한 zip 파일로는 판매할 수 없습니다. pack_product.py는 Pillow를 사용하여 파일을 묶고, 1280×720 크기의 커버와 600×600 크기의 썸네일을 렌더링하여, 이들을 _system 폴더에 작성합니다. 이 폴더는 --dir 또는 PACK_ROOT를 사용하여 위치를 옮길 수 있습니다. 전체 키트에는 서드파티 패키지가 총 두 개 포함됩니다: Pillow와 reportlab입니다. 그 외 모든 것은 표준 라이브러리입니다.
폴더 레이아웃이 진정한 제품이다
제가 가장 과소평가했던 부분은 다음과 같습니다. 카탈로그는 고정된 구조가 없으면 잡동사니 서랍처럼 변해버립니다. 마침내 형태를 갖춘 레이아웃은 다섯 개의 폴더로 구성됩니다: 원본 출력을 위한 assets/, 제출 준비 파일 및 메타데이터 CSV 파일을 위한 output/, 완성된 zip과 커버를 위한 packs/, 메모와 스크랩 로그를 위한 research/, 도구를 위한 scripts/입니다.
제가 뼈저리게 배운 규칙이 있습니다: research/는 영원히 보존해야 합니다. 저는 한때 "정리"하기 위해 제 폴더를 삭제했다가, 미래의 세 가지 제품에 필요한 원자재를 잃어버렸습니다. 스크랩 로그와 메모는 잡동사니가 아니라 재고입니다.
결과적으로 이렇게 되었습니다
이 모든 과정에서 파이프라인은 완성되어 총 39개의 제품을 패키징했고, 그 출력물은 856 MB에 달합니다. 이 시스템 뒤에는 약 19,000줄의 상위 프로젝트 코드와 규칙을 강제하는 300페이지가 넘는 연구 자료가 있습니다. 모든 것은 동일한 사양의 vCPU 1개, RAM 2GB 박스에서 검증되었습니다. 위에 언급된 네 개의 스크립트는 무료 오케스트레이션 스택 아래에 놓이는 패키징 계층이며, Hermes Agent가 작업을 예약하고 9Router가 현지에서 LLM 접근을 $0으로 처리합니다.
저는 도구 키트를 만들려고 시작한 것이 아니었습니다. 배치(batch)를 계속해서 잃어버리는 것을 멈추기 위해 시작했습니다. 친구가 "그 CSV 스크립트들"을 요청하는 세 번째 이후, 실제로 작동하는 네 개의 스크립트를 정리하여 한곳에 모았습니다. 프레임워크도, 클라우드 계정도 필요 없으며, Python 3.9 이상만 있으면 됩니다. 만약 메타데이터와 추적 기능만 필요하다면 아예 pip install 할 필요조차 없습니다.
설명 대신 정확한 스크립트가 필요하시다면, 여기에서 확인하실 수 있습니다:
디지털 제품을 만드나요? 디지털 제품 판매자를 위한 무료 가이드 및 키트.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기