1바이트는 몇 비트인가요?
- 면접 출제
- ★
- 예상 시간
- 5분
1바이트는 8비트입니다. 비트는 0이나 1 하나를 나타내는 가장 작은 단위이고, 이 비트 8개를 묶은 것이 1바이트입니다. 8비트는 2^8 = 256가지 값을 표현할 수 있습니다.
바이트가 처음부터 8비트였던 것은 아닙니다. 바이트는 원래 "문자 하나를 담는 단위"라는 뜻으로, 초기 컴퓨터들은 6비트나 7비트 바이트를 쓰기도 했습니다. 1960년대 IBM System/360이 8비트 바이트를 채택해 큰 성공을 거두면서 사실상의 표준이 되었고, 이후 메모리 주소가 바이트 단위로 매겨지는 관례로 굳어졌습니다.
8이라는 수 자체의 장점도 있습니다. 2의 거듭제곱이라 반으로 나누기 좋고(4비트 니블 둘), 영문자와 기호를 담던 7비트 ASCII에 여유 1비트를 더한 크기이며, 16진수 두 자리로 딱 떨어집니다(0xFF). 그래서 메모리 덤프나 색상 코드처럼 바이트를 다루는 표기가 모두 16진수 두 자리 단위로 읽힙니다.
꼬리질문
조금 더 깊게 물어본다면
답변 뒤에 이어질 수 있는 질문들을 하나씩 열어볼 수 있어요.
1바이트로 표현할 수 있는 값의 범위는?
부호가 없는 경우 0부터 255(2^8 - 1)까지, 부호가 있는 경우 -128부터 127까지입니다.
중요한 점은 unsigned와 signed가 서로 다른 데이터가 아니라 같은 비트 패턴을 다르게 해석하는 약속이라는 것입니다. 11111111이라는 8비트는 unsigned로 읽으면 255이고 signed(2의 보수)로 읽으면 -1입니다. 메모리에는 해석 정보가 없고, 타입이 해석 방법을 정합니다.
범위를 벗어나면 overflow가 일어나 값이 반대편 끝으로 감깁니다. unsigned 8비트에서 255 + 1은 0이 되고, signed 8비트에서 127 + 1은 -128이 됩니다. 나이나 개수처럼 작아 보이는 값을 작은 정수 타입에 담았다가 경계를 넘겨 음수나 0으로 돌아오는 것이 전형적인 overflow 버그입니다.
색상 채널값이 0-255인 이유는?
채널 하나를 1바이트로 저장하기 때문입니다. 8비트로 표현할 수 있는 값이 2^8 = 256가지이므로, 0부터 255까지 256단계가 됩니다.
채널당 8비트가 표준이 된 것은 사람 눈의 한계와 맞아떨어졌기 때문입니다. 256단계면 인접 단계의 밝기 차이를 대부분의 환경에서 구분하기 어렵습니다. 반대로 단계가 부족하면 하늘 그라데이션 같은 완만한 변화에서 경계선이 띠처럼 보이는 banding 현상이 나타납니다. 채널당 4비트(16단계)로 줄여 보면 바로 보입니다.
다만 256단계가 늘 충분한 것은 아닙니다. 어두운 영역을 크게 보정하는 사진 편집에서는 중간 계산의 정밀도가 부족해지고, 밝기 범위가 넓은 HDR 디스플레이에서는 밝은 쪽과 어두운 쪽을 모두 담기에 모자랍니다. 그래서 HDR 영상은 채널당 10비트(1024단계), 전문 편집 포맷은 16비트를 쓰기도 합니다. "채널 = 1바이트"는 물리 법칙이 아니라 용도에 맞춘 기본값입니다.
비트 연산은 왜 빠른가요?
비트 연산은 CPU가 하드웨어 수준에서 직접 처리하는 명령이라 덧셈이나 곱셈보다 훨씬 빠릅니다. 예를 들어 2의 거듭제곱 곱셈은 left shift(<<)로, 나눗셈은 right shift(>>)로 대체할 수 있습니다.
shift가 곱셈이 되는 이유는 이진법의 자릿수 이동이기 때문입니다. 십진수에서 왼쪽으로 한 자리 밀면 10배가 되듯, 이진수에서 한 비트 밀면 2배가 됩니다. x << 3은 x × 8이고, x >> 1은 x ÷ 2(내림)입니다. 나머지 연산도 2의 거듭제곱이라면 mask로 대체됩니다. x & 7은 x % 8과 같은데, 하위 3비트만 남기는 것이 8로 나눈 나머지이기 때문입니다.
다만 "빠르니까 shift를 쓰자"는 현대에는 대부분 불필요합니다. 컴파일러가 x * 8을 알아서 shift로 바꿔 주므로, 사람이 읽기 좋은 곱셈으로 쓰는 것이 낫습니다. 비트 연산이 여전히 본질적인 곳은 성능이 아니라 표현입니다. 플래그 묶음, 해시 계산, 프로토콜 파싱처럼 데이터 자체가 비트 단위로 정의된 경우입니다.
1920x1080 이미지의 raw 메모리는?
RGBA라면 1920 × 1080 × 4 = 약 8.3MB입니다. 압축 전 픽셀 데이터를 그대로 메모리에 올릴 때의 크기입니다. 실제 PNG나 JPG 파일은 이보다 훨씬 작습니다.
이 계산이 실무와 연결되는 지점은 "파일 크기와 메모리 사용량은 다르다"는 사실입니다. 500KB짜리 PNG도 화면에 그리려면 디코딩되어 raw 픽셀로 메모리에 올라가므로, 1080p 이미지 한 장이 8MB를 차지합니다. 이미지가 많은 갤러리 화면에서 파일 크기만 보고 안심했다가 메모리 사용량이 폭증하는 이유가 이것입니다.
그래서 이미지를 다루는 플랫폼들은 표시 크기에 맞춘 다운샘플링을 중요하게 다룹니다. 4000 × 3000 사진 원본을 디코딩하면 48MB지만, 목록의 200 × 150 썸네일로 디코딩하면 120KB입니다. 화면에 보이는 크기보다 큰 해상도로 디코딩하는 것은 순수한 메모리 낭비이므로, 표시 크기를 알고 그 크기로만 디코딩하는 것이 기본 최적화가 됩니다.
실제 PNG 파일이 raw보다 작은 이유는?
압축 때문입니다. 1080p RGBA 이미지의 raw 데이터는 약 8.3MB이지만, PNG는 무손실 압축을 적용해 실제 파일은 훨씬 작아집니다. 화면 내용이 단순할수록 압축률이 높아집니다.
PNG는 원본 정보를 그대로 보존하는 무손실 압축 방식이고, JPG는 사람 눈에 덜 민감한 정보를 일부 버리는 손실 압축 방식입니다. 그래서 같은 사진을 저장하면 JPG 파일이 PNG보다 훨씬 작습니다.
무손실 압축이 가능한 이유는 픽셀 데이터에 중복이 많기 때문입니다. 흰 배경이 이어지면 "흰색 픽셀 1만 개"를 일일이 저장할 필요 없이 반복을 짧게 표현할 수 있습니다. PNG는 여기에 필터라는 전처리를 더해, 각 픽셀을 그대로 저장하는 대신 왼쪽이나 위 픽셀과의 차이만 저장합니다. 이웃 픽셀은 비슷한 경우가 많아 차이값은 0 근처에 몰리고, 값이 몰릴수록 뒤이은 DEFLATE 압축이 잘 듣습니다.
같은 원리로 "어떤 이미지냐"에 따라 압축률이 크게 달라집니다. 단색 영역과 반복이 많은 스크린샷이나 다이어그램은 PNG가 원본의 몇 퍼센트 수준까지 줄지만, 모든 픽셀이 미세하게 다른 사진은 중복이 적어 무손실로는 거의 줄지 않습니다. 사진에 손실 압축(JPG)을 쓰는 것은 취향이 아니라 무손실의 한계 때문입니다.
이미지 포맷(PNG/JPG/WebP)은 어떻게 압축하나요?
PNG는 DEFLATE 알고리즘으로 무손실 압축합니다. JPG는 DCT(이산 코사인 변환)로 주파수 성분을 분리한 뒤 고주파 정보를 버리는 손실 압축을 씁니다. WebP는 VP8 기반의 손실 압축과 무손실 모드를 모두 지원하며, 같은 품질 기준으로 JPG보다 파일 크기가 작습니다.
손실 압축이 무엇을 버리는지가 핵심입니다. JPG가 버리는 고주파 성분은 픽셀 값이 급격하게 변하는 미세한 질감입니다. 사람 눈은 완만한 변화(하늘의 그라데이션)는 잘 보지만 미세한 변화(잔디의 결)는 대충 보므로, 후자를 뭉개도 체감 품질이 크게 떨어지지 않습니다. 색상도 마찬가지로, 눈이 밝기 변화보다 색상 변화에 둔감하다는 점을 이용해 색상 정보를 밝기보다 낮은 해상도로 저장합니다(chroma subsampling).
이 원리를 알면 JPG의 약점도 설명됩니다. 텍스트나 선명한 경계선은 정확히 고주파 성분이라, JPG로 저장하면 글자 주변에 얼룩 같은 잡음(compression artifact)이 생깁니다. 스크린샷과 다이어그램에 PNG, 사진에 JPG라는 선택 기준은 "버리는 정보가 그 이미지에서 중요한 정보인가"라는 질문의 답입니다.
4K(UHD) 화면 한 프레임의 비압축 용량은?
4K(UHD)는 3840 × 2160입니다. 비압축 RGB 기준 3840 × 2160 × 3바이트 ≈ 24MB이고, 실제 영상은 H.264, HEVC 같은 코덱으로 100배 이상 압축됩니다.
해상도 이름은 두 가지 체계가 섞여 있어 헷갈리기 쉽습니다. K 표기는 가로 픽셀 기준이지만 엄밀한 표준은 아닙니다. 정확한 2K는 2048 × 1080(DCI 영화 규격)이고, FHD(1920 × 1080)는 이에 가깝다는 이유로 마케팅에서 2K로 부릅니다. QHD(2560 × 1440)는 K 표기와 무관하게 HD(1280 × 720)의 픽셀 수가 4배라는 뜻이고, 가로가 약 2500px라 2.5K로 부르기도 합니다.
맨 앞 비트만 바꿔서 음수를 표현하면 안 되나요?
안 됩니다. +0과 -0이 두 개 생기고, 덧셈 회로를 재사용할 수 없어서 실제 컴퓨터에서는 쓰지 않습니다.
이 방식을 부호-절댓값(sign-magnitude) 표현이라 합니다. 두 가지 문제가 있습니다.
첫째, +0(00000000)과 -0(10000000)이 따로 존재합니다. 0이 두 개면 비교 회로가 복잡해집니다.
둘째, 덧셈이 맞지 않습니다. 0001(+1)과 1001(-1)을 그대로 더하면 1010(-2)가 나오는데 결과는 0이어야 합니다. 별도의 뺄셈 회로가 필요해집니다.
그럼 컴퓨터에서 음수를 어떻게 표현하나요?
2의 보수(Two's complement)로 표현합니다. 양수의 모든 비트를 반전하고 1을 더한 값이 음수입니다.
2의 보수는 0이 하나뿐이고, 기존 덧셈 회로를 그대로 재사용할 수 있습니다. -1 + 1이 정확히 0이 나옵니다.
+1 = 00000001
→ 비트 반전: 11111110
→ +1: 11111111 (= -1)
검증: 11111111(-1) + 00000001(+1) = 100000000
→ 9번째 비트 버림 → 00000000 (= 0) ✓signed 8비트에서 -1을 이진수로 나타내면?
11111111입니다. +1(00000001) → 비트 전체 반전(11111110) → +1 = 11111111입니다.
2의 보수 변환 절차를 그대로 적용합니다.
+1 = 00000001
반전 = 11111110
+1 = 11111111 (= -1)다른 값도 같은 방식입니다. -7이라면: +7(00000111) → 반전(11111000) → +1 = 11111001.
signed 8비트의 최솟값은 왜 -128이고, 최댓값은 127인가요?
00000000이 +0 하나만 나타내기 때문에 양수 쪽 범위가 하나 줄어 비대칭이 됩니다.
양수는 00000001127, 음수는 01111111로 110000000-1입니다.11111111로 -128
10000000을 2의 보수 규칙으로 계산하면 반전(01111111) + 1 = 10000000이 되어 자기 자신으로 돌아오는 예외 케이스입니다. 그래서 이 값을 -128로 정의합니다.
JavaScript에서 비트 연산이 쓰이는 경우는?
권한 플래그를 비트마스크로 표현하거나, canvas pixel 데이터를 처리할 때 RGBA 채널을 비트 연산으로 분리하는 경우가 있습니다. 또한 hash 함수 구현이나 색상 값을 16진수로 변환할 때도 shift와 AND 연산을 씁니다.
JavaScript에서 비트 연산에는 특유의 함정이 있습니다. JS의 number는 64비트 부동소수점인데, 비트 연산자는 피연산자를 32비트 정수로 변환한 뒤 연산하고 다시 number로 돌려놓습니다. 그래서 32비트를 넘는 큰 정수에 비트 연산을 하면 상위 비트가 잘려 나갑니다. 2^32 이상의 값이나 timestamp에 shift를 썼다가 값이 망가지는 것이 대표적인 실수입니다.
비트마스크 플래그의 동작을 보면, 읽기 권한 1, 쓰기 권한 2, 실행 권한 4처럼 각 권한에 서로 다른 비트를 배정하고 OR로 합쳐 하나의 정수에 담습니다. 확인은 flags & WRITE가 0이 아닌지로, 추가는 flags | WRITE로, 제거는 flags & ~WRITE로 합니다. Unix 파일 권한(755 같은 8진수 표기)과 Linux 시스템 콜의 옵션 인자가 모두 이 방식입니다.
네트워크 패킷에서 비트 단위 파싱이 쓰이는 이유는?
IP, TCP, DNS 같은 프로토콜은 헤더 필드를 바이트 단위가 아닌 비트 단위로 정의합니다. 예를 들어 TCP 헤더의 flag(SYN, ACK, FIN)는 각각 1비트이므로, 해당 바이트에서 mask와 shift로 특정 비트를 읽어야 합니다.
프로토콜이 비트 단위로 필드를 정의하는 이유는 헤더가 모든 패킷에 붙는 고정 비용이기 때문입니다. 참/거짓 플래그 하나에 1바이트를 쓰면 7비트가 낭비되는데, 초당 수백만 패킷이 오가는 네트워크에서는 이 낭비가 실제 대역폭 손실이 됩니다. 그래서 IP 헤더의 버전 필드는 4비트, TCP flag는 각 1비트처럼 필요한 만큼만 배정합니다.
파싱의 실제 모습은 mask와 shift의 조합입니다. TCP 헤더의 flag 바이트에서 SYN이 켜져 있는지는 해당 비트 위치만 남기는 AND 연산으로 확인하고, 한 바이트의 상위 4비트에 담긴 필드는 오른쪽으로 4비트 shift해 읽습니다. 프로토콜 명세의 "몇 번째 비트부터 몇 비트" 정의를 코드로 옮기는 것이라, 네트워크 프로그래밍에서 비트 연산은 최적화가 아니라 명세 구현 그 자체입니다.
부가 설명
비트에서 바이트로 넘어가는 이 관계가 실제로 자주 쓰입니다.
색상 채널값이 0-255 범위인 것도 이 때문입니다. 빨강, 초록, 파랑 각각을 1바이트(0-255)로 표현하면 채널 하나당 256단계를 쓸 수 있고, RGB 픽셀 하나는 3바이트, RGBA면 4바이트가 됩니다.
큰 단위도 같은 방식입니다. 1KB는 2^10 = 1024바이트, 1MB는 2^20 바이트, 1GB는 2^30 바이트입니다.
한 줄 정리
1바이트 = 8비트 = 256가지 값이며, 색상 채널값, 정수 타입, 이미지 메모리 계산의 기준입니다.