React Router v7에서 세션 간 언어 선택을 기억하는 방법

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

문제

사용자가 명시적으로 언어를 선택할 때, 그 선택은 사용자의 선호도를 반영하며 자동 감지를 무시해야 합니다. 지속성이 없으면 이 선택은 브라우저가 닫히거나 세션이 종료될 때 사라집니다. 다음 방문 시, 애플리케이션은 새로 시작하여 사용자가 다시 언어를 선택하도록 강요합니다. 이러한 반복은 애플리케이션이 사용자의 선호도를 존중하지 않는다는 신호를 보내며, 마찰을 일으키고 신뢰를 감소시킵니다.

해결책

사용자가 언어를 선택할 때 쿠키와 같은 지속적인 위치에 해당 선택을 저장합니다. 이후 방문 시, 브라우저 헤더나 다른 감지 방법으로 돌아가기 전에 이 저장된 선호도를 확인합니다. 유효한 저장된 언어가 발견되면 사용자를 자동으로 해당 언어의 경로로 리디렉션합니다. 이렇게 하면 사용자의 명시적 선택이 우선시되고 세션 간에 지속됩니다.

단계

1. 언어 선호도를 저장하기 위한 쿠키 생성

긴 만료 시간을 가진 사용자의 선택된 언어를 보관할 쿠키를 정의합니다.

import { createCookie } from "react-router";

export const languagePreference = createCookie("language-preference", {
  maxAge: 31536000,
  httpOnly: false,
  secure: process.env.NODE_ENV === "production",
  sameSite: "lax",
});

이 쿠키는 1년 동안 지속되며 선호도를 읽기 위해 클라이언트 측 코드에서 접근할 수 있습니다.

2. 언어 선택을 저장하는 액션 추가

언어 선택 폼 제출을 처리하고 선택을 쿠키에 저장하는 액션을 생성합니다.

import { redirect } from "react-router";
import type { Route } from "./+types/root";
import { languagePreference } from "./cookies";

export async function action({ request }: Route.ActionArgs) {
  const formData = await request.formData();
  const selectedLanguage = formData.get("language");

  if (typeof selectedLanguage === "string") {
    return redirect(`/${selectedLanguage}`, {
      headers: {
        "Set-Cookie": await languagePreference.serialize(selectedLanguage),
      },
    });
  }

  return redirect("/");
}

사용자가 언어 선택을 제출하면, 이 액션은 그것을 쿠키에 저장하고 사용자를 적절한 언어 경로로 리디렉션합니다.

3. 언어 선택 컴포넌트 만들기

사용자가 선호하는 언어를 선택할 수 있는 폼 컴포넌트를 구축합니다.

import { Form } from "react-router";

export function LanguageSelector({
  currentLanguage,
}: {
  currentLanguage: string;
}) {
  return (
    <Form method="post">
      <select
        name="language"
        defaultValue={currentLanguage}
        onChange={(e) => e.currentTarget.form?.requestSubmit()}
      >
        <option value="en">English</option>
        <option value="es">Español</option>
        <option value="fr">Français</option>
        <option value="de">Deutsch</option>
      </select>
    </Form>
  );
}

이 컴포넌트는 사용자가 선택을 변경하면 자동으로 제출되어 환경설정을 저장하는 액션을 트리거합니다.

4. 루트 로더에서 저장된 환경설정 확인하기

루트 라우트 로더에 저장된 언어 환경설정을 확인하고 그에 따라 리다이렉트하는 로직을 추가합니다.

import { redirect } from "react-router";
import type { Route } from "./+types/root";
import { languagePreference } from "./cookies";

export async function loader({ request }: Route.LoaderArgs) {
  const url = new URL(request.url);
  const cookieHeader = request.headers.get("Cookie");
  const storedLanguage = await languagePreference.parse(cookieHeader);

  if (url.pathname === "/" && storedLanguage) {
    return redirect(`/${storedLanguage}`);
  }

  return null;
}

사용자가 루트 경로를 방문하면 이 로더는 저장된 언어 환경설정을 확인하고 선택한 언어 라우트로 리다이렉트합니다(존재하는 경우).

5. 저장된 언어를 지원되는 로케일과 비교하여 검증하기

리다이렉션에 사용하기 전에 저장된 환경설정이 유효한지 확인합니다.

import { redirect } from "react-router";
import type { Route } from "./+types/root";
import { languagePreference } from "./cookies";

const SUPPORTED_LANGUAGES = ["en", "es", "fr", "de"];

export async function loader({ request }: Route.LoaderArgs) {
  const url = new URL(request.url);
  const cookieHeader = request.headers.get("Cookie");
  const storedLanguage = await languagePreference.parse(cookieHeader);

  if (
    url.pathname === "/" &&
    storedLanguage &&
    SUPPORTED_LANGUAGES.includes(storedLanguage)
  ) {
    return redirect(`/${storedLanguage}`);
  }

  return null;
}

이 검증은 쿠키 값이 변조되었거나 환경설정이 저장된 이후 지원되는 언어가 변경된 경우 잘못되거나 지원되지 않는 언어 라우트로 리다이렉트되는 것을 방지합니다.