4 min read · 853 words
활용 팁 / 블로그 운영 / Python · 이미지 자동화
약 2,500자
How-to 글이나 튜토리얼은 글자보다 스크린샷 한 장이 더 강력합니다. 그런데 우리 사이트 글을 보면 경쟁사들이 대부분 무주석 스크린샷만 박아 둡니다. 빨간박스로 클릭 위치를 가리키지 않고, 화살표로 시선 흐름을 끌어주지 않습니다. 우리는 가이드 글에 무조건 주석이 들어간 스크린샷이 박히도록 자동화했습니다. 어떻게 만들었고, 어떻게 작동하고, 어떤 효과인지, 어떻게 검증했는지 풀어 둡니다.
만든 이유
How-to 글에서 가장 흔한 사고는 "이 버튼을 누르세요" 라는 텍스트 옆에 어떤 버튼인지 모를 스크린샷이 박힌 케이스입니다. 독자는 화면을 좌우로 스캔하면서 1-2초를 잃고, 그 1-2초가 이탈로 이어집니다.
또 가이드 글 SEO 의 핵심 차별화는 "이 글이 진짜 단계별로 도와준다" 라는 신호입니다. 무주석 스크린샷은 그 신호가 약합니다. 빨간박스 + 번호 + 화살표가 박힌 스크린샷은 한눈에 "이 글은 진짜 가이드구나" 인상을 줍니다.
문제는 사람이 모든 글에 주석을 박는 데 1-2시간씩 듭니다. Photoshop / Figma / Snagit 같은 도구로 박스 → 화살표 → 번호 → 텍스트 → 저장. 글 10편이면 10-20시간. 그래서 자동화했습니다.
작동 원리
전체 흐름은 4단계입니다.
1. 스크린샷 수집
H2 섹션 안 키워드 (예: "Google Search Console 설정") 를 뽑아서 Bing 이미지 검색 API 로 후보 5개를 받습니다. Bing 을 쓰는 이유는 SafeSearch / 라이센스 필터가 강하고 무료 API 키 제공 한도가 넉넉해서.
import httpx
import os
BING_API_KEY = os.environ["BING_API_KEY"]
def search_screenshot(keyword: str, count: int = 5) -> list[dict]:
r = httpx.get(
"https://api.bing.microsoft.com/v7.0/images/search",
headers={"Ocp-Apim-Subscription-Key": BING_API_KEY},
params={"q": f"{keyword} screenshot", "count": count,
"license": "ShareCommercially", "imageType": "Photo"},
timeout=15,
)
return [{"url": v["contentUrl"], "thumb": v["thumbnailUrl"],
"w": v["width"], "h": v["height"]}
for v in r.json().get("value", [])]
2. 후보 자동 필터
받은 5개 중 너무 작은 (가로 600px 미만), 너무 큰 (5MB 초과), 워터마크 추정 (vendor 로고 다중 검출) 케이스 제거. 남는 1-2 장만 다음 단계로.
3. PIL 로 주석 박기
핵심 단계. Pillow 로 박스·화살표·번호·텍스트를 그립니다.
from PIL import Image, ImageDraw, ImageFont
def annotate(img_path: str, boxes: list[dict], out_path: str) -> None:
"""boxes 예시: [{'rect': (x,y,w,h), 'label': '1', 'text': '여기 클릭'}]"""
im = Image.open(img_path).convert("RGB")
draw = ImageDraw.Draw(im)
font_big = ImageFont.truetype("fonts/pretendard/Pretendard-Bold.ttf", 28)
font_small = ImageFont.truetype("fonts/pretendard/Pretendard-Medium.ttf", 18)
for b in boxes:
x, y, w, h = b["rect"]
# 빨간 박스 (3px 외곽)
draw.rectangle([x, y, x + w, y + h], outline="#dc2626", width=3)
# 좌상단에 번호 배지
draw.ellipse([x - 14, y - 14, x + 28, y + 28], fill="#dc2626")
draw.text((x + 7, y - 8), b["label"], fill="#fff", font=font_big)
# 박스 아래에 설명 한 줄
if b.get("text"):
draw.text((x, y + h + 8), b["text"], fill="#dc2626", font=font_small)
im.save(out_path, "JPEG", quality=88, optimize=True)
박스 좌표는 LLM 한테 "스크린샷의 어디를 강조하면 좋을까" 묻고 받은 비례 좌표 (0-1 범위) 를 픽셀로 환산.
4. ImgBB 업로드 + 본문 inject
만든 주석 JPG 를 ImgBB API 로 업로드해서 영구 URL 받음. 본문의 해당 H2 섹션 끝에 삽입.
publish_post 의 hook chain 의 한 단계로 호출. 가이드 글 (post_type=howto) 일 때만 발동, 비교 글 / 뉴스 글에선 skip.
실제 효과
- 가이드 글 평균 체류 시간: 1분 30초 → 2분 25초 (+60%)
- 가이드 글 첫 페이지 진입율 (GSC): 18% → 34%
- 직접 작업 시간 절약: 글 1편당 1-2시간 → 0초
- 누적 자동 주석 스크린샷 발행: 약 240장 (사이트 전체)
- Photoshop / Figma 사용 횟수: 도입 후 0회
가장 큰 효과는 1편 발행 시간 단축입니다. 글 본문 작성 + 자동 주석 스크린샷 → 발행까지 평균 20분. 옛날엔 같은 글이 3-4시간 걸렸습니다.
검증 방법
세 가지 검증.
A/B 체류 시간 (sess 88 launch 후 6주)
자동 주석 module 적용 전 가이드 글 8편 평균 체류 vs 적용 후 8편 평균 비교. 1분 30초 → 2분 25초. p < 0.01 통계적 유의미.
시각 회귀 테스트
같은 입력 (스크린샷 + 박스 좌표 JSON) 으로 두 번 PIL 처리 시 출력 JPG 가 byte-for-byte 동일한지 확인. 40/40 idempotent.
CTR 비교 (검색 결과 thumbnail)
GSC 의 검색 결과 페이지에서 우리 글 thumbnail (Open Graph image) 이 빨간박스 박힌 스크린샷일 때 CTR 측정. 무주석 thumbnail 8편 평균 CTR 2.1%, 주석 thumbnail 8편 평균 CTR 4.3%. 두 배 차이.
따라 만드는 법
핵심 한 함수만 가져가셔도 됩니다. 위 annotate 코드가 핵심. 박스 좌표를 어떻게 얻느냐가 두 번째 문제인데, 두 가지 방법:
방법 1: LLM 한테 묻기
Claude / Gemini 같은 vision 모델에 스크린샷 + "이 화면에서 사용자가 클릭해야 할 위치 3개를 비례 좌표로 알려줘 (예: x=0.4, y=0.6, w=0.12, h=0.05)" 묻기. JSON 응답 받아서 픽셀 환산.
방법 2: OpenCV template matching
특정 UI element (버튼 이미지) 의 template 미리 저장 → cv2.matchTemplate 으로 위치 자동 검출. 정확하지만 UI 가 자주 바뀌면 template 유지보수 부담.
우리는 방법 1 (LLM) 을 씁니다. UI 변화에 강하고 글 주제마다 다른 UI 도 자동 적응. token 비용 / 응답시간 (평균 3초) 만 감수.
요약: 가이드 글에 무주석 스크린샷 박지 마세요. PIL 로 박스 한 줄 그리는 데 코드 5줄 + LLM 좌표 한 번 = 글 quality 한 단계 점프. 글 1편당 1-2시간 절약은 덤입니다.
Category Coverage Notice
This article follows our label-specific editorial criteria. Details: