Skip to content
난이도 문제은행 3

컴퓨터가 이해하는 정보 2 ​

컴퓨터가 이해하는 정보에서 본 수 표현에 이어, 글자와 바이트 순서를 다룬다. 한글이 몇 바이트인지, 여러 바이트짜리 정수를 메모리에 어떤 순서로 두는지를 알면 "깨진 글자"의 원인과 네트워크로 값을 보낼 때 순서를 맞추는 이유를 설명할 수 있다. 마지막으로 사람이 쓴 소스 코드가 CPU가 실행하는 0과 1로 바뀌는 과정을 본다.

문자: 인코딩 ​

문자 인코딩(character encoding)은 문자를 어떤 바이트로 바꿔 저장할지 정한 규칙이다. 보낸 쪽과 받는 쪽이 다른 규칙을 쓰면 글자가 깨진다.

  • ASCII(American Standard Code for Information Interchange, 미국 정보 교환 표준 부호)는 영문자·숫자·기호 128개를 7비트 번호로 정한 초기 규칙이다. A는 65, a는 97이다. 한글은 담을 수 없다.
  • 유니코드(Unicode)는 전 세계 문자에 고유 번호를 붙인 표준 문자 집합이다. 이 번호를 코드 포인트(code point)라 하고 U+AC00(가)처럼 적는다. 유니코드는 번호표일 뿐이고, 번호를 실제 바이트로 바꾸는 방법은 따로 있다.
  • UTF-8(Unicode Transformation Format 8-bit, 유니코드 8비트 변환 형식)은 코드 포인트를 1~4바이트로 저장하는 방법이다. ASCII 문자는 1바이트 그대로라 옛 ASCII 파일과 호환되고, 한글 음절은 3바이트를 쓴다. 이 호환성 덕분에 웹의 사실상 표준이 됐다.

그래서 "한글 한 글자는 몇 바이트인가?"의 답은 인코딩에 따라 다르다. UTF-8에서는 3바이트, 한국어 전용 옛 인코딩인 EUC-KR(Extended Unix Code-Korean, 확장 유닉스 한국어 코드)에서는 2바이트다. 데이터베이스 칼럼의 최대 길이를 글자 수가 아니라 바이트 수로 정해 두면, 같은 10바이트에 UTF-8 한글은 세 글자만 들어간다. 그래서 EUC-KR 기준으로 잡은 길이에 UTF-8 한글을 넣으면 뒷글자가 잘린다.

바이트 순서: 엔디언 ​

4바이트 정수 0x12345678을 메모리 주소 100~103에 저장한다고 해 보자. 앞의 0x는 16진수(hexadecimal)라는 표시이고, 16진수 두 자리가 1바이트다. 어떤 바이트를 낮은 주소에 먼저 둘지에 대한 방식을 엔디언(endianness)이라 한다.

주소100101102103
빅 엔디언(big-endian)12345678
리틀 엔디언(little-endian)78563412
  • 빅 엔디언은 가장 큰 자리 바이트(이 예에서는 12)부터 낮은 주소에 둔다. 사람이 숫자를 쓰는 순서와 같고, 네트워크로 보낼 때의 표준 순서다.
  • 리틀 엔디언은 작은 자리 바이트부터 둔다. 인텔·AMD의 x86 CPU와 스마트폰에 많이 쓰는 ARM CPU 대부분이 이 방식을 쓴다.

한 컴퓨터 안에서는 문제가 없지만, 바이트를 파일이나 네트워크로 주고받을 때는 순서를 맞춰야 한다. 그래서 네트워크 프로그램은 보내기 전에 값을 네트워크 바이트 순서(network byte order), 곧 빅 엔디언으로 바꾸고, 받은 뒤 다시 자기 컴퓨터의 순서로 바꾼다.

소스 코드가 기계어가 되기까지 ​

CPU가 직접 실행할 수 있는 것은 0과 1로 된 기계어(machine code)뿐이다. 사람이 읽고 쓰는 소스 코드는 기계어로 바뀌어야 실행된다.

  • 어셈블리어(assembly language)는 기계어 명령어를 MOV·ADD 같은 짧은 단어로 1:1 대응시킨 언어다. 어셈블러(assembler)라는 프로그램이 이것을 기계어로 바꾼다. CPU 종류마다 기계어가 달라서 어셈블리어도 CPU마다 다르다.
  • 컴파일러(compiler)는 소스 코드 전체를 실행 전에 미리 번역한다. C·Go가 대표적이다. 번역에 시간이 들지만 실행은 빠르고, 문법 오류는 실행 전에 드러난다.
  • 인터프리터(interpreter)는 소스 코드를 한 줄씩 읽으며 바로 실행한다. 바로 실행해 볼 수 있어 편하지만 실행할 때마다 해석하는 비용이 든다.
다이어그램 로딩…

실제 언어는 둘을 섞는 경우가 많다. Java는 컴파일러(javac)가 소스 코드를 바이트코드(bytecode)라는 중간 코드로 바꾼다. 바이트코드는 특정 CPU용이 아니어서, JVM(Java Virtual Machine, 자바 가상 머신)이라는 프로그램만 있으면 x86이든 ARM이든 같은 .class 파일을 실행할 수 있다. JVM은 처음에는 바이트코드를 해석하며 실행하다가, 자주 실행되는 부분만 JIT(Just-In-Time compilation, 실행 시점 컴파일)로 실행 중에 기계어로 바꿔 빠르게 한다. 실행 중에 모든 코드를 컴파일하면 그 시간만큼 느려지므로 자주 쓰는 부분만 고른다. Python도 소스를 바이트코드로 바꾼 뒤 인터프리터가 실행한다. 그래서 "컴파일 언어냐 인터프리터 언어냐"는 언어 자체보다 구현 방식의 문제다.

면접 포인트 ​

  • "UTF-8의 장점은?" → 1~4바이트 가변 길이, ASCII 호환. 한글 음절은 3바이트.
  • "빅 엔디언과 리틀 엔디언?" → 큰 자리부터 vs 작은 자리부터. 네트워크 순서는 빅 엔디언, x86은 리틀.
  • "컴파일러와 인터프리터 차이?" → 미리 전체 번역 vs 한 줄씩 실행. Java는 바이트코드 + JIT로 섞는다.

관련 문서