Next.js(Pages Router) v16에서 대체 언어 버전을 연결하는 방법

검색 엔진을 위한 언어 대체 버전 연결

문제

웹사이트가 여러 언어로 동일한 콘텐츠를 제공할 때, 검색 엔진은 각 버전에 대해 별도의 URL을 접하지만 그들 간의 관계를 이해하지 못합니다. 프랑스어 사용자가 검색할 때 프랑스어 버전이 존재함에도 불구하고 영어 버전이 더 높은 순위로 표시될 수 있습니다. 마찬가지로, 영어 페이지와 프랑스어 번역본이 조율된 대체 버전이 아닌 경쟁 중복 콘텐츠로 취급될 수 있습니다. 이러한 언어 버전을 연결하는 명시적인 신호가 없으면, 검색 엔진은 사용자의 언어 선호도에 따라 가장 적절한 버전을 자신 있게 제공할 수 없으며, 이는 분산된 순위 권한과 열악한 사용자 경험으로 이어집니다.

해결 방법

각 페이지의 head 섹션에 hreflang 속성을 가진 link 요소를 추가하여 페이지 자체를 포함한 모든 언어 버전을 나열합니다. 각 페이지 변형은 사용 가능한 모든 언어 버전을 참조하는 동일한 링크 세트를 포함해야 합니다. 이러한 양방향 연결은 검색 엔진이 페이지를 중복이 아닌 번역본으로 인식하도록 보장하여, 브라우저 선호도와 검색 컨텍스트를 기반으로 사용자에게 올바른 언어 버전을 제공할 수 있게 합니다.

단계

1. 대체 언어 링크를 생성하는 컴포넌트 생성

next/head에서 Head 컴포넌트를 가져와 페이지 메타데이터를 수정합니다. useRouter 훅을 통해 로케일 정보에 액세스하여 사용 가능한 모든 언어 버전에 대한 링크를 구축합니다.

import Head from "next/head";
import { useRouter } from "next/router";

interface AlternateLinksProps {
  path?: string;
}

export default function AlternateLinks({ path }: AlternateLinksProps) {
  const router = useRouter();
  const { locales, locale: currentLocale, asPath } = router;
  const canonicalPath = path || asPath;

  if (!locales) {
    return null;
  }

  return (
    <Head>
      {locales.map((locale) => (
        <link
          key={locale}
          rel="alternate"
          hrefLang={locale}
          href={`${process.env.NEXT_PUBLIC_SITE_URL}/${locale}${canonicalPath}`}
        />
      ))}
    </Head>
  );
}

컴포넌트는 라우터에서 사용 가능한 로케일을 매핑하고 모든 페이지에 대해 동적으로 link 요소를 생성합니다.

2. 번역본이 있는 페이지에 컴포넌트 추가

여러 언어로 존재하는 각 페이지 컴포넌트에 AlternateLinks 컴포넌트를 포함합니다.

import AlternateLinks from "@/components/AlternateLinks";

export default function AboutPage() {
  return (
    <>
      <AlternateLinks />
      <main>
        <h1>About Us</h1>
      </main>
    </>
  );
}

이 컴포넌트는 각 페이지에 완전한 대체 링크 세트가 포함되도록 보장하여 모든 변형이 서로를 참조해야 한다는 요구사항을 충족합니다.

3. 현재 로케일에 대한 자체 참조 링크 포함

모든 페이지는 자체 언어를 나타내는 자체 참조 hreflang 태그를 포함해야 합니다. 이 컴포넌트는 현재 로케일을 포함한 모든 로케일을 반복하여 이를 처리합니다.

export default function AlternateLinks({ path }: AlternateLinksProps) {
  const router = useRouter();
  const { locales, asPath } = router;
  const canonicalPath = path || asPath;

  if (!locales) {
    return null;
  }

  return (
    <Head>
      {locales.map((locale) => (
        <link
          key={locale}
          rel="alternate"
          hrefLang={locale}
          href={`${process.env.NEXT_PUBLIC_SITE_URL}/${locale}${canonicalPath}`}
        />
      ))}
    </Head>
  );
}

이제 각 페이지에는 다른 버전에 대한 태그와 함께 자신을 가리키는 hreflang 태그가 포함됩니다.

4. x-default 폴백 링크 추가

사용자의 언어를 사용할 수 없을 때 표시할 버전을 지정하기 위해 x-default 링크를 추가합니다.

export default function AlternateLinks({ path }: AlternateLinksProps) {
  const router = useRouter();
  const { locales, defaultLocale, asPath } = router;
  const canonicalPath = path || asPath;

  if (!locales || !defaultLocale) {
    return null;
  }

  return (
    <Head>
      {locales.map((locale) => (
        <link
          key={locale}
          rel="alternate"
          hrefLang={locale}
          href={`${process.env.NEXT_PUBLIC_SITE_URL}/${locale}${canonicalPath}`}
        />
      ))}
      <link
        rel="alternate"
        hrefLang="x-default"
        href={`${process.env.NEXT_PUBLIC_SITE_URL}/${defaultLocale}${canonicalPath}`}
      />
    </Head>
  );
}

x-default 링크는 언어 기본 설정이 일치하지 않는 사용자를 기본 로케일로 안내합니다.