3 min read · 609 words
활용 팁 / 블로그 운영 / 디버깅 스토리
약 1,600자
LLM 으로 글 본문을 받아서 그대로 Blogger 에 박았더니 데스크탑은 멀쩡한데 모바일에서 글이 화면 밖으로 삐져나갔습니다. 같은 문제가 사이트 글 41 편에 누적되어 있었습니다. 추적과 일괄 fix 패턴을 풀어 둡니다.
사고 발견
모바일 (375px iPhone) 에서 글을 열어보니 본문이 화면보다 넓어서 가로 스크롤이 발생. 글 안 사진은 화면 밖으로 잘림. 표는 옆으로 미끄러져 사라짐.
데스크탑 (1280px) 에서는 멀쩡해서 한참 동안 못 발견. 사장님이 휴대폰으로 본인 글을 처음 본 날 발견했습니다.
추적 — 본문 안 위험 inline style
HTML 본문을 grep:
grep -oE 'style="[^"]*"' post_body.html | sort | uniq -c | sort -rn | head
가장 흔한 패턴:
width:800px(LLM 이 표 너비 명시)margin-left:-30px(LLM 이 좌측 정렬 시도)width:100vw(LLM 이 hero 이미지 풀너비)min-width:600px(LLM 이 카드 최소 너비)position:absolute(LLM 이 배지 floating 시도)
이 다섯 패턴이 모바일 좌우 짤림의 90%. LLM 출력에 자주 나타납니다.
해결 — publish-time sanitize
발행 직전 단계에서 위험 inline-style 자동 strip.
import re
def strip_dangerous_styles(html: str) -> tuple[str, int]:
n = 0
def clean(m):
nonlocal n
val = m.group(1)
orig = val
# 고정 너비 400px+
val = re.sub(r'width\s*:\s*(?:[4-9]\d{2}|[1-9]\d{3,})px\s*;?\s*', '', val)
# 100vw
val = re.sub(r'width\s*:\s*100vw\s*;?\s*', '', val)
# min-width 400px+
val = re.sub(r'min-width\s*:\s*(?:[4-9]\d{2}|[1-9]\d{3,})px\s*;?\s*', '', val)
# 음수 horizontal margin
val = re.sub(r'margin(?:-left|-right)\s*:\s*-\d+px\s*;?\s*', '', val)
# position:absolute
val = re.sub(r'position\s*:\s*absolute\s*;?\s*', '', val)
val = val.strip().rstrip(';').strip()
if val != orig:
n += 1
return f'style="{val}"' if val else ''
return m.group(0)
html = re.sub(r'style="([^"]*)"', clean, html)
# <img> 태그의 고정 width/height 속성도 제거
html = re.sub(r'(<img\b[^>]*)\s+width="\d+"', r'\1', html)
html = re.sub(r'(<img\b[^>]*)\s+height="\d+"', r'\1', html)
return html, n
# 사용
new_html, fixes = strip_dangerous_styles(html)
print(f"strip count: {fixes}")
이 한 함수만 발행 직전에 호출하면 좌우 짤림 사고가 사라집니다. 동시에 safety CSS 한 줄도 본문 top 에 강제 inject.
safety CSS
본문 컨테이너에 max-width 강제. LLM 이 어떤 inline style 박더라도 컨테이너가 화면 밖으로 안 나가게 안전망.
<style data-tsp-safety-css="v2">
.post-body, .entry-content, .article-content {
max-width: 100% !important;
overflow-wrap: anywhere;
word-break: keep-all;
}
.post-body img, .post-body iframe, .post-body video,
.post-body svg, .post-body canvas {
max-width: 100% !important;
height: auto !important;
}
.post-body table {
width: 100% !important;
max-width: 100% !important;
}
.post-body pre, .post-body code {
max-width: 100% !important;
white-space: pre-wrap;
word-break: break-word;
overflow-x: auto;
}
</style>
!important 가 들어가서 LLM 의 inline style 보다 우선. 컨테이너 단단함.
일괄 fix
기존 발행된 41 편에는 적용 안 됨. 그래서 walker 한 번 돌렸습니다.
import asyncio
from googleapiclient.discovery import build
async def fix_all_live():
svc = build_blogger_service()
posts = svc.posts().list(blogId=BLOG_ID, maxResults=500).execute()
fixed = 0
for p in posts.get("items", []):
old = p["content"]
new, n_strip = strip_dangerous_styles(old)
# safety CSS 강제 inject (idempotent — 옛 marker strip 후 새로 박힘)
new = re.sub(r'<style[^>]*data-tsp-safety-css[^>]*>.*?</style>', '', new, flags=re.S)
new = SAFETY_CSS + new
if new != old:
svc.posts().patch(blogId=BLOG_ID, postId=p["id"],
body={"content": new}).execute()
fixed += 1
print(f"fixed {fixed} posts")
asyncio.run(fix_all_live())
41 / 41 fixed. 모바일에서 다시 봤을 때 좌우 짤림 0건.
따라하실 분께
LLM 으로 본문 받는 모든 블로그가 같은 사고 가능. 위 두 패턴 (sanitize 함수 + safety CSS) 만 발행 hook 한 곳에 박아두면 평생 예방.
요약: LLM 출력의 fixed-width inline style 이 모바일 좌우 짤림의 90% 원인. 발행 직전 strip 한 줄 + safety CSS 한 블록 = 사고 0.
Category Coverage Notice
This article follows our label-specific editorial criteria. Details: