본문 바로가기
TYLER SONGBlog
블로그 목록
CS학습

Hexadecimal: 4비트를 한 글자로 압축하는 16진법 표기

16진법(Hexadecimal)은 0~9와 A~F 16개의 기호로 수를 표현하는 진법으로, 이진수 4자리를 정확히 1자리로 대응시킬 수 있어 컴퓨터 내부 데이터를 사람이 읽기 쉽게 표현하는 데 쓰인다. 메모리 주소, 색상 코드, 해시값, 디버깅 로그 등 저수준 데이터를 다룰 때 사실상 표준 표기법으로 자리 잡았다. 이 글에서는 16진법이 왜 2진법과 궁합이

송민성5분 읽기

1. 개념

16진법(Hexadecimal, 줄여서 hex)은 16을 밑(base)으로 하는 수 표현 체계다. 0~9까지는 숫자 그대로 쓰고, 10~15는 각각 A, B, C, D, E, F 알파벳으로 표현한다.

text
10진수:  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15
16진수:  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F

코드에서는 보통 0x 접두사를 붙여 16진수임을 표시한다. 예를 들어 0xFF는 10진수로 255다.

핵심은 4비트(bit)가 정확히 16진수 한 자리와 1:1 대응한다는 점이다. 2^4 = 16이기 때문에, 이진수 4자리 조합(0000~1111)이 16진수 한 글자(0~F)와 정확히 일치한다.

2. 왜 사용하는가

컴퓨터는 내부적으로 모든 데이터를 2진수(binary)로 저장하지만, 사람이 긴 이진수를 읽고 쓰는 건 비효율적이고 오류가 나기 쉽다.

text
2진수:  1101 1010 1111 0001
16진수: D    A    F    1

같은 정보를 2진수는 16자리, 16진수는 4자리로 표현한다. 8진법(Octal)도 대안이 될 수 있지만, 8은 2의 거듭제곱이 아니라서(2^3=8이지만 바이트 단위 8비트와 딱 안 맞아떨어짐) 바이트 경계와 자연스럽게 맞아떨어지지 않는다. 반면 16진법은 4비트 니블(nibble) 단위, 8비트 바이트(byte) 단위와 깔끔하게 정렬되기 때문에 저수준 프로그래밍에서 압도적으로 선호된다.

3. 동작 원리

3.1 진법 변환의 수학적 원리

16진수의 각 자리는 16의 거듭제곱 가중치를 가진다.

text
0x1A3F
= 1×16³ + A(10)×16² + 3×16¹ + F(15)×16⁰
= 1×4096 + 10×256 + 3×16 + 15×1
= 4096 + 2560 + 48 + 15
= 6719

3.2 2진수 ↔ 16진수 변환이 빠른 이유

10진수로 변환하지 않고도 2진수와 16진수는 서로 직접 치환이 가능하다. 4비트씩 끊어서 대응표만 외우면 되기 때문이다.

text
1101 1010 1111 0001
 D    A    F    1
→ 0xDAF1

10진수와 2진수 사이의 변환은 나눗셈을 반복해야 하지만, 16진수와 2진수 사이는 단순 치환(lookup)만으로 끝난다. 이것이 하드웨어 레지스터 값이나 메모리 덤프를 표기할 때 16진법을 쓰는 근본적인 이유다.

4. 코드 예제

python
# 진법 변환 예제 (Python) # 10진수 -> 16진수 n = 6719 print(hex(n)) # '0x1a3f' print(f"{n:X}") # '1A3F' (대문자) print(f"{n:#06x}") # '0x1a3f' (0으로 패딩) # 16진수 문자열 -> 10진수 hex_str = "1A3F" print(int(hex_str, 16)) # 6719 # 2진수와 16진수의 관계 직접 확인 binary_str = "1101101011110001" n2 = int(binary_str, 2) print(hex(n2)) # '0xdaf1' # 4비트씩 끊어서 수동 변환 (원리 확인용) def bin_to_hex_manual(bstr: str) -> str: # 4비트 단위로 패딩 padded = bstr.zfill((len(bstr) + 3) // 4 * 4) hex_map = "0123456789ABCDEF" chunks = [padded[i:i+4] for i in range(0, len(padded), 4)] return "".join(hex_map[int(chunk, 2)] for chunk in chunks) print(bin_to_hex_manual("1101101011110001")) # 'DAF1'
typescript
// 바이트 배열을 hex 문자열로 (실무에서 흔한 패턴, 해시값 표현 등) function bytesToHex(bytes: Uint8Array): string { return Array.from(bytes) .map((b) => b.toString(16).padStart(2, "0")) .join(""); } const data = new Uint8Array([255, 10, 0, 171]); console.log(bytesToHex(data)); // 'ff0a00ab' // 16진수 색상 코드 파싱 function hexToRgb(hex: string): [number, number, number] { const clean = hex.replace("#", ""); const r = parseInt(clean.substring(0, 2), 16); const g = parseInt(clean.substring(2, 4), 16); const b = parseInt(clean.substring(4, 6), 16); return [r, g, b]; } console.log(hexToRgb("#FF5733")); // [255, 87, 51]

5. 시간 복잡도 또는 성능 특성

16진법 자체는 알고리즘이 아니라 표기 체계이므로 시간 복잡도 개념이 직접 적용되지는 않는다. 대신 성능/공간 관점에서 짚을 점은 다음과 같다.

  • 표현 밀도: n비트 정수를 16진수로 표현하면 문자열 길이는 n/4자리다. 반면 2진수 문자열은 n자리, 10진수는 대략 n × log10(2) ≈ n × 0.301자리가 필요하다. 32비트 정수 기준으로 2진수 32자리, 16진수 8자리, 10진수 최대 10자리다.
  • 변환 비용: 2진수 ↔ 16진수 변환은 비트 시프트와 마스킹(masking) 연산만으로 O(n) 시간에 처리되며, 자릿수 나눗셈이 필요한 10진수 변환보다 하드웨어 친화적이다.

6. 실무 사용 사례

  • 메모리 주소 및 포인터 표기: 디버거나 프로파일러에서 0x7ffee3a1c9d8 같은 형태로 주소를 표시한다.
  • 색상 코드: CSS/디자인 툴에서 #FF5733처럼 RGB 각 채널을 2자리 16진수로 표현한다.
  • 해시값/체크섬: MD5, SHA-256 등의 해시 함수 출력은 관례적으로 16진수 문자열로 표기한다(e3b0c44298fc1c14...).
  • 네트워크/저수준 디버깅: tcpdump, xxd, hexdump 같은 도구로 패킷이나 파일의 바이트를 16진수로 덤프해서 확인한다.
  • 유니코드 코드포인트: U+1F600처럼 문자 코드를 16진수로 표기한다.
  • 비트 플래그 마스크: 권한이나 옵션 플래그를 0x01, 0x02, 0x04처럼 16진수로 정의해 가독성을 높인다.
shell
# 파일을 16진수로 덤프해서 확인 xxd sample.bin | head -3 # 문자열의 SHA-256 해시값(16진수 출력) echo -n "hello" | sha256sum

7. 주의할 점

  • 대소문자 혼용 주의: 0xff0xFF는 값이 같지만, 코드 스타일 가이드나 프로젝트 컨벤션에 따라 대소문자를 통일해야 협업 시 혼란이 없다.
  • 자릿수 패딩 누락: 0xA를 색상 코드 등 고정 자릿수가 필요한 곳에 쓸 때 0x0A로 패딩하지 않으면 파싱 오류가 날 수 있다.
  • 부호 있는 수 표현 혼동: 16진수 0xFFFFFFFF가 부호 없는(unsigned) 32비트에서는 4294967295지만, 부호 있는(signed) 32비트에서는 -1로 해석된다. 2의 보수(two's complement) 표현을 이해하지 못하면 버그로 이어진다.
  • 문자열 파싱 시 접두사 처리: int(hex_str, 16)처럼 진법을 명시하는 함수에 0x 접두사가 섞여 있으면 언어/라이브러리마다 처리 방식이 다르므로 확인이 필요하다.

8. 핵심 정리

16진법은 4비트와 1:1로 대응하기 때문에 2진수를 압축해서 사람이 읽기 편하게 만든 표기법이다. 진법 변환 자체에 새로운 계산이 필요한 게 아니라 비트 그룹을 단순 치환하는 것이므로 메모리 주소, 색상 코드, 해시값, 비트 플래그 등 저수준 데이터를 다룰 때 표준처럼 쓰인다. 값 자체는 2진수와 동일하므로 부호 표현이나 자릿수 패딩 같은 표현상의 규칙만 정확히 이해하면 실무에서 헷갈릴 일이 없다.

© 2026 Tyler Song