PHP 4.x와 PHP 5.x에서 널리 사용되던 모듈 중에는 유용한 것들이 많이 있습니다. 이미지 처리, 데이터베이스 연동, 암호화, MIME TYPE 관련 연동 등 종류도 매우 많으며, 실질적인 웹 기술에 직접 영향을 끼치는 모듈들도 많이 있어서 웹 관련 개발에 큰 생산성 향상을 가져다주기도 합니다.

Phalanger가 문법적으로는 PHP와 상당한 호환성을 보여주지만 결정적으로 궁금한 것은 과연 기존에 사용하던 Native PHP 모듈을 모두 사용할 수 있는가에 대한 부분일 것입니다. 얼핏 보기에는 Phalanger가 그저 호환성 보존 차원에서 API 계층만 제공하는게 아니냐는 생각이 들 수 있는데 사실 Phalanger의 절반을 이루는 것은 바로 이 상호운용성에 대한 내용입니다.

이번 아티클에서는 Phalanger가 지원하는 대표적인 Native PHP Module들을 활성화하는 방법을 소개해보겠습니다. 이 방법을 사용하면 기존의 php.ini와는 달리 웹 서버 전역이 아닌 특정 웹 응용프로그램 풀 안에서만 적용되는 모듈 구성이 가능합니다.

Custom Configuration Hanlder 등록하기

ASP.NET 위에서 호스팅되는 모든 Web App들은 web.config이라고 하는 사전에 약속된 설정 파일을 사용하여 모든 구성을 지정할 수 있으며 이는 Apache에서 사용하는 httpd.conf와 같은 성격의 파일이기도 하지만, 동시에 완벽한 XML을 사용하기 때문에 다양한 관리 도구와 충돌 없이 상호작용할 수 있습니다.

우선 <configuration> 요소 아래의 제일 첫 번째 노드로 와야 하는 내용이 있습니다. 

<configSections>
...
    <section name="phpNet" type="PHP.Core.ConfigurationSectionHandler, PhpNetCore, Version=3.0.0.0, Culture=neutral, PublicKeyToken=0a8e8c4c76728c71" />
...
</configSections>

바로 <configSections> 요소인데, 이 요소 아래에 <phpNet> 요소에 대한 처리기를 위와 같이 지정해야 합니다. 이렇게 설정하여 각 웹 사이트 별로 독립적인 PHP 설정을 가지게할 수 있습니다.

<phpNet> 요소 설정하기

아래의 XML 코드 조각은 보통의 php.ini 파일에서 활용할 수 있는 거의 대부분의 Native Module들을 열거한 것입니다. 아래와 같이 지정하였을 때 실제로 해당 Module들을 .NET Runtime 위로 불러오게 됩니다.

<phpNet>
    <classLibrary>
        <add assembly="System.Configuration, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
        <add assembly="System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
  <add assembly="PhpNetClassLibrary, Version=3.0.0.0, Culture=neutral, PublicKeyToken=4af37afe3cde05fb" section="bcl" />
  <add assembly="PhpNetMsSql, Version=3.0.0.0, Culture=neutral, PublicKeyToken=2771987119c16a03" section="mssql" />
  <add assembly="php_bcmath.mng, Version=3.0.0.0, Culture=neutral, PublicKeyToken=4ef6ed87c53048a3" section="bcmath" />
  <add assembly="php_com.mng, Version=3.0.0.0, Culture=neutral, PublicKeyToken=4ef6ed87c53048a3" section="com" />
  <add assembly="php_image.mng, Version=3.0.0.0, Culture=neutral, PublicKeyToken=4ef6ed87c53048a3" section="image" />
  <add assembly="php_zlib.mng, Version=3.0.0.0, Culture=neutral, PublicKeyToken=4ef6ed87c53048a3" section="zlib" />
  <add assembly="php_odbc.mng, Version=3.0.0.0, Culture=neutral, PublicKeyToken=4ef6ed87c53048a3" section="odbc" />
  <add assembly="php_ftp.mng, Version=3.0.0.0, Culture=neutral, PublicKeyToken=4ef6ed87c53048a3" section="ftp" />
  <add assembly="php_calendar.mng, Version=3.0.0.0, Culture=neutral, PublicKeyToken=4ef6ed87c53048a3" section="calendar" />
  <add assembly="php_bz2.mng, Version=3.0.0.0, Culture=neutral, PublicKeyToken=4ef6ed87c53048a3" section="bz2" />
  <add assembly="php_cpdf.mng, Version=3.0.0.0, Culture=neutral, PublicKeyToken=4ef6ed87c53048a3" section="cpdf" />
  <add assembly="php_crack.mng, Version=3.0.0.0, Culture=neutral, PublicKeyToken=4ef6ed87c53048a3" section="crack" />
  <add assembly="php_curl.mng, Version=3.0.0.0, Culture=neutral, PublicKeyToken=4ef6ed87c53048a3" section="curl" />
  <add assembly="php_db.mng, Version=3.0.0.0, Culture=neutral, PublicKeyToken=4ef6ed87c53048a3" section="db" />
  <add assembly="php_dba.mng, Version=3.0.0.0, Culture=neutral, PublicKeyToken=4ef6ed87c53048a3" section="dba" />
  <add assembly="php_dbase.mng, Version=3.0.0.0, Culture=neutral, PublicKeyToken=4ef6ed87c53048a3" section="dbase" />
  <add assembly="php_dbx.mng, Version=3.0.0.0, Culture=neutral, PublicKeyToken=4ef6ed87c53048a3" section="dbx" />
  <add assembly="php_domxml.mng, Version=3.0.0.0, Culture=neutral, PublicKeyToken=4ef6ed87c53048a3" section="domxml" />
  <add assembly="php_exif.mng, Version=3.0.0.0, Culture=neutral, PublicKeyToken=4ef6ed87c53048a3" section="exif" />
  <add assembly="php_filepro.mng, Version=3.0.0.0, Culture=neutral, PublicKeyToken=4ef6ed87c53048a3" section="filepro" />
  <add assembly="php_gd2.mng, Version=3.0.0.0, Culture=neutral, PublicKeyToken=4ef6ed87c53048a3" section="gd2" />
  <add assembly="php_gettext.mng, Version=3.0.0.0, Culture=neutral, PublicKeyToken=4ef6ed87c53048a3" section="gettext" />
  <add assembly="php_hyperwave.mng, Version=3.0.0.0, Culture=neutral, PublicKeyToken=4ef6ed87c53048a3" section="hyperwave" />
  <add assembly="php_iconv.mng, Version=3.0.0.0, Culture=neutral, PublicKeyToken=4ef6ed87c53048a3" section="iconv" />
  <add assembly="php_imap.mng, Version=3.0.0.0, Culture=neutral, PublicKeyToken=4ef6ed87c53048a3" section="imap" />
  <add assembly="php_java.mng, Version=3.0.0.0, Culture=neutral, PublicKeyToken=4ef6ed87c53048a3" section="java" />
  <add assembly="php_ldap.mng, Version=3.0.0.0, Culture=neutral, PublicKeyToken=4ef6ed87c53048a3" section="ldap" />
  <add assembly="php_mbstring.mng, Version=3.0.0.0, Culture=neutral, PublicKeyToken=4ef6ed87c53048a3" section="mbstring" />
  <add assembly="php_mcrypt.mng, Version=3.0.0.0, Culture=neutral, PublicKeyToken=4ef6ed87c53048a3" section="mcrypt" />
  <add assembly="php_mhash.mng, Version=3.0.0.0, Culture=neutral, PublicKeyToken=4ef6ed87c53048a3" section="mhash" />
  <add assembly="php_mime_magic.mng, Version=3.0.0.0, Culture=neutral, PublicKeyToken=4ef6ed87c53048a3" section="mime-magic" />
  <add assembly="php_ming.mng, Version=3.0.0.0, Culture=neutral, PublicKeyToken=4ef6ed87c53048a3" section="ming" />
  <add assembly="php_msql.mng, Version=3.0.0.0, Culture=neutral, PublicKeyToken=4ef6ed87c53048a3" section="msql" />
        <add assembly="php_mysql.mng, Version=3.0.0.0, Culture=neutral, PublicKeyToken=4ef6ed87c53048a3" section="mysql" />
  <add assembly="php_openssl.mng, Version=3.0.0.0, Culture=neutral, PublicKeyToken=4ef6ed87c53048a3" section="openssl" />
  <add assembly="php_pdf.mng, Version=3.0.0.0, Culture=neutral, PublicKeyToken=4ef6ed87c53048a3" section="pdf" />
  <add assembly="php_pgsql.mng, Version=3.0.0.0, Culture=neutral, PublicKeyToken=4ef6ed87c53048a3" section="pgsql" />
  <add assembly="php_shmop.mng, Version=3.0.0.0, Culture=neutral, PublicKeyToken=4ef6ed87c53048a3" section="shmop" />
  <add assembly="php_snmp.mng, Version=3.0.0.0, Culture=neutral, PublicKeyToken=4ef6ed87c53048a3" section="snmp" />
  <add assembly="php_sockets.mng, Version=3.0.0.0, Culture=neutral, PublicKeyToken=4ef6ed87c53048a3" section="sockets" />
  <add assembly="php_xml.mng, Version=3.0.0.0, Culture=neutral, PublicKeyToken=4ef6ed87c53048a3" section="xml" />
  <add assembly="php_xmlrpc.mng, Version=3.0.0.0, Culture=neutral, PublicKeyToken=4ef6ed87c53048a3" section="xmlrpc" />
  <add assembly="php_xslt.mng, Version=3.0.0.0, Culture=neutral, PublicKeyToken=4ef6ed87c53048a3" section="xslt" />
  <add assembly="php_yaz.mng, Version=3.0.0.0, Culture=neutral, PublicKeyToken=4ef6ed87c53048a3" section="yaz" />
  <add assembly="php_zip.mng, Version=3.0.0.0, Culture=neutral, PublicKeyToken=4ef6ed87c53048a3" section="zip" />
  <add assembly="php_big_int.mng, Version=3.0.0.0, Culture=neutral, PublicKeyToken=4ef6ed87c53048a3" section="big_int" />
 </classLibrary>
</phpNet>

gd2, xml, zip과 같이 널리 사용하는 모듈들을 불러오고 있습니다만 조금 독특한 부분이 있습니다. Native DLL들을 사용하기로 하였지만 DLL에 대한 시그니처는 닷넷 프레임워크에서 사용하는 정규화된 Full Name을 사용하고 있는데, 사실 Phalanger는 Native DLL들을 as-is로 사용하기보다는 별도의 Managed Wrapper를 만들어서 사용하기 때문에 이와 같은 형태가 됩니다. Phalanger 3.0에 들어와서 64비트를 적극적으로 지원할 수 있게되었다고 이야기할 수 있는 것은 이와 같은 Native DLL에 대한 Wrapper를 제공하기 때문입니다.

더 나아가기

Phalanger는 이제 상당한 수준의 웹 개발 프레임워크로 자리 잡았습니다. 그렇지만 이것을 어떻게 활용할 것인가에 대한 고민은 아직 끝나지 않았고, 또한 ASP.NET MVC와의 연계나 기존 PHP 코드와의 호환성 개선 등 서로 다른 양쪽의 환경을 자연스럽게 이어줄 수 있는 다리 역할을 얼마나 잘 할 수 있을지도 사실 아직은 시험대 위에 놓여있는 상태입니다. 그렇지만 무궁무진한 발전이 기대되는 프레임워크인 것은 사실인듯 합니다.

저작자 표시 비영리 변경 금지
크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by Windows Azure MVP 남정현 (rkttu.com)

지난 아티클에서는 Phalanger와 PHP 사이에 차이점들이 있다고 말씀드렸습니다. 구체적으로 어떤 차이점들이 있을까요? 여러 프로그래밍 언어를 지원한다는 사실이 가장 큰 차이점이고 닷넷 기반 위에서 실행된다는 것이 구분되는 점이겠지만 이런 점을 차치하고 PHP 관점에서 차이점을 살펴본다면 지금 이야기하려는 토픽들에 대한 이야기가 빠질 수 없을 것입니다.

App_Code 폴더의 사용

Phalanger가 ASP.NET을 기반으로 하고 있기 때문에 자동으로 이어받는 특성으로, App_Code 폴더의 사용에 관한 부분이 있습니다. ASP.NET에서는 App_Code 폴더 안에 낱개 코드 파일들을 넣어두면 이것을 자동으로 웹 페이지의 서버 런타임에서 자유롭게 가져다쓸 수 있다고 하였는데, Phalanger도 마찬가지입니다. 웹 페이지를 렌더링하기 위한 목적이 아닌 공통이 되는 PHP 코드를 이 폴더에 넣어두기만 하면 자동으로 이 폴더에 속한 모든 PHP 코드들이 글로벌 문맥 상에서 사용 가능하게 활성화됩니다.

단, 조심해야 할 부작용이 하나 있다면 여기에 지나치게 많은 코드를 배치할 경우 컴파일 시간이 늘어나서 처음 사이트를 시작할 때 시간이 오래 걸리게 될 가능성이 있습니다. 안타깝게도 C나 C++처럼 병렬 컴파일은 아직 지원되지 않기 때문에 컴파일 시간이 오래 걸릴 경우 다양한 문제를 야기할 가능성이 있습니다.

php.ini와 같은 Global Configuration이 아닌 web.config에 의한 설정

PHP의 경우 설정을 변경하기 위해서는 PHP 전체의 설정을 주관하는 php.ini 파일을 업데이트하거나, PHP를 다시 컴파일하여 설치하는 번거로운 과정을 거쳐야만 모듈에 대한 설정이나 추가/제거가 가능했습니다. 하지만 Phalanger의 경우 현재 만들어진 응용프로그램 풀마다 다른 설정을 가지도록 구성할 수 있으므로 좀 더 자유도 높은 설정이 가능합니다. 이를 위해서 web.config 파일을 수정하고 저장하기만 하면 됩니다.

이러한 설정을 다루기 위해서는 phpNet이라는 XML 요소를 web.config에 지정해야 하는데, 그냥 지정할 수는 없고 반드시 적절한 처리기를 연결해주어야 합니다. web.config은 단순한 XML 파일이 아니라 닷넷 프레임워크가 직접 내용을 검사하고 분석하는 프로그램 코드의 일부이기 때문에 규칙을 준수하는 것이 매우 중요합니다.

phpNet 요소를 추가하려면 web.config에서 <configuration> 요소의 제일 첫 번째 노드로 아래 XML 조각이 배치되어야 합니다.

<configSections>
<section name="phpNet" type="PHP.Core.ConfigurationSectionHandler, PhpNetCore, Version=3.0.0.0, Culture=neutral, PublicKeyToken=0a8e8c4c76728c71" />
</configSections>

그 다음, 보기에 편리한 위치에 phpNet 요소를 추가합니다. 보통 아래의 코드 조각으로 최초 설정을 시작하면 무난합니다.

<phpNet>
<classLibrary>
  <add assembly="PhpNetClassLibrary, Version=3.0.0.0, Culture=neutral, PublicKeyToken=4af37afe3cde05fb" section="bcl" />
  <add assembly="PhpNetXmlDom, Version=3.0.0.0, Culture=neutral, PublicKeyToken=2771987119c16a03" section="dom"/>
</classLibrary>   
</phpNet>

php.ini 파일의 샘플 사본을 복사하여 php.ini 파일로 사용하던 것과 비슷한 접근 방법이지만 각 사이트 혹은 도메인 별로 따로 사용하는 web.config 파일 안에서 이러한 설정을 다루는 것이 중요한 차이점입니다. 그리고 무엇보다도 안심해도 좋은 것은 INI 파일처럼 프로그램이 잘못 다루게 될 가능성이 있는 파일이 아니라, XML의 형태로 설정 파일이 관리되므로 web.config 파일을 건드리는 다른 써드 파티 어플리케이션 때문에 Phalanger의 설정이 깨지거나 변형될 일이 거의 없다는 점입니다.

위의 기본 설정을 지정하면 Phalanger에서 기본적인 PHP API를 사용할 수 있으며, PHP5부터 기본으로 제공되는 SimpleXMLElement도 위의 설정으로 기본으로 활성화됩니다.

PHP/CLR의 사용

이제 위의 설정을 토대로 PHP/CLR을 활성화하여 닷넷 프레임워크의 기본 API를 Phalanger에서 즉시 호출하여 사용할 수 있습니다. 위의 <phpNet> 요소 아래에 다음의 XML 요소를 추가하면 됩니다.

<compiler>
  <set name="LanguageFeatures">
    <add value="PhpClr" />
  </set>
</compiler>

그리고 <classLibrary> 요소 아래에 .NET Framework 기본 어셈블리에 대한 레퍼런스를 추가합니다.

<classLibrary>
  <add assembly="mscorlib" />
  <add assembly="System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
  <add assembly="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
  <add assembly="PhpNetClassLibrary, Version=3.0.0.0, Culture=neutral, PublicKeyToken=4af37afe3cde05fb" section="bcl" />
  <add assembly="PhpNetXmlDom, Version=3.0.0.0, Culture=neutral, PublicKeyToken=2771987119c16a03" section="dom"/>
</classLibrary>

이제 새로 추가한 코드가 의도한대로 잘 작동하는지 살펴보기 위하여, 이미지 리사이징을 수행하는 샘플 코드를 Phalanger 위에서 실행하도록 PHP/CLR 기반으로 코드를 만들어보도록 하겠습니다. 이 코드는 http://wiki.php-compiler.net/Code_Samples/Resize_image 의 예제를 발췌하여 조금 변형한 것입니다.

<?php
use System\Drawing\Bitmap;
use System\Drawing\Graphics;
use System\Drawing\GraphicsUnit;
use System\Drawing\Rectangle;
use System\Web\HttpContext;

function resize_imageSysDraw($from,$wid,$hgt)
{
  $bmp = Bitmap::FromFile($from);
  $fmt = $bmp->RawFormat;
  $new = new Bitmap($wid, $hgt);
  $gr = Graphics::FromImage($new);
  $gr->DrawImage($bmp,
    new Rectangle(0,0,$wid,$hgt),
    new Rectangle(0,0,$bmp->Width, $bmp->Height),
    GraphicsUnit::Pixel);
  $gr->Dispose();
  $new->Save(HttpContext::$Current->Response->OutputStream, $fmt);
  $new->Dispose();
}

resize_imageSysDraw(realpath('Penguins.jpg'), 320, 240);
?>

PHP/CLR의 경우 여느 닷넷 언어들과 마찬가지로 네임스페이스에 속한 클래스에 대한 참조를 use 명령어로 지정하고 있으며, 정적 멤버에 대해서는 :: 연산자를, 객체 생성은 new 연산자를 사용하였습니다. resize_imageSysDraw 함수에서는 ASP.NET의 HttpContext를 가져와서 기본 출력 대신 비트맵 이미지를 내보내도록 만들었고 그 결과 아래와 같이 축소된 이미지가 렌더링되서 나타나게 됩니다.

Phalanger의 LINQ 지원

이제 마지막으로 PHP/CLR의 하이라이트라고 할 수 있는 LINQ 지원에 대해서 살펴보겠습니다. LINQ는 Microsoft Research에서 C# 언어의 확장 사양인 C-omega 언어의 일부로 개발 중이던 사양을 정규화하여 Production Spec으로 만든 것으로, C# 이외에 VB.NET에도 영향을 주었으며 Prism이나 지금 소개하는 Phalanger에서도 개념을 적극 채택하여 정규 사양으로 활용 중입니다. 그리고 F#은 이러한 접근을 더욱 드라마틱하게 활용하여 함수형 언어로 발전시키기도 하였습니다.

LINQ에 대해서 이야기하려면 책을 한 권 따로 만들어야할 만큼 방대합니다. 그래서 자세한 이야기는 하지 않고, LINQ 자체에 대해서 진지하게 학습하기 원한다면 LINQ 관련 국내외 도서들을 검토하기 바랍니다. 개인적으로는 "생각하는 C# LINQ"라는 책을 추천합니다. :-)

http://kangcom.com/sub/view.asp?sku=200809180001&mcd=571

LINQ는 한 마디로 이야기하면, 프로그래밍 코드를 한 방향에서만 바라보도록 뷰 포인트의 시각을 고정한 것과 같습니다. 본디, 어떤 연관성이 있는 데이터 집합을 접근하는 방법에는 여러 가지 방법이 있을 수 있지만 LINQ는 데이터가 어떤 순서로 들어있든, 어떤 형태로 연결되어있든 관계없이 데이터를 꺼내올 수 있도록 도와주는 Iterator 패턴의 한 형태인 Enumerator를 조금 독특하게 해석하였습니다.

Enumerator가 열거할 대상을 미리 정할 수 있도록 만들고, 열거할 때 조건을 지정하여 필요없는 데이터는 건너뛸 수 있게 해준다던지 이런 취지에서 해석을 한 것이 LINQ입니다. 그리고 Enumerator를 수정하게 되는 시점이 이미 메모리 상에 저장된 데이터 셋에 대한 작업인지, 아니면 아직 수신되지 않은 미지의 데이터 셋에 대한 작업인지에 따라서도 지연 실행이냐 즉시 실행이냐 이렇게 구분하기도 하구요. 그러면서도 항상 잃지 않는 것은 핵심은 Enumerator라는 사실이며, 이에 입각하여 배열이나 리스트같은 정규화된 자료 구조로 변환할 수 있는 길을 항상 열어놓아 최대한의 유연성을 부여하기도 합니다.

어렵게 들릴 수도 있지만 Enumerator를 수정할 수 있게 해준다는 컨셉은 생각보다 활용 폭이 넓은데, 가장 가까이 있는 예로는 SQL 쿼리가 될 수 있습니다. 처음의 아이디어는 SQL 쿼리를 이용하여 전체 데이터 셋보다 가능한 적게 데이터를 반환하여 네트워크 트래픽을 줄이고 빠르게 데이터를 검색할 수 있도록 최적화하자는 것에 있었을 것이며, 이것을 좀 더 프로그래밍 언어와 친화적으로 만들 방법을 모색한 끝에 LINQ to SQL이 나타나게 된 셈입니다. 그리고 이를 필두로 접근할 수 있는 모든 유형의 컬렉션에 대해서 이런 아이디어를 대입하여 현재는 오픈 소스를 찾아보면 정말 엄청나게 많은 LINQ provider들을 발견할 수 있을 정도입니다.

이렇게 독창적이고 전례없던 기술을 Phalanger에서도 이용할 수 있다는 것은 매우 좋은 일입니다. <phpNet> 요소에 대해 PHP-CLR을 활성화하도록 설정을 수정한 후 아래 코드를 테스트해보기 바랍니다.

<?php
$myarray = json_decode('[
    {"label":"foo","name":"baz"},
    {"label":"boop","name":"beep2"},
    {"label":"foo","name":"baz1"},
    {"label":"boop","name":"beep3"},
    {"label":"foo","name":"baz2"},
    {"label":"boop","name":"beep1"}
]', true);

$result =
from $myarray as $x
where $x['label'] == 'foo'
select $x['name'];

foreach ($result as $x) {
    print($x.'<br />');
}

print_r($result);

?>

json_decode라는 기본 PHP 함수를 이용하여 JSON을 PHP 연관 배열로 바꾸고, 이것을 LINQ로 조회한 다음, 그 결과를 foreach 문을 통해서 출력하도록 만들었습니다. C#이나 VB.NET의 LINQ와 약간 다른 점은, from 절에서 in 연산자 대신 as 연산자를 사용하고 in 연산자와는 도치되는 좌/우항 관계를 가집니다. 즉, [나열 변수] in [데이터 소스] 에서 [데이터 소스] as [나열 변수]로 바뀝니다. 그리고 이것은 foreach 문에도 동일하게 적용됩니다. 아래는 실행 결과입니다.

PHP, JSON, 그리고 LINQ가 한 자리에 모여 매우 재미있는 상호 작용을 이룬 것을 볼 수 있습니다. 이 정도면 닷넷에서의 웹 프로그래밍이 이전과는 제법 많이 달라질 수 있다는 것을 체감할 수 있을 것입니다.

다음번에는 Phalanger가 기존 PHP의 모듈들을 어떻게 다루고 관리하는지에 대한 상세한 내용을 살펴보도록 하겠습니다. 긴 글 읽어주셔서 감사합니다. :-)

저작자 표시 비영리 변경 금지
크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by Windows Azure MVP 남정현 (rkttu.com)

지난 아티클에 이어 오늘은 Phalanger를 이용하여 C#과 VB.NET 코드를 동시에 활용하는 예를 한 번 조명해보려고 합니다. 사실 이것이 Phalanger의 하이라이트라고 하여도 무방하지 않습니다. 기존의 C#과 VB.NET 로직을 재사용하면서도 Phalanger로 빠르고 가볍게 웹 페이지를 풀어나가는 방식은 여러모로 웹 개발에 가속을 붙여줄 것입니다.

계속 설명하기 전에, Phalanger가 ASP.NET 위에서 실행되기 때문에 적용되는 한 가지 특수한 규칙을 이야기할 것이 있습니다. 바로 미리 정해진 이름의 폴더인데, Bin, App_Code 폴더입니다. 그 외 많은 폴더들이 있지만 지금은 두 가지만 살펴보겠습니다.

앞에서 이야기한대로 ASP.NET은 닷넷 프레임워크를 기반으로 작동한다고 하였고, 닷넷 프레임워크는 언어의 종류와 무관하게 MSIL이라는 코드로 컴파일되서 나오는 DLL 파일들을 취급할 수 있습니다. 그렇게 만들어지는 DLL 파일들은 Bin 폴더에 파일을 넣어주면면 자동으로 참조가 활성화되서 해당 웹 사이트 안의 모든 닷넷 코드에서 클래스 라이브러리를 사용할 수 있게 됩니다. 단, 주의할 것은 이 폴더 안에 아무 DLL이나 넣을 수 있는 것은 아니며 반드시 닷넷으로 컴파일된 DLL만 포함시킬 수 있습니다.

그리고 이번 아티클에서 가장 핵심이 되는 기능을 제공하는 폴더인 App_Code 폴더는, 컴파일을 하려는 모듈에 대한 코드를 여기에 넣어서 모든 페이지에서 공유할 수 있습니다. 기본적으로 이 폴더에는 한 종류의 언어에 해당되는 파일들만 넣을 수 있고, 그외 다른 언어의 파일이 포함되면 아래 이미지와 같은 오류가 발생합니다. 하지만 제가 이전에 소개했던 약간의 추가 설정을 적용하면 App_Code 폴더에서 여러 프로그래밍 언어를 동시에 사용할 수 있습니다.

위와 같이 나타나는 문제를 해결하기 위하여, App_Code 폴더 자체에서 사용할 언어는 Phalanger로 정하고, App_Code 폴더 안의 두 번째 수준의 디렉터리들에 C#이나 VB.NET 같은 언어를 사용하기로 결정합니다. 그리고 web.config 파일에 다음과 같이 <system.web> 요소 아래에 하위 디렉터리가 존재한다는 것을 알려줍니다.

<system.web>
<globalization requestEncoding="utf-8" responseEncoding="utf-8" fileEncoding="utf-8" />
<compilation>
  <codeSubDirectories>
    <add directoryName="Cs" />
    <add directoryName="Vb" />
  </codeSubDirectories>
</compilation>
</system.web>

그리고 App_Code 폴더에 각각 Cs 폴더와 Vb 폴더를 만듭니다. 폴더 이름에서 바로 알 수 있듯이 각각 C#과 VB.NET 코드를 따로 보관할 수 있도록 하고, 위의 그림처럼 오류가 발생하지 않으면서 자동으로 C#과 VB.NET으로 만든 코드를 Phalanger에서 액세스할 수 있도록 하게 할 것입니다. 이것으로 준비는 모두 끝났으며 우리가 원하는대로 C#과 VB.NET 코드를 추가하기만 하면됩니다.

C# 코드

using System;
using System.Collections.Generic;
using System.Web;

/// <summary>
/// Summary description for ClassName
/// </summary>
public class ClassName
{
    public string Name { get; set; }
    public int Age { get; set; }
    public override string ToString() {
        return String.Format("Name: {0} / Age: {1:D3}", Name, Age);
    }
}

VB.NET 코드

Imports Microsoft.VisualBasic

Public Class ClassName2
    Public Content As String
End Class

그리고 중요한 부분이 있습니다. Phalanger가 서브 디렉터리에 있는 코드들을 컴파일하였을 때 각각 따로 만들게 될 코드 조각들을 인식할 수 있도록 최소한 1개 이상의 임의의 PHP 파일이 필요합니다. 이후 아티클에서 따로 설명하겠지만 Phalanger는 App_Code 폴더 안에 있는 PHP 파일을 모두 자동으로 include하는 동작을 가지고 있습니다. (이것이 이전의 Original PHP와는 다른 부분입니다.)

지극히 일상적이고도 당연한 클래스 선언을 담고 있는 파일을 각 디렉터리에 추가하였습니다. 이제 웹 매트릭스의 폴더 레이아웃은 아래 그림과 같은 형태가 되면 됩니다.

이제 마지막으로 index.php 코드를 아래와 같이 작성합니다. 아래와 같이 작성하면 정말 PHP에서 C#과 VB.NET 코드를 자동으로 불러올 수 있을까요? 기대됩니다. :-)

<?php       
// C#
$test1 = new ClassName();
$test1->Name = "남정현";
$test1->Age = 2012-1987;
print $test1->ToString().'<br />';
print get_class($test1).'<br />';

// VB.NET
$test2 = new ClassName2();
$test2->Content = '<strong>안녕하세요!!!</strong>'.'<br />';
print $test2->Content;
print get_class($test2).'<br />';

phpinfo();
?>

ClassName은 C#으로, ClassName2는 VB.NET으로 만든 코드입니다. PHP/CLR 확장의 도움으로 Name, Age, Content 프로퍼티에 문자열을 대입하고 있으며, print 문을 이용하여 웹 페이지 상에 문자열을 출력합니다. 그리고 get_class라는 기본 PHP 함수를 사용하여 클래스 형식명을 가져오는 일도 하려고 하는군요. 마지막으로는 phpinfo() 함수를 호출하여 Phalanger 및 서버 시스템에 대한 정보도 덤프로 출력합니다. 이제 이 페이지를 실행해보면 아래와 같이 나타나게 될 것입니다. :-)

훌륭합니다! C#과 VB.NET 컴파일러를 따로 부르는 일 없이 앉은 자리에서 한 번에 코드를 컴파일하고, 이것을 PHP에서 보기 좋게 가져다 쓰는 일까지 해냈습니다.

다음 아티클에서는 마지막으로 Phalanger만의 고유한 기능이라고 할 수 있는 자동 include에 대해서 마지막으로 이야기해볼까 합니다. Original PHP에서는 include나 include_once 같은 함수를 어떤 인과 관계에 따라서 포함하게 될 것인지, 그리고 순서에 대해서도 많은 고민을 해야 했지만 Phalanger는 이러한 부분에 대한 고민을 많이 덜어낼 수 있습니다. 그리고 그 중심에는 App_Code 폴더의 역할이 크다고 했는데요, 이 부분에 대해서 집중적으로 살펴보려고 합니다.

 

저작자 표시 비영리 변경 금지
크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by Windows Azure MVP 남정현 (rkttu.com)

Phalanger 공식 웹 사이트에서 따로 소개된 적은 없지만, Phalanger와 웹 매트릭스는 아주 이상적인 궁합을 보여주고 있으며 Phalanger가 보여주고자 하는 모습을 가감없이 완벽하게 보여줍니다. 이 아티클을 다 읽고 나면 지금 말하는 것이 어떤 의미인지 알게 될 것입니다.

Phalanger 설치하기

우선 컴퓨터에 Phalanger를 설치해야 합니다. 이전 아티클에서 이야기한대로 Phalanger는 .NET Framework 4.0을 필요로 합니다. 그리고 지금 우리가 실습하려는 WebMatrix도 설치해야 하는데, 결론적으로 지금 이야기하는 도구와 프레임워크들은 한 번에 Microsoft /web 홈페이지에서 제공하는 Web Platform Installer를 이용하여 한 번에 설치할 수 있습니다. http://www.microsoft.com/web 에서 WPI를 설치하고 WebMatrix와 .NET Framework 4.0을 선택하여 설치를 진행하도록 합니다. Phalanger를 설명하는 과정에서는 필요없지만, C#이나 VB.NET을 같이 사용하려고 한다면 Visual Web Developer 2010 Express도 설치합니다.

기본 구성 요소 설치가 끝나면 http://www.codeplex.com/phalanger에서 최신 버전의 Phalanger 설치 프로그램을 다운로드하여 설치를 시작합니다. Phalanger 설치 프로그램 안에는 런타임과 Visual Studio 도구가 모두 들어있습니다. 웹 개발에는 Visual Studio 도구가 따로 필요하지 않으며, Phalanger를 이용해서 PHP/PEAR와 같은 응용프로그램 개발을 하기 원한다면 Visual Studio 도구를 사용하는 것이 편합니다.

WebMatrix에서 Phalanger 사용하기

기본적으로 WebMatrix는 PHP4와 PHP5를 지원합니다. 그러나 WebMatrix가 기본으로 제공하는 PHP 런타임은 사용하지 않고 앞 단계에서 설치한 Phalanger 런타임을 대신 불러오도록 설정을 업데이트할 것입니다. 그러면서도, 기존의 PHP 개발 템플릿을 그대로 이용할 수 있습니다.

WebMatrix에서 Phalanger를 사용하는 방법은 간단합니다. 여러분이 원하는대로 사이트를 하나 새로 만들고, 사이트 탭을 클릭하고 설정 메뉴를 클릭하면 아래와 같이 화면이 나타납니다. 그림에 적은 설명대로, .NET Framework는 버전 4.0을 사용해야 하며, PHP 설정은 사용하지 않고, 필요하다면 index.php가 기본 페이지로 지정되도록 합니다.

이제 파일 탭을 클릭하고 F5키를 눌러 web.config 파일이 있는지 확인합니다. 만약 없다면 web.config 파일을 새 파일로 하나 추가합니다. web.config 템플릿이 WebMatrix에 기본으로 제공되므로 쉽게 추가할 수 있을 것입니다.

참고로 web.config은 기존의 ASP.NET 환경에서도 쓰이지만 WebMatrix가 내부적으로 서버로 사용하는 IIS 7 Express 및 IIS 7의 설정 파일로도 사용되며, IIS 6 이하에서 사용하던 메타베이스 기반 설정과는 달리 Apache HTTP Server의 httpd.conf와 같은 맥락의 디렉터리 단위 설정 파일이라고 보면 되겠습니다.

web.config 파일을 열어서 <system.webServer> XML 요소 아래의 내용을 수정해야 합니다. (다른 부분이 있더라도 여기서는 필요하지 않습니다.) 아래의 내용을 추가하여 PHP 확장자에 대한 지원을 추가하도록 합니다.

<handlers>
  <add name="PhalangerHandler" path="*.php" verb="*" type="PHP.Core.RequestHandler, PhpNetCore, Version=3.0.0.0, Culture=neutral, PublicKeyToken=0a8e8c4c76728c71" resourceType="Unspecified" preCondition="integratedMode" />
</handlers>

파일을 저장하고, 잘 작동하는지 테스트하기 위하여 index.php 파일을 만들고 아래와 같이 코드를 작성합니다. 그리고 상단의 실행 버튼을 눌러 잘 나타나는지 확인합니다.

<?php

class myClass {
    var $x;
}

$test = new myClass();
$test->x = "Hello, World! in ";
print $test->x.'<br />';
print get_class($test);

phpinfo();

?>

그러면 아래와 같이 화면이 나타날 것입니다. 클래스를 선언하고, 변수 필드를 선언하고, 여기에 값을 대입하거나 print 문을 사용하여 문자열을 출력하고, get_class 같은 간단한 형식 조회 함수도 씁니다. 아, 그리고 phpinfo 함수는 PHP 세계에서는 매우 기초적이고도 기본적인 함수였죠. :-)

그리고 흔히 사용하는 include_once 같은 API도 잘 작동합니다. 같은 디렉터리 상에 test.php를 만들고 class 선언만 따로 떼어 저장한 다음 include_once 함수를 호출해보기 바랍니다.

test.php

<?php
class myClass {
    var $x;
}

?>

index.php

<?php

include_once('test.php');

$test = new myClass();
$test->x = "Hello, World! in ";
print $test->x.'<br />';
print get_class($test);

phpinfo();

?>

Phalanger에서 한글을 사용하려면

PHP4나 PHP5와 다를바 없는 실행 모습입니다. 그런데 한 가지 점검해봐야 할 것이 있습니다. Phalanger에서 한국어나 일본어같은 2바이트 문자를 정상적으로 취급할 수 있을까요? 기본 설정으로는 그렇지 않을 가능성이 있습니다. 그리고 웹 매트릭스는 모든 파일을 UTF-8로 저장하기 때문에 문제가 됩니다. 이러한 문제를 예방하기 위해서는 반드시 web.config 설정을 변경해야 합니다.

<system.web> 요소 아래에 다음의 코드를 추가하도록 합니다.

<globalization requestEncoding="utf-8" responseEncoding="utf-8" fileEncoding="utf-8" />

그리고 앞의 코드에서 한국어 문자열을 포함하는 print 명령문을 한 번 더 추가하여 한글이 잘 나오는지 확인합니다.

이제 Phalanger를 WebMatrix에서 개발할 준비는 다 끝났습니다. 다음 아티클에서는 Phalanger만의 고유한 기술적 특징을 살펴보면서, Phalanger를 C#과 VB.NET과 함께 사용하는 방법을 살펴보도록 하겠습니다. :-)

저작자 표시 비영리 변경 금지
크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by Windows Azure MVP 남정현 (rkttu.com)

그 동안 .NET은 ASP.NET을 통해서 개발하는 것이 가장 최선이었고 실제로도 많은 개발 프로젝트는 ASP.NET 웹 폼을 통해서 진행되어왔습니다. 그리고 당연한 이야기이지만 PHP와 .NET은 전혀 다른 도메인에 속해있던 분리된 환경이었고, 그저 IIS를 통해서 호스팅 가능한 서로 다른 응용프로그램 도메인 상의 환경일 뿐이었습니다.

그러나 최근에 아주 흥미롭고 참신한 발견을 다시 했는데, 그간 베타 수준에만 머물러있던 Phalanger의 꾸준한 버전 업그레이드를 통해서 올해 1월에 3.0 버전을 발표했습니다. 64비트 시스템에 대한 지원도 충실히 하고 있으며, PHP의 태생적 한계로 자주 지목된 성능 상의 문제도 .NET 런타임을 사용하기로 하였기 때문에 구조적으로 해결하고 있으며, 무엇보다도 중요한 것은 엄격하게 PHP의 문법을 준수한다는 전제 아래에서 PHP 코드를 사용할 수 있다는 점입니다.

ps. Phalanger의 발음은 '팔란저'라고 하면 됩니다. 뜻은 '여우 원숭이'입니다.

Phalanger 프로젝트의 주요 기능은 다음과 같습니다.

PHP를 .NET 환경의 주요 프로그래밍 언어로 사용 가능하게 만듭니다.
* PHP 언어를 .NET CLR에서 사용 가능한 MSIL로 컴파일합니다.
* .NET 객체를 직접 PHP/CLR 언어 확장을 통해서 곧바로 사용할 수 있습니다.
* C#, VB.NET 등의 .NET 프로그래밍 환경에서 기존 PHP 코드 기반 라이브러리를 재사용할 수 있습니다.

기존 PHP 응용프로그램의 실행 속도를 향상시킵니다.
* 기존의 많은 수의 PHP 응용프로그램을 컴파일할 수 있습니다.
* Just-in-Time (JIT) 컴필레이션을 통해서 실행 속도를 개선할 수 있습니다.
* 표준 PHP 라이브러리 함수들과 네이티브 PHP4 확장 플러그인들을 그대로 사용할 수 있습니다.

PHP/CLR 확장을 통해서 PHP의 기능을 확장할 수 있습니다.
* PHP/CLR을 통해서 기존의 .NET CTS 시스템과 완벽하게 연동할 수 있습니다.
* PHP/CLR 프로젝트에 기존의 C#, VB.NET, C++ CLR 등으로 작성한 코드의 네임스페이스를 불러올 수 있습니다.
* PHP 언어로 .NET 제네릭 형식을 불러오거나 제작할 수 있습니다.
* .NET 커스텀 어트리뷰트, 부분 클래스, 프로퍼티 등 주요 기능들을 지원합니다.

PHP 언어로 .NET 라이브러리를 만듭니다.
* .NET/Mono 어셈블리에 맞추어 PHP 스크립트를 직접 DLL로 컴파일할 수 있습니다.
* Pure Mode를 사용하면 네이티브 코드에 의존하지 않는 완전한 .NET 환경에 맞출 수 있습니다.
* Legacy Mode를 사용하면 기존 PHP4/PHP5와의 호환성을 유지하면서도 .NET 환경에 맞출 수 있습니다.

PHP 프로젝트에서 .NET 라이브러리를 사용할 수 있습니다.
* C#이나 VB.NET으로 작성한 비즈니스 로직 위에 PHP를 표현 수단으로 채택할 수 있습니다.
* Phalanger를 통해서 기존에 작성한 어떤 종류의 .NET 객체이더라도 PHP에서 사용할 수 있습니다.
* ASP.NET 2.0 멤버십 API를 Phalanger 기반 PHP와 ASP.NET 모두에 적용할 수 있습니다.

PHP 언어를 Visual Studio에 통합할 수 있습니다.
* 프로젝트 템플릿, 문법 하이라이트, 디버거를 설치할 수 있습니다.
* 이를 통하여 Windows Forms, Console, Simple Win32 App을 만들 수 있습니다.

미리 이야기할 것이 하나 더 있는데 안타깝게도, Phalanger가 지원하는 PHP 문법은 기존의 PHP 문법과 완벽하게 일치하는 것이 아닙니다. 그래서 Phalanger를 기존의 PHP와 완벽하게 동일한 집합이나 기술로 보는 것은 다소 무리가 있으며, 대신 기존에 여러분이 알고 있던 PHP를 .NET 환경에서 그대로 쓸 수 있다는 사실에 집중하는 것이 바람직합니다.

개인적으로 Phalanger를 테스트하기에 가장 이상적인 환경은 WebMatrix인 것 같습니다. 다음 아티클에서는 Phalanger를 설치하고 WebMatrix위에 Phalanger로 PHP 코드를 작성하는 방법을 소개하도록 하겠습니다.

저작자 표시 비영리 변경 금지
크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by Windows Azure MVP 남정현 (rkttu.com)