Wednesday
개발

퓨니코드한글 도메인의 비밀

Wednesday, January 21, 2026

퓨니코드란?

한글.com이라는 도메인을 브라우저에 입력하면 어떻게 될까요? 놀랍게도 실제로 접속이 됩니다. 하지만 DNS 시스템은 오직 ASCII 문자만 이해할 수 있습니다. 그렇다면 한글 도메인은 어떻게 동작하는 걸까요? 그 비밀은 바로 퓨니코드(Punycode) 에 있습니다.

DNS와 ASCII의 벽

인터넷 초창기, DNS(Domain Name System)는 미국에서 설계되었습니다. 당시에는 영문자와 숫자, 하이픈만으로 충분했습니다. DNS 레이블은 다음 규칙을 따릅니다:

  • 오직 a-z, 0-9, - 문자만 허용
  • 대소문자 구분 없음
  • 각 레이블은 63바이트 이하

이 제한은 RFC 1035에 명시되어 있으며, 전 세계 DNS 서버들이 이 규칙을 따릅니다. 한글, 일본어, 아랍어 등 비ASCII 문자는 원천적으로 차단됩니다.

IDN과 퓨니코드의 등장

2003년, IETF는 IDN(Internationalized Domain Names) 표준을 발표합니다(RFC 3490). 핵심 아이디어는 단순합니다:

비ASCII 문자를 ASCII로 변환하여 기존 DNS 인프라를 그대로 활용한다.

이 변환 알고리즘이 바로 퓨니코드입니다. RFC 3492에 정의된 퓨니코드는 Bootstring 알고리즘을 기반으로 합니다.

퓨니코드의 구조

퓨니코드로 인코딩된 도메인은 특별한 접두사로 시작합니다:

CODE
xn--
END

이 접두사를 ACE(ASCII Compatible Encoding) prefix 라고 부릅니다(RFC 5891). 예를 들어:

원본 도메인퓨니코드
한글xn--bj0bj06e
日本xn--wgv71a
münchenxn--mnchen-3ya
中文xn--fiq228c

Bootstring 알고리즘의 원리

Bootstring은 세 단계로 동작합니다:

1단계: 기본 코드 포인트 분리

먼저 ASCII 문자(기본 코드 포인트)를 그대로 복사합니다. münchen의 경우 mnchen이 먼저 추출됩니다.

2단계: 델타 인코딩

비ASCII 문자의 위치와 코드 포인트를 델타(delta) 값으로 인코딩합니다. 델타는 "이전 위치에서 얼마나 떨어져 있는가"를 나타냅니다.

예를 들어, münchen에서 ü(U+00FC)는 2번째 위치에 있습니다. 알고리즘은 코드 포인트를 오름차순으로 처리하면서, 각 비ASCII 문자의 위치와 코드 포인트 값을 하나의 델타 숫자로 압축합니다. ü의 경우, 코드 포인트 252(0xFC)와 위치 정보가 결합되어 델타 값이 계산됩니다.

핵심 아이디어는 다음과 같습니다:

  • 문자열을 선형으로 스캔하면서 비ASCII 문자의 위치를 기록
  • 각 문자의 유니코드 코드 포인트를 순차적으로 처리
  • 작은 델타 값이 자주 나타나므로 가변 길이 인코딩이 효율적

3단계: 일반화된 가변 길이 정수 인코딩

델타 값을 base-36 문자(a-z, 0-9)로 변환합니다. 이때 적응형 바이어스(adaptive bias) 를 사용해 자주 나타나는 값을 더 짧게 인코딩합니다.

CODE
// 의사 코드: 바이어스 적응 함수 (RFC 3492 Section 6.1)
function adapt(delta, numpoints, firsttime) {
  delta = firsttime ? Math.floor(delta / DAMP) : delta >> 1;
  delta += Math.floor(delta / numpoints);
 
  let k = 0;
  while (delta > ((BASE - TMIN) * TMAX) >> 1) {
    delta = Math.floor(delta / (BASE - TMIN));
    k += BASE;
  }
  return k + Math.floor((BASE - TMIN + 1) * delta / (delta + SKEW));
}
END

여기서 상수들은 RFC 3492에 정의된 값입니다: BASE는 36(알파벳+숫자), TMIN은 1, TMAX는 26, DAMP는 700, SKEW는 38입니다. 이 함수는 자주 나타나는 델타 값에 더 짧은 인코딩을 할당하여 압축 효율을 높입니다.

이 적응형 바이어스 덕분에 퓨니코드는 다양한 언어에서 효율적인 압축률을 보입니다.

이론을 이해했다면, 실제 예제를 통해 확인해 봅시다.

실제 인코딩 과정: "한글" 예제

한글을 퓨니코드로 변환하는 과정을 따라가 봅시다.

CODE
입력: "한글"
유니코드: U+D55C (한), U+AE00 (글)
END
  1. ASCII 문자가 없으므로 기본 부분은 비어 있음
  2. 가장 작은 코드 포인트 U+AE00(글)부터 처리
  3. 델타 계산: 첫 번째 문자의 위치와 코드 포인트
  4. base-36 인코딩 수행
CODE
결과: xn--bj0bj06e
END

실습: JavaScript로 확인하기

브라우저와 Node.js 모두에서 URL API를 통해 퓨니코드 변환을 확인할 수 있습니다:

CODE
// 브라우저 & Node.js 공통: URL API
const url = new URL('https://한글.com');
console.log(url.hostname);  // xn--bj0bj06e.com
END

Node.js에서는 punycode 모듈을 사용해 직접 인코딩/디코딩할 수도 있습니다:

CODE
// Node.js 전용: punycode 모듈
const punycode = require('punycode/');
console.log(punycode.encode('한글'));  // bj0bj06e
console.log(punycode.decode('bj0bj06e'));  // 한글
END

브라우저 주소창에서는 사용자 편의를 위해 원래 문자로 표시하지만, 실제 DNS 쿼리는 퓨니코드로 이루어집니다.

아래 변환기로 직접 퓨니코드 인코딩/디코딩을 체험해 보세요:

Punycode Converter
결과가 여기에 표시됩니다

호모그래프 공격: 퓨니코드의 보안 문제

퓨니코드가 다양한 문자를 허용하면서 새로운 보안 문제가 등장했습니다. 호모그래프 공격(Homograph Attack) 은 시각적으로 유사한 문자를 이용해 사용자를 속이는 공격입니다:

CODE
apple.com     (정상)
аpple.com     (а는 키릴 문자)
→ xn--pple-43d.com
END

육안으로는 구분이 불가능하지만 전혀 다른 도메인입니다. 이 때문에 현대 브라우저들은 혼합 스크립트 도메인을 퓨니코드 형태로 표시하는 등의 방어책을 적용하고 있습니다. 자세한 브라우저별 정책은 Chromium IDN Display PolicyUnicode Security Guide(TR39)를 참고하세요.

표준의 변화: IDNA 2003 vs IDNA 2008

퓨니코드를 실제로 사용하는 표준은 IDNA(Internationalized Domain Names in Applications) 입니다:

버전특징참고 문서
IDNA 2003대소문자 매핑, 일부 문자 자동 변환RFC 3490
IDNA 2008더 엄격한 규칙, 일부 문자 금지RFC 5891

예를 들어 독일어 ß는:

  • IDNA 2003: ss로 자동 변환
  • IDNA 2008: 그대로 유지 (xn--로 인코딩)

현재 대부분의 시스템은 IDNA 2008을 따르지만, 하위 호환성을 위해 두 표준이 공존하고 있습니다.

마치며

퓨니코드는 인터넷의 국제화를 가능하게 한 핵심 기술입니다. 단순해 보이는 xn-- 접두사 뒤에는 정교한 압축 알고리즘과 수십 년에 걸친 표준화 노력이 숨어 있습니다. 브라우저 주소창에 한글 도메인을 입력할 때, 그 뒤에서 벌어지는 변환 과정을 한 번쯤 떠올려 보는 것도 좋겠습니다.

참고 자료

Finis