지난 2월에 사용하던 2GB 서버가 배치 작업 도중에 가득 찼습니다. make_metadata.py가 240개의 Adobe 에셋에 대한 CSV 파일을 작성하고 있을 때 디스크가 100%를 기록했고, 쓰기 작업이 잘렸습니다. 저는 이를 알아차리지 못했습니다. 그리고 잘린 CSV 파일을 Adobe Stock에 업로드했고, 표준적인 즉시 거부 메시지인 '헤더 행 오류(bad header row)'를 받았습니다. 그러고는 이 문제를 '수정'하기 위해 배치를 다시 실행했는데, 원장 파일(ledger)을 잊어버리고 Vecteezy에 60개의 파일을 중복 게시했습니다. 그 주에 저는 저장 공간 압박과 원장의 무결성이 같은 문제라는 것을 깨달았는데, 왜냐하면 공간 확보를 위해 무언가를 삭제하기 시작하는 순간, 가장 먼저 잘못된 것들을 지우게 되기 때문입니다.
제가 지금 하는 방법은 다음과 같습니다.
보호해야 할 첫 번째 것은 원장(Ledger)이다, 마지막이 아니다
upload_tracker.py는 표준 라이브러리에 포함된 일반 텍스트 원장 파일이며 의존성이 없습니다. 이 스크립트는 하나의 명령어로 완료된 모든 에셋을 기록합니다:
python upload_tracker.py done "asset-001.jpg"
핵심은 재실행(re-run)이 절대 중복 게시를 일으키지 않는다는 것입니다. 스톡 플랫폼들은 중복 제출을 스팸 신호로 간주하며, 반복되는 중복 업로드는 계정이 플래그 지정될 수 있는 가장 빠른 방법이라는 것을 제가 알고 있습니다. 따라서 원장 파일은 research/ 폴더에 보관하는데, 이 폴더는 제가 절대 삭제하지 않는 유일한 폴더이며, 어떤 정리 작업(cleanup pass)을 하기 전 항상 백업합니다. 그 크기는 몇 킬로바이트에 불과합니다. 이 파이프라인이 생성한 856MB의 완성된 결과물은 원본 에셋에서 대체할 수 있습니다. 하지만 원장은 그렇지 않습니다. 왜냐하면 어떤 플랫폼에 이미 업로드되었는지 기록하고 있기 때문입니다.
저장 공간 정리에는 정해진 순서가 있다
디스크가 가득 찰 때, 대부분의 사람들은 가장 큰 폴더부터 삭제합니다. 이것은 정확히 반대입니다. 제가 지금 사용하는 순서는 다음과 같습니다:
- 이미
packs/에 패키징된 원본 AI 이미지 출력물. ZIP 파일과 커버 및 썸네일이 제품입니다. 중간 과정(intermediates) 결과물은 아닙니다.
- 미리보기 렌더링 파일. 저는 300dpi 대신 60dpi로 검사하여, 미리보기 크기를 약 25배 줄이고 전체 해상도 파일이 존재하지 않아도 필요한 것을 볼 수 있게 합니다.
- 오래된 CSV 파일. 메타데이터는 에셋 폴더에서 몇 초 만에 재생성할 수 있으므로, 오래된 CSV 파일은 가치가 없습니다.
내가 절대 삭제하지 않는 것: research/, 원장(ledger), 그리고 아직 제출되지 않은 모든 항목을 위한 assets/. 나의 스크랩 로그와 메모는 미래 제품을 위한 원자재다. 나는 '정리'하려다가 이들을 한 번 지우는 실수를 저질렀고, 이미 기록해 둔 맥락을 재구성하는 데 일주일이라는 시간을 잃었다.
CSV 헤더가 즉시 실패하니, 수정하지 말고 재생성하세요
Adobe는 5개 열의 헤더를 원하고, Vecteezy는 4개 열을, Dreamstime은 15개 열을 원한다. 단 하나의 열만 잘못해도 플랫폼이 인간 검토자에게 도달하기도 전에 업로드를 거부한다. 저장 공간 압박 속에서 잘린 파일에 CSV를 수동으로 편집하는 것이 지난 2월 실패의 원인이었다.
make_metadata.py는 각 채널이 요구하는 정확한 헤더만 작성하고 그 외에는 아무것도 쓰지 않는다:
python make_metadata.py ./my-assets --platform adobe --out metadata.csv --ext .jpg
폴더를 읽고, 파일을 만들면 끝이다. 만약 디스크가 가득 차서 출력이 잘렸다면, CSV를 삭제하고 재생성한다. 2초다. CSV는 항상 파생된 결과물(derived artifact)일 뿐이다. 진실의 원천은 에셋 폴더와 titles.json이다. 메타데이터를 유지해야 할 문서가 아니라 일회용 출력물로 취급하는 순간, '내 CSV가 손상되었을까' 하는 두려움이 사라진다.
채우기 전에 천장을 측정하세요
전체 파이프라인은 1 vCPU, 2 GB RAM, GPU 없이 작동한다. 이 시스템을 유지하게 해준 습관들은 다음과 같다:
- 한 번에 하나의 프로세스만 실행한다. 단일 코어에서 이미지 작업을 병렬로 분산시키지 않는다.
- 각 에셋 처리 후
gc.collect()를 호출한다.
- 평균이 아닌 최대 RSS(Resident Set Size) 값을 위해
resource.getrusage(RUSAGE_SELF).ru_maxrss를 사용한다. 왜냐하면 최고치가 너를 죽이기 때문이다.
- 무거운 렌더링의 경우 서브프로세스당 하나의 파일만 처리하여 메모리가 OS로 반환되도록 한다.
디스크도 마찬가지 원칙이 적용된다. 거부 이메일을 받은 후에가 아니라 배치 작업 전에 여유 공간을 확인한다. 풀 해상도의 240개 에셋 배치는 기가바이트 단위로 측정된다. 만약 내가 그보다 적은 여유 공간만 가지고 있다면, 배치를 분할하고, 실행하고, 패키징한 다음, 다음 작업을 시작하기 전에 중간 결과물들을 정리한다.
폴더 레이아웃이 정리 작업을 안전하게 만든다
project/
assets/ 원시 출력물
output/ 제출 준비 파일 + metadata.csv
...
output/과 packs/의 모든 내용은 파생되었기 때문에, 정리 작업은 기계적입니다: assets/ 하류에 있는 것은 모두 재구축될 수 있습니다. 반면, research/에 있는 것은 영원히 보존됩니다. 이 단 하나의 규칙이 저장 공간 압박을 패닉에서 체크리스트로 바꾸었습니다.
도구가 위치한 곳
위에 언급된 모든 내용은 이론이 아닙니다. 이것들은 하나의 프롬프트 엔진으로부터 39개의 제품을 패키징했던 동일한 네 가지 스크립트입니다: make_metadata.py, upload_tracker.py, md2pdf.py, 그리고 pack_product.py이며, 약 19,000줄의 파이프라인 코드와 300페이지 이상의 연구를 기반으로 하고, 모두 동일한 1 vCPU, 2 GB 사양의 장비에서 검증되었습니다. 이들은 프레임워크나 클라우드 계정 없이 독립적인 Python 스크립트이며, 두 가지 메타데이터 도구는 표준 라이브러리 외에는 아무것도 필요하지 않습니다.
저는 이것들을 Gumroad에 벡터 프롬프트 팩으로 모아두었습니다. 이것은 이 게시물의 정직한 버전입니다: 설명이 아니라 실제로 작동하는 도구들입니다.
벡터 프롬프트 팩 받기
디지털 제품을 제작하시나요? 디지털 제품 판매자를 위한 무료 가이드 및 키트.