빠른 시작: 앱에 검색 추가(HTML)

[ 이 문서는 Windows 런타임 앱을 작성하는 Windows에서 8.x 및 Windows Phone 8.x 개발자를 대상으로 합니다. Windows 10용으로 개발하는 경우에는 최신 설명서를 참조하세요.]

대부분 사용자는 검색에 의존하여 찾고 있는 내용을 찾습니다. 예를 들어 앱이 미디어 파일을 재생하는 경우 사용자는 특정 음악이나 동영상을 검색할 수 있기를 원합니다. 앱이 요리 앱인 경우 사용자는 특정 조리법이나 재료를 검색할 수 있기를 원합니다.

약간의 계획을 통해 앱에 검색 기능을 쉽게 추가할 수 있습니다. 필요한 사항은 다음과 같습니다.

  • 검색할 데이터 소스. 사용자가 검색할 항목의 카탈로그 또는 인벤토리가 필요합니다. 이 인벤토리의 정보가 자세할수록 검색 결과가 더 정확해집니다.
  • 검색 쿼리를 입력하기 위한 컨트롤. Windows는 앱에서 사용할 수 있는 SearchBox 컨트롤을 제공합니다. SearchBox는 쿼리를 입력하기 위한 입력 영역, 검색을 실행하기 위한 검색 단추 및 검색 쿼리를 처리하기 위한 이벤트를 제공합니다. 몇 가지 검색 제안도 자동으로 제공합니다.
  • 검색 결과를 표시하기 위한 페이지. Microsoft Visual Studio는 검색 쿼리를 처리하고 결과를 표시하는 데 필요한 많은 코드를 만드는 검색 결과 페이지 템플릿을 제공합니다.

이 빠른 시작에서는 이러한 항목을 사용하여 앱에 검색 기능을 추가하는 방법을 설명합니다.

앱 기능 전체 프로세스 시리즈의 일부로 이 기능의 작동 방법을 살펴보세요.: Windows 스토어 앱 UI 전체 프로세스

사전 요구 사항

데이터 설정

사용자가 검색 쿼리를 입력하면 앱에서 사용자가 찾고 있는 항목을 검색합니다. 앱에서 검색하는 데이터는 XML 파일, JSON(JavaScript Object Notation) 데이터, 데이터베이스, 웹 서비스, 파일 시스템의 파일 등 여러 형태일 수 있습니다.

이 빠른 시작의 예제는 Microsoft Visual Studio에서 새 프로젝트를 만들 때 Visual Studio에서 생성하는 샘플 데이터를 사용합니다.

Visual Studio를 사용하여 새 그리드 앱, 허브 앱 또는 분할 앱을 만들면 앱의 js 폴더에 data.js 파일이 생성됩니다. 이 파일에는 사용자 데이터로 바꿀 수 있는 정적 데이터가 포함됩니다. 예를 들어 앱이 RSS 또는 JSON 데이터를 가져오도록 단일 xhr 요청을 만들 경우 코드를 data.js에 추가할 수 있습니다. 여기에 이 코드를 포함하면 검색 결과 페이지에 사용되는 데이터 모델을 변경하지 않고 사용자 데이터를 쉽게 사용할 수 있습니다.

다음은 데이터가 표시되는 샘플의 예입니다.

function generateSampleData() {
    // . . .
    var sampleGroups = [
        { key: "group1", title: "Group Title: 1", // . . .
        // . . .
    ];

    var sampleItems = [
        { group: sampleGroups[0], title: "Item Title: 1", // . . .
        // . . .
    ];

    return sampleItems;
}

이 데이터가 사용자 파일에 액세스할 수 있도록 data.js 파일에서는 다음 멤버를 노출하는 Data 네임스페이스를 정의합니다.

  • items: 데이터 항목이 포함된 WinJS.Binding.List입니다. 그룹화된 List입니다.
  • groups: 데이터 항목이 속해 있는 그룹이 포함된 WinJS.Binding.List입니다. (items.groups를 호출하여 그룹을 가져올 수도 있습니다.)
  • getItemReference: 지정된 항목의 그룹 키와 제목이 포함된 개체를 검색합니다.
  • getItemsFromGroup: 지정된 키가 있는 그룹에 속한 항목이 포함된 FilteredListProjection을 검색합니다.
  • resolveGroupReference: 지정된 키가 있는 그룹을 나타내는 개체를 검색합니다.
  • resolveItemReference: 이 메서드는 두 개의 문자열인 그룹 키와 제목이 포함된 배열을 사용합니다. 이 메서드는 지정된 그룹 키와 제목이 있는 항목을 검색합니다.

데이터를 포함하기 위해 이 네임스페이스와 이러한 멤버를 사용할 필요는 없지만, 사용할 경우 검색 결과 페이지 템플릿을 더 쉽게 사용할 수 있습니다.

(템플릿 생성 데이터를 사용하는 방법에 대한 자세한 내용은 Visual Studio 템플릿 데이터를 사용자 지정하는 방법을 참조하세요.)

검색 결과 페이지 추가

검색 결과 페이지는 검색 쿼리를 처리하고 결과를 표시합니다. 프로젝트에 검색 결과 페이지를 추가하겠습니다. (이러한 지침은 프로젝트가 허브, 그리드 또는 분할 템플릿에서 생성되었다고 가정합니다. )

Hh465238.wedge(ko-kr,WIN.10).gif검색 결과 페이지 항목 추가

  1. 솔루션 탐색기pages 프로젝트 폴더에서 search라는 새 폴더를 추가합니다.

  2. search 폴더에 대한 바로 가기 메뉴를 열고 추가 > 새 항목을 선택합니다.

  3. 새 항목 추가 대화 상자의 가운데 창에서 검색 결과 페이지를 선택합니다. 이 예제에서는 이름 상자에 표시되는 기본 이름인 searchResults.html을 그대로 유지합니다.

  4. 추가를 선택합니다.

    search 폴더의 프로젝트에 searchResults.html, searchResults.css 및 searchResults.js가 추가됩니다.

검색 상자 추가

검색 결과 페이지에서 수행할 작업이 남아 있지만 먼저 앱에 SearchBox를 추가하겠습니다. SearchBox를 포함하면 구현 시 검색 결과 페이지를 더 쉽게 테스트할 수 있습니다.

SearchBox에서는 사용자가 쿼리를 입력할 수 있습니다. 제안을 표시할 수도 있습니다. 앱에 SearchBox를 추가하려면 HTML 페이지에 다음 태그를 추가하면 됩니다.

<div class="searchBox"
    data-win-control="WinJS.UI.SearchBox"
    data-win-options="{placeholderText: 'Search'}">
</div>

(onquerysubmitted 이벤트에 등록해야 할 수도 있습니다. 이후 단계에서 이 작업을 합니다.)

검색 상자는 어디에 배치해야 할까요? 사용자가 원할 때마다 쉽게 검색할 수 있도록 앱의 각 페이지에 검색 상자를 배치하는 것이 좋습니다. 공간이 문제가 되는 경우 위쪽 앱 바에 검색 상자를 배치할 수 있습니다.

Hh465238.wedge(ko-kr,WIN.10).gif페이지에 SearchBox 추가

  1. 앱 페이지 중 하나에 SearchBox를 추가해보겠습니다. 이러한 지침은 Page 컨트롤을 기반으로 하는 모든 페이지에 적용됩니다.

    일반적으로 페이지의 오른쪽 위에 SearchBox를 배치하는 것이 가장 좋습니다. Visual Studio 템플릿을 통해 만든 대부분 페이지(예: Page 컨트롤 템플릿)에는 페이지 제목과 뒤로 단추가 포함된 header 요소가 있습니다.

            <header aria-label="Header content" role="banner">
                <button data-win-control="WinJS.UI.BackButton"></button>
                <h1 class="titlearea win-type-ellipsis">
                    <span class="pagetitle"></span>
                </h1>
            </header>
    

    h1 요소 뒤에 SearchBox를 추가하면 됩니다.

            <header aria-label="Header content" role="banner">
                <button data-win-control="WinJS.UI.BackButton"></button>
                <h1 class="titlearea win-type-ellipsis">
                    <span class="pagetitle">Welcome to basicPage</span>
                </h1>
                <div class="searchBox"
                     data-win-control="WinJS.UI.SearchBox"
                     data-win-options="{placeholderText: 'Search'}">
                </div>
            </header>
    
  2. (권장) 사용자가 키보드에서 검색을 시작하여 앱에서 콘텐츠를 검색할 수 있게 해야 합니다.

    대부분의 사용자는 키보드를 사용하여 Windows 8을 조작합니다. 사용자가 입력하여 검색하도록 하면 키보드 조작을 효율적으로 사용할 수 있으며 앱의 검색 환경이 시작 화면과 일치하게 됩니다.

    사용자가 입력할 때 검색 상자에 입력이 포함되도록 SearchBox 컨트롤의 focusOnKeyboardInput 속성을 true로 설정합니다.

                <div class="searchBox"
                     data-win-control="WinJS.UI.SearchBox"
                     data-win-options="{placeholderText: 'Search',
                     focusOnKeyboardInput: true }">
                </div>
    
  3. Visual Studio에서 생성되는 default.css 스타일시트는 헤더 요소에 -ms-grid 레이아웃을 제공합니다. SearchBox를 페이지의 오른쪽 위 모서리에 배치하려면 페이지에 대한 CSS(CSS 스타일시트) 파일에 이 스타일을 추가하면 됩니다.

    .searchBox {
        -ms-grid-column: 4;
        margin-top: 57px;
        margin-right: 29px;
    }
    

Hh465238.wedge(ko-kr,WIN.10).gifonquerysubmitted 이벤트 처리

  1. 앱에 여러 SearchBox 컨트롤이 포함될 수 있습니다. 이러한 컨트롤이 모두 사용할 수 있는 단일 onquerysubmitted 이벤트 처리기를 정의해보겠습니다.

    앱의 default.js 파일을 엽니다.

  2. "args"라는 단일 인수를 사용하는 "querySubmittedHandler"라는 onquerysubmitted 이벤트 처리기를 만듭니다. (기존 default.js 코드를 래핑하는 익명 함수 내부 아무 곳이나 이 메서드 정의를 넣을 수 있습니다.)

        function querySubmittedHandler(args) {
    
        }
    
  3. 이벤트 처리기를 통해 WinJS.Navigation.navigate를 호출하여 새 검색 결과 페이지를 탐색합니다. args.details 속성에는 WinJS.Navigation.navigate를 호출할 때 이 개체를 전달하도록 검색 결과 페이지에 필요한 이벤트 정보를 제공하는 개체가 포함되어 있습니다.

        function querySubmittedHandler(args) {
            WinJS.Navigation.navigate('/pages/search/searchResults.html', args.detail);
        }
    

    경고  빈 앱 템플릿을 사용하여 앱을 만든 경우 검색이 작동하려면 앱에 탐색 지원을 추가해야 합니다. PageControlNavigator라는 사용자 지정 컨트롤을 앱에 추가하면 표 형태, 분할 및 탐색 앱 템플릿과 동일한 방식으로 탐색을 지원할 수 있습니다. 이 사용자 지정 컨트롤이 탐색을 지원하는 방법은 빠른 시작: 단일 페이지 탐색 사용에서 확인할 수 있습니다. 사용자 지정 컨트롤을 사용하지 않고 탐색을 지원하는 것이 더 나은 경우에는 WinJS.Navigation.navigated와 같은 탐색 이벤트를 수신 대기하고 응답하는 코드를 직접 작성해야 합니다. PageControlNavigator와 같은 사용자 지정 컨트롤을 사용하지 않고 탐색을 지원하는 방법에 대한 예는 탐색 및 탐색 기록 샘플에서 확인할 수 있습니다.

     

  4. 이제 네임스페이스를 정의하고 처리기를 멤버로 만들어 이 이벤트 처리기를 공개적으로 노출해야 합니다. 네임스페이스 "SearchUtils"를 호출해보겠습니다. 또한 이벤트 처리기를 선언적으로 설정할 수 있도록 WinJS.UI.eventHandler 메서드를 사용해야 합니다(작업 방법에 대한 자세한 내용은 선언적으로 이벤트 처리기를 설정하는 방법 참조).

        WinJS.Namespace.define("SearchUtils",
        {
            querySubmittedHandler: WinJS.UI.eventHandler(querySubmittedHandler)
        }
        );
    
  5. SearchBox가 포함된 HTML 페이지를 엽니다. data-win-options 속성을 사용하여 onquerysubmitted 이벤트를 SampleUtils.querySubmittedHandler로 설정합니다.

                <div class="searchBox"
                     data-win-control="WinJS.UI.SearchBox"
                     data-win-options="{placeholderText: 'Search',
                     focusOnKeyboardInput: true,
                     onquerysubmitted: SearchUtils.querySubmittedHandler}">
                </div>
    

시도해 보세요. 앱을 실행하고 SearchBox에 테스트 쿼리를 입력한 후 Enter 키를 누릅니다. Visual Studio에서 제공된 샘플 데이터를 사용하는 경우 테스트 쿼리로 "1"을 사용해 보세요.

테스트 쿼리

작성한 onquerysubmitted 이벤트 처리기는 검색 결과 페이지를 탐색하고 입력된 쿼리를 전달합니다.

테스트 쿼리의 결과

샘플 데이터를 사용한 경우 테스트 쿼리에 대한 일치하는 항목이 표시되어야 합니다. 사용자 데이터를 사용하는 경우 아직 결과가 제공되지 않았을 것입니다. 먼저 검색 결과 페이지를 업데이트해야 합니다. 이후 단계에서 자세히 살펴보겠습니다.

데이터 검색

이제 검색 결과 페이지로 돌아가겠습니다. 앱이 검색 결과 페이지로 이동할 때 호출하는 첫 번째 메서드 중 하나는 _handleQuery 메서드입니다. _handleQuery는 여기서 수정해야 하는 여러 메서드를 호출합니다.

  1. _generateFilters

    사용자가 클릭하여 결과를 필터링할 수 있는 필터 목록을 생성합니다.

  2. _searchData

    데이터에서 일치하는 항목을 검색하고 originalResults라는 List에 저장합니다.

  3. _populateFilterBar

    필터 목록에 필터를 표시합니다.

이러한 메서드를 업데이트하여 사용자 데이터에 맞게 사용자 지정해보겠습니다.

필터 업데이트

_generateFilters 메서드는 사용자가 클릭하여 결과를 필터링할 수 있는 필터 목록을 생성합니다. 템플릿 생성 메서드는 세 가지 필터인, 모든 결과를 표시하는 "모두" 필터, 그룹 1의 항목을 표시하는 필터, 나머지 모든 항목을 표시하는 필터를 만듭니다. 템플릿 생성 코드를 동적으로 필터 목록을 생성하는 코드로 바꿔보겠습니다. 이런 방법을 통해 샘플 데이터를 변경할 경우 새 필터가 페이지에 표시됩니다. _generateFilters 코드를 업데이트하고 두 개의 도우미 메서드를 만들어보겠습니다. 하지만 그룹 목록에 액세스할 수 있도록 먼저 data.js 파일을 업데이트해야 합니다. 이러한 그룹을 사용하여 필터를 정의합니다.

Hh465238.wedge(ko-kr,WIN.10).gif_generateFilters 메서드 업데이트

  1. searchResults.js에서 _generateFilters 메서드를 찾고 포함된 코드를 삭제합니다.

  2. _filters 배열을 초기화합니다. (_filters 배열은 검색 결과 페이지에서 정의된 멤버 변수입니다.)

    
            _generateFilters: function () {
                this._filters = [];
    
  3. 이제 필터를 만듭니다. 필터는 세 개의 속성이 포함된 개체입니다.

    • results: 표시할 항목의 List입니다. 지금은 이 속성을 null로 설정합니다.
    • text: 필터의 표시 텍스트입니다.
    • predicate: 항목을 사용하는 함수입니다. 항목이 필터 조건을 충족하는 경우(이 필터를 선택하면 표시되어야 하는 경우) 이 함수는 true를 반환하고, 그렇지 않으면 false를 반환합니다.

    먼저 "모두" 필터를 만들어보겠습니다. 모든 필터가 항상 항목을 표시하므로 predicate가 항상 true를 반환합니다.

    
                this._filters.push({ results: null, text: "All", predicate: function (item) { return true; } });
    
  4. 이제 데이터의 각 그룹에 대한 필터를 만들어보겠습니다. 그룹은 Data.groups라는 List로 저장됩니다. forEach 메서드를 사용하여 List에서 각 그룹을 반복합니다. forEach 메서드는 매개 변수로 함수를 사용하며, 이 함수는 목록의 각 항목에 대해 호출됩니다. 메서드에 _createFiltersForGroups라는 멤버 함수를 전달해보겠습니다. 다음 단계에서 함수를 만듭니다.

                if (window.Data) {
                    Data.groups.forEach(this._createFiltersForGroups.bind(this));
                }
            },
    
  5. 이제 _createFiltersForGroups 함수를 만들겠습니다.

    1. 세 개의 매개 변수 element, indexarray를 사용하는 _createFiltersForGroups라는 멤버 함수를 만듭니다.

              _createFiltersForGroups: function (element, index, array){
      
      
    2. element 매개 변수에는 그룹 개체가 포함됩니다. 새 필터 개체를 만들고 push 메서드를 사용하여 _filters 배열에 추가합니다. 필터의 results 속성을 null, text 속성을 element.title, predicate 속성을 _filterPredicate라는 함수로 설정합니다. 다음 단계에서는 _filterPredicate 메서드를 정의하겠습니다.

                  this._filters.push(
                      { results: null, text: element.title, predicate: this._filterPredicate.bind(element)}
                      );
              },
      
    3. 단일 매개 변수 item을 사용하는 _filterPredicate라는 멤버 함수를 만듭니다. item 매개 변수의 group 속성이 현재 그룹 개체와 같은 경우 true를 반환합니다.

              _filterPredicate: function (item) {
      
                  return item.group === this;          
              },
      

방금 만든 메서드 세 개에 대한 전체 코드는 다음과 같습니다.

        _generateFilters: function () {
            this._filters = [];
            this._filters.push({ results: null, text: "All", predicate: function (item) { return true; } });

            if (window.Data) {
                Data.groups.forEach(this._createFiltersForGroups.bind(this));
            }
        },

        _createFiltersForGroups: function (element, index, array){
            
            this._filters.push(
                { results: null, text: element.title, predicate: this._filterPredicate.bind(element)}
                );
        },

        _filterPredicate: function (item) {

            return item.group === this;          
        },

앱을 실행하고 검색을 수행하세요. 필터 막대에 새 필터가 표시되어야 합니다.

업데이트된 필터 목록

템플릿 생성 샘플 데이터를 사용하는 경우 일부 그룹이 잘릴 수 있습니다. 검색 결과 페이지에 대한 CSS 파일을 약간 조정하여 문제를 해결할 수 있습니다.

Hh465238.wedge(ko-kr,WIN.10).gif검색 결과 페이지에 대한 CSS 업데이트

  1. searchResults.css를 엽니다.

  2. .searchResults section[role=main] 스타일을 찾고 -ms-grid-rows 속성 값을 "auto 1fr"로 변경합니다.

    .searchResults section[role=main] {
        /* Define a grid with rows for the filters and results */
        -ms-grid-columns: 1fr;
        -ms-grid-rows: auto 1fr;
        -ms-grid-row: 1;
        -ms-grid-row-span: 2;
        display: -ms-grid;
    }
    
  3. .searchResults section[role=main] .filterbar 스타일을 찾고 word-wrap 속성 값을 "normal"로 변경하고 margin-bottom을 "20px"로 설정합니다.

        .searchResults section[role=main] .filterbar {
            -ms-font-feature-settings: "case" 1;
            -ms-grid-row: 1;
            list-style-type: none;
            margin-left: 60px;
            margin-right: 60px;
            margin-top: 133px;
            max-width: calc(100% - 120px);
            position: relative;
            white-space: normal;
            z-index: 1; 
            margin-bottom: 20px; 
        }
    
  4. .searchResults section[role=main] .filterbar li 스타일을 찾고 display 속성 값을 "inline-block"으로 변경합니다.

            .searchResults section[role=main] .filterbar li {
                display: inline-block; 
                margin-left: 20px;
                margin-right: 20px;
                margin-top: 5px;
                opacity: 0.6;
            }
    
  5. .searchResults section[role=main] .resultslist 스타일을 찾고 -ms-grid-row 속성 값을 "2"로 변경하고 -ms-grid-row-span을 "1"로 설정합니다.

        .searchResults section[role=main] .resultslist {
            -ms-grid-row: 2;
            -ms-grid-row-span: 1;
            height: 100%;
            position: relative;
            width: 100%;
            z-index: 0;
        }
    

앱을 실행하고 다른 검색을 수행하세요. 이제 모든 필터가 표시되어야 합니다.

업데이트된 필터 목록

검색 알고리즘 업데이트

_searchData 메서드는 데이터에서 검색 쿼리와 일치하는 항목을 검색합니다. 템플릿 생성 코드는 각 항목의 제목, 부제목, 설명을 검색합니다. 관련성에 따라 결과의 순위를 지정하는 검색 코드를 작성해보겠습니다.

Hh465238.wedge(ko-kr,WIN.10).gif_searchData 메서드 업데이트

  1. searchResults.js를 열고 _searchData 메서드를 찾은 다음 포함된 코드를 삭제합니다.

  2. 반환 값으로 사용할 originalResults라는 변수를 만듭니다.

            // This function populates a WinJS.Binding.List with search results for the
            // provided query.
            _searchData: function (queryText) {
    
                // Create a variable for the results list.
                var originalResults;
    
  3. 쿼리 텍스트와 찾고 있는 텍스트를 소문자로 변환하여 검색에서 대/소문자를 구분하지 않도록 설정해보겠습니다. 먼저 쿼리를 소문자로 변환하고 lowercaseQueryText라는 변수로 저장해보겠습니다.

                // Convert the query to lowercase. 
                var lowercaseQueryText = queryText.toLocaleLowerCase();
    
  4. 데이터에 액세스하기 전에 데이터가 있는지 확인합니다.

                if (window.Data)
                {
    
  5. data.js에 제공된 샘플 데이터를 사용하는 경우 Data.items에 저장한 항목은 WinJS.Binding.List 개체입니다. createFiltered 메서드를 사용하여 검색 쿼리를 충족하지 않는 항목을 필터링합니다.

    createFiltered 메서드는 매개 변수로 필터링 함수를 사용합니다. 이 필터링 함수는 단일 매개 변수, item을 사용합니다. List는 목록의 각 항목에 대해 이 함수를 호출하여 필터링된 목록에 포함되어야 하는지 결정합니다. 항목이 포함되어야 하는 경우 함수는 true를 반환하고, 생략되어야 하는 경우 false를 반환합니다.

                    originalResults = Data.items.createFiltered(
    
                        function (item) {
    
  6. JavaScript에서 기존 개체에 새 속성을 연결할 수 있습니다. itemranking 속성을 추가하고 값을 "-1"로 설정합니다.

                            // A ranking < 0 means that a match wasn't found. 
                            item.ranking = -1;
    
  7. 먼저 항목의 제목에 쿼리 텍스트가 포함되어 있는지 확인해보겠습니다. 포함된 경우 항목에 10점을 제공합니다.

                            if (item.title.toLocaleLowerCase().indexOf(lowercaseQueryText) >= 0) {
    
                                item.ranking += 10;
                            }
    
  8. 다음으로 부제목 필드에서 적중이 있는지 확인해보겠습니다. 일치 항목을 찾으면 항목에 5점을 제공합니다.

                            if (item.subtitle.toLocaleLowerCase().indexOf(lowercaseQueryText) >= 0) {
                                item.ranking += 5;
                            }
    
  9. 마지막으로 설명 필드를 확인해보겠습니다. 일치 항목을 찾으면 항목에 1점을 제공합니다.

                            if (item.description.toLocaleLowerCase().indexOf(lowercaseQueryText) >= 0) {
                                item.ranking += 1;
                            }
    
  10. 항목의 순위가 -1이면 검색 쿼리와 일치하지 않는 것입니다. 반환 값으로 항목의 순위가 0 이상인 경우 true를 반환합니다.

                            return (item.ranking >= 0);
                        }
                     );
    
  11. 지금까지 목록을 검색 쿼리와 일치하는 항목으로만 필터링하고 순위 정보를 추가했습니다. 이제 createSorted 메서드를 사용하여 점수가 가장 높은 항목이 첫 번째로 표시되도록 결과 목록을 정렬해보겠습니다.

                    // Sort the results by the ranking info we added. 
                    originalResults = originalResults.createSorted(function (firstItem, secondItem){
                            if (firstItem.ranking == secondItem.ranking) {
                                return 0;
                            }
                            else if (firstItem.ranking < secondItem.ranking)
                                return 1;
                            else
                                return -1;
                        });
    
                }
    
  12. 데이터가 누락된 경우 빈 목록을 만드세요.

                else {
    
                    // For some reason, the Data namespace is null, so we 
                    // create an empty list to return. 
                    originalResults = new WinJS.Binding.List();
    
                }
    
  13. 마지막으로 결과를 반환합니다.

                return originalResults;
            }
    

다음은 업데이트된 _searchData 메서드의 전체 코드입니다.

        _searchData: function (queryText) {

            // Create a variable for the results list.
            var originalResults;

            // Convert the query to lowercase. 
            var lowercaseQueryText = queryText.toLocaleLowerCase();

            if (window.Data)
            {
                originalResults = Data.items.createFiltered(

                    function (item) {

                        // A ranking < 0 means that a match wasn't found. 
                        item.ranking = -1;

                        if (item.title.toLocaleLowerCase().indexOf(lowercaseQueryText) >= 0) {

                            item.ranking += 10;
                        }
                        if (item.subtitle.toLocaleLowerCase().indexOf(lowercaseQueryText) >= 0) {
                            item.ranking += 5;
                        }
                        if (item.description.toLocaleLowerCase().indexOf(lowercaseQueryText) >= 0) {
                            item.ranking += 1;
                        }

                        return (item.ranking >= 0);
                    }
                 );

                // Sort the results by the ranking info we added. 
                originalResults = originalResults.createSorted(function (firstItem, secondItem){
                        if (firstItem.ranking == secondItem.ranking) {
                            return 0;
                        }
                        else if (firstItem.ranking < secondItem.ranking)
                            return 1;
                        else
                            return -1;
                    });

            }
            else {

                // For some reason, the Data namespace is null, so we 
                // create an empty list to return. 
                originalResults = new WinJS.Binding.List();

            }

            return originalResults;
        }

검색에서 반환된 항목에 대한 탐색 제공

앱을 실행하고 검색을 수행하면 검색 결과 페이지의 ListView 컨트롤에 결과가 표시됩니다. 검색 결과 항목 중 하나를 클릭해도 아무것도 수행되지 않습니다. 사용자가 클릭할 때 항목을 표시하도록 몇 가지 코드를 추가해보겠습니다.

사용자가 ListView에서 항목을 클릭하면 ListViewoniteminvoked 이벤트를 발생시킵니다. 검색 결과 페이지에 대한 템플릿 생성 코드는 _itemInvoked라는 oniteminvoked 이벤트 처리기를 정의합니다. 코드를 업데이트하여 호출된 항목으로 이동해보겠습니다.

Hh465238.wedge(ko-kr,WIN.10).gif항목에 탐색을 추가하려면

  • searchResults.js를 열고 _itemInvoked 함수에 코드를 추가하여 올바른 페이지로 이동합니다. 주의  여기에 표시된 URI는 허브 템플릿에 대한 것입니다. 그리드 템플릿의 경우 URI는 다음과 같아야 합니다. /pages/itemDetail/itemDetail.html. 분할 템플릿의 경우 URL은 다음과 같아야 합니다. /pages/items/items.html.

     

            _itemInvoked: function (args) {
                args.detail.itemPromise.done(function itemInvoked(item) {
                    // TODO: Navigate to the item that was invoked.
                    var itemData = [item.groupKey, item.data.title];
                    WinJS.Navigation.navigate("/pages/item/item.html", { item: itemData });
                });
            },
    

(옵션) ListView 컨트롤의 항목 템플릿 업데이트

템플릿 생성 검색 결과 페이지는 Visual Studio에서 생성되는 샘플 데이터 원본을 사용하게 설계된 itemTemplate을 정의합니다. 이 페이지의 각 데이터 항목에는 "이미지", "제목", "부제목", "설명" 필드가 필요합니다.

데이터 항목에 다른 필드가 있는 경우 itemTemplate을 수정해야 합니다. 자세한 내용은 빠른 시작: ListView 추가를 참조하세요.

(옵션) 검색 제안 추가

검색 제안은 검색 창의 검색 상자 아래에 표시됩니다. 제안은 사용자의 시간을 절약하고 앱에서 사용자가 검색할 수 있는 항목의 종류에 대한 유용한 힌트를 제공하므로 중요합니다.

여러 소스에서 제안을 가져올 수 있습니다.

  • 직접 제안을 정의할 수 있습니다. 예를 들어 자동차 제조업체 목록을 만들 수 있습니다.
  • 앱이 로컬 파일을 검색하는 경우 Windows에서 제안을 가져올 수 있습니다.
  • 웹 서비스나 서버에서 제안을 가져올 수 있습니다.

제안을 표시하는 방법에 대한 사용자 환경 지침은 검색에 대한 지침 및 검사 목록을 참조하세요.

LocalContentSuggestionSettings를 사용하면 Windows의 로컬 파일을 기반으로 단 몇 줄의 코드만 사용하여 제안을 추가할 수 있습니다. 또는 검색 상자 컨트롤의 onsuggestionsrequested 이벤트를 등록하고 다른 소스(예: 로컬에 정의된 목록 또는 웹 서비스)에서 검색한 제안으로 구성되는 제안 목록을 직접 작성할 수 있습니다. 이 빠른 시작에서는 onsuggestionsrequested 이벤트를 처리하는 방법을 보여 줍니다.

검색 제안을 추가하는 방법을 보여 주는 추가 코드 예제는 SearchBox 컨트롤 샘플(영문)을 다운로드하세요. 샘플에서는 세 가지 가능한 소스를 모두 사용하여 검색 제안을 추가하는 방법 및 IME(입력기)에서 생성된 쿼리 텍스트의 대체 형식을 사용하여 동아시아 언어에 대한 제안을 추가하는 방법을 보여 줍니다. 일본어 또는 중국어 사용자가 앱을 사용하는 경우 쿼리 텍스트 대안을 사용하는 것이 좋습니다.

Hh465238.wedge(ko-kr,WIN.10).gifSuggestionsRequested 이벤트 처리

  1. 앱에 SearchBox 컨트롤이 여러 개 있을 수도 있습니다. default.js 파일에서 모든 컨트롤이 사용할 수 있는 단일 이벤트 처리기를 정의하겠습니다. 이전 단계에서 만든 querySubmittedHandler 메서드 뒤에 이 코드를 추가합니다.

        function suggestionsRequestedHandler(args) {
    
  2. SearchBox 쿼리 텍스트를 소문자로 변환합니다.

            var query = args.detail.queryText.toLocaleLowerCase();
    
  3. 사용자가 수행한 이전 검색 등의 일부 검색 제안이 자동으로 제공됩니다. 시스템에서 제공하는 항목에 검색 제안을 추가하겠습니다.

            // Retrieve the system-supplied suggestions.
            var suggestionCollection = args.detail.searchSuggestionCollection;
    
  4. 쿼리에 문자가 하나 이상 포함되어 있고 데이터에 액세스할 수 있는지 확인합니다.

            if (query.length > 0 && window.Data) {
    
  5. 데이터의 각 항목을 반복하고 일치 항목을 확인합니다. 일치 항목을 찾으면 검색 제안 컬렉션에 일치 항목의 제목을 추가합니다.

                Data.items.forEach(
                    function (element, index, array) {
                        if (element.title.substr(0, query.length).toLocaleLowerCase() === query) {
                            suggestionCollection.appendQuerySuggestion(element.title);
                        }
    
                    });
    
  6. args.detail.linguisticDetails.queryTextAlternatives 속성은 IME에서 사용자가 입력한 텍스트에 대한 추가 제안을 제공합니다. 이러한 제안을 사용하면 동아시아 언어 사용자의 검색 환경이 향상됩니다. 원래 쿼리가 포함된 문자열에 대한 쿼리 텍스트 대안을 확인하고 검색 제안 목록에 추가하겠습니다.

                args.detail.linguisticDetails.queryTextAlternatives.forEach(
                    function (element, index, array) {
                        if (element.substr(0, query.length).toLocaleLowerCase() === query) {
                            suggestionCollection.appendQuerySuggestion(element);
                        }
    
                    });
            }
        }
    

    이것이 검색 제안 이벤트 처리기에 필요한 모든 코드입니다. 다음은 전체 suggestionsRequestedHandler 메서드입니다.

        function suggestionsRequestedHandler(args) {
    
            var query = args.detail.queryText.toLocaleLowerCase();
    
            // Retrieve the system-supplied suggestions.
            var suggestionCollection = args.detail.searchSuggestionCollection;
    
            if (query.length > 0 && window.Data) {
    
                Data.items.forEach(
                    function (element, index, array) {
                        if (element.title.substr(0, query.length).toLocaleLowerCase() === query) {
                            suggestionCollection.appendQuerySuggestion(element.title);
                        }
    
                    });
    
                args.detail.linguisticDetails.queryTextAlternatives.forEach(
                    function (element, index, array) {
                        if (element.substr(0, query.length).toLocaleLowerCase() === query) {
                            suggestionCollection.appendQuerySuggestion(element);
                        }
    
                    });
    
            }
        }
    

    참고  데이터 원본이 비동기적이면 검색 제안 컬렉션에 대한 업데이트를 Promise로 래핑해야 합니다. 샘플 코드는 동기 데이터 원본인 List를 사용하지만, List가 비동기 데이터 원본인 경우에는 메서드가 다음과 같이 표시됩니다.

     

        function suggestionsRequestedHandler(args) {
    
            var query = args.detail.queryText.toLocaleLowerCase();
    
            // Retrieve the system-supplied suggestions.
            var suggestionCollection = args.detail.searchSuggestionCollection;
    
            if (query.length > 0 && window.Data) {
    
                args.detail.setPromise(WinJS.Promise.then(null, 
                    function () {
                        Data.items.forEach(
                            function (element, index, array) {
                                if (element.title.substr(0, query.length).toLocaleLowerCase() === query) {
                                    suggestionCollection.appendQuerySuggestion(element.title);
                                }
    
                            });
    
                        args.detail.linguisticDetails.queryTextAlternatives.forEach(
                            function (element, index, array) {
                                if (element.substr(0, query.length).toLocaleLowerCase() === query) {
                                    suggestionCollection.appendQuerySuggestion(element);
                                }
    
                            });
    
                    })
                 );
            }
        }
    
  7. 이것이 검색 제안 이벤트 처리기에 필요한 모든 코드입니다. 이전 단계에서 정의한 SearchUtils 네임스페이스를 통해 노출하여 공개적으로 액세스할 수 있게 만들어보겠습니다.

        WinJS.Namespace.define("SearchUtils",
        {
            querySubmittedHandler: WinJS.UI.eventHandler(querySubmittedHandler),
            suggestionsRequestedHandler: WinJS.UI.eventHandler(suggestionsRequestedHandler)
        }
        );
    
  8. 이제 SearchBox에 이벤트를 등록하겠습니다. SearchBox가 포함된 HTML 페이지를 열고 onsuggestionsrequested 이벤트를 SearchUtils.suggestionsRequestedHandler로 설정합니다.

                <div class="searchBox"
                     data-win-control="WinJS.UI.SearchBox"
                     data-win-options="{placeholderText: 'Search',
                     focusOnKeyboardInput: true,
                     onquerysubmitted: SearchUtils.querySubmittedHandler,
                     onsuggestionsrequested: SearchUtils.suggestionsRequestedHandler}">
                </div>
    

검색 계약 구현(이전 Windows 버전)

Windows 8.1 이전에는 앱이 검색 참 메뉴를 사용하여 앱에서 바로 검색을 제공했습니다. 개발자는 검색 계약을 구현하고 SearchPane API를 사용하여 쿼리를 처리하고 제안 및 결과를 가져왔습니다.

Windows 8 검색 계약 및 SearchPane API도 계속 지원되지만 Windows 8.1부터는 SearchPane 대신 SearchBox 컨트롤을 사용하는 것이 좋습니다. SearchBox를 사용하는 앱은 검색 계약을 구현할 필요가 없습니다.

앱이 SearchPane 및 검색 계약을 사용해야 하나요? 사용자가 앱을 자주 검색할 것 같지 않은 경우 SearchPane 및 검색 계약을 사용할 수 있습니다. 사용자가 검색 창을 활성화하기 위해 클릭할 수 있는 검색 문자 모양(Segoe UI Symbol 0xE0094, 15pt)이 포함된 단추를 앱에 사용하는 것이 좋습니다. SearchPane 및 검색 계약을 구현하는 코드를 보려면 검색 계약 샘플을 참조하세요.

요약 및 다음 단계

SearchBox 컨트롤과 검색 결과 페이지를 사용하여 앱에 검색 기능을 추가했습니다.

사용자에게 적합한 검색 환경 설계 및 만들기에 대한 지침은 검색에 대한 지침 및 검사 목록을 참조하세요.

관련 항목

SearchBox 컨트롤 샘플

검색에 대한 지침 및 검사 목록