Node.js

  • AI 없이 코드로 만든 썸네일 자동 생성 (비용 0원)

    AI 없이 코드로 만든 썸네일 자동 생성 (비용 0원)

    블로그를 운영하다 보면 어느 순간 홈 화면이 이상하게 어수선해 보이는 때가 온다. 나한테는 그게 썸네일이었다.

    스크린샷으로 썸네일을 만들면 뭐가 문제일까?

    처음에는 대수롭지 않게 생각했다. 글 쓰고, 관련 화면 캡처하고, 그걸 대표 이미지로 올리면 끝. 빠르고 간단했다.

    근데 카드가 쌓이면서 문제가 드러났다. 어떤 카드는 흰 배경의 밝은 에디터 화면, 어떤 카드는 어두운 터미널 화면. 톤이 제각각이었다. 여기서 끝이 아니었다. 스크린샷마다 여백 비율이 달랐다. 카드 썸네일로 자르면 내용이 잘리거나 텅 빈 것처럼 보이는 것도 나왔다.

    사용자가 홈 화면을 보고 한마디 했다. “좀 더 깔끔하고 고급스럽게 만들 수 없을까요?” 그 말을 듣고 보니, 스크린샷 짜깁기로는 한계가 명확했다.

    스크린샷 대신 코드로 브랜드 썸네일 자동 생성하기 (AI 크레딧 0원) 스크린샷 1

    위가 당시 실제 홈 화면이다. 카드 하나하나를 따로 보면 그럭저럭 봐줄 만한데, 그리드 전체를 놓고 보면 통일감이 없다.

    AI 이미지 생성이 아닌 코드 합성을 고른 이유

    AI로 이미지를 생성하는 방법도 고려했다. 잠깐 진지하게.

    근데 두 가지 이유로 접었다. 첫 번째는 비용이다. 매 글마다 이미지 생성 API(외부 서버에 요청을 보내 이미지를 만드는 서비스)를 호출하면 글을 쓸 때마다 크레딧이 나간다. 두 번째는 일관성이다. AI 생성 이미지는 프롬프트(AI에게 전달하는 명령 텍스트)가 같아도 매번 스타일이 미묘하게 달라진다. 카드 그리드의 통일감을 위해 썸네일을 바꾸는 건데, AI로 만들면 오히려 통일감이 더 흐트러질 수 있다.

    결론은 단순했다. 제목 텍스트와 카테고리를 코드로 합성(여러 요소를 프로그래밍으로 조합해 이미지를 만드는 것)해서, 항상 똑같은 스타일의 썸네일을 찍어내는 쪽을 고른다. 비용은 0원. 스타일은 항상 동일하다.

    Sharp로 SVG 썸네일을 PNG로 변환하기

    실제 구현 방식은 이렇다. sharp라는 이미지 처리 라이브러리(Node.js 환경에서 이미지를 읽고 변환하고 저장하는 도구)를 사용했다. SVG(코드로 그리는 벡터 이미지 형식)로 썸네일 디자인을 정의하고, 그걸 PNG로 변환하는 구조다.

    캔버스(이미지의 전체 작업 공간) 크기는 1200×675. 16:9 비율이다. 배경은 인디고 계열 그라데이션을 깔았다. 왼쪽 위에는 ‘AgentLog’ 로고 텍스트를 넣고, 그 아래 카테고리 배지, 그리고 글 제목을 크게 배치했다.

    신경 쓴 부분이 두 가지 있었다. 제목이 길면 자동으로 줄바꿈되게 했고, 글자 수에 맞춰 폰트 크기도 줄어들게 처리했다. 제목이 30자짜리 글도 있고 10자짜리 글도 있으니, 이걸 고정 크기로 두면 어디선가 반드시 넘친다.

    제일 걱정한 건 한글 렌더링(화면에 글자를 실제로 그려내는 과정)이었다. SVG 안에서 한글이 깨지거나 이상하게 나오면 처음부터 다시 접근해야 하니까. 테스트해보니 macOS 기본 폰트로 깨짐 없이 잘 나왔다. 이 부분은 솔직히 예상보다 수월했다.

    스크린샷 대신 코드로 브랜드 썸네일 자동 생성하기 (AI 크레딧 0원) 스크린샷 2

    변환 결과물이다. 스크린샷 모음이랑 비교하면 차이가 바로 보인다. 단순하지만 톤이 일정하다.

    워드프레스 API로 기존 글 14개 일괄 처리하기

    새 글에만 적용하면 어색해진다. 예전 글 썸네일은 여전히 스크린샷이고, 새 글 썸네일만 정돈된 디자인이면 오히려 더 눈에 띄게 이상하다.

    그래서 기존 글도 전부 다시 처리했다. 발행된 글 8개, 예약된 글 6개. 총 14개를 한 번에 재생성하는 스크립트(자동으로 반복 작업을 처리하는 코드 파일)를 따로 만들었다.

    흐름은 이렇다. 전에도 REST API로 글 데이터를 직접 다룬 적이 있는데, 이번에도 같은 방식이었다. 워드프레스 REST API(워드프레스가 외부 코드와 데이터를 주고받을 수 있도록 제공하는 통신 창구)로 각 글의 제목과 카테고리를 가져온다. 그 데이터로 썸네일을 생성한다. 생성된 이미지를 미디어 라이브러리(워드프레스에서 이미지, 파일 등을 보관하는 공간)에 업로드하고, 해당 글의 대표 이미지를 갈아끼운다. 14개 전부 한 번에 실행했고, 몇 분 만에 끝났다.

    스크립트 하나로 14개를 처리한다는 게 처음엔 좀 무섭기도 했다. 잘못되면 14개가 동시에 망가지니까. 실행 전에 글 하나만 따로 테스트하고, 결과를 눈으로 확인한 다음 전체를 돌렸다.

    Alt 텍스트까지 한 번에 채우기

    썸네일은 바뀌었는데 문제가 하나 더 있었다. 워드프레스 미디어 라이브러리에 등록된 대체 텍스트(alt text, 이미지를 볼 수 없을 때 대신 읽히는 텍스트로 접근성과 SEO에 영향을 준다)가 비어있었다.

    마침 이 시점에 워드프레스에 새로 생긴 ‘커넥터’ 화면을 발견했다. AI API 키(외부 AI 서비스를 사용하기 위한 인증 코드)를 저장해두는 코어 기능인데, 여기에 연결하면 대표 이미지·대체 텍스트·제목을 AI로 자동 생성해주는 공식 플러그인을 설치할 수 있는 구조였다.

    잠깐 혹했다. 근데 생각해보면 이미 답이 있었다. 썸네일 이미지 안에 들어가는 내용 자체가 글 제목이다. AI를 부를 이유가 없다. 제목 텍스트를 그대로 alt로 넣으면 충분하다. 새 플러그인 설치 없이, 스크립트에 코드 몇 줄 추가하는 것으로 끝났다.

    스크린샷 대신 코드로 브랜드 썸네일 자동 생성하기 (AI 크레딧 0원) 스크린샷 3

    미디어 라이브러리에서 alt 텍스트가 채워진 상태다. 간단한 해결이었는데, 찾는 데 오히려 시간이 걸렸다.

    배포할 때 실제로 주의할 점

    이 작업을 따라 해보려는 사람이 있다면, 몇 가지는 반드시 미리 알고 시작해야 한다.

    • 글 하나만 먼저 테스트해라. 14개를 한 번에 돌리기 전에 반드시 글 하나에만 스크립트를 적용하고 결과를 확인해야 한다. 일괄 처리 스크립트는 잘못될 경우 피해 규모도 일괄이다.
    • 기존 썸네일을 먼저 백업해라. 미디어 라이브러리에서 원본 이미지를 지우지 마라. 대표 이미지만 갈아끼우고 기존 파일은 남겨두면, 문제가 생겼을 때 되돌릴 수 있다.
    • API 인증 정보를 코드 안에 직접 쓰지 마라. 워드프레스 REST API를 쓰려면 사용자 이름과 애플리케이션 비밀번호(Application Password, 워드프레스에서 API 접근용으로 따로 발급하는 비밀번호)가 필요하다. 이 값은 .env 파일(환경 변수를 따로 저장하는 설정 파일)에 넣고, 절대 GitHub(코드를 공개 저장하는 플랫폼) 같은 곳에 올리지 마라.
    • 폰트 문제는 환경마다 다르다. macOS에서 한글이 잘 나왔다고 서버에서도 잘 나온다는 보장이 없다. 리눅스 서버 환경에서 자동화할 계획이라면 폰트 파일을 따로 지정하는 방식을 써야 한다.

    참고로 이 썸네일, 글 상세 페이지에서는 다른 비율로 잘려서 나온 적이 있었다. 이미지 생성 코드는 멀쩡했는데 템플릿 설정이 문제였다.

    이번 작업을 끝내고 나서 하나 다시 확인한 게 있다. 새로운 기능이 필요할 때 플러그인이나 유료 API부터 찾는 건 좋은 습관이 아니다. 이미 가진 데이터로 해결되는지 먼저 따져봐야 한다. alt 텍스트가 그랬다. AI를 부르기 직전에 “잠깐, 제목이 이미 있잖아”라는 생각 하나로 불필요한 의존성이 사라졌다.

    다음에는 썸네일 디자인 자체에 변화를 줘볼 생각이다. 카테고리별로 배경색을 다르게 주는 방식. 지금은 전부 인디고 단색인데, 카테고리가 시각적으로 구분되면 홈 화면의 카드 그리드가 한 번 더 정돈될 것이다. 아직 확신은 없다. 일단 해봐야 안다.