빠른 시작
몇 분 안에 첫 문서를 렌더링하세요
이 안내서는 빈 Program.cs 파일을 가진 ASP.NET Core 앱을 브라우저에서 문서가 렌더링되는 상태까지 진행합니다: 서버 등록, 전체 Viewer 패키지(Viewer 툴바, Viewer 마운트 및 선택적 Search/Annotation 리본), 자산 참조, 클라이언트 초기화, 문서 열기 및 실행.
서버 설정
AddDoconut()는 서비스를 등록하고; UseDoconutResources()와 UseDoconut()는 미들웨어를 연결합니다. 리소스 호출은 먼저 와야 합니다. 세션 호출도 필요합니다 — Doconut의 기본 문서 보안은 ASP.NET 세션 상태에 대한 모든 페이지 요청을 검증합니다. 이미 Installation 중에 Doconut을 등록했나요? 다음 섹션으로 건너뛰세요.
builder.Services.AddDoconut(options =>
{
options.LicensePath = "Doconut.Viewer.lic";
});
builder.Services.AddSession(); // Doconut document security rides on ASP.NET session state
app.UseSession(); // call UseSession() before UseDoconut()
app.UseDoconutResources(); // must be registered before UseDoconut()
app.UseDoconut();프로덕션 스타일 경로 레이아웃을 위해, 문서 미들웨어를 명시적인 브랜치에 매핑하고 네 개의 경로 설정을 정렬된 상태로 유지합니다:
builder.Services.AddDoconut(options =>
{
options.LicensePath = Path.Combine(AppContext.BaseDirectory, "Doconut.Viewer.lic");
options.MiddlewarePath = "/doconut";
options.ResourcesPath = "/doconut-res";
options.UnsafeMode = false;
});
builder.Services.AddSession();
app.UseRouting();
app.UseSession();
app.UseDoconutResources();
app.Map("/doconut", branch => branch.UseDoconut());MiddlewarePath는 조정 값이며, 자체적으로 ASP.NET Core 브랜치를 매핑하지는 않습니다. 이 예시에서는 호스트가 /doconut을 매핑하므로 클라이언트는 BasePath: '/doconut'을 사용해야 합니다. ResourcesPath는 /doconut-res에 포함된 번들을 제공하고, 위젯의 이미지 리소스 경로는 따라서 ResPath: '/doconut-res/images'가 됩니다.
페이지에 뷰어 추가
Viewer는 페이지에 반드시 포함되어야 하는 핵심입니다. 렌더링 표면은 두 개의 중첩된 div를 사용합니다:
<div id="divDocViewer">
<div id="div_ctlDoc"></div>
</div>툴바, 모듈 마운트 및 Viewer 표면을 하나의 페이지 구성으로 취급합니다. Search와 Annotation은 선택적 마운트에 자체 리본을 삽입하지만, 해당 모듈은 절대 독립적으로 존재하지 않으며 항상 같은 페이지의 Viewer에 부착됩니다. Doconut.TestApp 및 Doconut.TestApp.Distributed와 동일한 순서를 사용하세요:
<nav id="toolbar" aria-label="Document viewer controls">
<!-- Viewer navigation, zoom, Search, and Annotation buttons -->
</nav>
<div id="searchBarMount"></div>
<div id="annBarMount"></div>
<div id="divDocViewer">
<div id="div_ctlDoc"></div>
</div>뷰어 자산 참조
Razor 뷰에서 주입된 Viewer 서비스는 의존 순서대로 뷰어의 <link> 및 <script> 태그를 출력합니다 — 위젯은 jQuery 플러그인이므로 jQuery가 뷰어 스크립트보다 먼저 로드되어야 합니다:
@inject Doconut.Viewer Viewer
@Html.Raw(Viewer.ReferenceCss(new CssConfig
{
IncludeBootstrapCss = true,
IncludeViewerCss = true
}))
@Html.Raw(Viewer.ReferenceScripts(new ScriptConfig
{
IncludeJQuery = true,
IncludeBootstrap = true,
IncludeViewerScripts = true
}))전체 Viewer 패키지를 위해 Viewer와 모듈 리소스를 함께 요청합니다:
@Html.Raw(Viewer.ReferenceCss(new CssConfig
{
IncludeBootstrapCss = true,
IncludeViewerCss = true,
IncludeSearchCss = true,
IncludeAnnotationCss = true
}))
@Html.Raw(Viewer.ReferenceScripts(new ScriptConfig
{
IncludeJQuery = true,
IncludeBootstrap = true,
IncludeViewerScripts = true,
IncludeSearchScripts = true,
IncludeSearchBar = true,
IncludeAnnotationScripts = true,
IncludeAnnotationBar = true
}))IncludeViewerCss와 IncludeViewerScripts는 필수 핵심 플래그입니다. Search 또는 Annotation 리본 예제를 이 플래그 없이, Viewer 마운트 및 docViewer 인스턴스 없이 공개하지 마세요. 현재 라이선스가 해당 기능을 허용하지 않을 경우 ReferenceCss와 ReferenceScripts는 선택적 모듈의 리소스를 생략하지만, 핵심 Viewer는 여전히 시작됩니다.
뷰어 초기화
클라이언트 측 위젯은 jQuery 플러그인입니다. 아래는 실제 초기화 옵션의 최소 집합이며(의사코드 아님)입니다:
let searchBar = null;
let annBar = null;
const objViewer = $('#div_ctlDoc').docViewer({
showThumbs: true,
autoLoad: false,
pageZoom: 100,
FitType: 'width',
BasePath: '/doconut',
ResPath: '/doconut-res/images',
onViewerReady: function () {
// pages are visible; safe to hide a loading spinner here
},
// Forward annotation lifecycle events to the embedded ribbon when it is present.
onAnnLoaded: () => annBar?.handleAnnLoaded(),
onAnnSaved: () => annBar?.handleAnnSaved(),
onAnnSaveError: () => annBar?.handleAnnSaveError(),
onAnnClosed: () => annBar?.handleAnnClosed(),
onError: function (message) {
console.error('Doconut viewer error:', message);
}
});옵션 케이스가 실제로 혼합되어 있습니다 — showThumbs, autoLoad, pageZoom는 camelCase이고, FitType, BasePath, ResPath는 PascalCase입니다. 일관된 규칙이 없으며, 케이스를 잘못 지정하면 옵션이 조용히 무시됩니다(위젯이 기본값으로 되돌아갑니다).
전체 Viewer 패키지 조립
두 .NET 8 참조 애플리케이션은 다음 구성 요소들을 한 페이지에 함께 설치합니다:
| 패키지 구성 요소 | 요구 사항 | 연결 방식 |
|---|---|---|
Viewer 리소스, 마운트 및 objViewer | 필수 | 핵심 문서 렌더러 |
| Viewer 툴바 | 참조 구성에서 필수 | 호스트 마크업; 버튼이 동일한 objViewer를 호출 |
| 검색 리본 | 선택 사항, 라이선스된 모듈 | doconutSearchBar(...).attach(objViewer) |
| 주석 리본 | 선택 사항, 라이선스된 모듈 | doconutAnnotationBar(...).attach(objViewer) |
주요 Viewer 툴바가 호스트 마크업이긴 하지만 Viewer와 함께 설치되며 절대 독립된 컨트롤로 문서화되지 않아야 합니다. 이렇게 하면 레이아웃, 라벨, 아이콘 및 권한 규칙을 애플리케이션이 제어하면서 모든 버튼이 동일한 Viewer 인스턴스를 구동합니다:
<nav id="toolbar" aria-label="Document viewer controls">
<button type="button" onclick="objViewer.GotoPage(1)">First</button>
<button type="button" onclick="objViewer.Next(false)">Previous</button>
<button type="button" onclick="objViewer.Next(true)">Next</button>
<button type="button" onclick="objViewer.GotoPage(objViewer.TotalPages())">Last</button>
<button type="button" onclick="objViewer.Zoom(false)">Zoom out</button>
<button type="button" onclick="objViewer.Zoom(true)">Zoom in</button>
<button type="button" onclick="objViewer.FitType('width')">Fit width</button>
<button type="button" onclick="objViewer.FitType('height')">Fit height</button>
<button type="button" id="openSearch">Search</button>
<button type="button" id="openAnnotations">Annotations</button>
</nav>전체 참조 툴바는 또한 wwwroot/js/viewerToolbar.js 파일을 호스트 애플리케이션에 복사하여 회전, 썸네일, 인쇄, 전체 화면, 레이아웃 및 버튼 상태 도우미를 제공합니다. 이 파일은 Viewer.ReferenceScripts(...) 이후에 로드하세요. 전체 데모 구현을 복사할 때는 도우미와 <nav id="toolbar"> 마크업을 함께 유지합니다.
두 참조 애플리케이션이 사용하는 패키지 초기화 순서를 유지하세요:
- Viewer, Search 및 Annotation 리소스를 함께 내보냅니다.
- Viewer 툴바, 리본 마운트 및 Viewer 마운트를 함께 렌더링합니다.
docViewer를 먼저 초기화합니다.- 각 라이선스된 리본을 생성하고 동일한
objViewer에 연결합니다. - 문서를 열고 모듈 요청에 사용할 토큰을 보관합니다.
Doconut.TestApp.Distributed는 이 UI 구성을 정확히 유지하고 동일한 Viewer‑툴바 도우미를 사용합니다. 추가된 access 요청 값과 비동기 렌더링 재시도 설정은 분산 전송에만 해당되며, Viewer, 툴바 또는 리본 조립 방식에는 영향을 주지 않습니다.
서버 측 가드가 중요합니다: 선택적 기능이 사용 불가능할 경우 해당 스크립트가 출력되지 않아 jQuery 플러그인 함수 자체가 존재하지 않게 됩니다.
<script>
let currentToken = '';
const refitViewer = () =>
requestAnimationFrame(() => objViewer.Refit());
@if (Viewer.IsSearchEnabled)
{
<text>
searchBar = $('#searchBarMount').doconutSearchBar({
docId: 'ctlDoc',
getRequestParams: () => ({ token: currentToken }),
onLayout: refitViewer
});
searchBar.attach(objViewer);
</text>
}
@if (Viewer.IsAnnotationEnabled)
{
<text>
annBar = $('#annBarMount').doconutAnnotationBar({
docId: 'ctlDoc',
getRequestParams: () => ({ token: currentToken }),
onLayout: refitViewer
});
annBar.attach(objViewer);
</text>
}
document.getElementById('openSearch').addEventListener('click', () => {
if (!searchBar) return;
searchBar.isOpen() ? searchBar.close() : searchBar.open();
});
document.getElementById('openAnnotations').addEventListener('click', () => {
if (!annBar) return;
annBar.isOpen() ? annBar.close() : annBar.open();
});
</script>두 임베디드 컴포넌트는 각각 자체 리본 DOM을 생성합니다. Search는 Find, Options, Results 그룹을, Annotation은 저작 도구, 스타일 제어, 저장 동작 및 선택적 내보내기/이미지 동작을 포함합니다. 각 바는 open(), close(), reset(), isOpen()을 제공하며, 생성 후 반드시 한 번 attach(objViewer)를 호출해야 합니다.
위 예시는 시작을 최소화하기 위해 선택적 호스트 콜백 및 Annotation 내보내기/이미지 엔드포인트를 생략했습니다. 전체 기능별 설정은 검색 및 주석 문서를 참고하거나, 호스트가 소유한 Viewer 툴바를 스타일링하거나 교체하려면 맞춤 테마를 확인하세요.
문서 열기
서버 측은 하나의 엔드포인트만 존재합니다: 주입된 Viewer 서비스가 문서를 열고 세션 토큰을 반환합니다.
app.MapPost("/api/open", async (Viewer viewer) =>
{
// The token is opaque — hand it to the widget, never log or persist it.
string token = await viewer.OpenDocumentAsync("wwwroot/files/Sample.pdf");
return Results.Ok(new { token });
});클라이언트는 해당 토큰을 가져와 위젯에 objViewer.View(token)으로 전달합니다:
fetch('/api/open', { method: 'POST' })
.then(resp => resp.json())
.then(data => {
currentToken = data.token;
objViewer.View(currentToken);
});문서 닫기
사용자가 뷰어를 떠나거나 다른 문서를 열 때 objViewer.Close()를 호출합니다. 서버 주도 워크플로에서는 viewer.CloseDocument(token)이 캐시된 세션을 즉시 제거하고 렌더링 엔진을 해제하며 보안 마커를 삭제하고 토큰을 폐기합니다. 슬라이딩 만료도 결국 동일한 정리를 수행하지만, 대용량 문서의 경우 명시적 닫기가 권장됩니다.
완료된 요청 흐름은 다음과 같습니다:
AddDoconut + middleware
-> render CSS/scripts and mount div
-> initialize docViewer
-> OpenDocumentAsync
-> return opaque token
-> objViewer.View(token)
-> page/search/annotation requests
-> Close / CloseDocument토큰을 베어러 자격 증명처럼 취급하세요: 절대 로그에 남기지 말고, 영구 저장하지 말며, 위젯에만 전달합니다. 토큰은 서버에서 살아있는 문서 세션을 식별하며 세션이 만료되면 작동을 멈춥니다 — 새 토큰을 얻으려면 문서를 다시 열어야 합니다.
실행하기
wwwroot/files/Sample.pdf에 PDF 파일을 두고 dotnet run을 실행한 뒤 위젯을 호스팅하는 페이지를 엽니다. 첫 페이지가 뷰어에 렌더링되고 왼쪽에 썸네일 패널이 표시됩니다. 표시되지 않으면 문제 해결을 확인하세요.
라이선스 없이 얻는 것
라이선스가 없다고 오류가 발생하지는 않습니다. 뷰어는 정상적으로 렌더링되지만 모든 페이지에 평가용 워터마크가 표시됩니다. Doconut이 라이선스를 찾는 방법과 라이선스가 확보되면 어떤 변화가 일어나는지는 라이선스 설정을 참고하세요.
이 페이지가 도움이 되었나요?