# 댓글

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

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

- Requested language: `ko-KR`

- Content language: `ko-KR`

- Documentation version: `1.0.4`

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

---

#### Package metadata

```json
{
  "mobile": true,
  "preset": [
    {
      "client": "@univerjs/preset-sheets-thread-comment",
      "locale": "@univerjs/preset-sheets-thread-comment/locales/ko-KR",
      "style": "@univerjs/preset-sheets-thread-comment/lib/index.css"
    }
  ],
  "plugins": [
    {
      "client": "@univerjs/thread-comment"
    },
    {
      "client": "@univerjs/thread-comment-ui",
      "locale": "@univerjs/thread-comment-ui/locale/ko-KR",
      "style": "@univerjs/thread-comment-ui/lib/index.css"
    },
    {
      "client": "@univerjs/sheets-thread-comment",
      "facade": "@univerjs/sheets-thread-comment/facade"
    },
    {
      "client": "@univerjs/sheets-thread-comment-ui",
      "locale": "@univerjs/sheets-thread-comment-ui/locale/ko-KR",
      "style": "@univerjs/sheets-thread-comment-ui/lib/index.css"
    }
  ],
  "server": "optional"
}
```

댓글 기능을 사용하면 문서에 댓글과 답글을 추가할 수 있어 팀원 간의 소통과 협업이 쉬워집니다.

## 플러그인 모드

### 설치

#### npm

```bash
npm install @univerjs/thread-comment @univerjs/thread-comment-ui @univerjs/sheets-thread-comment @univerjs/sheets-thread-comment-ui
```

#### pnpm

```bash
pnpm add @univerjs/thread-comment @univerjs/thread-comment-ui @univerjs/sheets-thread-comment @univerjs/sheets-thread-comment-ui
```

#### yarn

```bash
yarn add @univerjs/thread-comment @univerjs/thread-comment-ui @univerjs/sheets-thread-comment @univerjs/sheets-thread-comment-ui
```

#### bun

```bash
bun add @univerjs/thread-comment @univerjs/thread-comment-ui @univerjs/sheets-thread-comment @univerjs/sheets-thread-comment-ui
```

### 사용법

```typescript
import { LocaleType, mergeLocales, Univer } from '@univerjs/core'
import { UniverSheetsThreadCommentPlugin } from '@univerjs/sheets-thread-comment' // [!code ++]
import { UniverSheetsThreadCommentUIPlugin } from '@univerjs/sheets-thread-comment-ui' // [!code ++]
import SheetsThreadCommentUIEnUS from '@univerjs/sheets-thread-comment-ui/locale/en-US' // [!code ++]
import { UniverThreadCommentPlugin } from '@univerjs/thread-comment' // [!code ++]
import { UniverThreadCommentUIPlugin } from '@univerjs/thread-comment-ui' // [!code ++]
import ThreadCommentUIEnUS from '@univerjs/thread-comment-ui/locale/en-US' // [!code ++]

import '@univerjs/sheets-thread-comment-ui/lib/index.css'
import '@univerjs/thread-comment-ui/lib/index.css' // [!code ++]

import '@univerjs/sheets-thread-comment/facade' // [!code ++]

const univer = new Univer({
  locale: LocaleType.EN_US,
  locales: {
    [LocaleType.EN_US]: mergeLocales(
      ThreadCommentUIEnUS, // [!code ++]
      SheetsThreadCommentUIEnUS, // [!code ++]
    ),
  },
})

univer.registerPlugin(UniverThreadCommentPlugin) // [!code ++]
univer.registerPlugin(UniverThreadCommentUIPlugin) // [!code ++]
univer.registerPlugin(UniverSheetsThreadCommentPlugin) // [!code ++]
univer.registerPlugin(UniverSheetsThreadCommentUIPlugin) // [!code ++]
```

#### 공동 편집 기능과 함께 사용하기

[공동 편집](https://docs.univer.ai/ko-KR/guides/sheets/features/collaboration.md) 기능을 사용하는 경우 다음과 같이 구성하세요.

#### npm

```bash
npm install @univerjs-pro/thread-comment-datasource
```

#### pnpm

```bash
pnpm add @univerjs-pro/thread-comment-datasource
```

#### yarn

```bash
yarn add @univerjs-pro/thread-comment-datasource
```

#### bun

```bash
bun add @univerjs-pro/thread-comment-datasource
```

```typescript
import { UniverThreadCommentDataSourcePlugin } from '@univerjs-pro/thread-comment-datasource' // [!code ++]

univer.registerPlugin(UniverThreadCommentDataSourcePlugin) // [!code ++]
```

## 모바일 지원

먼저 [모바일 에디터](https://docs.univer.ai/ko-KR/guides/sheets/getting-started/installation.md#mobile-support)를 구성하세요. 이 페이지의 플러그인 모드 초기화 코드에서 아래 데스크톱 등록을 같은 패키지의 모바일 버전으로 바꾸고 **문서를 열기 전에** 완료하세요. 모델 플러그인, 스타일, 언어 파일과 Facade 가져오기는 유지하며 두 버전을 동시에 등록하지 마세요.

| 패키지                                  | 데스크톱 등록                             | 모바일 등록                                    |
| ------------------------------------ | ----------------------------------- | ----------------------------------------- |
| `@univerjs/sheets-thread-comment-ui` | `UniverSheetsThreadCommentUIPlugin` | `UniverSheetsThreadCommentMobileUIPlugin` |
| `@univerjs/thread-comment-ui`        | `UniverThreadCommentUIPlugin`       | `UniverThreadCommentMobileUIPlugin`       |

## 프리셋 모드

### 설치

#### npm

```bash
npm install @univerjs/preset-sheets-thread-comment
```

#### pnpm

```bash
pnpm add @univerjs/preset-sheets-thread-comment
```

#### yarn

```bash
yarn add @univerjs/preset-sheets-thread-comment
```

#### bun

```bash
bun add @univerjs/preset-sheets-thread-comment
```

### 사용법

```typescript
import { UniverSheetsCorePreset } from '@univerjs/preset-sheets-core'
import UniverPresetSheetsCoreEnUS from '@univerjs/preset-sheets-core/locales/en-US'
import { UniverSheetsThreadCommentPreset } from '@univerjs/preset-sheets-thread-comment' // [!code ++]
import UniverPresetSheetsThreadCommentEnUS from '@univerjs/preset-sheets-thread-comment/locales/en-US' // [!code ++]
import { createUniver, LocaleType, mergeLocales } from '@univerjs/presets'

import '@univerjs/preset-sheets-core/lib/index.css'
import '@univerjs/preset-sheets-thread-comment/lib/index.css' // [!code ++]

const { univerAPI } = createUniver({
  locale: LocaleType.EN_US,
  locales: {
    [LocaleType.EN_US]: mergeLocales(
      UniverPresetSheetsCoreEnUS,
      UniverPresetSheetsThreadCommentEnUS, // [!code ++]
    ),
  },
  presets: [
    UniverSheetsCorePreset(),
    UniverSheetsThreadCommentPreset(), // [!code ++]
  ],
})
```

## Facade API

전체 Facade API 타입 정의는 [FacadeAPI](https://docs.univer.ai/ko-KR/reference/facade/thread-comment.md)에서 확인할 수 있습니다.

### 가져오기

> [!INFO: 플러그인 모드 참고 사항]
> 플러그인 모드에서만 Facade 패키지를 직접 가져와야 합니다. 프리셋 모드에는 해당 Facade 패키지가 이미 포함되어 있으므로 추가로 가져올 필요가 없습니다.

```typescript
import '@univerjs/sheets-thread-comment/facade'
```

### 셀 댓글 만들기

`univerAPI.newTheadComment()`를 사용하여 새 댓글 빌더를 만들 수 있습니다. 이 메서드는 `FTheadCommentBuilder` 인스턴스를 반환하며, 메서드를 체이닝하여 댓글의 여러 속성을 설정할 수 있습니다.

다음은 [`FTheadCommentBuilder`](https://docs.univer.ai/ko-KR/reference/facade/thead-comment-builder.md)의 일부 멤버 메서드입니다.

| 메서드         | 설명                |
| ----------- | ----------------- |
| setContent  | 댓글 내용을 설정합니다      |
| setPersonId | 댓글 작성자의 ID를 설정합니다 |
| setDateTime | 댓글의 날짜와 시간을 설정합니다 |
| setId       | 댓글 ID를 설정합니다      |
| setThreadId | 댓글 스레드 ID를 설정합니다  |

```typescript
// Create a new comment
const richText = univerAPI.newRichText().insertText('hello univer')
const commentBuilder = univerAPI.newTheadComment()
  .setContent(richText)

console.log(commentBuilder.content.toPlainText())

// Add the comment to cell A1
const fWorkbook = univerAPI.getActiveWorkbook()
const fWorksheet = fWorkbook.getActiveSheet()
const cell = fWorksheet.getRange('A1')
const result = await cell.addCommentAsync(commentBuilder)
```

### 셀 댓글 가져오기

다음 메서드를 사용하여 댓글을 가져올 수 있습니다. 각 메서드는 `FThreadComment` 인스턴스를 반환하며, 이를 통해 댓글을 업데이트하거나 삭제 또는 해결할 수 있습니다.

* `FWorkbook.getComments()`: 통합 문서의 모든 댓글을 가져옵니다
* `FfWorksheet.getComments()`: 워크시트의 모든 댓글을 가져옵니다
* `FRange.getComment()`: 범위의 왼쪽 위 셀에 있는 댓글을 가져옵니다
* `FRange.getComments()`: 범위의 모든 댓글을 가져옵니다

다음은 [`FThreadComment`](https://docs.univer.ai/ko-KR/reference/facade/thread-comment.md)의 일부 멤버 메서드입니다.

| 메서드            | 설명                   |
| -------------- | -------------------- |
| getIsRoot      | 댓글이 루트 댓글인지 확인합니다    |
| getCommentData | 댓글 데이터를 가져옵니다        |
| getReplies     | 댓글의 답글 목록을 가져옵니다     |
| getRange       | 댓글의 범위를 가져옵니다        |
| getRichText    | 댓글의 리치 텍스트 내용을 가져옵니다 |
| deleteAsync    | 댓글과 답글을 삭제합니다        |
| updateAsync    | 댓글 내용을 업데이트합니다       |
| resolveAsync   | 댓글을 해결합니다            |
| replyAsync     | 댓글에 답글을 작성합니다        |

```typescript
// Get all comments in the workbook
const fWorkbook = univerAPI.getActiveWorkbook()
fWorkbook.getComments()

// Get all comments in the active worksheet
const fWorksheet = fWorkbook.getActiveSheet()
fWorksheet.getComments()

const fRange = fWorksheet.getRange('A1:B2')
// Get the comment of cell A1
fRange.getComment()

// Get all comments in the range A1:B2
fRange.getComments()
```

### 셀 댓글 지우기

* `FWorkbook.clearComments()`: 통합 문서의 모든 댓글을 지웁니다
* `FWorksheet.clearComments()`: 워크시트의 모든 댓글을 지웁니다
* `FRange.clearCommentAsync()`: 범위의 왼쪽 위 셀에 있는 댓글을 지웁니다
* `FRange.clearCommentsAsync()`: 범위의 모든 댓글을 지웁니다

```typescript
// Clear all comments in the workbook
const fWorkbook = univerAPI.getActiveWorkbook()
await fWorkbook.clearComments()

// Clear all comments in the active worksheet
const fWorksheet = fWorkbook.getActiveSheet()
await fWorksheet.clearComments()

const fRange = fWorksheet.getRange('A1:B2')

// Clear the comment of cell A1
await fRange.clearCommentAsync()

// Clear all comments in the range A1:B2
await fRange.clearCommentsAsync()
```

### 댓글 답글 목록 가져오기

```typescript
const fWorkbook = univerAPI.getActiveWorkbook()
const fWorksheet = fWorkbook.getActiveSheet()

// Get all comments in the worksheet
const comments = fWorksheet.getComments()
comments.forEach((comment) => {
  // If the comment is a root comment, get its replies
  if (comment.getIsRoot()) {
    const replies = comment.getReplies()
    replies.forEach((reply) => {
      console.log(reply.getCommentData())
    })
  }
})
```

### 댓글 업데이트/삭제/해결/답글 작성

```typescript
const fWorkbook = univerAPI.getActiveWorkbook()
const fWorksheet = fWorkbook.getActiveSheet()

// Create a new comment and add it to cell A1
const richText = univerAPI.newRichText().insertText('hello univer')
const commentBuilder = univerAPI.newTheadComment()
  .setContent(richText)
  .setId('mock-comment-id')
const cell = fWorksheet.getRange('A1')
await cell.addCommentAsync(commentBuilder)

// Update the comment and reply after 3 seconds
setTimeout(async () => {
  // Get the comment by ID and update its content
  const comment = fWorksheet.getCommentById('mock-comment-id')
  const newRichText = univerAPI.newRichText().insertText('Hello Univer AI')
  await comment.updateAsync(newRichText)

  // Create a new reply comment and set its content
  const replyText = univerAPI.newRichText().insertText('Hello Univer AI! GO! GO! GO!')
  const reply = univerAPI.newTheadComment().setContent(replyText)
  await comment.replyAsync(reply)
}, 3000)

// Resolve the comment and delete it after 6 seconds
setTimeout(async () => {
  const comment = fWorksheet.getCommentById('mock-comment-id')
  await comment.resolveAsync()
  await comment.deleteAsync()
}, 6000)
```

### 이벤트 리스너

전체 이벤트 타입 정의는 [Events](https://docs.univer.ai/ko-KR/reference/facade/events.md)에서 확인할 수 있습니다.

댓글 모듈은 댓글 추가, 업데이트, 삭제 및 해결 상태 변경을 수신할 수 있는 일련의 이벤트를 제공합니다. 모든 이벤트는 `univerAPI.addEvent()`를 사용하여 수신할 수 있습니다.

#### 댓글 추가 이벤트

`univerAPI.Event.CommentAdded`: 댓글이 추가된 후 트리거됩니다

```typescript
const disposable = univerAPI.addEvent(univerAPI.Event.CommentAdded, (params) => {
  const { comment, workbook, worksheet, row, col } = params
})

// Remove the event listener, use `disposable.dispose()`
```

`univerAPI.Event.BeforeCommentAdd`: 댓글이 추가되기 전에 트리거됩니다

```typescript
const disposable = univerAPI.addEvent(univerAPI.Event.BeforeCommentAdd, (params) => {
  const { comment, workbook, worksheet, row, col } = params

  // Cancel the comment addition operation
  params.cancel = true
})

// Remove the event listener, use `disposable.dispose()`
```

#### 댓글 업데이트 이벤트

`univerAPI.Event.CommentUpdated`: 댓글이 업데이트된 후 트리거됩니다

```typescript
const disposable = univerAPI.addEvent(univerAPI.Event.CommentUpdated, (params) => {
  const { comment, workbook, worksheet, row, col } = params
})

// Remove the event listener, use `disposable.dispose()`
```

`univerAPI.Event.BeforeCommentUpdate`: 댓글이 업데이트되기 전에 트리거됩니다

```typescript
const disposable = univerAPI.addEvent(univerAPI.Event.BeforeCommentUpdate, (params) => {
  const { comment, workbook, worksheet, row, col, newContent } = params

  // Cancel the comment update operation
  params.cancel = true
})

// Remove the event listener, use `disposable.dispose()`
```

#### 댓글 삭제 이벤트

`univerAPI.Event.CommentDeleted`: 댓글이 삭제된 후 트리거됩니다

```typescript
const disposable = univerAPI.addEvent(univerAPI.Event.CommentDeleted, (params) => {
  const { commentId, workbook, worksheet } = params
})

// Remove the event listener, use `disposable.dispose()`
```

`univerAPI.Event.BeforeCommentDelete`: 댓글이 삭제되기 전에 트리거됩니다

```typescript
const disposable = univerAPI.addEvent(univerAPI.Event.BeforeCommentDelete, (params) => {
  const { comment, workbook, worksheet, row, col } = params

  // Cancel the comment deletion operation
  params.cancel = true
})

// Remove the event listener, use `disposable.dispose()`
```

#### 댓글 해결 상태 이벤트

`univerAPI.Event.CommentResolved`: 댓글의 해결 상태가 변경된 후 트리거됩니다

```typescript
const disposable = univerAPI.addEvent(univerAPI.Event.CommentResolved, (params) => {
  const { comment, row, col, resolved, workbook, worksheet } = params
})

// Remove the event listener, use `disposable.dispose()`
```

`univerAPI.Event.BeforeCommentResolve`: 댓글의 해결 상태가 변경되기 전에 트리거됩니다

```typescript
const disposable = univerAPI.addEvent(univerAPI.Event.BeforeCommentResolve, (params) => {
  const { comment, row, col, resolved, workbook, worksheet } = params

  // Cancel the comment resolution operation
  params.cancel = true
})

// Remove the event listener, use `disposable.dispose()`
```

모든 이벤트에는 다음과 같은 공통 매개변수가 포함됩니다.

* `workbook`: 현재 통합 문서 인스턴스
* `worksheet`: 현재 워크시트 인스턴스
* `row`: 댓글의 행 인덱스
* `col`: 댓글의 열 인덱스
* `comment`: 댓글 객체(삭제 이벤트에는 `commentId`만 포함됨)

특수 매개변수는 다음과 같습니다.

* `BeforeCommentUpdate` 이벤트에는 `newContent`가 포함됩니다: 새 댓글 내용(RichTextValue 타입)
* `CommentResolved` 및 `BeforeCommentResolve` 이벤트에는 `resolved`가 포함됩니다: 댓글 해결 상태

접두사가 `Before`인 모든 이벤트 콜백 함수는 `params.cancel = true`를 반환하여 해당 작업을 막을 수 있습니다.

## 공통 댓글 작업

공통 댓글 Facade와 제품 Facade를 모두 가져오세요. 제품 메서드는 안정적인 앵커를 만들고 공통 API는 답글, 수정, 해결 및 삭제를 처리합니다. `getComments()`는 로컬 상태를 읽습니다. `listCommentsAsync()`는 설정된 데이터 소스를 통해 알려진 스레드를 동기화하며 서버의 모든 스레드를 검색하지 않습니다.

```ts
import '@univerjs/thread-comment/facade'

const [thread] = univerAPI.getComments({ resolved: false })
if (thread) {
  await univerAPI.replyCommentAsync({
    unitId: thread.unitId,
    subUnitId: thread.subUnitId,
    threadId: thread.threadId,
    content: 'Reviewed.',
  })
}
```

## 공유 토론

[Server SDK](https://docs.univer.ai/ko-KR/server/collaboration/extensions.md)로 Comment Service와 Endpoint를 통합하고 협업 및 라이선스를 설정한 뒤 `@univerjs-pro/thread-comment-datasource`의 `UniverThreadCommentDataSourcePlugin`을 등록하세요. 내장 데이터 소스는 `/universer-api/comment/unit/{unitId}/...`와 `/universer-api/user/list`를 사용하므로 앱에서 해당 경로를 제공해야 합니다. 서버는 문서 편집 권한과 별도로 댓글 읽기 및 쓰기 권한을 확인해야 합니다.
