HTTP/Guides/Media types

김동현·2026년 3월 22일

안녕하세요! 오늘도 프론트엔드 정복을 위해 달려오신 여러분, 환영합니다! 🎉

이번 시간에는 프론트엔드 개발자라면 백엔드와 소통할 때, 그리고 브라우저가 파일을 제대로 읽게 만들 때 무조건 알아야 하는 핵심 개념인 MIME 타입(Media types)에 대해 알아보겠습니다. 원본 문서의 내용을 하나도 빠짐없이 꼼꼼하게 번역하고, 제가 실무를 하면서 겪었던 팁과 부연 설명도 가득 담아두었으니 천천히 읽어보세요!


미디어 타입 (MIME types)

미디어 타입(Media type)은 (예전에는 MIME 타입 또는 Multipurpose Internet Mail Extensions라고 불렸어요) 문서, 파일, 또는 바이트 모음의 성격과 형식을 나타내는 표준 식별자입니다.
MIME 타입은 IETF의 RFC 6838 문서에서 정의되고 표준화되어 있어요.

공식적인 모든 MIME 타입은 인터넷 할당 번호 관리기관(IANA)에서 책임지고 관리하고 있으며, 가장 최신화된 전체 목록은 IANA의 미디어 타입(Media Types) 페이지에서 확인하실 수 있습니다.

⚠️ 주의사항 (매우 중요!)
브라우저는 URL을 어떻게 처리할지 결정할 때 파일 확장자(.jpg, .css 등)가 아니라 MIME 타입을 사용합니다!
따라서 웹 서버가 응답(Response)을 보낼 때 Content-Type 헤더에 올바른 MIME 타입을 담아서 보내는 것이 정말 중요해요. 만약 이 설정이 잘못되어 있으면 브라우저가 파일의 내용을 오해해서 사이트가 제대로 동작하지 않거나, 사용자가 다운로드한 파일이 엉뚱하게 처리될 수 있습니다.

💡 강사의 실무 TIP!
"어? 저는 분명히 style.css를 만들어서 연결했는데 화면에 스타일이 안 먹혀요!"
초보 개발자분들이 정말 많이 겪는 문제입니다. 개발자 도구의 네트워크 탭을 열어보면, 서버가 .css 파일을 주긴 했는데 Content-Typetext/plain으로 잘못 보내고 있는 경우가 많아요. 브라우저는 확장자가 css라도 서버가 plain(일반 텍스트)이라고 하면 텍스트로 취급해서 스타일을 적용하지 않습니다. 항상 파일 확장자가 아닌 응답 헤더의 Content-Type이 진짜 정체라는 걸 명심하세요!


MIME 타입의 구조 (Structure of a MIME type)

MIME 타입은 일반적으로 슬래시(/)로 구분된 타입(type)서브타입(subtype), 이렇게 두 부분으로 구성되며 중간에 공백은 들어가지 않습니다:

type/subtype
  • 타입 (type): 데이터가 속하는 일반적인 카테고리를 나타냅니다. 예를 들면 videotext 같은 것들이죠.
  • 서브타입 (subtype): 해당 타입 내에서 이 데이터가 정확히 어떤 종류인지를 명시합니다. 예를 들어 MIME 타입이 text라면, 서브타입은 일반 텍스트인 plain, HTML 소스 코드인 html, 또는 iCalendar 파일인 calendar가 될 수 있습니다.

각각의 타입은 자신만의 고유한 서브타입 세트를 가지고 있습니다. MIME 타입은 반드시 '타입'과 '서브타입' 두 가지를 모두 가져야 하며, 둘 중 하나만 단독으로 쓸 수는 없어요.

여기에 추가적인 세부 정보를 제공하기 위해 선택적으로 매개변수(parameter)를 덧붙일 수도 있습니다:

type/subtype;parameter=value

예를 들어, 메인 타입이 text인 MIME 타입의 경우, 데이터 내 문자들이 어떤 문자셋으로 인코딩되었는지 알려주기 위해 charset 매개변수를 추가할 수 있습니다.
만약 charset이 명시되지 않으면, 사용자 에이전트(User Agent)(브라우저)의 설정에 의해 덮어씌워지지 않는 이상 ASCII (US-ASCII)가 기본값이 됩니다.
따라서 이 파일이 UTF-8 텍스트 파일임을 명확히 하려면 text/plain;charset=UTF-8이라고 적어주면 됩니다.

참고로 MIME 타입은 대소문자를 구분하지 않지만, 전통적으로는 모두 소문자로 작성합니다. 단, 매개변수의 값(value)은 대소문자를 구분할 수 있습니다.


타입 종류 (Types)

타입은 크게 개별 타입(discrete)멀티파트 타입(multipart) 두 가지 클래스로 나뉩니다.
개별 타입은 텍스트 파일 1개, 음악 파일 1개, 비디오 1개처럼 단일 파일이나 단일 매체를 나타냅니다.
반면 멀티파트 타입은 각각 고유한 MIME 타입을 가진 여러 개의 컴포넌트들로 이루어진 문서를 나타내거나, 한 번의 전송(트랜잭션) 안에 여러 파일을 하나로 캡슐화해서 보낼 때 사용합니다. (이메일에 여러 개의 파일을 첨부할 때 바로 이 멀티파트 타입이 쓰이죠!)

개별 타입 (Discrete types)

현재 IANA에 등록된 개별 타입들은 다음과 같습니다:

  • application
    다른 명시적인 타입에 속하지 않는 모든 종류의 바이너리 데이터입니다. 어떤 방식으로든 실행되거나 해석될 데이터, 또는 특정 애플리케이션이나 카테고리가 있어야만 사용할 수 있는 바이너리 데이터를 뜻해요.
    일반적인 범용 바이너리 데이터(또는 진짜 정체를 모르는 바이너리 데이터)는 application/octet-stream을 사용합니다. 자주 쓰이는 예시로는 application/pdf, application/pkcs8, application/zip 등이 있습니다.

  • audio
    오디오나 음악 데이터입니다. 예시로는 audio/mpeg, audio/vorbis 등이 있습니다.

  • example
    MIME 타입을 어떻게 사용하는지 보여주는 예시나 플레이스홀더(자리 표시자) 용도로 예약된 타입입니다. 실제 서비스 환경이나 코드에서는 절대 사용하면 안 됩니다.
    example은 서브타입으로도 쓸 수 있어요. 예를 들어 웹 오디오 작업 예제 코드에서 audio/example이라고 되어 있다면, "여기에 진짜 오디오 MIME 타입을 넣으세요"라는 뜻입니다.

  • font
    폰트(글꼴) 데이터입니다. 프론트엔드에서 자주 볼 수 있는 예로 font/woff, font/ttf, font/otf 등이 있습니다.

  • image
    비트맵, 벡터 이미지는 물론이고 APNG나 애니메이션 GIF 같은 애니메이션 이미지 포맷까지 모두 포함하는 그래픽 데이터입니다. 일반적인 예로 image/jpeg, image/png, image/svg+xml 등이 있어요.

  • model
    3D 객체나 씬(scene)을 위한 모델 데이터입니다. 예시로는 model/3mf, model/vrml 등이 있습니다.

  • text
    사람이 읽을 수 있는(human-readable) 콘텐츠, 소스 코드, 또는 CSV(comma-separated value) 포맷 같은 텍스트 형태의 데이터입니다.
    예시로는 text/plain, text/csv, text/html 등이 있습니다.

  • video
    MP4 영화 파일(video/mp4) 같은 비디오 데이터입니다.

서브타입을 명확히 지정할 수 없는 텍스트 문서는 기본적으로 text/plain을 사용해야 합니다.
마찬가지로 서브타입을 알 수 없는 바이너리(이진) 문서는 application/octet-stream을 사용해야 합니다.

멀티파트 타입 (Multipart types)

멀티파트(Multipart) 타입은 문서가 여러 조각으로 나뉘어 있고, 그 조각들이 각기 다른 MIME 타입을 가질 수 있는 문서 카테고리를 나타냅니다. 특히 이메일 환경에서 하나의 전송 안에 여러 개의 독립적인 파일들이 포함되어 있을 때 주로 사용돼요. 즉, 복합 문서(composite document)를 나타내는 것이죠.

하지만 웹(HTTP)에서는 멀티파트 문서를 특별하게 대우하지 않습니다. (단축 브라우저가 해당 문서를 띄울 줄 모른다면 보통 "다른 이름으로 저장" 창을 띄우게 됩니다.)
다만, 예외적으로 다음 두 가지는 HTTP에서 매우 특별하게 쓰입니다.
1. HTML 폼(Forms)에서 POST 메서드를 사용할 때 쓰이는 multipart/form-data
2. 문서의 일부분만 전송하는 206 Partial Content 응답 상태 코드와 함께 쓰이는 multipart/byteranges

멀티파트 타입에는 두 가지가 있습니다:

  • message
    다른 메시지를 캡슐화하는 메시지입니다. 이메일을 전달(forward)할 때 원본 메시지를 포함하거나, 엄청나게 큰 메시지를 여러 개의 작은 메시지로 쪼개서 보낼 때 사용됩니다.

  • multipart
    각각 다른 MIME 타입을 가질 수 있는 여러 개의 컴포넌트로 구성된 데이터입니다.
    가장 대표적인 예가 FormData API를 이용해 데이터를 보낼 때 쓰이는 multipart/form-data이고, 미디어 스트리밍 시 범위를 지정해 가져올 때 쓰는 multipart/byteranges가 있습니다.


웹 개발자를 위한 중요한 MIME 타입들 (Important MIME types for Web developers)

프론트엔드 개발자라면 아래에 나오는 MIME 타입들은 눈감고도 알 정도로 친숙해지셔야 합니다!

application/octet-stream

바이너리 파일의 가장 기본적인 디폴트 값입니다. 브라우저는 이 타입을 '정체를 알 수 없는 바이너리 파일'로 인식하기 때문에, 이 파일을 화면에 보여주려 하거나 실행하지 않습니다. 대신 사용자에게 무조건 다운로드("다른 이름으로 저장") 창을 띄워버립니다. (마치 Content-Disposition 헤더가 attachment로 설정된 것처럼 행동하죠.)

text/plain

텍스트 파일의 디폴트 값입니다. 브라우저는 이게 "정확히 뭔지 모르는 텍스트 파일"이라는 건 알지만, 일단 텍스트니까 브라우저 화면에 그대로 텍스트를 뿌려줍니다.

📝 참고:
text/plain이 "모든 텍스트 데이터"를 의미하는 만능키는 아닙니다. 브라우저가 특정한 종류의 텍스트(예: CSS)를 기대하고 있는데 서버가 text/plain을 주면 브라우저는 무시해 버립니다.
예를 들어 HTML의 <link> 태그로 CSS 파일을 불러왔는데 서버가 text/plain으로 주면, 브라우저는 유효한 CSS 파일로 인정하지 않습니다. 반드시 text/css를 사용해야 합니다.

text/css

웹 페이지의 스타일을 담당하는 CSS 파일은 반드시 text/css로 전송되어야 합니다.
서버 설정이 덜 되어 있어서 .css 확장자를 인식하지 못하고 text/plain이나 application/octet-stream으로 보내버리면, 브라우저는 이를 무시하고 웹 페이지는 스타일이 싹 빠진 앙상한 뼈대만 보이게 될 거예요.

text/html

모든 HTML 콘텐츠는 이 타입으로 제공되어야 합니다. 과거에 쓰이던 XHTML용 대체 MIME 타입(예: application/xhtml+xml)은 요즘은 거의 쓸모가 없습니다.

📝 참고:
XML의 엄격한 파싱 규칙, <![CDATA[…]]> 섹션, 또는 HTML/SVG/MathML 네임스페이스가 아닌 요소를 사용해야만 한다면 application/xml 이나 application/xhtml+xml을 사용하세요.

text/javascript

자바스크립트 파일은 항상 text/javascript라는 MIME 타입으로 전송되어야 합니다.
역사적인 이유로 브라우저들이 아래에 나열된 수많은 레거시(과거) 자바스크립트 타입들을 지원하긴 하지만, text/javascript가 아닌 다른 타입으로 보냈을 때 항상 스크립트가 정상적으로 실행될 거라 장담하면 안 됩니다.

또한, HTML 파일 내에서 자바스크립트를 부를 때 사용하는 <script> 요소의 type 속성은 오직 자바스크립트 MIME 타입의 정수만을 담을 수 있습니다. 즉, text/javascriptmodule (ES 모듈용), importmap만 허용됩니다.
만약 <script type="text/javascript;charset=utf-8">처럼 파라미터를 섞어버리면 브라우저는 이를 알 수 없는 값으로 취급해서, 코드를 실행하지 않고 그냥 데이터 블록으로만 간주합니다.

사실 요즘은 <script> 태그에 type="text/javascript"를 굳이 적어줄 필요가 없습니다. 이게 기본값이거든요! 그냥 과감하게 생략하세요. (단, 서버에서 응답 헤더로 보내는 Content-Type에는 평소처럼 charset을 지정해도 문제없습니다.)

레거시(과거의) 자바스크립트 MIME 타입 (Legacy JavaScript MIME types)

MIME Sniffing 표준에 따르면, 역사적인 호환성을 위해 아래의 타입들도 자바스크립트로 인정해 주긴 합니다. (하지만 새 프로젝트에서는 쓰지 마세요!)

  • application/javascript (비권장)
  • application/ecmascript (비권장)
  • application/x-ecmascript (비표준)
  • application/x-javascript (비표준)
  • text/ecmascript (비권장)
  • text/javascript1.0 ~ 1.5 (비표준)
  • text/jscript (비표준)
  • text/livescript (비표준)
  • text/x-ecmascript (비표준)
  • text/x-javascript (비표준)

💡 강사의 실무 TIP!
과거에는 application/javascript를 쓰라는 문서도 많았지만, 이제 표준은 명확하게 text/javascript 하나로 통일되었습니다! 헷갈리지 마시고 이것만 쓰시면 됩니다.

application/json

JSON (JavaScript Object Notation)은 자바스크립트 객체 문법을 기반으로 구조화된 데이터를 표현하기 위한 텍스트 기반의 표준 포맷입니다. 프론트엔드와 백엔드가 API로 데이터를 주고받을 때 거의 99% 사용된다고 보시면 됩니다!

이미지 타입 (Image types)

image MIME 타입은 말 그대로 이미지 데이터를 의미합니다.
웹 페이지에서 사용하기에 '안전하고 보편적인' 이미지 타입들은 다음과 같습니다:

오디오 및 비디오 타입 (Audio and video types)

이미지와 마찬가지로 HTML은 브라우저에게 특정 오디오/비디오 코덱을 강제하지 않습니다. 따라서 <audio><video> 태그를 쓸 때는 내 타겟 사용자들이 어떤 브라우저를 쓸지 고려해서 형식을 골라야 합니다. 보통 오디오/비디오의 MIME 타입은 파일의 포맷(컨테이너 포맷)을 지정합니다. (자세한 건 코덱 관련 가이드를 참고하세요.)

multipart/form-data

브라우저에서 서버로 작성된 HTML 폼(Form) 데이터를 전송할 때(특히 파일 업로드가 있을 때) 사용합니다.

멀티파트 문서로서, 이것은 바운더리(boundary)라고 불리는 구분선(보통 --로 시작하는 긴 문자열)으로 나뉘어진 여러 개의 부분(part)들로 구성됩니다.
각각의 부분은 자기 자신만의 독립된 HTTP 헤더를 가지며, 파일 업로드 필드의 경우 각각 Content-DispositionContent-Type을 명시합니다.

Content-Type: multipart/form-data; boundary=boundaryString
(기타 다른 헤더들...)

--boundaryString
Content-Disposition: form-data; name="myFile"; filename="img.jpg"
Content-Type: image/jpeg

(실제 이미지 바이너리 데이터)
--boundaryString
Content-Disposition: form-data; name="myField"

(텍스트 필드의 데이터)
--boundaryString
(더 많은 서브파트들...)
--boundaryString--

예를 들어, 아래와 같은 HTML <form> 폼이 있다고 가정해볼까요?

<form
  action="http://localhost:8000/"
  method="post"
  enctype="multipart/form-data">
  <label>Name: <input name="myTextField" value="Test" /></label>
  <label><input type="checkbox" name="myCheckBox" /> Check</label>
  <label>
    Upload file: <input type="file" name="myFile" value="test.txt" />
  </label>
  <button>Send the file</button>
</form>

사용자가 'Send the file' 버튼을 누르면, 브라우저는 다음과 같은 형태의 메시지를 서버로 날립니다:

POST / HTTP/1.1
Host: localhost:8000
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:50.0) Gecko/20100101 Firefox/50.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
Upgrade-Insecure-Requests: 1
Content-Type: multipart/form-data; boundary=---------------------------8721656041911415653955004498
Content-Length: 465

-----------------------------8721656041911415653955004498
Content-Disposition: form-data; name="myTextField"

Test
-----------------------------8721656041911415653955004498
Content-Disposition: form-data; name="myCheckBox"

on
-----------------------------8721656041911415653955004498
Content-Disposition: form-data; name="myFile"; filename="test.txt"
Content-Type: text/plain

Simple file.
-----------------------------8721656041911415653955004498--

💡 강사의 실무 TIP!
리액트(React)나 뷰(Vue) 같은 프론트엔드 환경에서 fetchaxios 라이브러리로 파일을 업로드하실 때 주의할 점!
자바스크립트의 FormData 객체를 만들어서 통신에 넘겨줄 때, 직접 헤더에 Content-Type: 'multipart/form-data'를 수동으로 적어주면 절대 안 됩니다! > 위 예시처럼 브라우저가 전송할 때마다 무작위로 생성되는 boundary=-----어쩌구저쩌구 값을 자동으로 생성해서 헤더에 붙여줘야 하는데, 여러분이 헤더를 강제로 덮어씌우면 이 바운더리가 누락되어 서버가 데이터를 파싱하지 못하고 에러를 뿜어냅니다.
그냥 Body에 FormData 객체만 넘겨주면, 브라우저가 알아서 완벽한 Content-Type 헤더를 만들어 준다는 점 꼭 기억하세요!

multipart/byteranges

multipart/byteranges MIME 타입은 브라우저에 '부분 응답(partial responses)'을 보낼 때 사용됩니다.
서버가 전체 데이터가 아닌 쪼개진 일부만 보냈다는 206 Partial Content 상태 코드를 보낼 때, 이 문서가 여러 조각으로 이루어져 있음을 이 MIME 타입으로 알립니다. 다른 멀티파트 타입처럼 헤더에 boundary를 사용해서 조각들을 구분 짓습니다.

HTTP/1.1 206 Partial Content
Accept-Ranges: bytes
Content-Type: multipart/byteranges; boundary=3d6b6a416f9b5
Content-Length: 385

--3d6b6a416f9b5
Content-Type: text/html
Content-Range: bytes 100-200/1270

eta http-equiv="Content-type" content="text/html; charset=utf-8" />
    <meta name="viewport" content
--3d6b6a416f9b5
Content-Type: text/html
Content-Range: bytes 300-400/1270

-color: #f0f0f2;
        margin: 0;
        padding: 0;
        font-family: "Open Sans", "Helvetica
--3d6b6a416f9b5--

올바른 MIME 타입을 설정하는 것의 중요성 (Importance of setting the correct MIME type)

일부 서버는 MIME 타입을 확인해서 파일 병합, 압축, 캐싱 같은 최적화 작업을 수행합니다. 올바르게 설정해야 이런 최적화 혜택을 온전히 누릴 수 있어요.

대부분의 웹 서버는 확장자가 없어 자신도 정체를 모르는 리소스에 대해 방어적으로 application/octet-stream MIME 타입을 보냅니다.
브라우저는 보안상의 이유로 이런 알 수 없는 리소스에 대해 사용자가 강제로 연결 프로그램("워드로 열기" 등)을 설정하지 못하도록 막아버립니다. 사용자는 울며 겨자 먹기로 무조건 파일을 디스크에 저장(다운로드)한 뒤에야 파일을 열어볼 수 있게 되죠.

자주 발생하는 잘못된 서버 설정 예시들은 다음과 같습니다:

  • RAR 압축 파일: 제일 좋은 건 원래 파일의 진짜 타입을 알려주는 거지만, RAR 안에는 온갖 종류의 파일이 섞여 있을 수 있어서 불가능하죠. 이럴 땐 서버가 application/x-rar-compressed를 보내도록 설정해야 합니다.
  • 오디오 및 비디오: <video><audio> 요소는 MIME 타입이 정확하게 일치하는 리소스만 재생합니다. 소리가 안 나거나 영상이 안 뜬다면 이 부분을 확인해 보세요.
  • 독점적인(Proprietary) 파일 타입: application/vnd.mspowerpoint 같은 특정 타입을 보내주면, 사용자가 파일을 클릭했을 때 자동으로 파워포인트 같은 특정 프로그램으로 열리게끔 도와줍니다.

MIME 스니핑 (MIME sniffing)

만약 서버가 MIME 타입을 안 보내줬거나, 브라우저가 판단하기에 서버가 보낸 MIME 타입이 엉터리 같을 때, 브라우저는 똑똑하게(때로는 오지랖 넓게) 리소스의 바이트를 훑어보고 진짜 MIME 타입이 뭔지 유추하는 작업을 합니다. 이를 MIME 스니핑(MIME sniffing)이라고 부릅니다.

문제는 브라우저마다 이 스니핑을 수행하는 방식과 상황이 다르다는 거예요. (예를 들어 사파리는 서버가 보낸 MIME 타입이 이상하면 URL의 확장자를 보고 판단하기도 합니다.)
이 기능은 보안상 큰 취약점이 될 수 있습니다. 텍스트 파일인 척 위장한 악성 실행 코드를 브라우저가 스니핑으로 파악하고 멋대로 실행해버릴 수도 있으니까요.
그래서 보안에 신경 쓰는 현대의 서버 개발자들은 응답 헤더에 X-Content-Type-Options: nosniff를 포함시켜서 "내가 보낸 MIME 타입을 의심하지 말고 무조건 그대로 믿어! 스니핑 절대 금지!"라고 브라우저에게 강력하게 명령합니다.

💡 강사의 실무 TIP!
프론트엔드 보안 측면에서 정말 중요한 개념입니다! 여러분이 나중에 Node.js나 Next.js 등으로 서버를 다루거나, Nginx 설정을 할 때 X-Content-Type-Options: nosniff 설정은 보안 표준 관례(Best Practice)로 여겨지니 꼭 기억해 두세요!


문서 유형을 전달하는 다른 방법들 (Other methods of conveying document type)

사실 컴퓨터 세계에서 파일의 정체를 알리는 방법이 MIME 타입만 있는 건 아닙니다:

  • 파일 이름의 확장자(Suffixes/Extensions): 특히 윈도우(Windows) 운영체제에서 맹신하는 방식입니다. 하지만 Linux나 macOS 같은 체제에서는 확장자를 그렇게 중요하게 생각하지 않고, 무엇보다 확장자는 사용자가 맘대로 바꿀 수 있어서 100% 신뢰할 수 없습니다.
  • 매직 넘버 (Magic numbers): 파일 안의 실제 바이트 구조(첫 부분)를 까보고 정체를 추론하는 방법입니다.
    예를 들어, GIF 파일은 항상 16진수 값 47 49 46 38 39 (GIF89)로 시작하고, PNG 파일은 89 50 4E 47 (.PNG)로 시작합니다. 하지만 모든 파일 포맷이 이런 고유한 매직 넘버를 가지는 건 아니기 때문에 이 역시 만능은 아닙니다.

더 알아보기 (See also)

아래 링크들은 원본 MDN 문서들입니다. 더 깊은 학습을 원하신다면 방문해 보세요!


이 페이지는 MDN 기여자들에 의해 2025년 12월 9일에 마지막으로 수정되었습니다.

profile
프론트에_가까운_풀스택_개발자

0개의 댓글