React Router v7에서 언어 전환 컴포넌트를 구축하는 방법

동일한 페이지에 머물면서 언어 전환하기

문제

사용자는 언어 전환기가 현재 컨텍스트를 유지하기를 기대합니다. 제품 페이지, 도움말 문서 또는 계정 설정을 탐색할 때 영어에서 스페인어로 전환하면 해당 페이지가 스페인어로 표시되어야 합니다. 그러나 많은 구현에서는 언어 선택을 탐색 이벤트로 처리하여 사용자를 새 언어의 홈페이지로 리디렉션하므로 사용자가 이전 위치로 다시 이동해야 합니다. 이는 사용자의 흐름을 방해하고 특히 사용자가 탐색 계층 구조 깊숙이 있을 수 있는 콘텐츠 중심 애플리케이션에서 불편함을 초래합니다.

근본 원인은 언어 전환기가 현재 페이지를 기반으로 URL을 동적으로 구성하는 대신 하드코딩된 대상 URL을 사용하는 경우가 많기 때문입니다. 현재 URL 구조를 읽고 변환하지 않으면 전환기는 언어 변경 시 애플리케이션에서 사용자의 위치를 유지할 수 없습니다.

솔루션

현재 URL을 읽고 활성 로케일 매개변수와 나머지 경로 세그먼트를 모두 추출하는 언어 전환 컴포넌트를 구축합니다. 지원되는 각 언어에 대해 다른 모든 경로 세그먼트와 쿼리 매개변수를 그대로 유지하면서 로케일 세그먼트만 교체하여 새 URL을 생성합니다. 이러한 URL을 링크로 렌더링하여 사용자가 애플리케이션에서 자신의 위치를 잃지 않고 언어를 전환할 수 있도록 합니다.

이 접근 방식은 로케일을 탐색 대상이 아닌 URL 구조의 교체 가능한 매개변수로 취급하여 /en/products/shoes에서 /es/products/shoes로 전환할 때 사용자의 컨텍스트를 유지합니다.

단계

1. 로케일 인식 URL을 구축하는 헬퍼 함수 생성

현재 경로명과 대상 로케일을 받아 로케일 세그먼트를 교체하여 새 경로를 구성하는 함수를 정의합니다.

export function buildLocalePath(
  currentPath: string,
  newLocale: string,
): string {
  const segments = currentPath.split("/").filter(Boolean);

  if (segments.length === 0) {
    return `/${newLocale}`;
  }

  segments[0] = newLocale;
  return `/${segments.join("/")}`;
}

이 함수는 경로명을 세그먼트로 분할하고, 첫 번째 세그먼트를 대상 로케일로 교체한 후 경로를 재구성합니다. 루트 경로와 같은 엣지 케이스를 처리하며 로케일이 항상 첫 번째 세그먼트가 되도록 보장합니다.

2. 지원되는 로케일 정의

애플리케이션이 지원하는 모든 언어를 나열하는 구성 객체를 생성합니다.

export const locales = [
  { code: "en", label: "English" },
  { code: "es", label: "Español" },
  { code: "fr", label: "Français" },
  { code: "de", label: "Deutsch" },
];

이 구성은 스위처에 표시할 언어에 대한 단일 정보 소스 역할을 하며 각 로케일에 대해 사용자 친화적인 레이블을 제공합니다.

3. 언어 스위처 컴포넌트 구축

현재 위치를 읽고, 활성 로케일을 결정하며, 지원되는 다른 모든 언어에 대한 링크를 렌더링하는 컴포넌트를 생성합니다.

import { Link, useLocation, useParams } from "react-router";
import { locales, buildLocalePath } from "./i18n-config";

export function LanguageSwitcher() {
  const location = useLocation();
  const params = useParams();
  const currentLocale = params.locale || "en";

  return (
    <nav aria-label="Language switcher">
      <ul>
        {locales.map((locale) => {
          const isActive = locale.code === currentLocale;
          const newPath = buildLocalePath(location.pathname, locale.code);

          return (
            <li key={locale.code}>
              {isActive ? (
                <span aria-current="true">{locale.label}</span>
              ) : (
                <Link to={newPath}>{locale.label}</Link>
              )}
            </li>
          );
        })}
      </ul>
    </nav>
  );
}

컴포넌트는 useLocation를 사용하여 현재 경로명에 액세스하고 useParams를 사용하여 URL에서 활성 로케일을 추출합니다. 지원되는 각 로케일에 대해 헬퍼 함수를 사용하여 새 경로를 생성하고 현재 언어에 대해서는 링크 또는 비대화형 요소를 렌더링합니다.

4. 쿼리 파라미터 및 해시 프래그먼트 보존

언어를 전환할 때 쿼리 문자열과 URL 프래그먼트를 유지하도록 헬퍼 함수를 확장합니다.

export function buildLocalePath(
  currentPath: string,
  search: string,
  hash: string,
  newLocale: string,
): string {
  const segments = currentPath.split("/").filter(Boolean);

  if (segments.length === 0) {
    return `/${newLocale}${search}${hash}`;
  }

  segments[0] = newLocale;
  return `/${segments.join("/")}${search}${hash}`;
}

이 업데이트된 버전은 location 객체의 search 및 hash 속성을 받아 생성된 경로에 추가하여 필터, 정렬 파라미터 및 앵커 링크가 언어 전환 후에도 유지되도록 보장합니다.

5. 향상된 헬퍼를 사용하도록 컴포넌트 업데이트

전체 위치 정보를 헬퍼 함수에 전달하도록 스위처를 수정합니다.

import { Link, useLocation, useParams } from "react-router";
import { locales, buildLocalePath } from "./i18n-config";

export function LanguageSwitcher() {
  const location = useLocation();
  const params = useParams();
  const currentLocale = params.locale || "en";

  return (
    <nav aria-label="Language switcher">
      <ul>
        {locales.map((locale) => {
          const isActive = locale.code === currentLocale;
          const newPath = buildLocalePath(
            location.pathname,
            location.search,
            location.hash,
            locale.code,
          );

          return (
            <li key={locale.code}>
              {isActive ? (
                <span aria-current="true">{locale.label}</span>
              ) : (
                <Link to={newPath}>{locale.label}</Link>
              )}
            </li>
          );
        })}
      </ul>
    </nav>
  );
}

이제 컴포넌트는 location.searchlocation.hash를 헬퍼에 전달하여 스페인어로 전환할 때 /en/products?category=shoes#reviews와 같은 URL이 /es/products?category=shoes#reviews가 되도록 합니다.