C 코딩 스타일 (C Coding Style)

개발 노트 2008. 5. 26. 13:40 posted by 무병장수권력자


출처 : http://bebop.emstone.com/projects/programming_rules/emstone_guidelines/c_coding_style

Contents

코딩 스타일(Conding Style)이란

소스 코드가 작성되는 형식을 말한다. C를 예로들면 중괄호({})의 위치, 들여쓰기, 괄호가 사용되는 방법들을 의미하는 것이다.

대부분의 오픈 소스 프로젝트에서는 가능하면 프로그래머 개인의 취향이 강하게 반영되는 코딩 스타일에 그리 많은 제약을 하지 않지만, 코드의 '일관성'은 매우 중요하게 여긴다. 즉, 하나의 모듈이나 프로젝트에서는 가능하면 우선적으로 작성된 방식의 코딩 스타일을 따르는 것은 최소한의 예의면서 동시에 프로그램의 일관성을 지키도록 도와 준다.

여기서 기술하는 코딩 스타일은 많이 알고 있는 리눅스 커널 코딩 스타일과 Glib, Gtk+등을 포함하는 GNOME 관련 프로그래밍에서 사용되는 방식을 뼈대로 하지만, 필요한 부분만 발췌하고 나머지는 새로 추가된 것이다.

GNU에서 기본적으로 제안하는 GNU 방식의 코딩 스타일의 특징은 들여쓰기 단위가 2칸, 모든 중괄호는 새로운 행에서 시작한다는 점이다. 그래서 어떤 면에서는 코드 내부의 블럭별 구분이 쉬워지고, Gtk+ 프로그래밍과 같이 API 이름이 대부분 긴 프로그래밍을 할때 웬만해선 하나의 명령어가 한 줄을 넘어가지 않아서 보기 좋다는 장점이 있다. 하지만, 중괄호 위치를 놓는 방식은 실제 코드보다 중괄호에 할당되는 여백이 더 많고, 로직의 깊이에 신경쓰지 않는 프로그래밍 습관을 낳을 우려가 있으며, 결정적으로 이맥스가 아닌 에디터에서는 배보다 배꼽이 더 큰 코딩스타일이 되버린다.

리눅스 커널 코딩 스타일은 위에서 말한 장점과 단점이 그대로 반대가 되는 경우이다. (리눅스 커널 코딩 스타일 문서에 있는 리눅스의 농담을 무시하라. GNU의 코딩 스타일은 개인적인 선호와는 별개로 소프트웨어 공학 관점에서는 코드의 유지보수와 디버깅, 그리고 버그 없는 코드를 만드는데 매우 효율적인 것으로 알려져 있다)

들여쓰기 및 텍스트 너비

들여쓰기는 리눅스 커널 코딩 스타일과 같은 8 칸을 기준으로 하며, 공백문자(space)가 아닌 탭(tab) 문자를 사용하는 것을 원칙으로 한다.

8칸 탭을 사용하면 좋은 가장 큰 이유는 코드 읽기가 쉬워진다는 점이다. 또한 리눅스코딩스타일 에서도 지적한 바와 같이 들여쓰기 깊이가 3단계를 넘어갈 경우 (이런 경우 80칸 기준의 터미널에서는 반드시 행이 넘어가서 코드가 못생겨진다) 함수 설계가 잘못 되었다는 걸 의미한다. 즉, 그런 경우 다른 함수로 모듈화하거나 다른 방법을 검토해 보아야 한다.

소스코드가 씌어지는 텍스트의 너비는 80칸 을 기준으로 한다. 넘어가게 될 경우 다음 줄에 한 단계 더 들여쓰기를 해 이어 작성한다. 하지만 만일 이어지는 코드가 의미적으로 반드시 같은 라인에 있는게 코드의 가독성을 높일 경우 한 라인에 작성해도 된다. 80칸 기준에 너무 얽매이기 보다는 적당한 여유를 두되 전체적으로 일관성을 유지하도록 하는게 좋다.

함수 호출 인수 같은 파라미터는 다음과 같이 괄호 다음 첫번째 인수에 맞춰 정렬한다. 하지만 그리 많지 않다면 한 줄에 모두 넣는 것을 권장한다.

loving_you_with_or_without_me_f(first_param, second_param,
                                      third_parameter, fourth_param);

함수 이름이 너무 길어 인수를 적을 공간이 적을 경우 다음과 같은 형태도 가능하다.

loving_you_with_or_without_me_f(
        first_parameter,            /* with you ? */
        second_parameter,       /* without you ? */
        third_parameter,           /* etc... */
        fourth_parameter);

만일 이맥스(Emacs) 사용자라면 리눅스 커널 들여쓰기 스타일을 다음과 같이 ~/.emacs 파일에 저장하면 편하게 사용할 수 있다.

(add-hook 'c-mode-common-hook
           (lambda ()
           (c-set-style "k&r")
           (setq c-basic-offset 8)))

21.x 버전 이후의 ''이맥스''에서는 다음과 같은 설정으로 충분하다.

(add-hook 'c-mode-common-hook
           (lambda ()
           (c-set-style "linux")))

Vi(m) 을 사용한다면 다음과 같은 설정을 ~/.vimrc 파일에 넣는 것 만으로 충분하다.

set ts=8
if !exists("autocommands_loaded")
 let autocommands_loaded = 1
 augroup C
     autocmd BufRead *.c set cindent
 augroup END
endif

 하지만 가독성을 위해 한 줄을 여러 줄로 나눌 경우 CVS나 SubVersion 같은 버전관리 시스템을 사용할때 버전별 차이점(diff)을 확인할때는 오히려 가독성이 떨어진다. 예를 들어 의미적으로 한 줄만 변경되었는데 코드 상으로 여러 줄이 변경될 경우 그 차이점을 추적할때 매우 피곤해지는 걸 느낄 수 있다.

indent 프로그램

이맥스Vim 같은 에디터가 아니더라도 좋은 에디터는 대부분 코딩 스타일에 따라 커스터마이징할 수 있는 기능이 있다. 하지만, 그런 기능이 없더라도 코드의 내용은 건드리지 않으면서 코드의 모양만 다시 가공해주는 프로그램이 indent 이다.

매뉴얼을 찾아보면 더 많고 자세한 옵션을 알수 있지만, 대개 -kr 옵션만 주어도 예쁘고 이 문서에 비슷한 코딩 스타일을 얻을 수 있다. 하지만, 조금 더 세밀하게 조정된 옵션은 다음과 같다.

-kr -i8 -ts8 -br -nce -bap -sob -l80 -npcs -ncs -nss -bs -di1 -nbc -lp -npsl

위 내용을 가지는 .indent.pro 라는 이름의 파일을 자신의 홈디렉토리나 실행중인 디렉토리에 넣으면 indent 는 자동으로 옵션을 읽어들이게 된다.

하지만 명심할 것은, indent 가 반드시 나쁜 프로그래밍 습관을 고쳐주지는 않는다는 점이다.

중괄호 위치

들여쓰기와는 달리, 하나의 중괄호 치는 방법을 선택해야 할 이유는 별로 없지만, 여기서 선택한 방법은 C와 C++의 저자들이 즐겨 쓰던 방법인 여는 괄호를 줄 마지막에 놓고, 닫는 괄호는 맨 앞에 놓는 것이다.

if (x is true) {
        we do y;
} else {
        wo do z;
}

for (i = 0; i < max; i++) {
        ;
}

switch (prev->state) {
case TASK_INTERRUPTIBLE:
        if (unlikely(signal_pending(prev))) {
                prev->state = TASK_RUNNING;
                break;
        }
default:
        deactivate_task(prev, rq);
case TASK_RUNNING:
        ;
}

코드의 라인 수로 프로그래머의 생산량을 판단하던 시절이라면 이런 코딩 스타일은 분명히 환영받지 못할 것이다. 하지만 반대로 이 방법은 중괄호를 넣기 위해 한 줄을 낭비할 필요가 없으며 유용한 주석을 코드에 더 넣을 수 있는 여지를 준다.

하지만 분명히 함수만은 다른 규칙을 따른다. 중괄호를 다음 라인의 시작에 넣는다. 이는 C뿐 아니라, C++과 같은 클래스 내부 함수를 직접 코딩할때도 해당한다.

int function(int x)
{
       body of function
}

닫는 괄호에 대한 예외 는 문장에 이어지는 부분이 있을 경우이다. 예를 들어 do ~ while 절과 연속되는 else if 절이다. 이 역시 K&R을 따른 것이다.

do {
        body of do-loop
} while (condition);
if (x == y) {
        ..
} else if (x > y) {
        ...
} else {
        ....
}

이름짓기

이름 짓기, 즉 명명법은 코딩 스타일에서 가장 반드시 지켜야 할 만큼 중요하다. 특히 공동 작업으로 이루어지는 개발 프로젝트에서는 함수나 변수의 이름을 통해서 많은 정보를 추가 노력 없이 얻을 수 있기 때문에 코딩은 물론 코드 이해에 드는 노력도 줄어든다.

물론 C++과 달리 C 언어에서는 별다른 이름 공간(Name Space)를 구분하지 않기 때문에 전체적인 이름 공간을 오염시키지 않기 위해서도 반드시 필요하다. 다시 말하면, 서로 다른 모듈에 같은 이름의 함수가 있을 경우 컴파일 후 링크시 이름 충돌로 인해 에러가 발생한다. 또한 규칙 없이 사용된 이름은 코드의 재사용 및 추적을 매우 어렵게 한다.

함수 이름 은 모두 소문자로 이루어지며, 다음과 같은 규칙을 따른다.

{프로젝트}_모듈{_하위모듈}_동사/형용사{_목적어}

구성한다. 여기서 중괄호({})로 둘러 쌓인 부분, 즉 프로젝트 이름과 하위모듈은 경우에 따라 생략될 수 있다는 것을 의미한다. 프로젝트 이름은 약자를 사용할 수도 있고, 프로젝트 이름을 그대로 사용할 수도 있다.

전역 변수 이름 역시 함수와 같은 규칙으로 구성된다. 이는 static 이 붙는 모듈 내부의 전역 변수도 포함된다.

{프로젝트}_모듈{_하위모듈}_명사/형용사

짧은 약어 같은 이름을 사용하면 안된다. 전역변수나 구조체, 클래스에 종속된 변수는 반드시 길고 서술적인 이름을 사용해야 한다. 리누스가 얘기한 것처럼 이름은 설명적이어야 한다. cntusr() 같은 이름 대신 count_active_users() 와 같은 이름을 사용한다. 이는 코드를 더욱 읽기 쉽게 하며 그 자체가 문서 역할을 하게 된다.

하지만, 가능하면 모듈 외부로 전역 변수를 공개하는 것은 피해야 한다. 성능이나 구조적인 이유 때문에 어쩔 수 없는 경우라 하더라도 반드시 데이터에 접근하기 위한 인터페이스 함수를 제공하는 것이 좋다.

지역 변수(local variables) 는 "i"나 "tmp"처럼 짧은 이름을 사용할 수 있다. 이는 물론 모든 전역 변수가 네이밍룰을 완전히 지킨다는 가정하에서만 옳다. 즉, 약어로 표현된 변수는 지역변수임을 알아채고, 같은 블럭의 시작이나 함수의 시작 부분을 검사하면 바로 확인할 수 있기 때문에 가능하다. 이는 휠씬 쓰기 쉽고 이해하기 어렵지 않으며, 오히려 길게 쓰는 건 비생산적이다. 하지만, 그 이름 역시 오해의 여지가 있으면 안된다.

전체적으로 이러한 규칙은 다음과 항목에도 사용된다.

  • 함수 이름은 소문자이며, 밑줄(underscore)로 단어를 구분한다. (e_video_set_property(), viewport_switch_next())
  • 매크로(macro)와 열거형(enum)은 대문자이며, 역시 밑줄로 단어를 구분한다. (E_VIDEO_GET_DEVICE(), E_VIDEO_NTSC)
  • 클래스(class)나 타입(type), 구조체(struct) 이름은 EVideoDevice 와 같이 대소문자를 섞어쓴다. 구조체의 경우 typedef 문으로 정의하여 사용하는 경우 이름 앞에 '_' 문자를 붙여 타입(type)과 구조체(struct)를 구분한다.
  • 객체 안에 포함되는 함수(member functions)도 위의 함수와 같은 규칙을 따른다. 단 유의할 점은, 이름을 지을때 의미 중복을 피해야 한다는 점이다. 이는 아래에서 다시 논한다.

파일 이름 은 약간 다른 규칙을 따른다.

  • 대문자 없이 밑줄로 단어를 구분하는 이름을 사용한다.
  • 파일 이름은 동일한 이름의 모듈, 구조체, 클래스 이름을 따른다. 즉, 누군가 avmania_ 로 시작하는 함수나 변수를 참고하고 싶을 경우, avmania.h``나 ``avmania.c, avmania.cpp 파일을 열면 바로 찾아볼 수 있도록 해야 한다.
  • 파일이 라이브러리의 일부일 경우 라이브러리 이름을 접두어로 항상 붙인다. (예: libpantilt 라이브러리의 파일은 pantilt_ 로 시작)

단어 구분에 밑줄(_)을 사용하면 좋은 점은 코드에 사용할 수 있는 단어를 좀 더 자유롭게 사용할 수 있고, 단위 이동을 지원하는 대부분의 에디터에서 더 쉽고 빠르게 이동하고 편집할 수 있다는 점이다.

일관된 이름

변수 이름을 일관적으로 지어야 한다는 사실은 매우 중요하다. 한 예로, 리스트를 다루는 모듈이 리스트 포인터 변수의 이름을 "l"이라고 지었다. 물론 이는 간결함과 간편함을 위해서다. 하지만, 위젯과 크기를 다루는 모듈이 위젯(widgets)과 가로(widths)를 모두 "w"라고 이름을 지으면 안된다. 이는 코드를 매우 읽기 어렵게 하고 코드의 일관성을 떨어뜨린다.

좋은 변수나 함수 이름을 지을때 일반적 의미의 단어를 피하는 것도 좋은 방법이다. 또한 이름을 지을때 가능한 기술적인 용어를 피하는 것도 좋은 방법이다.

의미 중복 피하기

대부분의 경우 하나의 이름에는 같은 단어가 두 개 이상 들어갈 필요가 없다. 또한 단어가 중복되면 타이핑에 드는 노력도 더 많이 든다. 반드시 의미상으로 구분해야할 필요가 있을 경우에만 같은 단어를 사용하는 것을 원칙으로 한다.

특히 구조체나 클래스의 함수 이름 같은 경우 구조체나 클래스의 이름이 이미 포함하고 있는 내용을 함수 이름에 다시 넣는 경우가 많은데, 이는 피해야 한다.

함수

함수는 짧고 예뻐야 하며 단지 한가지 일만 해야 한다. 특히 switch ~ case``문이 아닌 ``if ~ else if 가 길게 반복되는 코드에서 각 블럭이 길어질 경우 하나의 함수로 분리하는 것을 고려한다. 성능을 고려해야 된다면 static inline 을 함수 앞에 붙이는 것과 같은 컴파일러의 인라인(in-line) 기능을 이용하면 된다. 컴파일러는 프로그래머가 원하는 코드를 생성해 줄 것이다.

특히 복잡한 함수일 경우, 영어를 할 줄 아는 고등학교 1학년 학생이 읽어도 코드를 이해할 수 있을만큼의 코드를 작성하기는 힘들다. 이를 위해서는 실행 단위별로 주석을 다는 방법도 괜찮지만, 각각의 실행 단위를 충분히 설명적인 보조 함수로 나누는 것도 좋은 방법이다.

또 하나의 고려사항은 함수에서 사용하는 지역변수의 개수이다. 지역변수는 5~10개를 넘지 말아야 한다. 만일 그렇다면 뭔가 잘못하고 있는 것이다. 다시 생각해보고, 더 작은 단위로 나눈다. 일반적으로 사람의 두뇌는 서로 다른 7가지 일은 동시에 유지할 수 있지만, 그 이상이 되면 힘들어지는 것으로 알려져 있다. 당신이 천재라 할 지라도, 2주 전에 자신이 작성한 코드를 이해하고 싶을 때가 올 것이다.

static과 const 사용

들여쓰기와 이름의 일관성만 지켜도 코드는 거의 명료해진다. 하지만 이 두가지는 프로그래머, 즉 사람의 뇌를 피곤하지 않게 하기 위한 방법일 뿐이다. 하지만, 컴파일러의 도움을 받으면 프로그래머는 더욱 즐거워진다.

모든 함수와 변수는 항상 static 으로 선언한다. 그리고 현재 모듈 바깥에서 사용하는 것만 진짜 전역(global) 함수로 선언한다.

이 방법의 장점은 첫째 static 키워드가 붙은 키워드는 모듈 밖에서 참조할 수 없도록 컴파일러가 막아주기 때문에, 다시 말해 전역으로 취급되지 않기 때문에(export) 전체적인 소프트웨어 코드의 이름 공간(name space)을 오염시키지 않는다. 두번째 장점은 이름 공간이 현재 모듈로만 제한되기 때문에 다른 모듈에서 내부에서 사용할지 모르는 어떤 이름이라도 마음 놓고 사용할 수 있다. 따라서 모듈 안에서만 사용되는 간단한 함수에 이름짓기 규칙을 반영할 필요가 없어져 두뇌 노동을 덜 수 있다. 그리고, 마지막으로 GCC와 같은 컴파일러는 -Wall -Wunused 옵션을 주어 컴파일하면 static 으로 선언이 되었으면서도 한 번도 사용된 적이 없으면 경고해 준다. 따라서 복잡한 로직에서 빠뜨리기 쉬운 점을 다시 한 번 검토할 수 있다.

const 키워드 역시 남용해 주길 바란다. 이를 통해 컴파일러는 더욱 많은 숨겨진 버그를 찾아줄 것이다.

사용자가 해제(free)하면 안되는 내부 데이터 포인터를 돌려주는 함수가 있다면, const 를 사용해야 한다. 이는 사용자가 그런 작업을 하려 하면 자동으로 경고해 줄 것이다. 다음 예제에서 컴파일러는 함수가 돌려준 문자열을 해제하는 코드가 있을 경우 사용자에게 경고할 것이다.

const char *man_get_name_by_id(int id);

매크로 및 enum

규칙이 없는 마법수(magic values) 는 코드에 숫자 그대로 적지 말고 항상 매크로를 사용해 정의한다.

/* Amount of padding for GUI elements */
#define GNOME_PAD       8
#define GNOME_PAD_SMALL 4
#define GNOME_PAD_BIG   12

한 변수에 가능한 값이 여러개일 경우 대부분의 경우 매크로를 이용해 그 값을 정의해 사용한다. 하지만 그렇게 하지 말라. 반드시 enum 을 사용해 정의하라. 컴파일러는 물론 디버거까지도 그 값에 대한 심볼 정보를 알게 되기 때문에 컴파일시 뿐 아니라 디버깅 시에도 암호 같은 숫자 대신 enum 으로 정의한 심볼을 볼 수 있을 것이다. 물론 이런식으로 선언된 열거형 변수를 int형 으로 선언하지 말고 반드시 enum 에서 정의한 타입으로 선언해야 효과가 있다. 다음 예제는 매우 많이 처하는 환경의 한 예일 것이다.

/** Shadow types */
typedef enum {
       GTK_SHADOW_NONE,
       GTK_SHADOW_IN,
       GTK_SHADOW_OUT,
       GTK_SHADOW_ETCHED_IN,
       GTK_SHADOW_ETCHED_OUT
} GtkShadowType;

void gtk_frame_set_shadow_type (Gtkframe *frame, GtkShadowType type);

이런 방법은 비트 필드(bit field)방식의 집합에도 적용할 수 있다.

/** Update flags for items */
typedef enum {
       GNOME_CANVAS_UPDATE_REQUESTED  = 1 << 0,
       GNOME_CANVAS_UPDATE_AffINE     = 1 << 1,
       GNOME_CANVAS_UPDATE_CLIP       = 1 << 2,
       GNOME_CANVAS_UPDATE_VISIBILITY = 1 << 3,
       GNOME_CANVAS_UPDATE_IS_VISIBLE = 1 << 4
} GnomeCanvasUpdateType;

헤더 파일

한 모듈의 헤더 파일은 그 모듈의 인터페이스 역할을 한다. 즉, 외부에서 모듈에 접근하기 위한 API와 그 API를 사용하는데 필요한 자료구조나 정의만 포함해야 한다.

모듈 안에서 헤더 파일을 포함(include)할때는 다음과 같은 순서로 정렬한다.

  1. 시스템 라이브러리 헤더 파일
  2. 다른 모듈 헤더 파일
  3. 자신의 인터페이스 헤더 파일

좋은 코딩 습관

위에서 적은 코딩 스타일 외에 다음 내용은 머리와 손이 습관처럼 따라야할 프로그래밍 습관이다.

  • 특별한 예외가 아닌 이상 파일 이름도 위의 이름 규칙을 따른다.
  • 파일 이름과 모듈 이름은 가능하면 일치시켜라. 예를 들어 e_video_* 형태의 API는 e_video.c 파일에 위치하는 것이 좋다.
  • 헤더파일에는 그 모듈의 인터페이스만 두어라. 반드시 extern 을 앞에 붙이고, 다른 모듈에서 사용할 수 있는 그 모듈의 API만 두어라. 선언이나 형도 API를 사용하는데 필요한 것만 헤더파일에 둔다.
  • 절대로 애매한 코드는 작성하지 말고, 항상 엄격하게 작성하라.
  • 수식을 명확하게 표현하는 것 이상의 괄호를 사용하지 말라.
  • 모든 키워드의 앞뒤, 콤마(,) 다음, 이진연산자(+, -, ...) 앞뒤에 공백을 넣어라.
  • 어설프게 코드를 수정하려 하지 말고, 명료하고 확장가능하며 유지보수가 쉬운 코드를 다시 만들어라.
  • 컴파일시 단 하나의 경고(warnings) 메시지도 무시하지 말고, 먼저 처리해서 없애라. 이는 헤더파일에 있는 함수 프로토타입 일치성 등과 같은 흘리기 쉬운 버그들을 잡도록 도와 준다.
  • 주석을 달아라. 각 함수 앞부분에 무엇을 하는 녀석인지 설명해 두는 것을 잊지 말라. 절대로 필요한 경우가 아니면, 어떻게(HOW) 동작하는지는 적지 말고 무엇을(WHAT) 하는지 기술하라. 그런 건 코드를 읽어보면 안다. 그렇지 않다면, 코드를 이해하기 쉽도록 다시 작성하라. 하지만, 라이브러리나 운영체제, 환경 상의 이유로 일부러 그렇게 작성한 코드는 반드시 주석을 달아야 한다.


[REST ②] Java Restlet을 이용한 RESTful 웹서비스의 구현

웹, 인터넷 2008. 5. 14. 17:40 posted by 무병장수권력자


Java Restlet을 이용한 RESTful 웹서비스의 구현

작성자 : 김문규
작성일 : 2008. 5.14

이번 포스트에서는 Restlet을 이용하여 RESTful 웹서비스를 구현하는 간단한 예제를 작성해 본다.

1. Restlet 이란?
Noelios Consulting이라는 회사에서 만든 Java 기반 REST 구현체이다.
자세한 튜토리얼이나 API 문서는
http://www.restlet.org/을 참조하길 바란다.
- 주요 기능
 . URI 입력과 서비스를 제공하는 클래스를 서로 연결
 . request의 method에 따라 해당 핸들러 함수를 호출
 . request와 response를 다루기 위한 라이브러리 클래스 제공
 . representation을 생성하기 위한 라이브러리 클래스 제공

2. 예제
 1) HTTP 서버 구동
 [방식 1]
 Restlet은 자체적으로 웹서버를 내장하고 있다. 따라서 개발 시 별도의 웹서버 없이 간단하게 테스트가 가능하다. Restlet 컴포넌트에 간단하게 HTTP 서버 connector를 연결함으로서 별도의 웹서버를 띄우지 않고도 간단하게 테스트가 가능하다. 여기서 Restlet에서 중요한 요소인 application 클래스가 등장한다. application 클래스는 web request를 확인하고 적절한 리소스를 연결해주는 역할을 한다. 태스크를 분배하는 관리자와 비슷하다고 할까?

<DefaultServer.java>
import org.restlet.Application;
import org.restlet.Component;
import org.restlet.data.Protocol;
public class DefaultServer{
  public static void main(String[] args) throws Exception {
    try {
      // Create a component
      Component component = new Component();
      component.getServers().add(Protocol.HTTP, 8182); // port number : 8182
      component.getClients().add(Protocol.FILE);
      // Create Application
      Application application = new DefaultApplication(component.getContext());
      // Attach the application to the component and start it
      component.getDefaultHost().attach(application);
      component.start();
    } catch (Exception e) {
      // Something is wrong.
      e.printStackTrace();
    }
  }
}

 [방식 2]
 WAS를 이용하여 HTTP 서버를 구동할 경우에는 다음과 같이 web.xml을 수정하면 된다.
 적색으로 표시된 부분만 본인의 서비스에 맞는 application과 연결하면 된다.

<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.4"
            xmlns="http://java.sun.com/xml/ns/j2ee"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
                 http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
   <display-name>first steps servlet</display-name>
   <!-- Application class name -->
   <context-param>
      <param-name>org.restlet.application</param-name>
      <param-value>
        
DefaultApplication
      </param-value>
   </context-param>

   <!-- Restlet adapter -->
   <servlet>
      <servlet-name>RestletServlet</servlet-name>
      <servlet-class>
         com.noelios.restlet.ext.servlet.ServerServlet
      </servlet-class>
   </servlet>

   <!-- Catch all requests -->
   <servlet-mapping>
      <servlet-name>RestletServlet</servlet-name>
      <url-pattern>/*</url-pattern>
   </servlet-mapping>
</web-app>

 2)application 클래스 구현
 application 클래스는 web request를 확인하고 적절한 리소스를 연결해주는 역할을 한다. router 클래스가 실질적으로 이런 연결 작업을 해준다.

import org.restlet.Application;
import org.restlet.Context;
import org.restlet.Restlet;
import org.restlet.Router;
public class DefaultApplication extends Application {
    public DefaultApplication(Context parentContext) {
        super(parentContext);
    }
    /**
     * Creates a root Restlet that will receive all incoming calls.
     */
    @Override
    public Restlet createRoot() {
        // Create a root router
        Router router = new Router(getContext());
        // Attach the handlers to the root router
        // 주소가 'service/item'이면 ResourceItems.class를 생성하고 적절한 핸들러 함수를 호출해라.
        router.attach("/service/item", ResourceItems.class);
       
        // Return the root router
        return router;
    }
};

 3) Resource 클래스의 구현
Resource 클래스는 실제 요청받은 작업들을 수행하는 곳이다. 어떤 데이터를 CRUD(create, read, update, delete)하고 그 결과를 HTTP response에 담아서 되돌려 주는 역할을 한다.

allowGet(), allowPost(), allowPut(), allowDelete() 함수는 web request의 허용 여부를 결정해준다. allowGet()은 기본적으로 true값을 가지기 때문에 별도의 overriding이 필요치 않다.

post(), put(), delete()는 해당 method에 대한 응답을 작성하는 핸들러 함수이다. getRepresentation()은 get method에 대해서 xml, xhtml, html representation을 생성하는 핸들러 함수이다. 각자 원하는 동작을 하도록 만들어 주면 된다.

import java.util.Iterator;
import java.util.List;
import javax.xml.xpath.XPathConstants;
import org.restlet.Context;
import org.restlet.data.MediaType;
import org.restlet.data.Request;
import org.restlet.data.Response;
import org.restlet.data.Status;
import org.restlet.resource.DomRepresentation;
import org.restlet.resource.Representation;
import org.restlet.resource.Resource;
import org.restlet.resource.SaxRepresentation;
import org.restlet.resource.Variant;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Text;
import com.sec.nurisam.DefaultDatabase;
import com.sec.nurisam.billing.model.DataItem;
public class ResourceItems extends Resource{
 
  private DataItem _item;
 
  public ResourceItems(Context context, Request request, Response response) {
    super(context, request, response);
   
    // This representation has only one type of representation.
    getVariants().add(new Variant(MediaType.TEXT_XML));
  }
   
  @Override
  public boolean allowPut() {
    return true;
  }
   
  @Override
  public void put(Representation entity) {
    String name = null;
    int price = -1;
    SaxRepresentation sax = getRequest().getEntityAsSax();
    try {
      name = (String)sax.evaluate("/param/name", XPathConstants.STRING);
      price = Integer.parseInt((String)sax.evaluate("/param/price", XPathConstants.STRING));
    } catch (Exception ex) {
      ex.printStackTrace();
    }
    if(name != null && price != -1) {
      _item = new DataItem(name, price);
      if(DefaultDatabase.getItemList().add(_item)) getResponse().setStatus(Status.SUCCESS_CREATED);
      else getResponse().setStatus(Status.SERVER_ERROR_INTERNAL);
    } else {
      getResponse().setStatus(Status.CLIENT_ERROR_BAD_REQUEST);
    }
  }
   
  @Override
  public Representation getRepresentation(Variant variant) {
    Representation representation = null;
    if(variant.getMediaType() == MediaType.TEXT_XML) {
      representation = this.getItems();
    } else {
      // do something
    }
    return representation;
  }
}

4) 정리
요약해보면, 하나의 서비스를 제공하기 위해서
 - application을 생성하고,
 - 여기서 router를 이용해서 URI-리소스를 매핑하며
 - 해당 리소스에는 각 요청 method에 대한 핸들러 함수를 설정하면 된다.
이 핵심 경로를 이해하면 다른 부분의 응용에 큰 문제가 없어 보인다.

3. 맺음말
개념에 관련된 내용만 책으로 한권을 읽었는데... 실제로 구현이 너무 쉽게 되는 것 같아서 다소 실망스러운 감이 없지 않을 것이다. 하지만, 실제로 서비스를 구현해 보면 REST의 철학과 관련되어 부딪히는 문제들이 속속 발생할 것이다. 이를 잘 해결하기 위해 Restlet보다 RESTful 웹 서비스에 대해 잘 이해하고 넘어가길 바란다.
           



윈도우 터미널 서비스 포트 변경

개발 노트 2008. 4. 23. 00:13 posted by 무병장수권력자


HKLM\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp
HKLM\SYSTEM\CurrentControlSet\Control\Terminal Server\Wds\rdpwd\Tds\tcp

에 PortNumber를 원하는 값으로 변경한다.

사용자 삽입 이미지


[REST ①] RESTful 웹서비스에 대해 알아보자!

웹, 인터넷 2008. 4. 2. 14:40 posted by 무병장수권력자


RESTful 웹서비스
작성자 : 김문규
최초 작성일 : 2008. 4. 2
 
 인터넷 업계는 OpenAPI의 열풍이 불고 있다. 너도나도 OpenAPI를 공개하고 있고 사용자들에게 다양한 방식의 사용을 기대하고 있다. 최근 이 OpenAPI와 함께 거론되는 기술을 당연 REST이다. 구글, 아마존, 네이버 모두가 OpenAPI를 REST 방식으로 지원한다. (물론 기존의 SOAP 방식도 지원한다.)
 그렇다면 REST란 과연 어떤것일까? W3C 표준이 아님에도 불구하고 업계의 사랑을 받는 이유는 무엇일까? 이 궁금증을 풀기위해 본 포스트를 작성하고자 한다.

1) 정의
+
REST는 ROA를 따르는 웹 서비스 디자인 표준이다.
 - ROA : Resource Oriented Architecture

2) 주요 특징
+ REST 방식의 웹서비스는 잘 정의된 Cool URI로 리소스를 표현한다.
무분별한 파라미터의 남발이 아니라, 마치 오브젝트의 멤버변수를 따라가듯이~
예를 들면 아래와 같다.
http://www.iamcorean.net/user/mk/age/32
기존의 서블릿을 이용한 URI는 대부분 이랬다.
http://www.iamcorean.net/finduser.jsp?user=mk&age=32
차이가 보이는가?

+ REST 방식의 웹서비스는 세션을 쓰지 않는다는 거다.
기존의 서블릿 개발에서는 세션을 이용해서 인증 정보들을 가지고 다닌다. 개발자의 개념(?)에 따라서는 파라미터까지 마구마구 집어넣어서 사용하기도 한다. 이 때문에 요청 처리가 너무너무 무거워진다.
또한 요청간의 전후 관련성이 생기기 때문에 한 세션의 일련의 요청은 무조건 하나의 서버가 처리해야 한다. 그래서 로드발란싱을 위해 고가의 로드발란싱 서버가 필요하게 된다.
하지만 REST는 세션을 사용하지 않기 때문에 각각의 요청을 완벽하게 독립적이다. 따라서 각각의 요청은 이전 요청과는 무관하게 어떠한 서버라도 처리할 수 있게 된다. 즉! 로드발란싱이 간단해 질 것이라는 것이 느낌이 오는가? (물론 인증 관련해서는 복잡한 문제가 생긴다.)

3) ROA의 정의
+ ROA는
 - 웹의 모든 리소스를 URI로 표현하고
 - 모든 리소스를 구조적이고 유기적으로 연결하여
 - 비 상태 지향적인 방법으로
 - 정해진 method만을 사용하여 리소스를 사용하는
 아키텍쳐 이다.

+ 이는 4가지의 고유한 속성과 연관되어 진다.
 - Addressablilty
 - Connectedness
 - Statelessness
 -
Homogeneous Interface

+ 여기서 잠깐! 정리하자면
REST란 위에 언급한 4가지 속성을 지향하는 웹서비스 디자인 표준이다.

4) RESTful 웹 서비스 속성 (ROA 속성)
+ Addressablilty (주소로 표현 가능함)
 - 제공하는 모든 정보를 URI로 표시할 수 있어야 한다.
 - 직접 URI로 접근할 수 없고 HyperLink를 따라서만 해당 리소스에 접근할 수 있다면 이는 RESTful하지 않은 웹서비스이다.
 - 예를 들면, GMail은 모든 메일 리소스는 하나의 URI와 연결되어 있다. (http://mail.google.com/mail/?hl=ko#sent/1036854d04d6de9e) 하지만 코리아닷컴의 경우에는 http://mbox07.korea.com/mail/mailView.crd 라는 주소에 접근한 후에 페이지에 표시된 메일의 링크를 통해서만 접근이 가능하다. (특정 회사를 지칭해서 유감입니다. 관계자 분들은 이해 부탁드립니다.) 둘 간의 차이가 이해가 되십니까?

+ Connectedness (연결됨)
 - 일반 웹 페이지처럼 하나의 리소스들은 서로 주변의 연관 리소스들과 연결되어 표현(Presentation)되어야 한다.
 - 예를 들면,
<user>
  <name>MK</name>
</user> 는 연결되지 않은 독립적인 리소스이다.
<user>
  <name>MK</name>
  <home>MK/home/</home>
  <office>MK/office</office>
</user> 는 관련 리소스(home, office)가 잘 연결된 리소스의 표현이다.

+ Statelessness (상태 없음)
 - 현재 클라이언트의 상태를 절대로 서버에서 관리하지 않아야 한다.
 - 모든 요청은 일회성의 성격을 가지며 이전의 요청에 영향을 받지 말아야 한다.
 - 다시 또 코리아닷컴의 예를 들면 메일을 확인하기 위해 꼭 '..코리아닷컴../mailView.crd'에 접근하여 해당 세션을 유지한 상태에서 메일 리소스에 접근해야 한다. 이것이 바로 Statelessness가 없는 예이다.
 - 세션을 유지 하지 않기 때문에 서버 로드 발란싱이 매우 유리하다.
 - URI에 현재 state를 표현할 수 있어야 한다. (권장사항)

+ Homogeneous Interface (동일한 인터페이스)
 - HTTP에서 제공하는 기본적인 4가지의 method와 추가적인 2가지의 method를 이용해서 리소스의 모든 동작을 정의한다.
 - 리소스 조회 : GET
 - 새로운 리소스 생성 : PUT, POST (새로운 리소스의 URI를 생성하는 주체가 서버이면 POST를 사용)
 - 존재하는 리소스 변경 : PUT
 - 존재하는 리소스 삭제 : DELETE
 - 존재하는 리소스 메타데이터 보기 : HEAD
 - 존재하는 리소스의 지원 method 체크 : OPTION
 - 대부분의 리소스 조작은 위의 method를 이용하여 대부분 처리 가능하다. 만일 이것들로만 절대로 불가능한 액션이 필요할 경우에는 POST를 이용하여 추가 액션을 정의할 수 있다. (되도록 지양하자)


5) 정리
REST는 . 웹의 모든 리소스를 URI로 표현하고 . 이를 구조적이고 유기적으로 연결하여 . 비 상태 지향적인 방법으로 . 일관된 method를 사용하여 리소스를 사용하는 웹 서비스 디자인 표준이다.

6) SOAP과 비교

사용자 삽입 이미지
사용자 삽입 이미지



SOAP은 SOAP메세지를 이용하여 특정한 서비스를 요청한다. SOAP 메세지를 사용해야 하기 때문에 무겁다고 말하고 서비스를 요청하기 때문에 SOA와 가깝다.
REST는 URI를 이용해서 리소스를 요청한다. URI를 사용하기 때문에 가볍다고 말하고 리소스를 요청하기 때문에 ROA와 가깝다.
SOA와 ROA에 대한 관점은 시각에 따라 다를 수 있지만 극명하게 대조하기 위해서는 위와 같이 차이점을 이해하는 것이 가장 좋다고 생각된다.

7) 맺음말
 최대한 쉽게 작성하고자 했지만 여전히 MK 본인에게도 설명하기 어려운 것은 사실이다. 하지만 중요한 것은 REST는 아키텍쳐가 아니라는 사실이다. ROA라는 아키텍쳐를 반영한 디자인 철학이다.
ROA의 4가지 중요한 속성을 잘 이해한 후 이를 가슴에 깊이 새기고 이를 어기지 않고 웹 서비스를 디자인하면 이것이 바로 RESTful 한 웹 서비스가 될 것이라는 것이다.
 다음 포스트에서는 REST 구현체들에 대해 생각해 볼 예정이다. .net, java, ruby on rails에서 각각의 접근 방식에 대해서 좀 더 구체적으로 알아보도록 하자.

8) 참고자료
1) Fielding 논문 원문

2) Restlet.org의 FAQ를 한국어로 번역한 자료 3) KUT의 한연희님의 자료
4) overstock.com에서 만든 자료 5) RESTful Web Service , O'Reilly (author : Leonard Richardson & Sam Ruby)




Traditional Web Service vs. OpenAPI

웹, 인터넷 2008. 4. 1. 10:37 posted by 무병장수권력자


Traditional Web Service vs. OpenAPI

작성자 : 김문규
최초 작성일 : 2008. 4. 1



구분

Traditional Web APIs

Web 2.0 APIs

선도기업

IBM, Microsoft

Google, Yahoo, Amazon, eBay..

주요기술

SOAP, XML-RPC Web Services

REST, XML

데이터 유형

HTML + (일부 XML 교환용 데이터)

다양한 XML (RSS, ATOM, RDF,..), JSON

기술 선도

기업

사용자

개발 프레임워크

Business Application Framework

Web Application Framework

개발 참여자

소수개발자

다수 사용자

기술 복잡도

복잡

단순

개발 속도

저속

고속

기술 관점

Application Centric

Platform Centric

연동방식

System Integration

Mash up

통합방식

수직적 통합

협력적 활용 

< 출처 : 2007년 TTA 정보통신표준화 워크샵>

웹서비스가 소개된지는 꽤 오래 되었다. SOAP 기반 웹 서비스가 그 중 대표 선수이다.
한 때 SOAP이 새로운 변화를 가져올 것이라 이야기 했지만 그 아키텍쳐 자체의 무거움과 서비스의 폐쇄성으로 인해 차츰 그 빛을 잃어 갔다.

웹 2.0의 시대가 도래하고 기업 주도적인 서비스가 아니라 개인 주도적인 서비스들이 우후죽순처럼 만들어 지고 있다. 1인 1 블로그의 시대가 멀지 않았고 하루에 몇만 히트를 자랑하는 전문 블로그들도 속출하고 있다. 즉, 웹이라는 것이 기업의 전유물이 아니게 된 것이다.
이에 따라 하나의 서비스를 여기저기서 나누어 쓰기를 원하게 되었고 언제부턴가 홈페이지에 하나 둘씩 특정 포탈의 검색 창이 생기고 너도나도 광고를 게재하기 시작했다.

드디어 웹서비스의 세상이 도래한 것이다. 하지만 기존의 SOAP은 프로토콜 자체가 다소 무겁기 때문에 여기저기서 쓰기에는 무리가 따르게 되었다. 그래서 급기야 사용자들은 필요한 기능만으로 꽉 찬 좀더 가벼운 새로운 웹 서비스 방식을 필요로 하게 된다.

가볍고, 사용하기 편하고, 관리할 필요가 없는 신개념 웹서비스!
그것이 바로 REST이다. RESTful Web Service라는 이름으로 알려져 있다. 여기서 OpenAPI라는 용어가 사용되기 시작했다. 뭔가 다르고 특이해 보이지만 기능면에서는 기존의 웹서비스와 전혀 다른 것이 아니다. 다만 간소화되고 사용하기 편한 웹서비스 인것이다.

바로 이게 요즘 말많은 OpenAPI인 것이다.


Google 사투리 번역 서비스 출시

Google 2008. 4. 1. 10:30 posted by 무병장수권력자


http://www.google.co.kr/landing/saturi/
사용자 삽입 이미지


회사 동료를 통해 구글의 획기적인 서비스 소식을 들었다.

이 글을 보는 순간! 나의 개발자 기질에 불을 지르고 말았다.
(개인적으로 구글의 개방적인 모습이 맘에 든다. 단지 밖에서 보이는 모습일지라도...)

멋지다!
이런 쓰잘데기 없는 서비스 개발을 할 수 있다는 사실이 넘 멋졌다.
(게다가, IT 업계에 팽배한 구글 코리아의 철수론에 보란 듯이 내놓은 야심작! 그런 느낌이 아닌가?)

하지만, ^^..... 만우절 유머 란다.
솔직히 나는 혼자서 너무 흥분해서 리뷰를 준비했었다. (이 글이 리뷰가 되버렸지만...)

근데, 갑자기 궁금해 지긴 했다. 진짜 만들면 어떻게 해야 할까?
일단 전문가를 초빙해서
캐 삽질을 통해 DB를 구축하고...
형태소를 분석한 후
....... (에잇 모르겠다.)

과제가 끝나면 적어도 팔도 사투리에는 능통해 지겠군...

However, 오늘 사건으로 구글에 대한 관심도는 대폭 증가 !

아쉬운 해프닝으로 끝났지만 조만간 출시될 구글 코리아의 야심작을 기대해본다.

'Google' 카테고리의 다른 글

시덥잖은 Google App Engine 튜토리얼  (0) 2008.08.21
Google App Engine 계정을 드디어 받았습니다.  (2) 2008.08.20
Google's Master Plan  (0) 2008.07.24
Google Code Jam  (0) 2008.07.09
Google App Engine Overview  (0) 2008.06.11


특정 문자를 제거하는 함수를 구현하라.

개발 노트 2008. 3. 30. 23:48 posted by 무병장수권력자


문자열에서 문자를 삭제하는 효율적인 함수를 작성하라. 함수 원형은 다음과 같다.
string removeChars( string str, string remove );
remove라고 인자로 전달된 문자열에 있는 모든 문자를 str이라는 문자열에서 삭제한다. 예를 들어, str이 "Battle of the Vowels: Hawaii vs. Grozny"로 주어지고 remove가 "aeiou"로 주어진다면 이 함수에서 str을 "Bttl f th Vwls: Hw vs. Grzny"로 변환시켜야 한다. 자신이 설계한 방식에 대해 합당한 근거를 제시하고 풀이의 효율에 대해 논하라.

작성자 : 김문규
최초 작성일 : 2008. 3. 30

풀이)

<1>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int is_remover(char ch, char* str);
char* remove_chars(char **str, char *remove);

int main(int argc, char** argv)
{
    char* org_str = NULL;
    char* remove = NULL;
    if(argc != 3) {
        printf("Usage : program [original_string] [remove_char]\n");fflush(stdout);
        return 0;
    } else {
        org_str = (char*)malloc(strlen(argv[1]));
        remove = (char*)malloc(strlen(argv[2]));
        sprintf(org_str, "%s", argv[1]);
        sprintf(remove, "%s", argv[2]);

        printf("original : %s, remover : %s\n", org_str, remove);fflush(stdout);
        printf("result : %s\n", remove_chars(&org_str, remove));

        free(org_str);
        free(remove);
    }
    return 1;
}

char* remove_chars(char **str, char *remove)
{
    int i = 0;

    int len = (!*str)?0:strlen(*str);
    char* pread = *str;
    char* pwrite = *str;

    for(i = 0; i < len; i++)
    {
        if(!is_remover(*pread, remove))
        {
            memcpy(pwrite, pread, 1);
            pwrite++;
        }
        pread++;
    }
    *(pwrite++) = '\0';

    return *str;
}

int is_remover(char ch, char* str)
{
    int i = 0;
    int len = (!str)?0:strlen(str);

    for(i = 0; i < len; i++)
    {
        if( ch == *str ) return 1;
        str++;
    }
    return 0;
}

<2> 모범 풀이 및 설명
 위의 풀이는 알고리즘의 효율 측면에서 문제가 있다. 현재의 풀이는 O(n*m)의 알고리즘이다(n:str 길이, m:remove 길이). 이 풀이는 Hash를 이용해서 O(n+m)의 알고리즘으로 구현가능하다.
 이와 같이 문자 검색의 경우에는 Hash를 이용하는 방법과 배열을 이용하는 방법 두가지가 있다. 비교되는 문자의 길이가 길고 사용 가능한 문자의 개수가 적을 경우에는 배열이 유리하고 반대의 경우에는 Hash가 유리하다. 역시나 그때 그때 경우에 따른 적절한 알고리즘의 선택이 중요하다.
 다음은 배열을 이용하여 알고리즘을 약간 개선한 풀이이다.

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int is_remover(char ch, char* str);
char* remove_chars(char **str, char *remove);

int main(int argc, char** argv)
{
    char* org_str = NULL;
    char* remove = NULL;
    if(argc != 3) {
        printf("Usage : program [original_string] [remove_char]\n");fflush(stdout);
        return 0;
    } else {
        org_str = (char*)malloc(strlen(argv[1]));
        remove = (char*)malloc(strlen(argv[2]));
        sprintf(org_str, "%s", argv[1]);
        sprintf(remove, "%s", argv[2]);

        printf("original : %s, remover : %s\n", org_str, remove);fflush(stdout);
        printf("result : %s\n", remove_chars(&org_str, remove));

        free(org_str);
        free(remove);
    }
    return 1;
}

int check[128]; // assume char is ASCII only.
char* remove_chars(char **str, char *remove)
{
    int i = 0;

    int len = (!*str)?0:strlen(*str);
    char* pread = *str;
    char* pwrite = *str;

    char* ptemp = remove;
    int len2 = (!remove)?0:strlen(remove);
    for(i=0; i < len2; i++)
    {
        check[*ptemp++] = 1;
    }

    for(i = 0; i < len; i++)
    {
        if(!check[*pread])
        {
            memcpy(pwrite, pread, 1);
            pwrite++;
        }
        pread++;
    }
    *(pwrite++) = '\0';

    return *str;
}



Web of Services - Open API and Mashup

웹, 인터넷 2008. 3. 29. 23:20 posted by 무병장수권력자


웹 서비스에 대한 기초 자료이다.
OpenAPI, Mash-up에 대해서 잘 설명되어 있으니 개념을 잡기 위해 사용하면 좋을 듯 하다.



출처 : 2007 TTA 정보통신 표준화 워크샵, ETRI


UltraEdit로 Ruby 개발하기! (Highlight)

개발 노트 2008. 3. 27. 10:32 posted by 무병장수권력자


아래의 파일이 해당 언어의 syntax에 대한 정의를 포함하고 있으며 이를 기준으로 강조가 실행된다.
C:\Program Files\IDM Computer Solutions\UltraEdit-32\wordfile.txt

해당 파일의 맨 마지막에 아래의 파일의 내용을 붙여 넣는다. 그냥 한가지 주의 L11, L12 등으로 번호가 정해져 있다. 이를 맟추어 주고 싶으면 자신의 UltraEdit 설정 파일을 유심히 본 후 해당 값만 수정하면 된다.


Ruby on Rails 개발 환경 구축

개발 노트 2008. 3. 27. 09:57 posted by 무병장수권력자


Ruby on Rails 개발 환경 구축하기

작성자 : 김문규
최초 작성일 : 2008. 3.27

1. One Click Ruby Installer 를 설치한다.
http://rubyinstaller.rubyforge.org

2. rails를 설치한다.
gem (ruby의 package manager)를 이용함
gem install rails -p
http://(proxy_server_ip):(proxy_server_port) --include-dependencies

3. MySQL 설치
Vista에서 문제(side-by-side 관련 에러)가 발생한다. 아래와 같은 과정으로 해결한다.

To install MySQL Server 5.0.51a in Vista
1. Disable the UAC in Windows Control Panel->User Accounts
2. Use mysql-essential-5.0.51a-win32.msi
3. In the final step uncheck "Configure MySQL Server now"
4. Download and run Resource Hacker http://www.angusj.com/resourcehacker/
5. Open ...\MySQL Server 5.0\bin\MySQLInstanceConfig.exe with Resource Hacker
6. Navigate to 24\1\1033
7. Change
<requestedExecutionLevel level="asAdministrator" uiAccess="false">
to
<requestedExecutionLevel level="requireAdministrator" uiAccess="false">
8. Press "Compile script"
9. Exit Resource Hacker and save the result (overwrite the initial MySQLInstanceConfig.exe)
10. Now MySQLInstanceConfig.exe should start normally.
11. Configure the server.
12. Sometimes the server doesn't start:
a) Check Windows Firewall settings (3306/TCP)
b) Try changing the compability mode for the file ...\MySQL Server 5.0\bin\mysqld-nt.exe to Windows XP-SP2.
13. That's all.
- from @ http://forums.mysql.com/read.php?11,195569,195569#msg-195569 -

MySQL port : 3306

4. ruby MySQL 드라이버를 설치한다.
이미 설치된 드라이버에 비하여 성능이 더 좋은 것이다. 되도록이면 설치하도록 하자.
gem install mysql -p
http://(proxy_server_ip):(proxy_server_port)