ASP.NET 묶기(Bundling) 및 축소(Minification)

등록일시: 2013-05-21 20:15,  수정일시: 2016-09-02 07:24
조회수: 6,832
이 문서는 ASP.NET MVC 기술을 널리 알리고자 하는 개인적인 취지로 제공되는 번역문서입니다. 이 문서에 대한 모든 저작권은 마이크로소프트에 있으며 요청이 있을 경우 언제라도 게시가 중단될 수 있습니다. 번역 내용에 오역이 존재할 수 있고 주석은 번역자 개인의 의견일 뿐이며 마이크로소프트는 이에 관한 어떠한 보장도 하지 않습니다. 번역이 완료된 이후에도 대상 제품 및 기술이 개선되거나 변경됨에 따라 원문의 내용도 변경되거나 보완되었을 수 있으므로 주의하시기 바랍니다.

ASP.NET 4.5에서는 요청 적재 시간(Request Load Time)을 향상시킬 수 있는 묶기와 축소라는 두 가지 기법이 제공됩니다. 묶기와 축소는 서버에 대한 요청 횟수를 줄이고, 요청된 자산(Assets, CSS 및 자바스크립트 등)의 전체 크기를 감소시키는 방법을 통해서 적재 시간을 향상시켜줍니다.

대다수의 최신 유명 브라우저들은 동시 연결 갯수를 호스트 이름 당 여섯 개로 제한합니다. 이 얘기는 특정 호스트에 대해서 여섯 개의 요청이 처리되는 동안, 그 외의 추가적인 요청들은 브라우저에 의해 큐에서 대기하게 된다는 뜻입니다. 아래의 이미지는, IE에서 F12 버튼을 누르면 나타나는 개발자 도구의 네트워크 탭의 모습으로, 예제 응용 프로그램의 About 뷰에서 요청되는 자산들에 대한 적재 시점들을 보여주고 있습니다.

B/M

이 이미지에서 회색 막대는 이미 브라우저가 여섯 개의 연결을 모두 사용하고 있기 때문에, 해당 요청이 제한에 걸려 브라우저에 의해 큐에서 대기하느라 소모된 시간을 나타냅니다. 노란색 막대는 요청에 대한 최초 바이트(First Byte)를 받기까지의 시간을 의미하는데, 서버로 요청을 보낸 뒤 최초 응답, 즉 첫 번째 바이트를 받을 때까지 걸린 시간을 뜻합니다. 마지막으로, 파란색 막대는 서버로부터 응답 데이터를 모두 수신하는데 걸린 시간을 보여줍니다. 특정 자산을 더블 클릭하면 보다 상세한 시점 정보를 살펴볼 수 있습니다. 가령, 다음은 /Scripts/MyScripts/JavaScript6.js 파일 적재에 걸린 시점 상세 정보를 보여주고 있습니다.

이 이미지를 자세히 살펴보면 Start라는 이벤트가 존재하는 것을 알 수 있는데, 바로 이 이벤트가 해당 요청이 브라우저의 동시 연결 제한으로 인해 큐에서 대기한 시간을 나타냅니다. 따라서, 이 사례의 경우 앞선 요청이 완료될 때까지 46 밀리세컨트 동안 큐에서 대기했다는 것을 알 수 있습니다.

묶기(Bundling)

묶기는 ASP.NET 4.5의 새로운 기능으로 손쉽게 여러 개의 파일들을 하나의 파일로 결합시키고 묶어줍니다. CSS나 자바스크립트, 또는 그 밖의 파일들을 묶을 수 있는데, 이는 파일의 갯수가 줄어드는 결과를 가져옵니다. 그리고, 파일의 개수가 적다는 얘기는 필요한 HTTP 요청의 개수도 적어진다는 뜻이 되므로, 결과적으로 첫 번째 페이지의 적재 성능이 향상되는 결과를 가져옵니다.

다음 이미지는 앞에서 살펴봤던 이미지와 동일한 About 뷰의 적재 시점 정보를 보여주고 있습니다. 다만, 이번에는 묶기와 축소가 활성화 된 상태라는 점이 다릅니다.

축소(Minification)

축소는 스크립트나 CSS를 대상으로 불필요한 공백 문자와 주석을 제거하고 변수명들을 한 문자로 짧게 줄여주는 등의 다양한 코드 최적화 작업을 처리해줍니다. 가령, 다음과 같은 자바스크립트 함수가 존재한다고 가정해보겠습니다.

AddAltToImg = function (imageTagAndImageID, imageContext) {
    ///<signature>
    ///<summary> Adds an alt tab to the image
    ///</summary>
    ///<param name="imgElement" type="String">The image selector.</param>
    ///<param name="ContextForImage" type="String">The image context.</param>
    ///</signature>
    var imageElement = $(imageTagAndImageID, imageContext);
    imageElement.attr('alt', imageElement.attr('id').replace(/ID/, ''));
}

이 함수에 축소를 적용하고 나면, 코드가 다음과 같이 줄어들게 됩니다:

AddAltToImg = function (n, t) { var i = $(n, t); i.attr("alt", i.attr("id").replace(/ID/, "")) }

주석과 불필요한 공백이 제거되며, 다음과 같이 다음의 매개변수 및 변수들의 이름이 (짧게) 변경됩니다:

원본 변경본
imageTagAndImageID n
imageContext t
imageElement i

묶기와 축소의 효과

다음의 표는 동일한 프로그램을 대상으로 모든 자산들을 독립적으로 적재하는 경우와, 묶기와 축소(B/M)를 적용한 경우를 비교해서 몇 가지 중요한 차이점을 보여줍니다.

  B/M 적용 B/M 미적용 격차
파일 요청 9 34 256%
KB 전송 3.26 11.92 266%
KB 수신 388.51 530 36%
적재 시간 510 MS 780 MS 53%

가령, 브라우저가 요청을 전송할 때마다 포함시키는 HTTP 헤더가 상당히 장황하기 때문에 Byte 전송 항목이 눈에 띄게 줄어든 것을 알 수 있습니다. 반면, Byte 수신 항목은 그리 크게 줄어들지 않았는데, 그 이유는 대상 파일들 중에서 가장 큰 편에 속하는 파일들이 (Scripts\jquery-ui-1.8.11.min.jsScripts\jquery-1.7.1.min.js) 이미 축소되어 있기 때문입니다.

노트: 이 샘플 프로그램의 시점 정보는 Fiddler를 이용해서 느린 네트워크를 가상으로 시뮬레이션 한 것입니다. (Fiddler의 Rules 메뉴에서 Performance를 선택한 다음, Simulate Modem Speeds를 선택합니다.)

묶음과 축소가 적용된 자바스크립트 디버깅하기

개발 환경에서는 (Web.config 파일의 compilation 요소가 debug="true"로 설정된) 자바스크립트 파일들이 묶이거나 축소되지 않기 때문에 자바스크립트를 디버그하기가 쉽습니다. 그러나, 자바스크립트 파일들이 묶이고 축소된 릴리즈 빌드도 디버그를 할 수 있습니다. 다음과 같이 IE F12 개발자 도구를 이용하면 묶이거나 축소된 자바스크립트 파일의 함수를 디버그 할 수 있습니다:

  1. 스크립트 탭을 선택한 다음, 디버깅 시작 버튼을 누릅니다.
  2. 자산 버튼을 이용해서 디버그하고자 하는 자바스크립트 함수가 포함되어 있는 묶음을 선택합니다.
  3. 그리고, 구성(Configuration) 버튼을 클릭한 다음, JavaScript 형식(Format JavaScript)을 선택해서, 축소된 자바스크립트의 서식을 복원합니다.
  4. 스크립트 검색(Search Script) 입력 박스에 디버그하고자 하는 함수의 이름을 입력합니다. 가령, 다음 이미지는 스크립트 검색(Search Script) 입력 박스에 AddAltToImg를 입력한 경우를 보여주고 있습니다.

F12 개발자 도구를 이용한 디버깅에 대한 보다 많은 정보는 MSDN의 기사, Using the F12 Developer Tools to Debug JavaScript Errors를 참고하시기 바랍니다.

묶기와 축소 제어하기

묶기와 축소는 Web.config 파일의 compilation 요소의 debug 어트리뷰트 값을 설정하여 활성화시키거나 비활성화 시킬 수 있습니다. 가령, 다음 XML에서는 debug 어트리뷰트가 true로 설정되어 있으므로 묶기와 축소가 비활성화 됩니다.

<system.web>
  <compilation debug="true" />
  <!-- Lines removed for clarity. -->
</system.web>

묶기와 축소를 활성화시키려면 debug 값을 "false"로 지정합니다. 또한, BundleTable 클래스의 EnableOptimizations 속성을 설정하면 Web.config 설정을 다시 지정할 수 있습니다. 예를 들어서, 다음 코드는 묶기와 축소를 활성화시키고 Web.config 파일의 설정을 다시 지정합니다.

public static void Registerbundles(BundleCollection bundles)
{
    bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
                "~/Scripts/jquery-{version}.js"));

    // Code removed for clarity.
    BundleTable.EnableOptimizations = true;
}
노트: EnableOptimizations 속성값을 true로 설정하거나, Web.config 파일의 compilation 요소의 debug 어트리뷰트가 false로 설정하지 않는 한, 파일들은 묶이거나 축소되지 않을 것입니다. 또한, .min 버전의 파일들 대신, 디버그 버전의 파일들이 선택될 것입니다. EnableOptimizations 속성은 Web.config 파일의 compilation 요소의 debug 어트리뷰트를 재지정합니다.

ASP.NET 웹폼 및 웹 페이지에서 묶기와 축소 사용하기

ASP.NET MVC에서 묶기와 축소 사용하기

이번 절에서는 묶기와 축소를 실제로 테스트하기 위한 ASP.NET MVC 프로젝트를 생성해보겠습니다. 먼저, MvcBM이라는 이름으로 기본적인 상태에서 어떠한 변경도 가하지 않은 새로운 ASP.NET MVC 인터넷 응용 프로그램 프로젝트를 생성합니다.

그런 다음, App_Start\BundleConfig.cs 파일을 열어서 묶음을 생성하고, 등록하고, 구성하는데 사용되는 Registerbundles 메서드를 살펴봅니다. 다음 코드는 Registerbundles 메서드의 일부 코드입니다.

public static void Registerbundles(BundleCollection bundles)
{
    bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
                "~/Scripts/jquery-{version}.js"));

    // Code removed for clarity.
}

이 코드는 Scripts 폴더에 존재하는 적합한 (.vsdoc 파일을 제외한 디버그 또는 축소된) 파일들 중에서, 와일드 카드 문자열이 포함된 "~/Scripts/jquery-{version}.js"와 일치하는 모든 자바스크립트 파일들을 담고 있는 ~/bundles/jquery 라는 이름의 자바스크립트 묶음을 생성하고 있습니다. 결과적으로 디버그 구성 상태에서는 ASP.NET MVC 4가 jquery-1.7.1.js라는 파일을 묶음에 추가하게 됩니다. 반면, 릴리즈 구성 상태에서는 jquery-1.7.1.min.js라는 파일을 묶음에 추가해줍니다. 이와 같이, 번들링 프레임워크는 다음과 같은 몇 가지 공통적인 규약을 따릅니다:

  1. "FileX.min.js" 파일과 "FileX.js" 파일이 존재하는 경우, 릴리즈 구성에서는 ".min" 파일이 선택됩니다.
  2. 디버그 구성에서는 비 ".min" 파일이 선택됩니다.
  3. "-vsdoc" 파일들은 무시되는데 (jquery-1.7.1-vsdoc.js 등), 이 파일들은 인텔리센스에 의해서만 사용되기 때문입니다.

이 코드에 사용된 {version} 와일드 카드 매칭은 Scripts 폴더에 존재하는 jQuery의 버전에 적합한 jQuery 번들을 자동으로 생성하기 위한 용도로 사용됩니다. 이 예제의 경우 와일드 카드 공급자를 사용함으로써 다음과 같은 이점을 얻을 수 있습니다:

  1. 지금까지 살펴본 묶음 코드를 변경하거나 뷰 페이지의 jQuery 참조를 변경하지 않고서도, NuGet을 사용하여 새로운 jQuery 버전으로 업데이트 할 수 있습니다.
  2. 자동으로 디버그 구성에서는 완전한 버전이, 릴리즈 빌드에서는 ".min" 버전이 선택됩니다.

CDN 사용하기

다음 코드는 자체 jQuery 묶음을 CDN jQuery 묶음으로 교체합니다.

public static void Registerbundles(BundleCollection bundles)
{
    //bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
    //            "~/Scripts/jquery-{version}.js"));

    bundles.UseCdn = true;   //enable CDN support

    // add link to jquery on the CDN
    var jqueryCdnPath = "http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.1.min.js";

    bundles.Add(new ScriptBundle("~/bundles/jquery",
                jqueryCdnPath).Include(
                "~/Scripts/jquery-{version}.js"));

    // Code removed for clarity.
}

이 코드를 사용하면 릴리즈 모드에서는 CDN 상의 jQuery가 요청되고, 디버그 모드에서는 프로젝트 자체의 디버그 버전 jQuery가 페치됩니다. 참고로, CDN을 사용하는 경우에는 반드시 CDN 요청이 실패했을 경우를 대비한 대체 방안을 마련해야 합니다. 가령, 다음은 레이아웃 파일의 마지막 부분에 위치한 마크업으로, CDN이 실패한 경우 jQuery 요청을 추가하는 스크립트를 보여주고 있습니다.

        </footer>

        @Scripts.Render("~/bundles/jquery")

        <script type="text/javascript">
            if (typeof jQuery == 'undefined') {
                var e = document.createElement('script');
                e.src = '@Url.Content("~/Scripts/jquery-1.7.1.js")';
                e.type = 'text/javascript';
                document.getElementsByTagName("head")[0].appendChild(e);
            }
        </script>

        @RenderSection("scripts", required: false)
    </body>
</html>

묶음 생성하기

Bundle 클래스의 Include 메서드는 문자열 배열을 인자로 받으며, 배열의 각 문자열들은 리소스들의 가상 경로입니다. App_Start\BundleConfig.cs 파일에 존재하는 Registerbundles 메서드의 다음 코드는 다수의 파일들을 묶음에 추가하는 사례를 보여줍니다:

bundles.Add(new StyleBundle("~/Content/themes/base/css").Include(
            "~/Content/themes/base/jquery.ui.core.css",
            "~/Content/themes/base/jquery.ui.resizable.css",
            "~/Content/themes/base/jquery.ui.selectable.css",
            "~/Content/themes/base/jquery.ui.accordion.css",
            "~/Content/themes/base/jquery.ui.autocomplete.css",
            "~/Content/themes/base/jquery.ui.button.css",
            "~/Content/themes/base/jquery.ui.dialog.css",
            "~/Content/themes/base/jquery.ui.slider.css",
            "~/Content/themes/base/jquery.ui.tabs.css",
            "~/Content/themes/base/jquery.ui.datepicker.css",
            "~/Content/themes/base/jquery.ui.progressbar.css",
            "~/Content/themes/base/jquery.ui.theme.css"));

Bundle 클래스의 IncludeDirectory 메서드는 지정한 검색 패턴과 일치하는 특정 디렉터리 내의 (옵션 지정에 따라 모든 하위 디렉터리까지 포함해서) 모든 파일들을 묶음에 추가하는 기능을 제공해줍니다. Bundle 클래스의 IncludeDirectory API는 다음과 같습니다:

public Bundle IncludeDirectory(
       string directoryVirtualPath,  // The Virtual Path for the directory.
       string searchPattern)         // The search pattern.

public Bundle IncludeDirectory(
       string directoryVirtualPath,  // The Virtual Path for the directory.
       string searchPattern,         // The search pattern.
       bool searchSubdirectories)    // true to search subdirectories.

묶음은 뷰에서 Render 메서드를 이용해서 참조됩니다 (CSS는 Styles.Render 메서드, 자바스크립트는 Scripts.Render 메서드). 다음은 Views\Shared\_Layout.cshtml 파일의 마크업으로, 기본 ASP.NET 인터넷 응용 프로그램 프로젝트 뷰에서 CSS와 자바스크립트 묶음을 참조하는 방식을 보여줍니다.

<!DOCTYPE html>
<html lang="en">
<head>
    @* Markup removed for clarity. *@

    @Styles.Render("~/Content/themes/base/css", "~/Content/css")
    @Scripts.Render("~/bundles/modernizr")
</head>
<body>
    @* Markup removed for clarity. *@

    @Scripts.Render("~/bundles/jquery")
    @RenderSection("scripts", required: false)
</body>
</html>

Render 메서드가 문자열 배열을 인자로 받을 수 있기 때문에, 여러 개의 묶음을 한 줄의 코드에 추가할 수 있다는 사실을 기억해두시기 바랍니다. 대부분, Render 메서드를 사용해서 자산을 참조하는 HTML을 생성하는 방식이 일반적입니다. 그러나, 마크업 없이 자산을 참조하기 위한 목적으로 자산에 대한 URL을 직접 생성하고자 하는 경우라면 Url 메서드를 사용할 수 있습니다. 가령, HTML5의 새로운 async 어트리뷰트를 사용하고 싶을 수도 있을 것입니다. 다음 코드는 Url 메서드를 사용하여 Modernizr를 참조하는 방법을 보여주고 있습니다.

<head>
    @* Markup removed for clarity *@
    <meta charset="utf-8" />
    <title>@ViewBag.Title - MVC 4 B/M</title>
    <link href="~/favicon.ico" rel="shortcut icon" type="image/x-icon" />
    <meta name="viewport" content="width=device-width" />
    @Styles.Render("~/Content/css")

    @* @Scripts.Render("~/bundles/modernizr") *@
    <script src='@Scripts.Url("~/bundles/modernizr")' async> </script>
</head>

"*" 와일드카드 문자로 파일 선택하기

Include 메서드에 지정된 가상 경로나 IncludeDirectory 메서드의 검색 패턴 중 마지막 경로 요소에 대해 접두사나 접미사로 한 개의 "*" 와일드 카드 문자를 사용할 수 있습니다. 검색 문자열은 대소문자를 구분하지 않으며, IncludeDirectory 메서드는 하위 디렉터리 검색 여부에 대한 옵션도 지원해줍니다.

예를 들어서, 프로젝트에 다음과 같은 자바스크립트 파일들이 존재한다고 가정해보겠습니다:

  • Scripts\Common\AddAltToImg.js
  • Scripts\Common\ToggleDiv.js
  • Scripts\Common\ToggleImg.js
  • Scripts\Common\Sub1\ToggleLinks.js

dir imag

다음의 표는 와일드카드를 사용하는 방식에 따라 묶음에 추가되는 파일들을 보여줍니다:

호출 추가되는 파일들 또는 발생하는 예외
Include("~/Scripts/Common/*.js") AddAltToImg.js, ToggleDiv.js, ToggleImg.js
Include("~/Scripts/Common/T*.js") Invalid pattern 예외 발생. 와일드카드 문자는 접두사나 접미사로만 사용할 수 있습니다.
Include("~/Scripts/Common/*og.*") Invalid pattern 예외 발생. 단 한 개의 와일드카드 문자만 사용할 수 있습니다.
"Include("~/Scripts/Common/T*") ToggleDiv.js, ToggleImg.js
"Include("~/Scripts/Common/*") Invalid pattern 예외 발생. 와일드카드로만 구성된 경로 요소는 사용할 수 없습니다.
IncludeDirectory("~/Scripts/Common", "T*") ToggleDiv.js, ToggleImg.js
IncludeDirectory("~/Scripts/Common", "T*", true) ToggleDiv.js, ToggleImg.js, ToggleLinks.js

대게의 경우, 와일드카드를 이용해서 파일들을 로딩하는 방법보다는 명시적으로 각각의 파일들을 지정해서 묶음에 포함시키는 방법이 선호되는 편인데, 그 이유는 다음과 같습니다.

  1. 기본적으로 와일드카드를 이용해서 스크립트들을 추가하면 알파벳 순으로 로딩되는데, 대부분 이는 우리들이 원하는 결과가 아닙니다. CSS 및 자바스크립트 파일들은 특정한 순서로 (알파벳 순이 아닌) 추가되어야 하는 경우가 빈번합니다. 사용자 정의 IBundleOrderer 인터페이스 구현을 추가해서 이 위험도를 줄일 수야 있지만, 각각의 파일들을 명시적으로 추가하는 편이 대체적으로 오류가 적습니다. 가령, 향후에 해당 폴더에 새로운 자원을 추가하게 될 수도 있는데, 그로 인해서 여러분이 구현한 IBundleOrderer 인터페이스를 수정할 수 밖에 없을 수도 있습니다.
  2. 특정 뷰에 종속적인 파일들이 와일드카드 로딩에 사용되는 디렉터리에 추가되면, 이 파일들이 해당 묶음을 참조하는 모든 뷰에 포함될 수 있습니다. 만약, 특정 뷰에 종속적인 스크립트 파일이 묶음에 추가되면, 해당 묶음을 참조하는 다른 뷰에서는 자바스크립트 오류가 발생하게 될 것입니다.
  3. 다른 파일들을 임포트하는 CSS 파일들은 임포트 되는 파일들을 두 번 로드하는 결과를 가져오게 됩니다. 가령, 다음의 코드는 대부분의 jQuery UI 테마 CSS 파일들을 두 번 로드하는 묶음을 생성하게 됩니다.
    bundles.Add(new StyleBundle("~/jQueryUI/themes/baseAll")
           .IncludeDirectory("~/Content/themes/base", "*.css"));
    즉, 와일드카드 "*.css" 선택자는 Content\themes\base\jquery.ui.all.css 파일을 포함해서 해당 폴더의 모든 CSS 파일들을 추가하는데, jquery.ui.all.css 파일 자체에서도 다른 CSS 파일들을 임포트하기 때문입니다.

묶음 캐싱

기본적으로 묶음의 HTTP 만료 헤더값은 해당 묶음이 생성된 날로부터 1년 까지로 설정됩니다. 기존에 방문한 적이 있는 페이지를 다시 방문하면서 피들러로 살펴보면, IE가 해당 묶음에 대한 조건부 요청(Conditional Request)을 만들어내지 않는다는 사실을 확인할 수 있습니다. 즉, 해당 묶음에 대한 IE의 HTTP GET 요청이 발생하지 않을 뿐만아니라, 서버로부터 HTTP 304 응답도 돌아오지 않습니다. 참고로, F5 키를 사용해서 IE로 하여금 각각의 묶음에 대해 조건부 요청을 생성하도록 만들 수 있습니다 (이 경우, 각각의 묶음에 대한 HTTP 304 응답이 만들어집니다). 그리고, Ctrl+F5 키를 사용하면 완전히 새로 고침을 할 수도 있습니다 (이 경우, 각각의 묶음에 대한 HTTP 200 응답이 만들어집니다).

다음 이미지는 Fiddler 응답 패인의 Caching 탭을 보여주고 있습니다:

fiddler caching image

다음은 AllMyScripts 묶음에 대한 요청인데, v=r0sLDicvP58AIXN_mc3QdyVvVj5euZNzdsa2N1PKvb81라는 쿼리스트링 쌍을 포함하고 있는 것을 볼 수 있습니다.

http://localhost/MvcBM_time/bundles/AllMyScripts?v=r0sLDicvP58AIXN_mc3QdyVvVj5euZNzdsa2N1PKvb81

이 쿼리스트링 v의 값은 캐싱에 사용되는 유일한 식별자인 토큰입니다. 묶음이 변경되지 않는 한, ASP.NET 응용 프로그램은 이 토큰을 사용해서 AllMyScripts 묶음을 요청하게 됩니다. 묶음에 포함된 파일 중, 한 파일이라도 변경되면, ASP.NET 최적화 프레임워크(Optimization Framework)가 새로운 토큰을 생성하고, 해당 묶음에 대한 브라우저 요청에 대해 가장 최신의 묶음이 반환되는 것을 보장해줍니다.

그러나, IE9의 F12 개발자 도구를 이용해서 로드했던 적이 있는 페이지를 방문해보면, IE는 각각의 묶음에 대해서 조건부 GET 요청이 만들어지고 서버로부터 HTTP 304가 반환되는 부정확한 결과를 보여줍니다. 왜 IE9에서 이런 조건부 요청에 대한 결정 문제가 발생하는지에 관해서는 Using CDNs and Expires to Improve Web Site Performance 블로그 문서를 참고하시기 바랍니다.

LESS, CoffeeScript, SCSS, Sass 묶기

묶기 및 축소 프레임워크에서는 SCSS, Sass, LESS 또는 Coffeescript 등과 같은 중간 언어(Intermediate Languages) 처리를 위한 메커니즘을 지원해주며, 최종 묶음 축소 등에 변환을 적용해줍니다. 가령, .less 파일들을 MVC 4 프로젝트에 추가하려면:

  1. LESS 콘텐트를 위한 폴더를 생성합니다. 이번 예제에서는 Content\MyLess라는 폴더를 사용한다고 가정하겠습니다.
  2. 그리고, dotless .less NuGet 패키지를 프로젝트에 추가합니다.
    NuGet dotless install

  3. IBundleTransform 인터페이스를 구현한 클래스를 추가합니다. 가령, 다음과 같이 .less 변환을 위한 코드를 프로젝트에 추가합니다.
    using System.Web.Optimization;
    
    public class LessTransform : IBundleTransform
    {
        public void Process(BundleContext context, BundleResponse response)
        {
            response.Content = dotless.Core.Less.Parse(response.Content);
            response.ContentType = "text/css";
        }
    }
  4. LessTransformCssMinify 변환을 사용해서 LESS 파일들의 묶음을 생성합니다. 다음의 코드를 App_Start\BundleConfig.cs 파일의 Registerbundles 메서드에 추가합니다.
    var lessBundle = new Bundle("~/My/Less").IncludeDirectory("~/My", "*.less");
    lessBundle.Transforms.Add(new LessTransform());
    lessBundle.Transforms.Add(new CssMinify());
    bundles.Add(lessBundle);
  5. 이제, LESS 묶음을 참조하고자 하는 뷰에서는 다음과 같은 코드를 사용하면 됩니다.
    @Styles.Render("~/My/Less");

묶음 사용시 고려사항

묶음을 생성할 때, 가급적 준수하면 바람직한 규약들 중 하나는 묶음 이름에 접두사로 "bundle"을 포함시키는 것입니다. 그러면, 라우팅 충돌이 발생할 가능성을 줄일 수 있습니다.

묶음에 포함된 파일이 변경되면 묶음 쿼리스트링 매개변수에 사용되는 새로운 토큰이 생성되며, 그 이후에 클라이언트가 해당 묶음을 포함하고 있는 페이지를 요청하면 묶음 전체가 다운로드됩니다. 반면, 각각의 리소스들을 개별적으로 나열하는 전통적인 마크업 방식에서는 변경된 파일만 다운로드됩니다. 따라서, 빈번하게 변경되는 파일들은 묶음에 포함시키지 않는 것이 좋습니다.

묶기와 축소는 주로 첫 번째 페이지 요청 시의 적재 시간을 향상시킵니다. 일단 웹 페이지가 한 번 요청되고 나면 브라우저가 해당 자산들을 (자바스크립트, CSS 및 이미지) 캐싱하므로, 동일한 페이지를 요청하거나 동일한 사이트에서 동일한 리소스들을 요청하는 경우에는 묶기와 축소가 아무런 성능 개선 효과를 제공해주지 못합니다. 만약, 리소스들에 대한 만료 헤더가 올바르게 설정되지 않은 상태에서 묶기와 축소 기능도 사용하지 않는다면, 브라우저는 몇 일 뒤에야 해당 리소스들이 최신 정보가 아니라는 것을 감지하고 각각의 자산들에 대한 유효성 검사를 요청하게 될 것입니다. 이런 경우에는, 묶기와 축소를 사용해서 첫 번째 페이지 요청 이후의 성능 향상을 이끌어 낼 수 있습니다. 보다 자세한 정보는 Using CDNs and Expires to Improve Web Site Performance 블로그 문서를 참고하시기 바랍니다.

브라우저가 가지고 있는 호스트 이름 당 여섯 개의 동시 연결 제한은 CDN을 사용함으로서 완화시킬 수 있습니다. CDN의 호스트명은 여러분이 관리하는 사이트의 호스트명과 다르기 때문에, CDN에 대한 리소스 요청은 여러분의 호스팅 환경에 대한 여섯 개의 동시 연결 제한에 포함되지 않습니다. 게다가, CDN은 일반적인 패키지 캐싱 및 에지 캐싱(Edge Caching)의 장점도 제공해줍니다.

묶음은 해당 묶음을 필요로 하는 페이지들에 따라 분할되어야만 합니다. 가령, 인터넷 응용 프로그램에 대한 기본 ASP.NET MVC 템플릿은 jQuery 묶음과 별도로 jQuery 유효성 검사 묶음을 생성하는 것을 볼 수 있습니다. 왜냐하면, 기본 뷰 같은 경우는 입력도 없고 전송되는 값도 전혀 없기 때문에, 유효성 검사 묶음을 포함할 필요가 없기 때문입니다.

마지막으로, System.Web.Optimization 네임스페이스는 System.Web.Optimization.DLL에 구현되어 있습니다. 그리고, 축소 기능을 위해서 Antlr3.Runtime.dll을 사용하는 WebGrease 라이브러리(WebGrease.dll)를 이용합니다.

필자는 빠른 소식 전달과 링크 공유를 위해 트위터를 이용합니다. 필자의 트위터 핸들은 @RickAndMSFT 입니다.

추가 참고자료

도움을 주신 분들