# Firestore 개요

## Content

Wijmo의 **Firestore** 클래스는 Firestore REST API를 사용하여 Firestore 데이터베이스를 로드하고, 보고, 수정할 수 있도록 해줍니다. Firestore 클래스는 빠르고 가볍지만, Firestore 웹 클라이언트 라이브러리가 제공하는 오프라인 캐싱이나 실시간 업데이트 같은 고급 기능은 제공하지 않습니다. 애플리케이션에 이러한 기능이 필요하거나 Firestore 웹 클라이언트 라이브러리를 이미 사용 중이라면, Firestore 클래스 대신 **Snapshot** 클래스를 사용하는 것을 고려해 보시기 바랍니다.

## Firestore 데이터베이스 생성

먼저 Firestore 데이터베이스를 생성해야 합니다. 단계는 다음과 같습니다:

1. **Firebase** 콘솔로 이동
2. 새 프로젝트를 생성 (또는 기존 프로젝트 선택)
3. 데이터베이스 버튼 클릭
4. **Firestore** 데이터베이스 추가
5. UI를 사용하여 데이터베이스 구성

Firestore 데이터베이스 설정에 대한 자세한 내용은 여러 튜토리얼과 동영상을 참고할 수 있습니다.

* [Cloud Firestore 시작하기](https://firebase.google.com/docs/firestore/quickstart?hl=ko)
* [Firestore 비디오 튜토리얼](https://www.youtube.com/watch?v=4d-gIPGzmK4)

데이터베이스를 생성하고 데이터를 채운 후, 애플리케이션이 데이터베이스에 연결할 수 있도록 API 키를 생성해야 합니다. 이는 [Google API 콘솔](https://console.cloud.google.com/apis/credentials)을 통해 가능합니다.

## 애플리케이션의 Firestore 접근

이제 데이터베이스가 생성되고 API 키도 생성되었으니, Firestore 클래스를 가져와 이를 사용하여 데이터베이스에 연결할 수 있습니다.

먼저, **Firestore** 클래스를 가져와야합니다.:

```Javascript
import { Firestore } from '@mescius/wijmo.cloud';
```

그 다음, 우리의 데이터베이스에 연결된 **Firestore** 객체를 생성합니다:

```Javascript
const API_Key = 'AIzaSyCvuXEzP57I5CQ9ifZDG2_K8M3nDa1LOPE';
const PROJECT_ID = 'test-9c0be';
let fsNWind = new Firestore(PROJECT_ID, API_KEY, {
    collections: ['Products', 'Categories', 'Suppliers']
});
```

마지막으로, 사용자가 제품 카테고리를 보고 선택할 수 있도록 **ComboBox**를 추가하고, 사용자가 선택한 카테고리의 데이터를 표시할 **FlexGrid**와 데이터베이스에서 제품의 단가를 보여줄 **FlexChart**를 추가합니다.

**ComboBox**

```JavaScript
let categoryCombo = new ComboBox('#categoryCombo', {
    placeholder: '(All Categories)',
    isRequired: false,
    displayMemberPath: 'CategoryName',
    itemsSource: fsNWind.getCollection('Categories'),
    selectedIndexChanged: (s, e) => {
        let cat = s.selectedItem;
        fsNWind.getSheet('Products').filter = (item => {
            return cat == null || cat.CategoryID == item.CategoryID;
        });
    }
});
```

**FlexGrid**

```JavaScript
let supplierMap = new DataMap(fsNWind.getCollection('Suppliers'), 'SupplierID', 'CompanyName');
let productGrid = new FlexGrid('#productGrid', {
    ...gridOptions,
    autoGenerateColumns: false,
    columns: [
        { binding: 'ProductName', header: 'Product Name' },
        { binding: 'UnitPrice', header: 'Unit Price', format: 'n2' },
        { binding: 'QuantityPerUnit', header: 'Quantity Per Unit' },
        { binding: 'SupplierID', header: 'Supplier', dataMap: supplierMap },
        { binding: 'UnitsInStock', header: 'In Stock', format: 'n0' },
        { binding: 'UnitsOnOrder', header: 'On Order', format: 'n0' },
    ],
    itemsSource: fsNWind.getCollection('Products')
});
```

**FlexChart**

```JavaScript
let productChart = new FlexChart('#productChart', {
    chartType: 'Bar',
    axisX: { majorGrid: true, axisLine: false },
    axisY: { majorGrid: false, axisLine: false, reversed: true },
    legend: { position: 'Bottom' },
    bindingX: 'ProductName',
    series: [
        { binding: 'UnitPrice', name: 'Product Unit Prices (US$)' }
    ],
    itemsSource: fsNWind.getCollection('Products')
});
```

이제 컨트롤이 Firestore 데이터베이스에 연결되었습니다. 이는 모두 표준 Wijmo 코드이며, 데이터는 어떤 데이터 소스에서든 로드될 수 있습니다. 이번에는 **Firestore** 객체를 사용하여 데이터를 컨트롤에 로드했습니다.

## 변경사항 커밋

사용자가 항목을 변경, 추가 또는 제거할 때마다 **Firestore** 클래스는 데이터를 즉시 데이터 저장소에 커밋합니다. 이러한 기본 동작은 데이터 손실의 가능성을 최소화합니다.

커밋 작업을 연기하고 필요할 때 적용하려면 **Collection**의 **deferCommits** 속성을 true로 설정하고, 원하는 시점에 **commitChanges**(또는 **cancelChanges**)메서드를 호출하면 됩니다.

예를 들어, 아래 코드에서는 **hasPendingChanges** 속성을 사용하여 **Collection** 에 미결 변경 사항이 있는지 확인하고, **commitChanges** 및 **cancelChanges** 메서드를 사용하여 변경 사항을 적용하거나 취소하는 방법을 보여줍니다.:

```Javascript
// get the collection and configure it  
let collection = fs.getCollection('restaurants');  
collection.deferCommits = true;  

// commit/cancel changes buttons  
let btnCommit = document.getElementById('btn-commit') as HTMLButtonElement,  
    btnCancel = document.getElementById('btn-cancel') as HTMLButtonElement;  

// commit or cancel pending changes  
btnCommit.addEventListener('click', () => collection.commitChanges());  
btnCancel.addEventListener('click', () => collection.cancelChanges());  

// enable buttons when there are pending changes  
collection.hasPendingChangesChanged.addHandler((s, e) => {  
    btnCommit.disabled = btnCancel.disabled = !collection.hasPendingChanges;  
});
```