# 충돌 및 변환

## Content

동시 작업 편집에서 충돌(conflict)이란 여러 사용자가 문서의 동일하거나 관련된 부분을 동시에 편집하여, 각자의 편집 작업이 서로 영향을 주거나 간섭함으로써 문서의 일관성이나 사용자의 의도를 훼손할 수 있는 상황을 의미합니다.
동시 편집이 발생하는 경우, 시스템은 다음을 보장해야 합니다.

* **모든 사본 간 데이터 일관성 유지**: 최종적으로 각 사본의 데이터가 동일하게 유지되어야 함
* **사용자 의도의 최대한 보존**: 최종 결과는 가능한 한 사용자의 기대에 부합해야 함. 다만 일부 충돌 상황에서는 모든 사용자의 의도를 동시에 만족시키기 어려울 수 있음

## 충돌 시나리오 예제

>type=warning
> 다음 예제의 작업(Op)은 설명을 단순화한 것입니다. 실제 환경에서는 문서 타입, 작업 복잡도, 구현 방식에 따라 Op의 구조와 내용이 달라질 수 있습니다.

초기 텍스트가 `"xyz"`인 공유 문서를 두 클라이언트(Client A, Client B)가 동시에 편집한다고 가정합니다.

* **Client A**: 위치 1(“x”와 “y” 사이)에 `"a"`를 삽입하여 결과가 `"xayz"`가 되기를 기대
* **Client B**: 위치 2(“y”와 “z” 사이)에 `"b"`를 삽입하여 결과가 `"xybz"`가 되기를 기대

서버가 수신한 순서대로 작업(op)을 단순 적용·브로드캐스트만 한다면, 다음 그림과 같은 불일치 상황이 발생합니다.

>type=note
> 네트워크 지연 등의 이유로 메시지 전송과 브로드캐스트는 즉시 전달되지 않습니다. 아래 그림은 이를 단순화하여 표현합니다.

![collaboration-conflicts-transform-20251218.0d8439.png](https://gcdocumentsitekrblob.blob.core.windows.net/document-site-files/images/36b63360-fb01-457c-a448-c3ce32aaf454/collaboration-conflicts-transform-20251218.0d8439-20260107.f1bf4f.png?width=600)

**설명**

1. **초기 상태**: 모든 클라이언트와 서버의 텍스트는 `"xyz"`
2. **로컬 작업 실행**:
    1. **Client A**가 `{ pos: 1, text: 'a' }`를 실행하여 `"xayz"`가 됨
    2. **Client B**가 `{ pos: 2, text: 'b' }`를 실행하여 `"xybz"`가 됨
3. **제출 및 서버 처리**:
    1. 서버는 `a`를 먼저 적용 및 브로드캐스트 (`"xyz"` → `"xayz"`), 이후 `b`를 적용 (`"xayz"` → `"xabyz"`)
4. **클라이언트 수신 및 적용**:
    1. **Client A**는 `b`(`{ pos: 2, text: 'b' }`)를 수신하여 `"xayz"`의 위치 2(“a”와 “y” 사이)에 `"b"`를 삽입, 결과 `"xabyz"`
    2. **Client B**는 `a`(`{ pos: 1, text: 'a' }`)를 수신하여 `"xybz"`의 위치 1(“x”와 “y” 사이)에 `"a"`를 삽입, 결과 `"xaybz"`
5. **결과**: Client A는 `"xabyz"`, Client B는 `"xaybz"`를 가지게 되어 불일치 발생

## OT 기술이 충돌을 해결하는 방법

Operational Transformation(OT)은 이미 실행된 작업의 영향을 반영하도록 작업을 조정하여, 모든 클라이언트와 서버에서 최종 상태가 일관되도록 충돌을 해결합니다. 서버와 클라이언트 모두 수신한 작업을 변환(Transform)하여 동시 작업을 수용합니다.
위 예제에 대해 다음과 같은 Transform 규칙을 정의할 수 있습니다.

```typescript
// side는 서버에 나중에 도착한 op를 나타냅니다. side === 'left'이면 op1이 op2보다 나중에 서버에 도착했음을 의미합니다.
function transform(op1, op2, side: 'left' | 'right') { 
    if (op1.pos > op2.pos || (op1.pos === op2.pos && side === 'left')) {
        return { pos: op1.pos + op2.text.length, text: op1.text };
    }
    return op1;
}
```

**다이어그램**: OT Transform을 적용한 처리 과정
![conflicts_and_transform2.2cbb56.png](https://gcdocumentsitekrblob.blob.core.windows.net/document-site-files/images/36b63360-fb01-457c-a448-c3ce32aaf454/conflicts_and_transform2.2cbb56-20260107.cf4cdf.png?width=700)

**설명:**

1. **초기 상태**: 모든 클라이언트와 서버의 텍스트는 `"xyz"`
2. **로컬 작업 실행**:
    1. **Client A**가 `{ pos: 1, text: 'a' }`를 실행하여 `"xayz"`가 됨
    2. **Client B**가 `{ pos: 2, text: 'b' }`를 실행하여 `"xybz"`가 됨
3. **제출 및 서버 처리**:
    1. 서버는 `a`를 수신·적용하여 `"xayz"`가 됨
    2. 서버는 `b`를 수신하고 `b`를 변환: `b_transformed = Transform(b, a, 'left')`
        1. `b.pos (2) > a.pos (1)`이므로 `"a"`의 길이(1)만큼 `pos`를 증가시켜 `{ pos: 3, text: 'b' }`가 됨
    3. 서버는 `b_transformed`를 적용하여 `"xayz"`의 위치 3(“y”와 “z” 사이)에 `"b"`를 삽입, 결과 `"xaybz"`
4. **브로드캐스트 및 적용**:
    1. **Client A**는 `b_transformed`(`{ pos: 3, text: 'b' }`)를 수신하여 `"xayz"`의 위치 3에 `"b"`를 삽입, 결과 `"xaybz"`
    2. **Client B**는 `a`를 수신하고 `a`를 변환: `a_transformed = Transform(a, b, 'right')`
        1. `a.pos (1) < b.pos (2)`이므로 `pos`는 변경되지 않아 `{ pos: 1, text: 'a' }`가 됨
    3. **Client B**는 `"xybz"`의 위치 1에 `"a"`를 삽입하여 `"xaybz"`가 됨
5. **결과**: 모든 클라이언트와 서버가 `"xaybz"`를 가지며, 최종 상태는 일관됨

## 요약

Operational Transformation의 핵심은 동시 작업의 영향을 고려하여 작업을 동적으로 조정하는 데 있습니다. 이 방식은 문서 잠금 없이도 사용자가 자유롭게 편집할 수 있게 하면서 일관성을 보장하므로, 실시간 동시 작업 편집 시나리오에 매우 적합합니다.

## 다음 단계

[OT_Types](/spreadjs/docs/v19/spreadjs-collaboration-server/collaboration-framework/js-collaboration-ot/ottypes)