# 워크시트 외부에서 수식 계산

## Content

SpreadJS는 워크시트 셀 외부에서도 수식을 평가하고 계산을 수행할 수 있는 강력한 계산 엔진을 제공합니다. [`GC.Spread.Sheets.CalcEngine`](/spreadjs/api/v18/modules/GC.Spread.Sheets.CalcEngine) 모듈은 워크시트에 직접 설정하지 않고도 수식을 파싱, 평가, 변환할 수 있는 여러 유틸리티 메서드를 포함하고 있습니다.
판매 데이터가 포함된 테이블을 준비합니다.
![image.9916ad.png](https://gcdocumentsitekrblob.blob.core.windows.net/document-site-files/images/216c2e7e-5246-4c37-8843-3854aa344a6a/image.9916ad.e26626.png?width=400)

## evaluateFormula: 지정된 수식 문자열을 평가하고 결과를 반환합니다.

**사용 사례**:

* 코드에서 동적으로 수식 결과를 계산해야 할 때 (예: 백엔드에서 데이터를 처리하거나 수식 논리를 일괄 검증할 때), 워크시트 렌더링이나 사용자 동작에 의존하지 않고 계산 가능.
* 배열 수식의 여러 결과를 모두 유지해야 할 때, `preserveArrayResult: true`를 사용하여 전체 배열을 얻을 수 있음.
* 코드에서 R1C1 형식으로 수식을 생성하거나 파싱할 때.

**메서드**: `evaluateFormula(context, formula, baseRow?, baseColumn?, useR1C1?, preserveArrayResult?)`

#### 매개변수:

* **`context`** *(Object)* – 평가 컨텍스트 (일반적으로 활성 시트 객체).
* **`formula`** *(string)* – 평가할 수식 문자열.
* **`baseRow`** *(number, optional)* – 상대 참조의 기준이 되는 행 인덱스.
* **`baseColumn`** *(number, optional)* – 상대 참조의 기준이 되는 열 인덱스.
* **`useR1C1`** *(boolean, optional)* – R1C1 참조 스타일 사용 여부 (기본: `false`).
* **`preserveArrayResult`** *(boolean, optional)* – `true`이면 배열 결과 반환, 아니면 단일 값 반환.

**예제 1**: 계산 결과 직접 가져오기

```javascript
var sheet = spread.getActiveSheet();
GC.Spread.Sheets.CalcEngine.evaluateFormula(sheet, '=SUM(Table1[[#Data],[Units]])')
//결과: 84878
```

**예제 2:** baseRow, baseColumn으로 상대 참조 사용

```javascript
var sheet = spread.getActiveSheet();
GC.Spread.Sheets.CalcEngine.evaluateFormula(sheet, '=Table1[@[Sales Rep]]', 2, 0);
//결과: Tom
GC.Spread.Sheets.CalcEngine.evaluateFormula(sheet, '=Table1[@[Sales Rep]]', 5, 0);
//결과: Sal
GC.Spread.Sheets.CalcEngine.evaluateFormula(sheet, '=Table1[@[Sales Rep]]', 12, 0);
//결과: Amy
```

**예제 3:** R1C1 스타일 사용

```javascript
var sheet = spread.getActiveSheet();
GC.Spread.Sheets.CalcEngine.evaluateFormula(sheet, '=R[3]C[3]', 0, 0, true);
//결과: Grape
GC.Spread.Sheets.CalcEngine.evaluateFormula(sheet, '=R[3]C[3]', 2, 1, true);
//결과: 5323
```

**예제 4:** 배열 결과를 preserveArrayResult로 가져오기

```javascript
var sheet = spread.getActiveSheet();
spread.options.allowDynamicArray = true; 
GC.Spread.Sheets.CalcEngine.evaluateFormula(sheet, '=UNIQUE(Table1[[#Data],[Product]])')
//결과: Apple
GC.Spread.Sheets.CalcEngine.evaluateFormula(sheet, '=UNIQUE(Table1[[#Data],[Product]])', null, null, null, true)
//결과: [['Apple'],['Grape'],['Pear'],['Banana']]
```

***

## evaluateFormulaAsync: 지정된 수식 문자열을 평가하고 결과를 Promise로 반환합니다.

>type=note
> **참고:**
> AsyncFunctionEvaluateMode.onInterval 또는 REFRESH 함수는 지원하지 않습니다.

**사용 사례**:

* 비동기 작업을 수행하는 사용자 정의 함수를 사용할 때.
* API 또는 데이터를 비동기로 반환하는 데이터 소스와 통합할 때.
* 사용자 인터페이스가 멈추지 않도록 장시간 계산을 수행할 때.

**메서드**: `evaluateFormulaAsync(context: Object, formula: string, baseRow?: number, baseColumn?: number)`

### 매개변수:

* **`context`**: 평가 컨텍스트 객체. 대부분의 경우 활성 시트 객체(`GC.Spread.Sheets.Worksheet`)를 전달합니다.
* **`formula`**: 평가할 수식 문자열.

>type=note
> **참고:**
> A1 참조 스타일만 지원합니다.

* **`baseRow`** (선택): 수식의 기준이 되는 행 인덱스. 셀 외부에서 상대 참조를 사용할 때 필요합니다.
* **`baseColumn`** (선택): 수식의 기준이 되는 열 인덱스. 셀 외부에서 상대 참조를 사용할 때 필요합니다.

**반환값:**
수식 평가 결과를 가진 Promise를 반환합니다.

>type=note
> **참고:**
> 여러 번 `context.setAsyncResult`를 호출하더라도 Promise는 최초 한 번만 해결됩니다.

**예제:** 간단한 비동기 사용자 정의 함수 `ASUM` (Asynchronous Sum) 정의

```javascript
// 비동기 함수 ASUM 정의
var asyncSum = function () {};
asyncSum.prototype = new GC.Spread.CalcEngine.Functions.AsyncFunction("ASUM", 1, 255);

// 계산 중 표시될 기본값
asyncSum.prototype.defaultValue = function () {
    return "Loading...";
};

// 비동기 평가 로직
asyncSum.prototype.evaluateAsync = function (context) {
    var args = arguments;
    setTimeout(function () {
        var result = 0;
        // 인수는 1번 인덱스부터 시작 (0번 인덱스는 context)
        for (var i = 1; i < args.length; i++) {
            result += args[i];
        }
        // context를 통해 최종 결과 설정
        context.setAsyncResult(result * 2); // 예: 합계를 두 배로
    }, 2000); // 2초 지연 시뮬레이션
};

// 사용자 정의 함수 전역 등록
GC.Spread.CalcEngine.Functions.defineGlobalCustomFunction("ASUM", new asyncSum());
```

**사용 예제 1: 기본 사용**

```javascript
GC.Spread.Sheets.CalcEngine.evaluateFormulaAsync(sheet, "ASUM(2, 2)", 0, 0)
    .then(result => {
        console.log("ASUM(2, 2) 결과:", result); // 약 2초 후: ASUM(2, 2) 결과: 8
    })
    .catch(error => {
        console.error("평가 오류:", error);
    });
//결과: Promise{8}
```

**사용 예제 2: 병렬 비동기 함수**

```javascript
GC.Spread.Sheets.CalcEngine.evaluateFormulaAsync(sheet, "ASUM(2, 2) + ASUM(2, 2)", 0, 0)
    .then(result => {
        console.log("ASUM(2, 2) + ASUM(2, 2) 결과:", result); // 약 2초 후: 16
    })
    .catch(error => {
        console.error("평가 오류:", error);
    });
//결과: Promise{16}
```

**사용 예제 3: 연속(중첩) 비동기 함수**

```javascript
GC.Spread.Sheets.CalcEngine.evaluateFormulaAsync(sheet, "ASUM(ASUM(2, 2), 2)", 0, 0)
    .then(result => {
        console.log("ASUM(ASUM(2, 2), 2) 결과:", result); // 약 4초 후: 20
    })
    .catch(error => {
        console.error("평가 오류:", error);
    });
//결과: Promise{20}
```

**사용 예제 4: 일반(동기) 수식 평가**

```javascript
GC.Spread.Sheets.CalcEngine.evaluateFormulaAsync(sheet, "VSTACK(ASUM(2, 2), ASUM(2))", 0, 0)
    .then(result => {
        console.log("VSTACK 결과:", result); // 약 2초 후: [[8],[4]]
    })
    .catch(error => {
        console.error("평가 오류:", error);
    });
//결과: Promise{[[8],[4]]}
```

**사용 예제 5: 셀/범위 참조 평가**

```javascript
GC.Spread.Sheets.CalcEngine.evaluateFormulaAsync(sheet, "1 + 23", 0, 0)
    .then(result => {
        console.log("일반 수식 결과:", result); // 결과: 24
    })
    .catch(error => {
        console.error("평가 오류:", error);
    });
//결과: Promise{24}
```

***

## expressionToFormula: 표현식을 수식 문자열로 변환

**사용 사례**:

* 복잡한 수식을 동적으로 생성할 때 (문자열 연결 위험 회피), 일반적으로 formulaToExpression과 함께 사용.
* 사용자 정의 이름의 표현식을 파싱하고 해당 수식 생성.

**메서드**: `expressionToFormula(context, expression, baseRow?, baseColumn?, useR1C1?)`

#### 매개변수:

* **`context`** *(Object)* – 컨텍스트 (일반적으로 활성 시트 객체).
* **`expression`** *(GC.Spread.CalcEngine.Expression)* – 변환할 표현식 트리.
* **`baseRow`** *(number, optional)* – 상대 참조 기준 행.
* **`baseColumn`** *(number, optional)* – 상대 참조 기준 열.
* **`useR1C1`** *(boolean, optional)* – R1C1 스타일 사용 여부 (기본: `false`).

**예제: 사용자 정의 이름 표현식을 수식으로 변환**

```javascript
var sheet = spread.getActiveSheet();
sheet.addCustomName('averageUnits', '=AVERAGE(Table1[[#Data],[Units]])');
var cname = sheet.getCustomName("averageUnits");
var expr = cname.getExpression();
GC.Spread.Sheets.CalcEngine.expressionToFormula(sheet, expr);
//결과: AVERAGE(Table1[[#Data],[Units]])
```

***

## formulaToExpression: 수식 문자열을 표현식으로 파싱

**사용 사례**:

* 기존 수식을 프로그램적으로 파싱하고, 일부 인수나 로직 수정 후 다시 수식 생성.
* 수식에서 참조되는 셀, 함수, 종속성을 확인하여 데이터 흐름 분석 또는 감사.
* R1C1과 A1 스타일 간 변환 시, 먼저 수식을 표현식 트리로 변환 후 재구성.
* 문자열만으로 수식 검증하지 않고 표현식을 사용하여 보다 정확한 분석 가능.

**메서드**: `formulaToExpression(context, formula, baseRow?, baseColumn?, useR1C1?)`

#### 매개변수:

* **`context`** *(Object)* – 평가 컨텍스트 (일반적으로 활성 시트 객체).
* **`formula`** *(string)* – 평가할 수식 문자열.
* **`baseRow`** *(number, optional)* – 상대 참조 기준 행.
* **`baseColumn`** *(number, optional)* – 상대 참조 기준 열.
* **`useR1C1`** *(boolean, optional)* – R1C1 스타일 사용 여부 (기본: `false`).

**예제: 수식을 표현식 객체로 변환**

```javascript
var sheet = spread.getActiveSheet();
var expr = GC.Spread.Sheets.CalcEngine.formulaToExpression(sheet, '=CONCAT(Table1[@Region],Table1[@[Sales Rep]],Table1[@Product])', 4, 0)
//결과:
//expr.functionName is CONCAT
//expr.arguments.length is 3
```

***

## formulaToRanges: 수식 문자열을 참조된 셀 범위 배열로 변환

**사용 사례**:

* 수식이 참조하는 모든 셀 범위를 신속히 가져와 데이터 종속성 그래프 또는 영향 분석 수행.
* UI에서 수식이 참조하는 모든 셀을 하이라이트하여 사용자가 직관적으로 이해하도록 도움.
* 수식의 논리 구조 대신 물리적 위치(row, column) 정보가 필요할 때, formulaToExpression보다 더 직접적이고 효율적.

**메서드**: `formulaToRanges(sheet, formula, baseRow?, baseCol?)`

#### 매개변수:

* **`sheet`** *(GC.Spread.Sheets.Worksheet)* – 기준 워크시트.
* **`formula`** *(string)* – 분석할 수식.
* **`baseRow`** *(number, optional)* – 상대 참조 기준 행.
* **`baseColumn`** *(number, optional)* – 상대 참조 기준 열.

**예제: 수식 참조 범위 가져오기**

```javascript
var sheet = spread.getActiveSheet();
var result = GC.Spread.Sheets.CalcEngine.formulaToRanges(sheet, '=SUM(A1,A2,A5:C8)');
//결과:
//result.sheetName is Sheet1
//result.ranges:[
//  Range {row: 0, rowCount: 1, col: 0, colCount: 1}
//  Range {row: 1, rowCount: 1, col: 0, colCount: 1}
//  Range {row: 4, rowCount: 4, col: 0, colCount: 3}
//  Range {row: 2, rowCount: 16, col: 4, colCount: 1}
//]
```

***

## rangesToFormula: 범위 배열을 수식 문자열로 변환

**사용 사례**:

* 런타임 데이터 기반으로 범위 참조 수식을 동적으로 생성할 때.
* 코드에서 워크시트 참조 처리 시 복잡한 문자열 수동 연결을 피할 때.
* 상대/절대 참조를 유연하게 전환할 필요가 있을 때.

**메서드**: `rangesToFormula(ranges, baseRow?, baseCol?, rangeReferenceRelative?, useR1C1?)`

#### 매개변수:

* **`ranges`** *(GC.Spread.Sheets.Range[])* – 변환할 범위 배열.
* **`baseRow`** *(number, optional)* – 상대 참조 기준 행.
* **`baseColumn`** *(number, optional)* – 상대 참조 기준 열.
* **`rangeReferenceRelative`** *(RangeReferenceRelative, optional)* – 참조 형태 지정 (절대/상대).
* **`useR1C1`** *(boolean, optional)* – R1C1 스타일 사용 여부.

**예제: 범위 배열을 수식으로 변환**

```javascript
var ranges = [
    new GC.Spread.Sheets.Range(0,0,1,1),
    new GC.Spread.Sheets.Range(3,3,5,5),
];
GC.Spread.Sheets.CalcEngine.rangesToFormula(ranges,0,0,GC.Spread.Sheets.CalcEngine.RangeReferenceRelative.allRelative);
//결과: 'A1, D4:H8'
GC.Spread.Sheets.CalcEngine.rangesToFormula(ranges,0,0,GC.Spread.Sheets.CalcEngine.RangeReferenceRelative.allAbsolute);
//결과: '$A$1, $D$4:$H$8'
GC.Spread.Sheets.CalcEngine.rangesToFormula(ranges,0,0,GC.Spread.Sheets.CalcEngine.RangeReferenceRelative.colRelative);
//결과: 'A$1, D$4:H$8'
GC.Spread.Sheets.CalcEngine.rangesToFormula(ranges,0,0,GC.Spread.Sheets.CalcEngine.RangeReferenceRelative.allRelative, true);
//결과: 'RC, R[3]C[3]:R[7]C[7]'
```