์ž‘๊ฐ€:
    ์ƒ์„ฑ:2025-04-18๋งˆ์ง€๋ง‰ ์—…๋ฐ์ดํŠธ:2026-09-21

    Intlayer๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Angular 19 (Webpack) ์›น์‚ฌ์ดํŠธ ๋ฒˆ์—ญํ•˜๊ธฐ | ๊ตญ์ œํ™” (i18n)

    ๋ชฉ์ฐจ

    ๋Œ€์•ˆ๋ณด๋‹ค Intlayer๋ฅผ ์„ ํƒํ•ด์•ผ ํ•˜๋Š” ์ด์œ ๋Š” ๋ฌด์—‡์ž…๋‹ˆ๊นŒ?

    'ngx-translate' ๋˜๋Š” 'angular-l10n'๊ณผ ๊ฐ™์€ ์ฃผ์š” ์†”๋ฃจ์…˜๊ณผ ๋น„๊ตํ•  ๋•Œ Intlayer๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ†ตํ•ฉ ์ตœ์ ํ™”๊ฐ€ ์ œ๊ณต๋˜๋Š” ์†”๋ฃจ์…˜์ž…๋‹ˆ๋‹ค.

    Intlayer๋Š” ๊ตฌ์„ฑ ์š”์†Œ ์ˆ˜์ค€ ์ฝ˜ํ…์ธ  ๋ฒ”์œ„ ์ง€์ •, ์ง€์—ฐ ๋กœ๋“œ ๋ฒˆ์—ญ ๋ฐ ๊ตญ์ œํ™” ํ™•์žฅ(i18n)์— ํ•„์š”ํ•œ ๋ชจ๋“  ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜์—ฌ Angular์™€ ์™„๋ฒฝํ•˜๊ฒŒ ์ž‘๋™ํ•˜๋„๋ก ์ตœ์ ํ™”๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

    ๋Œ€์šฉ๋Ÿ‰ JSON ํŒŒ์ผ์„ ํŽ˜์ด์ง€์— ๋กœ๋“œํ•˜๋Š” ๋Œ€์‹  ํ•„์š”ํ•œ ์ฝ˜ํ…์ธ ๋งŒ ๋กœ๋“œํ•˜์„ธ์š”. Intlayer๋Š” ๋ฒˆ๋“ค ๋ฐ ํŽ˜์ด์ง€ ํฌ๊ธฐ๋ฅผ ์ตœ๋Œ€ 50% ์ค„์ด๋Š” ๋ฐ ๋„์›€์ด ๋ฉ๋‹ˆ๋‹ค.

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

    ์ฝ˜ํ…์ธ ๋ฅผ ๊ฐ™์€ ์œ„์น˜์— ๋ฐฐ์น˜ํ•˜๋ฉด LLM(๋Œ€ํ˜• ์–ธ์–ด ๋ชจ๋ธ)์— ํ•„์š”ํ•œ ์ปจํ…์ŠคํŠธ๊ฐ€ ์ค„์–ด๋“ญ๋‹ˆ๋‹ค. Intlayer์—๋Š” ๋ˆ„๋ฝ๋œ ๋ฒˆ์—ญ์„ ํ…Œ์ŠคํŠธํ•˜๊ธฐ ์œ„ํ•œ CLI, LSP, MCP ๋ฐ agent skills, AI ์—์ด์ „ํŠธ๋ฅผ ์œ„ํ•œ ๊ฐœ๋ฐœ์ž ๊ฒฝํ—˜(DX)์„ ๋”์šฑ ์›ํ™œํ•˜๊ฒŒ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

    AI ๊ณต๊ธ‰์ž์˜ ๋น„์šฉ์œผ๋กœ ์„ ํƒํ•œ LLM์„ ์‚ฌ์šฉํ•˜์—ฌ CI/CD ํŒŒ์ดํ”„๋ผ์ธ์„ ๋ฒˆ์—ญํ•˜๋ ค๋ฉด ์ž๋™ํ™”๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”. Intlayer๋Š” ๋˜ํ•œ ์ฝ˜ํ…์ธ  ์ถ”์ถœ์„ ์ž๋™ํ™”ํ•˜๋Š” ์ปดํŒŒ์ผ๋Ÿฌ์™€ ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ๋ฒˆ์—ญ์„ ๋•๋Š” ์›น ํ”Œ๋žซํผ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

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

    Intlayer๋Š” ๋‹จ์ˆœํ•œ i18n ์†”๋ฃจ์…˜ ๊ทธ ์ด์ƒ์œผ๋กœ ๊ด€๋ฆฌ์— ๋„์›€์ด ๋˜๋Š” ์ž์ฒด ํ˜ธ์ŠคํŒ… ๋น„์ฃผ์–ผ ํŽธ์ง‘๊ธฐ ๋ฐ ์ „์ฒด CMS๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ๋‹ค๊ตญ์–ด ์ฝ˜ํ…์ธ ๋ฅผ ์‹ค์‹œ๊ฐ„์œผ๋กœ ์ œ๊ณตํ•˜์—ฌ ๋ฒˆ์—ญ๊ฐ€, ์นดํ”ผ๋ผ์ดํ„ฐ, ๊ธฐํƒ€ ํŒ€์›๊ณผ ์›ํ™œํ•˜๊ฒŒ ํ˜‘์—…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ฝ˜ํ…์ธ ๋Š” ๋กœ์ปฌ ๋ฐ/๋˜๋Š” ์›๊ฒฉ์œผ๋กœ ์ €์žฅ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    Angular ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ Intlayer๋ฅผ ์„ค์ •ํ•˜๊ธฐ ์œ„ํ•œ ๋‹จ๊ณ„๋ณ„ ๊ฐ€์ด๋“œ

    ide.intlayer.org
    intlayer-angular-19-template.vercel.app

    GitHub์—์„œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ํ…œํ”Œ๋ฆฟ์„ ํ™•์ธํ•˜์„ธ์š”.

    1. ์ข…์†์„ฑ ์„ค์น˜

      npm์„ ์‚ฌ์šฉํ•˜์—ฌ ํ•„์š”ํ•œ ํŒจํ‚ค์ง€๋ฅผ ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค:

      bash
      npx intlayer init --interactive
      
      --interactive ํ”Œ๋ž˜๊ทธ๋Š” ์„ ํƒ ์‚ฌํ•ญ์ž…๋‹ˆ๋‹ค. AI ์—์ด์ „ํŠธ์ธ ๊ฒฝ์šฐ intlayer-cli init๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”.
      ์ด ๋ช…๋ น์€ ํ™˜๊ฒฝ์„ ๊ฐ์ง€ํ•˜๊ณ  ํ•„์š”ํ•œ ํŒจํ‚ค์ง€๋ฅผ ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด:
      bash
      npm install intlayer angular-intlayer
      npm install @angular-builders/custom-webpack --save-dev
      
      • intlayer

        ๊ตฌ์„ฑ ๊ด€๋ฆฌ, ๋ฒˆ์—ญ, ์ฝ˜ํ…์ธ  ์„ ์–ธ, ํŠธ๋žœ์ŠคํŒŒ์ผ ๋ฐ CLI ๋ช…๋ น์–ด๋ฅผ ์œ„ํ•œ ๊ตญ์ œํ™” ๋„๊ตฌ๋ฅผ ์ œ๊ณตํ•˜๋Š” ํ•ต์‹ฌ ํŒจํ‚ค์ง€์ž…๋‹ˆ๋‹ค.

      • angular-intlayer Intlayer๋ฅผ Angular ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜๊ณผ ํ†ตํ•ฉํ•˜๋Š” ํŒจํ‚ค์ง€์ž…๋‹ˆ๋‹ค. Angular ๊ตญ์ œํ™”๋ฅผ ์œ„ํ•œ ์ปจํ…์ŠคํŠธ ๊ณต๊ธ‰์ž์™€ ํ›…์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

      • @angular-builders/custom-webpack Angular CLI์˜ Webpack ๊ตฌ์„ฑ์„ ์‚ฌ์šฉ์ž ์ •์˜ํ•˜๋Š” ๋ฐ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

    2. ํ”„๋กœ์ ํŠธ ๊ตฌ์„ฑ

      ์•„ํ‚คํ…์ฒ˜

      ์ด ์•„ํ‚คํ…์ฒ˜์—์„œ angular-intlayer๋Š” provideIntlayer()๋ฅผ ์ œ๊ณตํ•˜๋ฉฐ, src/app/app.config.ts์— ๋“ฑ๋ก๋˜์–ด ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ๊ฐ€ useIntlayer ์‹œ๊ทธ๋„์„ ํ†ตํ•ด ์ฝ˜ํ…์ธ ๋ฅผ ์ฝ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Angular CLI๋Š” ์ปค์Šคํ…€ Webpack ๋นŒ๋”๋กœ ์ „ํ™˜๋˜๋ฉฐ, webpack.config.ts๋Š” Intlayer ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ๋นŒ๋“œ์— ๋ณ‘ํ•ฉํ•˜์—ฌ ์ฝ˜ํ…์ธ  ์„ ์–ธ์„ ๊ฐ์‹œํ•˜๊ณ  ๋‹ค์‹œ ๋นŒ๋“œํ•ฉ๋‹ˆ๋‹ค. ์ฝ˜ํ…์ธ  ์„ ์–ธ์€ src/app/์˜ ์ปดํฌ๋„ŒํŠธ์™€ ํ•จ๊ป˜ ๋ฐฐ์น˜๋ฉ๋‹ˆ๋‹ค.

      bash
      .
      โ”œโ”€โ”€ src
      โ”‚   โ”œโ”€โ”€ app
      โ”‚   โ”‚   โ”œโ”€โ”€ app.component.html        # Root template reading the content signal
      โ”‚   โ”‚   โ”œโ”€โ”€ app.component.ts          # Root component using useIntlayer
      โ”‚   โ”‚   โ”œโ”€โ”€ app.config.ts             # Application config with provideIntlayer()
      โ”‚   โ”‚   โ”œโ”€โ”€ app.content.ts            # App content declaration
      โ”‚   โ”‚   โ””โ”€โ”€ locale-switcher.component.ts
      โ”‚   โ””โ”€โ”€ main.ts
      โ”œโ”€โ”€ angular.json                      # Custom Webpack builder for build and serve
      โ”œโ”€โ”€ intlayer.config.ts
      โ”œโ”€โ”€ package.json
      โ”œโ”€โ”€ tsconfig.json
      โ””โ”€โ”€ webpack.config.ts                 # mergeConfig() from angular-intlayer/webpack
      

      ์„ค์ •

      ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์–ธ์–ด๋ฅผ ๊ตฌ์„ฑํ•˜๊ธฐ ์œ„ํ•œ ์„ค์ • ํŒŒ์ผ์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค:

      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 ๋กœ๊ทธ ๋น„ํ™œ์„ฑํ™” ๋“ฑ์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๋งค๊ฐœ๋ณ€์ˆ˜์˜ ์ „์ฒด ๋ชฉ๋ก์€ ๊ตฌ์„ฑ ๋ฌธ์„œ๋ฅผ ์ฐธ์กฐํ•˜์„ธ์š”.
    3. Angular ๊ตฌ์„ฑ์— Intlayer ํ†ตํ•ฉ

      Intlayer๋ฅผ Angular CLI์™€ ํ†ตํ•ฉํ•˜๋ ค๋ฉด ์‚ฌ์šฉ์ž ์ •์˜ ๋นŒ๋”๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ฐ€์ด๋“œ์—์„œ๋Š” Webpack(๋Œ€๋ถ€๋ถ„์˜ Angular ํ”„๋กœ์ ํŠธ์˜ ๊ธฐ๋ณธ๊ฐ’)์„ ์‚ฌ์šฉํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•ฉ๋‹ˆ๋‹ค.

      ๋จผ์ €, angular.json์„ ์ˆ˜์ •ํ•˜์—ฌ ์‚ฌ์šฉ์ž ์ •์˜ Webpack ๋นŒ๋”๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค. build ๋ฐ serve ๊ตฌ์„ฑ์„ ์—…๋ฐ์ดํŠธํ•˜์„ธ์š”:

      angular.json
      {
        "projects": {
          "your-app-name": {
            "architect": {
              "build": {
                "builder": "@angular-builders/custom-webpack:browser", // replace "@angular-devkit/build-angular:application",
                "options": {
                  "customWebpackConfig": {
                    "path": "./webpack.config.ts",
                    "mergeStrategies": { "module.rules": "prepend" },
                  },
                  "main": "src/main.ts", // replace "browser": "src/main.ts",
                  // ...
                },
              },
              "serve": {
                "builder": "@angular-builders/custom-webpack:dev-server",
              },
            },
          },
        },
      }
      
      angular.json์—์„œ your-app-name์„ ์‹ค์ œ ํ”„๋กœ์ ํŠธ ์ด๋ฆ„์œผ๋กœ ๋ฐ”๊พธ์„ธ์š”.

      ๋‹ค์Œ์œผ๋กœ, ํ”„๋กœ์ ํŠธ ๋ฃจํŠธ์— webpack.config.ts ํŒŒ์ผ์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค:

      webpack.config.ts
      import { mergeConfig } from "angular-intlayer/webpack";
      
      export default mergeConfig({});
      
      mergeConfig ํ•จ์ˆ˜๋Š” Intlayer๋กœ Webpack์„ ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค. IntlayerPlugin(์ฝ˜ํ…์ธ  ์„ ์–ธ ํŒŒ์ผ ์ฒ˜๋ฆฌ์šฉ)์„ ์ฃผ์ž…ํ•˜๊ณ  ์ตœ์ ์˜ ์„ฑ๋Šฅ์„ ์œ„ํ•œ ๋ณ„์นญ(alias)์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
    4. ์ฝ˜ํ…์ธ  ์„ ์–ธ

      ๋ฒˆ์—ญ์„ ์ €์žฅํ•˜๊ธฐ ์œ„ํ•ด ์ฝ˜ํ…์ธ  ์„ ์–ธ์„ ์ƒ์„ฑํ•˜๊ณ  ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค:

      ์ฝ˜ํ…์ธ  ์„ ์–ธ์€ contentDir ๋””๋ ‰ํ† ๋ฆฌ(๊ธฐ๋ณธ๊ฐ’ ./src)์— ํฌํ•จ๋˜์–ด ์žˆ๊ณ  ์ฝ˜ํ…์ธ  ์„ ์–ธ ํŒŒ์ผ ํ™•์žฅ์ž(๊ธฐ๋ณธ๊ฐ’ .content.{json,ts,tsx,js,jsx,mjs,cjs,md,mdx,yaml,yml})์™€ ์ผ์น˜ํ•œ๋‹ค๋ฉด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์–ด๋””์—์„œ๋‚˜ ์ •์˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
      ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์ฝ˜ํ…์ธ  ์„ ์–ธ ๋ฌธ์„œ๋ฅผ ์ฐธ์กฐํ•˜์„ธ์š”.
    5. ์ฝ”๋“œ์—์„œ Intlayer ํ™œ์šฉ

      Angular ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ „์ฒด์—์„œ Intlayer์˜ ๊ตญ์ œํ™” ๊ธฐ๋Šฅ์„ ํ™œ์šฉํ•˜๋ ค๋ฉด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ตฌ์„ฑ์— Intlayer๋ฅผ ์ œ๊ณตํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

      src/app/app.config.ts
      import { ApplicationConfig } from "@angular/core";
      import { provideRouter } from "@angular/router";
      import { provideIntlayer } from "angular-intlayer";
      import { routes } from "./app.routes";
      
      export const appConfig: ApplicationConfig = {
        providers: [
          provideRouter(routes),
          provideIntlayer(), // ์—ฌ๊ธฐ์— Intlayer ๊ณต๊ธ‰์ž ์ถ”๊ฐ€
        ],
      };
      

      ๊ทธ๋Ÿฐ ๋‹ค์Œ, ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ ๋‚ด์—์„œ useIntlayer ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

      src/app/app.component.ts
      import { Component } from "@angular/core";
      import { RouterOutlet } from "@angular/router";
      import { useIntlayer } from "angular-intlayer";
      
      @Component({
        selector: "app-root",
        standalone: true,
        imports: [RouterOutlet],
        templateUrl: "./app.component.html",
        styleUrl: "./app.component.css",
      })
      export class AppComponent {
        content = useIntlayer("app");
      }
      

      ๊ทธ๋ฆฌ๊ณ  ํ…œํ”Œ๋ฆฟ์—์„œ:

      src/app/app.component.html
      <div class="content">
        <h1>{{ content().title }}</h1>
        <p>{{ content().congratulations }}</p>
      </div>
      

      Intlayer ์ฝ˜ํ…์ธ ๋Š” Signal๋กœ ๋ฐ˜ํ™˜๋˜๋ฏ€๋กœ ์‹œ๊ทธ๋„์„ ํ˜ธ์ถœํ•˜์—ฌ ๊ฐ’์— ์•ก์„ธ์Šคํ•ฉ๋‹ˆ๋‹ค: content().title.

    6. ์ฝ˜ํ…์ธ  ์–ธ์–ด ๋ณ€๊ฒฝ

      ์„ ํƒ์‚ฌํ•ญ

      ์ฝ˜ํ…์ธ  ์–ธ์–ด๋ฅผ ๋ณ€๊ฒฝํ•˜๋ ค๋ฉด useLocale ํ•จ์ˆ˜์—์„œ ์ œ๊ณตํ•˜๋Š” setLocale ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋กœ์ผ€์ผ์„ ์„ค์ •ํ•˜๊ณ  ๊ทธ์— ๋”ฐ๋ผ ์ฝ˜ํ…์ธ ๋ฅผ ์—…๋ฐ์ดํŠธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

      ์–ธ์–ด๋ฅผ ์ „ํ™˜ํ•  ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค:

      src/app/locale-switcher.component.ts
      import { Component } from "@angular/core";
      import { CommonModule } from "@angular/common";
      import { useLocale } from "angular-intlayer";
      
      @Component({
        selector: "app-locale-switcher",
        standalone: true,
        imports: [CommonModule],
        template: `
          <div class="locale-switcher">
            <select
              [value]="locale()"
              (change)="setLocale($any($event.target).value)"
            >
              @for (loc of availableLocales; track loc) {
                <option [value]="loc">{{ loc }}</option>
              }
            </select>
          </div>
        `,
      })
      export class LocaleSwitcherComponent {
        localeCtx = useLocale();
      
        locale = this.localeCtx.locale;
        availableLocales = this.localeCtx.availableLocales;
        setLocale = this.localeCtx.setLocale;
      }
      

      ๊ทธ๋Ÿฐ ๋‹ค์Œ, app.component.ts์—์„œ ์ด ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค:

      src/app/app.component.ts
      import { Component } from "@angular/core";
      import { RouterOutlet } from "@angular/router";
      import { useIntlayer } from "angular-intlayer";
      import { LocaleSwitcherComponent } from "./locale-switcher.component";
      
      @Component({
        selector: "app-root",
        standalone: true,
        imports: [RouterOutlet, LocaleSwitcherComponent],
        templateUrl: "./app.component.html",
        styleUrl: "./app.component.css",
      })
      export class AppComponent {
        content = useIntlayer("app");
      }
      

    TypeScript ๊ตฌ์„ฑ

    Intlayer๋Š” TypeScript์˜ ์ด์ ์„ ํ™œ์šฉํ•˜๊ณ  ์ฝ”๋“œ๋ฒ ์ด์Šค๋ฅผ ๋”์šฑ ๊ฐ•๋ ฅํ•˜๊ฒŒ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด ๋ชจ๋“ˆ ๋ณด๊ฐ•(module augmentation)์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

    ์ž๋™ ์™„์„ฑ

    ๋ฒˆ์—ญ ์˜ค๋ฅ˜

    TypeScript ๊ตฌ์„ฑ์— ์ž๋™ ์ƒ์„ฑ๋œ ํƒ€์ž…์ด ํฌํ•จ๋˜์–ด ์žˆ๋Š”์ง€ ํ™•์ธํ•˜์„ธ์š”.

    tsconfig.json
    {
      // ... ๊ธฐ์กด TypeScript ๊ตฌ์„ฑ
      "include": [
        // ... ๊ธฐ์กด TypeScript ๊ตฌ์„ฑ
        ".intlayer/**/*.ts", // ์ž๋™ ์ƒ์„ฑ๋œ ํƒ€์ž… ํฌํ•จ
      ],
    }
    

    Git ๊ตฌ์„ฑ

    Intlayer์—์„œ ์ƒ์„ฑ๋œ ํŒŒ์ผ์€ ๋ฌด์‹œํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด Git ์ €์žฅ์†Œ์— ์ปค๋ฐ‹๋˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    ์ด๋ ‡๊ฒŒ ํ•˜๋ ค๋ฉด .gitignore ํŒŒ์ผ์— ๋‹ค์Œ ์ง€์นจ์„ ์ถ”๊ฐ€ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค:

    bash
    #  Intlayer์—์„œ ์ƒ์„ฑ๋œ ํŒŒ์ผ ๋ฌด์‹œ
    .intlayer
    

    VS Code ํ™•์žฅ ํ”„๋กœ๊ทธ๋žจ

    Intlayer ๊ฐœ๋ฐœ ๊ฒฝํ—˜์„ ๊ฐœ์„ ํ•˜๊ธฐ ์œ„ํ•ด ๊ณต์‹ Intlayer VS Code Extension์„ ์„ค์น˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    VS Code ๋งˆ์ผ“ํ”Œ๋ ˆ์ด์Šค์—์„œ ์„ค์น˜

    ์ด ํ™•์žฅ ํ”„๋กœ๊ทธ๋žจ์€ ๋‹ค์Œ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค:

    • ๋ฒˆ์—ญ ํ‚ค์— ๋Œ€ํ•œ ์ž๋™ ์™„์„ฑ.
    • ๋ˆ„๋ฝ๋œ ๋ฒˆ์—ญ์— ๋Œ€ํ•œ ์‹ค์‹œ๊ฐ„ ์˜ค๋ฅ˜ ๊ฐ์ง€.
    • ๋ฒˆ์—ญ๋œ ์ฝ˜ํ…์ธ ์˜ ์ธ๋ผ์ธ ๋ฏธ๋ฆฌ๋ณด๊ธฐ.
    • ๋ฒˆ์—ญ์„ ์‰ฝ๊ฒŒ ์ƒ์„ฑํ•˜๊ณ  ์—…๋ฐ์ดํŠธํ•  ์ˆ˜ ์žˆ๋Š” ๋น ๋ฅธ ์ž‘์—….

    ํ™•์žฅ ํ”„๋กœ๊ทธ๋žจ ์‚ฌ์šฉ ๋ฐฉ๋ฒ•์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ Intlayer VS Code Extension ๋ฌธ์„œ๋ฅผ ์ฐธ์กฐํ•˜์„ธ์š”.

    ๋” ์•Œ์•„๋ณด๊ธฐ

    ๋” ์ž์„ธํžˆ ์•Œ์•„๋ณด๋ ค๋ฉด ๋น„์ฃผ์–ผ ์—๋””ํ„ฐ๋ฅผ ๊ตฌํ˜„ํ•˜๊ฑฐ๋‚˜ CMS๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ฝ˜ํ…์ธ ๋ฅผ ์™ธ๋ถ€ํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

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

    • @angular/localize(๋‚ด์žฅ i18n): ๋ฉ”์‹œ์ง€๊ฐ€ XLIFF๋กœ ์ถ”์ถœ๋˜๊ณ  ๊ฐ ๋กœ์ผ€์ผ์ด ์ž์ฒด ๋นŒ๋“œ๋กœ ์ปดํŒŒ์ผ๋˜๋ฏ€๋กœ ์–ธ์–ด๋‹น ํ•˜๋‚˜์˜ ๋ฐฐํฌ ๊ฒฐ๊ณผ๋ฌผ์ด ์ƒ์„ฑ๋˜๊ณ  ๋Ÿฐํƒ€์ž„ ๋กœ์ผ€์ผ ์ „ํ™˜์ด ์ง€์›๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
    • ngx-translate ๋ฐ Transloco: ์„œ๋น„์Šค๋ฅผ ํ†ตํ•ด ๋กœ๋“œ๋˜๋Š” ๋Ÿฐํƒ€์ž„ JSON ์นดํƒˆ๋กœ๊ทธ ๋ฐฉ์‹์œผ๋กœ ๋กœ์ผ€์ผ ์ „ํ™˜์€ ๊ฐ€๋Šฅํ•˜์ง€๋งŒ ๋นŒ๋“œ ํƒ€์ž„ ํƒ€์ž… ์•ˆ์ „์„ฑ์ด ๋ถ€์กฑํ•ฉ๋‹ˆ๋‹ค.
    • Intlayer: ๊ฐ€์žฅ ์ง„๋ณด๋œ ์†”๋ฃจ์…˜์ž…๋‹ˆ๋‹ค. ์ฝ”๋“œ๋ฒ ์ด์Šค ์–ด๋””์—๋‚˜ ์ฝ˜ํ…์ธ ๋ฅผ ์„ ์–ธํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ(๊ฐ ์ปดํฌ๋„ŒํŠธ ์˜† ๋˜๋Š” ์ค‘์•™ ์ง‘์ค‘์‹), ๋นŒ๋“œ ํƒ€์ž„์— ์ปดํŒŒ์ผ๋˜๊ณ , ์™„์ „ํ•œ ํƒ€์ž… ์•ˆ์ „์„ฑ์„ ์ œ๊ณตํ•˜๋ฉฐ ๋Ÿฐํƒ€์ž„ ๋กœ์ผ€์ผ ์ „ํ™˜, AI ๋ฒˆ์—ญ, ๋น„์ฃผ์–ผ ์—๋””ํ„ฐ ๋ฐ CMS๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.

    ์ด ๊ฐ€์ด๋“œ๋Š” Angular 19๋ฅผ ๋‹ค๋ฃน๋‹ˆ๋‹ค. Angular 21 ์ด์ƒ์€ Angular ๊ฐ€์ด๋“œ๋ฅผ ์ฐธ์กฐํ•˜์„ธ์š”. ์™œ Intlayer์ธ๊ฐ€๋„ ํ™•์ธํ•˜์„ธ์š”.

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

    ๋Œ€๋ถ€๋ถ„ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ์ฝ˜ํ…์ธ ๋ฅผ ์ด์ „ํ•˜๋ ค๋ฉด ngx-translate ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๊ฐ€์ด๋“œ ๋˜๋Š” Transloco ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๊ฐ€์ด๋“œ๋ฅผ ๋”ฐ๋ฅด์„ธ์š”. ์ ์ง„์ ์œผ๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค: 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๋งŒ ๊ฒ€ํ† ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. extract ๋ช…๋ น์„ ์ฐธ์กฐํ•˜์„ธ์š”.

    ์™„์ „ ์ž๋™ํ™”๋œ ํŒŒ์ดํ”„๋ผ์ธ์„ ์œ„ํ•ด Intlayer ์ปดํŒŒ์ผ๋Ÿฌ๋Š” ๋นŒ๋“œ ํƒ€์ž„์— JSX, TSX, Vue ๋ฐ Svelte ์†Œ์Šค์—์„œ ๋™์ผํ•œ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜์—ฌ ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค ์‚ฌ์ „์„ ์ƒ์„ฑํ•˜๋ฏ€๋กœ ์ˆ˜๋™์œผ๋กœ ํ‚ค๋ฅผ ๊ด€๋ฆฌํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ์ •์  ๋ถ„์„์œผ๋กœ ์ž‘๋™ํ•˜๋ฏ€๋กœ ๋Ÿฐํƒ€์ž„์—๋งŒ ์กด์žฌํ•˜๋Š” ๋ฌธ์ž์—ด์€ ์ œ์™ธ๋˜๋ฉฐ, ์‚ฌ์šฉ์ž ํ…์ŠคํŠธ์™€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋กœ์ง์„ ๊ตฌ๋ถ„ํ•˜๊ธฐ ์œ„ํ•ด ๋ช‡ ๊ฐ€์ง€ ์ฃผ์„์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

    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 ๊ทœ์น™์œผ๋กœ ํ•˜๋“œ์ฝ”๋”ฉ๋œ ๋ฌธ์ž์—ด์„ ํ‘œ์‹œํ•˜๊ณ , ์ •์  ์‚ฌ์ „ ํ‚ค ๋ฐ ์‚ฌ์šฉ๋˜์ง€ ์•Š๋Š” ์ฝ˜ํ…์ธ ์— ๋Œ€ํ•œ ์ถ”๊ฐ€ ๊ทœ์น™์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.