스트리밍은 대부분의 브라우저와
Developer 앱에서 사용할 수 있습니다.
-
Core Spotlight로 시맨틱 검색 지원하기
Core Spotlight를 사용하여 앱에서 시맨틱 검색 결과를 제공하는 방법을 알아보세요. 사용자가 자연어를 사용하여 항목을 검색할 수 있도록 앱의 콘텐츠를 비공개 온디바이스 인덱스에서 사용하는 방법을 확인해 보세요. 인덱싱 활동의 스케줄을 지정하여 앱의 성능을 최적화하는 방법도 공유합니다. 이 세션을 최대한 활용하려면 먼저 Apple Developer 웹사이트의 Core Spotlight 문서를 살펴보는 것이 좋습니다.
챕터
- 0:00 - Introduction
- 1:37 - Searchable content
- 5:05 - Demo: Creating an index delegate extension
- 6:56 - Results and suggestions
- 9:18 - Ranking
- 10:17 - Wrap-up
리소스
관련 비디오
WWDC24
WWDC21
-
다운로드
안녕하세요, Spotlight 팀의 엔지니어인 Jennifer입니다 저희는 검색에 큰 관심을 두고 있습니다 이번 세션에서는 앱에서 강력한 검색 경험을 빌드하는 데 도움이 되는 Spotlight의 새 API를 모두 소개해 드리겠습니다 CoreSpotlight는 검색 가능한 콘텐츠를 앱에서 Spotlight에 제공하고 해당 콘텐츠를 쿼리를 통해 검색할 수 있도록 하는 프레임워크입니다 검색을 위해 앱의 영구 저장 솔루션을 개선할 때 효과적입니다 앱에서 제공한 검색 가능한 콘텐츠는 기기를 벗어나지 않는 비공개 로컬 인덱스에 저장됩니다 Spotlight에서는 해당 콘텐츠를 검색할 수 있지만 다른 앱은 이 데이터를 조회할 수 없습니다 CoreSpotlight를 사용하면 검색 막대에서 직접 가져오는 자연어 검색어를 통해 앱에서 검색 결과와 추천을 쉽게 제공할 수 있죠 올해부터 CoreSpotlight는 시맨틱 검색을 통한 쿼리 이해를 지원하죠 지금까지 Spotlight는 앱의 콘텐츠를 검색했지만 검색어가 정확히 일치해야 했습니다 시맨틱 검색을 통해 사용자는 유사한 뜻을 갖는 검색어를 통해 자신만의 방식으로 앱 콘텐츠를 검색할 수 있습니다 Spotlight의 쿼리 이해 모델 덕분에 어떻게 검색하든 사용자는 원하는 결과를 얻을 수 있습니다 오늘은 앱에서 완전한 검색 경험을 빌드하는 방법을 다룰게요 검색 인덱스에 콘텐츠를 제공하는 것부터 시작하여 데이터 마이그레이션 및 복구에 대한 모범 사례에 따라 빌드하죠 결과와 추천을 검색하는 방법을 살펴보고 사용자와 가장 관련성이 높은 검색 결과의 순위를 높이는 방법을 알아보겠습니다 사용자가 자신이 작성한 일기 내용을 검색할 수 있는 앱을 빌드해 볼게요 아래의 링크에서 이 샘플 앱의 전체 코드를 확인할 수 있습니다
멋진 검색 경험을 빌드하기 위한 첫 단계는 사용자가 앱에서 검색할 가능성이 있는 검색 가능한 콘텐츠를 Spotlight에 제공하는 것입니다 일기 앱에서 사용자는 모든 일기 내용을 검색할 수 있으므로 각 일기 내용이 검색 가능한 항목이 됩니다 이를 고려했을 때, 쿼리로 검색할 수 있는 방식으로 항목을 인덱싱하여 사용자 인터페이스의 보기를 바로 채울 수 있도록 해야 합니다 현재 시맨틱 검색은 이미지, 동영상 등의 텍스트나 미디어 애셋에 대해 가장 효과적으로 작동합니다 따라서 최상의 결과를 얻으려면 검색 가능한 항목이 적절한 콘텐츠 유형을 갖추어야 합니다 또한 가능하면 시스템 정의 속성을 사용하세요 uniqueIdentifier, 선택 사항인 domainIdentifier와 attributeSet를 제공하여 CSSearchableItem을 만드세요 모든 항목 데이터가 복구될 수 있도록 uniqueIdentifier는 앱의 영구 저장 솔루션에 저장해야 합니다 그다음 CSSearchableAttributeSet를 만드세요 반드시 유효한 contentType를 설정해야 합니다 문서를 통해 지원되는 UT 유형의 전체 목록을 확인하거나 맞춤형 콘텐츠 유형을 만드는 방법을 알아보세요 텍스트가 포함된 검색 가능한 항목을 제공하는 경우 title과 textContent를 설정해야 합니다 이러한 속성은 시맨틱 인덱스에서 처리됩니다 항목이 이미지나 동영상 애셋을 나타내는 경우 애셋으로 연결되는 경로를 contentURL에 설정해야 합니다 이렇게 하면 앱의 샌드박싱된 컨테이너 내 시맨틱 인덱스에서 애셋을 처리할 수 있습니다
항목이 첨부 파일이나 웹 콘텐츠를 참조하는 경우 이러한 내용은 자체 콘텐츠 유형과 속성을 통해 별도의 항목으로 인덱스에 제공하는 것이 좋습니다 relatedUniqueIdentifier를 사용하여 소스 항목과의 관계를 유지할 수 있습니다
검색 가능한 항목을 디자인했다면 Spotlight에서 검색 가능한 인덱스를 만든 후 항목을 제공하세요 우수한 새 API를 사용하면 앱 디자인에서 더 효율적으로 항목 제공을 처리할 수 있습니다 클라이언트 상태를 통한 배치 인덱싱과 항목 업데이트를 포함하죠 시작하려면 이름이 지정된 CSSearchableIndex를 만들고 Spotlight에 전송한 마지막 클라이언트 상태를 가져오고 검증한 후 검색 가능한 항목을 인덱싱합니다 여기서 인덱싱 호출은 제공된 항목의 배치 처리를 시작 및 종료하는 호출로 래핑되어 있으며 여기에 다음 클라이언트 상태를 보낼 수 있죠
클라이언트 상태는 대량의 항목 카탈로그를 관리하고 앱과 Spotlight에서 데이터 무결성을 유지하는 데 유용합니다 클라이언트 상태는 앱의 성능에 영향을 줄 수 있는 지나친 항목 제공을 방지하는 데도 효과적이죠 새 isUpdate 플래그를 함께 사용하면 필요한 항목만 앱에서 제공하게 됩니다 마이그레이션 및 복구는 일관된 검색 경험을 빌드할 때 매우 중요합니다 Spotlight 인덱스는 로컬에서 처리되며 공개되지 않습니다 그러므로 검색 가능한 콘텐츠가 Spotlight에서 최신 상태로 유지되도록 조치를 취해야 합니다 Spotlight에서 인덱스를 마이그레이션하거나 데이터 손상이나 중단에 의해 복구해야 하거나 다른 문제가 있는 경우 모든 항목이나 특정 항목을 다시 인덱싱하라는 요청을 앱에 전송합니다 이러한 요청에 응답하려면 앱이 실행 중인 경우를 위해 delegate 프로토콜을 채택하고 Spotlight에서 앱에 대해 별도로 호출할 수 있는 위임 확장 프로그램을 구현하면 됩니다 인덱스 위임 확장 프로그램을 사용하면 기기가 절전 모드에 있거나 유휴 상태일 때와 같이 적절한 시점에 Spotlight가 요청을 예약할 수 있으므로 앱의 기존 검색 기능을 거의 변경하지 않아도 시간을 두고 항목을 마이그레이션할 수 있습니다 새 Xcode 파일 타깃을 사용하면 인덱싱 위임 확장 프로그램을 정말 쉽게 설정할 수 있습니다 방법을 알아보겠습니다 일기 앱에서 시작할게요 이미 이 앱은 번들 ID를 통해 검색 가능한 콘텐츠를 제공하도록 구성되어 있습니다 가장 먼저 할 일은 새 타깃을 만드는 것입니다 File 메뉴로 이동한 다음 New, Target을 차례로 선택합니다
확장 프로그램을 위한 플랫폼을 선택합니다, 저는 macOS로 시작하고 새 CoreSpotlight Delegate 확장 프로그램 템플릿을 찾을게요
Next를 클릭하여 새 타깃을 구성하고 Finish를 클릭하여 새 타깃을 프로젝트에 추가합니다
타깃이 추가되면 새 확장 프로그램을 활성화합니다
이제 스텁 구현을 확인해 볼게요
이제 Spotlight에서 이 프로세스를 대신 실행하므로 명령어 라인 유틸리티를 사용하면 편리하게 디버깅할 수 있죠 지금 바로 시작할게요
먼저 reindexAll 메소드에 중단점을 설정하여 코드에서 이 지점을 포착할게요 acknowledgmentHandler를 호출하여 호출자가 차단되지 않도록 합니다
새 앱 확장 프로그램을 포함하도록 앱을 다시 빌드해야 합니다 그러므로 앱의 타깃을 선택한 다음 Product 메뉴에서 Build를 선택합니다
그다음 확장 프로그램 타깃으로 돌아간 후 Debug 메뉴에서 Attach to Process를 선택합니다
이제 디버깅할 준비가 되었습니다 이 시점에서 터미널 앱을 열어 유틸리티 명령을 실행해야 합니다
mdutil 도구를 사용하면 번들 ID에 대해 요청을 시뮬레이션하여 디버깅할 수 있습니다 이제 실행한 후
Xcode의 프로젝트로 돌아가면 중단점에 도달한 것을 볼 수 있습니다 이제 모든 항목의 인덱싱과 일부 항목의 인덱싱 드래그 앤 드롭에 대한 지원 추가 및 중요한 제공 경로와 관련된 스로틀 시나리오에 대한 응답의 구현을 완료할 수 있습니다 검색 가능한 항목을 인덱싱했으니 이제 사용자 인터페이스에서 검색 경험을 지원해야 합니다 사용자 인터페이스 요구 사항을 잘 지원하도록 쿼리를 구성할 수 있습니다 시맨틱 검색은 기본적으로 활성화되어 있으나 Spotlight가 사용하는 것과 같은 최신 머신 러닝 모델로 순위가 매겨진 결과를 반환하도록 쿼리를 구성할 수도 있으며 앱에서 추천 메뉴를 지원하도록 쿼리를 구성할 수도 있습니다 CSUserQueryContext를 사용하여 사용자 인터페이스에 적합한 쿼리를 구성하세요 쿼리에서 반환된 각 항목에 대해 가져올 속성의 목록을 설정해야 합니다 가져온 속성에는 일반적으로 사용자 인터페이스에 콘텐츠를 표시하는 데 필요한 것만 포함됩니다 순위가 매겨진 결과는 플래그로 활성화되며 그 수를 제한할 수 있습니다 이러한 결과는 종종 별도의 가장 연관성 높은 항목 섹션에 표시됩니다 모든 결과가 반환되면 앱에서 결과를 정렬해야 합니다 이는 새로운 compareByRank comparator로 처리할 수 있죠
추천은 숫자를 기준으로 구성하거나 사용 중지할 수 있습니다 제안 사항은 순위가 지정된 순서대로 반환되지만 순서는 정렬 시 복구될 수도 있습니다 메타데이터 구문을 통해 구조화된 쿼리를 사용하면 효과적으로 앱의 사용자 인터페이스에 맞게 검색 결과를 맞춤화할 수 있습니다 예를 들어, 사용자가 이미지만 표시하는 탭을 선택한 경우 이와 같은 필터 쿼리를 사용하면 결과 세트에 이미지만 포함되도록 지정할 수 있습니다 필터 쿼리는 앱의 특정 부분으로 연결되는 브레드크럼과 같이 Spotlight에만 표시하려는 콘텐츠에도 유용합니다 아래 문서에서 메타데이터 구문을 통해 맞춤형 필터 쿼리를 빌드하는 방법을 확인해 보세요
마지막 단계는 검색 막대에서 가져온 사용자의 쿼리 문자열과 쿼리 컨텍스트로 CSUserQuery를 만드는 것입니다 결과 및 추천은 비동기 응답으로 반환됩니다 항목 결과는 비동기 배치로 반환되므로 순위 지정이 활성화되면 항목을 표시하기 전에 정렬해야 합니다 일반적으로 추천은 사용자가 입력한 문자열의 완성으로 반환됩니다 CSSuggestion은 추천 메뉴에서 표시될 수 있는 속성이 지정된 문자열을 제공합니다 사용자가 추천과 상호작용하면 검색 막대의 텍스트를 이 문자열로 대체하여 새 검색을 트리거하세요
시맨틱 검색을 사용하려면 기기에 머신 러닝 모델을 다운로드해야 하며 앱의 프로세스를 통해 실행됩니다 실행 중 앱의 메모리 공간을 유지하기 위해 이러한 모델은 언제든지 로드되거나 언로드될 수 있습니다 그러므로 검색 인터페이스가 표시되기 직전에 이 Class 메소드를 매번 호출하는 것이 좋습니다 사용자가 검색을 시작하자마자 모든 리소스를 사용할 수 있게 되죠 이제 검색이 작동하므로 시간이 지나면서 검색 경험을 더 개선하는 방법을 고려해야 합니다 사용자가 가장 관심을 갖는 콘텐츠의 순위를 높일 수 있는 신호를 제공하면 되죠 참여도와 최신성은 적응형 순위 지정 경험을 제공하는 데 중요한 신호입니다 사용자가 검색 가능한 항목과 관련된 콘텐츠를 살펴볼 수 있죠 또는 검색을 시작하고 결과 세트에 있는 항목을 스크롤하면서 보다가 최종적으로 특정 결과와 상호작용하여 세부 정보 보기를 확인할 수 있죠 각 경우에서 앱은 향후 검색에서 순위를 높이기 위해 참여도 신호를 Spotlight에 전송할 수 있습니다
사용자가 CSSearchableItem과 관련된 콘텐츠를 탐색하는 경우 항목 속성 세트에서 lastUsedDate 속성을 설정한 다음 인덱스에 업데이트로 다시 제공하세요 그리고 사용자가 결과나 쿼리 추천과 상호작용하면 해당 쿼리와 관련된 상호작용을 표시할 수 있습니다 끝났습니다! 이제 사용자는 여러 플랫폼과 로케일에서 다양한 검색 콘텐츠를 원활하게 처리하는 완전한 검색 경험을 사용할 수 있습니다 이번 세션에서는 비공개 온디바이스 검색 인덱스를 통해 콘텐츠를 제공하는 방법과 결과와 추천을 검색하는 방법을 살펴보고 시간 경과에 따라 관련성이 가장 큰 검색 결과의 순위를 높이는 방법을 다루었죠 Core Spotlight를 도입하면 사용자가 앱에서 콘텐츠를 찾는 데 도움이 됩니다 그리고 시맨틱 검색 덕분에 검색이 더욱더 강력해졌습니다
Spotlight를 통한 다른 구현에 대해 자세히 알아보려면 App Intent의 활용과 Core Data를 통해 무료로 CoreSpotlight 제공을 구현하는 방법을 다루는 세션을 확인하세요 시청해 주셔서 감사합니다 Spotlight를 많이 사용해 주세요
-
-
2:14 - Creating CSSearchableItem
// Creating searchable items for donation let item = CSSearchableItem(uniqueIdentifier: uniqueIdentifier, domainIdentifier: domainIdentifier, attributeSet: attributeSet)
-
2:28 - Creating CSSearchableAttributeSet
// Creating searchable content for donation let attributeSet = CSSearchableItemAttributeSet(contentType: UTType.text) attributeSet.contentType = UTType.text.identifier
-
2:40 - Searchable items with type
// Searchable items with text attributeSet.title attributeSet.textContent // Searchable items with media attributeSet.contentType attributeSet.contentURL // Searchable items with links attributeSet.contentURL attributeSet.relatedUniqueIdentifier
-
3:31 - Batch indexing with client state
// Batch indexing with client state let index = CSSearchableIndex(name: "SpotlightSearchSample") index.fetchLastClientState { state, error in if state == nil { index.beginBatch() index.indexSearchableItems(items) index.endIndexBatch(expectedClientState: state, newClientState: newState) { error in } } }
-
3:56 - Avoid overwriting existing attributes
// Make it an update to avoid overwriting existing attributes item.isUpdate = true
-
7:19 - Configure a query
// Configure a query let queryContext = CSUserQueryContext() queryContext.fetchAttributes = ["title", "contentDescription"]
-
7:33 - Ranked results
// Ranked results queryContext.enableRankedResults = true queryContext.maxRankedResultCount = 2
-
7:47 - Suggestions
// Suggestions queryContext.maxSuggestionCount = 4
-
7:55 - Filter queries
// Filter queries queryContext.filterQueries = ["contentTypeTree=\"public.image\""]
-
8:23 - Query for searchable items and suggestions
// Query for searchable items and suggestions let query = CSUserQuery(userQueryString: "windsurfing carmel", userQueryContext: queryContext) for try await element in query.responses { switch(element) { case .item(let item): self.items.append(item) break case .suggestion(let suggestion): self.suggestions.append(suggestion) break } }
-
8:40 - Suggestions
// Suggestions suggestion.localizedAttributedSuggestion
-
8:56 - Preparing for queries
// Preparing for queries CSUserQuery.prepare CSUserQuery.prepareWithProtectionClasses
-
9:50 - Set the lastUsedDate
// Set the lastUsedDate when the user interacts with the item item.attributeSet.lastUsedDate = Date.now item.isUpdate = true
-
10:00 - Interactions with items and suggestions from a query
// Interactions with items and suggestions from a query query.userEngaged(item, visibleItems: visibleItems, interaction: CSUserQuery.UserInteractionKind.select) query.userEngaged(suggestion, visibleSuggestions: visibleSuggestions, interaction: CSUserQuery.UserInteractionKind.select)
-
-
찾고 계신 콘텐츠가 있나요? 위에 주제를 입력하고 원하는 내용을 바로 검색해 보세요.
쿼리를 제출하는 중에 오류가 발생했습니다. 인터넷 연결을 확인하고 다시 시도해 주세요.