๋ค์ด๊ฐ๋ฉฐ
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 ๋ฐฉ์๊ณผ 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 ์ ๋ณด๊ฐ ์์)
์ค์ ์์ : ์กฐ๊ฑด๋ถ ๋ฉํ ํ๊ทธ
- 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;
}
- 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 ํ๋ ๊ทธ๋ ๊ฒ ๊ถ์ฅํ๊ณ ์์ผ๋๊น์.