개발 slecs

세 시간 동안 사이트 두 개 갈아엎고 봇 네 개 패치한 밤

목차

어제 밤 21시부터 자정까지, 처음부터 거창한 계획이 있었던 건 아니었다. diet 사이트 리뉴얼 작업이 생각보다 빨리 끝나면서 "이 김에 tools도 손 보자"로 번졌고, 그 사이 봇 로그를 보다가 파서 버그를 발견해 패치가 줄줄이 이어졌다. 전형적인 야간 스노우볼이었다.


리디자인 드라이브 — diet → tools 순서로 번진 이유

diet 사이트는 이미지 없이 텍스트 콘텐츠만 서비스하는 구조다 보니, 기존 카드형 레이아웃이 썸네일 박스만 빈 채로 떠 있어서 시각적으로 영 어색했다. 오늘 작업의 출발점은 이걸 텍스트 리스트로 전환하는 것이었는데, 거기서 자연스럽게 "어차피 손 댄 김에 전체 톤도 바꾸자"로 이어졌다. 민트그린 + 지마켓산스 조합의 geniet 스타일로 방향을 잡았고, 이모지를 전부 인라인 SVG로 교체하고 검색 우선 히어로 섹션으로 랜딩을 재구성했다.

리뉴얼이 마무리된 뒤 tools 사이트를 열어보니 디자인 퀄리티 차이가 눈에 확 들어왔다. 기존 tools는 슬롭이 꽤 쌓여 있었고 — 불필요한 컴포넌트 노출, 광고 배치가 UX를 방해하는 구조 — 그래서 광고를 전면 제거하고 img2pdf 계열의 클린 인디고 톤으로 통일하는 리디자인을 이어서 진행했다. 9폭 반응형 그리드도 이때 잡았다. 두 사이트를 같은 밤에 리뉴얼한 건 계획이 아니라 맥락이 끌어당긴 결과였다.


AdSense 검증 스크립트를 세 번 날려먹은 이야기

diet, tools, ads 관련 레포 세 곳 모두에서 같은 커밋 메시지가 남았다: "AdSense verification 스크립트 복구 (표시광고만 제거, 향후 승인 대비 — 실수로 같이 지웠던 것)". 세 번이다.

광고를 제거하는 작업을 할 때마다 <head> 영역을 통째로 정리하면서 검증용 메타 태그까지 날려버리는 패턴이 반복됐다. 세 번째 복구를 마치고 나서는 docs(ad) 커밋으로 운영지침에 명시를 박아뒀다 — 광고 일괄 제거 시 AdSense 검증 마커·로더·빌드스텝은 보존 대상이며, ad-free 스윕은 재주입을 전제로 한다는 내용이다. 같은 실수를 세 번 하고 나서야 문서화가 됐다는 건 창피하지만, 이런 게 쌓여서 운영지침이 두터워지는 거라고 생각한다.

사이트 작업 AdSense 복구 필요 여부
diet 광고 전면 제거 + 리뉴얼 ✅ 복구 필요
tools 슬롭 제거 + 광고 전면 제거 ✅ 복구 필요
ads 관련 레포 ad-free 스윕 ✅ 복구 필요

봇 파서 견고화 — diet에서 시작해 네 개로 번진 패치

diet 봇 작업을 하다가 LLM 응답에서 TITLE:, DESC:, METATITLE:, METADESC: 같은 메타라벨이 그대로 본문에 누출되는 버그를 발견했다. 프롬프트 구조상 LLM이 응답 포맷을 헤더+바디로 나눠 뱉는데, 파서가 그 경계를 항상 깔끔하게 자르지 못하는 케이스가 있었다.

diet 파서를 고치고 나서 lotto, funeral, pet 파서를 열어봤더니 — 예상대로였다. 세 곳 모두 동일한 취약 패턴. 같은 날 만든 봇들이라 코드를 거의 복사해서 쓴 게 원인이었다. 픽스도 동일한 패턴으로 적용했다: 라벨 키워드가 포함된 라인은 본문 파싱 단계에서 필터링하도록.

# 개념적으로 이런 패턴의 견고화
METALABELS = {"TITLE", "DESC", "METATITLE", "METADESC"}

def strip_meta_leakage(lines):
    return [
        line for line in lines
        if not any(line.strip().startswith(f"{label}:") for label in METALABELS)
    ]

한 곳에서 발견한 버그가 네 곳으로 퍼지는 건 귀찮지만, 그 귀찮음을 한 번에 처리한다는 점에서 발견 시점이 빠를수록 이득이다.


그 외 잡다하지만 중요한 것들

cafe24 배포 스크립트에서 오래된 지뢰를 하나 밟았다. bulk_seed.pysync_covers.py 두 스크립트가 SSH 접속 시 pubkey 인증을 먼저 시도하는 방식으로 짜여 있었는데, 이게 fail2ban의 실패 시도로 카운트되면서 배포 서버 IP가 차단되는 문제였다. 해결책은 단순했다 — pubkey-first 옵션 제거. 오랫동안 간헐적으로 배포가 실패하던 원인이 여기 있었던 것.

SEO 쪽으로는 두 가지를 처리했다. 그룹 페이지 meta description에 현재 멤버 이름을 노출시킨 것 — 팬들이 멤버 이름으로 검색할 때 스니펫에서 걸릴 확률을 높이기 위한 의도다. 그리고 diet의 검색결과 페이지는 noindex 처리했다. 검색 쿼리 파라미터로 생성되는 페이지가 구글에 색인되면 thin content나 중복 페이지 문제로 이어지기 때문에, 이건 애초에 막아두는 게 맞다.

admin 패널에서는 작은 UX 버그 두 개를 잡았다. 예약 글의 발행 날짜를 단순 날짜 문자열로 보여주던 걸 "N일 후" 형태로 바꿨고, 사이드바 활성 그룹이 라우팅 변경 시 갱신되지 않던 문제도 수정했다. 둘 다 오래된 이슈였는데 리뉴얼 작업 사이 잠깐 시간이 생겼을 때 처리했다.

마지막으로 GSC 제출 로직에 apex 도메인 마이그레이션 301 감지 시 cms_site.subdomain을 자동으로 NULL 처리하는 코드를 추가하고 seo-infra.md에 문서화했다. portfolio 조회수 추적도 전용 로그 파이프라인으로 분리했다.


야간 3시간짜리 작업치고는 커밋 개수가 제법 됐다. 리디자인 드라이브가 뼈대였고, 그 위에 봇 패치·배포 버그·SEO 정비가 층층이 쌓인 구조다. 미리 계획한 게 아니라 작업하면서 연결고리가 보여서 끌려간 흐름이었는데, 이런 밤이 쌓이면서 인프라가 조금씩 단단해진다고 느낀다.


🛒 이 글과 어울리는 추천 상품

*위 링크는 쿠팡파트너스 활동의 일환이며, 일정액의 수수료를 제공받을 수 있습니다.

댓글 0

첫 댓글 달아줘.