Next.js(Pages Router) v16에서 세션 간 언어 선택을 기억하는 방법

사용자의 명시적 언어 선택 저장

문제

사용자가 명시적으로 언어를 선택하면 해당 선택은 사용자의 선호도를 반영하며 브라우저 헤더나 지리적 위치에서 자동 감지되는 것보다 우선해야 합니다. 지속성이 없으면 브라우저가 닫히거나 세션이 종료될 때 이 선호도가 손실됩니다. 다음 방문 시 애플리케이션이 새로 시작되어 사용자가 언어를 다시 선택해야 합니다. 이러한 반복은 애플리케이션이 사용자 선호도를 존중하지 않는다는 신호를 보내며 마찰을 일으키고 신뢰를 떨어뜨립니다.

문제는 두 가지입니다. 선택 시점에 사용자의 명시적 선택을 캡처하고 자동 감지 로직이 실행되기 전에 후속 방문 시 해당 선택을 검색하는 것입니다. 저장된 선호도가 요청 수명 주기 초기에 확인되지 않으면 사용자가 명시적 선택 대신 브라우저 설정에 따라 리디렉션될 수 있으며, 이는 애초에 선택한 가치를 훼손합니다.

솔루션

사용자가 명시적으로 언어를 선택할 때 지속적인 쿠키에 사용자의 언어 선택을 저장합니다. 애플리케이션 루트에 대한 향후 방문 시 브라우저 기반 로케일 감지로 폴백하기 전에 이 쿠키를 확인합니다. 유효한 저장된 로케일이 발견되면 사용자를 해당 로케일의 루트 경로로 리디렉션하여 선호도가 즉시 존중되도록 합니다.

이 접근 방식은 명시적 사용자 선택을 자동 감지와 분리합니다. 쿠키는 브라우저 재시작 후에도 유지되는 의도의 지속적인 신호 역할을 하며 Accept-Language 헤더와 같은 일시적 신호보다 우선합니다. 초기 요청 중에 서버 측에서 쿠키를 확인함으로써 페이지가 렌더링되기 전에 리디렉션이 발생하여 원활한 경험을 제공합니다.

단계

1. 클라이언트 측에서 로케일 기본 설정 쿠키를 설정하는 헬퍼 생성

사용자가 언어를 선택하면 세션 간에 유지되는 쿠키에 선택 사항을 저장합니다.

export function setLocalePreference(locale: string) {
  const maxAge = 60 * 60 * 24 * 365;
  document.cookie = `NEXT_LOCALE=${locale}; path=/; max-age=${maxAge}; SameSite=Lax`;
}

이 함수는 선택한 로케일을 NEXT_LOCALE라는 이름의 쿠키에 작성하며, 유효 기간은 1년입니다. path=/는 애플리케이션 전체에서 사용할 수 있도록 보장하며, SameSite=Lax는 최상위 탐색에서 쿠키를 전송할 수 있도록 허용하면서 합리적인 CSRF 보호를 제공합니다.

2. 사용자가 언어를 선택할 때 헬퍼 호출

언어 전환기 컴포넌트에 헬퍼를 통합하여 선택 즉시 기본 설정이 저장되도록 합니다.

import { useRouter } from "next/router";
import { setLocalePreference } from "@/lib/locale";

export default function LanguageSwitcher() {
  const router = useRouter();
  const { locales, locale: currentLocale } = router;

  const handleLocaleChange = (newLocale: string) => {
    setLocalePreference(newLocale);
    router.push(router.pathname, router.asPath, { locale: newLocale });
  };

  return (
    <select
      value={currentLocale}
      onChange={(e) => handleLocaleChange(e.target.value)}
    >
      {locales?.map((loc) => (
        <option key={loc} value={loc}>
          {loc.toUpperCase()}
        </option>
      ))}
    </select>
  );
}

사용자가 선택을 변경하면 쿠키가 설정되고 라우터가 새 로케일의 동일한 페이지로 이동합니다. 쿠키는 이후의 모든 요청에서 사용할 수 있습니다.

3. 루트 페이지에서 저장된 기본 설정 확인

루트 페이지의 getServerSideProps에서 쿠키를 읽고, 쿠키가 존재하고 유효한 경우 저장된 로케일로 리디렉션합니다.

import { GetServerSideProps } from "next";

export const getServerSideProps: GetServerSideProps = async (context) => {
  const storedLocale = context.req.cookies.NEXT_LOCALE;
  const { locales, defaultLocale } = context;

  if (
    storedLocale &&
    locales?.includes(storedLocale) &&
    storedLocale !== defaultLocale
  ) {
    return {
      redirect: {
        destination: `/${storedLocale}`,
        permanent: false,
      },
    };
  }

  return {
    redirect: {
      destination: `/${defaultLocale}`,
      permanent: false,
    },
  };
};

export default function RootPage() {
  return null;
}

이 코드는 NEXT_LOCALE 쿠키가 존재하고 애플리케이션의 구성된 목록에서 유효한 로케일을 포함하는지 확인합니다. 저장된 로케일이 기본값이 아닌 경우, 사용자는 해당 로케일의 루트로 리디렉션됩니다. 그렇지 않으면 기본 로케일로 리디렉션됩니다. 리디렉션은 렌더링 전에 서버 측에서 발생하므로 사용자가 즉시 올바른 로케일에 도달하게 됩니다.

4. Next.js에서 로케일 라우팅 구성

next.config.js에서 지원되는 로케일을 정의하여 리디렉션 로직이 저장된 기본 설정을 검증할 수 있도록 해야 합니다.

module.exports = {
  i18n: {
    locales: ["en", "fr", "de", "es"],
    defaultLocale: "en",
  },
};

이 구성은 Next.js의 내장 i18n 라우팅을 활성화하고 getServerSideProps에서 localesdefaultLocale를 사용할 수 있도록 합니다. 루트 페이지 로직은 이러한 값을 사용하여 저장된 쿠키를 검증하고 올바른 리디렉션 대상을 구성합니다.