재우니 개발자 블로그

문제 분석 및 원인

PDF.js는 버전이 업데이트되면서 보안 및 기능 개선이 이루어졌습니다. 특히 URL 파라미터 처리 방식에 변화가 있었습니다. 2.3.200 버전에서는 file 파라미터 뒤에 인코딩된 URL이 디코딩되어 백엔드 호출이 정상적으로 이루어졌지만, 5.4.149 버전에서는 보안 강화로 인해 URL에 특정 문자가 포함된 경우 요청이 차단되거나 정상적으로 파싱되지 않는 문제가 발생할 수 있습니다. 이는 URL의 유효성 검사 로직이 강화되었기 때문입니다.

 

해결 방안

PDF.js 최신 버전에서 URL을 안전하게 전달하려면, URL 자체를 URL-encoded string으로 만들고, 이를 다시 file 파라미터의 값으로 전달해야 합니다. 이는 URL 내의 특수문자(?, &, / 등)가 PDF.js 내부 파서에 의해 오인되지 않도록 하기 위함입니다.

다음은 최신 PDF.js 버전에서 URL을 전달하는 코드 예시입니다.

 

 

 

// jQuery를 사용한 코드
// HTML에 <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> 가 포함되어 있어야 합니다.

// 문서 준비 완료 후 실행
$(document).ready(function() {

    // 백엔드 API URL !! 필히 도메인주소 넣으세요.~
    var backendUrl = 'https://your-backend-api.com/get-pdf-file';

    // 백엔드 API에 전달할 파라미터 객체
    var params = {
        id: 'document-12345',
        type: 'invoice'
    };

    // jQuery.param()을 사용하여 URL 파라미터 문자열 생성
    var queryString = $.param(params);

    // 완전한 URL 구성
    var fullBackendUrl = backendUrl + '?' + queryString;

    // URL 인코딩
    var encodedUrl = encodeURIComponent(fullBackendUrl);

    // PDF.js 뷰어 URL 구성
    var pdfViewerUrl = 'https://your-cdn-path.com/pdfjs-5.4.149/web/viewer.html?file=' + encodedUrl;

    // jQuery 셀렉터를 사용하여 iframe에 src 속성 설정
    $('#pdfViewer').attr('src', pdfViewerUrl);
});

 

<iframe id="ifrPDFViewer" frameborder="0" style="width:100%;height:auto; min-height:740px;"></iframe>

 

 

// HTML에서 데이터 속성을 통해 값을 가져오는 것이 더 좋은 방법입니다.
// 이 예시에서는 기존 코드와 동일하게 변수를 사용합니다.
const backendUrl = '@domain/FileDownload/GetFileStream';
const pdfFilePath = '@pdfFilePath';

// URL 파라미터 구성
const params = {
    url: pdfFilePath,
};
const queryString = $.param(params);

// PDF Viewer URL 구성 및 iframe src 설정
const fullBackendUrl = `${backendUrl}?${queryString}`;
const encodedUrl = encodeURIComponent(fullBackendUrl);
const pdfViewerUrl = `/scripts/plugins/pdfJS-5.4.149/web/viewer.html?file=${encodedUrl}`;
$('#ifrPDFViewerNew').attr('src', pdfViewerUrl);

// iframe 로드 완료 시 이벤트 처리
$('#ifrPDFViewerNew').on('load', function () {
    try {
        const iframeContents = $(this).contents();
        
        // 숨기고자 하는 버튼 ID 목록을 배열로 관리
        const hiddenButtons = [
            '#printButton',
            '#downloadButton',
            '#editorStampButton',
            '#secondaryToolbarToggleButton'
        ];
        
        // jQuery의 .each() 메서드 또는 JavaScript의 forEach를 사용해 반복 처리
        $(hiddenButtons.join(', ')).each(function() {
             iframeContents.find($(this).selector).hide();
        });
        
    } catch (e) {
        // Cross-origin 이슈를 명확히 설명
        console.error("iframe 콘텐츠 접근 오류: 다른 도메인에서 로드된 콘텐츠일 수 있습니다.", e);
    }
});

 

 

file 파라미터에 도메인 주소 URL 꼭 기재하는것을 잊지 마세요. 🚨🚨🚨🚨

https://sample.univ.me/scripts/plugins/pdfJS-5.4.54/web/viewer.html?file=https%3A%2F%2Fsample.univ.me%2FFileDownload%2FGetFileStream%3Furl%3D%252FFileData%252F1%252FBoard%252F202509%252F11832%252FAttach%252Ff310bd9e-9b01-45b5-9e14-d103f88a0eff.pdf

 

 

file 파라미터에 실제 pdf 링크 주소나 backend url 주소로도 가능합니다.

public FileResult GetFileStream(string url)
{
    //url = "/FileData/1/Board/202503/5347/Attach/compressed.tracemonkey-pldi-09.pdf";
    url = Server.UrlDecode(url);
    
    var fileStream = FileHelper.GetFileToStream(url);
    var fileExtension = Path.GetExtension(url);
    var mimeType = GetMimeType(fileExtension);
    return File(fileStream, mimeType);
}