> ## Documentation Index
> Fetch the complete documentation index at: https://make.edicus.io/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# 세션 API

> 이벤트를 받고 커맨드를 보내는 방법

띄우기 메서드가 반환하는 값이 **세션**입니다. 세션 하나가 iframe 하나를 담당하고, 그 iframe과 주고받는 모든 메시지를 다룹니다.

`EditorSession`, `TnViewSession`, `PreviewSession`은 모두 같은 `Session` 베이스를 상속하므로 아래 멤버를 공통으로 갖습니다.

## 클라이언트 만들기

```ts theme={null}
import { createEdicusClient } from '@edicus/sdk'

const edicus = createEdicusClient({
  baseUrl: 'https://editor.edicus.io',
  getToken: async () => (await fetch('/api/edicus/token').then((r) => r.json())).token,
})
```

<ParamField path="baseUrl" type="string" default="https://editor.edicus.io">
  편집기 호스트입니다.
</ParamField>

<ParamField path="getToken" type="() => string | Promise<string>">
  편집기가 토큰을 다시 요청할 때 SDK가 부를 함수입니다. [토큰 자동 갱신](/docs/concepts/authentication#토큰-자동-갱신)을 보세요.
</ParamField>

`client.destroy()`는 이 클라이언트가 만든 모든 세션을 닫고, 세션들이 함께 쓰던 `message` 리스너도 제거합니다. 세션 하나만 닫으려면 `session.close()`를 씁니다.

클라이언트 하나로 여러 세션을 띄울 수 있습니다. 들어온 메시지는 `event.source`가 어느 세션의 iframe인지 보고 그 세션에만 전달합니다. 어느 세션의 iframe에서도 오지 않은 메시지는 버립니다. 그래서 편집기와 Preview를 동시에 띄워도 메시지가 섞이지 않습니다.

## 세션 멤버

| 멤버 | 설명 |
| - | - |
| `on(action, handler)` | action 하나를 구독합니다. 핸들러는 `(info, envelope)`를 받습니다. 구독 해제 함수를 반환합니다 |
| `once(action, handler)` | 첫 1회만 실행하고 자동으로 해제됩니다 |
| `off(action, handler)` | `on`이나 `once`로 등록한 핸들러를 해제합니다 |
| `onAny(handler)` | 이 세션의 모든 인바운드 메시지를 원본 그대로 받습니다. 알려지지 않은 action도 옵니다 |
| `waitFor(action, { timeoutMs? })` | 다음 action 하나를 `Promise`로 기다립니다 |
| `waitForExit({ settleTimeoutMs? })` | 편집기의 종료 요청을 기다립니다. [닫기와 저장 후처리](/docs/sdk/exit)를 보세요 |
| `post(action, info)` | 커맨드를 iframe으로 보냅니다. 세션 종류마다 보낼 수 있는 action이 다릅니다 |
| `close()` | iframe을 제거하고 리스너를 해제하며 대기 중인 `waitFor`를 전부 reject합니다. 여러 번 불러도 안전합니다 |
| `state` | `'mounting'`, `'ready'`, `'closed'` 중 하나입니다 |
| `iframe` | 마운트된 `HTMLIFrameElement`입니다 |

세션은 항상 동기로 반환됩니다. `postMessage`는 비동기이므로 반환 직후에 `on()`을 붙여도 첫 이벤트를 놓치지 않습니다.

`state`는 편집기의 경우 `ready-to-listen`을 받는 시점에, TnView와 Preview의 경우 첫 메시지를 받는 시점에 `'ready'`가 됩니다.

## 세션 종류별 메서드

공통 멤버 말고도 세션 종류마다 전용 메서드가 있습니다.

| 세션 | 메서드 | 하는 일 |
| - | - | - |
| `EditorSession` | `changeProject(project_id)` | iframe을 다시 만들지 않고 다른 프로젝트로 바꿉니다 |
| `EditorSession` | `changeTemplate({ ps_code, template_uri, content_uri?, command? })` | iframe을 다시 만들지 않고 다른 템플릿으로 바꿉니다 |
| `EditorSession` | `waitForExit()` | `'close'` 또는 `'goto-cart'`로 resolve합니다 |
| `TnViewSession` | `movePage('prev' \| 'next')` | 보이는 페이지를 앞이나 뒤로 옮깁니다 |
| `TnViewSession` | `setVariableDataRow(row)` | 가변 데이터 한 행을 반영합니다 |
| `TnViewSession` | `save()` | 현재 문서를 저장합니다 |
| `TnViewSession` | `waitForExit()` | `'close'`로 resolve합니다 |
| `PreviewSession` | `movePage('prev' \| 'next')` | 보이는 페이지를 앞이나 뒤로 옮깁니다 |
| `PreviewSession` | `waitForExit()` | `'close'`로 resolve합니다 |

`changeProject`와 `changeTemplate`은 `post`와 달리 `type: 'to-edicus-root'` 메시지로 나갑니다. 사용법은 [편집기 띄우기](/docs/sdk/launch#띄운-뒤에-프로젝트나-템플릿-바꾸기)에 있습니다. TnView 메서드는 [TnView와 VDP](/docs/sdk/tnview), Preview 메서드는 [Preview 모드](/docs/sdk/preview)를 보세요.

## 이벤트 받기

```ts theme={null}
const off = session.on('save-doc-report', (report) => {
  if (report.status === 'end') {
    console.log('저장 완료', report.docInfo.projectID)
  }
})

// 나중에 해제
off()
```

핸들러의 두 번째 인자는 원본 메시지(`Envelope`)입니다. 필요할 때만 받으면 됩니다.

핸들러의 반환값은 무시됩니다. 다만 `Promise`를 반환하면 세션이 그것이 끝날 때까지 추적하고, `waitForExit()`가 그 끝을 기다립니다. 자세한 내용은 [닫기와 저장 후처리](/docs/sdk/exit)에 있습니다. 추적 중인 `Promise`가 reject되면 세션이 `console.error`로 남깁니다.

한 번만 받으면 되는 이벤트는 `once`나 `waitFor`를 씁니다.

```ts theme={null}
const { project_id } = await session.waitFor('project-id-created')
```

`waitFor`는 세션이 닫히거나 타임아웃되면 reject합니다.

```ts theme={null}
try {
  await session.waitFor('doc-changed', { timeoutMs: 5000 })
} catch (e) {
  console.error('5초 안에 문서가 로드되지 않았습니다')
}
```

### 알려지지 않은 메시지까지 보기

편집기가 새 action을 보내기 시작했거나, 어떤 이벤트가 오는지 확인하고 싶을 때 씁니다.

```ts theme={null}
session.onAny((envelope) => {
  console.log(envelope.type, envelope.action, envelope.info)
})
```

<Warning>
  dispatch는 `on`과 `once` 핸들러를 먼저 실행하고 그다음 `onAny`를 실행합니다. `on('goto-cart')` 핸들러 안에서 `close()`를 부르면 그 자리에서 모든 핸들러가 해제되므로, **같은 메시지에 대한 `onAny`는 실행되지 않습니다.** 종료를 `onAny`에서 기록하고 계셨다면 그 줄만 조용히 비어 보입니다. 기록이 필요하면 `close()`보다 먼저 하거나, `waitForExit()`로 종료를 받고 그 뒤에 닫으세요.
</Warning>

## 커맨드 보내기

```ts theme={null}
session.post('command', { type: 'save', force_save: true })
```

`post`는 세션 종류의 커맨드 맵으로 타입 지정돼 있어, action 이름을 쓰면 `info`의 타입이 따라옵니다.

| 세션 | 커맨드 맵 | 목록 |
| - | - | - |
| `EditorSession` | `EditorCommandMap` | [편집기 커맨드](/docs/sdk/commands) |
| `TnViewSession` | `TnViewCommandMap` | [TnView와 VDP](/docs/sdk/tnview#커맨드) |
| `PreviewSession` | `PreviewCommandMap` | [Preview 모드](/docs/sdk/preview#커맨드) |

`on`과 `waitFor`도 같은 방식으로 `EditorEventMap`, `TnViewEventMap`, `PreviewEventMap`에 맞춰 타입이 잡힙니다.

## 세션 닫기

```ts theme={null}
session.close()
```

iframe을 제거하고, 리스너를 해제하고, 대기 중인 `waitFor`를 전부 reject합니다. 여러 번 불러도 문제없습니다.

사용자가 편집기 안에서 나가기를 눌렀을 때 닫는 흐름은 [닫기와 저장 후처리](/docs/sdk/exit)를 보세요. 저장 후처리가 있다면 그냥 `close()`를 부르면 안 됩니다.

## 메시지 형식

편집기와 주고받는 메시지는 `type`과 `action`과 `info`를 가진 객체입니다. 직접 다룰 일은 거의 없지만 `onAny`를 쓸 때 보입니다.

| 세션 | 받는 메시지의 `type` | 보내는 메시지의 `type` |
| - | - | - |
| 편집기 | `from-edicus` | `to-edicus` |
| TnView | `from-edicus-tnview` | `to-edicus-tnview` |
| Preview | `from-edicus-preview` | `to-edicus-preview` |

`EditorSession`의 `changeProject`와 `changeTemplate`은 예외로 `to-edicus-root`를 씁니다.

<Note>
  `type`과 `action`이 둘 다 문자열인 메시지만 유효한 것으로 봅니다. 페이지의 다른 스크립트가 보낸 `postMessage`는 조용히 무시됩니다.
</Note>

`type`이 `from-edicus-private`인 메시지는 SDK가 직접 처리합니다. 지연 파라미터를 달라는 요청(`waiting-for-extra-param`, `waiting-for-ddp-data`)이 이 채널로 오고, SDK가 띄울 때 받아 둔 값으로 답합니다. 이 메시지는 `on`에도 `onAny`에도 오지 않습니다. 지연 파라미터는 [띄우기 옵션](/docs/reference/launch-options#지연-파라미터)에 있습니다.

`postMessage`의 targetOrigin은 띄울 때 쓰인 origin(`baseUrl`의 origin)으로 고정됩니다. v2는 항상 `'*'`로 보냈었습니다.

## 함께 내보내는 타입

`@edicus/sdk`에서 세션과 클라이언트 관련 타입을 가져올 수 있습니다.

| 타입 | 내용 |
| - | - |
| `EdicusClient` | `createEdicusClient()`가 반환하는 클라이언트입니다 |
| `EdicusClientConfig` | `createEdicusClient()`의 설정(`baseUrl`, `getToken`)입니다 |
| `SessionState` | `'mounting' \| 'ready' \| 'closed'`입니다 |
| `WaitForExitOptions` | `waitForExit()`의 옵션(`settleTimeoutMs`)입니다 |
| `ChangeTemplateOptions` | `changeTemplate()`의 인자입니다 |
| `ExperimentalSession` | lite 편집기 같은 experimental 메서드가 반환하는 느슨한 세션입니다 |
| `Envelope` | `{ type, action, info?, extra? }` 모양의 원본 메시지입니다 |

```ts theme={null}
import type { EdicusClientConfig, SessionState, Envelope } from '@edicus/sdk'
```

## 다음 단계

<Columns cols={2}>
  <Card title="편집기 이벤트" icon="arrow-left" href="/docs/sdk/events">
    편집기가 보내는 모든 이벤트와 payload를 봅니다.
  </Card>

  <Card title="편집기 커맨드" icon="arrow-right" href="/docs/sdk/commands">
    편집기로 보낼 수 있는 모든 커맨드를 봅니다.
  </Card>
</Columns>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.