© K-BOOKS · 무료 웹 열람용 콘텐츠입니다. 무단 복제·저장·인쇄·재배포를 제한합니다.
세 번째 발걸음컴퓨터는 정보를 어떤 모양으로 다루는가
당신이 화면에서 읽는 이름과 숫자는 컴퓨터 내부에서 같은 형태로 존재하지 않습니다. 회로와 저장장치는 서로 구별되는 상태를 이용해 정보를 표현합니다. 그 상태를 0과 1로 나타낸 가장 작은 논리적 단위를 비트(bit)라고 합니다. 비트 한 개에는 두 가지 경우가 있고, 비트 두 개에는 00·01·10·11의 네 가지 경우가 있습니다. 비트가 하나 늘 때마다 표현 가능한 경우의 수가 두 배가 됩니다.
이 책에서는 8비트를 한 바이트(byte)로 다룹니다. 네트워크 문서에는 정확히 8비트를 뜻하는 옥텟(octet)이라는 말도 자주 나옵니다. 8비트에는 2의 8제곱, 곧 256가지 패턴이 있습니다. 이를 부호 없는 정수로 해석하면 0부터 255까지 나타낼 수 있습니다. 같은 패턴을 문자나 명령어 일부로 읽을 수도 있으므로, 숫자 자체와 그 숫자를 해석하는 규칙을 구분해야 합니다.
십진수는 자리가 왼쪽으로 옮겨갈 때마다 10배가 됩니다. 이진수는 2배가 됩니다. 오른쪽부터 자리의 값이 1, 2, 4, 8, 16, 32, 64, 128인 이유입니다. 예를 들어 8비트 패턴 00101101에서 1이 선 자리의 값을 더하면 32+8+4+1=45입니다. 이 원리는 뒤에서 IP 주소의 네트워크 부분을 나누거나 파일 권한을 읽을 때 다시 사용합니다.
긴 비트열을 사람이 읽기에는 불편해서 16진수를 함께 씁니다. 숫자 0부터 9 다음에 A·B·C·D·E·F를 써서 10부터 15를 표현합니다. 4비트는 16가지 패턴이므로 16진수 한 자리와 대응합니다. 0010은 2, 1101은 D이므로 앞의 00101101은 16진수 2D입니다. 0x2D처럼 앞에 0x를 붙여 진법을 표시하기도 합니다. 값이 달라진 것이 아니라 같은 값을 다른 표기법으로 쓴 것입니다.
문자에는 문자를 구별하는 번호와 그 번호를 바이트로 표현하는 규칙이 필요합니다. 유니코드는 문자를 코드 포인트로 식별하는 표준 체계이며, UTF-8은 이를 1~4바이트로 표현하는 인코딩 방식입니다. ASCII 범위의 A는 UTF-8에서 1바이트이지만 한글 ‘가’는 3바이트입니다. 화면에서 한 글자처럼 보여도 저장 공간은 같지 않습니다. 입력 길이를 제한할 때 문자 수와 바이트 수를 혼동하면 예상보다 큰 데이터가 들어갈 수 있습니다.
인코딩은 내용을 다른 표현으로 바꾸어 전달하거나 저장하기 위한 규칙입니다. 비밀을 가진 사람만 되돌릴 수 있게 하는 암호화와 목적이 다릅니다. Base64는 바이트를 정해진 문자 집합으로 표현하는 인코딩입니다. 이름이 알아보기 어려운 문자열로 바뀌었다고 해서 비밀이 된 것은 아닙니다. 누구나 같은 규칙으로 원래 바이트를 복원할 수 있습니다. 웹 토큰의 일부가 Base64 계열로 표시되었다는 이유만으로 안에 있는 정보가 숨겨진다고 생각해서는 안 됩니다.
두 바이트를 묶어 한 정수로 해석할 때에도 순서를 정해야 합니다. 값이 큰 자리의 바이트를 먼저 두는 빅 엔디언과 작은 자리부터 두는 리틀 엔디언이 있습니다. 16진수 1234를 두 바이트로 저장할 때 12 34와 34 12의 차이입니다. 파일과 프로토콜의 명세가 어떤 순서를 쓰는지 알아야 정확히 읽을 수 있습니다. 뒤에서 헤더와 메모리 값을 볼 때는 바이트가 눈에 보인다고 뜻도 자동으로 정해지는 것이 아니라는 사실을 기억하세요.
| 같은 값의 표현 | 읽는 방법 |
|---|---|
| 십진수 45 | 일상에서 쓰는 열 가지 숫자의 자리값으로 읽는다. |
| 이진수 00101101 | 32+8+4+1을 더한다. |
| 16진수 2D | 2×16+13을 계산한다. |
함께 따라가기 · 보이는 글자와 저장되는 바이트
Python 3의 기본 기능으로 짧은 문자열을 바이트로 바꾼 결과를 읽어봅시다. 암호화 실습이 아니라 표현 규칙을 확인하는 예입니다.
문자열 A의 UTF-8 바이트는 16진수 41입니다. 십진수로는 65입니다. 이것은 문자 A에 연결된 값이며, 앞에서 계산한 십진수 45와는 다른 값입니다.
문자열 가의 UTF-8 바이트는 EA B0 80입니다. 세 바이트가 모여 한 코드 포인트를 나타냅니다. 한 바이트만 떼어 임의로 문자처럼 해석하면 올바른 글자가 되지 않을 수 있습니다.
바이트 41을 Base64로 표시하면 QQ==가 됩니다. 이 문자열을 디코딩하면 다시 바이트 41을 얻습니다. 여기에 비밀키는 쓰이지 않았으므로 표현이 낯설어져도 비밀을 보호한 것은 아닙니다.
깨달음정보를 읽을 때에는 비트와 바이트, 문자 인코딩, 진법을 구별해야 합니다. 이 구별은 패킷 분석과 암호, 입력 검증의 공통 기초입니다.
"A".encode("utf-8").hex() # 41
"가".encode("utf-8").hex() # eab080
int("00101101", 2) # 45
int("2D", 16) # 45Python 3 표현 예시. # 뒤의 글은 실행 결과를 설명하는 주석입니다.