์ž‘๊ฐ€:
    ์ƒ์„ฑ:2024-03-07๋งˆ์ง€๋ง‰ ์—…๋ฐ์ดํŠธ:2026-09-27

    ๊ธฐ์กด Vite ๋ฐ React ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์‚ฌํ›„์— ๋‹ค๊ตญ์–ด(i18n)ํ™”ํ•˜๋Š” ๋ฐฉ๋ฒ• (2026 ๊ฐ€์ด๋“œ)

    ์ฒ˜์Œ๋ถ€ํ„ฐ Vite ๋ฐ React ํ”„๋กœ์ ํŠธ์— ๊ตญ์ œํ™”(i18n)๋ฅผ ์ ์šฉํ•˜๋Š” ๊ฒƒ์€ ๋น„๊ต์  ๊ฐ„๋‹จํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ด๋ฏธ ๋‹จ์ผ ์–ธ์–ด๋กœ ๊ตฌ์ถ•๋˜์–ด ๋ฐฐํฌ ์ค‘์ธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ์‚ฌํ›„์— ๋‹ค๊ตญ์–ด๋ฅผ ์ ์šฉํ•ด์•ผ ํ•œ๋‹ค๋ฉด ์–ด๋–ป๊ฒŒ ํ•ด์•ผ ํ• ๊นŒ์š”?

    react-i18next๋‚˜ react-intl ๊ฐ™์€ ๊ธฐ์กด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•ด ๋ณธ ๊ฐœ๋ฐœ์ž๋ผ๋ฉด ๊ทธ ๋ฒˆ๊ฑฐ๋กœ์›€์„ ์ž˜ ์•Œ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค:

    • ์ˆ˜๋ฐฑ ๊ฐœ์˜ JSX ๋ฐ TSX ํŒŒ์ผ์—์„œ ํ•˜๋“œ์ฝ”๋”ฉ๋œ ๋ฌธ์ž์—ด์„ ์ผ์ผ์ด ์ฐพ์•„๋‚ด๊ธฐ.
    • ์ค‘์ฒฉ๋œ JSON ๋”•์…”๋„ˆ๋ฆฌ๋ฅผ ์ˆ˜๋™์œผ๋กœ ์ƒ์„ฑํ•˜๊ณ  ๋ฒˆ์—ญ ํ‚ค(components.header.title ๋“ฑ)๋ฅผ ์ž„์˜๋กœ ์ž‘๋ช…ํ•˜๊ธฐ.
    • UI ํ…์ŠคํŠธ๋ฅผ ๋ฒˆ๊ฑฐ๋กœ์šด ํ›…(t('...'))์œผ๋กœ ๊ต์ฒดํ•˜๊ธฐ.
    • ํด๋ผ์ด์–ธํŠธ ์ธก ๋ผ์šฐํŒ…, ์ƒํƒœ ๊ด€๋ฆฌ ๋ฐ ์–ธ์–ด ์ „ํ™˜ ๋กœ์ง์„ ์žฌ๊ตฌ์„ฑํ•˜๊ธฐ.

    2026๋…„์—๋Š” ์ฝ”๋“œ๋ฒ ์ด์Šค ์ „์ฒด๋ฅผ ์ฒ˜์Œ๋ถ€ํ„ฐ ๋‹ค์‹œ ์ž‘์„ฑํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. Intlayer๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ž๋™ ํ…์ŠคํŠธ ์ถ”์ถœ, AI ๋ฒˆ์—ญ ๋ฐ ๋งค๋„๋Ÿฌ์šด Vite ํ†ตํ•ฉ์„ ํ†ตํ•ด ๋‹จ ๋ช‡ ๋ถ„ ๋งŒ์— ๊ธฐ์กด Vite ๋ฐ React ์•ฑ์— ๋‹ค๊ตญ์–ด๋ฅผ ๋„์ž…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    Vite ๋ฐ React์— ๋Œ€ํ•œ ๋‹จ๊ณ„๋ณ„ ๊ธฐ์ˆ  ๊ฐ€์ด๋“œ๋ฅผ ์ฐพ๊ณ  ๊ณ„์‹ ๊ฐ€์š”? ์ „์šฉ ๋ฌธ์„œ๋ฅผ ํ™•์ธํ•˜์„ธ์š”: Intlayer๋กœ Vite ๋ฐ React ๋ฒˆ์—ญํ•˜๊ธฐ.

    ๋ชฉ์ฐจ

    ์‚ฌํ›„ ์ ์šฉ์˜ ๋”œ๋ ˆ๋งˆ: ๊ธฐ์กด ์•ฑ์— ๋‹ค๊ตญ์–ด๋ฅผ ์ถ”๊ฐ€ํ•˜๊ธฐ ํž˜๋“  ์ด์œ 

    ๊ธฐ์กด Vite ๋ฐ React ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ๊ตญ์ œํ™”๋ฅผ ์ ์šฉํ•  ๋•Œ ๊ฐœ๋ฐœ์ž๋Š” ์„ธ ๊ฐ€์ง€ ํฐ ์žฅ์• ๋ฌผ์— ์ง๋ฉดํ•ฉ๋‹ˆ๋‹ค:

    1. ์ฝ”๋“œ๋ฒ ์ด์Šค ์˜ค์—ผ: ๋ฌธ์ž์—ด์„ ์ˆ˜๋™์œผ๋กœ JSON ๋”•์…”๋„ˆ๋ฆฌ์— ์ถ”์ถœํ•˜๋ ค๋ฉด ๊ฑฐ์˜ ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ ํŒŒ์ผ์„ ์ˆ˜์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ๋Œ€๊ทœ๋ชจ Git diff, ๋ณ‘ํ•ฉ ์ถฉ๋Œ ์œ„ํ—˜ ๋ฐ ํ™”๋ฉด ๊นจ์ง ๋ฒ„๊ทธ๋ฅผ ์œ ๋ฐœํ•ฉ๋‹ˆ๋‹ค.
    2. ๋ฒˆ์—ญ ํ‚ค ๊ด€๋ฆฌ ๋ถ€๋‹ด: ํ…์ŠคํŠธ ์กฐ๊ฐ๋งˆ๋‹ค dashboard.hero.ctaButton๊ณผ ๊ฐ™์€ ํ‚ค๋ฅผ ์ž‘๋ช…ํ•˜๋Š” ๊ฒƒ์€ ๊ฐœ๋ฐœ ์†๋„๋ฅผ ๋Šฆ์ถ”๊ณ  ๋ฌธ๊ตฌ๊ฐ€ ๋ฐ”๋€” ๋•Œ๋งˆ๋‹ค ์ธ์ง€์  ๋ถ€๋‹ด์„ ์ค๋‹ˆ๋‹ค.
    3. ์ง€๋ฃจํ•œ ๋ฒˆ์—ญ ์ž‘์—…: ๋ฌธ์ž์—ด์„ ์ถ”์ถœํ•œ ํ›„ 5๊ฐœ, 10๊ฐœ ๋˜๋Š” 20๊ฐœ ์–ธ์–ด๋กœ ๋”•์…”๋„ˆ๋ฆฌ๋ฅผ ์ฑ„์šฐ๋ ค๋ฉด ๋์—†๋Š” ๋ณต์‚ฌ-๋ถ™์—ฌ๋„ฃ๊ธฐ๋‚˜ ๊ฐ’๋น„์‹ผ ์™ธ๋ถ€ ๋ฒˆ์—ญ ์„œ๋น„์Šค๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

    Intlayer๋Š” ์ปดํŒŒ์ผ๋Ÿฌ ๊ธฐ๋ฐ˜ ์ž๋™ ์ถ”์ถœ, ์ปดํฌ๋„ŒํŠธ ๋‹จ์œ„ ์„ ์–ธ์  ๋”•์…”๋„ˆ๋ฆฌ, ๊ทธ๋ฆฌ๊ณ  Vite์™€์˜ ์™„๋ฒฝํ•œ ํ†ตํ•ฉ์„ ํ†ตํ•ด ์ด๋Ÿฌํ•œ ๋ฌธ์ œ๋ฅผ ๊ทผ๋ณธ์ ์œผ๋กœ ํ•ด๊ฒฐํ•ฉ๋‹ˆ๋‹ค.

    ์ž๋™ ์ฝ˜ํ…์ธ  ์ถ”์ถœ (์ˆ˜๋™ ๋ฌธ์ž์—ด ํƒ์ƒ‰ ๋ถˆํ•„์š”)

    JSX์—์„œ ํ•˜๋“œ์ฝ”๋”ฉ๋œ ๋ฌธ์ž์—ด์„ ์ผ์ผ์ด ๋ณต์‚ฌํ•  ํ•„์š” ์—†์ด, Intlayer๋Š” ๋‘ ๊ฐ€์ง€ ํŽธ๋ฆฌํ•œ ๊ฒฝ๋กœ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค:

    ์˜ต์…˜ A: CLI ์ถ”์ถœ ๋„๊ตฌ (npx intlayer extract)

    ์ฝ”๋“œ๋ฒ ์ด์Šค์—์„œ ๋ฐ”๋กœ Intlayer์˜ ์ถ”์ถœ ๋„๊ตฌ๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

    bash
    npx intlayer extract
    

    ์ด ๋ช…๋ น์–ด๋Š” React ์ปดํฌ๋„ŒํŠธ๋ฅผ ํŒŒ์‹ฑํ•˜๊ณ  ์‚ฌ์šฉ์ž์—๊ฒŒ ํ‘œ์‹œ๋˜๋Š” ํ…์ŠคํŠธ๋ฅผ ๊ฐ์ง€ํ•˜์—ฌ ์ปดํฌ๋„ŒํŠธ ํŒŒ์ผ ๋ฐ”๋กœ ์˜†์— ์ฝ˜ํ…์ธ  ์„ ์–ธ ํŒŒ์ผ(.content.ts)์„ ์ž๋™ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ์ปดํฌ๋„ŒํŠธ ๋กœ์ง์€ ์„ ์–ธ์ ์ด๊ณ  ๋ช…ํ™•ํ•˜๋ฉฐ ํƒ€์ž… ์•ˆ์ „ํ•˜๊ฒŒ ์œ ์ง€๋˜๋ฉฐ, ๋ฒˆ์—ญ ํ‚ค๋ฅผ ์†์ˆ˜ ์ž‘์„ฑํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.

    ์˜ต์…˜ B: Intlayer ์ปดํŒŒ์ผ๋Ÿฌ (๋นŒ๋“œ ํƒ€์ž„ ์ž๋™ ์ถ”์ถœ)

    ์„ค์ •์—์„œ Intlayer ์ปดํŒŒ์ผ๋Ÿฌ๋ฅผ ํ™œ์„ฑํ™”ํ•˜๋ฉด ๊ธฐ๋ณธ ์–ธ์–ด์˜ ์ผ๋ฐ˜ ํ…์ŠคํŠธ๋กœ ์ปดํฌ๋„ŒํŠธ๋ฅผ ํ‰์†Œ์ฒ˜๋Ÿผ ๊ณ„์† ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋นŒ๋“œ ์‹œ ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ํ…์ŠคํŠธ๋ฅผ ์ถ”์ถœํ•˜๊ณ  ์ง€์—ญํ™”๋œ ์ฝ˜ํ…์ธ ๋ฅผ ์ž๋™์œผ๋กœ ์ฃผ์ž…ํ•ฉ๋‹ˆ๋‹ค:

    src/App.tsx
    // ์ผ๋ฐ˜์ ์ธ React ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜์„ธ์š”. ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ์ž๋™์œผ๋กœ ํ…์ŠคํŠธ๋ฅผ ์ถ”์ถœํ•ฉ๋‹ˆ๋‹ค
    export default function App() {
      return (
        <section>
          <h1>ํ”Œ๋žซํผ์— ์˜ค์‹  ๊ฒƒ์„ ํ™˜์˜ํ•ฉ๋‹ˆ๋‹ค</h1>
          <p>์ง€๊ธˆ ์ตœ์‹  ๊ธฐ๋Šฅ์„ ๊ฒฝํ—˜ํ•ด ๋ณด์„ธ์š”.</p>
        </section>
      );
    }
    

    ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ Intlayer๊ฐ€ ๋”•์…”๋„ˆ๋ฆฌ๋ฅผ ๋นŒ๋“œํ•˜๊ณ  ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ง€์—ญํ™”๋œ ์ฝ˜ํ…์ธ ์™€ ์ž๋™์œผ๋กœ ์—ฐ๊ฒฐํ•˜๋ฏ€๋กœ ์ˆ˜๋™ ๋ฆฌํŒฉํ† ๋ง ๋‹จ๊ณ„๊ฐ€ ์™„์ „ํžˆ ์ƒ๋žต๋ฉ๋‹ˆ๋‹ค.

    ์ด ๊ฒฝ์šฐ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ตฌ์กฐ์˜ src/App.content.ts ์„ ์–ธ ํŒŒ์ผ์ด ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค:

    src/App.content.ts
    import { Dictionary } from "intlayer";
    
    const content = {
      key: "app",
      content: {
        peullaespomeOsinGeoseulHwan: t({
          ko: "ํ”Œ๋žซํผ์— ์˜ค์‹  ๊ฒƒ์„ ํ™˜์˜ํ•ฉ๋‹ˆ๋‹ค",
        }),
        jigeumChoesinGineungeulGyeon: t({
          ko: "์ง€๊ธˆ ์ตœ์‹  ๊ธฐ๋Šฅ์„ ๊ฒฝํ—˜ํ•ด ๋ณด์„ธ์š”.",
        }),
      },
    };
    
    export default content;
    

    ์„ ํ˜ธํ•˜๋Š” LLM์„ ํ™œ์šฉํ•œ AI ์ž๋™ ๋ฒˆ์—ญ

    ์ฝ˜ํ…์ธ ๊ฐ€ ์ถ”์ถœ๋œ ํ›„ ์ˆ˜์‹ญ ๊ฐœ ์–ธ์–ด๋กœ ๋ฒˆ์—ญํ•˜๋Š” ๋ฐ ๋ฉฐ์น ์”ฉ ๊ฑธ๋ฆด ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. Intlayer์—๋Š” ์ž์ฒด API ํ‚ค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ OpenAI, Anthropic, DeepSeek, Mistral๊ณผ ์ง์ ‘ ์—ฐ๋™๋˜๋Š” AI ๋ฒˆ์—ญ CLI๊ฐ€ ๋‚ด์žฅ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค:

    bash
    npx intlayer fill
    

    intlayer.config.ts์—์„œ ์ง€์›ํ•  ๋กœ์ผ€์ผ๊ณผ AI ์ œ๊ณต์ž๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค:

    intlayer.config.ts
    import { Locales, type IntlayerConfig } from "intlayer";
    
    const config: IntlayerConfig = {
      internationalization: {
        locales: [Locales.ENGLISH, Locales.FRENCH, Locales.SPANISH, Locales.KOREAN],
        defaultLocale: Locales.KOREAN,
      },
      ai: {
        provider: "openai",
        model: "gpt-4o-mini",
        apiKey: process.env.OPENAI_API_KEY,
        applicationContext:
          "Vite ๋ฐ React๋กœ ๊ตฌ์ถ•๋œ ์ตœ์‹  SaaS ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ฐ ๋Œ€์‹œ๋ณด๋“œ",
      },
    };
    
    export default config;
    

    npx intlayer fill์„ ์‹คํ–‰ํ•˜๋ฉด ์„ค์ •๋œ ๋ชจ๋“  ๋กœ์ผ€์ผ์— ๋Œ€ํ•ด ๊ณ ํ’ˆ์งˆ ๋ฒˆ์—ญ์ด ์ฝ˜ํ…์ธ  ์„ ์–ธ ํŒŒ์ผ์— ์ฑ„์›Œ์ง‘๋‹ˆ๋‹ค:

    src/App.content.ts
    import { Dictionary } from "intlayer";
    
    const content = {
      key: "app",
      content: {
        peullaespomeOsinGeoseulHwan: t({
          ko: "ํ”Œ๋žซํผ์— ์˜ค์‹  ๊ฒƒ์„ ํ™˜์˜ํ•ฉ๋‹ˆ๋‹ค",
          en: "Welcome to our platform",
          fr: "Bienvenue sur notre plateforme",
          es: "Bienvenido a nuestra plataforma",
        }),
        jigeumChoesinGineungeulGyeon: t({
          ko: "์ง€๊ธˆ ์ตœ์‹  ๊ธฐ๋Šฅ์„ ๊ฒฝํ—˜ํ•ด ๋ณด์„ธ์š”.",
          en: "Start exploring our modern features today.",
          fr: "Dรฉcouvrez nos fonctionnalitรฉs modernes dรจs aujourd'hui.",
          es: "Comience a explorar nuestras funciones modernas hoy.",
        }),
      },
    };
    
    export default content;
    

    Intlayer๊ฐ€ ๋ชจ๋ธ์— applicationContext๋ฅผ ์ œ๊ณตํ•˜๋ฏ€๋กœ ์ƒ์„ฑ๋œ ๋ฒˆ์—ญ์€ ์ผ๋ฐ˜ ๊ธฐ๊ณ„ ๋ฒˆ์—ญ๋ณด๋‹ค ๊ธฐ์ˆ ์  ๋งฅ๋ฝ, ๋ธŒ๋žœ๋“œ ๋ณด์ด์Šค ๋ฐ ๋ฌธ๋ฒ•์  ๋””ํ…Œ์ผ์„ ํ›จ์”ฌ ๋” ์ •ํ™•ํ•˜๊ฒŒ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค.

    ๋ฐฐํฌ ์ „ ๋ˆ„๋ฝ๋œ ํ…์ŠคํŠธ๊ฐ€ ์—†๋Š”์ง€ ํ™•์ธํ•˜๋ ค๋ฉด ๋‹ค์Œ์„ ์‹คํ–‰ํ•˜์„ธ์š”:

    bash
    npx intlayer test
    

    Vite ํ†ตํ•ฉ ๋ฐ Provider ์„ค์ •

    Vite์— Intlayer๋ฅผ ํ†ตํ•ฉํ•˜๋Š” ๊ฒƒ์€ vite.config.ts์— ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์ถ”๊ฐ€ํ•˜๊ณ  ๋ฃจํŠธ ์ปดํฌ๋„ŒํŠธ๋ฅผ IntlayerProvider๋กœ ๊ฐ์‹ธ๋Š” ๊ฒƒ๋งŒ์œผ๋กœ ์™„๋ฃŒ๋ฉ๋‹ˆ๋‹ค:

    vite.config.ts
    import { defineConfig } from "vite";
    import react from "@vitejs/plugin-react-swc";
    import { intlayer } from "vite-intlayer";
    
    export default defineConfig({
      plugins: [react(), intlayer()],
    });
    
    Intlayer v9๋ถ€ํ„ฐ ์ปดํŒŒ์ผ๋Ÿฌ๋Š” intlayer() ํ”Œ๋Ÿฌ๊ทธ์ธ์— ๋‚ด์žฅ๋˜์–ด ์žˆ์–ด intlayer.config.ts์— compiler.enabled๋ฅผ ์„ค์ •ํ•˜๋ฉด ์ž๋™์œผ๋กœ ํ™œ์„ฑํ™”๋ฉ๋‹ˆ๋‹ค.

    ๋ฃจํŠธ ์ปดํฌ๋„ŒํŠธ์—์„œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ IntlayerProvider๋กœ ๊ฐ์‹ธ์ค๋‹ˆ๋‹ค:

    src/App.tsx
    import { FC } from "react";
    import { IntlayerProvider } from "react-intlayer";
    import { MainContent } from "./MainContent";
    
    const App: FC = () => {
      return (
        <IntlayerProvider>
          <MainContent />
        </IntlayerProvider>
      );
    };
    
    export default App;
    

    ๋™์  ์–ธ์–ด ์ „ํ™˜

    useLocale ํ›…์„ ์‚ฌ์šฉํ•˜์—ฌ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์–ด๋””์—์„œ๋‚˜ ๊ฐ„ํŽธํ•˜๊ฒŒ ์–ธ์–ด๋ฅผ ์ „ํ™˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

    src/components/LocaleSwitcher.tsx
    import { FC } from "react";
    import { Locales } from "intlayer";
    import { useLocale } from "react-intlayer";
    
    export const LocaleSwitcher: FC = () => {
      const { locale, setLocale, availableLocales } = useLocale();
    
      return (
        <select
          value={locale}
          onChange={(e) => setLocale(e.target.value as Locales)}
        >
          {availableLocales.map((loc) => (
            <option key={loc} value={loc}>
              {loc}
            </option>
          ))}
        </select>
      );
    };
    

    ๋‹ค๊ตญ์–ด SEO (Sitemap ๋ฐ Robots.txt)

    Intlayer๋Š” ์ •์  Vite ๋ฐฐํฌ ํ™˜๊ฒฝ์„ ์œ„ํ•ด ํฌ๋กค๋Ÿฌ ์นœํ™”์ ์ธ sitemap.xml ๋ฐ robots.txt๋ฅผ ์ƒ์„ฑํ•˜๋Š” generateSitemap ๋ฐ getMultilingualUrls์™€ ๊ฐ™์€ ์œ ํ‹ธ๋ฆฌํ‹ฐ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค:

    generate-seo.mjs
    import fs from "fs";
    import path from "path";
    import { fileURLToPath } from "url";
    import { generateSitemap, getMultilingualUrls } from "intlayer";
    
    const __dirname = path.dirname(fileURLToPath(import.meta.url));
    const SITE_URL = (process.env.SITE_URL || "https://example.com").replace(
      /\/$/,
      ""
    );
    
    const pathList = [
      { path: "/", changefreq: "daily", priority: 1.0 },
      { path: "/about", changefreq: "monthly", priority: 0.7 },
    ];
    
    const sitemapXml = generateSitemap(pathList, {
      siteUrl: "https://example.com",
    });
    fs.writeFileSync(path.join(__dirname, "public", "sitemap.xml"), sitemapXml);
    
    const getAllMultilingualUrls = (urls) =>
      urls.flatMap((url) => Object.values(getMultilingualUrls(url)));
    
    const disallowedPaths = getAllMultilingualUrls(["/admin", "/private"]);
    
    const robotsTxt = [
      "User-agent: *",
      "Allow: /",
      ...disallowedPaths.map((path) => `Disallow: ${path}`),
      "",
      `Sitemap: https://example.com/sitemap.xml`,
    ].join("\n");
    
    fs.writeFileSync(path.join(__dirname, "public", "robots.txt"), robotsTxt);
    console.log("SEO ํŒŒ์ผ์ด ์„ฑ๊ณต์ ์œผ๋กœ ์ƒ์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.");
    

    package.json์— prebuild ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ vite build ์ „์— ์‹คํ–‰๋˜๋„๋ก ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค:

    package.json
    {
      "scripts": {
        "dev": "vite",
        "prebuild": "node generate-seo.mjs",
        "build": "vite build",
        "preview": "vite preview"
      }
    }
    

    ์‹ฌ์ธต ๊ฐ€์ด๋“œ: ๋‹จ๊ณ„๋ณ„ ์„ค์ •์ด ์ค€๋น„๋˜์…จ๋‚˜์š”?

    ์ด ๊ฐ€์ด๋“œ๋Š” 2026๋…„์— ๋Œ€๊ทœ๋ชจ ๋ฆฌํŒฉํ† ๋ง ์—†์ด ๊ธฐ์กด Vite ๋ฐ React ์•ฑ์„ ๋‹ค๊ตญ์–ดํ™”ํ•˜๋Š” ์ „๋ฐ˜์ ์ธ ๊ฐœ๋…์„ ์†Œ๊ฐœํ–ˆ์Šต๋‹ˆ๋‹ค.

    ์—„๊ฒฉํ•œ TypeScript ํƒ€์ž… ์•ˆ์ „์„ฑ, ๋™์  ๋”•์…”๋„ˆ๋ฆฌ ๋ฐ ๋น„์ฃผ์–ผ ์—๋””ํ„ฐ๋ฅผ ํฌํ•จํ•œ ๋ชจ๋“  ์„ค์ • ๋‹จ๊ณ„๋ฅผ ์ž์„ธํžˆ ํ™•์ธํ•˜๋ ค๋ฉด ๊ณต์‹ ๊ธฐ์ˆ  ๊ฐ€์ด๋“œ๋ฅผ ์ฐธ๊ณ ํ•˜์„ธ์š”:

    ๐Ÿ‘‰ Intlayer๋ฅผ ํ™œ์šฉํ•œ Vite ๋ฐ React ๋ฒˆ์—ญ ์ „์ฒด ๊ฐ€์ด๋“œ

    ์ž์ฃผ ๋ฌป๋Š” ์งˆ๋ฌธ (FAQ)

    ๋„ค. npx intlayer extract๋ฅผ ์‹คํ–‰ํ•˜์—ฌ ํ•˜๋“œ์ฝ”๋”ฉ๋œ ํ…์ŠคํŠธ๋ฅผ ์ง€์—ญํ™”๋œ ์ฝ˜ํ…์ธ  ์„ ์–ธ ํŒŒ์ผ๋กœ ์ž๋™ ์ถ”์ถœํ•˜๊ฑฐ๋‚˜, Intlayer ์ปดํŒŒ์ผ๋Ÿฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋นŒ๋“œ ์‹œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ž๋™ ๋ณ€ํ™˜ํ•˜๋ฉด์„œ ํ‘œ์ค€ JSX๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    Intlayer๋Š” ์ปดํฌ๋„ŒํŠธ๋ณ„ ๋”•์…”๋„ˆ๋ฆฌ ์ •์˜์™€ ๋นŒ๋“œ ํƒ€์ž„ ๋งคํฌ๋กœ ์ตœ์ ํ™”๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๊ฑฐ๋Œ€ํ•œ JSON ํŒŒ์ผ์„ ํ•œ ๋ฒˆ์— ๋ถˆ๋Ÿฌ์˜ค๋Š” ๋Œ€์‹  ํ˜„์žฌ ํ™”๋ฉด์— ๋ Œ๋”๋ง๋˜๋Š” ์ปดํฌ๋„ŒํŠธ์— ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋งŒ ๋ฒˆ๋“ค์— ํฌํ•จ๋ฉ๋‹ˆ๋‹ค. ๋™์  ๋”•์…”๋„ˆ๋ฆฌ๋ฅผ ํ†ตํ•ด ์–ธ์–ด๋ณ„ ์ง€์—ฐ ๋กœ๋”ฉ๋„ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

    ๋„ค. Intlayer CLI์˜ npx intlayer fill ๋ช…๋ น์–ด๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์„ค์ •๋œ AI ์ œ๊ณต์ž(OpenAI, Anthropic, Mistral, DeepSeek)์™€ ์—ฐ๋™ํ•˜์—ฌ ํ”„๋กœ์ ํŠธ ์ „์ฒด์—์„œ ๋ˆ„๋ฝ๋œ ์–ธ์–ด ๋ฒˆ์—ญ์„ ๋ฌธ๋งฅ์— ๋งž๊ฒŒ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    ๋„ค. Intlayer๋Š” react-i18next ๋ฐ react-intl์„ ์œ„ํ•œ ํ˜ธํ™˜ ์–ด๋Œ‘ํ„ฐ๋ฅผ ์ œ๊ณตํ•˜๋ฉฐ, ๊ธฐ์กด JSON ๋ฒˆ์—ญ ํŒŒ์ผ์„ ๋™๊ธฐํ™”ํ•  ์ˆ˜ ์žˆ๋Š” ์ „์šฉ ํ”Œ๋Ÿฌ๊ทธ์ธ(sync-json)๋„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

    ๋Œ“๊ธ€

    ์•„์ง ๋Œ“๊ธ€์ด ์—†์Šต๋‹ˆ๋‹ค. ์ฒซ ๋ฒˆ์งธ๋กœ ์˜๊ฒฌ์„ ๋‚˜๋ˆ ๋ณด์„ธ์š”.

    ๊ด€๋ จ ๊ฒŒ์‹œ๋ฌผ

    ์ตœ๊ทผ ๊ฒŒ์‹œ๋ฌผ