IBM®
메인 컨텐츠로 가기
    Korea [국가변경]    이용약관
 
 
   
        제품    서비스 & 솔루션    고객지원 & 다운로드    회원 서비스    
한국 developerWorks   >   Open developerWorks  > developerworks

Twisted를 이용한 네트워크 서버 개발



박준철박준철 jooncheol@gmail.com

Python DB API 2.0을 지원하는 MS SQL 모듈인 pymssql 저자이며, 현재 미지리서치에서 책임 연구원으로 근무중이고 SMIL, WAP, MMS 라이브러리를 개발하였다. 요즘은 모바일 리눅스 환경의 UI 프레임워크를 개발하고 있다.




난이도 : 초급
2007년 5월29일


[오픈 디벨로퍼웍스]는 여러분이 직접 필자로 참가하는 코너입니다. 이 글에서는 Twisted를 이용한 네트워크 서버 개발에 대해 알아봅니다.

Twisted는 파이썬(Python)으로 작성된 네트워킹 엔진이다. 클라이언트 서버 프로그램 작성을 위한 잘 정리된 프레임워크를 제공하고 있고, 이것을 통해 프로토콜 구현, 서버 프로그램 작성, 배포, 시스템 설치까지 거의 모든 과정을 실현시켜 준다. Twisted는 인터넷을 위한 잘 짜인 엔진을 목표로 하고 있고, 이를 위해 인터넷에서 사용되는 대부분의 표준 프로토콜을 라이브러리로 제공하고 있으며, 개발자는 Twisted 프레임워크 안에서 자유롭게 사용할 수 있다. Twisted는 자유롭게 수정, 배포가 가능한 MIT 라이선스로 사용할 수 있으며 지난 몇 년간 오픈소스 소프트웨어에서 상용 소프트웨어까지 수많은 프로젝트에서 애용되면서 탄탄한 구조를 인정 받고 있다.
그 사례로 얼마 전 애플(Apple)은 차세대 Mac OS X Leopard Server에 Twisted.web2로 작성된 iCal Server를 탑재한다고 발표하였고, Twisted 개발 그룹인 Twisted Matrix Laboratories에 Xserve 머신도 기증하였다. 오픈소스로도 공개된 iCal Server는 공개된 프로토콜인 CalDAV를 사용하며 모질라 선버드나 마이크로소프트 아웃룩 및 다른 일정 관리 프로그램에서도 사용할 수 있도록 설계했다. 애플은 파이썬과 Twisted.web2를 사용하여 공개 기술을 도입한 iCal Server를 공개함으로써 폐쇄적이고 오래된 기술을 사용하는 마이크로소프트의 익스체인지 서버 같은 제품군에 대항하는 경쟁 제품을 갖게 되었다는 평가를 받고 있다.
이 글에서는 네트워크 서버의 개발 과정에서 발생하는 고민들을 짚어보며 파이썬과 Twisted를 이용하여 개발하는 방법에 대해 살펴볼 것이다. 먼저 파이썬과 Twisted 조합으로 서버 프로그램을 개발할 때 제일 먼저 부딪치는 문제인 “파이썬을 과연 사용해도 되는가?”부터 출발하여 Twisted의 매력을 차근차근 알아보자.


서버 개발 언어로서 파이썬 선택 이유

그동안 알려진 다수의 성공 사례에도 불구하고 많은 개발자들이 아직도 의심을 하고 있는 것 중 하나가 “파이썬을 사용해도 되는가?”이다. 또 어떤 개발자들은 파이썬의 장점을 깨닫고 파이썬으로 서버를 개발했음에도 파이썬으로 구동된다는 사실을 숨기기도 한다. C가 워낙 오랫동안 서버 프로그램 개발 언어로 사용되고, 성능에 있어서 그만한 대안이 없었다 보니 그런 선입견이 생겨서인지도 모르겠다. 그러나 최근 많은 서버 프로그램들이 파이썬을 이용해 개발되는 데는 다음과 같은 이유가 있다.

  • 크로스 플랫폼 지원
  • 컴파일 방식 언어를 이용했을 때보다 빠른 개발 가능
  • 통신 프로토콜 개발 후 테스트를 위해 쉽게 프로토타입 작성 가능
  • HTTP, Mail, XMLRPC 등의 인터넷과 관련된 고수준 표준 라이브러리 제공
  • 각종 DBMS 관련 모듈 및 다양한 오픈소스 모듈 사용 가능
  • 고수준 자료형을 바탕으로 버퍼 오버플로우 문제없음
  • C 확장 모듈 인터페이스를 통해 C/C++로 작성된 라이브러리 직접 사용 가능

이렇게 좋은 측면이 있는 반면에 지적받을 수 있는 문제들도 있다. 파이썬이 스크립트 언어이고, 인터프리터에 의해 실시간으로 실행되기 대문에 네이티브 소프트웨어에 비해 아무래도 느린 편이다. 그리고 상용화 단계에서 스크립트 소스 코드 노출이 문제가 되는 경우도 있다.
그러나 이런 문제들은 어느 정도는 극복할 수 있다. 런타임 속도는 아무래도 C로 작성한 프로그램에 비해 상대적으로 느리지만, 요즘 같은 고성능 하드웨어 사양에서는 크게 느낄 수 없는 차이이며 대부분의 경우 문제가 되지 않는다. 그래도 속도에 민감한 중요한 모듈의 경우 C/C++를 이용하여 파이썬 확장 모듈을 작성함으로써 극복할 수 있다. 소스 코드 노출의 경우 중요 모듈을 C 확장 모듈 인터페이스에 맞추어 C/C++로 작성할 수 있고, 좀더 쉽게 Pyrex 같은 툴을 사용하여 파이썬 코드를 C 코드로 변환하는 방법도 있다. Pyrex는 파이썬 스크립트를 C 확장 모듈로 만들어 주기 때문에 성능 향상도 되고 소스코드 보안 문제도 해결할 수 있다.



위로



프레임워크로서 Twisted 선택 이유

서버 개발 언어로 파이썬을 선택했다면 이제 서버 프로그램을 작성하면 된다. 파이썬은 POSIX 함수와 BSD 소켓 모듈을 표준 라이브러리로 제공하기 때문에 C로 서버 프로그램을 작성해 보았다면, 바로 작업할 수 있다. 전통적인 방법으로 클라이언트가 접속했을 때 fork하여 부모 프로세스는 계속 다른 클라이언트를 기다리고, 자식 프로세스는 클라이언트와 통신을 하도록 구성해도 되고, poll이나 select를 이용하여 비동기식 I/O를 처리하는 구조를 직접 설계할 수도 있다. 하지만 이렇게 밑바닥부터 차근차근 직접 만들어갈 수도 있겠지만, 이미 갖춰져 있고 검증된 구조를 가져다가 사용하는 방법도 있다. 바로 기존에 알려진 프레임워크를 도입하는 방법이다. 프레임워크를 도입한다면 다음과 같은 효과를 누릴 수 있다.

  • 구조 설계 및 잠재적 버그로부터 부담을 줄여줌
  • 여러 프로젝트를 거치며 다양한 요구사항을 충족시켜 주는 API가 제공됨
  • 문제 봉착시 같은 프레임워크를 도입한 다른 프로젝트의 사례 참조 가능
  • 같은 프레임워크를 경험했던 인력의 개발 참여시 한창 진행중인 프로젝트에서도 쉽게 적응 가능

이런 장점들을 그대로 지닌 Twisted는 프로토콜(Protocols) 클래스와 팩토리(Factories), 연기된(Deferred) 객체, 콜백(Call back), 에러백(Error Back), 콜백 체인(Callback Chain), 타이머(Timer) 및 여러 종류의 리액터(reactor)와 같은 고수준의 개발 방법을 제공하고 있다. 문서화도 꽤 잘 되어 있고 예제 프로그램도 많이 제공하기 때문에 쉽게 Twisted 프레임워크에 익숙해질 수 있을 것이다.
Twisted 프레임워크는 상당히 안정적이어서, 이것들로 작성한 대부분의 서버들이 죽지 않고 웬만해서는 다시 시작할 필요 없이 작동되곤 한다. 또한 기존에 알려진 많은 프로토콜들이 Twisted의 프로토콜 클래스에 기반을 두고 작성되어 라이브러리로 제공된다. 이들을 활용하여 번개같은 속도로 커스텀한 용도의 프로토콜을 작성할 수 있다. 이제부터 Twisted가 제공하는 제반 시설과 도구들을 하나씩 알아보자.



위로



리액터

리액터는 일종의 메인 이벤트 루프(main event loop)다. 기본적으로 I/O 멀티플렉싱, 타이머 등의 처리를 담당한다. 메인 이벤트 루프이기 때문에 리액터가 종료되면 서버가 종료한다. 따라서 Twisted에서는 서버를 종료시키려면 sys.exit()가 아닌 reactor.stop()을 호출하는 것이 좋다.
리액터는 싱글톤(Singleton) 디자인 패턴에 의해 하나만 존재가 가능하고 Twisted에서는 전역적으로 어디서든지 접근할 수 있다. 리액터는 여러 종류가 있는데, 기본값으로 select reactor가 사용된다. 때에 따라 특정 플랫폼에서 더 좋은 성능을 내기 위해 플랫폼 의존적인 리액터로 바꿀 수도 있다. 많이 사용되는 리액터는 다음과 같다.

일반적인 목적의 리액터

  • Select 기반 리액터(모든 플랫폼 지원 - 기본)
  • Poll 기반 리액터(모든 플랫폼 지원)
특정 플랫폼 전용 리액터
  • KQueue 리액터(FreeBSD 전용)
  • Win32(WFMO) 리액터(윈도우 전용)
  • Win32(IOCP) 리액터(윈도우 전용)

리액터 교체는 다음과 같이 한다. 예를 들어 FreeBSD 기반의 Kqueue 리액터를 사용하고자 한다면 다음과 같이 한다.

  

from twisted.internet import kqreactor
kqreactor.install()




위로



프로토콜

Twisted는 프로토콜(Protocols) 클래스를 상속하여 서버에서 클라이언트와의 통신 규약을 처리하도록 하고 있다. 이 클래스에서는 순수하게 프로토콜만을 담당한다. 프로토콜은 기본적으로 connectionMade, connectionLost 그리고 dataRecieved 메서드를 오버라이드(override)하여 작성하도록 되어 있다. 작성하려는 프로토콜이 메아리(echo) 프로토콜이라면 다음과 같다.

  

from twisted.internet.protocol import Protocol
class EchoProtocol (Protocol):
    def connectionMade (self):
        self.transport.write("Hello\r\n") 
    def dataReceived (self, data):
        self.transport.write(data)


위 예제에서는 connectionMade 함수를 오버라이드 하였는데, 이 함수는 클라이언트가 접속하였을 때 호출된다. 이 경우 클라이언트 접속시 “Hello\r\n”을 전송하고 있다. 이처럼 프로토콜 서브 클래스 내에서 클라이언트에 어떤 패킷을 전송하고자 한다면 self.transport로 접근 가능한 Transport 객체를 이용하여 보내야 한다.
마찬가지로 클라이언트와 접속이 끊긴 경우 connectionLost가 호출되는데, 굳이 오버라이드 하지 않아도 상관없다. Twisted가 알아서 메모리에서 FooProtocol의 인스턴스를 제거해주기 때문이다.
클라이언트로부터 패킷을 전송 받게 되면 dataReceived가 호출된다. 받은 패킷이 data 인자로 넘어오게 되며, 이미 Twisted에 의해 데이터를 받은 후이므로 Transport 객체를 이용하여 별도의 읽는 작업을 하지 않아도 된다. 만약 문자열 라인 단위로 주고받는 프로토콜을 구현해야 한다면 LineReceiver 프로토콜을 상속하면 편하다. 라인을 보낼 때는 LineReceiver의 sendLine 메서드를 이용하면 자동으로 CR-LF를 붙여 클라이언트에 전송한다. 물론 LineReceiver도 Protocol의 서브 클래스이고, 많은 인터넷 프로토콜들이 라인 단위로 통신하기 때문에 Twisted에서는 기본 프로토콜 클래스로 제공된다. 다음은 LineReceiver를 이용한 메아리 프로토콜이다.

  

from twisted.protocols.basic import LineReceiver
class QuotedEchoProtocol (LineReceiver):
    def lineReceived(self, line):
        self.sendLine(‘”’+line+’”’)  # hello라고 입력 받으면 “hello”로 돌려 준다.




위로



팩토리

팩토리(Factories)는 말 그대로 공장이다. 유명한 디자인 패턴인 팩토리 패턴을 사용한 것인데, 클라이언트가 접속하면 팩토리에 의해 적당한 프로토콜을 찍어낸다. 즉 프로토콜 클래스의 인스턴스를 만든다. 다음 예가 앞에서 사용하였던 QuotedEchoProtocol을 찍어내는 간단한 팩토리다.

  

from twisted.protocols.basic import LineReceiver
from twisted.internet import protocol, reactor
class QuotedEchoProtocol (LineReceiver):
    def lineReceived(self, line):
        self.sendLine('"'+line+'"')

class QEFactory(protocol.ServerFactory):
    protocol = QuotedEchoProtocol

reactor.listenTCP(8888, QEFactory())
reactor.run()


리액터에게 8888번 포트로 Listen하도록 하고 클라이언트 접속시 QEFactory로 보내도록 설정해 놓으면 QEFactory는 QuotedEchoProtocol의 인스턴스를 하나 생성한다. 이 때부터 QuotedEchoProtocol의 connectionMade가 호출되면서 QuotedEcho 프로토콜을 주고 받게 된다.
만약 프로토콜 인스턴스끼리 통신을 주고 받아야 한다거나 프로토콜의 인스턴스에서 팩토리에 접근해야 한다면 프로토콜에서는 self.factory로 소속 팩토리에 접근할 수 있다. 이것으로 팩토리의 어떤 설정값을 변경한다든지, 다른 클라이언트로 접근할 수 있다.



위로



Deferred 객체

말 그대로 연기된 객체다. 이것이 Twisted가 제시하는 프레임워크의 핵심이라고 할 수 있다. 대부분의 인터넷에서 사용하는 프로토콜들은 비록 동기식으로 보일지라도 네트워크 상에서 패킷을 주고 받는 행위 자체가 비동기식으로 이루어지기 때문에 비동기적인(asynchronous) 프레임워크로 처리해야 한다. 비동기적인 I/O 핸들링의 중심에는 앞서 언급한 리액터가 있다.
하지만 I/O 핸들링이 비동기식이라고 할지라도 데이터를 처리하는 쪽에서 기다리는 시간이 길어진다면 전혀 비동기적이지 않을 것이다. 앞서 예를 들었던 QuotedEchoProtocol의 lineReceived 함수에서 데이터를 처리하는 시간이 오래 걸려 5초나 걸려야지 lineReceived 함수가 끝난다면, 리액터도 5초 동안 블록(block)이 걸린 것이나 마찬가지가 될 것이다. 블로킹 현상은 서버에 있어 매우 치명적이다. 이것에 대한 해답이 non-blocking call 모델이다. Twisted에서는 디퍼드(Deferred) 객체가 그것이다. 만약 바로 응답을 줄 수 없는 경우라면, Deferred 객체를 리턴하고 리액터에 의해 메인 이벤트 루프를 계속 진행하게 하면 된다. 즉 Deferred에 의해 응답을 잠시 미루고 리액터로 하여금 또 다른 클라이언트의 프로토콜을 처리하도록 한다. 뒷일은 Deferred 객체의 콜백과 에러백으로 책임진다. Deferred 객체가 응답을 할 준비가 되었다면 등록된 콜백 함수를 호출하고 콜백 함수에 의해 응답을 처리한다. 만약 오류가 발생했다면 에러백 함수를 호출하면 된다.



위로



콜백, 에러백 그리고 콜백 체인

아래 예제는 QuotedEchoProtocol을 좀 발전시켜 Deferred 객체를 상속하여 만든 SpellCheck 객체를 적용한 예다. SpellCheck 객체는 인자로 받은 문자열을 다른 네트워크에 떨어져 있는 가상의 국립국어원의 철자 확인서버 – 그런 게 있다고 가정해 보자 - 에 전송하여, 철자가 맞았는지 확인하고 틀렸다면 정확한 문자열로 고쳐 메아리(Echo) 쳐주도록 해준다.
만약 이 처리과정을 SpellCheck를 사용하지 않고 lineReceived 함수 내에서 다 처리한다면, 다른 네트워크의 철자 서버에 접속하여 데이터를 주고 받는 과정이 포함되어 있기 때문에 lineReceived 함수의 처리 시간이 상당히 길어질 것이고, 이것이 바로 블로킹의 원인이 된다.
하지만 SpellCheck는 Deferred를 상속했으니 걱정하지 않아도 된다. lineReceived 함수는 호출되자마자 바로 끝나 버려 블로킹이 걸리지 않을 것이고, 앞서 이야기한 일련의 철자 확인 작업은 리액터에 의해 잘게 잘게 쪼개져 또 다른 클라이언트들과 통신하는 동안 처리될 것이다. 철자 확인 과정이 끝나면 (즉 응답할 준비가 되면) SpellCheck는 등록된 콜백 함수를 호출해 줄 것이다. 만약 철자 서버와 통신에 장애가 발생한다면 에러백 함수가 호출될 것이다. 호출된 콜백 함수에서 응답을 해주면 블로킹 없는 프로토콜 구현이 완성된다.
필자는 Twisted가 명시적으로 에러백이라는 개념을 두었다는 점에 큰 점수를 주고 싶다. 많은 프레임워크들이 콜백은 규정하고 있지만, 에러백은 명시적으로 사용하고 있지 않다. 그런 경우 대부분 콜백 안에서 에러 처리를 하게 되고 이것은 프레임워크가 있으나마나 한 지저분한 코드의 원인이 될 수 있기 때문이다.

  

from spellcheck import SpellCheck  # Deferred 기반의 철자 체크 객체
class QuotedEchoProtocol (LineReceiver):
    def lineReceived(self, line):
        d = SpellCheck(line)
        # 콜백 함수 self.checkSucceed와 에러백 함수인 self.checkFail 등록
        d.addCallbacks(self.checkSucceed, self.checkFail)
    def checkSucceed(self, goodstr):  # 콜백 함수
        # goodstr은 철자 서버에 의해 바로 잡은 문자열
        self.sendLine('"'+line+'"') # “”을 씌워 메아리 친다
    def checkFail(self, failure):  # 에러백 함수, 에러 발생 사유를 전송한다.
        self.sendLine(‘Spell check fail - ‘+str(failure))


다음은 콜백 체인에 대해 알아보자. Twisted는 콜백 체인(Callback Chain)이라는 개념을 제공한다. 만약 다음과 같이 콜백/에러백을 여러 번 등록했다고 하자. d.callback을 호출하여 d에 등록된 콜백을 호출 해보면 제일 먼저 self.s1(‘data’)가 호출된다. 만약 self.s1(‘data’)가 아무런 Exception 없이 실행 되었다면 그 결과가 self.s2의 인자로 호출될 것이다. 즉 self.s2(self.s1(‘data’))와 같다. self.s2도 Exception 없이 끝나면 그 결과 값이 self.s3의 인자가 되어 호출된다. 만약 self.s1에서 Exception이 발생되면 그 다음 에러벡인 self.f2가 호출된다. 그런데 self.f2가 아무런 Exception 없이 호출되면 에러벡이 콜백으로 변하여 self.s3가 호출될 것이다.
이것이 바로 콜백 체인 개념이다. 아래 그림은 Twisted 홈페이지에 설명되어 있는 콜백체인의 흐름도다. 그다지 많이 사용될 것 같지는 않아 보이지만, 잘만 사용하면 까다로운 조건과 시나리오를 가진 프로토콜을 구현하는 데 유용하게 사용할 수 있다.



그림1


  

d = defer.Deferred()
d.addCallbacks(self.s1, self.f1)
d.addCallbacks(self.s2, self.f2)
d.addCallbacks(self.s3, self.f3)
d.callback('data')


이것을 왜 콜백 체인이라고 부르는지는 다음 코드를 보면 직관적으로 이해가 쉬울 것이다. 이 코드는 앞의 코드와 같은 코드다.

  

d = defer.Deferred()
d.addCallbacks(self.s1, self.f1).addCallbacks(self.s2, self.f2).addCallbacks(self.s3, self.f3)
d.callback('data')




위로



스케줄링

리액터가 제공하는 callLater를 이용하여 함수 호출을 지연시킬 수 있다. 이것은 Deferred와 함께 가장 많이 사용되는 도구다. 일종의 타이머라고 볼 수 있다. 클라이언트와 패킷을 주고 받다가 5분 동안 클라이언트로의 응답이 없다면 접속을 끊어야 하는 경우에도 유용하게 쓰인다. call_id = reactor.callLater(600, killClient)라고 호출하면 된다. 600초(5분) 후에 killClient 함수를 호출하여 클라이언트와 접속을 끊을 수 있다. 만약 그 사이에 클라이언트로부터 응답이 온다면 call_id.cancel()을 호출하여 5분 뒤의 killClient 호출을 취소할 수 있다.
앞에서 사용했던 메아리 서버의 예제를 조금 수정하여, 10초 뒤에 메아리 치도록 딜레이를 주어 보자. 다음과 같이 하면 되는데 callLater를 이용하여 아주 간단하게 구현할 수 있다. 만약 Twisted를 이용하지 않고 이런 기능을 블로킹 없게 직접 구현해 본다면, 은근히 까다롭다는 것을 알게 될 것이다. 탄탄하고 안정된 메인 이벤트 루프를 가지고 있어야 이런 것이 가능한데, 이것은 매우 힘든 작업이다. 그러나 우리는 Twisted라는 검증된 프레임워크를 사용할 수 있고, Twisted의 리액터를 이용하여 이렇게 쉽게 구현할 수 있다.

  

class QuotedEchoProtocol (LineReceiver):
    def lineReceived(self, line):
        reactor.callLater(10, self.sendLine, '"'+line+'"')




위로



다양한 프로토콜 모듈 및 Twisted.web

Twisted는 앞서 열거한 리액터, Deferred 객체, 콜백/에러백, 스케줄링을 바탕으로 다양한 종류의 인터넷 프로토콜 구현을 제공하고 있다. FTP, HTTP, IMAP4, POP3, SMTP, DNS, IRC, JABBER, MSN, SIP, SOCKS, NNMP 등을 사용할 수 있다. 이렇게 제공되는 프로토콜 모듈들은 지금까지 언급했던 기술들로 이루어져 있기 때문에 쉽게 구조를 파악할 수 있고 상속을 통해 나만의 특성을 추가하여 새로운 프로토콜로 탈바꿈시킬 수 있다.
또 최근에 주목 받기 시작한 것이 Twisted.web인데, 이는 Twisted 기반의 웹 서버다. 이것을 이용하여 다양한 목적의 커스텀 웹 서버를 작성할 수 있다. 필자는 몇 년 전부터 Apache-PHP-Postgres로 구성되었던 개인 홈페이지를 Twisted.web-PHP-Postgres 구성으로 바꾸어 운용하고 있다. 필자는 좋은 서버 관리자가 아니라서 Apache의 httpd.conf가 복잡하고 어렵다고 느끼고 있었다. 그러던 참에 Twisted.web 기반으로 간단하게 몇 줄 작성하여 나만의 커스텀 웹 서버를 만들어 사용하고 있다. 아래 코드는 필자의 웹 서버의 전체 소스 코드다.

  

from twisted.internet import reactor
from twisted.web import static, server, vhost, twcgi, script, trp
from customftp import CustomFTPServer  # 필자의 커스텀 FTP 서버 모듈 임포트 
import  rpc                                            # 필자의 XMLRPC 핸들러 모듈 임포트

root = vhost.NameVirtualHost()
exmanhost = static.File("exman")         # exman.pe.kr을 위한 Document root 디렉터리
jegihost = static.File("jegi/wiki/htdocs") # jegi.org를 위한 Document root 디렉터리
root.default = exmanhost                     # 기본 호스트는 exman.pe.kr

root.addHost("exman.pe.kr", exmanhost)  # exman.pe.kr을 가상 호스트에 추가
root.addHost("jegi.org", jegihost)             # jegi.org를 가상 호스트에 추가

static.File.directoryListing = None     # 디렉터리 인덱스 서비스 지원하지 않음
exmanhost.processors = {
            '.php': twcgi.PHPScript,   # .php를 PHPScript로 처리하도록 지정
            '.cgi': twcgi.CGIScript,     # .cgi를 CGIScript로 처리하도록 지정
}
jegihost.processors = exmanhost.processors # jegi.org도 exman.pe.kr과 같은 설정

root.putChild('RPC2', rpc.Handler())  # /RPC2로 들어오는 요청은 필자의 RPC 핸들러가 처리

reactor.listenTCP(80, server.Site(root))      # 80 포트로 웹 서비스 시작
reactor.listenTCP(21, CustomFTPServer())  # 21번 포트로 ExmanFTP 서비스 시작
reactor.run()


이 서버는 PHP 및 CGI 서빙 환경을 제공하고 가상 호스트도 지원하는, 갖출 것은 다 갖춘 웹 서버다. 이 웹 서버는 80포트로 웹 서비스를 하는 동시에 21번 포트로 Twisted의 FTP 프로토콜을 상속하여 필자의 입맛에 맞춘 커스텀 FTP도 서비스하고 있다. FTP 계정의 접속 IP 차단 기능 및 계정별 FTP 셸 권한의 차별화 기능 - 특정 계정은 ls, get 등의 readonly 기능만 사용할 수 있게 하는 - 을 갖추었다. 또한 ‘/RPC2’로 들어오는 HTTP 요청은 필자가 Twisted XMLRPC 모듈을 상속해 작성한 XMLRPC 핸들러가 가로채어 ISAPI나 아파치 ServerAPI와 같은 메커니즘의 고성능 XMLRPC 서비스를 제공하도록 하였다.
분량 관계상 커스텀 FTP 서버와 XMLRPC 핸들러의 소스는 포함하지 않았지만 지금까지 설명한 방법들과 Twisted가 제공하는 프로토콜 모듈을 상속받아 쉽게 작성할 수 있다. 이쯤 되면 아파치와는 또 다른 경쟁력을 가진 필자만의 커스텀 웹 서버가 아닐까 싶다.
필요하다면 입맛에 따라 Twisted가 제공하는 WebDAV나 SOCKS 등의 추가적인 프로토콜을 집어 넣은 커스텀 웹 서버를 작성할 수도 있을 것이다. 현재 실험 모듈이지만 앞으로 공식 모듈로 포함될 Twisted.web2는 Twisted.web에 비해 더 빠르고 고성능을 지향한다고 하니 기능뿐만 아니라 성능 면에서도 우수한 커스텀 웹 서버를 작성할 수 있을 것이다. Twisted.web 역시 MIT 라이선스로 자유롭게 수정, 배포할 수 있다.



위로



서버 개발의 마무리 단계

지금까지 알아본 Twisted 프레임워크로 개발한 서버를 실전에 투입해 보자. 프로토콜을 구현하느라 군데군데 print 문이 섞여 있을 것이고, 특정 UID/GID로 실행한다든가, 로그를 파일로 저장한다거나, syslogd로 보낸다거나, 데몬처럼 동작시키고 싶을 수도 있다. 이런 작업들은 서버 개발 단계에서 꼭 한번씩은 고민하게 만드는 작업들이다. 매번 서버 개발을 할 때마다 해야 하는 귀찮은 작업이기도 하다. 그러나 Twisted로 서버를 개발하였다면 이런 것들에 대한 고민을 할 필요가 없어진다. Twistd라는 도구가 제공되기 때문이다. 앞에서 만들었던 QuotedEcho Server를 다음과 같이 service.Application을 이용해 약간만 수정해주고, 파일 확장자를 .py에서 .tac로 바꿔 주기만 하면 twistd를 사용할 수 있다.

  

from twisted.protocols.basic import LineReceiver
from twisted.internet import protocol, reactor, defer
from twisted.application import internet, service

class QuotedEchoProtocol (LineReceiver):
    def lineReceived(self, line):
        reactor.callLater(5, self.sendLine, '"'+line+'"')
class QEFactory(protocol.ServerFactory):
    protocol = QuotedEchoProtocol

# 서버를 uid 1, gid 1로 실행
application = service.Application('QuotedEcho', uid=1, gid=1)
internet.TCPServer(8888, QEFactory()).setServiceParent(
    service.IServiceCollection(application))


이 소스를 qecho.tac로 저장하면 Twisted에서 제공하는 twistd 스크립트를 이용하여 다음과 같은 방법들로 실행하여 원하는 결과를 얻을 수 있다.

  

twistd -y qecho.tac  # 데몬화하여 실행한다. Uid 1, gid 1로 실행 
twistd -y qecho.tac  --pid=/var/qecho.pid # 서버의 pid를 저장한다.
twistd -y qecho.tac  -l qecho.log  # 로그를 qecho.log로 출력한다.
twistd -y qecho.tac  --syslog  # 로그를 syslog로 출력한다.
twistd -y qecho.tac  --chroot=/var  # chroot를 적용한다.


마지막으로 이 서버를 상품화하여 라이선스를 받고 배포한다고 가정해 보자. Twisted는 서버용 OS인 리눅스용 패키지 제작도 해준다. qecho.tac 파일은 레드햇 리눅스용 패키지인 RPM이나 데비안 기반의 우분투 리눅스용 DEB 패키지로 변환할 수 있다. 서버 프로그램을 단순하게 tar.gz로 압축하여 배포하는 것에 비해 RPM이나 DEB 패키지로 배포하고 간단하게 설치하도록 지원한다면 상품으로서 제법 그럴싸한 모습을 갖출 것이다. 만들어진 RPM이나 DEB 패키지를 시스템에 설치하면 /etc/init.d 아래의 run-script로도 등록되어 시스템 부팅시 자동으로 서버를 시작할 수도 있다.

  

tap2rpm -t qecho.tac   
tap2deb -m ‘jooncheol Park <jooncheol@...>’ -t qecho.tac 


   소셜 북마크

   mar.gar.in mar.gar.in
    digg Digg
    del.icio.us del.icio.us
    Slashdot Slashdot

지금까지 보았듯이 Twisted는 서버 개발 프레임워크으로서 팩토리 디자인 패턴에 기반을 둔 클라이언트 처리, 싱글톤 개념의 리액터, Deferred 객체, 콜백/에러백, 콜백체인, 스케줄링 등의 기법들을 제시하고 있고 다양한 배터리들(프로토콜 구현)을 마음껏 사용할 수 있으며, 마지막으로 서버 개발 마무리 단계까지 어느 한 군데 가려운 곳 없이 친절하게 도와준다. Twisted와 파이썬을 이용하여 짧은 기간 동안에 안정적이고, 마무리까지 깔끔한 서버 개발을 경험해 보자.




위로


참고문헌





이제 전문가의 글을 단순히 ‘보는 것’에서, 직접 여러분이 developerWorks의 필자가 될 수 있습니다. IBM developerWorks를 통해 공유하고 싶은 지식이 있으신 분들은 원고 기획안을 접수해주세요. 채택되신 분께는 소정의 원고료를 드립니다.
Open developerWorks 신청하기   MS워드 아이콘   아래아한글 아이콘



[지난 Open dW 보기]
[이 기사에 대한 의견 쓰기 - 오픈 소스 포럼]

사이트 여행

dW 커뮤니티
포럼 | 블로그 | Spaces
dW Student Community

로컬 컨텐츠

행사 및 세미나

기획 기사

개발자 입문

튜토리얼 및 교육

TOP 10 인기자료

SW 다운로드

RSS 피드

뉴스레터
  
자바스크립트가 작동이 중지되었습니다. 이 기능을 수행하시려면 브라우저에서 자바스크립스트를 작동시켜 주시거나 이곳을 클릭해주세요.
Special offers
IBM SOA Sandbox 시험판
dW Student Community
로보코드
코드 트레이닝


    IBM 소개 개인정보 보호정책 문의