본문 바로가기
개발/dart

11, Use Indexed DB

by 허허 그림 2014. 3. 14.
728x90

드디어 11챕터 완료!

이제 마지막 한 챕터만 남았다.


Use Indexed DB

인덱스 DB 사용하기.

Save data on the client.
클라이언트에 데이터 저장하기.

Browsers provide several ways for your apps to store data on the client side. One storage option is IndexedDB—a web standard supported by many browsers. IndexedDB provides an implementation of an indexed database, in which each record is identified by a unique index or key, making data retrieval speedy. You can store large amounts of structured data, such as images, arrays, maps, and objects using IndexedDB. The standard does not specify size limits for individual data items or for the database itself, but browsers may impose storage limits.

브라우저는 앱에서 클라이튼 쪽에 데이터를 저장하는 몇가지 방법을 제공합니다. 한가지 저장 방식은 인덱스DB 입니다.이 방법은 많은 브라우저에서 지원하는 웹 표준 방식입니다. 인덱스DB는 빠르게 데이터를 검색하기 위해 각각의 정보들이 유일한 인덱스나 키로 구분되어지는 인덱스되어진 데이타베이스를 구현한 것입니다. 당신은 이미지, 배열, 맵 그리고 인덱스DB를 사용하는 객체들과 같은 아주 큰 구조적인 데이터를 저장할 수 있습니다. 이 표준은 개개의 데이터 항목의 크기나 데이타 베이스 자체의 크기 제한을 두지 않지만, 브라우저는 저장용량 제한을 둘 수도 있습니다.

IndexedDB provides several advantages. Your apps

인덱스DB는 몇가지 이점을 제공합니다. 당신은 앱이

  • can have full functionality even if a network connection is not available.

  • 네트워크 연결이 불가능하게 되었을지라도 기능적으로 완전하게 작동할 수 있습니다.

  • can cache data and restore state between invocations.

  • 데이터를 캐시하고 호출간에 상태를 복원 할 수 있습니다.

  • won’t lose data if the network connection is interrupted.

  • 네트워크 연결이 중단된 경우에도 데이터 손실이 없습니다.

  • generate less network traffic.

  • 네트워크 트래픽을 줄입니다.

  • perform better because data management happens on the local computer rather than over the Internet.

  • 데이터 관리가 인터넷이 아니라 로컬 컴퓨터에서 일어나기 때문에 더 나은 퍼포먼스를 보여줍니다.

Note: Some browsers don’t yet support IndexedDB. Check caniuse.com for up-to-date information. Your app can check programmatically if the current platform supports IndexedDB and adjust accordingly.

참고: 몇 몇 브라우저는 아직까지 인덱스DB를 지원하지 않습니다. 최신 정보를 여기에서 확인하세요. 당신의 앱은 현재 플랫폼이 인덱스DB를 지원하는 경우 프로그램으로 확인할 수 있고  그에 따라 조정할 수도 있습니다.

This tutorial shows you how to use dart:indexed_db to store data to and retrieve data from the browser’s IndexedDB.

이번 튜토리얼에서는 당신이 데이터를 저장하거나 브라우저의 인덱스DB에서 데이터를 찾기 위해서 어떻게 dart:indexed_db 를 사용하는지 알아 볼 것입니다.

Run the app
앱 실행하기

The count_down app below maintains a list of milestones and displays a countdown timer for each one.

아래에 있는 이번 count_down 앱은(실행가능한 앱은 https://www.dartlang.org/docs/tutorials/indexeddb/#run-the-app 참고하세요.)  중대한 시점을 유지하고 각 각에 대해서 카운트 다운 타이머를 보여줍니다.

Try it! Enter the name, date, and time of a milestone—your birthday, for example—and click the plus (+) button. The app starts a countdown timer and displays the amount of time remaining until the milestone occurs. The app updates the display every second.

시도! 중대한 시점의 이름, 날짜 그리고 시간을 입력하세요. 예를 들면 당신의 생일이 되겠지요. 그리고 플러스(+) 버튼을 클릭하세요. 이 앱은 카운트다운 타이머가 시작하고 중대한 시점이 발생하기까지 남은 시간의 양을 보여줍니다. 이 앱은 매초 업데이트 됩니다.

Close this browser window and reload this page. The milestone you created still exists because the app stored it in an IndexedDB in the browser.

브라우저 윈도우를 닫고 이 페이지를 다시 로드 하세요. 당신이 만들어놓은 중대한 시점은 여전히 존재합니다. 왜냐하면 이 앱을 브라우저의 인덱스DB에 저장을 했기 때문입니다.

Use the minus (-) button to the right of a milestone to delete that milestone. Use the Clear button to delete all the milestones.

현재의 중대 시점을 지우고 싶으면 중대한 시점의 오른쪽에 있는 마이너스(-) 버튼을 클릭하세요. 모든 중대시점을 지우고 싶다면 Clear 버튼을 클릭하세요.

Version Note: The count_down app is compatible with polymer.dart 0.9.

버전정보: count_down 앱은 polymer.dart 0.9. 버전과 호환됩니다.

Using developer tools to look at the database
데이터 베이스를 살펴보기 위해서 개발자 도구 사용하기.

You can use the browser’s developer tools to explore the IndexedDB databases used by your apps. In Chrome, select View > Developer > Developer Tools, then choose Resources from the tabs along the top of the window. The following diagram shows the database for the count_down app with two milestones.

당신이 만든 앱에서 사용된 인덱스DB 데이터 베이스를 보고 위해서 브라우저의 개발자 도구를 사용할 수 있습니다. 크롬에서는, View > Developer > Developer Tools 선택하고 그 다음에 윈도우의 위쪽에 있는 탭중에서 Resources 탭을 선택하세요.  다음의 그림은 2개의 중대시점을 등록한 count_down 앱의 데이터 베이스를 보여줍니다.

The count_down app has a single database named milestoneDB. and within it, a single object store named milestoneStore. In this example, each record in the object store is a milestone stored as a Map. The index, called name_index, associates each milestone name with a database key allowing for searches by milestone name.

count_down 앱은 milestoneDB 라고 하는 단일 데이터베이스를 가지고 있습니다. 그리고 그 milestoneDB 안에 milestoneStore 라고 하는 단일 객체가 저장되어 있습니다. 이 예제에서 , 객체 저장소의 각 레코드는 맵 객체로 저장되어 있습니다. name_index 라고 하는 이 인덱스는 중대시점의 이름으로 검색을 가능하게 하는 데이터베이스 키와 각 중대시점의 이름을 연결합니다.Using Dev Tools to view indexed databases

About the app: The basics
앱에 대해: 기초

The count_down app uses a Model, View, View-model (MVVM) structure.

count_down 앱은 모델, 뷰, 뷰-모델 구조를 사용합니다.Structure of the count_down app

  • The View-model, in the center of the diagram, connects the View and the Model, using UI and Timer events to make changes to the Model. The MilestoneApp class is the primary class that implements the View-Model—it manages the timer, and implements the app’s business logic, which manages the information exchange between the Model and the View.

  • 그림의 가운데에 있는 뷰-모델은 UI와 모델에 변화를 주는 타이머 이벤트를 연결합니다. 마일스스톤 앱 클래스는, 타이머를 관리하고 앱의 비지니스 로직을 구현하고 있는 뷰-모델을 구현하는 중요 클래스입니다. 그리고 모델과 뷰 사이에 정보 교환도 관리 합니다.

  • The View provides the user interface for the app. Two custom elements implement the View in the count_down app: CountDownComponent describes the user interface for the app as a whole, and MilestoneComponent describes the UI for an individual milestone. These components inform the View-model of UI events.

  • 뷰는 앱의 사용자 인터페이스를 제공합니다. 2개의 사용자 요소가 count_down 앱에서 뷰를 구현합니다.CountDownComponent 는 전체 앱의 사용자 인터페이스를 작성하고 MilestoneComponent 은 개별 중대시점 UI를 작성합니다. 이 컴포넌트들은  뷰-모델에게 UI 이벤트를 알립니다.

  • The Model contains and manages the data. The Model, implemented by the MilestoneStore class, manages a list of Milestone objects in memory and keeps an IndexedDB in sync with the list, saving the milestone data persistently. The View-model queries the Model upon initialization and uses Polymer data-bindings to keep the View in sync. Also, it uses Timer events to trigger updates in the Model.

  • 모델은 데이터를 포함하고 관리합니다. MilestoneStore 클래스에 의해 구현된 모델은 메모리에 있는 Milestone 객체 리스트를 관리하고, 지속적으로 중대시점 데이터를 저장하면서 Milestone 객체 리스트를 동기화해서 인덱스DB를 유지합니다. 뷰-모델은 초기화시에 모델을 질의하고 뷰와 동기화를 유지하기 위해서 Polymer 데이터 바인딩을 사용합니다. 또한 모델에서 업데이트를 작동시키는 타이머 이벤트를 사용합니다.

The libraries used by the count_down app
count_down 앱에 사용되어진 라이버리

The count_down app uses the following libraries:

Library

Description

dart:indexed_db

Save data into an indexed database for persistence and offline capability
지속성과 오프라인 기능을 위해 인덱스 데이터베이스에 데이터를 저장한다.

dart:async

Perform tasks asynchronously with Futures
Future를 가지고 비동기적으로 작업을 수행한다.

dart:core

Use DateTime and Duration to manage time-related tasks
시간과 관련된 작업을 관리하기 위해 DateTime 과 Duration을 사용한다.

Polymer

Create UIs with custom elements and data binding.
사용자 요소 UI와 데이터 바인딩을 만듭니다.

This tutorial explains the Dart API for IndexedDB used by the count_down app.

이번 튜토리얼은 count_down 앱에 사용되어진 인덱스DB를 위한 Dart API를 설명합니다.

Note: This tutorial does not cover Futures or Polymer. For information about Futures, see Use Future-Based APIs and Futures and Error Handling. For information about Polymer, refer to Define a Custom Element.

참고: 이번 튜토리얼은 Future와 Polymer은 설명하지 않습니다. Future에 대해서 정보를 원한다면 Use Future-Based APIs  과  Futures and Error Handling 을 보세요. Polymer에 대해서 정보를 원한다면 Define a Custom Element 을 참고하세요.

Details about IndexedDB
인덱스DB에 대해 자세히.

Some important facts you need to know about IndexedDB:

인덱스DB에 대해서 당신이 알아야할 몇가지 중요한 사실:

  • Each origin (host, protocol, and port) has its own set of databases. A unique name identifies each database within an origin. IndexedDB has a same-origin policy, which requires that the database and the app be from the same origin.

  • 각 기원(호스트, 프로토콜 그리고 포트)는 자기 자신만을 위한 데이터베이스 셋을 가집니다. 고유한 이름은 하나의 기원안에서 각 데이터베이스를 식별합니다. 인덱스DB은 데이터 베이스와 앱으 같은 기원에 있어야 한다는 same-origin 정책을 가지고 있습니다.

  • A database is identified by a name and version number. A database can have only one version at a time.

  • 데이터베이스는 이름과 버전번호에 의해서 식별됩니다. 데이터 베이스는 한번에 하나의 버전만을 가질 수 있습니다.

  • An object store is identified by a unique name. You can create an object store only during an “upgrade needed” event. You store data in records in an object store. A database can have multiple named object stores.

  • 객체 저장은 고유한 이름에 의해 식별됩니다. 당신은 "upgrade needed” 이벤트 동안에만 객체 저장을 만들 수 있습니다. 객체 저장소 안의 레코드에 데이터를 저장합니다. 데이타베이스는 여러개의 지정된 객체 저장소를 가질 수 있습니다.

  • A transaction provides reliable data access and data modification on a database. All interactions with the data in the database must happen within the scope of a transaction.

  • 트랜잭션은 데이타 베이스에서 신뢰할만한 데이터 액세스 및 데이터 변경 작업을 제공합니다. 데이터베이스에 있는 데이터들과의 모든 상호작용은 반드시 트랜잭션 범위내에서 발생해야 합니다.

  • A record is a key-value pair, where the key is a unique identifier for the corresponding data value. You can set your own keys or you can have the object store create them for you.

  • 레코드는 키-값의 쌍입니다.키는 데이터 값과 일치하는 고유한 식별자 입니다. 당신은 자신만의 키를 설정하거나 그러한 키들을 만드는 객체 저장소를 가질수도 있습니다.

  • An index is a specialized object store that maps database keys to the key field in the saved object. Using an index is optional.

  • 인덱스는 저장된 객체의 키 필드에 데이터페이스 키를 맵핑하는 특별한 객체 저장소 입니다. 인덱스 사용은 선택사항입니다.

  • An app may use multiple databases, each of which may have multiple object stores, each of which may have multiple records.

  • 앱은 여러개의 데이터베이스를 가지고 있어도 좋습니다. 각각의 데이터베이스는 여러개의 객체 저장소를,  또 각각의 객체 저장소는 여러개의 레코드를 가질수 있습니다.Indexed Databases in general

IndexedDB in the count_down app
count_down 앱의 인덱스DB

The count_down app uses one database, named milestoneDB with the version number 1, and one object store named milestoneStore. The app stores a milestone object as a Map; both the key and the value in the map are strings. The milestoneName field is the unique name of the milestone. The happensOn field is the date and time concatenated together. This is enough information to restore the state of the app when it’s restarted.

count_down 앱은 버전 넘버 1 의 milestoneDB 라고 하는 한개의 데이터베이스와 milestoneStore 라고 하는 한개의 객체 저장소를 사용합니다. 이 앱은 Map 형태로 milestone 객체를 저장합니다. 맵에 있는 키와 값 둘다는 문자열입니다. milestoneName 필드는 milestone의 고유한 이름입니다. happensOn 필드는 날짜와 시간이 함께 연결되어져 있습니다. 이런 것들은 앱이 다시 시작되었을때 앱의 상태를 복원하는데 충분한 정보들입니다.Indexed Database in the count_down app

Because the milestone names are unique, the app could use them as primary keys in the database. Instead, for illustration purposes, the app uses a primary key generated by the database. You can see this in the Developer Tools window:

milestone 이름은 고유하기 때문에, 이 앱은 데이터베이스에 그 이름을 기본키로 사용할 수 있습니다. 대신에, 다음의 그림에서 보듯이, 앱은 데이터베이스에 의해 생성된 기본키를 사용합니다. 개발자 도구 창에서 이것을 볼 수 있습니다.Milestone objects in the object store

Importing the IndexedDB library
인덱스DB 라이브러리 가져오기

To use the Dart IndexedDB API, your app must import the IndexedDB library provided with the Dart SDK:

Dart 인덱스DB API 를 사용하기 위해서, 당신의 앱은 반드시 Dart SDK 에서 제공하는 인덱스DB 라이브러리를 가져와야 합니다.

import 'dart:indexed_db';

Checking for IndexedDB support
인덱스DB 지원여부 체크하기

Use the getter supported from the IdbFactory class to determine if IndexedDB is supported.

인덱스DB의 지원여부를 알기위해서 IdbFactory 클래스에 있는 supported 게터 메소드를 사용하세요.

bool idbAvailable = IdbFactory.supported;

If supported is false, your app could

supported 가 거짓이라면, 당신은 앱은

  • throw an exception and quit,

  • 예외를 던지고 중지됩니다.

  • use an alternative API, such as window.localStorage, for client-side storage,

  • 클라이언트 쪽 저장소인 window.localStorage 와 같은 대체 API를 사용하세요.

  • run anyway, sacrificing persistence and offline capability,

  • 지속적이고 오프라인 기능이 없이 어쨌든 실행은 됩니다.

  • or, like the count_down app, disable the UI and display an error message.

  • 또는,  count_down 앱처럼, UI를 사용할수 없게 하고 에러 메시지를 보여주세요.

Creating and opening a database
데이터 베이스를 만들고 열기

Use window.indexedDB.open() to create a new database or to open an existing database. Whether it opens or creates the database depends on the name and version number you pass in as parameters.

새로운 데이터베이스를 만들거나 기존에 있는 데이터베이스를 열기 위해서 window.indexedDB.open() 을 사용하세요. 데이터베이스를 열거나 생성할지 여부는 매개변수로 전달할 이름과 버전 번호에 따라 다릅니다.

  • To open a database, use the name and current version number of an existing database.

  • 데이타베이스를 열기위해서는, 기존에 존재하는 데이터베이스의 이름과 현재 버전 번호를 사용하세요.

  • To update to a new version, call open() with the name of an existing database and a higher version number. (A database can have only one version at a time; it can’t exist in multiple versions at once.) This fires an upgrade needed event.

  • 새로운 버전을 업데이트하기 위해서는, 기존 데이터베이스의 이름과 더 높은 버전 번호로 open() 를 호출하세요. (데이터베이스는 한번에 하나의 버전만 가질 수 있습니다. 한 번에 여러가지 버전으로 존재할 수 없습니다.) 이렇게 하면 업그레이드에 필요한 이벤트가 발생합니다.

  • To create a completely new database, open it with a new, unique name. This also fires an upgrade needed event.

  • 완전히 새로운 데이터베이스를 만들려면, 새롭고 고유한 이름으로 데이터베이스를 여세요. 이렇게 하는것도 업그레이드에 필요한 이벤트가 발생합니다.

Here’s the function in the MilestoneStore class that creates and opens the database:

아래에 데이터베이스를 만들고 여는 MilestoneStore 클래스의 함수가 있습니다.

Future open() {
 
return window.indexedDB.open('milestoneDB',
     version
: 1,
     onUpgradeNeeded
: _initializeDatabase)
   
.then(_loadFromDB);
}

The first two parameters indicate the name and version of the database to open. The first time the count_down app runs from a particular origin, milestoneDB version 1 gets created and opened. The next time the app runs from that same origin, the database is simply opened.

처음 2개의 인자는  열려고 하는 데이타베이스의 이름과 버전을 가르킵니다. count_app이 처음에는 milestoneDB  의 버전이 1이 생성되고 열리면서 특정 기원에서 실행합니다. 다음번에 이앱이 같은 기원에서 실행될때는 데이터베이스는 간단히 열리기만 합니다.

The third parameter, onUpgradeNeeded, provides a callback function that gets called when an upgrade needed event is fired, which happens when a new database is created or the version of a database is updated. This gives your app an opportunity to create an object store. The only place you can create an object store is within an upgrade needed event. You must have an object store to add records to the database. The next section covers how to create an object store.

onUpgradeNeeded 인 3번째 인자는 업그레이드가 필요한 이벤트가 발생될때 호출되는 콜백함수를 제공합니다. 업그레이드가 필요한 이벤트는 새로운 데이터베이스가 만들어지거나 데이터베이스의 버전이 업데이트가 될때 발생합니다. 이것은 앱에 객제 저장소를 만들수 있는 기회를 줍니다. 객체 저장소를 만들수 있는 유일한 장소는 업그레이드가 필요한 이벤트가 내에서 입니다. 당신은 데이터베이스에 리코드를 추가하기 위해서는 반드시 객체 저장소를 가지고 있어야 합니다. 다음 섹션은 어떻게 객체 저장소를 만드는지에 대해서 알아 보겠습니다.

The following flow chart describes the logic of the window.indexedDB.open() function.

아래의 flow 차트는 window.indexedDB.open() 함수의 로직을 설명합니다.Logic flow of database creation

Because creating and opening a database can take time, window.indexedDB.open() returns a Future object, which runs asynchronously and returns a value, the database object, sometime in the future. The database object is returned to a callback function registered with then(). In this example, the callback function is called _loadFromDB(). Using cursors,_loadFromDB() reads all the milestones from the database and populates the app. The details are covered in Using a cursor to get all the records.

데이터베이스를 만들고 여는데 시간이 걸릴수 있기 때문에, window.indexedDB.open() 은 , 비동기적으로 실행되고 미래의 어느 시점에 값과 데이터베이스 객체를 리턴하는 Future 객체를 리턴합니다. 데이터베이스 객체는 then() 에 등록된 콜백함수에 리턴되어집니다. 이 예제에서, 콜백함수는 _loadFromDB() 입니다. 커서를 사용해서 , _loadFromDB() 는 데이타베이스에서 모든 중대시점을 일고 앱을 챕웁니다. 자세한 내용은 Using a cursor to get all the records 에 설명되어 있습니다.

Creating an object store
객체 저장소 만들기

When a new database is created, it contains no object stores. The only place you can create an object store is during an upgrade needed event. Fortunately, an upgrade needed event is fired when a new database (or a new version of a database) is created.

새로운 데이터베이스가 만들어질때, 새롭게 만들어진 데이터베이스는 아무런 객체 저장소가 없습니다. 객체 저장소를 만들수 있는 유일한 장소는 업그레이드가 필요한 이벤트가 발생하는 동안입니다. 다행스럽게도, 업그레이드가 필요한 이벤트는 새로운 데이터베이스(또는 데이터베이스의 새로운 버전)이 만들어질때 발생합니다.

The callback function for upgrade needed events in the count_down app is _initializeDatabase. This function creates an object store and an index.

count_down 앱에서 업그레이드가 필요한 이벤트 콜백함수는 _initializeDatabase 입니다. 이 함수는 객체 저장소와 인덱스를 만듭니다.

static const String MILESTONE_STORE = 'milestoneStore';
static const String NAME_INDEX = 'name_index';

void _initializeDatabase(VersionChangeEvent e) {
 Database db
= (e.target as Request).result;
 
 
var objectStore = db.createObjectStore(MILESTONE_STORE,
     autoIncrement
: true);
 
var index = objectStore.createIndex(NAME_INDEX, 'milestoneName',
     unique
: true);
}

The code gets the database object from the VersionChangeEvent object that is passed into the callback function as an argument.

이 코드는 인자로 콜백함수에 전달된 VersionChangeEvent 객체에서 데이터베이스 객체를 가지고 옵니다.

Use the Database object’s createObjectStore() method to create a new object store with the given name. Each object store must have a unique name. The count_down app uses one object store called milestoneStore. All the countdown milestones are stored and retrieved in this object store.

주어진 이름으로 새로운 객체 저장소를 만들려면 데이터베이스 객체의 createObjectStore() 메소드를 사용하세요. 각각의 객체 저장소는 반드시 고유한 이름을 가져야 합니다. count_down 앱은 milestoneStore 라고 하는 하나의 객체 저장소를 사용합니다. 모든 카운트다운 중대시점은 객체 저장소에 저장되어지고 검색되어집니다.

The code sets autoIncrement on the object store to true. When autoIncrement is true, the database generates unique, primary keys for you, which saves you the trouble of ensuring unique keys.

코드는 객체저장소에 autoIncrement 를 참으로 설정했습니다. autoIncrement 가 참일때, 데이터베이스는 고유한 키를 보장하는 어려움을 해결하고자 유일하고 기본키를 생성합니다.

Finally, _initializeDatabase creates a name index.

마지막으로, _initializeDatabase 은 이름 인덱스를 만듭니다.

Using a name index
이름 인덱스 사용하기

An index provides a lookup table. You can associate a primary key with a field in the stored objects. In the example, the index associates a primary key with the milestoneName field.

인덱스는 조회 테이블을 제공합니다. 당신은 저장된 객체의 필드와 기본 키를 연결할 수 있습니다. 예를 들어, 인덱스는 milestoneName 필드와 기본키를 연결합니다.


Use an index to allow for searches on a field in the objects

Using an index provides two benefits:

인덱스를 사용하면 다음의 2가지 이점을 제공합니다.

  • you can search on a field in the object instead of by primary key,

  • 기본키 대신에 객체에 있는 필드로 검색할 수 있습니다.

  • and you can use the index to ensure the value of the field is unique.

  • 그리고 필드의 값이 고유하다는 것을 보장하기 위해 인덱스를 사용할 수 있습니다.

The count_down app creates an index at the same time it creates the object store: during an upgrade needed event, which is the the only time you can create an index.

인덱스를 만들수 있는 유일한 시간인 업그레드가 필요한 이벤트가 발생하는 동안, count_down 앱은 객제저장소를 만듬과  동시에 인덱스를 만듭니다.

static const String NAME_INDEX = 'name_index';
...
objectStore
.createIndex(NAME_INDEX, 'milestoneName', unique: true);

The createIndex method takes three parameters:

createIndex 메소드는 3개의 인자를 가집니다.

  • the name of the index, here ‘name_index’. This must be unique.

  • 인덱스의 이름, 이 예제에서는 ‘name_index’. 이름은 반드시 고유해야 합니다.

  • the key path, which indicates the field in the stored object to index.

  • 인덱스 하기 위해 저장된 객체의 필드를 가르키는 키 경로

  • unique, a boolean value. When true, the index ensures that the milestone name is unique. In the count_down app, if you try to add a milestone with the same name as another, it is this index that causes the add() to fail.

  • 고유한 부울 값. 참일때, 인덱스는 중ㅐ시점 이름이 고유하다는 것을 보장합니다. count_down 앱에서, 똑 같은 이름의 중대시점을 추가하려고 한다면 add() 가 실패할 것입니다.

Using transactions
트랜잭션 사용하기

All database operations must be performed within a Transaction.

모든 데이터베이스 작동은 트랜잭션안에서 수행되어져야 합니다.

Important: About the life-cycle of a transaction

중요: 트랜잭션의 라이프 사이클에 대해

The life-cycle of a transaction is as follows:

트랜잭션의 라이프 사이클은 아래와 같다.

  • Open a transaction.

  • 트랜잭션을 연다.

  • Place requests on the transaction and register callbacks.

  • 트랜잭션에 요청을 배치하고 콜백을 등록한다.

  • When there are no more requests and the last callback finishes, the transaction completes.

  • 더 이상의 요청이 없고 마지막 콜백이 끝날때 트랜잭션은 완료됩니다.

So, a transaction remains alive until all requests are complete and the final callback completes. You can keep a transaction alive by placing a new request within a callback.

그래서, 트랜잭션은 모든 요청이 완료되고 콜백이 완료될때까지 살아있습니다. 당신은 콜백에 새로운 요청을 함으로써 트랜잭션을 살아있게 유지할 수 있습니다.

A good rule: During the scope of a transaction, perform only database-related tasks to avoid crazy, mind-boggling bugs related to the DOM event loop.

좋은 규칙: 트랜잭션의 범위동안에,  DOM 이벤트 루프와 관련된 돌아버리고 상상도 할수 없는 그런 버그를 피하기 위해 데이타베이스 관련 작업만을 수행하세요.

Get a transaction from the Database object, which in the count_app is named _db.

count_app 에서 _db 라고 이름지어진 데이터베이스 객체에서 트랜잭션을 가지고 오세요.

Transaction t = _db.transaction(storeNameOrNames, mode);

The first argument to transaction() is the scope of the transaction. In the count_down app, the scope is always milestoneStore, the lone object store in the database, but you could specify multiple object stores. For efficiency, you should specify only the stores you need.

transaction()의 첫번째 인자는 트랜잭션 범위 입니다. count_down 앱에서, 그 범위는 항상 데이터베이스에 있는 긴 객체저장소인 milestoneStore 입니다만, 여러개의 객체 저장소를 지정할 수도 있습니다. 효율성을 위해서, 당신이 필요한 저장소에만 지정해야 합니다.

The second argument is a string that specifies the mode; it can be readwrite or readonly or versionchange. The count_down app uses only readwrite and readonly transactions. Use readwrite transactions only when necessary: readonly transactions are more efficient, and you can run multiple readonly transactions concurrently on overlapping scopes, but only one readwrite transaction.

두번재 인자는 모드를 지정하는 문자열입니다. 그것은 readwrite 또는 readonly 또는 versionchange 가 될 수 있습니다.count_down 앱은 readwrite 와 readonly 트랜잭션만을 사용합니다. 필요한 경우에만 readwrite 트랜잭션을 사용하세요. readonly 트랙잭션은 훨씬 효율적이고 중복범위에서 동시에 여러개의 readonly 트랜잭션을 실행시킬수 있지만, readwrite 트랜잭션은 단 하나만 가능합니다.

Once you’ve created a transaction, you can perform one or more operations using that transaction. Database operations can take time, so the work is performed off of the main UI thread and results are provided via Futures. Each operation uses a Future, which completes when the operation is complete. The transaction itself also uses a Future, which completes when all operations on the transaction complete.

트랜잭션을 만든 후에, 당신은 만들어 놓은 트랜잭션을 사용해서 하나 또는 여러개의 작업을 할 수 있습니다. 데이타베이스 작업은 시간이 걸릴수 있기 때문에 , 작업은 메인 UI 쓰레드와 분리되어 수행되고 결과는 Future로 제공되어 집니다. 각 작업은, 해당 작업이 완료되었을때 완료된 Future를 사용합니다. 또한 트랜잭션은 그 자체로써, 트랜잭션에 걸려있는 모든 작업이 완료될때 완료하는 Future 를 사용합니다.

For example, you might use one transaction to add multiple records to an object store. Each add is a separate operation and uses a separate Future. The following diagram shows the logic flow of a program that adds three records to a database. When all three add operations complete, the transaction completes.

예를 들어, 하나의 객체 저장소에 여러개의 레코드를 저장하기 위해 1개의 트랜잭션을 사용하는 경우가 있습니다. 각각의 추가는 독립적인 작업이며 독립된 Future를 사용합니다. 아래의 그림은 데이타베이스에 3개의 리코드를 저장하는 프로그램의 논리적인 흐름을 보여줍니다.3개 모두의 추가 작업이 완료되면, 이 트랜잭션은 완료가 됩니다.Transactions, object store operations, and futures

Many database transactions follow this pattern:

많은 데이타베이스 트랜잭션은 이런 패턴을 따릅니다.

  • Create a transaction on an object store.

  • 객체 저장소에 트랜잭션을 만듭니다.

  • Perform one or more operations using the transaction.

  • 트랜잭션을 이용해서 하나 또는 여러개의 작업을 수행합니다.

  • Use the operation’s callback function to perform a task when the operation completes successfully. For example, when adding records to a database, you can get the key generated by the database for the added record.

  • 작업이 성공적으로 완료가 되면 다른 작업을 수행하기 위한 작업 콜백 함수를 사용하세요. 예를 들어, 데이터베이스에 레코드들 추가할때, 추가된 레코드의 데이터베이스에 의해 생성된 키를 가져올 수 있습니다.

  • Use the transaction.completed callback function to perform a task when all the operations complete successfully. Generally, the count_down app uses this callback function to keep the list of milestones in sync with the database.

  • 모든 작업이 성공적으로 완료가 되면  다음 작업을 수행하기 위한 transaction.completed 콜백 함수를 사용하세요. 일반적으로, count_down 앱은 데이터베이스와 중대시점의 리스트의 동기화를 유지하기 위해서 이 콜백 함수를 사용합니다.

Adding data
데이타 추가하기.

Here’s the code that adds a new record to the database.

데이타베이스에 새로운 레코드를 추가하는 코드는 아래와 같습니다.Adding a record to an IndexedDB

The code creates a new Milestone object and converts it to a Map, then creates a new readwrite Transaction on the object store.

이 코드는 새로운 Milestone 객체를 만들고 그것을 맵으로 변경합니다. 그다음에 객체저장소에 새로운 readwrite 트랜잭션을 만듭니다.

Then, to add the milestone Map to the database, the code calls the add() method on the object store. and registers a callback function using then(). Because the object store was created with autoIncrement: true, adding a record to the database automatically creates a unique primary key for the new record. This key is returned as a parameter to the callback function.

그런 다음에, 데이타베이스에 milestone 맵 객체를 추가하기 위해, 이 코드는 객체 저장소에서 add() 메소드를 호출합니다. 그리고 then() 을 사용해서 콜백 함수를 등록합니다. 객체 저장소는 autoIncrement: true 로 만들어졌기 때문에, 데이타페이스에 레코드를 추가하면 자동적으로 새로운 레코드에 기본키를 생성합니다. 이 키는 콜뱀함수에 인자로 리턴되어 집니다.

Finally, the code registers a callback function for the transaction.

마지막으로, 이 코드는 트랜잭션에 콜백함수를 등록합니다.

When the add operation has been performed on the database, the Future related to the add operation completes and the callback function is called with the auto-generated key. The count_down app saves the key in the Milestone object.

레코드를 추가하는 작업이 데이타베이스에서 수행되어질때,  추가하는 작업과 관계된 Future 는 완료되고 콜백함수는 자동생성된 키와 함께 호출됩니다. count_down 앱은 Milestone 객체에 그 키를 저장합니다.

At this point, it is important to note that although the add operation is complete, the transaction is not! Any changes made to the database by the operation are not available until the transaction completes.

이 시점에서, 레코드를 추가하는 작업이 완료되었을지라도, 그 트랜잭션은 아직 완료것이 아니라는 것에 주의하세요! 트랜잭션이 완료될때까지는 데이터베이스에 작업을 해서 어떠한 변화를 주는것은 불가능합니다.

When all operations on a transaction complete, in this case, a single add operation, the transaction completes and its callback function is called. In the transaction’s callback function, the count_down app adds the milestone to the list in memory and the Future returns with the new milestone object (and the View-model starts the Timer).

트랜잭션에 있는 모든 작업이 완료되면, 이 경우에는 하나의 추가하는 작업인, 이 트랜잭션이 완료되고 콜백함수는 호출됩니다. 트랜잭션의 콜뱀함수에서, count_down 앱은 milestone을 메모리에 있는 리스트에 저장하고 Future는 새로운 milestone 객체를 리턴합니다.(그리고 뷰-모델은 타이머를 시작합니다.)

Removing data
데이터 제거하기

Here’s the code that removes a key-value pair from a database.

데이터베이스에서 키-값의 쌍을 제거하는 코드가 있습니다.

var transaction = _db.transaction(MILESTONE_STORE, 'readwrite');
transaction
.objectStore(MILESTONE_STORE).delete(milestone.dbKey);

return transaction.completed.then((_) {
 milestone
.dbKey = null;
 milestones
.remove(milestone);
});

Again the code creates a readwrite transaction on a named object store. To delete a key-value pair, the mode must be readwrite. Use the delete() method on the object store to delete a key-value pair. Specify the key as the argument to delete().

다시 한번 코드는 지정된 객체 저장소에 readwrite 트랜잭션을 만듭니다. 키-값의 쌍을 지우기 위해서, 모드는 반드시 readwrite 이여야 합니다. 키와 값의 쌍을 지우기 위해서는 객체 저장소의  delete() 메소드를 사용하세요. delete()에 넘겨줄 인자로 키를 지정하세요.

When the transaction completes successfully, the count_down app can safely remove the milestone from the list in local memory and stop the timer, if necessary.

트랜잭션이 성공적으로 완료되면, count_down 앱은 로컬 메모리에 있는 리스트에서 해당하는 milestone을 안전하게 제거하고 필요하다면 타이머를 멈춥니다.

Note that this code does not specify a callback function for the delete operation, only for the transaction. The task it needs to do, namely to remove the milestone from the list in memory, can occur when the transaction completes.

이 코드는 삭제 작업에 대한 콜백함수를 지정하지 않고 있다는 것을 알아두세요. 단지 트랜잭션만을 사용합니다. 할 필요가 있는 작업, 즉 다시 말해서 메모리에 있는 리스트에서 milestone 을 제거하는 작업은 트랜잭션이 완료될때 일어날 수 있습니다.

Clearing all data
모든 데이터 지우기

Use the clear method on the object store to remove all records.

모든 레코드를 지우려면 객체 저장소의 clear 메소드를 사용하세요.

var transaction = _db.transaction(MILESTONE_STORE, 'readwrite');
transaction
.objectStore(MILESTONE_STORE).clear();

return transaction.completed.then((_) {
 milestones
.clear();
});

Using a cursor to get all the records
모든 레코드를 가져오기 위해 커서 사용하기

When the app starts, if there’s an existing database and that database has milestones, the count_down app reads the records from the database and initializes the internal list of milestones from that information.

앱이 실행될때,  데이터베이스가 존재하고  그 데이터베이스가 milestone을 가지고 있으면, count_down 앱은 데이터베이스에서 레코드를 읽고 그 정보로 milestone의 내부 리스트를 초기화 합니다.

The _loadFromDB() method gets called after the database has been successfully opened. It is within this method that the database is read.

_loadFromDB()  메소드는 데이터베이스가 성공적으로 열린 후에 호출됩니다.그것은  데이터베이스가 읽게 된 이 메소드의 내부에 있습니다.

Future _loadFromDB(Database db) {
 _db
= db;

 
var trans = db.transaction(MILESTONE_STORE, 'readonly');
 
var store = trans.objectStore(MILESTONE_STORE);

 
var cursors = store.openCursor(autoAdvance: true).asBroadcastStream();
 cursors
.listen((cursor) {
   
var milestone = new Milestone.fromRaw(cursor.key, cursor.value);
   milestones
.add(milestone);
 
});

 
return cursors.length.then((_) {
   
return milestones.length;
 
});
}

Because getting records does not modify the database, this transaction is readonly.

메소드를 읽는것은 데이터베이스를 수정하는 것이 아니기 때문에, 이 트랜잭션은 readonly 입니다.

You can use a Cursor object to step through the records in the database one by one, creating a Milestone object for each. The object store uses a stream to fire an event for each record retrieved from the database. By listening on the stream, your code can be notified for each record read. The count_down app creates a corresponding Milestone object in the internal list in memory for each database record retrieved.

당신은 각각의 Milestone 객체를 만들려면,  데이터베이스에 있는 레코드를 하나씩 하나씩 단계별로 Cursor 객체를 사용할 수 있습니다.객체 저장소는 데이터베이스에서 검색된 각각의 레코드마다 이벤트를 발생시키는 스트림을 사용합니다. 스트림에서 수신하는 것에 의해서, 당신의 코드는 각각의 레코드의 읽기를 위한 통지를 줄수 있습니다.count_down 앱은 검색된 각각의 데이터베이스 레코드의 메모리의 내부 리스트에서 해당 Milestone 객체를 만듭니다.

Open a cursor on a transaction’s object store with openCursor. The cursor indicates the current position in the database. The count_down app opens a cursor and sets the optional autoAdvance argument to true. This means that after reading a record from the database and returning its value, the cursor advances automatically to the next record. If you don’t use autoAdvance, your code has to use the next() method to advance the cursor to the next record.

openCursor 을 사용해서 트랜잭션 객체저장소의 커서를 여세요. 이 커서는 데이터베이스에서 현재의 위치를 지정합니다. count_down 앱은 커서를 열고 옵션인 autoAdvance 인자를 참으로 설정합니다. 이것은 데이터베이스에서 레코드를 읽고 그것의 값을 리턴한 후에 커서가 다음 레코드로 자동으적으로 이동한다는 것을 뜻합니다. 당신이 autoAdvance을 사용하지 않는다면, 코드에는 다음 레코드로 커서를 이동시키기 위해서 next() 메소드를 사용해야 합니다.Advance the cursor with autoAdvance or cursor.next

The openCursor() method returns a stream on which you can listen for events. Here, the stream is a broadcast stream, so the app can listen both for read events and for a final count of records retrieved.

openCursor() 메소드를 사용하면 이벤트를 수신할 수 있는 스트림을 반환합니다. 여기에는 broadcast 스트림을 사용합니다. 그래서 앱은  이벤트를 읽는것과 검색된 레코드의 마지막 갯수 둘 다 수신할 수 있습니다.

For each record retrieved from the database, an event fires and the callback function is called. A CursorWithValue object, named cursor in this example, is passed to the callback function. Use cursor.key to get the key for the record just retrieved. Use cursor.value to get the value for that record.

데이터베이스에서 검색된 각 레코드에 대해, 이벤트는 발생하고 콜백 함수가 호출됩니다. 이 예제에서 CursorWithValue 커서 객체는 콜백 함수에 전달됩니다. 검색된 레코드의 키를 가져오려면 cursor.key 을 사용하세요. 그리고 검색된 레코드의 값을 가지고 오려면 cursor.value 를 사용하세요.

The _loadFromDB method returns a Future that returns the length of the stream.

_loadFromDB 메소드는 스트림의 길이를 리턴하는 Future 를 반환합니다.

Other resources
다른 자원들

What next?
다음은?

You’ve completed the tutorials! Check out the Dart code samples and articles.

이번 튜토리얼을 완료하게 됩니다! Dart code samplesarticles 를 받아가세요.



300x250

'개발 > dart' 카테고리의 다른 글

"나의 로또" 개인정보 처리 방침  (0) 2024.02.06
12. Write Command-Line Apps  (0) 2014.03.17
10. Get Input from a Fom  (0) 2014.03.12
9. Fetch Data Dynamically  (0) 2014.03.04
8. Use Streams for Data  (0) 2014.03.02

댓글