여러분의 사이트맵 제대로 작동하는가?

이미지 태그 포함 XML 사이트맵 — 워드프레스 기본 사이트맵을 대체하는 이유

지금까지 색인 관리 그룹을 다뤘다. 이제 사이트맵 그룹으로 넘어간다.

첫 번째는 일반 XML 사이트맵이다. 워드프레스는 이미 자체 사이트맵(wp-sitemap.xml)을 기본으로 제공한다.

그런데 나는 SOHO SEO를 만들면서 이걸 꺼버리고 완전히 새로 만들었다. 이번 글에서는 왜 있는 걸 굳이 대체했는지, 그리고 그 과정에서 실제로 부딪힌 문제들을 얘기하려 한다.

코어 사이트맵이 부족한 지점

워드프레스 코어 사이트맵의 가장 큰 한계는 이미지 정보를 제대로 담지 않는다는 것이다.

코어 사이트맵은 URL과 마지막 수정일 정도만 나열한다. 나는 이게 디스커버 시대에는 부족하다고 본다. 지금까지 이 시리즈에서 계속 강조했듯, 디스커버는 이미지 중심의 채널이다.

구글이 이미지를 잘 크롤링하고 인식하게 만들려면, 사이트맵 단계에서부터 이미지 정보를 명시적으로 넘겨줘야 한다. 그래서 나는 <image:image> 태그를 포함한 사이트맵을 만들었다.

대표 이미지 URL을 각 글의 사이트맵 항목에 함께 넣어서, 구글봇이 한 번의 크롤링으로 텍스트와 이미지를 동시에 발견하게 만든다.

이건 지난 글들에서 다룬 크롤링 단계의 효율성 문제와 직결된다.

크롤 버짓은 무한하지 않다. 이미지를 별도로 발견시키기 위해 구글봇이 또 다른 경로를 거치게 만드는 것보다, 사이트맵 하나로 텍스트와 이미지 정보를 한꺼번에 전달하는 게 훨씬 효율적이다.

다른 플러그인과 자리싸움이 붙는다

사이트맵을 자체 제작하기로 한 순간부터 신경 써야 할 문제가 생긴다.

워드프레스 사이트에는 대체로 여러 플러그인이 동시에 설치돼 있다. 요스트, 랭크매스, All in One SEO, SEOPress 같은 플러그인들도 전부 자기만의 사이트�임을 만든다.

그리고 대부분 같은 주소, /sitemap_index.xml에 자기 사이트맵을 등록하려 한다. 이러면 자리 다툼이 벌어진다.

나는 이 문제를 실제로 겪었다. 새 글이 사이트맵에 반영이 안 된다는 문의가 있었는데, 원인을 파고들어보니 SOHO SEO의 사이트맵이 갱신 안 된 게 아니라, 예전에 지웠다고 생각했던 다른 플러그인의 사이트맵이 대신 응답하고 있었던 거였다.

그래서 지금은 요스트, 랭크매스, AIOSEO, SEOPress가 공식적으로 제공하는 “내 사이트맵 끄기” 필터를 전부 명시적으로 호출한다.

해당 플러그인이 설치돼 있지 않으면 이 필터들은 그냥 아무 효과 없이 무시된다. 이 사이트에서는 SOHO SEO의 사이트맵만 유일하게 응답하도록 보장하는 것이다.

robots.txt의 Sitemap 줄도 직접 챙긴다

robots.txt에는 관례적으로 “Sitemap: [주소]”라는 줄을 넣어서 검색엔진에게 사이트맵 위치를 알려준다.

그런데 나는 이 줄이 우연에 의존하고 있는 사이트를 여러 번 봤다. 예전에 설치했던 요스트가 남겨둔 줄이 그대로 남아있는 식이다.

그래서 나는 이 줄을 다른 플러그인의 존재 여부와 무관하게 SOHO SEO가 직접 보장하도록 만들었다.

이미 같은 줄이 있으면 중복 추가하지 않고, 없으면 새로 넣는다.

뒷슬래시 하나 때문에 사이트맵이 HTML을 반환한 사고

이 기능을 만들면서 겪은 가장 골치 아팠던 버그 하나를 얘기하고 싶다.

실제 운영 사이트에서 /sitemap_index.xml, /sitemap-posts.xml 같은 주소로 요청이 들어오면, 워드프레스의 캐노니컬 리다이렉트 기능이 이 요청에 뒷슬래시를 붙여 301 리다이렉트를 걸어버리는 현상이 있었다.

정상적으로는 요청 경로에 점(.)이 있으면 파일로 인식해서 이 리다이렉트를 건너뛰어야 하는데, 실제 서버에서는 그렇게 동작하지 않았다.

그 결과 뒷슬래시가 붙은 주소로 리다이렉트됐고, 이 뒷슬래시 버전은 사이트맵 플러그인의 rewrite 규칙과 매칭되지 않아 워드프레스가 그냥 홈페이지로 처리해버렸다.

결과가 뭐였냐면, 사이트맵 주소로 접속했는데 응답은 200, Content-Type은 text/html, 실제 내용은 블로그 홈페이지였다.

사이트맵이 사이트맵이 아니라 홈페이지 HTML을 반환하는 상황이었다.

구글 서치 콘솔은 이걸 “Sitemap이 HTML입니다”라는 오류로 잡아냈다. 나는 이 원인을 추적하면서, 정확한 원인이 호스팅사의 리다이렉트 규칙 때문인지 코어 동작 변화 때문인지 완전히 특정하지는 못했다.

하지만 원인을 완벽히 특정할 수 없다고 해서 손 놓고 있을 수는 없었다. 그래서 SOHO SEO의 사이트맵 관련 쿼리 변수가 이미 인식된 요청에 대해서는, 애초에 캐노니컬 리다이렉트 자체를 못 하도록 명시적으로 막아버렸다.

원인이 무엇이든 재발은 막는 방식이다. 나는 이런 종류의 버그가 SEO 플러그인 개발에서 가장 무섭다고 생각한다.

코드 자체는 틀린 게 없는데, 워드프레스 코어의 다른 동작과 맞물려서 예상 못 한 결과가 나오는 경우다.

캐시가 새 글을 숨기는 문제

또 하나 신경 쓴 지점은 캐싱과의 충돌이다.

사이트맵은 매 요청마다 새로 계산돼서 나가지만, 그 앞단에 LiteSpeed Cache 같은 서버·CDN 캐시가 이미 예전 시점에 만들어진 사본을 저장해두고 있으면, no-cache 헤더를 새로 붙여봐야 그 다음 요청부터만 효과가 있다.

이미 저장된 옛 사본을 지워주지는 못한다. 실제로 다른 SEO 플러그인을 쓰다가 SOHO SEO로 옮겨온 사이트에서, 옛 사이트맵 사본이 새 글이 여러 건 쌓이도록 계속 그대로 나온 사례가 있었다.

그래서 글이 발행되거나 수정되거나 삭제될 때마다, 그 즉시 사이트맵 관련 주소들의 캐시를 능동적으로 비우게 만들었다.

어떤 캐싱 플러그인이 설치돼 있는지 미리 알 수 없으니, LiteSpeed Cache, W3 Total Cache, WP Rocket이 제공하는 URL 단위 캐시 삭제 훅을 전부 최선을 다해 호출한다. 해당 플러그인이 없으면 그냥 아무 동작도 하지 않는 안전한 방식이다.

사이트 전체 캐시를 통째로 비우는 것보다 훨씬 가볍게 목적을 달성할 수 있다. 그리고 이것도 완벽하지 않다는 걸 실서버에서 다시 확인했다.

URL 단위 삭제가 캐싱 플러그인이 그 URL을 정확히 같은 문자열로 저장해뒀을 때만 지워지는데, 정규화 차이나 CDN 계층 때문에 실제로는 안 지워지는 경우가 있었다. 이 문제는 지금도 내가 계속 관찰하고 있는 지점이다.

켜고 끄는 걸 사용자에게 맡긴 이유

이 모든 기능에도 불구하고, 나는 사이트맵 온오프 스위치를 설정 화면에 남겨뒀다.

꺼두면 SOHO SEO는 사이트맵과 관련해 정말 아무것도 건드리지 않는다. 워드프레스 코어 사이트맵도, 다른 SEO 플러그인의 사이트맵도 막지 않고 그대로 둔다.

이건 랭크매스나 요스트와 동일한 동작 방식이다. 이유는 명확하다. 이미 다른 사이트맵 솔루션에 만족하고 있는 사용자에게 억지로 SOHO SEO의 사이트맵을 강제할 이유가 없다.

이렇게 만든 이유는 코드 구조상의 이유인데, 이 생성자 자체가 이미 워드프레스의 init 훅 실행 도중에 호출되기 때문에, 여기서 다시 rewrite rule 등록을 뒤로 미루면 그 요청에서는 아예 실행되지 않는 문제가 생긴다.

그래서 설정과 무관하게 규칙은 항상 동기적으로 즉시 등록하고, 실제 응답 여부만 설정으로 갈린다.

SOHO SEO 사이트맵 프로세스 결론

이미지 태그를 포함한 사이트맵은 겉보기엔 단순한 기능이지만, 실제로 안정적으로 작동시키려면 다른 플러그인과의 자리싸움, 워드프레스 코어의 예상 못 한 리다이렉트 동작, 여러 캐싱 솔루션과의 충돌까지 전부 신경 써야 했다.

나는 이 과정에서 배운 게 있다. 기능 하나를 “만드는 것”과 “실제 운영 환경에서 안정적으로 동작하게 만드는 것” 사이에는 큰 차이가 있다는 것이다. 후자를 위해 쏟은 시간이 실제로는 전자보다 훨씬 길었다.

필자는 이미 유명 플러그인을 사용하다가 사이트맵이 에러가 나는 상황을 겪었다. 그 플러그인이 무조건 문제였다고 보긴 어렵지만 내가 플러그인을 만들면서 시스템의 안전성을 확보하는 것이 쉽지는 않지만 그렇다고 어려운 것도 아니란걸 알았다.

경험을 통해 자신에게 가장 좋은 시스템을 선택하는 것도 지혜라고 볼 수 있다.

다음 글에서는 구글 뉴스 사이트맵으로 넘어간다. 48시간이라는 엄격한 규칙이 왜 존재하고, 이게 디스커버의 최신성 신호로 어떻게 이어지는지 다룰 것이다.