지난 3월, 저는 단 하나의 헤더 행 때문에 주말 전체를 날렸습니다. output/ 폴더에는 메타데이터가 구축되고 업로드가 대기 중인 완성된 에셋 120개가 있었습니다. Adobe에 배치 파일을 제출했고 한 시간 만에 거부 통보를 받았습니다. 원인은 단 하나의 열이었습니다. 저는 템플릿에서 요구하는 Keywords; 스타일의 파싱을 해야 할 곳에 Keywords라고 작성했습니다. 그런데 Adobe의 검증기는 실제 데이터가 괜찮다는 사실은 신경 쓰지 않습니다. 잘못된 헤더는 전체 CSV 파일을 가져오기(importer)에 사용할 수 없게 만들고, 그래서 문제가 된 한 열뿐만 아니라 모든 120개 행이 거부되었습니다. 가장 가슴 아픈 부분은 이것입니다. 하나의 실수가 행 하나가 아닌 배치 전체를 비용으로 지불하게 만든다는 것입니다.
제가 이 문제를 해결하며 배운 점과, 왜 메타데이터 계층(metadata layer)이 이제 제 파이프라인에서 가장 방어적인 코드가 되었는지에 대해 말씀드리겠습니다.
헤더는 협상 불가이며, 플랫폼마다 다르다
모든 스톡 플랫폼은 자체 업로드 CSV 형식을 가지고 있으며, 이들은 서로 일치하지 않습니다. 제가 판매하는 세 곳의 플랫폼은 다음과 같습니다:
- Adobe Stock:
Filename, Title, Keywords, Category, Releases 5개 열을 요구합니다.
- Vecteezy:
Filename, Title, Keywords, Releases 4개 열을 요구합니다.
- Dreamstime: 15개 열을 요구합니다. 무려 15개입니다. 다른 두 플랫폼이 요청하지 않는 항목들을 포함하며, 이 모든 것이 올바른 위치에 있어야 합니다.
실패 모드는 항상 같습니다. 마스터 스프레드시트 하나를 만들고, 각 플랫폼별로 복사본을 수동으로 편집한 다음, 그 복사본 중 하나가 틀어지게 됩니다. 열 이름이 바뀌거나, 누락되거나, 추가적인 꼬리 쉼표(trailing comma) 같은 것이죠. 검증기는 유용한 차이점(diff)을 알려주지 않습니다. 파일을 거부하고, 여러분은 15개 열과 120개 행 전체를 눈으로 찾아내야 합니다.
그래서 저는 수동 편집을 중단했습니다. 제 파이프라인의 make_metadata.py는 에셋 폴더를 받아 각 플랫폼이 요구하는 정확한 헤더만 작성하고, 그 외에는 아무것도 쓰지 않습니다. 플랫폼별로 한 번의 명령만 실행합니다:
python make_metadata.py ./my-assets --platform adobe --out metadata.csv --ext .jpg
헤더는 생성되는 것이 아니라 하드코딩됩니다. 이것이 의도적입니다. 절대 변하지 않는 템플릿 문자열은 틀어질 수 없기 때문입니다.
두 번째 침묵의 살인자: 배치를 두 번 실행하는 것
이것은 단순히 파일만 반송되는 것을 넘어 계정 자체가 플래그가 지정될 수 있어 더 심각합니다. 만약 120개 배치 중 중간에 업로드 스크립트가 충돌한다면, 본능적으로 다시 실행하고 싶을 것입니다. 하지만 이 스크립트가 이미 게시한 내용을 기억하지 못한다면, 60개의 파일을 중복으로 게시하게 됩니다. 플랫폼들은 중복 제출을 스팸 행위로 간주하며, 충분한 양의 중복 제출은 계정에 플래그를 지정하여 당신이 볼 수는 없지만 검토 시간에서 확실히 느낄 수 있게 만듭니다.
해결책은 지루하지만 필수적입니다: 원장(ledger)입니다. upload_tracker.py는 세 가지 명령어를 가진 작은 스크립트입니다:
python upload_tracker.py pending items.txt
python upload_tracker.py done "asset-001.jpg"
python upload_tracker.py status
성공적인 업로드는 다음 파일이 시작되기 전에 해당 파일을 완료(done) 상태로 표시합니다. 재실행은 원장에 이미 있는 것은 건너뜁니다. 이 스크립트는 표준 라이브러리만 사용하며, 별도의 설치가 필요 없습니다. 이 스크립트는 정확히 한 번의 중복 게시 사고를 막았고, 그 한 번으로도 영원히 사용할 가치가 충분합니다.
제목 및 키워드: 판단이 아닌 구조 자동화하기
이 스크립트는 파일 이름을 Title-case로 변환하고, 사용자가 제목을 제공하지 않으면 이를 제목으로 사용합니다. 첫 번째 시도로는 괜찮지만, 이 도구의 한계에 대해 솔직하게 말하고 싶습니다. geometric-pattern-004.jpg와 같은 파일 이름은 검색용 제목으로는 매우 좋지 않으며, 플랫폼들은 이 기준으로 순위를 매깁니다. 따라서 무언가를 제출하기 전에 저는 실제 제목과 에셋별 키워드를 담은 --titles titles.json을 전달합니다. 스크립트는 CSV 구조를 처리하고, 저는 단어(콘텐츠)를 처리합니다. 템플릿으로 키워드 판단까지 자동화하려고 하면, 모든 것이 동일한 8개의 일반적인 태그를 공유하는 120개의 에셋이 되어 아무런 순위도 얻지 못하게 됩니다.
실제 환경에서의 모습
이 모든 과정에 인프라는 필요하지 않습니다. 전체 파이프라인, 메타데이터 생성, 업로드 추적, PDF 렌더링, 제품 패키징은 GPU가 없는 1 vCPU, 2 GB RAM 서버에서 실행되었습니다. 메타데이터 및 추적 스크립트는 Python 표준 라이브러리만을 사용합니다. 외부 패키지는 커버 이미지용 Pillow와 PDF용 reportlab 단 두 가지입니다. 전체 제품 파이프라인을 위해 pip install 명령어가 두 번 필요했습니다.
이렇게 작은 환경에서도 살아남게 해준 습관들은 다음과 같습니다: 한 번에 하나의 프로세스만 처리하기, 각 에셋 사용 후 gc.collect() 실행하기, 추측하는 대신 ru_maxrss로 최대 RSS를 측정하기, 그리고 실제로 필요한 해상도로 렌더링하기. 300dpi가 아닌 60dpi에서 미리보기를 검사하는 것이 RAM에 맞추는 것과 스와핑을 하는 것의 차이를 만듭니다.
제 정신 건강을 지켜준 폴더 구조
마지막 교훈은 조직화였습니다. 제품이 39개에 달하면, 구조화되지 않은 폴더는 잡동사니 서랍처럼 변하고 무엇이 제출되었고, 무엇이 패키징되었으며, 무엇이 원본인지 추적하기 어려워집니다. 제가 정착한 구조는 다음과 같습니다:
project/
assets/ 원본 출력물
output/ 제출 준비 파일 + metadata.csv
...
경험에서 우러나온 경고가 하나 있습니다: research/ 폴더를 정리한다고 절대 삭제하지 마세요. 제가 한 번 그렇게 했습니다. 그 노트들은 다음 제품을 위한 원자재이며, 저는 그 실수를 반복하지 않았습니다.
도구들이 있는 곳
이 포스트에 있는 모든 것은 제가 매주 사용하는 실제 코드이며, 이를 파이프라인 스타터 키트(Pipeline Starter Kit)로 패키징했습니다. 이 키트는 네 개의 독립적인 스크립트(make_metadata.py, upload_tracker.py, md2pdf.py, pack_product.py)로 구성되어 있으며, 약 19,000줄 분량의 상위 프로젝트에서 만들어졌습니다. 이 프로젝트는 39개 제품과 856 MB의 출력을 패키징했으며, 스크립트가 강제하는 규칙 뒤에는 300가지 이상의 플랫폼 리서치가 담겨 있습니다. 이것은 CSV 파일이 거부되었던 그날 제가 갖고 싶었던 도구입니다.
만약 여러분도 프롬프트로 예술 작품을 만들고 콘텐츠 측면의 도움이 필요하다면, 벡터 프롬프트 팩(vector prompt pack)이 애초에 이 파이프라인에 연료를 공급했던 프롬프트 엔진입니다.
Get vector prompt pack
디지털 제품을 구축하고 계신가요? 디지털 제품 판매자를 위한 무료 가이드 및 키트를 확인하세요.