본문 바로가기

Scout

업데이트됨

번역일: 2026년 9월 23일

이 페이지는 원문이 업데이트되어 번역이 갱신되었습니다.

원문 수정
2026년 9월 23일
번역 갱신
2026년 9월 23일

Scout

소개

Laravel Scout는 Eloquent 모델에 전문(full-text) 검색 기능을 손쉽게 추가할 수 있도록 해주는 패키지입니다. 모델 옵저버를 사용해 Scout는 Eloquent 레코드의 변경 사항을 검색 인덱스와 자동으로 동기화합니다.

Scout는 현재 Algolia, Meilisearch, Typesense, Turbopuffer 드라이버를 기본 제공합니다. 또한 외부 의존성이나 서드파티 서비스 없이 로컬 개발 환경에서 사용하기 적합한 "database"와 "collection" 드라이버도 함께 제공되어, 별도의 설정 없이 바로 검색 기능을 테스트해볼 수 있습니다.

더 나아가 Scout는 직접 커스텀 드라이버를 작성하기 쉽도록 설계되어 있어서, 자체적인 검색 구현체로 Scout를 자유롭게 확장할 수 있습니다.

NOTE

Scout를 사용하려면 Algolia나 Meilisearch처럼 실제 텍스트 검색 알고리즘을 갖춘 엔진이 필요합니다. 반면 "database"와 "collection" 드라이버는 소규모 데이터베이스나 부하가 적은 애플리케이션에는 적합하지만, 실제 프로덕션 환경의 검색 인덱스가 갖는 이점(예: 오타 허용, 관련도 순위 등)을 제공하지는 않습니다.

설치

먼저, Composer 패키지 매니저를 통해 Scout를 설치합니다.

composer require laravel/scout

Scout 설치가 끝나면 vendor:publish Artisan 명령어를 사용해 Scout 설정 파일을 게시해야 합니다. 이 명령어를 실행하면 애플리케이션의 config 디렉터리에 scout.php 설정 파일이 생성됩니다.

php artisan vendor:publish --provider="Laravel\Scout\ScoutServiceProvider"

마지막으로, 검색 기능을 추가하고 싶은 모델에 Laravel\Scout\Searchable 트레이트를 추가합니다. 이 트레이트는 모델 옵저버를 등록해서 모델이 검색 엔진과 자동으로 동기화되도록 해줍니다.

<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; use Laravel\Scout\Searchable; class Post extends Model { use Searchable; }

큐 사용하기

Scout를 사용할 때 필수는 아니지만, 큐 드라이버를 설정해서 사용하는 것을 강력히 권장합니다. 큐 워커를 실행해두면 모델 정보를 검색 인덱스와 동기화하는 모든 작업이 큐에 올라가서, 애플리케이션의 웹 인터페이스 응답 속도에 훨씬 더 나은 반응성을 제공할 수 있습니다.

큐 드라이버를 설정한 후에는 config/scout.php 설정 파일에서 queue 옵션 값을 true로 지정하세요.

'queue' => true,

queue 옵션을 false로 설정하더라도, Algolia나 Meilisearch 같은 일부 Scout 드라이버는 항상 비동기적으로 레코드를 인덱싱한다는 점을 기억해두세요. 즉, Laravel 애플리케이션 내에서 인덱싱 작업이 완료되더라도, 검색 엔진 자체에서는 새로 추가되거나 갱신된 레코드가 즉시 검색 결과에 반영되지 않을 수 있습니다.

Scout 작업이 사용할 커넥션과 큐를 지정하려면, queue 설정 옵션을 배열 형태로 정의하면 됩니다.

'queue' => [ 'connection' => 'redis', 'queue' => 'scout' ],

물론 Scout 작업에 사용할 커넥션과 큐를 커스텀했다면, 해당 커넥션과 큐를 처리할 큐 워커를 실행해야 합니다.

php artisan queue:work redis --queue=scout

드라이버 사전 준비사항

Algolia

Algolia 드라이버를 사용하려면 config/scout.php 설정 파일에서 Algolia의 id와 secret 자격 증명을 설정해야 합니다. 자격 증명을 설정한 후에는 Composer 패키지 매니저를 통해 Algolia PHP SDK도 설치해야 합니다.

composer require algolia/algoliasearch-client-php

Meilisearch

Meilisearch는 매우 빠르고 오픈소스인 검색 엔진입니다. 로컬 컴퓨터에서 Meilisearch를 어떻게 설치해야 할지 잘 모르겠다면, Laravel의 공식 Docker 개발 환경인 Laravel Sail을 사용할 수 있습니다.

Meilisearch 드라이버를 사용하려면 Composer 패키지 매니저를 통해 Meilisearch PHP SDK를 설치해야 합니다.

composer require meilisearch/meilisearch-php http-interop/http-factory-guzzle

그런 다음 애플리케이션의 .env 파일에서 SCOUT_DRIVER 환경 변수와 Meilisearch의 host, key 자격 증명을 설정합니다.

SCOUT_DRIVER=meilisearch MEILISEARCH_HOST=http://localhost:7700 MEILISEARCH_KEY=masterKey

Meilisearch에 대한 더 자세한 내용은 Meilisearch 공식 문서를 참고하시기 바랍니다.

또한 meilisearch/meilisearch-php를 사용하는 만큼, 해당 Meilisearch 바이너리 버전과 호환되는 Meilisearch 서비스를 사용하고 있는지 반드시 Meilisearch 문서의 호환성 안내를 통해 확인해야 합니다.

NOTE

Meilisearch를 사용하는 애플리케이션에서 Scout를 업데이트할 때는, 항상 Meilisearch 서비스 자체의 추가적인 변경사항(breaking changes)이 있는지 확인해야 합니다.

Typesense

Typesense는 오타 허용, 벡터 검색, 지리적 위치 검색(geo-search)을 지원하는 매우 빠른 오픈소스 검색 엔진입니다. Typesense를 셀프 호스팅하거나 Typesense Cloud를 이용할 수 있습니다.

Scout와 함께 Typesense를 사용하려면 Composer 패키지 매니저를 통해 Typesense PHP SDK를 설치합니다.

composer require typesense/typesense-php

그런 다음 애플리케이션의 .env 파일에서 SCOUT_DRIVER 환경 변수와 Typesense host, API 키를 설정합니다.

SCOUT_DRIVER=typesense TYPESENSE_API_KEY=masterKey TYPESENSE_HOST=localhost

Laravel Sail을 사용 중이라면, 애플리케이션의 Typesense 서비스 버전에 맞게 이 값들을 조정해야 할 수도 있습니다.

Typesense Cloud를 이용하는 경우, Typesense Cloud에서 제공하는 설정 값에 맞게 다음 항목들을 조정하면 됩니다.

TYPESENSE_HOST=xxx.a1.typesense.net TYPESENSE_PORT=443 TYPESENSE_PROTOCOL=https

Typesense에 대한 더 자세한 내용은 Typesense 공식 문서를 참고하시기 바랍니다.

Turbopuffer

Turbopuffer는 시맨틱 검색을 위해 설계된 서버리스 벡터 검색 엔진입니다. Turbopuffer 드라이버를 사용하려면 config/scout.php 설정 파일에서 Turbopuffer의 api_key를 설정해야 합니다.

Turbopuffer는 텍스트 자체가 아닌 벡터 임베딩(embedding)을 검색하기 때문에, 임베딩을 생성해줄 프로바이더도 설정해야 합니다. Scout는 기본적으로 OpenAI를 지원합니다. OpenAI를 사용하려면 애플리케이션의 .env 파일에 OPENAI_API_KEY 환경 변수를 설정하세요.

SCOUT_DRIVER=turbopuffer TURBOPUFFER_API_KEY=your-turbopuffer-api-key OPENAI_API_KEY=your-openai-api-key

NOTE

Turbopuffer는 시맨틱(벡터) 검색만 지원합니다. 자세한 내용은 시맨틱 검색 섹션을 참고하세요.

Scout

소개

Laravel Scout는 Eloquent 모델에 전문 검색(full-text search) 기능을 손쉽게 추가할 수 있는 드라이버 기반 검색 솔루션입니다. Scout는 모델 옵저버(observer)를 이용해 Eloquent 레코드가 변경될 때마다 검색 인덱스를 자동으로 동기화해 줍니다. 즉, 개발자가 직접 인덱스를 갱신하는 코드를 작성하지 않아도 모델을 저장하거나 삭제하는 것만으로 검색 데이터가 항상 최신 상태로 유지됩니다.

Scout에는 별도의 외부 서비스 없이도 사용할 수 있는 database 엔진이 기본으로 내장되어 있습니다. 이 엔진은 MySQL / PostgreSQL의 전문 검색 인덱스와 LIKE 절을 활용해 기존 데이터베이스만으로 검색을 수행합니다. 대부분의 애플리케이션에서는 이 정도만으로도 충분합니다. Laravel에서 제공하는 검색 관련 옵션 전반을 살펴보고 싶다면 검색 문서를 참고하세요.

오타 허용 검색(typo tolerance), 패싯 필터링(faceted filtering), 벡터 검색, 대규모 지리 검색(geo-search)처럼 좀 더 고급 기능이 필요하다면 Algolia, Meilisearch, Typesense, Turbopuffer 드라이버를 사용할 수 있습니다. 또한 로컬 개발 환경에 적합한 "collection" 드라이버도 제공되며, 필요하다면 직접 커스텀 엔진을 작성할 수도 있습니다.

NOTE

예를 들어 국내 커머스 서비스에서 상품 검색에 오타 허용이나 자동완성 기능이 필요하다면 Meilisearch나 Algolia 드라이버를, 단순히 게시글이나 회원 목록 검색 정도라면 별도 인프라 구축 없이 database 엔진만으로도 충분한 경우가 많습니다.

설치

먼저 Composer 패키지 매니저를 통해 Scout를 설치합니다:

composer require laravel/scout

Scout 설치 후에는 vendor:publish Artisan 명령어로 설정 파일을 퍼블리시해야 합니다. 이 명령어는 scout.php 설정 파일을 애플리케이션의 config 디렉터리에 생성합니다:

php artisan vendor:publish --provider="Laravel\Scout\ScoutServiceProvider"

마지막으로, 검색 기능을 사용하려는 모델에 Laravel\Scout\Searchable 트레이트를 추가합니다. 이 트레이트는 모델 옵저버를 등록하여 모델 데이터가 자동으로 검색 드라이버와 동기화되도록 해줍니다:

<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; use Laravel\Scout\Searchable; class Post extends Model { use Searchable; }

큐 사용하기

database나 collection 엔진이 아닌 다른 엔진을 사용하는 경우, 라이브러리를 본격적으로 사용하기 전에 큐 드라이버를 설정하는 것을 적극 권장합니다. 큐 워커를 실행해두면 Scout는 모델 정보를 검색 인덱스와 동기화하는 작업을 큐에 넣어 처리하게 되고, 그 결과 애플리케이션의 웹 화면 응답 속도가 훨씬 빨라집니다.

NOTE

큐를 사용하지 않으면 모델을 저장하거나 삭제할 때마다 검색 엔진과의 동기화 작업이 요청-응답 흐름 안에서 동기적으로 처리됩니다. 검색 서버가 느리거나 네트워크 지연이 있을 경우 사용자가 그 지연을 그대로 체감하게 되므로, 운영 환경에서는 큐 사용을 기본값으로 생각하는 것이 좋습니다.

큐 드라이버를 설정했다면, config/scout.php 설정 파일에서 queue 옵션 값을 true로 지정하세요:

'queue' => true,

queue 옵션을 false로 설정하더라도, Algolia나 Meilisearch 같은 일부 Scout 드라이버는 항상 비동기 방식으로 레코드를 인덱싱한다는 점을 기억해두어야 합니다. 즉, Laravel 애플리케이션 내에서는 인덱싱 작업이 이미 끝난 것처럼 보여도, 검색 엔진 쪽에서는 새로 추가되거나 수정된 레코드가 즉시 반영되지 않을 수 있습니다.

Scout Job이 사용할 커넥션과 큐를 지정하고 싶다면, queue 설정 옵션을 배열 형태로 정의하면 됩니다:

'queue' => [ 'connection' => 'redis', 'queue' => 'scout' ],

이렇게 Scout Job이 사용할 커넥션과 큐를 직접 지정했다면, 해당 커넥션과 큐에서 Job을 처리할 큐 워커도 함께 실행해야 합니다:

php artisan queue:work redis --queue=scout

중복 없는 Job (Unique Jobs)

쓰기 작업이 많은 애플리케이션에서는 동일한 모델 레코드에 대해 Scout가 중복된 Job을 큐에 쌓지 않도록 하고 싶을 수 있습니다. 이런 경우 MakeSearchableUniquely와 RemoveFromSearchUniquely Job 클래스를 등록하여 중복 방지 인덱싱 기능을 사용할 수 있습니다. 보통 서비스 프로바이더의 boot 메서드에서 다음과 같이 등록합니다:

use Laravel\Scout\Jobs\MakeSearchableUniquely; use Laravel\Scout\Jobs\RemoveFromSearchUniquely; use Laravel\Scout\Scout; Scout::makeSearchableUsing(MakeSearchableUniquely::class); Scout::removeFromSearchUsing(RemoveFromSearchUniquely::class);

이 Job들은 Laravel의 고유 Job 잠금(unique job locks) 기능을 활용하여, 동일한 검색 대상 모델 레코드에 대해 이미 같은 종류의 Job이 큐에 대기 중이라면 중복된 인덱싱 작업을 추가로 큐에 넣지 않도록 방지합니다.

드라이버 사전 준비 사항

Algolia 드라이버를 사용하려면 config/scout.php 설정 파일에 Algolia의 id와 secret 자격 증명을 설정해야 합니다. 자격 증명 설정을 마쳤다면, Composer 패키지 매니저를 통해 Algolia PHP SDK도 설치해야 합니다:

composer require algolia/algoliasearch-client-php

Meilisearch

Meilisearch는 빠르고 오픈소스인 검색 엔진입니다. 로컬 머신에 Meilisearch를 어떻게 설치해야 할지 모르겠다면, Laravel이 공식적으로 지원하는 Docker 개발 환경인 Laravel Sail을 사용해보세요.

Meilisearch 드라이버를 사용하려면 Composer 패키지 매니저를 통해 Meilisearch PHP SDK를 설치해야 합니다:

composer require meilisearch/meilisearch-php http-interop/http-factory-guzzle

그런 다음, 애플리케이션의 .env 파일에 SCOUT_DRIVER 환경 변수와 함께 Meilisearch의 host, key 자격 증명을 설정합니다:

SCOUT_DRIVER=meilisearch MEILISEARCH_HOST=http://127.0.0.1:7700 MEILISEARCH_KEY=masterKey

Meilisearch에 대한 더 자세한 내용은 Meilisearch 공식 문서를 참고하세요.

또한 meilisearch/meilisearch-php 패키지를 설치할 때는 사용 중인 Meilisearch 바이너리 버전과 호환되는 버전을 설치해야 합니다. Meilisearch의 바이너리 호환성 문서를 참고하여 확인하세요.

WARNING

Meilisearch를 사용하는 애플리케이션에서 Scout를 업그레이드할 때는, Meilisearch 서비스 자체에 추가적인 하위 호환성 이슈(Breaking Changes)가 없는지 항상 확인해야 합니다.

Typesense

Typesense는 매우 빠른 오픈소스 검색 엔진으로, 키워드 검색뿐만 아니라 시맨틱 검색, 지리 정보(geo) 검색, 벡터 검색까지 지원합니다.

Typesense는 직접 셀프 호스팅하거나 Typesense Cloud를 이용할 수 있습니다.

Scout와 함께 Typesense를 사용하려면, Composer 패키지 매니저를 통해 Typesense PHP SDK를 설치하세요:

composer require typesense/typesense-php

그런 다음, 애플리케이션의 .env 파일에 SCOUT_DRIVER 환경 변수와 함께 Typesense 호스트 및 API 키 자격 증명을 설정합니다:

SCOUT_DRIVER=typesense TYPESENSE_API_KEY=masterKey TYPESENSE_HOST=localhost

Laravel Sail을 사용하고 있다면, TYPESENSE_HOST 환경 변수 값을 Docker 컨테이너 이름에 맞게 수정해야 할 수도 있습니다. 필요하다면 설치 환경의 포트, 경로, 프로토콜도 함께 지정할 수 있습니다:

TYPESENSE_PORT=8108 TYPESENSE_PATH= TYPESENSE_PROTOCOL=http

Typesense 컬렉션에 대한 추가 설정과 스키마 정의는 애플리케이션의 config/scout.php 설정 파일에서 확인할 수 있습니다. Typesense에 대한 더 자세한 내용은 Typesense 공식 문서를 참고하세요.

Turbopuffer

Turbopuffer는 전문(full-text) 검색, 시맨틱 검색, 하이브리드 검색을 모두 지원하는 검색 엔진입니다. Turbopuffer 드라이버를 사용하려면 SCOUT_DRIVER 환경 변수를 설정하고 Turbopuffer API 키를 지정하세요:

SCOUT_DRIVER=turbopuffer TURBOPUFFER_API_KEY=tpuf_... TURBOPUFFER_REGION=gcp-us-central1

TURBOPUFFER_REGION 환경 변수는 선택 사항이며, 기본값은 gcp-us-central1입니다.

Scout

검색 대상 데이터 설정

기본적으로 Scout는 모델의 toArray 결과 전체를 검색 인덱스에 저장합니다. 검색 인덱스에 동기화되는 데이터를 원하는 형태로 커스터마이징하고 싶다면, 모델에서 toSearchableArray 메서드를 오버라이드하면 됩니다:

<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; use Laravel\Scout\Searchable; class Post extends Model { use Searchable; /** * 모델의 인덱싱 대상 데이터 배열을 반환합니다. * * @return array<string, mixed> */ public function toSearchableArray(): array { $array = $this->toArray(); // 데이터 배열을 원하는 형태로 커스터마이징... return $array; } }

NOTE

검색 인덱스에는 검색과 결과 표시에 실제로 필요한 필드만 포함하는 것이 좋습니다. 불필요한 관계 데이터나 대용량 컬럼까지 모두 포함시키면 인덱싱 속도가 느려지고 검색 엔진의 저장 공간도 낭비될 수 있습니다.

모델별 검색 엔진 설정

Scout는 검색을 수행할 때 기본적으로 애플리케이션의 scout 설정 파일에 지정된 기본 검색 엔진을 사용합니다. 하지만 특정 모델에 대해서만 다른 검색 엔진을 사용하고 싶다면, 모델에서 searchableUsing 메서드를 오버라이드하면 됩니다:

<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; use Laravel\Scout\Engines\Engine; use Laravel\Scout\Scout; use Laravel\Scout\Searchable; class User extends Model { use Searchable; /** * 모델을 인덱싱할 때 사용할 검색 엔진을 반환합니다. */ public function searchableUsing(): Engine { return Scout::engine('meilisearch'); } }

예를 들어, 대부분의 모델은 Meilisearch를 기본 엔진으로 사용하되 User 모델만 별도로 Algolia 인덱스를 유지하고 싶은 경우처럼, 모델 특성에 맞게 검색 엔진을 다르게 지정할 수 있습니다.

데이터베이스/컬렉션 엔진

데이터베이스 엔진

WARNING

데이터베이스 엔진은 현재 MySQL과 PostgreSQL만 지원합니다. 두 데이터베이스 모두 빠른 전문(full-text) 컬럼 인덱싱 기능을 제공합니다.

database 엔진은 MySQL/PostgreSQL의 전문 인덱스와 LIKE 절을 사용해 기존 데이터베이스를 직접 검색합니다. 별도의 외부 서비스나 추가 인프라 없이도 검색 기능을 붙일 수 있기 때문에, 대부분의 애플리케이션에서 가장 간단하고 실용적인 선택지입니다.

데이터베이스 엔진을 사용하려면 SCOUT_DRIVER 환경 변수를 database로 설정하세요.

SCOUT_DRIVER=database

설정을 마쳤다면 검색 대상 데이터를 정의하고 모델에 대해 검색 쿼리를 실행할 수 있습니다. 다른 서드파티 엔진과 달리 데이터베이스 엔진은 별도의 인덱싱 과정이 필요 없습니다. 데이터베이스 테이블을 곧바로 검색하기 때문입니다.

시맨틱 검색과 하이브리드 검색

데이터베이스 엔진은 PostgreSQL과 pgvector 확장을 함께 사용할 경우 시맨틱 검색과 하이브리드 검색을 지원합니다. 시작하려면 모델의 테이블에 nullable한 벡터 컬럼과 전문 인덱스를 추가해야 합니다. Scout는 모델이 저장된 이후에 임베딩 값을 채워 넣기 때문에, 벡터 컬럼은 반드시 nullable이어야 합니다.

Schema::ensureVectorExtensionExists(); Schema::table('articles', function (Blueprint $table) { // ... $table->vector('embedding', dimensions: 1536)->nullable(); $table->vectorIndex('embedding'); $table->fullText(['title', 'body']); });

다음으로, 모델에 toSearchableEmbedding 메서드를 정의합니다. 이 메서드는 Scout가 임베딩을 생성할 원본 텍스트를 반환하거나, 이미 계산된 임베딩 배열을 직접 반환할 수도 있습니다. Scout는 기본적으로 embedding 컬럼에 임베딩 값을 저장합니다. 다른 컬럼을 사용하고 싶다면 모델에 searchableEmbeddingColumn 메서드를 정의하면 됩니다.

데이터베이스 검색 전략 커스터마이징

기본적으로 데이터베이스 엔진은 검색 대상으로 설정한 모든 모델 속성에 대해 LIKE 쿼리를 실행합니다. 하지만 특정 컬럼에 대해서는 더 효율적인 검색 전략을 지정할 수 있습니다. SearchUsingFullText 속성을 사용하면 해당 컬럼의 전문 인덱스를 활용하고, SearchUsingPrefix 속성을 사용하면 문자열 전체(%example%)가 아니라 문자열 앞부분(example%)만 매칭합니다.

이런 동작을 정의하려면 모델의 toSearchableArray 메서드에 PHP 속성(attribute)을 지정하면 됩니다. 속성이 지정되지 않은 컬럼은 기본 LIKE 전략을 그대로 사용합니다.

use Laravel\Scout\Attributes\SearchUsingFullText; use Laravel\Scout\Attributes\SearchUsingPrefix; /** * 모델의 인덱싱 대상 데이터 배열을 반환합니다. * * @return array<string, mixed> */ #[SearchUsingPrefix(['id', 'email'])] #[SearchUsingFullText(['bio'])] public function toSearchableArray(): array { return [ 'id' => $this->id, 'name' => $this->name, 'email' => $this->email, 'bio' => $this->bio, ]; }

WARNING

특정 컬럼에 전문 검색 제약 조건을 지정하기 전에, 해당 컬럼에 전문 인덱스가 생성되어 있는지 반드시 확인하세요.

컬렉션 엔진

"컬렉션" 엔진은 빠른 프로토타입 제작, 아주 작은 규모의 데이터셋(수백 건 이하), 또는 테스트 실행 용도로 만들어졌습니다. 데이터베이스에서 가능한 모든 레코드를 가져온 뒤 Laravel의 Str::is 헬퍼를 사용해 PHP에서 직접 필터링하는 방식이기 때문에, 별도의 인덱싱이나 데이터베이스 전용 기능이 필요하지 않습니다. 단순한 용도를 넘어서는 상황이라면 데이터베이스 엔진을 사용하는 것이 좋습니다.

컬렉션 엔진을 사용하려면 SCOUT_DRIVER 환경 변수 값을 collection으로 지정하거나, 애플리케이션의 scout 설정 파일에서 collection 드라이버를 직접 지정하면 됩니다.

SCOUT_DRIVER=collection

컬렉션 드라이버를 지정했다면 모델에 대해 곧바로 검색 쿼리를 실행할 수 있습니다. Algolia, Meilisearch, Typesense 인덱스를 채우기 위해 필요했던 검색 엔진 인덱싱 과정은 컬렉션 엔진을 사용할 때는 필요하지 않습니다.

데이터베이스 엔진이 전문 인덱스와 LIKE 절을 사용해 효율적으로 일치하는 레코드를 찾는 반면, 컬렉션 엔진은 모든 레코드를 가져온 뒤 PHP에서 필터링합니다. 컬렉션 엔진은 SQLite, SQL Server를 포함해 Laravel이 지원하는 모든 관계형 데이터베이스에서 동작하기 때문에 이식성이 가장 뛰어납니다. 다만 데이터베이스 엔진에 비해 효율이 크게 떨어지므로 대용량 데이터셋에는 사용하지 않는 것이 좋습니다.

서드파티 엔진 설정

아래 설정 항목들은 Algolia, Meilisearch, Typesense와 같은 서드파티 검색 엔진을 사용할 때만 적용됩니다. 데이터베이스 엔진을 사용하고 있다면 이번 섹션은 건너뛰어도 무방합니다.

모델 인덱스 설정

서드파티 엔진을 사용하는 경우, 각 Eloquent 모델은 해당 모델의 검색 가능한 레코드를 모두 담고 있는 특정 검색 "인덱스"와 동기화됩니다. 기본적으로 각 모델은 해당 모델의 일반적인 "테이블" 이름과 일치하는 인덱스에 저장됩니다. 보통은 모델명의 복수형이 사용되지만, 모델에서 searchableAs 메서드를 오버라이드하면 인덱스 이름을 자유롭게 지정할 수 있습니다.

<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; use Laravel\Scout\Searchable; class Post extends Model { use Searchable; /** * 모델과 연결된 인덱스 이름을 반환합니다. */ public function searchableAs(): string { return 'posts_index'; } }

NOTE

searchableAs 메서드는 데이터베이스 엔진을 사용할 때는 아무런 효과가 없습니다. 데이터베이스 엔진은 항상 모델의 데이터베이스 테이블을 직접 검색하기 때문입니다.

모델 ID 설정하기

기본적으로 Scout는 모델의 기본 키(primary key)를 검색 인덱스에 저장되는 모델의 고유 ID / 키로 사용합니다. 서드파티 엔진을 사용할 때 이 동작을 커스터마이징해야 한다면, 모델에서 getScoutKey와 getScoutKeyName 메서드를 오버라이드하면 됩니다.

<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; use Laravel\Scout\Searchable; class User extends Model { use Searchable; /** * 모델을 인덱싱할 때 사용할 값을 반환합니다. */ public function getScoutKey(): mixed { return $this->email; } /** * 모델을 인덱싱할 때 사용할 키 이름을 반환합니다. */ public function getScoutKeyName(): mixed { return 'email'; } }

NOTE

getScoutKey와 getScoutKeyName 메서드는 데이터베이스 엔진을 사용할 때는 아무런 효과가 없습니다. 데이터베이스 엔진은 항상 모델의 기본 키를 사용합니다.

Algolia

인덱스 설정

Algolia 인덱스에 추가적인 설정을 적용하고 싶을 때가 있습니다. Algolia UI를 통해서도 이런 설정을 관리할 수 있지만, 애플리케이션의 config/scout.php 설정 파일에서 인덱스 구성의 원하는 상태를 직접 관리하는 편이 더 효율적인 경우가 많습니다.

이렇게 설정 파일에서 관리하면 애플리케이션의 자동화된 배포 파이프라인을 통해 설정을 배포할 수 있어, 수동 설정 작업을 없애고 여러 환경 간의 일관성을 보장할 수 있습니다. 필터링 가능한 속성(filterable attributes), 랭킹, 패싯(faceting) 등 지원되는 다양한 설정을 구성할 수 있습니다.

먼저 애플리케이션의 config/scout.php 설정 파일에 각 인덱스에 대한 설정을 추가합니다.

use App\Models\User; use App\Models\Flight; 'algolia' => [ 'id' => env('ALGOLIA_APP_ID', ''), 'secret' => env('ALGOLIA_SECRET', ''), 'index-settings' => [ User::class => [ 'searchableAttributes' => ['id', 'name', 'email'], 'attributesForFaceting'=> ['filterOnly(email)'], // 그 외 설정 필드... ], Flight::class => [ 'searchableAttributes'=> ['id', 'destination'], ], ], ],

특정 인덱스에 해당하는 모델이 소프트 삭제(soft delete)를 지원하고 index-settings 배열에 포함되어 있다면, Scout는 해당 인덱스에서 소프트 삭제된 모델에 대한 패싯 처리를 자동으로 지원합니다. 소프트 삭제 가능한 모델 인덱스에 별도로 정의할 패싯 속성이 없다면, 해당 모델에 대해 빈 항목만 index-settings 배열에 추가해주면 됩니다.

'index-settings' => [ Flight::class => [] ],

인덱스 설정을 구성한 후에는 scout:sync-index-settings Artisan 명령어를 실행해야 합니다. 이 명령어는 현재 설정된 인덱스 설정 내용을 Algolia에 전달합니다. 편의를 위해 이 명령어를 배포 프로세스의 일부로 포함시켜 두는 것을 권장합니다.

php artisan scout:sync-index-settings

사용자 식별

Scout는 Algolia를 사용할 때 사용자를 자동으로 식별하는 기능을 제공합니다. 인증된 사용자를 검색 작업과 연결해두면 Algolia 대시보드에서 검색 분석 데이터를 확인할 때 유용합니다. 애플리케이션의 .env 파일에 SCOUT_IDENTIFY 환경 변수를 true로 설정하면 사용자 식별 기능을 활성화할 수 있습니다.

SCOUT_IDENTIFY=true

이 기능을 활성화하면 요청의 IP 주소와 인증된 사용자의 기본 식별자가 함께 Algolia로 전달되어, 사용자가 수행한 검색 요청과 해당 데이터가 연결됩니다.

Meilisearch

인덱스 설정

Meilisearch는 필터링 가능한 속성(filterable attributes), 정렬 가능한 속성(sortable attributes) 등 지원되는 설정 필드를 미리 정의해두어야 합니다.

필터링 가능한 속성은 Scout의 where 메서드를 호출할 때 필터 조건으로 사용할 속성을 의미하고, 정렬 가능한 속성은 orderBy 메서드를 호출할 때 정렬 기준으로 사용할 속성을 의미합니다. 인덱스 설정을 정의하려면, 애플리케이션의 scout 설정 파일에서 meilisearch 설정 항목의 index-settings 부분을 수정합니다.

use App\Models\User; use App\Models\Flight; 'meilisearch' => [ 'host' => env('MEILISEARCH_HOST', 'http://localhost:7700'), 'key' => env('MEILISEARCH_KEY', null), 'index-settings' => [ User::class => [ 'filterableAttributes'=> ['id', 'name', 'email'], 'sortableAttributes' => ['created_at'], // 그 외 설정 필드... ], Flight::class => [ 'filterableAttributes'=> ['id', 'destination'], 'sortableAttributes' => ['updated_at'], ], ], ],

특정 인덱스에 해당하는 모델이 소프트 삭제를 지원하고 index-settings 배열에 포함되어 있다면, Scout는 해당 인덱스에서 소프트 삭제된 모델에 대한 필터링을 자동으로 지원합니다. 소프트 삭제 가능한 모델 인덱스에 별도로 정의할 필터링/정렬 속성이 없다면, 해당 모델에 대해 빈 항목만 index-settings 배열에 추가해주면 됩니다.

'index-settings' => [ Flight::class => [] ],

인덱스 설정을 구성한 후에는 scout:sync-index-settings Artisan 명령어를 실행해야 합니다. 이 명령어는 현재 설정된 인덱스 설정 내용을 Meilisearch에 전달합니다. 편의를 위해 이 명령어를 배포 프로세스의 일부로 포함시켜 두는 것을 권장합니다.

php artisan scout:sync-index-settings

시맨틱 검색과 하이브리드 검색

Meilisearch에서 시맨틱 검색이나 하이브리드 검색을 사용하려면, 인덱스 설정에 임베더(embedder)를 구성하고 검색 가능한 각 모델에 대한 임베딩 설정을 추가해야 합니다.

'meilisearch' => [ // ... 'index-settings' => [ Article::class => [ 'embedders' => [ 'default' => [ 'source' => 'userProvided', 'dimensions' => 1536, ], ], ], ], 'model-settings' => [ Article::class => [ 'embedding' => [ 'embedder' => 'default', 'dimensions' => 1536, ], ], ], ],

모델의 toSearchableEmbedding 메서드는 원본 텍스트를 반환할 수 있으며, Scout는 Laravel AI SDK를 사용해 이를 임베딩으로 변환합니다. 미리 계산된 임베딩 배열을 직접 반환하는 것도 가능합니다. 설정을 변경한 후에는 scout:sync-index-settings 명령어를 실행하세요.

또는, 임베딩 driver를 meilisearch로 설정하면 Meilisearch의 네이티브 임베딩 기능을 사용할 수 있습니다. 이 방식에서는 Meilisearch가 구성된 임베더를 사용해 문서와 쿼리의 임베딩을 직접 생성하므로, dimensions 옵션이나 toSearchableEmbedding 메서드가 필요하지 않습니다.

'meilisearch' => [ 'index-settings' => [ Article::class => [ 'embedders' => [ 'default' => [ 'source' => 'openAi', 'apiKey' => env('OPENAI_API_KEY'), 'model' => 'text-embedding-3-small', 'documentTemplate' => 'An article titled {{ doc.title }}: {{ doc.body }}', ], ], ], ], 'model-settings' => [ Article::class => [ 'embedding' => [ 'embedder' => 'default', 'driver' => 'meilisearch', ], ], ], ],

네이티브 임베딩을 사용할 경우 Scout는 인덱싱되는 문서에 벡터를 생성하거나 추가하지 않습니다. 다만 vector 검색 옵션을 사용해 미리 계산된 쿼리 벡터를 직접 전달할 수는 있습니다.

검색 가능한 데이터 타입

Meilisearch는 올바른 타입의 데이터에 대해서만 필터 연산(>, < 등)을 수행합니다. 검색 가능한 데이터를 커스터마이징할 때는 숫자 값이 올바른 타입으로 캐스팅되도록 신경 써야 합니다.

public function toSearchableArray() { return [ 'id' => (int) $this->id, 'name' => $this->name, 'price' => (float) $this->price, ]; }

Typesense

검색 가능한 데이터 준비하기

Typesense를 사용할 때는 검색 가능한 모델에서 toSearchableArray 메서드를 정의하여, 모델의 기본 키를 문자열로, 생성일을 UNIX 타임스탬프로 캐스팅해주어야 합니다.

/** * 모델의 인덱싱 가능한 데이터 배열을 반환합니다. * * @return array<string, mixed> */ public function toSearchableArray(): array { return array_merge($this->toArray(),[ 'id' => (string) $this->id, 'created_at' => $this->created_at->timestamp, ]); }

또한 애플리케이션의 config/scout.php 파일에 Typesense 컬렉션 스키마를 정의해야 합니다. 컬렉션 스키마는 Typesense를 통해 검색 가능한 각 필드의 데이터 타입을 정의합니다. 사용 가능한 전체 스키마 옵션에 대해서는 Typesense 공식 문서를 참고하세요.

이미 정의된 Typesense 컬렉션의 스키마를 변경해야 한다면, scout:flush와 scout:import를 순서대로 실행해 기존 인덱싱 데이터를 모두 삭제하고 스키마를 다시 생성할 수 있습니다. 또는 Typesense의 API를 사용하여 인덱싱된 데이터를 삭제하지 않고도 컬렉션 스키마를 수정할 수 있습니다.

검색 가능한 모델이 소프트 삭제를 지원한다면, 애플리케이션의 config/scout.php 설정 파일에서 해당 모델의 Typesense 스키마에 __soft_deleted 필드를 정의해야 합니다.

User::class => [ 'collection-schema' => [ 'fields' => [ // ... [ 'name' => '__soft_deleted', 'type' => 'int32', 'optional' => true, ], ], ], ],

임베딩

시맨틱 검색과 하이브리드 검색을 활성화하려면, 모델의 Typesense 설정에 embedding 설정과 벡터 필드를 정의해야 합니다. 기본적으로 Scout는 Laravel AI SDK를 사용하여 임베딩을 생성합니다.

use App\Models\Article; 'model-settings' => [ Article::class => [ 'collection-schema' => [ 'fields' => [ ['name' => 'title', 'type' => 'string'], ['name' => 'embedding', 'type' => 'float[]', 'num_dim' => 1536], ], ], 'search-parameters' => ['query_by' => 'title'], 'embedding' => [ 'attribute' => 'embedding', 'dimensions' => 1536, ], ], ],

모델의 toSearchableEmbedding 메서드는 Scout가 임베딩으로 변환할 원본 텍스트를 반환하거나, 미리 계산된 임베딩 배열을 직접 반환해야 합니다.

public function toSearchableEmbedding(): string|array { return $this->title.' '.$this->body; }

동적 검색 파라미터

Typesense는 검색을 수행할 때 options 메서드를 통해 검색 파라미터를 동적으로 변경할 수 있도록 지원합니다.

use App\Models\Todo; Todo::search('Groceries')->options([ 'query_by' => 'title, description' ])->get();

Turbopuffer

Turbopuffer는 각 모델에 대해 스키마와 검색 가능한 속성을 요구합니다. scout 설정 파일 안의 turbopuffer 설정에서 model-settings 배열에 이를 정의합니다.

use App\Models\Article; 'turbopuffer' => [ // ... 'model-settings' => [ Article::class => [ 'searchable-attributes' => [ 'title' => 3, 'body' => 1, ], 'schema' => [ 'title' => ['type' => 'string', 'full_text_search' => true], 'body' => ['type' => 'string', 'full_text_search' => true], 'status' => ['type' => 'string'], ], ], ], ],

searchable-attributes에 지정된 숫자 값은 상대적인 BM25 가중치입니다. 위 예시에서는 게시글 제목(title)에서 일치하는 항목이 본문(body)에서 일치하는 항목보다 3배 높은 점수를 갖습니다.

시맨틱 검색과 하이브리드 검색을 활성화하려면, 모델 설정에 embedding 설정과 벡터 스키마를 추가하세요.

'turbopuffer' => [ // ... 'model-settings' => [ Article::class => [ 'searchable-attributes' => [ 'title' => 3, 'body' => 1, ], 'embedding' => [ 'attribute' => 'embedding', 'dimensions' => 1536, ], 'schema' => [ 'title' => ['type' => 'string', 'full_text_search' => true], 'body' => ['type' => 'string', 'full_text_search' => true], 'embedding' => ['type' => '[1536]f32', 'ann' => true], ], ], ], ],

모델의 toSearchableEmbedding 메서드는 Scout가 임베딩으로 변환할 원본 텍스트를 반환하거나, 미리 계산된 임베딩 배열을 직접 반환해야 합니다. Scout는 원본 텍스트에 대한 임베딩을 Laravel AI SDK를 통해 생성합니다.

또는 Laravel AI SDK를 설치하거나 toSearchableEmbedding 메서드를 정의하지 않고도 Turbopuffer의 네이티브 임베딩 기능을 사용할 수 있습니다. 임베딩 드라이버를 turbopuffer로 설정하고, 검색 가능한 원본 속성에 embed 스키마를 구성하면 됩니다.

'embedding' => [ 'driver' => 'turbopuffer', 'attribute' => 'embedding_text', ], 'schema' => [ // ... 'embedding_text' => [ 'type' => 'string', 'embed' => [ 'model' => 'voyage/voyage-4', 'dimensions' => 1024, 'attribute' => 'embedding', ], ], ],

원본 속성은 반드시 모델의 toSearchableArray 반환 값에 포함되어 있어야 합니다.

서드파티 엔진 인덱싱

NOTE

이번 섹션에서 설명하는 인덱싱 기능은 서드파티 엔진(Algolia, Meilisearch, Typesense, Turbopuffer)을 사용할 때 주로 해당됩니다. database 엔진은 데이터베이스 테이블을 직접 검색하기 때문에 별도의 인덱스 관리가 필요하지 않습니다.

일괄 임포트(Batch Import)

기존 프로젝트에 Scout를 도입하는 경우, 이미 데이터베이스에 있는 레코드들을 인덱스로 가져와야 할 수 있습니다. Scout는 기존 레코드를 검색 인덱스로 한 번에 가져올 수 있는 scout:import Artisan 명령어를 제공합니다:

php artisan scout:import "App\Models\Post"

큐 작업을 이용해 기존 레코드를 임포트하려면 scout:queue-import 명령어를 사용하면 됩니다:

php artisan scout:queue-import "App\Models\Post" --chunk=500

특정 모델의 모든 레코드를 검색 인덱스에서 제거하려면 flush 명령어를 사용합니다:

php artisan scout:flush "App\Models\Post"

임포트 쿼리 수정하기

일괄 임포트 시 모델을 조회하는 쿼리를 직접 수정하고 싶다면, 모델에 makeAllSearchableUsing 메서드를 정의하면 됩니다. 임포트 전에 필요한 연관관계를 즉시 로딩(eager loading)하기에 좋은 위치입니다:

use Illuminate\Database\Eloquent\Builder; /** * 모든 모델을 검색 가능하게 만들 때 사용할 쿼리를 수정합니다. */ protected function makeAllSearchableUsing(Builder $query): Builder { return $query->with('author'); }

WARNING

큐를 사용해 모델을 일괄 임포트하는 경우에는 makeAllSearchableUsing 메서드가 적용되지 않을 수 있습니다. Job이 모델 컬렉션을 처리할 때는 연관관계가 복원되지 않기 때문입니다.

모델에 Laravel\Scout\Searchable 트레이트를 추가했다면, 이후에는 그냥 모델 인스턴스를 save하거나 create하기만 하면 자동으로 검색 인덱스에 추가됩니다. Scout가 큐를 사용하도록 설정되어 있다면 이 작업은 큐 워커에 의해 백그라운드에서 처리됩니다:

use App\Models\Order; $order = new Order; // ... $order->save();

쿼리를 통해 레코드 추가하기

Eloquent 쿼리를 통해 여러 모델을 한 번에 검색 인덱스에 추가하고 싶다면, Eloquent 쿼리에 searchable 메서드를 체이닝하면 됩니다. searchable 메서드는 쿼리 결과를 청크 단위로 나누어 검색 인덱스에 추가합니다. 이때도 Scout가 큐를 사용하도록 설정되어 있다면, 모든 청크는 큐 워커에 의해 백그라운드에서 임포트됩니다:

use App\Models\Order; Order::where('price', '>', 100)->searchable();

Eloquent 연관관계 인스턴스에서도 searchable 메서드를 호출할 수 있습니다:

$user->orders()->searchable();

또는 이미 메모리상에 Eloquent 모델 컬렉션을 가지고 있다면, 컬렉션 인스턴스에서 searchable 메서드를 호출하여 해당 모델 인스턴스들을 각자의 인덱스에 추가할 수 있습니다:

$orders->searchable();

NOTE

searchable 메서드는 일종의 "upsert" 작업이라고 볼 수 있습니다. 즉, 모델 레코드가 이미 인덱스에 존재하면 업데이트되고, 존재하지 않으면 새로 추가됩니다.

레코드 업데이트하기

검색 가능한 모델을 업데이트하려면, 모델 인스턴스의 속성을 변경한 뒤 데이터베이스에 save하기만 하면 됩니다. Scout가 자동으로 변경 사항을 검색 인덱스에 반영합니다:

use App\Models\Order; $order = Order::find(1); // 주문 정보 업데이트... $order->save();

Eloquent 쿼리 인스턴스에서 searchable 메서드를 호출하여 여러 모델을 한 번에 업데이트할 수도 있습니다. 만약 해당 모델이 검색 인덱스에 존재하지 않는다면 새로 생성됩니다:

Order::where('price', '>', 100)->searchable();

연관관계에 속한 모든 모델의 검색 인덱스 레코드를 업데이트하고 싶다면, 연관관계 인스턴스에서 searchable을 호출하면 됩니다:

$user->orders()->searchable();

또는 이미 메모리상에 Eloquent 모델 컬렉션을 가지고 있다면, 컬렉션 인스턴스에서 searchable 메서드를 호출하여 해당 인덱스의 모델 인스턴스들을 업데이트할 수 있습니다:

$orders->searchable();

임포트 전 레코드 수정하기

검색 가능하게 만들기 전에 모델 컬렉션을 미리 가공해야 할 때가 있습니다. 예를 들어, 연관관계 데이터를 검색 인덱스에 효율적으로 추가하기 위해 미리 즉시 로딩(eager loading)을 하고 싶을 수 있습니다. 이럴 때는 해당 모델에 makeSearchableUsing 메서드를 정의하면 됩니다:

use Illuminate\Database\Eloquent\Collection; /** * 검색 가능하게 만들 모델 컬렉션을 수정합니다. */ public function makeSearchableUsing(Collection $models): Collection { return $models->load('author'); }

조건부로 검색 인덱스 업데이트하기

기본적으로 Scout는 어떤 속성이 변경되었는지와 관계없이 업데이트된 모델을 다시 인덱싱합니다. 이 동작을 커스터마이징하고 싶다면, 모델에 searchIndexShouldBeUpdated 메서드를 정의하면 됩니다:

/** * 검색 인덱스를 업데이트해야 하는지 여부를 결정합니다. */ public function searchIndexShouldBeUpdated(): bool { return $this->wasRecentlyCreated || $this->wasChanged(['title', 'body']); }

레코드 삭제하기

인덱스에서 레코드를 제거하려면 데이터베이스에서 해당 모델을 delete하기만 하면 됩니다. 이는 소프트 삭제(soft delete) 모델을 사용하는 경우에도 동일하게 동작합니다:

use App\Models\Order; $order = Order::find(1); $order->delete();

레코드를 삭제하기 전에 모델을 굳이 조회하고 싶지 않다면, Eloquent 쿼리 인스턴스에서 unsearchable 메서드를 사용할 수 있습니다:

Order::where('price', '>', 100)->unsearchable();

연관관계에 속한 모든 모델의 검색 인덱스 레코드를 제거하고 싶다면, 연관관계 인스턴스에서 unsearchable을 호출하면 됩니다:

$user->orders()->unsearchable();

또는 이미 메모리상에 Eloquent 모델 컬렉션을 가지고 있다면, 컬렉션 인스턴스에서 unsearchable 메서드를 호출하여 해당 모델 인스턴스들을 인덱스에서 제거할 수 있습니다:

$orders->unsearchable();

특정 모델의 모든 레코드를 대응하는 인덱스에서 제거하려면 removeAllFromSearch 메서드를 호출하면 됩니다:

Order::removeAllFromSearch();

인덱싱 일시 중지하기

경우에 따라 모델 데이터를 검색 인덱스에 동기화하지 않고 Eloquent 작업을 일괄로 수행해야 할 때가 있습니다. 이럴 때는 withoutSyncingToSearch 메서드를 사용할 수 있습니다. 이 메서드는 즉시 실행되는 클로저 하나를 인자로 받으며, 클로저 안에서 발생하는 모델 작업들은 해당 모델의 인덱스에 동기화되지 않습니다:

use App\Models\Order; Order::withoutSyncingToSearch(function () { // 모델 관련 작업 수행... });

조건부로 검색 가능한 모델 인스턴스

특정 조건에서만 모델을 검색 가능하게 만들어야 하는 경우가 있습니다. 예를 들어 App\Models\Post 모델이 "draft"(임시 저장)와 "published"(게시됨) 두 가지 상태 중 하나를 가질 수 있다고 가정해봅시다. 이때 "published" 상태의 게시글만 검색 가능하도록 하고 싶을 것입니다. 이를 구현하려면 모델에 shouldBeSearchable 메서드를 정의하면 됩니다:

/** * 모델이 검색 가능해야 하는지 여부를 결정합니다. */ public function shouldBeSearchable(): bool { return $this->isPublished(); }

shouldBeSearchable 메서드는 save, create 메서드나 쿼리, 연관관계를 통해 모델을 다룰 때만 적용됩니다. searchable 메서드를 사용해 모델이나 컬렉션을 직접 검색 가능하게 만드는 경우에는 shouldBeSearchable의 결과가 무시되고 무조건 적용됩니다.

WARNING

Scout의 "database" 엔진을 사용할 때는 모든 검색 대상 데이터가 항상 데이터베이스에 저장되어 있으므로 shouldBeSearchable 메서드가 적용되지 않습니다. database 엔진을 사용하면서 비슷한 동작을 구현하려면 where 절을 대신 사용해야 합니다.

검색

search 메서드를 사용하면 모델을 검색할 수 있습니다. search 메서드는 모델을 검색하는 데 사용할 문자열 하나를 인자로 받습니다. 이후 get 메서드를 체이닝하면, 검색 쿼리와 일치하는 Eloquent 모델들을 조회할 수 있습니다.

use App\Models\Order; $orders = Order::search('Star Trek')->get();

Scout 검색 결과는 Eloquent 모델 컬렉션이므로, 라우트나 컨트롤러에서 결과를 그대로 반환해도 자동으로 JSON으로 변환됩니다.

use App\Models\Order; use Illuminate\Http\Request; Route::get('/search', function (Request $request) { return Order::search($request->search)->get(); });

Eloquent 모델로 변환되기 전의 원본 검색 결과를 얻고 싶다면 raw 메서드를 사용하세요.

$orders = Order::search('Star Trek')->raw();

시맨틱 검색 (Semantic Search)

데이터베이스, Meilisearch, Typesense, Turbopuffer 엔진은 쿼리의 "의미"를 기준으로 레코드를 매칭하는 시맨틱 검색을 지원합니다. Scout가 임베딩을 직접 생성하는 경우, 시맨틱 검색과 하이브리드 검색을 사용하려면 Laravel AI SDK가 필요합니다. 단, Typesense 자체 임베딩, Turbopuffer 자체 임베딩, 그리고 미리 계산된 쿼리 벡터를 사용하는 경우에는 Laravel AI SDK가 필요하지 않습니다.

선택한 엔진에서 임베딩 설정을 마쳤다면, 검색 쿼리에 semantic 메서드를 호출하면 됩니다.

$articles = Article::search('staying cool in the summer') ->semantic() ->get();

선택한 엔진이 지원하는 경우, 최소 유사도 임계값을 지정할 수도 있습니다.

$articles = Article::search('renewable energy storage') ->semantic(minSimilarity: 0.6) ->get();

전문(full-text) 검색과 시맨틱 검색을 결합하려면 hybrid 메서드를 사용하세요. 처음 두 인자는 각각 텍스트 검색 결과와 시맨틱 검색 결과의 상대적 가중치를 조절합니다.

$articles = Article::search('renewable energy storage') ->hybrid(textWeight: 1, semanticWeight: 2) ->get();

커스텀 인덱스

서드파티 엔진을 사용해 검색할 경우, 일반적으로 모델의 searchableAs 메서드가 지정한 인덱스를 대상으로 쿼리가 수행됩니다. 하지만 within 메서드를 사용하면 검색 대상 인덱스를 직접 지정할 수 있습니다.

$orders = Order::search('Star Trek') ->within('tv_shows_popularity_desc') ->get();

Where 절

Scout에서는 검색 쿼리에 "where" 절을 추가할 수 있습니다. 예를 들어, 소유자 ID로 검색 범위를 좁히는 것처럼 단순 동등 비교를 활용할 수 있습니다.

use App\Models\Order; $orders = Order::search('Star Trek')->where('user_id', 1)->get();

=, !=, <, >, >=, <= 비교 연산자를 사용해 좀 더 복잡한 조건도 만들 수 있습니다.

Order::search('Star Trek') ->where('status', '=', 'completed') ->where('is_refunded', '!=', true) ->where('total_price', '>', 100) ->where('shipping_cost', '<', 20) ->where('discount_percent', '>=', 10) ->where('item_count', '<=', 5) ->get();

또한 whereIn 메서드를 사용해 특정 컬럼 값이 주어진 배열에 포함되어 있는지 확인할 수 있습니다.

$orders = Order::search('Star Trek')->whereIn( 'status', ['open', 'paid'] )->get();

반대로 whereNotIn 메서드는 컬럼 값이 주어진 배열에 포함되지 않는지 확인합니다.

$orders = Order::search('Star Trek')->whereNotIn( 'status', ['closed'] )->get();

WARNING

애플리케이션이 Meilisearch를 사용하고 있다면, Scout의 "where" 절을 사용하기 전에 필터 가능한 속성(filterable attributes)을 먼저 설정해야 합니다.

Eloquent 결과 쿼리 커스터마이징

Scout는 검색 엔진에서 일치하는 모델 목록을 가져온 뒤, 해당 기본 키(primary key)를 이용해 Eloquent로 실제 모델들을 조회합니다. 이 조회 쿼리는 query 메서드를 통해 커스터마이징할 수 있습니다. query 메서드는 Eloquent 쿼리 빌더 인스턴스를 인자로 받는 클로저를 받습니다.

use App\Models\Order; use Illuminate\Database\Eloquent\Builder; $orders = Order::search('Star Trek') ->query(fn (Builder $query) => $query->with('invoices')) ->get();

서드파티 엔진을 사용하는 경우, 이 콜백은 검색 엔진에서 이미 관련 모델을 조회한 이후에 실행되므로 결과를 "필터링"하는 용도로 사용해서는 안 됩니다. 필터링이 필요하다면 Scout where 절을 사용하세요. 다만 데이터베이스 엔진을 사용하는 경우에는 query 메서드의 조건이 데이터베이스 쿼리에 직접 적용되므로, 필터링 용도로도 사용할 수 있습니다.

페이지네이션

컬렉션 형태로 결과를 조회하는 것 외에, paginate 메서드를 사용해 검색 결과를 페이지네이션할 수도 있습니다. 이 메서드는 일반 Eloquent 쿼리를 페이지네이션했을 때와 마찬가지로 Illuminate\Pagination\LengthAwarePaginator 인스턴스를 반환합니다.

use App\Models\Order; $orders = Order::search('Star Trek')->paginate();

paginate 메서드의 첫 번째 인자로 개수를 전달하면 페이지당 조회할 모델 수를 지정할 수 있습니다.

$orders = Order::search('Star Trek')->paginate(15);

데이터베이스 엔진을 사용할 경우 simplePaginate 메서드도 사용할 수 있습니다. paginate는 전체 페이지 수를 표시하기 위해 일치하는 레코드의 총 개수를 조회하지만, simplePaginate는 현재 페이지 다음에 결과가 더 있는지 여부만 판단합니다. 따라서 "이전"·"다음" 링크만 필요한 대용량 데이터셋에서는 simplePaginate가 더 효율적입니다.

$orders = Order::search('Star Trek')->simplePaginate(15);

결과를 조회한 뒤에는, 일반 Eloquent 쿼리를 페이지네이션할 때와 마찬가지로 Blade를 사용해 결과를 출력하고 페이지 링크를 렌더링할 수 있습니다.

<div class="container"> @foreach ($orders as $order) {{ $order->price }} @endforeach </div> {{ $orders->links() }}

페이지네이션 결과를 JSON으로 반환하고 싶다면, 라우트나 컨트롤러에서 페이지네이터 인스턴스를 그대로 반환하면 됩니다.

use App\Models\Order; use Illuminate\Http\Request; Route::get('/orders', function (Request $request) { return Order::search($request->input('query'))->paginate(15); });

WARNING

검색 엔진은 Eloquent 모델의 글로벌 스코프 정의를 알지 못하므로, Scout 페이지네이션을 사용하는 애플리케이션에서는 글로벌 스코프를 사용하지 않는 것이 좋습니다. 글로벌 스코프가 꼭 필요하다면, Scout로 검색할 때 해당 스코프의 조건을 직접 다시 구현해 적용해야 합니다.

소프트 삭제(Soft Delete) 처리

인덱싱 대상 모델이 소프트 삭제를 사용하고, 소프트 삭제된 모델도 검색해야 한다면 config/scout.php 설정 파일의 soft_delete 옵션을 true로 설정하세요.

'soft_delete' => true,

이 옵션이 true이면 Scout는 소프트 삭제된 모델을 검색 인덱스에서 제거하지 않고, 대신 인덱싱된 레코드에 숨겨진 __soft_deleted 속성을 설정합니다. 이후 검색 시 withTrashed 또는 onlyTrashed 메서드를 사용해 소프트 삭제된 레코드를 조회할 수 있습니다.

use App\Models\Order; // 삭제된 레코드를 포함해서 결과 조회... $orders = Order::search('Star Trek')->withTrashed()->get(); // 삭제된 레코드만 결과로 조회... $orders = Order::search('Star Trek')->onlyTrashed()->get();

NOTE

소프트 삭제된 모델이 forceDelete로 완전히 삭제되면, Scout는 해당 모델을 검색 인덱스에서 자동으로 제거합니다.

검색 엔진 동작 커스터마이징

엔진의 검색 동작을 세밀하게 커스터마이징해야 한다면, search 메서드의 두 번째 인자로 클로저를 전달할 수 있습니다. 예를 들어, Algolia에 검색 쿼리를 전달하기 전에 이 콜백을 이용해 위치 기반(geo-location) 검색 옵션을 추가할 수 있습니다.

use Algolia\AlgoliaSearch\SearchIndex; use App\Models\Order; Order::search( 'Star Trek', function (SearchIndex $algolia, string $query, array $options) { $options['body']['query']['bool']['filter']['geo_distance'] = [ 'distance' => '1000km', 'location' => ['lat' => 36, 'lon' => 111], ]; return $algolia->search($query, $options); } )->get();

Scout

커스텀 엔진

엔진 작성하기

Scout에 내장된 검색 엔진들이 요구사항에 맞지 않는다면, 직접 커스텀 엔진을 작성해서 Scout에 등록할 수 있습니다. 커스텀 엔진은 Laravel\Scout\Engines\Engine 추상 클래스를 상속받아야 합니다. 이 추상 클래스에는 반드시 구현해야 하는 8개의 메서드가 정의되어 있습니다.

use Laravel\Scout\Builder; abstract public function update($models); abstract public function delete($models); abstract public function search(Builder $builder); abstract public function paginate(Builder $builder, $perPage, $page); abstract public function mapIds($results); abstract public function map(Builder $builder, $results, $model); abstract public function getTotalCount($results); abstract public function flush($model);

각 메서드를 실제로 어떻게 구현해야 할지 감이 잘 안 잡힌다면, Laravel\Scout\Engines\AlgoliaEngine 클래스의 구현을 참고해 보세요. 이 클래스는 자체 엔진을 구현하는 방법을 배우기에 좋은 참고 자료가 되어줄 것입니다.

엔진 등록하기

커스텀 엔진 작성을 마쳤다면, Scout 엔진 매니저의 extend 메서드를 사용해서 애플리케이션에 등록할 수 있습니다. Scout의 엔진 매니저는 라라벨 서비스 컨테이너를 통해 resolve할 수 있습니다. extend 메서드는 App\Providers\AppServiceProvider 클래스의 boot 메서드에서 호출하거나, 애플리케이션에서 사용 중인 다른 서비스 프로바이더의 boot 메서드에서 호출하면 됩니다.

use App\ScoutExtensions\MySqlSearchEngine; use Laravel\Scout\EngineManager; /** * 애플리케이션의 서비스를 부트스트랩합니다. */ public function boot(): void { resolve(EngineManager::class)->extend('mysql', function () { return new MySqlSearchEngine; }); }

엔진을 등록한 후에는, 애플리케이션의 config/scout.php 설정 파일에서 이 엔진을 기본 Scout driver로 지정할 수 있습니다.

'driver' => 'mysql',

이 문서는 Laravel 공식 문서(MIT)를 한국 개발자를 위해 번역·재구성한 것입니다.

번역일: 2026년 9월 23일