지난 3월, 매일 업로드한 한 달 동안 Adobe Stock 대시보드를 확인하고는 충격적인 숫자를 마주했습니다. 2,400회의 조회수(views)에 3개의 다운로드가었습니다. 이는 0.12%의 전환율입니다. 저는 그림을 만드는 데 몇 주를 보냈고, 제 작품이 문제라고 자책할 준비가 되어 있었습니다. 하지만 저는 틀렸습니다.
문제는 이미지 자체가 아니었습니다. 제가 조회수가 무엇을 의미하는지 전혀 알지 못했다는 것이었습니다. 저는 조회수를 성공 지표로 보고 있었지만, 실제로는 진단 도구였습니다. 제가 배운 점과 이제부터 주시하는 단 하나의 숫자가 있습니다.
조회수는 검색 문제이고, 판매는 매칭 문제입니다
스톡 플랫폼은 사용자들이 특정 것을 검색할 때 여러분의 작품을 보여줍니다. '조회수'란 썸네일이 나타났거나 클릭되었다는 의미입니다. 반면, '판매'는 파일이 그 사람이 실제로 필요로 하는 것과 일치했다는 의미입니다. 이 둘은 완전히 다른 시스템인데, 대부분의 사람들은 첫 번째 시스템(조회수)만 최적화합니다.
제가 다운로드 기록 3건을 분석했을 때, 패턴은 명확했습니다. 판매된 에셋들은 사물 자체를 설명하는 제목이 아니라 '사용 사례(use case)'를 설명하는 제목을 가지고 있었습니다. 동일한 스타일의 작품임에도 불구하고, '결혼 초대장용 수채화 유칼립투스 테두리'가 '유칼립투스 일러스트레이션'보다 훨씬 높은 성과를 보였습니다. 같은 포트폴리오, 같은 달이었습니다. 차이는 구매자가 1초 만에 이 파일이 자신의 문제를 해결해 줄 수 있는지 알 수 있느냐 여부였습니다.
그래서 제가 주목하는 단 하나의 지표는 이것입니다: 에셋당 조회수 100회 대비 판매 건수(sales per 100 views, per asset). 포트폴리오 전체 평균이 아니라, 개별 에셋 단위로 측정해야 합니다. 포트폴리오 평균은 최고의 작품과 최악의 작품을 서로 가려버립니다. 이 숫자로 정렬한 후, 저는 어떤 제품을 더 많이 만들지 추측하는 것을 멈췄습니다.
메타데이터가 대부분의 매칭이 일어나는 곳입니다
제가 업로드하는 세 개의 주요 채널 모두 잘못된 CSV 헤더를 즉시 거부합니다. Adobe는 5개 열을 요구하고, Vecteezy는 4개를 요구하며, Dreamstime은 15개를 요구합니다. 하나라도 틀리면 사람이 보기 전에 일괄 처리가 실패하여 하루를 날리게 됩니다. 저는 같은 주에 두 번이나 그런 일을 겪었기 때문에 이 사실을 잘 알고 있습니다.
하지만 유효성 검사를 통과하는 것은 기본입니다. 열(column)에는 일치하는 정보를 넣어야 합니다. 제목과 키워드는 구매자가 다운로드하기 전에 볼 수 있는 유일한 텍스트이므로, 전환 부담을 거의 모두 지니고 있습니다. 제가 초기에 업로드했던 것들은 파일명에서 파생된 제목들이었는데, 그게 빨랐기 때문입니다. 전환 데이터를 보니 빠르다는 것이 비쌌습니다. 그러한 자산들은 거의 제로에 머물렀던 반면, 제가 직접 제목을 붙인 것들은 거의 모든 것을 벌어들였습니다.
효과가 좋았던 해결책은 다음과 같습니다. 구매자가 입력할 법한 문장으로 제목을 작성하고, 사용 목적, 스타일, 주제 순서로 키워드를 선택하는 것입니다. 저는 이제 이 과정을 스크립트로 일괄 처리합니다. 이 스크립트는 하나의 폴더에 있는 파일들로부터 세 가지 플랫폼의 CSV 파일을 생성하며, 각 플랫폼이 요구하는 정확한 헤더만 포함합니다. 제목은 JSON 파일에 직접 작성하고, 열(column) 처리는 스크립트가 담당하게 합니다.
중복 업로드는 계정을 망치고, 제 데이터를 파괴했습니다
여기에는 두 가지 비용이 따르는 실수가 있습니다. 저는 첫 번째 실행이 실패했다고 생각하여 업로드 배치를 한 번 다시 실행했습니다. 하지만 실제로는 그렇지 않았습니다. 그 결과는 중복 자산이었는데, 플랫폼들은 이를 스팸으로 취급하며, 제 장부에도 혼란을 야기했습니다. 어떤 자산에 판매가 발생했는지 더 이상 알 수 없었습니다. 그 후 두 달 동안 저의 자산당 지표(per-asset metric)는 쓰레기였습니다.
플랫폼들은 중복 제출 건을 플래그합니다. 이것은 추측이 아니라, 기여자 가이드라인에 명시되어 있습니다. 그래서 이제 장부 스크립트는 게시된 모든 파일명을 기록하고, 재실행 시에는 먼저 이 장부를 확인합니다. pending은 아직 완료되지 않은 것을 보여주고, done은 항목을 표시하며, status는 개수를 출력합니다. 지루하지만, 제 계정을 살려주었습니다.
파이프라인은 아무것도 없는 상태에서 실행되어야 합니다
저는 GPU가 없는 1 vCPU, 2 GB RAM 서버에서 이 모든 것을 실행합니다. 그 제약 조건이 어디서든 좋은 관행으로 판명된 습관들을 강요했습니다.:
- 한 번에 하나의 프로세스만 처리하세요. 단일 코어에서 이미지 작업을 병렬로 분산시키지 마세요.
- 각 에셋 처리 후
gc.collect()를 호출하여 메모리를 해제하세요.
- 평균 RSS가 아닌,
resource.getrusage(RUSAGE_SELF).ru_maxrss를 사용하여 최대 피크 RSS를 측정하세요. 크래시를 일으키는 것은 피크 값입니다.
- 무거운 단계는 각 서브프로세스별로 하나의 파일로 렌더링하여 메모리가 OS로 반환되게 하세요.
- 구성을 확인하기 위해 300dpi가 아닌 60dpi에서 미리보기를 검사하세요. 전체 해상도가 필요하지 않습니다.
프롬프트부터 완성된 제품까지의 전체 파이프라인은 해당 장치에서 39개의 제품과 856MB의 출력을 생성했습니다. 이 도구는 부모 프로젝트에 약 19,000줄의 Python 코드로 구현되어 있으며, 각 플랫폼이 실제로 강제하는 사항에 대한 약 300페이지 분량의 연구 자료를 기반으로 하고 있습니다.
패키징은 마지막 구간이며, 선택 사항이 아닙니다
단순 파일만으로는 판매할 수 없습니다. 동일한 콘텐츠라도 실제 크기인 1280x720 커버와 600x600 썸네일을 포함하여 압축하면, 제 자체 리스팅에서 눈에 띄게 더 좋은 전환율을 보였습니다. 이는 남겨진 파일이 아니라 제품으로 인식됩니다. 제 패키징 스크립트는 폴더, 제목, 부제목, 가격을 입력받아 하나의 명령어로 zip과 두 이미지를 모두 작성합니다. 커버에는 Pillow를 사용하며, 문서 형태의 제품은 reportlab을 사용하여 마크다운 문서를 스타일이 적용된 PDF로 변환하는 별도의 스크립트를 사용합니다. 이 모든 키트에서 외부 패키지는 이 두 가지뿐입니다. 메타데이터 빌더와 업로드 트래커는 Python 표준 라이브러리만 사용합니다.
제가 March-me에게 해주고 싶은 말
조회수(views)를 보는 것을 멈추세요. 에셋을 조회수 100당 판매량 순으로 정렬하고, 트래픽은 있지만 판매가 없는 제품의 제목과 키워드를 수정하며, 원장(ledger) 없이 업로드 배치를 다시 실행하지 마세요. 이 순서 덕분에 저는 최악의 달을 다음으로 무엇을 만들고 왜 만들어야 하는지 정확히 아는 포트폴리오로 바꿀 수 있었습니다.
위에 언급된 모든 기능들은 실제 작업물로 구축되었습니다: Adobe, Vecteezy, Dreamstime을 위한 메타데이터 빌더, 업로드 트래커, PDF 렌더러, 그리고 패키징 스크립트가 모두 하나의 작은 키트에 담겨 있으며, 프레임워크나 클라우드 계정 없이 2GB 박스에서 실행됩니다. 만약 이 도구들을 다시 구축하는 대신 그대로 사용하고 싶다면, 여기에서 확인할 수 있습니다:
키트 확인하기
디지털 제품을 제작 중이신가요? 디지털 제품 판매자를 위한 무료 가이드 및 키트.