13화 | [AI와 1인기업] 카드뉴스 제작 자동화: Python Pillow와 dynamic font 가로배치 알고리즘 구현
카드뉴스 제작 자동화: Python Pillow와 dynamic font 가로배치 알고리즘 구현 (13화)
안녕하세요! AI 크루들과 무자본 IT 1인 기업을 꾸려가고 있는 크리에이티브 디렉터 DAVID입니다.
지난 12화에서는 파이썬(Python) 코드를 활용해 여러 장의 카드뉴스 이미지 슬라이드와 본문 캡션을 결합하여 인스타그램 서버에 3단계 보안 셰이크로 자동 예약 업로드하는 '인스타그램 API 실전 코딩'을 다뤘습니다. 이로써 소셜 미디어 플랫폼에 무인으로 피드를 쏘아 올릴 '철로(배포 레일)'는 완벽하게 구축되었습니다. 하지만 이 레일 위에 매일 얹어 보낼 5장~10장짜리 카드뉴스 이미지 자체를 사람이 매번 포토샵이나 캔바(Canva)로 그리고 글자를 기계처럼 복사해 채워 넣고 있다면, 그것은 진정한 의미의 완전 자동화가 아닙니다.
글자와 텍스트 초안만 시스템에 던져주면, 백그라운드 서버가 단 1초 만에 최적의 폰트 크기와 정밀한 자간/행간을 연산하여 고해상도 카드뉴스 이미지 슬라이드를 스스로 렌더링해내는 '카드뉴스 동적 렌더링 알고리즘'이 더해져야 비로소 무인 트래픽 공장이 완성됩니다. 오늘 그 파이썬 Pillow 기반의 동적 타이포그래피 드로잉 핵심 코딩 로직을 낱낱이 파헤칩니다!
1. 텍스트 가변성에 대응하는 동적 레이아웃의 3대 난제
단순히 이미지 위에 글씨를 그리는 것(Draw Text)은 어렵지 않습니다. 하지만 1인 기업 자동화 파이프라인에서 입력받는 텍스트는 매일 주제에 따라 글자 수가 무작위로 변한다는 것이 문제입니다. 고정된 디자인 템플릿에 가변 텍스트를 무작정 밀어 넣으면 다음과 같은 3대 레이아웃 붕괴 현상이 발생합니다.
- 텍스트 탈출 현상 (Overflow): 글자 수가 조금만 길어져도 텍스트가 카드뉴스 캔버스 영역 밖으로 뚫고 나가 잘려버립니다.
- 가독성 훼손 (No Word Wrap): 단어의 중간이 뚝 끊기거나 강제 줄바꿈이 일어나서 눈에 몹시 거슬리는 배치가 됩니다.
- 자막 오버레이 겹침: 폰트 크기가 고정되어 있어서 텍스트가 여러 줄이 되었을 때 하단의 뱃지나 장식 레이아웃과 포개져 글씨를 알아볼 수 없게 됩니다.
이 문제를 공학적으로 해결하기 위해 우리는 Word Wrap(자동 줄바꿈)과 Dynamic Font Size Fitting(동적 폰트 축소 연산) 알고리즘을 이식했습니다.
2. 텍스트 자동 줄바꿈(Word Wrap) 및 폰트 피팅 알고리즘 메커니즘
가변 텍스트를 카드뉴스 캔버스 안에 정교하게 밀어 넣는 3단계 프로세스 구조는 다음과 같습니다.
| 처리 단계 | 알고리즘 명칭 | 동작 상세 설명 |
|---|---|---|
| 1단계 | Greedy Word Wrap | 지정된 최대 가로 픽셀폭(예: 캔버스 크기 1024px의 80%인 820px)을 한계선으로 정하고, 단어 단위로 쪼갠 텍스트가 이 경계선을 넘을 때 자동으로 줄바꿈(\n) 기호를 삽입해 줍니다. |
| 2단계 | Font Size Fitting | 줄바꿈 처리된 최종 텍스트의 상하좌우 바운딩 박스(Bounding Box)를 계산하여, 이미지 전체 높이와 너비를 벗어난다면 폰트 크기(Font Size)를 2px씩 단계적으로 감축하여 한계선 안으로 피팅시킵니다. |
| 3단계 | Double Layer Drawing | 검은색 반투명 오버레이 박스를 먼저 깔고, 그 위에 4방향으로 1px씩 이동해 가며 아웃라인(외곽선) 글씨를 그린 다음, 마지막에 정중앙에 메인 흰색 텍스트를 올려 야외 배경에서도 미친 가독성을 지니게 드로잉합니다. |
3. 카드뉴스 자동 드로잉 파이썬(Python) 실전 소스코드
실제 저희 Art Gourmet 시스템 내부에서 텍스트 수집 즉시 고해상도 카드뉴스를 뽑아내는 Pillow 렌더링 코드 스펙입니다.
from PIL import Image, ImageDraw, ImageFont
def get_wrapped_text(text, font, max_width):
words = text.split(' ')
lines = []
current_line = []
for word in words:
current_line.append(word)
# getbbox를 사용해 텍스트의 실질 픽셀 가로 크기를 정밀 측정
line_width = font.getbbox(' '.join(current_line))[2]
if line_width > max_width:
current_line.pop()
lines.append(' '.join(current_line))
current_line = [word]
if current_line:
lines.append(' '.join(current_line))
return '\n'.join(lines)
def draw_card_news(background_image_path, output_path, text, title_badge):
# 1. 1024x1024 캔버스 생성 및 백그라운드 로드
img = Image.open(background_image_path).resize((1024, 1024))
draw = ImageDraw.Draw(img)
# 2. 폰트 로드 및 다이나믹 피팅
font_path = "assets/fonts/Outfit-Bold.ttf"
font_size = 63 # (1024 * 0.062 비율 기준)
max_text_width = 820
max_text_height = 220
font = ImageFont.truetype(font_path, font_size)
wrapped_text = get_wrapped_text(text, font, max_text_width)
# 텍스트 높이가 허용 범위를 넘어가면 폰트 크기 자동 축소
while True:
text_bbox = draw.textbbox((0, 0), wrapped_text, font=font)
text_w = text_bbox[2] - text_bbox[0]
text_h = text_bbox[3] - text_bbox[1]
if text_h <= max_text_height or font_size <= 32:
break
font_size -= 2
font = ImageFont.truetype(font_path, font_size)
wrapped_text = get_wrapped_text(text, font, max_text_width)
# 3. 하단 반투명 블랙 오버레이 박스(알파 210) 합성
overlay = Image.new('RGBA', img.size, (0, 0, 0, 0))
overlay_draw = ImageDraw.Draw(overlay)
# 하단 22% 영역 밀착 박스 그리기
overlay_draw.rectangle([0, 800, 1024, 1024], fill=(0, 0, 0, 210))
img = Image.alpha_composite(img.convert('RGBA'), overlay)
draw = ImageDraw.Draw(img)
# 4. 아웃라인 처리 가독성 텍스트 드로잉
x, y = 102, 850
# 4방향 1px 오프셋 굵은 외곽선 처리
for dx, dy in [(-1,-1), (1,-1), (-1,1), (1,1)]:
draw.text((x+dx, y+dy), wrapped_text, font=font, fill=(0, 0, 0))
# 중앙 본문 그리기
draw.text((x, y), wrapped_text, font=font, fill=(255, 255, 255))
# 5. 최상단 타이틀 뱃지 드로잉
badge_font = ImageFont.truetype(font_path, 24)
draw.rounded_rectangle([102, 50, 280, 90], radius=5, fill=(243, 156, 18))
draw.text((120, 58), title_badge, font=badge_font, fill=(255, 255, 255))
# 6. 최종 파일 저장
img.convert('RGB').save(output_path, 'JPEG', quality=95)
print(f"카드뉴스 이미지 동적 렌더링 완료: {output_path}")
4. 13화를 마치며: 1초 만에 퍼지는 정보성 콘텐츠
동적 렌더링 줄바꿈 코드를 이식함으로써, 이제 10줄 남짓한 뉴스 초안이나 리포트 텍스트만 서버 데몬에 밀어 넣어주면, 인공지능이 즉시 여러 장의 고해상도 카드뉴스 이미지 세트를 알아서 인코딩해내는 무인 콘텐츠 크리에이팅 파이프라인의 핵심 퍼즐을 완성했습니다.
카드뉴스 자동 제작과 예약 업로드 파이프라인이 정밀하게 완성되어 SNS 트래픽을 당길 모든 만반의 준비를 끝마쳤습니다.
다음 14화에서는 이렇게 생성된 릴스 비디오나 이미지 카드뉴스 포스팅 아래에 삽입되어, 방문자들을 실제 내 상점이나 도메인으로 자석처럼 끌어당기는 "소셜 마케팅의 완착: 프로필 링크 클릭률(CTR)을 극대화하는 바이오(Bio) 랜딩 페이지 기획 및 설계"를 입체적으로 다루며 3부의 마침표를 장식해 보겠습니다.
자동 드로잉 알고리즘의 강력한 생산성을 비즈니스에 직접 얹어 보세요. 다음 편에서 뵙겠습니다! 😊🚀
*글쓴이: AI와 함께 무한한 트래픽 엔진을 빌드하는 Creative Director, DAVID.*
댓글
댓글 쓰기