저는 폴더 안에 완성된 에셋 30개를 가지고 있었지만, 팔 것이 없었습니다. 정말 절망적인 순간이었습니다. 아트 작업은 끝냈고, 프롬프트 엔진도 작동했으며, 저는 저녁 내내 수동으로 파일을 압축(zipping)하고, 화면에 보이는 크기로 커버 이미지를 스크린샷 찍고, 스프레드시트에 CSV 행을 직접 입력하는 작업을 했습니다. 에셋 12개쯤에서 Adobe Stock 업로더에 한 행을 붙여 넣었는데 즉시 거절당했습니다. 열(column) 개수가 잘못된 것이었습니다. 저는 정확히 5개를 요구하는 템플릿에 대해 6개의 열을 작성했었습니다. 그 이후의 모든 행도 틀렸습니다.
그날 밤, 저는 아트 작업을 멈추고 패키징 레이어 작성을 시작했습니다. 하나의 에셋이 왜 세 가지 방식으로 팔려야 하는지, 그리고 그것을 실제로 수행하는 스크립트가 어떤 모습인지에 대해 배운 내용입니다.
하나의 에셋이 판매되는 세 가지 방식
동일한 작업물로도 세 가지의 독립적인 제품으로 출하될 수 있으며, 각각은 다른 패키징이 필요합니다:
- 스톡 업로드. 개별 파일과 각 플랫폼이 요구하는 정확한 형식의 메타데이터 CSV가 필요합니다. Adobe Stock은 5개 열 헤더를 원하고, Vecteezy는 4개 열을 원하며, Dreamstime은 15개를 원합니다. 헤더만 잘못해도 아무도 아트를 보기 전에 업로드가 거절됩니다.
- 제품 팩(Product packs). 관련 파일들을 압축하여 Gumroad나 자체 사이트에서 하나의 단위로 판매하는 것입니다. 이것은 한눈에 읽을 수 있는 커버가 필요합니다.
- 리스팅 렌더(Listing renders). 1280x720 크기의 커버와 600x600의 썸네일이 필요합니다. 이 이미지들이 구매자들이 실제로 보는 것입니다. 미리 보기 없이 단순히 압축 파일만으로는 팔리지 않습니다. 제 첫 리스팅에 아무것도 없어 판매량이 '0'에 머물렀기 때문에 저는 그것을 알고 있습니다.
하나의 에셋 폴더가 이 세 가지를 모두 공급합니다. 실수는 이것들을 수동으로 처리하는 세 개의 별개의 잡무로 취급하는 것입니다.
메타데이터는 작성 문제가 아니라 형식 문제입니다
CSV 헤더는 협상할 수 없습니다. Adobe 업로더는 헤더 행을 확인하고 잘못된 경우 즉시 거절하는데, 이것이 저를 힘들게 했습니다. 그래서 저는 각 플랫폼이 요구하는 헤더만을 출력하고 그 외에는 아무것도 하지 않는 make_metadata.py를 작성했습니다:
폴더를 순회하며 파일명을 제목 형식으로 변환하고 행을 작성합니다. 실제 제목과 키워드가 있다면 titles.json 파일을 전달할 수 있습니다. 이 파일이 없다면, 파일명이 1차 작업에는 충분하지만 제출하기 전에 반드시 교체해야 합니다. 이 스크립트는 Python 표준 라이브러리만을 사용하며, pip 설치가 필요 없습니다. 두 개의 플랫폼을 확인하고 각각 하나의 명령어로 처리하므로 헤더가 틀릴 일이 절대 없습니다.
더블 포스팅은 플래그를 받을 가장 빠른 방법
이것 때문에 정말 깜짝 놀랐습니다. 배치를 두 번 실행하거나, 동일한 에셋을 같은 플랫폼에 두 번 게시하는 것은 계정 검토의 알려진 트리거입니다. 어떤 것을 자동화하든 재실행(re-run)은 '만약' 문제가 아니라 '언젠가' 발생하는 일입니다. cron job이 두 번 작동하거나, 이미 배치가 나갔다는 사실을 잊어버리는 등 말이죠.
upload_tracker.py는 작은 원장 역할을 합니다:
python upload_tracker.py pending items.txt
python upload_tracker.py done "asset-001.jpg"
python upload_tracker.py status
무엇을 게시했는지 기억하여 재실행 시 완료된 항목은 건너뛰게 합니다. 이 역시 표준 라이브러리만 사용합니다. 제가 작성한 100줄 중 가장 화려하지 않은 코드일지 모르지만, 파이프라인에서 다른 어떤 것보다 더 많은 피해를 막아주었습니다.
하나의 명령어로 커버와 zip 파일 생성
pack_product.py는 제품을 번들링하고 이미지를 렌더링합니다:
python pack_product.py my-product "My Product" "A short subtitle" 9
이 명령은 my-product.zip, 1280x720 커버 이미지, 그리고 600x600 썸네일을 실행 위치 옆의 _system 폴더에 작성합니다. --dir을 사용하거나 PACK_ROOT 환경 변수를 통해 다른 곳으로 경로를 지정할 수 있습니다. 이미지를 렌더링하는 데는 Pillow가 사용되는데, 이는 전체 키트에서 유일한 두 개의 외부 패키지 중 하나입니다. 나머지 하나는 Markdown 문서를 스타일이 적용된 PDF 제품으로 한 번에 변환하는 md2pdf.py를 위한 reportlab입니다.
왜 1280x720과 600x600인가요? 그것들은 렌더링 시간을 낭비하지 않으면서 리스팅 미리보기와 썸네일 그리드에서 보기 좋은 크기이기 때문입니다. 필요한 해상도로 렌더링하세요. 300dpi 대신 60dpi로 미리보기를 검사하는 것은 이 모든 것이 GPU가 없는 1 vCPU, 2 GB RAM 서버에서 작동하게 만든 습관 중 하나입니다.
반복 가능한 결과를 만드는 소규모 서버의 습관들
총 856MB의 출력을 생성한 39개 제품을 패키징하는 전체 파이프라인은 그 작은 박스 위에서 실행되었습니다. 이것이 작동하는 이유는 하드웨어가 아니라 습관 덕분입니다:
- 한 번에 하나의 프로세스를 처리합니다. 코어 1개에서 이미지 작업을 병렬로 분산시키지 마세요.
- 각 에셋 처리 후
gc.collect()를 호출하여 항목 간 메모리를 확보하세요.
- 평균값이 아닌
resource.getrusage(RUSAGE_SELF).ru_maxrss로 최대 RSS(Resident Set Size)를 측정하세요. 평균값은 당신의 상한선을 속입니다.
- 무거운 렌더링 단계에는 서브프로세스당 하나의 파일을 사용해야 메모리가 OS로 실제로 반환됩니다.
이 중 어느 것도 영리한 것은 아닙니다. 단지 독학으로 만든 제작자가 거의 비용이 들지 않는 하드웨어에서 실제 파이프라인을 실행할 수 있게 해주는 것일 뿐입니다.
키트가 필요한 이유
위의 모든 내용은 이제 네 개의 독립적인 스크립트로 구성되었습니다: make_metadata.py, upload_tracker.py, md2pdf.py, pack_product.py. 프레임워크도, 클라우드 계정도 필요 없습니다. 단지 두 개의 pip 패키지만 있으면 됩니다. 이들은 39개 제품을 패키징했던 것과 동일하며, 부모 프로젝트의 약 19,000줄의 파이프라인 코드와 규칙 뒤에 있는 300페이지 이상의 연구 자료를 기반으로 합니다. 저는 이것들을 ai sticker factory playbook로 모았으며, 자정 무렵 거부된 CSV 파일에서 다시 구축하고 싶지 않은 패키징 레이어가 필요하다면 그곳에 있습니다.
Get ai sticker factory playbook
디지털 제품을 제작하시나요? 디지털 제품 판매자를 위한 무료 가이드 및 키트.