달력

22025  이전 다음

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
<TITLE>First letter Test</TITLE>

<Style>

  P:first-letter {float:left; font-size:48pt; margin-top:30px; }

</Style>
</HEAD>

<BODY>

<table border="1" align="left" width="300" >
  <tr>
    <td>

<P>
와우저 여러분 안녕하십니까
상용화 개시 시점에서의 접속 폭주로 인하여 게임 로그인 및 결제 장애를 겪고 있습니다. 불편을 끼쳐 드려서 대단히 죄송합니다.
현재 벌어지고 있는 문제에 대한 보상으로 개인 사용자는 하루, PC방에는 100시간을 일괄적으로 추가해드릴 것입니다.
또한 로그인 장애에 관한 문제는 조속히 해결하도록 최선의 노력을 다하겠습니다.

이 보상정책은 예약 결제자 및 18일까지 결제하시는 모든 개인 및 PC방에 적용됩니다.

월드 오브 워크래프트팀

</P>

    </td>
  </tr>
</table>


</BODY>
</HTML>

 

와우를 못하는 관계로 ... 결재가 안되네 떠그럴.. ;;

Posted by tornado
|

한글 제목 자체가 깨져서 들어올 경우가 있다.

 

헤더의 Subject 부분이 =?인코딩?B?안전한 문자 ?=  처럼 들어오면 인코딩이 가능하다.

 

그런데 Subject 부분에 그냥 한글을 쓸 경우 제목이 깨지게 되는데..

 

byte[] bytes = string.getBytes("iso-8859-1");

 

String subject = new String(bytes, "euc-kr");

 

과 같이 ISO-8859-1 로 풀어 헤쳐서 다시 스트링으로 조립해 줘야 한다는 ㅡㅡ;;

 

포털 메일에서 첨부 파일 있을때는 안전한 문자로 변형시켜 캐릭터 셋 포함시키고 전달하고..

 

첨부 파일이 없을때는 그냥 보내고 ㅡㅡ

 

어떤 포털은 또 반대로 보내서리... 경우의 수가 여러개 생기넹 ...

 

base64 로 온거 풀어 헤칠때두 어디는 제대로 파싱되는 반면..

 

어디는 깨지고 ㅡㅡ

 

야후에서 보낸거 다음에서 보면 제대로 안보이고 ㅡㅡ

 

 

개발자 맘대로 메일 보내는 부분을 만들면 어쩌냐고여...

 

RFC 규약은 괜히 있냐고여~~ 짱나네 -.-;;

 

 

 

'JAVA > JSP_Servlet' 카테고리의 다른 글

[펌] Trackback CGI 설계  (0) 2005.02.03
[펌] 검색엔진 만들기  (0) 2005.01.21
[펌] 메일 헤더 구성 요소  (0) 2005.01.17
[펌] [MAIL] MIME 관련  (0) 2005.01.17
[펌] 코드시스템및 한글문자체계  (0) 2005.01.17
Posted by tornado
|

헤더는 매우 다양한 구성요소가 있습니다. 메시지를 전달하기 위해 반드시 필요한 것도 있고 꼭 필요하지 않은 것도 있습니다. 또 어떤 헤더는 여러분이 입력하지 않아도 자동으로 입력되는 것도 있습니다. 자동으로 입력되는 헤더는 다시 메일/뉴스 프로그램이 직접 기록하는 것과 서버에서 직접 기록하는 것이 있습니다.

자신이 직접 입력할 수 있는 헤더

자신이 직접 입력할 수 있는 헤더는 주로 메일 프로그램에서 새 메시지를 작성할 때 입력할 수 있는 항목과 프로그램의 설정 항목에서 작성한 내용입니다. 프로그램의 설정 항목에 기록한 내용은 이메일을 보낼때마다 자동으로 헤더에 첨부됩니다. 자신의 소속, 이메일 주소, 진짜 이름등등은 이메일을 보낼때마다 매번 기록할 필요가 없을 것입니다. 그래서 그런 것들은 프로그램 설정 항목에 기록하도록 해서 자동으로 입력할 수 있도록 하는 것입니다. 그러나 받는 사람, 제목, 참조, 숨은 참조등은 이메일을 보낼때마다 매번 바뀌는 항목이라서 매번 새롭게 입력해야 합니다.

 

1. 받은 사람
헤더 - To:

메시지를 보내려면 당연히 받는 사람의 주소를 적어야겠지요. To: 헤더에 아무것도 입력하지 않으면 메시지를 보낼 수 없습니다. 메일 서버는 이곳에 있는 주소로 여러분의 메시지를 보냅니다. 프로그램에서 "수신인" 또는 "받는 사람" 입력창에 입력한 내용이 To: 헤더에 기록됩니다.

2. 참조
헤더 - Cc:

Cc는 "Carbon Copy"의 약자로써 원본 메시지를 참조하는 것을 말합니다. 참조는 어떤 메시지를 여러 사람에게 보낼 때 원본 메시지와는 별도로 다른 사람이 그 메시지를 참조삼아 읽으라고 보내는 경우에 사용합니다. A라는 사람에게 보내는 메시지를 B와 C가 참조할 필요가 있다고 생각한다면 A를 받는 사람으로 지정하고 B와 C는 참조로 지정하여 메시지를 보내면 됩니다. 참조형식으로 보낸 메시지의 헤더에는 Cc: 부분에 받는 사람의 주소가 모두 표시됩니다.

3. 숨은 참조
헤더 - Bcc:

Bcc는 "Blind Carbon Copy"의 약자인데, 숨은 참조라고 합니다. 숨은 참조는 여러 사람에게 메시지를 보낼 때 누구에게 그 메시지를 보냈는지 알 수 없도록 참조인의 주소를 숨길 때 사용합니다. 참조를 나타내는 Cc: 헤더와는 달리 숨은 참조란에 기록한 주소는 헤더에 표시되지 않습니다. 100명에게 메시지를 보낼 때 참조를 이용하는 것은 좋지 못합니다. 100명은 자신에게 필요하지 않은 99명의 이메일 주소가 헤더에 포함된 이메일을 받아야할 것이니까요.

4. 제목
헤더 - Subject:

보낼 메시지의 제목을 나타내는 헤더입니다. 설령 여러분이 제목을 적지 않고 이메일을 보냈더라도 헤더는 표시됩니다. 제목을 "안녕"이라고 적었다면 "Subject: 안녕"이라고 될 것이고 제목을 적지 않았다면 단지 "Subject:"라고만 헤더에 표시될 것입니다.

프로그램의 설정 메뉴에서 기록하는 헤더

사용하는 이메일 프로그램의 설정 메뉴에서 기록한 정보는 보내는 이메일 메시지의 헤더에 자동으로 기록됩니다. 이런 정보는 늘 입력할 필요가 없는 고정된 정보입니다. 이메일을 보내며 매번 자신의 이메일 주소와 이름, 소속, 답신받을 주소를 입력할 필요는 없을 것입니다. 그런 것들은 자동으로 입력하도록 설정해 두는 것이 훨씬 편할 것입니다.

 

1. 전자 우편 주소
헤더 - From:

자신의 주소가 없다면 보낸 메시지가 잘못되었을 때 어디로 반송되어 올지 알 수 없을 것입니다. 자신의 이메일 주소를 적지 않으면 메시지를 작성할 수 없습니다. 정 주소를 밝히기 싫다면 가짜 주소라도 적어야 합니다. somebody@nonono.com 처럼 정체불명의 주소라도 적지 않으면 메시지를 작성할 수 없습니다. 아웃룩 익스프레스에서 From: 헤더를 설정하는 곳은 [계정] 메뉴에서 사용자 정보 입력창입니다. [전자 우편 주소]라는 곳에 입력한 정보가 From: 헤더에 기록됩니다.

2. 이름
헤더 - From:


자신의 이름을 적는 곳입니다. 이곳을 비워두면 메시지를 작성할 수 없습니다. 자기 이름을 입력해도 좋고, 이름을 밝히기 싫다면 가짜 이름 - 'SkyHawk'와 같은 별명이나 가짜 이름을 적어도 무방합니다. 여기에 적은 이름은 From: 헤더에 기록되어 다음과 같이 나타납니다.

From: "Lee JunYoung"
leejuy@hyowon.pusan.ac.kr

3. 회신 주소
헤더 - Reply-To:


상대방이 여러분의 이메일에 대해 회신하기 위해 이메일 프로그램의 회신 기능을 사용할 때 이곳에 적어둔 주소로 답신이 보내집니다. 회신 주소 입력창에 굳이 주소를 적지 않아도 괜찮습니다. Reply-To 헤더가 없을 경우 '전자 우편 주소'로 답신이 보내지게 됩니다.

Reply-To: "Lee JunYoung" <leejuy@hyowon.pusan.ac.kr>

자동으로 붙는 헤더

여러분이 받은 이메일의 헤더를 잘 살펴 보십시오. 그곳에는 여러분이 설정할 수 없는 헤더들이 있을 것입니다. 이런 헤더들은 대부분 센드메일에서 이메일을 주고 받으며 붙인 헤더나 여러분이 사용중인 메일 프로그램에서 붙인 헤더입니다. 이런 헤더는 여러분이 어떻게 조정할 수 없는 것들입니다.

 

1. 메일이 전달된 경로
헤더 - Received:

여러분의 편지함에 도착한 메일이 어디를 거쳐서 왔는지 표시하는 헤더입니다. [from A by B for C]라는 문장으로 이루어져 있습니다. "B라는 메일 서버를 통해 A라는 메일 서버로 전송된 이메일이다. 그리고 그것은 C에게 전달되었다."라는 내용을 담고 있습니다. 이 부분의 헤더를 잘 살펴보면 메일이 어디를 거쳐서 여러분에게 전달되었는지 잘 알 수 있습니다. 여기에 그 사람이 사용하는 컴퓨터의 이름이 표시되기도 하고, 메일 서버로 어떤 프로그램을 사용하는지, 센드메일의 버전등이 표시 됩니다. 아래는 제가 받은 메일에 포함된 Received: 헤더입니다. 이것을 잘 분석해 보십시오.

Received: from hyowon.cc.pusan.ac.kr (hyowon.cc.pusan.ac.kr [164.125.9.3])
by bora.dacom.co.kr (8.8.6/8.8.6) with SMTP id BAA11063
for <social91@bora.dacom.co.kr>; Fri, 30 Jan 1998 01:47:06 +0900 (KST)

첫 라인에서 메일이 hyowon.cc.pusan.ac.kr이라는 서버에서 보내진 것임을 알 수 있습니다. 이곳의 IP 주소는 164.125.9.3이구요. 두 번째 라인에서 메일이 bora.dacom.co.kr이라는 서버로 전달되었음을 알 수 있습니다. 그리고 이 서버는 SMTP 서버로군요. 그리고, 세 번째 라인에서 1998년 1월 30일, 금요일 오전 1시 47분에 social91@bora.dacom.co.kr이라는 사용자에게 전달되었음을 알 수 있습니다. 날짜는 여러분의 메일 서버로 메일이 도착한 시간을 의미합니다. 뒤에 얘기할 Date: 헤더는 상대방이 메일을 보낸 시간을 의미하는데 이것을 보고 메일 서버간의 전달 시간을 체크해 볼 수 있습니다. "+0900 (KST)"는 한국 표준 시각을 말합니다. 그리니치 표준시보다 9시간 빠르다는 뜻이죠?

가끔 Received: 헤더가 여러개 포함된 메시지를 발견할 수 있습니다. 곧장 자신에게 전달된 메시지가 아니라 다른 몇 개의 서버를 거쳐 전달된 경우 Received: 헤더에는 거쳐온 메일 서버들이 모두 표시됩니다. 맨 아래의 Received: 헤더가 제일 처음 메일이 발송된 곳이고 그곳에서 차례대로 위쪽으로 지나온 것입니다. 제가 구독중인 온라인 잡지인 People에서 보내온 메일의 Received: 헤더를 보십시오.

(1) Received: from hyowon.cc.pusan.ac.kr (hyowon.cc.pusan.ac.kr [164.125.9.3])
by bora.dacom.co.kr (8.8.6/8.8.6) with SMTP id CAA13191
for <social91@bora.dacom.co.kr>; Fri, 30 Jan 1998 02:10:40 +0900 (KST)

(2) Received: from listserv.pathfinder.com ([204.71.242.6]) by hyowon.cc.pusan.ac.kr (8.6.11/8.6.8)
with ESMTP id CAA14694 for <leejuy@HYOWON.PUSAN.AC.KR>;
Fri, 30 Jan 1998 02:10:39 +0900

(3) Received: (from daemon@localhost) by pathfinder.com (*private*/SMI-SVR4) id
LAA21552 for peopledaily-text@listserv.pathfinder.com; Thu, 29 Jan
1998 11:44:40 -0500 (EST)

3개의 Received: 헤더가 있습니다. 여러분은 이것을 보고 어떤 정보를 얻을 수 있습니까?

(3) 의 Received: 헤더를 보세요. pathfinder.com이라는 컴퓨터에서 listserv.pathfinder.com이라는 컴퓨터에게 메일을 보내도록 지시했음을 알 수 있습니다. pathfinder.com이라는 컴퓨터는 People지의 메인 컴퓨터입니다. 여기서 온라인 신문을 구독하는 사람들을 위해 신문을 작성합니다. 그리고 실제로 메일을 보내는 컴퓨터인 listserv.pathfinder.com이라는 컴퓨터로 메시지를 보내면 이 컴퓨터가 메일 발송작업을 하는 것입니다. 메일은 미국 동부 표준시(EST) 1998년 1월 29일 오전 11시 44분에 전세계 온라인 People 구독자에게 발송되었습니다.

(2) 의 Received: 헤더에서 이 메시지가 한국 시각 1월 30일 오전 2시 10분 39초에 hyowon.pusan.ac.kr이라는 컴퓨터로 도착했음을 알 수 있습니다. 이 컴퓨터는 제가 학교에서 사용하는 계정이 있는 컴퓨터입니다. 이 주소로 도착한 메일은 즉시 bora.dacom.co.kr이라는 컴퓨터로 돌려 보내도록 설정해 두었죠.

(3)의 Received: 헤더에서 한국 시각 1월 30일 오전 2시 10분 40초에 bora.dacom.co.kr로 메시지가 도착했음을 알 수 있습니다.

메일을 보내고 받은 시각은 센드 메일에서 그리니치 표준시로 계산합니다. +0900 (KST)는 한국 표준 시각이 그리니치 표준시보다 9시간 빠르다는 말입니다. 결국 메일이 미국 동부의 People 메인 컴퓨터에서 social91@bora.dacom.co.kr까지 전달되는데 약 26분이 걸린 것을 알 수 있습니다.

Received: 헤더를 통해 여러분은 이렇게 메일이 전달되는 경로를 알 수 있을 뿐만 아니라 메일의 전달과정에 문제가 생겼을 때 도움을 받기도 합니다. 만약 메일이 전달되는 과정에서 매우 시간이 많이 걸렸거나 또는 데이터의 손실이 발생했다면 Received: 헤더를 참조하여 중간에 어떤 메일 서버를 경유했는지 확인할 수 있습니다. 그리고 그 메일 서버가 문제가 있는지 조사해 보거나 서버 담당자에게 문의할 수 있습니다. 만약 여러분이 메일을 주고 받는 메일 서버의 센드메일 버전이 너무 낮아 메일이 깨지는 문제가 발생한다면 다른 곳으로 서비스를 옮기는 것을 고려할수도 있습니다.

2. 메시지 아이디
헤더 - Message-ID

이 헤더는 메일 서버에서 메시지를 외부로 보내며 붙이는 일련번호입니다. 메시지 아이디는 다음과 같이 표시됩니다.

Message-ID: <199801291644.LAA21552@pathfinder.com>

여러분은 이것을 통해 단지 그 메시지가 어떤 컴퓨터에서 보내졌는지 알 수 있습니다. 그러나, '@' 앞에 적혀있는 이상한 문자열은 여러분을 위한 것이 아니라 컴퓨터가 그 메시지에 임의로 붙인 번호표입니다.

3. 보낸 날짜
헤더 - Date

메일을 보낸 날짜를 표시하며 다음과 같은 형식으로 표시됩니다.

Date: Thu, 29 Jan 1998 02:37:50 -0800 (PST)

시간을 표기하는 방식은 24시간 표기방식으로 마지막에 그리니치 표준시를 표시합니다. 이것을 보고 그 컴퓨터가 위치한 지역을 대략 예상해 볼 수 있습니다. 예문에 제시된 PST(Pacific Standard Timezone)는 태평양 표준시로써 미국과 캐나다에서 사용하는 표준 시간대를 의미합니다. Date: 헤더는 서버에서 직접 붙이는 경우가 있고, 사용자의 컴퓨터 시간을 따르는 경우가 있습니다. 계정에 접속하여 PINE을 통해 메일을 보내면 Date 헤더에 서버의 시계에 맞춰서 Date: 헤더가 붙습니다. 그러나, 아웃룩 익스프레스를 이용하여 메일을 보낼 경우 사용자의 컴퓨터에 있는 시계에 맞춰서 Date 헤더가 붙게 됩니다.

Received: 헤더에 나와있는 시각이 실제로 서버에 메일이 도착한 시간이라면 Date: 헤더는 메일 프로그램에서 메일이 보내진 시각을 의미합니다.

4. MIME 헤더

MIME이란 멀티미디어 메일을 위한 인터넷 표준 규약입니다. 여러분이 MIME 표준을 지원하는 메일 프로그램을 사용하며 'MIME을 사용하여 메일 보내기' 옵션을 설정하면 다음과 같은 헤더를 볼 수 있습니다.

가) MIME-Version: 1.0
MIME의 버전을 나타냅니다.

나) Content-Type:

Content-Type: 헤더는 메일 본문이 어떤 형식인지 알려 줍니다.
text/plain - 일반 문자열을 사용한 본문
multipart/ - 일반 문자열과 다른 여러 가지 인코딩 방식이 섞여 있을 경우 multipart/mixed는 일반 텍스트에 파일을 첨부했을 경우에 사용합니다. multipart/alternative는 똑같은 내용이 일반 텍스트와 html로 반복되어 있어서 둘 중 하나를 선택해서 읽을 수 있을 경우에 사용합니다. multipart/related 는 HTML 형식의 메시지를 보내며 배경그림을 첨부했을 때 사용됩니다.

다) charset="euc-kr"
charset= 부분은 본문이 어떤 언어로 작성되었는지를 나타냅니다. Content-Type 헤더에 함께 나타납니다. 한글 메일의 경우 euc-kr이란 문자셋이 표시되어야 합니다.

라) Content-Transfer-Encoding:
본문이 인코딩된 방식을 표시합니다. 한글 메시지에 8비트라고 표시되어 있으면 인코딩없이 본문을 그대로 보낸 것입니다. BASE64라든가 Quoted Printable라고 적혀 있으면 그런 방식으로 인코딩을 했다는 의미입니다.

제 멋대로 붙인 헤더

헤더는 철저히 규칙에 맞게 붙여야 합니다. 만약 헤더를 마음대로 붙인다면 컴퓨터간에 메시지 전송이 이루어질 수 없기 때문입니다. 어떤 컴퓨터는 O.K를 '좋다'는 뜻으로 이해하는데 다른 컴퓨터는 O.K를 '엉망이다'라는 뜻으로 이해하면 어떻게 되겠습니까?

그런데, 이런 엄격한 규칙을 가진 헤더지만 제 멋대로 붙일 수 있는 헤더도 있습니다. 'X-'라는 문자열로 시작하는 헤더가 그런 경우입니다. 이런 헤더는 메일 서버에서 신경을 쓰지 않고 그냥 넘겨 버립니다. 신경을 쓰지 않기 때문에 이런 헤더에 무슨 내용을 쓰더라도 메일의 전송에 영향을 끼치지 않습니다. 아웃룩 익스프레스를 사용하여 보낸 메일에는 이런 'X-'가 붙은 헤더를 몇 개 볼 수 있습니다.

X-Priority: 3
X-MSMail-Priority: Normal
X-Mailer: Microsoft Outlook Express 4.72.2106.4
X-MimeOLE: Produced By Microsoft MimeOLE V4.72.2106.4

이 헤더들은 아웃룩 익스프레스라는 프로그램이 임의로 붙인 헤더입니다. 이런 헤더들은 인터넷의 표준이 아닐뿐더러 표준으로 인정받을 필요도 없습니다. 그렇기 때문에 'X-'라는 헤더를 사용한 것이죠.

'X-' 헤더는 매우 다양한 목적으로 사용될 수 있는데, 예문에서 볼 수 있듯이 메일 프로그램을 헤더에 표시하는데 사용되기도 합니다. X-Mailer: 헤더를 통해 메일이 어떤 프로그램을 사용하여 보내졌는지 표시하고 있습니다. X-MimeOLE: 는 메일에 포함된 바이너리 파일을 윈도우의 OLE와 연결시키는 것에 대한 헤더입니다.

또는 그 메일 프로그램이 제공하는 특별한 기능을 표시하기 위해 사용되기도 합니다. 예문에 있는 X-Priority: 헤더와 X-MSMail-Priority: 헤더는 아웃룩 익스프레스를 사용하는 메일에만 붙습니다. 이것은 메일의 보내며 중요도를 설정할 때 나타납니다.

중요도 설정에 따라 헤더의 내용이 달라집니다.

중요도X-MSMail-PriorityX-Priority
높 음High1
보 통Normal3
낮 음Low5

중요도가 높게 설정된 메시지를 받으면 메시지 표시 창에 느낌표(!)가 표시되어 그 메시지가 중요한 것임을 표시합니다. 이것은 단지 아웃룩 익스프레스에서 제공하는 특별한 기능일 뿐 메일 암호화라든가 보안문제를 해결하는 기능은 아닙니다.

'JAVA > JSP_Servlet' 카테고리의 다른 글

[펌] 검색엔진 만들기  (0) 2005.01.21
[javamail] 제목 깨질때...  (0) 2005.01.18
[펌] [MAIL] MIME 관련  (0) 2005.01.17
[펌] 코드시스템및 한글문자체계  (0) 2005.01.17
[펌] http RFC  (0) 2005.01.17
Posted by tornado
|
. 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 방식) ? (인코딩된 문자열) ?=

'JAVA > JSP_Servlet' 카테고리의 다른 글

[javamail] 제목 깨질때...  (0) 2005.01.18
[펌] 메일 헤더 구성 요소  (0) 2005.01.17
[펌] 코드시스템및 한글문자체계  (0) 2005.01.17
[펌] http RFC  (0) 2005.01.17
[펌]Single Sign On...  (0) 2005.01.17
Posted by tornado
|

Report on Code system and Hangeul Character set

 

 

Table of Contents

1.  Introduction

2.  Code 체계

3.  한글 Character Set

4.  Problem Definitions

5.  Conclusion

 

1.      Introduction

Home Page, E-mail, Java 등을 사용하다 보면 Unicode (ISO/IEC 10646) 부호계를 알지 못하면 그런데서 한글을 사용하기가 불편해 진다. ISO/IEC 10646 국제 표준 부호계와 한글 부호계에 대하여 살펴 보고자 한다. 또한 E-mail에서 많이 쓰이는 Quoted Printable (QP) Base64 살펴 , UTF-16, UTF-8, UTF-7 각종 UTF 대하여 살펴본다. ISO/IEC 10646 KS X 1005-1 (과거 KS C 5700)으로 받아 들였는데, 이에 대한 소개도 더붙인다. 현재 Internet E-mail 등에서는 한글 완성형 KS X 1001 ASCII (KS X 1003) 같이 , MIME charset 이름으로 EUC-KR 쓰고 있는데, EUC-KR 이라는 MIME charset KS C 5601 대하여도 살펴보며, 이러한 code체계가 한글에 미치는 영향과 문제점들을 정리하였다.

 

2.      Code 체계

정보 처리 분야의 규격이 늘어나게 데는 국제 표준 기구의 영향이 크다. 1987년에 ISO(International Organization for Standardization) TC97 IEC(International Electrotechnical Commission ) TC83 공동으로 ISO/IEC JTC1 (Joint Technical Committee 1) 만들면서 정보 처리 분야의 국내 규격이 많이 늘어나게 되었다.

1992 6-7월에 열린 ISO/IEC JTC1/SC2/WG2 22 회의에서 ISO/IEC 10646-1 국제 표준 (IS)으로 확정함에 따라서, 거기에 들어 있던 한글 부호계도 확정되게 되었다. ISO/IEC 10646 글자를 4 Byte 나타내는 4 Byte 부호계 (UCS-4) 시작하였으며, 전세계의 주요한 글자계를 거의 모두 나타 있다. [경석]

 

2.1      용어 정의

2.1.1     code (부호계)

code codeword 집합이라고 있다. ISO/IEC 10646 code에서 첫가끝 조합형 첫소리 글자 ㄱ의 codeword 0x0000 1100 된다.

2.1.2     Codeword(부호값)

code codeword 집합이다. 표준에 따라서는 codeword라는 용어 대신 ISO/IEC 10646 에서는 code position이라는 용어를 사용하고, Unicode에서는 code point라는 용어를 쓰며, 다른 곳에서는 code element라는 용어를 사용하기도 하나 모두 같은 용어이다.

2.1.3     Plane

4 Byte code UCS-4에서 윗자리 16bit 값은 같고, 나머지 아래자리 16bit 다른 codeword 집합을 plane이라고 한다. Plane 하나에는 codeword 64K(65,536) 만큼 있게 되며, plane Group 번호와 Plane번호를 각각 hexadecimal 나타낸다. 예를 들면 BMP (Basic Multilingual Plane) 경우 G=00, P=00 된다.

 

BMP는 다시 4개의 영역으로 나누어 지는데 각 영역의 특징은 다음과 같다.

             A-zone: alphabetic and syllabic scripts together with various symbols

             I-zone: Chinese/Japanese/Korean (CJK) unified ideographs (unified East Asian ideographs)

             O-zone: reserved for future standardization

             R-zone: restricted use zone (private use characters, presentation forms, compatibility characters, etc.)

그림 1. BMP(Basic Multilingual Plane)

 

2.1.4     Group

4 Byte code UCS-4에서 윗자리 8bit 값은 같고, 나머지 아래자리 24bit 다른 codeword 집합을 group이라고 한다. group 하나에는 codeword 16M(1600) 만큼 있게 되며, group 번호는 hexadecimal 두개로 나타낸다. 우리가 알고 있는 대부분의 codeword 거의 모두 G=00이다.

 

2.2      ISO/IEC 10646-1

2.2.1     UCS-4 : 4Byte code

ISO/IEC 10646 표준의 이름은 Universal Multiple-Octet Coded Character Set (UCS)이다. UCS 처음에 4Byte code 시작하였는데, 이를 UCS-4라고 한다. , 글자는 4byte 쓰며, 따라서 hexadecimal 8개로 나타낸다.

한글 첫가끝 조합형 첫소리 글자 codeword 0x0000 1100이고, 가운뎃소리

'JAVA > JSP_Servlet' 카테고리의 다른 글

[펌] 메일 헤더 구성 요소  (0) 2005.01.17
[펌] [MAIL] MIME 관련  (0) 2005.01.17
[펌] http RFC  (0) 2005.01.17
[펌]Single Sign On...  (0) 2005.01.17
JSP 다운로드시 헤더 ㅡㅡ  (0) 2005.01.13
Posted by tornado
|

[펌] http RFC

JAVA/JSP_Servlet 2005. 1. 17. 21:30

RFC 2068 - Hypertext Transfer Protocol -- HTTP/1.1
rfc2068 :하이퍼텍스트 전송규약 1.1표준(안)
출처 : 한국전자통신연구소요약하이퍼텍스트 전송 규약(HTTP)은 분산 정보 시스템, 종합 정보시스템 및 하이퍼미디어 정보시스템에서사용하는 응용 계층 규약으로서 요구 방법의 확장을 통해서 네임 서버와 분산 객체 관리 시스템과 같은수많은 작업에 사용될 수 있는 보편적인 객체 지향형 규약이다. HTTP는 어떤 문서의 데이터 표현 형식을규정하고 협상하여 전송 중인 데이터와 무관하게 시스템을 구축할 수 있게 한다.HTTP는 1990년 이후 World-Wide Web 범 세계 정보 이니셔티브에 의하여 사용되고 있다.이 규격은 "HTTP/1.1"로 언급되는 규약을 정의하고 있다.
목차 1. 도입 1.1 목적 1.2 필요 사항 1.3 용어 1.4 전반적인 운영 2. 기호 관례 및 일반적인 문법 2.1 추가된 BNF 2.2 기본적인 규칙 3. 규약 파라미터 3.1 HTTP 버전 3.2 보편적 자원 식별자(Uniform Resource Identifiers) 3.2.1 일반적 형식 3.2.2 http URL 3.2.3 URI 비교 3.3 날짜/시간 형식 3.3.1 완전한 날짜 3.3.2 Delta Seconds 3.4 문자 집합 3.5 내용 코딩 (Content Coding) 3.6 전송 코딩 (Transfer Coding) 3.7 미디어 타입 3.7.1 정형화(Canonicalization) 및 텍스트 기본값 3.7.2 Multipart Type 3.8 제품 토큰 3.9 품질 등급 값 3.10 언어 태그 3.11 엔터티 태그 3.12 영역 단위 4. HTTP 메시지 4.1 메시지 유형 4.2 메시지 헤더 4.3 메시지 본문 4.4 메시지 길이 4.5 일반적인 헤더 필드 5. 요구 5.1 Request-Line 5.1.1 Method 5.1.2 Request-URI 5.2 요구에 의해 식별되는 자원 5.3 요구 헤더 필드 6. 응답 6.1 상태-라인 6.1.1 상태 코드 및 이유 문구 6.2 응답 헤더 필드 7 엔터티(Entity) 7.1 엔터티 헤더 필드 7.2 엔터티 본문 7.2.1 유형 7.2.2 길이 8. 접속 8.1 지속적인 접속 8.1.1 목적 8.1.2 전반적인 운영 8.1.3 프락시 서버(Proxy Servers) 8.1.4 실제적인 고려 사항 8.2 메시지 전송 필요 조건 9. Method 정의 9.1 안전 및 멱등원(冪等元) method 9.1.1 안전 method 9.1.2 멱등원 method 9.2 OPTIONS 9.3 GET 9.4 HEAD 9.5 POST 9.6 PUT 9.7 DELETE 9.8 TRACE 10. 상태 코드 정의 10.1 Informational 1xx (정보를 알려 주는 1xx) 10.1.1 100 Continue (계속) 10.1.2 101 Switching Protocols (규약 전환) 10.2 Successful 2xx (성공을 알려 주는 2xx) 10.2.1 200 OK 10.2.2 201 Created (생성 되었음) 10.2.3 202 Accepted (접수 되었음) 10.2.4 203 Non-Authoritative Information (비 인증 정보) 10.2.5 204 No Content (내용이 없음) 10.2.6 205 Reset Content (내용을 지움) 10.2.7 206 Partial Content (부분적 내용) 10.3 Redirection 3xx (방향을 재설정하는 3xx) 10.3.1 300 Multiple Choices (복수 선택) 10.3.2 301 Moved Permanently (영구 이동) 10.3.3 302 Moved Temporarily (임시 이동) 10.3.4 303 See Other (다른 것을 참조) 10.3.5 304 Not Modified (변경되지 않았음) 10.3.6 305 Use Proxy (프락시를 사용할 것) 10.4 Client Error 4xx (클라이언트 에러 4xx) 10.4.1 400 Bad Request (잘못된 요구) 10.4.2 401 Unauthorized (인증되지 않았음) 10.4.3 402 Payment Required (요금 지불 요청) 10.4.4 403 Forbidden (금지되었음) 10.4.5 404 Not Found (찾을 수 없음) 10.4.6 405 Method Not Allowed (Method를 사용할 수 없음) 10.4.7 406 Not Acceptable (접수할 수 없음) 10.4.8 407 Proxy Authentication Required (프락시 인증이 필요함) 10.4.9 408 Request Timeout (요구 시간 초과) 10.4.10 409 Conflict (충돌) 10.4.11 410 Gone (내용물이 사라졌음) 10.4.12 411 Length Required (길이가 필요함) 10.4.13 412 Precondition Failed (사전 조건 충족 실패) 10.4.14 413 Request Entity Too Large (요구 엔터티가 너무 큼) 10.4.15 414 Request-URI Too Long (Request-URI이 너무 김) 10.4.16 415 Unsupported Media Type (지원되지 않는 media type) 10.5 Server Error 5xx (서버 에러 5xx) 10.5.1 500 Internal Server Error (서버 내부 에러) 10.5.2 501 Not Implemented (구현되지 않았음) 10.5.3 502 Bad Gateway (불량 게이트웨이) 10.5.4 503 Service Unavailable (서비스를 사용할 수 없음) 10.5.5 504 Gateway Timeout (게이트웨이 시간 초과) 10.5.6 505 HTTP Version Not Supported (지원되지 않는 HTTP 버전) 11. 접속 인증 11.1 기본 인증 scheme 11.2 요약 인증 scheme 12. 내용 협상(Content Negotiation) 12.1 서버가 주도하는 협상 12.2 에이전트가 주도하는 협상 12.3 투명한 협상(Transparent Negotiation ) 13. HTTP에서의 캐시 13.1.1 캐시의 정확성 13.1.2 경고 13.1.3 캐시-제어 메커니즘 13.1.4 명백한 사용자 에이전트 경고 13.1.5 규칙 및 경고의 예외 사항 13.1.6 클라이언트가 제어하는 행태 13.2 유효일 모델 13.2.1 서버가 명시한 만기일 13.2.2 스스로 만기일을 찾음(Heuristic Expiration) 13.2.3 경과 시간 계산(Age Calculations) 13.2.4 만기일 계산 13.2.5 만기일 값을 명확하게 하기 13.2.6 복수의 응답을 명확하게 하기 13.3 검증 모델 13.3.1 최종 갱신 날짜 13.3.2 엔터티 태그 캐시 검증자(Validators) 13.3.3 약한/강한 검증자 13.3.4 엔터티 태그와 최종 갱신 날짜를 사용할 때를 결정하는 규칙 13.3.5 검증을 하지 않는 조건 법 13.4 응답을 캐시할 수 있는 정도(Cachability) 13.5 캐시에서 응답을 구축하기 13.5.1 End-to-end 및 Hop-by-hop 헤더 13.5.2 변경할 수 없는 헤더 13.5.3 헤더의 결합 13.5.4 바이트 영역(Byte Ranges)의 결합 13.6 협상을 통한 응답을 캐시하기 13.7 공유/비공유 캐시 13.8 에러 또는 불완전한 응답 캐시 행태 13.9 GET 및 HEAD의 부작용 13.10 갱신 또는 삭제 후의 무효화 13.11 의무적으로 서버를 통하여 기입 13.12 캐시 대체 13.13 History List 14. 헤더 필드 정의 14.1 Accept 14.2 Accept-Charset 14.3 Accept-Encoding 14.4 Accept-Language 14.5 Accept-Ranges 14.6 Age 14.7 Allow 14.8 Authorization 14.9 Cache-Control 14.9.1 무엇을 캐시할 수 있는가 14.9.2 캐시에 의해 무엇을 저장할 수 있는가 14.9.3 기본적인 만기일 메커니즘의 변경 14.9.4 캐시의 재검증 및 Reload 제어 14.9.5 비 변경 지시어 14.9.6 캐시 제어 확장 14.10 Connection 14.11 Content-Base 14.12 Content-Encoding 14.13 Content-Language 14.14 Content-Length 14.15 Content-Location 14.16 Content-MD5 14.17 Content-Range 14.18 Content-Type 14.19 Date 14.20 ETag 14.21 Expires 14.22 From 14.23 Host 14.24 If-Modified-Since 14.25 If-Match 14.26 If-None-Match 14.27 If-Range 14.28 If-Unmodified-Since 14.29 Last-Modified 14.30 Location 14.31 Max-Forwards 14.32 Pragma 14.33 Proxy-Authenticate 14.34 Proxy-Authorization 14.35 Public 14.36 Range 14.36.1 Byte Ranges 14.36.2 Range Retrieval Requests 14.37 Referer 14.38 Retry-After 14.39 Server 14.40 Transfer-Encoding 14.41 Upgrade 14.42 User-Agent 14.43 Vary 14.44 Via 14.45 Warning 14.46 WWW-Authenticate 15. 보안에 대한 고려 사항 15.1 클라이언트의 인증 15.2 인증 scheme을 선택할 수 있도록 함 15.3 서버 로그 정보의 남용 15.4 민감한 정보의 전송 15.5 파일 및 경로 이름에 기초한 공격 15.6 개인적인 정보 15.7 Accept 헤더와 연결된 사생활 보호의 이슈 15.8 DNS Spoofing(속이기) 15.9 Location 헤더와 Spoofing(속이기) 16. 감사의 말 17. 참고 문헌 18. 저자의 주소 19. 부록 19.1 Internet Media Type message/http 19.2 Internet Media Type multipart/byteranges 19.3 Tolerant Applications 19.4 HTTP 엔터티와 MIME 엔터티의 차이점 19.4.1 규범적인 폼으로 변환 19.4.2 날짜 형식의 변환 19.4.3 Content-Encoding 소개 19.4.4 No Content-Transfer-Encoding 19.4.5 Multipart Body-Part의 HTTP 헤더 필드 19.4.6 Transmit-Encoding 소개 19.4.7 MIME-Version 19.5 HTTP/1.0 이후 변경 사항 19.5.1 복수의 홈을 가진 웹 서버를 단순하게 하기 위한 변경 사항 및 IP 주소 보존 19.6 추가 기능 19.6.1 추가적인 요구 method 19.6.2 추가 헤더 필드 정의 19.7 이전 버전과의 호환성 19.7.1 HTTP/1.0 지속적인 연결과의 호환성
1. 서론1.1 목적하이퍼텍스트 전송 규약(HTTP)은 분산 정보시스템, 종합 정보시스템 및 하이퍼미디어 정보시스템에서사용하는 응용계층의 규약이다. HTTP는 1990년 이후 World-Wide Web 범 세계 정보 이니셔티브에의하여 사용되고 있다. "HTTP/0.9"로 언급되는 HTTP의 첫 버전은 인터넷 상에서 저장되어 있는 원래데이터(raw data)를 전송하기 위한 단순한 규약이었다. RFC 1945 [6]이 규정한 HTTP/1.0은 메시지를전송되는 문서 데이터에 대한 메타 정보 및 요구/응답 용어의 변경자를 포함하는 MIME과 유사한메시지의 형식으로 사용할 수 있도록 함으로써 규약을 향상시켰다. 그러나 HTTP/1.0은 계층적 프락시(hierarchical proxies), 캐시, 지속적인 연결의 필요성 및 가상 호스트(virtual host) 등의 영향을충분히 고려하지 않았다. 또한 "HTTP/1.0"을 지원한다고 하면서도 규약의 불완전 구현 및 오해에 의한잘못된 구현 등에 의해 응용 프로그램 사이에 종종 문제가 발생하였기에 상호 협상할 수 있는 응용프로그램이 상대방의 진정한 성능을 파악할 수 있도록 규약 버전을 갱신할 필요가 생겼다. 이 규격은 "HTTP/1.1"로 불리우는 하이퍼텍스트 전송 규약을 정의한다. 이 규약은 기능을 신뢰할 수있도록 구현하기 위해 HTTP/1.0보다 더 엄격한 필요 조건을 포함하고 있다.실제적인 정보 시스템은 단순한 조회보다 검색, 프런트-엔드(front-end) 갱신 및 주석 달기 등 많은기능을 필요로 한다. HTTP는 요구의 목적을 표시하는 일련의 개방된 method를 (open-ended setof methods) 허용한다. 이 규약은 보편적 자원 식별자(URI) [3][20], 자원 위치 (URL) [4] 또는 자원이름(URN)이 제공하는 참고 방법에 따라 method를 적용할 자원을 지칭하는 데 사용한다.메시지는 다용도 인터넷 메일 확장(MIME)에서 정의된 것처럼 인터넷 메일에서 사용되는 것과 유사한형식으로 전송된다. HTTP는 사용자 에이전트, 프락시/게이트웨이와 SMTP [16], NNTP [13], FTP [18], Gopher [2], 및 WAIS [10] 등을 지원하는 다른 인터넷 시스템 사이의 통신을 위한 범용 규약으로서 사용된다. 이러한 방식으로 HTTP는 기본적인 하이퍼미디어가 다양한 애플리케이션의 자원에 접근할 수 있도록한다. 1.2 필요 조건이 규격은 각각의 특별한 필요 조건의 중요도를 정의할 때 RFC 1123 [8]와 동일한 용어를 사용한다.이러한 용어는 다음과 같다. MUST이 단어 또는 "요구된"이라는 형용사는 해당 항목이 규격의 절대적인 필요 조건임을 의미한다. SHOULD이 단어 또는 "추천된"이라는 형용사는 특정 상황에서 해당 항목을 무시할 합당한 이유가 있을 수있다는 것을 의미한다. 그러나 충분히 함축적 의미를 이해해야 하고 다른 방법을 선택하기 전에 사례를충분히 검토해야 한다. MAY이 단어 또는 "선택적"이라는 형용사는 해당 항목이 진정으로 선택적이라는 것을 의미한다.한 판매회사는 특정 항목을 특정 시장이 요구하기 때문에 또는 예를 들어 제품의 기능을 향상시켜 주기때문에 다른 판매 회사와 달리 동일한 항목을 포함할 수 있다.구현 방법이 하나 또는 그 이상의 MUST 규약 필요 조건을 충족시켜 주지 못하면 규약에 따르지 않는것이다. 구현 방식이 모든 MUST 및 SHOULD 필요 조건을 충족한다면 "무조건적으로 충족한다"고 할 수있고, 모든 MUST 필요 조건을 충족하지만 모든 SHOULD 필요 조건을 충족하지 못한다면 "조건적으로충족한다"고 할 수 있다. 1.3 용어이 규격은 HTTP 통신의 참여자 및 객체가 수행하는 역할을 지칭하는 몇몇 용어를 사용하고 있다. connection(연결)통신을 목적으로 두 프로그램 간에 설정된 전송 계층의 가상적 회로 message(메시지)HTTP 통신의 기본 전송 단위. 4 장에 규정된 의미론을 따르는 구조적인 데이터 표현 형태이며, 일련의8 비트(octets)로 구성되어 있고 연결을 통하여 전송된다. request(요구) 5 장에 규정된 HTTP 요구 메시지. response(응답) 5 장에 규정된 HTTP 응답 메시지. resource(자원)3.2절에 규정되어 있는 URI에 의하여 식별되는 네트워크 데이터 객체 또는 서비스. 자원은 다양한 표현형태(예를 들어 언어, 데이터 형식, 크기 및 해상도)를 지닐 수 있으며 다양한 방법으로 변형될 수 있다. entity(엔터티)요구나 응답 메시지의 페이로드(payload)로서 전송되는 정보. 엔터티는 7 장에서 설명된 대로 Entity-Header필드 형태의 메타 정보 및 Entity-Body 형태의 내용으로 구성되어 있다. representation(표현)12 장에서 기술한 내용 협상의 통제를 따르는 응답에 포함된 엔터티. 특정한 응답 상태와 연관된 다수의표현 방법이 있을 수 있다. content negotiation(내용 협상)12 장에서 기술한 대로 요구를 처리할 때 적절한 표현 방법을 선택하는 메커니즘. 어떠한 응답에서는엔터티의 표현은 협상할 수 있다.(에러 응답 포함) variant(변형자)자원은 특정한 경우에 자원과 관련된 하나 이상의 표현 방식을 가질 수 있다. 이러한 각각의 표현 방식을"변형자"라고 부른다. "변형자"라는 용어를 사용한다고 해서 자원이 반드시 내용 협상의 대상인 것은아니다. client(클라이언트)요구 메시지를 전송할 목적으로 연결을 설정하는 프로그램. user agent(사용자 에이전트)요구 메시지를 시작하는 클라이언트. 이것은 종종 브라우저, 편집기, 스파이더(웹을 탐색하는 로봇) 또는다른 사용자 툴(tool)일 수 있다. server(서버)요구 메시지를 처리하기 위해 접속을 수신하는 애플리케이션으로서 응답 메시지를 전송한다.어떤 프로그램이든 동시에 클라이언트와 서버가 될 수 있다. 이 규격에서 이 용어를 사용하는 것은프로그램의 일반적인 능력을 참조하기보다는 특정한 연결을 위해 프로그램이 수행하는 역할만을 참조하는것이다.마찬가지로 어떠한 서버도 원서버, 프락시, 게이트웨이, 터널 등 각 요구의 성격에 따라 동작을 전환하는역할을 할 수 있다. origin server(원서버)해당 자원이 보관되어 있거나 자원을 생성할 수 있는 서버. proxy(프락시)다른 클라이언트를 대신하여 요구를 작성할 목적으로 서버와 클라이언트의 역할을 모두 수행하는 중간프로그램. 요구는 내부적으로 처리되어 가능하면 해석되어 다른 서버로 전달된다. 프락시는 이 규격의클라이언트와 서버의 필요 조건을 모두 구현해야만 한다. gateway(게이트웨이)다른 서버를 위해 중간 역할을 하는 서버. 프락시와는 달리 게이트웨이는 요구 메시지를, 요청받은자원을 서비스하는 최종적인 원서버처럼 수신한다. 요구한 클라이언트는 자신이 게이트웨이와 통신하고있다는 것을 알지 못할 수 있다. tunnel(터널)두 연결 사이를 무조건 중계하는 역할을 하는 중간 프로그램. 활성화되면 비록 HTTP 요구에 의하여시작되지만 터널은 HTTP 통신의 참여자로 간주되지 않는다. 터널은 중계하고 있는 양 쪽의 연결이종결되면 사라진다. cache(캐시)프로그램이 응답 메시지를 저장하는 로컬 저장소. 메시지 보관, 조회 및 삭제를 제어하는 하부 시스템이기도하다. 캐시는 응답 시간, 향후 네트워크 대역폭 소모 및 동일한 요구를 감소시킬 목적으로 캐시할수 있는 응답을 저장한다. 어떤 클라이언트나 서버도 캐시를 포함할 수 있다. 단지 터널 역할을 하는서버는 캐시를 사용할 수 없다. cachable(캐시할 수 있는)응답 메시지의 사본을 저장하여 계속적인 요구 응답에 사용할 수 있으면 응답을 캐시할 수 있다고 한다.HTTP 응답의 캐시 가능 여부를 결정하는 원칙은 13 장에 정의되어 있다. 자원을 캐시할 수 있다 하더라도 캐시가 특정 요구에 대하여 캐시 된 사본을 사용할 수 있는지 여부에대한 추가적인 제한 사항이 있을 수 있다. first-hand(직접)응답이 직접적으로 오며 원서버로부터 하나 또는 그 이상의 프락시를 거쳐옴으로써 발생하는 불필요한지연이 없을 경우 응답이 직접 온다고 할 수 있다. 또한 검증이 원서버에서 직접 이루어진다면 응답이직접 온다고 할 수 있다. explicit expiration time(명백한 유효 시간)원서버가 추가적인 검증 없이는 캐시에 의해 엔터티를 더 이상 되돌려 주지 않기로 한 시간. 즉, 원서버가캐시된 데이터의 유효성을 보장할 수 있는 시간. heuristic expiration time(자동으로 설정되는 유효 시간)분명한 유효 시간이 설정되어 있지 않을 때 캐시가 할당하는 유효 시간 age(경과 시간)응답 메시지의 경과 시간은 원서버로부터 전송된 후, 또는 성공적으로 검증된 후의 시간. freshness lifetime(신선한 기간)응답의 생성 시점과 유효시간 만기 시점 사이의 시간 길이 fresh(신선한)응답의 경과 시간이 신선한 기간을 넘어서지 않았을 때 응답이 신선하다고 할 수 있다. stale(낡은)응답의 경과 시간이 신선한 기간을 넘어섰다면 응답이 낡았다고 할 수 있다. semantically transparent(의미상으로 분명한)성능을 향상시키고자 하는 목적을 제외하고 캐시의 사용이 요구하는 클라이언트나 원서버에 영향을미치지 않을 때 특정한 요구에 대하여 캐시가 "의미상으로 분명하게" 작동한다고 할 수 있다. 캐시가의미상으로 분명할 때 클라이언트는 원서버가 직접 처리했을 때와 완전히 동일할 응답을 수신하게된다.( hop-by-hop 헤더는 제외). validator(검증자)캐시 엔트리가 엔터티의 복사본과 동일한지 알아내는 데 사용하는 규약 요소(예를 들면 엔터티태그나 Last-Modified 시간)1.4 Overall OperationHTTP 규약은 요구/응답 규약이다. 클라이언트는 요구 method, URI, 규약 버전의 형태로 서버에 요구메시지를 전송한다. 요구 변경자, 클라이언트 정보, 서버와의 접속에 사용되는 본문 내용을 포함하는MIME 유형의 메시지가 뒤따른다. 서버는 메시지의 규약 버전 및 성공 또는 실패 코드를 포함하는 상태정보로서 응답한다. 서버 정보, 엔터티 메타 정보, Entity-Body 내용을 포함하는 MIME 유형의 메시지도뒤따른다.대부분의 통신은 사용자 에이전트가 구동하며 특정 원서버에 적용할 요구로 구성되어 있다. 가장 단순한 경우 이것은 사용자 에이전트(UA)와 원서버(O) 사이의 단일 접속(v)에 의해 성취할 수있을 것이다. request chain ---------------------->UA ---------------- v ------------------- O <--------------------- response chain좀 더 복잡한 상황은 Request/Response chain에 하나 또는 그 이상의 중간 매개자가 있는 경우이다.프락시, 게이트웨이 및 터널의 세 가지 일반적인 중간 매개 형태가 있다. 프락시는 전송 에이전트로절대 표현 형태의 URI 요구를 수신하여 메시지의 전체 혹은 부분을 재작성한 후 URI가 표시하는 서버로재구성된 요구 메시지를 전달한다. 게이트웨이는 수신 에이전트로 다른 서버 위의 계층 역할을 수행하며필요하다면 원서버의 규약에 맞도록 요구를 해석하기도 한다. 터널은 메시지를 변경하지 않고 두 연결지점을 연결하는 중계역할을 수행한다. 터널은 통신(communication)이 중간 매개자가 메시지의 내용을이해할 수 없을 때라도 방화벽과 같은 중간 매개자를 통과할 필요가 있을 때 사용한다. request chain ------------------------------------->UA -----v----- A -----v----- B -----v----- C -----v----- O <------------------------------------ response chain위의 도표는 사용자 에이전트와 원서버 사이의 세 중간 매개자(A, B 및 C)를 보여 준다. 전체 고리를통과하는 요구 또는 응답 메시지는 네 개의 별도 연결을 통과하게 된다. 몇몇 HTTP 통신 선택 사항은최고 근접 거리의 비터널 이웃과의 통신, 연쇄적 연결 고리의 마지막 부분에만 또는 연결 고리에 따르는모든 연결에 적용되기 때문에 이러한 구분은 중요하다. 그림이 선형이지만 각 참여자는 복수의 동시통신에 참여할 수 있다. 예를 들어 B는 A의 요구를 처리함과 동시에 A를 제외한 복수의 클라이언트요구를 수신하고/수신하거나 C 이외의 서버에게 요구를 전송할 수 있다.터널 역할을 수행하는 것이 아닌 통신에 참여하는 어떤 것이라도 요구를 처리할 때 내부 캐시를 사용할수 있다. 캐시의 효과는 연결 고리를 따라 참가자 중 하나가 해당되는 요구에 적용할 수 있는 캐시된응답을 갖고 있다면 Request/Response chain이 짧아진다. 다음은 UA 또는 A가 캐시하지 않은요구에 대한 O (C를 경유) 초기 응답의 사본을 B가 가지고 있을 때의 결과 고리를 설명하고 있다. request chain --------->UA -----v----- A -----v----- B - - - - - - C - - - - - - O <-------- response chain보통 모든 응답을 캐시할 수 있는 것은 아니며 어떤 요구는 캐시 방식에 특별 요구를 하는 변경자를포함할 수 있다. 13 장에 캐시 방식과 캐시할 수 있는 응답에 대한 필요 조건이 기록되어 있다.사실상 World Wide Web에는 현재 실험되고 있거나 배포되고 있는 캐시와 프락시의 다양한 아키텍쳐와환경설정 방법이 있다. 이러한 것 중에는 대륙간 대역폭을 절약하기 위한 프락시 캐시의 국가적 계층,캐시 엔트리를 배포하거나 복수로 배포하는 시스템, CD-ROM 등을 통하여 캐시 된 데이터의 하부세트를 배포하는 조직 등이 있다. HTTP 시스템은 광대역 연결을 통한 기업 인트라넷, 저동력 무선연결의 PDA를 통한 연결 및 간헐적인 연결에 사용된다. HTTP/1.1의 목적은 고도의 신뢰성과 신뢰성을확보할 수 없다면 신뢰할 수 있는 실패의 표시 기능을 지닌 웹 응용프로그램을 개발하는 개발자의요구를 충족하는 규약 구조물을 새로 소개하면서도 이미 배포된 다양한 환경을 지원하는 것이다.HTTP 통신은 대개 TCP/IP 연결을 통하여 이루어진다. 기본 포트는 TCP 80 이지만 다른 포트를 사용할수도 있다. 그러나 이것은 HTTP가 인터넷 상의 다른 규약이나 다른 네트워크 위에서 구현될 수 없게하는 것은 아니다. HTTP는 단순히 신뢰할 수 있는 전송 수단을 가정할 뿐이며 이러한 보장을 해 줄 수있는 어떠한 규약을 사용해도 된다. HTTP/1.1의 요구 응답 구조를 적용하고자 하는 규약의 전송 데이터단위로 배치(mapping)하는 것은 이 규격의 범위 밖의 것이다.HTTP/1.0에서 대부분의 구현 방식은 각각의 요구/응답 교환에 새로운 접속을 사용하는 것이다.또한 HTTP/1.1에서는 하나의 접속을 하나 또는 그 이상의 요구/응답 교환에 사용할 수 있으나 연결이여러 가지 이유로 단절될 수 있다.( 8.1 절 참조)
2. 기호 관례 및 일반적인 문법2.1 추가된 BNF이 문서에서 명시된 모든 메커니즘은 설명형 문구로서 RFC 822 [9]에서 사용한 것과 유사한 추가된Backus-Naur Form (BNF)으로 설명되어 있다. 구현자는 이 규격을 이해하기 위하여 이러한 기호에익숙할 필요가 있다. 추가된 BNF는 다음의 구성 요소를 포함한다.name = definition규칙의 이름이 이름 그 자체(둘러싸는 "<" 및 ">"이 없는)이며 정의 부분과는 등호 문자("=")로 구별된다.계속되는 공백 문자는 규칙에 대한 규정이 한 줄 이상에 걸쳐 있음을 표시하는 들여쓰기의 경우에만의미가 있다. SP, LWS, HT, CRLF, DIGIT, ALPHA 등과 같은 몇몇 기본 규칙은 대문자로만 사용한다.정의문 내에서 소괄호는 규칙 이름의 사용 구별을 용이하게 해줄 경우에는 언제든지 사용한다."literal"인용 부호로 문자 텍스트 주위를 감싼다. 별도의 언급이 없으면 문자는 대소문자를 구별한다.rule1 | rule2막대 ("|")로 구분된 요소는 선택 사항이다. 예를 들어 "yes |no" 는 yes 나 no어느 것이든 가능하다.(rule1 rule2)괄호로 둘러싼 요소는 단일 요소로 취급한다. 따라서 "(elem (foo | bar) elem)"는 "elem foo elem"및 "elem bar elem"의 토큰 순서를 허용한다.*rule이것은 반복을 의미하는 것으로서 뒤이어서 나올 #rule과 혼동을 일으키는 표현 방식이므로 유의해야한다. 반복을 통해 이루어지는 결과는 하나의 단어나 수와 같이 한 개 요소의 표현 형태로 되는 것이며,#rule에서는 똑같은 반복이지만 여러 개 단어나 수의 열 형태와 같이 여러 개 요소의 나열 형태로 표현되는 것이다. *element와 같은 표기 방법으로 쓰인다. 이것은 적어도 n개와 최대 m개의 요소로구성되는 한 가지 결과를 의미한다. 즉, 1*2DIGIT 라는 표현은 숫자가 적어도 한 개 최대 두 개로 구성되어 한 개의 수를 나타낸다는 뜻이다. 4는 한 가지 예이며, 45도 한 가지 예가 된다. 그러나 345의 경우에는 숫자 세 개로 구성된 한 개 요소이므로 최대 갯수에 위배되어 적합하지 않다.n과 m은 생략될 수 있으며, 이 경우에 n의 기본값은 0이고 m의 기본값은 무한대이다. 그러므로 "*(element)"는 0개를 포함해서 어떤 개수라도 가능하고, "1*element"의 경우는 한 요소의표현에 있어 적어도 한 개는 있어야 하며 최대 갯수에는 제한이 없다.[rule]대괄호는 선택 요소를 둘러 싼다. "[foo bar]" 는 "*1(foo bar)"와 동일하다.N rule특정 횟수의 반복을 나타낸다. "(element)" 은 "*(element)"와 동일하다. 즉 요소(element)가 정확하게 번 표시된다. 따라서 2 DIGIT 는 2 자리 숫자, 3 ALPHA 는 세 개의알파벳 문자로 구성된 문자열이다.#rule앞서 설명한 것처럼 반복을 나타내긴 하지만 요소들의 나열로서 표현되는 것이다. 즉, 1#DIGIT 라고하면 여러 개의 수로 구성된 수열로서 표현되는데, 최소 한 개의 수는 있어야 하고 최대 갯수는 제한이없는 수열이 된다. 각 요소들 사이의 구분은 ","와 LWS를 이용하는데, 여러 개의 나열 형태를 쉽게표현할 수 있게 해준다. 예를 들어, (*LWS element *(*LWS "," *LWS element)) 이것을 간단하게1#element 이와 같이 표현할 수 있다. 또 다른 예를 들자면, 1#2(2DIGIT)이것은 숫자 두 개로 구성된수가 적어도 한 개가 있어야 하며 최대 두 개까지 가능하다는 것이다. 즉, 23 이렇게 표현될 수도 있고, 23, 56 이렇게 두 개로 표현될 수도 있다. 이것이 *rule과의 차이점이고, #rule 에서도 "#element" 의구성이 그대로 성립한다. 이에 대한 설명은 *rule 의 경우와 같다. ","를 이용하여 나열함에 있어, nullelement가 허용된다. 예를 들어, 1#3(2DIGIT)과 같은 표현식에 대해23, , 56, 34 이렇게 null element표시가 가능하지만, 실제 갯수는 세 개로서 간주된다. 따라서 최소 한 개 최대 세 개의 제한에 위배되지않는다.; comment규칙 문장에서 오른쪽으로 약간 떨어져 있는 세미콜론은 해당 라인의 끝에까지 계속되는 주석의 시작을의미한다. 이것은 규격과 병행하여 적절한 설명을 포함시키기 위한 방법이다.implied *LWS두 개의 인접한 단어 (token or quoted-string) 또는 인접한 토큰(tokens)과 식별자 (tspecials) 사이에LWS (linear whitespace)가 포함될 수 있다. 여기서 두 개의 토큰 사이에는 반드시 적어도 하나의식별자가 존재하여 각기 하나의 토큰으로 간주되지 않게끔 구별되어야 한다.2.2 기본 규칙다음의 규칙은 기본적인 분석 구조를 설명하기 위해 이 규격 전반에 걸쳐 사용되고 있다. US-ASCII로코드화 된 문자 집합은 ANSI X3.4-1986 [21]에 의하여 규정되었다. OCTET = <모든 8-bit 연속 데이터> CHAR = <모든 US-ASCII 문자 (octets 0 - 127)> UPALPHA = <모든US-ASCII 대문자 "A".."Z"> LOALPHA = <모든 US-ASCII 소문자 "a".."z"> ALPHA = UPALPHA | LOALPHA DIGIT = <모든 US-ASCII 숫자 "0".."9"> CTL = <모든 US-ASCII 제어 문자 (octets 0 - 31) 및 DEL (127)> CR = <US-ASCII CR, 캐리지 리턴(13)> LF = <US-ASCII LF, 라인피드 (10)> SP = <US-ASCII SP, 스페이스 (32)> HT = <US-ASCII HT, 수평 탭 (9)> <"> = <US-ASCII 이중 인용 부호(34)>HTTP/1.1은 연속적인 CR LF를 Entity-Body를 (부록 19.3 참조) 제외한 모든 규약 요소의 라인 마감부호로 정의한다. Entity-Body 내에서의 라인 마감 부호는 3.7 절에서 설명된 것처럼 연관된 mediatype에 의하여 정의한다. CRLF = CR LFHTTP/1.1 헤더는 계속되는 라인이 스페이스나 수평 탭으로 시작한다면 복수의 라인에 걸쳐 계속작성할 수 있다. 폴딩(folding)을 포함한 모든 선형 공백 스페이스는 SP와 동일한 의미를 가진다. LWS = [CRLF] 1*( SP | HT )TEXT 규칙은 메시지 분석기가 해석하지 않도록

'JAVA > JSP_Servlet' 카테고리의 다른 글

[펌] [MAIL] MIME 관련  (0) 2005.01.17
[펌] 코드시스템및 한글문자체계  (0) 2005.01.17
[펌]Single Sign On...  (0) 2005.01.17
JSP 다운로드시 헤더 ㅡㅡ  (0) 2005.01.13
헐.. 죽이넹  (0) 2004.12.21
Posted by tornado
|

'JAVA > JSP_Servlet' 카테고리의 다른 글

[펌] 코드시스템및 한글문자체계  (0) 2005.01.17
[펌] http RFC  (0) 2005.01.17
JSP 다운로드시 헤더 ㅡㅡ  (0) 2005.01.13
헐.. 죽이넹  (0) 2004.12.21
얼... 좋은걸~  (0) 2004.12.17
Posted by tornado
|
[방문히트이벤트] 4300 히트를 잡아라! (이웃한정)
박종복님이 당첨되었습니다.
Posted by tornado
|

[nio] Channels 클래스

JAVA/JSE 2005. 1. 17. 11:20

간단한 회사 웹 메일 만들던 중 메일 발송 부분에서 자료 첨부를 Struts 의 FormBean 을 이용해서

파일을 보내는데 nio 를 이용해 보려고 했다.

 

폼 파일에서는 FormFile.getInputStream() 이라는 것만 제공해 주는데..

무식하게 스리 getInputStream() 으로 반납되는 InputStream 을 FileInputStream 으로 바꾸려

뻘짓거리를 ㅡㅡ;;

 

그래서 API 를 뒤져보니.. java.nio.channels 클래스를 보니 newChannel() 메소드 발견~

 

아래와 같이 처리 함.

 

File file = new File("경로","파일명");

InputStream is = formFile.getInputStream();

ReadableByteChannel byteChannel = Channels.newChannel( is )

FileChannel outputChannel = new FileOutputStream(file).getChannel();

outputChannel.transferFrom(byteChannel, 0, is.available());

 

일단 되는것 확인...

 

Channels 클래스에 InputStream, OutputStream, Reader, Writer 로 변환해 주는게 다 모여있넹.

 

대충 알았으니 이제 놀자 ㅋㅋ

 

 

 

 

'JAVA > JSE' 카테고리의 다른 글

[펌] 손쉬운 정적 분석 툴로 버그 잡기  (0) 2005.05.11
FileFilter... 까먹기 싫어~~~  (0) 2005.02.12
[펌] Sorting Algorithm  (0) 2005.01.15
[link] 자바 검색 봇~  (0) 2005.01.11
[펌] [Collection Framework ] List, Set and Map  (0) 2004.12.29
Posted by tornado
|

[펌] Sorting Algorithm

JAVA/JSE 2005. 1. 15. 17:16

Sorting algorithm

From Wikipedia, the free encyclopedia.

In computer science and mathematics, a sorting algorithm is an algorithm that puts elements of a list in a certain order. The most used orders are numerical order and lexicographical order. Efficient sorting is important to optimizing the use of other algorithms (such as search and merge algorithms) that require sorted lists to work correctly; it is also often useful for canonicalizing data and for producing human-readable output.

Contents [hide]


 

Classification

Sorting algorithms used in computer science are often classified by:

  • computational complexity (worst, average and best behaviour) in terms of the size of the list (n). Typically, good behaviour is O(n log n) and bad behaviour is O(n2). Sort algorithms which only use an abstract key comparison operation always need at least O(n log n) comparisons on average; sort algorithms which exploit the structure of the key space cannot sort faster than O(n log k) where k is the size of the keyspace.
  • memory usage (and use of other computer resources)
  • stability: stable sorting algorithms maintain the relative order of records with equal keys. That is, a sorting algorithm is stable if whenever there are two records R and S with the same key and with R appearing before S in the original list, R will appear before S in the sorted list.

When equal elements are indistinguishable, such as with integers, stability is not an issue. However, say we were sorting these pairs of numbers by their first coordinate:

(4, 1) (3, 1) (3, 7) (5, 6) 

In this case, two different results are possible, one which maintains the relative order records with equal keys, and one which doesn't:

(3, 1) (3, 7) (4, 1) (5, 6) (order maintained) (3, 7) (3, 1) (4, 1) (5, 6) (order changed) 

Unstable sorting algorithms may change the relative order of records with equal keys, stable sorting algorithms never so. Unstable sorting algorithms can be specially implemented to be stable. One way of doing this is to artificially extend the key comparison, so that comparisons between two objects with otherwise equal keys are decided using the order of the entries in the original data order as a tie-breaker. Remembering this order, however, often involves an additional space penalty.

Table of sorting algorithms

In this table, n is the number of records to be sorted, k is the number of distinct keys, and u is the number of unique records.

Stable

Unstable

Questionable sort algorithms not intended for production use:

Summaries of the popular sorting algorithms

Bubble sort

Bubble sort is the most straightforward and simplistic method of sorting data that could actually be considered for real world use. The algorithm starts at the beginning of the data set. It compares the first two elements, and if the first is greater than the second, it swaps them, then repeats until no swaps have occurred on the last pass. The algorithm does this for each pair of adjacent elements until there are no more pairs to compare. This algorithm, however, is vastly inefficient, and is rarely used except in education (i.e., beginning programming classes). A slightly better variant is generally called shuttle sort, and works by inverting the ordering criteria, and the pass direction on alternating passes.

Insertion sort

Insertion sort is similar to bubble sort, but is more efficient as it reduces element comparisons somewhat with each pass. An element is compared to all the prior elements until a lesser element is found. In other words, if an element contains a value less than all the previous elements, it compares the element to all the previous elements before going on to the next comparison. Although this algorithm is more efficient than the Bubble sort, it is still inefficient compared to many other sort algorithms since it, and bubble sort, move elements only one position at a time. However, insertion sort is a good choice for small lists (about 30 elements or fewer), and for nearly-sorted lists. These observations can be combined to create a variant of insertion sort which works efficiently for larger lists. This variant is called shell sort (see below).

Shell sort

Shell sort was invented by Donald Shell in 1959. It improves upon bubble sort and insertion sort by moving out of order elements more than one position at a time. One implementation can be described as arranging the data sequence in a two-dimensional array (in reality, the array is an appropriately indexed one dimensional array) and then sorting the columns of the array using the Insertion sort method. Although this method is inefficient for large data sets, it is one of the fastest algorithms for sorting small numbers of elements (sets with less than 10 or so elements). Another advantage of this algorithm is that it requires relatively small amounts of memory.

See work-in-place article for the list of sorting algorithms that can be written as work-in-place.

Merge sort

Merge sort takes advantage of the ease of merging already sorted lists into a new sorted list. It starts by comparing every two elements (i.e. 1 with 2, then 3 with 4...) and swapping them if the first should come after the second. It then merges each of the resulting lists of two into lists of four, then merges those lists of four, and so on; until at last two lists are merged into the final sorted list.

Heapsort

Heapsort is a member of the family of selection sorts. This family of algorithms works by determining the largest (or smallest) element of the list, placing that at the end (or beginning) of the list, then continuing with the rest of the list. Straight selection sort runs in O(n2) time, but Heapsort accomplishes its task efficiently by using a data structure called a heap, which is a binary tree where each parent is larger than either of its children. Once the data list has been made into a heap, the root node is guaranteed to be the largest element. It is removed and placed at the end of the list, then the remaining list is "heapified" again.

Radix sort

Some radix sort algorithms are counterintuitive, but they can be surprisingly efficient. If we take the list to be sorted as a list of binary strings, we can sort them on the least significant bit, preserving their relative order. This "bitwise" sort must be stable, otherwise the algorithm will not work. Then we sort them on the next bit, and so on from right to left, and the list will end up sorted. This is most efficient on a binary computer (which nearly all computers are). If we had a ternary computer, we would regard the list as a list of base 3 strings and proceed in the same way. Most often, the bucket sort algorithm is used to accomplish the bitwise sorting.

Radix sort can also be accomplished from left to right, but this makes the algorithm recursive. On a binary (radix 2) computer, we would have to sort on the leftmost bit, and then sort the sublist with 0 as the leftmost bit, and then sort the sublist with 1 as the leftmost bit, and so on.

Graphical representations

Microsoft's "Quick" programming languages (such as QuickBASIC and QuickPascal) have a file named "sortdemo" (with extension BAS and PAS for QB and QP, respectively) in the examples folder that provides a graphical representation of several of the various sort procedures described here, as well as performance ratings of each.

Also, a program by Robb Cutler called The Sorter (http://faculty.harker.org/robbc/Pages/Software/TheSorter/TheSorter.htm) for classic Mac OS performs a similar function. It illustrates Quick Sort, Merge Sort, Heap Sort, Shell Sort, Insertion Sort, Bubble Sort, Shaker Sort, Bin Sort, and Selection Sort.

See also

External links and references

'JAVA > JSE' 카테고리의 다른 글

FileFilter... 까먹기 싫어~~~  (0) 2005.02.12
[nio] Channels 클래스  (0) 2005.01.17
[link] 자바 검색 봇~  (0) 2005.01.11
[펌] [Collection Framework ] List, Set and Map  (0) 2004.12.29
소스포지.. 디컴파일해두 소스 안보여주는  (0) 2004.11.23
Posted by tornado
|

 

김 영 동

           - 산 행 (바람의 소리:1999) -

 

국악의 대중음악화, 현대음악화에 힘써온 김영동님의 곡입니다. 

너무나 유명한 '삼포가는 길'과 '어디로 갈꺼나'는 다들 아실 겁니다.

특히 삼포가는 길은 동명 TV 드라마의 주제곡으로도 쓰였지요. 영화음악에서도 이분의 작품을

접할 수 있지요.

깊어가는 가을...

이번 주말엔 가까운 도봉산이라도 올라야겠습니다.

 

 


'이것저것 > 음악' 카테고리의 다른 글

[펌] Limp Bizkit - Nookie  (0) 2005.04.22
[펌] 크래쉬 / N.EX.T - 니가 진짜로 원하는게 뭐야  (0) 2005.04.14
[펌] 조동진 - 제비꽃  (0) 2005.01.10
[펌] 이병우 - 새  (0) 2004.12.03
[펌] Keren Ann - Not Going Anywhere  (0) 2004.08.30
Posted by tornado
|

[펌] MySQL의 SQL

SQL 2005. 1. 14. 12:54

3장 MySQL의 SQL

MySQL 데이터 베이스에서 데이터를 입/출력할 때에는 구조화된 질의 언어(SQL, Structured Query Language)를 사용한다. SQL을 사용하면 데이터를 검색하거나 입력, 수정, 삭제할 수 있다. MySQL과 데이터를 주고받을 때 가장 기본이 되는 도구는 SQL이다. 애플리케이션이나 그래픽 사용자 인터페이스를 통해 데이터베이스를 액세스할 때에도 애플리케이션에서는 SQL을 만들어낸다.
SQL은 일종의 "자연 언어"다. 바꿔 말하면 SQL 선언문은(적어도 겉으로 보기에는) 영어 문장의 구문과 비슷하게 생겼다. 이러한 방법을 사용하면 장점도 있고 단점도 있지만 결국 C, 자바, 펄과 같은 일반 언어와 비슷하다.
SQL 기초
SQL은 규격화된 규칙을 따른다는 점에서 "구조화"된 언어이다. 컴퓨터 프로그램을 이용하면 정형화된 SQL 질의를 쉽게 파싱할 수 있다. 오라일리에서 나온 존 레바인, 토니 메이슨, 더그 브라운의 lex dy yacc, end ed. (오라일리, 1992)에서는 언어를 해석하기 위한 프로그램을 만드는 예로 SQL 문법을 구현하고 있다. 질의(query)는 데이터베이스 서버로 전송되는 규격화된 명령어이며 이 명령어를 통해 원하는 동작을 처리할 수 있다. SQL 질의의 예로 다음과 같은 구문을 들 수 있다:
 SELECT name FROM people WHERE name LIKE 'Stac%'
위의 질의를 보면 SQL 구문은 영어 구문을 조금 이상하게 써 놓은 것과 매우 비슷하다는 사실을 알 수 있다(Select names from a list of people where the names are like Stac). SQL에서는 다른 컴퓨터 언어에서 많이 사용하는 특별한 형식이나 특수 문자를 거의 사용하지 않는다.
SQL 이야기
SQL은 코드(Codd) 박사가 관계형 데이터베이스의 개념을 개발하고 얼마 지나지 않은 1970년대에 IBM에서 처음 탄생했다. SQL은 처음 개발되었을 때부터 강력하면서도 쉽게 배울 수 있는 언어로 제작되었다. 영어처럼 자연 언어와 매우 비슷해서 컴퓨터 기술에 능숙하지 않은 사람들도 쉽게 배울 수 있다. 1970년대에도 그랬듯이 SQL의 이러한 특징은 지금도 큰 장점으로 작용하고 있다.
1970년대 초반에는 소위 말하는 해커가 없었다. BASIC을 배우거나 HTML로 웹 페이지를 만들면서 어린 시절을 보낸 사람이 없었기 때문이다. 컴퓨터 프로그래밍을 하는 사람은 컴퓨터를 속속들이 아는 사람들뿐이었다. SQL은 컴퓨터 기술을 잘 모르는 회계, 비즈니스 및 행정 담당자들도 손쉽게 관계형 데이터베이스를 활용할 수 있도록 하기 위해 개발되었다.
SQL이 컴퓨터 기술자가 아닌 일반인들에게 선풍적인 인기를 끌게 되자 1980년대에 오라클에서는 세계 최초로 대중적으로 사용할 수 있는 상용 SQL 시스템을 출시하기 시작했다. 오라클 SQL은 엄청난 반향을 불러 일으켰고, SQL을 기반으로 하는 산업 전반에 급속하게 퍼져 나갔다. 그 이후로 사이베이스, 인포믹스, 마이크로소프트를 비롯한 여러 회사에서 SQL 기반의 관계형 데이터베이스 관리 시스템(RDBMS)을 내놓게 되었다.
오라클 및 다른 경쟁 회사들이 막 증가하기 시작할 무렵에도 SQL은 비교적 최신 기술에 속했고, 아직 표준이 정해지지 않은 상태였다. 1989년이 되어서야 ANSI 표준 위원회에서 최초의 공식 SQL 표준을 제정하였다. 지금은 그 당시에 발표된 표준안을 SQL89라고 한다. 하지만 그 표준안에서는 SQL 언어의 기술적인 구조를 제대로 정의하지 못했다. 그로 인해 다양한 상용 SQL 언어가 어느 정도 비슷해지긴 했지만 여전히 문법이 조금씩 차이가 나서 서로 다른 시스템을 사용하기가 까다로웠다. 결국 1992년이 되어서야 ANSI SQL 표준이 제대로 정립되었다.
1992년에 발표된 표준안은 SQL92 또는 SQL2라고 한다. SQL2 표준에서는 상업용 소프트웨어에서 사용하는 다양한 확장 기능을 최대한 많이 포함시켰다. 대부분의 교차 DBMS 도구는 관계형 데이테베이스에서 사용하는 방법과 같이 SQL2를 바탕으로 표준화되었다. 하지만 SQL2 표준안에서 워낙 확장성을 중요시했기 때문에 표준을 완벽하게 따르는 관계형 데이터베이스는 너무 복잡하고 자원을 많이 소모한다.
SQL2가 SQL의 최종 표준안은 아니다. 객체 지향 데이터베이스 관리 시스템(OODBMS, Object-Oriented Database Management System)과 객체 관계형 데이터베이스 관리 시스템(ORDBMS, Object-Relational Database Management System)이 인기를 얻으면서 SQL 표준에도 객체 지향 데이터베이스 액세스를 지원해야 할 필요성이 제기되었다. 가장 최근에 나온 SQL3에서는 객체 지향 데이터베이스 액세스를 지원한다.
MySQL에서는 데이터베이스 서버 개발이라는 사업 분야에 새로운 방식으로 접근하였다. 몬티는 대기업 제품에 비해 성능이 떨어지는 새로운 초대형 RDBMS를 만들기보다 자주 쓰이는 SQL 기능만 제공하는 작고 빠른 데이터베이스를 만들기로 했다. 시간이 지나면서 MySQL은 더 많은 기능을 지원하게 되었고, 요즘은 데이터베이스 애플리케이션을 만들 때 필요한 거의 모든 기능을 제공한다.
SQL 설계
앞에서도 말했듯이 SQL은 간단하면서도 확실하게 정의된 명령문 형태의 구조를 가지기 때문에 컴퓨터 언어라기보다는 사람들이 사용하는 언어에 더 가깝다. "질의"라고 하는 SQL 명령은 영어 구문과 마찬가지로 언어 요소별로 나눌 수 있다. 다음과 같은 예를 생각해보자:
 CREATE  TABKE    people (name CHAR(10)) 동사    목적어     형용사구 INSERT INTO people   VALUE ('me') 동사   간접 목적어   직접목적어 SELECT   name         FROM people     WHERE name LIKE '%e' 동사     직접목적어   간접 목적어        형용사구
MySQL을 포함해서 대부분의 SQL에서는 대소문자를 구분하지 않는다. SQL 키워드의 대소문자는 전혀 구분하지 않는다. 위에 있는 CREATE 구문의 예는 다음과 같이 입력해도 된다:
 cREatE TAblE people (name cHAR(10))
하지만 SQL 키워드를 제외한 부분에서는 대소문자르르 구분한다. MySQL에서는 데이터베이스, 테이블 및 열의 이름에서도 대소문자를 구분한다. 하지만 모든 데이터베이스 엔진에서 이런 식으로 대소문자를 구분하지는 않는다. 따라서 데이터베이스에서 사용할 수 있는 애플리케이션을 만들고 싶다면 모든 이름에 사용하는 대소문자를 확실히 구분하는 것이 좋다.
SQL 질의는 반드시 동사로 시작한다. 이 동사는 데이터베이스 엔진에서 취할 동작을 나타낸다. 선언문의 나머지 부분은 동사에 따라 다르지만 항상 정해진 형식으로 따른다. 동작을 취할 대상의 이름을 적은 다음 그 동작에서 사용할 데이터를 기술한다. 예를 들어, CRETE TABLE people (name CHAR(10))이라는 문장에서는 CREATE라는 동사를 사용하고, 그 뒤에 TABLE이라는 목적어가 온다. 질의의 나머지 부분에는 새로 만들 테이블에 대한 설명이 들어간다.
SQL 질의는 클라이언트(사용자가 데이터베이스와 데이터를 주고받는 창구같은 역할을 하는 애플리케이션)에서 입력한다. 클라이언트에서는 사용자의 동작에 따라 질의를 구성한 다음 그 질의를 SQL 서버에 보낸다. 서버에서는 그 질의를 처리하고 주어진 동작을 수행한다. 서버에서 할 일을 끝마치면 어떤 값을 클라이언트에 보낸다.
SQL의 주된 목적은 데이터베이스 서버에 어떤 동작을 전달하는 것이기 때문에 범용 언어만큼 유연성이 뛰어나지는 않다. SQL의 거의 모든 기능은 데이터 추가, 변경, 읽기와 같은 데이터베이스의 입출력과 연관되어 있다. 다른 기능도 어느 정도 제공하지만 항상 데이터베이스에 있는 데이터를 조작하는 방법에 주안점을 둔다.
MySQL에 SQL을 보내는 방법
MySQL에 SQL을 보낼 때는 다양한 방법을 사용할 수 있다. 그 중 3부에서 다루는 프로그래밍 API를 경유하는 방법을 가장 많이 사용한다. 하지만 이 장에서는 SQL을 배우는 것이 목적이므로 대화식 명령행 도구인 mysql을 사용하기로 하자. 명령행에서 이 프로그램을 실행시키면 SQL을 받아들일 수 있는 프롬프트가 나타난다:
 [09:04pm] carthage$ mysql -u root -p Enther password: Welcome to the MySQL monitor. Commands end with ; or g. Your MySQL connection id is 3 to server version: 3.22.29 Type 'help' for help. mysql>
위에 나와 있는 'mysqwl -u root -p'는 루트 사용자로(-u 옵션) 비밀번호를 입력하도록 하여 (-p 옵션) MySQL 서버에 연결하라는 명령이다. 원격 호스트에 있는 MySQL 서버에 연결할 때에는 -h 옵션을 사용한다:
 [09:04pm] carthage$ mysql -u root -h db.imginary.com -p
운영체제의 계정과 MySQL의 계정은 서로 독립적이다. 즉 MySQL용 사용자 목록이 따로있고 MySQL이 있는 호스트와는 별개로 MySQL 관리자가 별도로 새로운 사용자를 추가해야 한다. 따라서 처음 MySQL을 설치했을 때에는 루트 외에는 다른 사용자가 전혀 없다. 여기에서 루트는 유닉스 루트 게정과는 다르다. 일반적으로 데이터베이스 관리 작업을 하는 경우를 제외하면 절대 MySQL에 루트로 접속하면 안 된다. 새로 설치한 MySQL이 있고, 그 MySQL을 독자가 임의대로 쓸 수 있다면 루트로 접속해서 이 장에 나와 있는 데이터베이스를 만들고 지우는 예제를 실행해도 된다. 그렇지 않다면 항상 자신에게 할당된 계정으로 MySQL에 접속해야 한다.
mysql을 실행하고 나면 SQL 명령을 한 줄에 모두 입력해도 되고, 여러 줄에 걸쳐서 입력해도 된다. MySQL에서는 세미콜론이 입력될 때까지 SQL을 실행하지 않는다:
 mysql> SELECT book_number    -> FROM book    -> ; +-------------+ | book_number | +-------------+ |           1 | |           2 | |           3 | +-------------+ 3 rows in set (0.00 sec)
클라이언트 도구를 컴파일한 방법에 따라 mysql 명령행에서 명령 히스토리를 사용할 수 있다. mysql 클라이언트를 만들 때 명령 히스토리 기능을 포함시켜서 컴파일했다면 키보드의 위, 아래 화살표로 앞에서 실행시킨 SQL 명령을 다시 사용할 수 있다.
데이터베이스 생성
MySQL을 사용하려면 데이터베이스를 만들어야 한다. 우선 SHOW DATABASES 명령으로 처음 설치할 때 만들어진 데이터베이스를 살펴보자. MySQL 3.23.40을 설치하고 나면 다음과 같은 테이블이 생성된다:
 mysql> SHOW DATABASES; +-------------+ |     Database| +-------------+ |        mysql| |         test| +-------------+ 2 rows in set (0.37 sec)
첫 번째 데이터베이스인 mysql은 MySQL의 시스템 데이터베이스이며, 자세한 내용은 5장에 나와 있다. 두 번째 데이터베이스인 test는 MySQL을 배우거나 테스트할 때 쓸 수 있는 테스트용 데이터베이스이다. MySQL이 원래 깔려 있었다면 다른 데이터베이스가 들어있을 수 있다. 우선 MySQL의 CREATE 선언문을 사용하는 법을 알아보기 위해 새로운 데이터베이스를 만들어 보자:
 CREATE DATABASE TEMPDB;
그리고 새로운 데이터베이스인 TEMPDB로 작업을 하자:
 USE TEMPDB;
DROP DATABASE 명령을 내리면 데이터베이스를 삭제할 수 있다:
 DROP DATABASE TEMPDB;
방금 해 본 것과 같이 CREATE 선언문을 이용하면 새로운 대상을 만들 수 있고 DROP 선언문을 이용하면 삭제할 수 있다.
테이블 관리
지금까지 MySQL 서버에 있는 데이터베이스에 연결하는 방법을 알아보았다. 이제 MySQL을 설치했을 때 기본으로 들어 있는 test 데이터베이스 또는 사용자가 만든 실습용 데이터베이스를 사용하여 실습해 보자. 데이터베이스의 목록을 출력할 때와 마찬가지로 SHOW 명령어로 현재 데이터베이스에 들어 있는 테이블의 목록을 출력할 수 있다. mysql 시스템 데이터베이스에 연결된 상태에서 SHOW TABLES 명령을 내리면 다음과 같은 화면이 출력된다:
 mysql> USE mysql; Database changed mysql> SHOW TABLES; +-------------------+ |    Tables_in_mysql| +-------------------+ |       columns_priv| |                 db| |               func| |               host| |        tables priv| |               user| +-------------------+ 6 rows in set (0.00 sec)
위에 있는 여섯 개의 테이블은 MySQL이 동작하기 위해 필요한 시스템 테이블이다. 이 테이블 중 특정 테이블의 구성을 알고 싶다면 DESCRIBE 명령을 사용하면 된다:
 mysql> DESCRIBE db; +----------------------------------------------------------------------+ |Field           |Type           |Null |Key |Default   |Extra   |      | +----------------------------------------------------------------------+ |Host            |char(60) binary|     |PRI |          |        |      | |Db              |char(64) binary|     |PRI |          |        |      | |User            |char(16) binary|     |PRI |          |        |      | |Select priv     |enum('N', 'Y') |     |    |N         |        |      | |Insert priv     |enum('N', 'Y') |     |    |N         |        |      | |Update priv     |enum('N', 'Y') |     |    |N         |        |      | |Delete priv     |enum('N', 'Y') |     |    |N         |        |      | |Create priv     |enum('N', 'Y') |     |    |N         |        |      | |Drop priv       |enum('N', 'Y') |     |    |N         |        |      | |Grant priv      |enum('N', 'Y') |     |    |N         |        |      | |References priv |enum('N', 'Y') |     |    |N         |        |      | |Index priv      |enum('N', 'Y') |     |    |N         |        |      | |Alter priv      |enum('N', 'Y') |     |    |N         |        |      | +----------------------------------------------------------------------+ 13 rows in set (0.36 sec)
13 rows in set (0.36sec) 위의 결과를 보면 테이블에 있는 각 열이 나오고, 그 열에 널 값이 들어 있는지 여부, 키의 종류, 기본값, 추가 정보 등이 나와 있다. 이게 무슨 소린지 잘 모르는 독자들도 있겠지만, 앞으로 모두 배우게 될 내용이니 걱정하지 않아도 된다.
이제 테이블을 직접 만들어 보자. 우선 MySQL을 처음 설치하면 자동으로 생성되는 test 데이터베이스에 접속하자:
 USE test;
mysql에는 새로운 테이블을 추가하면 안 되기 때문에 test 테이터베이스에 우선 접속해야 한다는 점에 주의하도록 하자. 테이블(table)은 데이터를 구조적으로 저장하기 위한 컨테이너이며 관계형 데이터베이스에 있어서 가장 기본적인 개념이다. 테이블에 데이터를 추가하기 전에 데이블의 구조를 정의해야 한다. 다음과 같은 레이아웃을 생각해 보자:
 +------------------------------+ |          people              | +------------------------------+ | name     | char(10) not null | +------------------------------+ | address  | text(100)         | +------------------------------+ | id       | int               | +------------------------------+
테이블에는 열 이름뿐 아니라 각 필드의 유형과 그 필드에 들어갈 수 있는 추가 정보까지 포함된다. 필드의 데이터 유형은 그 필드에 저장할 수 있는 데이터 필드의 종류를 나타낸다. SQL의 데이터 유형은 다른 프로그래밍 언어의 데이터 유형과 비슷하다. SQL 표준에서는 매우 광범위한 데이터 유형을 사용할 수 있도록 되어 있다. MySQL에서는 SQL 표준에 있는 거의 모든 유형 외에도 MySQL에만 있는 몇 가지 데이터 유형을 지원한다.
테이블을 만들 때 사용하는 일반적인 문법은 다음과 같다:
 CREATE TABLE table_name (  column_name1 type [modifiers]  [, column_name2 type [modifiers]] )
사용할 수 있는 식별자(identifier, 테이블이나 열의 이름)는 DBMS에 따라 조금씩 다르다. MySQL에서는 식별자로 64자까지 사용할 수 잇으며, $ 문자도 사용할 수 있고 숫자로 시작해도 된다. 또한 MySQL이 설치된 시스템에서 사용할 수 있는 문자 세트에 포함된 모든 문자를 식별자로 사용할 수 있다.
열(column)은 테이블에 있는 행을 위한 개별 데이터의 단위이다. 테이블에 들어갈 수 있는 열의 개수에는 제한이 없지만 열이 너무 많아지면 효율이 떨어진다. 이런 면에서 7장에서 다루는 좋은 데이터베이스 설계 방법이 중요하다. 정규화된 테이블을 잘 만들면 여러 개의 테이블에서 저장된 테이터를 연결하여 검색할 수 있다. 연결(join)에 대한 것은 이 장의 후반부에 다룬다.
다음과 같은 CREATE 선언문을 생각해 보자:
 CREATE TABLE USER (  USER_ID BIGINT UNSIGNED NOT NULL PRIMARY KEY,  USER_NAME CHAR(10) NOT NULL,  LAST_NAME VARCHAR(30),  FIRST_NAME VARCHAR(30),  OFFICE CHAR(2) NOT NULL DEFAULT 'NY');
이 선언문은 USER_ID, USER_NAME, LAST_NAME, FIRST_NAME, OFFICE라는 다섯 개의 열이 있는 USER라는 테이블을 만든다. 각 열 이름 뒤에는 그 열의 데이터 유형이 나오고 그 뒤에 다른 변경자(modifier)가 붙는다.
NOT NULL 변경자는 그 열에 널 값이 들어갈 수 없다는 것을 의미한다. 그 열에 널 값을 대입하려고 하면 SQL에서 에러가 난다. 하지만 여기에는 두 가지 예외가 있다. 첫 번째는 어떤 열을 AUTO_INCREMENT로 정의한 경우인데 이 때는 널값을 대입하면 새로운 값이 자동으로 생성된다(자동 증가는 잠시 후에는 알아보기로 하자). 두 번째는 위에 나온 OFFICE 열에서처럼 열에 기본값을 지정하는 경우인데 이 때는 널 값을 대입하면 기본값(위의 경우에는 NY)이 대입된다(데이터 유형과 PRIMARY KEY 변경자에 대한 내용은 잠시 후에 다루겠다).
언제나 그렇듯이 삭제하는 것은 만드는 것보다 쉽다. 데이터베이스에서 테이블을 삭제하는 명령은 다음과 같다:
 DROP TABLE table_name
위와 같은 명령을 사용하면 데이터베이스에서 table_name이라는 이름을 가진 테이블이 흔적도 없이 사라진다. 주어진 테이블에 있는 모든 데이터는 남김 없이 삭제된다. 일단 지우고 나면 테이블을 미리 백업해두지 않은 이상 절대로 데이터를 살릴 수 없다. 따라서 항상 데이터를 백업해 두고 테이블을 삭제할 때는 신중을 기해야 한다. 데이터를 백업해 두는 습관을 길러 두면 언젠가는 반드시 크게 도움이 될 것이다.
MySQL에서는 테이블 이름을 쉼표로 구분하여 입력하면 여러 개의 테이블을 한꺼번에 삭제할 수 있다. 예를 들어, DROP TABLE people, animals, plants라는 명령을 사용하면 people, animals, plants라는 세 개의 테이블이 지워진다. 또한 삭제하고자 하는 테이블이 원래 없는 경우를 대비하여 IF EXISTS 변경자를 사용할 수도 있다. 이 변경자는 데이터베이스와 그 안에 들어갈 모든 테이블을 한꺼번에 만드는 복잡한 스크립트를 만들 때 많이 쓰인다. 이러한 스크립트에서는 데이터베이스를 만들기 전에 DROP TABLE IF EXISTS table_name이라는 명령을 사용한다.
MySQL 데이터 유형
테이블의 각 열에는 유형이 있다. 앞에서 언급했다시피 SQL의 데이터 유형은 일반적인 프로그래밍 언어의 데이터 유형과 비슷하다. 보통 컴퓨터 언어에서는 완결성을 위한 최소한의 유형만 정의하지만 SQL에서는 일반 사용자들의 유용하게 쓸 수 있도록 DATE와 같은 유형들을 추가로 제공한다. DATE 유형을 그냥 숫자 유형으로 저장할 수도 있지만, 날짜 처리를 위한 유형을 따로 만들어 두면 SQL을 더 쉽게 사용할 수 있다(사용의 용이성은 SQL의 핵심 목표중 하나다).
MySQL에서 지원하는 SQL 유형에 대한 자세한 내용은 16장에서 나와 있다. [표 3-1]에서는 가장 흔하게 쓰이는 유형을 간략하게 모아놓았다.
[표 3-1] MySQL에서 자주 사용하는 데이터 유형 (전체 목록은 16장 참조)
데이터 유형
설명
INT
정수값. MySQL에서 INT는 부호가 있을 수도 있고 없을 수도 있다.
REAL
부동수수점 값. 이 유형은 INT 유형에 비해 범위도 더 넓고 유효숫자의 수도 많지만 INT처럼 정확하지는 않다.
CHAR(length)
길이가 고정된 문자 값. CHAR 필드에서는 length 값으로 정해진 길이 이상의 문자열을 저장할 수 없다. 필드의 길이가 짧으면 남는 부분은 공백으로 채워진다. SQL에서 가장 많이 쓰이는 유형.
VARCHAR(length)
길이가 바뀔 수 있는 문자 값
TEXT(length)
길이가 바뀔 수 있는 문자 값
DATE
표준 날짜 값. DATE에는 과거, 현재, 미래의 임의의 날짜를 저장할 수 있다.
TIME
표준 시간 값. 이 유형에는 날짜와 상관 없이 시각을 저장 할 수 있다. 날짜와 함께 사용하면 특정한 날짜의 특정한 시각을 저장할 수 있다. MySQL에서는 한 필드에 날짜와 시각을 동시에 저장할 수 있도록 DATETIME이라는 유형을 지원한다.
MySQL에서는 모든 숫자 유형에서 UNSIGNED 속성을 지원한다. 이 변경자를 사용하면 그열에 (부호는 없는) 양수만 저장할 수 있다. 부호가 없는 필드의 최대값은 부호가 있는 필드의 최대값의 두 배다. 예를 들어, 부호가 없는 TINYINT(MySQL의 1바이트 숫자 유형)의 범위는 0 에서 255까지이고, 부호가 있는 TINYINT의 범위는 -128에서 127까지이다. MySQL에서는 [표 3-1]에 정리한 것보다 더 많은 유형을 지원한다. 하지만 일상적인 프로그래밍에서는 위에 나와 있는 유형을 가장 많이 사용할 것이다. MySQL 테이블을 설계할 때에는 저장하고자 하는 데이터의 크기에 알맞은 유형을 선택하는 것이 좋다.
숫자 유형
테이블을 만들기 전에 먼저 테이블에 저장할 테이터의 종류를 알아야 한다. 데이터가 문자 기반인지 숫자 기반인지를 결정하고 나면 저장할 데이터의 대략적인 크기를 알아야 한다. 숫자 필드라면 최대, 최소값이 얼마인지, 나중에 바뀔수 있는지 등을 고려해야 한다. 최소값이 0 이상이라면 부호가 없는 유형을 사용하는 것이 좋다. 될 수 있으면 사용하게 될 최대값을 지원하는 유형 중 가장 작은 숫자 유형을 사용해야 한다. 예를 들어, 어떤 주의 인구를 나타내튼 필드가 있다면 부호가 없는 INT 필드를 사용하면 된다. 인구 수가 음수인 경우는 없고, 그 주에 사는 사람들의 수가 전 세계 인구수에 육박하지 않는 이상 한 주의 인구를 나타내기에는 부호가 없는 INT 필드로도 충분하기 때문이다.
문자 유형
문자 유형은 조금 더 복잡하다. 문자열의 최소 및 최대 길이뿐만 아니라 길이의 평균 및 편차도 감안해야 한다. 인덱스(index)는 검색하고자 하는 필드 또는 필드의 조합이다(WHERE 절에 들어갈 필드라고 생각하면 된다). 인덱싱은 사실 훨씬 복잡하기 때문에 나중에 자세히 다루기로 하겠다. 일단 여기에서 중요한 것은 필드의 길이가 고정되어 있을 때 문자 필드에 대한 인덱싱이 가장 잘 동작한다는 점이다. 문자 필드의 길이에 편차가 거의 없을 때(가능하다면 아예 없을 때)에는 CHAR 유형이 가장 좋다. CHAR 필드로 사용할 수 있는 좋은 예로 국가 코드를 들 수 있다. ISO에서는 국가 코드를 나타내는 두 글자로 표기되는 표준을 정해 놓았다(미국은 US, 프랑스는 FR 등). 이렇나 코드는 항상 두 글자이므로 ISO 표기법을 따르는 국가 코드를 저장할 때에는 CHAR(2)를 사용하는 것이 최선의 방법이다.
CHAR 필드에 저장되는 값의 길이가 항상 일정해야 하는 것은 아니다. 하지만 편차가 아주 작아야 한다. 예를 들어, 전화번호는 나라마다 조금씩 다르긴 하지만 CHAR(13)에 저장해도 문제가 되지 않는다. 편차가 충분히 작기 때문에 전화번호 필드를 굳이 길이가 바뀔 수 있는 필드로 만들지 않아도 된다. CHAR 필드를 사용할 때에는 저장되는 문자열의 길이가 아무리 크더라도 필드의 크기로 정해진 문자의 개수만큼의 공간을 차지한다는 점을 기억해 두자(절대로 그 이상, 그 이하의 공간을 차지하지 않는다). 저장되는 텍스트의 길이와 필드의 길이가 차이가 난다면 남는 부분은 공백으로 채워진다. 전화번호 데이터에서 조금 공간이 남아도는 게 큰 문제가 안 될지 몰라도 너무 많은 공간을 낭비하는 것은 좋지 않다.
가변 길이(variable-length) 텍스트 필드는 길이가 크게 달라지는 텍스트 필드에 적합하다. 가변 길이 데이터 유형이 필요한 필드의 대표적인 예로 웹 URL을 들 수 있다. 대부분의 웹 주소는 비교적 짧기 때문에(예를 들면, http://www.ora.com이나 http://www.imaginary.com, http://www.mysql.com 등) 별 문제가 없지만 다음과 같이 긴 웹 주소가 등장할 때가 있다:
 http://www.innekw.com/wkwekekkkkkkkkkkkkkkkkkkkkkkkkkkkkjre
이렇게 긴 URL도 저장할 수 있을 만큼 큰 CHAR 필드를 만들면 다른 짧은 URL을 저장 할 때에는 너무 많은 공간을 낭비하게 된다. 하지만 가변 길이 필드를 사용하면 짧은 값을 저장할 때에는 공간을 낭비하지 않고, 위와 같이 긴 텍스트 값도 저장할 수 있다.
MySQL의 길이를 바꿀 수 있는 텍스트 필드는 필드에 값을 저장하는데 필요한 공간만을 차지한다. 예를 들어, VARCHAR(225) 필드에 "hello world"라는 문자열을 저장하더라도 12바이트(한 글자가 1바이트씩 차지하고, 나머지 1바이트에 길이를 저장한다)만 차지한다.
MySQL에서는 ANSI 표준과 다르게 VARCHAR 필드에서 남는 자리에 공백 문자를 채우지 않고, 값을 저장하기 전에 남는 공백을 모두 삭제한다.
지정한 필드 길이보다 긴 문자열을 저장할 수는 없다. VARCHAR(4) 필드를 만드었다면 최대 네 글자 길이의 문자열만 저장할 수 있다. 만약 "happy birthday"라는 문자열을 그 필드에 저장하려고 하면 MySQL에서는 "happ"까지만 저장한다. 이렇게 원래 정해 놓은 필드 크기를 벗어나는 문자열을 저장할 수 없다는 단점이 있다. [표 3-2]는 각 테스트 데이터유형으로 앞에 나왔던 144 길이의 와인 감별사 사이트 URL과 일반적인 30자 길이의 URL을 저장할 때 필요한 저장 공간 그리고 각 유형의 최대 문자열 길이를 정리해 놓은 표다.
[표 3-2] 여러 가지 MySQL 문자 유형에서 필요한 저장 공간
데이터 유형
144자 문자열
30자 문자열
최대 문자열 길이
CHAR(150)
150
150
255
VARCHAR(150)
145
31
255
TINYTEXT(150)
145
31
255
TEXT(150)
146
32
65535
MEDIUMTEXT(150)
147
33
16777215
LONGTEXT(150)
148
34
4294967295
이 표를 보면 길이를 바꿀 수 있는 유형인 MEDIUMTEXT와 LONGTEXT에서는 데이터을 저장하는데 필요한 공간이 각각 1, 2 바이트씩 더 필요하다는 것을 알 수 있다. 이는 필드의 크기를 저장하는데 각각 1바이트씩 공간이 더 필요하기 때문이다. TEXT에서는 문자열 길이를 저장하는 데 1바이트로는 모자라기 때문에 VARCHAR보다 한 바이트가 더 필요하고 마찬가지 이유로 MEDIUMTEXT, LONGTEXT로 갈수록 2바이트, 3바이트 씩의 공간이 더 있어야 한다.
데이터베이스를 사용하다 보면 처음에는 VARCHAR(25) 만으로도 충분했던 필드 크기를 더 늘려야 할 때도 있는데, 이런 문제는 그리 어렵지 않게 해결할 수 있다. MySQL에서는 ALTER TABLE이라는 명령을 지원하기 때문에 이 명령을 이용하여 데이터 손실 없이 필드 유형을 재정할 수 있다:
 ALTER TABLE mytable MODIFY mycolumn LONGTEXT
이진 데이터 유형
MySQL에서는 문자 유형과 매우 유사한 이진 데이터 유형을 제공한다. MySQL의 이진 유형에는 CHAR, BINARY, VARCHAR, BINARY, TINYBLOB, BLOB, MEDIUMBLOB, LONGBLOB이 있다. 문자 유형과 그에 상응하는 이진 유형간의 실질적인 차이점은 인코딩이라는 개념에서 찾을 수 있다. 이진 데이터(binary data)은 기본적으로 MySQL에서 해석하지 않는 데이터 조각이다. 하지만 문자 데이터는 사람들이 사용하는 글자로 이루어진 텍스트 형식의 데이터를 나타낸다. 따라서 문자 데이터는 해당 문자 세트에 알맞은 규칙을 바탕으로 인코딩되고 정렬된다. ASCII 시스템에서 문자 데이터를 정렬할 때에는 대소문자를 구분하여 ASCII 순서대로 정렬한다.
열거 및 집합
그 밖에도 MySQL에서 제공하는 특수 유형이 있다. ENUM 유형(열거 유형)을 이용하면 테이블을 만들 때 그 필드에 들어갈 수 있는 값들의 목록을 지정할 수 있다. 예를 들어 apple, orange, kiwi, banana 중 하나만 들어갈 수 있는 fruit라는 열이 있는데, 이 열의 유형을 ENUM으로 한다고 가정해 보자:
 CREATE TABLE meal (meal_id INT NOT NULL PRIMARY KEY,                fruit ENUM('apple', 'orange', 'kiwi',                          'banana'))
이 열에 어떤 값을 넣는다면 그 값을 넣는다면 그 값을 위에 열거되어 있는 과일 이름 중 하나가 되어야 한다. MySQL에서는 미리 이 열에 들어갈 수 있는 값들을 알고 있기 때문에 문자열 형태가 아닌 숫자 형태로 저장한다. 즉 apple을 열에 대입할 때 문자열이 아니라 내부적으로 한 바이트자리 숫자로 바꿔서 저장한다. 하지만 MySQL에서 그 값을 읽어들이거나 질의에서 사용할 때에는 그냥 apple이라고 표기하면 된다. 또한 테이블에서 결과를 보거나 테이블을 호출할 때에도 apple을 사용하면 된다.
MySQL의 SET 유형(집합 유형)도 똑같은 방식으로 작동하지만 하나의 필드에 여러 값을 동시에 저장할 수 있고 바이트 대신 비트를 사용한다는 점이 다르다.
기타 데이터 유형
모든 데이터는 숫자 또는 문자 유형으로 저장할 수 있다. 사실 숫자도 문자 유영으로 저장할 수 있다. 하지만 그렇게 할 수 있다고 해서 꼭 그렇게 해야 하는 것은 아니다. 예를 들어, 데이터베이스에 날짜를 저장하는 것을 생각해 보자. 그 값을 유닉스 형식의 BIGINT로 저장할 수도 있고 날짜, 월, 연도를 저장하기 위한 열을 따로 만들 수도 있다. 하지만 그런 경우에 특정한 날짜보다 이틀 이상 이전의 날짜가 들어 있는 열을 찾으려면 어떻게 해야 할까? 그 날짜를 숫자로 표시해 놓은 값을 직접 계산하거나 날짜, 월, 연도 값을 모두 고려하는 아주 복잡한 연산을 처리해야 할 것이다.
실제 프로그래밍을 할 때 이렇게 하면 정말 골치 아프다. MySQL에서 이런 문제를 모두 해결해 줄수는 없을까? 다행히도 MySQL에는 해결책이 있다. MySQL에서는 일상적인 개념을 데이터로 표현할 수 있도록 여러 가지 복합 데이터 유형을 제공한다. 날짜의 개념을 DATE 데이터 유형을 통해 지원한다. 이와 비슷한 것으로 DATETIME과 TIMESTAMP가 있다.
인덱싱
MySQL은 다른 대형 데이터베이스 서버보다 속도가 훨씬 빠르지만, 데이터베이스를 설계할 때에는 몇가지 문제를 고려해야 한다. 예를 들어, 행의 개수가 수백만 개인 테이블을 사용한다면 하나의 행을 찾는 데에도 꽤 오랜 시간이 걸릴 수 있다. 대부분의 데이터베이스 엔진에서는 빠른 검색을 위해 인덱스를 사용한다.
인덱스는 데이터베이스에서 데이터를 저장할 때 더 빠르게 검색할 수 있도록 도와주는 역할을 한다. 이렇게 검색 속도를 향상시키려면 어쩔 수 없이 디스크 공간과 데이터 수정 속도 면에서 약간의 희생을 감수해야 한다. 인덱스의 효율을 높이고 싶다면 가장 많이 검색하는 열의 인덱스를 만드는 것이 좋다. 데이블의 인덱스를 만들 때에는 다음과 같은 구문을 사용한다:
 CREATE INDEX index_name ON tablename (column1, column2, ..., columnN)
다음과 같이 하면 테이블을 처음 만들 때 인덱스도 같이 만들 수도 있다:
 CREATE TABLE masterial (id INT NOT NULL,                        name CHAR(50) NOT NULL,                        resistance INT,                        melting_pt REAL,                        INDEX index1 (id, name),                        UNIQUE INDEX index2 (name))
이 예에서는 테이블에 두 개의 인덱스를 만든다. 첫 번째 인덱스(index1)는 id와 name 필드로 구성된다. 두 번째 인덱스에는 name 필드만 포함되며 name 필드의 값은 반드시 유일해야 한다고 되어 있다. 이미 데이터베이스의 다른 행의 name 열에 들어 있는 값과 같은 name 값을 가진 필드를 새로 삽입하면 에러가 난다. 보통 유일 인덱스(UNIQUE INDEX)에 포함된 필드는 NOT NULL로 선언해야 한다.
name 자체의 인덱스는 만들었지만 id의 인덱스는 별도로 만들지 않았다. id만으로 구성된 인덱스가 필요하다면 굳이 따로 만들 필요가 없다. 이미 만들어져 있기 때문이다. 인덱스에 두 개 이상의 열이 포함된다면(예를 들어, name, rank, serial_number 같은 식으로) MySQL에서는 그 열들을 왼쪽에서 오른쪽으로 가는 열의 부분집합이 메인 인덱스 안에 자동으로 생성된다. 예를 들어, name, rank, serial_number 인덱스를 만들면 name 자체와 name, rank로 구성되는 인덱스가 자동으로 만들어진다. 하지만 rank만으로 또는 name과 serial_number로 이루어지는 인덱스는 따로 만들어야 한다.
MySQL에서는 기본키(primary key)라는 특별한 인덱스에 대한 ANSI SQL 문법도 지원한다. MySQL에서 기본키는 PRIMARY라는 이름이 붙은 유일한 키이다. 어떤 열을 기본키로 선언하면 이 열을 테이블 연결을 지원하는 지원하는 유일 인덱스가 된다. 다음은 id를 기본키로 하여 cities라는 테이블을 만드는 예이다:
 CREATE TABLE cities (id INT NOT NULL PRIMARY KEY,                    name VARCHAR(100),                    pop MEDIUMINT,                    founded DATE)
테이블을 만들기 전에 우선 키를 지정할 계획이라면 어떤 필드를 키로 지정할지 결정해야 한다. 앞에서 말했듯이 연결을 지원할 필드를 기본키로 지정하는 것이 좋다. 테이블을 만들 때 적절한 기본키를 선정하는 방법은 7장에서 자세하게 알아보자.
ANSI SQL에서는 외부키(foreign key)라는 특별한 키를 지원한다. 외부키는 다른 테이블에 의존적인 관계를 가진 행의 삭제와 같은 작업을 데이터베이스에 관리할 수 있게 함으로써 데이터베이스의 무결성을 보호하는 도움을 준다.
MySQL에서도 외부키에 대한 ANSI 문법을 지원하긴 하지만 데이터베이스의 무결성을 검사할 때에 실제 그 기능을 사용하지는 않는다. 이 기능은 속도만 느리게 만들고 실절적인 장점이 거의 없기 때문이다. 일반적으로 외부키 무결성은 애플리케이션 자체에서 신경을 쓰도록 하는 편이 좋다.
데이터 관리
데이블을 새로 만들고 나서 가장 먼서 할 일은 데이터를 추가하는 것이다. 데이터를 집어넣고 나면 테이블을 관리(추사, 수정 및 삭제)해야 한다.
삽입
테이블에 행을 추가하는 방법은 비교적 간단하다. 이미 앞에서도 몇 번 해 본 작업이다. 우선 SQL 표준 INSERT 문법을 사용하는 방법이 있다:
 INSERT INTO table_name (column1, column2, ..., columnN) VALUES (value1, value2, ..., valueN)
이 문법에서는 열의 이름을 지정한 다음 그 열에 채울 값들을 순서대로 입력한다. 숫자 필드에 데이터를 삽입할 때에는 그대로 사용하면 되지만 다른 필드에 들어갈 값들은 작은 따옴표로 감싸야 한다. 예를 들어, 주소록 테이블에 새로운 행을 삽입할 때에는 다음과 같은 식으로 하면 된다:
 INSERT INTO addresses (name, address, city, state, phone, age) VALUES ('Irving Forbush', '123 Mockingbird Lane', 'Corbin', 'KY',      '(800) 555-1234', 26)
작은 따옴표나 이스케이프 문자 자체를 표시할 때에는 이스케이프 문자(기본값은 )를 앞에 덧붙이면 된다:
 # c:PersonalStacie 에 있는 Stacie's Directory라는 # 디렉토리에 대한 정보를 넣는다. INSERT INTO files (description, location) VALUES ('Stacie 's Directory', 'C:\Personal\Stacie')
MySQL에서는 모든 값을 테이블을 만들 때 사용한 CREATE 명령에서 지정한 열 순서대로 값을 입력할 때에는 열 이름을 생략해도 된다. 하지만 어떤 열에 기본값이 있다면 기본값이 아닌 테이더를 입력하고자 하는 열의 이름은 확실히 지정해야 한다. 예를 들어, 앞에 나온 files 테이블에 size라는 열이 있었다면 그 열에는 Stacie's Directory가 아닌 기본값이 적용된다. 기본값은 테이블을 만드는 CREATE 명령에서 지정할 수 있다. NOT NULL 이면서 기본값을 지정하지 않은 열이 있다면 INSERT 선언문에 그 열을 반드시 포함시키고 NULL이 아닌 값을 넣어야 한다.
MySQL 최근 버전에서는 한꺼번에 여러 행을 삽입할 수 있는 비표준 INSERT 호출도 지원한다:
 INSERT INTO foods VALUES (NULL, 'Oranges', 133, 0, 2, 39),                        (NULL, 'Bananas', 122, 0, 4, 29),                        (NULL, 'Liver', 232, 3, 15, 10)
간단한 시스템 관리에는 MySQL에서 지원되는 비표준 문법을 사용하는 것도 좋지만 데이터베이스 애플리케이션을 만들 때에는 속도 문제가 아주 중요한 경우를 제외하면 비표준 문법을 사용하지 않는 편이 좋다. 될 수 있으면 MySQL에선 허용하는 범위 안에서는 최대한 ANSI SQL2 표준을 따르도록 하자. 이렇게 해야 나중에 데이터베이스를 바꿔도 애플리케이션을 그대로 사용할 수 있다. 중간 규모의 데이터베이스를 사용하는 사람들도 대체로 나중에는 대규모 데이터베이스를 사용하고자 하는 경향이 있기 때문에 애플리케이션을 만들 때에도 이런 경우를 감안하여 유연하게 만들어야 한다.
MySQL에서는 열의 이름과 값을 함께 지정하는 비표준 문법도 지원한다:
 INSERT INTO book SET title='The Vampire Leatat, author='Anne Rice';
마지막으로 다른 테이블(또는 여러 개의 다른 테이블)의 데이터를 이용하여 데이터를 삽입할 수도 있다. 예를 들면, 다음과 같다:
 INSERT INTO foods (name, fat) SELECT food_name, fat_grams FROM recipes
INSERT 호출에 들어 있는 열의 개수가 SELECT 호출에 들어 있는 열의 개수와 같어야 한다는 점에 유의해야 한다. 또한 INSERT 열의 데이터 유형도 그에 해당하는 SELECT 열의 데이터 유형과 맞아야 한다. 마지막으로 INSERT 선언문에 들어가는 SELECT 절에는 ORDER BY 변경자를 사용할 수 없으며, INSERT 작업을 하는 테이블과 같은 테이블에 대해 SELECT를 실행시킬 수 없다.
시퀀스 생성
기본키로 가장 좋은 것은 기본키 역할 외에 데이터베이스에서 아무 의미가 없는 열이다. 기본키는 관계형 데이터베이스에서 각 열을 유일하게 알아볼 수 있도록 해 주는 도구이다. 계정이나 이메일 주소 같은 정보를 기본키로 사용하면 그 계정이나 이메일 주소가 그 사용자를 나타낼 수 있는 고유의 성질이라고 간주하는 셈이 된다. 만약 그 사람이 계정이나 이메일 주소를 바꾸면 데이터베이스에 있는 데이터의 무결성을 새로 점검해야 하기 때문에 일이 번거로워진다. 따라서 좋은 데이터베이스를 설계하려면 다른 의미를 가지지 않는 숫자를 기본키로 사용하는 것이 좋다.
아무런 의미를 가지지 않는 숫자를 기본키로 사용하고 싶다면 매번 새로운 행을 추가할 때마다 1씩 증가하는 숫자를 기본키로 사용하면 된다. 예를 들어, 앞에서 나온 cities 테이블의 경우에는 첫 번째로 집어넣는 도시의 id가 1이 되고, 두 번째 도시의 id는 2, 세번째 도시의 id는 3, 이런 식으로 id를 정하면 된다. 이러한 기본키 시퀀스를 제대로 관리하려면 한 번에 하나의 클라이언트만 이 숫자를 읽고, 그 값에 1을 더할 수 있도록 해야 한다. 이렇게 하고 싶다면 기본키 필드를 AUTO_INCREMENT로 지정하면 된다.
MySQL에서 데이블을 만들 때에는 두 개 이상의 열을 AUTO_INCREMENT로 지정할 수 없다. 어떤 열을 AUTO_INCREMENT로 지정하면 행을 삽입할 때 그 열에 0 또는 NULL 값을 집어넣을 때 현재 그 열에 있는 값 중 가장 큰 값에 1을 더한 값이 자동으로 입력된다. AUTO_INCREMENT 열은 반드시 인덱싱해야 한다. 다음과 같은 명령으로 자동으로 증가하는 id 필드가 있는 cities 테이블을 만들 수 있다:
 CREATE TABLE cities (id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,                    name VARCHAR(100),                    pop INT,                    founded DATE)
INSERT 선언문에서 id 필드의 값을 0이나 NULL로 대입하면 첫 번째 행의 id 필드의 값은 자동으로 1이 된다. 아래의 명령은 AUTO_INCREMENT 기능을 활용하는 방법을 보여준다:
 INSERT INTO cities (id, name, pop) VALUES (NULL, 'Houston', 3000000)
테이블에 다른 데이터가 없는 상태에서 위와 같은 명령을 내리면 MySQL에서는 이 필드에 NULL이 아닌 1을 자동으로 넣는다(이 필드에서 NULL 값이 들어갈 수 없다). 테이블에 이미 다른 데이터가 있다면 새로 삽입한 행의 id 값에는 현재 있는 가장 큰 id 값에 1을 더한 숫자가 들어간다.
LAST_INSERT_ID() 함수에서 리턴한 값에 직접 1을 더해서 수동으로 시퀀스를 만들 수도 있다:
 UPDATE table_name SET id = LAST_INSERT_ID(id + 1);
AUTO_INCREMENT 속성은 테이블에 있는 정수 유형의 열 중 최대 1 개의 열에만 적용할 수 있다. 이 속성이 적용되는 열은 정수 유형이어야만 하며, 기본키 혹은 그 열만으로 유일 인덱스를 구성할 수 있어야 한다. 그러한 정수 필드가 있는 테이블에 새로운 행을 삽입할 때 해당 필드에 아무 값도 대입하지 않거나 NULL 값을 대입하면 그 열에 있는 값 중 가장 큰 값에 1을 더한 값이 자동으로 입력된다.
LAST_INSERT_ID() 함수에 대한 상세한 내용은 17장에 있다.
갱신
데이터베이스에 새로운 행을 삽입하는 것은 데이터 관리의 시작에 불과하다. 데이터베이스가 읽기 전용이 아닌 이상 데이터는 주기적으로 바뀐다. 표준 SQL 변경 선언문은 다음과 같다:
 UPDATE table_name SET column1=value1, column2=value2, ..., columnN=valueN [WHERE 절]
갱신하고자 하는 테이블의 이름을 지정한 다음 SET 절에 대입할 값을 적는다. WHERE 절에서는 갱신할 행을 지정한다. WHERE 절을 생략하면 테이블에 있는 모든 행을 갱신한다.
열에 리터럴 값 대신 계산 결과를 대입할 수도 있다. 다른 열에 있는 값으로부터 대입할 값을 계산하는 것도 가능하다:
 UPDATE years SET end_year = begin_year + 5
위의 명령을 사용하면 테이블의 모든 행에 있는 end_year 열에 begin_year 열의 값에 5를 더한 숫자를 대입할 수 있다.
WHERE 절
앞에서 SQL에서 가장 중요한 것 중 하나인 WHERE 절을 소개하였다. SQL에서 WHERE 절은 어떤 조건을 지정해서 테이블의 특정 행을 선택할 수 있게 해 주는 역할을 한다. 예를 들면 다음과 같다:
 UPDATE bands SET lead_singer = 'Ian Anderson' WHERE band_name = 'Jethro Tull'
이 UPDATE 문에서는 band_name이 Jethro Tull인 행에 대해서만 lead_singer 열을 바꾸도록 한다. band_name 열이 유일 인덱스가 아니라면 WHERE 절의 조건에 맞는 행이 여러 개 나올 수도 있다. SQL을 사용하다 보면 WHERE 절로 조작할 행을 선택하는 경우가 많다. WHERE 절에 들어 있는 열을 검색하게 되므로 일반적으로 WHERE 절에 많이 들어가는 열에 대해 인덱스를 만드는 것이 좋다. WHERE 절에서 사용할 수 있는 비교문은 잠시후에 알아보도록 하자.
삭제
데이터를 삭제하는 작업은 간단하다. 테이블 이름을 입력하고 WHERE 절을 이용하여 삭제할 행을 지정하기만 하면 된다:
 DELETE FROM table_name [WHERE 절]
WHERE 절을 사용할 수 있는 다른 명령과 마찬가지로 WHERE 절을 반드시 사용해야 하는 것은 아니다. WHERE 절을 생략하면 테이블을 있는 모든 데이터가 지워진다. SQL에 데이터를 없애는 명령어가 많이 있지만, 그 중 DELETE는 가장 쉽게 데이터를 지울 수 잇는 명령어이므로 특히 조심해야 한다.
MySQL 4.0에서는 명령어 하나로 여러 개의 테이블에 있는 데이블에 있는 데이터를 지울 수 있는, 다소 위험한 DELETE 형식을 새로 지원한다:
 DELETE table1, table2, ..., tableN FROM table1, table2, ... tableN [WHERE 절]
이 구문의 FROM 절은 간단한 형태의 DELETE 구문에서 쓰이는 FROM 절과는 다른 의미로 쓰인다. 즉 행을 지울 테이블을 나타내는 것이 아니라 WHERE 절에서 참조하는 테이블의 목록을 나타낸다. SELECT 선언문을 떠올려 보면 SELECT 선언문의 FROM 절과 똑같은 식으로 작동하는 것을 알 수 있다. 데이터를 지울 테이블을 DELETE 선어문 바로 뒤에 입력해야 한다:
 DELETE Author, Address FROM Author, Book, Address WHERE Author.author_id = Address.address_id AND Author.author_id = Book.author_id AND Book.publish_date < 1980;
위의 선언문을 사용하면 1979년 이전에 책을 출판한 모든 저자에 대한 정보(Author 테이블)와 그 저자의 주소에 대한 정보(Address 테이블)를 삭제할 수 있다. 하지만 DELETE 키워드 뒤에 Book 테이블을 입력하지 않았기 때문에 Book 테이블에 있는 책에 관한 정보는 그대로 남는다. WHERE 절에 대한 복잡한 내용은 나중에 알아보기로 하자.
질의
마지막으로 배울 SQL 명령은 SELECT이다. SELECT를 이용하면 데이터베이스에 있는 데이터를 볼 수 있다. SELECT는 SQL에 있는 다른 어떤 명령보다도 많이 쓰인다. 신규 데이터를 추가하거나 데이터를 변경하는 일은 가끔 있을 뿐 사실 데이터베이스에서는 대부분의 시간을 데이터를 읽어들이는 데 쓴다. SELECT 선언문의 일반적인 형식은 다음과 같다:
 SELECT column1, column2, ..., columnN FROM table1, table2, ..., tableN [WHERE 절]
위와 같은 문법으로 거의 모든 SQL 데이터베이스에서 데이터를 읽어올 수 있다. SELECT 선언문을 이용하면 하나 혹은 여러 개의 테이블로부터 원하는 열을 선택할 수 있다. WHERE 절은 찾고자 하는 데이터가 들어 있는 행을 선택하는 부분이다.
물론 여러 가지 방법으로 복잡한 그리고 강력한 질의를 처리할 수 있다(SELECT와 관련된 자세한 문법은 15장에서 다룬다). 아래의 SELECT 선언문은 가장 간단한 SELECT 명령이다:
 SELECT 1;
이 선언문은 간단하긴 하지만 아무 쓸모가 없다. 단지 1이라는 값을 가진 하나의 열을 가진 하나의 행이 질의의 결과로 돌아올 뿐이다. 다음과 같은 질의는 어느 정도 써 먹을 수가 있다:
 mysql> SELECT DATABASE(); +---------------+ | DATABASE()    | +---------------+ | TEST          | +---------------+ 1 row in set (0.01 sec)
DATABASE()라는 표현식은 현재 데이터베이스의 이름을 리턴하는 MySQL 함수이다(함수에 대한 자세한 내용은 잠시 후에 알아보기로 하자). 위와 같은 예에서 알 수 있듯이 SQL를 이용하여 중요한 정보를 간단하게 찾아낼 수 있다.
하지만 대부분의 경우 데이터베이스에 있는 테이블에서 테이터를 추출하기 위해 조금 더 복잡한 질의를 사용한다. SELECT 선언문의 첫 번째 부분에서는 가져오고자 하는 열을 열거한다. 모든 열을 선택할 때에는 *을 사용하면 된다. FROM 절은 그 열을 어떤 테이블로부터 선택할지를 나타낸다. WHERE 절에서는 행을 선택하는 조건을 지정할 있으며 두 개의 테이블을 연결하는 방법을 지정할 수도 있다.
연결
연결(join)은 어떤 테이블에 있는 데이터를 다른 테이블에 있는 데이터와 연관시켜주는 기능으로 관계형 데이터베이스의 핵심이라고 할 수 있다. 기본적인 연결 형식은 내부 연결(inner join)이라고도 한다. 테이블을 연결할 때에는 서로 다른 테이블에 있는 열 사이에 등호를 넣으면 된다:
 SELECT book.title, author.name FROM author, book WHERE book.author = author.id
이렇게 하면 두 테이블에 있는 행끼리 어떤 관계가 있을 때 두개의 서로 다른 테이블로부터 열을 뽑아낼 수 있다. 위의 질의에서는 book 테이블의 author 열의 값이 author 테이블의 id 값과 같은 경우를 찾아낸다. book 테이블은 [표 3-3], author 테이블은 [표 3-4]와 같이 주어지는 테이터베이스를 생각해 보자:
 [표 3-3] book 테이블 -------------------------------------------------------------------------------  ID           | 제목                   | 저자               | 페이지 -------------------------------------------------------------------------------  1            | The Green Mile         | 4                  | 894  2            | Guards, Guards!        | 2                  | 302  3            | Imzadi                 | 3                  | 354  4            | Gold                   | 1                  | 405  5            | Howling Mad            | 3                  | 294 ------------------------------------------------------------------------------- [표 3-4] author 테이블 -------------------------------------------------------------------------------  ID           | 이름                       | 국적 -------------------------------------------------------------------------------  1            | Isaac Asimov               | US               | Terry Pratchett            | UK  2            | Peter David                | US  3            | Stephen King               | US  4            | Neil Gaiman                | UK -------------------------------------------------------------------------------
내부 연결을 하면 데이터베이스 서버에서는 두 테이블로부터 질의를 만족시키는 행을 찾아서 두 테이블의 필드를 합친 가상 테이블을 만든다. 위에 나온 예에서는 질의에서 book 테이블의 author 필드가 author 테이블의 id 필드와 같은 경우를 요구했다. 따라서 질의의 결과는 [표 3-5]와 같다:
 [표 3-5] 내부 연결에 의한 질의 결과 -------------------------------------------------------------------------------  책 제목                        | 저자 이름 -------------------------------------------------------------------------------  The Green Mile                 | Stephen King  Guards, Guards!                | Terry Pratcher  Imzadi                         | Peter David  Gold                           | Isasc Asimov  Howling Mad                    | Peter David -------------------------------------------------------------------------------
닐 게이먼(Neil Gaiman)은 위의 결과에 포함되지 않는다. 그 저자에 해당하는 author.id 값이 book 테이블의 author 열에 없기 때문이다. 즉 이 데이터베이스에 있는 책 중에는 닐 게이먼의 책이 하나도 없다는 것을 의미한다. 내부 연결의 결과에는 질의와 정확하게 일치하는 행만 포함된다. 데이터베이스에 저자가 들어 있지만 책이 없는 경우에 사용할 수 있는 외부 연결은 잠시 후에 알아보기로 하자.
별칭
테이블 및 열 이름을 만들다 보면 이름이 너무 길어지게 될 수 있다. 또한 SQL 함수를 사용하다 보면 하나의 선언문에서 같은 함수를 여러 번 호출하는 것이 귀찮게 느껴질 수도 있다. 별칭(alias)을 이용하면 이러한 문제를 해결할 수 있다. 보통 복잡한 이름 대신 더 간단하고 이해하기 쉬운 별칭을 사용한다. SQL 선언문에서 복잡한 이름 대신 간단한 별칭을 사용할 수 있다. 예를 들면, 다음과 같다:
 # 열 별칭 SELECT long_field_name_are_annoying AS myfiled FROM table_name WHERE myfiled = 'Joe' # 테이블 별칭 SELECT people.names, tests.score FROM tests, really_long_people_table_name AS people
정렬 및 분류
SELECT의 결과에는 따로 순서가 정해져 있지 않다. 하지만 이렇게 무작위적으로 열거된 결과를 원하는 순서대로 정리할 수 있도록 SQL에서는 정렬(ordering)과 분류(grouping) 기능을 제공한다.
기본정렬
데이터베이스에서 나온 결과는 특정 열을 기준으로 정렬할 수 있다. 예를 들어, 질의 결과를 last_name 순으로 정렬하도록 하면 last_name 값을 기준으로 알파벳 순서대로 정렬된 결과가 나타난다. 정렬은 ORDER BY 절로 처리할 수 있다:
 SELECT last_name, first_name, age FROM people ORDER BY last_name, first_name
위의 질의에서는 두 개의 열을 기준으로 정렬한다. 정렬을 할 때 기준의 되는 열의 개수에는 제한이 없다. 결과를 무작위순으로 출력하고 싶다면 ORDER BY RAND() 절을 사용하면 된다.
DESC (내림차순, descending) 키워드를 사용하면 순서를 거꾸로 하여(내림차순으로) 정렬 할 수 있다:
 ORDER BY last_name DESC
DESC 키워드는 그 키워드 바로 앞에 있는 필드에만 적용된다. 여러 개의 필드를 기준으로 정렬한다면 DESC 바로 앞에 있는 필드에 대해서만 내림차순으로 정렬하고 나머지에 대해서는 오름차순으로 정렬한다.
지역화 정렬
전 세계 여러 나라의 컴퓨터에서 실행시켜야 하는 애플리케이션에서는 사실 정렬이 꽤 골치 아픈 문제이다. 문자열을 정렬하는 방법이 사용하는 알파벳에 따라 다를 뿐만 아니라 같은 알파벳에 대해서도 정렬 순서가 다를 수 있기 때문이다. MySQL에서는 MySQL 엔진에서 사용하는 문자 세트에 따라 정렬을 다르게 하는 방법으로 이 문제를 해결한다. 처음 설치했을 때의 기본 문자 세트는 ISO-8859-1(Latin 1)이며 ISO-8859-1과 스웨덴어, 핀란드어의 정렬 방법을 기본으로 사용한다.
정렬 방벙을 바꾸고 싶다면 문자 세트를 바꾸면 된다. 우선 MySQL을 컴파일하고 설치할 때 원하는 문자 세트를 포함시켰는지 확인해야 한다. 서버를 컴파일할 때 문자 세트를 제대로 포함시켰다면 서버를 시작할 때 -default-characte-set=CHARSET 이라는 인자를 사용하여 기본 문자 세트를 바꿀 수 있다. 영문 알파벳은 워낙 간단하기 때문에 영어만 사용하는 경우에는 MySQL의 기본 문자세트인 ISO-8859-1을 그대로 사용하면 된다. 하지만 스웨덴어나 독일어는 둘 다 ISO-8859-1을 사용하긴 하지만 정렬할 때 문제를 일으킬 수 있다. 스웨덴어에서는 정렬을 할때 a가 z 다음에 오지만 독일어에서는 a가 a앞으로 간다. 따라서 독일어를 사용하는 경우에는 기본 정렬 방법을 그대로 사용할 수 가 없다.
MySQL에서는 이러한 문제를 해결하기 위해 사용자 정의 문자 세트를 만들 수 있도록 되어 있다. 원하는 문자 세트에 대한 설정 파일이 있다면 드라이버를 컴파일할 때 그 문자 세트 지원 기능을 포함시키면 된다. 이 파일에는 그 문자 세트를 구성하는 문자와 그 문자를 정렬하는 방법이 들어 있다. 이 파일은 직접 만들어서 쓸 수도 있고 MySQL에 포함되어 있는 것을 사용해도 된다.
여기에서 가장 문제가 되는 것
Posted by tornado
|

STOP WORD

SQL 2005. 1. 14. 12:48

http://www.lextek.com/manuals/onix/stopwords2.html

 

mysql 은 아니지만.... .작성할 곳이 없다...

 

스탑워드 리스트 나온 사이트..

 

한글은 없나??

 

 

'SQL' 카테고리의 다른 글

[mysql] 디비사랑넷 펌 ... 4.1버젼 한글사용  (0) 2005.02.02
[펌] MySQL의 SQL  (0) 2005.01.14
[MySQL] 다수의 테이블에서 데이터 동시 삭제하기  (0) 2004.10.27
[펌] [오라클] 인덱스  (0) 2004.09.22
[mysql] ifnull  (0) 2004.09.16
Posted by tornado
|

글쓴이:Qindex 접속자의 국가를 알아내서 해당 국기로 표시하기조회수:854

http://qindex.info

아래 아이피 대역에 관한 글에 코멘트를 잠깐 달았는데 물어보시는 분이 있어 자세히 올립니다.
=================================================================================

http://ip-to-country.webhosting.info/node/view/5

먼저 ip-country.csv.zip파일을 다운받아서 압축을 풉니다.
범위가 50,000개가 넘으니 상당히 정확한 것으로 생각됩니다.
ip-country.csv파일은 다음과 같은 형식으로 되어 있습니다.

"0033996344","0033996351","GB","GBR","UNITED KINGDOM"
"0050331648","0083886079","US","USA","UNITED STATES"
"0094585424","0094585439","SE","SWE","SWEDEN"

각 칼럼값은,

0033996344: IP address 범위의 시작
0033996351: IP address 범위의 끝
GB: ISO 3166에 의한 두글자 국가코드
GBR: ISO 3166에 의한 세글자 국가코드
UNITED KINGDOM: ISO 3166에 의한 국가이름

입니다.
여기서 범위값은 만약 IP address가 A.B.C.D인 경우
A x (256*256*256) + B x (256*256) + C x 256 + D가 됩니다.
이 데이터를 DB에 입력한 후 접속자의 아이피와 대조해서 접속자 국가를 알아냅니다.

접속자의 국가를 국기로 표시하려고 하면 flags파일을 내려받아서 이용합니다.
(http://ip-to-country.webhosting.info/node/view/91)

적용사례는 http://www.qindex.info/Q_frame.php?s_clss=stts_show 입니다

'이것저것 > 낙서장' 카테고리의 다른 글

4400 히트 이벤트!!!  (2) 2005.01.19
4300 히트 이벤트!!!  (2) 2005.01.17
4100 히트 이벤트!!!  (2) 2005.01.04
도대체 모지??  (0) 2005.01.03
4000 히트 이벤트!!!  (0) 2004.12.22
Posted by tornado
|

이거 원 만들때 마다 새로 만드니 당췌ㅡㅡ

 

Tomcat 4.1.30 + window XP SP2 단독 테스트

Tomcat 4.1.24 + Apache 2.0.43(맞나??) + linux  테스트.

 

IE 6.0 / Firefox 만 딱 두번씩 테스트 했네 ㅡㅡ

 


package com.javarush.util;

 

import java.io.File;
import java.io.FileInputStream;
import java.io.OutputStream;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.WritableByteChannel;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

 

/**
 * @author Tornado
 */
public class DownloadUtil {
   
    private File file;
    private HttpServletRequest req;
    private HttpServletResponse res;
   
    public DownloadUtil(){   }
   
    public DownloadUtil(File file, HttpServletRequest req, HttpServletResponse res ){
       
        this.file = file;
        this.req = req;
        this.res = res;
       
    }
     
    public void sendFile() throws Exception{
       
        if(!(file.isFile() && file.exists())){
           
            throw new Exception("file.not.found");
           
        }
 
        if(!initResponse()){
           
            throw new Exception("download.failure");
           
        }
       
        FileInputStream fin = null;
       
        FileChannel inputChannel = null;
       
        WritableByteChannel outputChannel = null;
       
        try{
           
            fin = new FileInputStream(file);
           
            inputChannel = fin.getChannel();
           
            outputChannel = Channels.newChannel(res.getOutputStream());
           
            inputChannel.transferTo(0, fin.available(), outputChannel);
           
        }catch(Exception e){
           
            throw e;
           
        }finally{
           
            try{
               
                if(fin != null) fin.close();
            
             if(inputChannel.isOpen()) inputChannel.close();
            
             if(outputChannel.isOpen()) outputChannel.close();
            
            }catch(Exception e){
               
            }
           
        }
       
    }
       
    public boolean initResponse() throws Exception{
        
        if(res == null || req == null) return false;
       
        if(file == null)  return false;
       
        res.setContentType("appliction/octet-stream");
       
        res.setHeader("Accept-Ranges","bytes");
       
  if (req.getHeader("User-Agent").indexOf("MSIE 5.5") > -1) {
      // IE 6.0 Test Ok
   res.setHeader("Content-Disposition", "filename=" + java.net.URLEncoder.encode(file.getName(), "UTF-8") + ";");
   
  } else if(req.getHeader("User-Agent").indexOf("Mozilla/5.0") > -1){
      // firefox Test Ok
      if(req.getServerPort() == 8080){
         
          res.setHeader("Content-Disposition", "filename=" + new String(file.getName().getBytes("KSC5601"), "ISO-8859-1") + ";");
         
      }else{
         
          res.setHeader("Content-Disposition", "filename=" + new String(file.getName().getBytes(), "KSC5601" ) + ";");
         
      }
     
  } else {
     
   res.setHeader("Content-Disposition", "attachment; filename=" + java.net.URLEncoder.encode(file.getName(), "UTF-8") + ";");
   
  }
  
  res.setContentLength((int)file.length());
       
        return true;
       
    }
   
}

 

 

사용법 :


<%@ page contentType="text/html; charset=euc-kr" import="java.io.*, java.net.*, com.javarush.util.*" %><%
        request.setCharacterEncoding("euc-kr");
        String fileName = request.getParameter("fileName");

        if(fileName != null){
                //fileName = URLDecoder.decode(fileName, "euc-kr");

                // get 으로 넘어오건.. POST 로 넘어오건.. .

                // DB 에서 파일이름을 읽어오건..

                // 한글로 된 파일 이름이 콘솔에 제대로 나와야

                // 다운로드가 제대로 됨

                System.out.println("fileName : " + fileName);


                File dir = new File(request.getContextPath() + "upload");
                File file = new File(dir, fileName);
                DownloadUtil downUtil = new DownloadUtil(file, request, response);

                downUtil.sendFile();
        }


%>

 


 

 

'JAVA > JSP_Servlet' 카테고리의 다른 글

[펌] http RFC  (0) 2005.01.17
[펌]Single Sign On...  (0) 2005.01.17
헐.. 죽이넹  (0) 2004.12.21
얼... 좋은걸~  (0) 2004.12.17
Converting JDBC Result Sets to XML  (0) 2004.12.09
Posted by tornado
|

[펌] winbbs.com --> xp  질문 게시판

 

내컴퓨터-속성-고급-성능-설정-시각효과-여기서 설정이 제일위에 내컴퓨터에 가장좋은 설정을 자동선택 여기에 체크가되어있고 밑에 박스안에 모두 체크가 되어있어야합니다.

Posted by tornado
|

[link] 자바 검색 봇~

JAVA/JSE 2005. 1. 11. 15:44

http://www.nutch.org/docs/en/

 

lucene 도 봐야 허고.. 이거도 봐야하고..

간단한 게시판에 역인덱스 구조도 만들어 봐야 하고

할꺼 디지게 많은데.. 와우가 나를 부른다.. ㅡㅡ

'JAVA > JSE' 카테고리의 다른 글

[nio] Channels 클래스  (0) 2005.01.17
[펌] Sorting Algorithm  (0) 2005.01.15
[펌] [Collection Framework ] List, Set and Map  (0) 2004.12.29
소스포지.. 디컴파일해두 소스 안보여주는  (0) 2004.11.23
[허가없는 링크^^] 패턴  (0) 2004.10.25
Posted by tornado
|

 

조동진 - 제비꽃

 


 

    내가 처음 너를 만났을 때
    너는 작은소녀였고 머리엔 제비꽃

    너는 웃으며 내게 말했지
    아주 멀리 새처럼 날으고 싶어

    내가 다시 너를 만났을 때 너는 많이 야위였고
    이마엔 땀방울 너는 웃으며 내게 말했지

    아주 작은 일에도 눈물이 나와
    내가 마지막 너를 보았을 때

    너는 아주 평화롭고 창 너머 먼 눈길
    너는 웃으며 내게 말했지

    아주 한밤중에도 깨어있고 싶어

 

Posted by tornado
|
[방문히트이벤트] 4100 히트를 잡아라!
박종복님이 당첨되었습니다.

'이것저것 > 낙서장' 카테고리의 다른 글

4300 히트 이벤트!!!  (2) 2005.01.17
[phpschool 펌]접속자의 국가를 알아내서 해당 국기로 표시하기  (0) 2005.01.14
도대체 모지??  (0) 2005.01.03
4000 히트 이벤트!!!  (0) 2004.12.22
[펌] 바쇼의 하이쿠  (0) 2004.12.21
Posted by tornado
|

아침에 오니 회사 인트라넷 로긴 안됨.. ㅡㅡ

 

FormBean 이 Null 이라네 ㅡㅡ

 

절라게 헤매다가 struts.jar 파일 덮어 씌우니까 갑자기 됨 ㅡㅡ

 

원인 분석 불가 ㅡㅡ

 

모 이런 경우가 다 있냐.. . 에러로그도 안남고 ㅡㅡ

 

한마디로... 썅~!

 

 

 

Posted by tornado
|