LazarusCompleteGuide:12.1
아키텍처
라자루스 IDE와 LCL은 고유의 데이터베이스 엔진을 제공하지 않을 뿐더러 완전한 데이터베이스 관리 시스템도 제공하지 않는다. 모든 데이터베이스 시스템은 고유의 특성을 지니며, 대부분은 데이터베이스를 생성 및 유지하기 위해 특수화된 툴들이 함께 따라온다. 일반 용도의 툴은 특수화된 기능에 대등할 수가 없다. 따라서 라자루스에서 데이터베이스 애플리케이션을 개발 시에는 이용할 수 있는 네이티브 데이터베이스 툴이 무엇이든 필요할 것이다 (예: MySQL로 작업 시엔 mysqladmin, Firebird로 작업 시엔 FlameRobin).
라자루스와 LCL는 데이터베이스 엔진과 툴이 가진 기능을 중복시키는 대신 가능한 한 많은 데이터베이스 시스템으로 연결하는 데 초점을 둔 데이터베이스 애플리케이션을 생성하도록 두 가지 지원을 제공한다.
데이터베이스 접근
라자루스에서 서로 다른 데이터 소스로 접근은 통합된 데이터베이스 인터페이스를 통해 이루어지는데, 이것은 컴포넌트의 형태로 제공된다. 사용자 인터페이스를 작성하는 데에 이용 가능한 대부분 표준 컨트롤은 그에 상응하는 데이터 인식(data-aware) 버전을 가진다. 데이터 인식 컨트롤은 데이터 인터페이스로부터 데이터를 얻는 방법과, 데이터의 변경 내용을 다시 데이터베이스로 전달하는 방법을 안다.
통합 아키텍처 덕분에 당신은 사용자 인터페이스를 기반이 되는 데이터베이스에 대해 편집이 가능한 데이터베이스 애플리케이션으로 변경하지 않아도 된다. 데이터베이스 계층(layer)은 시각적 컨트롤에 의존하지 않으므로 서비스 또는 웹 애플리케이션에서 사용할 수 있다.
데이터베이스 인터페이스는 순수 오브젝트 파스칼로 작성되므로 완전히 접근이 가능하며, 라자루스는 오픈 소스인지 여부와 상관없이 여러 데이터베이스 타입과 파일 포맷으로 접근하도록 이미 만들어진(ready-made) 컴포넌트를 포함한다: CVS 데이터 (콤마로 구분된 값), DBF 파일, Firebird와 Interbase 데이터베이스, MySQL 데이터베이스 (4.0, 4.1 또는 5 버전), Oracle 데이터베이스, PostGreSQL 데이터베이스, 내장형 SQLite 데이터베이스, 그 외 ODBC 드라이버를 이용할 수 있는 기타 데이터베이스.
이에 더해, 라자루스는 메모리에 데이터를 보관하기 위한 컴포넌트도 제공한다. 오브젝트 파스칼 접근법을 사용 시 이점으로, 데이터를 IDE에서 표시하여 설계 시 검사할 수 있다는 점을 들 수 있겠다. 데이터를 보기 위해 굳이 애플리케이션을 실행시킬 필요가 없는 것이다.
위에 제시한 방법 대안으로, 제3자의 데이터베이스 접근 인터페이스를 들 수 있는데, Advantage Database Server, 다른 데이터베이스로 접근을 위한 ZeosLib 컴포넌트, 기타 UIB의 여러 Firebird/Interbase 컴포넌트를 (주요 컴포넌트가 라자루스 TDataset 클래스를 기반으로 하지 않은) 예로 들 수 있겠다. FBLib은 Firebird/Interbase 데이터베이스로 접근도 제공한다. 이론상으로는 누구나 데이터베이스 접근을 위한 컴포넌트를 개발할 수 있다.
LCL에서 (또는 FCL에서) 데이터베이스 연결을 위해 설계된 컴포넌트들은 두 가지 범주 중 하나에 속한다: 데이터 접근 계층 (TDataset 자손들) 아니면 표현 계층 (다양한 시각적 컴포넌트들, 예: TDBGrid). 두 범주는 TDatasource 인스턴스를 통해 연결되어 있다. 이 컴포넌트는 표현 컴포넌트를 데이터베이스 접근 계층으로 연결한다. 컴포넌트 팔레트의 Data Access 페이지는 데이터베이스로 데이터를 전송하기 위해 (또는 데이터를 그로부터 수신하기 위해) 해당 데이터베이스로 연결하기 위한 일련의 컴포넌트들을 포함한다. 데이터의 소스가 되는 시각적 컴포넌트들이 본 장에서 다루게 될 주제이다.
GUI 데이터베이스 애플리케이션을 개발하기 위해서는 데이터 접근 및 데이터 표현 컴포넌트를 모두 사용할 것이다. 이에 주로 아래의 3가지 (또는 4가지) 단계가 수반된다:
- 데이터베이스 연결 컴포넌트를 폼에 드롭하기. (대부분 단층 파일 데이터베이스에서는 이 단계를 건너뛰어도 좋다.)
- 폼에 하나 또는 이상의 TDataset 자손을 드롭하고 이를 단계 1에서 생성된 데이터베이스 연결 컴포넌트로 연결하기.
- 폼에 단계 2에서 생성된 TDataset 자손마다 TDatasource 컴포넌트를 드롭하고 해당하는 TDataset 자손으로 연결하기.
- 마지막으로, 데이터를 보기 위해 폼에 필요한 수만큼 데이터 인식 컨트롤을 위치시키기. 각각 단계 3에서 생성된 TDatabase 인스턴스로 연결되어야 한다.
첫 번째 단계는 폼에 데이터베이스 연결을 드롭하는 것이다. 이 때 다음과 같은 질문이 발생한다: 어떤 데이터베이스를 사용해야 하는가? 이 주제를 다음 절에서 논하겠다.
데이터베이스 엔진의 사용
애플리케이션이 데이터를 (또는 무엇이든) 저장해야 할 때마다 프로그래머는 어떤 포맷으로 저장할 것인지 결정해야 한다. 커스텀 포맷 데이터 파일을 이용하거나 기존 데이터베이스 엔진을 이용하여 데이터를 읽고 쓸 수 있다. 오브젝트 파스칼은 매우 유연한 파일 읽기 및 쓰기 루틴을 가지므로 포맷이 무엇이든 파일로 저장된 데이터를 읽고 쓰기가 쉽다. 따라서 커스텀 루틴을 이용해 일반 파일로 데이터를 쓰는 것이 그럴 듯해 보인다. 사실상 많은 사례에서 이 방법을 선호한다.
하지만 데이터양이 많거나 다른 애플리케이션과 데이터를 공유하는 경우, 다른 애플리케이션이 파일 읽기-쓰기 코드를 중복하도록 강요하므로 이 방법은 그다지 좋지 못한 선택이다. 두 개의 애플리케이션이 동일한 데이터로 동시에 접근을 시도 시 동시 실행 문제가 발생할 수도 있다. 이런 경우 데이터를 관리하기 위한 데이터베이스 엔진과 같은 것을 사용하는 편이 낫다.
데이터베이스 엔진에는 여러 종류가 있다. 그것이 관리하는 데이터를 저장하는 방법에 따라 엔진에 차이가 있다. 어떤 것은 단순한 단층 파일(flat file)로 저장하는 반면 어떤 것은 큰 데이터베이스 파일 혹은 심지어 전체 디스크로 저장하기도 한다. 일반적으로 데이터는 데이터베이스 엔진 전용 포맷으로 작성된다.
데이터베이스 엔진은 다양한 유형의 정보를 저장할 수 있다. 일부 데이터베이스는 매우 자유로운 포맷을 허용하여서 데이터의 다양한 항목들이 매우 다른 구조를 가질 수 있다. 다양하게 구조화된 데이터를 허용하는 데이터베이스의 예로 XML과 같은 파일, .INI 파일, 혹은 윈도우 레지스트리(Windows Registry)를 예로 들 수 있다. 이들은 주로 구성 데이터(상당히 다르게 구조화된 데이터 유형을 포함한)를 저장하는 데에 사용된다.
판연히 다른 타입의 엔진은 전적으로 테이블로 나타낸 데이터만 저장할 것이다. 각 테이블은 동일하게 구조화된 항목만 포함한다. 각 항목을 레코드라고 부르는데, 각 레코드에 다양한 엔트리를 필드라고 부른다. 그러한 시스템에서 다르게 구조화된 레코드를 저장하기 위해서는 서로 다른 테이블의 생성을 필요로 한다. 측정 데이터, 주소, 사용자 리스트, 좀 더 복잡한 데이터의 보관이 이러한 시스템에 일반적으로 사용된다. 그러한 테이블을 모두 모아 데이터베이스라고 부른다. 데이터베이스 내 다양한 테이블들은 서로 연관될 수 있다. 예를 들어, 송장(invoice)을 포함하는 테이블을 고객 데이터를 포함한 테이블로 연결함으로써 고객별 송장 리스트를 쉽게 검색하고, 고객이 특별한 송장을 가진 경우 고객 테이블에서 고객을 삭제 시 경고를 표시할 수 있다. 이 모든 것은 RDBMS (관계형 데이터베이스 관리 시스템) 이론에서 설명되는데, 사실상 이는 수학적으로 설명된 모델이다.
상업용 RDBMS 엔진으로는 Oracle, MS-SQL 서버, DB2를 비롯해 Firebird, PostGreSQL, MySQL과 같은 오픈 소스용 엔진도 있다. RDBMS 엔진은 표로 된 데이터를 저장하는 것 이상의 기능을 가진다. 이들은 여러 테이블에서 다양한 정보를 한 번에 모으는 기능을 가진다. 테이블을 개별적으로 접근할 필요가 없는 것이, 크로스 테이블 쿼리가 RDBMS의 핵심 부분이기 때문이다.
특정 데이터를 재빨리 위치시키는 것을 가능하게 하려면 RDBMS가 테이블에 색인을 생성해야 한다. 인덱스는 색인 테이블(lookup table)이다. 일련의 키(key)와, 테이블에서 해당 행에 대한 포인터를 포함한다ㅡ예를 들어 Invoices 테이블에서는 송장 번호가 색인이 될 가능성이 높다.
RDBMS는 송장 번호 필드에서 색인을 생성할 수 있는데, 이는 Invoices 테이블에서 송장 번호의 정렬된 리스트와 함께 해당 송장 레코드의 위치를 유지할 수 있음을 의미한다. 특정 송장의 레코드를 검색 시 RDBMS는 그 송장 번호에 대한 색인을 살펴보고ㅡ번호가 정렬되어 있으므로 빠른 동작을 야기ㅡ번호를 찾으면, Invoices 테이블에서 위치 정보를 이용해 송장 레코드를 재빨리 검색할 것이다. 색인이 없는 경우 Invoices 테이블의 모든 레코드를 스캔해야 하며, 올바른 레코드를 발견할 때까지 그 송장 번호 필드를 검사할 것이다 (큰 Invoices 파일의 경우 매우 느릴 가능성이 있음). 일부 단층 파일 시스템은 색인도 지원하고 그와 연관된 색인 파일도 가질 수 있다 (일례를 들자면 dBase 파일이 되겠다). 그러한 색인 파일들은 특정 순서로 레코드를 빠르게 표시하도록 해준다.
데이터를 저장하는 것 외에도 RDBMS는 데이터를 검색 및 수정할 수 있도록, 데이터베이스에 저장되고 데이터베이스 엔진에 의해 실행되는 프로시저를 작성하도록 해준다. 이런 경우 엄청나게 속도가 증가하는데, 데이터베이스에서 애플리케이션으로 데이터를 전송하고, 수정하며, 수정내용을 다시 데이터베이스로 전송하는 작업은 시간이 많이 소요되는 연산이기 때문이다. 저장된 프로시저는 이렇게 시간이 많이 소요되는 데이터를 전송할 필요가 없도록 설계되었다.
뿐만 아니라 RDBMS는 복잡한 관리 루틴을 제공할 것이다. 애플리케이션은 변화하고, 가끔은 추가 데이터를 저장해야 하는데, 배송 주소(shipping address)와 같은 데이터를 예로 들 수 있겠다. 단층 파일 내에 있는 데이터의 경우, 배송 주소를 위한 추가 기억 공간을 단층 파일로 추가하기 위해 변환 루틴(conversion routine)과 같은 것을 작성함을 의미한다. RDBMS에는 이를 위한 기능이 내장되어 있다.
모든 RDBMS는 공통된 인터페이스를 제공하여 당신이 데이터와 데이터의 구조를 조작하도록 해준다. RDBMS는 모두 SQL라 (구조화 질의어) 불리는 언어를 사용한다. 이 언어를 사용한다는 것은 이론상 하나의 엔진으로부터 다른 엔진으로 데이터를 전송할 수 있으며, 데이터의 검색과 조작에 SQL를 사용하는 한 애플리케이션의 내용은 거의 변경되지 않아야 함을 의미한다. 이러한 점 때문에 프로그래머들이 XML이나 레지스트리와 같은 메커니즘과 비교할 때 RDBMS를 더 매력적으로 생각한다.
사실상 RDBMS들 간 SQL 호환성은 그다지 신뢰성이 없다. 이러한 엔진들의 통신 인터페이스와 프로그래밍 모델에는 엄청난 차이가 있다. 각각은 데이터베이스 내 데이터로 접근하기 위해 API를 (애플리케이션 프로그래밍 인터페이스) 제공한다. 이는 주로 SQL 문을 실행하고 결과를 (해당 시) 검색할 때 필요한 루틴을 포함하는 라이브러리가 (DLL) 된다.
이러한 루틴들은 엔진마다 다르다.
일부 데이터베이스 메커니즘의 경우 엔진에 공유 라이브러리밖에 없다ㅡ전체 데이터베이스 엔진은 공유 라이브러리에 포함되므로 애플리케이션의 일부가 된다. 엔진은 애플리케이션에 내장된다(embedded).
그 외 데이터베이스 메커니즘의 경우, 공유 라이브러리는 실제 데이터베이스를 관리하면서 구분되어 실행되는 프로세스인 외부 데이터베이스 엔진에 대한 통신 진입지점(entry point)만 포함한다.
내장된 접근법으로, MS Access, Advantage Database System, SQLite, 심지어 Berkeley DB를 예로 들 수 있다. 그 외 RDBMS는 구분된 프로세스가 데이터베이스를 (어쩌면 다른 컴퓨터에) 실행하고 관리하는 후자 접근법을 사용하며, 이 프로세스와 통신에는 DLL (클라이언트 라이브러리)가 필요하다.
Firebird와 같은 일부 시스템은 하이브리드 접근법을 제공하는데, 내장된 형태와 클라이언트/서버 옵션을 모두 제공한다.
구분된 프로세스가 데이터베이스를 관리하도록 할 경우, 애플리케이션이 실행되어야 하는 PC에 더 많은 처리 능력을 필요로 한다는 것은 더 이상 말할 필요가 없다. 필요한 서버도 설치되어야 있어야 하며 (일반 사용자에겐 버거운 작업일지 모른다), 데이터베이스 계층이 애플리케이션에 내장될 때보다 더 많은 관리를 요할 것이다.
다양한 데이터베이스 엔진에 대해 서로 다른 인터페이스를 사용 시 문제를 해결하기 위해 ODBC(오픈 데이터베이스 연속성) 시스템이 개발되었다. 이 시스템은 하나의 잘 정의된 API를 제공하는데, 어떤 프로그래밍 언어에서든 애플리케이션이 모든 데이터베이스로 연결할 수 있도록 허용한다. ODBC 시스템은 드라이버 라이브러리를 통해 다양한 데이터베이스로 연결에 관한 세부내용을 처리한다.
속도가 문제가 아닌 경우거나 접근해야 하는 특정 데이터베이스에 드라이버를 이용할 수 있는 경우 효과가 뛰어날 것이다. ODBC는 이용 가능한 플랫폼 대부분에 존재하므로 프리 파스칼이나 라자루스에서 데이터베이스 접근 계층으로 사용 시 이상적이다.
프리 파스칼은 데이터로 접근 시 ODBC에 의존하는 대신 자체적으로 추상 계층을 구현하여 (델파이에서 볼 수 있듯) 테이블로 된 모든 데이터가 TDataset 클래스로 표현된다. TDataset 클래스를 사용한다는 것은 관리되는 데이터가 통일된 구조를 가진다고 가정한다. 프로그래머는 자신의 데이터를 모두 동일한 형태를 가진 레코드로 구조화할 수 있어야 하는데, 동일한 필드 집합을 가진 레코드를 예로 들 수 있겠다. 즉, 데이터가 도표 형식으로 되어 있고, 행(레코드)과 열(필드)로 구성되어 있다는 말이다. TDataset 클래스는 데이터를 검색하고, 다양한 열(필드)에서 값을 검색하며, 그 값을 변경하도록 해주는 루틴 집합을 제공한다.
TDataset 컴포넌트에 의해 관리되는 데이터의 기원은 무관하다ㅡ이는 실제 TDataset 자손들에게만 알려진다. 구조화된 단층 파일의 내용이거나, SQL 쿼리의 결과, 아니면 간단한 인 메모리 데이터가 될 수도 있다.
실제로 라자루스/FCL은 이미 각 유형의 데이터베이스 엔진에 대해 자손 클래스를 제공한다. 예를 들어, TDBF는 TDataset 자손으로서, dBase 파일로부터 데이터를 처리할 수 있다. TSQLiteDataset 은 SQLite 데이터베이스의 데이터를 작업하도록 해주는 TDataset 자손이다. 두 엔진 모두 TDataset의 자손이므로, 동일한 메소드 집합을 이용해 데이터를 검색 또는 수정함을 의미한다.
그러한 자손들 대다수는 프리 파스칼 팀에 의해 구현되었고, 모든 공식 발매판마다 포함되어 있다.
TDataset 컴포넌트가 정의된 DB 유닛은 쿼리를 위한 파라미터와(TParameter) 색인 정의를 (TIndexDef) 설명하기 위한 클래스도 포함한다. 이러한 클래스들은 쿼리의 파라미터 혹은 테이블의 색인을 설명하는 기능 외에는 어떠한 기능도 없다. 추가 기능은 도입하지 않았다.
데이터베이스 데스크톱 애플리케이션
라자루스에는 데이터베이스로의 접근에 필요한 컴포넌트 외에도 Lazarus Database Desktop 이라 불리는 애플리케이션이 포함되어 있는데, 이는 아래의 경우에 사용된다:
- 라자루스가 지원하는 모든 데이터베이스로 접근하기.
- SQL 기반의 데이터베이스에 SQL 쿼리를 실행하고 결과 보기.
- 데이터베이스 생성하기.
- 테이블에 공통된 SQL 문 생성하기.
- 애플리케이션 내 데이터세트에 적용할 수 있는 데이터 사전을 생성하고 관리하기.
- 데이터를 다른 포맷으로 내보내기.
- 데이터베이스 구조를 기반으로 객체 지향 코딩을 위한 코드 생성하기.
데이터베이스 데스크톱(Database Desktop)은 데이터베이스 관리 시스템은 아니지만 일반 작업은 실행 가능하며, 데이터베이스 개발에 도움을 준다. 이는 데이터베이스로 접근을 위한 LCL 메커니즘과 데이터베이스를 모두 이해하므로 라자루스 IDE에서 코드를 생성 시 사용할 수 있다. 이와 관련된 내용은 12.4절에서 상세히 다루겠다.