codingstairs
노트에듀라이프연락
⌕검색⌘K
koen

Navigation

  • Intro
  • Blog
  • Life

연락하기

로그인 없이도 보낼 수 있어요. 답변이 필요하면 이메일을 함께 적어 주세요.

  • 익명 폼으로 의견 남기기 →
  • ✉ warragon112@gmail.com
  • 카카오톡 오픈채팅 ↗

© 2026 codingstairs

  • 노트
  • 에듀
  • 검색
  • 라이프
  • 연락
  • 약관
  • RSS
  • GitHub
노트›frontend

i18n — 한국어를 1순위로

2026-04-28 게시· 2026-05-18 갱신·0회 조회

i18n — 한국어를 1순위로

국제화 (internationalization · i18n) 는 앱이 여러 언어·지역에서 자연스럽게 동작하도록 만드는 작업입니다.

1. 용어와 표준

  • i18n (internationalization) — 코드를 특정 언어/지역에 묶지 않게 만드는 일.
  • l10n (localization) — 그 위에 특정 지역의 번역·포맷을 얹는 일.
  • locale — 언어와 지역을 합친 식별자 (BCP 47 표준). ko · ko-KR · en-US · zh-Hant-TW 등.

표준:

  • BCP 47 — 언어 태그 표준. ko-KR.
  • CLDR (Unicode Common Locale Data Repository) — 각 locale 의 날짜·숫자·단위·복수 규칙 데이터. Unicode Consortium 이 관리.
  • ICU MessageFormat — 메시지 안의 변수·복수·성별 처리를 위한 표기.
  • Intl API — ECMAScript 표준 내장 객체. Intl.NumberFormat · Intl.DateTimeFormat · Intl.RelativeTimeFormat · Intl.PluralRules · Intl.Collator · Intl.Segmenter (2021+).

브라우저·Node 가 Intl 을 가지고 있어서 실제로는 라이브러리가 Intl 위에 얇은 층을 두는 경우가 많습니다.

2. React 진영의 라이브러리

라이브러리 첫 릴리스 만든이 비고
react-i18next 2014 (i18next) Jan Mühlemann i18next (JS) 의 React 바인딩. 가장 넓게 채택.
react-intl (FormatJS) 2014 Yahoo (현 OpenJS Foundation) ICU MessageFormat 1 급 지원.
LinguiJS 2017 Tomáš Roch ICU + 매크로 + 컴파일된 카탈로그.
next-intl 2022 Jan Amann Next.js App Router 특화.
Tolgee 2021 Tolgee 인-컨텍스트 편집 강조.

Next.js App Router 는 라우팅 자체가 i18n 을 가정하지 않기 때문에 next-intl 같은 도구가 자라났습니다. 이 도구는 [locale] 동적 세그먼트와 미들웨어를 묶어 locale 분기를 단순화합니다.

3. 메시지 카탈로그

흔한 두 형태입니다.

JSON / NS 분리 (i18next 스타일):

// public/locales/ko/common.json
{ "save": "저장", "cancel": "취소" }
import { useTranslation } from "react-i18next"
const { t } = useTranslation("common")
t("save")   // "저장"

ICU MessageFormat (FormatJS / Lingui / next-intl):

{ "items": "{count, plural, =0 {항목 없음} other {#개의 항목}}" }
t("items", { count: 3 })   // "3개의 항목"

ICU 는 표준이고 도구 간 이식성이 좋습니다. 단순 키-값보다 학습 곡선이 있습니다.

4. 키 네이밍 관습

자주 쓰이는 두 갈래.

① 점 표기 (네임스페이스): auth.login.title · cart.item.removed
② 자연어 키: "이 항목을 정말 삭제할까요?" 자체를 키로 사용 (Lingui 매크로)

장단점이 갈립니다. 점 표기는 검색이 쉽고 충돌이 적지만 키만 보고 의미를 알기 어렵습니다. 자연어 키는 의미가 명확하지만 텍스트가 바뀌면 키도 바뀝니다 (빌드 도구가 보강).

5. 날짜·숫자 — Intl 활용

new Intl.NumberFormat("ko-KR", { style: "currency", currency: "KRW" })
  .format(123456)                        // "₩123,456"

new Intl.DateTimeFormat("ko-KR", { dateStyle: "long" })
  .format(new Date())                    // "2026년 4월 25일"

new Intl.RelativeTimeFormat("ko", { numeric: "auto" })
  .format(-1, "day")                     // "어제"

date-fns (2014) 와 dayjs (2018) 도 자체 locale 패키지를 가집니다. 새 코드에서는 Intl 만으로 충분한 경우가 늘었다는 평이 흔합니다.

6. 한국어 조사 처리

한국어는 명사 끝의 받침에 따라 조사가 달라집니다.

  • 받침 있음: 책을 · 사람이 · 학교과 (→ "와")
  • 받침 없음: 사과를 · 친구가 · 학교와

{name} 을(를) 삭제했습니다 같은 회피 표기는 자연스럽지 않습니다. 자주 쓰이는 길:

  • 끝 글자 코드포인트 ((ch - 0xAC00) % 28 !== 0) 로 받침 유무를 판정해 적절한 조사를 고르는 헬퍼.
  • 메시지를 다시 써서 조사를 회피 (예: "삭제 완료: {name}").
  • 라이브러리: es-hangul · hangul-js.

7. 한국어 숫자·복수·콜레이션

한국어는 만 (萬) 단위로 자릿수를 묶습니다 ("12,345" 보다 "1만 2345" 가 자연스러울 때가 있습니다). 영문권 표준 천 단위 콤마와 충돌하기 때문에 UI 에 따라 선택합니다.

영어와 달리 한국어는 단/복수가 거의 구분되지 않습니다 (사과 1개 · 사과 3개 모두 같은 단어 형태). ICU 의 plural 카테고리가 한국어에서는 사실상 other 하나라는 점이 자주 헷갈리는 자리입니다.

콜레이션 (정렬) 은 가나다 순. Intl.Collator("ko-KR") 가 기본 처리합니다.

["하늘", "가을", "나무"].sort(new Intl.Collator("ko-KR").compare)
// ["가을", "나무", "하늘"]

8. 폰트와 IME

Pretendard (길형진, 2021) 가 한국어 UI 에서 사실상 표준 한 갈래로 자리잡았습니다 (가변 폰트 · OFL 라이선스). 한자·일본어 글리프가 섞이면 폰트 폴백을 잘 정의해야 누락이 안 보입니다. 행간 (line-height) 은 영어 기준 1.4 가 한국어에서는 답답할 수 있습니다. 1.6~1.8 이 자주 권장됩니다.

한글 입력 중에는 keydown 이 조합 단계에서 발생해 자동 검색·즉시 제출 같은 패턴이 의도와 다르게 동작합니다. compositionstart · compositionend 이벤트로 조합 중을 식별합니다.

const [composing, setComposing] = useState(false)
<input
  onCompositionStart={() => setComposing(true)}
  onCompositionEnd={() => setComposing(false)}
  onKeyDown={(e) => {
    if (composing) return
    if (e.key === "Enter") submit()
  }}
/>

9. next-intl 최소 예

// messages/ko.json
{ "Home": { "title": "안녕하세요", "items": "{count}개" } }

// app/[locale]/page.tsx
import { useTranslations } from "next-intl"
export default function Page() {
  const t = useTranslations("Home")
  return <h1>{t("title")}</h1>
}

10. 자주 걸리는 자리

하드코딩 누락 — UI 텍스트 일부가 코드에 직접 박히면 뒤늦게 발견됩니다. ESLint 룰 (react/jsx-no-literals) 또는 추출 도구로 보강합니다.

번역 키 일치 검증 — 다국어 추가 시 누락된 키가 런타임에서야 드러납니다. CI 에서 키 차집합 검사를 추가합니다.

시간대 — new Date(...) 는 클라이언트 시간대에 의존합니다. 서버 렌더와 클라이언트 렌더의 결과가 다르면 hydration mismatch. UTC 로 다루고 표시 시점에만 변환합니다.

숫자 0/1 분기를 영어 기준으로 — 1 item / 2 items 분기는 한국어에서 의미가 없습니다. ICU plural 의 =0 one other 를 한국어 카탈로그에서는 other 하나로 통일하는 편이 자연스럽습니다.

RTL 가능성 — 향후 아랍어·히브리어 추가 가능성이 있다면 CSS 의 logical property (margin-inline-start) · dir="rtl" 호환을 미리 봅니다.

하고픈 말

i18n 은 도입 시점이 늦어질수록 비용이 빠르게 자랍니다. 한국어 단일 앱이라도 키 분리는 처음부터 권장합니다. 조사 처리는 라이브러리보다 메시지 다시 쓰기가 가장 단순한 길입니다.

Next

  • material3-tokens
  • forms-zod

Unicode CLDR · ICU MessageFormat · BCP 47 · MDN Intl · react-i18next · FormatJS · LinguiJS · next-intl · Pretendard · es-hangul 을 참고합니다.

frontend 카테고리의 다른 글

카테고리 전체 보기 →
  • 도메인 위젯의 통일성 — 4개 도메인에 3개 위젯만 두지 마라
  • 관리자 UI — ResourceTable SSOT 패턴
  • 페이지 로딩 UX
  • 네이티브 통합 — OS 기능들
  • OCR · STT · TTS
  • SQLite — 로컬 앱의 단일 파일 DB