MIME 규격

 . MIME

MIME(Multipurpose Internet Message Extension)은 말 그대로 Internet Message를 통해서 여러 가지 내용을 보낼 수 있도록 확장한 규격입니다.

한글같은 2바이트 Non-ASCII 문자가 여러 메일 서버를 무사히 통과하기 위해서는 7비트 ASCII 문자로 일단 변환되어야 합니다. 물론, 요즘의 대부분의 메일 시스템은 보통 8비트의 Non-ASCII 문자들도 손상시키지 않고 통과시키만 (8bit clean이라 하죠...), 그렇지 않은 경우도 아직 꽤 많습니다. 때문에, 그냥 8비트인 상태로 보내면, 메시지가 올바로 간다고 보장할 수 없게 됩니다. 게다가 단순한 텍스트 뿐만이 아니라, 요즘에는 바이너리 파일도 메일에 첨부하는 경우가 많죠?

그런 것들을 위해서 MIME에서는 Message Body에 있는 내용의 형태, Encoding 방식, 각 부분의 연관 관계 등을 지정할 수 있는 규약을 정하고 있습니다.



2. MIME Header

MIME 형태를 지정하기 위해서 RFC822에서 정의된 Header Field 외에 몇 가지 Header Field가 새로 정의되었습니다. 이 Header들은 message의 Top Level Header 혹은 Message Body Part의 Header에 들어갑니다.

MIME-Version Header Field
Content-Type Header Field
Content-Transfer-Encoding Header Field
Content-Disposition Header Field
Content-ID Header Field
Content-Description Header Field


MIME-Version Header Field는 Message의 주 Header에 있게 됩니다.
Content-Type, Content-Transfer-Encoding Header Field는 Top Level Header에 있을 수도 있고, Body Part의 Header에 있을 수도 있습니다.
Content-Disposition Header Field는 Top Level Header에 쓰일 수도 있지만, 대부분의 경우에는 Body Part의 Header에 있게 됩니다.


1. MIME-Version 헤더 필드

MIME 규약에 따르는 Message는 이 Header를 반드시 가지고 있어야만 합니다. 필수라고 RFC에서는 강조하고 있지만, 이 Header FIeld를 가지고 처리할 수 있는 건 아무 것도 없습니다. 그래서 실제로는 이 Header Field가 빠진 메시지도 상당히 많습니다.

MIME-Version: 1.0

지금 현재 MIME 버전은 현재까지는 무조건 1.0 입니다. 이 헤더를 가지고 있으면, 이후의 모든 내용은 MIME의 규격을 준수한다는 것을 의미하게 됩니다. 물론 실제로 보장되지는 않습니다.
EBNF Form으로 구성하면 아래와 같이 됩니다.
version := "MIME-Version" ":" 1Digit "." 1Digit

아래의 세 가지 예 모두 동일하며, 문제 없는 형식입니다.
MIME-Version: 1.0 (prodeced by MetaSend Vx.x)
MIME-Version: (prodeced by MetaSend Vx.x) 1.0
MIME-Version: 1.(prodeced by MetaSend Vx.x)0



2. Content-Type 헤더 필드

메시지 본문 혹은 본문의 각 부분의 내용의 종류를 지정하는 헤더 필드입니다. 보통 MIME Type이라고 부르는 것들은 바로 이걸 말하죠. 이 헤더 필드의 문법은 아래와 같습니다.
content := "Content-Type" ":" type "/" subtype *(";" parameter)

"Content-Type:" 이라는 헤더 이름 다음에, "타입/서브타입"과 같은 형태로 지정합니다.
그리고 여기에 parameter를 붙이고 싶으면 세미콜론으로 구분하면서 계속 적어나가면 됩니다.

타입은 크게 두 가지로 나뉩니다.
첫번째는 Discrete-Type인데, 이 것은 그 자체로 어떤 의미를 가지고 있는 Content-Type이 됩니다. 예를 들어, Text라든지, Image 같은 것들이 여기에 해당됩니다.
각각의 Type은 여러 가지 Subtype을 가질 수 있습니다. 예를 들어서, Text의 경우에는, 일반 ASCII 텍스트를 나타내는, text/plain, text/html 등의 Subtype을 가집니다.
Discrete-Type에 해당되는 것들은 아래와 같습니다.
"text" / "image" / "audio" / "video" / "application" / extension-token

두번째는 Composite-Type입니다. 이것은 Discrete-Type 혹은 Composite-Type의 개체 여러 개가 복합되어 만들어진 타입입니다. 이런... 재귀적인 설명이 되어 버렸군요. Composite-Type은 일단은 두 가지 입니다. 그 외에 확장된 타입을 둘 수 있다고 되어 있습니다. 이 중에서 MIME 규약이 점점 커지면서 다양한 서브 타입이 생겨나고 있는 것은 multipart 타입입니다.
"message" / "multipart"

Content-Type 헤더 필드가 없는 메시지도 많습니다. 이런 경우에는 어떻게 처리해야 할까요?
이 헤더가 없는 메시지는 MIME 규약이 생기기 전에 만들어진 메시지로 취급하면 됩니다. 즉, 단순한 ASCII 텍스트로만 이루어진 메시지로 처리하는 거죠...
아래가 Content-Type 헤더 필드가 없는 경우의 기본 Content-Type이 됩니다.
Content-Type: text/plain; charset=us-ascii

각 Content-Type에 대한 좀 더 자세한 내용은 다음에 설명하도록 하겠습니다.



3. Content-Transfer-Encoding 헤더 필드

이 헤더 필드는 메시지의 본문이나 본문의 각 부분이 어떤 방식으로 Encoding되었는지를 나타냅니다. 이 헤더 필드의 문법은 아래와 같습니다.
encoding := "Content-Transfer-Encoding" ":" mechanism

mechanism에는 다섯 가지가 있습니다.
"7bit" / "8bit" / "binary" / "quoted-printable" / "base64"

7bit, 8bit, binary를 먼저 보자면... 이 경우는 Encoding을 하지 않았다는 얘깁니다.

   7bit는 원래 ASCII 문자만 있다는 것을 의미하고,
   8bit는 8비트 문자들도 그래도 들어 있다는 것을 의미하고,
   binary는 8bit와 같은데, 한 줄의 길이의 제한이 없다는 것을 의미합니다. (안 쓰인다고 보면 됩니다).

물론, 한글을 7비트로 Encoding한 경우는 물론 Decoding이 필요합니다.
이 방식은 사라지고 있고, 요즘에는 한글은 base64로 Encoding하는 경우가 절대 다수이니까요.

그 다음에 특정한 알고리즘을 필요로 하는 두 개의 Encoding 방식이 있습니다.
Quoted-Printable하고 base64 Encoding이죠.

   Base64 Encoding은 간단히 말해서 3바이트의 데이타를 4바이트에 저장하는 겁니다. 24비트를 6비트씩 4개로 나누어서 따로 저장하고 각 바이트의 위쪽 두 비트를 0으로 하니까 무조건 ASCII 문자라는게 보장이 되는 거죠.
따라서 8비트 중에서 6비트만 사용하므로, 인코딩 결과로 나온 메시지는 모두 64가지의 Octet만을 가질 수 있게 됩니다. 그래서 Base64라고 부릅니다.
이 64개의 Octet는 ASCII와 같은 코드셋에도 독립적으로 설계되었습니다.
이 Encoding 방식의 장점이라고 하면, 원래의 데이타가 Encoding 후에 크기가 얼마가 될지 거의 정확하게 예측할 수 있다는 거죠. 약 33%정도가 늘어나죠. 또 다른 인코딩 방식에 비해 공간을 덜 차지하기 때문에, 효율적이라고 할 수 있습니다. 반면, Encoding을 하고 나면 원래 내용이 뭔지 도저히 알아볼 수 없다는 단점이 있죠.

   Quoted-Printable 방식은 ASCII 문자가 아닌 놈들만 "=XX" (X : 0...9, A...F)와 같은 모양으로 Encoding 하는 방식을 말합니다. 따라서 7Bit ASCII로 표현할 수 없는 문자는 한 바이트가 3바이트로 늘어나게 됩니다. 따라서, 이 방식은 크기가 최대 3배로 늘어날 수 있으니까 효율성 면에서는 거의 최악이라고 할 수 있습니다.

이 Encoding 방식은 영어 문화권에서 보면 나쁜 Encoding이 아닐 수 있습니다. 영어 문화권에서 만들어지는 텍스트는, 대부분의 문자가 7비트 ASCII 문자로 되어 있고, 그렇지 않은 문자가 훨씬 적습니다. 따라서, 이 방식으로 Encoding을 하면, 대부분의 글자가 Encoding이 되지 않고 일부분만이 Encoding이 됩니다.
그러니까, 일단 오히려 효율적일 수도 있고, 다양한 Decoding을 수행하지 못하는 MUA에서 메일을 보더라도, 대략적으로 내용을 파악할 수 있다는 거죠. 반면 한글 같이 거의가 Non-ASCII 문자인 Message나 바이너리 파일 같은 경우는 이 방식으로 Encoding 해봤자 좋은 점이 없습니다. 괜히 메모리나 디스크, 네트워크만 더 잡아먹을 뿐이지요.

정리하자면 Quoted-Printable Encoding은 Decoding이 안 되더라도 내용을 대략적으로 알아볼 수 있지만, 효율이 안 좋고, base64 Encoding은 효율이 좋지만, Decoding하기 전까지는 내용을 전혀 못 알아볼 수 없다는 겁니다.

그래서, 대부분의 ASCII 문자가 많은 텍스트나 HTML의 경우에는 Quoted-Printable로 Encoding 하는 경우가 많고, 한글이나 바이너리 파일은 거의 무조건 base64로 Encoding합니다.
이것은 그냥 그런 경우가 많이 있다는 것이고, 실제로 각 Body Part의 Encoding의 MUA 맘대로 (혹은 사용자의 선택으로) 하게 되겠죠.


4. Content-Disposition 헤더 필드

이 헤더 필드는 각각의 본문의 부분이 화면에 바로 보여야 하는지, 아니면 첨부된 파일로 보여서, 사용자가 따로 처리해야 하는지를 명시하기 위해서 사용됩니다. 문법은 아래와 같습니다.
disposition := "Content-Disposition" ":" disposition-type *(";" disposition-param)

disposition-type이 붙고 parameter가 붙을 수 있습니다.
disposition-type에는 두 가지가 있을 수 있습니다.
"inline" / "attachment"

inline은 사용자가 메시지를 보려고 할 때, 무조건 화면에 나타나야 한다는 걸 말하고, attachment는 메시지의 주 본문과는 별도로 처리될 수 있다는 걸 표시합니다. 물론 요즘 Windows용 MUA같은 경우에는 첨부된 그림 파일 같은 건 바로 화면에 보여주죠? attachment는 반드시 따로 처리해야 하는게 아니라, 반드시 화면에 바로 나타낼 필요는 없다는 걸 알려주는 겁니다.

parameter는 아래와 같은 형식으로 만들 수 있습니다. attachment같은 경우에는 "filename" parameter를 지정하는 것이 일반적입니다.
disposition-param := "filename" "=" value
    / "creation-date" "=" quoted-date-time
    / "modification-date" "=" quoted-date-time
    / "read-date" "=" quoted-date-time
    / "size" "=" 1*Digit

이 헤더가 필요한 이유는 Content-Type만을 보고 이 메시지 덩어리가 inline인지, attachment 인지 판단하기 힘든 경우가 많기 때문입니다.
예를 들어서, 어떤 사람이 텍스트로 메일을 열심히 쓴 다음에 참고할 만한 텍스트 파일을 첨부해서 메일로 보냈다고 합시다. 그러면 이 사람이 작성한 주 본문의 Content-Type은 text/plain이겠죠? 이 사람이 첨부한 파일의 Content-Type도 text/plain이겠죠?
그러면 메일을 받았을 때, 어느 부분이 진짜 주 본문인지 판단하기 힘들죠. 보통은 주 본문을 앞에 두니까, 별 상관없다고 할 수 있지만, 받은 메시지만 보고 판단할 방법은 없다는 겁니다.
어쨌든 보통의 MUA에서는 주 본문에 해당되는 부분에는 이 헤더를 굳이 설정하지 않고, attach에 해당되는 부분에는 이 헤더를 표기해 주는 경우가 많습니다.
MTA와는 전혀 관계없고, 순전히 MUA를 위해서 사용되는 Header Field라고 할 수 있습니다.



5. Content-ID, Content-Description 헤더 필드

이 두 헤더 필드의 문법은 아래와 같습니다.
id = "Content-ID" ":" "<" localpart @ domain ">"
description := "Content-Description" ":" *text

이 중 Content-ID와 같은 경우에는 평소에는 별로 중요하게 쓰이지 않지만, HTML Message 중 Embedded Image가 있는 경우에는 반드시 사용되는 중요한 Field입니다.

1. Content-Type의 종류들...

MIME의 Content-Type으로 공식적으로 등록된 것들은 얼마나 될까요? 엄청나게 많겠죠? Windows 탐색기의 파일 타입만 보더라도, 매우 많은 타입을 볼 수 있지만, 많은 응용 프로그램들이 생겨나고 있으니, 갈수록 더 늘어나겠죠.

"Text" 타입

인터넷 텍스트 메시지의 기본적인 타입입니다. 여기에 포함되는 서브 타입들을 보면...

text/plain, text/html 등등...

"Text" 타입은 charset이라고 부르는 문자 집합을 지정하는 parameter를 가질 수 있습니다.
기본 값은 앞에서도 말씀드렸듯이 US-ASCII죠.
Content-Type이 지정되지 않은 메시지 본문은 text/plain으로 처리합니다. 모든 메시지 본문의 기본 Content-Type은...
text/plain; charset=US-ASCII

요렇게 되는 거죠. charset에는 그밖에도 iso-8859-X (X : 1~10) 이라는 것이 있습니다. 한글을 비롯한 여러 국가에서 쓰이는 EUC (Extended Unix Characterset) 라는 것도 있죠. 한글 메세지의 실제로 많이 사용되는 표준 문자 집합은 EUC-KR라고 생각되네요.

"Image" 타입

이 타입은 GIF, JPEG 등의 이미지를 지원하기 위해서 처음 만들어졌습니다. 물론 Image에 해당되는 서브 타입도 아주 많지만, 거의 모든 곳에서 지원되는 타입이라면,
image/jpeg, image/gif

이 두 가지라고 할 수있겠네요.

"Audio" 타입

이 타입은 MUA가 동작하는 환경에서 이를 얼마나 지원하느냐가 문제겠죠? 그리고 Audio에 대해서는 특별한 표준이... 없습니다. MP3, Real Media 등과 같은 Audio 소스가 요즘에는 많지만, MIME에서는 공식적으로 사용하는 기본 규격은 8비트 PCM, 8KHz 규격입니다. -_-;
audio/basic

"Video" 타입

Vidoe 서브 타입도 Audio와 마찬가지로 MUA의 환경이 문제가 됩니다. 이 타입의 기본 서브타입은 MPEG으로 되어 있습니다.
video/mpeg

"Application" 타입

Application 타입은 다른 응용 프로그램이 이걸 처리해야 한다는 걸 의미합니다. 그래서 이 타입의 기본 동작은 사용자의 디스크에 저장하는게 되는 거죠. 여러 MUA에서 바로 이런 타입의 Content를 열 수 있는 기능을 지원하지만, 사실은 임시 공간에 파일을 저장하고 응용 프로그램을 부르는 방식이죠.

넘쳐나는 응용프로그램의 MIME Content-Type을 모두 인식하기란 사실상 힘들다고 볼 수 있습니다. 따라서 알 수 없는 파일의 기본 Content-Type은,
application/octet-stream


로 정의되고, 이 Content-Type에 대한 기본 동작은, "로컬 디스크에 저장"이 됩니다.

RFC에 언급되기로는 application/Postscript과 같은 서브타입이 있고, 보안 관련 이슈가 상당히 많지만, 별로 흥미가 없네요. ^^;

1. 기본 구조

"Multipart" 타입의 기본 구조를 보기 전에, Multipart 타입의 기본 서브 타입에 대해서 이야기를 해야겠네요.

Multipart Content-Type의 기본 Subtype은 "multipart/mixed"라는 서브 타입입니다. 각각의 부분은 연관관계가 없으며, 각 부분의 순서만 미미하게(?) 의미가 있는 거죠. 꼭 하나의 (특히 첫번째) 본문 부분이 inline 메시지고, 나머지는 attachment라는 보장은 없습니다. 또, 각각의 본문 부분도 multipart로 구성될 수 있습니다. 따라서 이론적으로 무한대의 중첩이 가능합니다. 실제로는 최대 세 단계 정도로 mulitpart가 구성되는 것이 일반적입니다. 어째서 세 단계까지인지는 이후에 설명하겠습니다.

맨 앞에서 메시지는 헤더와 본문으로 구성되고 둘의 구분은 NULL 라인으로 한다고 말씀드렸죠?
메시지 본문은 단순히 라인의 집합의 형태로 평면적인 구성을 하고 있기 때문에, 여러 부분으로 나누기 위해서는 각 부분의 경계를 표시할 수 있는 표식이 필요하고, 이것이 Multipart Content-Type의 기본 Parameter가 됩니다. 이 Parameter를 그걸 boundary라 부릅니다.

기본 구조를 보면 아래와 같습니다.

Content-Type: multipart/mixed; boundary=gc0p4Jq0M2Yt08j34c0p

boundary로 쓰일 수 있는 문자는 ASCII 문자 중에서 일부에 속합니다. 아래의 예는 올바른 boundary 구성이라고 볼 수 없습니다. 콜론이 들어가 있기 때문입니다. 콜론은 헤더 필드 이름과 필드 본문을 구분하기 위해서 사용되는 문자니까요.
Content-Type: multipart/mixed; boundary=gc0p4Jq0M:2Yt08j34c0p

그렇다고 완전히 틀린 건 아니고, 아래처럼 따옴표로 묶으면 적법한 boundary가 됩니다. 좀 애매한 기준이긴 합니다만, 실제로는 boundary를 Uniqueue하게 만들기 위해서 이런 식으로 쓰이는 경우도 많습니다.
Content-Type: multipart/mixed; "boundary=gc0p4Jq0M:2Yt08j34c0p"

boundary는 메시지 안에 이런 형태가 나타나지 않도록 신중하게 만들어져야 합니다. 특히, 다음에 설명할 "message" Content-Type과 같이 메시지를 가지고 있는 본문 부분의 경우에 문제가 발생할 소지가 많기 때문이죠.

multipart로 구성된 메시지의 예를 하나 보죠.
From: Nathaniel Borenstein
To: Ned Freed
Date: Sun, 21 Mar 1993 23:56:48 -0800 (PST)
Subject: Sample message
MIME-Version: 1.0
Content-type: multipart/mixed; boundary="simple boundary"

This is the preamble. It is to be ignored, though it
is a handy place for composition agents to include an
explanatory note to non-MIME conformant readers.

--simple boundary

This is implicitly typed plain US-ASCII text.
It does NOT end with a linebreak.
--simple boundary
Content-type: text/plain; charset=us-ascii

This is explicitly typed plain US-ASCII text.
It DOES end with a linebreak.

--simple boundary--

This is the epilogue. It is also to be ignored.

보면, 인자로 주어진 boundary에 두 가지 변형을 해서 boundary로 사용하고 있습니다. 첫번째는 앞에 "--"를 붙인 것입니다. 이것을 메시지 부분간의 실제 경계로 사용합니다. 그 담엔 앞 뒤에 "--"를 붙인 것이 있지요? 이건 multipart의 끝을 의미합니다.

일단 Content-Type이 multipart로 선언되면 첫번째 경계가 나타나기 전까지의 메시지는 "반드시" 무시해야 합니다. 그리고 경계가 나타나면 다음 경계가 나타나기 전까지의 부분을 하나의 본문 파트로 처리하면 됩니다. 그러다가 multipart의 끝을 나타내는 표식이 나타나면 실제 mulitpart 메시지가 끝난 걸로 처리합니다. 이후에 나타나는 모든 문자는 역시 "반드시" 무시해야 합니다.
뭐 생각보다 간단하다고 생각할 수도 있겠지만, 이 기본적인 구조를 지키지 않는 메일 시스템이 참 많습니다. boundary를 단순히 "mail-boundary"와 같이 표시하는 것들은 좀 문제를 많이 일으키죠. 또한 에필로그에 해당되는 경계를 제대로 넣어주지 않는 경우도 많이 있습니다.



2. "multipart" 타입의 각 서브 타입

이게 각각의 서브 타입들을 살펴보도록 하죠. 앞에서 본 기본 구조를 머리 속에 두고 각각의 서브 타입의 차이점들을 살펴보도록 하죠.

"Mixed" 서브 타입

앞서 말씀드린 것과 같이 mulitpart 타입의 기본 서브타입입니다. 내용은 앞과 마찬가지고, 중요한 것은, 알 수 없는 multipart의 어떤 서브 타입이 나타나더라도 처리를 못해서는 안 된다는 겁니다. 알 수 없는 multipart 서브 타입는 이 multipart/mixed 서브 타입으로 처리하도록 되어 있습니다.

"Alternative" 서브 타입

이 타입은 형식상으로는 multipart/mixed 타입과 완전히 동일합니다. 다른 점은, 이 타입은 같은 내용에 대한 서로 다른 버전을 담고 있다는 것이죠. 이 메시지를 처리하는 쪽 즉, MUA에서는 자신의 환경에 맞는 가장 좋은 버전 하나만을 선택하면 됩니다.
보통은 가장 훌륭한 버전을 맨 뒤에 놓습니다. 어떤 생각해 보면, 가장 좋은 버전이 맨 앞에 있어야 하는 것 같기도 하지만, 이런 타입을 제대로 처리하지 못하는 MUA 환경을 생각할 때, 가장 단순하고 일반적인 타입이 맨 앞에 있어야 그나마 처리가 쉬워집니다.

예를 하나 보죠.
From: Nathaniel Borenstein
To: Ned Freed
Date: Mon, 22 Mar 1993 09:41:09 -0800 (PST)
Subject: Formatted text mail
MIME-Version: 1.0
Content-Type: multipart/alternative; boundary=boundary42

--boundary42
Content-Type: text/plain; charset=us-ascii

... plain text version of message goes here ...

--boundary42
Content-Type: text/enriched

... RFC 1896 text/enriched version of same message goes here ...

--boundary42
Content-Type: application/x-whatever

... fanciest version of same message goes here ...

--boundary42--

각각의 내용은 모두 같지만, 서로 다른 포맷을 가지는 내용을 이렇게 구분할 수 있겠죠. 이 타입을 사용하는 중요한 예는 바로 HTML 문서를 메일로 보내는 경우가 됩니다. MUA마다 HTML를 처리할 수 있는지 없는지가 달라지기 때문에, 앞부분에는 그냥 일반적인 텍스트 즉, text/plain을 넣고 그 뒤에 text/html을 넣는 겁니다.

"Digest" 서브 타입

이 서브 타입의 기본 문법은 "multipart/mixed"와 동일합니다. 다른 점은 multipart/mixed 타입에서, 각 부분의 기본 Content-Type이 text/plain인 것과는 달리 이 타입의 기본 Content-Type은 "message/rfc822"라는 겁니다.

message/rfc822라는 타입은 헤더를 포함한 메시지 자체를 나타냅니다.
즉, 메일 메시지를 캡슐화하고 있는 거라고 보면 됩니다. 보통 되돌아온 메일에 attach로 달려 있는 원래 메시지가 이 타입을 가지고 있습니다.

실제로 아직 Multipart-Digest 타입으로 된 메시지는 보지 못했습니다. 이 형태를 Multipart-Mixed 타입으로 처리해도 큰 문제는 없습니다.

예를 한 번 보죠.
From: Moderator-Address
To: Recipient-List
Date: Mon, 22 Mar 1994 13:34:51 +0000
Subject: Internet Digest, volume 42
MIME-Version: 1.0
Content-Type: multipart/digest; boundary="---- main boundary ----"

------ main boundary ----

...Introductory text or table of contents...

------ main boundary ----
Content-Type: multipart/digest;
boundary="---- next message ----"

------ next message ----

From: someone-else
Date: Fri, 26 Mar 1993 11:13:32 +0200
Subject: my opinion

...body goes here ...

------ next message ----

From: someone-else-again
Date: Fri, 26 Mar 1993 10:07:13 -0500
Subject: my different opinion

... another body goes here ...

------ next message ------

------ main boundary ------


"Parallel" 서브 타입

"multipart/mixed"와 다른 점이라고 하자면, 이 타입에서는 각각의 본문 파트가, 하드웨어에 의하든 소프트웨어에 의하든 간에 병렬적으로 디스플레이가 가능해야 한다는 거죠. 예를 들어 프리젠테이션과 같이 텍스트와 이미지와 오디오가 동시에 나타나야 하는 경우를 들 수 있겠네요. 하지만, 상당히 플랫폼의 제약을 많이 받기 때문에, 실제로 사용되는 일은 거의 없다고 보면 되겠습니다.

"Report" 서브 타입

이 타입은 현재로는 메시지의 상태를 알리기 위한 것으로 사용되고 있습니다. 즉 되돌아오는 메일 등을 이 형태로 처리하게 됩니다. 좀 더 자세한 내용은 Notification Message에서 다루도록 하겠습니다.

이 타입은 어떤 본문 파트의 일부분으로는 사용될 수 없으며, "반드시" Top Level의 Content- Type으로만 쓰이도록 되어 있습니다. 그리고 이 메시지의 전체는 반드시 7비트 ASCII 문자로만 이루어져 있어야 합니다. (실제로는 8bit로 된 경우도 존재합니다.)

이 타입의 서브 파트는 크게 두 개 혹은 세 개이고, 이 순서로 나타나야 합니다. 세번째 Part는 없을 수도 있습니다.

첫번째 부분은 사람이 알아볼 수 있는 상태 설명 부분입니다. 보통 Content-Type이 지정되어 있지 않기 때문에, Content-Type을 text/plain으로 설정해서 보게 됩니다.

두번째 부분은 기계가 파싱해서 사용할 수 있는 상태 설명입니다. Content-Type은 "message/delivery-status"와 같은 형태로 나타납니다. 이 부분도 반드시 나타나야 합니다.

세번째 부분은 선택 사항으로 원래 메시지 전체 혹은 일부분을 가지고 있습니다. 보통은 헤더에 문제가 있어서 메시지가 되돌아 오는 경우가 많기 때문에, RFC에서는 메시지 헤더만을 가지는 "text/rfc822" (실제로는 message/rfc822-headers가 더 많습니다) 타입을 권장하고 있습니다. 하지만, 요즘 같이 네트워크 용량이 풍부한 세상에서는 앞서 말씀드린 "message/rfc822" 타입으로 메시지 전체가 포함되는 것이 일반적입니다.


"Related" 서브 타입

이 타입이 multipart 타입 중에서 가장 어려운 타입이라고 볼 수 있겠네요. 이 타입은 각 본문 파트가 연관을 가지고 있다는 것을 말합니다. 그런데, 각 부분을 연결하는 방식이 응용 프로그램마다 천차만별일 수밖에 없기 때문에, 이 타입은 뭐라고 설명하기가 어렵네요.

하지만, 역시 이 타입이 중요한 이유는 HTML 문서를 메시지로 전달하기 위한 것입니다. HTML 문서에는 단순히 텍스트 뿐만이 아니라, 이미지 등의 많은 객체가 포함되어야 하는데, 거기에 바로 각 Content들을 Content-ID로 연결해서 사용하는 겁니다. 자세한 내용은 HTML 메시지에서 알아보도록 하죠.

1. "Message" 타입의 각 서브 타입

"RFC822" Subtype

앞서서 말씀드렸듯이 가장 간단하게 메일 메시지를 포함하고 있는 타입을 뜻합니다.
되돌아온 메시지에 가장 많이 포함된다고 말씀드렸죠?
"Partial" 서브 타입

메일 시스템 용량이 아무리 빠방하다 하더라도, 어떤 큰 메시지라도 끄떡없이 보낼 수 있는 건 아닙니다. 한 번에 통과할 수 있는 메시지의 크기가 여러 가지 이유로 제한이 생기기 마련이죠. 그래서, 뭔가 큰 메시지를 보내기 위해서는 여러 개의 메시지로 잘라서 보내고 받는 쪽에서 합쳐서 봐어 합니다. 그것을 위한 타입입니다.

그래서, 각각의 메시지에 몇 번째 조각이며, 전체는 몇 조각으로 되어 있는지를 표시합니다. 물론 같은 메시지의 조각임을 나타내기 위해서 id라는 parameter를 사용합니다. 아래의 예를 보죠.

3조각 메시지의 두번째 조각
Content-Type: Message/Partial; number=2; total=3;
    id="oc=jpbe0M2Yt4s@thumper.bellcore.com"
혹은,
Content-Type: Message/Partial;
    id="oc=jpbe0M2Yt4s@thumper.bellcore.com";
    number=2

3조각 메시지의 세번째 조각 - 반드시 전체 조각수를 가지고 있어서, 자신이 마지막 조각임을 알려야 합니다.
Content-Type: Message/Partial; number=3; total=3;
    id="oc=jpbe0M2Yt4s@thumper.bellcore.com"

그렇다고 메시지를 조각낼 때, 아무렇게나 조각낼 수 있는 건 아니고, 몇 가지 원칙이 있습니다.

   일단 메시지는 "반드시" 라인이 끝나는 지점에서만 잘려야 합니다.
   각 메시지 마다 Content-*, Subject, Message-ID, Encrypted, MIME-Version를 제외한 모든 헤더 필드가 덧붙여져야 합니다.
   메시지가 합쳐질 때는, 헤더 중 Content-*, Subject, Message-ID, Encrypted, MIME-Version만이 살아남고, 나머지는 무시됩니다.
   두번째 조각 이후의 헤더 필드는 깡그리 무시됩니다.

어쨌든 쉽지 않은 모양이네요...


"External-Body" 서브 타입

이 타입은 실제 내용이 메시지 내부에 있는게 아니라 외부에 있다고 알리는 건데요. 그 외부 소스의 형태가 메일, FTP, HTTP, 로칼 디스크 등 워낙 다양하기 때문에, 사용하기도 어렵고, 설명하긴 더 힘드네요. ^^;

1. 헤더 필드의 확장

일단, 문법을 좀 보죠.

encoded-word := "=?" charset "?" encoding "?" encoded-text "?="

좀 복잡하죠? 간단히 쓰자면
=? (문자집합) ? (Encoding 방식) ? (인코딩된 문자열) ?=

이렇게 되는 거죠. 여기서 문자집합은 앞서서 말씀드린 US-ASCII, ISO-8859-1, EUC-KR 등이 되는 겁니다. Encoding 방식은 그 문자들을 어떻게 Encoding했는가를 나타내구요. 각각의 지정된 내용을 ?로 구분하고 양쪽에 =?, ?=로 덮어 싸는 거죠. 물론, 여기서 문자 집합이나 Encoding에 대한 지정 방식은 대소문자를 구분하지 않습니다.



2. Encoding

여기에 사용되는 Encoding 방식은 두 가지가 있습니다.

"B" Encoding
이 방식은 말 그대로 base64 Encoding을 이용하는 겁니다. (RFC2045)

"Q" Encoding
이 방식은 Quoted-Printable과 유사한 Encoding입니다. 물론, 기본적으로는 Quoted-Printable과 같지만 몇 가지가 다르죠. 차이점을 보자면...

   "="은 "=3D"으로 SPACE는 "=20"으로 처리합니다. (16진수의 A~F는 대문자여야 합니다)
   16진수로 20은 반드시 "_"로 대치합니다. EBCDIC 코드에서는 20이 스페이스가 아니지만, 역시 무조건 "_"로 대치해야 합니다.



3. 실제 Encoding 예

아래의 예들을 보면서 실제로 사용시에 주의해야 될 점들을 살펴보죠.
Encoding된 형태 나타날 형태
(=?ISO-8849-1?Q?a?=)
     -> 당연하죠?
(a)
(=?ISO-8849-1?Q?a?= b)
     -> 역시...
(a b)
(=?ISO-8849-1?Q?a?= =?ISO-8849-1?Q?b?=)
     -> Encoding된 부분 사이의 LWSP는 없는 걸로 처리합니다.
(ab)
(=?ISO-8849-1?Q?a?=    =?ISO-8849-1?Q?a?=)
     -> LWSP가 몇 개이든 상관없습니다.
(ab)
(=?ISO-8849-1?Q?a?=
   =?ISO-8849-1?Q?a?=)
     -> CRLF도 마찬가지로 LWSP의 일부분으로 처리합니다.
(ab)
(=?ISO-8849-1?Q?a_b?=)
     -> "_"는 16진수로 20이고 ASCII 코드에서는 SPACE죠.
(a b)
(=?ISO-8849-1?Q?a?= =?ISO-8849-1?Q?_b?=)
     -> 역시 앞서 설명과 같습니다. 재확인...
(a b)

 

이 글은 스프링노트에서 작성되었습니다.

.

'Computer > PHP' 카테고리의 다른 글

한글메일의 인코딩,디코딩  (0) 2012.08.10
인코딩 디코딩의 종류  (0) 2012.08.10
MIME 규격  (0) 2012.08.10
base64 규칙  (0) 2012.08.10
세마포어(semaphore)와 공유 메모리(shared memory) 함수  (0) 2012.08.10
PEAR #2 : 간단한 사용법  (0) 2012.08.10
TAGS.

Comments 0