본문 바로가기
프로그래밍/Erlang

Erlang 프로그래밍 소개, Part 2: 고급 기능 사용하기

by o테리o 2013. 8. 26.

Erlang 프로그래밍 소개, Part 2: 고급 기능 사용하기

   Erlang은 동시성 및 분산 시스템을 개발하는 데 주로 사용되는 다목적 프로그래밍 언어입니다. 이 시리즈의 Part 1에서는 Erlang을 소개하면서 Erlang의 함수형 프로그래밍 스타일과 기타 프로그래밍 패러다임(예: 필수적, 절차적 및 오브젝트 지향 프로그래밍)을 비교하여 살펴보았습니다. Part 2에서는 몇 가지 고급 기능을 사용하게 됩니다. 먼저 기본 기능을 배운 후에 동시성 프로그래밍과 프로세스 및 메시징을 배웁니다. 이러한 기능은 함께 작동하여 Erlang의 강력한 기능인 분산 프로그래밍을 지원합니다.

동시성 프로그래밍

단일 프로그램 내에서 동시에 여러 개의 프로세스를 실행하는 기능(다수의 프로그램을 동시에 실행하는 것과는 대조됨)을 구현하려면 언제나 기존의 함수형 프로그래밍 언어의 프로그래밍 방식을 변경해야 한다. 이러한 동시성(또는 멀티스레딩) 관련 문제점으로 인해 정보를 업데이트할 대상 스레드는 처리할 데이터와 정보에만 영향을 미치게 된다. 예를 들면, 파일을 업데이트하는 프로세스에서 파일이 손상될 위험이 있기 때문에 하나의 파일을 업데이트하는 프로세스를 여러 번 실행해서는 안 된다.
Erlang은 모든 프로그램을 동시에 실행하되, 함수 및 모듈과 같은 컴포넌트가 결코 데이터를 공유하지 않는 방식을 취한다. 그 대신, 메시지를 사용하여 컴포넌트 간에 데이터를 교환한다. 메시지를 사용하는 경우에는 개별 컴포넌트가 데이터를 수정하는 기능을 제한하여 동시성 데이터 수정 문제점을 쉽게 제거할 수 있다. 그러나 데이터를 직접 변경하지 않고 메시지를 전송하여 데이터를 업데이트 하면 업데이트를 분리하거나 동시에 업데이트하기가 더 어려워진다. 또한, Erlang은 조작이 실패할 것이라는 원칙에 따라 작동한다. 따라서 Erlang을 사용하면 시스템에서 적절하게 오류를 처리할 수 있고 필요한 경우에는 오류를 복구할 수 있다.
Erlang은 소형의 경량 프로세스를 작성하여 동시성 관련 문제점을 내부적으로 처리한다. C와 같은 다른 언어와 달리 이러한 프로세스는 기본 운영 체제 프로세스나 스레드 모델을 기반으로 빌드되지 않고, Erlang의 가상 머신에 의해 내부적으로 작성되고 관리된다. 이렇게 되면 각 프로세스가 메모리와 CPU 요구사항 면에서 기본 OS 스레드보다 훨씬 더 경량화된다. 게다가 Erlang은 작동 과정에서 이러한 다수의 소형 프로세스가 자동으로 작성되고 사용되기 때문에 비교적 단순한 프로그램에서도 일반적으로 수천 또는 수백만 개의 프로세스가 실행된다.
Erlang에는 기본적으로 동시성 모델이 내장되어 있기 때문에 이처럼 많은 프로세스를 관리하는 작업은 Erlang이 작동하는 데 중요한 역할을 한다. 또한, 프로세스 간에 데이터를 전송할 때 사용되는 메시징 시스템을 내장하고 있다. 메시징 시스템은 현재 실행 중인 프로세스 수와 관계없이 모든 프로세스에게 매우 효율적으로 메시지를 배포할 수 있게 설계되었다. 메시지를 구현함으로써 생기는 부수 이익으로는 메시지를 내부적으로 전송할 수 있을 뿐만 아니라 네트워크 전체로도 전송할 수 있으며, 메시지를 이용하면 Erlang으로 다수의 인스턴스 전체에서 메시지를 공유하여 시스템 전체에서 분산 프로그래밍을 지원할 수 있다.

프로세스

Erlang의 프로세스 시스템은 매우 경량이기 때문에 프로세스를 작성하는 과정이 매우 간단하고 쉽다. 그리고 오버헤드가 없으므로 결과에 대해 걱정할 필요가 없다. 따라서 어떤 이유로든 프로세스를 새로 작성하여 애플리케이션에 도움을 줄 수 있다.
실제로는 내장 함수 spawn()을 호출하여 프로세스를 작성한다. 이 내장 함수는 다음과 같은 호출 구조(PID = spawn(module, function, arguments)로 되어 있다. 여기서 module, function, arguments는 각각 모듈 이름, 함수 이름 및 함수에 제공할 인수 목록이다. 리턴 값(위 예제의 PID)은 프로세스 ID이다.
예를 들면, 이 시리즈의 이전 파트에서 작성한 피보나치 함수는 다음과 같은 형식(PID = spawn(fib,printfibo,[10]))을 사용하여 새 프로세스로 호출된다.
spawn() 함수의 마지막 인수는 요소가 하나만 있는 목록으로 직접 함수를 호출할 때 사용하는 단일 인수가 아니다. 결과적으로 마치 함수가 호출된 것처럼, 함수를 실행할 프로세스(fib:printfibo(10))가 새로 작성된다.
spawn() 함수에 의해 작성되는 새 프로세스는 오류 없이 정상적으로 종료하거나 결함이 생겨서 비정상적으로 종료할 때까지 계속 실행된다.
실제 스포닝(spawning) 프로세스 자체는 호출할 함수가 존재하지 않는 경우에도 결코 실패하지 않는다. 따라서 해당 코드의 스포닝 프로세스를 테스트할 필요성이 줄어든다. Erlang 쉘의 오류뿐만 아니라 프로세스 오류가 오류 보고를 처리하는 내장 프로세스인 오류 로그 프로그램에 의해 처리되고 기록된다.
일반적으로 프로세스는 동시성을 지원하기 위해 사용된다. 위에 있는 피보나치 샘플에서는 또 다른 프로세스로 printfibo() 함수를 실행하면 쓸모 없는 리턴 값이 생성된다. 그러나 기본 fibo() 함수와 같은 새 프로세스를 생성하여 정보를 프로세스에 전송하거나 그 반대의 작업을 수행하고자 할 경우에는 어떻게 해야 할까?
내장 메시지 시스템은 이러한 상호 작용을 처리한다.

메시징

Erlang의 메시징 시스템은 Erlang의 실행 환경에 내장된 또 다른 부분으로 프로세스 시스템과 함께 작동하여 데이터와 메시지를 효과적으로 교환한다.
각 프로세스에는 또 다른 프로세스가 메시지를 전송할 수 있는 "메일함"이 있다. 메시지는 전송되는 순서대로 저장된다. 따라서 메시지 두 개(A 및 B)를 차례로 한 프로세스에서 다른 프로세스로 전송하면, 메일함에는 메시지 A가 먼저 표시되고 그 다음에는 메시지 B가 표시된다. 프로세스 시스템의 동시성 특성으로 인해 다수의 프로세스에서 하나의 프로세스로 전송되는 메시지의 순서는 동일한 프로세스의 개별 메시지 전송 순서 이외의 특정 방식을 따르지 않는다.
프로세스에 메시지를 전송하려면 통신하고자 하는 프로세스의 프로세스 ID를 알아야 한다. 그 다음에는 Pid ! Message 구문을 사용한다. 여기서 Pid는 프로세스 ID이고 Message는 Erlang의 데이터 유형이다.
프로세스 내에서 메시지를 수신하려면 receive문을 사용한다. receive문 내에서 패턴 일치를 사용하여 메시지 컨텐츠를 기반으로 수행할 작업을 결정한다. 패턴 일치에 성공하면 메일함으로부터 메시지가 수신되며, 패턴 일치 변수에 메시지 인수를 바인드하면 메시지 인수를 사용할 수 있고 해당 절이 실행된다.
예를 들면, '저장소'와 값의 원자를 제공하는 메시지를 대상으로 하는 패턴 일치는 목록 1과 같다.

목록 1. receive문을 사용하여 프로세스 내에서 메시지 수신 receive {store, Value} -> store(Value), {get, Value} -> get(Value) end

이 예제에서는 코드에서 패턴 일치를 사용하여 원자와 왼쪽에 있는 변수를 일치시킨 다음, 오른쪽에서 조작을 수행한다. 이 경우에는 메시지의 컨텐츠를 기반으로 값을 저장하고 값을 가져온다.
receive문은 프로세스가 일시적으로 실행을 중지하게 한다. 따라서 조작이 수행되기 전에 새 메시지가 수신될 때까지 프로세스가 대기하게 된다. 이와 관련된 전형적인 예는 다수의 프로세스 간에 공유될 수 있는 변수를 저장하는 기본 조작이다.
애플리케이션 내에서는 일반적으로 receive문을 루프의 일부로 사용하여, 프로세스에 전송된 새 메시지를 점진적으로 읽은 후??제(참고자료 참조)에서 알 수 있듯이 Erlang에는 전통적인 의미의 루프가 없다. 따라서 자신을 호출하는 함수를 작성하여 다음 메시지를 처리한다(목록 2 참조).

목록 2. 자신을 호출하는 함수를 작성하여 다음 메시지 처리 dbrequest() -> receive {store, Value} -> store(Value), {get, Value} -> get(Value) end

전통적인 동시성 환경에서는 세마포어와 같은 솔루션을 사용하여 프로세스가 변수가 "사용 중"인지 또는 업데이트될 수 있는지 판별하게 한다. 대부분의 환경에서는 세마포어를 사용하여, 동일한 값을 업데이트하려고 시도하는 각 프로세스를 대기하게 하며 이로 인해 프로그램의 실행이 지연될 수 있다. Erlang 내에서는 프로세스 내에서 개별 조작을 둘러싼 후, 메시징을 사용하여 업데이트를 처리한다. 메시지는 순서대로 수신되므로 그림 1과 같이 각 메시지를 순서대로 처리하여 각 조작을 개별적으로 수행할 수 있다.



그림 1. 메시징을 사용하여 단일 값으로 업데이트 처리

이러한 기본 동시성 및 메시징 구조는 수많은 다양한 애플리케이션에서는 기본적인 것이다. 예를 들면, Facebook에서는 Facebook 메시징을 위한 메시지 환경을 사용한다. 기본적으로 MochiWeb을 사용하여 웹상에서 인터페이스를 제공하는 문서 기반 데이터베이스인 CouchDB는 프로세스와 메시징 시스템을 사용함으로써 다른 데이터베이스에서 발생하는 전형적인 동시성 업데이트 문제점을 회피하면서 데이터베이스 업데이트와 응답이 올바르게 처리될 수 있게 한다.
특히, 메시징 기능을 동시 처리와 결합하여 사용하면 다양한 위치에서 다수의 요청이 수신되는 경우에도 프로그램에서 정보를 순차적으로 처리할 수 있다. 이렇게 하면 처리 과정에서 정보가 손상되거나 파괴되는 것을 신경 쓰지 않고도 데이터와 조작을 공유할 수 있다는 점에서 다른 언어의 전형적인 동시성 프로그래밍의 주요 문제점 중 하나를 피할 수 있게 된다. 또한, 대부분의 동시성 프로그래밍 문제점에서 경험하게 되는 기본적인 난점 중 하나를 제거할 수 있게 된다.
그러나 이제까지는 특히 최신 네트워크 및 웹 애플리케이션의 성능을 개선하고 솔루션을 확장하는 데 따르는 문제점을 해결하기 위해 동시성만을 사용했다. 이제는 둘 이상의 서버를 사용하여 이러한 문제점을 해결할 시점이 되었다. 다행히도 Erlang에는 분산 프로그래밍 문제점을 해결할 수 있는 솔루션이 있다.

분산 프로그래밍

Erlang의 분산 프로그래밍은, 메시지를 전송하고 수신할 뿐만 아니라 무엇보다도 기본 RPC 및 웹 서비스와 같은 환경에서 지원되는 이러한 유형의 원격 프로시저 호출을 지원하는 메커니즘을 제공하기 위해 이미 앞에서 살펴본 간단한 네트워크 서버와 메시징 시스템을 조합하여 이루어진다.
분산된다고 해서 반드시 서로 다른 시스템에 배치되는 것은 아니며, 서로 통신하여 정보나 조작을 공유하고자 하는 서로 다른 두 개의 Erlang 애플리케이션이 있을 수 있다. 통신할 시스템을 식별하는 것을 제외하면 일반적 사용 과정에서 Erlang은 해당 시스템의 로컬 또는 원격 특성을 구분하지 않는다.
분산 프로그래밍을 사용하기 시작하려면 먼저, Erlang을 시작하고 각 Erlang 인스턴스에 고유 이름을 지정해야 한다. 이 이름은 식별 과정에서 사용되어 이름이 지정된 Erlang 인스턴스에 메시지를 전송할 수 있게 한다. 명령행에서 Erlang을 사용할 때 이름을 설정하려면 sname 명령행 옵션을 사용한다(목록 3 참조).

목록 3. sname 명령행 옵션 사용 $ erl -sname one Erlang R13B04 (erts-5.7.5) [source] [64-bit] [rq:1] [async-threads:0] Eshell V5.7.5 (abort with ^G) (one@mammoth)1>

이름과 호스트 이름을 나타내도록 프롬프트가 변경되었으며 이는 해당 노드의 고유 ID로 Erlang 내에서 노드를 식별하고 노드 간에 통신을 수행하기 위해 사용된다.
이제 또 다른 Erlang 쉘 인스턴스를 시작하면 목록 4와 같이 서로 다른 다양한 이름을 설정할 수 있다.

목록 4. 다양한 이름 설정 $ erl -sname two Erlang R14B (erts-5.8.1) [source] [smp:8:8] [rq:8] [async-threads:0] [hipe] [kernel-poll:false] Eshell V5.8.1 (abort with ^G) (two@mammoth)1>

두 개의 노드가 실행 중이므로 net_adm:ping()을 사용하여 노드 간의 통신을 테스트할 수 있다(목록 5 참조).

목록 5. net_adm:ping()을 사용하여 노드 간의 통신을 테스트 (one@mammoth)3> net_adm:ping('two@mammoth'). pong

이는 인스턴스 간에 통신이 제대로 이루어지고 있다는 것을 나타낸다.
두 개의 프로세스 간에 메시지를 전송하려면 수신자의 프로세스 ID와 노드 이름이 포함된 수정된 메시징 연산자를 사용한다. 예를 들어, 프로세스가 basic이라는 이름으로 등록된 경우에는 목록 5에 있는 코드를 사용하여 메시지를 전송한다. 작성한 첫 번째 Erlang 인스턴스의 경우에는 쉘에 다음 구문을 입력한다. { basic, 'two@mammoth'} ! { self(), "message" }.
두 번째 호스트에서 현재 프로세스(즉, 쉘)를 'basic'으로 등록한 경우에는 메시지를 출력할 수 있는 메시지를 검색한다. 인스턴스 one에서 메시지를 전송하기 전에 현재 프로세스를 등록해야 한다(목록 6 참조).

목록 6. 현재 프로세스 등록 (two@mammoth)1> register(basic,self()). true

이제 메시지를 출력할 receive문을 작성한다(목록 7 참조).

목록 7. 메시지를 출력할 receive문 작성 (two@mammoth)2> receive (two@mammoth)2> {From,Message} -> io:format(Message ++ "~n") (two@mammoth)2> end. something ok

두 개의 Erlang 인스턴스 간에 메시지가 성공적으로 전송되었다. 이러한 Erlang 인스턴스는 서로 같은 시스템에 있지 않고 다른 위치에 있을 수도 있다. 두 시스템 간에 메시지를 전송할 수 있는 경우에는 프로세스 ID와 노드 이름만 알면, 모든 유형의 데이터를 간단하게 전송하고 수신할 수 있다.
또한, 원격 프로시저 호출을 더 쉽게 할 수 있도록 Erlang은 rpc:call() 함수를 지원한다. 이 함수의 호출 구조는 rpc:call(Node, Module, Function, Arguments)이다.
이 함수는 제공된 인수를 사용하여 원격 노드를 호출하고 특정 모듈과 함수를 실행하여 그 결과를 호출자에게 리턴한다. rpc 모듈에는 동기, 비동기 및 블로킹 호출을 할 수 있는 확장이 포함되어 있다. 이 호출은 직접 함수 호출이어서 많은 클라이언트가 동시에 이것을 실행하면 종속성 문제점이 발생하기 쉽기 때문에 메시징 모델과 비교하면 좋지는 않지만, 이점은 애플리케이션에 따라 달라진다.

MochiWeb 사용

MochiWeb은 Erlang을 기반으로 빌드된 전체 HTTP 웹 스택이다. MochiWeb은 이 기사에서 다룬 Erlang의 기능을 많이 사용한다. 예를 들면, 메시징과 프로세스를 사용하여 고성능의 상위 레벨 동시성을 제공한다. MochiWeb은 프로세스 시스템을 사용하여 동시성을 지원하고 메시지를 사용하여 요청을 처리하고 그 결과를 축적한다.
MochiWeb 자체는 고유한 애플리케이션을 빌드하고 확장할 수 있는 기본 프레임워크를 신속하게 작성하는 데 필요한 스크립트 모음과 코드가 조합된 것이다. 이 마지막 섹션에서는 MochiWeb을 사용하는 방법과 웹 서버 애플리케이션을 새로 설정하는 방법 및 MochiWeb을 확장하여 자체 애플리케이션을 지원하는 방법을 설명한다.
GitHub에서 소스를 가져오는 것이 MochiWeb을 가장 쉽게 얻을 수 있는 방법이다. git 명령을 사용하거나 GitHub 웹 사이트에서 다운로드 가능한 패키지를 사용하여 이 작업을 수행할 수 있다(참고자료 참조).
git 명령을 사용하여 소스를 가져오려면 $ git clone https://github.com/mochi/mochiweb.git 명령을 사용한다.
이 명령을 실행하면 현재 디렉토리에 mochiweb 디렉토리가 작성된다. MochiWeb을 사용하려면 소스를 빌드해야 한다. 이렇게 하면 MochiWeb 애플리케이션을 새로 작성할 수 있도록 MochiWeb이 준비된다.
이렇게 하려면 먼저, 목록 8과 같이 mochiweb 디렉토리에서 make를 실행한다.

목록 8. 자원 빌드 $ cd mochiweb $ make ==> mochiweb (get-deps) ==> mochiweb (compile) Compiled src/mochiweb_sup.erl Compiled src/mochifmt.erl Compiled src/mochiweb_charref.erl Compiled src/mochiweb_request_tests.erl Compiled src/mochifmt_records.erl Compiled src/mochiweb_socket.erl Compiled src/mochiweb_app.erl Compiled src/mochiweb_io.erl Compiled src/mochifmt_std.erl Compiled src/mochiglobal.erl Compiled src/mochiweb_socket_server.erl Compiled src/mochijson.erl Compiled src/mochihex.erl Compiled src/mochiweb_html.erl Compiled src/mochiweb_multipart.erl Compiled src/mochilogfile2.erl Compiled src/mochiweb_cover.erl Compiled src/mochiweb_util.erl Compiled src/mochitemp.erl Compiled src/reloader.erl Compiled src/mochinum.erl Compiled src/mochiweb_headers.erl Compiled src/mochiweb_skel.erl Compiled src/mochiutf8.erl Compiled src/mochiweb_echo.erl Compiled src/mochiweb_acceptor.erl Compiled src/mochiweb_http.erl Compiled src/mochijson2.erl Compiled src/mochiweb_cookies.erl Compiled src/mochiweb.erl Compiled src/mochiweb_mime.erl Compiled src/mochilists.erl Compiled src/mochiweb_response.erl Compiled src/mochiweb_request.erl

그러면 mochiweb 소스가 컴파일된다. 자체 웹 서버를 빌드하는 데 사용할 수 있는 샘플 애플리케이션 프레임워크를 작성하려면 다시 make를 사용하여 프로젝트 디렉토리를 새로 빌드한다. PROJECT는 해당 프로젝트와 디렉토리의 이름이고 PREFIX는 PROJECT 디렉토리가 새로 작성될 디렉토리의 이름이다. 예를 들어, mywebserver라고 하는 프로젝트를 작성하려면 $ make app PROJECT=mywebserver PREFIX=../ 명령을 사용한다.
위에 있는 명령을 실행하면 상위 디렉토리(즉, mochiweb과 동일한 레벨)에 MochiWeb 애플리케이션이 새로 작성된다.
새로 작성된 디렉토리는 기본적으로 모든 인터페이스의 8080 포트에서 대기하는 기본 웹 서버이다. 애플리케이션을 빌드하려면 다시 make를 실행하여 MochiWeb 컴포넌트와 개별 애플리케이션이 컴파일되었는지 확인한다.

$ cd ../mywebserver $ make

마지막으로 start-dev.sh 스크립트를 실행하여 기본 애플리케이션을 실행한다. 이렇게 하면 많은 결과물이 생성되는데, 이러한 모든 결과물은 웹 서버를 처리하기 위해 작성되는 개별 프로세스가 표시되어 있는 '진행 보고서'이다. 결과물에서 오류가 보고되지 않은 경우에는 웹 서버가 시작하여 실행 중인 것이다(목록 9 참조).

목록 9. 웹 서버가 시작하여 실행 중 Erlang R13B04 (erts-5.7.5) [source] [64-bit] [rq:1] [async-threads:0] =PROGRESS REPORT==== 7-Apr-2011::11:40:36 === supervisor: {local,sasl_safe_sup} started: [{pid,<0.42.0>}, {name,alarm_handler}, {mfa,{alarm_handler,start_link,[]}}, {restart_type,permanent}, {shutdown,2000}, {child_type,worker}] =PROGRESS REPORT==== 7-Apr-2011::11:40:36 === supervisor: {local,sasl_safe_sup} started: [{pid,<0.43.0>}, {name,overload}, {mfa,{overload,start_link,[]}}, {restart_type,permanent}, {shutdown,2000}, {child_type,worker}] =PROGRESS REPORT==== 7-Apr-2011::11:40:36 === supervisor: {local,sasl_sup} started: [{pid,<0.41.0>}, {name,sasl_safe_sup}, {mfa, {supervisor,start_link, [{local,sasl_safe_sup},sasl,safe]}}, {restart_type,permanent}, {shutdown,infinity}, {child_type,supervisor}] =PROGRESS REPORT==== 7-Apr-2011::11:40:36 === supervisor: {local,sasl_sup} started: [{pid,<0.44.0>}, {name,release_handler}, {mfa,{release_handler,start_link,[]}}, {restart_type,permanent}, {shutdown,2000}, {child_type,worker}] =PROGRESS REPORT==== 7-Apr-2011::11:40:36 === application: sasl started_at: mywebserver_dev@localhost Eshell V5.7.5 (abort with ^G) (mywebserver_dev@localhost)1> ** Found 0 name clashes in code paths =PROGRESS REPORT==== 7-Apr-2011::11:40:36 === supervisor: {local,crypto_sup} started: [{pid,<0.54.0>}, {name,crypto_server}, {mfa,{crypto_server,start_link,[]}}, {restart_type,permanent}, {shutdown,2000}, {child_type,worker}] =PROGRESS REPORT==== 7-Apr-2011::11:40:36 === application: crypto started_at: mywebserver_dev@localhost ** Found 0 name clashes in code paths =PROGRESS REPORT==== 7-Apr-2011::11:40:36 === supervisor: {local,mywebserver_sup} started: [{pid,<0.59.0>}, {name,mywebserver_web}, {mfa, {mywebserver_web,start, [[{ip,{0,0,0,0}}, {port,8080}, {docroot, "/root/mybase/mywebserver/priv/www"}]]}}, {restart_type,permanent}, {shutdown,5000}, {child_type,worker}] =PROGRESS REPORT==== 7-Apr-2011::11:40:36 === application: mywebserver started_at: mywebserver_dev@localhost =PROGRESS REPORT==== 7-Apr-2011::11:40:36 === supervisor: {local,kernel_safe_sup} started: [{pid,<0.77.0>}, {name,timer_server}, {mfa,{timer,start_link,[]}}, {restart_type,permanent}, {shutdown,1000}, {child_type,worker}]

웹 브라우저를 열어서 새 웹 서버에 액세스할 수 있다. 웹 서버가 같은 시스템에 있는 경우에는 http://localhost:8080/을 연다. 모든 것이 올바르게 작동하면 'It Worked" 제목과 'webserver running' 메시지가 있는 페이지가 표시된다.
새 웹 서버를 수정할 경우에는 애플리케이션 디렉토리에 있는 src/mywebserver_web.erl 파일을 편집한다. 이 파일에는 웹 서비스를 실행하고 지원하는 데 필요한 핵심 코드가 들어 있다.
프로세스의 핵심은 기본 MochiWeb 시스템에서 요청을 수신할 때마다 호출되는 loop() 함수이다. 이 함수에는 두 개의 인수, 즉 요청 구조(요청 유형, 경로 및 본문 데이터가 포함된)와 DocRoot가 있다. 기본적으로 서버는 파일 시스템에서 요청된 파일을 지정된 문서 루트 내에서 제공하기 때문에 DocRoot가 필요하다.
요청 프로세스는 두 가지 단계로 이루어진다. 먼저, case문을 사용하여 요청 유형(GET, POST 등)을 추출한다. 그런 다음, 두 번째 case문을 사용하여 요청 경로를 식별한다.
Erlang의 패턴 일치를 사용하여 한쪽에 있는 경로가 특정 응답을 트리거하게 할 수 있다. 예를 들면, 목록 10과 같이 코드를 수정하여 서버에서 경로 /hello를 액세스하면 'Hello world' 구문이 리턴되도록 할 수 있다.

목록 10. Erlang의 패턴 일치 loop(Req, DocRoot) -> "/" ++ Path = Req:get(path), try case Req:get(method) of Method when Method =:= 'GET'; Method =:= 'HEAD' -> case Path of "congrat" -> Req:ok({"text/html", [],["<h1>Congratulation </h1>"]}); "hello" -> Req:ok({"text/plain",[],["Hello world"]}); _ -> Req:serve_file(Path, DocRoot) end; 'POST' -> case Path of _ -> Req:not_found() end; _ -> Req:respond({501, [], []}) end catch Type:What -> Report = ["web request failed", {path, Path}, {type, Type}, {what, What}, {trace, erlang:get_stacktrace()}], error_logger:error_report(Report), %% NOTE: mustache templates need \ because they are not awesome. Req:respond({500, [{"Content-Type", "text/plain"}], "request failed, sorry\n"}) end.

파일을 편집했으면 make를 실행하여 애플리케이션을 다시 빌드하고 start-dev.sh 스크립트를 사용하여 애플리케이션을 다시 시작한다.
이제 http://localhost:8080/hello를 사용하여 웹 서버 URL에 액세스하면 'Hello world' 메시지가 표시된다.
이 예제는 기본적인 것이지만, 이 예제에서는 POST 또는 PUT 요청을 검색하여 문서의 본문을 처리한 후, 해당 정보를 저장하는 과정을 통해 기본 REST 서비스 지원과 같은 새로운 기능을 쉽게 추가할 수 있다는 사실을 확인할 수 있다. 생성된 프로세스와 메시징을 사용하여 서버로부터 수신한 요청을 큐에 넣었다가 정보를 저장하고 업데이트 및 검색할 수 있다.

결론

Erlang은 과거에 전화 스위치 환경에서 사용된 적이 있으며, 이는 Erlang의 핵심 기능이 기타 다른 언어와는 완전히 다른 환경에서 개발되었다는 것을 의미한다. 이 언어에는 다수의 프로세스를 작성하고 실행하여 많은 동시성 조작(예: 전화 통화)을 처리하는 데 따르는 문제점이 있다.
프로세스를 새로 작성하여 프로세스 간에 정보를 공유하는 데 필요한 복잡한 세마포어 시스템을 사용하지 않는 방식이나 비파괴적인 방식으로 프로세스 간에 통신을 하는 과정이 내장된 메시징 시스템을 사용함으로써 단순화되었다. 메시징은 순차적으로 수행되므로 메시징을 이용하면 각 프로세스 간의 통신을 손쉽게 처리할 수 있다. 게다가 메시지 시스템은 Erlang 인스턴스와 네트워크 전체에서 작동하므로 시스템 간의 통신이 단순하고 간단해진다.
MochiWeb은 이러한 다양한 기능을 서로 결합하여 많은 노력을 들이지 않고도 쉽게 확장하여 추가 기능을 지원할 수 있는 확장 가능한 고성능 웹 서버 솔루션을 생성한다.

출처 : IBM developerWorks

제공 : DB포탈사이트 DBguide.net