---
createdAt: 2025-09-22
updatedAt: 2026-01-26
title: 새로운 Intlayer v8 - 무엇이 새로워졌나요?
description: Intlayer v8의 신규 기능을 확인하세요. 이번 릴리스는 developer experience 향상, schema validation을 통한 데이터 무결성 보장, 그리고 dictionary management에 대한 제어 강화에 중점을 둡니다.
keywords:
  - Intlayer
  - CMS
  - Developer Experience
  - Features
  - React
  - Next.js
  - JavaScript
  - TypeScript
youtubeVideo: https://www.youtube.com/watch?v=ia6JmVf-kkU
slugs:
  - doc
  - releases
  - v8
---

# 새로운 Intlayer v8 - 무엇이 새로워졌나요?

Intlayer v8에 오신 것을 환영합니다! 이번 릴리스는 자동 콘텐츠 감지를 통한 developer experience 향상, schema validation으로 데이터 무결성 보장, 그리고 dictionary management에 대한 더 많은 제어 제공에 중점을 둡니다.

<iframe
  src="https://www.youtube.com/embed/ia6JmVf-kkU"
  className="m-auto overflow-hidden rounded-lg border-0 max-md:size-full max-md:h-[700px] md:aspect-16/9 md:w-full"
  title="Demo Video - New Intlayer v8 - What's new?"
  sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"
  loading="lazy"
/>

## 목차

<TOC levels={[2]} maxDepth={1} />

---

## 리치 콘텐츠의 진화: Markdown & HTML

Intlayer v8는 리치 콘텐츠를 처리하는 방식에 큰 개선을 가져왔으며, **HTML 노드**(v7에는 존재하지 않았음)를 도입하고 **Markdown 노드**(v7에 존재했지만 향상됨)와 API를 통합했습니다.

### 통합된 `.use()` API

Markdown 및 HTML 노드 모두에 `.use()` 메서드를 도입했습니다. 이 메서드를 사용하면 렌더링 중에 사용되는 HTML 태그나 컴포넌트를 사용자 정의할 수 있습니다.

- **컴포넌트 교체**: HTML 태그나 커스텀 컴포넌트를 사용자 프레임워크 컴포넌트로 쉽게 교체할 수 있습니다 (예: `<a>`를 `NextLink`로 교체하거나 `<CustomCmp>`를 React 컴포넌트로 교체).
- **타입 안정성**: 컴포넌트를 제공하기 위한 모든 함수는 완전하게 타입이 지정되어 올바른 props를 받을 수 있도록 보장합니다.

### 기본 렌더링 동작

v7에서는 Provider가 정의되지 않은 경우 Markdown 노드가 원시 문자열로 렌더링되어 이를 파싱하기 위해 외부 라이브러리가 필요한 경우가 많았습니다.

**v8에서 Intlayer는 자체 내부 Markdown 파서를 포함합니다.** 이제 기본적으로 Markdown 노드는 외부 라이브러리 없이 직접 HTML로 렌더링됩니다.

### 새로운 렌더러 및 Provider 유틸리티

표준 `useIntlayer` 흐름 외부에서도 더 많은 제어권을 가질 수 있도록 새로운 독립형 렌더러 함수와 컴포넌트를 도입했습니다.

- **Markdown**: `MarkdownRenderer`, `useMarkdownRenderer`, `renderMarkdown`. (참고: `MarkdownProvider`는 v7에도 있었지만 이제 이 새로운 도구들과 통합됩니다).
- **HTML**: `HTMLRenderer`, `useHTMLRenderer`, `renderHTML`, `HTMLProvider`.

#### 예제: Markdown 렌더링 도구

<Tabs group='framework'>
  <Tab label="React / Next.js" value="react">

    **1. 컴포넌트 사용:**

    ```tsx
    import { MarkdownRenderer } from "react-intlayer/markdown";

    <MarkdownRenderer
      forceBlock={true}
      components={{
        h1: ({ children }) => <h1 className="text-2xl">{children}</h1>
      }}
    >
      {"# My Title"}
    </MarkdownRenderer>
    ```

    **2. Hook 사용:**

    ```tsx
    import { useMarkdownRenderer } from "react-intlayer/markdown";

    const renderMarkdown = useMarkdownRenderer({
      components: {
        h1: ({ children }) => <h1 className="text-red-500">{children}</h1>
      }
    });

    return <div>{renderMarkdown("# My Title")}</div>;
    ```

    **3. 유틸리티 함수 사용:**

    ```tsx
    import { renderMarkdown } from "react-intlayer/markdown";

    const html = renderMarkdown("# My Title", {
      forceBlock: true
    });
    ```

  </Tab>
  <Tab label="Vue" value="vue">

    **1. 컴포넌트 사용:**

    ```vue
    <script setup>
    import { MarkdownRenderer } from "vue-intlayer/markdown";
    </script>

    <template>
      <MarkdownRenderer :forceBlock="true" content="# My Title" />
    </template>
    ```

  </Tab>
  <Tab label="Svelte" value="svelte">

    **1. 컴포넌트 사용:**

    ```svelte
    <script>
      import { MarkdownRenderer } from "svelte-intlayer/markdown";
    </script>

    <MarkdownRenderer forceBlock={true} value="# My Title" />
    ```

    **2. Hook 사용:**

    ```svelte
    <script>
      import { useMarkdownRenderer } from "svelte-intlayer/markdown";
      const render = useMarkdownRenderer();
    </script>

    {@html render("# My Title")}
    ```

    **3. 유틸리티 함수 사용:**

    ```svelte
    <script>
      import { renderMarkdown } from "svelte-intlayer/markdown";
    </script>

    {@html renderMarkdown("# My Title")}
    ```

  </Tab>
  <Tab label="Angular" value="angular">

    **1. 서비스 사용:**

    ```typescript
    import { Component } from "@angular/core";
    import { IntlayerMarkdownService } from "angular-intlayer";

    @Component({ ... })
    export class MyComponent {
      constructor(private markdownService: IntlayerMarkdownService) {}

      render(markdown: string) {
        return this.markdownService.renderMarkdown(markdown);
      }
    }
    ```

  </Tab>
  <Tab label="Solid" value="solid">

    **1. 컴포넌트 사용:**

    ```tsx
    import { MarkdownRenderer } from "solid-intlayer/markdown";

    <MarkdownRenderer forceBlock={true}>
      {"# My Title"}
    </MarkdownRenderer>
    ```

    **2. Hook 사용:**

    ```tsx
    import { useMarkdownRenderer } from "solid-intlayer/markdown";

    const render = useMarkdownRenderer();

    return <div>{render("# My Title")}</div>;
    ```

    **3. 유틸리티 함수 사용:**

    ```tsx
    import { renderMarkdown } from "solid-intlayer/markdown";

    return <div>{renderMarkdown("# My Title")}</div>;
    ```

  </Tab>
  <Tab label="Preact" value="preact">

    **1. 컴포넌트 사용:**

    ```tsx
    import { MarkdownRenderer } from "preact-intlayer/markdown";

    <MarkdownRenderer forceBlock={true}>
      {"# My Title"}
    </MarkdownRenderer>
    ```

    **2. Hook 사용:**

    ```tsx
    import { useMarkdownRenderer } from "preact-intlayer/markdown";

    const render = useMarkdownRenderer();

    return <div>{render("# My Title")}</div>;
    ```

    **3. 유틸리티 함수 사용:**

    ```tsx
    import { renderMarkdown } from "preact-intlayer/markdown";

    return <div>{renderMarkdown("# My Title")}</div>;
    ```

  </Tab>
</Tabs>

#### 예제: HTML 렌더링 도구

<Tabs group='framework'>
  <Tab label="React / Next.js" value="react">

    **1. 컴포넌트 사용:**

    ```tsx
    import { HTMLRenderer } from "react-intlayer/html";

    <HTMLRenderer
      components={{
        p: ({ children }) => <p className="mb-4">{children}</p>
      }}
    >
      {"<p>Hello World</p>"}
    </HTMLRenderer>
    ```

    **2. Hook 사용:**

    ```tsx
    import { useHTMLRenderer } from "react-intlayer/html";

    const renderHTML = useHTMLRenderer({
      components: {
        strong: ({ children }) => <b className="font-bold">{children}</b>
      }
    });

    return <div>{renderHTML("<p>Hello <strong>World</strong></p>")}</div>;
    ```

    **3. 유틸리티 함수 사용:**

    ```tsx
    import { renderHTML } from "react-intlayer/html";

    const html = renderHTML("<p>Hello World</p>");
    ```

  </Tab>
  <Tab label="Vue" value="vue">

    **1. 컴포넌트 사용:**

    ```vue
    <script setup>
    import { HTMLRenderer } from "vue-intlayer/html";
    </script>

    <template>
      <HTMLRenderer content="<p>Hello World</p>" />
    </template>
    ```

  </Tab>
  <Tab label="Svelte" value="svelte">

    **1. 컴포넌트 사용:**

    ```svelte
    <script>
      import { HTMLRenderer } from "svelte-intlayer/html";
    </script>

    <HTMLRenderer value="<p>Hello World</p>" />
    ```

    **2. Hook 사용:**

    ```svelte
    <script>
      import { useHTMLRenderer } from "svelte-intlayer/html";
      const render = useHTMLRenderer();
    </script>

    {@html render("<p>Hello World</p>")}
    ```

    **3. 유틸리티 함수 사용:**

    ```svelte
    <script>
      import { renderHTML } from "svelte-intlayer/html";
    </script>

    {@html renderHTML("<p>Hello World</p>")}
    ```

  </Tab>
  <Tab label="Angular" value="angular">

    **1. 직접 사용:**

    Angular에서는 표준 `[innerHTML]` 바인딩을 사용할 수 있습니다.

    ```html
    <div [innerHTML]="'<p>Hello World</p>'"></div>
    ```

  </Tab>
  <Tab label="Solid" value="solid">

    **1. 컴포넌트 사용:**

    ```tsx
    import { HTMLRenderer } from "solid-intlayer/html";

    <HTMLRenderer>
      {"<p>Hello World</p>"}
    </HTMLRenderer>
    ```

    **2. Hook 사용:**

    ```tsx
    import { useHTMLRenderer } from "solid-intlayer/html";

    const render = useHTMLRenderer();

    return <div>{render("<p>Hello World</p>")}</div>;
    ```

    **3. 유틸리티 함수 사용:**

    ```tsx
    import { renderHTML } from "solid-intlayer/html";

    return <div>{renderHTML("<p>Hello World</p>")}</div>;
    ```

  </Tab>
  <Tab label="Preact" value="preact">

    **1. 컴포넌트 사용:**

    ```tsx
    import { HTMLRenderer } from "preact-intlayer/html";

    <HTMLRenderer>
      {"<p>Hello World</p>"}
    </HTMLRenderer>
    ```

    **2. Hook 사용:**

    ```tsx
    import { useHTMLRenderer } from "preact-intlayer/html";

    const render = useHTMLRenderer();

    return <div>{render("<p>Hello World</p>")}</div>;
    ```

    **3. 유틸리티 함수 사용:**

    ```tsx
    import { renderHTML } from "preact-intlayer/html";

    return <div>{renderHTML("<p>Hello World</p>")}</div>;
    ```

  </Tab>
</Tabs>

더 자세한 내용은 [HTML Content Documentation](https://github.com/aymericzip/intlayer/blob/main/docs/docs/ko/dictionary/html.md) 및 [Markdown Documentation](https://github.com/aymericzip/intlayer/blob/main/docs/docs/ko/dictionary/markdown.md)를 참조하세요.

---

## 사용자 정의 URL 재작성

Intlayer v8은 **사용자 정의 URL 재작성(Custom URL Rewrites)** 을 도입하여 표준 `/locale/path` 구조와 다른 로케일별 경로를 정의할 수 있게 합니다. 이는 로컬 SEO를 개선하고 영어가 아닌 사용자에게 보다 자연스러운 사용자 경험을 제공하는 강력한 기능입니다.

**v8의 주요 개선 사항:**

- **Framework Formatters**: 각 라우터에 맞는 관용적 패턴 구문을 제공하기 위해 새로운 `nextjsRewrite`, `svelteKitRewrite`, `reactRouterRewrite`, `vueRouterRewrite`, `solidRouterRewrite`, `tanstackRouterRewrite`, `nuxtRewrite`, 및 `viteRewrite`가 추가되었습니다.
- **`useRewriteURL` Hook**: 라우터 네비게이션을 트리거하지 않고 주소 표시줄을 '예쁜' 지역화된 URL로 조용히 수정하는 새로운 클라이언트 사이드 훅입니다.
- **Automatic SEO Redirects**: 내장 프록시는 사용자가 수동으로 입력한 표준 경로(예: `/fr/about`)를 더 보기 좋은 지역화된 버전(예: `/fr/a-propos`)으로 자동으로 리디렉트합니다.

**예시 구성:**

<Tabs group='routers'>
  <Tab label="Next.js" value="nextjs">

    ```typescript fileName="intlayer.config.ts"
    import { Locales, type IntlayerConfig } from "intlayer";
    import { nextjsRewrite } from "intlayer/routing";

    const config: IntlayerConfig = {
      internationalization: {
        locales: [Locales.ENGLISH, Locales.FRENCH, Locales.SPANISH],
        defaultLocale: Locales.ENGLISH,
      },
      routing: {
        mode: "prefix-no-default",
        rewrite: nextjsRewrite({
          "/[locale]/about": {
            fr: "/[locale]/a-propos",
            es: "/[locale]/acerca-de",
          },
          "/[locale]/products/[id]": {
            fr: "/[locale]/produits/[id]",
            es: "/[locale]/productos/[id]",
          },
        }),
      },
    };

    export default config;
    ```

  </Tab>
    <Tab label="React Router" value="reactrouter">

    ```typescript fileName="intlayer.config.ts"
    import { Locales, type IntlayerConfig } from "intlayer";
    import { reactRouterRewrite } from "intlayer/routing";

    const config: IntlayerConfig = {
      internationalization: {
        locales: [Locales.ENGLISH, Locales.FRENCH, Locales.SPANISH],
        defaultLocale: Locales.ENGLISH,
      },
      routing: {
        mode: "prefix-all",
        rewrite: reactRouterRewrite({
          "/:locale/about": {
            fr: "/:locale/a-propos",
            es: "/:locale/acerca-de",
          },
          "/:locale/products/:id": {
            fr: "/:locale/produits/:id",
            es: "/:locale/productos/:id",
          },
        }),
      },
    };

    export default config;
    ```

  </Tab>
  <Tab label="Vite (Vue, Solid, Svelte)" value="vite">

    ```typescript fileName="intlayer.config.ts"
    import { Locales, type IntlayerConfig } from "intlayer";
    import { viteRewrite } from "intlayer/routing";

    const config: IntlayerConfig = {
      internationalization: {
        locales: [Locales.ENGLISH, Locales.FRENCH, Locales.SPANISH],
        defaultLocale: Locales.ENGLISH,
      },
      routing: {
        mode: "prefix-all",
        rewrite: viteRewrite({
          "/:locale/about": {
            fr: "/:locale/a-propos",
            es: "/:locale/acerca-de",
          },
          "/:locale/products/:id": {
            fr: "/:locale/produits/:id",
            es: "/:locale/productos/:id",
          },
        }),
      },
    };

    export default config;
    ```

  </Tab>
  <Tab label="Nuxt" value="nuxt">

    ```typescript fileName="intlayer.config.ts"
    import { Locales, type IntlayerConfig } from "intlayer";
    import { nuxtRewrite } from "intlayer/routing";

    const config: IntlayerConfig = {
      internationalization: {
        locales: [Locales.ENGLISH, Locales.FRENCH, Locales.SPANISH],
        defaultLocale: Locales.ENGLISH,
      },
      routing: {
        mode: "prefix-all",
        rewrite: nuxtRewrite({
          "/[locale]/about": {
            fr: "/[locale]/a-propos",
            es: "/[locale]/acerca-de",
          },
          "/[locale]/products/[id]": {
            fr: "/[locale]/produits/[id]",
            es: "/[locale]/productos/[id]",
          },
        }),
      },
    };

    export default config;
    ```

  </Tab>
  <Tab label="SvelteKit" value="sveltekit">

    ```typescript fileName="intlayer.config.ts"
    import { Locales, type IntlayerConfig } from "intlayer";
    import { svelteKitRewrite } from "intlayer/routing";

    const config: IntlayerConfig = {
      internationalization: {
        locales: [Locales.ENGLISH, Locales.FRENCH, Locales.SPANISH],
        defaultLocale: Locales.ENGLISH,
      },
      routing: {
        mode: "prefix-all",
        rewrite: svelteKitRewrite({
          "/[locale]/about": {
            fr: "/[locale]/a-propos",
            es: "/[locale]/acerca-de",
          },
          "/[locale]/products/[id]": {
            fr: "/[locale]/produits/[id]",
            es: "/[locale]/productos/[id]",
          },
        }),
      },
    };

    export default config;
    ```

  </Tab>
</Tabs>

이 기능은 Intlayer 프록시를 통해 **Next.js** 및 **Vite**에서 기본 제공되며, **TanStack Router**, **React Router**, **Vue Router**, **SvelteKit**, **Solid Router** 같은 다른 라우터에도 쉽게 통합할 수 있습니다.

자세한 정보와 통합 가이드는 [Custom URL Rewrites Documentation](https://github.com/aymericzip/intlayer/blob/main/docs/docs/ko/custom_url_rewrites.md)를 참조하세요.

---

### 향상된 삽입 값

v8에서는 삽입 값이 이제 문자열과 숫자 외에 **React 요소(또는 Vue 노드)** 를 허용할 수 있습니다. 이를 통해 삽입 템플릿에 풍부하고 상호작용 가능한 컴포넌트를 직접 주입할 수 있습니다.

Intlayer는 이제 삽입(insertion) 내의 중첩된 React 및 Preact 노드를 안정적으로 처리하여 복잡한 UI 구조가 보존되고 올바르게 렌더링되도록 합니다.

**예제:**

```typescript fileName="src/example.content.ts"
import { insert } from "intlayer";

export default {
  key: "my-key",
  content: {
    myInsertion: insert("Hi {{name}}"),
  },
};
```

<Tabs group='framework'>
  <Tab label="Next.js" value="nextjs">

    ```tsx
    import { useIntlayer } from "next-intlayer";

    const { myInsertion } = useIntlayer("my-key");

    return (
      <div>
        {myInsertion({
          name: 2, // 숫자
          // 또는
          name: "John", // 문자열
          // 또는
          name: <span>John</span>, // React 요소
        })}
      </div>
    );
    ```

  </Tab>
  <Tab label="React" value="react">

    ```tsx
    import { useIntlayer } from "react-intlayer";

    const { myInsertion } = useIntlayer("my-key");

    return (
      <div>
        {myInsertion({
          name: 2, // 숫자
          // 또는
          name: "John", // 문자열
          // 또는
          name: <span>John</span>, // React 요소
        })}
      </div>
    );
    ```

  </Tab>
  <Tab label="Vue" value="vue">

    ```vue
    <script setup>
    import { h } from "vue";
    import { useIntlayer } from "vue-intlayer";

    const { myInsertion } = useIntlayer("my-key");
    </script>

    <template>
      <div>
        <component
          :is="myInsertion({
            name: 2,
            // 또는
            name: 'John',
            // 또는
            name: h('span', 'John'),
          })"
        />
      </div>
    </template>
    ```

  </Tab>
  <Tab label="Preact" value="preact">

    ```tsx
    import { useIntlayer } from "preact-intlayer";

    const { myInsertion } = useIntlayer("my-key");

    return (
      <div>
        {myInsertion({
          name: 2, // 숫자
          // 또는
          name: "John", // 문자열
          // 또는
          name: <span>John</span>, // Preact 요소
        })}
      </div>
    );
    ```

  </Tab>
  <Tab label="Solid" value="solid">

    ```tsx
    import { useIntlayer } from "solid-intlayer";

    const { myInsertion } = useIntlayer("my-key");

    return (
      <div>
        {myInsertion({
          name: 2, // 숫자
          // 또는
          name: "John", // 문자열
          // 또는
          name: <span>John</span>, // Solid 요소
        })}
      </div>
    );
    ```

  </Tab>
  <Tab label="Svelte" value="svelte">

    ```svelte
    <script>
      import { useIntlayer } from "svelte-intlayer";

      const { myInsertion } = useIntlayer("my-key");
    </script>

    <div>
      {myInsertion({
        name: 2, // 숫자
        // 또는
        name: "John", // 문자열
      })}
    </div>
    ```

  </Tab>
  <Tab label="Angular" value="angular">

    ```typescript
    import { Component } from "@angular/core";
    import { useIntlayer } from "angular-intlayer";

    @Component({
      selector: "app-insertion-example",
      template: `
        <div>
          {{ content().myInsertion({
            name: 'John'
          }) }}
        </div>
      `,
    })
    export class InsertionExampleComponent {
      content = useIntlayer("my-key");
    }
    ```

  </Tab>
</Tabs>

## 콘텐츠 스키마 검증

Intlayer v8에서는 딕셔너리에 대한 스키마 검증을 도입합니다. 이제 Zod를 사용해 구성 파일에 재사용 가능한 검증 스키마를 정의하고 이를 콘텐츠 파일에 적용할 수 있습니다. 이를 통해 콘텐츠가 항상 예상된 구조를 따르도록 보장하여 빌드 시점에 오류를 포착할 수 있습니다.

### 1. 스키마 정의

`intlayer.config.ts`에서 스키마를 정의하세요:

```typescript fileName="intlayer.config.ts"
import { z } from "zod";

export default {
  schemas: {
    "seo-metadata": z.object({
      title: z.string().min(50).max(60),
      description: z.string().min(150).max(160),
    }),
  },
};
```

### 2. 딕셔너리에 스키마 적용

딕셔너리 정의에서 스키마 키를 참조하세요:

```typescript fileName="src/example.content.ts"
import { type Dictionary } from "intlayer";

const aboutPageMetaContent = {
  key: "about-page-meta",
  schema: "seo-metadata", // <--
  content: {
    title: "About Our Company - Learn More About Us",
    description: "Discover our company's mission, values, and team.",
  },
} satisfies Dictionary;

export default aboutPageMetaContent;
```

콘텐츠가 스키마와 일치하지 않으면(예: title이 너무 짧은 경우) 빌드 프로세스가 오류를 발생시킵니다.

---

### 향상된 자동 콘텐츠 감지

v8에서 Intlayer는 콘텐츠 문자열에서 Markdown 문법, HTML 태그 및 변수 삽입을 지능적으로 감지합니다. 이는 `md()`, `html()`, 또는 `insert()`와 같은 헬퍼 함수를 종종 생략할 수 있음을 의미합니다.

이 동작은 기본적으로 활성화되어 있습니다. 이제 이 감지 기능을 전역(`intlayer.config.ts`) 또는 dictionary별로 세부 조정할 수 있습니다.

#### 세부 제어

특정 유형의 변환을 활성화하거나 비활성화할 수 있습니다:

```typescript fileName="intlayer.config.ts"
export default {
  dictionary: {
    // contentAutoTransformation: false (기본값)
    contentAutoTransformation: {
      markdown: true,
      html: true,
      insertion: false, // 자동 insertion 감지 비활성화
    },
  },
};
```

**v7 동작 (수동 래핑):**

```typescript fileName="src/example.content.ts"
import { md, insert } from "intlayer";

export default {
  key: "my-key",
  content: {
    myMarkdown: md("## Hello World"),
    myInsertion: insert("Hi {{name}}"),
  },
};
```

**v8 동작 (자동 감지):**

```typescript fileName="src/example.content.ts"
export default {
  key: "my-key",
  contentAutoTransformation: true, // dictionary 정의에서 또는 전역적으로 intlayer.config.ts에서 설정할 수도 있습니다
  content: {
    myMarkdown: "## Hello World", // 자동으로 Markdown으로 감지됩니다
    myHTML: "<p>Hello World</p>", // 자동으로 HTML로 감지됩니다
    myInsertion: "Hi {{name}}", // 자동으로 Insertion으로 감지됩니다
  },
};
```

기본 JSON 결과는 동일하게 유지되며 렌더링에 필요한 풍부한 타입 정보를 보존합니다:

```json
{
  "key": "my-key",
  "content": {
    "myMarkdown": {
      "nodeType": "markdown",
      "markdown": "## Hello World"
    },
    "myHTML": {
      "nodeType": "html",
      "html": "<p>Hello World</p>"
    },
    "myInsertion": {
      "nodeType": "insertion",
      "insertion": "Hi {{name}}"
    }
  }
}
```

---

## 현지화: 새로운 `useIntl` 훅

React, Next.js 및 Vue에서 새로운 `useIntl()` 훅이 사용할 수 있게 되었습니다. 이 훅은 현재 언어에 바인딩된 `Intl` 객체를 제공하여, 로케일을 수동으로 전달할 필요 없이 숫자, 날짜 등 형식을 자동으로 현재 언어에 맞춰 처리합니다.

<Tabs group='framework'>
  <Tab label="Next.js" value="nextjs">

    ```tsx
    import { useIntl } from "next-intlayer";

    const intl = useIntl();

    const formattedPrice = new intl.NumberFormat({
      style: "currency",
      currency: "USD",
    }).format(123.45);
    ```

  </Tab>
  <Tab label="React" value="react">

    ```tsx
    import { useIntl } from "react-intlayer";

    const intl = useIntl();

    const formattedPrice = new intl.NumberFormat({
      style: "currency",
      currency: "USD",
    }).format(123.45);
    ```

  </Tab>
  <Tab label="Vue" value="vue">

    ```vue
    <script setup>
    import { useIntl } from "vue-intlayer";

    const intl = useIntl();

    const formattedPrice = new intl.NumberFormat({
      style: "currency",
      currency: "USD",
    }).format(123.45);
    </script>
    ```

  </Tab>
  <Tab label="Preact" value="preact">

    ```tsx
    import { useIntl } from "preact-intlayer";

    const intl = useIntl();

    const formattedPrice = new intl.NumberFormat({
      style: "currency",
      currency: "USD",
    }).format(123.45);
    ```

  </Tab>
  <Tab label="Solid" value="solid">

    ```tsx
    import { useIntl } from "solid-intlayer";

    const intl = useIntl();

    const formattedPrice = new intl.NumberFormat({
      style: "currency",
      currency: "USD",
    }).format(123.45);
    ```

  </Tab>
  <Tab label="Svelte" value="svelte">

    ```svelte
    <script>
      import { useIntl } from "svelte-intlayer";

      const intl = useIntl();

      const formattedPrice = new intl.NumberFormat({
        style: "currency",
        currency: "USD",
      }).format(123.45);
    </script>
    ```

  </Tab>
  <Tab label="Angular" value="angular">

    ```typescript
    import { Component, computed } from "@angular/core";
    import { useIntl } from "angular-intlayer";

    @Component({
      selector: "app-intl-example",
      template: `<div>{{ formattedPrice() }}</div>`,
    })
    export class IntlExampleComponent {
      intl = useIntl();

      formattedPrice = computed(() =>
        new (this.intl().NumberFormat)({
          style: "currency",
          currency: "USD",
        }).format(123.45)
      );
    }
    ```

  </Tab>
</Tabs>

---

## 도구: VSCode 확장 기능 향상

Intlayer VSCode 확장 기능은 v8에서 국제화 워크플로우를 간소화하기 위해 주요 업데이트를 받았습니다:

- **시작 시간**: 프로젝트를 열 때의 성능 개선.
- **캐싱**: 거의 즉시 검증 및 자동완성을 제공하는 향상된 캐싱 레이어.
- **사용되지 않는 키 및 중복 키 감지**: 사전 전체에서 **사용되지 않는 키**와 **중복 키**를 자동으로 감지하는 새 기능으로, 콘텐츠를 깔끔하고 효율적으로 유지하는 데 도움을 줍니다.

---

## 컴파일러 최적화

Intlayer v8에는 Markdown 및 HTML 컴파일러를 위한 새로운 캐싱 레이어가 포함되어 있습니다. 이는 동일한 설정을 가진 동일한 콘텐츠 문자열을 한 번만 파싱하도록 하여, 재렌더링 시나 동일한 콘텐츠를 여러 곳에서 사용할 때 발생하는 오버헤드를 크게 줄여줍니다.

<Tabs group='bundler'>
  <Tab label="Next.js" value="nextjs">
  
    ```typescript fileName="babel.config.js"
      const {
      intlayerExtractBabelPlugin,
      intlayerOptimizeBabelPlugin,
      getExtractPluginOptions,
      getOptimizePluginOptions,
    } = require('@intlayer/babel');

    module.exports = {
      presets: ['next/babel'],
      plugins: [
        // 컴포넌트에서 딕셔너리로 콘텐츠 추출
        [intlayerExtractBabelPlugin, getExtractPluginOptions()],
        // useIntlayer를 직접 dictionary import로 대체하여 임포트를 최적화합니다
        [intlayerOptimizeBabelPlugin, getOptimizePluginOptions()],
      ],
    };
    ```

  </Tab>
  <Tab label="Vite" value="vite">
   
    ```typescript fileName="vite.config.js"
    import { defineConfig } from 'vite';
    import { intlayer, intlayerCompiler } from "vite-intlayer";

    export default defineConfig({
      plugins: [intlayer(), intlayerCompiler()],
    });
    ```

> Vue / Svelte의 경우 적절한 컴파일러 패키지를 설치해야 합니다:
>
> ```bash
> # Vue의 경우
> npm install @intlayer/vue-compiler
> ```
>
> ```bash
> # Svelte의 경우
> npm install @intlayer/svelte-compiler
> ```

  </Tab>
</Tabs>

---

## 유연성: 통합된 Import 모드

`live` boolean 속성은 더 포괄적인 `importMode` 속성으로 대체(deprecated)되었습니다. 이를 통해 딕셔너리를 정적(static), 동적(dynamic), 또는 라이브 동기화 방식으로 어떻게 로드할지 명시적으로 정의할 수 있습니다.

### 모드

- **`static`** (기본): 딕셔너리가 빌드 시 번들에 포함됩니다. 성능에 가장 적합합니다.
- **`dynamic`**: 런타임에 딕셔너리를 로드합니다(예: JSON fetch 또는 suspense).
- **`fetch`**: 런타임에 CMS/서버에서 딕셔너리를 가져와 동기화합니다.

**마이그레이션:**

| v7 구성       | v8 구성                                 |
| :------------ | :-------------------------------------- |
| `live: true`  | `importMode: 'fetch'`                   |
| `live: false` | `importMode: 'static'` (또는 'dynamic') |

참고: Intlayer v8에서는 `importMode` 속성이 `intlayer.config.ts`의 `build` 구성에서 `dictionary` 구성으로 이동되었습니다. 이를 통해 모든 사전에 대한 기본 import 모드를 정의하면서도 각 사전별로 이를 재정의할 수 있습니다.

**전역 구성 예시:**

```typescript fileName="intlayer.config.ts"
export default {
  dictionary: {
    importMode: "dynamic", // 전역 기본값
  },
  // ...
};
```

**사전 예시:**

```typescript fileName="src/example.content.ts"
export default {
    key: 'my-key',
    importMode: "fetch", // 전역 설정을 재정의함
    content: { ... }
}
```

---

## 사전 위치 제어

v8에서는 사전이 위치하는 곳과 동기화 방식을 명시적으로 관리할 수 있도록 `location` 속성이 도입되었습니다. 이는 로컬 파일과 원격 CMS 콘텐츠를 함께 사용하는 하이브리드 워크플로에 특히 유용합니다.

### 옵션

- **`local`**: 사전은 로컬에만 존재합니다. 원격 CMS로 푸시되지 않습니다.
- **`remote`**: 사전은 원격에서 관리됩니다. CMS에 푸시된 후 로컬 사전과 분리(detached)됩니다. 원격 사전은 CMS에서 pull 됩니다.
- **`local_and_remote`**: 사전은 양쪽 모두에 존재합니다. 로컬 변경사항은 푸시되고 원격 변경사항은 pull 되어 동기화됩니다.

**예제:**

```typescript fileName="src/example.content.ts"
export default {
    key: 'my-key',
    location: "local", // 이 딕셔너리를 로컬 전용으로 유지합니다
    content: { ... }
}
```

---

## 시스템 구성 분리

Intlayer v8은 콘텐츠 소스 구성과 내부 시스템 및 출력 경로 구성을 분리합니다. 이는 `content` 속성을 정리하고 어떤 설정이 사용자 관리용인지, 어떤 설정이 Intlayer 시스템에서 관리하는지 명확히 합니다.

다음 속성들은 `content`에서 `intlayer.config.ts`의 새 `system` 속성으로 이동되었습니다:

- `dictionariesDir`
- `moduleAugmentationDir`
- `unmergedDictionariesDir`
- `typesDir`
- `mainDir`
- `configDir`
- `cacheDir`
- `outputFilesPatternWithPath`

**v7 동작:**

```typescript fileName="intlayer.config.ts"
export default {
  content: {
    contentDir: ["src"],
    dictionariesDir: ".intlayer/dictionary", // 소스 구성과 혼합됨
  },
};
```

**v8 동작:**

```typescript fileName="intlayer.config.ts"
export default {
  content: {
    contentDir: ["src"],
  },
  system: {
    dictionariesDir: ".intlayer/dictionary", // 명확하게 분리됨
  },
};
```

---

## 콘텐츠 및 코드 디렉토리 분리

Intlayer v8는 콘텐츠 정의 파일에 대한 설정을 코드 변환 설정과 분리합니다. 이를 통해 watching과 scanning을 보다 정확하게 수행할 수 있어 빌드 성능이 향상됩니다.

이전에는 `contentDir`가 `.content.*` 파일의 감시와 `useIntlayer` 호출을 위한 코드 스캔에 모두 사용되었습니다. 이제는:

- **`contentDir`**: 콘텐츠 선언 파일을 위한 전용 설정입니다.
- **`codeDir`**: 변환(예: pruning, optimization)을 필요로 하는 애플리케이션 코드 전용입니다.

**마이그레이션:**

이전에 `contentDir`를 설정한 경우, Intlayer v8은 이를 `codeDir`의 기본값으로 사용하지만 경고를 출력합니다. 구성에서 `codeDir`를 명시적으로 정의해야 합니다.

**v7 동작:**

```typescript fileName="intlayer.config.ts"
export default {
  content: {
    contentDir: ["src", "@packages/design-system"], // 콘텐츠와 코드 모두에 사용됨
  },
};
```

**v8 동작:**

```typescript fileName="intlayer.config.ts"
export default {
  content: {
    contentDir: ["src/content", "@packages/design-system"], // 여기서는 src/content/*.content.* 파일과 @packages/design-system/dist/*.content.* 파일만 감시합니다
    codeDir: ["src", "@packages/design-system"], // 여기서는 코드 변환을 위해 코드만 스캔하며 @packages/design-system/src/*.content.* 파일을 스캔합니다
  },
};
```

---

## 프레임워크: Svelte 개선사항

Svelte에서의 Markdown 및 HTML 콘텐츠는 이제 문자열화될 때 자동으로 HTML로 파싱됩니다. 이로 인해 Svelte의 `{@html}` 구문과 함께 사용하기가 훨씬 쉬워졌으며, 이제 콘텐츠 노드를 직접 전달하기만 하면 됩니다.

---

## v7에서의 마이그레이션 노트

### 구성 변경사항

- **`live` 속성**: 딕셔너리에서의 `live` 속성은 제거되었습니다. 대신 `importMode: 'fetch'`를 사용하세요.
- **importMode**: 설정에서 `build.importMode` 속성은 더 이상 권장되지 않습니다. 대신 `dictionary.importMode`를 사용하세요.
- **`contentDir` and `codeDir`**: `contentDir`는 이제 콘텐츠 파일 전용입니다. 코드 변환을 위한 새로운 `codeDir` 속성이 추가되었습니다. `codeDir`가 설정되어 있지 않으면 Intlayer는 `contentDir`로 폴백(fallback)하고 경고를 기록합니다.
- **스키마 검증**: 새로운 `schema` 기능을 사용하려면 프로젝트에 `zod`가 설치되어 있는지 확인하세요.

---

## 유용한 링크

- [구성 참조](https://github.com/aymericzip/intlayer/blob/main/docs/docs/ko/configuration.md)
- [콘텐츠 파일 문서](https://github.com/aymericzip/intlayer/blob/main/docs/docs/ko/dictionary/content_file.md)
- [HTML 콘텐츠 문서](https://github.com/aymericzip/intlayer/blob/main/docs/docs/ko/dictionary/html.md)
- [Markdown 콘텐츠 문서](https://github.com/aymericzip/intlayer/blob/main/docs/docs/ko/dictionary/markdown.md)
- [사용자 정의 URL 재작성 문서](https://github.com/aymericzip/intlayer/blob/main/docs/docs/ko/custom_url_rewrites.md)
