# SpreadJS Sheets Collaboration 패키지

## Content

SpreadJS 동시 작업은 **OT(Operational Transformation) 방식**을 채택하며, [js-collaboration-ot](/spreadjs/docs/spreadjs-collaboration-server/collaboration-framework/js-collaboration-ot)에 정의된 제약 조건을 기반으로 SpreadJS 동시 작업 요구 사항을 충족하는 OT\_Type을 구현합니다. 또한 추가 기능도 포함되어 있습니다. 코드베이스는 프런트엔드와 백엔드를 위한 두 개의 패키지로 분리되어 있습니다.

* `spread-sheets-collaboration-client`: 클라이언트 측 기능을 처리합니다.
* `spread-sheets-collaboration`: 서버 측 기능을 처리합니다.

## 설치

npm 설치만 제공되며, 다음 두 개의 패키지를 사용할 수 있습니다.

```bash
npm install @mescius/spread-sheets-collaboration-client
npm install @mescius/spread-sheets-collaboration
```

## 클라이언트

`spread-sheets-collaboration-client` 패키지는 다음과 같은 기능을 제공합니다.

* OT\_Type 구현
* Workbook과 Doc 간의 간단한 바인딩 제공
* 동시 작업이 필요 없는 Ops 필터링
* SpreadJS Presence 관련 기능 바인딩

### OT\_Type

`spread-sheets-collaboration-client` 패키지는 SpreadJS 동시 작업에 최적화된 [OT_Type](/spreadjs/docs/spreadjs-collaboration-server/collaboration-framework/js-collaboration-ot/ottypes)를 제공합니다. 사용자는 이를 참조하여 직접 사용할 수 있습니다.
타입 등록:

```typescript
import * as OT from "@mescius/js-collaboration-ot-client";
import {type} from '@mescius/spread-sheets-collaboration-client';

OT.TypesManager.register(type);
```

타입을 지정하여 문서 생성:

```typescript
import {type} from '@mescius/spread-sheets-collaboration-client';

try {
    doc.create('Hello', type.uri, {}).then(() => {
        console.log('문서가 성공적으로 생성되었습니다:', doc.data); // "Hello"
    });
} catch (err) {
    console.error('생성 실패:', err);
}
```

Workbook에 타입 등록:

```typescript
import {type} from '@mescius/spread-sheets-collaboration-client';

const workbook = new GC.Spread.Sheets.Workbook('ss');
workbook.collaboration.registerCollaborationType(type);
```

### Workbook과 Doc 바인딩

Workbook은 현재 Snapshot과 ChangeSet을 위한 여러 API를 제공하며, 자세한 내용은 [SpreadJS Sheets Collaboration Add-on](/spreadjs/docs/spreadjs-collaboration-server/spreadjs-sheets-collaboration/spreadjs-sheets-collaboration-add-on)을 참고하십시오.
Doc 또한 Ops 수신 및 제출 기능을 제공하며, 자세한 내용은 [SharedDoc Class](/spreadjs/docs/spreadjs-collaboration-server/collaboration-framework/js-collaboration-ot/shareddoc-class)를 참고하십시오.
Workbook 관점에서 ChangeSet은 Doc의 Op와 동일하며, Doc은 개별 Op의 의미에는 관여하지 않습니다.
동시 작업 환경에서 Workbook은 Ops의 생산자이자 소비자 역할을 하고, Doc은 Ops를 제출하고 수신하는 엔터티 역할을 합니다. 따라서 Workbook과 Doc을 바인딩해야 합니다.

**Active Binding**
핵심 개념은 [SharedDoc Class - Integration with UI Components](/spreadjs/docs/spreadjs-collaboration-server/collaboration-framework/js-collaboration-ot/shareddoc-class)와 동일합니다.

```typescript
// 문서 구독
doc.subscribe().then(async () => {
    // 타입이 없으면 아직 문서가 생성되지 않았으므로 생성
    if (!doc.type) {
        // 문서 생성
        await doc.create(workbook.collaboration.toSnapshot()/* 기본 스냅샷 */, type.uri, {});
        console.log('생성 성공:', doc.data);
    }

    // 스냅샷을 기준으로 현재 Workbook 갱신
    workbook.collaboration.fromSnapshot(doc.data);

    // Workbook 변경 이벤트 바인딩: 변경 발생 시 Doc으로 제출
    workbook.collaboration.onChangeSet(async (changeSet: IChangeSet) => {
        await doc.submitOp(changeSet, { source: doc.connection.id });
    });

    // Doc에서 수신한 Op 처리
    doc.on('op', (changeSet: IChangeSet, source: unknown) => {
        // 본인이 전송한 Op인 경우 처리하지 않음
        if (source === doc.connection.id) {
            return;
        }
        // Workbook에 Op 적용
        workbook.collaboration.applyChangeSet(changeSet);
    });
});
```

**기본 Bind 메서드**
`spread-sheets-collaboration-client` 패키지는 Doc과 Workbook을 바인딩하기 위한 간단한 [bind](/spreadjs/api/collaboration/spread-sheets-collaboration-client/README#bind) 메서드를 제공합니다. 이 메서드는 문서를 생성하지는 않습니다.

```typescript
import { bind } from '@mescius/spread-sheets-collaboration-client';

// 문서 요청
doc.fetch().then(async ()=>{
    // 문서가 없으면 생성
    if(!doc.type){
        await doc.create(workbook.collaboration.toSnapshot(), type.uri, {});
        bind(workbook, doc);
    }else{
        bind(workbook, doc);
    }
});
```

### 특정 Ops 필터링

Ops 소개: [SpreadJS Sheets Add-on - Op](/spreadjs/docs/spreadjs-collaboration-server/spreadjs-sheets-collaboration/spreadjs-sheets-collaboration-add-on)
Ops는 모델의 모든 변경 사항을 설명하므로, 특정 데이터 변경(예: 확대/축소 변경, 활성 시트 변경)을 동시 작업에서 제외하려는 경우 다음 두 가지 방법을 사용할 수 있습니다.

#### 특정 Ops를 제출하지 않음

특정 Op를 다른 클라이언트 및 서버에 적용하지 않으려면, Doc에 제출하기 전에 OpType 기준으로 필터링합니다.

```typescript
workbook.collaboration.onChangeSet((changeSet: IChangeSet) => {
    const submitOps = changeSet.ops.filter(op => op.type !== OpType.updateZoom);
    if (!submitOps || submitOps.length === 0) {
        return;
    }
    doc.submitOp({ ...changeSet, ops: submitOps }, { source: doc.connection.id });
});
```

#### 특정 Ops를 적용하지 않음

서버에는 적용하되 다른 클라이언트에는 적용하지 않으려면, Workbook에 적용하기 전에 OpType 기준으로 필터링합니다.

```typescript
doc.on('op', (changeSet: IChangeSet, source: unknown) => {
    if (source === doc.connection.id) {
        return;
    }

    const needApplyOps = changeSet.ops.filter(op => op.type !== OpType.setActiveSheetId);
    if (!needApplyOps || needApplyOps.length === 0) {
        return;
    };
});
```

#### Bind에 내장된 필터링

`bind` 메서드에는 현재 다음과 같은 내장 필터가 포함되어 있습니다.

* 제출하지 않음: `OpType.updateZoom`, `OpType.updateTopLeftPosition`
* 적용하지 않음: `OpType.setActiveSheetId`, `OpType.setTabSelected`, `OpType.setStartSheetIndex`

이 필터를 사용하지 않으려면 자체 bind 메서드를 구현하십시오.

### BindPresence

`bindPresence` 메서드는 SpreadJS에서 Presence 기능을 구현하기 위해 제공됩니다. 자세한 내용은 [Presence](/spreadjs/docs/spreadjs-collaboration-server/spreadjs-sheets-collaboration/user/presence)를 참고하십시오.

## 서버

`spread-sheets-collaboration` 패키지는 [OT_Type](/spreadjs/docs/spreadjs-collaboration-server/collaboration-framework/js-collaboration-ot/ottypes)를 제공합니다.

### OT\_Type

`spread-sheets-collaboration` 패키지는 SpreadJS 동시 작업에 최적화된 OT\_Type을 제공합니다. 사용자는 이를 참조하여 직접 사용할 수 있습니다.
타입 등록:

```typescript
import * as OT from '@mescius/js-collaboration-ot';
import { type } from '@mescius/spread-sheets-collaboration';

OT.TypesManager.register(type);
```

### 기타 서버 로직

나머지 서버 사용 방법과 다른 동시 작업 워크플로와의 연계 방식은 [서버 초기화의 전체 예제](/spreadjs/docs/spreadjs-collaboration-server/collaboration-framework/js-collaboration-ot/initalizing-documentservices)를 참고하십시오.