빠른 시작

몇 분 안에 첫 문서를 렌더링하세요

이 안내서는 빈 Program.cs 파일을 가진 ASP.NET Core 앱을 브라우저에서 문서가 렌더링되는 상태까지 진행합니다: 서버 등록, 전체 Viewer 패키지(Viewer 툴바, Viewer 마운트 및 선택적 Search/Annotation 리본), 자산 참조, 클라이언트 초기화, 문서 열기 및 실행 과정 전체를 다룹니다.

서버 설정

AddDoconut()는 서비스를 등록하고; UseDoconutResources()UseDoconut()는 미들웨어를 연결합니다. 리소스 호출은 먼저 와야 합니다. 세션 호출도 필요합니다 — Doconut의 기본 문서 보안은 ASP.NET 세션 상태를 기준으로 모든 페이지 요청을 검증합니다. 이미 설치 단계에서 Doconut을 등록했나요? 다음 섹션으로 바로 넘어가세요.

csharp
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();

프로덕션 스타일 경로 레이아웃을 위해, 문서 미들웨어를 명시적인 브랜치에 매핑하고 네 개의 경로 설정을 일치시킵니다:

csharp
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를 사용합니다:

html
<div id="divDocViewer">
    <div id="div_ctlDoc"></div>
</div>

툴바, 모듈 마운트, Viewer 표면을 하나의 페이지 구성으로 취급하십시오. Search와 Annotation은 선택적 마운트에 자체 리본을 삽입하지만, 해당 모듈은 절대 독립적으로 존재하지 않으며 항상 같은 페이지의 Viewer에 붙어 있습니다. Doconut.TestAppDoconut.TestApp.Distributed와 동일한 순서를 사용합니다:

html
<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가 뷰어 스크립트보다 먼저 로드되어야 합니다:

html
@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
@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
}))

IncludeViewerCssIncludeViewerScripts는 필수 핵심 플래그입니다. 이들 없이 Search 또는 Annotation 리본 예제를 배포하지 마세요. Viewer 마운트와 docViewer 인스턴스가 반드시 필요합니다. ReferenceCssReferenceScripts는 현재 라이선스가 해당 기능을 허용하지 않을 경우 선택적 모듈의 리소스를 생략합니다; 핵심 Viewer는 여전히 시작됩니다.

뷰어 초기화하기

클라이언트 측 위젯은 jQuery 플러그인입니다. 아래는 실제 초기화 옵션의 최소 집합이며(의사코드가 아님)입니다:

javascript
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 6 레퍼런스 애플리케이션은 다음 구성 요소들을 한 페이지에 함께 설치합니다:

패키지 구성 요소필요 여부연결 방식
Viewer 리소스, 마운트 및 objViewer필수핵심 문서 렌더러
Viewer 툴바레퍼런스 구성에 필수호스트 마크업; 버튼이 동일 objViewer를 호출
Search 리본선택적, 라이선스 모듈doconutSearchBar(...).attach(objViewer)
Annotation 리본선택적, 라이선스 모듈doconutAnnotationBar(...).attach(objViewer)

주된 Viewer 툴바는 호스트 마크업이지만 Viewer와 함께 설치되며, 절대로 독립된 컨트롤로 문서화되지 않아야 합니다. 이렇게 하면 레이아웃, 레이블, 아이콘 및 권한 규칙이 애플리케이션의 제어 하에 유지되면서 모든 버튼이 동일 Viewer 인스턴스를 구동합니다:

html
<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"> 마크업을 함께 유지하세요.

두 레퍼런스 애플리케이션이 사용하는 패키지 초기화 순서를 유지하십시오:

  1. Viewer와 라이선스 모듈용 CSS를 출력합니다.
  2. Viewer 툴바, Search/Annotation 마운트, Viewer 마운트를 함께 렌더링합니다.
  3. Viewer와 라이선스 모듈용 스크립트를 출력합니다.
  4. 호스트 애플리케이션의 viewerToolbar.js를 로드합니다.
  5. docViewer를 초기화하고 결과 objViewer를 보관합니다.
  6. 각각의 라이선스된 Search 또는 Annotation 리본을 초기화합니다.
  7. 모든 리본에 attach(objViewer)를 호출합니다.
  8. 문서를 열고 Viewer와 모듈 요청에 사용할 토큰을 보관합니다.

Doconut.TestApp.Distributed는 동일한 UI 구성을 유지하며 동일한 Viewer‑툴바 도우미를 사용합니다. 추가된 access 요청 값과 비동기 렌더링 재시도 설정은 분산 전송에만 해당되며, Viewer, 툴바 또는 리본 조합 방식에는 영향을 주지 않습니다.

서버 측 가드도 중요합니다: 선택적 기능이 사용 불가능할 경우 해당 스크립트가 출력되지 않으므로 jQuery 플러그인 함수 자체가 존재하지 않게 됩니다.

html
<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 내보내기/이미지 엔드포인트를 생략했습니다. 전체 기능별 설정은 SearchAnnotations를 참고하고, 호스트가 소유한 Viewer 툴바를 스타일링하거나 교체하려면 Custom Themes를 확인하십시오.

문서 열기

서버 측은 하나의 엔드포인트만 존재합니다: 주입된 Viewer 서비스가 문서를 열고 세션 토큰을 반환합니다.

csharp
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)에 전달합니다:

javascript
fetch('/api/open', { method: 'POST' })
    .then(resp => resp.json())
    .then(data => {
        currentToken = data.token;
        objViewer.View(currentToken);
    });

문서 닫기

사용자가 뷰어를 떠나거나 다른 문서를 열 때 objViewer.Close()를 호출하십시오. 서버‑주도 워크플로에서는 viewer.CloseDocument(token)이 즉시 캐시된 세션을 제거하고 렌더링 엔진을 해제하며 보안 마커를 삭제하고 토큰을 폐기합니다. 슬라이딩 만료도 동일한 정리를 수행하지만, 대용량 문서의 경우 명시적 닫기를 권장합니다.

완료된 요청 흐름은 다음과 같습니다:

text
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 파일을 배치하고 dotnet run을 실행한 뒤 위젯을 호스팅하는 페이지를 엽니다. 첫 페이지가 뷰어에 렌더링되고 왼쪽에 썸네일 패널이 표시됩니다. 표시되지 않으면 Troubleshooting을 확인하십시오.

라이선스 없이 얻을 수 있는 것

라이선스가 없다고 오류가 발생하지는 않습니다. 뷰어는 정상적으로 렌더링되지만 모든 페이지에 평가용 워터마크가 표시됩니다. Doconut이 라이선스를 찾는 방법과 라이선스를 찾은 후 어떤 변화가 일어나는지는 License Setup에서 확인하십시오.

이 페이지가 도움이 되었나요?