# 테이블 시트 뷰

## Content

테이블 시트 뷰는 테이블 시트의 중심이며, 가져온 데이터 소스의 필드들로 구성됩니다. 이는 데이터 열을 풍부하게 하고 더 많은 기능을 부여하는 데 사용됩니다.
![](https://gcdocumentsitekrblob.blob.core.windows.net/document-site-files/images/8d606653-16a0-474d-b9dc-e2b4d01c2446/ts-views-flowchart.cb2296.png)

위의 흐름도 이미지에서 볼 수 있듯이, 테이블 시트 뷰는 다음과 같은 방식으로 표시될 수 있습니다:

* **테이블 열(Table columns)**: 필드에서 직접 생성되는 기본 열로, 필드 이름 속성만 가지며 기본 보기로 사용됩니다.
* **뷰 열(View columns)**: 스타일, 포맷 등 다양한 속성으로 사용자 정의할 수 있는 고급 열로, `caption`, `style`, `width`, `headerStyle` 등의 속성을 통해 테이블 시트 뷰에 렌더링됩니다.

테이블 시트 뷰는 다양한 **IColumn** 속성을 설정하여 사용자 지정할 수 있습니다. 예를 들어, **헤더 스타일**, **헤더 핏 모드**, **조건부 서식**, **데이터 유효성 검사**, 그리고 **셀 타입 및 드롭다운**과 같은 열 스타일을 설정할 수 있습니다.

## 열 표시 값

기존 열 데이터를 대신하여 사용자 정의 값을 표시하려면, 열의 `style` 속성에 포함된 `formatter` 수식을 사용할 수 있습니다. 이 수식은 `[@]` 연산자를 이용해 현재 행의 전체 데이터를 참조할 수 있습니다.
테이블 시트 열에서 사용자 지정 값을 표시하는 방법은 다음 두 가지가 있습니다:

* **formatter 안에 수식 사용**
    =[@contactTitle] & ":" & [@employee.FirstName] & " " & [@employee.LastName]
* **formatter 안에 템플릿 수식 사용**
    {{[@contactTitle]}} : {{=[@employee.FirstName] & " " & [@employee.LastName]}}

![column-display-value.png](https://gcdocumentsitekrblob.blob.core.windows.net/document-site-files/images/8d606653-16a0-474d-b9dc-e2b4d01c2446/column-display-value.63ee46.png)
다음 코드 샘플은 formatter 안의 수식을 사용하여 사용자 정의 열 값을 표시하는 방법을 보여줍니다:

```JavaScript
//세일즈 테이블과 고객 테이블 사이 관계 추가
dataManager.addRelationship(salesTable, "customerKey", "myCustomer", customerTable, "customerKey", "mySales");

//사용자 정의 뷰 추가 
customerTable.fetch().then(function () {

var myView = salesTable.addView("myView", [
       { value: "saleKey", width: 100, caption: "Sale Key" },
       { value: "salesPerson", width: 150, caption: "Sales Person" },
       { value: "myCustomer", width: 350, caption: "Customer Information", style: { formatter: '=[@myCustomer.primaryContact] & " " & [@myCustomer.postalCode]' } },
       { value: "address", width: 350, caption: "Address" },
       { value: "stockItem", width: 350, caption: "Stock Item" },
       { value: "quantity", width: 100, caption: "Quantity" }
          ]);
```

## 열 헤더 스타일

열 헤더 스타일은 뷰의 `headerStyle` 속성에 **HeaderStyleOptions** 를 정의하여 사용자 지정할 수 있습니다. `HeaderStyleOptions`는 배경색, 테마 글꼴, 자동 줄 바꿈 등과 같은 옵션을 포함합니다.
![](https://gcdocumentsitekrblob.blob.core.windows.net/document-site-files/images/8d606653-16a0-474d-b9dc-e2b4d01c2446/images/ts-columnheaderstyle.png)

다음 코드 샘플은 테이블 시트 뷰에서 열 헤더 스타일을 설정하는 방법을 보여줍니다:

```javascript
// a열 헤더에 대한 스타일 정의 및 addView() 메서드를 사용하여 뷰에 추가하는 동안 할당
var headerStyle = {
    font: "italic bold 13pt Calibri",
    borderTop: {
        color: "red",
        style: "thick"
    },
    borderLeft: {
        color: "red",
        style: "thin"
    },
    borderRight: {
        color: "red",
        style: "thin"
    },
    borderBottom: {
        color: "red",
        style: "thin"
    }
};


//테이블 시트에 뷰 바인딩
customerTable.fetch().then(function () {
    var view = customerTable.addView("myView", [
        { value: "customerKey", width: 130, headerStyle: headerStyle, caption: "Customer Key" },// set the style of column header using headerStyle property
        { value: "customer", width: 200, headerStyle: headerStyle, caption: "Customer" }, 
        { value: "billToCustomer", width: 200, headerStyle: headerStyle, caption: "Bill To Customer" },
        { value: "category", width: 120, headerStyle: headerStyle, caption: "Category" },
        { value: "buyingGroup", width: 180, headerStyle: headerStyle, caption: "Buying Group" },
        { value: "primaryContact", width: 170, headerStyle: headerStyle, caption: "Primary Contact" },
        { value: "postalCode", width: 120, headerStyle: headerStyle, caption: "Postal Code" },
        { value: "validFrom", width: 150, headerStyle: headerStyle, caption: "Valid From" },
        { value: "validTo", width: 180, headerStyle: headerStyle, caption: "Valid To" }

    ]);
    //테이블의 모든 기본 열을 가진 뷰
    sheet.setDataView(view);
});
```

## **열 헤더 맞춤 모드**

테이블 시트 뷰에서 지정된 열의 헤더 레이아웃을 적절히 표시할 수 있도록 열 헤더 맞춤 모드를 설정할 수 있습니다.
열 데이터의 내용 길이는 짧지만 헤더 캡션이 길거나 일부 열이 관계를 가질 때 유용합니다.

**headerFit** 뷰 옵션 속성을 사용하여 다음과 같이 세 가지 유형의 헤더 맞춤 모드를 설정할 수 있습니다.

* **Normal**: 열 헤더 텍스트 방향이 수평이며 왼쪽에서 오른쪽으로 표시됩니다. 기본 헤더 맞춤 모드입니다.
<br>
    ![](https://gcdocumentsitekrblob.blob.core.windows.net/document-site-files/images/8d606653-16a0-474d-b9dc-e2b4d01c2446/images/ts-columnheaderfit-normal.png)
* **Vertical**: 열 헤더 텍스트 방향이 수직이며 위에서 아래로 표시됩니다.
<br>
    <br>
    ![](https://gcdocumentsitekrblob.blob.core.windows.net/document-site-files/images/8d606653-16a0-474d-b9dc-e2b4d01c2446/images/ts-columnheaderfit-vertical.png)
* **Stack**: 열 너비가 열 헤더 텍스트를 표시하기에 충분하지 않을 때 헤더가 위에서 아래로 쌓여 표시됩니다.
    ![](https://gcdocumentsitekrblob.blob.core.windows.net/document-site-files/images/8d606653-16a0-474d-b9dc-e2b4d01c2446/images/ts-columnheaderfit-stack.png)
<br>
    Stack 헤더 맞춤 모드는 인접한 열과 함께 표시될 경우 Vertical 모드와 교차하여 함께 표시됩니다.
<br>
    <br>
    ![](https://gcdocumentsitekrblob.blob.core.windows.net/document-site-files/images/8d606653-16a0-474d-b9dc-e2b4d01c2446/images/ts-columnheaderfit-mixed.png)

테이블 시트 뷰에서 **defaultStackRowHeight** 옵션을 사용하여 Stack 행 높이를 설정할 수 있습니다. 값이 `null`일 경우 평균 행 높이를 계산하며, 유효한 숫자를 지정하면 위에서 아래로 그 값을 기준으로 높이를 계산합니다.

다음 코드 샘플은 열 헤더 맞춤 모드를 설정하고 테이블 시트 뷰에서 행 높이를 조정하는 방법을 보여줍니다.

```javascript
//tablesheet에 뷰 바인딩
myTable.fetch().then(function () {
    var view = myTable.addView("myView", [
        { value: "saleKey", caption: "Sale Key", headerFit: "stack", headerStyle: headerStyle },// set headerFit를 스택에 설정
        { value: "cityKey", caption: "City Key", headerFit: "stack", headerStyle: headerStyle },
        { value: "stockItemKey", width: 50, caption: "Stock Item Key", headerFit: "stack", headerStyle: headerStyle },
        { value: "invoiceDateKey", width: 120, caption: "Invoice Date Key", headerFit: "vertical", headerStyle: headerStyle },
        { value: "deliveryDateKey", width: 120, caption: "Delivery Date Key", headerFit: "vertical", headerStyle: headerStyle },
        { value: "salesPerson", width: 150, caption: "Sales Person", headerFit: "vertical", headerStyle: headerStyle },
        { value: "address", caption: "Address", width: 300, headerFit: "normal", headerStyle: headerStyle }

    ]);
    sheet.setDataView(view);
    sheet.options.defaultStackRowHeight = 30;
    sheet.setDefaultRowHeight(220, 1);
});
```

>type=warning
> **참고**: 행 높이로 전체 캡션을 표시할 수 없는 경우, 해당 열의 스택된 헤더는 잘려서 표시될 수 있습니다. 이 경우 `setDefaultRowHeight` 메서드를 사용하여 조정할 수 있습니다.

## **셀 유형 및 드롭다운**

테이블 시트 뷰에서 다양한 셀 유형과 드롭다운을 설정하여 정보가 표시되는 방식을 지정하고, 데이터를 보다 쉽게 선택할 수 있도록 도와줍니다.

![Cell Types and Dropdowns in SpreadJS TableSheet](https://gcdocumentsitekrblob.blob.core.windows.net/document-site-files/images/8d606653-16a0-474d-b9dc-e2b4d01c2446/ts-celltype-dropdown.c03983.gif)

테이블 시트에서 지원하는 셀 유형과 드롭다운은 다음과 같습니다:

* **셀 유형** – 체크박스, 콤보박스, 하이퍼링크, 라디오 버튼 리스트, 체크박스 리스트, 범위 템플릿
* **드롭다운** – 색상 선택기, 날짜 선택기, 시간 선택기, 월 선택기, 리스트, 슬라이더, 계산기, 워크플로우 리스트, 다중 열

다음 코드 샘플은 테이블 시트 뷰에 셀 유형과 드롭다운을 추가하는 방법을 보여줍니다.

```javascript
//  RadioButtonList
var radioButtonListStyle = new GC.Spread.Sheets.Style();
var cellTypeRadioButtonList = new GC.Spread.Sheets.CellTypes.RadioButtonList();
cellTypeRadioButtonList.items([
    { text: "Tailspin Toys (Head Office)", value: "Tailspin Toys (Head Office)" },
    { text: "Wingtip Toys (Head Office)", value: "Wingtip Toys (Head Office)" },
]);
radioButtonListStyle.cellType = cellTypeRadioButtonList;

//  ButtonList
var buttonListStyle = new GC.Spread.Sheets.Style();
var buttonListCellType = new GC.Spread.Sheets.CellTypes.ButtonList();
buttonListCellType.items([
    { text: "Tailspin Toys", value: "Tailspin Toys" },
    { text: "Wingtip Toys", value: "Wingtip Toys" },
]);
buttonListStyle.cellType = buttonListCellType;

// Date DropDown
var dateStyle = {};
dateStyle.cellButtons = [
    {
        imageType: "dropdown",
        command: "openDateTimePicker",
        useButtonStyle: true,
    }
];
dateStyle.dropDowns = [
    {
        type: "dateTimePicker",
        option: {
            showTime: true
        }
    }
];

//tablesheet에 뷰 바인딩
customerTable.fetch().then(function () {
    var view = customerTable.addView("myView", [
        { value: "customerKey", width: 120, caption: "Customer Key" },
        { value: "billToCustomer", width: 400, caption: "Bill To Customer", style: radioButtonListStyle },
        { value: "buyingGroup", width: 250, caption: "Buying Group", style: buttonListStyle },
        { value: "primaryContact", width: 170, caption: "Primary Contact" },
        { value: "validFrom", width: 190, caption: "Valid From", style: dateStyle }
    ]);
    //테이블의 모든 기본 열을 가진 뷰
    sheet.setDataView(view);
});
```

## **열 캡션 다중 헤더**

테이블 시트 뷰에서는 여러 열을 공통 헤더 아래로 그룹화할 수 있도록 열 캡션을 설정할 수 있습니다. 이는 관련 정보를 공통 범주로 묶어 보다 체계적으로 표현하는 데 도움이 됩니다.
**addView** 메서드에 전달되는 `columnInfos` 매개변수의 `"caption"` 속성은 문자열 배열을 받아 테이블 시트의 열 헤더를 여러 행으로 설정할 수 있습니다. 동일한 캡션 값을 가진 열들은 행 방향과 열 방향으로 자동 병합됩니다.
다음 예제에서는 **Customer Key**, **Customer Name**, **Bill To Customer**, **Primary Contact**와 같은 고객 정보를 “**Customer Information**”이라는 공통 캡션 아래에 그룹화하여 표시하며, 각 열의 기존 캡션은 유지됩니다. 이와 유사하게 제품 유효 기간도 하나의 공통 헤더 아래 표시할 수 있습니다.
![](https://gcdocumentsitekrblob.blob.core.windows.net/document-site-files/images/8d606653-16a0-474d-b9dc-e2b4d01c2446/images/ts-columnheader-multiplecol.png)

다음 코드 샘플은 테이블 시트 뷰에서 여러 열에 동일한 캡션을 설정하는 방법을 보여줍니다.

```javascript
//tablesheet 뷰에 바인딩
myTable.fetch().then(function () {
   var view = myTable.addView("myView", [
       { value: "customerKey", width: 130, caption: ["Customer Information", "Customer Key"] }, // Set string array to caption
       { value: "customer", width: 200, caption: ["Customer Information", "Customer"] },
       { value: "billToCustomer", width: 200, caption: ["Customer Information", "Bill To Customer"] },
       { value: "primaryContact", width: 170, caption: ["Customer Information", "Primary Contact"] },
       { value: "validFrom", width: 150, caption: ["Validity", "Valid From"], style: { formatter: "MM/dd/yyyy"} },
       { value: "validTo", width: 180, caption: ["Validity", "Valid To"], style: { formatter: "MM/dd/yyyy"} }
   ]);
   sheet.setDataView(view);
});
```

SpreadJS 디자이너에서 다중 헤더 열 캡션을 적용하는 방법은 [테이블 시트 디자인 모드](/spreadjs/docs/spreadjs-designer-component/spdesignerwork/tablesheet-designer/DesignMode)를 참고하시기 바랍니다.

## **자유 헤더 영역**

테이블 시트 뷰에는 시트를 설명하거나 수식과 스파크라인을 사용하여 집계 데이터를 표시할 수 있는 헤더 영역을 추가할 수 있습니다. 자유 헤더 영역은 테이블 시트 뷰 상단에 할당되며, 아래 이미지와 같이 하나 이상의 행으로 구성될 수 있습니다.
![](https://gcdocumentsitekrblob.blob.core.windows.net/document-site-files/images/8d606653-16a0-474d-b9dc-e2b4d01c2446/images/ts-freeheader.png)
열 헤더의 자유 레이아웃 영역을 구성하려면 [applyFreeHeaderArea](/spreadjs/api/classes/GC.Spread.Sheets.TableSheet.TableSheet#applyFreeHeaderArea) 메서드를 사용합니다. 이 메서드는 **toJSON** 메서드를 사용하여 생성한 워크시트 JSON을 인수로 받습니다. 시트 이름이 포함된 수식을 유지하려면 JSON을 생성하기 전에 **keepUnknownFormulas** 옵션을 `true`로 설정해야 합니다.

다음 코드 샘플은 템플릿 워크시트를 생성하고 테이블 시트 뷰에 자유 헤더 영역을 적용하는 방법을 보여줍니다.

```javascript
//tablesheet에 뷰 바인딩
myTable.fetch().then(function () {
   var view = myTable.addView("myView", [

       { value: "stockItem", width: 300, caption: "Stock Item", headerStyle: headerStyle },
       { value: "unitPrice", width: 120, caption: "Unit Price", headerStyle: headerStyle },
       { value: "taxRate", width: 120, caption: "Tax Rate", headerStyle: headerStyle },
       { value: "recommendedRetailPrice", width: 190, caption: "Recommended Retail Price", style: currencyFormatter, headerStyle: headerStyle, conditionalFormats: [dataBarRule1] },
       { value: "=([@unitPrice] * [@taxRate])/100 + [@unitPrice]", caption: "Actual Retail Price", style: currencyFormatter, width: 190, headerStyle: headerStyle, conditionalFormats: [dataBarRule2] }, // 계산된 열 

   ]);

   // 자유 헤더 영역 json에 대한 템플릿 시트 생성
   var templateSheet = new GC.Spread.Sheets.Worksheet();
   templateSheet.options.keepUnknownFormulas = true;
   var currencyFormatterStyle = new GC.Spread.Sheets.Style();
   currencyFormatterStyle.formatter = formatter;
   currencyFormatterStyle.hAlign = GC.Spread.Sheets.HorizontalAlign.left;
   templateSheet.setRowCount(6);
   var freeHeaderAreaStyle = new GC.Spread.Sheets.Style();
   freeHeaderAreaStyle.backColor = Colors.white;

   templateSheet.setValue(0, 0, "Retail Price Analysis");
   templateSheet.getCell(0, 0).hAlign(GC.Spread.Sheets.HorizontalAlign.left).font("bold 30px \"Calibri\"");
   templateSheet.addSpan(0, 0, 1, 5);
   var titleStyle = new GC.Spread.Sheets.Style();
   titleStyle.backColor = Colors.lightGreen;
   titleStyle.foreColor = Colors.black;
   templateSheet.setStyle(0, -1, titleStyle);
   templateSheet.setRowHeight(0, 80);

   templateSheet.getCell(1, 0)
       .value("Whether a Stock Item's Actual Retail Price is either under or over Recommended Retail Price is automatically calculated.")
       .font("italic 12px \"Calibri\"")
       .foreColor("rgb(120,120,120)")
       .textIndent(2);
   templateSheet.setStyle(1, -1, titleStyle);
   templateSheet.addSpan(1, 0, 1, 5);

   var headerDescriptionStyle = new GC.Spread.Sheets.Style();
   headerDescriptionStyle.backColor = Colors.lightGreen;
   headerDescriptionStyle.borderBottom = new GC.Spread.Sheets.LineBorder(Colors.middleGreen, GC.Spread.Sheets.LineStyle.thick);
   templateSheet.setStyle(2, -1, headerDescriptionStyle);
   templateSheet.addSpan(2, 0, 1, 5);
   templateSheet.setRowHeight(2, 16);

   templateSheet.setValue(3, 0, "Total Recommended Retail Price");
   templateSheet.getCell(3, 0).hAlign(GC.Spread.Sheets.HorizontalAlign.left).font("bold 14px \"Calibri\"").foreColor(Colors.darkGreen);
   templateSheet.addSpan(3, 0, 1, 2);
   templateSheet.setFormula(3, 2, '=SUM(TableSheet1[Recommended Retail Price])');
   templateSheet.getCell(3, 2).hAlign(GC.Spread.Sheets.HorizontalAlign.left).font("bold 14px \"Calibri\"").foreColor(Colors.darkGreen).formatter(formatter);
   templateSheet.setFormula(3, 3, '=HBARSPARKLINE(ROUND(C4/MAX(C4,C5),2),"' + Colors.darkGreen + '",false)');
   templateSheet.addSpan(3, 3, 1, 2);
   templateSheet.setStyle(3, -1, freeHeaderAreaStyle);

   templateSheet.setValue(4, 0, "Total Actual Retail Price");
   templateSheet.getCell(4, 0).hAlign(GC.Spread.Sheets.HorizontalAlign.left).font("bold 14px \"Calibri\"").foreColor(Colors.brown);
   templateSheet.addSpan(4, 0, 1, 2);
   templateSheet.setFormula(4, 2, '=SUM(TableSheet1[Actual Retail Price])');
   templateSheet.getCell(4, 2).hAlign(GC.Spread.Sheets.HorizontalAlign.left).font("bold 14px \"Calibri\"").foreColor(Colors.brown).formatter(formatter);
   var contentBorderStyle = new GC.Spread.Sheets.Style();
   contentBorderStyle.backColor = Colors.white;
   contentBorderStyle.borderBottom = new GC.Spread.Sheets.LineBorder(Colors.brown, GC.Spread.Sheets.LineStyle.thick);
   templateSheet.setStyle(4, -1, contentBorderStyle);
   templateSheet.setFormula(4, 3, '=HBARSPARKLINE(ROUND(C5/MAX(C4,C5),2),"' + Colors.brown + '",false)');
   templateSheet.addSpan(4, 3, 1, 2);

   templateSheet.setFormula(5, 0, '=IF(C4>C5,"Budget is under Total Recommended Retail Price by","Budget is over Total Recommended Retail Price")');
   templateSheet.getCell(5, 0).hAlign(GC.Spread.Sheets.HorizontalAlign.left).font("bold 14px \"Calibri\"").foreColor(Colors.black);
   templateSheet.addSpan(5, 0, 1, 2);
   templateSheet.setFormula(5, 2, '=C4-C5');
   templateSheet.getCell(5, 2).hAlign(GC.Spread.Sheets.HorizontalAlign.left).font("bold 14px \"Calibri\"").formatter(formatter).foreColor(Colors.black);
   templateSheet.setStyle(5, -1, freeHeaderAreaStyle);
   templateSheet.addSpan(5, 2, 1, 3);

   let template = templateSheet.toJSON();
   sheet.applyFreeHeaderArea(template);
   sheet.setDataView(view);
});
```

자유 헤더는 셀에서 수식을 편집하거나 생성할 때 수식 입력 상자를 지원합니다. 이를 통해 테이블 시트 열, 열 범위, 여러 열, 교차 시트 열에 대한 선택 작업을 수행할 수 있습니다.
![](https://gcdocumentsitekrblob.blob.core.windows.net/document-site-files/images/8d606653-16a0-474d-b9dc-e2b4d01c2446/images/ts-freeheader-formulabox.gif)
SpreadJS 디자이너에서 자유 헤더 영역을 적용하는 방법은 [테이블 시트 디자인 모드](/spreadjs/docs/spreadjs-designer-component/spdesignerwork/tablesheet-designer/DesignMode)를 참고하시기 바랍니다.

## 트리거 수식

컬럼에 독립적인 데이터를 저장하면서도 **트리거 수식**을 사용하여 테이블 시트의 컬럼 데이터를 계산할 수 있습니다. 이는 선택된 조건에 따라 데이터를 다시 계산하거나, 새 값이 입력될 때 데이터를 정리하거나, 컬럼에 적절한 기본값을 설정하는 데 유용합니다.
SpreadJS는 트리거를 구성하기 위해 `when`, `formula`, `fields`와 같은 속성을 포함하는 새로운 인터페이스 `GC.Data.ITriggerFormulaOptions`를 제공합니다.

| 이름 | 설명 | 사용법 |
| --- | --- | --- |
| when<br>(필수) | 언제 트리거할지를 나타냅니다. | <ul><li>**onNew**: 새로 생성된 데이터에 수식을 적용.</li><li>**onNewAndUpdate**: 새로 생성되거나 업데이트된 데이터에 수식을 적용.</li></ul> |
| formula<br>(필수) | 적용할 수식을 나타냅니다. | 평가된 수식 결과가 행 데이터에 설정됩니다. |
| fields<br>(필수) | 수식 트리거에 영향을 줄 수 있는 필드를 나타냅니다. | “Any Field”는 모든 필드를 의미합니다.<br>“field1, field2, field3“ 은 쉼표로 구분된 관련 필드를 의미합니다.<br>**when** 값이 **onNew**인 경우 필드 지정이 필요 없으나, 그 외에는 필수입니다. |

다음 코드 샘플은 트리거 수식을 구현한 예제입니다.

```auto
schema: {
        type: "csv",
        columns: {
            SKU: {
                dataType: "string",
                trigger: {
                    when: "onNewAndUpdate",
                    formula: "=UPPER(TRIM([@SKU]))", // SKU 입력 텍스트 포맷팅
                    fields: "SKU" // SKU 필드가 입력되면 트리거가 발동되어 입력 텍스트를 포맷팅
                }
            },
            OrderDate: {
                dataType: "date",
                trigger: {
                    when: "onNew", // 새 주문이 추가될 때 주문 생성일 기록
                    formula: "=TODAY()"
               }
            },
            lastUpdatedTime: {
                dataType: "date",
                trigger: {
                    when: "onNewAndUpdate",
                    formula: "=NOW()",
                    fields: "*" // 모든 필드가 업데이트되거나 주문이 추가되면 갱신 시간 기록
                }
            },
            lastUpdateUser: {
                dataType: "string",
                trigger: {
                    when: "onNewAndUpdate",
                    formula: "=ACTIVEUSER()", // 사용자가 현재 업데이트한 사용자 정의 가능
                    fields: "*" // 모든 필드가 업데이트되거나 주문이 추가되면 업데이트 사용자 기록
                }
            },
            OrderPhone: {
                dataType: "string",
                trigger: {
                    when: "onNewAndUpdate",
                    formula: "=IF(ISBLANK([@OrderPhone]),[@customer.Phone],[@OrderPhone])",
                    fields: "CustomerId,OrderPhone"// 주문 전화번호가 비어 있으면 고객 전화번호 제공
                }
            },
            Amount: {
                dataType: "number",
                trigger: {
                    when: "onNewAndUpdate",
                    formula: "=LET(amount, [@UnitPrice] * [@Quantity], IF([@Quantity] > 30, amount * 0.8, amount))",
                    fields: "UnitPrice,Quantity"  // 단가 또는 수량 변경 시 자동으로 계산된 결과를 Amount 필드에 설정
                }
            },
            CustomerId: {
                lookup: {
                    name: 'customer', columns: [
                        { value: "Id", width: 60 },
                        { value: "CompanyName", width: 200 },
                        { value: "ContactName", width: 140 },
                        { value: "ContactTitle", width: 140 }
                    ]
                }
            },
            Quantity: { dataType: "number" },
            UnitPrice: { dataType: "number" },
        }
    }
});
```

트리거 수식에는 다음과 같은 정책이 적용됩니다:

* 새 행 데이터가 추가되면, **onNew** 및 **onNewAndUpdating** 트리거가 평가되어 업데이트와 유사하게 동작합니다.
* 행의 여러 필드 데이터가 업데이트되면, 트리거는 감시 중인 필드의 값을 변경합니다. 그렇지 않으면 트리거되지 않습니다.
* 필드의 트리거가 자기 자신을 감시하면, 새 값으로 수식을 평가하고 그 결과를 해당 필드의 업데이트 값으로 처리합니다.
* 트리거 수식에서 평가된 값은 해당 필드의 업데이트 값으로 간주되며, 이는 트리거 수식 평가 시 직접 참조될 수 있습니다.
* 컬럼에 기본값과 트리거 수식이 동시에 적용된 경우, **기본값이 더 높은 우선순위**를 가집니다.