'IT 이야기'에 해당되는 글 31건

  1. 2009.03.16 How vs Who
  2. 2009.03.12 프로젝트의 예측
  3. 2009.03.11 Scott Adams
  4. 2009.03.11 사람이 읽는 코드
  5. 2009.03.01 Unicode와 Database
  6. 2009.03.01 멘탈 ?
  7. 2009.02.26 Global Software - Unicode와 Programming
  8. 2009.02.25 Global Software - Unicode
  9. 2009.02.23 소프트웨어의 변증법
  10. 2009.02.22 Global Software
IT 이야기2009. 3. 16. 05:06

방법론에서는 How가 중요하다. 어떤 분석방법을 사용해서 어떤 툴을 사용해서 설계를 하고 어떤 표준으로 코딩을 하고 등등.. 하지만 로버트의 말은 새겨들을 필요가 있다.  

   "많은 프로젝트의 성공과 실패는 어떻게 수행하는가보다 누가 수행하는가에 따라 결정된다.
    - 로버트 L 글라스, 소프트웨어 공학의 사실과 오해"


짧게 말해 How보다는 Who라는 사실이다. 성공한 프로젝트의 How를 흉내내는건 그래서 별로 효과적이지 못하다. NASA의 프로젝트에 감명을 받았다면 NASA의 방법론을 베낄게 아니라 NASA의 사람을 데려와야 한다. 그럼에도 왜 이렇게 많은 방법론 책들이 있을까 ? 그 의문에 대해 디마르코는 다음과 같이 말했다. 

    "사람들이 업무의 인간적인 측면보다 기술적인 측면에 주로 매달리는 가장 큰 이유는
     기술적인 부분이 더욱 중요하기 때문이 아니라 거기에 매달리는 것이 훨씬 더 쉽기 떄문이다. 
     -톰 디마르코, 피플웨어


기술적인 측면에 매진한 결과는 이렇다. 

     "대부분의 프로젝트는 기술이 아니라 인적 자원과 프로젝트 관리의 문제로 실패한다.
      - R. Thomsett
     "업무에서 발생하는 문제들은 대부분 기본적으로 기술의 문제가 아니라 조직사회학의 문제다. 
      -톰 디마르코, 피플웨어


사실 좋은 프로그래머를 찾는 것은 

      "생산성에 대해 어떤 사람들은 다른 사람들보다 5배나 우수하고
       디버깅에서는 어떤 사람들은 다른 사람들보다 28배나 우수하다.
       - 로버트 L 글라스, 소프트웨어 컨플릭트 2.0


에 의하면 타당하다.  5배가 우수한 사람이라도 5배의 비용이 들지는 않을 것이기 때문이다. 혹시 2배 뛰어난 사람을 구하진 못해도 2배 더 일하게 하는 건 어떨까 라고 생각하는 관리자가 있다면 

      "초과근무 시간 증가는 생상성 감소 기법이다. 스트레스를 받는 사람들은 머리가 빨리 돌아가지 않는 법이다. 
       - 톰 디마르코, 피플웨어

     "아홉 명의 여자가 투입된다고 해서 아기를 한 달만에 낳을 수는 없다.
     - 프레드릭 브룩스, Man-Month의 신화

라는 유명한 격언이 있으니 굳이 다시 해볼 필요는 없다. 사실 대부분의 프로그래머는 이미 주 70-80시간 정도를 일하고 있으니 초과근무가 해법이 아닌것은 분명하다.


이러한 관리자에게 해주고 싶은 말은 앞의 디마리코가 이미 언급하였다. 

     "관리자가 진정해야 하는 일은 사람들에게 일을 시키는 것이 아니라
      그들이 일에 전념할 수 있는 환경을 만들어 주는 것이다.
      - 톰 디마르코, 피플웨어


단순히 프로그래머에게 일하는 시간의 양이 중요하지 않다는 얘기는

      "프로그래밍에서는 평균적인 수준의 노동력을 유지하는 것보다
       영감이 샘물처럼 솟아나는 소중한 순간을 놓치지 않는 것이 중요하다. 
       - 임백준

가 언급하였다.


경력이 비슷한 프로그래머간에도 품질과 생산성의 격차가 생기는 이유는 공장에서 찍어내는 KS가 찍힌 양산품이 아니기 때문이다. 

       "컴퓨터 사이언스를 가르치는 교육이 어떤 사람을 전문적인 프로그래머로 만들지 못하는 것은,
        붓질과 채색방법을 가르치는 교육이 어떤 사람을 전문적인 화가로 만들지 못하는 것과 같다.
        - 에릭레이먼드, 해커와 화가 중



그래서 일찍이 제럴드 와인버그는 이런 말을 했다.

      "모든 프로젝트에는 세가지 문제가 있다. 사람 사람 사람 .
      제럴드 와인버그, 컴퓨터 프로그래밍 심리학

'IT 이야기' 카테고리의 다른 글

Nice game  (0) 2009.03.25
멘탈 2  (0) 2009.03.23
프로젝트의 예측  (0) 2009.03.12
Scott Adams  (0) 2009.03.11
사람이 읽는 코드  (0) 2009.03.11
Posted by bleujin
IT 이야기2009. 3. 12. 06:41



- 우리 프로젝트 계획은 평소처럼 아래의 일반적인 과정을 겪을 것입니다.

- 1단계는 능력의 과신으로 인한 근거없는 낙관주의 입니다.

- 2단계는 방해자들이 그들의 소굴에서 기어나와 우리의 꿈을 말살하려고 할겁니다.

- 3단계는 무지와 시기심에서 비롯된 루머들이 반복해서 양산되다가 급기야 일반적 사실로 둔갑합니다.

- 자원은 이런 그릇된 정보와 편협된 취사선택의 과정을 통해서 배분될 것입니다.

- 그리고 프로젝트가 엉망이되고 불가능해질때까지 요구사항들은 둥둥 떠따닐 것입니다.

- 그리고 나서 우리한테 2주가 남겠죠

- 저 그냥 근거없는 낙관론자로 돌아가고 싶은데요



여기서 웃음 포인트는 저기 "그리고 나서 우리한테 2주가 남겠죠"이다. 저 시기는 사용자, 관리자, 프로젝트 팀원, 그리고 프로젝트의 모든 다른 이해관계자들이 프로젝트를 납기안에 끝내지 못할 것이라는 것을 시인해야 하는 "진실의 순간"이다.  팀원들이 자기를 리더라고 생각해주길 바라는 관리자가 일정을 대폭 단축했기 때문에 프로젝트가 시작하고 바로 누군가 이건 불가능해 혹은 이게 성공할 확률은 로또보다 낮아라고 부르짖었을지라도 그건 이미 잊혀졌다.

개발자가 수일에 걸쳐 한 예측을 아무 근거없이 10-50%를 깍고 자신의 능력으로 이러한 단축을 이끌어 낼 수 있었다고 생각하는 관리자는 수없이 많다. 자신의 일은 이렇게 예산과 납기기한을 줄이는 것이라고 생각하는 듯하다. 물론 그렇다고 프로젝트 실행시간은 빨라지지 않는다. 프로젝트가 10%일찍 끝나지 않는다면 수십만달러의 손실이 발생할것이라고 프로그래머를 압박하는 대신에 애초에 왜 그만큼 일찍 시작하지 못했는지를 생각해야 한다. 

어쨌든 이 진실의 순간 프로젝트 관리자는 경질되고 후임이 온다. 만약 행운이 따르지 않는다면 다시 1단계로 올라가서 같은 과정을 반복하게 되고 다시 또 2주가 남게된다.


'IT 이야기' 카테고리의 다른 글

멘탈 2  (0) 2009.03.23
How vs Who  (0) 2009.03.16
Scott Adams  (0) 2009.03.11
사람이 읽는 코드  (0) 2009.03.11
멘탈 ?  (0) 2009.03.01
Posted by bleujin
IT 이야기2009. 3. 11. 06:42



(Dogbert의 대통령 선거 연설)
저에게 투표하지 않으면 테러리스트가 여러분의 해골을 샐러드 접시로 사용할겁니다.

제가 당선되면 저에게 투표하지 않은 사람들의 돈을 뺐어서 저를 지지한 사람들에게 나누어 주겠습니다.

공해는 비타민을 함유하고 있습니다. 
 - 감성적인것이 좋군요..



시니컬한 딜버트로 유명한 스콧 아담스의 만화중 하나인데 그 뛰어난 통찰력이 놀랍다.

연설의 기본적인 3가지 범주인데
첫번째는 일단 자신이 아니면 안된다는 공포심을 조장해야 하고
두번째는 편을 갈라서 자기편을 들면 이만큼의 돈을 얻을 것이다라고 말하고(그걸 꼭 지켜야 할 필요는 없다)
세번째로 근거없는 황당한 소리를 지지해줄 몇명의 박수부대를 숨겨놓는 것이다. (이 의미가 맞는지는 불확실)


재미있어서 퍼오긴 했지만 IT 하고 별로 상관은 없어서 다른 유머를 추가한다.


한 여자가, 기구로 비행을 하던 도중 바람에 지도를 날려버렸다. 아무래도 어떤 방향으로 가야할지 몰랐기에
그녀는 어쩔 수 없이 저 아래를 걷고있던 남자에게 소리를 질렀다.

「저, 실례합니다. 여기가 어디인지 가르쳐 주실 수 없습니까? 원래는 1시간 안에 돌아갈 계획이었지만 여전히
   이렇게 하늘만 맴도는 중입니다···」

남자는 이렇게 대답했다.

「당신이 있는 장소는, 대략 지상 30미터 정도의 상공입니다. 위치는 북위 36도 30분과 35분의 사이, 동경
   39도 45부와 50분 사이지요」

그 말을 듣고 여자가 물었다.

「실례지만, 직업이 엔지니어 아니신가요?」
「그렇습니다. 어떻게 아셨죠?」
「지금 받은 정보는 분명 이론적으로는 틀림없을 것입니다. 그러나 숫자는 해석 방법을 모르면 전혀 도움이
   되지않습니다. 실제로 저는 변함없이 계속 헤매고 있을 뿐이고 문제는 아무 것도 해결되지 않았습니다」

그러자 남자는 이렇게 말했다.

「당신은 프로젝트 매니저 아닙니까?」
「네, 그렇습니다만, 어떻게 아셨죠?」
「우선 당신은 자신이 지금 있는 위치나 자신이 향해야 할 목표조차 모릅니다. 게다가 지킬 수 있을지 어떨지
   모를 약속을 혼자 스스로 한 주제에 저에게 문제 해결을 요구하고 있기 때문입니다. 쉽게말해, 상황 자체는
   아무런 변화가 없는데도 당신은 자연스럽게 전부 제 탓이라고 말하고 있기 때문입니다」

'IT 이야기' 카테고리의 다른 글

How vs Who  (0) 2009.03.16
프로젝트의 예측  (0) 2009.03.12
사람이 읽는 코드  (0) 2009.03.11
멘탈 ?  (0) 2009.03.01
소프트웨어의 변증법  (0) 2009.02.23
Posted by bleujin
IT 이야기2009. 3. 11. 02:35


대부분의 프로그램은 한번에 작성되지 않는다. 프로그램들은 그 수명주기 동안 계속해서 개정되고 재 작성된다. 요구사항의 변화와 기능성의 개선 필요성 때문에 프로그램은 계속해서 수정되어야 한다. 이러한 과정에서 반드시 인간이 원래의 코드를 읽고 이해할 수 있어야 한다. 따라서 컴퓨터가 프로그램을 이해할 수 잇는가 보다 인간이 프로그램을 이해 할수 있는가가 훨씬 중요하다.

물론 컴퓨터는 복잡성을 불평없이 다룰 수 있다. 그러나 사람은 그렇지 못하다. 무어의 법칙에 따르면 컴퓨터 칩의 집적도는 18개월마다 2배씩 향상되어 2배의 복잡함을 처리할 수 있지만(보통 알려져 있는 것과 달리 무어는 클럭 스피드가 아니라 칩의 집적도와 복잡함에 대해 말한 것이다. ) 인간은 18년이 지나도 2배의 복잡도를 가진 문제에 적응할 수 없다. 그리고 이러한 읽기 어려운 코드는 사람의 생산성을 크게 훼손시키기 마련이다. 반면 쉽게 이해할 수 있는 코드는 생산성을 높여준다. 그리고 우리는 그러한 코드에서 미를 발견한다.

컴퓨터 프로그램을 읽기 쉽게 만드는 요인은 무엇인가? 다시 말해서, 무엇이 아름다운 코드인가? 아름다운 프로그램에 대한 기준은 사람마다 다르겠지만, 컴퓨터 코드의 특성을 판정한다는 것이 단순히 미학의 문제는 아니다. 컴퓨터 프로그램은 그것이 주어진 과제를 얼마나 잘 수행하는지에 따라 판정된다. 다른 말로 하면 아름다운 코드는 프로그래머의 노력과는 무관하게 존재하는 추상적인 미덕이 아니다. 아름다운 코드라면 프로그래머를 행복하게, 그리고 생산성을 높이는데 도움을 줄 수 있어야 한다.

아름다운 코드를 논할 때 다소 논쟁의 여지를 제공하는 측면으로는 익숙함이 있다. 사람들은 자신이 생각하는 것보다 보수적이다. 대부분의 사람들은 새로운 개념을 받아들이거나 사고방식을 바꾸기를 꺼려하며, 변화보다는 참고 견디는 것을 더 좋아하는 듯 하다. 익숙한 도구를 다른것으로 교체하거나 새로운 언어를 배우는 일을 특별한 이유없이 꺼리는 사람들도 많다. 사람들은 가능하면 자신이 항상 상식으로 가눚하던 것을 잣대로 해서 새로운 방법을 평가하는 경향이 있다. 그러다보니 새로운 방법을 부당하게 저평가 해버린다.

사고방식을 바꾸는 데 드는 비용은 흔히 생각하는 것보다 훨씬 높다. 완전히 다른 개념으로 전환(이를테면 절차적 프로그래밍에서 논리적 혹은 함수적 프로그래밍으로의 전환)을 용이하게 하려면 다양한 개념들에 정통할 필요가 있다. 가파른 학습곡선은 인간의 두뇌를 고통스럽게 만들고 결국 프로그래머의 생산성을 낮춘다.

아름다운 코드란 결국 간결하며 유연함과 효율성을 가짐과 동시에 보수성을 가진다. 그런데 그것들 자체로 아름다운 코드가 보장되지는 않는다. 그런 요소들 모두의 균형을 처음부터 염두에 두어야 각 요소가 다른 요소와 조화를 이루어서 아름다운 코드가 만들어 지는 것이다. 


컴퓨터가 아니라 사람에게 쉬운 코드를 작성해야 하는 걸 알았다면 이제 사람 누구에게 쉽다는 건지를 파악해보자? 쉽다라는 말 자체가 상대적인 판단을 의미하고 판단을 내리는 주체는 코드를 작성한 사람이 아니라 코드를 읽는 사람이다. (얼마의 시간이 지난후의 작성자도 한명의 독자가 될 수 있다. )

좀더 얘기를 하기전에 이쯤에서 잠깐 퀴즈를 하나 내보자 "어떤일을 하는데 있어서 가장 빠른 방법은 무엇일까?" 정답은 어떤 일을 하지 않는 것이다. 소프트웨어에서 가장 아름다운 코드, 가장 아름다운 함수, 그리고 가장 아름다운 프로그램은 바로 전혀 존재하지 않는 코드(또는 함수, 프로그램)라는 말이 있는 것처럼 가장 쉬운 코드는 존재하지 않는 코드이고 두번째로 쉬운 코드는 읽을 필요가 없는 코드이다.

단순히 스터디를 위해서 참조하는 경우를 제외하고 프로그래머가 다른 사람의 소스를 보는 이유는 
 - 사용방법을 몰라서
 - 해봤는데 기대하단 결과가 나오지 않아서(잘못 사용했거나 버그)
 - 인수 인계 등으로 프로그램을 이해하기 위해서
이다. 그렇다는 얘기는 프로그램이 잘 설계되어 있어서 사용방법이 쉽고 예측대로 작동된다면 굳이 함수나 클래스 내용을 뒤져볼 필요가 없다는 것을 뜻한다. 그래서 사실 읽기 쉬운 코드란 코드 그 자체의 대한 이야기라기 보단 좋은 설계에 대한 이야기이다.


"어려운 코드보다 단순한 코드가 더 좋다" 라는 말을 한번쯤 들어보았을 것이다. 번역되는 책마다 용어 선택의 차이 일수도 있고 아니면 우리 언어가 가지는 추상성의 문제일 수도 있지만 이 글에서는 쉽다와 간결하다를 구분하기로 하자. 쉽다는 어렵다(difficult)의 반대 용어로 사용하고 간결하다는 복잡하다(complicate)의 반대 용어이다. 그래서 쉬우면서 복잡하거나 쉬우면서 간결한 코드가 있을 수 있다.

물론 베스트는 쉬우면서 간결한 코드이고 최악은 어려우면서 복잡한 코드이다. 문제가 되는건 쉽지만 복잡한 코드일때와 어렵지만 간결한 코드일때 어느게 더 나을까 하는 점이다.

예컨데 프로그래밍은 상상이다등의 책에서 자주 언급되는

Case1 ) if else를 사용
if (conditon)
   one line code
else
   one line code

Case 2) 삼항연산자 사용
condition ? one line code : one line code

Case1은 쉽지만 복잡한 코드이고 Case2는 상대적으로 어렵지만 간결한 코드이다. Case 1의 지지자들은 삼항연산자가 잘 쓰이지 않는 연산자이기 때문에 앞에서 말한대로 코드를 읽는 사람을 배려해야 한다고 주장한다. 이 블로그 자체가 원칙을 논하는 블로그는 아니므로 옳다라고 하는 건 아니지만 개인적으로는 Case 2) 상대적으로 어렵지만 간결한 코드를 선호한다.

물론 코드는 읽는 사람을 배려해야 하는 사실을 인정하지 못하는 것은 아니다. 이유는 두가지가 있다.

첫째 어려운 것은 시간이 감에 따라 나아지지만 복잡한것은 그리 나아지지 않기 때문이다. 인간의 지적인 순수 능력은 수십년 전에 비하면 훨신 향상되었지만 여전히 7자리 넘는 숫자를 외우는 데에는 버거워 한다. 설사 읽는 사람이 삼항연산자를 몰랐더라도 아마도 시간이 지남에 따라 곧 적응이 될 테지만 읽는 사람이 몇년차 프로그래머인지에 상관없이 복잡한 프로그램은 여전히 복잡한 프로그램이다.


두번째 이유를 설명하기 전에 위 케이스는 익숙하긴 하지만 난이도 자체가 너무 쉬위므로 아래의 소스를 보자

Case A)
(function(){
  var alertFun = window.alert, c=0;
  window.alert=function(q){
   
    if (++c%4==0) { 
      if (!confirm(q+'\nThere have been '+c+' alert boxes, continue displaying them?')) window.alert=function(){};
    } else alertFun (q);
  }
})();

위 코드를 보고 한눈에 이해했다면 자바스크립트에 비교적 능숙한 프로그래머 일것이다. 위 코드는 웹 페이지의 무한 alert 핵을 막기위해 연속으로 alert 메소드가 호출될 경우 4번마다 한번씩 확인창을 띄어서 원하지 않을 경우 중단시킬 수 있도록 window.alert을 재정의한 코드이다. 자바스크립트의 함수형 언어의 특징과 클로저가 사용되서 어려워진 위의 코드를 쉽게 사용한다면

Case B)
var alertFun = window.alert,c = 0 ;
window.alert = function() {
    if (++c%4==0) { 
      if (!confirm(q+'\nThere have been '+c+' alert boxes, continue displaying them?')) window.alert=function(){};
    } else alertFun (q);

와 같이 바꿀수도 있다. 자바스크립트를 거의 모른다 하더래도 어쨌거나 첫번째 코드에 비해 훨씬 더 이해하기 쉽다. 

이러한 선택이 주어진다면 나는 고민없이 CaseA를 택한다. 단순히 코드 자체만을 본다면 Case A의 코드가 더 어렵다. 하지만 나중에 문제가 생길확률은 B가 월등이 높다. Case B의 코드는 쉽기는 하지만 전역변수인 alertFun의 존재를 기억하고 있어야 하고 혹시 그것을 잊어버려서 다른 곳에서 동일 이름의 변수를 사용한다면 버그의 이유를 찾는데 더 오랜 시간이 걸리기 때문이다. CaseA의 코드는 어렵기는 하지만 다른 코드에 의존하는 것이 거의 없고 매우 간결하고 독립적이기 때문에 문제를 일으킬 소지가 낮아서 애초에 Case A의 소스를 봐야 할 확률이 낮다. (가장 좋은 코드는 존재하지 않는 코드라는 것을 기억하자.)

사실 코드를 읽는 사람의 수준을 고려해 쉽지만 복잡한 코드를 작성하는 것보다 - 쉬운 코드라는 것은 작성자의 입장에서는 아무런 상관이 없는 말이고 - 쉬운 코드를 버리는 대신 복잡한걸 간결하게만 만들 수만 있다면  어렵더라도 애초에 코드를 읽을 필요가 없게 작성해주는 것이 더 좋다. 


정말 아이슈타인이 만들었는지는 모르지만

1. 다섯채의 각각 색깔이 다른 집이 있다.
2. 각 집에는 각각 다른 국적의 사람이 산다.
3. 주인들은 각각 다른 음료수를 마신다.
4. 다른 종류의 담배를 피고, 다른 종류의 동물을 기른다.
5. 영국인은 빨강색 집에 산다.
6. 스웨덴인은 개를 기른다.
7. 덴마크인은 홍차를 마신다.
8. 녹색 집은 흰색 집 왼쪽에 위치해 있다.
9. 녹색 집 사람은 커피를 마신다.
10. 풀몰 담배를 피우는 사람은 새를 기른다.
11. 노란색 집에 사는 사람은 던힐 담배를 피운다.
12. 한가운데 사는 사람은 우유를 마신다.
13. 노르웨이인은 첫번째 집에 산다.
14. 블랜드 담배를 피우는 사람은 고양이를 기르는 사람 옆집에 산다.
15. 말을 기르는 사람은 던힐 담배를 피우는 사람 옆집에 산다.
16. 블루매스터 담배를 피우는 사람은 맥주를 마신다.
17. 독일인은 프린스 담배를 피운다.
18. 노르웨이인은 파란색 집 옆집에 산다.
19. 블랜드 담배를 피우는 사람은 물을 마시는 사람 옆집에 산다.

그럼 여기서 금붕어를키우는사람은 누구일까요~
.. 라는 널리 알려진 퀴즈가 있다.

보통의 프로그래머라면 3-5분 이내에 풀수 있지만 중요한건 결코 프로그램을 이딴식으로 만들어서는 안된다는 것이다.

위의 명제들은 모두 단순하지만 서로 얽혀서 이를 이해하는데 종이와 연필이 필요하다.(만약 위 문제를 머리속으로만 풀었다면 멋지다.) 위 문제는 쉽지만 복잡하다. 만약 위의 각 명제가 하나의 함수라고 가정한다면 복잡한 구조때문에 함수 하나하나는 쉽지만 코드는 무척 이해하기 어려운 구조가 된다. 달리 말해서 코드나 알고리즘의 쉽다 어렵다 보다는 구조에 있어서의 간결한 구조가 훨씬 더 중요하고 만약 구조가 간결하다면 애초에 코드 레벨의 소스를 살펴봐야할 필요는 없을 것이다.





보통의 책에서 쉽다(가독성이 좋다)는 말에는 쉽다는 말과 간결하다는 중의적 의미를 내포하고 있고 단순히 쉽다=좋다는 것이 아니므로 좀더 구분해서 이해해야 한다. 





간결함에 대해 선배들은 다음과 같이 충고하였다. 펄리스의 말은 특히 훌룡하다.  



코드를 삭제함으로써 기능을 추가하도록 노력하라

설계자는 더 추가할 것이 없을때가 아니라 더 뺄것이 없을때 완벽함을 이루었음을 알게 된다. (생떽쥐페리)


강건한 문장은 간결하다, 필요없는 단어는 생략하라(스트렁크와 화이트)

가장 싸고 빠르고 신뢰할 수 있는 요소는 바로 존재하지 않는 요소이다. (벨)

더 적은 것으로 더 많은 것을 얻도록 노력하라

만일 시간이 더 주어졌다면 편지를 더 짧게 썼을 것입니다. (파스칼)

간결함은 복잡함보다 앞서지 않는다. 그 뒤에 오는 것이다(펄리스)

적을수록 많다(브라우닝)

모든 것을 최대한 간결하게, 그러나 너무 단순하지는 않게 만들라(아이슈타인)

종종 소프트웨어는 하나의 비누 거품으로 보아야 한다(펄리스)

간결함을 통해서 아름다움을 추구해라





# 무어의 법칙
Moore의 법칙은 잘못 인용되고 있는 것들 중 하나이다. 대부분의 전문가들은 컴퓨터는 18 개월마다 속도가 두 배로 증가한다고 주장하고 싶어한다. 사실 인텔 창립자 Gordon Moore는 1965년 컴포넌트 당 가장 싼 칩의 복잡함은 매년 두배로 증가했다는 것을 관찰했고, 이러한 트랜드는 약 10년간 지속될 것이라고 예견했다. 그런데 이것은 퍼포먼스가 두 배로 증가한다는 것을 의미하는 것은 아니다. 컴포넌트의 수가 그렇다는 것을 의미한다. Moore의 예견은 데이터에 의해 합리적으로 입증되었다. 1975년 그는 18 개월로 조정했으며 이것이 바로 대부분의 사람들이 요즘 인용하는 것이다. 재미있는 것은 Moore의 법칙은 시스템 디자인의 복잡함에 대해 언급하는 것 과는 달리 클럭(clock) 스피드에 대해서 많이 언급하지 않는다.

무어의 정확한 말을 보려면 http://ko.wikipedia.org/wiki/%EB%AC%B4%EC%96%B4%EC%9D%98_%EB%B2%95%EC%B9%99로 가면된다.

소크라테스는 ‘악법도 법이다’라는 소리를 지껄인 적이 없다…는 주장이 점점 힘을 받고 있는데, ‘아 다르고 어 다르다’ 소크라테스도 공자도 자기가 하고 싶은 바를 후세에 그대로 전달하지는 못했나보다. 무엇이든 자기가 바라는대로 맞춰서 전파하려던 선글래스 독재자까지는 아니더라도 이 시대를 살아가는 사람들은 옛 사람이 남긴 말을 그대로 전달하려고 하지를 않는다.

'IT 이야기' 카테고리의 다른 글

프로젝트의 예측  (0) 2009.03.12
Scott Adams  (0) 2009.03.11
멘탈 ?  (0) 2009.03.01
소프트웨어의 변증법  (0) 2009.02.23
추상화  (0) 2009.02.22
Posted by bleujin
IT 이야기/유니코드2009. 3. 1. 09:53

유니코드를 적용하는데 있어 어려운 점은 단순히 프로그래밍에서 Content-Type만을 바꿔서는 할 수 없다는 것이다. 유니코드를 적용하려면 프로그래밍 외에도 메시지등의 텍스트 리소스와 데이타베이스 등도 모두 Unicode로 전환해야 한다.

그 중 문제가 되는 것은 기존의 레거시 파일들과 데이타 베이스이다. 새로 시작하는 프로젝트가 아닌이상 기존의 데이타베이스 레거시 데이타를 유니코드로 바꾸는 것은 매우*100 힘들다. 단순히 기술적으로 어렵다는 얘기가 아니라 여러가지 제한이 있기 때문에 완벽한 변환은 사실상 불가능하다.

일단 오라클의 경우 한글 처리에 대해 살펴보자.



오라클을 설치중에 위와 같은 화면이 나오는데 여기서 한국어의 선택은 한국어의 저장여부와 아무 상관이 없다. 여기서 한국어를 선택하면 오라클 클라이언트의 메시지가 한글로 나오는 것과 한글 폰트를 사용하고 Territory 지원을 한국으로 설정한다는 뜻이다.(즉 숫자나 날짜 포멧등을 Korea에 맞춘다.)

오라클의 환경변수중 하나인 NLS_LANG을 보면  KOREAN_KOREA.KO16MSWIN949, AMERICAN_AMERICA.KO16MSWIN949, KOREAN_KOREA.KO16KO16KSC5601 와 같이 사용하는데. 언어_영역.캐릭터셋의 구조로 되어 있다. 흔히 UTF를 사용하고자 한다면 NLS_LANG을 바꿔야 하는거냐고 오해하는데 NLS_LANG은 데이타베이스 캐릭터 셋과 상관없다. (NLS_LANG가 데이타베이스 캐릭터 셋과 항상 같아야 한다면 굳이 따로 설정해야 할 필요가 없다.) NLS_LANG은 원격지의 데이타베이스 환경이 아니라 자신이 속해 있는 환경을 데이타베이스에 알려주는 역할을 한다. 이 NLS_LANG 값의 의거해 데이타 베이스는 실제 UTF8로 저장이 된 데이타를 MSWIN949 코드로 변경하여 사용자에게 보여준다.



이 화면 뒤에 선택할수 있는 오라클에서 한글을 저장할 수 있는 캐릭터 셋은 KO16KSC5601, KO16MSWIN949, UTF 계열(오라클의 버전별로 UTF8, AL32UTF8, AI16UTF16 등이 있다.) 3가지가 있다. 한글 처리에 대해 익숙하지 않던 아주 오래된 레거시 데이타베이스의 경우 US7ASCII 캐릭터 셋에 한글이 저장되었다고 하는 경우도 있으나 이때의 한글은 한글이 아니라 단순히 바이트 덩어리이므로 한글이 저장되는 캐릭터 셋이라고 할 수 없다. (DB에서는 캐릭터 셋과 인코딩의 의미를 명백히 구분하지 않는다. 인코딩을 담당하는게 오라클 DB 자체이므로 사용자는 인코딩의 구현에 알 필요가 없으며 캐릭터 셋의 선택은 곧 인코딩이라고 할 수 있다. <- 이게 앞에서 말한 의도와 구현을 분리한 장점이 된다.)

KO16KSC5601
앞에서 말한 2350자의 완성형 한글만을 지원하는 캐릭터 셋이다. 유닉스에서 Lang=ko 셋팅시 기본값으로 선택이 되고 이름에서 나오는 그럴듯함때문에 선택되기도 하지만 새로운 프로젝트라면 위 캐릭터 셋 사용은 자제해야 한다. 혹자는 insert into test(a) values('숖') 같은 insert문이 실행되기 때문에 확장 한글이 들어간다고 오해하지만 실제로 select 시에 ?로 나온다. MS949를 사용하는 윈도우의 클라이언트 툴로 글자를 적을 수는 있지만 인코딩 해쉬함수를 거칠때 엉뚱한 숫자로 저장이 된다. 그래서 insert가 되는 것처럼 보이지만 실제로 select는 되지 않는다.)

KO16MSWIN949
Windows-949라는 캐릭터 셋은 MS만 사용하는 캐릭터 셋은 아니다. 캐릭터 셋 자체가 일종의 개념적인 코드 맵이므로 어느 소프트웨어든 상관없이 사용할 수 있으며 KO16MSWIN949는 오라클의 MS949 같은 캐릭터 셋이다. 완성형(KO16KSC5601)을 그대로 포함하고 있으며 추가적인 조합인 8822자의 한글을 추가적으로 포함하고 있는 KSC5601의 슈퍼셋이다. . KSC5601에서 사용하지 않는 바이트 코드를 사용하므로 한글 자모순서의 정렬이 제대로 되지 않기 때문에 일반적은 Order by문으로는 정렬 되지 않는다. 제대로 정렬하려면 Select * From test Order by NLSSORT(a, ‘NLS_SORT=UNICODE_BINARY’) 와 같은 구문을 사용해야 한다. 물론 인덱스도 위와 같이 캐릭터 셋을 적용하지 않고 만들었으면 인덱스를 사용할 수 없고 별도의 정렬과정을 거쳐야 하기 때문에 느리다.

UTF8(UTF8/AL32UTF8 등)
UTF8은 유니코드를 구현한 캐릭터 셋중에 가변길이 인코딩을 택하고 잇는 캐릭터 셋이다. 인코딩에 대한 얘기는 앞에서 했으니 생략하겠지만 가변 길이를 위해 일종의 플래그 비트를 각 바이트마다 포함시켜야 하다보니 한 글자를 표현하는데 필요한 바이트의 길이가 최대 1-4바이트(AL32UTF8의 경우 최대 6바이트)까지 늘어날 수 있다. 유니코드는 잘 알려진 바와 같이 현대 한글 11172자를 모두 가나다 순으로 정렬된 상태로 포함하고 있다. CJK 한자가 3바이트의 물리적 공간을 차지하기때문에 약간 공간을 많이 차지하지만 한글 이외에도 다른 언어들을 함께 데이타베이스에 저장해야 한다면 다른 선택의 여지가 없는 유일한  선택이 된다.

캐릭터 셋을 통한 방법 말고도 한글을 저장할 수 있는 방법이 한가지 더 있는데 데이터베이스 character set에 관계없이 NCHAR (National Character) 데이터타입을 이용하는 방법이다. 오라클 9i부터 NCHAR 데이터타입에 유니코드 character set을 지정할 수 있게 되었으므로 두 번째 방법은 9i부터 고려할 수 있는 방법이다. (NCHAR 데이터타입의 character set은 기본값으로 데이터베이스 생성시 NATIONAL CHARACTER SET에 지정된 값에 따르기 때문에 단순히 NCHAR를 사용한다고 해서 Unicode로 저장되지 않는다는 걸 주의하자.즉 만약 어떤 이유에 따라 NCS를 바꾸게 되면 이전에 저장한 글자는 제대로 보이지 않고 한가지 언어(+ASCII)만을 저장할 수 있다는 것을 주의해야 한다. 같은 컬럼에 한글과 일본어가 같이 저장되야 한다면 NCHAR는 사용해서는 안된다. ) 언뜻 보이기에 편해 보일지 모르지만 프로그래밍 환경, 마이그레이션의 용이성, 성능, 데이터 타입, 어플리케이션의 타입 등 많은 요소들을 고려해야 하기 때문에 결코 쉬운 일은 아니다.


   KO16KSC5601 KO16MSWIN949  UTF8  AL32UTF8 
 한글 지원  한글 2350자  2350+8822(11172자) 11172자   11172자 
 인코딩 버전  한글 완성형  확장은 MS949에 따라 배열  Unicode 2.1, 3.0  Unicode 3.0, 3.1, 3.2, 4.0
 한글 바이트  2 byte  2 byte  3 byte  3 byte
 지원 버전  7.x  8.0.6 이상  8.0 이후  9i R1 이상
 NChar로 설정가능  불가  불가  가능  불가
 한글 정렬  단순 바이너리 정렬  KOREAN_M, UNICODE_BINARY  한글 : 바이너리
 한자 : KOREAN_M
 
 장점  .  2byte로 모든 한글 입출력 가능  정렬이 효과적, 다른 언어들과 같이 저장되어야 할 경우 다른 대안이 없음  동일
 단점  한글 2350만 가능
 가능한 사용자제
 완성형과 호환문제로 글자 배열순과 정렬 순서가 다름  공간의 소모
 인코딩/디코딩 시간
 

가장 이상적인 방법은 AL32UTF8을 database character set으로 정하고 UTF8을 지원하는 언어로 프로그램을 개발해서 character set conversion이 없이 데이터가 오가는 것이다.

유니코드만을 비교하면 오라클의 경우 V7에서는 유니코드 버전 1.1을 지원하는 AL24UTFFSS (V7에서만 사용가능) 도입하면서 유니코드를 지원하기 시작하였고 V8에서 유니코드 버전 2.0을 지원하는 UTF8이 도입되고 V9에서는 유니코드 버전 3.1을 지원하는 AL32UTF8 / AL16UTF16이 도입되어 유니코드를 지원하고 있다. 자세한 것은 아래 표를 보면 알 수 있다.

Character set

지원 버전

유니코드

인코딩

유니코드 버전

Database character set

National character set

AL24UTFFSS

7.2-8i

UTF-8

1.1

지원

지원 안함

UTF8

8.0-9i

UTF-8

8.0-8.1.6 : 2.1

8.1.7이후 : 3.0

지원

9i만 지원

UTFE

8.0-9i

UTF-8

8.0-8.1.6 : 2.1

8.1.7이후 : 3.0

지원

지원 안함

AL32UTF8

9i

UFT-8

9iR1 : 3.0

9iR2 : 3.1

지원

지원 안함

Al16UTF16

9i

UFT-16

9iR1 : 3.0

9iR2 : 3.1

지원 안함

지원



캐릭터 셋 선택

1. 한글 지원을 위해서는 반드시 위의 3가지 선택(KO16KSC5601, KO16MSWIN949, UTF 계열)중의 하나를 선택해야 한다.

2. 한국에서만 쓸거고 한글만 쓸거다. 그리고 하드디스크 값이 비싸다 라는 시스템이라면 KO16MSWIN949을 선택해도 된다. 다만 정렬에 문제가 있다.

3. 한국어 뿐 아니라 중국어, 일본어, 러시아 어 등 다양한 데이타를 저장해야 한다면 UTF 계열을 쓴다. 인코딩 시간으로 MSWIN949보다 조금 속도 저하가 있다고 알려져 있으나 사실상 거의 영향을 미치지 않는다.

4. 대부분이 한글이며, 특정 나라의 외국어가 필요하다면 KO16MSWIN949를 사용하면서 NChar 컬럼에 외국어를 저장하는 것도 고려해볼만 하다.

5. KO16KSC5601은 쓰지 말자.
오라클 얘기만 했는데 MSSQL2000은 다국어 지원이 상대적으로 미비하다. 일반적으로 MSSQL은 윈도우 위에 설치되기 때문에 다국어를 사용하기 위해서는 사실 위의 방법 4밖에 대한이 없다. MSSQL의 NCHAR 계열의 컬럼은 UCS-2의 인코딩(UCS2의 캐릭터 셋의 바이트를 그대로 저장한다. 그래서 1개의 BMP만을 지원하며 16개의 SMP 문자는 사용할 수 없다.)을 사용하기 때문에 NCHAR의 모든 문자는 2byte다.(아스키도 마찬가지) 그래서 만약 MSSQL과 오라클이 둘다 지원되는 유니코드 다국어 시스템을 만든다면 두 DB가 길이 계산이 다르고 MSSQL은 SMP는 지원하지 않는 다는 사실을 유념해야 한다.

 

일반적인 프로그래밍 과정을 보자.

1. Client(Browser - JSP)
사용자가 메시지를 작성한다. 사용자의 request는 Stream 형태로 Server에 전송되는데 해당 웹 페이지가 UTF-8로 보여주고 있었다면 사용자가 텍스트 박스에 작성한 메시지도(request Body) UTF-8인코딩 바이트로 넘어가게 된다. (익스플로어의 경우 보기-인코딩 메뉴를 보면 현재페이지의 인코딩을 확인할 수 없다. )

HTTP 규약상 request는  request Header와 request Body로 구성되어 있는데 header에는 URI와  GET Parameter 그리고 쿠기 등으로 되어 있고 request Body는 Post 방식으로 넘어간 parameter들이 저장된다. 

request Body가 아닌 URI는 디폴트로 ISO-8859-1 방식으로 넘어가므로 Get 방식의 파라미터는 UTF-8이 아닌 ISO-8859-1로 인코딩 된다. (익스플로러의 경우 UTF-8로 URL을 보내기가 기본 false로 설정되어 있다.) 그렇기 때문에 GET방식을 사용하면 파라미터로 다국어를 넘기는게 사실상 어렵다.(a=한국어&b=일본어가 넘어올때랑 a=일본어&b=한국어 인 경우를 상상해보자.) GET으로 다국어 메시지를 넘기는 유일한 방법은 직접 파라미터를 UTF-8로 인코딩해서 16진수 숫자로 넘기는 방법이 있는데 이 경우 이미 GET의 가장 큰 장점인 간결을 희생한 결과다. 다만 이 방법을 사용하면 결과페이지를 즐겨찾기에 추가할 수 있는 장점이 있다.


2. Client(Browser - JSP) -> Server Program
사용자가 Send 버튼을 누른다.
Server 프로그래밍에서는 자바의 필터에 request.setCharacterEncoding("UTF-8"); 구문을 넣어주어 사용자의 request Body가 UTF8로 인코딩되었다는 사실을 알려준다. 정보를 설정하였을뿐 실제로 컨버팅이 되는건 아니라는 사실에 주의하자. 4점대의 구버전의 톰캣의 경우 Get으로 넘긴 파라미터로 넘긴 정보도 영향을 받았으나 많은 혼란을 일으켰고 이후 수정되었다. (보통 web.xml 파일에 필터를 설정)


3. Server Program(Java)
Java에서 사용하는 모든 변수는 UTF-16 인코딩을 사용하고 있지만 request.getCharacterEncoding()를 통해 Java는 사용자가 작성한 메시지의 인코딩 타입을 알 수 있으므로 request.getParam(name)의 값을 연산에 사용하는데 무리가 없다.

String a1 = request.getParam("name");   
// a1은 자바의 메모리에 저장되는 변수이므로 UTF16 인코딩을 사용한다. request.getCharacterEncoding() 값을 읽어서 자동 컨버팅한다. 만약 request.setCharacterEncoding을 하지 않았다면 디폴트로 request는 ISO-8859-1이 설정되어 있다.
인코딩에 대해서 잘 모르면 아래와 같은 웃긴 코드를 작성하게 된다.
// bad case1
String a2 = new String(a1.getBytes("MS949"),"UTF-8"); // a1을 MS949로 convert한 byte[]를 얻어서 이를 UTF-8로 디코딩한 값을 a2에 저장한다. 당연히 a2를 출력하려면 깨진다 -ㅅ-;

// bad case2
String a3 = new String(request.getParam("name").getBytes("EUC-KR"), "UTF-8")
request를 euc-kr로 컨버트한 byte[]을 얻어서 이를 UTF-8로 디코딩한 값을 a3에 저장한다. 물론 깨진다. new String(byte[], CharSet)은 앞의 byte[] 배열은 뒤쪽의 CharSet으로 디코딩을 해야 한다고 java에게 알려주는 역할을 한다.

// bad case3
String a3 = new String(request.getParam("name").getBytes("ISO-8859-1"), "MS949")
2000년 초반에 인코딩에 대한 충분한 지식이 없었을때 작성하던 코드들로 기본으로 브라우저는 ISO-8859-1 인코딩을 사용하기 때문에 별도의 설정 등이 없이 페이지를 만들었을때 사용자가 작성한 한글도 ISO-8859-1로 인코딩된다.(물론 잘못 인코딩되어 있는 것이다.) 이걸 ISO-8859-1의 byte[] 읽어서 이 녀석은 원래는 MS949로 인코딩이 된거라고 알려준다.(사용자는 requestBody에 한글을 입력하였으므로) 물론 이렇게 해도 한글이 읽히긴 읽힌다. 다국어를 사용할 수 없고 모든 parameter에 이 과정을 해줘야 한다는 불편이 있지만 말이다. 호미로 막을것을 가레로 막는 코드이기에 그냥 request.setCharacterEncoding를 해주는게 낫고 다국어를 사용하려면 코드에 지역 캐릭터 셋이 사용되서는 안된다.


4. Server Program -> DB(Oracle - UTF-8)
지금까지 제대로 되어 있다면 자바의 변수는 UTF-16, request Body는 UTF8로 되어 있을테고 DB에는 UTF-8로 컨버팅되어 들어간다. 자동으로 컨버팅이 될 수 있는 이유는 변환되기 전의 값들의 인코딩 정보를 알 수 있기 때문이다. 그러나 모든 DB가 자동으로 convert가 되는게 아니고(Driver URL의 옵션으로 알려주어야 하는 DB도 있다.) 모든 DB가 Unicode를 지원하는게 아니다.


5. DB(Oracle UTF-8) -> Server Program
DB에 저장된 UTF-8로 인코딩된 byte[]들을 InputStream으로 읽어오지만 String a = rs.getString(name); 로 자바 힙에 스트링 객체를 생성할때는 다시 UTF-16으로 컨버트 되어 연산에 참여한다.


6. JSP - Server Program(Servlet)
JSP는 첫 호출시에 Servlet 엔진에 의해 개별적인 Servlet으로 변환되는데 이때 JSP 파일을 Load하는데 이때 참조하는 정보가 JSP 제일 위에 있는 page 지시자인 <%@ page contentType="text/html; charset=euc-kr"> 이다. 파일 그 자체에는 Charset 정보가 없기 때문에 파일 첫머리에 해당 파일의 인코딩 정보를 기록한다. 유니코드 세상으로 가기위해서는 JSP도 UTF8로 작성해야 한다고 알려져 있는데 맞는 말이기도 하고 틀린말이기도 하다. 사실 JSP 편집을 디폴트로 MS949 인코딩을 사용하는 이클립스나 UEdit를 사용해도 문제가 없고 JSP의 contentType을 euc-kr로 설정해도 상관없다. 단 JSP에는 ASCII문자만 사용해야 한다. 물론 html Body안의 모든 문자도 마찬가지이다. 일단 ASCII의 0-127번은 모든 인코딩에서 겹치지 않으므로 문서를 euc-kr로 작성하나 UTF-8로 작성하나 차이가 없다. JSP에 한글을 사용하고 UTF-8로 인코딩을 해도 되지만 메시지를 한글로 작성한다는 자체가 이미 다국어를 지원하는 시스템이 아니다.

ASCII를 제외한 모든 message 문자는 별도의 파일에서 읽어들어 사용한다. 현재 이부분이 아직 이해가 되지 않는다. 일단 jsp는 어플리케이션 서버 내부적으로 지역적 인코딩을 사용하는 getWriter를 호출해서 사용한다. 그리고 메시지 파일에서 읽어온 값들은 UTF-8로 저장되어 있지만 자바 맵에 저장하는 순간 UTF-16으로 바뀐다. 서블릿에서 어떻게 연산이 되길래 UTF-8포맷으로 제대로 보이는걸까?? -ㅅ-??


7. Server Program -> Browser(Client)
Servlet은 Stream을 반환하는데 해당 Stream의 인코딩 정보를 Client Browser에게 알려주는게 response.setContentType("text/html; charset=UTF-8"); 이다. String이라는건 자바 내부 메모리 구조에서 UTF-16인코딩일뿐 기본적인 Network 통신은 항상 byte[] 형태의 Stream 형태로 이루어지므로 해당 Stream의 byte[]의 인코딩 정보를 알려줘야 한다. 만약 response의 setContentType을 설정하지 않았을경우 익스플로어는 독특한 짓을 하는데 특정 언어를 사용할때 나타나는 바이트 빈도를 토대로 언어와 인코딩의 방식을 추측한다. 다만 이경우 일반적인 분포도와 다른 글자들을 사용할 경우 잘못 인식하므로 화면이 깨진다.


여기서 헛갈리지 말아야 할것은 request나 response의 setCharacterEncoding은 Stream의 인코딩 정보를 알려주는 것일뿐 실제로 컨버팅을 하는게 아니라는 거다.

 

참고

 

http://www.oracle.com/technology/global/kr/pub/columns/oracle_lns_1.html
http://www.oracle.com/technology/global/kr/pub/columns/oracle_lns_2.html

'IT 이야기 > 유니코드' 카테고리의 다른 글

다시 쓰는 UTF  (0) 2009.06.12
Global Software - Unicode와 Programming  (0) 2009.02.26
Global Software - Unicode  (0) 2009.02.25
Global Software  (0) 2009.02.22
Posted by bleujin
IT 이야기2009. 3. 1. 09:52

야구는 멘탈 스포츠라고 불린다. 

취미를 제외한 직업적인 모든 스포츠들이 그러하듯이 야구도 1등을 추구한다. 야구같은 충분한 보상이 주어지는 경우 그러한 과정에서 모든 도전자들은 자신이 할 수 있는 최선의 노력을 경주한다. 그러함에도 중도 탈락하거나 2군을 남거나 레귤러가 되는등의 전혀 다른 결과를 만들어 낸다. 개인적으로 재능이라는 것의 비율을 크게 생각하지 않기 때문에 비슷한 노력을 했음에도 이렇게 다른 결과가 나오는 것에는 항상 의문이다. 그래서 무언가 계산할 수 없는 다른 변수를 이를테면 정신력으로 묶어버린다는 기분을 버릴 수 없다.

프로젝트의 성공도 단순히 프로그래밍이 훌륭하다고 해서 보장되는 것은 아니다. 누구나 알고 있는 사실이지만 보통의 사람들이 생각하는 것보다 훨씬 낮다. 훌륭한 프로그램이 성공하는 확률은 고작해야 10% 안팎이지 않을까 생각한다. 반대로 프로그래밍이 훌륭하지 못하면 프로젝트 실패의 확률은 90% 가까이 된다. 어쨌거나 그게 마케팅이건 시기의 적절성이건 무슨 원인을 내어도 알수 없는 변수가 끼어들기 마련이고 이때 다시 나오는 건 소위 해병대 정신식의 정신력이다.

그러나 iT에서 정신력을 강조하는 것은 대부분의 경우 그렇게 현명하지 못해다. 이게 과도해지면 진정한 프로그래머는 잠따위는 자지 않는다 식으로 변질이 된다. 상급자는 직원에게 이렇게 말한다. "우리가 했던 모든 프로젝트는 이렇고 이게 우리의 방식이야, 그렇지 못하겠다면 우리와 함께 일하기 어려워" 꼭 IT가 아니더라도 "정신력이 부족해" 라고 말하는 것은 우스운 일이다. 마치 지금의 MB가 긍정의 힘이 부족하다라고 말하는 것처럼 말이다. 긍정의 힘이나 정신력의 강조하는게 도덕적이나 실제상으로 옳다 아니다를 말하는 얘기는 아니다. 과거에 그렇게 했다고 해서 이번 프로젝트가 성공한다고 것도 아니고 또 프로젝트가 잘못되거나 설령 성공한다고 해도 프로젝트 팀원은 살아남을수 있다고 보장해 주는 것도 아니기 때문이다. 다만 이런 정신력을 강조하는 배경에는 그럴만한 배경이 있다는 사실을 의미할 뿐이다.



'IT 이야기' 카테고리의 다른 글

Scott Adams  (0) 2009.03.11
사람이 읽는 코드  (0) 2009.03.11
소프트웨어의 변증법  (0) 2009.02.23
추상화  (0) 2009.02.22
십계 - 변화의 속도  (0) 2009.02.04
Posted by bleujin
IT 이야기/유니코드2009. 2. 26. 04:14


Unicode에 대해 이해가 됐더라도(원래 유니코드는 복잡하지 않다. 다만 잘못 알려진 정보들과 혼합되어 혼란스웠을뿐 - 원래 진실을 숨기는 가장 좋은 방법은 노이즈 데이타를 많이 발생시키는 것이다. - 유니코드의 경우 의도된것은 아니었지만 - 진실과 거짓이 뒤섞이게 되고 대부분의 사람들은 귀찮아서 진실을 애써 찾기 않게 된다.) "이제 난 i18n 프로그래머야 핫핫" 이라고 말할 수 없다.

Unicode가 복잡하다고 사람들이 말하는 것은 이론적인 UNICODE 보다는 실제적인 프로그래밍과 DB에서의 사용에 있다. 프로그래머들이 가장 자주하는 삽질은 

// write code - bad
  File file = new File(hanFile);
  FileWriter w = new FileWriter(file);

  w.write(message);
  w.close();

// read code - bad
   BufferedReader r = new BufferedReader(new FileReader(file)) ;
   String m = r.readLine() ;
   r.close() ;

와 같은 코드이다. 앞서 언급한바와 같이 파일 그 자체에는 캐릭터 셋 정보가 들어가 있지 않다. 파일은 그냥 바이트 덩어리 일뿐이다. 그래서 Java와 같은 프로그래밍 언어로 파일을 읽거나 쓸때 캐릭터 셋 정보를 명시하지 않으면 시스템 기본 캐릭터 셋과 인코딩/디코딩을 사용하게 된다. 만약 사용하는게 한글 윈도우일 경우 기본으로 CP949(MS949) 인코딩을 사용하게 된다. 이 경우 파일을 쓰는 컴퓨터와 파일을 읽는 컴퓨터가 다르면 같은 자바로 작성되었다고 하더라도 서로 이해할 수 없는 문자가 나오게 된다. (코드 작성자의 컴퓨터와 실행하는 컴퓨터가 다를경우도 마찬가지이다. )

예컨데 Writer는 한글 윈도우에서 하고 유닉스에서 Reader를 하려면 종종 글자가 깨지는 것이다.(예컨데 윈도우는 MS949를 쓰고 리눅스는 기본 캐릭터 셋으로 UTF-8을 사용한다.) 즉 위 코드는 해당 프로그램이 실행되는 OS와 OS 셋팅에 따라 다른 결과를 야기할 수 있다.

OS에 상관없이 같은 바이트 배열을 가지는 파일을 생성하려면

// write code - not bad
  File file = new File(hanFile);
  FileWriter w = new FileWriter(new OutputStreamWriter(new FileOutputStream(file, charset)));

  w.write(message);
  w.close();

// read code - not bad
   BufferedReader r = new BufferedReader(new InputStreamReader(new InputStream(file, charset))) ;
   String m = r.readLine() ;
   r.close() ;

와 같이 명시적으로 Stream을 이용하여 charset을 설정해야 한다. 이 말은 달리 말해서 파일은 인코딩된 캐릭터 셋을 모르면 - 다른 프로그램이 생성한 파일 등 - 제대로 읽을수 없다는 뜻이다.



유니코드를 제외한 한글을 사용할 수 있는 캐릭터 셋은 KSC5601이며 인코딩은 cp949, MS949, euc-kr, ks_c_5601-1987 가 있다. KSC5601은 엄밀히 말해서 인코딩이 아니라 94*94 매트릭스에 정의된 캐릭터 셋이다. 그리고 유니코드 조직에 등록된 한글의 표준 캐릭터 셋이기도 하다. 공식적인 명칭은 KS 표준 완성형 코드 KSC5601-1987이며 좁은 의미로는 캐릭터 셋이지만 당시에는 코드포인트=물리적 바이트코드 였기 때문에 인코딩의 의미로도 쓰인다. 참고로 KSC5601-1992는 앞글에서 언급한 1+5+5+5의 조합형 한글 표준이다. (그러나 별로 쓰이지 않는다.)

이미 많은 사람들이 제기한대로 KSC5601은 문제가 많은 캐릭터 셋이었다. 이미 실질적으로 표준이나 마찬가지인 아스키 코드의 128번 문자 이하는 사용할 수 없는 상태에서 모든 한글이 아니라 자주 사용 가능한 문자 94 * 94(8836)자에 대해서만 코드포인트를 지정하였으므로 현대 한글 11172자를 모두 표현할 수가 없는 문제점이 발생한다. 8836자에는  많이 사용하는 한글 음절 2350자("똠", "햏", "먄" 등의 글자를 사용 못했다.), 한자 4888자, 특수문자 1128자, 나머지 470자를 배정한다. (이후 KSC5657-1991 확장 표준코드가 지정되었는데 한글 1930자, 한자 2856자, 옛한글 1677자 등이 추가되었다. )

어쨌건 조합형 한글을 지지하는 사람들이 이런 제한의 문제점을 제기하였으며 그래서 완성형 지지자였던 MS는 한글 코드포인트를 추가한 "확장 한글 완성형"인 CP949(UHC)를 내놓았고 이후 수천개의 한글을 더 추가하여 현재의 11000자 정도를 지원하는 MS949(MS-Window 확장 완성형 한글)를 사용하였다. MS949는 기존의 KSC5601의 코드 포인트 그대로 포함하고 있으며 기존의 문서와의 호환성 때문에 이전에 사용하지 않던 빈 코드 포인트에 추가 했기 때문에 한글이 제대로 정렬되지 않는 단점이 있다. MS949는 KSC5601의 슈퍼셋이 되기때문에 KSC-5601로 쓴 파일을 MS949로 읽어도 잘 읽힌다.(물론 반대는 안된다.)


한글은 중국의 한문같은 계열의 표의 언어와 알파벳류의 표음 언어의 중간적인 존재다.(일본어는 잘 모르지만 일본어도 중간적 위치에 있다고 알고있다.) 그러나 IT에서 문자는 표음 언어가 좀 더 용이한 표현이 가능하기 때문에 자판에서 영어 알파벳과 자모를 대칭시킨것처럼 한글을 "ㅎ ㅏ ㄴ ㄱ ㅡ ㄹ" 식으로 저장했다면 좀더 효율적이었을 것이다. 하지만 대부분의 상식과는 달리 효율이 우선시 되어 정해지는 일은 거의 없다.


일반 사용자는 많이 사용하지 않았지만 지역화의 문제를 일찍 겪었던 유닉스 계열은 영문은 KSC5636(영문자에 대한 표준 - 기존 ASCII와 역슬래스가 \으로 바뀐것만 빼고 동일)로 처리하고 한글은 KSC5601-1987로 처리하는 euc-kr(Extended Unix Code-Korean)를 사용하고 있었다.

앞서 말한대로 당시의 표준원의 공식 완성형 표준인 KSC5601은 2바이트로 가능한 655,536개 문자중에 한자와 특수부호를 사용할 공간 확보를 위해 한글은 자주 사용되는 2,530자만 표현 가능하게 제안하였다. (이후 명칭을 KSX1001로 바꿈) 하지만 제안 당시에 편의주의적 발상이라는 비판에 조합형이 함께 존재하는 상태에서 독단적으로 채택되었는데다 윈도우즈 95가 나오고 인터넷이 발달하면서 KSC5601은 현실에서 사실상 표준으로 인정받지 못했다.




WIN95가 나올때 MS는 사실 완성형 한글 보다는 이미 Windows 내부적으로 사용하고 있는 Unicode를 사용할 것을 권했지만 KSC5601이 공식 표준으로 지정된 상태였기때문에 이를 무시할수는 없었고 추가 한글을 표현하기 위해 앞의 CP949를 한글 윈도우즈의 기본 캐릭터셋으로 사용할것을 밝혔다. 

이에 편가르고 싸우고 있던 조합형 지지자뿐 아니라 KSC5601 완성형 지지자까지, 게다가 멋모르던 정부까지 나서서 사용거부 및 수입규제 검토등을 내세우며 강력히 반발하였다. 확장 완성형 한글은 앞서 말한대로 새로운 확장 글자를 호환을 이유로 빈영역에 추가하였기 때문에 정렬시 문제가 있고, 이미 표준안이 존재하는데 추가적인 한글 코드는 논란을 가중시킬뿐 아니라 일단 완성형은 한글 제작 원리에 맞지 않는다는게 반대 이유였다.(당시의 신문 사설에 '세종대왕이 통곡한다니~, 마이크로소프트가 조합형을 처리할 기술이 없느니 하면서 이른바 애국심 주장을 펼쳤던 과거'를 기억하는 개발자로서 쓸데 없는데 애국심을 끌어들이는 주장은 대부분 헛소리라는걸 깨닫게 해준 사건이다. 사실 유니코드는 초중성의 모든 조합의 한글이 표현가능했기 때문에 그냥 완성형인 KSC5601이나 현재의 MS949보다는 나은 선택이었다고 생각한다. 조합형을 고려하지 않은 건 아니었지만 기존 파일과의 호환 문제를 고려하지 않을 수 없었다.)



그러나 한국 정부의 목소리 큰것과 상관없이 힘은 MS에 있었기에 MS는 CP949 캐릭터셋(이후 MS949)를 밀어붙였고, 절대 다수인 Windows 사용자들은 왜 윈도우 에서는 제대로 나오는 글자가 인터넷에서는 제대로 쓸 수 없는지 불평했다. 사용자의 불평에 대응하기 위해 불쌍한 프로그래머들이 케이스별로 작성한 온갖 이상야릇한 코드로 치장되던 혼란의 시간이 흐른 후 사실상 인터넷에서의 한글 표준은 MS949가 되었다. 다만 여전히 공식적인 RFC상에는 MS949가 표준이 아니었기 때문에 HTML에서 <META> 태그에는 MS949라고 적는게 아니라 EUC-KR로 적어야만 확장 한글 표현이 가능하면서 캐릭터 셋의 명칭에 혼란이 야기 되었다. (즉 이전의 유닉스 계의 euc-kr - KSC5601에 기반을 두었다 - 과 meta tag에 적는 euc-kr - 익스플로러에서는 CP949에 기반을 두었다 - 은 의미가 다르다. 이런 엽기적인 짓이 가능했던 것은 당시(2003 - 2005년) 브라우저의 90% 이상을 차지하고 있던 MS 익스플로러 였기에 가능했다. )


그러나 진정한 혼란은 여기서부터다. 한글 표준이 이렇게 아웅다웅 하고 있을 시절에 옆에서 한글지원을 하는 소프트웨어를 판매하는 다른 업체들도 혼란스러워졌고 발표시기와 정책에 따라 차이점을 보였다. 

표준과 현실사이의 갭에서 사람들이 우왕좌왕하고 있는거와 상관없이 어쨌건 기준을 정해야 했던 Sun은 "euc-kr"과 cp949를 "그냥 완성형 한글"이라는 의미를 사용하였고 이후에 "확장 완성형 한글" 인코딩으로 MS949를 추가하였다. 그래서 자바에서 "확장 한글 완성형"을 사용하기 위해서는 MS949 인코딩을 사용해야 한다. (단 특정회사의 자바버전의 경우 MS949 is not supported by the current VM operation system 란 예외가 나오면서 MS949를 인식하지 못하는 런타임도 있으니 이때는 Sun의 1.5이상의 자바를 설치해야 한다.) 그래서 자바 request는 MS949로 보내고 브라우저가 인식하기 위해 HTML Meta Tag에는 EUC-KR로 적는 우스운 상황이 되었다.

그렇지만 또 다른 언어인 Perl의 경우에는 euc-kr은 "그냥 완성형"이고 cp949는 "확장 완성형 한글"을 의미한다. 당시의 역사에 좀더 자세한 내용은 http://sluvy.tistory.com/entry/%ED%8D%BC%EC%98%A8-%EA%B8%80%ED%95%9C%EA%B8%80-%EC%A1%B0%ED%95%A9%ED%98%95%EC%99%84%EC%84%B1%ED%98%95%EC%9C%A0%EB%8B%88%EC%BD%94%EB%93%9C%EC%9D%98-%EB%AA%A8%EB%93%A0%EA%B2%83 에서 확인할 수 있다.


언어에 따라 그리고 인터넷에서는 euc-kr란 의미가 달리 사용되면서 프로그래머들은 완전히 혼란스러웠다. 어떤 책에는 euc-kr은 "그냥 완성형"이고 어떤 책은 "확장 완성형"이란 의미로 사용했으며 프로그래밍 언어에 따라 의미도 달랐기 때문에 의사소통시 심각한 오해와 장애를 불러 일으켰다. 과거의 책을 보고 euc-kr은 "그냥 완성형"이라는 의미였지만 인터넷에서는 확장 완성형 한글을 쓰기 위해서는 META 태그에 EUC-KR을 써야 했고 Perl책의 한글 인코딩과 자바책의 한글 인코딩은 다른 의미가 되어버렸다. 이런식으로 노이즈가 섞이면서 어느게 진실인지 알 수 없게 되어버렸다.


이쯤에서 그럼 이런 뒤죽박죽한 상황에서 어떤 한글 인코딩을 써야 하는가? 라는 의문으로 돌아가보자
대답은 간단하다. 안쓰면 된다. (그러면 모두 잊어버려도 상관없다.-ㅅ-) 한글을 버리고 국제화에 맞춰 Unicode를 프로그래머는 사용해야 한다.


여기서의 문제는 20세기 교수들이 21세기 아이들을 가르킨다는 농담처럼 아직도 많은 책들과 인터넷 문서에는 여전히 한글 처리라는 명목으로

PrintWriter out = new PrintWriter(new OutputStreamWriter(res.getOutputStream(),”KSC5601”),true);
new String(searchWord.getBytes("iso-8859-1"),"euc-kr")

와 같은 코드를 소개하고 있다. 이런 코드가 나온다면 뒤도 돌아보지 말고 Back 버튼을 눌러야 한다. 제대로 한글 처리를 위해 유니코드를 사용하면 위와 같이 String.getByte의 Charset변환을 전혀 사용하지 않아야 한다. 즉 모든 인프라와 리소스를 UTF8인코딩 하나로 사용하기 때문에 변환을 해야할 이유가 없다.


이 얘기는 나중에 다시 하기로 하고 일단 유니코드를 쓰기전에 간단히 알아야 할 것이 있다.

package test.unicode;

import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.OutputStreamWriter;
import java.io.Writer;

import junit.framework.TestCase;

public class TestFile extends TestCase {

  String message = "한글햏ABC123";
  String hanFile = "c:\\temp\\han.txt";
  String utf8File = "c:\\temp\\utf8.txt";
  String utf16File = "c:\\temp\\utf16.txt";
  String utf16LEFile = "c:\\temp\\utf16le.txt";
  String utf16BEFile = "c:\\temp\\utf16be.txt";

  String utf8ByUEdit = "c:\\temp\\utf8ByUEdit.txt"// UEdit를 사용하여 UTF8모드로 저장한 파일 ...-_-

  public void testScenario() throws Exception {
    File file = createHan();
    System.out.println(ByteUtils.toHex(file));

    file = createUTF8();
    System.out.println(ByteUtils.toHex(file));

    file = createUTF16();
    System.out.println(ByteUtils.toHex(file));

    file = createUTF16LE();
    System.out.println(ByteUtils.toHex(file));

    file = createUTF16BE();
    System.out.println(ByteUtils.toHex(file));

    System.out.println(ByteUtils.toHex(new File(utf8ByUEdit)));

  }

  public File createHan() throws Exception {
    File file = new File(hanFile);
    FileWriter w = new FileWriter(file);

    w.write(message);
    w.close();
    return file;
  }

  public File createUTF8() throws Exception {
    File file = new File(utf8File);
    Writer w = new OutputStreamWriter(new FileOutputStream(file)"UTF8");

    w.write(message);
    w.close();
    return file;
  }

  public File createUTF16() throws Exception {
    File file = new File(utf16File);
    Writer w = new OutputStreamWriter(new FileOutputStream(utf16File)"UTF16");

    w.write(message);
    w.close();
    return file;
  }

  public File createUTF16LE() throws Exception {
    File file = new File(utf16LEFile);
    Writer w = new OutputStreamWriter(new FileOutputStream(utf16LEFile)"UTF-16LE");

    w.write(message);
    w.close();
    return file;
  }
  public File createUTF16BE() throws Exception {
    File file = new File(utf16BEFile);
    Writer w = new OutputStreamWriter(new FileOutputStream(utf16BEFile)"UTF-16BE");

    w.write(message);
    w.close();
    return file;
  }

}


를 작성해서 실행시키면

createHan()
결과 : C7D1 B1DB C164 41 42 43 31 32 33
윈도우 XP에서 실행하였기에 기본 인코딩인 MS949가 적용되어 C7D1-한, B1DB-글, C164-햏 (41,42,43) ABC, (31,32,33) 123 에 해당하는 Hex 코드가 나온다. 확장 완성형 한글 한자당 2byte다.

createUTF8()
결과 : ED959C EAB880 ED968F 41 42 43 31 32 33
UTF8 인코딩은 "ED959C-한, EAB880-글, ED968F-햏" 로 한글 한글자당 3byte가 할당되고 영어와 숫자는 1byte이다. 기본적으로 ASCII는 모든 캐릭터 셋의 서브셋(슈퍼셋의 반대의미)이기 때문에 코드 포인트는 동일하다.

createUTF16()
결과 : FEFF D55C AE00 D58F 0041 0042 0043 0031 0032 0033
UTF16 인코딩을 하면 "D55C-한 AE00-글 D58F-햏" 이다. 대부분의 한글은 BMP에 속하므로 2byte가 할당된다. ASCII문자의 경우 코드 포인트는 동일하지만 UTF16인코딩의 특성상 2byte가 할당되기 때문에 "00"이 앞에 붙었다. 젤 처음에 있는 FEFF는 빅 엔디안 즉 유니코드 바이트 순서 표시이다. 관례상 유니코드는 바이트 순서 표시를 하게 되 있으나 이상과 달리 현실에서는 모든 유니코드에 표시하지는 않으며 표시 할때도 있고 안할때도 있다. 자바의 경우 UTF16 인코딩 시에만 엔디안 표시를 하고 있으나 사실 프로그래밍 언어나 툴마다 지멋대로다 -ㅅ-

createUTF16LE()
결과 : 5CD5 00AE 8FD5 4100 4200 4300 3100 3200 3300
UTF-16LE는 리틀 엔디안 UTF-16을 말한다. 영문자 A의 경우 0041이 아니라 4100으로 표현되기 때문에 2바이트씩 끊어서 순서를 뒤집어 읽어야 한다. 5CD5 00AE 8FD5를 2바이트씩 끊어서 앞뒤 바이트를 바꾸면 위의 createUTF16과 결과가 같다. UTF-16LE라고 명시적으로 인코딩에 엔디안을 명시하면 생성되는 파일에도 엔디안을 표시하지 않는 것도 주의해야 한다.(물론 자바에 한정한 말이고 다른 언어는 어케 될지 모른다.)


createUTF16BE()
결과 : D55C AE00 D58F 0041 0042 0043 0031 0032 0033
자바는 빅 엔디안이 기본이므로 앞의 엔디안 마크를 제외하면 createUTF16()와 동일하다. 참고적으로 윈도우는 리틀 엔디안을 기본값으로 하고 있기때문에 윈도위 계열의 리틀엔디안을 사용하는 편집기로 위 파일을 열면 제대로 보이지 않는다.


맨 마지막의
EFBBBF ED959C EAB880 ED968F 41 42 43 31 32 33 는 자바가 아니라 울트라 에디트로 UTF8 편집모드로 같은 내용을 직접 저장해서 Hex코드를 읽은 것이다. 자바로 생성한 createUTF8()와는 달리 맨 앞에 UTF8에 해당하는 EFBBBF의 엔디안 표시가 붙었다.(참고적으로 UTF-32/LE의 표시는 FF FE 00 00를 사용한다.) 그래서 울트라 에디터로 작성한 UTF8 파일을 자바로 읽으면 "?한글ABC123"와 같이 첫글자가 깨진다. 자바는 UTF8 인코딩/디코딩시 엔디안을 표시하지 않기 때문에 엔디안으로 인식하지 못하고 깨진 문자로 나오게 된다. (참고로 UltraEdit의 Hex모드로 보기는 UTF 파일 편집때 제대로 동작하지 않기 때문에 무시하는게 좋다.)


....................

인생 사는게 쉬운게 아니다. -ㅅ-
이상과는 달리 유니코드의 현실도 - 한글의 시궁창만큼은 아니지만 - 그리 만만하지 않다.

유니코드를 사용하기 위해 첫번째로 생각해야 할것은 어떤 인코딩(UTF8,16,32)을 사용할 것이냐 인데 이는 보통 과거 ASCII 파일과의 호환 문제로(UTF-16, 32는 레거시 ASCII 파일을 제대로 읽지 못한다.) UTF8 포멧을 주로 사용하는데 이때 엔디안의 종료와 엔디안 표시 여부가 툴마다 언어마다 다르다.

그래서 UTF8로 통일하더라도 이기종간이나 여러 언어로 작성한 컴포턴트간 통신에는 역시 주의하여야 한다.





-- 엔디안
조나단 스위프트의 걸리버 여행기에 나오는 이야기로 삶은 달걀을 둥근쪽을 깨서 먹는 사람들 Big Endian과 뾰족한 쪽을 깨서 먹는 사람들 Little Endian이 서로 나뉘어 대립을 하는 소인국 이야기에서 따온 말이며 빅엔디안은 우리가 평소에 보던 방식으로 메모리에 쓰는 방식이고 리틀엔디안은 뒤집어 져서 쓴다고 생각하면 된다.  

우리가 사용하는 인텔(Intel)의 x86 계열의 CPU등은 리틀엔디언 방식의 CPU이고 Motorola, Sun, Sparc 같은 Risc 타입은 빅엔디안 방식이다.

리틀 엔디안을 쓰는 이유는 산술연산유닛(ALU)에서 메모리를 읽는 방식이 메모리 주소가 낮은 쪽에서부터 높은 쪽으로 읽기 때문에 산술 연산의 수행이 더 쉽기 때문이다.

빅엔디안 방식의 장점이라면, 정수로 정렬된 수에대한 비교가 메모리에서 읽는 순서대로 바로 비교가 가능 정수와 숫자타입의 데이터를 같은 순서 방향으로 읽을수 있다는 점이 있다.

'IT 이야기 > 유니코드' 카테고리의 다른 글

다시 쓰는 UTF  (0) 2009.06.12
Unicode와 Database  (0) 2009.03.01
Global Software - Unicode  (0) 2009.02.25
Global Software  (0) 2009.02.22
Posted by bleujin
IT 이야기/유니코드2009. 2. 25. 07:24

이전글에 이어서..

사실 서구권보다는 우리나라가 유니코드에 대해 좀더 절실하지만 영어를 잘하는 개발자의 비율이 낮기때문인지 잘못 알려진 사실들이 많이 있다. 그 중에 대표적인 것은 인코딩과 문자셋의 차이에 대한 오해와 유니코드는 2byte라는 미신이 있다.("유니코드는 2byte"라고 잘못 소개한 책들 "만"의 탓이라고 돌릴수는 없다.) 상식적으로 문자처리에 대해 그렇게 과도한 지식을 요구하는 체계를 만들지는 않으므로 기본 상식선에서 쉽게 접근할수 있는 문제이다.

유니코드에 대한 미신을 깨기위해 가장 먼저 알아야 할것은 캐릭터 셋의 개념이다. 이전까지 euc-kr이 인코딩 그 자체였던 것에 비해 유니코드란 말은 인코딩이 아니라 일종의 문자 처리의 철학에 가깝다. 'A'를 UCS-2로 표현하면 0x0041인데 이때의 0041은 실제 디스크에 해당 바이트로 저장된다는 뜻이 아니라 그냥 관념적인 매핑이다. 굳이 비유하자면 일종의 인터페이스라고 생각할 수도 있다. 'A' 글자를 UCS2 매핑맵에 따라 0x0041이라는 코드 포인트로 매핑시켰을뿐 실제 디스크에 어떻게 저장이 되는가는 인코딩 방법에 따라 다르다. UCS2라는 것은 글자를 2byte의 코드 포인트로 표현한다는 것일뿐 실제 'A'라는 글자가 2byte라는걸 뜻하는 것은 아니다. 만약 'A'를 UCS4로 표현하면 0x00000041이 된다.


좀더 얘기를 진행하기 전에 왜 이런짓을 할까? 그냥 글자대 바이트코드 1:1매핑이 편하지 않을까 하는 의문이 들수 있다. 첫번째 이유는 의도와 구현을 분리시키는 설계의 기본이념에 따라 인코딩의 구현에 상관없이 글자를 정의하는데 있다. 만약 우리가 글자의 캐릭터 셋을 사용하지 않는다면 "에이"의 바이트코드는 어떻게 되지? 라고 누군가가 묻는다면 "에이"라고 발음되는 다른 문자와 혼란될 염려도 있고 그 에이가 만약 영어라고 해도 대문자인지 소문자인지도 확인해야 하며 언젠가 먼 미래에 혹은 과거에 에이는 다른 의미를 가지게 될지도 모른다. 그것보다는 0041문자의 바이트 코드는 어떻게 되지? 라고 묻는게 훨씬 더 손쉽다. 수학은 만국공용의 언어이기 때문이다. 그리고 이렇게 분리되었을 경우 인코딩과 디코딩의 구현의 방법이 변하더라도(다음에 얘기할 빅엔디안 -리틀엔디안의 경우를 보면 이런일이 이해가 될것이다.) 글자의 정의 자체는 변하지 않아도 되는 장점이 있다. 


 이렇게 모든 글자의 매핑 맵을 만들어서 문자별 코드 point을 지정한게 캐릭터 셋이다. 만약 UCS4의 코드포인트 수인 2^32보다 많은 문자를 정의해야 한다면(현재로선 충분해보이지만 이를테면 수만종의 외계인을 만나다던가 수만종의 지구 생물의 언어체계를 이해하게 된다던가) UCS8이라는 매핑맵을 만들어서 추가적인 코드 포인트를 지정하면 그만이기 때문에 이론적으로 유니코드가 정의할 수 있는 글자 개수에는 제한이 없다.

이러한 관념적인 숫자인 0x0041을 어떻게 메모리 혹은 디스크에 저장할 것인가에 대한 - 즉 다시 말해서 인코딩 - 얘기는 비교적 익숙한 UTF-8, UTF-16, UTF-32 등이 있다. 과거 ASCII 시절에는 문자를 표현하는게 복잡하지 않았기 때문에 캐릭터 셋과 인코딩은 굳이 구별할 필요가 없었다. 이를테면 A는 0x41이고 저장장치에 표현될때도 41이라고 저장하면 그만이었다. 그러다가 세상이 복잡해지고 컴퓨터로 표현해야 할 문자도 많아지면서 관념적인 캐릭터 셋과 물리적인 인코딩이라는 것을 구분하여 쓰기 시작했다. 인코딩을 구현하는 입장에서 보면 캐릭터 셋은 문자의 종류와 각 문자들을 구분할 수 있는 기준이기 때문에 캐릭터 셋의 존재를 인지해야 한다.

유니코드의 캐릭터 셋은 대표적으로 UCS2와 UCS4 두가지가 있다. UCS2는 16진수 2개로 코드 포인트를 지정했고 UCS4는 16진수 4개로 코드포인트를 부여하였다. 이를테면 UCS2에 따르면 영문자 A는 '0x0041'이다. 이제 영문 대문자 A라고 부르지 않고 '0x0041'번 문자라고 불러도 된다. 이 '0x0041' 문자가 실제 디스크나 메모리에 어떻게 저장될지는 인코딩을 구현하는 쪽에서 알아서 할일이다. 다만 캐릭터 셋은 서로 구별되는 이만큼의 문자가 있다는 것을 알려주는 것이다. 16진수 2자리로 표현할 수 있는 최대 경우의 수는 65536이기 때문에 UCS2 에는 이론상으론 최대 65536자의 코드 포인트를 지정할 수 있다. 인긴이 사용하는 언어는 아주 많아 보이지만 대부분이 표음언어이기 때문에 일명 CJK언어(중국,일본,한국)의 표의언어를 제외하면 생각보다 많지 않기 때문에 대충 65000 의 코드 포인트로도 표현이 가능하다.

이를테면 한글의 경우 초성19* 중성21* 종성28를 하면 고작(?) 경우의 수는 11172이고 한문의 경우에도 대략 4만자 일본어의 대략 몇천자를 제외하면 다른 표음언어(라틴어-영어,그리스어,키릴어,아르메니아어,히브리어,아랍어,시리아어,벵골어,몽골어 등)를 모두 합쳐도 60000여자 안팍이다. 이를 모두 모아서 BMP(Basic Mulitilingual Plane)이라고 부른다. (2^16개의 코드포인트들을 하나의 Plain이라고 부른다.)


60000자 정도로도 표현할 수 있다면 UCS4의 4byte의 표현의 캐릭터 셋이 존재해야 하는 이유는 무엇일까? 첫째 이유는 현재 사용하지는 않지만 특수한 지명이나 이름에서만 사용되는 한자의 고어나 연구 목적으로 사용되는 다양한 언어들의 고어 - 이를테면 수메르 언어를 쓰지는 않지만 연구목적으로 수메르 언어를 표현해야 할 때는 있다. 추가적으로 수식 기호, 음표등의 다양한 문자들까지 합치면 6만자가 넘는다. 편의를 위해 이런 문자들을 16개의 Plane으로 만들었는데(앞서 말했다시피 하나의 Plane는 65536의 코드 포인트를 가지도록 구분) 이를 보충언어판(SMP, Supplementary Multilingual Plane )이라고 부른다. 

그렇다고 SMP의 18 * 65536의 모든 코드 포인트가 정의되어 있는 것은 아니다. Plane은 단지 비슷한 것끼리 묶어놓은 그룹이며 실제로 SMP에 정의된 코드포인트는 그리 많지 않다. 또 이후 새로운 수학기호가 생길지도 모르니 수학기호와 관련한 Plane을 비워두기 때문에 Plane은 일종의 Grouping 이라고 생각하면 된다. 


고작해야 16*65536 (2^4*2*16 = 2^20)의 SMP와 기존의 BMP를 합쳤을때( (16 + 1) * 2 ^ 16 는 대략 2^21 )  4바이트(2^32)는 과해 보일지 모르지만 기본적으로 컴퓨터를 하는 사람들은 짝수를 좋아하고 우리가 언제 외계인을 만나서 외계인의 언어를 디스크에 저장해야 할때도 없으리라고 할 수 없지는 않은가? 머 어쨌든 실제 UCS4의 많은 공간은 17개의 Plane를 제외하면 비워져 있다. 물론 코드 포인트들은 앞에서 부터 촘촘히 채워져 있는 건 아니기 때문에 각 Plane에도 중간 중간 비어있는 코드 포인트 들이 있다. 여기서 다시 256개의 Plane을 묶은걸 하나의 Group이라고 부르고 현재 17개의 BMP+SMP는 00번 Group에 속해 있다.



먼저 UTF-16 인코딩에 대해 먼저 알아보자.

처음에는 그냥 코드 포인트의 숫자를 그대로 두 바이트로 저장하는 것은 어떨까 하는 누구나 할수 있는 생각을 하게 되는데 이 생각이 잘못 전해져서 유니코드는 2byte라는 미신을 만든다. 기본적인 생각은 '0x0041'이라는 코드포인트를 디스크에 '00 41'로 저장하자 라는 생각이다.

그러나 앞에서 말했다시피 SMP에 지정되어 있는 (UCS2에서는 지정되지 않았지만 UCS4에 지정되어 있는) 코드포인트를 가지는 글자들은 어떻게 처리할까? SMP영역의 글자들을 표현하기 위해 DBCS에서 했던 편법을 응용해서 적용한다. 즉 2byte가 만약 D800 - DBFF(이 영역은 UCS2에서 원래 비어있는 영역이다.) 사이에 있으면 다음 2byte와 같이 읽어서 하나의 글자를 처리하는 방식이다. 이 경우 다음 2byte는 DC00..DFFF(이것도 역시 UCS2에서는 비어있는 영역이다.) 사이에 있어야 한다. 즉 D800 - DBFF + DC00..DFFF의 4byte로 하나의 글자를 인코딩하게 된다. 앞의 범위를 상위 대행코드 뒤의 범위를 하위 대행코드라고 하는데 이 경우 총 표현할수 있는 글자수는 2^10*2^10 = 2^20 의 경우의 수를 가지게 된다. (D800 에서 DBFF 사이의 경우의 수는 2^2*2^8=2^10이다.) 2^20 = 2^4 * 2^16 = 16 * 65636 이므로 16개의 SMP는 이렇게 4바이트를 사용하여 표현할 수 있게 되었다.

상하위 대행 코드 영역으로 2^10*2영역은 비어 있어야 하기 때문에 실제 BMP의 표현 가능한 갯수는 2^16 -  2^10*2가 되지만 원래 BMP에도 충분한 여유 공간이 있기 때문에 상관이 없다. 그래서 UTF-16인코딩은 BMP에 있는 문자들은 2byte로 SMP에 있는 문자들은 4바이트로 표현을 한다. 이렇게 UTF-16 인코딩은 현재로서는 17개의 BMP+SMP만 표현하도록 만든 인코딩이기 때문에 외계인의 언어를 저장하는데 이 인코딩은 사용할 수 없지만 최소한 지구내에서 쓸때는 충분하다.


UTF-16에 대한 얘기를 끝내기전에 처음 들었다면 당황스런 얘기일지 모르지만 실제로 초기 구현가는 특정 CPU가 가장 빨리 동작할 수 있는 모드에 맞춰 '0x0041'이라는 코드 포인트를 '00 41'과 '41 00' 두가지 모드 형태로 사용하고 싶어했고 그러다 보니 유니코드 문자열 시작부분에 FE FF 를 저장하는 관례가 생겼다. (FE FF는 순서대로 읽은 빅 엔디안 방식이고 FF FE는 순서를 뒤집어서 읽는 리틀 엔디안 방식이다.)

UTF-16 인코딩을 사용하는 대표적인 사례는 바로 자바의 문자열 처리방법이다. "1Ab가햏"라는 글자의 경우 자바는 메모리상에 실제로 "FE FF 00 31 00 41 00 62 AC 00 D5 8F"와 같이 저장한다. 처음 FE FF는 엔디안이고 1 = 00 31, A = 00 41, 가 = 00 62 AC, 햏 = 00 D5 8F로 표현된다. 자바가 UTF-16 인코딩을 사용하는 이유를 추측하자면 아마도 프로그래머들이 사용하는 대부분의 글자는 2byte로 표현이 가능해서 - SMP인 수메르 언어로 주석을 다는 별종은 없을테니까 말이다.-  다음에 얘기할 1-6 가변 바이트인 UTF8보다 처리가 쉽고 또 BMP의 글자는 2byte 고정바이트이기 때문에 그만큼 처리 속도에 우위를 가지기 때문이라고 추측된다. 


정리하자면 UTF-16은 2byte-4byte의 가변 처리 인코딩이며 DBCS에 했던 비슷한 방법인 대행코드라는 방식을 사용하기 때문에 SMP에 있는 글자의 경우 UCS4의 코드 포인트와 실제 저장되는 바이트 숫자와는 다르다. 만약 UFT-16으로 인코딩된 글자의 UCS-4의 코드 포인트를 보고 싶다면 (상위대행코드-0xD800)*(하위대행코드-0xDC00) + 0x10000 의 간단한 수식을 통해 확인할수 있다.

UTF-16의 인코딩은 비교적 이해하기가 손쉽지만 눈에 띄는 단점이 있다. 
첫번째 이전의 ASCII로 저장된 레거시 문서와 호환이 되지 않는다. 
두번째 BMP+16 SMP(고어+수학기호, 음표 등등)의 문자만 인코딩이 가능하다. 앞서 얘기했듯이 앞으로 생길지도 모르는 외계인의 언어를 저장할수는 없다. 



이제 UTF-8 인코딩에 대해서 알아보자

앞에서 UCS-4는 매핑셋이기때문에 총 표현가능한 코드 포인트는 2^32이지만 UTF-16의 인코딩은  (2^16 -  2^10*2) + 16 * 2^16 대충 17 * 2^16 만을 표현할 수 있기 때문에 모든 UCS-4의 글자를 표현하지 못하는 단점이 있고(물론 외계인과 조우하지 않는다면 최소한 지구상의 과거와 현재 존재하는 모든 언어는 표현이 가능하지만 말이다.) 라틴어 계열의 프로그래머들은 'ABC'를 저장하기 위해 '00 41 00 42 00 43'와 같이 사용 되는데 절대 다수인 라틴어(영어) 계열의 프로그래머들은 수많은 00 들을 낭비라고 생각했다. 

그냥 참고 말지 라는 단순히 낭비의 문제를 넘어서 UTF-16은 과거의 ASCII로 사용되었던 문서와의 호환문제가 있었기 때문에 다른 인코딩을 만들게 되는데 그게 UTF-8이다.

여기에도 DBCS에서 사용되었던 아이디어(사실 이 아이디어는 네트워크의 서브넷 마스크에도 쓰인다.)를 조금 바꿔서 첫 byte의 비트를 사용해 (0xxxxxxx, 110xxxxx, 1110xxxx 등) 추가 글자를 인코딩, 디코딩을 한다.

UTF-8과 UCS-4간의 변환 규칙
UCS-4 UTF-8

위의 표에서 보다시피 UTF-8로 인코딩된 파일을 디코딩할경우 바이트의 앞의 비트를 읽어서 이게 몇바이트 글자인지 확인하고 해당 바이트만큼 묶어서 읽게된다. 예컨데 첫번째 비트가 0이라면 해당 글자는 ASCII 문자라는 것을 의미하고 뒤의 7비트를 읽어서 표현한다. 처음 3비트가 110이면 두 바이트를 묶어서 글자를 읽는다. 만약 처음 4비트가 11110이면 표현할수 있는 가지수는(x의 숫자를 모두 더하면 된다) 2^21이고 이는 앞에서 말한 BMP와 SMP의 합인 17 * 2^16보다 크면서 최소의 2의 승수이다. 따라서 UTF-8인코등은 4byte로 17개의 Plane을 표현할 수 있다. 

UTF-8로 UTF-16처럼 17개의 BMP+SMP만 표현하고자 한다면 빨간색으로 칠한 부분을 0x0010FFFF로 변경하고 나머지는 삭제해야 정확한 유니코드의 UTF-8 인코딩이라고 할 수 있다.(우리는 아직 우주인과 접축하지 않아서 17개의 Plane을 제외하면 모두 영역만 구분하였을뿐 모두 비어있다.) 다만 이를 모두 표현한 것은 6바이트까지 확장하면 UTF-8로도 UCS-4의 전역을 인코딩할 수 있음을 보여주기 위한 것이다.

UTF8은 유니코드에서 가장 널리 쓰이는 인코딩이다. 기존의 BMP에 있던 한글들은 UTF-16에서 2byte로 표현되던게 UTF-8에서는 3byte로 표현되지만 절대 다수인 ASCII 코드들이 UTF-16에서 2byte로 표현되던 것이 UTF-8에서는 1byte로 표현되고 이는 다시 말해서 기존 과거의 레거시 아스키 문서들을 UTF-8로 디코딩해도 아무런 문제가 없기 때문이다.


UTF8의 가장 큰 장점은 이전의 레거시 문서와 호환이 된다 이지만 단점은 대부분의 CJK가 3byte로 표현된다는데 있다. 
이게 왜 단점이 되냐면 기존의 UTF16 인코딩에 비해 평균적으로 저장공간이 더 많이 소모되고 length 체크시에 다소 더 복잡하다는데 있다. 이를테면 오라클이 UTF8을 사용할때 varchar2(4000)이라는 의미는 4000byte라는 뜻이므로 CJK와 ASCII가 혼합된 문자열의 저장시 length를 정확히 체크해야 한다. 



UTF-32는 이해하기 쉽다.

다만 UTF-32는 고정길이인 4바이트로 모든 유니코드를 표현하기 때문에(UTF-16은 2혹은 4byte, UTF-8은 1-4(6)으로 표현한다.) 그냥 UCS-4의 매핑셋을 일대일 매핑시켜서 현재는 17개의 언어판만을 대상으로 하는 UCS-4의 코드 포인트만을 사용한다. 즉 표준상 즉 UTF-32의 인코딩 영역은 0x00000000에서 0x0010FFFF로 제한된다.(이 제한을 무시하면 UCS-4와 동일하기때문에 UCS4의 경우 인코딩의 의미로도 사용하기도 한다. UCS2의 경우에도 BMP만을 사용하기로 한다면 그냥 1:1 매핑시켜서코드 포인트를 바로 바이트로 표현해도 되기 때문에 인코딩의 하나의 방법이 될 수는 있다. 다만 이렇게 말하면 글의 문맥상 의미를 읽어야 하기 때문에 이해하기 어려워서 그냥 UCS2와 UCS4는 캐릭터 셋이라고 생각하는게 맘이 편하다. ) 그러나 실제로 UTF-32 인코딩을 사용하는 사례는 별로 없다. 개념적으로는 간단하지만 저장공간의 낭비가 다른 인코딩에 비해 너무 심하기 때문이다.




정리하자면 외계인과의 조우 가능성등을 이유로-ㅅ- UCS-4의 코드 포인트를 정해놓았다. (산술적인 경우의 수는 42억이지만 인간은 분류하기를 좋아하기때문에 2^16의 코드포인트를 가지는 256개의 언어판을 하나의 Group을 묶어 128개의 Group을 만들어 놨으므로 실제 UCS4에 정의된 코드 포인트는 128*256*2^16 = 2^31이다. 128개의 Group이 있으며 한개의 Group에는 256개의 Plane이 있으며 하나의 Plane에는 2^16개의 코드 포인트를 정해 놓았다. ) 이때 'A'를 UCS-2의 코드 포인트로 표현하면 '0x0041' 이지만 실제 메모리나 디스크에 저장될때는 UTF-8, UTF-16, UTF-32 같은 인코딩 방법에 따라 다르다.

character set은 문자에 숫자코드를 부여한 문자집합이다. 이 상태는 아직 논리적 상태로 컴퓨터에서 어떻게 표현되는가는 정해지지 않는 상태이다. encoding는 물리적으로 컴퓨터에서 어떻게 표현되는가까지가 정해진 상태의 문자 집합이다. 예컨데 KSC5601 완성형(KSX1001로 1987년 이름이 변경)은 charset이고 이걸 UNIX에서 encoding한게 euc-kr, DOS에서 encoding한게 codeset 949이다.

character set과 encoding은 오래전에는 같은 뜻이었다. 그러다가 시스템의 종류도 많아지고 Unicode 지원 등이 생기면서 논리적인 character set과 물리적인 encoding이 분리된 의미로 사용되었다. 그리고 이러한 이유로 혼란이 야기되었다-ㅅ-







이 글을 쓰며 흥미있었던건 5년전에 Unicode란 무언인가에 대해 말하기 위해 무려 4시간짜리 세미나를 했다는 거다. 무언가를 더 잘 이해하면 더 짧고 쉬운 말로 적을 수 있다는걸 새삼 깨닫게 되었달까..


'IT 이야기 > 유니코드' 카테고리의 다른 글

다시 쓰는 UTF  (0) 2009.06.12
Unicode와 Database  (0) 2009.03.01
Global Software - Unicode와 Programming  (0) 2009.02.26
Global Software  (0) 2009.02.22
Posted by bleujin
IT 이야기2009. 2. 23. 07:07

처음에 파스칼을 처음 배웠을때 아니 그 이전에 Basic을 처음 배웠던 때를 생각해보면 언어는 프로그래밍의 거의 전부라고 할 수 있을 정도로 중요했었다.

시간이 지나서 몇개의 언어를 더 익히고 고수는 언어에 종속되지 않는다 라는 몇몇 얘기들이나 책에 휘말려서 "아 그런가~" 진정한 고수는 형식의 언어에 좌우되지 않는구나(아 참 귀얇다-ㅅ-) 라고 생각하곤 한때 그런 믿음을 갖게 된적도 있었다. 그러다가 C#을 따라하기 30일 수준을 벗어나면서 다시금 아 언어란 참 중요한 것이다라고 생각을 고쳐먹었다. 언어학에서 말하는대로 언어는 개인적인 프로그래밍 사고력의 큰 비중을 차지했기 때문이다. 그러다가 다시 몇개의 객체지향 언어를 배우다보니 아 딱히 언어보단 다른게 중요하구나.. 등등. 이하 반복-ㅅ-;;

.....

Tool에도 그런 경험이 있다. 고수란 툴에 얽매이지 않는다 라는 말에 손쉽게 낚여서-ㅅ- 그런 생각을 한동안 가지기도 했다. 그러다가 몇몇 좋은 툴을 다루어보곤 아 툴이란 참 중요해 라고 생각했다가 또 어느순간 툴에 종속되지 않는 프로그래밍 기술 같은 거에 빠졌다가... 다시.. 그리고 다시.... -ㅅ-;;


그런 경험들을 몇번씩 반복하다 보니 이제는 "툴은 중요한거야" 라는 얘기를 들으면 아 저사람은 몇번째 반복일까? 2번째? 3번째? 라는 생각을 하고 "언어는 전혀 중요하지 않아" 라는 얘기를 들으면서 저사람은 다른 스킬로 판단하건데.. 3번째 반복쯤인가? 라고 생각하게 된다.

스스로가 이런 견해를 왔다갔다 하다보니 이제는 더 이상 "XX 프레임워크는 정말 좋아. 이 좋은걸 안 쓰는 프로그래머들은 다 바보 멍청이지" 라는 얘기를 들어도 시큰둥하다. 또는 "XX 프레임워크 따위가 다 뭐야. 실력없는 프로그래머들이나 그딴걸 의지하지" 라고 들어도 무덤덤하다. 있는건 갖다쓰고 모자란건 고쳐쓰고 없는건 만들면 되지 라는게 기본적인 생각이긴 하지만 좋은걸 만들어 쓰건 좋은걸 갖다 쓰건 그 자체의 견해보다는 몇번째 반복인가가 더 영향을 많이 미친다는걸 많이 경험했기 때문이다.

혹시 주위에 이 멋진 XXX(여기에 아무거나 생각해도 된다. 그게 언어이든 툴이든 방법론이든)을 안쓰는 바보 멍청이(?)가 보인다 하더라도 그렇게 심각하게 우리나라 IT의 미래를 걱정하며 슬퍼하지 않아도 된다. 아마 그 사람도 몇번째인지는 모르지만 순환적인 변증법의 하나의 단계에 있을테니 말이다.

'IT 이야기' 카테고리의 다른 글

사람이 읽는 코드  (0) 2009.03.11
멘탈 ?  (0) 2009.03.01
추상화  (0) 2009.02.22
십계 - 변화의 속도  (0) 2009.02.04
금붕어 이야기  (0) 2009.02.04
Posted by bleujin
IT 이야기/유니코드2009. 2. 22. 04:42

유전자 비율의 보존에 대한 하디-바이베크로 법칙대로 그리고 조엘이 언급한대로 변하지 않는 비율중의 하나는 Global Software에 대해 이해하고 있는 프로그래머의 비율이다. 이는 많은 비율의 서구에서는 굳이 고려하지 않아도 경제적 손실이 크지 않다 - 즉 별로 상관없다 - 는 점에서 기인하는 것이겠으나 사실 딱히 우리나라 라고 낫지는 않다. 

GS의 Global이란 의미는 크게 2가지로 나눌수 있는데 첫째는 Territory 지원 둘째는 Lanaguage 지원이다. 

Territory(영역) 지원은 문화적 차이에 의한 지원 기능이다. 예컨데 첫째 요일이 월요일이나 일요일이냐가 서로 다르고 2009년의 1월의 첫째주를 1-3일로 보느냐 혹은 4-10일로 보느냐도 서로 다르다. 날짜 포맷이 05/08/10라고 했을때 이를 2005년 8월 10일로 해석하는 나라도 있고 2010년 5월 8일로 해석하는 나라도 있다. 中華民國94年07月21日 같은 날짜 포맷도 있다. 가까운 일본의 경우만 봐도 에도 몇년 혹은 쇼와 몇년 식의 정권을 기준으로 삼은 날짜 포맷도 있다. 
숫자의 경우 동양은 만단위로 끊는 2,000.00 이 익숙하지만 서양은 천단위인 20,000.00 으로 사용한다. 게다가 20.000,00(오타가 아니다) 같은 포맷을 사용하는 터키같은 나라도 있다. 영역지원이 되는 대표적인 소프트웨어는 윈도우와 오라클이 있다.


이 포스트의 주요 내용인 Language 지원은 어찌보면 간단하기도 달리 보면 복잡하기도 한 문제다. 언어 지원에 대해 많이 나오는 Term들은 인코딩, 디코딩, ContentType, ISO-8859-1, 이메일(브라우저)이 깨져 보여요, UTF-8, Unicode, UTF32, Cdoe 947, i18n, NLS-Lang, KO16MSWIN949, KSC5601 등등 매우 많다. 이 얘기는 IT에서 언어지원의 역사가 그리 순조롭게 진행되지 않았다는 뜻이기도 하다.

실제 컴퓨터는 문자 그 자체를 저장하는게 아니고 전기신호를 통한 이진 비트를 통해 정보를 저장한다. 그래서 인코딩-디코딩 영어 그대로 해석하자면 문자를 비트를 통해 코드화 시키는 것을 인코드라고 하고 반대로 코드를 화면에 문자로 보여주는 것을 디코드라고 부른다. 여기서 중요한 점은 인코드와 디코드 방식이 동일해야 문자를 제대로 표현할 수 있다는 점이다. 예컨데 A를 65(0100 0001)라고 인코드 시켰으면 디코드시에 65는 A라고 읽어야 하는 상호 합의의 약속 같은 것이다.

컴퓨터가 처음 발명된 구미에서는 알파벳과 약간의 특수문자만 사용해도 충분했기 때문에 1byte=8bit의 00-FF까지의 256개의 코드를 사용해도 충분했었다. 아니 오히려 남았기 때문에 00-7F 까지의 128개만 사용하는 ASCII라는 코드 페이지를 가지고 있었다. 문제의 시초는 10000000 - 11111111 까지의 여분이었다. 많은 사람이 128-255 사이에 위치한 코드에 대해 지엽적인 생각으로 몇몇 강조문자와 선그리기 문자따위에 사용하였다. 미국 이외의 지역에 PC는 각종 OEM 문자 집합을 지역적으로 정의하였기 때문에 컴퓨터간 문자호환은 되지 않았다. 여기까지는 비교적 상식이다

아시아권으로 넘어오면서 문자체계는 더욱 복잡해졌는데 알파벳이 아닌 수천개의 문자를 저장해야 했으므로 1byte로 문자를 저장하긴 무리였다. 그래서 다소 이상해보이지만 첫번째 비트가 1이면 2byte를 묶어서 하나의 문자를 표현하고 첫번째 비트가 0이면 1byte 문자를 표현하는 DBCS라는 인코드 방식이 생겼다.(물론 이전의 ASCII 문서의 호환을 위해서이다.) 이를테면 16bit로 표현했을때 B0 A1 41 은 B0는 첫째 비트가 1이므로 B0 A1은 묶어서 디코딩을 하고 41은 첫째 비트가 0 이므로 41 한 바이트만을 디코딩해서 "가a"로 해석하는 방식이다.

그러나 앞서 말했듯이 인코딩과 디코딩 방식은 서로 합의가 되어 있어야 한다고 했는데 "가"를 "B0 A1"으로 디스크에 저장하는 인코딩과 디스크에 저장되어 있는 "B0 A1"을 "가"로 표현하는 디코딩 방식은 한국에서 사용하는 방식일뿐 일본은 일본 나름대로 중국은 중국 나름대로의 각기 다른 인코딩과 디코딩을 사용했기 때문에 여전히 지역적으로 다른 컴퓨터간의 문서 교환은 불가능했다. 머 그렇다라도 현실적으로 지역이 다른 컴퓨터간 문서교환은 그리 자주 일어나지 않았기 때문에 그리고 소프트웨어를 다른 나라에 팔때 이런 노가다식의 코드 변환 작업을 해주는 누군가가 있었기 때문에 크게 불편을 느끼지 않았다. 게다가 컴퓨터를 사용하는 인구는 아주 소수였기 때문에 이런 문제는 널리 인지되지는 않았다.

이 시기에 우리나라에서는 2byte를 패리티 1bit + 초성 5 bit + 중성 5 bit + 종성 5bit로 표현하자는 조합형 한글과 패리티 1bit + 그냥 15bit 코드로 사용하자는 완성형 한글 사이의 표준 다툼이 있었는데 당시 대표적인 아래아 워드 프로세스는 조합형 한글을 밀고 있었고 MS 워드는 완성형 한글을 지원하고 있었다. 국문학 대학 교수들도 사설을 통해 우리나라 문자는 조합형 한글이 더 적절하다고 주장하였지만 컴퓨터가 단순히 국내에서만 사용되는 것은 아닌 만큼 상대적으로 복잡한 코드 표현방식과 한글이 아니지만 자주 사용하는 다른 글자를 표현하는데 문제(즉 16bit가 모두 한글을 코드화하는데 사용되면 다른 그림문자나 한문등은 어떻게 해야 하는문제가 있다.)가 있었던만큼 승패는 이미 예견된 것이었다.

많은 사람들이 이때 MS Word의 로비나 밀어붙이기식의 방법때문에 어쩔수 없이 완성형 한글이 도입된거라고 오해하지만 사실 컴퓨터를 하는 입장에서는 이후 네크웍을 통한 문자 교환을 생각하면 완성형 한글이 좀 더 적합한 방식이었다고 생각한다. 조합형 한글은 현재 한글의 모든 한글을 표현할 수 있었지만 워드 프로세스에서 자주 사용되는 한자나 특수문자 등을 표현하기 위해 추가적인 코드 페이지를 필요로 했고(1+5+5+5= 2byte였기 때문에 여유 코드가 없다.) 완성형 한글은 패리티 1비트를 제외하고 32000개의 문자집합을 영역별로 나누어 한글뿐 아니라 자주 사용되는 한자, 일본어, 특수문자 까지 2byte의 코드 페이지에 같이 담아둘수 있는 장점이 있는 반면에 "햏"이나 "똠" "믜" "먄" "숖" 같이 자주 사용되지 않는다는 이유로 코드화 되지 않는 한글은 사용할 수 없는 단점이 있었다.

한국의 완성형과 조합형 같은 지역적인 문제들은 다른 아시아권에서도 마찬가지긴 했지만 그 해결책이 완벽한것은 아니더라도 어쨌든 그럴듯하게 작동하였고 컴퓨터에서 문자를 표현하는 문제는 잠시 해결된듯 보였지만 사실 더 큰 문제를 내포하였다.. 컴퓨터가 네트워킹화 되고 인터넷이라는게 생기면서 컴퓨터간 문자 교환의 문제는 지역간에 서로 다른 인코딩과 디코딩은 심각한 문제가 되었다. 이전까지 각 나라에서는 자기 나라만의(혹은 나라에서도 몇가지 계파로 갈라지거나) 인코드 디코드 코드 페이지를 사용하였는데 당연히 다른 나라 컴퓨터와 통신시 A나라에서 A'라는 인코등 방식을 사용해 글자를 보내면 B 나라에서는 B'라는 디코딩 방식으로 문자를 표현할려고 하였으므로 제대로 된 문서로 보이지 않았다. 특정 텍스트 파일의 인코딩 방식은 텍스트 파일 자체에 저장된 것이 아니었기 때문에 어떤 글자나 텍스트를 읽을때 어떤 디코딩 방식을 사용해야 할지 알수 없으므로 모든 글자는 ?로 표현되거나 알수 없는 - 소위 깨진 글자로 보이게 된다.


다음에..

'IT 이야기 > 유니코드' 카테고리의 다른 글

다시 쓰는 UTF  (0) 2009.06.12
Unicode와 Database  (0) 2009.03.01
Global Software - Unicode와 Programming  (0) 2009.02.26
Global Software - Unicode  (0) 2009.02.25
Posted by bleujin