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

    ์ปดํฌ๋„ŒํŠธ๋ณ„ i18n vs ์ค‘์•™์ง‘์ค‘์‹ i18n

    ์ปดํฌ๋„ŒํŠธ๋ณ„ ์ ‘๊ทผ๋ฒ•์€ ์ƒˆ๋กœ์šด ๊ฐœ๋…์ด ์•„๋‹™๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, Vue ์ƒํƒœ๊ณ„์—์„œ vue-i18n์€ SFC i18n (Single File Component)์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. Nuxt๋„ ์ปดํฌ๋„ŒํŠธ๋ณ„ ๋ฒˆ์—ญ์„ ์ œ๊ณตํ•˜๋ฉฐ, Angular๋Š” Feature Modules๋ฅผ ํ†ตํ•ด ์œ ์‚ฌํ•œ ํŒจํ„ด์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

    Flutter ์•ฑ์—์„œ๋„ ์ข…์ข… ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํŒจํ„ด์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

    bash
    lib/
    โ””โ”€โ”€ features/
        โ””โ”€โ”€ login/
            โ”œโ”€โ”€ login_screen.dart
            โ””โ”€โ”€ login_screen.i18n.dart  # <- ๋ฒˆ์—ญ์€ ์—ฌ๊ธฐ ์ €์žฅ๋ฉ๋‹ˆ๋‹ค
    
    lib/features/login/login_screen.i18n.dart
    import 'package:i18n_extension/i18n_extension.dart';
    
    extension Localization on String {
      static var _t = Translations.byText("en") +
          {
            "Hello": {
              "en": "Hello",
              "fr": "Bonjour",
            },
          };
    
      String get i18n => localize(this, _t);
    }
    

    ํ•˜์ง€๋งŒ React ํ™˜๊ฒฝ์—์„œ๋Š” ์ฃผ๋กœ ์—ฌ๋Ÿฌ ๋‹ค๋ฅธ ์ ‘๊ทผ ๋ฐฉ์‹์„ ๋ณด๋ฉฐ, ์ €๋Š” ์ด๋ฅผ ์„ธ ๊ฐ€์ง€ ๋ฒ”์ฃผ๋กœ ๋‚˜๋ˆ„๊ฒ ์Šต๋‹ˆ๋‹ค:

    ์ค‘์•™์ง‘์ค‘์‹ ์ ‘๊ทผ (i18next, next-intl, react-intl, lingui)

    • (๋„ค์ž„์ŠคํŽ˜์ด์Šค ์—†์ด) ๋‹จ์ผ ์†Œ์Šค์—์„œ ์ฝ˜ํ…์ธ ๋ฅผ ๊ฐ€์ ธ์˜จ๋‹ค๊ณ  ๊ฐ€์ •ํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ๋ณธ์ ์œผ๋กœ ์•ฑ์ด ๋กœ๋“œ๋  ๋•Œ ๋ชจ๋“  ํŽ˜์ด์ง€์˜ ์ฝ˜ํ…์ธ ๋ฅผ ๋กœ๋“œํ•ฉ๋‹ˆ๋‹ค.

    ์„ธ๋ถ„ํ™”๋œ ์ ‘๊ทผ (intlayer, inlang)

    • ํ‚ค ๋‹จ์œ„ ๋˜๋Š” ์ปดํฌ๋„ŒํŠธ ๋‹จ์œ„๋กœ ์ฝ˜ํ…์ธ  ์กฐํšŒ๋ฅผ ์„ธ๋ถ„ํ™”ํ•ฉ๋‹ˆ๋‹ค.

    ์ด ๋ธ”๋กœ๊ทธ์—์„œ๋Š” ์ปดํŒŒ์ผ๋Ÿฌ ๊ธฐ๋ฐ˜ ์†”๋ฃจ์…˜์— ์ดˆ์ ์„ ๋งž์ถ”์ง€ ์•Š๊ฒ ์Šต๋‹ˆ๋‹ค. ํ•ด๋‹น ๋‚ด์šฉ์€ ์ด๋ฏธ ์—ฌ๊ธฐ์—์„œ ๋‹ค๋ค˜์Šต๋‹ˆ๋‹ค: Compiler vs Declarative i18n. ์ปดํŒŒ์ผ๋Ÿฌ ๊ธฐ๋ฐ˜ i18n(์˜ˆ: Lingui)์€ ๋‹จ์ˆœํžˆ ์ฝ˜ํ…์ธ  ์ถ”์ถœ๊ณผ ๋กœ๋”ฉ์„ ์ž๋™ํ™”ํ•  ๋ฟ์ด๋ผ๋Š” ์ ์„ ์œ ์˜ํ•˜์„ธ์š”. ๋‚ด๋ถ€์ ์œผ๋กœ๋Š” ๋‹ค๋ฅธ ์ ‘๊ทผ ๋ฐฉ์‹๋“ค๊ณผ ๋™์ผํ•œ ํ•œ๊ณ„๋ฅผ ๊ณต์œ ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์Šต๋‹ˆ๋‹ค.

    ์ฝ˜ํ…์ธ ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๋ฐฉ์‹์„ ๋” ์„ธ๋ถ„ํ™”ํ• ์ˆ˜๋ก ์ปดํฌ๋„ŒํŠธ์— ์ถ”๊ฐ€์ ์ธ ์ƒํƒœ์™€ ๋กœ์ง์„ ์‚ฝ์ž…ํ•  ์œ„ํ—˜์ด ์ปค์ง‘๋‹ˆ๋‹ค.

    ์„ธ๋ถ„ํ™”๋œ ์ ‘๊ทผ ๋ฐฉ์‹์€ ์ค‘์•™์ง‘์ค‘์‹ ์ ‘๊ทผ๋ณด๋‹ค ๋” ์œ ์—ฐํ•˜์ง€๋งŒ, ์ข…์ข… ํŠธ๋ ˆ์ด๋“œ์˜คํ”„๊ฐ€ ๋”ฐ๋ฆ…๋‹ˆ๋‹ค. ํ•ด๋‹น ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค์ด "tree shaking"์„ ํ™๋ณดํ•˜๋”๋ผ๋„, ์‹ค์ œ๋กœ๋Š” ์ข…์ข… ๋ชจ๋“  ์–ธ์–ด์— ๋Œ€ํ•ด ํŽ˜์ด์ง€๋ฅผ ๋กœ๋“œํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

    ์š”์•ฝํ•˜๋ฉด, ๊ฒฐ์ •์€ ๋Œ€์ฒด๋กœ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋‚˜๋‰ฉ๋‹ˆ๋‹ค:

    • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ํŽ˜์ด์ง€ ์ˆ˜๊ฐ€ ์–ธ์–ด ์ˆ˜๋ณด๋‹ค ๋งŽ์€ ๊ฒฝ์šฐ์—๋Š” ์„ธ๋ถ„ํ™”๋œ ์ ‘๊ทผ ๋ฐฉ์‹์„ ์„ ํ˜ธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
    • ์–ธ์–ด ์ˆ˜๊ฐ€ ํŽ˜์ด์ง€ ์ˆ˜๋ณด๋‹ค ๋งŽ์€ ๊ฒฝ์šฐ์—๋Š” ์ค‘์•™์ง‘์ค‘์‹ ์ ‘๊ทผ์„ ํƒํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

    ๋ฌผ๋ก  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ €์ž๋“ค์€ ์ด๋Ÿฌํ•œ ํ•œ๊ณ„๋ฅผ ์ธ์ง€ํ•˜๊ณ  ์šฐํšŒ ๋ฐฉ๋ฒ•์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ๊ทธ์ค‘์—๋Š” ๋„ค์ž„์ŠคํŽ˜์ด์Šค๋กœ ๋ถ„๋ฆฌํ•˜๊ธฐ, JSON ํŒŒ์ผ์„ ๋™์ ์œผ๋กœ ๋กœ๋“œํ•˜๊ธฐ (await import()), ๋˜๋Š” ๋นŒ๋“œ ์‹œ ์ฝ˜ํ…์ธ ๋ฅผ ์ •๋ฆฌ(purge)ํ•˜๋Š” ๋ฐฉ๋ฒ• ๋“ฑ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

    ์•„๋ž˜ ๊ทธ๋ž˜ํ”„๋Š” 110๊ฐœ์˜ ํŽ˜์ด์ง€๋ฅผ 110๊ฐœ ์–ธ์–ด๋กœ ๋ฒˆ์—ญํ•œ ์ด๋ก ์ƒ์˜ ์•ฑ(ํŽ˜์ด์ง€๋‹น ์•ฝ 30KB์˜ ํ…์ŠคํŠธ)์˜ ์ฝ˜ํ…์ธ  ์šฉ๋Ÿ‰์„ ์ถ”์ •ํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋กœ์ผ€์ผ๋ณ„ ๋™์  ๋กœ๋”ฉ์€ ์–ธ์–ด ์ถ•์„ ์—†์• ๊ณ , ์ปดํฌ๋„ŒํŠธ๋‚˜ ๋ผ์šฐํŠธ ๋‹จ์œ„๋กœ ์ฝ˜ํ…์ธ ๋ฅผ ์Šค์ฝ”ํ”„ํ•˜๋ฉด ํŽ˜์ด์ง€ ์ถ•์ด ์—†์–ด์ง€๋ฉฐ, ๋‘ ๊ฐ€์ง€๋ฅผ ๊ฒฐํ•ฉํ–ˆ์„ ๋•Œ๋งŒ ์šฉ๋Ÿ‰์ด ์ผ์ •ํ•˜๊ฒŒ ์œ ์ง€๋ฉ๋‹ˆ๋‹ค.

    ์•„ํ‚คํ…์ฒ˜๋ณ„ ์ด๋ก ์ƒ์˜ ์ฝ˜ํ…์ธ  ๋ˆ„์ˆ˜

    ๋™์‹œ์—, ์ฝ˜ํ…์ธ ๋ฅผ ๋™์ ์œผ๋กœ ๋กœ๋“œํ•˜๋ฉด ์„œ๋ฒ„์— ๋Œ€ํ•œ ์ถ”๊ฐ€ ์š”์ฒญ์ด ๋ฐœ์ƒํ•œ๋‹ค๋Š” ์ ์„ ์•Œ์•„๋‘์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ถ”๊ฐ€์ ์ธ useState๋‚˜ hook ํ•˜๋‚˜๋งˆ๋‹ค ์„œ๋ฒ„ ์š”์ฒญ์ด ํ•˜๋‚˜ ๋” ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

    ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด, Intlayer๋Š” ์—ฌ๋Ÿฌ ์ฝ˜ํ…์ธ  ์ •์˜๋ฅผ ๋™์ผํ•œ ํ‚ค ์•„๋ž˜์— ๊ทธ๋ฃนํ™”ํ•  ๊ฒƒ์„ ์ œ์•ˆํ•ฉ๋‹ˆ๋‹ค. Intlayer๋Š” ๊ทธ๋Ÿฐ ๋‹ค์Œ ํ•ด๋‹น ์ฝ˜ํ…์ธ ๋ฅผ ๋ณ‘ํ•ฉํ•ฉ๋‹ˆ๋‹ค.

    ํ•˜์ง€๋งŒ ์ด๋Ÿฌํ•œ ๋ชจ๋“  ํ•ด๊ฒฐ์ฑ…์„ ์ข…ํ•ฉํ•ด๋ณด๋ฉด, ๊ฐ€์žฅ ๋„๋ฆฌ ์‚ฌ์šฉ๋˜๋Š” ์ ‘๊ทผ ๋ฐฉ์‹์€ ์ค‘์•™์ง‘์ค‘์‹ ๋ฐฉ์‹์ด๋ผ๋Š” ์ ์ด ๋ถ„๋ช…ํ•ฉ๋‹ˆ๋‹ค.

    ๊ทธ๋ ‡๋‹ค๋ฉด ์ค‘์•™์ง‘์ค‘์‹ ์ ‘๊ทผ ๋ฐฉ์‹์ด ์™œ ์ด๋ ‡๊ฒŒ ์ธ๊ธฐ ์žˆ์„๊นŒ์š”?

    • ๋จผ์ €, i18next๋Š” ๋„๋ฆฌ ์‚ฌ์šฉ๋œ ์ตœ์ดˆ์˜ ์†”๋ฃจ์…˜ ์ค‘ ํ•˜๋‚˜๋กœ, PHP์™€ Java ์•„ํ‚คํ…์ฒ˜(MVC)์—์„œ ์˜๊ฐ์„ ๋ฐ›์€ ์ฒ ํ•™(๊ด€์‹ฌ์‚ฌ์˜ ์—„๊ฒฉํ•œ ๋ถ„๋ฆฌ, ์ฝ˜ํ…์ธ ๋ฅผ ์ฝ”๋“œ์™€ ๋ถ„๋ฆฌํ•ด์„œ ์œ ์ง€)์„ ๋”ฐ๋ž์Šต๋‹ˆ๋‹ค. 2011๋…„์— ๋“ฑ์žฅํ•˜์—ฌ React์™€ ๊ฐ™์€ ์ปดํฌ๋„ŒํŠธ ๊ธฐ๋ฐ˜ ์•„ํ‚คํ…์ฒ˜๋กœ์˜ ๋Œ€๋Œ€์ ์ธ ์ „ํ™˜๋ณด๋‹ค๋„ ๋จผ์ € ๊ทธ ํ‘œ์ค€์„ ํ™•๋ฆฝํ–ˆ์Šต๋‹ˆ๋‹ค.
    • ํ•œ ๋ฒˆ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ๋„๋ฆฌ ์ฑ„ํƒ๋˜๋ฉด ์—์ฝ”์‹œ์Šคํ…œ์„ ๋‹ค๋ฅธ ํŒจํ„ด์œผ๋กœ ์ „ํ™˜ํ•˜๊ธฐ๊ฐ€ ์–ด๋ ค์›Œ์ง‘๋‹ˆ๋‹ค.
    • ์ค‘์•™์ง‘์ค‘์‹ ์ ‘๊ทผ ๋ฐฉ์‹์€ Crowdin, Phrase ๋˜๋Š” Localized์™€ ๊ฐ™์€ ๋ฒˆ์—ญ ๊ด€๋ฆฌ ์‹œ์Šคํ…œ(Translation Management Systems)์—์„œ๋„ ์ž‘์—…์„ ๋” ์ˆ˜์›”ํ•˜๊ฒŒ ๋งŒ๋“ญ๋‹ˆ๋‹ค.
    • ์ปดํฌ๋„ŒํŠธ๋ณ„ ์ ‘๊ทผ ๋ฐฉ์‹์˜ ๋กœ์ง์€ ์ค‘์•™์ง‘์ค‘์‹ ๋ฐฉ์‹๋ณด๋‹ค ๋” ๋ณต์žกํ•˜๊ณ  ๊ฐœ๋ฐœ์— ์ถ”๊ฐ€ ์‹œ๊ฐ„์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ํŠนํžˆ ์ฝ˜ํ…์ธ ๊ฐ€ ์–ด๋””์— ์œ„์น˜ํ•˜๋Š”์ง€ ์‹๋ณ„ํ•˜๋Š” ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ด์•ผ ํ•  ๋•Œ ๊ทธ๋ ‡์Šต๋‹ˆ๋‹ค.
    ์ด๋Ÿฌํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์–ด๋””์—์„œ ์™”๋Š”์ง€ ์ดํ•ดํ•˜๋ ค๋ฉด JavaScript i18n์˜ ์—ญ์‚ฌ๋ฅผ ์ฝ์–ด๋ณด์„ธ์š”.

    ์•Œ๊ฒ ์ง€๋งŒ, ์ค‘์•™์ง‘์ค‘์‹ ์ ‘๊ทผ ๋ฐฉ์‹์„ ๊ณ ์ˆ˜ํ•˜๋ฉด ์•ˆ ๋˜๋Š” ์ด์œ ๋Š” ๋ฌด์—‡์ธ๊ฐ€์š”?

    ๋‹ค์Œ์€ ๊ท€ํ•˜์˜ ์•ฑ์— ๋ฌธ์ œ๊ฐ€ ๋  ์ˆ˜ ์žˆ๋Š” ์ด์œ ์ž…๋‹ˆ๋‹ค:

    • ๋ฏธ์‚ฌ์šฉ ๋ฐ์ดํ„ฐ: ํŽ˜์ด์ง€๊ฐ€ ๋กœ๋“œ๋  ๋•Œ ์ข…์ข… ๋‹ค๋ฅธ ๋ชจ๋“  ํŽ˜์ด์ง€์˜ ์ฝ˜ํ…์ธ ๋ฅผ ํ•จ๊ป˜ ๋กœ๋“œํ•ฉ๋‹ˆ๋‹ค. (10ํŽ˜์ด์ง€ ์•ฑ์ด๋ผ๋ฉด ๋กœ๋“œ๋œ ์ฝ˜ํ…์ธ ์˜ 90%๊ฐ€ ์‚ฌ์šฉ๋˜์ง€ ์•Š์Œ). ๋ชจ๋‹ฌ์„ ์ง€์—ฐ ๋กœ๋“œ(lazy load)ํ•˜๋‚˜์š”? i18n ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ์ƒ๊ด€ํ•˜์ง€ ์•Š๊ณ  ์–ด์จŒ๋“  ๋ฌธ์ž์—ด์„ ๋จผ์ € ๋กœ๋“œํ•ฉ๋‹ˆ๋‹ค.
    • ์„ฑ๋Šฅ: ๊ฐ ๋ฆฌ๋ Œ๋”๋ง๋งˆ๋‹ค ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๊ฑฐ๋Œ€ํ•œ JSON ํŽ˜์ด๋กœ๋“œ๋กœ ํ•˜์ด๋“œ๋ ˆ์ด์…˜๋˜์–ด, ์•ฑ์ด ์ปค์งˆ์ˆ˜๋ก ๋ฐ˜์‘์„ฑ์— ์˜ํ–ฅ์„ ์ค๋‹ˆ๋‹ค.
    • ์œ ์ง€๋ณด์ˆ˜: ํฐ JSON ํŒŒ์ผ์„ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์€ ๊ณ ํ†ต์Šค๋Ÿฝ์Šต๋‹ˆ๋‹ค. ๋ฒˆ์—ญ์„ ์ถ”๊ฐ€ํ•˜๋ ค๋ฉด ํŒŒ์ผ์„ ์ด๊ณณ์ €๊ณณ ์ด๋™ํ•ด์•ผ ํ•˜๊ณ , ๋ˆ„๋ฝ๋œ ๋ฒˆ์—ญ์ด ์—†๋Š”์ง€, orphan keys๊ฐ€ ๋‚จ์•„์žˆ์ง€ ์•Š์€์ง€ ํ™•์ธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
    • ๋””์ž์ธ ์‹œ์Šคํ…œ: ์ด๋Š” ๋””์ž์ธ ์‹œ์Šคํ…œ(์˜ˆ: LoginForm ์ปดํฌ๋„ŒํŠธ)๊ณผ์˜ ๋น„ํ˜ธํ™˜์„ฑ์„ ์ดˆ๋ž˜ํ•˜๋ฉฐ, ์„œ๋กœ ๋‹ค๋ฅธ ์•ฑ๋“ค ๊ฐ„์˜ ์ปดํฌ๋„ŒํŠธ ๋ณต์ œ๋ฅผ ์ œํ•œํ•ฉ๋‹ˆ๋‹ค.

    "ํ•˜์ง€๋งŒ ์šฐ๋ฆฌ๋Š” Namespaces๋ฅผ ๋ฐœ๋ช…ํ–ˆ์ž–์•„!"

    ๋ฌผ๋ก ์ด๊ณ , ์ด๋Š” ์—„์ฒญ๋‚œ ์ง„์ „์ž…๋‹ˆ๋‹ค. ์ด์ œ Vite + React + React Router v7 + Intlayer ์„ค์ •์—์„œ ๋ฉ”์ธ ๋ฒˆ๋“ค ํฌ๊ธฐ๋ฅผ ๋น„๊ตํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. 20ํŽ˜์ด์ง€์งœ๋ฆฌ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์‹œ๋ฎฌ๋ ˆ์ด์…˜ํ–ˆ์Šต๋‹ˆ๋‹ค.

    ์ฒซ ๋ฒˆ์งธ ์˜ˆ์‹œ๋Š” locale๋ณ„๋กœ lazy-loaded ๋ฒˆ์—ญ์„ ํฌํ•จํ•˜์ง€ ์•Š์•˜์œผ๋ฉฐ ๋„ค์ž„์ŠคํŽ˜์ด์Šค ๋ถ„ํ• ๋„ ์—†์Šต๋‹ˆ๋‹ค. ๋‘ ๋ฒˆ์งธ๋Š” content purging + ๋ฒˆ์—ญ์˜ ๋™์  ๋กœ๋”ฉ์„ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค.

    ์ตœ์ ํ™”๋œ ๋ฒˆ๋“ค์ตœ์ ํ™”๋˜์ง€ ์•Š์€ ๋ฒˆ๋“ค
    ์ตœ์ ํ™”๋˜์ง€ ์•Š์€ ๋ฒˆ๋“ค์ตœ์ ํ™”๋œ ๋ฒˆ๋“ค

    ๋„ค์ž„์ŠคํŽ˜์ด์Šค ๋•๋ถ„์—, ์šฐ๋ฆฌ๋Š” ๋‹ค์Œ ๊ตฌ์กฐ์—์„œ:

    bash
    locale/
    โ”œโ”€โ”€ en.json
    โ”œโ”€โ”€ fr.json
    โ””โ”€โ”€ es.json
    

    ๋‹ค์Œ ๊ตฌ์กฐ๋กœ:

    bash
    locale/
    โ”œโ”€โ”€ en/
    โ”‚   โ”œโ”€โ”€ common.json
    โ”‚   โ”œโ”€โ”€ navbar.json
    โ”‚   โ”œโ”€โ”€ footer.json
    โ”‚   โ”œโ”€โ”€ home.json
    โ”‚   โ””โ”€โ”€ about.json
    โ”œโ”€โ”€ fr/
    โ”‚   โ””โ”€โ”€ ...
    โ””โ”€โ”€ es/
        โ””โ”€โ”€ ...
    
    

    ์ด์ œ ์•ฑ์˜ ์–ด๋–ค ์ฝ˜ํ…์ธ ๋ฅผ ์–ธ์ œ ์–ด๋””์„œ ๋กœ๋“œํ• ์ง€ ์„ธ๋ฐ€ํ•˜๊ฒŒ ๊ด€๋ฆฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ฒฐ๋ก ์ ์œผ๋กœ, ๋ณต์žก์„ฑ ๋•Œ๋ฌธ์— ๋Œ€๋‹ค์ˆ˜์˜ ํ”„๋กœ์ ํŠธ๋Š” ์ด ๋ถ€๋ถ„์„ ๊ฑด๋„ˆ๋›ฐ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค(์˜ˆ: ์ข‹์€ ๊ด€ํ–‰์„ ๋”ฐ๋ฅด๋Š” ๊ฒƒ๋งŒ์œผ๋กœ ๋ฐœ์ƒํ•˜๋Š” ์–ด๋ ค์›€์„ ๋ณด๋ ค๋ฉด next-i18next ๊ฐ€์ด๋“œ๋ฅผ ์ฐธ์กฐํ•˜์„ธ์š”). ๊ทธ ๊ฒฐ๊ณผ, ์ด๋Ÿฌํ•œ ํ”„๋กœ์ ํŠธ๋“ค์€ ์•ž์„œ ์„ค๋ช…ํ•œ ๋Œ€๊ทœ๋ชจ JSON ๋กœ๋”ฉ ๋ฌธ์ œ์— ์ง๋ฉดํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

    ์ฐธ๊ณ : ์ด ๋ฌธ์ œ๋Š” i18next์—๋งŒ ๊ตญํ•œ๋œ ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ์œ„์— ์—ด๊ฑฐํ•œ ๋ชจ๋“  ์ค‘์•™์ง‘์ค‘์‹ ์ ‘๊ทผ ๋ฐฉ์‹์— ๊ณตํ†ต์ ์œผ๋กœ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

    ํ•˜์ง€๋งŒ ๋ชจ๋“  ์„ธ๋ถ„ํ™”๋œ ์ ‘๊ทผ ๋ฐฉ์‹์ด ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๊ฒƒ์€ ์•„๋‹ˆ๋ผ๋Š” ์ ์„ ์ƒ๊ธฐ์‹œํ‚ค๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, vue-i18n SFC๋‚˜ inlang์™€ ๊ฐ™์€ ์ ‘๊ทผ ๋ฐฉ์‹์€ ๋ณธ์งˆ์ ์œผ๋กœ ๋กœ์ผ€์ผ๋ณ„ ๋ฒˆ์—ญ์„ ์ง€์—ฐ ๋กœ๋“œ(lazy load)ํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ ๋ฒˆ๋“ค ํฌ๊ธฐ ๋ฌธ์ œ๋ฅผ ๋‹จ์ง€ ๋‹ค๋ฅธ ๋ฌธ์ œ๋กœ ๋ฐ”๊พธ๋Š” ๊ฒƒ์— ๋ถˆ๊ณผํ•ฉ๋‹ˆ๋‹ค.

    ๋˜ํ•œ, ๊ด€์‹ฌ์‚ฌ์˜ ๋ถ„๋ฆฌ๊ฐ€ ์ œ๋Œ€๋กœ ์ด๋ฃจ์–ด์ง€์ง€ ์•Š์œผ๋ฉด ๋ฒˆ์—ญ๊ฐ€์—๊ฒŒ ๊ฒ€ํ† ์šฉ์œผ๋กœ ๋ฒˆ์—ญ์„ ์ถ”์ถœํ•˜๊ณ  ์ œ๊ณตํ•˜๋Š” ์ž‘์—…์ด ํ›จ์”ฌ ๋” ์–ด๋ ค์›Œ์ง‘๋‹ˆ๋‹ค.

    Intlayer์˜ ์ปดํฌ๋„ŒํŠธ๋ณ„ ์ ‘๊ทผ ๋ฐฉ์‹์ด ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๋ฐฉ๋ฒ•

    Intlayer๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์—ฌ๋Ÿฌ ๋‹จ๊ณ„๋ฅผ ๊ฑฐ์นฉ๋‹ˆ๋‹ค:

    1. ์„ ์–ธ(Declaration): *.content.{ts|jsx|cjs|json|json5|...} ํŒŒ์ผ์„ ์‚ฌ์šฉํ•˜์—ฌ ์ฝ”๋“œ๋ฒ ์ด์Šค ์–ด๋””์—์„œ๋‚˜ ์ฝ˜ํ…์ธ ๋ฅผ ์„ ์–ธํ•˜์„ธ์š”. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์ฝ˜ํ…์ธ ๋ฅผ ์ฝ”๋“œ์™€ ๋™์ผ ์œ„์น˜์— ๋‘๋ฉด์„œ ๊ด€์‹ฌ์‚ฌ์˜ ๋ถ„๋ฆฌ๋ฅผ ๋ณด์žฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ฝ˜ํ…์ธ  ํŒŒ์ผ์€ ๋กœ์ผ€์ผ๋ณ„์ผ ์ˆ˜๋„ ์žˆ๊ณ  ๋‹ค๊ตญ์–ด(multilingual)์ผ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.
    2. ์ฒ˜๋ฆฌ: Intlayer๋Š” ๋นŒ๋“œ ๋‹จ๊ณ„์—์„œ JS ๋กœ์ง์„ ์ฒ˜๋ฆฌํ•˜๊ณ , ๋ˆ„๋ฝ๋œ ๋ฒˆ์—ญ ํด๋ฐฑ์„ ์ฒ˜๋ฆฌํ•˜๋ฉฐ, TypeScript ํƒ€์ž…์„ ์ƒ์„ฑํ•˜๊ณ , ์ค‘๋ณต๋œ ์ฝ˜ํ…์ธ ๋ฅผ ๊ด€๋ฆฌํ•˜๊ณ , CMS์—์„œ ์ฝ˜ํ…์ธ ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๋“ฑ ๋‹ค์–‘ํ•œ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.
    3. ์ •๋ฆฌ(Purging): ์•ฑ์„ ๋นŒ๋“œํ•  ๋•Œ Intlayer๋Š” ์‚ฌ์šฉ๋˜์ง€ ์•Š๋Š” ์ฝ˜ํ…์ธ ๋ฅผ ์ •๋ฆฌํ•ฉ๋‹ˆ๋‹ค(์•ฝ๊ฐ„ Tailwind๊ฐ€ ํด๋ž˜์Šค ๊ด€๋ฆฌ๋ฅผ ํ•˜๋Š” ๋ฐฉ์‹๊ณผ ์œ ์‚ฌ). ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ฝ˜ํ…์ธ ๋ฅผ ๋Œ€์ฒดํ•ฉ๋‹ˆ๋‹ค:

    ์„ ์–ธ:

    tsx
    // src/MyComponent.tsx
    export const MyComponent = () => {
      const content = useIntlayer("my-key");
      return <h1>{content.title}</h1>;
    };
    
    tsx
    // src/myComponent.content.ts
    export const {
      key: "my-key",
      content: t({
        en: { title: "My title" },
        fr: { title: "Mon titre" }
      })
    }
    
    

    ์ฒ˜๋ฆฌ: Intlayer๋Š” .content ํŒŒ์ผ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ์‚ฌ์ „(dictionary)์„ ๋นŒ๋“œํ•˜๊ณ  ๋‹ค์Œ์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค:

    json5
    // .intlayer/dynamic_dictionary/ko/my-key.json
    {
      "key": "my-key",
      "content": { "title": "๋‚ด ์ œ๋ชฉ" },
    }
    

    ๋Œ€์ฒด: Intlayer๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋นŒ๋“œ ์ค‘์— ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

    - ์ •์  ์ž„ํฌํŠธ ๋ชจ๋“œ:

    tsx
    // JSX์™€ ์œ ์‚ฌํ•œ ๋ฌธ๋ฒ•์œผ๋กœ ํ‘œํ˜„ํ•œ ์ปดํฌ๋„ŒํŠธ
    export const MyComponent = () => {
      const content = useDictionary({
        key: "my-key",
        content: {
          nodeType: "translation",
          translation: {
            en: { title: "๋‚ด ์ œ๋ชฉ" },
            fr: { title: "๋‚ด ์ œ๋ชฉ" },
          },
        },
      });
    
      return <h1>{content.title}</h1>;
    };
    

    - ๋™์  ์ž„ํฌํŠธ ๋ชจ๋“œ:

    tsx
    // JSX์™€ ์œ ์‚ฌํ•œ ๋ฌธ๋ฒ•์œผ๋กœ ํ‘œํ˜„ํ•œ ์ปดํฌ๋„ŒํŠธ
    export const MyComponent = () => {
      const content = useDictionaryAsync({
        en: () =>
          import(".intlayer/dynamic_dictionary/en/my-key.json", {
            with: { type: "json" },
          }).then((mod) => mod.default),
        // ๋‹ค๋ฅธ ์–ธ์–ด๋„ ๋™์ผํ•ฉ๋‹ˆ๋‹ค
      });
    
      return <h1>{content.title}</h1>;
    };
    
    useDictionaryAsync๋Š” ํ•„์š”ํ•  ๋•Œ๋งŒ ๋กœ์ปฌ๋ผ์ด์ฆˆ๋œ JSON์„ ๋กœ๋“œํ•˜๊ธฐ ์œ„ํ•ด Suspense ์œ ์‚ฌ ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

    ์ด ์ปดํฌ๋„ŒํŠธ๋ณ„ ์ ‘๊ทผ ๋ฐฉ์‹์˜ ์ฃผ์š” ์ด์ :

    • ์ปดํฌ๋„ŒํŠธ ๊ฐ€๊นŒ์ด์— ์ฝ˜ํ…์ธ  ์„ ์–ธ์„ ์œ ์ง€ํ•˜๋ฉด ์œ ์ง€๋ณด์ˆ˜๊ฐ€ ๋” ์‰ฌ์›Œ์ง‘๋‹ˆ๋‹ค(์˜ˆ: ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋‹ค๋ฅธ ์•ฑ์ด๋‚˜ ๋””์ž์ธ ์‹œ์Šคํ…œ์œผ๋กœ ์˜ฎ๊ธฐ๋Š” ๊ฒฝ์šฐ). ์ปดํฌ๋„ŒํŠธ ํด๋”๋ฅผ ์‚ญ์ œํ•˜๋ฉด ๊ด€๋ จ ์ฝ˜ํ…์ธ ๋„ ํ•จ๊ป˜ ์ œ๊ฑฐ๋˜๋ฏ€๋กœ, ์ด๋ฏธ .test, .stories์— ๋Œ€ํ•ด ํ•˜์‹œ๋Š” ๊ฒƒ๊ณผ ๋™์ผํ•œ ๋ฐฉ์‹์„ ์ ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค)

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

    ์ œํ•œ ์‚ฌํ•ญ

    ๋ฌผ๋ก , ์ด ์ ‘๊ทผ ๋ฐฉ์‹์—๋Š” ํŠธ๋ ˆ์ด๋“œ์˜คํ”„๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค:

    • ๋‹ค๋ฅธ l10n ์‹œ์Šคํ…œ์ด๋‚˜ ์ถ”๊ฐ€ ํˆด๋ง๊ณผ ์—ฐ๋™ํ•˜๊ธฐ๊ฐ€ ๋” ์–ด๋ ต์Šต๋‹ˆ๋‹ค.
    • ๋ฝ์ธ(lock-in)์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค(ํŠน์ • ๋ฌธ๋ฒ• ๋•Œ๋ฌธ์— ์‚ฌ์‹ค์ƒ ๋Œ€๋ถ€๋ถ„์˜ i18n ์†”๋ฃจ์…˜์—์„œ ์ด๋ฏธ ๊ทธ๋Ÿฌํ•ฉ๋‹ˆ๋‹ค).

    ๋ฐ”๋กœ ์ด๋Ÿฐ ์ด์œ ๋กœ Intlayer๋Š” i18n์„ ์œ„ํ•œ ์™„์ „ํ•œ ํˆด์…‹(100% ๋ฌด๋ฃŒ ๋ฐ OSS)์„ ์ œ๊ณตํ•˜๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์—๋Š” ์ž์ฒด AI Provider์™€ API keys๋ฅผ ์‚ฌ์šฉํ•˜๋Š” AI ๋ฒˆ์—ญ ๊ธฐ๋Šฅ๋„ ํฌํ•จ๋ฉ๋‹ˆ๋‹ค. Intlayer๋Š” ๋˜ํ•œ JSON์„ ๋™๊ธฐํ™”ํ•˜๋Š” ํˆด๋ง์„ ์ œ๊ณตํ•˜๋Š”๋ฐ, ์ด๋Š” ICU / vue-i18n / i18next ๋ฉ”์‹œ์ง€ ํฌ๋งคํ„ฐ์ฒ˜๋Ÿผ ๋™์ž‘ํ•˜์—ฌ ์ฝ˜ํ…์ธ ๋ฅผ ํ•ด๋‹น ํฌ๋งท์— ๋งž๊ฒŒ ๋งคํ•‘ํ•ฉ๋‹ˆ๋‹ค.

    ๋Œ“๊ธ€

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

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

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