Headless Univer

Headless Univer는 대화형 편집기 UI가 없는 Univer 콘텐츠 런타임입니다. 브라우저 편집기와 콘텐츠 모델, 명령, 계산, Facade API를 공유합니다. Node.js에서 콘텐츠를 처리하거나 브라우저에서 UI 없는 작업을 실행할 수 있으며, 사용 가능한 기능은 등록한 제품·기능 플러그인에 따라 결정됩니다.

Node.js에서 문서를 생성·로드하고 문단, 텍스트, 구조, 스타일을 수정해 Univer 스냅샷을 저장할 수 있습니다. 표, 목록, 단, 수식, 도형은 해당 콘텐츠 플러그인이 필요합니다. 콘텐츠 수정만으로 페이지 스크린샷이 생성되지는 않습니다.

아래 예제는 플러그인 모드를 사용합니다. @univerjs/core와 제품 콘텐츠 플러그인은 Headless 계층에 속하지만 @univerjs/ui, 제품 *-ui, 기본 브라우저 코어 프리셋은 포함하지 않습니다. @univerjs/engine-render는 공유 의존성으로 등록하며 Canvas나 편집기를 마운트하지 않습니다. 패키지 경계에서 분류와 예외를 확인하세요.

로컬 스냅샷 작업에는 협업 서비스가 필요하지 않습니다. 협업 예제에는 클라이언트, 서버, 인증이 추가로 필요합니다. Office 변환과 스크린샷에는 각각 Server SDK와 렌더링 런타임이 필요합니다. /facade 가져오기만으로 기능 플러그인이 등록되거나 UI API가 Headless API로 바뀌지는 않습니다.

설치

Shell
pnpm add @univerjs/core @univerjs/engine-render @univerjs/engine-formula @univerjs/docs

사용법

TypeScript
import { LocaleType, Univer } from '@univerjs/core'import { FUniver } from '@univerjs/core/facade'import { UniverDocsPlugin } from '@univerjs/docs'import { UniverFormulaEnginePlugin } from '@univerjs/engine-formula'import { UniverRenderEnginePlugin } from '@univerjs/engine-render'import '@univerjs/docs/facade'const univer = new Univer({  locale: LocaleType.KO_KR,  locales: {    [LocaleType.KO_KR]: {},  },})univer.registerPlugin(UniverRenderEnginePlugin)univer.registerPlugin(UniverFormulaEnginePlugin)univer.registerPlugin(UniverDocsPlugin)const univerAPI = FUniver.newAPI(univer)univerAPI.createDocument({})

Node.js 환경의 공동 편집 예제

Node.js를 공동 편집 클라이언트로 사용하여 서버에 있는 기존 문서를 불러오고 Facade API로 내용을 수정한 뒤, 같은 문서를 연 다른 클라이언트에 변경 사항을 동기화할 수 있습니다. 실행 전에 공동 편집 서비스, 클라이언트 라이선스, 접근 가능한 문서 ID를 준비하세요.

예제 파일

파일을 다음 구조로 배치합니다.

text
src/docs/node/├── main.ts└── univer.ts
  • univer.ts: Univer를 생성하고 문서 기능 플러그인, Facade 확장 및 Node.js 공동 편집 클라이언트를 등록합니다.
  • main.ts: 공동 편집 문서를 불러와 문단, 텍스트, 머리글과 바닥글, 콜아웃, 코드 블록, 인용 블록, 목록, 도형, 표 및 단 나누기 API를 차례로 실행합니다.

univer.ts의 your-license, 서버 주소, Cookie와 main.ts의 문서 ID를 바꾸세요.

TypeScript
import { UniverCollaborationPlugin } from '@univerjs-pro/collaboration'import { UniverCollaborationClientPlugin } from '@univerjs-pro/collaboration-client'import {  NodeCollaborationSocketService,  UniverCollaborationClientNodePlugin,} from '@univerjs-pro/collaboration-client-node'import { UniverDocsCalloutPlugin } from '@univerjs-pro/docs-callout'import { UniverDocsCodePlugin } from '@univerjs-pro/docs-code'import { UniverDocsColumnPlugin } from '@univerjs-pro/docs-column'import { UniverDocsLatexPlugin } from '@univerjs-pro/docs-latex'import { UniverDocsListPlugin } from '@univerjs-pro/docs-list'import { UniverDocsQuotePlugin } from '@univerjs-pro/docs-quote'import { UniverDocsReferencePlugin } from '@univerjs-pro/docs-reference'import { UniverDocsShapePlugin } from '@univerjs-pro/docs-shape'import { UniverDocsTablePlugin } from '@univerjs-pro/docs-table'import { UniverLicensePlugin } from '@univerjs-pro/license'import {  IAuthzIoService,  IImageIoService,  IMentionIOService,  IUndoRedoService,  LocaleType,  Univer,} from '@univerjs/core'import { UniverDocsPlugin } from '@univerjs/docs'import { UniverDocsDrawingPlugin } from '@univerjs/docs-drawing'import { UniverDocsHyperLinkPlugin } from '@univerjs/docs-hyper-link'import { UniverDrawingPlugin } from '@univerjs/drawing'import { UniverRenderEnginePlugin } from '@univerjs/engine-render'import { UniverNetworkPlugin } from '@univerjs/network'import { UniverThreadCommentPlugin } from '@univerjs/thread-comment'import '@univerjs/docs/facade'import '@univerjs/docs-drawing/facade'import '@univerjs-pro/docs-shape/facade'import '@univerjs-pro/docs-callout/facade'import '@univerjs-pro/docs-reference/facade'import '@univerjs-pro/docs-code/facade'import '@univerjs-pro/docs-list/facade'import '@univerjs-pro/docs-quote/facade'import '@univerjs-pro/docs-table/facade'import '@univerjs-pro/docs-column/facade'import '@univerjs-pro/docs-latex/facade'import '@univerjs-pro/collaboration-client/facade'export function createUniverOnNode(): Univer {  const univer = new Univer({    locale: LocaleType.KO_KR,    locales: {},    override: [      [IAuthzIoService, null],      [IUndoRedoService, null],      [IMentionIOService, null],    ],  })  univer.registerPlugin(UniverLicensePlugin, {    license: 'your-license',  })  univer.registerPlugin(UniverRenderEnginePlugin)  univer.registerPlugin(UniverThreadCommentPlugin)  univer.registerPlugin(UniverDrawingPlugin, {    override: [[IImageIoService, null]],  })  univer.registerPlugin(UniverDocsPlugin)  univer.registerPlugin(UniverDocsDrawingPlugin)  univer.registerPlugin(UniverDocsHyperLinkPlugin)  univer.registerPlugin(UniverDocsReferencePlugin)  univer.registerPlugin(UniverDocsCalloutPlugin)  univer.registerPlugin(UniverDocsCodePlugin)  univer.registerPlugin(UniverDocsListPlugin)  univer.registerPlugin(UniverDocsQuotePlugin)  univer.registerPlugin(UniverDocsShapePlugin)  univer.registerPlugin(UniverDocsTablePlugin)  univer.registerPlugin(UniverDocsColumnPlugin)  univer.registerPlugin(UniverDocsLatexPlugin)  registerCollaborationPlugins(univer)  return univer}function registerCollaborationPlugins(univer: Univer): void {  const serverUrl = 'https://your-univer-server.example.com'  univer.registerPlugin(UniverNetworkPlugin)  univer.registerPlugin(UniverCollaborationPlugin)  univer.registerPlugin(UniverCollaborationClientPlugin, {    socketService: NodeCollaborationSocketService,    enableOfflineEditing: false,    enableSingleActiveInstanceLock: false,    snapshotServerUrl: `${serverUrl}/universer-api/snapshot`,    collabSubmitChangesetUrl: `${serverUrl}/universer-api/comb`,    collabWebSocketUrl: `${serverUrl}/universer-api/comb/connect`,    wsSessionTicketUrl: `${serverUrl}/universer-api/user/session-ticket`,    uploadFileServerUrl: `${serverUrl}/universer-api/stream/file/upload`,    signUrlServerUrl: `${serverUrl}/universer-api/file/{fileID}/sign-url`,    downloadEndpointUrl: serverUrl,    authzUrl: `${serverUrl}/universer-api/authz`,    sendChangesetTimeout: 200,    retryConnectingInterval: 1000,    customHeaders: {      // 예: Univer 서버에서 Cookie 인증이 필요한 경우 여기에 'cookie' 헤더를 설정할 수 있습니다.      cookie: 'your-cookie',      // ... 추가 헤더    },  })  univer.registerPlugin(UniverCollaborationClientNodePlugin)}

실행 안내

loadDocAsync()는 서버의 기존 문서를 불러오므로 createDocument()을 호출할 필요가 없습니다. 공동 편집 서비스는 customHeaders에 포함된 Cookie 등의 인증 정보로 사용자를 식별하고 권한을 확인하며, 연결의 memberID는 서버가 반환합니다.

이 예제는 브라우저 오프라인 캐시와 여러 탭 간 인스턴스 잠금을 비활성화하므로 계정별 캐시 격리를 위해 setCurrentUser()를 호출할 필요가 없습니다. 나중에 댓글처럼 로컬 현재 사용자에 의존하는 기능을 사용할 경우 UserManagerService로 업무 사용자를 설정할 수 있습니다. 이 설정은 서버 인증을 대체하지 않습니다.

머리글/바닥글 예제는 페이지 문서에만, 단 나누기 예제는 모던 문서에만 적용됩니다. 예제는 isModern()으로 문서 모드를 확인하고 지원하지 않는 작업을 건너뜁니다.

예제는 기존 문서에 내용을 삽입하므로 테스트 문서로 실행하는 것을 권장합니다. 예제는 실행 후에도 공동 편집 연결을 유지합니다. 브라우저에서 같은 문서를 열면 변경 사항을 확인할 수 있습니다. flush()는 대기 중인 공동 편집 변경 사항에 대한 서버 확인을 기다립니다. 일회성 스크립트로 변경한다면 모든 비동기 작업과 동기화가 완료된 뒤 univer.dispose()를 호출하여 인스턴스를 해제하세요.

이 가이드는 어떠셨나요?

© 2026 DreamNum Co., Ltd.