# 메시지 송수신

## Content

`js-collaboration`은 **클라이언트–서버 간 양방향 통신**을 지원하며, 서버가 **하나의 룸(room)에 속한 모든 클라이언트에게 메시지를 브로드캐스트**할 수 있도록 합니다. 이 문서는 다음 기술 구현에 대해 설명합니다.

* 클라이언트가 서버로 메시지를 전송
* 서버가 메시지를 수신하고 응답
* 메시지 브로드캐스트

***

## 기본 송수신

### 클라이언트 → 서버

클라이언트는 [connection.send()](/spreadjs/api/collaboration/js-collaboration-client/classes/Connection#send) 메서드를 사용하여 서버로 메시지를 전송합니다.
메시지는 문자열, 일반 객체, 숫자, 배열 등 다양한 데이터 타입을 지원합니다.

```typescript
// 클라이언트 측
// 서버로 메시지 전송
connection.send('hello, world!');
```

서버는 `message` 훅을 통해 클라이언트 메시지를 수신할 수 있습니다.

```typescript
// 서버 측
server.on('message', ({ connection, data }) => {
    console.log('클라이언트로부터 수신한 메시지:', data);
});
```

***

### 서버 → 클라이언트

클라이언트는 [connection.on('message')](/spreadjs/api/collaboration/js-collaboration-client/classes/Connection#on) 메서드를 사용하여 서버로부터 메시지를 수신합니다.

```typescript
// 클라이언트 측
connection.on('message', (data) => {
    console.log('서버로부터 수신한 메시지:', data);
});
```

서버는 [connection.send()](/spreadjs/api/collaboration/js-collaboration-client/classes/Connection#send) 메서드를 사용하여 클라이언트로 메시지를 전송합니다.

```typescript
// 서버 측
server.on('connect', ({ connection }) => {
    connection.send('hello, world!');
});
```

***

## 서버 브로드캐스팅

서버는 단일 클라이언트에게 메시지를 보내는 것뿐만 아니라,
[connection.broadcast()](/spreadjs/api/collaboration/js-collaboration/classes/Connection#broadcast) 메서드를 사용하여 **같은 룸에 속한 모든 클라이언트에게 메시지를 브로드캐스트**할 수 있습니다.

***

### 송신자를 제외한 브로드캐스트

`connection.broadcast()`에 `roomId`만 제공하면, 기본 동작은 **송신자를 제외한 클라이언트에게만** 메시지를 브로드캐스트하는 것입니다.
![messages_sending_and_receiving-broadcast1.1a1948.png](https://gcdocumentsitekrblob.blob.core.windows.net/document-site-files/images/36b63360-fb01-457c-a448-c3ce32aaf454/messages_sending_and_receiving-broadcast1.1a1948-20260106.2387c3.png?width=500)

```typescript
// 클라이언트 측
connection.on('message', (data) => {
    console.log('서버로부터 수신한 메시지:', data);
});
```

```typescript
// 서버 측
server.on('connect', ({ connection, roomId }) => {
    // 클라이언트가 연결되면, 같은 룸의 모든 클라이언트에게 메시지를 브로드캐스트 (송신자 제외)
    connection.broadcast(`${connection.id} join ${roomId}`);
});
```

***

### 송신자를 포함한 브로드캐스트

송신자를 포함하여 룸의 모든 참여자에게 메시지를 브로드캐스트하려면,
`includeSelf: true` 옵션을 설정합니다.
![messages_sending_and_receiving-broadcast2.a16051.png](https://gcdocumentsitekrblob.blob.core.windows.net/document-site-files/images/36b63360-fb01-457c-a448-c3ce32aaf454/messages_sending_and_receiving-broadcast2.a16051-20260106.53e860.png?width=500)

```typescript
// 클라이언트 측
connection.on('message', (data) => {
    console.log('서버로부터 수신한 메시지:', data);
});
```

```typescript
// 서버 측
server.on('connect', ({ connection, roomId }) => {
    // 클라이언트가 연결되면, 같은 룸의 모든 클라이언트에게 메시지를 브로드캐스트 (송신자 포함)
    connection.broadcast(`${connection.id} join ${roomId}`, '', true);
});
```

>type=note
> broadcast()의 두 번째 매개변수는 메시지 타입(선택 사항)이며, 이 예제에서는 빈 문자열을 사용합니다.

***

## 메시지 타입

메시지 타입은 메시지의 목적이나 범주를 식별하는 데 사용되며,
수신자가 해당 메시지가 자신에게 관련이 있는지 판단할 수 있도록 합니다.

예를 들어 동시 작업 시나리오에서는 문서 업데이트 메시지와 사용자 상태 메시지를 구분하기 위해
`"my-document"` 또는 `"my-presence"` 같은 타입을 사용할 수 있습니다.

```typescript
// 문서 업데이트 전송
connection.send({ content: 'New paragraph' }, 'my-document');
// 온라인 상태 전송
connection.send({ userId: 'user1', active: true }, 'my-presence');
// 메시지 수신 및 타입별 처리
connection.on('message', (data, type) => {
    if (type === 'my-document') {
        console.log('문서 업데이트:', data);
    } else if (type === 'my-presence') {
        console.log('상태 업데이트:', data);
    }
});
```

***

## 메시지 순서

`js-collaboration`은 **메시지가 전송된 순서대로 전달됨을 보장**합니다.
예를 들어 다음과 같이 메시지를 전송하면,

```typescript
connection.send('msg1');
connection.send('msg2');
connection.send('msg3');
```

수신자는 항상 다음 순서로 메시지를 받습니다.

```auto
msg1 → msg2 → msg3
```

***

## 메시지 전달 보장

### 전달 보장 방식

`js-collaboration`은 **At-Most-Once Delivery(최대 한 번 전달)** 메커니즘을 구현합니다. 이는 다음을 의미합니다.

* 연결이 끊긴 상태에서 메시지가 전송되면, 수신자가 해당 메시지를 받았는지 보장할 수 없습니다.
* 연결이 복구된 후에도, 이전에 전달되지 않은 메시지는 자동으로 재전송되지 않습니다.

***

### 클라이언트 동작 방식

연결이 끊기면 클라이언트는 전송되지 않은 메시지를 유지하기 위한 **버퍼링 전략**을 사용합니다.

* **미전송 메시지 버퍼링**
    `connection.send()` 호출 시 네트워크 오류 등으로 연결이 불가능하면,
    메시지는 즉시 폐기되지 않고 클라이언트 로컬에 버퍼링됩니다.
* **재연결 후 전송**
    연결이 복구되면, 버퍼링된 메시지가 원래 순서대로 전송됩니다.

***

### 서버 동작 방식

서버는 메시지 버퍼링 메커니즘을 제공하지 않습니다.

* **메시지 버퍼링 없음**
    서버가 `connection.send()` 또는 `connection.broadcast()`로 메시지를 보낼 때
    대상 클라이언트가 연결되어 있지 않으면 해당 메시지는 즉시 유실됩니다.
* **재전송 없음**
    클라이언트가 재연결하더라도, 서버는 연결 끊김 동안 놓친 메시지를 재전송하지 않습니다.

***

## 메시지 유실 대응 전략

At-Most-Once Delivery 특성상, 비즈니스 요구사항에 따라 **추가적인 신뢰성 메커니즘**을 설계해야 합니다.
다음은 일반적으로 사용되는 전략입니다.

### 애플리케이션 계층 확인 메커니즘

* **방법**: 송신자가 메시지에 고유 식별자(예: 메시지 ID)를 포함하고, 수신자는 확인(ACK) 메시지를 응답
* **구현**: 확인 응답을 받지 못하면 송신자가 재전송
* **사용 사례**: 문서 변경과 같은 중요 메시지

```typescript
// 클라이언트가 ID를 포함한 메시지 전송
connection.send({ id: 'msg123', type: 'update', content: 'xxx' });

// 서버에서 확인 응답 전송
server.on('message', ({ connection, data }) => {
    connection.send({ id: data.id, type: 'ack' });
});

// 클라이언트에서 확인 응답 검사
connection.on('message', (data) => {
    if (data.type === 'ack' && data.id === 'msg123') {
        console.log('메시지 전달 완료');
    }
});
```