5 min read

React Router 7์—์„œ meta ์ •๋ณด ์„ค์ •ํ•˜๊ธฐ. ๊ทผ๋ฐ ์ด์ œ React 19๋ฅผ ๊ณ๋“ค์ธ

Table of Contents

๋“ค์–ด๊ฐ€๋ฉฐ

React Router 7(Remix)๋ฅผ ์‚ฌ์šฉํ•˜๋‹ค ๋ณด๋ฉด ํŽ˜์ด์ง€์˜ ๋ฉ”ํƒ€ ์ •๋ณด๋ฅผ ์„ค์ •ํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์Šต๋‹ˆ๋‹ค. SEO๋ฅผ ์œ„ํ•œ title, description, ์†Œ์…œ ๋ฏธ๋””์–ด ๊ณต์œ ๋ฅผ ์œ„ํ•œ Open Graph ํƒœ๊ทธ ๋“ฑ์ด ๋Œ€ํ‘œ์ ์ด์ฃ .

React Router 7 ํ”„๋ ˆ์ž„์›Œํฌ ๋ชจ๋“œ์—์„œ๋Š” ์ „ํ†ต์ ์œผ๋กœ route module์—์„œ meta ํ•จ์ˆ˜๋ฅผ export ํ•˜๋Š” ๋ฐฉ์‹์„ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ React 19๋ถ€ํ„ฐ๋Š” ์ƒํ™ฉ์ด ์กฐ๊ธˆ ๋‹ฌ๋ผ์กŒ์Šต๋‹ˆ๋‹ค. React 19์— ๋ฉ”ํƒ€ ํƒœ๊ทธ๋ฅผ ๋‹ค๋ฃจ๋Š” ๊ธฐ๋Šฅ์ด ๋‚ด์žฅ๋˜๋ฉด์„œ, React Router 7 ๊ณต์‹ ๋ฌธ์„œ์—์„œ๋„ ์ด์ œ๋Š” React์˜ ํ‘œ์ค€ ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•˜๋ผ๊ณ  ์•ˆ๋‚ดํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

React Router 7 meta ๋ณ€๊ฒฝ ์•ˆ๋‚ด

์ด ๊ธ€์—์„œ๋Š” ๊ธฐ์กด React Router 7 ๋ฐฉ์‹๊ณผ React 19์˜ ์ƒˆ๋กœ์šด ๋ฐฉ์‹์„ ๋น„๊ตํ•˜๊ณ , ์–ด๋–ค ๋ฐฉ์‹์„ ์„ ํƒํ•ด์•ผ ํ• ์ง€ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

React Router 7์˜ ๋ฐฉ์‹: meta ํ•จ์ˆ˜

React Router 7์˜ ํ”„๋ ˆ์ž„์›Œํฌ ๋ชจ๋“œ์—์„œ๋Š” route module์—์„œ meta ํ•จ์ˆ˜๋ฅผ export ํ•˜์—ฌ ๋ฉ”ํƒ€ ์ •๋ณด๋ฅผ ์ •์˜ํ–ˆ์Šต๋‹ˆ๋‹ค.

๊ธฐ๋ณธ์ ์ธ ์‚ฌ์šฉ๋ฒ•์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

// app/routes/about.tsx
export function meta() {
  return [
    { title: "About Us" },
    {
      name: "description",
      content: "Learn more about our company",
    },
    {
      property: "og:title",
      content: "About Us - My Company",
    },
  ];
}

export default function About() {
  return <div>About page content</div>;
}

์ด ๋ฐฉ์‹์˜ ํŠน์ง•

  • route module ๋ ˆ๋ฒจ์—์„œ ๋ฉ”ํƒ€ ์ •๋ณด๋ฅผ ๊ด€๋ฆฌ
  • ๋ฐฐ์—ด ํ˜•ํƒœ๋กœ ์—ฌ๋Ÿฌ ๋ฉ”ํƒ€ ํƒœ๊ทธ๋ฅผ ์ •์˜
  • loader, clientLoader ๋ฐ์ดํ„ฐ๋ฅผ ํ™œ์šฉํ•œ ๋™์  ๋ฉ”ํƒ€ ์ •๋ณด ์ƒ์„ฑ ๊ฐ€๋Šฅ
export function meta({ loaderData }: Route.MetaArgs) {
  const { post } = loaderData;
  return [
    { title: post.title },
    { name: "description", content: post.summary },
  ];
}

<Meta /> ์ปดํฌ๋„ŒํŠธ๋ฅผ root.tsx์— ๋ฐฐ์น˜ํ•˜๋ฉด ์ž๋™์œผ๋กœ ๋ Œ๋”๋ง๋ฉ๋‹ˆ๋‹ค:

// app/root.tsx
import { Meta } from "react-router";

export default function Root() {
  return (
    <html>
      <head>
        <Meta />
      </head>
      <body>{/* ... */}</body>
    </html>
  );
}

React 19์˜ ์ƒˆ๋กœ์šด ๋ฐฉ์‹: ๋‚ด์žฅ ์ปดํฌ๋„ŒํŠธ

React 19๋ถ€ํ„ฐ๋Š” <meta> ํƒœ๊ทธ๋ฅผ ์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€์—์„œ ์ง์ ‘ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. React๊ฐ€ ์ž๋™์œผ๋กœ document์˜ <head>์— ๋ฐฐ์น˜ํ•ด์ค๋‹ˆ๋‹ค.

// app/routes/about.tsx
export default function About() {
  return (
    <div>
      <title>About Us</title>
      <meta name="description" content="Learn more about our company" />
      <meta property="og:title" content="About Us - My Company" />

      {/* ์‹ค์ œ ํŽ˜์ด์ง€ ์ปจํ…์ธ  */}
      <h1>About Our Company</h1>
      <p>We are a great company...</p>
    </div>
  );
}

์ด ๋ฐฉ์‹์˜ ํŠน์ง•

  • ์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€ ์–ด๋””์„œ๋“  <meta> ํƒœ๊ทธ ์‚ฌ์šฉ ๊ฐ€๋Šฅ
  • React๊ฐ€ ์ž๋™์œผ๋กœ <head>๋กœ ํ˜ธ์ด์ŠคํŒ…
  • ์กฐ๊ฑด๋ถ€ ๋ Œ๋”๋ง, ์ƒํƒœ ๊ธฐ๋ฐ˜ ๋ฉ”ํƒ€ ์ •๋ณด ์„ค์ •์ด ๋” ์ž์—ฐ์Šค๋Ÿฌ์›€

๋™์  ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ๋„ ๋งค์šฐ ์ง๊ด€์ ์ž…๋‹ˆ๋‹ค

import { useLoaderData } from "react-router";

export async function loader({ params }) {
  const post = await getPost(params.id);
  return { post };
}

export default function Post() {
  const { post } = useLoaderData<typeof loader>();

  return (
    <article>
      <title>{post.title}</title>
      <meta name="description" content={post.summary} />
      <meta property="og:title" content={post.title} />
      <meta property="og:image" content={post.coverImage} />

      <h1>{post.title}</h1>
      <div>{post.content}</div>
    </article>
  );
}

๋‘ ๋ฐฉ์‹ ๋น„๊ต

React Router 7 meta ํ•จ์ˆ˜ ๋ฐฉ์‹

์žฅ์ 

  • ๋ฉ”ํƒ€ ์ •๋ณด๊ฐ€ route module ๋ ˆ๋ฒจ์—์„œ ๋ช…ํ™•ํ•˜๊ฒŒ ๋ถ„๋ฆฌ๋จ (URL ๊ธฐ์ค€์œผ๋กœ ํ™•์‹คํ•˜๊ฒŒ ๋ถ„๋ฆฌ)
  • ํƒ€์ž… ์•ˆ์ •์„ฑ์ด ๋ช…์‹œ์  (์ž๋™์™„์„ฑ ๊ฐ€๋Šฅ)
  • ๊ธฐ์กด Remix/React Router ํ”„๋กœ์ ํŠธ์™€์˜ ์ผ๊ด€์„ฑ

๋‹จ์ 

  • ๋ณ„๋„์˜ ํ•จ์ˆ˜๋ฅผ export ํ•ด์•ผ ํ•จ
  • React 19์˜ ํ‘œ์ค€์€ ์•„๋‹˜. React Router 7(Remix)์˜ ๊ธฐ๋Šฅ

React 19 ๋‚ด์žฅ ๋ฐฉ์‹

์žฅ์ 

  • React์˜ ํ‘œ์ค€ ๊ธฐ๋Šฅ
  • ์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€์—์„œ ์ง์ ‘ ์ž‘์„ฑ ๊ฐ€๋Šฅ
  • ์กฐ๊ฑด๋ถ€ ๋ฉ”ํƒ€ ํƒœ๊ทธ ์„ค์ •์ด ๋” ์ž์—ฐ์Šค๋Ÿฌ์›€
  • React Router ํŒ€์ด ๊ถŒ์žฅํ•˜๋Š” ๋ฐฉ์‹

๋‹จ์ 

  • ๊ธฐ์กด ํ”„๋กœ์ ํŠธ๋ฅผ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๊ฐ€๋Šฅ์„ฑ ์žˆ์Œ
  • JSX ๋‚ด๋ถ€์— ๋ฉ”ํƒ€ ์ •๋ณด๊ฐ€ ์„ž์—ฌ ์žˆ์–ด ์ฝ”๋“œ๊ฐ€ ๊ธธ์–ด์งˆ ์ˆ˜ ์žˆ์Œ (UI์™€ meta ์ •๋ณด๊ฐ€ ์„ž์ž„)

์‹ค์ „ ์˜ˆ์ œ: ์กฐ๊ฑด๋ถ€ ๋ฉ”ํƒ€ ํƒœ๊ทธ

  1. React Router7์˜ meta ํ•จ์ˆ˜ ๋ฐฉ์‹
export function meta({ data }: Route.MetaArgs) {
  const tags = [
    { title: `${data.product.name} - Our Store` },
    { name: "description", content: data.product.description },
  ];

  // ํ• ์ธ ์ค‘์ผ ๋•Œ๋งŒ ์ปค์Šคํ…€ ๋ฉ”ํƒ€ ํƒœ๊ทธ ์ถ”๊ฐ€
  if (data.product.onSale) {
    tags.push({
      property: "product:sale_price",
      content: data.product.salePrice,
    });
  }

  // ์žฌ๊ณ ๊ฐ€ ์žˆ๋‹ค๋ฉด ์ปค์Šคํ…€ ๋ฉ”ํƒ€ ํƒœ๊ทธ ์ถ”๊ฐ€
  if (data.product.inStock) {
    tags.push({ property: "product:availability", content: "in stock" });
  }

  return tags;
}
  1. React 19 ๋‚ด์žฅ ๋ฐฉ์‹
export default function Product() {
  const { product } = useLoaderData<typeof loader>();

  return (
    <div>
      <title>{product.name} - Our Store</title>
      <meta name="description" content={product.description} />

      {/* ํ• ์ธ ์ค‘์ผ ๋•Œ๋งŒ ์ปค์Šคํ…€ ๋ฉ”ํƒ€ ํƒœ๊ทธ ์ถ”๊ฐ€ */}
      {product.onSale && (
        <meta property="product:sale_price" content={product.salePrice} />
      )}

      {/* ์žฌ๊ณ ๊ฐ€ ์žˆ๋‹ค๋ฉด ์ปค์Šคํ…€ ๋ฉ”ํƒ€ ํƒœ๊ทธ ์ถ”๊ฐ€ */}
      {product.inStock && (
        <meta property="product:availability" content="in stock" />
      )}

      {/* ์—ฌ๊ฑฐ์‹œ๋ถ€ํ„ฐ UI ํ‘œํ˜„ */}
      <h1>{product.name}</h1>
      {/* ... */}
    </div>
  );
}

์–ด๋–ค๊ฒŒ ๋” ๋‚˜์€ ๊ฒƒ ๊ฐ™๋‚˜์š”?

์–ด๋–ค ๋ฐฉ์‹์„ ์„ ํƒํ•ด์•ผ ํ• ๊นŒ?

์ƒˆ ํ”„๋กœ์ ํŠธ๋ผ๋ฉด

  • React 19์˜ ๋‚ด์žฅ <meta> ์ปดํฌ๋„ŒํŠธ ์‚ฌ์šฉ์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค
  • React Router ๊ณต์‹ ๋ฌธ์„œ์—์„œ๋„ ์ด ๋ฐฉ์‹์„ ์•ˆ๋‚ดํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค
  • React์˜ ํ‘œ์ค€์ด๋‹ˆ๊นŒ

๊ธฐ์กด ํ”„๋กœ์ ํŠธ๋ผ๋ฉด

  • ๊ธ‰ํ•˜๊ฒŒ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ํ•  ํ•„์š”๋Š” ์—†์Šต๋‹ˆ๋‹ค
  • ๋‘ ๋ฐฉ์‹์„ ๊ฐ™์ด ์‚ฌ์šฉํ•ด๋„ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค
  • ์ƒˆ๋กœ ์ž‘์„ฑํ•˜๋Š” route๋ถ€ํ„ฐ React 19 ๋ฐฉ์‹์„ ์ ์šฉํ•˜๋Š” ๊ฒƒ์„ ์ถ”์ฒœํ•ฉ๋‹ˆ๋‹ค

๋งˆ์น˜๋ฉฐ

๋ณ„๋„์˜ meta ํ•จ์ˆ˜๋ฅผ export ํ•˜๋Š” ๋Œ€์‹ , ์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€์—์„œ ์ง์ ‘ <meta> ํƒœ๊ทธ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ์‹์€ ๋” ์ง๊ด€์ ์ž…๋‹ˆ๋‹ค. React๊ฐ€ ์•Œ์•„์„œ <head>์— ๋ฐฐ์น˜ํ•ด์ฃผ๋‹ˆ ์šฐ๋ฆฌ๋Š” ๊ทธ์ € ํ•„์š”ํ•œ ๊ณณ์— ๋ฉ”ํƒ€ ์ •๋ณด๋ฅผ ์„ ์–ธํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

๋ฌผ๋ก  ๊ธฐ์กด ๋ฐฉ์‹๋„ ์—ฌ์ „ํžˆ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ƒˆ ํ”„๋กœ์ ํŠธ๋ฅผ ์‹œ์ž‘ํ•œ๋‹ค๋ฉด, React 19์˜ ํ‘œ์ค€ ๋ฐฉ์‹์„ ๋”ฐ๋ฅด๋Š” ๊ฒƒ์ด ์ข‹๊ฒ ์Šต๋‹ˆ๋‹ค. React Router ํŒ€๋„ ๊ทธ๋ ‡๊ฒŒ ๊ถŒ์žฅํ•˜๊ณ  ์žˆ์œผ๋‹ˆ๊นŒ์š”.


์ฐธ๊ณ  ์ž๋ฃŒ