React Router v7에서 내비게이션 링크의 로케일 유지하는 방법

내부 내비게이션에서 로케일 유지하기

문제

URL 경로에 로케일 정보가 인코딩되어 있을 때, 모든 내비게이션 링크는 일관된 사용자 경험을 유지하기 위해 해당 로케일을 보존해야 합니다. 사용자가 사이트의 프랑스어 버전을 탐색하다가 /about 링크를 클릭하면, 프랑스어 상태를 유지하며 /fr/about으로 이동하기를 기대합니다. 로케일을 인식하는 링크가 없으면 사용자는 세션 중간에 기본 언어로 전환되어 탐색 컨텍스트가 깨지고 수동으로 언어를 다시 전환해야 합니다. 이는 마찰을 일으키고 현지화된 경험을 저해합니다.

모든 링크에 로케일 접두사를 하드코딩하는 것은 오류가 발생하기 쉽고 코드베이스를 취약하게 만듭니다. 사용자가 애플리케이션을 탐색할 때 활성 로케일이 변경될 수 있으며, 수백 개의 링크를 수동으로 업데이트하는 것은 지속 불가능합니다.

해결책

URL에서 현재 로케일을 자동으로 읽고 모든 내부 내비게이션 경로에 접두사로 추가하는 사용자 정의 Link 컴포넌트를 만듭니다. React Router의 Link 컴포넌트를 래핑함으로써 로케일 처리를 한 곳에 중앙화합니다. 래퍼는 현재 경로에서 로케일 매개변수를 추출하고 모든 대상 경로에 이를 포함시켜 수동 개입 없이 사용자의 언어 선택을 보존합니다.

이 접근 방식은 애플리케이션 전체에서 링크 정의를 깔끔하고 로케일에 구애받지 않게 유지하면서 로케일 컨텍스트가 모든 클릭과 함께 이동하도록 보장합니다.

단계

URL에서 현재 로케일을 추출하기 위해 useParams를 사용하고 대상 경로에 로케일을 접두사로 추가하기 위해 React Router의 Link 컴포넌트를 래핑하는 사용자 정의 컴포넌트를 구축합니다.

import { Link, useParams } from "react-router";
import type { LinkProps } from "react-router";

export function LocaleLink({ to, ...props }: LinkProps) {
  const { locale } = useParams<{ locale: string }>();

  const localizedTo =
    typeof to === "string"
      ? `/${locale}${to.startsWith("/") ? to : `/${to}`}`
      : {
          ...to,
          pathname: `/${locale}${to.pathname?.startsWith("/") ? to.pathname : `/${to.pathname}`}`,
        };

  return <Link to={localizedTo} {...props} />;
}

이 컴포넌트는 현재 경로에서 로케일 매개변수를 읽고 to 속성에 전달하는 모든 경로에 자동으로 접두사를 추가하며, 문자열 형식과 객체 형식 모두 처리합니다.

로케일을 유지하는 내비게이션이 필요한 모든 곳에서 표준 Link 컴포넌트를 LocaleLink로 대체하세요.

import { LocaleLink } from "./LocaleLink";

export function Navigation() {
  return (
    <nav>
      <LocaleLink to="/">Home</LocaleLink>
      <LocaleLink to="/about">About</LocaleLink>
      <LocaleLink to="/products">Products</LocaleLink>
    </nav>
  );
}

사용자가 /fr/products에서 About 링크를 클릭하면 /fr/about으로 이동합니다. 로케일 접두사는 링크 정의를 복잡하게 만들지 않고 자동으로 추가됩니다.

3. 절대 경로 및 외부 링크에 대한 예외 처리하기

래퍼를 확장하여 경로에 이미 로케일이 포함되어 있거나 외부 URL을 가리키는 경우를 감지하여 접두사 중복 추가나 외부 내비게이션 손상을 방지합니다.

import { Link, useParams } from "react-router";
import type { LinkProps } from "react-router";

export function LocaleLink({ to, ...props }: LinkProps) {
  const { locale } = useParams<{ locale: string }>();

  if (!locale) {
    return <Link to={to} {...props} />;
  }

  const isExternal =
    typeof to === "string" &&
    (to.startsWith("http://") || to.startsWith("https://"));
  const alreadyLocalized =
    typeof to === "string" && to.startsWith(`/${locale}/`);

  if (isExternal || alreadyLocalized) {
    return <Link to={to} {...props} />;
  }

  const localizedTo =
    typeof to === "string"
      ? `/${locale}${to.startsWith("/") ? to : `/${to}`}`
      : {
          ...to,
          pathname: `/${locale}${to.pathname?.startsWith("/") ? to.pathname : `/${to.pathname}`}`,
        };

  return <Link to={localizedTo} {...props} />;
}

이 방법은 경로가 이미 로케일로 시작하는 경우 접두사 중복 추가를 방지하고 외부 URL은 변경 없이 전달하여 모든 내비게이션 시나리오에서 컴포넌트가 안정적으로 작동하도록 보장합니다.