저는 아직 스크린샷이 있습니다. 10월의 화요일 밤 11시 40분, Adobe Stock에서 제가 한 번에 올린 14개의 파일 중 14개를 거부했습니다. 모든 거부 이유가 같았습니다: malformed CSV(형식 오류 CSV). 저는 14개의 벡터를 만들고, 이를 렌더링하고, 이름을 바꾼 다음, 자정 무렵에 잘못된 열 순서로 메타데이터 스프레드시트를 손으로 작성했습니다. Adobe의 업로더는 다른 것을 확인하기 전에 헤더 행을 먼저 검사하며, 열이 정확한 5개 형식과 일치하지 않으면 파일 전체를 즉시 거부합니다. 완성된 제품은 14개였지만, 실제로 라이브 된 것은 0개였고, 제 평일 밤 시간은 사라졌습니다.
그날 밤은 제가 작업 스케줄을 짜는 방식을 바꾸어 놓았습니다. 문제는 제품을 만드는 과정 자체가 오래 걸리는 것이 아니었습니다. 문제가 되는 것은 지루한 부분이 주말에만 남는 시간에 새어 나와서, 결국 평일에 얻게 되는 짧은 시간들을 망가뜨린다는 것입니다. 이 분할 방식이 문제를 해결해 주었습니다.
주말에는 일괄 처리를 위해, 평일 밤에는 붙여넣기를 위해
저는 실제 에셋(assets)은 주말에 만듭니다. 제 벡터 프롬프트 팩이 원본 출력을 생성하고, 저는 이를 한 번의 긴 세션으로 실행합니다: 한 번에 하나의 프로세스만 진행하며, 병렬 작업은 하지 않습니다. 제 서버는 GPU가 없는 2GB RAM을 가진 1 vCPU 박스라, 이미지 작업을 병렬로 분산하면 기계가 과부하됩니다(thrashes the machine). 저는 모든 에셋마다 gc.collect()를 호출하고 resource.getrusage(RUSAGE_SELF).ru_maxrss를 확인하여 평균이 아닌 실제 최대 메모리 사용량을 체크합니다.
마지막 전체 실행으로, 같은 작은 박스에서 39개의 완성된 제품과 856 MB의 출력을 얻었습니다. 이것은 강력한 하드웨어가 필요하지 않습니다. 인내심과 한 번에 하나의 작업만 할 수 있습니다.
그런 다음 저는 중단합니다. 판단력, 제목, 키워드 선택, 어떤 에셋이 살아남을지 결정하는 모든 것은 제가 컨디션 좋을 주말에 처리합니다.
CSV 헤더는 제안이 아니라 계약이다
이것이 지난 화요일 밤의 시간을 앗아간 부분입니다. Adobe Stock은 특정 5개 열짜리 CSV를 요구합니다. Vecteezy는 자체적인 헤더 이름이 있는 4개 열짜리 CSV를 원하고, Dreamstime은 15개의 열을 원합니다. 세 개의 플랫폼, 세 가지 다른 계약 조건, 그리고 그들 모두는 단 하나의 이미지를 보기 전에 잘못된 헤더만으로 즉시 거부합니다.
그래서 저는 스프레드시트를 손으로 입력하는 것을 완전히 그만두었습니다. make_metadata.py는 파일 폴더를 읽어 각 플랫폼이 요구하는 정확한 헤더만을 작성하며, 다른 것은 아무것도 하지 않습니다. 주말에 실제 제목과 키워드를 작성했다면 --titles titles.json 파일을 사용합니다. 이 옵션 없이 실행하면 파일명을 제목 형식으로 변환(title-case)하는데, 초안으로는 괜찮지만 제출하기 전에 항상 수정합니다. 왜냐하면 geometric-pattern-04.jpg와 같은 파일명이 제목으로 읽히는 것은 그 자체로 무엇인지 그대로 보여주기 때문입니다.
평일 밤 업로드는 이제 이렇습니다. 플랫폼별로 명령어를 한 번씩 실행하고, uploader에서 CSV 파일을 열어준 다음 끝입니다. 세 개의 명령어만으로 커피 마시는 시간을 포함해 10분 정도 걸립니다.
아무도 경고해주지 않는 더블 포스팅 문제
잘못된 형식의 CSV 파일 다음으로 계정 제재를 받을 수 있는 두 번째 빠르고 쉬운 방법은 배치(batch) 작업을 두 번 실행하는 것입니다. 피곤한 평일 밤, 오타 하나를 수정하기 위해 재실행할 때 같은 파일을 실수로 두 번 업로드하는 것은 놀라울 정도로 쉽습니다. 플랫폼들은 이를 감지하며, 반복되는 중복 업로드는 계정에서 스팸 행위처럼 보입니다.
upload_tracker.py는 표준 라이브러리만 사용하고 설치가 필요 없는 단순한 원장(ledger) 역할을 합니다. python upload_tracker.py done "asset-001.jpg" 명령은 항목을 표시하고, pending은 실제로 남아있는 것을 보여주며, 이 장부는 기억하기 때문에 재실행으로 인해 절대 중복 포스팅이 일어나지 않습니다. 제가 가진 스크립트 중 가장 재미없고, 만약 모든 것을 잃는다면 가장 먼저 다시 만들고 싶은 것입니다.
패키징은 숙제가 아니라 제품 결정이다
맨 자(bare) ZIP 파일만으로는 잘 팔리지 않습니다. 하지만 실제 커버 이미지(1280×720)와 정사각형 썸네일(600×600)을 포함한 같은 파일들은 눈에 띄게 더 많이 팔립니다. 왜냐하면 구매자가 검색 그리드에서 보는 것은 오직 썸네일뿐이기 때문입니다. pack_product.py는 ZIP 파일을 번들로 만들고 두 이미지를 하나의 명령으로 렌더링합니다. 이 스크립트가 필요한 유일한 의존성은 Pillow입니다.
문서(docs) 측면에서는, md2pdf.py가 마크다운 파일(Markdown file)을 reportlab을 통해 스타일이 적용된 PDF로 변환하며, 제목, 표, 목록, 링크 등을 처리합니다. 이렇게 해서 제 라이선스 페이지와 빠른 시작 가이드가 느슨한 텍스트 파일이 아니라 제품의 일부가 됩니다.
폴더 레이아웃이야말로 진정한 자동화다
몇 달 동안 이를 미루다가 어느 날 오후를 여기에 낭비했습니다. 고정된 구조가 없으면 카탈로그는 잡동사니 서랍이 되고, 이미 했던 작업을 반복하게 됩니다. 이 파이프라인이 가정하는 레이아웃은 일부러 지루합니다:
project/
assets/ 원본 출력물
output/ 제출 준비 파일 + metadata.csv
...
제가 뼈저리게 배운 규칙 하나는 이것입니다: research/ 폴더를 절대 삭제하지 마세요. 제 스크랩 로그와 메모가 다음 제품의 원자재입니다. 이 폴더를 한 번 '정리'하려다 몇 주간의 기초 작업을 날린 적이 있습니다. 다시는 이런 일이 없을 겁니다.
실제로 일주일이 어떻게 흘러가는지
토요일: 에셋을 생성하고 큐레이션하며, titles.json에 제목과 키워드를 작성하고, 패키징 명령어를 실행합니다. 집중해서 네 시간 정도 걸립니다.
화요일 또는 목요일: 각 플랫폼별로 make_metadata.py를 실행하고, upload_tracker.py status를 확인하며, 각 사이트의 웹 양식을 통해 업로드하고, 완료된 항목에 표시만 합니다. 10분에서 15분 정도 걸립니다. 만약 일주일을 건너뛰어도 아무것도 상하거나 망가지지 않습니다. 왜냐하면 원장(ledger)과 폴더 구조가 저를 위해 상태를 유지해주기 때문입니다.
이 모든 것의 기반이 되는 부모 프로젝트는 약 19,000줄의 파이프라인 코드와 스크립트가 강제하는 규칙 뒤에 있는 300개 이상의 페이지 분량의 리서치로 이루어져 있으며, 여기에 있는 모든 것은 동일한 1 vCPU, 2 GB RAM 머신에서 검증되었습니다. 클라우드 계정도, 프레임워크도 없습니다. 독립적인 Python 스크립트 네 개와 서드파티 패키지 두 개만 사용하며, 매주 사용하는 이 두 스크립트는 Python만 있으면 됩니다.
저는 이 네 개의 스크립트를 폴더 레이아웃과 RAM 메모리 노트와 함께 제가 직접 사용하는 키트로 패키징했습니다. 만약 이것 중 어떤 것이 여러분이 막혀 있는 병목 지점(bottleneck)과 일치한다면, 여기에서 확인할 수 있습니다:
Get vector prompt pack
디지털 제품을 만들고 계신가요? 디지털 제품 판매자를 위한 무료 가이드 및 키트.