1KB는 1000바이트일까 1024바이트일까: Bit·Byte 단위, KiB vs KB, Mbps 계산

이 글의 핵심

1KB가 1000바이트인지 1024바이트인지, 500GB SSD가 왜 465GB로 보이는지, 100Mbps가 왜 12.5MB/s인지를 SI/IEC 표준 기준으로 정리합니다. 이미지·동영상·배열 메모리 크기 계산과 자주 하는 실수까지 다룹니다.

들어가며

Bit, Byte, KB, MB, GB는 프로그래머가 매일 사용하는 기본 단위입니다. 하지만 1KB가 1000바이트인지 1024바이트인지, 100Mbps가 초당 몇 MB인지 헷갈리는 경우가 많습니다. 비유로 말씀드리면, Bit는 전구 하나(켜짐/꺼짐)이고, Byte는 전구 8개의 조합입니다. 이 조합으로 문자, 숫자, 색상 등 모든 데이터를 표현합니다.

이 혼란은 단순한 용어 문제가 아니라 실제 버그와 오해로 이어집니다. 서버 디스크 사용률 알림을 “GB” 기준으로 걸었는데 모니터링 도구는 GiB로 계산해 임계값이 7% 가까이 어긋나거나, 클라우드 인스턴스의 “네트워크 10Gbps”를 초당 10GB로 착각해 백업 시간 계산을 8배 틀리는 일이 흔합니다. 이 글에서는 단위를 정의하는 두 표준(SI와 IEC)이 왜 공존하게 됐는지, 어떤 도구가 어떤 기준을 쓰는지, 그리고 계산할 때 어디서 실수가 생기는지를 중심으로 설명합니다.


Bit와 Byte 기초

Bit (비트)

Bit는 컴퓨터가 다루는 가장 작은 단위입니다.

Bit = Binary Digit (이진수 숫자)
값: 0 또는 1

예시:

0  → 꺼짐 (전구 꺼짐, 스위치 OFF)
1  → 켜짐 (전구 켜짐, 스위치 ON)

Byte (바이트)

Byte는 8개의 Bit입니다.

1 Byte = 8 Bits
예시:
01001000  → 'H' (ASCII 코드 72)
01100101  → 'e' (ASCII 코드 101)

왜 8개인가?

역사적 이유:

  • 초기 컴퓨터는 6비트, 7비트 등 다양
  • IBM System/360(1964)이 8비트 바이트를 채택하면서 사실상 표준이 됨
  • 8비트 = 256가지 조합 (2^8)
  • 영문 대소문자 + 숫자 + 특수문자 표현 충분

8이 2의 거듭제곱이라는 점도 중요합니다. 바이트가 8비트이면 비트 위치를 3비트(07)로 정확히 표현할 수 있고, 16진수 두 자리(0x000xFF)가 정확히 1바이트에 대응합니다. 메모리 덤프나 패킷 캡처를 16진수로 보는 이유가 여기에 있습니다. 참고로 네트워크 표준 문서(RFC)에서는 “바이트”가 역사적으로 8비트가 아닌 시스템도 있었기 때문에 8비트임을 명확히 하려고 옥텟(octet) 이라는 용어를 씁니다.

Bit와 Byte 표기

표기의미예시
b (소문자)Bit100Mbps (메가비트/초)
B (대문자)Byte100MB (메가바이트)

주의: 대소문자 구분이 매우 중요합니다! 소문자 b와 대문자 B의 차이는 정확히 8배입니다. 인터넷 요금제, 클라우드 인스턴스 대역폭, NIC 사양은 거의 항상 비트(bps) 단위로 표기되고, 브라우저·wget·curl의 다운로드 속도는 바이트(B/s) 단위로 표시되므로 두 숫자를 직접 비교하면 안 됩니다. 킬로의 접두어도 SI 규칙상 소문자 k(kB)가 맞지만 실무에서는 KB가 더 흔히 쓰입니다.


데이터 단위

2진수 기반 (컴퓨터 메모리)

2의 거듭제곱 사용:

단위기호값계산
ByteB12^0
KibibyteKiB1,0242^10
MebibyteMiB1,048,5762^20
GibibyteGiB1,073,741,8242^30
TebibyteTiB1,099,511,627,7762^40

예시:

RAM 8GB = 8 × 1,073,741,824 = 8,589,934,592 바이트

메모리가 2진수 단위를 쓰는 데는 하드웨어적 이유가 있습니다. 메모리 칩은 주소선(address line)이 n개면 정확히 2^n개의 위치를 가리킬 수 있으므로 용량이 자연스럽게 2의 거듭제곱이 됩니다. 그래서 RAM 모듈은 “8GB”라고 팔리지만 실제로는 8GiB이고, 이 경우만큼은 제조사 표기가 오히려 2진수 기준입니다. 엄밀하게는 위 예시도 8GiB로 쓰는 것이 맞습니다.

10진수 기반 (저장장치)

10의 거듭제곱 사용:

단위기호값계산
ByteB110^0
KilobyteKB1,00010^3
MegabyteMB1,000,00010^6
GigabyteGB1,000,000,00010^9
TerabyteTB1,000,000,000,00010^12

예시:

SSD 500GB = 500 × 1,000,000,000 = 500,000,000,000 바이트

KiB vs KB 차이

혼란의 원인:

1KB = 1,000 바이트 (10진수, SI 단위)
1KiB = 1,024 바이트 (2진수, IEC 표준)
차이: 1,024 - 1,000 = 24 바이트 (2.4%)

실제 영향:

용량10진수 (GB)2진수 (GiB)차이
100GB100,000,000,00093.13 GiB6.87%
500GB500,000,000,000465.66 GiB6.87%
1TB1,000,000,000,000931.32 GiB6.87%

왜 500GB SSD가 465GB로 보이나요?

제조사: 500GB = 500,000,000,000 바이트 (10진수)
OS: 500,000,000,000 ÷ 1,073,741,824 = 465.66 GiB (2진수)

여기서 주의할 점은 단위가 커질수록 차이가 커진다는 것입니다. KB와 KiB의 차이는 2.4%지만, GB와 GiB는 약 7.4%, TB와 TiB는 약 9.95%까지 벌어집니다(위 표의 6.87%는 반대 방향, 즉 10진 값을 2진 단위로 환산했을 때 “줄어드는” 비율입니다). 수십 TB 스토리지를 산정할 때 이 차이를 무시하면 수 TB가 비는 계획이 나옵니다.

어떤 도구가 어떤 기준을 쓰는지도 제각각입니다. Windows 탐색기는 2진 기준으로 계산하면서 “GB”라고 표시하고, macOS는 10.6(Snow Leopard)부터 Finder에서 10진 기준을 씁니다. Linux의 df -h, ls -lh는 2진 기준이면서 G, M처럼 접미사만 붙이고, df -H나 --si 옵션을 주면 10진 기준으로 바뀝니다. 저도 같은 디스크를 두 도구로 확인했다가 숫자가 안 맞아 한참 원인을 찾은 적이 있는데, 결국 옵션 하나 차이였습니다. 수치를 문서나 알림 임계값에 옮길 때는 어떤 도구의 어떤 옵션으로 얻은 값인지 함께 적어 두는 것이 안전합니다.


이진수 표현

이진수 기초

10진수를 2진수로 변환:

10진수: 13
2진수: 1101
계산:
13 = 8 + 4 + 1
   = 2³ + 2² + 2⁰
   = 1×8 + 1×4 + 0×2 + 1×1
   = 1101₂

2진수 → 10진수

1101₂ = ?
자릿수:  2³  2²  2¹  2⁰
값:      8   4   2   1
비트:    1   1   0   1
계산: 1×8 + 1×4 + 0×2 + 1×1 = 13

10진수 → 2진수

13을 2진수로 변환:
13 ÷ 2 = 6 ... 나머지 1  ↑
 6 ÷ 2 = 3 ... 나머지 0  |
 3 ÷ 2 = 1 ... 나머지 1  |
 1 ÷ 2 = 0 ... 나머지 1  |
아래에서 위로 읽기: 1101₂

프로그래밍 언어별 표현

// C++
int x = 0b1101;      // 2진수 리터럴 (C++14)
int y = 13;          // 10진수
int z = 0xD;         // 16진수
std::cout << std::bitset<8>(13);  // "00001101"
# Python
x = 0b1101      # 2진수
y = 13          # 10진수
z = 0xD         # 16진수
print(bin(13))  # "0b1101"
print(hex(13))  # "0xd"
// JavaScript
const x = 0b1101;  // 2진수
const y = 13;      // 10진수
const z = 0xD;     // 16진수
console.log((13).toString(2));   // "1101"
console.log((13).toString(16));  // "d"

네트워크 속도

Mbps vs MB/s

가장 헷갈리는 부분:

Mbps = Megabits per second (메가비트/초)
MB/s = Megabytes per second (메가바이트/초)
변환: Mbps ÷ 8 = MB/s

실전 계산

100Mbps 인터넷:

100Mbps = 100 메가비트/초
        = 100 ÷ 8 메가바이트/초
        = 12.5 MB/s
1GB 파일 다운로드 시간:
1,000MB ÷ 12.5MB/s = 80초

1Gbps 인터넷:

1Gbps = 1,000Mbps
      = 1,000 ÷ 8 MB/s
      = 125 MB/s
1GB 파일 다운로드 시간:
1,000MB ÷ 125MB/s = 8초

네트워크 속도 비교

속도MbpsMB/s1GB 다운로드
느린 ADSL101.2513분 20초
일반 인터넷10012.51분 20초
기가 인터넷1,0001258초
10기가 인터넷10,0001,2500.8초

왜 8로 나누나?

8로 나누는 이유는 단순히 1 Byte = 8 Bits이기 때문입니다. 네트워크 장비가 비트 단위로 속도를 표기하는 것은 물리 계층이 실제로 비트를 직렬로 한 개씩 전송하기 때문이고, 파일은 바이트 단위로 저장되므로 둘 사이를 옮길 때 8을 곱하거나 나눕니다.

다만 이것은 이론상 최대치입니다. 실제로는 전송 방식에 따라 오버헤드가 추가됩니다.

UART(시리얼 통신) 8N1 방식:
[시작 비트 1][데이터 8비트][정지 비트 1] = 10비트로 1바이트 전송
→ 115200bps 시리얼은 약 11,520 B/s (÷10)

이더넷 + TCP/IP:
1500바이트 MTU 중 IP/TCP 헤더 약 40바이트, 이더넷 프레임 오버헤드 별도
→ 순수 데이터 처리량은 회선 속도의 약 94~95% 수준이 상한

시작/정지 비트 때문에 “10으로 나누는” 계산은 시리얼 통신에 해당하는 이야기이고, 이더넷에서는 헤더와 프레임 간격이 오버헤드가 됩니다. 여기에 TCP 혼잡 제어, 서버 측 대역폭, Wi-Fi 신호 품질 등이 겹치므로 실측 속도는 대개 이론치보다 낮게 나옵니다.


실전 계산

파일 크기 계산

텍스트 파일:

"Hello" = 5글자
ASCII 인코딩:
H = 01001000 (1 Byte)
e = 01100101 (1 Byte)
l = 01101100 (1 Byte)
l = 01101100 (1 Byte)
o = 01101111 (1 Byte)
총 크기: 5 Bytes

UTF-8 한글:

"안녕" = 2글자
UTF-8 인코딩:
안 = 11101010 10110000 10010000 (3 Bytes)
녕 = 11101010 10110001 10010101 (3 Bytes)
총 크기: 6 Bytes

UTF-8은 가변 길이 인코딩이라 ASCII 범위(영문, 숫자)는 1바이트, 대부분의 한글 음절은 3바이트, 이모지는 4바이트를 씁니다. 그래서 “글자 수”와 “바이트 수”가 다르다는 점을 자주 놓칩니다. DB 컬럼 길이 제한이 바이트 기준인지 문자 기준인지(MySQL VARCHAR(n)은 문자 기준, 인덱스 키 길이 제한은 바이트 기준), 문자열을 바이트 단위로 자를 때 한글 한 글자가 중간에서 잘려 깨진 문자(�)가 되지 않는지 확인해야 합니다. JavaScript의 "안녕".length는 2지만 new TextEncoder().encode("안녕").length는 6입니다.

이미지 크기 계산

RGB 이미지:

해상도: 1920 × 1080 (Full HD)
색상: RGB (각 8비트)
계산:
픽셀 수 = 1920 × 1080 = 2,073,600
각 픽셀 = R(1 Byte) + G(1 Byte) + B(1 Byte) = 3 Bytes
총 크기 = 2,073,600 × 3 = 6,220,800 Bytes
        = 6,220,800 ÷ 1,024 = 6,075 KiB
        = 6,075 ÷ 1,024 = 5.93 MiB

압축 효과: 압축 후 크기는 이미지 내용에 따라 크게 달라지므로 고정된 비율로 말하기 어렵습니다. 사진처럼 색 변화가 많은 이미지는 JPEG/WebP 같은 손실 압축에서 원본의 수 % ~ 10% 수준까지 줄어드는 경우가 많고, 스크린샷·도표처럼 단색 영역이 넓은 이미지는 PNG 무손실 압축이 오히려 유리합니다. 중요한 점은 디스크에서 작아도 메모리에 올리면 원래 크기로 돌아간다는 것입니다. 300KB짜리 JPEG도 디코딩하면 1920×1080×4(RGBA) ≈ 7.9MiB의 픽셀 버퍼를 차지하므로, 썸네일 목록에서 원본 이미지를 그대로 수백 장 디코딩하면 메모리가 급증합니다.

동영상 크기 계산

1분 Full HD 동영상:

해상도: 1920 × 1080
프레임: 30 FPS
시간: 60초
계산:
프레임 수 = 30 × 60 = 1,800
각 프레임 = 5.93 MB (위 계산)
총 크기 = 1,800 × 5.93 MB = 10,674 MB (압축 전)
압축 후 크기는 비트레이트로 계산:
H.264 8Mbps 인코딩 → 8Mbps × 60초 = 480Mb = 60 MB

압축된 동영상의 크기는 해상도보다 비트레이트로 계산하는 편이 정확합니다. 인코더에 지정한 비트레이트(예: 8Mbps)에 재생 시간을 곱하고 8로 나누면 바이트 크기가 나옵니다. 여기서도 비트(Mbps)와 바이트(MB)가 섞이므로 8배 실수가 자주 나옵니다.

메모리 사용량 계산

배열 메모리:

// C++
int arr[1000];  // int는 4바이트
메모리 사용량:
1,000 × 4 = 4,000 Bytes = 4 KB

구조체 메모리:

struct Person {
    char name[20];    // 20 Bytes
    int age;          // 4 Bytes
    double salary;    // 8 Bytes
};  // 20 + 4 = 24는 8의 배수라 double 앞 패딩 없음 → sizeof 32
Person people[1000];
메모리 사용량:
1,000 × 32 = 32,000 Bytes = 32 KB

구조체 크기는 멤버 크기의 단순 합이 아닐 수 있습니다. 컴파일러는 각 멤버를 자신의 정렬 단위(double은 보통 8바이트) 경계에 맞추려고 패딩을 넣습니다. 위 예시는 우연히 24바이트 뒤에 double이 와서 패딩이 없지만, char name[21]로 바꾸면 sizeof(Person)은 33이 아니라 40이 됩니다. 수백만 개를 담는 배열이라면 멤버 순서만 바꿔도 메모리가 크게 달라지므로 실제 값은 항상 sizeof로 확인해야 합니다. 자세한 규칙은 C++ 정렬과 패딩에서 다룹니다.


트러블슈팅

파일 크기가 다르게 표시됨

문제:

Windows: 파일 크기 1,024 KB
macOS: 파일 크기 1 MB (정확히는 1.05 MB)

원인:

  • Windows: 1,024로 나누고 “KB”로 표기 (실제로는 KiB)
  • macOS: 1,000으로 나누는 10진 기준 해결:
실제 크기: 1,048,576 Bytes
Windows: 1,048,576 ÷ 1,024 = 1,024 KB
macOS: 1,048,576 ÷ 1,000,000 = 1.05 MB

네트워크 속도가 느림

문제:

인터넷: 100Mbps
다운로드 속도: 10 MB/s (예상: 12.5 MB/s)

원인:

  • 네트워크 오버헤드 (프로토콜, 헤더)
  • Wi-Fi 신호, 공유 회선, 상대 서버의 전송 속도 제한
  • 측정 속도가 이론치보다 낮은 것은 정상입니다. 유선이라면 앞에서 계산한 프로토콜 오버헤드 상한에 가깝게 나올 수 있지만, Wi-Fi나 멀리 있는 서버를 거치면 그보다 훨씬 낮아질 수 있습니다. 회선 자체의 속도가 궁금하다면 iperf3처럼 양 끝을 직접 통제하는 도구로 재 보는 것이 정확합니다. 계산:
100Mbps ÷ 8 = 12.5 MB/s (이론)
12.5 × 0.8 = 10 MB/s (이 예시에서 관찰한 속도, 이론치의 80%)

메모리 부족 에러

문제:

int arr[1000000000];  // 10억 개
// 메모리 부족 에러

원인:

필요 메모리:
1,000,000,000 × 4 Bytes = 4,000,000,000 Bytes
                        = 4 GB
스택 메모리 제한: Linux 기본 8 MB, Windows(MSVC) 기본 1 MB

해결:

// 힙 메모리 사용
vector<int> arr(1000000000);  // 힙에 할당
// 또는 동적 할당
int* arr = new int[1000000000];

함수 안의 지역 배열은 스택에 올라가므로 수 MB만 넘어도 Linux에서는 Segmentation fault, Windows에서는 0xC00000FD (Stack overflow)로 프로그램이 즉시 종료됩니다. 컴파일 에러가 아니라 실행 시점 크래시라 원인을 찾기 어렵습니다. 힙으로 옮기면 스택 문제는 사라지지만 4GB가 필요하다는 사실은 그대로입니다. 물리 메모리나 프로세스 한도가 부족하면 std::bad_alloc 예외가 나거나, Linux에서는 할당은 성공한 뒤 실제로 쓰는 순간 OOM killer에 의해 종료될 수 있습니다. 따라서 “힙에 할당하면 해결”이 아니라, 먼저 필요한 메모리를 개수 × sizeof(타입)으로 계산해 보고 정말 그만큼 필요한지부터 따져보는 것이 순서입니다.


마무리

Bit, Byte, KB, MB, GB는 프로그래머의 기본 언어입니다. 핵심 요약:

  1. 1 Byte = 8 Bits
  2. 1 KiB = 1,024 Bytes (2진수, 메모리)
  3. 1 KB = 1,000 Bytes (10진수, 저장장치)
  4. Mbps ÷ 8 = MB/s (네트워크 속도) 실전 팁:

단위 계산에서 실수를 줄이는 가장 확실한 방법은 계산 과정에 단위를 계속 붙여 쓰는 것입니다. “100 ÷ 8”이 아니라 “100 Mb/s ÷ 8 b/B = 12.5 MB/s”처럼 쓰면 비트와 바이트가 섞이는 순간 바로 눈에 띕니다.


자주 묻는 질문 (FAQ)

Q. 1TB 디스크를 샀는데 운영체제에서는 931GB로 보이는 이유는 무엇인가요?

A. 디스크 제조사는 1TB를 10진 단위인 1,000,000,000,000바이트로 표기하지만, Windows 같은 운영체제는 1024를 기준으로 한 2진 단위로 계산하면서 표기만 GB로 합니다. 1,000,000,000,000바이트를 2^30으로 나누면 약 931.32GiB가 되므로 약 6.87%가 줄어든 것처럼 보입니다. 용량이 사라진 것이 아니라 단위 기준이 다른 것이며, 정확히 표현하려면 2진 단위에는 GiB와 TiB를 쓰는 것이 좋습니다.


같이 보면 좋은 글