brunch

You can make anything
by writing

C.S.Lewis

by 허클베리 Jul 30. 2022

2. 소프트웨어적 사고 - 5

데이타의 표현, 인코딩

앞의 글에서 컴퓨터의 가장 중심이 되는 하드웨어인 CPU는 몇 가지 인스트럭션(instruction, 연산처리 명령)이 물리적 전자회로로 구현된 것이라 했다. 


각종 프로그래밍 언어로 작성된 소프트웨어는 기계어(0과 1의 조합)로 변환되는 과정을 통해 CPU에서 수행할 인스트럭션들과 해당 인스트럭션에서 처리할 데이타 혹은 데이타가 위치한 메모리의 주소데이타로 만들어진다. 


위에서 '몇 가지 인스트럭션'이라고는 했지만 인스트럭션의 수가 제한되어 있다는 것을 말하고자 함이지 실제로는 수십에서 수백 가지, 프로세서마다 다르다. 인스트럭션의 수가 많고 적음은 해당 프로세서가 갖고 있는 레지스터의 개수와 밀접한 관계가 있다. 갖고 있는 레지스터의 수가 많을수록 지원하는 인스트럭션의 수도 증가한다. 



이렇게 변환되어 만들어진 기계어 프로그램을 '바이너리 실행파일'이라고 하는데 이름처럼 인스트럭션이 되었든 데이타가 되었든 메모리의 주소 데이타가 되었든 사실상의 모든 표현이 앞에서 본 기계어 코드처럼 0과 1(바이너리, binary)로 되어 있다. 



자 그럼 근본적인 이야기로 돌아와서 데이타란 무엇일까? 란 질문을 다시 해보자. 


우린 일상적으로 대부분의 일을 컴퓨터로 처리 가능한 시대에 살고 있다. 


'헤이카카오(Hey Kakao)', '시리(Siri)', '알렉사(Alexa)'에게 '핸드폰 좀 찾아줘!', '신나는 음악 틀어줄래.', '30분 뒤에 알람 맞춰 줄래.', '오늘 강릉 날씨가 어때?'같은 이런저런 음성 데이터를 주고받기도 하고, 스마트폰 앱으로 태평양 건너 샌프란시스코에 있는 사람들과 화상으로 미팅하거나 이어폰, 냉장고, 각종 가전기기들을 컨트롤하기도 하고 쇼핑 주문은 물론 주식, 부동산, 암호화폐 거래 등도 한다. 네비게이션이 없으면 자동차를 운전하는 것조차 힘들고 돈 버는 일의 상당 부분은 물론 게임, 학습이나 취미생활도 컴퓨터나 스마트폰을 통해 한다. 



이 뻔한 이야기를 장황하게 늘어놓은 이유는 우리 삶의 너무나 많은 시간, 대부분의 일을 컴퓨터가 처리한다는 사실로부터 현실세계에서 처리해야 할 일 자체와 정보의 상당 부분이 소프트웨어가 이해할 수 있는 데이타로 변환된다는 것을 이야기하고자 함이다. 



컴퓨터 하드웨어는 소프트웨어로 프로그램된 인스트럭션들을 처리한다. 


몇 가지를 제외한 거의 대부분의 인스트럭션은 데이타(또는 주소 데이타)를 처리한다. 


그리고 0과 1의 조합이 아니면 어떤 상태(0)와 다른 상태(1) 즉 전기적 신호로 구분해 정보를 처리하는 전자회로 특성상 컴퓨터가 처리하는 모든 데이타는 0과 1의 조합으로 표현되어야 한다. 



음성도, 이미지도, 거래 트랜잭션도, 각종 신호도 결국은 처리 가능한 데이타인 0과 1의 배열로 메모리에 표현되어야 하는 것이다. 





 어느 한쪽 시스템이 이해하는 소리, 색깔, 영상, 신호, 문자, 숫자 등의 정보를 다른 시스템이 처리 가능한 정보데이타로 변환하는 작업, 이걸 우린 전문용어로 인코딩(encoding)이라고 한다. 


사람 역시 정보를 처리하는 시스템으로 본다면 우리가 이해하는 프로그래밍 언어로 만들어진 소프트웨어가 CPU가 이해하는 기계어로 인코딩 된다는 것도 같은 맥락에서 이해할 수 있다. 


우리가 흔히 일상에서 사용하는 '코딩'이란 용어도 사람의 생각과 논리를 프로그래밍 언어로 코드화 즉 인코딩한다는 말이다. 



그리고 disk나 SSD 같은 저장매체에 저장되는 데이타들 역시 마찬가지다. 


왜? 메모리에 읽어 들여야 하니까. 


그리고 기본적으로 0과 1의 신호로 모든 걸 처리하는 전기전자장치이니까. 만약 CPU가 0과 1 이외의 다른 상태 값을 인식해 처리할 수 있다면(사실 우린 이미 알고 있다. 양자컴퓨터라는 게 그런 거라는 걸) 처리 효율성을 위해 많은 것들이 바뀌어야 할 것이다. 



어쨌든 양자컴퓨터가 누구나 구매해 사용할 수 있을 정도로 상업적으로 보편화되려면 앞으로 20년, 아니 수십 년은 걸리지 않을까? 


아직까진 0과 1, 바이너리의 시대라는 말이다. 


이제 컴퓨터에 수많은 처리를 맡겨야 하는 우린 대부분의 데이터를 0과 1의 조합으로 인코딩해야 할 필요가 있다는 걸 알게 되었다. 



그런데 누구는 '사랑'을 011로 인코딩하고 누구는 017로, 또 누구는 019로 인코딩한다고 해보자. 아마 이렇게 사람마다 같을 수도 다를 수도 있는 여러 가지 방식으로 인코딩 된 '사랑'이란 데이타를 처리해야 하는 소프트웨어의 개발은 개발자가 죽기 전에 완료하기 힘들지도 모른다. 



그래서 사람들은 인코딩 방식에 대해 합의도 하고 표준이란 것도 만들고 그랬다. 똑같은 방식으로 인코딩 된 데이타 값이 같다면 같은 데이타인 거라고. 전 세계 공통으로 커뮤니케이션하는데 애로사항이 없도록. 그리고  소프트웨어는 그런 인코딩 방식을 사용해 데이타를 표현하면 된다는 식으로.



그렇게 알파벳 'A'는 1000001, 'B'는 1000010, 'b'는 1100010 이런 식으로 표현하게 되었다. 이렇게 알파벳의 값을 정의하고 있는 것이 아스키코드(ASCII, American Standard Code for Information Interchange)인데 대소문자 알파벳뿐만 아니라 숫자, 사칙연산, 문장부호 등 간단한 기호들을 포함 조명이나 볼륨 같은 특수 키를 제외하고는 영자 키보드에 표현된 대부분의 키값과 1:1로 매칭 되는 값을 코드로 정의한 것이라 보면 된다. 


그러니 우리가 어떤 프로그래밍 언어를 사용하든 영문 알파벳 대소문자와 숫자, 따옴표 같은 기본적인 문장부호, 공백 등을 표현할 때는 이 아스키코드를 사용하면 컴퓨터가 '아 이거구나' 하고 이해한다는 말이다. 



어? 그럼 한글은? 한자는? 알파벳이랑 비슷한데 프랑스어나 이탈리아어 문자는?


그렇다. 문자 하나하나 전부다 다른 코드값으로 정의되어 있다. 


물론 한글을 표현하기 위한 인코딩, 일본어를 표현하기 위한 인코딩 등 서로 다른 체계를 갖는 데이타들마다 인코딩 방식과 값을 정의하는 범위가 다르기 때문에 가능한 일이다. 특히 인코딩 방식이란 것은 어느 정도의 길이 즉 몇 바이트로 데이타를 표현할 것인가와 관련이 깊다.





 위에서 영어 알파벳 대문자(uppercase) 'A'의 기계어로 표현된 아스키코드값은 1000001이었다. 이건 무슨 계산 법칙에 의해 나온 값이 아니라 그냥 이 값으로 정하자 해서 나온 코드값이다. 그래도 'A'가 제일 먼저 떠올랐으니 1번을 단 듯하다. ('B'도 'a'도 1로 시작하니 일단은 'A'의 아스키코드값 1000001에서 맨 앞에 있는 1은 무시하자) 


이런 기계어 즉 바이너리 값은 우리가 흔히 이진법이라고 부르는 표기방식이다. 


이진법으로 표현된 수를 바이너리(이진수)라고 하는데 컴퓨터에 있는 '계산기' 소프트웨어를 열어 설정을 '프로그래머 용'으로 바꾸고 우리가 흔히 사용하는 숫자 값(십진수)을 입력하면 0과 1로 표현된 바이너리 값(이진수)이 함께 표현된다. 


이 'A'라고 정의된 바이너리 값 1000001을 우리가 일상에서 쓰는 십진수로 바꾸면 65가 된다.

그리고 'B'는 66, 'C'는 67.. 뭐 이런 식이다. 


이제 인코딩이란 것의 실체를 알았을 것이다. 다름 아닌 기준이 되는 문자나 기호의 값을 정한 뒤 그 뒤로 죽 다른 값을 부여해 구분한 것. 다시 말하면 넘버링하듯 값을 정해놓은 것 그 이상도 이하도 아니다. 


이제 한글이나 한자 같은 문자들이 어떻게 코드화 될지 살짝은 감이 올 것이다. 


그런데 한글과 한자는 문자를 만드는 체계에 있어 차이점이 하나 있다. 


바로 한글은 반드시 자음과 모음을 조합해 하나의 문자가 완성되는 문자라는 것이고, 한자는 자음과 모음의 구분이 없이 하나 이상의 획들이 덧붙여질 때마다 전혀 다른 의미를 갖는 문자가 된다는 것이다. 



우린 한글이 한자에 비해 훨씬 발전된 문자라는 것을 상식적으로 이해하고 있다. 그건 바로 자음과 모음의 조합으로 하나의 글자를 만들어내기 때문에 훨씬 경제적(체계적, 효율적)이기 때문이다. 이 원리는 디지털로 문자를 표현하는데도 응용될 수 있다.


'ㄱ', 'ㄴ', 'ㄷ'.. 'ㅎ', 'ㄲ', 'ㄸ',.. 'ㄹㄱ'... 받침으로 쓰이는 쌍자음을 포함한 모든 자음을 죽 늘어놓고 넘버링해도 30개를 넘지 않는다. 모음 역시 모든 모음을 죽 늘어놓고 세어보면 30개를 넘지 않는다.



생각해보자. 아스키코드처럼 어떻게 하면 한글을 잘 표현할 수 있을까? 

자음, 모음 전체를 1바이트(0과 1을 표현하는 단위를 bit라고 하고 이 bit를 8개 묶어 byte라고 한다. 따라서 1 byte는 8bit다)로 표현할 수 있을까?


참고로 1byte로는 256가지까지 넘버링할 수 있다. bit 하나당 2가지 다른 표현이 가능하므로 8개의 bit로는 2의 8승 즉 256개의 다른 수를 표현하는 것이 가능하기 때문이다.


그렇다 자음과 모음 전체 다 해봐야 100개가 안되니 1 byte로 모든 자음 모음은 다 표현할 수 있다. 


그런데 문자는? 


우리가 자음과 모음을 조합해 만들 수 있는 한글 문자의 개수는 만 개가 넘는다. 물론 일상생활에 그 문자들이 다 사용되고 있진 않다. 이론상 조합해 만들 수 있는 글자 수이기 때문이다. 여하튼 안 쓰이는 문자를 제외하더라도 우리가 실제 사용하는 글자들은 256개가 훨씬 넘는다. 



10,000은 바이트를 단위로 하면 몇 바이트로 표현 가능할까? 그렇다. 2 바이트면 된다. 2바이트 즉 16비트로 구분할 수 있는 수는 65536개 이므로 2 바이트면 모든 한글 문자를 다 표현할 수 있게 된다. 



한자는 8만 자가 넘는다고 하니 2바이트로는 모든 한자를 표현하는 것이 불가능하다. 그래서 한자는 한 글자를 표현하기 위해 3바이트 이상 사용해야 한다. 한글과 한자로 각각 꽉 채워 쓴 1000페이지짜리 책을 디지털로 바꾸면 어떤 쪽이 파일이 커질지는 쉽게 알 수 있다. 



국뽕이 아니라 디지털 인코딩을 생각해도 한글이 한자보다 훨씬 경제적이다. 



매거진의 이전글 2. 소프트웨어적 사고 - 4
브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari