Laravel Scout

번역일: 2026년 6월 27일

Laravel Scout

소개

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

현재 Scout가 기본으로 지원하는 드라이버는 Algolia, Meilisearch, Typesense, 그리고 MySQL / PostgreSQL(database) 드라이버입니다. 별도의 외부 서비스 없이 로컬에서 간단히 테스트하고 싶다면 database 드라이버로 시작해도 좋습니다. 또한 커스텀 드라이버를 직접 작성해 Scout를 원하는 검색 엔진과 연동할 수도 있습니다.

Laravel Scout

소개

Laravel ScoutEloquent 모델에 전문 검색(full-text search) 기능을 손쉽게 추가할 수 있는 드라이버 기반 패키지입니다. 모델 옵저버를 활용해 Eloquent 레코드가 변경될 때마다 검색 인덱스를 자동으로 동기화해 줍니다.

현재 Scout는 Algolia, Meilisearch, Typesense, 그리고 MySQL / PostgreSQL(database) 드라이버를 기본으로 제공합니다. 외부 서비스 없이 로컬 개발 환경에서 바로 사용할 수 있는 "collection" 드라이버도 포함되어 있어, 별도 설정 없이 검색 기능을 빠르게 테스트해 볼 수 있습니다. 또한 커스텀 드라이버를 직접 작성해 Scout를 원하는 방식으로 확장하는 것도 간단합니다.

Laravel Scout

설치

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

composer require laravel/scout

설치 후, vendor:publish Artisan 명령어로 Scout 설정 파일을 퍼블리시합니다. 이 명령어는 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; }

큐 설정

Scout를 사용하는 데 큐가 반드시 필요하지는 않지만, 사용 전에 큐 드라이버를 설정해 두는 것을 강력히 권장합니다. 큐 워커를 실행하면 모델 정보를 검색 인덱스에 동기화하는 작업이 백그라운드에서 처리되므로, 웹 인터페이스의 응답 속도가 훨씬 빨라집니다.

큐 드라이버를 설정했다면, config/scout.phpqueue 옵션을 true로 변경합니다:

'queue' => true,

NOTE

queue 옵션이 false로 설정되어 있더라도, Algolia나 Meilisearch 같은 일부 Scout 드라이버는 항상 레코드를 비동기로 인덱싱합니다. 즉, Laravel 애플리케이션에서 인덱싱 작업이 완료되었더라도 검색 엔진 자체에는 새로운 데이터나 변경 사항이 즉시 반영되지 않을 수 있습니다.

Scout Job이 사용할 커넥션과 큐를 직접 지정하고 싶다면, queue 옵션을 배열로 정의할 수 있습니다:

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

이렇게 커넥션과 큐를 직접 지정한 경우, 해당 커넥션과 큐에서 Job을 처리하는 큐 워커를 별도로 실행해야 합니다:

php artisan queue:work redis --queue=scout

드라이버 사전 요구사항

Algolia

Algolia 드라이버를 사용하려면 먼저 config/scout.php 설정 파일에 Algolia idsecret 자격 증명을 입력해야 합니다. 자격 증명을 설정한 후, 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/meilisearch-php 버전을 사용하고 있는지 반드시 확인하세요. 버전 호환성 정보는 Meilisearch PHP SDK의 호환성 문서에서 확인할 수 있습니다.

WARNING

Meilisearch를 사용하는 애플리케이션에서 Scout를 업그레이드할 때는 반드시 Meilisearch 서비스 자체의 주요 변경사항을 먼저 검토하세요.

Typesense

Typesense는 매우 빠른 오픈소스 검색 엔진으로, 키워드 검색, 시맨틱 검색, 지리 검색, 벡터 검색을 모두 지원합니다.

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 공식 문서를 참고하세요.

Typesense 저장을 위한 데이터 준비

Typesense를 사용할 때는 검색 가능한 모델에 반드시 toSearchableArray 메서드를 정의해야 합니다. 이 메서드에서 모델의 기본 키는 문자열로, 생성일시는 UNIX 타임스탬프로 형변환해야 합니다:

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

Typesense 컬렉션 스키마는 config/scout.php 파일에 정의합니다. 스키마는 Typesense에서 검색 가능한 각 필드의 데이터 타입을 기술합니다. 사용 가능한 스키마 옵션에 대한 자세한 내용은 Typesense 문서를 참고하세요.

스키마를 정의한 이후 변경이 필요하다면, scout:flushscout:import 명령어를 순서대로 실행하여 기존 인덱스 데이터를 모두 삭제하고 스키마를 새로 생성할 수 있습니다. 또는 인덱스 데이터를 삭제하지 않고 Typesense API를 직접 사용해 스키마를 수정하는 방법도 있습니다.

검색 가능한 모델이 소프트 삭제(soft delete)를 사용하는 경우, config/scout.php 설정 파일의 해당 Typesense 스키마에 __soft_deleted 필드를 추가로 정의해야 합니다:

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

동적 검색 파라미터

Typesense에서는 검색 수행 시 options 메서드를 통해 검색 파라미터를 동적으로 지정할 수 있습니다:

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

설정

모델 인덱스 설정

각 Eloquent 모델은 해당 모델의 검색 가능한 레코드를 저장하는 "인덱스"와 동기화됩니다. 인덱스는 MySQL의 테이블과 유사한 개념으로 이해하면 됩니다. 기본적으로 각 모델은 모델 이름의 복수형(테이블명)과 동일한 이름의 인덱스에 저장됩니다. 인덱스 이름을 직접 지정하려면 모델에서 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'; } }

검색 가능한 데이터 설정

기본적으로 모델의 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; } }

Meilisearch 같은 검색 엔진은 비교 연산자(>, < 등)를 사용할 때 데이터 타입이 정확히 일치해야 합니다. 따라서 검색 가능한 데이터를 커스터마이징할 때는 숫자 값을 올바른 타입으로 명시적으로 캐스팅해야 합니다:

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

인덱스 설정 (Algolia)

Algolia 인덱스에 필터링 가능한 속성, 랭킹, 패싯 등 추가 설정이 필요한 경우, Algolia 대시보드에서 직접 관리하는 것도 가능하지만, 애플리케이션의 config/scout.php 설정 파일에서 관리하는 것이 더 효율적입니다.

이 방식은 배포 파이프라인을 통해 설정을 자동 반영할 수 있어, 수동 설정 오류를 줄이고 여러 환경(개발·스테이징·운영) 간 일관성을 유지하는 데 유리합니다. Algolia가 지원하는 모든 설정 항목을 사용할 수 있습니다.

config/scout.phpalgolia 항목에 각 인덱스별 설정을 추가하세요:

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'], ], ], ],

index-settings 배열에 포함된 모델이 소프트 삭제를 지원하는 경우, Scout는 해당 인덱스에서 소프트 삭제된 모델에 대한 패싯 필터링을 자동으로 활성화합니다. 소프트 삭제 가능한 모델에 별도로 지정할 패싯 속성이 없다면, 빈 항목만 추가해도 됩니다:

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

설정을 마쳤으면 scout:sync-index-settings Artisan 명령어를 실행하여 Algolia에 설정을 반영하세요. 이 명령어는 배포 프로세스에 포함시켜 자동으로 실행되도록 구성하는 것을 권장합니다:

php artisan scout:sync-index-settings

필터링 가능한 데이터 및 인덱스 설정 (Meilisearch)

Algolia와 달리, Meilisearch는 검색 전에 필터링 가능한 속성(filterable attributes), 정렬 가능한 속성(sortable attributes) 등의 인덱스 설정을 미리 정의해야 합니다.

  • filterableAttributes: Scout의 where 메서드로 필터링할 속성
  • sortableAttributes: Scout의 orderBy 메서드로 정렬할 속성

config/scout.phpmeilisearch 항목에서 index-settings를 아래와 같이 설정하세요. Meilisearch가 지원하는 전체 설정 항목도 함께 참고하세요:

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' => [ Flight::class => [] ],

설정 후에는 scout:sync-index-settings Artisan 명령어를 실행하여 Meilisearch에 설정을 반영하세요. 이 명령어 역시 배포 파이프라인에 포함시키는 것을 권장합니다:

php artisan scout:sync-index-settings

모델 ID 설정

Scout는 기본적으로 모델의 기본 키(primary key)를 검색 인덱스의 고유 식별자로 사용합니다. 이를 변경하려면 모델에서 getScoutKeygetScoutKeyName 메서드를 오버라이드하세요:

<?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'; } }

모델별 검색 엔진 설정

Scout는 기본적으로 scout 설정 파일에 지정된 기본 검색 엔진을 사용합니다. 특정 모델에 다른 검색 엔진을 사용하려면 모델에서 searchableUsing 메서드를 오버라이드하세요:

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

사용자 식별 (Algolia)

Algolia를 사용할 때, Scout는 인증된 사용자를 검색 작업과 자동으로 연결할 수 있습니다. 이 기능을 활성화하면 Algolia 대시보드의 검색 분석에서 사용자별 검색 행동을 확인할 수 있어 유용합니다.

.env 파일에 다음 환경 변수를 추가하여 활성화하세요:

SCOUT_IDENTIFY=true

이 기능을 활성화하면 요청의 IP 주소와 인증된 사용자의 기본 키가 Algolia로 함께 전송되어, 해당 사용자의 모든 검색 요청에 연결됩니다.

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

데이터베이스 엔진

WARNING

데이터베이스 엔진은 현재 MySQL과 PostgreSQL만 지원합니다.

규모가 작거나 중간 정도인 데이터베이스를 사용하는 애플리케이션이라면, 별도의 외부 검색 서비스 없이 Scout의 "database" 엔진을 활용하는 것이 간편할 수 있습니다. 데이터베이스 엔진은 기존 데이터베이스에서 결과를 필터링할 때 WHERE LIKE 구문과 전문 검색(full text) 인덱스를 사용합니다.

데이터베이스 엔진을 사용하려면 .env 파일에서 SCOUT_DRIVER 환경 변수를 database로 설정하거나, scout 설정 파일에서 직접 드라이버를 지정하면 됩니다.

SCOUT_DRIVER=database

드라이버를 설정했다면 검색 대상 데이터 구성을 완료한 뒤, 바로 모델에 대해 검색 쿼리를 실행할 수 있습니다. 데이터베이스 엔진을 사용하면 Algolia, Meilisearch, Typesense처럼 별도로 인덱스를 생성하고 데이터를 동기화하는 작업이 필요하지 않습니다.

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

기본적으로 데이터베이스 엔진은 검색 가능하도록 설정된 모든 모델 속성에 대해 WHERE LIKE '%검색어%' 방식으로 쿼리를 실행합니다. 그러나 컬럼 수가 많거나 데이터량이 클 경우 성능 저하가 발생할 수 있습니다.

이를 개선하기 위해 특정 컬럼에 PHP 어트리뷰트(attribute)를 지정하여 검색 전략을 세분화할 수 있습니다.

  • #[SearchUsingPrefix] — 문자열 앞부분만 검색(example%)
  • #[SearchUsingFullText] — 전문 검색(full text) 인덱스 활용

어트리뷰트를 지정하지 않은 컬럼은 기본 WHERE 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

#[SearchUsingFullText]를 사용하기 전에 해당 컬럼에 전문 검색 인덱스가 생성되어 있는지 반드시 확인하세요.

컬렉션 엔진

로컬 개발 환경에서 Algolia, Meilisearch, Typesense를 직접 구성하기 번거롭다면 "collection" 엔진이 유용합니다. 컬렉션 엔진은 데이터베이스에서 레코드를 조회한 뒤, Laravel의 컬렉션 필터링을 통해 검색 결과를 결정합니다. 별도로 인덱스를 만들거나 데이터를 동기화할 필요가 없으며, 로컬 데이터베이스에서 바로 데이터를 가져옵니다.

컬렉션 엔진을 사용하려면 SCOUT_DRIVERcollection으로 설정하세요.

SCOUT_DRIVER=collection

드라이버를 지정하고 나면 외부 인덱싱 작업 없이 곧바로 검색 쿼리를 실행할 수 있습니다.

데이터베이스 엔진과의 차이점

데이터베이스 엔진과 컬렉션 엔진은 언뜻 비슷해 보이지만 내부 동작 방식에 중요한 차이가 있습니다.

항목데이터베이스 엔진컬렉션 엔진
검색 방식WHERE LIKE, 전문 검색 인덱스전체 레코드 로드 후 Str::is()로 필터링
전문 검색 인덱스 활용✅ 지원❌ 미지원
지원 데이터베이스MySQL, PostgreSQLLaravel이 지원하는 모든 RDBMS (SQLite, SQL Server 포함)
성능비교적 효율적데이터가 많을수록 비효율적

컬렉션 엔진은 SQLite, SQL Server를 포함한 Laravel 지원 모든 데이터베이스에서 동작하므로 이식성이 가장 높습니다. 다만 레코드 전체를 메모리에 올린 뒤 PHP 단에서 필터링하기 때문에, 데이터 규모가 커지면 데이터베이스 엔진보다 성능이 떨어질 수 있습니다.

NOTE

정리하면, 로컬 개발이나 빠른 프로토타이핑에는 컬렉션 엔진, MySQL/PostgreSQL 기반의 실제 서비스 환경에서 외부 검색 서버 없이 Scout를 쓰고 싶다면 데이터베이스 엔진을 선택하는 것이 좋습니다.

인덱싱

일괄 가져오기 (Batch Import)

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

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

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

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

가져오기 쿼리 수정

일괄 가져오기 시 모델을 조회하는 쿼리를 커스터마이징하려면 모델에 makeAllSearchableUsing 메서드를 정의하세요. 예를 들어, 인덱싱 전에 연관 관계를 미리 로드(eager load)해야 하는 경우 이 메서드에서 처리하면 됩니다:

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 쿼리에 searchable 메서드를 체이닝하면 쿼리 결과를 검색 인덱스에 추가할 수 있습니다. searchable 메서드는 결과를 청크 단위로 분할하여 인덱스에 추가합니다. 큐를 사용하도록 설정했다면 각 청크는 큐 워커가 백그라운드에서 처리합니다:

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();

또는 이미 메모리에 로드된 컬렉션이 있다면 컬렉션 인스턴스에서 직접 호출할 수 있습니다:

$orders->searchable();

인덱싱 전 레코드 전처리

모델을 검색 가능하게 만들기 전에 컬렉션을 미리 준비해야 하는 경우가 있습니다. 예를 들어, 연관 데이터를 효율적으로 인덱스에 포함시키기 위해 관계를 미리 로드하고 싶을 때, 모델에 makeSearchableUsing 메서드를 정의하면 됩니다:

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

레코드 제거

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

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

모델을 먼저 조회하지 않고 바로 인덱스에서 제거하려면, Eloquent 쿼리 인스턴스에 unsearchable 메서드를 사용하세요:

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

연관 관계에 속한 모델들의 인덱스 레코드를 일괄 제거하려면 관계 인스턴스에서 unsearchable을 호출하세요:

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

이미 메모리에 로드된 컬렉션이 있다면 컬렉션 인스턴스에서 직접 호출할 수도 있습니다:

$orders->unsearchable();

해당 모델의 모든 레코드를 인덱스에서 한 번에 제거하려면 removeAllFromSearch 메서드를 사용하세요:

Order::removeAllFromSearch();

인덱싱 일시 중단

여러 Eloquent 작업을 수행하는 동안 검색 인덱스와의 동기화를 잠시 멈춰야 할 때가 있습니다. withoutSyncingToSearch 메서드를 사용하면 클로저 내부에서 발생하는 모든 모델 작업이 인덱스에 반영되지 않습니다:

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

조건부 검색 가능 모델

특정 조건을 만족하는 경우에만 모델을 검색 가능하게 만들어야 할 때가 있습니다. 예를 들어, App\Models\Post 모델이 "임시저장(draft)"과 "게시됨(published)" 두 가지 상태를 가진다면, "게시됨" 상태의 게시물만 검색 인덱스에 포함시키고 싶을 수 있습니다. 이럴 때 모델에 shouldBeSearchable 메서드를 정의하세요:

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

shouldBeSearchable 메서드는 save, create 메서드, 쿼리, 또는 연관 관계를 통해 모델을 조작할 때만 적용됩니다. 컬렉션이나 모델 인스턴스에서 직접 searchable 메서드를 호출하면 shouldBeSearchable의 반환값과 무관하게 인덱스에 등록됩니다.

WARNING

Scout의 "database" 엔진을 사용하는 경우에는 shouldBeSearchable 메서드가 적용되지 않습니다. 데이터베이스 엔진은 모든 검색 데이터를 항상 데이터베이스에 저장하기 때문입니다. 데이터베이스 엔진에서 유사한 동작을 구현하려면 where 절을 사용하세요.

Laravel Scout - 검색

검색

search 메서드를 사용해 모델을 검색할 수 있습니다. 이 메서드는 검색어 문자열 하나를 인수로 받으며, 이어서 get 메서드를 체이닝하면 조건에 맞는 Eloquent 모델 컬렉션을 반환합니다.

use App\Models\Order; $orders = Order::search('노트북')->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) 검색 결과가 필요한 경우에는 raw 메서드를 사용하세요.

$orders = Order::search('노트북')->raw();

커스텀 인덱스

검색 쿼리는 기본적으로 모델의 searchableAs 메서드에서 지정한 인덱스를 대상으로 실행됩니다. 다른 인덱스를 사용하고 싶다면 within 메서드로 직접 지정할 수 있습니다.

$orders = Order::search('노트북') ->within('products_popularity_desc') ->get();

Where 절

Scout는 검색 쿼리에 간단한 "where" 조건을 추가하는 기능을 제공합니다. 현재는 기본적인 숫자 동등 비교만 지원하며, 특정 사용자 소유의 데이터로 검색 범위를 좁히는 용도에 주로 활용됩니다.

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

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

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

whereNotIn 메서드는 반대로, 주어진 배열에 포함되지 않는 레코드를 필터링합니다.

$orders = Order::search('노트북')->whereNotIn( 'status', ['closed'] )->get();

검색 인덱스는 관계형 데이터베이스가 아니므로, 더 복잡한 "where" 절은 현재 지원되지 않습니다.

WARNING

Meilisearch를 사용하는 경우, Scout의 "where" 절을 활용하기 전에 반드시 필터링 가능한 속성(filterable attributes)을 먼저 설정해야 합니다.

페이지네이션

모델 컬렉션을 한 번에 가져오는 것 외에도, paginate 메서드를 사용해 검색 결과를 페이지 단위로 나눌 수 있습니다. 이 메서드는 일반 Eloquent 쿼리의 페이지네이션과 동일하게 Illuminate\Pagination\LengthAwarePaginator 인스턴스를 반환합니다.

use App\Models\Order; $orders = Order::search('노트북')->paginate();

페이지당 표시할 모델 수는 paginate 메서드의 첫 번째 인수로 지정합니다.

$orders = Order::search('노트북')->paginate(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 검색 시 글로벌 스코프와 동일한 조건을 직접 재현해야 합니다.

소프트 삭제

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

'soft_delete' => true,

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

use App\Models\Order; // 소프트 삭제된 레코드를 포함하여 결과 조회 $orders = Order::search('노트북')->withTrashed()->get(); // 소프트 삭제된 레코드만 조회 $orders = Order::search('노트북')->onlyTrashed()->get();

NOTE

forceDelete로 모델을 영구 삭제하면, Scout가 해당 레코드를 검색 인덱스에서 자동으로 제거합니다.

엔진 검색 커스터마이징

검색 엔진의 동작을 세밀하게 제어해야 할 경우, search 메서드의 두 번째 인수로 클로저를 전달할 수 있습니다. 예를 들어, 아래 코드는 Algolia로 검색 쿼리가 전달되기 전에 위치 기반 필터 옵션을 추가하는 예시입니다.

use Algolia\AlgoliaSearch\SearchIndex; use App\Models\Order; Order::search( '노트북', function (SearchIndex $algolia, string $query, array $options) { $options['body']['query']['bool']['filter']['geo_distance'] = [ 'distance' => '500km', 'location' => ['lat' => 37.5665, 'lon' => 126.9780], // 서울 ]; return $algolia->search($query, $options); } )->get();

Eloquent 결과 쿼리 커스터마이징

Scout가 검색 엔진에서 일치하는 모델의 기본 키 목록을 가져온 후, Eloquent를 통해 해당 모델들을 실제로 조회합니다. 이 Eloquent 쿼리를 커스터마이징하려면 query 메서드를 사용하세요. query 메서드는 Eloquent 쿼리 빌더 인스턴스를 인수로 받는 클로저를 인수로 받습니다.

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

이 콜백은 검색 엔진에서 관련 모델을 이미 가져온 후에 실행되므로, query 메서드는 결과를 필터링하는 용도로 사용해서는 안 됩니다. 결과 필터링이 필요하다면 Scout where 절을 사용하세요.

Laravel 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의 엔진 매니저(EngineManager)가 제공하는 extend 메서드를 사용해 등록합니다. EngineManager는 Laravel 서비스 컨테이너에서 해석할 수 있으며, extend 메서드 호출은 App\Providers\AppServiceProviderboot 메서드 또는 애플리케이션에서 사용하는 다른 서비스 프로바이더에서 처리하는 것이 일반적입니다.

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

엔진을 등록한 후에는 config/scout.php 설정 파일에서 해당 엔진을 기본 driver로 지정하면 됩니다.

'driver' => 'mysql',

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

번역일: 2026년 6월 27일