파일 유형을 실제로 결정하는 것은 무엇인가

발행: (2025년 12월 13일 오후 03:13 GMT+9)
5 min read
원문: Dev.to

Source: Dev.to

Overview

모든 파일 형식은 사양을 가지고 있습니다—그 파일 안의 바이트가 어떻게 조직되는지를 정의한 합의된 구조입니다. 인터넷 프로토콜에 대한 표준이 있듯이, 파일 유형에 대한 표준도 존재합니다. 애플리케이션이 PDF를 열거나 PNG를 파싱할 때, 해당 형식이 미리 정의한 규칙에 따라 바이트를 읽습니다.

우리는 보통 파일 확장자(.zip, .txt, .jpg)로 파일을 식별합니다. 확장자는 인간과 운영 체제에 대한 힌트일 뿐이며, 진실의 근원이 아닙니다. photo.jpgphoto.png로 이름만 바꾸어도 이미지가 변환되지 않습니다.

파일 유형을 실제로 결정하는 것은 매직 넘버를 통해 이루어집니다.

Magic Numbers

매직 넘버는 파일의 시작 부분이나 특정 오프셋에 위치한 바이트 시퀀스로, 파일 형식이나 유형을 고유하게 식별하는 서명 역할을 합니다.

각 파일 형식은 합의된 매직 넘버를 가지고 있으며, 애플리케이션은 파일 확장자와 무관하게 파일 형식을 판단해야 할 때 이를 확인합니다.

  • PNG: 89 50 4E 47
  • ZIP: 50 4B 03 04
  • BMP: 42 4D (ASCII 문자 “BM”)

아래는 .bmp 확장자를 가진 파일을 읽고 매직 넘버를 확인하여 실제로 비트맵 이미지인지 검증하는 Go 예제입니다:

package main

import (
    "bytes"
    "fmt"
    "io"
    "log"
    "os"
)

func main() {
    f, err := os.Open("sample.bmp")
    if err != nil {
        log.Fatal(err)
    }
    defer f.Close()

    // Read the first 2 bytes (the magic number)
    header := make([]byte, 2)
    if _, err := io.ReadFull(f, header); err != nil {
        log.Fatal(err)
    }

    // BMP signature is 0x42, 0x4D
    bmpSig := []byte{0x42, 0x4D}

    if bytes.Equal(header, bmpSig) {
        fmt.Println("Valid BMP detected")
    } else {
        fmt.Println("Invalid file format")
    }
}

Unix file 명령은 이러한 서명을 사용해 확장자와 관계없이 파일을 식별합니다. 서명 뒤에는 대부분의 형식이 콘텐츠를 설명하는 메타데이터(이미지의 차원, 오디오의 샘플 레이트, 문서의 저자 정보 등)를 포함합니다.

Categories of File Structures

파일 형식은 일반적으로 몇 가지 구조적 카테고리로 나뉩니다:

  • 엄격한 구조를 가진 바이너리 형식 (PNG, JPEG, MP3): 사양에 따라 각 바이트 위치가 특정 의미를 가집니다. 프로그램은 정확한 오프셋을 읽어 파싱합니다.
  • 텍스트 기반 구조화 형식 (JSON, XML, HTML, CSV): 사람이 읽을 수 있는 텍스트이며 문법 규칙을 따릅니다. 디버깅이 쉽지만 파일 크기가 커지는 경향이 있습니다.
  • 컨테이너 형식 (ZIP, MP4, PDF): 파일 안에 파일 시스템처럼 동작하여 여러 임베디드 파일이나 스트림을 포함합니다. 예를 들어 MP4는 별도의 비디오, 오디오, 자막 트랙을 포함할 수 있습니다. DOCX는 실제로 XML 파일들을 담은 ZIP 파일입니다.

바이트를 어떻게 탐색하고 사양을 참고하는지를 이해하면 많은 파일 유형에 대한 파서를 직접 작성할 수 있습니다. 다음 글에서는 이미지 바이트를 직접 수정하여 이미지를 그레이스케일로 변환하는 간단한 스크립트를 시연할 예정입니다.

Back to Blog

관련 글

더 보기 »