CDN으로 Web SDK 가져오기
먼저 전체 예제를 실행한 뒤 자신의 페이지에 복사하세요. 편집 가능한 시트와 수식 계산에 필요한 의존성, 스타일, 언어 팩, Facade 등록이 포함되어 있습니다.
편집 가능한 시트 체험
- 예제 열기 또는 아래 HTML을 내려받아
index.html로 저장하세요. - 인터넷에 연결된 최신 브라우저에서 여세요. 빌드 도구나 서버가 필요하지 않습니다. 예제 UI와 표의 내용은 한국어입니다.
- 노트 합계는 36입니다. 수량을 3에서 4로 바꾸면 48이 됩니다. 셀 편집과 실행 취소도 체험할 수 있습니다.
전체 HTML 예제
이 예제는 React 18.3.1 UMD를 사용합니다. 전역 React 객체에는 Univer UMD 번들이 요구하는 jsx와 jsxs가 없으므로 아래 두 줄에서 공개 React.createElement API로 연결합니다. 패키지 관리자로 통합하면 빌드 도구가 react/jsx-runtime을 해석하므로 이 UMD 브리지를 복사할 필요가 없습니다.
리소스를 의존성 순서에 따라 jsDelivr에서 로드합니다. 요청이 실패하면 unpkg에서 같은 버전을 다시 시도한 뒤 계속 진행합니다. 두 공급자 모두 실패하면 시작을 중단하고 로드하지 못한 리소스를 페이지에 표시합니다.
HTML
<!doctype html><html lang="ko-KR"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Univer Sheets CDN 예제</title> <style> html, body, #root, #app { padding: 0; margin: 0; height: 100%; } </style> </head> <body> <div id="app"><p role="status" style="padding: 24px; font-family: system-ui">Univer 로딩 중…</p></div> <script> const cdnBases = ['https://cdn.jsdelivr.net/npm/', 'https://unpkg.com/'] const app = document.getElementById('app') async function loadResource(path, tag = 'script') { for (const base of cdnBases) { const element = document.createElement(tag) const url = base + path element.crossOrigin = 'anonymous' if (tag === 'link') { element.rel = 'stylesheet' element.href = url } else { element.src = url } try { await new Promise((resolve, reject) => { element.onload = () => resolve() element.onerror = () => reject(new Error(url)) document.head.appendChild(element) }) return } catch { element.remove() } } throw new Error(`Could not load ${path} from ${cdnBases.join(' or ')}`) } const styles = [ '@univerjs/design@1.3.0/lib/index.css', '@univerjs/ui@1.3.0/lib/index.css', '@univerjs/docs-ui@1.3.0/lib/index.css', '@univerjs/sheets-ui@1.3.0/lib/index.css', '@univerjs/sheets-formula-ui@1.3.0/lib/index.css', '@univerjs/sheets-numfmt-ui@1.3.0/lib/index.css', ] const reactScripts = [ 'react@18.3.1/umd/react.production.min.js', 'react-dom@18.3.1/umd/react-dom.production.min.js', ] const sdkScripts = [ 'rxjs@7.8.2/dist/bundles/rxjs.umd.min.js', '@wendellhu/redi@1.1.3/dist/umd/index.js', '@wendellhu/redi@1.1.3/dist/umd/react-bindings/index.js', '@univerjs/themes@1.3.0/lib/umd/index.js', '@univerjs/protocol@1.3.0/lib/umd/index.js', '@univerjs/core@1.3.0/lib/umd/index.js', '@univerjs/network@1.3.0/lib/umd/index.js', '@univerjs/rpc@1.3.0/lib/umd/index.js', '@univerjs/design@1.3.0/lib/umd/index.js', '@univerjs/engine-render@1.3.0/lib/umd/index.js', '@univerjs/engine-formula@1.3.0/lib/umd/index.js', '@univerjs/drawing@1.3.0/lib/umd/index.js', '@univerjs/telemetry@1.3.0/lib/umd/index.js', '@univerjs/ui@1.3.0/lib/umd/index.js', '@univerjs/docs@1.3.0/lib/umd/index.js', '@univerjs/docs-ui@1.3.0/lib/umd/index.js', '@univerjs/sheets@1.3.0/lib/umd/index.js', '@univerjs/sheets-ui@1.3.0/lib/umd/index.js', '@univerjs/sheets-formula@1.3.0/lib/umd/index.js', '@univerjs/sheets-formula-ui@1.3.0/lib/umd/index.js', '@univerjs/sheets-numfmt@1.3.0/lib/umd/index.js', '@univerjs/sheets-numfmt-ui@1.3.0/lib/umd/index.js', '@univerjs/core@1.3.0/lib/umd/facade.js', '@univerjs/engine-formula@1.3.0/lib/umd/facade.js', '@univerjs/ui@1.3.0/lib/umd/facade.js', '@univerjs/docs@1.3.0/lib/umd/facade.js', '@univerjs/docs-ui@1.3.0/lib/umd/facade.js', '@univerjs/sheets@1.3.0/lib/umd/facade.js', '@univerjs/sheets-ui@1.3.0/lib/umd/facade.js', '@univerjs/sheets-formula@1.3.0/lib/umd/facade.js', '@univerjs/sheets-numfmt@1.3.0/lib/umd/facade.js', '@univerjs/design@1.3.0/lib/umd/locale/ko-KR.js', '@univerjs/ui@1.3.0/lib/umd/locale/ko-KR.js', '@univerjs/sheets@1.3.0/lib/umd/locale/ko-KR.js', '@univerjs/sheets-ui@1.3.0/lib/umd/locale/ko-KR.js', '@univerjs/docs-ui@1.3.0/lib/umd/locale/ko-KR.js', '@univerjs/sheets-formula-ui@1.3.0/lib/umd/locale/ko-KR.js', '@univerjs/sheets-numfmt-ui@1.3.0/lib/umd/locale/ko-KR.js', ] // Fetch ahead in parallel; main() still executes scripts in dependency order. for (const [as, paths] of [ ['style', styles], ['script', [...reactScripts, ...sdkScripts]], ]) { for (const path of paths) { const preload = document.createElement('link') preload.rel = 'preload' preload.as = as preload.crossOrigin = 'anonymous' preload.href = cdnBases[0] + path preload.onerror = () => { preload.onerror = null preload.href = cdnBases[1] + path } document.head.appendChild(preload) } } async function main() { for (const path of styles) await loadResource(path, 'link') for (const path of reactScripts) await loadResource(path) // Bridge the React 18 UMD global to the JSX runtime expected by Univer UMD bundles. React.jsx = (type, props, key) => React.createElement(type, key === undefined ? props : { ...props, key }) React.jsxs = React.jsx for (const path of sdkScripts) await loadResource(path) app.textContent = '' const { Univer, LocaleType, mergeLocales, UniverInstanceType } = UniverCore const { FUniver } = UniverCoreFacade const { UniverRenderEnginePlugin } = UniverEngineRender const { UniverFormulaEnginePlugin } = UniverEngineFormula const { UniverUIPlugin } = UniverUi const { UniverSheetsPlugin } = UniverSheets const { UniverSheetsUIPlugin } = UniverSheetsUi const { UniverDocsPlugin } = UniverDocs const { UniverDocsUIPlugin } = UniverDocsUi const { UniverSheetsFormulaPlugin } = UniverSheetsFormula const { UniverSheetsFormulaUIPlugin } = UniverSheetsFormulaUi const { UniverSheetsNumfmtPlugin } = UniverSheetsNumfmt const { UniverSheetsNumfmtUIPlugin } = UniverSheetsNumfmtUi const univer = new Univer({ locale: LocaleType.KO_KR, locales: { [LocaleType.KO_KR]: mergeLocales( UniverDesignKoKR, UniverUiKoKR, UniverSheetsKoKR, UniverSheetsUiKoKR, UniverDocsUiKoKR, UniverSheetsFormulaUiKoKR, UniverSheetsNumfmtUiKoKR, ), }, }) univer.registerPlugin(UniverRenderEnginePlugin) univer.registerPlugin(UniverFormulaEnginePlugin) univer.registerPlugin(UniverUIPlugin, { container: 'app', }) univer.registerPlugin(UniverDocsPlugin) univer.registerPlugin(UniverDocsUIPlugin) univer.registerPlugin(UniverSheetsPlugin) univer.registerPlugin(UniverSheetsUIPlugin) univer.registerPlugin(UniverSheetsFormulaPlugin) univer.registerPlugin(UniverSheetsFormulaUIPlugin) univer.registerPlugin(UniverSheetsNumfmtPlugin) univer.registerPlugin(UniverSheetsNumfmtUIPlugin) univer.createUnit(UniverInstanceType.UNIVER_SHEET, {}) const univerAPI = FUniver.newAPI(univer) window.univerAPI = univerAPI univerAPI .getActiveWorkbook() .getActiveSheet() .getRange('A1:D3') .setValues([ ['품목', '수량', '단가', '합계'], ['노트', 3, 12, '=B2*C2'], ['펜', 5, 2, '=B3*C3'], ]) } main().catch((error) => { console.error(error) app.setAttribute('role', 'alert') app.style.cssText = 'padding: 24px; box-sizing: border-box; white-space: pre-wrap; overflow-wrap: anywhere; font: 14px/1.5 system-ui' app.textContent = '예제 로딩에 실패했습니다. 네트워크를 확인한 뒤 페이지를 새로 고치세요.' + '\n' + error.message }) </script> </body></html>네임스페이스 규칙
UMD 스크립트는 모듈 내보내기를 전역 객체로 제공합니다. 초기화 코드에서 다음 이름으로 플러그인과 Facade API에 접근하세요.
| 패키지 또는 진입점 | 전역 이름 |
|---|---|
@univerjs/core | UniverCore |
@univerjs/docs-ui | UniverDocsUi |
@univerjs/core/facade | UniverCoreFacade |
@univerjs/docs-ui/locale/ko-KR | UniverDocsUiKoKR |
버전 일치
예제는 Univer 1.3.0, React와 ReactDOM 18.3.1, RxJS 7.8.2로 고정되어 있습니다. 업그레이드할 때 Facade, CSS, 언어 파일을 포함한 모든 Univer URL을 함께 바꾸세요. 기존 앱 번들에 포함된 라이브러리를 중복으로 로드하지 마세요.
문제 해결
| 증상 | 확인할 사항 |
|---|---|
ITelemetryService | @univerjs/telemetry에 의존하는 @univerjs/sheets-ui보다 먼저 로드하고 전체 의존성 목록을 유지하세요. |
FDocument | @univerjs/docs-ui/facade보다 먼저 @univerjs/docs/facade를 로드하세요. Sheets도 Docs 텍스트 엔진을 사용합니다. |
[redi], t is not a function | 전체 예제로 시작하고 각 의존성을 동일한 버전으로 한 번만 로드하세요. 브라우저 Network에서 실패한 요청을 확인하세요. |
| 빈 에디터 | 컨테이너 높이를 지정하세요. 네트워크나 CSP가 CDN을 차단하면 직접 호스팅한 의존성이나 패키지 관리자를 사용하세요. |
기존 React 또는 Vue 앱에는 의존성을 관리하는 패키지 관리자 통합을 권장합니다. 긴 스크립트 목록을 직접 관리할 필요가 없습니다.
이 가이드는 어떠셨나요?