Blob Blame History Raw
<?xml version="1.0" encoding="utf-8"?>
<page xmlns="http://projectmallard.org/1.0/" xmlns:its="http://www.w3.org/2005/11/its" type="topic" id="record-collection.js" xml:lang="ko">

  <info>
  <title type="text">레코드 수집(JavaScript)</title>
    <link type="guide" xref="js#examples"/>

    <desc>음악 모음을 정렬하는 간단한 데이터베이스 프로그램을 만듭니다</desc>

    <revision pkgversion="0.1" version="0.1" date="2011-02-22" status="review"/>
    <credit type="author">
      <name>그놈 문서 프로젝트</name>
      <email its:translate="no">gnome-doc-list@gnome.org</email>
    </credit>
    <credit type="author">
      <name>Johannes Schmid</name>
      <email its:translate="no">jhs@gnome.org</email>
    </credit>
    <credit type="editor">
      <name>Marta Maria Casetti</name>
      <email its:translate="no">mmcasettii@gmail.com</email>
      <years>2013</years>
    </credit>
  
    <mal:credit xmlns:mal="http://projectmallard.org/1.0/" type="translator copyright">
      <mal:name>조성호</mal:name>
      <mal:email>shcho@gnome.org</mal:email>
      <mal:years>2017</mal:years>
    </mal:credit>
  </info>

<title>레코드 수집</title>

<synopsis>
  <p>이 지침서를 통해 다음을 배웁니다:</p>
  <list>
    <item><p>libgda로 데이터베이스 연결하기</p></item>
    <item><p>데이터베이스 테이블로의 레코드 삽입 및 테이블 탐색</p></item>
  </list>
</synopsis>

<section id="intro">
  <title>도입부</title>
  <p>이 시연 프로그램에서는 Javascript 언어를 활용합니다. GTK 프로그램에서 GDA(그놈 데이터 액세스) 라이브러리로 데이터베이스를 연결하고 활용하는 방법을 보여드리겠습니다. 고로, 이 라이브러리도 설치해야합니다.</p>
  <p>그놈 데이터 액세스(GDA)는 다양한 종류와 형식의 데이터 공급원에 통합 방식으로 접근하는 수단을 제공합니다. 전통적인 관계형 데이터베이스 시스템에서 시작하여 메일 서버, LDAP 디렉터리 등과 같은 상상할 수 있는 데이터 공급원을 다룹니다. 더 많은 내용, 전체 API, 문서를 확인해보시려면  <link href="http://library.gnome.org/devel/libgda/stable/">GDA 웹사이트</link>를 방문해보십시오.</p>
  <p>비록 코드 상당 부분이 사용자 인터페이스(GUI)와 관련 있지만, 데이터베이스 부분의 따라하기 지침에 역점을 두었습니다(아마도 관련이 있는 다른 부분에도 언급할지도 모릅니다). 그놈 Javascript 프로그래밍을 더 알아보려면 <link xref="image-viewer.js">그림 보기 프로그램</link> 따라하기 지침 부분을 참고하십시오.</p>
</section>

<section id="anjuta">
  <title>안주타에서 프로젝트 만들기</title>
  <p>코딩을 시작하기 전에 안주타에서 새 프로젝트를 설정해야합니다. 이 프로그램은 빌드에 필요한 모든 파일을 만들고 그 다음 코드를 실행합니다. 또한 이 모든 상태를 유지 관리하는데 쓸만합니다.</p>
  <steps>
    <item>
    <p>안주타를 시작하고 <guiseq><gui>파일</gui><gui>새로 만들기</gui><gui>프로젝트</gui></guiseq> 를 눌러 프로젝트 마법사를 여십시오.</p>
    </item>
    <item>
    <p><gui>JS</gui> 탭에서 <gui>일반 Javascript</gui>를 선택하고, <gui>앞으로</gui>을 누른 다음, 나타난 페이지에서 몇가지 자세한 내용을 입력하십시오. 프로젝트 이름과 디렉터리에 <file>record-collection</file>을 입력하십시오.</p>
   	</item>
    <item>
    <p><gui>마침</gui>을 누르면 프로젝트를 만들어줍니다. <gui>프로젝트</gui>나 <gui>파일</gui>탭에서 <file>src/main.js</file> 파일을 여십시오. 매우 기본적인 예제 코드가 있습니다.</p>
    </item>
  </steps>
</section>

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

<section id="start">
  <title>재미로 시작하기</title>
  <p>프로그램 뼈대 검토를 우선 시작하겠습니다:</p>
  <code mime="application/javascript" style="numbered">
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 ();</code>
  <list>
    <item><p>1~4번째 줄: 초기 임포팅입니다. JavaScript로 GDA 라이브러리를 가져오는 이 따라하기 지침의 핵심부분인 세번째 줄에 특히 주목하십시오.</p></item>
    <item><p>6~17번째 줄: <code>Demo</code> 클래스를 정의합니다. 전체 작업을 담당하는 메서드 3개를 호출하는 13~15번째 줄에 주목하십시오. 자세한 내용은 아래에서 설명하겠습니다.</p></item>
    <item><p>19~23번째 줄: 프로그램을 시작합니다.</p></item>
  </list>
</section>

<section id="design">
  <title>프로그램 설계</title>
  <p><code>setupWindow</code> 메서드를 살펴보겠습니다. 이 메서드는 사용자 인터페이스(UI)를 만드는 역할을 합니다. UI는 중요한 부분이 아니니 관련 부분만 설명하겠습니다.</p>
  <code mime="application/javascript" style="numbered">
  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: "&lt;b&gt;Insert a record&lt;/b&gt;", 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: "&lt;b&gt;Browse the table&lt;/b&gt;", 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 ();
  },</code>
  <list>
    <item><p>22번째, 27번째 줄: 어떤 사용자가 데이터베이스에 들어간 내용을 입력하는 항목 2개(필드 2개)를 만듭니다.</p></item>
    <item><p>31~34번째 줄: 삽입 단추를 만듭니다. <code>clicked</code> 시그널을 클래스의 <code>_insertClicked</code> private 메서드에 연결했습니다. 이 메서드는 아래에서 자세히 설명하겠습니다.</p></item>
    <item><p>39번째 줄: 테이블의 내용을 보여줄 위젯(<code>TextView</code>)을 만듭니다.</p></item>
    <item><p>44번째 줄: 테이블의 여러 레코드를 보여줄 레이블을 만듭니다. 초기에는 비어있습니다만, 나중에 업데이트합니다.</p></item>
  </list>
</section>

<section id="connect">
  <title>데이터베이스 연결 및 초기화</title>
  <p><code>setupDatabase</code> 메서드에서 데이터베이스를 연결하는 코드는 아래와 같습니다:</p>
  <code mime="application/javascript" style="numbered">
  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))");
    }
  },</code>
  <list>
    <item>
      <p>2~3번쨰 줄: GDA의 <code>Connection</code> 객체를 만듭니다. 생성자에 속성 몇가지를 넣어야합니다:</p>
      <list>
        <item>
          <p><code>provider</code>: GDA에서 지원하는 프로파이더 중 하나입니다. GDA에서는 SQLite, MySQL, PostSQL, Oracle 등 수많은 데이터베이스 엔진을 지원합니다. 시연 목적으로, 대부분의 배포판에서 기본적으로 SQLite를 설치하며 쓰기 쉽기 때문에(파일을 데이터베이스로 활용) SQLite 데이터베이스를 사용하겠습니다.</p>
        </item>
        <item>
          <p><code>cnc_string</code>: 연결 문자열. 프로바이더를 바꿉니다. SQLite용 문법은 <code>DB_DIR=<var>PATH</var>;DB_NAME=<var>FILENAME</var></code> 입니다. 시연 프로그램에서는 사용자 주 디렉터리(GLib의 <code>get_home_dir</code> 함수 호출 참고)에 둔 gnome_demo 데이터베이스에 접근하겠습니다.</p>
        </item>
      </list>
      <note>
        <p>GDA에서 지원하지 않는 프로바이더가 있고, 연결 문자열에 어떤 구성 요소가 빠지면, 2번째 줄에 예외가 일어납니다. 따라서 실제로 JavaScript의 구문 <code>try</code>...<code>catch</code>로 처리해야합니다.</p>
      </note>
    </item>

    <item><p>4번째 줄: 연결합니다. SQLite 프로바이더에서 데이터베이스가 없으면 이 단계에서 만듭니다.</p></item>
    <item>
      <p>6~10번째 줄: 테이블이 있는지(7번째 줄) 간단하게 SELECT 문을 시도해봅니다. (데이터베이스를 방금 만들어서)테이블이 없으면, 이 명령에서 <code>try</code>...<code>catch</code> 블록에서 처리할 예외가 나타납니다. 이 경우 CREATE TABLE 구문을 실행합니다(9번째 줄).</p>
      <p>GDA 연결용 <code>execute_select_command</code> 메서드와 <code>execute_non_select_command</code> 메서드로 위 SQL 명령을 실행합니다. 사용법은 단순하며  <code>Connection</code> 객체와 해석할 SQL 명령을 두 매개변수 값으로 활용하므로 단순합니다.</p>
    </item>
  </list>

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

<section id="select">
  <title>선택</title>
  <p>데이터베이스를 연결하고 나면, 데모 생성자에서 <code>selectData</code> 메서드를 호출합니다. 이 메서드는 테이블의 모든 레코드를 가져와서 <code>TextView</code> 위젯에 나타냅니다. 이제 살펴보도록 하겠습니다:</p>
  <code mime="application/javascript" style="numbered">
  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=&gt;\t" + name_field + '\n';
    }

    this.text.buffer.text = text;
    this.count_label.label = "&lt;i&gt;" + dm.get_n_rows () + " record(s)&lt;/i&gt;";
  },</code>
  <list>
    <item><p>2번째 줄: <code>SELECT</code> 명령. GDA 연결의 <code>execute_select_command</code> 메서드를 이 목적으로 활용합니다. 나중에 레코드 행을 가져올 때 사용할 <code>DataModel</code>을 반환합니다.</p></item>
    <item><p>3번째 줄: <code>DataModel</code>의 여러 레코드를 순회할 때 활용할 <code>Iter</code> 객체를 만듭니다.</p></item>
    <item><p>7번째 줄: 레코드 전체를 반복문으로 돌고 <code>Iter</code> 객체의 도움으로 가져옵니다. 여기서 <code>iter</code> 변수에는 실제로 가져온 데이터가 들어갑니다. <code>move_next</code> 메서드에서 마지막 레코드에 도달하면 <code>false</code>를 반환합니다.</p></item>
    <item>
      <p>8~9번째 줄: 다음 두가지 동작을 각 줄 별로 행합니다:</p>
      <list>
        <item><p>하나의 인자만 필요한 <code>Iter</code>의 <code>get_value_at</code> 메서드를 활용합니다. 이 인자에는 가져올 열 번호가 들어가며 0번 부터 시작합니다. <code>SELECT</code> 명령에서 열 두개만 반환하므로 0번과 1번 열만 가져옵니다.</p></item>
        <item><p><code>get_value_at</code> 메서드에서 GLib의 <code>GValue</code> 형식 필드를 반환합니다. 이 형식을 문자열로 변환하는 가장 쉬운 방법은 GDA의 전역 함수 <code>value_stringify</code>를 사용하면 됩니다. 이게 여기서 우리가 처리하려는 일이며,  <code>id_field</code>, <code>name_field</code> 변수에 결과를 저장합니다.</p></item>
      </list>
    </item>
    <item><p>11번째 줄: 두 필드 문자열을 하나로 합치며, 사이에 <code>"=&gt;"</code> 문자열을 넣고 <code>text</code>변수에 저장합니다.</p></item>
    <item><p>14번째 줄: 반복문 실행이 끝나면 <code>text</code> 변수에 형식을 갖추어 저장한 모든 레코드를 확보합니다. 이 줄에서는 해당 변수를 <code>TextView</code> 의 내용으로 설정합니다.</p></item>
    <item><p>15번째 줄: <code>DataModel</code>의 <code>get_n_rows</code> 메서드를 활용하여 테이블의 수많은 레코드를 표시합니다.</p></item>
  </list>
</section>

<section id="insert">
  <title>삽입</title>
  <p>좋습니다. 데이터베이스에 연결하고 테이블에서 행을 선택하는 방법을 알았습니다. 이제 테이블에 대해 <code>INSERT</code> 구문을 수행할 시간입니다. <code>setupWindow</code> 메서드에서 <gui>Insert</gui> 단추의  <code>clicked</code> 시그널을 <code>_insertClicked</code> 메서드에 연결한 부분을 기억하시죠? 이 메서드의 구현부를 살펴보겠습니다.</p>
  <code mime="application/javascript" style="numbered">
  _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 ();
  },</code>
  <p>GDA 연결의 메서드 <code>execute_select_command</code>, <code>execute_non_select_command</code>를 활용하여 데이터베이스에서 SQL 명령을 빠르게 실행하는 방법을 알았습니다. GDA 에서는 <code>SqlBuilder</code> 객체로 SQL 구문을 간접적으로 만듭니다. 어떤 장점이 있을까요? GDA에서는 SQL 구문을 동적으로 만들어, 활용하는 연결 제공자에서 올바른 구문으로 간주합니다(프로바이더에서 사용하는 동일한 SQL 변형 문법도 할용합니다). 코드를 살펴보겠습니다:</p>
  <list>
    <item><p>2~3번쨰 줄: 사용자가 모든 필드를 채웠는지 확인합니다. 자체 메서드 <code>_validateFields</code>의 코드는 정말 단순하며 전체 시연 소스코드에서 알아볼 수 있습니다.</p></item>
    <item><p>5번째 줄: <code>INSERT</code> 구문을 실행하는 빠른 방법입니다. 전 데이터베이스에 걸쳐 어디든 이식할 수 있는 SQL 구문을 만드는 <code>SqlBuilder</code> 활용법을 보고 싶으면 이 부분 주석을 제거하시면 됩니다.</p></item>
    <item><p>7번째 줄:  <code>SqlBuilder</code> 객체를 만듭니다. 빌드할 구문 형식을 넘겨야합니다. <code>SELECT</code>, <code>UPDATE</code>, <code>INSERT</code>, <code>DELETE</code> 중 하나입니다.</p></item>
    <item><p>8번째 줄: 만들어둔 구문을 수행할 테이블 이름을 설정합니다(<code>INSERT INTO demo</code> 구문을 만듬)</p></item>
    <item><p>9~10번째 줄: 구문의 일부로 필드 이름과 값을 설정합니다. 첫번째 인자는 필드 이름(테이블에 있는 그대로), 두번째 인자는 필드의 값입니다.</p></item>
    <item><p>11번째 줄: 동적으로 만든 SQL 구문 <code>Statement</code> 객체를 가져옵니다.</p></item>
    <item><p>12번째 줄: 마지막으로 SQL 구문(<code>INSERT</code>)을 실행합니다.</p></item>
    <item><p>14번째 줄: 화면의 id와 name 필드를 지웁니다. private 메서드 <code>_clearFields</code>의 코드는 정말 단순하며 전체 시연 소스 코드에서 확인해볼 수 있습니다.</p></item>
    <item><p>15번쨰 줄: 다른 <code>SELECT</code> 구문을 실행하며 화면의 뷰를 새로 고칩니다.</p></item>
  </list>
  <note><p>구문을 만들 때 매개변수를 사용할 수도 있습니다.  <code>SqlBuilder</code> 객체와 매개변수를 활용하여 SQL 인젝션 같은 공격을 덜 받을 수 있습니다. 매개변수에 대한 자세한 정보는 <link href="http://library.gnome.org/devel/libgda/stable/">GDA 문서</link>를 참고하십시오.</p></note>
</section>

<section id="run">
  <title>프로그램 실행</title>
  <p>필요한 모든 코드를 제자리에 넣었으니 코드를 실행해보겠습니다. 제대로 동작할겝니다. 이렇게 하여 레코드 수집용 데이터베이스를 만들었습니다!</p>
</section>

<section id="impl">
 <title>참조 구현체</title>
 <p>지침서를 따라하는 실행하는 과정에 문제가 있다면, <link href="record-collection/record-collection.js">참조 코드</link>와 여러분의 코드를 비교해보십시오.</p>
</section>
</page>