블로그 이미지
No pain, no gain!
lepoussin

Tag

Notice

Recent Post

Recent Comment

Recent Trackback

Archive

calendar

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 29 30
  • total
  • today
  • yesterday
04-19 21:02
2009. 7. 23. 00:51 Embedded System
AESOP S3C6410 보드의 경우 커널의 부트 아규먼트를 uBoot에서 커널로 넘겨줄 수 있습니다.
이외에 보드 환경 설정을 위한 다양한 환경 설정을 uBoot에서 지원 합니다.

따라서, 커널의 재 컴파일 작업이 없이도, 간단하게 커널의 부트 환경 설정을 변경할 수 있습니다.

1. NFS를 이용한 루트 파일 시스템 마운트 방법

최초로 이솝 보드를 받았을 때 NAND Flash에 루트 파일 시스템을 기록하기 위해서 또는 어플리케이션 등을 개발할 때는
NFS로 루트 파일 시스템을 마운트 하여 개발을 진행 합니다.

이 경우, uBoot의 명령 프롬포트에서 다음의 명령을 수행하면, NFS로 루트 파일 시스템을 마운트 합니다.
(해당 환경 설정을 수행하기 이전에, 호스트PC의 /nfsroot 디렉터리에 이솝 보드용 NFS 루트 파일 시스템이 있어야 합니다.)

setenv bootargs console=ttySAC0,115200 root=/dev/nfs rw nfsroot=[호스트PC의 IP주소]:/nfsroot/RootFS-aESOP6410 ip=[타깃 보드의 IP주소]:[호스트 PC의 IP주소]:[게이트 웨이 주소]:[서브넷 마스크]::eth0:off mem=128M ethaddr=[이더넷 컨트롤러의 MAC 주소]

saveenv => 환경 설정을 NAND Flash 저장

boot       => 적용한 환경 설정을 가지고 부팅

예)

setenv bootargs console=ttySAC0,115200 root=/dev/nfs rw nfsroot=192.168.1.15:/nfsroot/RootFS-aESOP6410 ip=192.168.1.85:192.168.1.15:192.168.1.1:255.255.255.0::eth0:off mem=128M ethaddr=00:40:5c:26:0a:5b

saveenv

boot

2. uBoot의 IP주소 설정 방법

uBoot에서 호스트 PC의 TFTP를 이용한 다운로드를 하기위한 IP 주소 설정은 다음과 같이 합니다.

setenv gatewayip [게이트 웨이 주소];setenv ipaddr [타깃 보드 IP주소];setenv serverip [호스트 PC IP주소]

saveenv => 환경 설정을 NAND Flash 저장

예)
setenv gatewayip 192.168.1.1;setenv ipaddr 192.168.1.100;setenv serverip 192.168.1.15

saveenv

3. TFTP를 이용하여 리눅스 커널 이미지를 다운로드 한 후 부팅

리눅스 커널 개발 시, 커널을 수정하면서 테스트가 필요할 경우 다음과 같이 옵션을 넣어주면,
이솝 보드는 TFTP를 통하여 리눅스 커널 이미지를 다운로드 받은 후 다운로드 받은 커널 이미지를 가지고 부팅을 수행 합니다.

setenv bootcmd tftp c0008000 zImage-aESOP6410\;bootm c0008000

saveenv => 환경 설정을 NAND Flash 저장

4. TFTP를 이용하여 커널을 다운로드 받고 NAND Flash에 기록

다음은 TFTP를 이용하여 리눅스 커널 이미지를 다운로드 받고, 자동으로 다운로드 받은 커널을 NAND Flash에
기록하는 명령 입니다.

tftp 0xc0008000 zImage-aESOP6410;nand erase 60000 200000;nand write 0xc0008000 60000 200000

5. NAND Flash에 저장된 커널로 자동 부팅

다음의 명령을 입력하면, 이솝 보드는 부팅 시 자동으로 NAND Flash에 기록된 커널을 읽어서 부팅을 수행 합니다.

setenv bootcmd nand read C0008000 60000 200000\;bootm C0008000

saveenv => 환경 설정을 NAND Flash 저장

6. TFTP를 이용하여 부트로더를 다운로드 받고 NAND Flash에 기록

부트로더가 수정되었거나, 교체가 필요할 경우 다음의 명령을 입력하면, 부트로더를 다운로드 받고 이것을
자동으로 NAND Flash에 기록 합니다.

tftp 0xc0008000 uBoot-aESOP6410.bin;nand write 0xc0008000 0 30000

7. NAND Flash에 저장된 루트 파일 시스템을 마운트 하여 부팅

루트 파일 시스템이 NAND Flah에 저장되어 있을 경우 부트로더에서 다음의 옵션을 입력하면, NAND Flash에
저장된 루트 파일 시스템을 마운트하여 부팅 합니다.

setenv bootargs root=/dev/mtdblock3 rootfstype=yaffs2 console=ttySAC0,115200

8. NAND Flash에 루트 파일 시스템을 기록하는 방법

제공 되는 이솝 보드용 NFS 루트 파일 시스템으로 부팅을 수행 한 후, 다음의 명령을 이용하여 NAND Flash에
루트 파일 시스템을 기록할 수 있습니다. (NAND Flash 기록된 루트 파일 시스템으로 부팅하려면 7번을 참조하세요.)

# NFS로 부팅한 리눅스 프롬포트에서 수행

flash_eraseall /dev/mtd3
tar -C /mnt/nand -xf ~/RootFS-aESOP6410.tar
sync
umount /mnt/nand

posted by lepoussin
2009. 7. 23. 00:25 Embedded System
커널에서 NFS로 파일시스템을 마운트 할 경우, 다음과 같은 메시지가 발생하여 부팅을 실패하는 경우가 많다.

nfs: server 192.168.1.15 not responding, still trying

위의 메시지 발생시, 커널의 부트 아규먼트에서 다음의 옵션을 추가해 주면 된다.

setenv bootargs root=/dev/nfs rw nfsroot=192.168.0.77:/opt/RootFS-aESOP6410,nolock,tcp,rwize=4096,wsize=4096 ip=192.168.0.102:192.168.0.77:192.168.0.1:255.255.255.0::eth0:off console=ttySAC0,115200n81 ethaddr=00:40:5c:26:0a:5c

posted by lepoussin
2009. 7. 8. 17:35 Language/PHP
PHP에서 무르익어가는 열매, PEAR

PEAR는 ‘PHP Extension and Application Repository’의 약자다. 직역하자면 PHP의 확장과 애플리케이션의 저장소 정도가 될 것이다. 과일의 배를 의미하는 영어 ‘pear’와 같이 읽으면 된다. 간략하게 PEAR를 정의하자면 재사용성을 높인 PHP 컴포넌트 또는 라이브러리들에 기반을 둔 프레임워크와 이의 배포 방식이라 이야기할 수 있겠다.

소프트웨어를 개발하면서 재사용이 가능한 결과물을 만들고 이러한 결과물들을 재사용하는 것이 미덕으로 여겨지던 시대는 이미 지나간 것 같다. 오히려 전체 결과물에서부터 이를 구성하는 개개의 요소까지 재사용을 고려하지 않는다면 해야 할 일을 하지 않았다는 지적을 받아도 변명할 틈이 없는 것이 현재의 개발자의 입장일 것이다.
이런 재사용을 위해 개인 라이브러리를 구축하기도 하고, 컴포넌트를 만드는 등 노력을 하지만 이런 경우 또한 개인의 재사용 여부를 넘어선 범용성이라는 문제가 다시 남게 된다. 특정 집단 내부에서만 소통되는 방식으로 만들어진 컴포넌트, API, 라이브러리, 프레임워크들은 대중이 알아듣기에는 다소 무리가 있는 사투리일 수 있기 때문이다. 스스로 PHP는 자신 있다고 생각하는 개발자라도 본인이 접하지 못했던 사투리를 알아듣는다는 것은 쉬운 일도 아니고 아울러 적잖은 시간을 필요로 하게 된다.
실세계의 언어에는 ‘표준어’라는 것이 존재하지만 소프트웨어 개발에 있어서는 그저 대세를 따르는 것이 가장 현명한 접근이 되어줄 뿐이라는 조언 이상이 힘들 것이다. 최소한 스스로 ‘대세’가 무엇인지를 판단해야 하는 숙제는 여전히 남게 된다. 다행히도 PHP의 경우 PEAR와 PECL이라는 자매 프로젝트가 대세로 인정받고 있다는 사실에 의심의 여지가 없으니 다른 개발 환경보다는 쉽게 앞서의 숙제에 대한 해답을 얻을 수 있는 셈이라고 이야기하고 싶다.
PEAR를 구성하는 라이브러리들을 유기적으로 모아둔 프레임워크뿐 아니라 이의 배포가 이루어지는 홈페이지와 사용자들의 의견 교환 공간 모두를 합한 것이 PEAR를 의미한다고 표방하고 있다. PEAR 사이트의 PEAR 소개를 적어보면 다음과 같다.

◆ PHP 사용자를 위한 오픈소스 기반 구조적 라이브러리
◆ 코드 배포와 패키지 관리 시스템
◆ PHP 표준 코딩 스타일
◆ PFC(PHP Foundation Class)
◆ PECL(PHP Extension Community Library)
◆ PEAR 프로젝트 지원을 위한 웹 사이트 및 메일링 리스트

프레임워크의 내용이 방대하다 보니 글에서 일일이 설명을 하지 못하고 상세한 내용은 매뉴얼 등 문서를 참조하길 바란다는 이야기를 미리 해야겠다. 모든 프레임워크가 그러하듯이 PEAR 또한 선행 학습이 없다면 프레임워크의 강력함을 제대로 맛보기 어려울 것이라는 이야기로 매뉴얼의 중요함을 다시 한 번 강조하면서 PEAR와의 만남을 시작하겠다.


go-pear를 이용한 PEAR 설치

먼저 PEAR의 설치부터 살펴보자. 앞의 목록에 포함되어 있는 ‘코드 배포와 패키지 관리 시스템’이 PEAR의 기본 설치와 추가 패키지 설치, 업그레이드 및 환경 설정을 해주는 자체 기능을 제공하고 있다는 이야기라고 바꾸어 말할 수 있겠다. PHP가 지원되는 수많은 환경의 수많은 경우를 다 언급하기는 힘드니 이 글은 윈도우 환경에서 PHP 4.3.9와 아파치 1.3.33 그리고 데이터베이스는 MySQL 4.1을 기준으로 진행하겠다. PHP, 아파치, MySQL 설치와 환경 설정은 별도의 설치 문서를 참조하길 바란다.
PEAR 설치는 PHP 4.3.× 이상의 버전에 포함되어 있는 매니저 프로그램을 이용해 진행한다. 먼저 PHP를 설치한 디렉토리를 윈도우의 PATH 환경 변수에 추가했다. PHP를 설치한 디렉터리를 살펴보면 go-pear.bat 파일이 있을 것이다. PEAR의 골격을 설치하고 기본적인 설정을 수행하는 프로그램이 go-pear다. 윈도우에서 커맨드 창을 실행한 후 go-pear.bat 파일을 실행하면 <화면 1>과 같이 환영 메시지가 나오고 계속 진행할 것인지를 묻는 것으로 설치가 시작된다.

<화면 1> go-pear의 환영 메세지
사용자 삽입 이미지

환영 메시지가 나타난 후에는 엔터 키를 눌러 진행을 계속한다. <화면 2>와 같이 설치와 관련된 디렉터리와 파일들의 설정을 보여준다. go-pear 실행 이전에 별도로 PHP 관련 디렉터리 구조를 바꾼 경우가 아니라면 엔터 키를 눌러 설정 전체에 대한 확인을 하고 설치를 계속 진행한다. <화면 3>과 같이 각종 패키지가 설치됐음을 알려주는 진행 상황이 화면에 나타날 것이다. 패키지 설치가 끝나면 <화면 4>의 중간 부분에서 보이는 것과 같이 PHP에 대한 설정을 담고 있는 php.ini 파일을 자동으로 수정할 것인지를 묻는다. ‘Y’를 입력해 php.ini 파일을 자동으로 수정한다. go-pear에서 수정한 부분은 php.ini 파일을 텍스트 편집기에서 열어보면 주석으로 go-pear 실행에 의해 바뀌었음이 적혀 있을 것이다.

<화면 2> PHP 관련 환경 설정 확인
사용자 삽입 이미지

<화면 3> 설치 진행 화면, 어떤 패키지들이 설치되고 있는가를 보여준다.
사용자 삽입 이미지

<화면 4> php.ini 자동 수정 확인
사용자 삽입 이미지

설치가 종료되면 설치 디렉터리 안에 윈도우 레지스트리 등록을 위한 PEAR_ENV.reg 파일이 생성되어 있을 것이다. 이 파일을 더블클릭해 레지스트리 정보 등록을 수행한다. <화면 5>의 내용이 PEAR_ENV.reg의 내용이다.

<화면 5> PEAR_ENV.reg 파일의 내용
사용자 삽입 이미지


pear 실행 파일을 이용한 환경 설정과 추가 패키지 설치

go-pear를 이용해 PEAR를 이루는 기본 골격을 설치한 후에는 pear.bat 파일을 이용해 패키지(설치 결과에 따라서는 pear.bat이라는 파일 이름 뒤에 old가 붙는 경우도 있을 것이다. old를 지워 pear.bat 파일로 파일명을 변경한다)를 추가하거나 환경 설정 등의 작업을 수행할 수 있게 된다. 먼저 자주 사용되는 명령어 몇 가지를 살펴보자. 전체 명령행 옵션은 매뉴얼 등을 참조하기 바란다.


패키지 목록 조회 관련 옵션

커맨드 창에서 <화면 6>과 같이 ‘pear list’라고 입력하면 현재 설치되어 있는 패키지 이름과 버전 목록이 조회된다. ‘pear list-remote’라고 입력하면 <화면 7>과 같이 현재 PEAR 프로젝트에서 다운로드할 수 있는 패키지 목록이 나타난다. 설치되어 있는 패키지와 다운로드 가능한 패키지를 동시에 보고 싶다면 ‘pear list-all’이라고 입력한다.

<화면 6> 현재 설치되어 있는 패키지 목록
사용자 삽입 이미지

<화면 7> 다운로드 가능한 패키지 목록
사용자 삽입 이미지


PEAR 관련 환경 설정 관련 옵션

커맨드 창에서 ‘pear config-show’라고 설정 보기 명령을 입력하면 <화면 8>과 같이 전체 환경 설정을 보여준다. 좌측부터 간략한 설명, 환경 변수 이름, 환경 변수의 값을 의미한다. ‘pear config-help’라고 입력하면 앞의 명령보다는 약간 더 상세한 설명을 볼 수 있을 것이다. 특정 환경 변수의 값을 보고 싶은 경우라면 ‘pear config-get 환경 변수 이름’을 입력하면 해당 환경 변수의 값을 조회할 수 있다. 반대로 특정 환경 변수의 값을 변경하고자 하는 경우에는 ‘pear config-set 환경 변수 이름’을 입력한다. 예를 들어 배포되는 패키지의 상태를 담고 있는 preferred_state의 기본값인 안정(stable) 상태의 패키지뿐 아니라 개발 중인 베타 버전의 패키지까지 설치하고 싶다면 preferred_state의 값을 stable에서 beta로 변경해야 한다. ‘pear config-set preferred_state beta’라고 입력하면 이후 패키지 설치시 안정 상태의 패키지뿐 아니라 베타 상태의 패키지도 설치할 수 있다. 역으로 preferred_state가 stable인 상태라면 베타 상태의 패키지는 설치되지 않는다. stable, beta, alpha, devel, snapshot이 상태를 변경할 수 있는 값이다.

<화면 8> PEAR 환경 설정 내용
사용자 삽입 이미지


패키지 설치 관련 옵션

앞서 설명한 list 등의 명령으로 추가하거나 업그레이드하고자 하는 패키지 이름을 확인한 후 install과 upgrade 명령을 수행해 설치와 업그레이드를 수행할 수 있다. ‘install 패키지명’, ‘upgrade 패키지명’과 같이 버전 번호를 포함한 패키지 이름을 입력해 설치와 업그레이드를 수행한다. 특정 패키지를 제거하고자 하는 경우라면 uninstall 명령어를 사용한다. upgrade-all 명령을 실행해 설치된 패키지 전체에 대한 일괄 업그레이드를 수행할 수도 있다. 이외에 CVS, FTP, 직접 다운로드를 통한 PEAR와 패키지 설치가 가능하다.
또 특정 패키지의 경우 상위 패키지가 설치되어야만 설치 가능한 하위 패키지인 경우가 있다. <화면 9>에서 보는 것처럼 Auth 패키지의 설치 없이 하위 패키지인 Auth_HTTP 패키지를 설치하려고 하는 경우 1.2.0 이상 버전의 Auth 패키지가 설치되어야 한다는 메시지가 나오고 Auth_HTTP 패키지 설치는 진행되지 않는다. 다시 Auth 패키지를 설치하고 Auth_HTTP 패키지의 설치 명령을 실행하면 이상 없이 패키지가 설치된다(각각의 진행 내용은 가로 선으로 화면상에서 구분했음).

<화면 9> 패키지간의 의존 관계가 있는 경우의 패키지 설치
사용자 삽입 이미지


PEAR와 MySQL을 이용한 사용자 인증

앞서 설치한 Auth 패키지를 이용해 사용자 인증 프로그램을 작성해 보자. 사용자 이름과 패스워드는 MySQL을 이용해 저장하고 이를 불러들여 인증이 이루어지는 방식으로 Auth 패키지의 사용 방법과 PEAR의 동작 방식을 살펴보자.
먼저 MySQL에서 사용자 인증에 사용할 auth라는 테이블을 작성하자. 다음과 같은 SQL문을 MySQL에서 실행해 auth 데이터베이스를 생성하고 다시 auth 데이터베이스 안에 auth 테이블을 작성한다. auth 테이블에 테스트에 사용할 test1, test2라는 사용자와 사용자의 패스워드를 입력한다. 우선 사용자 이름과 패스워드는 동일하게 입력해 보겠다.

1. auth 데이터베이스를 생성한다.
    CREATE DATABASE auth;

2. auth 데이터베이스를 선택한다.
    USE auth

3. auth 테이블을 생성한다.
    CREATE TABLE auth (     username VARCHAR(50) default ‘’ NOT NULL,     password VARCHAR(32) default ‘’ NOT NULL,     PRIMARY KEY (username),     KEY (password));

4. 사용자 이름과 비밀번호(텍스트 형태)를 두 건 입력한다. 두 번째 입력 값의 패스워드는 MD5() 함수를 적용한다.
    INSERT INTO auth values(‘test1’,’test1’),(‘test2’,MD5(‘test2’));

다음으로 <리스트 1>과 같이 간단한 인증 소스를 작성해 auth_test. php로 저장해 보자.

<리스트 1>
Auth 패키지를 이용한 인증 예
<?
php
require_once “Auth.php”;

function loginFunction()
{
    echo “<form method=\”post\” action=\”” . $_SERVER[‘PHP_SELF’] . “?login=1\”>”;
    echo “사용자명<input type=\”text\” name=\”username\”><BR>”;
    echo “패스워드<input type=\”password\” name=\”password\”><BR>”;
    echo “<input type=\”submit\”>”;
    echo “</form>”;
}

if (isset($_GET[‘login’]) && $_GET[‘login’] == 1) {
    $optional = true;
}
else {
    $optional = false;
}

$dsn = “mysql://mySQL계정:mySQL패스워드@서버IP:포트/auth”;
$a = new Auth(“DB”, $dsn, “loginFunction”, $optional);

$a->start();

if (isset($_GET[‘logout’]) && $_GET[‘logout’] == 1 && $a->checkAuth()) {
    $a->logout();
    $a->start();
}

echo “로그인 인증 성공 여부에 관계없이 나타나는 메시지입니다.<BR>”;

if (!isset($_GET[‘login’])) {
    echo “<a href=”” . $_SERVER[‘PHP_SELF’] . “?login=1”>로그인하려면 클릭해 주세요.</a><BR>\n”;
}

if ($a->getAuth()) {
    echo “로그인 인증 성공!<BR>”;
    echo “<a href=”auth_test.php?logout=1”>로그아웃을 원하면 클릭해 주세요.</a>\n”;
}
?>

$dsn = “mysql://MySQL계정:MySQL패스워드@127.0.0.1/auth”;의 MySQL 계정과 MySQL 패스워드, 서버 IP와 포트(기본값인 3306을 사용하는 경우는 적지 않아도 된다)를 각자의 환경에 맞춰 적어준다. 주석 한 줄 들어가 있지 않은 코드이니 먼저 실행해 그 결과를 확인해 보자. 웹 브라우저에서 auth_test.php를 호출하면 <화면 10>과 같은 초기 메시지가 나타날 것이다. 아래 줄의 링크를 클릭해 로그인 입력 화면으로 이동하면 <화면 11>과 같이 입력 양식이 나타날 것이다. 사용자 이름과 패스워드 입력란에 앞서 auth 테이블에 저장했던 ‘test1’을 입력하고 로그인을 수행해 보자. 로그인에 실패하고 다시 <화면 11>과 같은 상태로 되돌아올 것이다. 이번에는 ‘test2’를 입력하고 로그인을 수행해 보자. 정상적인 로그인 인증이 일어나고 <화면 12>와 같은 로그인 성공 메시지와 로그아웃 링크가 나타날 것이다.

<화면 10> 초기 메시지
사용자 삽입 이미지

<화면 11> 입력 양식
사용자 삽입 이미지

<화면 12> 로그인 성공 메시지와 로그아웃 링크
사용자 삽입 이미지

앞서 auth 테이블에 자료를 입력할 때 ’test1‘의 경우 패스워드를 텍스트 상태로 ’test1’이라고 입력했고, ‘test2’의 경우는 MySQL의 MD5() 함수를 사용해 패스워드를 저장했음을 기억해 내자. PEAR의 Auth 패키지는 기본적으로 패스워드 필드를 MD5가 적용된 것으로 인식한다. 다음으로 이어지는 loginFunction()의 내용은 HTML 입력 양식을 화면에 출력하라는 내용으로 구성된 함수다.


사용자 인증 프로그램의 동작 설명

이제 <리스트 1>의 PHP 소스를 차근히 살펴보자. 본격적인 코드의 첫줄인 require_once “Auth.php”;는 Auth 패키지를 사용하겠다는 의미임을 쉽게 알 수 있을 것이다(require와 require_once, 그리고 include의 차이점은 독자의 몫이라 생각하겠다). PEAR를 사용하는 경우 Auth와 같은 패키지 이름을 php 파일명으로 사용하지 않도록 주의한다.
이어지는 부분은 GET 파라미터로 login=1과 같이 값을 전달받은 경우 $optional 값을 true로, 그렇지 않은 경우에는 $optional 값을 false로 만드는 기능을 수행한다.
다음에 이어지는 부분이 $dsn 값에 URL 형식으로 데이터베이스 정보를 적는 내용이다. MySQL 이외의 데이터 연결의 경우도 형식에는 그리 크게 차이가 없다. 주요 데이터베이스별 연결은 이후 DB 패키지를 설명하면서 상세히 살펴보겠다.
그 다음에 나오는 $a = new Auth(“DB”, $dsn, “loginFunction”, $optional); 한 줄에 사실상 인증과 관련해 프로그래머가 신경 써 주어야 할 내용이 모두 담겨 있는 셈이다. 첫 번째 값 “DB”는 DB 패키지의 기능을 이용해 인증을 수행하겠다는 의미다. DB 외에 파일, LDAP, SMB, RADIUS, SOAP, IMAP 등을 인증 방법으로 사용할 수 있고 Auth 패키지의 하부 패키지를 확장하면 더 다양한 인증 방법을 사용할 수 있다.

두 번째로 입력받은 $dsn 값이 데이터베이스 연결 정보를 갖는 DSN(Data Source Name) 값이다. 세 번째 값인 “loginFunction”이 인증을 수행하기 위해 실행되어야 하는 함수의 이름이고, 마지막으로 앞서 true인지 false인지가 결정된 $optional의 값이 세 번째 값에 해당하는 함수의 실행 여부를 나타낸다. 초기에 php 확장자까지만 브라우저 주소 창에 입력한 경우라면 $optional의 값이 false가 되므로 “로그인 인증 성공 여부에 관계없이 나타나는 메시지입니다.”라는 문장과 “로그인하려면 클릭해 주세요”라는 링크 문장만 화면에 나타나게 된다. 이 링크를 클릭하는 경우 ?login=1이라는 GET 파라미터가 전달되고 $optional이 true로 변경되며 loginfuction()이 수행된 결과로 화면에 사용자 이름과 패스워드 입력 폼이 나타나게 된다.
‘$a->start()’를 호출하면 Auth 객체(PEAR는 객체지향이 아닌 구조적 라이브러리임을 표방하고 있으나 생성자에 의한 생성 결과물 등의 호출 방법을 표현하기 위해 객체라는 용어를 사용하겠다)가 생성되어 인증이 시작되고, ‘$a->getAuth()’로 인증 여부를 확인해 인증이 성공한 경우 “로그인 인증 성공!”이라는 메시지와 로그아웃할 수 있는 링크가 화면에 나타나게 된다. 로그아웃은 GET 파라미터로 logout=1이라는 값을 전달받은 경우에 수행되고 ‘$a->logout()’과 같이 호출하면 로그아웃이 이루어진다. 당연히 로그아웃할 대상으로 Auth 객체가 필요하므로 ‘$a->logout()’이 호출되기 이전에 ‘$a->start()’가 먼저 호출되어야 하며 로그아웃 이후에 다시 한 번 ‘$a->start()’를 호출해 로그인 초기 정보를 화면에 출력한다.


Auth의 진실 혹은 비밀

아직 PEAR에는 익숙하지 않지만 PHP에 익숙한 독자라면 앞의 코드를 보고 몇 가지 의문을 갖게 될 것이다. $dsn에 데이터베이스 연결에 필요한 정보가 담겨 있는 것은 알겠으나 데이터베이스를 연결하는 구문도 없고 어떤 테이블에서 정보를 읽어와야 하는지, 테이블의 어떤 컬럼이 사용자 이름이고, 어떤 컬럼이 MD5 패스워드인지 등의 정보가 전혀 없이 데이터베이스를 연결한 사용자 인증이 이루어진다는 점이 당연히 의문일 것이다.
먼저 MySQL_connect() 등의 함수 호출을 직접 수행하지 않는 것은 Auth 패키지의 New Auth() 생성자에 의해 앞서 $a에 해당하는 Auth 객체가 생성될 경우 PEAR::DB 패키지를 알아서 사용하게 되기 때문이다. 이 점에서 PEAR를 컴포넌트 또는 라이브러리라고 부르지 않고 프레임워크라 부를 수 있는 근거를 확인한 셈이다. 단순히 인증을 위한 컴포넌트 또는 라이브러리라면 인증과 데이터베이스 연결이라는 명확히 구분되는 독립적인 기능들이 서로를 호출해 사용한다는 것은 이의 독립성을 해치는 결과일 뿐이지만 명확히 프레임워크 내의 동작을 전제로 하는 PEAR의 패키지들은 기능적으로는 독립되어 있더라도 프레임워크 내의 다른 패키지들에 의존 관계를 갖는 것이 당연한 일이며 오히려 이러한 의존 관계들이 더 견고한 프레임워크를 개발자에게 전달하는 기반이 된다 하겠다.
이제 남은 의문은 테이블과 사용자 이름, 패스워드 컬럼을 어떻게 알았을까 하는 것이다. 한마디로 이야기하면 앞서 Auth 패키지의 기본 인증 방법인 MD5() 함수를 사용했듯이 예제를 위해 생성한 auth 테이블과 username, password 컬럼 또한 Auth 패키지의 기본값이라는 점이다. 별도의 테이블 이름 컬럼을 지정하지 않는 한 Auth 객체는 DB 인증을 위해 주어진 데이터베이스 DSN 내에서 auth라는 테이블을 찾고, auth 테이블의 username 컬럼을 사용자 이름으로, password 컬럼을 패스워드로 인식하여 동작하도록 기본값이 지정되어 있는 것이다. 실제로 사용되는 사용자 정보를 담고 있는 테이블의 경우라면 앞의 예처럼 사용자 이름과 패스워드 두 컬럼으로 이루어진 경우는 거의 없을 것이다. 그렇다고 auth의 기본값을 사용하기 위해 기존 테이블의 테이블 이름과 컬럼 이름을 바꾸고 프로그램을 수정할 필요는 없다. 기본값은 어디까지나 기본값일 뿐 기본값이라는 단어 자체가 변경할 수 있다는 의미를 담고 있는 셈이다. 테이블 이름과 컬럼 이름을 입맛에 맞게 바꾸고 싶다면 <리스트 2>와 같이 Array에 DSN, 테이블 이름, 사용자 이름, 컬럼 이름, 패스워드 컬럼 이름을 담아 Auth()의 두 번째 값으로 전달하면 된다.

<리스트 2>
Auth의 기본값 변경
$params = array(
  “dsn” =>”mysql://martin:test@localhost/auth”,
  “table” =>”myAuth”,
  “usernamecol” =>”myUserColumn”,
  “passwordcol” =>”myPasswordColumn”
);
$a = new Auth(“DB”, $params, “loginFunction”);
$a->start();


PEAR::DB 데이터베이스 패키지

업무 관련 프로그램의 30~40%를 차지하는 코드가 데이터 소스를 연결하고 데이터를 처리하는 코드라고 한다. 간단하게는 데이터가 담긴 파일을 읽어들이거나 각종 설정이 담긴 ini 파일 등을 읽는 일에서부터 데이터베이스를 연결해 사용하는 경우 분산 환경에서 소켓, 웹 서비스 등을 통해 스트림 형태의 XML 파일을 처리하는 경우까지, 이런 데이터 소스 연결과 데이터 처리를 위한 코드를 작성하는 수고를 조금이나마 덜 수 있다면 바로 개발 생산성 향상으로 이어질 것이라는 점은 쉽게 상상할 수 있을 것이다. 게다가 데이터 소스 형태가 바뀐다거나 데이터베이스 서버의 IP 설정값이 바뀌었다거나 하는 경우 두 종류 이상의 데이터 소스를 처리해야 한다면 계획적인 교통 정리가 선행된 이후에야 원하는 처리를 수행하는 것이 정답이 될 것이다. 이런 와중에 잘 정의되어 있는, 그리고 데이터 소스에 대해 비교적 독립적인 기능을 구할 수 있다면 사용하지 않을 이유가 없을 것이다.
PEAR의 DB 패키지는 이러한 역할을 수행하는 잘 정의되어 있는 패키지임에 틀림없다. 앞서의 다양한 데이터 소스에 모두를 지원하지는 않지만 적어도 관계형 데이터베이스의 연결과 데이터 처리에 대해서는 일관된 접근 방법을 제공하고 있는 패키지다. PEAR의 DB 패키지는 MySQL 관련 함수를 사용하는 경우와, OCI8 함수를 사용해 오라클을 연결하는 경우에서 나타나는 차이 없이 일관된 방법으로 데이터베이스 연결과 데이터 처리를 가능하게 해준다(DB 패키지가 제공하는 기능을 일일이 설명하기에는 지면이 부족할 것이다. 중요한 기능 중 유용한 기능을 개념적으로 살펴보는 것으로 PEAR::DB를 만나보자).
앞에서 PEAR::DB 패키지를 이용해 데이터베이스에 접근하기 위해서는 DSN이 필요하다는 것을 이야기했다. DSN의 형태는 다음과 같다.

phptype(dbsyntax)://username:password@protocol+hostspec/database?option=value

또는 다음과 같다.

phptype(syntax)://user:pass@protocol(proto_opts)/database(최근 버전의 경우에 지원하는 형태)

여기서 phptype에 해당하는 값이 다양한 데이터 소스의 이름이다. 주요 데이터베이스별로 phptype을 살펴보면 오라클의 경우 oci8, MS SQL·MySQL의 경우는 데이터베이스 이름과 같은 mssql과 mysql, PostgreSQL의 경우는 pgsql, MySQL4.1 이상의 버전과 PHP 5.×를 사용하는 경우라면 mysqli라는 phptype을 사용해 확장된 보안 기능 등을 사용할 수 있다. 사용하는 데이터베이스와 사용하는 방식에 따라 DSN 또한 수없이 많은 형태를 갖게 된다. 필요로 하는 데이터 소스와 접근 방법에 따른 DSN의 형태는 매뉴얼을 참조하길 바란다. MySQL과 관련된 DSN을 생성하는 방법 몇 가지를 적어 봤다.

◆ DSN 변수에 계정, 패스워드, 서버의 IP, 포트와 사용할 데이터베이스를 지정하는 일반적인 방법
   
$dsn = “mysql://mySQL계정:mySQL패스워드@서버IP:포트/데이터베이스명”;

◆ MySQL 소켓 경로를 지정해 직접 연결하는 방법
$dsn=mysql://user@unix(/path/to/socket)/데이터베이스명

◆ DSN의 값들을 Array에 저장해 사용하는 방법(PHP 5.0, MySQL 4.1 이상을 사용한 SSL 연결)
$dsn = array(
    ‘phptype’ =>’mysqli’,
    ‘username’ =>’사용자명’,
    ‘password’ =>’패스워드’,
    ‘hostspec’ =>’서버IP:포트’,
    ‘database’ =>’데이터베이스명’,
    /*
    * SSL 인증에 필요한 값과 인증 방법
    */
    ‘key’ =>’client-key.pem’,
    ‘cert’ =>’client-cert.pem’,
    ‘ca’ =>’cacert.pem’,
    ‘capath’ =>’/path/to/ca/dir’,
    ‘cipher’ =>’AES’,
);


데이터베이스의 연결과 질의 전달, 그리고 페치

이제 DSN이 준비됐으니 데이터베이스를 연결하자. 데이터베이스 연결은 $db =& DB::connect($dsn, $options);와 같이 & DB::connect()에 DSN과 옵션 값을 인자로 전달해 연결을 수행한다(‘&’를 사용해 assign by reference에 의해 참조한다). DSN과 옵션 값 모두 Array 형태로 전달할 수 있다. MySQL을 이용하는 경우라면 데이터베이스 연결 소스와 연결이 실패한 경우의 에러 처리는 <리스트 3>과 같다.

<리스트 3>
MySQL의 연결 예(일부 들여쓰기는 PEAR 코딩 표준과 다를 수 있다)
<?
php
include(“DB.php”);
$dsn = array(
‘phptype’ => “mysql”,
‘username’ => “test2”, // 사용자명
‘password’ => “test2”, // 패스워드
‘hostspec’ => “서버IP:포트”,
‘database’ => “auth”  // 데이터베이스명
);
$db = DB::connect($dsn);

if (DB::isError($db)) {
    // 데이터베이스 연결시 발생하는 오류를 처리한다.
    echo “데이터베이스 연결 과정에서 오류가 발생했습니다.<br>\n”;
    echo “오류 메시지: “ . $db->getMessage() . “<br>\n”;
    echo “오류 상세 설명: “ . $db->getDebugInfo() . “<br>\n”;
}
?>

DB 연결은 ‘$db = DB::connect($dsn);’ 구문에 별도의 옵션 없이 처리된다. 데이터베이스 관련 오류의 처리 과정을 살펴보자. DB::iserror()에 참이 전달된 경우 오류가 발생한 것으로 인식하고 오류 메시지 출력을 처리하는 과정을 거치게 된다. DB::iserror()는 네 가지 형태의 메시지를 처리해 준다. 호출 방법은 ‘$db->get Message()‘와 같이 호출하며 DB_Error::getMessage(), DB_Error::getCode()를 통해 데이터베이스 자체의 오류 메시지와 무관한 PEAR::DB의 에러 메시지와 에러 코드를 처리할 수 있으며 DB_Error::getDebugInfo(), DB_Error::getUserInfo()를 통해 오라클의 ORA-××××와 같은 데이터베이스 관리 시스템의 오류 메시지를 처리할 수 있다. DB_Error의 구성은 Pear::DB의 구성과 같다.
데이터베이스 연결 외의 경우에도 오류 처리를 하는 경우라면 PEAR_Error를 이용해 앞서의 getMessage() 등의 함수를 호출하면 된다. PEAR 프레임워크 내부에 오류 처리에 대해 일관된 표준과 접근 방법이 제공되고 있는 것이다. 오류 발생시 die 구문을 사용하고 싶다면 isError()로 시작하는 조건문 안에 die 구문을 사용하면 될 것이다.
다음은 SQL 질의를 전달하고 결과를 페치(fetch)하는 방법을 살펴보겠다. 데이터베이스를 연결하고 SQL 구문을 전달한 뒤 그 결과를 담고 있는 무엇인가로부터 데이터를 처리하는 과정이라 이야기할 수 있을 것이다. <리스트 3>의 ?> 윗줄에 <리스트 4>의 내용을 추가하면 $rows에 네스트 연관 배열(nest associated array) 형태로 결과 값이 저장된다.

<리스트 4>
SQL 구문의 전달과 결과 값 처리
$sql = “SELECT username, password FROM auth”;
$row = $db->getAll($sql, DB_FETCHMODE_ASSOC);

if (DB::isError($row))
{ // SQL 실행시에 발생하는 오류를 처리한다.
    echo “조회 결과 처리 과정에서 오류가 발생했습니다.<br>\n”;
    echo “오류 메시지: “ . $db->getMessage() . “<br>\n”;
    echo “오류 상세 설명: “ . $db->getDebugInfo() . “<br>\n”;
}

<리스트 4>의 경우 getALL()과 DB_FETCHMODE_ASSOC라는 페치 방법을 사용했다. get*()이나 fetch*()와 같이 페치를 수행하는 구문에서는 fetch mode 옵션을 지정할 수 있다. 크게 세 가지 형태로 기본값인 DB_FETCHMODE_ORDERED는 $row[0], $row[1]과 같이 질의 결과의 순서에 따라 접근할 수 있는 array 형태의 결과가 생성된다. DB_FETCHMODE_ASSOC는 $row[username], $row[password]와 같이 컬럼 이름에 의해 접근이 가능한 연관 배열(associated array) 형태의 결과가 생성된다. DB_FETCHMODE_OBJECT의 경우는 $row->username, $row->password와 같이 접근할 수 있는 객체가 결과로 생성된다. fetch mode의 값은 setFetchMode()를 이용해 지정할 수도 있다.
SQL 구문의 전달과 결과 생성을 동시에 하는 get*()에 해당하는 함수 몇가지를 추가로 살펴보면 앞서 사용한 getAll()은 질의의 결과 전체를, getOne()은 질의 결과의 첫 번째 로우의 첫 번째 컬럼을 결과로 생성하며, getRow()의 경우는 결과의 첫 번째 한 로우를 결과로 생성하며, getAssoc()는 입력받는 인자에 따라 연관 배열 또는 네스트 연관 배열 형태로 결과를 생성한다. SQL 구문의 전달과 페치를 별도로 구분하는 경우는 <리스트 5>와 같이 query()를 사용하고 fetchRow()나 fetchIntp()를 사용한다. 1로우 이상의 결과를 포함하는 경우는 루프 구문에서 반복 처리를 수행한다.

<리스트 5>
SQL 전달과 페치를 구분한 경우
$res =& $db->query(‘SELECT * FROM Table’);
while ($row =& $res->fetchRow())
{
    echo $row[0] . “\n”;
}

데이터 소스 연결과 결과 생성에 대한 이용이 끝난 경우에는 $res->free()와 같이 free()를 사용해 결과(result set)에 대한 릴리즈를 수행하고, 데이터 소스 연결에 대해서는 $db->disconnect()와 같이 disconnect()를 사용해 연결을 끊어준다. 이외에 DB 패키지에는 prepared statement의 사용, 시퀀스, 트랜잭션 처리 기능 등이 포함되어 있으니 데이터베이스와 응용 프로그램의 설계에 따라 필요한 기능 등을 살펴보기 바란다.


SQL 없이 데이터를 처리하는 DB_DataObject

지금까지 간략하게 데이터베이스와 관련된 DB 패키지를 살펴봤다. 데이터베이스와 관련해서는 여러 PHP 자료에서 사용하는 방식대로 SQL문을 작성하고 이를 전달하는 예제를 보았으나 이번에는 굳이 SQL문을 코딩하지 않아도 데이터 처리가 가능한 기능을 제공하는 DB_DataObject 패키지를 만나보겠다.
J2EE 프로그래밍을 하는 경우에 데이터베이스의 테이블 또는 뷰 전체에 대한 접근과 데이터 처리를 하는 엔티티 빈을 생성하거나 GUI 기반의 통합 개발 환경을 제공하는 환경에서는 데이터 소스와 연결되는 데이터 소스 객체 혹은 컴포넌트들을 사용해 테이블에 대한 질의와 Insert, Update 처리를 하기도 한다. 이러한 방식들이 제공하는 장점은 CRUD(Create, Read, Update, Delete)로 대변되는 데이터에 대한 주요 조작이 일정한 형태를 따르는 경우가 대부분이고, 특정 테이블의 이름과 키 값을 알고 Select를 수행한다면 변경된 값을 저장하기 위한 Update 문장이나 신규 입력 값에 따른 Insert 문장은 충분히 자동으로 생성할 수 있다는 점에서 우선적으로 SQL 문을 작성하는 수고를 덜어준다는 점이고, 동일한 테이블에 대한 처리를 수행하는 여러 개의 프로그램을 작성하면서 동일한 작업을 반복하거나 복사와 붙여넣기(copy and paste)를 되풀이하는 수고를 덜어준다는 점, 그리고 이미 검증된 결과물을 사용하는 경우라면 이에 대한 추가적인 테스트 등의 작업이 필요 없다는 점 등을 들 수 있을 것이다.
DB_DataObject 패키지는 DB 패키지의 하부 패키지로, 앞서 설명한 것과 같이 데이터베이스 연결과 테이블의 스키마(schema)와 관련된 정보를 파일 또는 XML 등으로 저장해 두고 필요한 경우 이를 불러다 사용할 수 있는 클래스를 제공해 준다.
J2EE 프로그래밍을 해본 독자라면 XML로 정보를 저장해 두고 이에 근거해 웹 애플리케이션 서버가 엔티티 빈을 생성하게 되는 CMP 빈의 생성 과정과 비슷하다고 생각해도 큰 무리는 없을 것이다. 수작업으로 이러한 스키마를 입맛에 맞게 생성할 수도 있으나 DB_DataObject에 포함된 자동 생성 기능을 이용해 앞서의 auth 데이터베이스에 포함된 테이블들에 대해 스키마와 클래스를 자동 생성하는 과정을 살펴보자.
먼저 <리스트 6>과 같이 데이터베이스와 자동 생성된 스키마와 클래스들이 저장될 경로 등을 지정한 ini 파일을 작성해 PHP 설치 디렉토리 아래의 Pear/DB/DataObject 디렉토리에 저장한다. 앞서의 예제에서 데이터베이스 이름을 auth로 했으므로 auth_DO.ini 파일로 저장하겠다.

리스트 6>
auth_DO.ini 파일의 내용
[DB_DataObject]
database = mysql://mySQL계정:mySQL패스워드@서버IP:포트/auth
schema_location = C:\php4\pear\DataObjects
class_location  = C:\php4\pear\DataObjects
require_prefix  = DataObjects/
class_prefix = DataObjects_


먼저 database의 값으로 DSN을 적어두고 스키마 정보를 담고 있는 ini 파일과 클래스 php 파일이 저장될 경로를 지정한다. require_prefix는 클래스 파일들이 저장될 경로명이고, Class_prefix는 클래스 이름에 사용될 접두어다. 클래스 이름은 ‘접두어+테이블명’ 형태로 생성된다. 위의 네 가지 필수 값 외에 시퀀스, 키 처리, 자동 생성 등과 관련된 추가 옵션이 존재한다.
이제 본격적으로 자동 생성 도구를 이용해 스키마와 클래스를 자동으로 생성하자. 커맨드 창에서 PHP 설치 디렉토리 아래의 Pear/DB/DataObject 디렉토리로 이동하면 createTables.php라는 파일을 찾을 수 있을 것이다. 이 파일을 셸 또는 커맨드 창에서 실행 가능한 php 파일(윈도우의 경우 PHP 설치 디렉토리의 php.exe 파일)을 이용해 실행한다. 다음과 같이 입력하면 <화면 13>처럼 MySQL의 auth 데이터베이스의 모든 테이블에 대해 스키마를 담고 있는 ini 파일과 클래스 php 파일을 생성하게 된다.

C:\PHP설치디렉토리\php.exe createTables.php authDO.ini

<화면 13> create Tables.php를 이용한 스키마와 클래스 생성
사용자 삽입 이미지

자동 생성된 auth.php 클래스 파일의 내용은 <리스트 7>과 같다. DataObjects_라는 접두어와 Auth라는 테이블 이름이 합쳐진 클래스 이름과 테이블의 컬럼 정보 등이 포함되어 있다. 자동 생성된 스키마 파일을 사용하지 않고 클래스 파일 내에 스키마와 관련된 정보를 담을 수도 있다. ‘’의 종료 태그가 없음에 유의한다.

<리스트 7>
auth.php 클래스 파일의 내용
<?
php
/*
 * Table Definition for auth
*/
require_once ‘DB/DataObject.php’;

class DataObjects_Auth extends DB_DataObject
{
    ###START_AUTOCODE
    /* the code below is auto generated do not remove the above tag */
    var $__table = ‘auth’; // table name
    var $username; // string(50) not_null primary_key
    var $password; // string(32) not_null multiple_key

    /* ZE2 compatibility trick*/
    function __clone() {
        return $this;
    }

    /* Static get */
    function staticGet($k,$v=NULL) {
        return DB_DataObject::staticGet(‘DataObjects_Auth’,$k,$v);
    }

    /* the code above is auto generated do not remove the tag below */
    ###END_AUTOCODE
}

자동 생성된 스키마 파일의 내용은 <리스트 8>과 같으며 테이블 이름과 ‘테이블 이름+keys’라는 두 개 영역의 정보를 담고 있다. 테이블 정보의 경우 컬럼 이름을 담고 있으며 컬럼 이름 뒤에 따라오는 숫자 값은 숫자 형태의 컬럼인 경우 1, 문자 형태의 컬럼인 경우는 2, null을 허용하는 컬럼인 경우는 0, not null 컬럼인 경우는 128이라는 값을 합쳐 컬럼의 형태 정보를 가지고 있게 된다. 스키마 파일에 저장된 해당 컬럼의 속성 값이 숫자 129라면 숫자의 1과 not null의 128이 합쳐진 것이며 컬럼의 값이 2이라면 null을 허용한다는 0과 문자형의 2가 합쳐진 것이다.

<리스트 8>
auth.ini 스키마 파일의 내용
[auth]
username = 130
password = 130

[auth__keys]
username = K

이제 웹 서버에 앞서 작성한 auth_DO.ini 파일을 복사한 후 <리스트 9>와 같이 DO_test.php 파일을 만들어 SELECT문 입력 없이 조회가 이루어지는 것을 확인해 보자.

<리스트 9>
DO_test.php 파일의 내용
<?
require ‘DB/DataObject.php’; // DB_DataObject 패키지를 사용한다.
// 웹 서버의 DataObjects 디렉토리에 저장된 auth_DO.ini 파일을 읽어온다.
$config = parse_ini_file(‘DataObjects/auth_DO.ini’,TRUE);
// auth_DO.ini 파일 정보에 따른 작업을 수행한다.
foreach($config as $class=>$values)
{
    $options = &PEAR::getStaticProperty($class,’options’);
    $options = $values;
}
// staticGet을 이용해 클래스에서 키 값(username)이 test2인 로우를 조회해 온다.
$auth_DO = DB_DataObject::staticGet(‘DataObjects_auth’,’test2’);
print_r($auth->password);  // 결과 로우에서 username 컬럼의 값을 출력한다.
?>

웹 브라우저에서 DO_test.php를 호출하면 앞서 테이블에 입력한 ‘test2’라는 텍스트에 MD5 함수를 적용한 결과를 확인할 수 있을 것이다.


DB_DataObject의 다양한 기능

<리스트 9>의 내용은 SELECT * FROM auth WHERE username = ‘test2’라는 SQL문을 실행하고, 그 결과 중 password 컬럼을 출력한 것과 같다. 이외에 복수의 결과 처리를 위한 fetch(), 데이터 입력을 위한 Insert(), 변경을 위한 update(), 삭제를 위한 Delete() 등 데이터 관련 기본 작업을 SQL의 직접 입력 없이 처리할 수 있는 기능이 제공되며, selectAdd()를 이용해 조회해 올 컬럼을 지정하거나 whereAdd()를 통해 조회 조건을 추가할 수도 있다. DB_DataObject의 기본값들을 변경할 수 있는 함수들과 SQL Escape 문자를 처리하는 함수, selectAS(), groupBy(), orderBy() 함수도 제공되니 어지간한 데이터 처리 작업은 따로 SQL 구문을 작성하지 않아도 가능하다고 할 수 있을 것이다.
또 자동 생성되는 클래스를 변경하거나 클래스를 직접 작성한다면 별도의 스키마 없이 클래스를 생성하는 것뿐 아니라 데이터베이스에 저장된 컬럼 형태를 변경하거나 합쳐서 클래스가 생성되게 할 수도 있다. auth_DO.ini 파일에 해당되는 설정 파일에는 두 개 이상의 데이터베이스를 지정할 수 있으며, 동일한 테이블을 다양한 이름으로 별칭을 주어 인식하게 할 수도 있다. 스키마 파일의 경우에도 단순히 테이블 하나에 해당하는 정보뿐 아니라 여러 테이블을 조인한 결과에 해당하는 클래스를 처리할 수 있는 스키마를 생성할 수도 있다. 예제에서는 parse_ini_file()을 이용해 ini 형태의 기본 설정을 사용했지만 XML 패키지를 이용해 이를 대체하는 것도 그리 어려운 일은 아니다.


기능 그 이상의 프레임워크

간략하게나마 Auth, DB, DB_DataObject 패키지를 통해 PEAR를 살펴봤다. 이외에 어떤 패키지들이 있으며 어떤 기능을 하는지를 일일이 설명하는 것은 단순한 나열조차 쉽지 않은 일이고 ‘pear upgrade-all’ 명령어를 입력하면 늘 실망시키지 않고 두세 개 이상의 패키지가 업데이트되는 활발한 프로젝트가 PEAR 프로젝트다.
혹자는 PEAR가 제공하는 기능의 일부분을 보고 이 기능은 내가 자주 사용하는 다른 기능보다 복잡하고 성능도 떨어진다는 이야기를 하기도 한다. 실례로 온라인에서 PEAR::DB와 ADODB를 사용하는 개발자간에 논쟁이 벌어지는 일도 적지 않게 접했던 기억이 있다.
주지해야 할 것은 PEAR가 제공하는 기능의 일부분만을 보고 PEAR를 논한다면 그것은 프레임워크로서의 PEAR에 대한 실례가 될 것이라는 점이다. PEAR::DB의 기능은 데이터베이스 관련 작업을 처리하는 본연의 기능 외에도 확인한 것과 같이 Auth 패키지 등에서도 이를 사용하고 있으며 Auth 패키지 또한 다른 패키지들이 동작하기 위한 필수 조건인 경우가 있다. 이처럼 유기적인 모습으로 패키지와 패키지들이 관계를 가지고 있는 것이 단순 라이브러리나 컴포넌트가 아닌 프레임워크의 모습일 것이다.
프레임워크를 사용하기 위해서는 이를 학습하는 노력을 선행해야 한다는 점을 다시 한 번 강조해 본다. 때로는 프레임워크의 환경을 이해하는 것이 프로그래밍 언어를 하나 익히는 것보다 더 많은 노력을 필요로 할 수 있다는 점을 기억해 두자.

[ PECL 그리고 Imagick ]

PEAR를 이야기하면서 빼놓을 수 없는 것이 PECL이다. PEAR와 PECL 홈페이지를 방문하면 모두 서로를 자매(sister)라고 소개하고 있다(누가 언니이고 누가 동생일지 궁금하다). 본문의 DataObject의 경우도 DB_oo라는 이름으로 PECL 프로젝트에서 PEAR로 이사한 경우다.
PHP 소스를 컴파일할 때 ‘--with-imagick’처럼 옵션을 주거나 PHP 설치 디렉터리의 extensions 디렉터리에 포함되어 있어 php.ini 파일을 수정해 php.ini 파일의 extension 부분에 추가해 PHP의 기능을 확장할 수 있게 해주는 라이브러리들이 PECL의 결과물들이다.
PECL과 비교했을 때 PEAR의 버전이 상대적으로 많이 낮은 패키지가 이미지 처리와 관련된 패키지들이다. PECL에 포함된 Imagick의 경우 버전 1.0을 앞두고 있으나 PEAR의 경우는 범용적인 이미지 처리 기능을 제공하는 안정된 상태의 패키지를 찾기 어려운 상황이다. Imagick의 경우 imagemagick의 라이브러리들을 사용하므로 GD 등으로 직접 작성한 프로그램보다 속도가 늦는 경우는 있으나 이미지와 관련된 기능을 다양하고 체계적으로 제공하고 있다. 매뉴얼에 상세한 설명이 없이 예제를 제공하는 정도이고 한글 지원이 되지 않는다는 단점이 있으나(이 점은 필자가 확인하지 못한 것일 수도 있다. Imagick을 사용해 한글 처리를 성공한 독자가 있다면 정보 공유를 부탁하고 싶다) 막강한 기능과 손쉬운 사용법, 그리고 오픈소스라는 분명 놓치기 아까운 매력을 가지고 있는 터라 간단하게 설치와 사용법을 살펴보고자 한다. 설치 환경은 본문의 환경과 동일하다. 먼저 Imagemagick을 다운로드해 설치한다. Imagemagick은 http://www. imagemagick.org에서 다운로드할 수 있다. 현재 6.1.× 버전이 배포되고 있으나 PHP에서 동작하지 않는 경우가 많으니 이전 버전인 5.5.7 버전의 윈도우 dll 버전을 설치할 것을 권장한다.
Imagemagick이 설치된 디렉터리를 PATH에 추가하고 MAGICK_HOME 환경 변수에도 동일한 디렉터리를 지정한다. 윈도우용으로 컴파일된 PECL 라이브러리들을 배포하는 kromann.info/pecl.php에서 php_imagick.dll 파일을 다운로드해 PHP 설치 디렉터리의 extensions 디렉터리에 저장한다. 추가 extension을 사용하지 않았던 경우라면 php.ini의 extension_dir의 값이 정확히 지정됐는지 확인한다. php.ini 파일의 extension 부분에 ‘extension = php_imagick.dll’이라고 추가한다. 아파치 웹 서버를 다시 시작한다. 이 때 imagick extension 관련 에러가 발생하지 않는다면 정상적인 설치가 이루어진 것이다. <리스트 1, 2>와 같이 imagick_test. html 파일과 iconize.php를 작성한다.

<리스트 1>
imagick_test.html
<html>
<head>
<title>이미지 테스트 페이지</title>
</head>
<body>

<p>
<div align=”CENTER”>

  <img src=”./image.jpg” alt=”[원본 JPEG 포맷]”>
</div>
<div align=”CENTER”>[원본 JPEG 포맷]</div>
<BR>
<div align=”CENTER”>

  <img src=”./iconize.php” alt=”[축소 GIF 포맷, EMBOSS 적용]”>
</div>
<div align=”CENTER”>[축소 GIF 포맷, EMBOSS 적용]</div>
</body>
</html>

<리스트 2>
iconize.php
<?
function ShowError($handle)
{
    // 에러 처리 함수
    $reason = imagick_failedreason( $handle ) ;
    $description = imagick_faileddescription( $handle ) ;
    print “handle failed!<BR>\nReason: $reason<BR>\nDescription: $description<BR>\n” ;
    exit ;
}

$handle = imagick_readimage( getcwd() . “/image.jpg” ) ;
// 이미지를 읽어들인다.
if ( imagick_iserror( $handle ) )
{
    ShowError($handle);
}

if ( !imagick_scale( $handle, 200, 200, “!” ) ) // 이미지 크기를 200x200으로 축소한다.
{
    ShowError($handle);
}

if ( !imagick_emboss( $handle, 1, 1 ) ) // 이미지에 emboss 필터를 적용한다.
{
    ShowError($handle);
}

imagick_convert( $handle, “GIF” ) ; // JPEG 이미지를 GIF 이미지로 변환한다.
if ( !( $image_data = imagick_image2blob( $handle ) ) ) // 이미지를 브라우저에 전달하기 위한 형태로 변경한다.
{
    ShowError($handle);
}

header( “Content-type: “ . imagick_getmimetype( $handle ) ) ;
// GIF mime 헤더를 출력한다.
print $image_data ;
// 이미지 출력(브라우저로 전송)한다.
?>


※ 출처 : 재사용성을 높인 PHP 프레임워크 | 작성자 사랑지기
posted by lepoussin