String vs Binary

데이터를 다룰 때 "이건 문자열(String)로 다뤄야 하나, 바이너리(Binary)로 다뤄야 하나"를 자주 고민하게 됩니다. 사실 컴퓨터 입장에서 둘 다 결국 바이트(byte) 나열이라는 점은 동일합니다. 차이는 그 바이트를 어떻게 해석(interpret)하기로 약속했는가 에 있습니다.

1. 본질: 모든 것은 바이트다

디스크에 저장되거나 네트워크로 전송되는 데이터는 예외 없이 0x48 0x69 같은 바이트 시퀀스입니다. String과 Binary의 구분은 데이터 자체의 속성이 아니라 해석 규약의 차이입니다.

  • String(텍스트): 바이트를 문자 인코딩 규칙(UTF-8, ASCII 등)에 따라 사람이 읽는 문자로 매핑해 다룹니다. 예: 0x48 0x69"Hi"
  • Binary: 바이트를 데이터 구조 정의(포맷 스펙)에 따라 해석합니다. 텍스트로의 변환을 전제하지 않습니다. 예: PNG 헤더, Protobuf 메시지, 정수의 리틀엔디안 표현

즉 같은 바이트라도 "텍스트로 본다"는 것은 인코딩 테이블을 통과시킨다는 뜻이고, "바이너리로 본다"는 것은 포맷 스펙대로 필드를 잘라 읽는다는 뜻입니다.

바이너리도 마찬가지로 약속이 필요합니다. 같은 바이트가 엔디안·타입 같은 포맷 스펙에 따라 다른 값으로 해석됩니다(예시는 3절). 결국 텍스트든 바이너리든 "바이트 자체에는 의미가 없고, 약속이 의미를 부여한다"는 점은 같고, 차이는 약속의 종류뿐입니다.

2. String이 곧 인코딩 문제인 이유

문자열을 다룬다는 건 항상 어떤 인코딩으로 다루는지를 동반합니다. 인코딩 정보가 빠지면 같은 바이트가 다른 문자로 읽혀 글자가 깨집니다(mojibake).

바이트:       EC 95 88 EB 85 95
UTF-8 해석:    안녕
EUC-KR 해석:   깨진 문자

그래서 텍스트 데이터를 주고받을 때는 인코딩을 명시(charset=utf-8)하거나 UTF-8을 기본 약속으로 두는 것이 중요합니다.

3. Binary도 해석 약속에 의존한다

텍스트가 인코딩 약속에 의존하듯, 바이너리도 같은 바이트가 포맷 스펙에 따라 다른 값으로 읽힙니다. 스펙이 없으면 바이너리 역시 의미를 확정할 수 없습니다.

3.1. 엔디안(바이트 순서)

바이트: 00 00 01 00
빅엔디안 uint32   → 256
리틀엔디안 uint32 → 65536

같은 4바이트라도 바이트 순서 약속이 다르면 값이 달라집니다.

3.2. 타입 해석

바이트: 00 00 28 42  (little-endian)
uint32  → 1109917696
float32 → 42.0

같은 비트열을 정수로 보느냐 부동소수점(IEEE 754)으로 보느냐에 따라 전혀 다른 값이 됩니다.

3.3. 포맷 식별(magic number)

파일이나 메시지 앞머리의 magic number·헤더가 "이 바이트 묶음을 어느 스펙으로 읽을지"를 알려줍니다. 예를 들어 PNG는 89 50 4E 47로 시작합니다. 잘못된 디코더로 읽으면 깨지거나 에러가 납니다.

정리하면 텍스트의 "인코딩"과 바이너리의 "포맷 스펙(엔디안·타입·magic)"은 같은 역할을 합니다. 바이너리가 스키마/스펙을 요구하는 이유(뒤의 포맷 비교표 "스키마" 항목)도 여기서 나옵니다.

4. 텍스트 포맷 vs 바이너리 포맷

직렬화(serialization) 포맷도 이 두 갈래로 나뉩니다.

구분 텍스트 포맷 바이너리 포맷
예시 JSON, XML, CSV, YAML Protobuf, MessagePack, Avro, 이미지/오디오, 실행 파일
사람이 읽기 가능 직접 읽기 어려움(decoder·viewer 필요)
크기 대체로 큼(필드명·구분자 반복) 대체로 작음(필드 태그·길이만)
파싱 속도 대체로 느림(문자 파싱·변환) 대체로 빠름(바이트 직접 매핑)
디버깅 curl·로그로 즉시 확인 디코더 없이는 불투명
스키마 문법 파싱은 스키마 없이 가능(의미 계약은 별도) 보통 스키마 필요
상호운용성 높음(어디서나 파싱) 스펙·라이브러리 의존

위 표는 일반적 경향이며 절대명제는 아닙니다. 특히 파싱 속도는 payload 크기, parser 구현 품질, schema validation 유무, 메모리 allocation, 압축 적용 여부에 따라 충분히 역전될 수 있습니다. 따라서 성능이 중요하다면 추정 대신 실제 워크로드로 측정하는 것이 안전합니다.

요약하면 일반적으로 텍스트는 가독성·이식성, 바이너리는 크기·속도 쪽에 유리한 경향을 보입니다.

5. 텍스트 통로로 바이너리를 보낼 때의 함정

텍스트만 안전하게 통과시키는 경로(예: JSON 문자열 필드, 일부 텍스트 프로토콜)로 바이너리를 그대로 실으면 문제가 생깁니다.

  • NUL 바이트(0x00) 문제: NUL-terminated 문자열 API는 0x00을 종료 문자로 보기 때문에 그 뒤 데이터가 잘릴 수 있습니다. 이래서 "binary-safe"한 문자열 처리가 별도로 거론됩니다(예: Redis의 String은 binary-safe).
  • 인코딩 위반: 임의 바이트는 유효한 UTF-8 시퀀스가 아닐 수 있어 디코딩 단계에서 깨지거나 예외가 납니다.

해법은 바이너리를 텍스트로 인코딩하는 것입니다. 대표적으로 Base64(3바이트 → 4문자, 약 33% 크기 증가)를 써서 안전한 문자 집합으로 변환합니다. 단, 크기·CPU 비용이 붙으므로 가능하면 바이너리 통로를 그대로 쓰는 편이 낫습니다.

6. 레이어별로 드러나는 같은 구분

이 구분은 추상 개념이 아니라 실무 곳곳에서 같은 형태로 반복됩니다.

  • HTTP: Content-Type: text/*; charset=...(텍스트) vs application/octet-stream·image/png(바이너리). → Octet-stream
  • DB: VARCHAR/TEXT(인코딩 가진 문자열) vs BLOB/BYTEA(바이트 그대로)
  • 언어 타입:
    • Go: string(읽기 전용 바이트, UTF-8 관례) vs []byte(가변 바이트)
    • Python: str(유니코드) vs bytes
    • Ruby: 모든 String이 encoding을 가지며 ASCII-8BIT(=BINARY) 인코딩으로 바이너리 취급
  • 압축/인코딩: 압축 알고리즘은 byte stream 위에서 동작합니다.

7. 무엇을 선택할까

상황 권장
사람이 읽고 디버깅·로깅이 중요 텍스트(JSON 등)
공개 API·이종 시스템 연동 텍스트(이식성)
대량·고빈도 트래픽, 지연·대역폭 민감 바이너리(Protobuf 등)
이미지·오디오·파일 등 본질적 바이너리 바이너리 그대로
텍스트 통로로 바이너리를 실어야 함 Base64 등으로 인코딩

기본값은 "텍스트로 시작하고, 측정된 성능 문제가 생기면 바이너리로 내려간다"가 무난합니다. 가독성을 일찍 포기할 이유는 보통 크지 않습니다.

Document History

  • 2026-06-15T14:15:19+09:00 - Written by Claude Opus 4.8 (1M context) via Claude Code
  • 2026-06-15T14:29:24+09:00 - Edited by Claude Opus 4.8 (1M context) via Claude Code
  • 2026-06-15T14:41:58+09:00 - Edited by Claude Opus 4.8 (1M context) via Claude Code
  • 2026-06-15T14:54:06+09:00 - Edited by Claude Opus 4.8 (1M context) via Claude Code
  • 2026-06-15T15:08:37+09:00 - Edited by Claude Opus 4.8 (1M context) via Claude Code
  • 2026-06-15T15:11:36+09:00 - Edited by Claude Opus 4.8 (1M context) via Claude Code
  • 2026-06-15T15:12:16+09:00 - Edited by Claude Opus 4.8 (1M context) via Claude Code
  • 2026-06-15T15:27:33+09:00 - Edited by Claude Opus 4.8 (1M context) via Claude Code
  • 2026-06-15T15:48:47+09:00 - Edited by Claude Opus 4.8 (1M context) via Claude Code
  • 2026-06-15T15:49:38+09:00 - Edited by Claude Opus 4.8 (1M context) via Claude Code
  • 2026-06-16T15:31:28+09:00 - Edited by Claude Opus 4.8 (1M context) via Claude Code: OpenZL 노트 삭제에 따른 링크 제거
🔒 Admin 로그인