Bit: 컴퓨터가 세상을 표현하는 최소 단위, 0과 1
비트(bit)는 정보를 표현하는 최소 단위로, 두 개의 상태만 가지는 이진 신호다. CPU의 논리 연산부터 권한 관리, 네트워크 마스크, 알고리즘 최적화까지 비트 연산은 소프트웨어 스택 전반에서 사용된다. 이 글에서는 비트의 개념과 연산 원리, 실무에서 비트마스크를 활용하는 패턴을 다룬다.
1. 개념
비트(bit, binary digit)는 정보를 표현하는 최소 단위로, 0 또는 1 두 가지 값만 가진다. 컴퓨터 하드웨어는 전압의 높고 낮음(high/low) 두 상태로 비트를 물리적으로 구현하며, 이 두 상태의 조합으로 숫자, 문자, 이미지, 명령어 등 모든 데이터를 표현한다.
비트 8개를 묶은 단위가 바이트(byte)다. 32비트, 64비트 같은 표현은 CPU가 한 번에 처리하는 데이터 폭(word size)을 의미한다.
2. 왜 사용하는가
디지털 회로에서 여러 단계의 전압을 정확히 구분하는 것은 잡음(noise)에 취약하고 회로 설계가 복잡해진다. 반면 두 상태(on/off)만 구분하면 노이즈 마진이 넓어 안정적으로 신호를 유지할 수 있다. 이런 이유로 트랜지스터 기반 디지털 회로는 이진 표현을 채택했고, 그 결과 비트가 정보의 최소 단위가 되었다.
소프트웨어 관점에서는 비트 연산이 다음과 같은 이점을 준다.
- 메모리를 절약할 수 있다. 32개의 불리언 플래그를 32비트 정수 하나로 표현 가능하다.
- CPU 레벨 논리 연산이므로 매우 빠르다.
- 권한, 상태 플래그처럼 여러 개의 독립적인 on/off 값을 하나의 정수로 압축해 다룰 수 있다.
3. 동작 원리
기본 비트 연산자는 다음과 같다.
| 연산자 | 이름 | 동작 | |
|---|---|---|---|
| & | AND | 두 비트가 모두 1일 때만 1 | |
| \ | OR | 둘 중 하나라도 1이면 1 | |
| ^ | XOR | 두 비트가 다르면 1 | |
| ~ | NOT | 비트 반전 | |
| << | 왼쪽 시프트 | 비트를 왼쪽으로 밀고 오른쪽을 0으로 채움 (값에 2의 거듭제곱을 곱하는 효과) | |
| >> | 오른쪽 시프트 | 비트를 오른쪽으로 밀음 (값을 2의 거듭제곱으로 나누는 효과) |
정수는 대부분 2의 보수(two's complement) 표현을 사용한다. 음수는 절댓값의 비트를 전부 반전한 뒤 1을 더해서 만든다. 이렇게 하면 덧셈 회로 하나로 뺄셈까지 처리할 수 있어 하드웨어가 단순해진다.
예를 들어 8비트 기준으로 5는 00000101이고, -5는 00000101을 반전한 11111010에 1을 더한 11111011이다.
비트마스크(bitmask)는 특정 비트를 조회, 설정, 해제하기 위해 사용하는 값이다. flags & mask로 조회하고, flags | mask로 설정하며, flags & ~mask로 해제한다.
4. 코드 예제
# 비트마스크로 권한 플래그 관리하기
READ = 1 << 0 # 0b0001
WRITE = 1 << 1 # 0b0010
EXECUTE = 1 << 2 # 0b0100
DELETE = 1 << 3 # 0b1000
def has_permission(flags: int, target: int) -> bool:
return (flags & target) == target
def add_permission(flags: int, target: int) -> int:
return flags | target
def remove_permission(flags: int, target: int) -> int:
return flags & ~target
def toggle_permission(flags: int, target: int) -> int:
return flags ^ target
# 사용 예시
user_flags = READ | WRITE # 0b0011
print(bin(user_flags)) # 0b11
print(has_permission(user_flags, WRITE)) # True
print(has_permission(user_flags, EXECUTE)) # False
user_flags = add_permission(user_flags, EXECUTE)
print(bin(user_flags)) # 0b111
user_flags = remove_permission(user_flags, WRITE)
print(bin(user_flags)) # 0b101# 비트마스크를 이용한 부분집합 순회 (DP에서 자주 쓰는 패턴)
def all_subsets(n: int):
subsets = []
for mask in range(1 << n): # 0부터 2^n - 1까지
subset = [i for i in range(n) if mask & (1 << i)]
subsets.append(subset)
return subsets
print(all_subsets(3))
# [[], [0], [1], [0, 1], [2], [0, 2], [1, 2], [0, 1, 2]]# 특정 비트 개수 세기 (Brian Kernighan 알고리즘)
def count_bits(n: int) -> int:
count = 0
while n:
n &= (n - 1) # 가장 오른쪽 1비트를 제거
count += 1
return count
print(count_bits(0b1011)) # 35. 시간 복잡도 또는 성능 특성
- 단일 비트 연산(
&,|,^,~,<<,>>)은 CPU 명령어 한 사이클 수준으로 처리되며 시간복잡도는 O(1)이다. - Brian Kernighan 알고리즘으로 세워진 비트 개수를 세는 연산은 세워진 비트 수를 k라 할 때 O(k)이며, 최악의 경우 O(비트 폭)이다.
- 비트마스크를 이용한 부분집합 순회는 원소 개수 n에 대해 O(2ⁿ)이다. 비트 연산 자체가 빠른 것이지 지수 시간 문제를 O(1)로 바꿔주지는 않는다.
- 비트 연산을 배열 순회나 분기문 대신 사용하면 캐시 친화적이고 분기 예측 실패(branch misprediction)를 줄일 수 있어 실질적인 성능 이득이 있다.
6. 실무 사용 사례
- 권한 시스템: 유닉스 파일 권한(rwx)이 대표적이며, 여러 서비스에서 사용자 권한을 비트 플래그로 저장한다.
- 네트워크 서브넷 마스크: IP 주소와 서브넷 마스크를 AND 연산해 네트워크 대역을 판별한다.
- 압축 및 해시: 체크섬, 해시 함수, CRC 계산에서 비트 시프트와 XOR을 활용한다.
- 비트마스크 동적 계획법(Bitmask DP): 외판원 문제(TSP)처럼 부분집합 상태를 정수 하나로 표현해 메모이제이션 키로 사용한다.
- RGBA 색상 값 인코딩: 32비트 정수 하나에 R, G, B, A 채널을 각각 8비트씩 압축해 저장한다.
- Feature flag 관리: 서비스에서 여러 기능 on/off 상태를 하나의 정수 컬럼으로 DB에 저장해 컬럼 수를 줄인다.
-- PostgreSQL에서 비트 연산으로 플래그 컬럼 조회하기
-- permissions 컬럼: READ=1, WRITE=2, EXECUTE=4
SELECT id, permissions
FROM users
WHERE (permissions & 2) = 2; -- WRITE 권한을 가진 사용자만 조회7. 주의할 점
- 시프트 연산은 언어와 정수 타입에 따라 동작이 다르다. 산술 시프트(부호 유지)와 논리 시프트(부호 무시)를 혼동하면 음수 처리에서 버그가 생긴다. Python의 정수는 임의 정밀도라 오버플로우가 없지만, C나 자바스크립트의 32비트 정수 연산에서는 오버플로우와 부호 문제를 항상 확인해야 한다.
- 비트마스크를 남용하면 코드 가독성이 떨어진다. 플래그 개수가 많아지면 상수 이름과 주석을 명확히 남기거나, enum이나 별도 자료구조로 감싸는 것이 유지보수에 유리하다.
~연산자는 언어에 따라 비트 폭 전체를 반전하므로 예상보다 큰 음수가 나올 수 있다. 특정 비트만 지우고 싶다면flags & ~mask처럼 항상 마스크와 함께 사용해야 한다.- 비트 필드를 데이터베이스나 API 스펙에 노출할 때는 플래그 값의 의미를 문서화해야 한다. 나중에 플래그를 추가하거나 순서를 바꾸면 기존 저장된 값과 충돌한다.
8. 핵심 정리
비트는 0과 1 두 상태로 정보를 표현하는 최소 단위이며, 하드웨어의 물리적 안정성 때문에 채택된 표현 방식이다. AND, OR, XOR, NOT, 시프트 연산을 조합하면 여러 플래그를 하나의 정수로 압축해 다룰 수 있고, 이 연산들은 O(1)로 매우 빠르다. 권한 관리, 네트워크 마스크, 비트마스크 DP 등 실무 전반에서 활용되지만, 부호 처리와 가독성 문제를 항상 염두에 두고 사용해야 한다.