# 일반 API

- Human documentation: [https://docs.univer.ai/ko-KR/guides/sheets/features/core/general-api](https://docs.univer.ai/ko-KR/guides/sheets/features/core/general-api)

- Agent Markdown: [https://docs.univer.ai/ko-KR/guides/sheets/features/core/general-api.md](https://docs.univer.ai/ko-KR/guides/sheets/features/core/general-api.md)

- Requested language: `ko-KR`

- Content language: `ko-KR`

- Documentation version: `1.0.4`

- Source: [sheets/features/core/general-api.ko-KR.mdx](https://github.com/dream-num/documentation/blob/dev/content/guides/sheets/features/core/general-api.ko-KR.mdx)

---

Univer에서 사용할 수 있는 Facade API는 문서 유형에 따라 다릅니다. 이 섹션에서는 모든 문서 유형에 적용되는 일반 Facade API를 소개합니다.

## 가져오기

```typescript
import { FUniver } from '@univerjs/core/facade'

const univerAPI = FUniver.newAPI(univer)
```

## 개념

### 명령

Univer의 작업 대부분은 명령 시스템에 등록되고 명령 시스템을 통해 실행됩니다. 이렇게 작업 방식을 통합하면 실행 취소, 다시 실행, 협업 등의 기능을 Univer에서 손쉽게 구현할 수 있습니다.

> [!NOTE]
> 설계에 대한 자세한 내용은 
> 
> [Univer 명령 시스템](/blog/univer#command-system)
> 
> 을 참고하세요.

### 명령

Univer의 작업 대부분은 명령 시스템에 등록되고 명령 시스템을 통해 실행됩니다. 이렇게 작업 방식을 통합하면 실행 취소, 다시 실행, 협업 등의 기능을 Univer에서 손쉽게 구현할 수 있습니다.

> [!NOTE]
> 명령은 Univer 내의 고유한 "이벤트"로 간단히 이해할 수 있습니다. 설계에 대한 자세한 내용은 [명령
> 시스템](/blog/univer#command-system)을 참고하세요.

### 명령 수신

Univer는 명령 실행 전과 실행 후, 두 가지 시점에 명령을 수신할 수 있는 방법을 제공합니다.

* `Event.BeforeCommandExecute`: 명령이 실행되기 전에 사용자 지정 로직을 실행합니다.
* `Event.CommandExecuted`: 명령이 실행된 후에 사용자 지정 로직을 실행합니다.

#### 실행 전 명령 수신

실행 전에는 `Event.BeforeCommandExecute`를 `FUniver.addEvent`에 전달하여 전처리 리스너를 등록합니다.

명령이 실행되면 전처리 리스너 내부의 로직이 실행됩니다.

```typescript
const univerAPI = FUniver.newAPI(univer)

univerAPI.addEvent(univerAPI.Event.BeforeCommandExecute, ({ id, type, params }) => {
  // Custom logic executed before the command is executed
})
```

명령 실행을 막으려면 리스너에서 `event.cancel`을 `true`로 설정합니다.

```typescript
const univerAPI = FUniver.newAPI(univer)

univerAPI.addEvent(univerAPI.Event.BeforeCommandExecute, (event) => {
  event.cancel = true
})
```

#### 실행 후 명령 수신

실행 후에는 `Event.CommandExecuted`를 `FUniver.addEvent`에 전달하여 후처리 리스너를 등록합니다.

명령이 실행되면 후처리 리스너 내부의 로직이 실행됩니다.

```typescript
const univerAPI = FUniver.newAPI(univer)

univerAPI.addEvent(univerAPI.Event.CommandExecuted, ({ id, type, params }) => {
  // Custom logic executed after the command is executed
})
```

### 수신 취소

명령 리스너를 등록하는 메서드는 `IDisposable` 객체를 반환하며, `IDisposable.dispose`를 호출하여 제거할 수 있습니다.

프로그램의 안정성을 높이려면 더 이상 사용하지 않는 리스너를 제거하는 것이 좋습니다.

```typescript
const univerAPI = FUniver.newAPI(univer)

// Register a listener
const disposable = univerAPI.addEvent(univerAPI.Event.BeforeCommandExecute, () => {
  // custom preprocessing logic before the command is executed
})

// Example: Destroy the listener after 1 second
setTimeout(() => {
  // Destroy the listener
  disposable.dispose()
}, 1000)
```

### 명령 실행

명령 ID와 전달해야 할 매개변수를 알고 있다면 `FUniver.executeCommand` 메서드로 명령을 실행할 수도 있습니다.

예를 들어 `sheet.command.set-range-values` 명령을 사용하여 A1 셀의 값을 설정할 수 있습니다.

```typescript
const univerAPI = FUniver.newAPI(univer)

// Execute the command
univerAPI.executeCommand('sheet.command.set-range-values', {
  value: { v: 'Hello, Univer!' },
  range: { startRow: 0, startColumn: 0, endRow: 0, endColumn: 0 },
})
```

## 이벤트 API

Univer는 스프레드시트의 다양한 변경 사항을 수신하고 이에 대응할 수 있는 포괄적인 이벤트 시스템을 제공합니다. 이벤트는 클립보드 작업, 선택 영역 변경, 셀 상호 작용, 시트 수정 등을 포함한 여러 그룹으로 크게 나눌 수 있습니다.

### 이벤트 범주

전체 이벤트 목록: /reference/facade/events

1. **클립보드 이벤트**
   * `BeforeClipboardChange`: 클립보드 내용이 변경되기 전에 실행되며, `params.cancel = true`를 설정하여 변경을 막을 수 있습니다.
   * `BeforeClipboardPaste`: 내용을 붙여넣기 전에 실행되며, `params.cancel = true`를 설정하여 붙여넣기를 막을 수 있습니다.
   * `ClipboardChanged`: 클립보드 내용이 변경된 후에 실행되며, 새 클립보드 내용을 제공합니다.
   * `ClipboardPasted`: 내용을 붙여넣은 후에 실행되며, 붙여넣은 내용을 제공합니다.

2. **선택 영역 이벤트**
   * `SelectionChanged`: 선택 영역이 변경될 때 실행되며, 새 선택 범위 정보를 제공합니다.
   * `SelectionMoveStart`: 선택 영역 이동이 시작될 때 실행되며, 사용자 지정 선택 효과에 유용합니다.
   * `SelectionMoveEnd`: 선택 영역 이동이 끝날 때 실행되며, 최종 선택 범위를 제공합니다.
   * `SelectionMoving`: 선택 영역을 이동하는 동안 실행되며, 실시간 UI 업데이트에 유용합니다.
   * `DragOver`: 셀 위로 드래그할 때 실행됩니다.
   * `Drop`: 선택한 내용을 놓을 때 실행됩니다.

3. **셀 이벤트**
   * `CellClicked`: 셀을 클릭할 때 실행되며, 셀 위치와 내용 정보를 제공합니다.
   * `CellHover`: 셀 위에 마우스 포인터를 올릴 때 실행되며, 툴팁을 표시하는 데 유용합니다.
   * `CellPointerDown`: 셀에서 포인터를 누를 때 실행되며, 드래그 또는 선택 작업을 시작하는 데 사용됩니다.
   * `CellPointerUp`: 셀에서 포인터를 놓을 때 실행되며, 작업을 완료하는 데 사용됩니다.
   * `CellPointerMove`: 셀 위에서 포인터를 움직일 때 실행됩니다.

4. **시트 이벤트**
   * `SheetValueChanged`: 시트 값이 변경될 때 실행되며, 변경된 범위와 새 값을 제공합니다.
   * `SheetZoomChanged`: 확대/축소 수준이 변경될 때 실행되며, 새 확대/축소 비율을 제공합니다.
   * `SheetSkeletonChanged`: 행/열 삽입 또는 삭제 등 시트 구조가 변경될 때 실행됩니다.
   * `BeforeSheetEditStart`: 셀 편집이 시작되기 전에 실행되며, 편집 허용 여부를 제어할 수 있습니다.
   * `SheetEditStarted`: 셀 편집이 시작될 때 실행되며, 사용자 지정 편집기 동작에 유용합니다.
   * `BeforeSheetEditEnd`: 셀 편집이 끝나기 전에 실행되며, 내용 검증에 사용할 수 있습니다.
   * `SheetEditEnded`: 셀 편집이 끝날 때 실행되며, 편집된 값을 제공합니다.
   * `SheetEditChanging`: 셀을 편집하는 동안 실행되며, 실시간 검증 또는 서식 지정에 유용합니다.
   * `Scroll`: 시트를 스크롤할 때 실행되며, 스크롤 위치 정보를 제공합니다.
   * `CrosshairHighlightColorChanged`: 십자선 강조 색상이 변경될 때 실행됩니다.
   * `CrosshairHighlightEnabledChanged`: 십자선 강조가 활성화되거나 비활성화될 때 실행됩니다.

5. **헤더 이벤트**
   * `RowHeaderClick`: 행 헤더를 클릭할 때 실행됩니다.
   * `RowHeaderHover`: 행 헤더 위에 마우스 포인터를 올릴 때 실행됩니다.
   * `ColumnHeaderClick`: 열 헤더를 클릭할 때 실행됩니다.
   * `ColumnHeaderHover`: 열 헤더 위에 마우스 포인터를 올릴 때 실행됩니다.

6. **데이터 유효성 검사 이벤트**
   * `BeforeSheetDataValidationAdd`: 데이터 유효성 검사 규칙을 추가하기 전에 실행됩니다.
   * `SheetDataValidationChanged`: 데이터 유효성 검사 규칙이 변경될 때 실행됩니다.
   * `SheetDataValidatorStatusChanged`: 데이터 유효성 검사 상태가 변경될 때 실행됩니다.

7. **댓글 이벤트**
   * `BeforeCommentAdd`: 댓글을 추가하기 전에 실행됩니다.
   * `CommentAdded`: 댓글을 추가한 후에 실행됩니다.
   * `BeforeCommentUpdate`: 댓글을 업데이트하기 전에 실행됩니다.
   * `CommentUpdated`: 댓글을 업데이트한 후에 실행됩니다.
   * `BeforeCommentDeleted`: 댓글을 삭제하기 전에 실행됩니다.
   * `CommentDeleted`: 댓글을 삭제한 후에 실행됩니다.

8. **데이터 처리 이벤트**
   * `SheetBeforeRangeSort`: 범위를 정렬하기 전에 실행됩니다.
   * `SheetRangeSorted`: 범위를 정렬한 후에 실행됩니다.
   * `SheetBeforeRangeFilter`: 범위를 필터링하기 전에 실행됩니다.
   * `SheetRangeFiltered`: 범위를 필터링한 후에 실행됩니다.
   * `BeforePivotTableAdd`: 피벗 테이블을 추가하기 전에 실행됩니다.
   * `PivotTableAdded`: 피벗 테이블을 추가한 후에 실행됩니다.

### 이벤트 사용

`univerAPI`의 `addEvent` 메서드를 사용하여 이벤트를 수신할 수 있습니다. 기본 패턴은 다음과 같습니다.

```typescript
univerAPI.addEvent(univerAPI.Event.EventName, (params) => {
  // Handle event parameters
})
```

#### 예시: 셀 클릭 수신

```typescript
univerAPI.addEvent(univerAPI.Event.CellClicked, (params) => {
  const { worksheet, workbook, row, column } = params
  console.log(`Cell clicked at row ${row}, column ${column}`)
})
```

#### 예시: 클립보드 작업 모니터링

```typescript
univerAPI.addEvent(univerAPI.Event.BeforeClipboardPaste, (params) => {
  const { text, html } = params
  // If you want to cancel the clipboard paste
  params.cancel = true
})
```

#### 예시: 선택 영역 변경 추적

```typescript
univerAPI.addEvent(univerAPI.Event.SelectionChanged, (params) => {
  const { worksheet, workbook, selections } = params
  console.log('Selection changed:', selections)
})
```

### 이벤트 리스너 취소

이벤트 리스너는 더 이상 필요하지 않을 때 리스너를 제거하는 데 사용할 수 있는 `IDisposable` 객체를 반환합니다.

```typescript
const disposable = univerAPI.addEvent(univerAPI.Event.SheetValueChanged, (params) => {
  // Handle value changes
})

// Later, when you want to remove the listener:
disposable.dispose()
```

### 모범 사례

1. 메모리 누수를 방지하려면 더 이상 필요하지 않은 이벤트 리스너를 항상 제거하세요.
2. 사용 사례에 적합한 이벤트를 사용하고, 일반 이벤트보다 구체적인 이벤트를 우선하세요.
3. 성능을 유지할 수 있도록 이벤트 핸들러를 가볍게 구성하세요.
4. 기본 동작을 막아야 할 때는 `BeforeClipboardChange` 및 이와 유사한 "before" 이벤트를 사용하는 것이 좋습니다.

## 실행 취소 및 다시 실행

### 실행 취소

```typescript
await univerAPI.undo()
```

### 다시 실행

```typescript
await univerAPI.redo()
```

## 시스템 클립보드

`FUniver.copy()` 및 `FUniver.paste()` 메서드를 사용하면 시스템 클립보드에서 데이터를 읽고 쓸 수 있습니다.

> [!NOTE]
> 복사와 붙여넣기는 브라우저의 네이티브 API에 의존합니다. 환경 조건이나 권한이 충분하지 않으면 복사 및 붙여넣기 기능이
> 작동하지 않습니다. 자세한 내용은 [MDN 문서](https://developer.mozilla.org/en-US/docs/Web/API/Clipboard/writeText)를
> 참고하세요.

예시: 시트 범위에서 복사 및 붙여넣기

```typescript
// Prevent failure due to loss of focus when executing copy and paste code in the console,
// this example listens for the cell click event and executes the copy and paste code.
univerAPI.addEvent(univerAPI.Event.CellClicked, async (params) => {
  const fWorkbook = univerAPI.getActiveWorkbook()
  const fWorksheet = fWorkbook.getActiveSheet()

  // Copy the range A1:B2 to the clipboard
  const fRange = fWorksheet.getRange('A1:B2')
  fRange.activate().setValues([
    [1, 2],
    [3, 4],
  ])
  await univerAPI.copy()

  // Paste the copied content to the range C1:D2
  const fRange2 = fWorksheet.getRange('C1')
  fRange2.activate()
  await univerAPI.paste()

  // Check the pasted content
  console.log(fWorksheet.getRange('C1:D2').getValues()) // [[1, 2], [3, 4]]
})
```

또는 명령 시스템을 사용하여 클립보드 기능을 호출할 수 있습니다.

```typescript
import { CopyCommand, PasteCommand } from '@univerjs/ui'

// Copy
univerAPI.executeCommand(CopyCommand.id)
// Paste
univerAPI.executeCommand(PasteCommand.id)
```

## UI

Univer UI를 확장하려면 다음 문서를 참고하세요.

* [Canvas 확장](/blog/custom-canvas)

## WebSocket

Facade API는 URL을 전달하여 WebSocket을 생성할 수 있는 편리한 `createSocket` API를 제공합니다.
그런 다음 open, message, close 및 error 이벤트를 수신하고, `send` 메서드로 메시지를 직접 전송하거나 `close` 메서드로 연결을 닫을 수 있습니다.

Presets 설치 방식에서는 `univerAPI.createSocket(url)`을 바로 사용하세요.
개별 설치 방식을 사용하는 경우 [`@univerjs/network`](https://docs.univer.ai/ko-KR/reference/packages/plugins/univerjs/network.md) 종속성 패키지를 설치하고 `UniverNetworkPlugin` 플러그인을 등록해야 합니다.

```typescript
// ... Omits other plugin imports
import { UniverNetworkPlugin } from '@univerjs/network'

// ... Omits other facade imports
import '@univerjs/network/facade'

// ... Omits other plugin registrations
univer.registerPlugin(UniverNetworkPlugin)
```

간단한 예시는 다음과 같습니다.

```typescript
// Replace the URL with the address of your own WebSocket service
const ws = univerAPI.createSocket('ws://47.100.177.253:8449/ws')

ws.open$.subscribe(() => {
  console.log('websocket opened')
  ws.send('hello')
})

ws.message$.subscribe((message) => {
  console.log('websocket message', message)
  const content = JSON.parse(message.data).content
  if (!content.includes('command')) {
    return
  }

  const commandInfo = JSON.parse(content)
  const { command, options } = commandInfo
  const { id, params } = command

  // Upon receiving collaborative data, it is locally saved
  univerAPI.executeCommand(id, params, options)
})

ws.close$.subscribe(() => {
  console.log('websocket closed')
})

ws.error$.subscribe((error) => {
  console.log('websocket error', error)
})

univerAPI.addEvent(univerAPI.Event.CommandExecuted, ({ id, type, params, options }) => {
  // Only synchronize local mutations
  if (
    type !== univerAPI.Enum.CommandType.MUTATION ||
    options?.fromCollab ||
    options?.onlyLocal ||
    id === 'doc.mutation.rich-text-editing'
  ) {
    return
  }

  const commandInfo = JSON.stringify({ command: { id, type, params }, options: { fromCollab: true } })
  ws.send(commandInfo)
})
```

참고: Univer를 시작할 때 unitID가 있어야 합니다. unitID를 지정하지 않으면 협업 기능이 작동하지 않습니다.

## 수식 등록

Facade API를 사용하면 현재 Univer 인스턴스에 사용자 지정 수식을 빠르고 간편하게 등록할 수 있습니다.

다음 예시와 같이 [`registerFunction`](https://docs.univer.ai/ko-KR/reference/facade/formula.md#registerfunction)을 사용하면 `CUSTOMSUM` 수식에 필요한 알고리즘, 이름 및 설명을 수식 플러그인에 한 번에 등록할 수 있습니다. 실행한 후에는 해당 수식을 사용할 수 있습니다. 빈 셀에 `=CUSTOMSUM`을 입력하면 안내가 표시됩니다.

```typescript
// Registered formulas support lambda functions
const formulaEngine = univerAPI.getFormula()
formulaEngine.registerFunction(
  'CUSTOMSUM',
  (...variants) => {
    let sum = 0
    const last = variants[variants.length - 1]

    if (last.isLambda && last.isLambda()) {
      variants.pop()
      const variantsList = variants.map((variant) => (Array.isArray(variant) ? variant[0][0] : variant))
      sum += last.executeCustom(...variantsList).getValue()
    }

    for (const variant of variants) {
      sum += Number(variant) || 0
    }

    return sum
  },
  'Adds its arguments',
)

// Use the function in a cell
const fWorkbook = univerAPI.getActiveWorkbook()
const fWorksheet = fWorkbook.getActiveSheet()
const cellA1 = fWorksheet.getRange('A1')
cellA1.setValue(1)
const cellA2 = fWorksheet.getRange('A2')
cellA2.setValue(2)
const cellA3 = fWorksheet.getRange('A3')
cellA3.setValue({ f: '=CUSTOMSUM(A1,A2,LAMBDA(x,y,x*y))' })

// A3 will display: 5
formulaEngine.calculationEnd((functionsExecutedState) => {
  if (functionsExecutedState === 3) {
    console.log(cellA3.getValue()) // 5
  }
})
```

수식 등록을 해제해야 한다면 `dispose` 메서드를 호출할 수 있습니다.

```typescript
const functionDisposable = formulaEngine.registerFunction({
  // calculate
})

// Unregister the function
functionDisposable.dispose()
```

더 완전한 다국어 콘텐츠와 설명을 제공하려면 `locales` 및 `description` 필드를 구성할 수도 있습니다. 예시는 다음과 같습니다.

```typescript
formulaEngine.registerFunction(
  'CUSTOMSUM',
  (...variants) => {
    let sum = 0
    const last = variants[variants.length - 1]

    if (last.isLambda && last.isLambda()) {
      variants.pop()
      const variantsList = variants.map((variant) => (Array.isArray(variant) ? variant[0][0] : variant))
      sum += last.executeCustom(...variantsList).getValue()
    }

    for (const variant of variants) {
      sum += Number(variant) || 0
    }

    return sum
  },
  {
    description: {
      functionName: 'CUSTOMSUM',
      description: 'formulaCustom.CUSTOMSUM.description',
      abstract: 'formulaCustom.CUSTOMSUM.abstract',
      functionParameter: [
        {
          name: 'formulaCustom.CUSTOMSUM.functionParameter.number1.name',
          detail: 'formulaCustom.CUSTOMSUM.functionParameter.number1.detail',
          example: 'A1:A20',
          require: 1,
          repeat: 0,
        },
        {
          name: 'formulaCustom.CUSTOMSUM.functionParameter.number2.name',
          detail: 'formulaCustom.CUSTOMSUM.functionParameter.number2.detail',
          example: 'B2:B10',
          require: 0,
          repeat: 1,
        },
      ],
    },
    locales: {
      zhCN: {
        formulaCustom: {
          CUSTOMSUM: {
            description: '将单个值、单元格引用或是区域相加，或者将三者的组合相加。',
            abstract: '求参数的和',
            functionParameter: {
              number1: {
                name: '数值1',
                detail:
                  '要相加的第一个数字。 该数字可以是 4 之类的数字，B6 之类的单元格引用或 B2:B8 之类的单元格范围。',
              },
              number2: {
                name: '数值2',
                detail: '这是要相加的第二个数字。 可以按照这种方式最多指定 255 个数字。',
              },
            },
          },
        },
      },
      enUS: {
        formulaCustom: {
          CUSTOMSUM: {
            description: `You can add individual values, cell references or ranges or a mix of all three.`,
            abstract: `Adds its arguments`,
            functionParameter: {
              number1: {
                name: 'number1',
                detail:
                  'The first number you want to add. The number can be like 4, a cell reference like B6, or a cell range like B2:B8.',
              },
              number2: {
                name: 'number2',
                detail: 'This is the second number you want to add. You can specify up to 255 numbers in this way.',
              },
            },
          },
        },
      },
    },
  },
)
```

참고

* `func`에는 계산 수식의 구체적인 알고리즘과 이름 매핑을 작성합니다. 입력 매개변수는 사용자가 수식을 사용할 때 입력한 내용이며, 숫자, 문자열, 불리언 값 또는 배열일 수 있습니다.
* `description`은 사용자 지정 수식의 설명을 설정합니다.
* `locales` 아래에 여러 언어를 설정할 수 있습니다. 명명 규칙은 [LocaleType](https://docs.univer.ai/ko-KR/reference/enums/locale-type.md)을 참고하세요. `functionList` 아래에 여러 수식의 번역을 추가할 수 있습니다. 필드에 대한 자세한 설명은 [Formula Engine에 수식을 추가하는 방법](/blog/custom-formula#how-to-add-formulas-in-formula-engine) 섹션을 참고하세요.

> [!NOTE]
> 수식 시스템에서 제공하는 알고리즘을 재사용하고 수식 알고리즘의 기능을 확장하려면 플러그인 구성을 통해 사용자 지정
> 수식을 등록할 수 있습니다. 자세한 튜토리얼은 [사용자 지정 수식](/blog/custom-formula)을 참고하세요.

## 열거형 API

Facade API는 개발 중에 사용할 수 있는 몇 가지 자주 쓰이는 열거형을 제공합니다. 예시는 다음과 같습니다.

```typescript
console.log(univerAPI.Enum)
console.log(univerAPI.Enum.UniverInstanceType.UNIVER_SHEET)
console.log(univerAPI.Enum.LifecycleStages.Rendered)
```

## 유틸리티 메서드 API

Facade API는 개발 중에 사용할 수 있는 몇 가지 자주 쓰이는 유틸리티 메서드를 제공합니다. 예시는 다음과 같습니다.

```typescript
console.log(univerAPI.Util)
console.log(univerAPI.Util.tools.chatAtABC(10))
console.log(univerAPI.Util.tools.ABCatNum('K'))
```
