개발 slecs

9개 사이트 SEO 새벽 롤아웃과 자동발행 봇 런칭

목차

자정을 넘기고 시작한 작업인데 결과적으로 새벽 6시까지 끌렸다. 처음엔 "푸터에 운영자 표기 하나 추가하면 되겠지" 싶었는데, 막상 건드리기 시작하자 연관 작업이 눈덩이처럼 불어났다. 총 9개 사이트에 Organization JSON-LD를 심고, 8개 국어 FAQ 섹션을 붙이고, 소셜 자동발행 봇까지 런칭했다. 오늘 그 흐름을 정리해두지 않으면 나중에 "왜 이렇게 했더라"를 답하기 어려울 것 같아서 적는다.

운영자 귀속(operator attribution) 전사 롤아웃

시작은 단순했다. E-E-A-T 맥락에서 "이 사이트를 누가 운영하는지"를 구글이 파악할 수 있어야 한다는 판단이었다. 실제 검색 결과에서 운영 주체가 불분명한 사이트는 신뢰도 점수에서 불이익을 받는다. 특히 다국어 사이트일수록 "누가 만들었냐"를 명시적으로 JSON-LD로 선언하는 게 유리하다.

처음에 한 사이트만 작업했는데, 나머지 사이트들도 전부 같은 상태라는 걸 금방 파악했다. 그래서 9개 사이트를 순서대로 돌면서 각 레이아웃 파일(Base.astro, Dex.astro, Footer.astro, Seo.astro, layout.tsx, server.mjs 등)에 동일한 패턴을 적용했다.

적용 내용은 두 가지로 통일했다:
- 푸터에 운영자 텍스트 크레딧 삽입
- <script type="application/ld+json">으로 Organization / WebSite JSON-LD에 publisher 또는 parentOrganization 링크 추가

작업하면서 발견한 버그가 하나 있었다. 처음에 운영자 링크를 사이트 내부 로컬 경로로 연결했는데, 이게 문제였다. 다국어 사이트에서 한국어 방문자한테는 /ko/about/을, 일본어 방문자한테는 /ja/about/을 보여줬는데, 글로벌 방문자가 봤을 때 어떤 언어 페이지로 떨어질지 일관성이 없었다. 운영자 귀속 링크는 전 세계 크롤러가 동일하게 인식해야 하니까 /en/about/으로 고정하는 게 맞다고 판단했다. 그래서 9개 사이트 전부를 두 라운드에 걸쳐 작업했다: 1차는 operator 추가, 2차는 링크를 /en/about/으로 수정.

같은 커밋 패턴이 반복되다 보니 중간에 copy-paste 실수도 있었다. blindboxdexindexnow-ping.js에서 HOST 변수를 다른 사이트에서 복붙해 온 값 그대로 두는 버그가 있었는데, 이번에 소셜 봇 작업하다가 같이 발견해서 수정했다. 이런 류의 버그가 왜 생기냐면, 9개 사이트 레이아웃이 구조적으로 비슷하다 보니 코드 블록을 그대로 들고 오는 습관이 있어서다.

검색의도 어순 + 8개국어 FAQ, 그룹/멤버 페이지 SEO

operator 작업이 마무리된 새벽 2시쯤부터는 콘텐츠 SEO로 전환했다.

두 가지를 동시에 진행했다.

첫째, 로케일별 SEO 제목 템플릿 정비. 그룹 페이지와 멤버 페이지 제목이 "브랜드명 | 그룹명 프로필"처럼 구성되어 있었는데, 이 어순이 검색의도와 맞지 않았다. 유저는 "BTS 프로필"로 검색하지 "브랜드명 BTS"로 검색하지 않는다. 한국어, 영어, 일본어, 중국어 등 각 언어별로 검색의도에 맞는 어순을 새로 정의하고 ui.ts에 i18n 키로 박았다. GroupPage.astro와 MemberPage.astro에서 이 키를 참조해서 제목을 조합하도록 변경.

// ui.ts 예시 구조 (실제 식별자 마스킹)
'seo.group.title': {
  ko: '{group} 프로필, 데뷔 정보 | {brand}',
  en: '{group} Profile & Debut Info | {brand}',
  ja: '{group} プロフィール・デビュー情報 | {brand}',
  // ...
}

둘째, FAQPage JSON-LD + 실제 FAQ 섹션 추가. 그룹/멤버 페이지에서 자주 묻는 질문 유형이 어느 정도 패턴화되어 있다. 데뷔일, 소속사, 멤버 구성 같은 것들. 이걸 8개 국어로 다 선언하고 FAQPage 스키마로 마크업했다. 구글 검색 결과에서 리치 스니펫이 뜨면 클릭률이 유의미하게 올라간다는 게 경험상 맞아서, 비용 대비 효과가 크다고 판단했다.

다국어 FAQ 작업 중에 주의해야 할 운영 원칙이 생겼다. 거짓0 가드라고 적어둔 건데, FAQ 답변을 동적으로 채울 때 숫자 0이나 빈 문자열이 falsy 처리돼서 "데이터 없음"으로 폴백되는 케이스가 있었다. "멤버 수가 0명"이라는 데이터가 실제로는 유효한 값인데 미기재로 처리될 수 있다는 뜻이다. 이 가드 조건을 CLAUDE.md에 명시해뒀다.

앨범 페이지 noindex 보류 결정도 같이 기록했다. 앨범 개별 페이지는 콘텐츠 밀도가 낮아서 색인 가치가 낮다는 판단도 있었는데, 일단 더 관찰하기로 했다. 이 판단 과정을 문서에 남겨두지 않으면 다음번에 다시 "왜 안 했지?"를 고민하게 되니까.

소셜 자동발행 봇과 SEO 리포트 수정

새벽 4시쯤부터는 완전히 다른 방향의 작업이 시작됐다. dex 계열 3개 사이트에서 새 콘텐츠가 발행될 때 Bluesky, Tumblr, Mastodon 세 플랫폼으로 자동 포스팅하는 봇이 필요했다. 소셜 트래픽 다각화 차원.

구조는 간단하게 잡았다:

컴포넌트 역할
social/content.py 발행 대상 콘텐츠 조회, 그룹명은 name_en 우선
social/clients/bluesky.py Bluesky AT Protocol 클라이언트
social/.env.example 3개 플랫폼 × 3개 사이트 자격증명 템플릿

여기서 kpop 사이트 처리 중에 작은 버그가 있었다. 그룹 이름을 한국어(name_ko)로 먼저 가져오다 보니 Bluesky 같은 영어권 플랫폼에서는 부적절했다. name_en을 우선하고 없을 때만 폴백하도록 수정했다.

SEO 리포트 쪽도 이 시간대에 손봤다. 일일 SEO 리포트에서 발행 수 집계가 두 가지 소스를 혼용하고 있었는데 - 실시간 크롤 결과와 cms_publish_daily DB 기록이 섞여 있었다 - 이 두 값이 가끔 달라지면서 리포트가 튀는 현상이 있었다. cms_publish_daily를 단일진실 소스로 통일하고 dex 카운팅 로직의 버그도 같이 잡았다. curiodot 사이트(구 psy 도메인)가 리포트에서 누락되어 있던 것도 이번에 추가했다. gamehotdeals, vtuberprofile 사이트의 라이브 dex 생존 체크 줄도 넣었다.

단위 표기도 자연스럽게 바꿨다. 기존에는 "딜 243" 이런 식으로 숫자만 뱉었는데 "딜 243개", "로스터 12명"처럼 읽기 편하게 바꾸는 게 맞다고 생각했다. 리포트를 매일 보는 사람이 한 명이라도 눈에 덜 피로해야 하니까.

배포 판정 함정과 ops 문서 정비

이날 작업에서 가장 뼈아팠던 부분은 kpopdex FAQ 배포 오판 사고였다. 로컬에서 eval 명령 원격실행 스크립트를 돌렸는데, 환경변수가 비어 있으니까 맥 로컬에서 실행되는 것처럼 성공 메시지가 나왔다. 에러가 없으니 "배포됐다"고 판정했는데, 실제로는 서버에 아무것도 안 올라간 상태였다. 이걸 나중에 라이브 렌더링 확인해서야 발견했다.

이 패턴을 hedvion-CLAUDE.md에 트랩(trap)으로 기록해뒀다. 요점은:

  • eval + 원격실행 스크립트는 변수 빈값일 때 로컬 실행으로 폴백하는 경우가 있다
  • 배포 성공 판정은 반드시 서버 git log + 라이브 렌더링으로만 해야 한다

idx_ping_smart 처리량 상향 기록도 남겼다. 검사 한도를 1800으로, 재핑 주기를 5일로 조정한 값을 문서에 박아둔 것. 시간이 지나면 왜 이 숫자로 설정했는지 까먹게 되니까 근거가 있을 때 바로 적어야 한다.

bots-infra.md에는 이날 만난 여러 함정들을 추가했다: kfoodlab NAS _lib 드리프트 문제, NAS git 소유권 이슈, dex 카운팅 혼선. 이런 것들은 한 번 겪고 나면 "아 맞다 이거 있었지" 하고 문서에서 찾게 되는 경우가 많은데, 문서에 없으면 두 번 삽질한다.

새벽 작업을 끝내고 보니 30개 가까운 커밋이 쌓였다. 9개 사이트를 건드린 작업치고는 생각보다 깔끔하게 롤아웃됐다. 다음에 비슷한 규모의 전사 SEO 작업이 생기면 - operator 수준이 아니라 더 큰 변경이라면 - 사이트별로 staged 롤아웃 플랜을 먼저 짜는 게 맞겠다 싶다. 오늘은 "9개 다 비슷하니까 순서대로 밀면 되겠지"가 통했지만, 다음번엔 아닐 수도 있으니까.


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

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

댓글 0

첫 댓글 달아줘.