내가 '불량 더미(reject pile)'가 얼마나 큰 비용인지 알게 된 아침, Adobe Stock 기여자 대시보드를 열어 지난 20개의 제출물 중 12개가 '거부됨(Rejected)' 상태에 놓여 있는 것을 발견했습니다. 예술 작품 때문이 아니었습니다. 메타데이터 때문이었습니다. 하나의 CSV 파일은 제목 열이 한 개 모자랐고, 다른 하나는 키워드가 단순히 파일 이름을 언더스코어(_)로 나눈 것이 전부였습니다. 11시간의 생성 및 렌더링 작업이 헤더 행(header row) 하나 때문에 버려진 것입니다.
그날 이후 저는 품질 관리를 마지막 점검으로 취급하는 것을 멈추고, 각기 다른 임무를 가진 두 개의 분리된 레이어로 다루기 시작했습니다. 그 이후로 이 파이프라인은 39개의 완성된 제품, 856 MB의 결과물을 패키징했으며, 새로운 제출물의 거부율은 사실상 제로가 되었습니다. 실제로 이 두 레이어가 무엇인지, 그리고 왜 작동하는지 알려드리겠습니다.
모든 스톡 플랫폼은 사람이 파일을 보기 전에 CSV를 검증합니다. Adobe Stock은 5개 열의 업로드 시트를 요구합니다. Vecteezy는 4개 열을 요구하고, Dreamstime이 가장 까다로워 15개 열을 요구합니다. 만약 헤더 행이 업로더가 요구하는 것과 일치하지 않으면 배치(batch)가 즉시 실패하며, 업로드를 기다린 후에야 알게 됩니다.
그래서 첫 번째 QC 레이어는 기계적입니다. make_metadata.py라는 스크립트는 파일 폴더에서 각 플랫폼의 CSV를 생성합니다. 이 스크립트는 해당 플랫폼이 요구하는 정확한 헤더와 그 외 아무것도 포함하지 않습니다. 프레임워크 없이 표준 라이브러리만 사용하므로, 고장 날 부분이 없습니다. 저는 에셋 폴더당 세 번씩, 즉 플랫폼별로 한 번씩 실행합니다:
python make_metadata.py ./my-assets --platform adobe --out metadata.csv --ext .jpg
python make_metadata.py ./my-assets --platform vecteezy --out vecteezy.csv --ext .jpg
python make_metadata.py ./my-assets --platform dreamstime --out dreamstime.csv --ext .jpg
확인은 간단합니다. CSV 파일을 열고, 헤더를 플랫폼이 발표한 사양과 비교하며, 열의 개수를 세는 것입니다. 30초밖에 걸리지 않으며, 이는 제출(submission)과 거절(rejection)의 차이를 만듭니다. --titles 파일 없이는 스크립트가 파일명을 플레이스홀더로 제목 대문자 처리(title-cases)하지만, 첫 번째 검토에는 괜찮지만 저는 제출하기 전에 항상 이를 수정합니다. 파일명은 제목이 아니며, 심사위원들은 그것을 알아차릴 수 있습니다.
레이어 2: 원장 계층 (ledger layer), 왜냐하면 이중 게시(double-posting)가 진짜 문제입니다
두 번째 레이어는 메타데이터와는 아무 관련이 없고, 계정 생존과 모든 것이 관련되어 있습니다. 어떤 스톡 플랫폼에서 플래그를 받을 수 있는 가장 빠른 방법은 같은 파일을 두 번 제출하는 것입니다. 자동화된 배치(automated batches)를 실행할 때, 배치를 두 번 실행하는 것은 예외적인 경우가 아니라 확실한 일입니다. 다운로드가 시간 초과되었거나, 하나의 파일을 수정했거나, 이미 했는지 잊어버려서 다시 실행하게 됩니다.
upload_tracker.py는 게시된 내용을 기억하는 작고 표준 라이브러리만 사용하는 원장(ledger)입니다:
python upload_tracker.py pending items.txt
python upload_tracker.py done "asset-001.jpg"
python upload_tracker.py status
규칙은 트래커가 '대기 중(pending)'이라고 말하지 않으면 어떤 파일도 출력 폴더를 떠나지 않는 것입니다. 이 습관 하나가 그 어떤 이미지 검사보다 더 많은 피해를 막아주었습니다. 잘못된 키워드는 하나의 자산을 거절시킵니다. 중복 제출은 전체 계정을 심사 대상에 올립니다.
두 레이어가 분리되어야 하는 이유
저는 처음에 모든 QC를 한 번의 패스에서 하려고 했습니다. 단일 스크립트 실행으로 파일, 메타데이터, 게시 상태를 모두 확인하는 것이었습니다. 그것은 지루한 이유로 실패했습니다. 즉, 하나의 검사가 실패하면 전체 배치가 멈추고, 어느 레이어가 문제를 포착했는지 추적할 수 없게 됩니다. 분리함으로써 메타데이터 검사는 몇 초 만에 40개 파일에서 실행될 수 있고, 원장 검사는 게시 시점에 실행되어 가장 중요한 유일한 것이 될 수 있습니다.
세 번째 점검은 QC(품질 관리) 과정에 넣지 않고 패키징 단계에서 처리합니다: 모든 제품에 실제 커버와 썸네일, 즉 1280×720 및 600×600 크기의 이미지를 Pillow를 사용하여 pack_product.py로 렌더링합니다. 적절한 커버가 포함된 zip 파일은 판매되지만, 빈 파일로는 판매되지 않습니다. 그리고 실제로 필요한 해상도로 렌더링하는 것은 파이프라인을 정직하게 유지해 줍니다. 이는 하드웨어 이야기로 이어집니다.
이 모든 것이 vCPU 1개와 RAM 2GB에서 실행되었습니다
GPU도, 클라우드 계정도 사용하지 않았습니다. 작동하게 만드는 습관들은 영리한 것이 아니라 규율적인 것입니다:
- 한 번에 하나의 프로세스만 처리합니다. 단일 코어에서 이미지 작업을 병렬로 분산시키지 마십시오.
- 각 에셋(asset) 작업 후
gc.collect()를 호출하여 메모리가 긴 배치(batch) 동안 누적되는 것을 방지합니다.
- 평균값 대신
resource.getrusage(RUSAGE_SELF).ru_maxrss로 최대 RSS(Resident Set Size)를 측정하십시오. 최고치가 시스템을 다운시키는 원인입니다.
- 무거운 렌더링 단계에서는 항목 간에 메모리가 OS로 반환되도록 서브프로세스당 하나의 파일을 사용합니다.
- 300dpi에서 렌더링하는 대신 60dpi에서 미리보기를 검사하십시오. 당신은 인쇄 품질이 아니라 구성을 확인하고 있는 것입니다.
전체 시스템은 무료 스택 위에서 작동합니다: 스케줄링 및 재시도(retry)를 위한 오케스트레이터 에이전트와 로컬 LLM 프록시가 사용되었으며, 둘 다 월 $0입니다. 네 개의 스크립트는 그 위에 놓인 패키징 계층입니다.
39개 제품을 통해 배운 것들
제가 경험한 모든 거절 사례들과 다른 제작자들에게 들은 사례들을 관통하는 패턴은 예술 자체가 거의 문제가 아니라는 것입니다. 실패는 지루한 레이어, 즉 잘못된 헤더, 중복 게시물, 누락된 커버, 제목으로 사용된 파일명에 있습니다. 이 규칙들 뒤에 있는 300페이지가 넘는 연구 자료들은 대부분 아무도 읽지 않다가 문제가 생길 때까지 무시되는 플랫폼 요구사항들을 문서화하고 있을 뿐입니다.
그 연구와 스크립트들이 컴플라이언스 메타데이터 키트가 되었습니다. 여기 설명된 것과 동일한 툴링입니다: Adobe, Vecteezy, Dreamstime용 CSV 빌더, 업로드 원장(upload ledger), PDF 및 패키징 스크립트, 그리고 이 모든 것을 2GB 박스에서 실행하는 방법에 대한 메모들입니다. 만약 파이프라인을 구축하고 오직 거절 더미(reject pile)에 빠지지 않게 해주는 부분만 빌려오고 싶다면, 메타데이터와 트래커 스크립트만으로도 Python 설치가 필요할 뿐입니다.
컴플라이언스 메타데이터 키트 받기
디지털 제품을 제작하시나요? 디지털 제품 판매자를 위한 무료 가이드 및 키트.