2005/11/22

Spyce와 함께 하는 파이썬 웹 프로그래밍 2

Spyce와 데이터베이스 프로그래밍

이번호에서는 웹 프로그램에서 땔래야 뗄 수 없는 공생관계를 유지하는 데이터베이스를 파이썬에서 사용하는 방법과 Spyce에서의 데이터베이스 사용에 대해서 소개하고 그 인터페이스를 알아본다.

이상호 search5@gmail.com

현재 리눅스 엔지니어로 일하고 있으며, 웹과 그 기반 기술들에 대해 관심이 많다. 파이썬과 PostgreSQL에도 관심이 많아 항상 이 둘을 어떻게 하면 잘 사용할까 생각한다. 요즘은 동갑내기 여자 가수인 이소은을 쫓아다니며 어떻게 하면 고백할 수 있을까 고민 중이다.

연재순서

1회 2005.10 Spyce를 통한 PSP 프로그래밍
2회 2005.11 Spyce와 데이터베이스 연동
3회 Spyce와 Cheetah를 이용한 템플릿 사용

웹 프로그램이라는 것이 웹의 초기엔 그다지 비중이 크지 않았지만 점차 웹의 한계가 사용자와 대화하는 측면이라는 인식이 널리 퍼졌있었을땐 웹 프로그램도 많이 발전해 있었을 뿐 아니라 그 기반에 데이터라는 요소 자체도 웹 프로그램에선 중요시 되게 되었다. 이러한 웹 프로그램에서 데이터를 활용하기 위해선 데이터베이스라는 개념과 그 개념을 이용한 DBMS를 이용하게 된다. 사실 데이터를 이용하기 위해서 꼭 데이터베이스를 이용해야 하는 것은 아니지만 구조적인 시스템 및 효과적인 데이터 활용을 위해서 웹 프로그램에서의 데이터베이스는 필요한 존재라고 규정할 수 있을 것이다.

전편에서 잠시 언급한 바 있는 여러 파이썬 웹 프로그래밍 방법이나 툴에 대해서 설명한것과 마찬가지로 데이터베이스 또한 같은 상황을 가지고 있다. 하나의 데이터베이스만 사용해야 하는 것은 아니며 선택할 수 있는 DBMS 및 다양한 DB 접근 인터페이스가 존재한다. 일반적으로 DB에 바로 접근해서 사용하는 인터페이스와 윈도우의 ODBC, MS의 ADODB, PHP의 PEAR, Perl 의 DBI 같이 특정 레이어를 사용한 인터페이스가 존재한다.

일반적으로 이식하기 쉽게 하거나 유지 보수를 쉽게 할 수 있는 방법은 DB 에 상관없는 레이어를 사용하는 편이 좋다. 중간에 데이터베이스 DBMS의 버전에 따라 특정 기능이 추가될 경우 사용하고자 하는 DB의 인터페이스의 특정 기능 때문에 다른 데이터베이스로 이전할 경우 그 작업량이 프로그램에 상대적으로 많아질 수 있다는 사실이다. 이러한 특성을 이식성이라고 부른다. 자 이제부터 본격적으로 Spyce와 Database 를 같이 사용하는 방법에 대해 여러분과 함께 알아보도록 한다.

DBMS

우선 Spyce에서 Database 를 사용하기 전에 DBMS에 대해 잠시 알아보도록 한다. DBMS의 어원은 1963년에 시스템 디벨로프사에서 개최한 ‘Development and Management of Computer - Center Data Bases' 심포지엄에서 처음 사용하였으며 데이터베이스의 독립성 개념을 도입한 데이터베이스의 모체는 1963년에 제너널 일렉트릭사에서 개발한 ’Integrated Data Store에서 찾아볼 수 있다. 또한 데이터베이스 표준화 작업은 1965년의 CODASYL의 DBTG에서 시도되었다.

그 후 데이터베이스에 대한 연구가 계속되었는데 1970년 E.F.Codd에 의하여 제안된 데이터베이스 모델인 관계형 데이터베이스가 나타남으로서 데이터베이스 시장엔 데이터베이스 설계 및 운용에 있어 일대 전기를 가져오게 되었다. 데이터베이스 시스템의 모델은 크게 몇 가지가 존재한다. 객체지향 데이터베이스, 망 구조 데이터베이스 시스템, 계층 구조 데이터베이스 시스템, 관계형 데이터베이스가 현재 운용중이거나 나타난 모델이다. 현재 가장 많이 사용하고 있는 시스템은 관계형 데이터베이스로 현존하는 DBMS의 대부분이 사용중이다. 가장 대표적인 DBMS로는 Oracle, MS-SQL, MySQL, PostgreSQL 등이 있다. 이후 1986년 DBMS에 질의하기 위한 언어로 SQL(Structured Query Language)이 표준 언어로 채택되었다.

SQL은 미 표준을 제정하는 ANSI와 국제 표준 기구인 ISO에 의해 SQL92, 93 등으로 표준이 제정되어 있다. SQL이 표준 관계형 데이터베이스 질의 언어이고 많은 관계형 DBMS가 채택하고 있지만 DBMS는 일부 기능구현을 위해 SQL에는 없는 자료형이나 함수를 포함하기도 한다. 그 반대로 SQL 정의엔 구현되어 있지만 DBMS에는 구현되어 있지 않은 것들도 있다. 이러한 것중 일부는 text 자료형이다. 그림 1은 관계형 데이터베이스 중 PostgreSQL의 홈페이지이다.

(picture-postgresql-homepage.png)
<그림1> PostgreSQL 홈페이지

데이터베이스가 가지는 장점

그렇다면 DBMS가 가지는 장점은 어떤 것이 있을까?

① 다중 사용자의 공유 및 보안이 가능하다.
 - 데이터베이스 시스템은 여러 사용자의 동시 접근을 관리하는 동시성 제어(concurrency control)가 가능하므로 같은 내용의 데이터를 여러 사람이 서로 다른 방법으로 동시에 공유하게 할 수 있다(대표적인 예로 웹에서의 데이터베이스 사용을 들 수 있다). 또한 데이터베이스에 대한 접근의 통제 및 개인 정보를 보호하는 보안(security) 등의 다양한 기능이 제공된다.

② 구조적 종속과 데이터 종속을 제거한다.
 - 데이터베이스 파일의 접근이 DBMS를 통해 자동적으로 처리됨으로써, 변경된 파일에 관계된 모든 프로그램을 수정해야 하는 부담에서 벗어날 수 있다. 즉 DBMS가 시스템으로부터 구조적 종속과 데이터 종속을 제거하며, 데이터를 저장하기 위한 효율적인 물리적인 구조를 생성하고 사용자가 입력한 데이터를 물리적 구조에 적합한 데이터로 변환을 한다. 따라서 사용자는 데이터를 항상 논리적 관점에서 볼 수 있고 프로그램 구현이 간단해진다.

③ 효율적인 검색이 가능하다.
 - 데이터베이스에서 데이터의 참조는 수록되어 있는 데이터 레코드들의 주소나 위치에 의해서가 아니라 데이터의 내용, 즉 데이터가 가지고 있는 값에 따라 참조된다. 따라서 SQL(Structured Query Language) 같은 언어를 사용하면 사용자의 질의를 효율적으로 처리하는 질의처리(query processing)가 가능하며, 참조하길 원하는 데이터의 자격 요건을 제시하면 이 조건을 만족하는 모든 레코드들은 하나의 논리적 단위로 취급되고 접근된다.

관계형 데이터베이스 구조

(picture-relation-database-structured.jpg)
<그림2> 관계형 데이터베이스의 구조

그림2에는 관계형 데이터베이스의 구조가 나와 있는데 그 모양은 표와 비슷한 모양을 가지고 있다. 표의 구조가 아련하다면 초중고 시절에 지겹게도 보아왔던 시간표 형태를 생각하면 적당한 모습을 가지고 있다고 생각하면 될것이다.

관계형 데이터베이스에서는 한 개체를 테이블이라 이름짓고 다른 말로는 릴레이션이라고 부르기도 한다. 그림 2에서 가로로 몇 개의 값이 나열되어 묶여 있는 것을 레코드(Record)라 하며 다른 말로 tuple이라 부르기도 한다. 테이블에서는 Row 라고 부른다. 반대로 세로로 나열되어 묶여있는 것을 필드(Field) 라고 하며 테이블에서는 Column이라고 부른다. 그림2에서는 이러한 것을 그림으로 표현하여 보여주고 있다.

테이블에서 레코드를 구분짓는 값으로 어떠한 레코드에서도 겹치지 않고 중복되지 않는 값을 기본 키(Primary Key)라고 한다. 이 기본 키는 각 Record에서 유일무이한 값이며 테이블 정의시에 PRIMARY KEY(FieldName)을 이용해 정의하게 된다. 기본 키(Primary Key)는 테이블 정의 구문에 단 1개만 정의할 수 있다.

mysql은 윈도우와 리눅스에서 데이터베이스와 테이블 생성시 이름에 대해 주의할 필요가 있다. 특수문자를 쓰지 말아야 한다는 것 외에도 대소문자를 구별한다는 것인데, 실제 서버에서 데이터베이스를 개발할 경우 서버의 운영체제가 윈도우인지, 리눅스인지 구분해놓을 필요는 있다. 실험결과 PostgreSQL은 운영체제가 윈도우이거나 리눅스일 경우에도 대소문자는 구분하지 않았다.

그럼 잠깐 테이블 이름과 필드 이름 생성 규칙에 대해 잠시 알아보고 가자. 데이터베이스는 데이터를 넣기 위해서 테이블과 필드를 생성해야 하는데 이것에 대해선 뒤에서 조금 더 알아보기로 하고 지금은 테이블과 필드를 생성할 때 적용되는 다음과 같은 특징이 있다

1. DBMS의 예약어는 사용할 수 없다(SQL문을 구성하는 select, insert, delete 등)
2. 특수문자는 사용할 수 없다
3. 공백 문자를 사용할 수 없다.
4. 테이블과 필드 이름 길이에 64개의 제한이 있다(DBMS에 따라 조금씩 다를 수 있다. 기준은 PostgreSQL)
5. 이름은 숫자와 알파벳, _(언더라인)으로만 구성할 수 있으며 숫자로 시작할 수 없다

그러나 예외적인 법칙은 있다. 바로 1번과 3번, 하이픈에 관한 것인데 테이블과 필드를 생성할 때 더블 쿼트(")로 둘러 쌓여주면 허용이 된다는 것이다. 이렇게 더블 쿼트로 선언된 테이블 또는 필드는 반드시 사용할때도 더블 쿼트(“)로 둘러서 사용을 해주지 않으면 사용할 수 없다.

create table "test-1" (idx int);
select * from "test-1";
create table test1 ("id1-x" int);
select "id1-x" from test1;
create table test1 ("id ref" int);
select "id ref" from test1;
<예제1> 하이픈과 DBMS 예약어 사용

예제1에서 필드 이름에 하이픈을 사용한 select 구문에서는 더블 쿼트(“)를 써야만 하는 이유를 쉽게 유추할 수 있는데, 하이픈은 우선 select 구문에서 수치연산을 하기 때문인데, 만약 다음과 같이 더블 쿼트를 빼고 사용한다면 id1 필드에서 x 필드의 값을 뺀 결과치가 임의 컬럼으로 출력될 것이다.

select id1-x from test1;

?column?
------------
6260
<예제2> 필드 이름에 하이픈을 포함한 select 구문의 결과

예제2에서 사용한 select 구문에서 연산의 종류를 +, / 등으로 변경시켜도 정상적으로 동작하는 것을 확인할 수 있을 것이다.

DBMS를 자유롭게 다루는 마법사. SQL

앞서 대부분의 데이터베이스가 관계형 데이터베이스 모델을 사용하고 질의 언어로 SQL을 사용한다고 하였다. 그렇다면 SQL은 어디까지 DBMS에 대해 힘을 미치고 있을까? 여기에서 규정하는 SQL은 DBMS를 자유롭게 다루는 질의 언어이다.

다시 말해 DBMS를 자유롭게 다루는 열쇠는 SQL이라고 보고 있다. 그렇지만 SQL이 DBMS 자체를 자유롭게 강력하게 다루는 열쇠는 아니다. SQL은 어디까지나 DBMS 내의 데이터를 자유롭게 다루는 언어이기 때문이다.

SQL에는 크게 DDL, DML, DCL로 나뉘는 구문들이 존재하게 된다.

분류 SQL 구문
DDL CREATE, DROP, ALTER 등
DML SELECT, INSERT, DELETE, UPDATE 등
DCL GRANT, REVOKE 등

<표1> DDL, DML, DCL로 나뉘는 구문.

우선 DDL이나 DML이니 단어는 정보처리기사나 산업기사의 데이터베이스 항목에 자주 출현하는 단어들로 본래 뜻은 다음과 같다

DDL : Data Definition Language
 - 데이터베이스, 테이블을 생성할 때 사용하는 언어이다. 이 분류에는 생성과 삭제에 관한 SQL 문, 테이블과 데이터베이스, 유저 생성, 변경 등이 포함된다.

DML : Data Manipulation Language
 - 데이터를 조작할 때 사용하는 언어이다. 이 분류에는 데이터를 조건에 따라 선택하거나 삽입, 삭제 하는 SQL 문이 포함된다.

DCL : Data Control Language
 - 테이블, 필드 등에 관한 접근 권한 등을 제어하는 언어이다. 이 분류에는 테이블 등에 접근권한을 제어하는 SQL 문이 포함된다.

데이터를 조작하기 위해서 DML만을 다룰텐데 지면에서는 DDL과 DCL에 대해서는 다루지 않고 SQL 에 관한 서적을 참조하기 바란다. DML에서는 크게 4가지의 구문이 가장 많이 쓰이는 형태이다.

SELECT .. FROM .. WHERE .. GROUP BY .. ORDER BY

INSERT INTO .. VALUES ..

UPDATE SET .. WHERE

DELETE FROM .. WHERE

표2는 DML 구문에 관한 설명인데, SELECT, INSERT, UPDATE, DELETE 에 대한 설명을 볼 수 있다.

SQL 문 설명
SELECT 하나 또는 그 이상의 테이블로부터 필드를 검색한다.
FROM 필드를 가져오거나 레코드를 삭제할 테이블을 지정한다. SELECT, DELETE 구문에서는 반드시 명시되어야 한다.
WHERE 검색할 열에 대한 조건을 지정한다. 즉, 조건에 맞는 특정한 열만 검색할 경우 사용한다.
GROUP BY 레코드를 그룹으로 나누기 위한 조건을 지정한다.
ORDER BY 레코드를 정렬하기 위한 조건을 명시한다.
INSERT 지정한 테이블에 데이터를 추가한다.
UPDATE 지정한 테이블의 데이터를 갱신한다.
DELETE 지정한 테이블에서 데이터를 삭제한다.

<표2> DML 구문 설명

표2에서는 DML 구문에 관한 설명을 볼 수 있다. DML 구문은 절이라는 단위가 있는데 다음과 같은 SQL 문은 WHERE 절의 내용은 a필드의 값이 1인 요소만 뽑아내라고 말한다.

select * from test1 where a='1';

SELECT 문에는 여러 절이 복합적으로 사용될 수 있는데, 앞에서 언급되지 않는 테이블 조인에 필요한 절도 포함될 수 있다. SELECT 구문에서는 GROUP BY 절과 ORDER BY 절에서 주의해야 할 점이 있는데 ORDER BY 절은 반드시 SELECT 문의 가장 끝에 와야 한다는 점이다. 다른 하나의 주의할 점은 GROUP BY 절 뒤에는 WHERE 절이 올 수 없다. 따라서 조건을 명시하려면 HAVING 절을 사용해야 한다.

레코드를 삭제하는 DELETE 문과 레코드를 업데이트 하는 UPDATE 문은 WHERE 절만이 사용가능하다. 반면에 INSERT INTO 문은 데이터를 삽입하는 문으로서 조건을 명시하는 절이 필요치 않다.

WHERE 절에는 필요한 연산을 수행할 수 있다. 일반적으로 같다면(=), 보다 크다면 (>=), 보다 작다면 (<=), LIKE, 범위 연산인 Between 연산 등을 수행할 수 있다.

이제 SQL에 관해서 몇 가지 예제를 알아보고 이제 파이썬에서 DB를 접근하는 방법에 대해 알아보자. 예제3은 테이블 생성 및 select, insert, into, delete, drop 에 관한 사용방법을 볼 수 있다.

// 테이블 생성
create table books (
idx serial,
title varchar(255),
EditionNumber char(1),
Copyright char(4),
PRIMARY KEY(idx)
);

// 데이터 삽입
insert into books (title, EditionNumber, Copyright) values ('Internet and World Wide Web How to Program', '2', '2002');

insert into books(title, EditionNumber, Copyright) values ('Java How to Program', '4', '2002');

// 데이터 선택
select * from books where EditionNumber='2';

idx |                        title                                                             | EditionNumber  | Copyright
----+----------------------------------------------------------------------+------------------------+--------
1    | Internet and World Wide Web How to Program    |2                              | 2002

// 데이터 수정
update books set Copyright='2004' where title='Internet and World Wide Web How to Program';

// 수정된 데이터 확인
select * from books where EditionNumber='2';

idx |                        title                                                             | EditionNumber  | Copyright
----+----------------------------------------------------------------------+------------------------+--------
1    | Internet and World Wide Web How to Program    |2                              | 2004

// 데이터 삭제
delete from books where EditionNumber='2' and Copyright='2004';

// 데이터 확인
idx | title
----+----
(0 rows)

// 테이블 삭제
drop table books;
<예제3> SQL 사용 예제

파이썬 DB API

파이썬 DB API는 처음에 언급한 바와 같이 크게 2가지 방식이 존재한다. DB 별로 제공되는 개별 인터페이스와 모든 데이터베이스에 공통적으로 적용할 수 있는 인터페이스가 그 것인데, 이 둘은 DB 에 접근해서 사용하는 것과는 같은 노선을 유지하지만 그 문법은 서로 조금씩 다르다.

여기에서는 PostgreSQL DBMS를 기준으로 데이터베이스에 의존적인 코드와, 파이썬 DB API 2.0을 사용한 코드와 비교해볼 것이다.
필자가 파이썬에서 DB를 연결하기 위해서 사용한 환경은 Debian Etch(testing) 버전이며 데이터베이스는 PostgreSQL 7.5를 사용하였다.

파이썬에서 PostgreSQL에 연결하기 위해선 여러 인터페이스가 있지만 그중 현재까지 활발히 개발되고 있는 PyGreSQL을 사용하기로 결정하였다. PyGreSQL은 PostgreSQL이 Postgre95로 개발되고 있었을때 개발된 모듈인데 현재의 모듈 이름은 PostgreSQL의 이름에 맞게 PyGreSQL로 변경되었다.

(picture-pygresql-homepage.png)
<그림3> PyGreSQL 홈페이지

PyGreSQL 홈페이지에 가면 PyGreSQL 을 다운로드 받을 수 있다.

PyGreSQL 설치

PyGreSQL은 Linux 환경에서 RPM과 tar로 설치가 가능하며, 필자의 컴퓨터에선 다음의 명령을 내려 PyGreSQL과 Python, PostgreSQL을 설치하였다.

# apt-get install python python-pygresql postgresql

많은 독자분들의 환경에 맞게 컴파일 환경을 구성해서 보여주어야 하지만 지면상 보여주지 못함에 양해를 바란다.

PyGreSQL 들여다 보기

파이썬에서 DB API는 각 모듈이 개별적으로 제공하게 된다(이는 PHP가 PEAR가 개별 라이브러리를 제어하는 것과 반대이다). 현재 개발되고 있는 파이썬 DB 인터페이스 프로젝트들은 대부분이 Python DB API 2.0을 따라서 개발되고 있다.

PyGreSQL은 크게 3가지의 모듈을 제공한다. 표3에는 모듈에 대한 설명이 나와 있다.

모듈 설명
pg 가장 원시적인 코드로 PHP에서의 pg_connect 등과 데이터베이스에 의존적인 모듈이다.
pg.DB pg의 wrapper로 pg와 같이 데이터베이스에 의존적이다.
pgdb Python DB API 2.0에 맞게 개발된 PyGreSQL 모듈이다.

<표3> PyGreSQL 모듈

단 pgdb 사용시 접속문자열의 경우엔 데이터베이스에 의존적인 인자를 사용하므로 미리 해당 데이터베이스의 접속 문자열을 알아둘 필요가 있다. 이 인자는 데이터베이스 시스템의 C 인터페이스에 보면 나와있다.

PyGreSQL db 모듈

PyGreSQL의 db 모듈은 데이터베이스에 의존적인 모듈인 만큼 PostgreSQL에 의존적인 메소드나 편의성을 위해서 만들어진 메소드가 몇 가지가 존재한다. 표4는 PyGreSQL에서 생성되는 객체에 제공되는 주요 메소드를 설명한 내용이다.

pg_module - pg에서 처음 제공되는 모듈에서 사용할 수 있는 Method
connect Syntax : connect(dbname, host, port, opt, tty, user, passwd)
데이터베이스로 연결을 시도하며 pgobject 객체를 리턴한다. 이때 인자는 필요한 것만 키워드인자로 넘겨줄 수 있다.
close Syntax : close() connect로부터 반환된 pgobject
결과를 닫는다. 객체를 완전히 삭제하려면 del 문을 사용해서 객체를 삭제한다.
pgobject - connect 메소드로부터 리턴되는 객체에서 사용할 수 있는 Method
query Syntax : query(command)
데이터베이스로 SQL문을 질의한다. 리턴값으로 pgqueryobject 객체를 반환한다. 결과 객체에 소켓으로 직접 데이터를 보낼 수 있다.
close Syntax : close()
query 메소드로부터 반환된 pgqueryobject를 닫는다. 객체를 완전히 삭제하려면 del 문을 사용해서 객체를 삭제한다.
inserttable Syntax : inserttable(table, values)
테이블에 데이터를 삽입한다. values에는 table의 필드 정의 순서에 따라 값이 들어 있는 리스트(list) 또는 튜플(tuple)을 넣는다.
pgqueryobject methods - pgobject의 query 메소드로부터 리턴되는 객체의 Method
getresult Syntax : getresult()
질의된 결과를 리스트에 튜플이 포함된 형태로 반환한다.
반환값 예) [(10103, 3843), (10103,3844343), (348248, 438432)]
dictresult Syntax : dictresult()
질의된 결과를 리스트에 사전 형태로 반환한다.
반환값 예) [{'x':3843, 'id1':10103}, {'x':3844343, 'id1':10103}, {'x':438432, 'id1':348248}]
listfields Syntax : listfield()
필드명을 반환한다. 반환값은 튜플이다.
반환값 예) (‘id1', 'x')
fieldname Syntax : fieldname(i)
인자로 받은 i 위치의 필드 이름을 반환한다. 반환값의 형태는 string이다. 필드번호는 0번부터 시작한다.
반환값 예) id1
fieldnum Syntax : fieldnum(name)
인자로 받은 name 의 필드 번호를 반환한다. 반환값의 형태는 numeric이다. 필드번호는 0부터 반환한다.
반환값 예) 0
ntuples Syntax : ntuple()
리턴된 결과의 record 개수를 반환한다.
반환값 예) 3

<표4> pg 모듈의 객체 분류에 따른 메소드

 파이썬은 문법이 함수 기반이 아닌 객체지향 기반인 클래스를 사용하므로 각각의 리턴되는 객체에 따라 사용할 수 있는 메소드가 달라진다. 따라서 표4에서 나타내는 pgobject, pgqueryobject 객체에서 사용할 수 있는 값을 확인해야 한다.

표4에서는 생략했지만 pglargeobject 객체에서 제공되는 메소드에 대한 내용도 따로 나와있다. 그 밖에 여러 사용가능한 메소드가 있지만 지면상 자세한 것은 PyGreSQL에서 배포되는 README.txt 파일을 참조하기 바란다.

다음에 나오는 예제4는 PyGreSQL의 pg 모듈을 사용한 프로그래밍 예제이다.

// pgobject 객체 생성
pool = pg.connect(dbname="leesoeun", host="localhost", user="shlee", passwd="xxx")

// pgqueryobject 객체 생성
result = pg.query("select * from guestHistory")

// getresult 값 추출
getresult = result.getresult()
print getresult

// dictresult 값 추출
dictresult = result.dictresult()
print dictresult

// ntuples 값 추출
print result.ntuples()

// pgqueryobject 객체 close 및 삭제
result.close()
del result

// pgobject 객체 close 및 삭제
pool.close()
del pool
<예제4> pg 모듈을 사용한 프로그래밍 예

예제 4에서 간단하게 pg 모듈을 직접적으로 사용하는 예를 알아보았다. 다음에 알아볼 것은 pg 모듈을 감싼(wrapper) 모듈인 pg.DB 이다. pg.DB는 pg 모듈을 감싸서 최대한 사용자가 쉽게 사용할 수 있도록 만든 모듈이다. 표5에는 pg.DB 모듈의 주요 메소드를 나타내었다.

pg.DB 메소드
pg.DB Syntax : pg.db(dbname, host, port, opt, tty, user, passwd)
표4의 connect 메소드에서 사용된 인자를 동일하게 사용한다. pgobject 객체를 생성한다.
insert Syntax : insert(table, a)
새로운 record를 삽입한다. a는 데이터베이스에 넣을 dictionary 값을 넣어주면 된다.
clear Syntax : clear(table, [a])
인자로 주어진 a가 없으면 테이블의 전체 행의 값을 0으로 만들어 dictionary(사전)으로 반환한다. 수행결과는 데이터베이스에 반영되지 않는다. a는 사전형태의 값이다.
close Syntax : close()
pgobject 객체를 닫는다.

<표5> pg.DB 의 메소드

pg.DB 모듈은 pg 모듈을 감싼 것이라고 잠시 언급한 바 있는데 pg.DB 의 자체 기능 뿐 아니라 기존에 pg에서 사용했던 메소드를 제공함으로서 pg 객체를 그대로 다시 활용이 가능하다. 예) query, getresult, dictresult 등

예제5는 pg.DB 모듈을 사용한 예제를 보여준다. 일부 메소드는 제대로 동작하지 않아 여기서는 생략하였다. 향후 기회가 되면 python 으로 DB 프로그래밍을 할때 다루지 않을까 싶다.

// pgobject 객체 생성
pool = pg.DB(dbname="leesoeun", host="localhost", user="shlee", passwd="xxx")

// 새로운 데이터 삽입
newoid = pool.insert('freeboard', {'title':‘오늘 하루는 어떠셨나요?’, 'contents':'월요일이 끝났네요. 몸은 피곤하지 않으신가요?‘})
print newoid // pgsql에 값을 넣었을때는 oid값을 반환한다.

// 특정 값을 가지는 레코드들을 0으로 초기화 한 값 반환
clearDict = pool.clear('guestHistory', {'date':20051015'}
print clearDict
{'date':0}

// pg.DB close
pool.close()
<예제5> pg.DB 모듈을 사용한 프로그래밍 예제

이제 알아볼 내용은 pgDB 모듈로 파이썬 DB API 2.0을 따르는 메소드를 가진 모듈이다. 이 글의 가장 앞 부분에서 파이썬 DB API를 사용해야 하는 이유를 역설했는데, 이 글을 읽기 위해 학수고대했다면 독자분들에게 이렇게 늦게 진행시켜서 미안한 마음을 전한다 :-

PyGreSQL을 설명하면서 파이썬에서 DB API는 DB 인터페이스에서 제공한다고 언급했는데, DB 인터페이스에 따라 파이썬의 DB API를 제공하지 않을 수 있다. PyGreSQL은 현재 정의되어 있는 파이썬 DB API 2.0 규격을 충실히 따르는 모듈과 메소드를 제공하고 있다.

이 모듈을 사용하기 위해서 파이썬에서 다음과 같은 명령을 내려주어야 한다.
import pgdb

언뜻 보면 앞서 나온 pg.DB와 비슷해 보이지만 모듈명이 pgdb인 것에 주의해서 사용해야 한다. 표6은 pgdb에서 사용 가능한 주요 메소드를 언급하고 있다.

Module Interface
.connect Syntax : connect(dsn, database, host, user, password)
데이터베이스에 접속한다. Connection Objects 를 반환한다. 접속인자는 dsn을 넣을 경우 부분적으로 host, database 등을 뺄 수 있다.
connection objects
.close Syntax : close()
connect 메소드가 반환한 connection objects를 닫는다.
.commit Syntax : commit()
cursor objects의 execute 를 실행한 후 데이터베이스에 최종적으로 커밋한다. 트랜잭션(Transaction)이 지원되는 데이터베이스만 가능하다.
.rollback Syntax : rollback()
cursor objects에서 execute 를 실행한 후 반영사항을 없애고 싶을 경우 이 메소드를 사용할 수 있다. commit 메소드와 같이 트랜잭션(Transaction)이 지원되는 데이터베이스만 가능하다.
.cursor Syntax : cursor()
Cursor Objects를 반환한다. SQL 문의 결과는 Cursor Objects에서만 실행할 수 있다.
cursor objects
.description Syntax : description()
execute에 의해 실행된 select 문의 테이블의 필드의 속성을 반환한다. execute에 의해 select 문이 실행된 다음에 실해야 한다.
반환값 : (필드명, 데이터형 코드, 표시크기, 내부크기, 정확도, 비율(string에선 의미없다), Null 가능 여부)
반환값 예) [('id1', 'int4', None, 4, None, None, None), ('x', 'int4', None, 4, None, None, None)]
.rowcount Syntax : rowcount
execute 의 결과 행 수를 반환한다. execute 메소드에 의해 select 문이 실행된 이후여야 한다.
.close Syntax : close()
Cursor Objects를 닫는다.
.execute Syntax : execute(command)
데이터베이스에 질의를 던지는 메소드이다. select 문을 실행했을 경우 description, fetchone, fetchmany, fetchall 을 사용할 수 있다. 이 메소드는 반환값이 없다.
.fetchone Syntax : fetchone()
execute의 실행결과에서 한 record를 가져온다. 실행할 경우 자동적으로 다음 레코드를 가져올 수 있게 된다.
.fetchmany Syntax : fetchmany([size=cursor.arraysize])
execute의 실행결과에서 다수의 행을 가져온다. 인자에는 숫자를 넘겨주거나 size=1 과 같은 식으로 인자를 지정할 수 있다. 지정하지 않을 경우 Cursor Objects의 arraysize 값을 반환한다.
.fetchall Syntax : fetchall()
execute의 실행결과의 전체 레코드를 가져온다. fetch* 메소드에서 한가지 눈여겨 볼 수 있는 결과는 pg 모듈에서 전체 레코드를 가져올 경우 일부 메소드는 튜플 형태로 반환하거나 사전 형태로 반환하기도 하지만 cursor 객체의 fetch* 메소드는 순수하게 리스트 형태로 값을 반환한다.
.arraysize Syntax : arraysize
fetchmany 메소드에서 사용되며 몇 개의 row를 가져올 것인지 지정한다.

<표6> pgdb의 주요 메소드

표6에서는 다루지 않은 메소드가 몇 가지 있지만 지면상 할애하지 못해 아쉬운 감이 있다. 표6에서 다룬 메소드 중에 fetch* 메소드의 특이한 점이 있는데, 예제 6을 보면 이해가 쉬울 것이다.

// 전체 테이블 내용
10103, 3844323
82838, 7277
949, 8599

// fetchone 실행(커서 객체의 execute가 실행되었다고 가정한다)
print cursor.fetchone()
[10103, 3844323]

// fetchmany 실행(arraysize는 1로 가정)
print cursor.fetchmany()
[[82838, 7277]]

// fetchall 실행
print cursor.fetchall()
[[949, 8599]]

// fetchone 실행
print cursor.fetchone()
[]
<예제6> fetch* 메소드의 실행

예제6에서 모든 결과를 주시할 필요가 있는데, 처음 하나는 값의 출력 순서는 테이블의 출력순서에 따른다. 다른 하나는 한번 fetch* 메소드를 실행하면 이미 출력한 값은 다시 출력할 수 없다는 것이다. 따라서 마지막에 전체 테이블 행이 3개인데, 이미 fetch* 메소드를 3번을 썼으니 마지막 fetchone을 실행하면 결과가 없다고 나오게 되는 것이다. 이 경우 해결방법은 cursor 객체에서 execute 메소드를 다시 한번 실행해주는 것이다. 하지만 웹을 상대로 했을 경우 이미 출력한 값으로 다시 거슬러 올라갈 필요가 있을까?라는 의문엔 없다라고 말하고 싶다.

이로써 파이썬 DB API 2.0를 사용하는 방법까지 모두 알아보았다. 이제 남은 부분은 전 월에서 다루지 못했던 spyce의 pool 객체를 사용하는 방법과 지금까지 배운 파이썬 DB API와, pg 모듈을 이용해서 spyce에서 사용하는 방법을 알아볼 것이다.

Spyce Pool

Spyce에는 Pool이라는 서버 객체가 제공된다. 뭐하는 놈인가 하면 서버측에 자원을 저장해놓은 것이다. 비슷한 개념으로 함수의 실행이 끝난 다음에도 값을 가지고 있는 정적변수를 그 예로 들 수 있다.

Spyce에서 pool은 도구가 제공하는 별도의 모듈인데, 이 모듈을 이용해서 서버측 자원을 이용할 수 있게 된다.

다음의 예제7은 spyce의 pool 을 이용한 프로그래밍 예이다.

[[.import names="pool"]]
<html><body>
  The pool module supports long-lived server-pooled objects,<br>
  useful for database connections, and other variables<br>
  that are expensive to computer.<br>
  [[    if pool.has_key('foo'):
      print 'Pooled object foo EXISTS.'
      pool['foo'] = pool['foo'] + 2
    else:
      pool['foo'] = 1
      print 'Pooled object foo CREATED.'
  ]]
  <br>
  Value: [[=pool['foo'] ]]<br>
</body></html>
<예제7> spyce의 pool을 이용한 예제

예제 7의 결과를 실행해보면 처음엔 그림 4와 같은 실행 결과 화면을 볼 수 있다.

(picture-spyce-pool-created.png)
<그림4> spyce pool 처음 실행결과

예제 7을 pool.spy로 저장후에 실행하면 처음엔 pool 모듈에 created 된 이후에 다시한번 pool.spy를 실행하면 그림5에 보이는 화면을 볼 수 있다.

(picture-spyce-pool-exists.png)
<그림5> spyce pool 2번째 실행결과

프로그램 실행이 만료된 후에도 서버에 자원을 저장하게 되므로 서버측에 저장해야 하는 데이터가 있다면 유용하게 사용할 수 있게 된다.

pool 모듈에서 사용할 수 있는 Operation(연산)이 7개의 연산이 제공되는데 각 Operation은 사전 데이터형에서 제공하는 것과 크게 다르지 않다.

pool 모듈에서 제공하는 연산의 종류
get, set, delete, has_key, keys, values, clear

상황에 따라 pool 모듈을 자유롭게 쓰기 위해서 위의 연산은 필요한 경우가 있을 것이다.

그러나 spyce의 pool에는 아직 치명적으로 생각되는 단점이 있는데, 한 클라이언트에 하나의 pool 이 아닌 여러 클라이언트가 접속했을때 pool은 초기화되지 않는다. 다시 말해 클라이언트에 상관없이 영속성을 지닌다는 것인데, 서버측에 pool은 오로지 하나만 존재할 수 있다는 것이다. 따라서 만약 이것을 데이터베이스의 커서 객체에 응용하려면 현재로선 추천할 수 없다.

그럼 서버측에서 pool는 어떻게 생성될까? 이 질문에 대해서는 리눅스일 경우 /tmp 디렉토리에 보면 spypool.pickle 이라는 파일이 생성되어 있는 걸 확인할 수 있다.

물론 이 파일은 사용자가 열어도 확인할 수 없는 형식으로 되어 있는데, 이미 눈치 빠른 독자라면 알아챘겠지만 이 파일은 파이썬의 피클링으로 인코딩되어 있다. 다시 말해 프로그램이 pool 요청을 받을 때마다 이 피클링된 파일을 계속 요청하게 되는 것이다. 그림6에는 이것에 대한 구조도가 나와있다.

(picture-pool-structured.png)
<그림6> pool의 서버 구조도

현재 pool 의 주 용도는 웹 게시판에 쓸 수 있는 데이터베이스 커서 객체가 아닌 방문자 통계치 등에 더욱 유용하게 사용할 수 있을 것으로 보인다. 향후에 이것에 관한 패치가 나올 경우 웹 게시판에도 무척 유용할 것으로 보인다.

Spyce와 파이썬 DB API 를 사용해서 프로그래밍 하기

앞서서 파이썬의 DB API를 사용해서 데이터베이스에 의존적인 코드와 파이썬 DB API 2.0 규격에 충실한 코드 작성 방법등을 알아보았다. 이제 할 일은 이것을 spyce에서 조합해서 데이터베이스에서 사용하는 방법을 알아볼 것이다.

spyce와 파이썬 DB 인터페이스의 사용

spyce에서 데이터베이스를 사용하기 위해서 가장 먼저 할 일은 모듈을 임포트 하는 일이다. 물론 여기서 모듈은 spyce 모듈이 아닌 파이썬에서의 모듈인데 모듈을 불러오는 방법이 다르므로 쉽게 가려낼 수 있다.

파이썬의 모듈 로드 : [[import pg]]
spyce의 모듈 로드 : [[.import name="pool"]]

파이썬의 모듈을 spyce에서 불러오기 전에 어떤 DB 모듈을 사용할 것인가 결정하는 것은 무척 중요한 일이다. 필자의 경우엔 호환성을 위해서 파이썬 DB API 2.0을 사용하기로 한다. 예제8은 파이썬 DB API 2.0 모듈을 불러와서 DB 에 연결하고 끊는 작업을 나타낸 예제이다.

[[-- PyGreSQL의 DB API 2.0 모듈 로드 --]]
[[import pgdb]]

[[-- 데이터베이스 접속 --]]
[[objConn = pgdb.connect(host="localhost",database="selee" user="shlee",password="xxxx") ]]

[[-- 데이터베이스 연결종료 --]]
[[objConn.close()]]
<예제8> 파이썬 DB API 2.0을 이용해 spyce에서 Connection을 수행한 예제

예제8에서 간단하게 데이터베이스에 접속하고 접속을 종료하는 예를 보였다. 파이썬에서 사용하던 방법과 동일한 문장을 사용하지만 다른게 있다면 문장의 앞뒤에 [[, ]] 로 감싸서 spyce 문장임을 확실히 했다.

다음 보여줄 예제9는 데이터베이스에 있는 내용을 웹에 뿌려주는 역할을 한다.

[[-- PyGreSQL의 DB API 2.0 모듈 로드 --]]
[[import pgdb]]

[[-- 데이터베이스 접속 --]]
[[objConn = pgdb.connect(host="localhost",database="selee" user="shlee",password="xxxx") ]]

[[-- 커서 획득 --]]
[[ cursor = objConn.cursor()]]

[[-- 도서 목록 질의 --]]
[[ cursor.execute("select * from books") ]]

[[-- 도서 목록 출력 --]]
<table>
<tr>
<th>도서명</th>
<th>출판사</th>
</tr>
[[ for title, press in cursor.fetchall(): { ]]
<tr>
<td>[[=title]]</td>
<td>[[=press]]</td>
</tr>
[[ } ]]

[[-- 커서 종료 및 삭제 --]]
[[cursor.close()
del cursor
]]

[[-- 데이터베이스 연결종료 및 삭제--]]
[[objConn.close()
del objConn
]]
<예제9> 데이터베이스 테이블의 내용 출력

예제 9에서는 테이블의 내용을 질의해서 얻어진 내용을 출력하는 스크립트이다. 여기서는 테이블 전체 내용을 가져오는 코드인데, 여기서 실제 테이블을 출력해주는 부분의 코드는 1회에서 다뤘던 람다식을 이용해 예제10과 같은 코드로 만들 수도 있다.

[[-- PyGreSQL의 DB API 2.0 모듈 로드 --]]
[[import pgdb]]
[[-- 데이터베이스 접속 --]]
[[objConn = pgdb.connect(host="localhost",database="selee" user="shlee",password="xxxx") ]]

[[-- 커서 획득 --]]
[[ cursor = objConn.cursor()]]

[[-- 테이블 람다식 --]]
booktable = [[spy! data:
<table>
<tr>
<th>도서명</th>
<th>출판사</th>
</tr>
[[ for title, press in cursor.fetchall(): { ]]
<tr>
<td>[[=title]]</td>
<td>[[=press]]</td>
</tr>
[[ } ]]
]]

[[-- 도서 목록 질의 --]]
[[ cursor.execute("select * from books") ]]

[[-- 도서 목록 출력 --]]
[[ booktable(cursor.fetchall()) ]]

[[-- 커서 종료 및 삭제 --]]
[[cursor.close()
del cursor
]]

[[-- 데이터베이스 연결종료 및 삭제--]]
[[objConn.close()
del objConn
]]
<예제10> 람다식을 이용한 데이터베이스 내용 출력

예제 10은 예제 9와 크게 다른건 없지만 테이블 표현이 람다식으로 빠져있기 때문에 람다식만 별도의 include 파일로 뺄 경우 람다식만 수정해도 템플릿을 쓰는 듯한 효과를 만들어 낼수도 있을 것이다.

그림7은 예제 9와 예10의 결과를 나타낸 화면이다. 두 화면 모두 결과가 동일하기 때문에 한 화면만 첨부한다.

(picture-spyce-database-list.png)
<그림7> 예제9와 예제10을 실행한 화면

지면을 조금 더 할애해서 pg 모듈을 사용해서 데이터를 출력하는 부분까지 다루고 싶지만 pg 모듈에 대한 이야기는 향후 세미나 또는 원고 게재가 있을때 조금 더 다뤄볼 수 있을 것이다.

마치며
많은 지면을 할애해서 파이썬에서의 db api 사용을 통한 간단한 웹 애플리케이션까지 알아보았다. 다음회에서는 이 연재의 마지막인 spyce와 cheetah 템플릿을 통한 사용방법과 도서목록 제작에 대해 알아볼 것이다. 마지막으로 궁금한 사항은 필자에게 언제든지 메일을 보내주면 친절하게 답변할 것을 약속한다.

댓글 없음: