์ด ํ์ด์ง์๋ ์ ํ๋ฆฌ์ผ์ด์ ํ ํ๋ฆฟ์ด ์ ๊ณต๋ฉ๋๋ค.
์ด ํ์ด์ง๋ ํ ํ๋ฆฟ์ ๋ผ์ด๋ธ ๋ฐ๋ชจ๋ก ์ฐ๊ฒฐ๋ฉ๋๋ค.
์ด ํ์ด์ง์ ์ํ๋ AI ์ด์์คํดํธ๋ฅผ ์ฌ์ฉํ์ฌ ๋ฌธ์๋ฅผ ์์ฝํฉ๋๋ค
๋ฒ์ ๊ธฐ๋ก
- "Solid useIntlayer API ์ฌ์ฉ๋ฒ์ ์ง์ ์์ฑ ์ก์ธ์ค๋ก ์
๋ฐ์ดํธ"v8.9.02026. 5. 4.
- "์ด๊ธฐ ๊ธฐ๋ก"v8.4.102026. 3. 23.
์ด ํ์ด์ง์ ์ฝํ ์ธ ๋ AI๋ฅผ ์ฌ์ฉํ์ฌ ๋ฒ์ญ๋์์ต๋๋ค.
์์ด ์๋ณธ ๋ด์ฉ์ ์ต์ ๋ฒ์ ์ ๋ณด๊ธฐ์ด ๋ฌธ์๋ฅผ ๊ฐ์ ํ ์์ด๋์ด๊ฐ ์์ผ์๋ฉด GitHub์ ํ ๋ฆฌํ์คํธ๋ฅผ ์ ์ถํ์ฌ ์์ ๋กญ๊ฒ ๊ธฐ์ฌํด ์ฃผ์ธ์.
๋ฌธ์์ ๋ํ GitHub ๋งํฌ๋ฌธ์์ Markdown์ ํด๋ฆฝ๋ณด๋์ ๋ณต์ฌ
Intlayer๋ฅผ ์ฌ์ฉํ์ฌ Vite ๋ฐ Lit ์น์ฌ์ดํธ ๋ฒ์ญํ๊ธฐ | ๊ตญ์ ํ (i18n)
๋ชฉ์ฐจ
๋์๋ณด๋ค Intlayer๋ฅผ ์ ํํด์ผ ํ๋ ์ด์ ๋ ๋ฌด์์ ๋๊น?
'lit-localize' ๋๋ 'i18next'์ ๊ฐ์ ์ฃผ์ ์๋ฃจ์ ๊ณผ ๋น๊ตํ ๋ Intlayer๋ ๋ค์๊ณผ ๊ฐ์ ํตํฉ ์ต์ ํ๊ฐ ์ ๊ณต๋๋ ์๋ฃจ์ ์ ๋๋ค.
Intlayer๋ ์น ๊ตฌ์ฑ์์ ์์ค ์ฝํ ์ธ ๋ฒ์ ์ง์ , TypeScript ์ง์ ๋ฐ ๊ตญ์ ํ ํ์ฅ(i18n)์ ํ์ํ ๋ชจ๋ ๊ธฐ๋ฅ์ ์ ๊ณตํ์ฌ Lit์ ์๋ฒฝํ๊ฒ ์๋ํ๋๋ก ์ต์ ํ๋์์ต๋๋ค.
๋์ฉ๋ JSON ํ์ผ์ ํ์ด์ง์ ๋ก๋ํ๋ ๋์ ํ์ํ ์ฝํ ์ธ ๋ง ๋ก๋ํ์ธ์. Intlayer๋ ๋ฒ๋ค ๋ฐ ํ์ด์ง ํฌ๊ธฐ๋ฅผ ์ต๋ 50% ์ค์ด๋ ๋ฐ ๋์์ด ๋ฉ๋๋ค.
์ ํ๋ฆฌ์ผ์ด์ ์ฝํ ์ธ ์ ๋ฒ์๋ฅผ ์ง์ ํ๋ฉด ๋๊ท๋ชจ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ ์ง ๊ด๋ฆฌ๊ฐ ์ฉ์ดํด์ง๋๋ค. ์ ์ฒด ์ฝํ ์ธ ์ฝ๋๋ฒ ์ด์ค๋ฅผ ๊ฒํ ํด์ผ ํ๋ ์ ์ ์ ๋ถ๋ด ์์ด ๋จ์ผ ๊ธฐ๋ฅ ํด๋๋ฅผ ๋ณต์ ํ๊ฑฐ๋ ์ญ์ ํ ์ ์์ต๋๋ค. ๋ํ Intlayer๋ ์์ ํ ์ ํ๋์ด ์ฝํ ์ธ ์ ์ ํ์ฑ์ ๋ณด์ฅํฉ๋๋ค.
์ฝํ ์ธ ๋ฅผ ๊ฐ์ ์์น์ ๋ฐฐ์นํ๋ฉด LLM(๋ํ ์ธ์ด ๋ชจ๋ธ)์ ํ์ํ ์ปจํ ์คํธ๊ฐ ์ค์ด๋ญ๋๋ค. Intlayer์๋ ๋๋ฝ๋ ๋ฒ์ญ์ ํ ์คํธํ๊ธฐ ์ํ CLI, LSP, MCP ๋ฐ agent skills, AI ์์ด์ ํธ๋ฅผ ์ํ ๊ฐ๋ฐ์ ๊ฒฝํ(DX)์ ๋์ฑ ์ํํ๊ฒ ๋ง๋ญ๋๋ค.
AI ๊ณต๊ธ์์ ๋น์ฉ์ผ๋ก ์ ํํ LLM์ ์ฌ์ฉํ์ฌ CI/CD ํ์ดํ๋ผ์ธ์ ๋ฒ์ญํ๋ ค๋ฉด ์๋ํ๋ฅผ ์ฌ์ฉํ์ธ์. Intlayer๋ ๋ํ ์ฝํ ์ธ ์ถ์ถ์ ์๋ํํ๋ ์ปดํ์ผ๋ฌ์ ๋ฐฑ๊ทธ๋ผ์ด๋์์ ๋ฒ์ญ์ ๋๋ ์น ํ๋ซํผ์ ์ ๊ณตํฉ๋๋ค.
๋๊ท๋ชจ JSON ํ์ผ์ ๊ตฌ์ฑ ์์์ ์ฐ๊ฒฐํ๋ฉด ์ฑ๋ฅ ๋ฐ ๋ฐ์์ฑ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์์ต๋๋ค. Intlayer๋ ๋น๋ ์ ์ฝํ ์ธ ๋ก๋ฉ์ ์ต์ ํํฉ๋๋ค.
Intlayer๋ ๋จ์ํ i18n ์๋ฃจ์ ๊ทธ ์ด์์ผ๋ก ๊ด๋ฆฌ์ ๋์์ด ๋๋ ์์ฒด ํธ์คํ ๋น์ฃผ์ผ ํธ์ง๊ธฐ ๋ฐ ์ ์ฒด CMS๋ฅผ ์ ๊ณตํฉ๋๋ค. ๋ค๊ตญ์ด ์ฝํ ์ธ ๋ฅผ ์ค์๊ฐ์ผ๋ก ์ ๊ณตํ์ฌ ๋ฒ์ญ๊ฐ, ์นดํผ๋ผ์ดํฐ, ๊ธฐํ ํ์๊ณผ ์ํํ๊ฒ ํ์ ํ ์ ์์ต๋๋ค. ์ฝํ ์ธ ๋ ๋ก์ปฌ ๋ฐ/๋๋ ์๊ฒฉ์ผ๋ก ์ ์ฅ๋ ์ ์์ต๋๋ค.
GitHub์์ Application Template์ ์ฐธ๊ณ ํ์ธ์.
Vite ๋ฐ Lit ์ ํ๋ฆฌ์ผ์ด์ ์์ Intlayer๋ฅผ ์ค์ ํ๊ธฐ ์ํ ๋จ๊ณ๋ณ ๊ฐ์ด๋
์ข ์์ฑ ์ค์น
npm์ ์ฌ์ฉํ์ฌ ํ์ํ ํจํค์ง๋ฅผ ์ค์นํฉ๋๋ค:
bash์ฝ๋ ๋ณต์ฌ์ฝ๋๋ฅผ ํด๋ฆฝ๋ณด๋์ ๋ณต์ฌ
--interactiveํ๋๊ทธ๋ ์ ํ ์ฌํญ์ ๋๋ค. AI ์์ด์ ํธ์ธ ๊ฒฝ์ฐintlayer-cli init๋ฅผ ์ฌ์ฉํ์ธ์.์ด ๋ช ๋ น์ ํ๊ฒฝ์ ๊ฐ์งํ๊ณ ํ์ํ ํจํค์ง๋ฅผ ์ค์นํฉ๋๋ค. ์๋ฅผ ๋ค์ด:
bash์ฝ๋ ๋ณต์ฌ์ฝ๋๋ฅผ ํด๋ฆฝ๋ณด๋์ ๋ณต์ฌ
intlayer
๊ตฌ์ฑ ๊ด๋ฆฌ, ๋ฒ์ญ, ์ฝํ ์ธ ์ ์ธ, ํธ๋์คํ์ผ ๋ฐ CLI ๋ช ๋ น์ ์ํ ๊ตญ์ ํ ๋๊ตฌ๋ฅผ ์ ๊ณตํ๋ ํต์ฌ ํจํค์ง์ ๋๋ค.
lit-intlayer Intlayer๋ฅผ Lit ์ ํ๋ฆฌ์ผ์ด์ ๊ณผ ํตํฉํ๋ ํจํค์ง์ ๋๋ค.
ReactiveController๊ธฐ๋ฐ ํ (useIntlayer,useLocale๋ฑ)์ ์ ๊ณตํ์ฌ ๋ก์ผ์ผ์ด ๋ณ๊ฒฝ๋ ๋ LitElement๊ฐ ์๋์ผ๋ก ๋ค์ ๋ ๋๋ง๋๋๋ก ํฉ๋๋ค.vite-intlayer Intlayer๋ฅผ Vite ๋ฒ๋ค๋ฌ์ ํตํฉํ๊ธฐ ์ํ Vite ํ๋ฌ๊ทธ์ธ๊ณผ ์ฌ์ฉ์์ ์ ํธ ๋ก์ผ์ผ ๊ฐ์ง, ์ฟ ํค ๊ด๋ฆฌ ๋ฐ URL ๋ฆฌ๋๋ ์ ์ฒ๋ฆฌ๋ฅผ ์ํ ๋ฏธ๋ค์จ์ด๊ฐ ํฌํจ๋์ด ์์ต๋๋ค.
ํ๋ก์ ํธ ๊ตฌ์ฑ
์ ํ๋ฆฌ์ผ์ด์ ์ ์ธ์ด๋ฅผ ๊ตฌ์ฑํ๊ธฐ ์ํ ์ค์ ํ์ผ์ ๋ง๋ญ๋๋ค:
intlayer.config.ts์ฝ๋ ๋ณต์ฌ์ฝ๋๋ฅผ ํด๋ฆฝ๋ณด๋์ ๋ณต์ฌ
import { Locales, type IntlayerConfig } from "intlayer"; const config: IntlayerConfig = { internationalization: { locales: [ Locales.ENGLISH, Locales.FRENCH, Locales.SPANISH, // ๊ธฐํ ๋ก์ผ์ผ ], defaultLocale: Locales.ENGLISH, }, }; export default config;์ด ๊ตฌ์ฑ ํ์ผ์ ํตํด ๋ก์ปฌ๋ผ์ด์ฆ๋ URL, ๋ฏธ๋ค์จ์ด ๋ฆฌ๋๋ ์ , ์ฟ ํค ์ด๋ฆ, ์ฝํ ์ธ ์ ์ธ ์์น ๋ฐ ํ์ฅ์, ์ฝ์ ๋ด Intlayer ๋ก๊ทธ ๋นํ์ฑํ ๋ฑ์ ์ค์ ํ ์ ์์ต๋๋ค. ์ฌ์ฉ ๊ฐ๋ฅํ ๋งค๊ฐ๋ณ์์ ์ ์ฒด ๋ชฉ๋ก์ ๊ตฌ์ฑ ๋ฌธ์๋ฅผ ์ฐธ์กฐํ์ธ์.
Vite ๊ตฌ์ฑ์ Intlayer ํตํฉ
Vite ๊ตฌ์ฑ์ intlayer ํ๋ฌ๊ทธ์ธ์ ์ถ๊ฐํฉ๋๋ค.
vite.config.ts์ฝ๋ ๋ณต์ฌ์ฝ๋๋ฅผ ํด๋ฆฝ๋ณด๋์ ๋ณต์ฌ
import { defineConfig } from "vite"; import { intlayer } from "vite-intlayer"; // https://vitejs.dev/config/ export default defineConfig({ plugins: [ intlayer({ proxy: { ignore: (req) => req.url?.startsWith("/api"), }, }), ], });intlayer()Vite ํ๋ฌ๊ทธ์ธ์ Intlayer๋ฅผ Vite์ ํตํฉํ๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค. ์ฝํ ์ธ ์ ์ธ ํ์ผ์ ๋น๋๋ฅผ ๋ณด์ฅํ๊ณ ๊ฐ๋ฐ ๋ชจ๋์์ ์ด๋ฅผ ๋ชจ๋ํฐ๋งํฉ๋๋ค. Vite ์ ํ๋ฆฌ์ผ์ด์ ๋ด์ Intlayer ํ๊ฒฝ ๋ณ์๋ฅผ ์ ์ํฉ๋๋ค. ๋ํ ์ฑ๋ฅ ์ต์ ํ๋ฅผ ์ํ ๋ณ์นญ์ ์ ๊ณตํฉ๋๋ค.์ํธ๋ฆฌ ํฌ์ธํธ์์ Intlayer ๋ถํธ์คํธ๋ฉ
์ฒซ ๋ฒ์งธ ์๋ฆฌ๋จผํธ๊ฐ ์ฐ๊ฒฐ๋ ๋ ๊ธ๋ก๋ฒ ๋ก์ผ์ผ ์ฑ๊ธํค์ด ์ค๋น๋๋๋ก ์ปค์คํ ์๋ฆฌ๋จผํธ๊ฐ ๋ฑ๋ก๋๊ธฐ ์ ์
installIntlayer()๋ฅผ ํธ์ถํ์ธ์.src/main.ts์ฝ๋ ๋ณต์ฌ์ฝ๋๋ฅผ ํด๋ฆฝ๋ณด๋์ ๋ณต์ฌ
md()์ฝํ ์ธ ์ ์ธ(Markdown)๋ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ ๋งํฌ๋ค์ด ๋ ๋๋ฌ๋ ์ค์นํ์ธ์:src/main.ts์ฝ๋ ๋ณต์ฌ์ฝ๋๋ฅผ ํด๋ฆฝ๋ณด๋์ ๋ณต์ฌ
์ฝํ ์ธ ์ ์ธ
๋ฒ์ญ์ ์ ์ฅํ๊ธฐ ์ํ ์ฝํ ์ธ ์ ์ธ์ ์์ฑํ๊ณ ๊ด๋ฆฌํฉ๋๋ค:
src/app.content.ts์ฝ๋ ๋ณต์ฌ์ฝ๋๋ฅผ ํด๋ฆฝ๋ณด๋์ ๋ณต์ฌ
import { t, type Dictionary } from "intlayer"; const appContent = { key: "app", content: { title: "Vite + Lit", viteLogo: t({ en: "Vite logo", fr: "Logo Vite", es: "Logo Vite", }), litLogo: t({ en: "Lit logo", fr: "Logo Lit", es: "Logo Lit", }), count: t({ en: "count is {{count}}", fr: "le compte est {{count}}", es: "el recuento es {{count}}", }), readTheDocs: t({ en: "Click on the Vite and Lit logos to learn more", fr: "Cliquez sur les logos Vite et Lit pour en savoir plus", es: "Haga clic en los logotipos de Vite y Lit para obtener mรกs informaciรณn", }), }, } satisfies Dictionary; export default appContent;์ฝํ ์ธ ์ ์ธ์
contentDir๋๋ ํ ๋ฆฌ(๊ธฐ๋ณธ๊ฐ./src)์ ํฌํจ๋๊ณ ์ฝํ ์ธ ์ ์ธ ํ์ผ ํ์ฅ์(๊ธฐ๋ณธ๊ฐ.content.{json,ts,tsx,js,jsx,mjs,cjs,md,mdx,yaml,yml})์ ์ผ์นํ๋ ๊ฒฝ์ฐ ์ ํ๋ฆฌ์ผ์ด์ ์ด๋์์๋ ์ ์ํ ์ ์์ต๋๋ค.์์ธํ ๋ด์ฉ์ ์ฝํ ์ธ ์ ์ธ ๋ฌธ์๋ฅผ ์ฐธ์กฐํ์ธ์.
LitElement์์ Intlayer ํ์ฉ
LitElement๋ด์์useIntlayer๋ฅผ ์ฌ์ฉํฉ๋๋ค. ํ์ฑ ๋ก์ผ์ผ์ด ๋ณ๊ฒฝ๋ ๋๋ง๋ค ์๋์ผ๋ก ๋ค์ ๋ ๋๋ง์ ํธ๋ฆฌ๊ฑฐํ๋ReactiveControllerํ๋ก์๋ฅผ ๋ฐํํ๋ฉฐ ๋ณ๋์ ์ค์ ์ด ํ์ํ์ง ์์ต๋๋ค.src/my-element.ts์ฝ๋ ๋ณต์ฌ์ฝ๋๋ฅผ ํด๋ฆฝ๋ณด๋์ ๋ณต์ฌ
๋ค์ดํฐ๋ธ HTML ์์ฑ(์:
alt,aria-label,title)์์ ๋ฒ์ญ๋ ๋ฌธ์์ด์ด ํ์ํ ๊ฒฝ์ฐ ๋ฆฌํ ๋ ธ๋์์.value๋ฅผ ํธ์ถํ์ธ์:typescript์ฝ๋ ๋ณต์ฌ์ฝ๋๋ฅผ ํด๋ฆฝ๋ณด๋์ ๋ณต์ฌ
์ฝํ ์ธ ์ธ์ด ๋ณ๊ฒฝ
์ ํ์ฌํญ์ฝํ ์ธ ์ธ์ด๋ฅผ ๋ณ๊ฒฝํ๋ ค๋ฉด
useLocale์ปจํธ๋กค๋ฌ์์ ๋ ธ์ถํ๋setLocale๋ฉ์๋๋ฅผ ์ฌ์ฉํฉ๋๋ค.src/locale-switcher.ts์ฝ๋ ๋ณต์ฌ์ฝ๋๋ฅผ ํด๋ฆฝ๋ณด๋์ ๋ณต์ฌ
๋งํฌ๋ค์ด ๋ฐ HTML ์ฝํ ์ธ ๋ ๋๋ง
์ ํ์ฌํญIntlayer๋
md()๋ฐhtml()์ฝํ ์ธ ์ ์ธ์ ์ง์ํฉ๋๋ค. Lit์์ ์ปดํ์ผ๋ ์ถ๋ ฅ์unsafeHTML๋๋ ํฐ๋ธ๋ฅผ ํตํด ์์ HTML๋ก ์ฝ์ ๋ฉ๋๋ค.์๋ฆฌ๋จผํธ์์ ์ปดํ์ผ๋ HTML์ ๋ ๋๋งํฉ๋๋ค:
src/my-element.ts์ฝ๋ ๋ณต์ฌ์ฝ๋๋ฅผ ํด๋ฆฝ๋ณด๋์ ๋ณต์ฌ
TIP String(content.editNote)๋ ์์ ๋งํฌ๋ค์ด ๋ฌธ์์ด์ ๋ฐํํ๋IntlayerNode์toString()์ ํธ์ถํฉ๋๋ค. ์ด๋ฅผcompileMarkdown์ ์ ๋ฌํ์ฌ HTML ๋ฌธ์์ด์ ๊ฐ์ ธ์จ ๋ค์ Lit์unsafeHTML๋๋ ํฐ๋ธ๋ก ๋ ๋๋งํฉ๋๋ค.์ ํ๋ฆฌ์ผ์ด์ ์ ๋ก์ปฌ๋ผ์ด์ฆ๋ ๋ผ์ฐํ ์ถ๊ฐ
์ ํ์ฌํญ๊ฐ ์ธ์ด์ ๋ํด ๊ณ ์ ํ ๋ผ์ฐํธ๋ฅผ ๋ง๋ค๋ ค๋ฉด(SEO์ ์ ์ฉ) Intlayer์
localeMap/localeFlatMapํฌํผ์ ์๋ฒ ์ธก ๋ก์ผ์ผ ๊ฐ์ง๋ฅผ ์ํintlayerProxyVite ํ๋ฌ๊ทธ์ธ๊ณผ ํจ๊ป ํด๋ผ์ด์ธํธ ์ธก ๋ผ์ฐํฐ๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.๋จผ์ Vite ๊ตฌ์ฑ์
intlayerProxy๋ฅผ ์ถ๊ฐํฉ๋๋ค:ํ๋ก๋์ ์์
intlayerProxy๋ฅผ ์ฌ์ฉํ๋ ค๋ฉดvite-intlayer๋ฅผdevDependencies์์dependencies๋ก ์ด๋ํด์ผ ํฉ๋๋ค.Intlayer v9๋ถํฐ
intlayerProxy()๋intlayer()ํ๋ฌ๊ทธ์ธ์ ์ง์ ๋ฒ๋ค๋ก ์ ๊ณต๋๋ฉฐrouting.enableProxy์ต์ (true์ด ๊ธฐ๋ณธ๊ฐ)์ ํตํด ๊ธฐ๋ณธ์ ์ผ๋ก ํ์ฑํ๋ฉ๋๋ค. ์๋์ ํ์๋ ๋๋ก ๋ณ๋๋ก ๋ฑ๋กํ๋ ๊ฒ์ ์ด์ ์ ํ ์ฌํญ์ ๋๋ค โ ์ด์ ๋ฒ์ ๊ณผ์ ํธํ์ฑ ๋ฐ ํ๋ฌ๊ทธ์ธ ์์๋ฅผ ์ ์ดํด์ผ ํ๋ ์ค์ ์ ์ํด ์ ์ง๋ฉ๋๋ค.routing.enableProxy: false๋ก ์ค์ ํ์ฌ ์ตํธ์์ํ ์ ์์ต๋๋ค. v9 ๋ฆด๋ฆฌ์ค ๋ ธํธ๋ฅผ ์ฐธ์กฐํ์ธ์.vite.config.ts์ฝ๋ ๋ณต์ฌ์ฝ๋๋ฅผ ํด๋ฆฝ๋ณด๋์ ๋ณต์ฌ
import { defineConfig } from "vite"; import { intlayer } from "vite-intlayer"; export default defineConfig({ plugins: [ intlayer({ proxy: { ignore: (req) => req.url?.startsWith("/api"), }, }), ], });๋ก์ผ์ผ ๋ณ๊ฒฝ ์ URL ๋ณ๊ฒฝ
์ ํ์ฌํญ๋ก์ผ์ผ์ด ๋ณ๊ฒฝ๋ ๋ ๋ธ๋ผ์ฐ์ URL์ ์ ๋ฐ์ดํธํ๋ ค๋ฉด ๋ก์ผ์ผ ์ค์์ฒ์ ํจ๊ป
useRewriteURL์ ์ฌ์ฉํฉ๋๋ค:src/locale-switcher.ts์ฝ๋ ๋ณต์ฌ์ฝ๋๋ฅผ ํด๋ฆฝ๋ณด๋์ ๋ณต์ฌ
HTML ์ธ์ด ๋ฐ ๋ฐฉํฅ ์์ฑ ์ ํ
์ ํ์ฌํญ์ ๊ทผ์ฑ ๋ฐ SEO๋ฅผ ์ํด ํ์ฌ ๋ก์ผ์ผ์ ๋ง๊ฒ
<html>ํ๊ทธ์lang๋ฐdir์์ฑ์ ์ ๋ฐ์ดํธํฉ๋๋ค.src/my-element.ts์ฝ๋ ๋ณต์ฌ์ฝ๋๋ฅผ ํด๋ฆฝ๋ณด๋์ ๋ณต์ฌ
์ปดํฌ๋ํธ์ ์ฝํ ์ธ ์ถ์ถ
์ ํ์ฌํญ๊ธฐ์กด ์ฝ๋๋ฒ ์ด์ค๊ฐ ์๋ ๊ฒฝ์ฐ ์์ฒ ๊ฐ์ ํ์ผ์ ๋ณํํ๋ ๋ฐ ์๊ฐ์ด ๋ง์ด ๊ฑธ๋ฆด ์ ์์ต๋๋ค.
์ด ํ๋ก์ธ์ค๋ฅผ ์ฝ๊ฒ ํ๊ธฐ ์ํด Intlayer๋ ์ปดํฌ๋ํธ๋ฅผ ๋ณํํ๊ณ ์ฝํ ์ธ ๋ฅผ ์ถ์ถํ๋ ์ปดํ์ผ๋ฌ / ์ถ์ถ๊ธฐ๋ฅผ ์ ์ํฉ๋๋ค.
์ค์ ํ๋ ค๋ฉด
intlayer.config.tsํ์ผ์compiler์น์ ์ ์ถ๊ฐํ๋ฉด ๋ฉ๋๋ค:intlayer.config.ts์ฝ๋ ๋ณต์ฌ์ฝ๋๋ฅผ ํด๋ฆฝ๋ณด๋์ ๋ณต์ฌ
(์ ํ) ์ฌ์ดํธ๋งต๊ณผ robots.txt(๋น๋ ์ ์์ฑ)
Intlayer๋ generateSitemap๊ณผ getMultilingualUrls๋ก ํฌ๋กค๋ฌ์ฉ ๋ค๊ตญ์ด sitemap.xml๊ณผ robots.txt๋ฅผ ๋ง๋ค์ด public/์ ์๋์ผ๋ก ์ธ ์ ์์ต๋๋ค. ๋ณดํต Vite ์คํ ์ ์ ์์ Node ์คํฌ๋ฆฝํธ๋ฅผ ๋๋ฆฝ๋๋ค(์: npm predev / prebuild).
์ฌ์ดํธ๋งต
Intlayer ์ฌ์ดํธ๋งต ์์ฑ๊ธฐ๋ ๋ก์ผ์ผ ์ค์ ์ ๋ฐ์ํ๊ณ ํฌ๋กค๋ฌ์ฉ ๋ฉํ๋ฐ์ดํฐ๋ฅผ ํฌํจํฉ๋๋ค.
์์ฑ๋ ์ฌ์ดํธ๋งต์xhtml:link(hreflang)๋ฅผ ์ง์ํฉ๋๋ค. ๋จ์ URL ๋์ด์ด ์๋๋ผ ๊ฐ ํ์ด์ง์ ๋ชจ๋ ์ธ์ด ๋ฒ์ ์ ์๋ฐฉํฅ์ผ๋ก ์ฐ๊ฒฐํฉ๋๋ค(์:/about,/fr/about,/about?lang=fr- ๋ผ์ฐํ ๋ชจ๋์ ๋ฐ๋ฆ).
Robots.txt
getMultilingualUrls๋ก Disallow๊ฐ ๋ฏผ๊ฐ ๊ฒฝ๋ก์ ๋ชจ๋ ํ์งํ ๋ณํ์ ์ ์ฉ๋๋๋ก ํ์ธ์.
1. ํ๋ก์ ํธ ๋ฃจํธ์ generate-seo.mjs ์ถ๊ฐ
์ฝ๋๋ฅผ ํด๋ฆฝ๋ณด๋์ ๋ณต์ฌ
์คํฌ๋ฆฝํธ๊ฐ intlayer๋ฅผ importํ๋ ค๋ฉด ํจํค์ง๊ฐ ์ค์น๋์ด ์์ด์ผ ํฉ๋๋ค. ํ๋ก๋์
์์๋ ํ๊ฒฝ ๋ณ์ SITE_URL์ ์ค์ ํ์ธ์(CI ๋ฑ).
Node ESM์๋generate-seo.mjs์ฌ์ฉ์ ๊ถ์ฅํฉ๋๋ค.generate-seo.js๋ฅผ ์ฐ๋ฉดpackage.json์"type": "module"๋ฑ์ผ๋ก ESM์ ์ผ์ธ์.
2. Vite ์ ์ ์คํฌ๋ฆฝํธ ์คํ
์ฝ๋๋ฅผ ํด๋ฆฝ๋ณด๋์ ๋ณต์ฌ
pnpm์ด๋ yarn์ ์ฐ๋ฉด ๋ช ๋ น์ ๋ง๊ฒ ๋ฐ๊พธ์ธ์. CI์์ ํธ์ถํด๋ ๋ฉ๋๋ค.
TypeScript ๊ตฌ์ฑ
TypeScript ๊ตฌ์ฑ์ ์๋ ์์ฑ๋ ํ์ ์ด ํฌํจ๋์ด ์๋์ง ํ์ธํ์ธ์.
์ฝ๋๋ฅผ ํด๋ฆฝ๋ณด๋์ ๋ณต์ฌ
experimentalDecorators๋ฐuseDefineForClassFields: false๋ Lit์์ ๋ฐ์ฝ๋ ์ดํฐ ์ง์์ ์ํด ํ์ํฉ๋๋ค.
Git ๊ตฌ์ฑ
Intlayer์์ ์์ฑ๋ ํ์ผ์ ๋ฌด์ํ๋ ๊ฒ์ด ์ข์ต๋๋ค. ์ด๋ ๊ฒ ํ๋ฉด Git ์ ์ฅ์์ ์ปค๋ฐํ๋ ๊ฒ์ ๋ฐฉ์งํ ์ ์์ต๋๋ค.
์ด๋ ๊ฒ ํ๋ ค๋ฉด .gitignore ํ์ผ์ ๋ค์ ์ง์นจ์ ์ถ๊ฐํ๋ฉด ๋ฉ๋๋ค:
์ฝ๋๋ฅผ ํด๋ฆฝ๋ณด๋์ ๋ณต์ฌ
VS Code ํ์ฅ ํ๋ก๊ทธ๋จ
Intlayer ๊ฐ๋ฐ ํ๊ฒฝ์ ๊ฐ์ ํ๊ธฐ ์ํด ๊ณต์ Intlayer VS Code Extension์ ์ค์นํ ์ ์์ต๋๋ค.
VS Code Marketplace์์ ์ค์น
์ด ํ์ฅ ํ๋ก๊ทธ๋จ์ ๋ค์์ ์ ๊ณตํฉ๋๋ค:
- ๋ฒ์ญ ํค์ ๋ํ ์๋ ์์ฑ.
- ๋๋ฝ๋ ๋ฒ์ญ์ ๋ํ ์ค์๊ฐ ์ค๋ฅ ๊ฐ์ง.
- ๋ฒ์ญ๋ ์ฝํ ์ธ ์ ์ธ๋ผ์ธ ๋ฏธ๋ฆฌ๋ณด๊ธฐ.
- ๋ฒ์ญ์ ์ฝ๊ฒ ์์ฑํ๊ณ ์ ๋ฐ์ดํธํ ์ ์๋ ๋น ๋ฅธ ์์ .
ํ์ฅ ํ๋ก๊ทธ๋จ ์ฌ์ฉ ๋ฐฉ๋ฒ์ ๋ํ ์์ธํ ๋ด์ฉ์ Intlayer VS Code Extension ๋ฌธ์๋ฅผ ์ฐธ์กฐํ์ธ์.
๋ ์์๋ณด๊ธฐ
๋ ์์ธํ ์์๋ณด๋ ค๋ฉด ๋น์ฃผ์ผ ์๋ํฐ๋ฅผ ๊ตฌํํ๊ฑฐ๋ CMS๋ฅผ ์ฌ์ฉํ์ฌ ์ฝํ ์ธ ๋ฅผ ์ธ๋ถํํ ์ ์์ต๋๋ค.
์์ฃผ ๋ฌป๋ ์ง๋ฌธ
Vite๋ i18n์ ๋ํด ํน์ ๋ฐฉ์์ ๊ฐ์ ํ์ง ์์ผ๋ฏ๋ก Lit ์ํ๊ณ์์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ ํํฉ๋๋ค:
@lit/localize: XLIFF ์ถ์ถ๊ณผ ๋ก์ผ์ผ๋น ํ๋์ ์ปดํ์ผ๋ ๋ฒ๋ค์ ๊ธฐ๋ฐ์ผ๋ก ํ๋ ๊ณต์ ์ต์ ์ด๋ฏ๋ก, ๋ฐํ์์ ์ธ์ด๋ฅผ ์ ํํ๋ ค๋ฉด ๋ฐํ์ ๋ชจ๋๊ฐ ํ์ํฉ๋๋ค.- ์๊ธฐ ์์ฑ ์ฌ์ (Hand-written dictionary): ๊ฐ ์์๋ก import๋๋ ๋ฐฉ์์ด์ง๋ง ์์กด์ฑ์ด ์๋ ๋์ ํ์ ์์ ์ฑ๊ณผ ๋๊ตฌ๊ฐ ๋ถ์กฑํฉ๋๋ค.
Intlayer: ๊ฐ์ฅ ์ง๋ณด๋ ์๋ฃจ์ ์ ๋๋ค. ์ฝ๋๋ฒ ์ด์ค ์ด๋์๋ ์ฝํ ์ธ ๋ฅผ ์ ์ธํ ์ ์์ผ๋ฉฐ(๊ฐ ์ปดํฌ๋ํธ ์ ๋๋ ์ค์ ์ง์ค์), ๋น๋ ํ์์ Vite ํ๋ฌ๊ทธ์ธ์ ์ํด ์ปดํ์ผ๋๊ณ , ์์ ํ ํ์ ์์ ์ฑ์ ์ ๊ณตํ๋ฉฐ AI ๋ฒ์ญ, ๋น์ฃผ์ผ ์๋ํฐ ๋ฐ CMS๋ฅผ ํฌํจํฉ๋๋ค.
Vite ํนํ๋ ์ฅ์ ์ ๋ฒ์ญ์ด ๋ฐํ์์ JSON์ผ๋ก ๊ฐ์ ธ์ค๋ ๋์ ์ปดํ์ผ ํ์์ ํ์ธ๋๊ณ ํธ๋ฆฌ ์์ดํน(tree-shaking)๋๋ฏ๋ก ํ์ด์ง๊ฐ ๋ ๋๋งํ๋ ํญ๋ชฉ๋ง ์ ํํ ์ ๋ฌ๋๋ค๋ ์ ์ ๋๋ค. ์ Intlayer์ธ๊ฐ์ ๋ฒค์น๋งํฌ๋ฅผ ์ฐธ์กฐํ์ธ์.
๋ค์์คํ์ด์ค ๊ธฐ๋ฐ ์ค์ ๋ณด๋ค ํจ์ฌ ์ ์ต๋๋ค. ํ์ด์ง๋ ๋ ๋๋งํ์ง ์๋ ์ธ์ด์ ์นดํ๋ก๊ทธ๋ฅผ ์ ๋ ๋ค์ด๋ก๋ํ์ง ์๊ธฐ ๋๋ฌธ์
๋๋ค. ๋น๋ ํ์ ์ปดํ์ผ๋ฌ๋ useIntlayer ํธ์ถ์ ์ปดํฌ๋ํธ๊ฐ ์ฌ์ฉํ๋ ์ ํํ ์ฌ์ ํญ๋ชฉ์ผ๋ก ๋์ฒดํ๋ฏ๋ก ์ฌ์ฉ๋์ง ์๋ ํค์ ์ธ์ด๋ ์ ๊ฑฐ๋๊ณ , ๋์ ์ฌ์ ์ ํตํด ๋ก์ผ์ผ๋ณ๋ก ๋ถํ ๋ฉ๋๋ค. ์ผ๋ฐ์ ์ธ ๋์๋ค๊ณผ ๋น๊ตํ์ ๋ Intlayer๋ ๋ฒ๋ค ๋ฐ ํ์ด์ง ํฌ๊ธฐ๋ฅผ ์ต๋ 50%๊น์ง ์ค์ฌ์ค๋๋ค. ๋ฒ๋ค ์ต์ ํ์ ๋ฒค์น๋งํฌ๋ฅผ ์ฐธ์กฐํ์ธ์.
๋๋ถ๋ถ ๊ฐ๋ฅํฉ๋๋ค. ์ฝํ ์ธ ๋ฅผ ์ด์ ํ๋ ค๋ฉด ํธํ ์ด๋ํฐ(compat adapters) ๊ฐ์๋ฅผ ๋ฐ๋ฅด์ธ์. ์ ์ง์ ์ผ๋ก ๋ง์ด๊ทธ๋ ์ด์ ํ ์๋ ์์ต๋๋ค: sync JSON ํ๋ฌ๊ทธ์ธ์ ๊ธฐ์กด JSON ์นดํ๋ก๊ทธ๋ฅผ ๋จ์ผ ์ง์ค ๊ณต๊ธ์(source of truth)์ผ๋ก ์ ์งํ๋ฉด์ Intlayer ์ฌ์ ์ ์์ฑํ๋ฏ๋ก ์ปดํฌ๋ํธ๋ฅผ ํ๋์ฉ ์ด์ ํ๋ ๋์ ๋ ๊ณ์ธต์ ๋๊ธฐํ๊ฐ ์ ์ง๋ฉ๋๋ค.
๋ค. sync JSON ํ๋ฌ๊ทธ์ธ์ /messages/{locale}/{namespace}.json ํ์ผ์ ๋จ์ผ ์ง์ค ๊ณต๊ธ์(source of truth)์ผ๋ก ์ ์งํ๋ฉด์ ์๋ฐฉํฅ์ผ๋ก Intlayer ์ฌ์ ์ ์์ฑํฉ๋๋ค. sync PO ํ๋ฌ๊ทธ์ธ์ gettext ์นดํ๋ก๊ทธ์ ๋ํด ๋์ผํ ์์
์ ์ํํ๋ฉฐ, ๋ก์ผ์ผ๋ณ ํ์ผ์ ํตํด ๋ก์ผ์ผ์ ํ ํ์ผ์ ๋ชจ์ผ๋ ๋์ ์ธ์ด๋ณ๋ก ์ฝํ
์ธ ๋ฅผ ๋ถํ ํ ์๋ ์์ต๋๋ค.
์๋๋๋ค. npx intlayer extract๋ฅผ ์คํํ๋ฉด Intlayer๊ฐ ์ปดํฌ๋ํธ๋ฅผ ์ฝ๊ณ ์ฌ์ฉ์ ๋๋ฉด ๋ฌธ์์ด์ ์ถ์ถํ์ฌ ๊ฐ ์ปดํฌ๋ํธ ์์ .content ํ์ผ์ ์์ฑํ๋ฏ๋ก ์นดํ๋ก๊ทธ์ ์ผ์ผ์ด ๋ณต์ฌํ ํ์ ์์ด diff๋ง ๊ฒํ ํ๋ฉด ๋ฉ๋๋ค. ์ด ๊ฐ์ด๋์ 12๋จ๊ณ๋ฅผ ํ์ธํ์ธ์.
์์ ์๋ํ๋ ํ์ดํ๋ผ์ธ์ ์ํด Intlayer ์ปดํ์ผ๋ฌ๋ ๋น๋ ํ์์ JSX, TSX, Vue ๋ฐ Svelte ์์ค์์ ๋์ผํ ์์ ์ ์ํํ์ฌ ๋ณ๊ฒฝ๋ ๋๋ง๋ค ์ฌ์ ์ ์์ฑํ๊ณ HMR์ ํตํด ๋๊ธฐํํ๋ฏ๋ก ์๋์ผ๋ก ํค๋ฅผ ๊ด๋ฆฌํ ํ์๊ฐ ์์ต๋๋ค.
์ปดํ์ผ๋ฌ๋ฅผ ์ผ๊ธฐ ์ ์ ์์๋์ด์ผ ํ ๋ ๊ฐ์ง ์ ํ ์ฌํญ์ด ์์ต๋๋ค. ์ ์ ๋ถ์์ผ๋ก ์๋ํ๋ฏ๋ก API ์ค๋ฅ ์ฝ๋๋ CMS ํ๋์ ๊ฐ์ด ๋ฐํ์์๋ง ์กด์ฌํ๋ ๋ฌธ์์ด์ ์ฒ๋ฆฌํ ์ ์์ต๋๋ค. ๋ํ ํฐ ์ฝ๋๋ฒ ์ด์ค์์ ๋ช ๊ฐ์ง ์ด๋
ธํ
์ด์
์ด ํ์ํ className="active" ๋๋ ์ํ ์ฝ๋์ ๊ฐ์ ์ ํ๋ฆฌ์ผ์ด์
๋ก์ง๊ณผ ์ฌ์ฉ์ ๋๋ฉด ํ
์คํธ๋ฅผ ๊ตฌ๋ถํด์ผ ํฉ๋๋ค. extract ๋ช
๋ น์ ์ฌ์ฉ์๊ฐ ์ง์ ์ ์ดํ ์ ์๋๋ก ํ์ฌ ๋ ๊ฐ์ง ๋ฌธ์ ๋ฅผ ๋ชจ๋ ๋ฐฉ์งํฉ๋๋ค.
5๊ฐ์ง ๋๊ตฌ๊ฐ ๋ชจ๋ ์ ํ ์ฌํญ์ผ๋ก ์ ๊ณต๋ฉ๋๋ค:
- VS Code ํ์ฅ ํ๋ก๊ทธ๋จ:
useIntlayerํค์์ ์ด๋ฅผ ์ ์ธํ ์ฝํ ์ธ ํ์ผ๋ก ๋ฐ๋ก ์ด๋ํ๊ณ , ์ปดํฌ๋ํธ์์ ์ฝํ ์ธ ๋ฅผ ์ถ์ถํ๋ฉฐ, ๋ช ๋ น ํ๋ ํธ๋ ์ ์ฉ Intlayer ํญ์์ build, fill, test, push, pull์ ์คํํ ์ ์์ต๋๋ค. - LSP ์๋ฒ: LSP๋ฅผ ์ง์ํ๋ ๋ชจ๋ ์๋ํฐ์์ ์ ์๋ก ์ด๋, ๋ชจ๋ ์ฐธ์กฐ ์ฐพ๊ธฐ, ๋ฒ์ญ ๊ฐ ๋ง์ฐ์ค ์ค๋ฒ ๋ฏธ๋ฆฌ๋ณด๊ธฐ, ํค ๋ฐ ํ๋ ์๋ ์์ฑ, ์ ์ธ๋์ง ์์ ํค์ ๋ํ ๊ฒฝ๊ณ ๋ฑ ๋์ผํ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค. ๋ํ
i18next,react-i18next,next-intl,use-intlํธ์ถ๋ ํด์ํ๋ฏ๋ก ๋ง์ด๊ทธ๋ ์ด์ ์ ์ ์ฉํฉ๋๋ค. - MCP ์๋ฒ: Cursor, VS Code, Claude Desktop, Claude Code, ChatGPT์ Intlayer ๋ฌธ์์ CLI๋ฅผ ๋
ธ์ถํ์ฌ AI ์ด์์คํดํธ๊ฐ ์ต์ ๋ฌธ์๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ ํํ ๋ต๋ณํ๊ณ
intlayer fill๋ฑ์ ๋ช ๋ น์ ์ง์ ์คํํ ์ ์๊ฒ ํฉ๋๋ค. - Agent Skills:
intlayer-config,intlayer-cli,intlayer-content๋ฐ ๊ฐ ํ๋ ์์ํฌ ์ ์ฉ ์คํฌ์ ํตํด AI ์์ด์ ํธ์๊ฒ ๋ผ์ฐํ ์ค์ ๊ณผ ์ฝํ ์ธ ๋ ธ๋ ํ์ ์ ํ์ต์ํต๋๋ค. - ESLint ํ๋ฌ๊ทธ์ธ:
no-raw-text๊ท์น์ผ๋ก ํ๋์ฝ๋ฉ๋ ๋ฌธ์์ด์ ํ์ํ๊ณ , ์ ์ ์ฌ์ ํค ๋ฐ ์ฌ์ฉ๋์ง ์๋ ์ฝํ ์ธ ์ ๋ํ ์ถ๊ฐ ๊ท์น์ ์ ๊ณตํฉ๋๋ค.
