개발 slecs

Thymeleaf SEO 메타 태그를 fragment로 통합해 일관성 확보

목차

Thymeleaf SEO fragment를 새로 만들었다. 사내 서비스의 HTML <head> 메타 정보가 여기저기 흩어져 있어서 한 번에 정리가 필요했던 작업.


왜 SEO fragment를 따로 뺐나

서버사이드 렌더링(SSR) 기반 프로젝트에서 Thymeleaf를 쓰다 보면, 초반엔 각 페이지 템플릿에 <meta> 태그를 그냥 직접 박아버리는 경우가 많다. 페이지 수가 적을 때는 별 문제가 없지만, 페이지가 늘어나기 시작하면 상황이 달라진다.

  • og:title, og:description, og:image 같은 Open Graph 태그가 페이지마다 조금씩 다른 형태로 박혀 있음
  • canonical URL이 있는 페이지, 없는 페이지가 뒤섞임
  • <title> 태그 포맷이 페이지마다 제각각 (서비스명 | 페이지명 vs 페이지명 - 서비스명)
  • robots 메타 태그가 필요한 페이지에만 들어가 있는 게 아니라 아예 빠진 페이지도 존재

팀 입장에서 이건 단순한 코드 스타일 문제가 아니라, 실제로 검색 노출에 직결되는 사안이다. SEO 감사(audit)를 한 번 돌려보면 일관성 없는 메타 정보가 얼마나 많은 경고를 뱉어내는지 바로 확인된다.


fragment 구조와 선택의 이유

backend/src/main/resources/templates/fragments/seo.html — 경로를 보면 기존에 이미 fragments/ 디렉터리가 있었을 가능성이 높다. Thymeleaf의 fragment 방식은 공통 레이아웃 관리에서 표준처럼 쓰이는 패턴이다.

<!-- fragments/seo.html -->
<th:block th:fragment="seo(title, description, canonical, ogImage)">
  <title th:text="${title} + ' | 서비스명'">서비스명</title>

  <meta name="description" th:content="${description}">
  <meta name="robots" content="index, follow">

  <link rel="canonical" th:href="${canonical}">

  <!-- Open Graph -->
  <meta property="og:title" th:content="${title}">
  <meta property="og:description" th:content="${description}">
  <meta property="og:url" th:content="${canonical}">
  <meta property="og:image" th:content="${ogImage}">
  <meta property="og:type" content="website">

  <!-- Twitter Card -->
  <meta name="twitter:card" content="summary_large_image">
  <meta name="twitter:title" th:content="${title}">
  <meta name="twitter:description" th:content="${description}">
</th:block>

위처럼 파라미터를 받는 fragment로 설계하면, 호출하는 쪽에서 페이지별 값만 넘겨주면 된다.

<!-- 각 페이지 템플릿에서 -->
<th:block th:replace="~{fragments/seo :: seo(
    ${pageTitle},
    ${pageDescription},
    ${canonicalUrl},
    ${ogImageUrl}
)}"/>
방식 장점 단점
각 페이지에 직접 작성 페이지별 커스터마이징 자유도 높음 일관성 유지 어려움, 중복 증가
공통 layout 파일에 고정 관리 포인트 단일화 페이지별 동적 값 처리 번거로움
파라미터 fragment 일관성 + 동적 값 모두 처리 가능 초기 설계 필요

결국 파라미터 fragment 방식이 SSR 프로젝트에서 SEO를 관리하는 가장 깔끔한 선택이라고 본다.


팀에 주는 영향과 코드리뷰 포인트

이런 fragment가 생기면 팀원들에게 "이제 새 페이지 만들 때 SEO fragment는 반드시 포함" 이라는 가이드를 함께 전달해야 한다. 코드리뷰에서 내가 주로 확인하는 항목은 이렇다.

  • canonical 값이 하드코딩이 아니라 컨트롤러에서 모델에 담겨 내려오는지
  • og:image fallback이 있는지 (값이 null일 때 기본 이미지로 떨어지는지)
  • <title> 길이가 60자를 넘지 않는 구조인지 (검색엔진 권장 범위)
  • description이 160자 이내로 제한되어 내려오는지

특히 canonical URL은 방심하기 쉬운 지점인데, 쿼리 파라미터가 붙은 URL이 그대로 canonical로 들어가면 중복 페이지 문제가 생긴다. 컨트롤러 레이어에서 정제된 값만 모델에 담도록 팀 내 규칙을 잡는 게 맞다.

fragment 하나 추가로 끝나는 작업처럼 보이지만, 이게 있고 없고의 차이는 나중에 SEO 개선 작업을 할 때 체감된다. 기준점이 없으면 개선 자체를 시작하기가 어렵다.

끝.


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

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

댓글 0

첫 댓글 달아줘.