사전 MIME
포맷

MIME

gabury1고친 사람 github-actions[bot]

MIME 은 글자만 나르던 전자메일에 그림과 첨부 파일을 실어 보내게 해 주는 규칙입니다. 본문을 여러 조각으로 나눕니다. 조각마다 형식 이름표를 붙입니다. 글자만 지나가는 통로를 넘도록 바이트를 글자로 바꾸는 방법도 함께 정합니다. 이 이름표 체계가 나중에 웹으로 퍼졌습니다.

쉽고 빠른 이해

MIME 은 메일 한 통에 여러 종류의 내용을 담는 규칙입니다. 본문 글과 문서 첨부 파일을 한 통에 넣습니다. 각각이 무슨 형식인지도 적어 둡니다.

이 규칙이 없으면 메일은 영어 글자만 나를 수 있습니다. 한글도 그림도 파일도 실을 방법이 없습니다.

어떻게 도나:

  1. 본문을 조각으로 나누고, 조각 사이에 약속한 구분 줄을 끼웁니다
  2. 조각마다 「이건 글」「이건 그림」 같은 형식 이름표를 붙입니다
  3. 글자로 못 나르는 바이트는 글자로 바꿔 싣습니다. 받는 쪽이 이것을 되돌립니다

메일이든 웹이든 본문이 무슨 형식인지 알려야 할 때 이 이름표를 붙입니다. 다만 바이트를 그대로 나르는 통로에서는 글자로 바꾸는 단계를 쓰지 않습니다.

대가도 있습니다. 파일을 글자로 바꾸면 크기가 3분의 1쯤 불어납니다.

상세

MIME(Multipurpose Internet Mail Extensions, 다목적 인터넷 메일 확장)은 전자메일 메시지의 본문을 적는 규칙을 넓힌 표준 묶음입니다. RFC(Request for Comments) 2045 부터 2049 까지 다섯 문서가 이 이름으로 묶입니다. RFC 는 인터넷 표준을 담는 문서 묶음입니다.

이 절은 MIME 이 원래 메일에 무엇을 더했는지 봅니다. 헤더 몇 줄과 본문을 나누는 방식은 메일 한 통으로 확인합니다.

MIME 이전의 메일

원래 인터넷 메일은 헤더 몇 줄, 빈 줄 하나, 본문으로 이루어진 글자 덩어리입니다. 헤더는 From: ... 이나 Subject: ... 처럼 보낸 사람·제목 같은 정보를 「이름: 값」 꼴로 적는 줄입니다. 이 모양 전체를 인터넷 메시지 형식이라고 부릅니다.

이 형식은 본문에 ASCII(American Standard Code for Information Interchange) 글자만 담는다고 가정했습니다. ASCII 는 영어 알파벳과 숫자, 기호를 7비트 번호로 적는 문자 표입니다.

이 가정 때문에 곤란한 일이 셋 생깁니다. 한글처럼 ASCII 밖 글자를 못 씁니다. 그림이나 PDF(Portable Document Format) 문서 같은 이진 데이터를 못 싣습니다. 본문이 한 덩어리라 글과 첨부 파일을 나눌 방법도 없습니다.

메일을 나르는 서버들이 이미 이 가정대로 돌고 있었다는 점이 중요합니다. 규칙을 새로 짜서 서버를 전부 바꿀 수는 없었습니다. 그래서 MIME 은 옛 서버가 보기에는 여전히 평범한 글자 메일이 되도록 설계됐습니다. 바뀐 것은 헤더 몇 줄과 본문을 해석하는 방법뿐입니다.

MIME 이 더한 헤더

MIME 은 메시지와 각 조각의 성격을 헤더로 알립니다. 받는 메일 프로그램은 이 헤더를 먼저 읽고 본문을 어떻게 풀지 정합니다. 자주 보는 헤더는 넷입니다.

헤더 알리는 것 예
MIME-Version 이 메시지가 MIME 규칙을 따른다 1.0
Content-Type 본문이나 조각의 형식 text/plain; charset=utf-8
Content-Transfer-Encoding 바이트를 어떤 방식으로 글자로 바꿨나 base64
Content-Disposition 본문에 펼칠지, 첨부 파일로 둘지 attachment; filename="a.pdf"

Content-Type 에 적는 text/plain 같은 이름을 미디어 타입이라고 부릅니다. 슬래시 앞이 큰 분류, 뒤가 구체 형식입니다. 메일에서 먼저 쓰였기 때문에 지금도 「MIME 타입」이라는 이름으로 많이 부릅니다. 요약에서 「형식 이름표」라 부른 것이 이 미디어 타입입니다.

전송 인코딩

메일 통로는 ASCII 글자만 안전하게 지나간다고 봐야 합니다. 그래서 그 밖의 바이트는 ASCII 글자로 바꿔서 싣습니다. 이 바꾸는 방식을 전송 인코딩이라고 부릅니다. 어떤 방식을 썼는지는 Content-Transfer-Encoding 헤더에 적습니다.

값 하는 일 어울리는 내용
7bit 바꾸지 않는다. 내용이 이미 ASCII 다 영어 글
8bit 바꾸지 않는다. ASCII 밖 바이트가 섞여 있다 ASCII 밖 바이트를 받는 통로에서만
binary 바꾸지 않는다. 줄 모양도 안 지킨 바이트다 거의 안 쓴다
quoted-printable ASCII 는 두고, 나머지 바이트만 =XX 로 적는다 영어가 대부분인 글
Base64 바이트 전체를 글자 64종으로 다시 적는다 그림 · PDF · 한글 글

앞의 셋은 「바꾸지 않았다」는 신고일 뿐입니다. 바이트를 바꾸는 것은 뒤의 둘뿐입니다.

앞의 셋이 갈리는 기준은 통로가 무엇을 견디느냐입니다. 옛 메일 통로는 글을 줄 단위로 나르고, 한 줄 길이에도 상한을 둡니다. 8bit 는 ASCII 밖 바이트를 담지만 이 줄 모양은 지킵니다. binary 는 줄 모양마저 없는 날 바이트라 옛 통로로는 나를 수 없습니다.

quoted-printable 은 읽을 수 있는 글자를 살려 둡니다. café 를 UTF-8(Unicode Transformation Format 8비트)로 적으면 é 가 C3 A9 두 바이트가 됩니다. quoted-printable 은 이 둘만 =C3=A9 로 바꾸고 나머지 글자는 손대지 않습니다.

Base64 는 바이트 세 개를 묶어 글자 네 개로 적습니다. 원래 무엇이었든 결과는 알파벳과 숫자, + / 뿐입니다. 대신 크기가 3분의 4 배, 곧 3분의 1쯤 불어납니다.

같은 바이트를 두 방식으로 바꾸면 이렇게 됩니다.

원래 글자 UTF-8 바이트 quoted-printable Base64
한 ED 95 9C =ED=95=9C 7ZWc
café 63 61 66 C3 A9 caf=C3=A9 Y2Fmw6k=

한글처럼 거의 모든 바이트가 ASCII 밖이면 quoted-printable 이 오히려 길어집니다. 바이트 하나가 글자 세 개가 되기 때문입니다. 그런 내용에는 Base64 를 씁니다.

여러 조각을 한 통에 담는 multipart

글과 첨부 파일을 한 메일에 넣으려면 본문을 조각으로 나눠야 합니다. 이때 Content-Type 에 multipart 분류를 적습니다. 그리고 조각 사이에 끼울 구분 문자열을 boundary 매개변수로 정합니다. 요약의 「구분 줄」은 이 문자열로 만든 줄입니다.

조각 안에 다시 multipart 를 넣을 수 있습니다. 그러면 메일 한 통이 나무 모양이 됩니다. 흔한 모양은 「글」과 「첨부 파일」을 나란히 두는 것입니다. 글은 다시 텍스트 판과 HTML(HyperText Markup Language) 판으로 나눕니다.

flowchart TD
    M["메일 전체 · multipart/mixed"] --> A["글 · multipart/alternative"]
    M --> F["첨부 파일 · application/pdf"]
    A --> P["텍스트 판 · text/plain"]
    A --> H["HTML 판 · text/html"]

위 그림에서 multipart/mixed 는 성격이 다른 조각을 모두 보여 주라는 뜻입니다. multipart/alternative 는 같은 내용의 여러 판 가운데 하나만 고르라는 뜻입니다.

alternative 에는 판을 놓는 순서에 약속이 있습니다. 뒤로 갈수록 원래 내용에 더 가까운, 풍부한 판을 둡니다. 받는 프로그램은 자기가 보여 줄 수 있는 판 가운데 가장 뒤의 것을 고릅니다. 그래서 텍스트 판을 앞에, HTML 판을 뒤에 둡니다.

실제 메일로 보면 이렇습니다. 보기 쉽게 그림에서 alternative 가지를 빼고, 텍스트 판 하나와 첨부 파일 하나만 남겼습니다.

MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="XyZ"

--XyZ
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: base64

7ZWc
--XyZ
Content-Type: application/pdf
Content-Disposition: attachment; filename="a.pdf"
Content-Transfer-Encoding: base64

JVBERi0xLjQK...
--XyZ--

구분 줄은 boundary 값 앞에 줄표 두 개(--)를 붙여 만듭니다. 그래서 방금 본 메일에서는 --XyZ 줄이 조각을 가릅니다. 각 조각은 자기 헤더, 빈 줄, 본문 순서로 다시 작은 메시지 모양을 갖습니다. 마지막 --XyZ-- 는 뒤에 두 줄표가 더 붙어 「조각이 여기서 끝난다」를 알립니다.

첫 조각의 본문 7ZWc 는 앞의 표에서 본 한 의 Base64 입니다. 두 번째 조각의 JVBERi0 는 모든 PDF 파일이 여는 글자 %PDF- 를 Base64 로 적은 앞머리입니다.

구분 문자열이 내용과 겹칠 때

구분자를 쓰는 형식은 늘 같은 문제를 안습니다. 조각 내용 안에 구분자와 똑같은 줄이 들어 있으면 받는 쪽이 거기서 조각을 잘라 버립니다. 흔한 해법은 이스케이프입니다. 내용 안의 겹치는 줄 앞에 표시 글자를 붙여 「이건 구분자가 아니다」라고 알리는 방법입니다.

MIME 은 이스케이프를 쓰지 않습니다. 대신 보내는 쪽이 내용에 나오지 않는 문자열을 골라 구분 문자열(boundary)로 씁니다. 메일 프로그램은 보통 긴 무작위 문자열을 만들어 겹칠 확률을 없애다시피 합니다. Base64 로 바꾼 내용에는 - 가 아예 안 나와서 겹칠 걱정이 더 줄어듭니다.

헤더에 한글 쓰기

전송 인코딩은 본문에만 붙습니다. 그런데 메일 제목이나 첨부 파일 이름 같은 헤더에도 한글이 들어갑니다. 헤더도 ASCII 만 담을 수 있으므로 따로 바꾸는 방법이 필요합니다.

이 방법을 encoded-word 라고 부르고 =?문자셋?방식?내용?= 꼴로 적습니다. 방식 칸의 B 는 Base64 입니다. Q 는 quoted-printable 처럼 대부분의 ASCII 는 두고 나머지 바이트를 =XX 로 적습니다. 헤더 안이라 공백은 _ 로 적습니다.

Subject: =?UTF-8?B?7ZWc?=

위 줄을 받은 메일 프로그램은 7ZWc 를 Base64 로 풉니다. 나온 바이트를 UTF-8 로 읽어 제목에 한 을 보여 줍니다. 받는 프로그램이 이 규칙을 모르면 제목에 =?UTF-8?B?... 가 그대로 찍혀 나옵니다. 메일 제목이 깨져 보이는 흔한 원인이 이것입니다.

메일 밖으로 나간 이름표

MIME 의 형식 이름표는 메일 밖에서 더 널리 쓰이게 됐습니다. HTTP(HyperText Transfer Protocol, 하이퍼텍스트 전송 프로토콜)가 Content-Type 헤더와 미디어 타입을 빌려 왔습니다. 웹 서버가 응답에 text/html 을 붙이면 브라우저가 그 본문을 HTML 문서로 읽습니다.

파일 올리기 폼도 MIME 에서 빌렸습니다. 폼 입력값과 파일을 한 요청에 싣는 multipart/form-data 는 앞에서 본 multipart 와 같은 방식으로 boundary 줄을 끼워 조각을 나눕니다.

다만 HTTP 는 MIME 을 통째로 가져가지 않았습니다. HTTP 연결은 바이트를 바꾸지 않고 나를 수 있어서 전송 인코딩이 필요 없습니다. 그래서 웹에서는 Content-Transfer-Encoding 을 거의 보지 않습니다.

치르는 값

MIME 은 옛 메일 통로를 그대로 쓰는 대신 몇 가지를 내줍니다. 첨부 파일은 Base64 로 바뀌어 3분의 1쯤 커집니다. 원래 파일이 메일 서버의 크기 제한보다 작아도 보내지 못하는 일이 생기는 까닭입니다.

형식 이름표는 보내는 쪽이 적은 말일 뿐입니다. 이름표와 실제 내용이 다를 수 있습니다. 받는 쪽은 그 이름표를 믿고 풀다가 틀릴 수 있습니다. 첨부 파일로 악성 코드를 보내는 공격이 이 틈을 노리기도 합니다.

관련 항목

MIME 이 얹히는 메일 규칙과 프로토콜

전자메일 · 인터넷 메시지 형식 · SMTP · IMAP · POP3 · 메시지 · 헤더

MIME 을 정의하는 표준 문서

RFC · RFC 2045 · RFC 2046 · RFC 2047 · RFC 2049 · RFC 5322 · RFC 6838 · IANA

MIME 메시지를 이루는 헤더와 구성 요소

MIME-Version · Content-Type · Content-Transfer-Encoding · Content-Disposition · 미디어 타입 · boundary · encoded-word · 문자셋

MIME 이 바이트를 글자로 바꾸는 인코딩

Base64 · quoted-printable · 7bit · 8bit · ASCII · UTF-8 · 문자 인코딩 · 이진 데이터

MIME 본문을 나누는 multipart 하위 종류

multipart/mixed · multipart/alternative · multipart/related · multipart/form-data · 구분자

MIME 이름표를 빌려 쓰는 기술

HTTP · HTML · S/MIME · 콘텐츠 협상 · MIME 스니핑 · 직렬화

MIME 메일에서 자주 나는 문제

문자 깨짐 · 첨부 파일 크기 제한 · 악성 첨부 파일 · 피싱

다른 이름: Multipurpose Internet Mail Extensions · 다목적 인터넷 메일 확장