레코드 수집(JavaScript) 음악 모음을 정렬하는 간단한 데이터베이스 프로그램을 만듭니다 그놈 문서 프로젝트 gnome-doc-list@gnome.org Johannes Schmid jhs@gnome.org Marta Maria Casetti mmcasettii@gmail.com 2013 조성호 shcho@gnome.org 2017 레코드 수집

이 지침서를 통해 다음을 배웁니다:

libgda로 데이터베이스 연결하기

데이터베이스 테이블로의 레코드 삽입 및 테이블 탐색

도입부

이 시연 프로그램에서는 Javascript 언어를 활용합니다. GTK 프로그램에서 GDA(그놈 데이터 액세스) 라이브러리로 데이터베이스를 연결하고 활용하는 방법을 보여드리겠습니다. 고로, 이 라이브러리도 설치해야합니다.

그놈 데이터 액세스(GDA)는 다양한 종류와 형식의 데이터 공급원에 통합 방식으로 접근하는 수단을 제공합니다. 전통적인 관계형 데이터베이스 시스템에서 시작하여 메일 서버, LDAP 디렉터리 등과 같은 상상할 수 있는 데이터 공급원을 다룹니다. 더 많은 내용, 전체 API, 문서를 확인해보시려면 GDA 웹사이트를 방문해보십시오.

비록 코드 상당 부분이 사용자 인터페이스(GUI)와 관련 있지만, 데이터베이스 부분의 따라하기 지침에 역점을 두었습니다(아마도 관련이 있는 다른 부분에도 언급할지도 모릅니다). 그놈 Javascript 프로그래밍을 더 알아보려면 그림 보기 프로그램 따라하기 지침 부분을 참고하십시오.

안주타에서 프로젝트 만들기

코딩을 시작하기 전에 안주타에서 새 프로젝트를 설정해야합니다. 이 프로그램은 빌드에 필요한 모든 파일을 만들고 그 다음 코드를 실행합니다. 또한 이 모든 상태를 유지 관리하는데 쓸만합니다.

안주타를 시작하고 파일새로 만들기프로젝트 를 눌러 프로젝트 마법사를 여십시오.

JS 탭에서 일반 Javascript를 선택하고, 앞으로을 누른 다음, 나타난 페이지에서 몇가지 자세한 내용을 입력하십시오. 프로젝트 이름과 디렉터리에 record-collection을 입력하십시오.

마침을 누르면 프로젝트를 만들어줍니다. 프로젝트파일탭에서 src/main.js 파일을 여십시오. 매우 기본적인 예제 코드가 있습니다.

프로그램 구조

이 시연 프로그램은 데이터베이스 테이브의 모든 레코드를 탐색할 뿐만 아니라 레코드를 넣는 기능도 있는 간단한(단일 창) GTK 프로그램입니다. 테이블에는 정수형 값을 넣는 id 필드와 가변 문자열(varchar) 형식의 name 필드가 있습니다. 프로그램의 (상단) 처음 섹션에서는 테이블에 레코드를 넣을 수 있습니다. 프로그램의 마지막 섹션(바닥)에서는 테이블의 모든 레코드를 살펴볼 수 있습니다. 이 내용은 새 레코드를 넣을때와 프로그램을 시작할 때마다 새로 고쳐서 보여줍니다.

재미로 시작하기

프로그램 뼈대 검토를 우선 시작하겠습니다:

const GLib = imports.gi.GLib; const Gtk = imports.gi.Gtk; const Gda = imports.gi.Gda; const Lang = imports.lang; function Demo () { this._init (); } Demo.prototype = { _init: function () { this.setupWindow (); this.setupDatabase (); this.selectData (); } } Gtk.init (null, null); var demo = new Demo (); Gtk.main ();

1~4번째 줄: 초기 임포팅입니다. JavaScript로 GDA 라이브러리를 가져오는 이 따라하기 지침의 핵심부분인 세번째 줄에 특히 주목하십시오.

6~17번째 줄: Demo 클래스를 정의합니다. 전체 작업을 담당하는 메서드 3개를 호출하는 13~15번째 줄에 주목하십시오. 자세한 내용은 아래에서 설명하겠습니다.

19~23번째 줄: 프로그램을 시작합니다.

프로그램 설계

setupWindow 메서드를 살펴보겠습니다. 이 메서드는 사용자 인터페이스(UI)를 만드는 역할을 합니다. UI는 중요한 부분이 아니니 관련 부분만 설명하겠습니다.

setupWindow: function () { this.window = new Gtk.Window ({title: "Data Access Demo", height_request: 350}); this.window.connect ("delete-event", function () { Gtk.main_quit(); return true; }); // main box var main_box = new Gtk.Box ({orientation: Gtk.Orientation.VERTICAL, spacing: 5}); this.window.add (main_box); // first label var info1 = new Gtk.Label ({label: "<b>Insert a record</b>", xalign: 0, use_markup: true}); main_box.pack_start (info1, false, false, 5); // "insert a record" horizontal box var insert_box = new Gtk.Box ({orientation: Gtk.Orientation.HORIZONTAL, spacing: 5}); main_box.pack_start (insert_box, false, false, 5); // ID field insert_box.pack_start (new Gtk.Label ({label: "ID:"}), false, false, 5); this.id_entry = new Gtk.Entry (); insert_box.pack_start (this.id_entry, false, false, 5); // Name field insert_box.pack_start (new Gtk.Label ({label: "Name:"}), false, false, 5); this.name_entry = new Gtk.Entry ({activates_default: true}); insert_box.pack_start (this.name_entry, true, true, 5); // Insert button var insert_button = new Gtk.Button ({label: "Insert", can_default: true}); insert_button.connect ("clicked", Lang.bind (this, this._insertClicked)); insert_box.pack_start (insert_button, false, false, 5); insert_button.grab_default (); // Browse textview var info2 = new Gtk.Label ({label: "<b>Browse the table</b>", xalign: 0, use_markup: true}); main_box.pack_start (info2, false, false, 5); this.text = new Gtk.TextView ({editable: false}); var sw = new Gtk.ScrolledWindow ({shadow_type:Gtk.ShadowType.IN}); sw.add (this.text); main_box.pack_start (sw, true, true, 5); this.count_label = new Gtk.Label ({label: "", xalign: 0, use_markup: true}); main_box.pack_start (this.count_label, false, false, 0); this.window.show_all (); },

22번째, 27번째 줄: 어떤 사용자가 데이터베이스에 들어간 내용을 입력하는 항목 2개(필드 2개)를 만듭니다.

31~34번째 줄: 삽입 단추를 만듭니다. clicked 시그널을 클래스의 _insertClicked private 메서드에 연결했습니다. 이 메서드는 아래에서 자세히 설명하겠습니다.

39번째 줄: 테이블의 내용을 보여줄 위젯(TextView)을 만듭니다.

44번째 줄: 테이블의 여러 레코드를 보여줄 레이블을 만듭니다. 초기에는 비어있습니다만, 나중에 업데이트합니다.

데이터베이스 연결 및 초기화

setupDatabase 메서드에서 데이터베이스를 연결하는 코드는 아래와 같습니다:

setupDatabase: function () { this.connection = new Gda.Connection ({provider: Gda.Config.get_provider("SQLite"), cnc_string:"DB_DIR=" + GLib.get_home_dir () + ";DB_NAME=gnome_demo"}); this.connection.open (); try { var dm = this.connection.execute_select_command ("select * from demo"); } catch (e) { this.connection.execute_non_select_command ("create table demo (id integer, name varchar(100))"); } },

2~3번쨰 줄: GDA의 Connection 객체를 만듭니다. 생성자에 속성 몇가지를 넣어야합니다:

provider: GDA에서 지원하는 프로파이더 중 하나입니다. GDA에서는 SQLite, MySQL, PostSQL, Oracle 등 수많은 데이터베이스 엔진을 지원합니다. 시연 목적으로, 대부분의 배포판에서 기본적으로 SQLite를 설치하며 쓰기 쉽기 때문에(파일을 데이터베이스로 활용) SQLite 데이터베이스를 사용하겠습니다.

cnc_string: 연결 문자열. 프로바이더를 바꿉니다. SQLite용 문법은 DB_DIR=PATH;DB_NAME=FILENAME 입니다. 시연 프로그램에서는 사용자 주 디렉터리(GLib의 get_home_dir 함수 호출 참고)에 둔 gnome_demo 데이터베이스에 접근하겠습니다.

GDA에서 지원하지 않는 프로바이더가 있고, 연결 문자열에 어떤 구성 요소가 빠지면, 2번째 줄에 예외가 일어납니다. 따라서 실제로 JavaScript의 구문 try...catch로 처리해야합니다.

4번째 줄: 연결합니다. SQLite 프로바이더에서 데이터베이스가 없으면 이 단계에서 만듭니다.

6~10번째 줄: 테이블이 있는지(7번째 줄) 간단하게 SELECT 문을 시도해봅니다. (데이터베이스를 방금 만들어서)테이블이 없으면, 이 명령에서 try...catch 블록에서 처리할 예외가 나타납니다. 이 경우 CREATE TABLE 구문을 실행합니다(9번째 줄).

GDA 연결용 execute_select_command 메서드와 execute_non_select_command 메서드로 위 SQL 명령을 실행합니다. 사용법은 단순하며 Connection 객체와 해석할 SQL 명령을 두 매개변수 값으로 활용하므로 단순합니다.

이 시점에 데이터베이스를 설정했고 사용할 준비가 끝났습니다.

선택

데이터베이스를 연결하고 나면, 데모 생성자에서 selectData 메서드를 호출합니다. 이 메서드는 테이블의 모든 레코드를 가져와서 TextView 위젯에 나타냅니다. 이제 살펴보도록 하겠습니다:

selectData: function () { var dm = this.connection.execute_select_command ("select * from demo order by 1, 2"); var iter = dm.create_iter (); var text = ""; while (iter.move_next ()) { var id_field = Gda.value_stringify (iter.get_value_at (0)); var name_field = Gda.value_stringify (iter.get_value_at (1)); text += id_field + "\t=>\t" + name_field + '\n'; } this.text.buffer.text = text; this.count_label.label = "<i>" + dm.get_n_rows () + " record(s)</i>"; },

2번째 줄: SELECT 명령. GDA 연결의 execute_select_command 메서드를 이 목적으로 활용합니다. 나중에 레코드 행을 가져올 때 사용할 DataModel을 반환합니다.

3번째 줄: DataModel의 여러 레코드를 순회할 때 활용할 Iter 객체를 만듭니다.

7번째 줄: 레코드 전체를 반복문으로 돌고 Iter 객체의 도움으로 가져옵니다. 여기서 iter 변수에는 실제로 가져온 데이터가 들어갑니다. move_next 메서드에서 마지막 레코드에 도달하면 false를 반환합니다.

8~9번째 줄: 다음 두가지 동작을 각 줄 별로 행합니다:

하나의 인자만 필요한 Iterget_value_at 메서드를 활용합니다. 이 인자에는 가져올 열 번호가 들어가며 0번 부터 시작합니다. SELECT 명령에서 열 두개만 반환하므로 0번과 1번 열만 가져옵니다.

get_value_at 메서드에서 GLib의 GValue 형식 필드를 반환합니다. 이 형식을 문자열로 변환하는 가장 쉬운 방법은 GDA의 전역 함수 value_stringify를 사용하면 됩니다. 이게 여기서 우리가 처리하려는 일이며, id_field, name_field 변수에 결과를 저장합니다.

11번째 줄: 두 필드 문자열을 하나로 합치며, 사이에 "=>" 문자열을 넣고 text변수에 저장합니다.

14번째 줄: 반복문 실행이 끝나면 text 변수에 형식을 갖추어 저장한 모든 레코드를 확보합니다. 이 줄에서는 해당 변수를 TextView 의 내용으로 설정합니다.

15번째 줄: DataModelget_n_rows 메서드를 활용하여 테이블의 수많은 레코드를 표시합니다.

삽입

좋습니다. 데이터베이스에 연결하고 테이블에서 행을 선택하는 방법을 알았습니다. 이제 테이블에 대해 INSERT 구문을 수행할 시간입니다. setupWindow 메서드에서 Insert 단추의 clicked 시그널을 _insertClicked 메서드에 연결한 부분을 기억하시죠? 이 메서드의 구현부를 살펴보겠습니다.

_insertClicked: function () { if (!this._validateFields ()) return; // Gda.execute_non_select_command (this.connection, // "insert into demo values ('" + this.id_entry.text + "', '" + this.name_entry.text + "')"); var b = new Gda.SqlBuilder ({stmt_type:Gda.SqlStatementType.INSERT}); b.set_table ("demo"); b.add_field_value_as_gvalue ("id", this.id_entry.text); b.add_field_value_as_gvalue ("name", this.name_entry.text); var stmt = b.get_statement (); this.connection.statement_execute_non_select (stmt, null); this._clearFields (); this.selectData (); },

GDA 연결의 메서드 execute_select_command, execute_non_select_command를 활용하여 데이터베이스에서 SQL 명령을 빠르게 실행하는 방법을 알았습니다. GDA 에서는 SqlBuilder 객체로 SQL 구문을 간접적으로 만듭니다. 어떤 장점이 있을까요? GDA에서는 SQL 구문을 동적으로 만들어, 활용하는 연결 제공자에서 올바른 구문으로 간주합니다(프로바이더에서 사용하는 동일한 SQL 변형 문법도 할용합니다). 코드를 살펴보겠습니다:

2~3번쨰 줄: 사용자가 모든 필드를 채웠는지 확인합니다. 자체 메서드 _validateFields의 코드는 정말 단순하며 전체 시연 소스코드에서 알아볼 수 있습니다.

5번째 줄: INSERT 구문을 실행하는 빠른 방법입니다. 전 데이터베이스에 걸쳐 어디든 이식할 수 있는 SQL 구문을 만드는 SqlBuilder 활용법을 보고 싶으면 이 부분 주석을 제거하시면 됩니다.

7번째 줄: SqlBuilder 객체를 만듭니다. 빌드할 구문 형식을 넘겨야합니다. SELECT, UPDATE, INSERT, DELETE 중 하나입니다.

8번째 줄: 만들어둔 구문을 수행할 테이블 이름을 설정합니다(INSERT INTO demo 구문을 만듬)

9~10번째 줄: 구문의 일부로 필드 이름과 값을 설정합니다. 첫번째 인자는 필드 이름(테이블에 있는 그대로), 두번째 인자는 필드의 값입니다.

11번째 줄: 동적으로 만든 SQL 구문 Statement 객체를 가져옵니다.

12번째 줄: 마지막으로 SQL 구문(INSERT)을 실행합니다.

14번째 줄: 화면의 id와 name 필드를 지웁니다. private 메서드 _clearFields의 코드는 정말 단순하며 전체 시연 소스 코드에서 확인해볼 수 있습니다.

15번쨰 줄: 다른 SELECT 구문을 실행하며 화면의 뷰를 새로 고칩니다.

구문을 만들 때 매개변수를 사용할 수도 있습니다. SqlBuilder 객체와 매개변수를 활용하여 SQL 인젝션 같은 공격을 덜 받을 수 있습니다. 매개변수에 대한 자세한 정보는 GDA 문서를 참고하십시오.

프로그램 실행

필요한 모든 코드를 제자리에 넣었으니 코드를 실행해보겠습니다. 제대로 동작할겝니다. 이렇게 하여 레코드 수집용 데이터베이스를 만들었습니다!

참조 구현체

지침서를 따라하는 실행하는 과정에 문제가 있다면, 참조 코드와 여러분의 코드를 비교해보십시오.