성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] VT sequences to "CONOUT$" vs. STD_O...
[정성태] NetCoreDbg is a managed code debugg...
[정성태] Evaluating tail call elimination in...
[정성태] What’s new in System.Text.Json in ....
[정성태] What's new in .NET 9: Cryptography ...
[정성태] 아... 제시해 주신 "https://akrzemi1.wordp...
[정성태] 다시 질문을 정리할 필요가 있을 것 같습니다. 제가 본문에...
[이승준] 완전히 잘못 짚었습니다. 댓글 지우고 싶네요. 검색을 해보...
[정성태] 우선 답글 감사합니다. ^^ 그런데, 사실 저 예제는 (g...
[이승준] 수정이 안되어서... byteArray는 BYTE* 타입입니다...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>유니코드의 Surrogate Pair, Supplementary Characters가 뭘까요?</h1> <p> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 유니코드와 한글 - 유니코드와 닷넷을 이용한 한글 처리 ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/1294'>http://www.sysnet.pe.kr/2/0/1294</a> 유니코드의 "compatibility character"가 뭘까요? ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/1607'>http://www.sysnet.pe.kr/2/0/1607</a> </pre> <br /> 위의 글도 읽어보시면 좋겠지요. ^^ 오늘은 유니코드의 Surrogate Pair, Supplementary Characters에 대한 용어를 설명해 보겠습니다.<br /> <br /> 혹시 여러분들은 2바이트 UTF-16 코드가 어떻게 6만 5천 글자를 넘어서는 글자들을 표현할 수 있는지에 대해 궁금하지 않으셨나요? 분명히 유니코드는 4바이트 영역을 쓰기로 작정한 문자셋이므로 16비트로 표현하기에는 역부족입니다. 바로 그 해결책이 Surrogate Pair입니다.<br /> <br /> Surrogate Pair는 마이크로소프트의 MSDN 문서에 보면 다음과 같이 친절하게 설명되어 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Surrogates and Supplementary Characters ; <a target='tab' href='https://learn.microsoft.com/en-us/windows/win32/intl/surrogates-and-supplementary-characters'>https://learn.microsoft.com/en-us/windows/win32/intl/surrogates-and-supplementary-characters</a> </pre> <br /> 우선 Supplementary Characters는 유니코드의 2바이트 기본 범위에 속하는 BMP(Basic Multilingual Plane: Plane 0) 영역을 넘어선 글자들이라고 합니다. 그리고 Surrogate Pair는 이 범주에 속하는 Supplementary Characters를 표현하기 위해 UTF-16에 도입된 인코딩 방식입니다.<br /> <br /> ^^ 그러고 보니, 아래의 글에서도 Surrogate Pair에 해당하는 문자를 다루고 지나갔었습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 유니코드와 한글 - 유니코드와 닷넷을 이용한 한글 처리 ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/1294'>http://www.sysnet.pe.kr/2/0/1294</a> </pre> <br /> 바로 U+10000 과 U+10001 2개의 글자가 Surrogate Pair 방식으로 표현되었던 것입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Unicode Character 'LINEAR B SYLLABLE B008 A' (U+10000) ; <a target='tab' href='http://www.fileformat.info/info/unicode/char/10000/index.htm'>http://www.fileformat.info/info/unicode/char/10000/index.htm</a> Unicode Character 'LINEAR B SYLLABLE B038 E' (U+10001) ; <a target='tab' href='http://www.fileformat.info/info/unicode/char/10001/index.htm'>http://www.fileformat.info/info/unicode/char/10001/index.htm</a> </pre> <br /> 위의 2개 문자를 보면 값이 2바이트 영역을 넘어서고 있습니다. 즉, U+10000 과 U+10001 글자들은 Supplementary Characters에 해당합니다. 그리고 해당 문자들을 표현하기 위해 각각 다음과 같이 2개의 UTF-16 코드가 할당되어 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > U+10000: \uD800 \uDC00 U+10001: \uD800 \uDC01 </pre> <br /> 여기서 왜 "Pair"라고 하는지 밝혀집니다. 즉, 2바이트 영역을 넘어서는 글자 하나를 표현하기 위해 2개의 2바이트가 사용된 것입니다.<br /> <br /> 그런데, 위의 0xd800, 0xdc00, 0xdc01 코드로 이미 기존에 할당된 문자들과는 그럼 어떻게 구분한다는 말입니까? 물론, 코드를 아무거나 사용할 수는 없지요. 그래서 원래 UTF-16인코딩에서 U+D800 ~ U+DFFF 사이의 2048 글자 영역은 비어 있었습니다. 그리고 이를 절반씩 1024개로 나눠서 pair의 앞글자와 뒷글자 영역을 담당하게 한 것입니다. 즉, surrogate pair는 다음의 쌍으로 이뤄집니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > [0xd800 ~ 0xdbff] [0xdc00 ~ 0xdfff] </pre> <br /> Pair의 앞 글자에 1024개의 경우의 수가 나오고 뒤의 글자에도 역시 1024개의 경우의 수가 나오므로 이를 조합하면 1,048,576으로 약 100만개의 글자 영역이 확보되는 것입니다. 일단, 기존의 6만 개가 넘는 글자에 100만개의 글자 영역이 추가로 확보되었으니 당분간은 외계 언어가 추가되지 않는 한 UTF-16으로 충분히 문자를 표현할 수 있을 정도는 된 것입니다. 실제로 "<a target='tab' href='https://learn.microsoft.com/en-us/windows/win32/intl/surrogates-and-supplementary-characters'>Surrogates and Supplementary Characters</a>" 글에 보면 유니코드 4.1에서 9만 7천자 정도가 추가된 정도라고 합니다. (정확히 하면 UTF-16은 현재 1,114,112개의 글자를 표현할 수 있다고 합니다.)<br /> <br /> <hr style='width: 50%' /><br /> <br /> 그럼, Supplementary Characters가 자신의 컴퓨터에서 어떻게 보여질 수 있는지 간단하게 테스트를 해볼까요? ^^ (윈도우 기준으로 하겠습니다.)<br /> <br /> 우선 차이점을 느끼기 위해 현재의 환경에서 다음의 웹 페이지를 Internet Explorer (또는 Chrome)로 방문해 봅니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Using UTF-16 Little-Endian Plane 1 Supplementary Characters ; <a target='tab' href='http://www.i18nguy.com/unicode/plane1-utf-16.html'>http://www.i18nguy.com/unicode/plane1-utf-16.html</a> </pre> <br /> 그럼, IE 11의 경우 다음과 같이 "<a target='tab' href='http://www.unicode.org/charts/PDF/U10000.pdf'>Linear B Syllabary</a>", "<a target='tab' href='http://www.unicode.org/charts/PDF/U10450.pdf'>Shavian</a>" 영역의 글자가 비정상적으로 나오는 것을 볼 수 있습니다.<br /> <br /> <img onclick='toggle_img(this)' class='imgView' alt='surrogate_pair_display_1.png' src='/SysWebRes/bbs/surrogate_pair_display_1.png' /><br /> <br /> 크롬의 경우는 다음과 같이 전체가 비정상적으로 나옵니다.<br /> <br /> <img onclick='toggle_img(this)' class='imgView' alt='surrogate_pair_display_2.png' src='/SysWebRes/bbs/surrogate_pair_display_2.png' /><br /> <br /> IE와 크롬의 차이는 아마도 웹 페이지에 포함된 "Arial Unicode MS" 폰트의 로드 차이인 것 같습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > td.plane1 { font-family : <span style='color: blue; font-weight: bold'>CODE2001</span>, <span style='color: blue; font-weight: bold'>"Arial Unicode MS"</span>, serif; font-size : 120%; text-align : left; direction : ltr; color : black; background-color : #FFFF99; } </pre> <br /> IE의 경우 기본적으로 일부 유니코드를 보여줄 수 있는 "Arial Unicode MS" 폰트를 로드할 수 있는 반면 크롬은 (웬일인지?) 로드를 하지 않는 것 같습니다.<br /> <br /> 자, 그럼 이 페이지들을 정상적으로 보이기 위한 설정을 해볼까요? ^^<br /> <br /> 우선, 여러분들이 만약 윈도우 운영체제를 XP/2003이하의 버전을 쓰고 있다면 3가지 레지스트리 설정을 선행해서 처리해야 합니다. 어떤 설정인지는 다음의 글을 참고하시면 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Setting up Microsoft Windows NT, 2000 or Windows XP to support Unicode supplementary characters ; <a target='tab' href='http://www.i18nguy.com/surrogates.html'>http://www.i18nguy.com/surrogates.html</a> </pre> (중간에 보면, "Setting 1", "Setting 2", "Setting 3"의 레지스트리 설정이 나오는데 이를 참고하시면 됩니다.)<br /> <br /> 저는 윈도우 8.1에서 테스트 하고 있기 때문에 위의 설정을 하지 않았습니다. 그다음, 웹 브라우저가 사용할 수 있는 폰트를 시스템에 등록해야 합니다.<br /> <br /> 위의 CSS 코드에 보면 "CODE2001"이라는 폰트 명을 볼 수 있는데요. 바로 이것을 여러분들의 윈도우 시스템에 설치해야 합니다. 아쉽게도 "<a target='tab' href='http://www.i18nguy.com/unicode/plane1-utf-16.html'>Using UTF-16 Little-Endian Plane 1 Supplementary Characters</a>" 글에 링크된 "http://home.att.net/~jameskass/CODE2001.ZIP" 파일은 현재 존재하지 않습니다. 어쩔 수 없이 웹 검색을 했는데 다음의 페이지에서 발견할 수 있었습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Code2001 font - Created in 1998 by James Kass ; <a target='tab' href='http://www.fontspace.com/james-kass/code2001'>http://www.fontspace.com/james-kass/code2001</a> ; <a target='tab' href='http://www.fontspace.com/download/13285/2489492aa53d466d94ffc197bfe1a865/james-kass_code2001.zip'>http://www.fontspace.com/download/13285/2489492aa53d466d94ffc197bfe1a865/james-kass_code2001.zip</a> Code2001 is an experimental Plane 1 Unicode based font. The first 255 code points are as in ISO-8859-001. The balance of the code points are from Plane 1, a supplementary plane of Unicode. </pre> <br /> 다운로드 받아 압축을 풀고 "CODE2001.TTF" 파일을 탐색기에서 두번 누르면 폰트 창이 뜨는데, 상단의 "Install" 버튼을 누르면 윈도우(C:\Windows\Fonts)에 설치가 됩니다. <br /> <br /> 설치 후 인터넷 익스플로러는 재시작할 필요없이 페이지 로딩만 다시 하면 곧바로 유니코드 폰트가 보입니다. (크롬의 경우에는 종료한 후 다시 페이지를 방문해야 합니다.)<br /> <br /> 어쨌든, 두가지 웹 브라우저 모두 다음과 같이 1번 Plane 영역에 속한 "Supplementary Characters" 유니코드 문자를 잘 보여줍니다. ^^<br /> <br /> <img onclick='toggle_img(this)' class='imgView' alt='surrogate_pair_display_3.png' src='/SysWebRes/bbs/surrogate_pair_display_3.png' /><br /> <br /> <hr style='width: 50%' /><br /> <br /> 그럼, C# 응용 프로그램에서는 어떻게 해야 할까요?<br /> <br /> "<a target='tab' href='https://learn.microsoft.com/en-us/windows/win32/intl/surrogates-and-supplementary-characters'>Surrogates and Supplementary Characters</a>" 글에 의하면, "Edit", "Rich Edit" 컨트롤이 "Supplementary Characters"를 지원한다고 합니다.<br /> <br /> 테스트를 위해 윈폼 프로젝트를 하나 만들고, TextBox, RichTextBox를 각각 올려 놓은 후 Form1_Load에서 다음과 같이 코딩을 해봅니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > private void Form1_Load(object sender, EventArgs e) { string text = "\uD800\uDF38"; // 또는 text = "𐌸"; this.textBox1.Text = text; this.richTextBox1.Text = text; } </pre> <br /> 이상하군요. TextBox의 경우에는 텍스트가 잘 보이지만, RichTextBox의 경우에는 출력을 못하고 있습니다.<br /> <br /> <img alt='surrogate_pair_display_4.png' src='/SysWebRes/bbs/surrogate_pair_display_4.png' /> <br /><br /> 더욱 이상한 것은 TextBox, RichTextBox 모두 기본적으로 설정된 폰트는 "Microsoft Sans Serif"였는데도 TextBox는 글자를 잘 보여주고 있습니다.<br /> <br /> 하지만, 폰트를 명시적으로 "Arial Unicode MS"로 바꿔도 여전히 RichTextBox는 출력이 안됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > this.textBox1.Font = new System.Drawing.Font("Arial Unicode MS", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.richTextBox1.Font = new System.Drawing.Font("Arial Unicode MS", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); </pre> <br /> 하는 김에 "<a target='tab' href='http://www.sysnet.pe.kr/2/0/1294'>유니코드와 한글 - 유니코드와 닷넷을 이용한 한글 처리</a>" 글에서 테스트한 "<a target='tab' href='http://www.fileformat.info/info/unicode/char/10000/index.htm'>Unicode Character 'LINEAR B SYLLABLE B008 A' (U+10000)</a>", "<a target='tab' href='http://www.fileformat.info/info/unicode/char/10001/index.htm'>Unicode Character 'LINEAR B SYLLABLE B038 E' (U+10001)</a>" 2개의 문자를 넣어봤는데,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > private void Form1_Load(object sender, EventArgs e) { this.textBox1.Font = new System.Drawing.Font("Arial Unicode MS", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.richTextBox1.Font = new System.Drawing.Font("Arial Unicode MS", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); string text = "\uD800\uDC00\uD800\uDC01"; this.textBox1.Text = text; this.richTextBox1.Text = text; } </pre> <br /> 이번에는 "Arial Unicode MS"를 지정했는데도 불구하고 TextBox, RichTextBox 2개 모두 글자가 전혀 나오지 않습니다.<br /> <br /> <img alt='surrogate_pair_display_5.png' src='/SysWebRes/bbs/surrogate_pair_display_5.png' /> <br /><br /> 혹시나 폰트가 해당 영역의 글자를 포함하지 않았나 싶어서 Code2001 폰트를 Visual Studio 속성 창에서 선택해 보았는데,<br /> <br /> <img onclick='toggle_img(this)' class='imgView' alt='surrogate_pair_display_6.png' src='/SysWebRes/bbs/surrogate_pair_display_6.png' /><br /> <br /> 다음과 같은 오류 메시지가 뜹니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > "Only TrueType fonts are supported. This is not a TrueType font." </pre> <br /> 검색해 보니, 폰트 대화창의 버그라는 글이 눈에 띄는데,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Only TrueType fonts are supported. This is not a TrueType font. ; <a target='tab' href='http://stackoverflow.com/questions/11551805/in-c-sharp-winform-i-got-only-truetype-fonts-are-supported-this-is-not-a-tru'>http://stackoverflow.com/questions/11551805/in-c-sharp-winform-i-got-only-truetype-fonts-are-supported-this-is-not-a-tru</a> </pre> <br /> 비주얼 스튜디오의 속성 창에서 선택한 폰트 대화창의 코드를 제가 변경할 수는 없으므로 그 방법은 일단 포기했습니다. 그렇지만 코드를 통해 직접 지정하는 것은,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > private void Form1_Load(object sender, EventArgs e) { <span style='color: blue; font-weight: bold'> this.textBox1.Font = new System.Drawing.Font("Code2001", 12F); this.richTextBox1.Font = new System.Drawing.Font("Code2001", 12F);</span> string text = "\uD800\uDC00\uD800\uDC01"; this.textBox1.Text = text; this.richTextBox1.Text = text; } </pre> <br /> TextBox, RichTextBox 2개에서 모두 잘 동작했습니다.<br /> <br /> <img alt='surrogate_pair_display_7.png' src='/SysWebRes/bbs/surrogate_pair_display_7.png' /> <br /><br /> 휴~~~ 어쨌든 다행이군요. ^^ 이것으로 Surrogate Pair, Supplementary Characters 용어에 대한 탐구는 끝이 납니다.<br /> <br /> (<a target='tab' href='http://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=863&boardid=331301885'>첨부한 파일은 위의 테스트를 수행한 간단한 WinForm 프로젝트</a>입니다.)<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
1570
(왼쪽의 숫자를 입력해야 합니다.)