> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Laravel Scout

> Eloquent 모델에 전문 검색 기능을 추가하는 공식 패키지. Meilisearch·Algolia·Typesense·데이터베이스 엔진에 대응하고, 모델 옵저버에 의해 인덱스를 자동 동기합니다.

## 시작하기

**Laravel Scout**는 [Eloquent 모델](/ko/eloquent)에 전문 검색 기능을 추가하기 위한, 심플한 드라이버 기반의 솔루션입니다. 모델 옵저버를 사용해, Eloquent 레코드와 검색 인덱스를 자동적으로 동기합니다.

Scout에는, MySQL / PostgreSQL의 전문 인덱스와 `LIKE` 절을 사용해 데이터베이스를 직접 검색하는 조립된 `database` 엔진이 포함되어 있어, 외부 서비스는 불필요합니다. 대규모의 프로덕션 환경에서 오타 허용·패싯 검색·지오 서치가 필요한 경우는, 외부 엔진이 도움이 됩니다.

### 대응 엔진 일람

| 엔진           | 특징                         | 용도        |
| ------------ | -------------------------- | --------- |
| `database`   | MySQL / PostgreSQL의 전문 인덱스 | 대부분의 앱    |
| `collection` | PHP로 필터링 (SQLite도 가능)      | 로컬 개발·테스트 |
| Meilisearch  | 오픈 소스, 오타 허용, 고속           | 프로덕션 환경   |
| Algolia      | 클라우드 SaaS, 고도의 기능          | 프로덕션 환경   |
| Typesense    | 오픈 소스, 벡터 검색 대응            | 프로덕션 환경   |

```mermaid theme={null}
flowchart LR
  APP["Laravel 앱<br>(Eloquent 모델)"] -->|"save / delete"| SCOUT["Laravel Scout<br>(모델 옵저버)"]
  SCOUT -->|"인덱스 동기"| ENGINE["검색 엔진<br>Meilisearch / Algolia / Typesense"]
  USER["사용자"] -->|"Model::search()"| APP
  ENGINE -->|"검색 결과"| APP
```

## 설치

Composer로 패키지를 설치합니다.

```shell theme={null}
composer require laravel/scout
```

설치 후, `vendor:publish` 명령으로 설정 파일을 공개합니다. `config/scout.php`가 생성됩니다.

```shell theme={null}
php artisan vendor:publish --provider="Laravel\Scout\ScoutServiceProvider"
```

마지막으로, 검색 가능하게 하고 싶은 모델에 `Laravel\Scout\Searchable` 트레이트를 추가합니다. 이 트레이트가 모델 옵저버를 등록하고, 검색 드라이버와의 자동 동기를 활성화합니다.

```php theme={null}
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Laravel\Scout\Searchable;

class Post extends Model
{
    use Searchable;
}
```

### 큐 설정

`database` 또는 `collection` 엔진 이외를 사용하는 경우, Scout를 사용하기 전에 [큐 드라이버](/ko/queues)의 설정을 강력히 권장합니다. 큐 워커를 동작시킴으로써, 인덱스 동기 조작이 백그라운드에서 실행되고, 웹 인터페이스의 응답 속도가 대폭 향상됩니다.

`config/scout.php`의 `queue` 옵션을 `true`로 설정합니다.

```php theme={null}
'queue' => true,
```

접속명과 큐명을 지정할 수도 있습니다.

```php theme={null}
'queue' => [
    'connection' => 'redis',
    'queue' => 'scout'
],
```

설정 후, 전용의 큐 워커를 기동합니다.

```shell theme={null}
php artisan queue:work redis --queue=scout
```

### 유니크한 잡의 사용

쓰기가 많은 애플리케이션에서는, 같은 모델 레코드에 대한 중복된 큐 잡이 큐에 등록되는 것을 막고 싶은 경우가 있습니다. `config/scout.php`에서 `MakeSearchableUniquely`와 `RemoveFromSearchUniquely` 잡 클래스를 등록함으로써, 유니크한 인덱스 잡을 사용할 수 있습니다. 통상, 이것은 서비스 프로바이더의 `boot` 메서드에서 설정합니다.

```php theme={null}
use Laravel\Scout\Jobs\MakeSearchableUniquely;
use Laravel\Scout\Jobs\RemoveFromSearchUniquely;
use Laravel\Scout\Scout;

Scout::makeSearchableUsing(MakeSearchableUniquely::class);
Scout::removeFromSearchUsing(RemoveFromSearchUniquely::class);
```

이러한 잡은 Laravel의 [유니크한 잡 락](/ko/queues#unique-jobs)을 사용해, 이미 큐에 등록되어 있는 같은 모델 레코드의 중복된 인덱스 조작을 디스패치하는 것을 막습니다.

## 드라이버의 전제 조건

### Algolia

Algolia 드라이버를 사용하는 경우는, `config/scout.php`에 `id`와 `secret`의 인증 정보를 설정하고, Algolia PHP SDK를 설치합니다.

```shell theme={null}
composer require algolia/algoliasearch-client-php
```

`.env` 파일에 인증 정보를 추가합니다.

```ini theme={null}
ALGOLIA_APP_ID=your-app-id
ALGOLIA_SECRET=your-secret-key
```

#### 인덱스 설정

Algolia에서는 `config/scout.php`에서 인덱스 설정을 관리할 수 있습니다.

```php theme={null}
use App\Models\User;

'algolia' => [
    'id' => env('ALGOLIA_APP_ID', ''),
    'secret' => env('ALGOLIA_SECRET', ''),
    'index-settings' => [
        User::class => [
            'searchableAttributes' => ['id', 'name', 'email'],
            'attributesForFaceting' => ['filterOnly(email)'],
        ],
    ],
],
```

설정 후, `scout:sync-index-settings` 명령을 실행해 Algolia에 설정을 반영시킵니다.

```shell theme={null}
php artisan scout:sync-index-settings
```

### Meilisearch

[Meilisearch](https://www.meilisearch.com)는 고속의 오픈 소스 검색 엔진입니다. 로컬 개발에서는 [Laravel Sail](https://laravel.com/docs/sail#meilisearch)의 Docker를 사용하는 것이 가장 간단합니다.

```shell theme={null}
# Laravel Sail을 사용하는 경우
./vendor/bin/sail up -d meilisearch
```

Sail을 사용하지 않는 경우는, Docker로 직접 기동할 수 있습니다.

```shell theme={null}
docker run -it --rm \
    -p 7700:7700 \
    getmeili/meilisearch:latest \
    meilisearch --master-key="masterKey"
```

Meilisearch PHP SDK를 설치합니다.

```shell theme={null}
composer require meilisearch/meilisearch-php http-interop/http-factory-guzzle
```

`.env` 파일에 드라이버와 호스트를 설정합니다.

```ini theme={null}
SCOUT_DRIVER=meilisearch
MEILISEARCH_HOST=http://127.0.0.1:7700
MEILISEARCH_KEY=masterKey
```

<Warning>
  Scout를 업그레이드할 때는, Meilisearch 서비스 자체의 [파괴적 변경](https://github.com/meilisearch/Meilisearch/releases)도 반드시 확인해 주세요.
</Warning>

#### 인덱스 설정 (Meilisearch)

Meilisearch에서는 `where()`로 필터링하는 컬럼을 `filterableAttributes`에, `orderBy()`로 정렬하는 컬럼을 `sortableAttributes`에 사전 등록할 필요가 있습니다.

```php theme={null}
use App\Models\User;

'meilisearch' => [
    'host' => env('MEILISEARCH_HOST', 'http://localhost:7700'),
    'key' => env('MEILISEARCH_KEY', null),
    'index-settings' => [
        User::class => [
            'filterableAttributes' => ['id', 'name', 'email'],
            'sortableAttributes' => ['created_at'],
        ],
    ],
],
```

수치 컬럼의 데이터 타입에 주의가 필요합니다. Meilisearch는 올바른 타입의 데이터에만 필터 조작(`>`, `<` 등)을 실행할 수 있습니다.

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

설정 후, `scout:sync-index-settings` 명령을 실행합니다.

```shell theme={null}
php artisan scout:sync-index-settings
```

### Typesense

[Typesense](https://typesense.org)는 고속의 오픈 소스 검색 엔진으로, 키워드 검색·시맨틱 검색·지오 검색·벡터 검색에 대응하고 있습니다.

```shell theme={null}
composer require typesense/typesense-php
```

`.env` 파일에 접속 정보를 설정합니다.

```ini theme={null}
SCOUT_DRIVER=typesense
TYPESENSE_API_KEY=masterKey
TYPESENSE_HOST=localhost
TYPESENSE_PORT=8108
TYPESENSE_PATH=
TYPESENSE_PROTOCOL=http
```

Typesense를 사용하는 경우, `toSearchableArray` 메서드로 모델의 주 키를 문자열로, 작성 일시를 UNIX 타임스탬프로 캐스트할 필요가 있습니다.

```php theme={null}
public function toSearchableArray(): array
{
    return array_merge($this->toArray(), [
        'id' => (string) $this->id,
        'created_at' => $this->created_at->timestamp,
    ]);
}
```

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

외부 서비스 없이 검색을 추가하고 싶은 경우에 최적의 옵션입니다.

**데이터베이스 엔진**은 MySQL / PostgreSQL의 전문 인덱스와 `LIKE` 절을 사용합니다. 대부분의 애플리케이션에서 이것으로 충분합니다.

```ini theme={null}
SCOUT_DRIVER=database
```

**컬렉션 엔진**은 PHP로 필터링하기 때문에, SQLite를 포함한 Laravel이 지원하는 모든 데이터베이스에서 동작합니다. 로컬 개발·테스트·소규모 데이터셋용입니다.

```ini theme={null}
SCOUT_DRIVER=collection
```

<Info>
  데이터베이스 엔진에서는 외부 엔진과 달리 인덱스의 수동 관리는 불필요합니다. 데이터베이스 테이블을 직접 검색합니다.
</Info>

## Searchable 트레이트

### toSearchableArray()의 커스터마이즈

기본적으로, 모델의 `toArray()`의 전 데이터가 검색 인덱스에 보존됩니다. 인덱스에 동기하는 데이터를 커스터마이즈하려면, `toSearchableArray` 메서드를 오버라이드합니다.

```php theme={null}
<?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;
    }
}
```

### 인덱스명의 커스터마이즈

기본적으로, 모델의 테이블명(복수형)이 인덱스명으로서 사용됩니다. `searchableAs` 메서드를 오버라이드해 커스터마이즈할 수 있습니다.

```php theme={null}
public function searchableAs(): string
{
    return 'posts_index';
}
```

### 데이터베이스 엔진용의 검색 전략

데이터베이스 엔진에서는, 컬럼별로 효율적인 검색 전략을 PHP 애트리뷰트로 지정할 수 있습니다.

```php theme={null}
use Laravel\Scout\Attributes\SearchUsingFullText;
use Laravel\Scout\Attributes\SearchUsingPrefix;

#[SearchUsingPrefix(['id', 'email'])]
#[SearchUsingFullText(['bio'])]
public function toSearchableArray(): array
{
    return [
        'id' => $this->id,
        'name' => $this->name,
        'email' => $this->email,
        'bio' => $this->bio,
    ];
}
```

<Warning>
  `SearchUsingFullText`를 사용하기 전에, 대상 컬럼에 [전문 인덱스](/ko/migrations)가 부여되어 있는지 확인해 주세요.
</Warning>

### 조건부로 검색 가능하게 하기

특정한 조건하에서만 모델을 검색 가능하게 하고 싶은 경우는, `shouldBeSearchable` 메서드를 정의합니다.

```php theme={null}
/**
 * 이 모델을 검색 가능하게 해야 할지 판단합니다.
 */
public function shouldBeSearchable(): bool
{
    return $this->isPublished();
}
```

<Warning>
  `shouldBeSearchable`는 데이터베이스 엔진에서는 기능하지 않습니다. 데이터베이스 엔진에서 같은 동작을 실현하려면, [where 절](#필터링과-정렬)을 사용해 주세요.
</Warning>

## 인덱스의 관리

<Info>
  이 섹션의 명령은, 주로 Algolia·Meilisearch·Typesense 등의 서드파티 엔진을 사용하는 경우에 관계합니다. 데이터베이스 엔진에서는 인덱스 관리는 불필요합니다.
</Info>

### 기존 레코드의 임포트

기존 프로젝트에 Scout를 도입하는 경우, `scout:import` 명령으로 기존 레코드를 인덱스에 임포트합니다.

```shell theme={null}
php artisan scout:import "App\Models\Post"
```

큐를 사용해 백그라운드에서 임포트할 수도 있습니다.

```shell theme={null}
php artisan scout:queue-import "App\Models\Post" --chunk=500
```

### 인덱스의 클리어

모델의 모든 레코드를 검색 인덱스로부터 삭제하려면 `scout:flush`를 사용합니다.

```shell theme={null}
php artisan scout:flush "App\Models\Post"
```

### 인덱스의 일시 정지

Eloquent 조작 중에 검색 인덱스와의 동기를 일시적으로 멈추고 싶은 경우는 `withoutSyncingToSearch`를 사용합니다.

```php theme={null}
use App\Models\Order;

Order::withoutSyncingToSearch(function () {
    // 이 안의 모델 조작은 검색 인덱스에 동기되지 않는다
});
```

### 레코드의 수동 추가·삭제

쿼리를 사용해 모델의 컬렉션을 인덱스에 추가할 수 있습니다.

```php theme={null}
// 쿼리 결과를 추가
Order::where('price', '>', 100)->searchable();

// 릴레이션 경유로 추가
$user->orders()->searchable();
```

인덱스에서 레코드를 삭제하려면 `unsearchable`을 사용합니다.

```php theme={null}
Order::where('price', '>', 100)->unsearchable();
```

모델을 `delete`하면, 인덱스에서도 자동적으로 삭제됩니다.

## 검색

`search` 메서드로 모델을 검색합니다. `get`을 연결해 Eloquent 모델의 컬렉션을 취득합니다.

```php theme={null}
use App\Models\Order;

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

컨트롤러나 라우트에서 직접 반환하면 JSON으로 자동 변환됩니다.

```php theme={null}
use Illuminate\Http\Request;

Route::get('/search', function (Request $request) {
    return Order::search($request->search)->get();
});
```

생 검색 결과가 필요한 경우는 `raw` 메서드를 사용합니다.

```php theme={null}
$orders = Order::search('Star Trek')->raw();
```

### 페이지네이션

`paginate` 메서드로 검색 결과를 페이지네이션할 수 있습니다. 통상의 Eloquent 쿼리의 페이지네이션과 마찬가지로 기능합니다.

```php theme={null}
$orders = Order::search('Star Trek')->paginate();

// 1페이지당 건수를 지정
$orders = Order::search('Star Trek')->paginate(15);
```

데이터베이스 엔진에서는 `simplePaginate`도 사용할 수 있습니다. 총 건수를 취득하지 않기 때문에 대규모 데이터셋에서 효율적입니다.

```php theme={null}
$orders = Order::search('Star Trek')->simplePaginate(15);
```

Blade 템플릿에서의 표시 예:

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

{{ $orders->links() }}
```

## 필터링과 정렬

`where` 메서드로 검색 쿼리에 필터 조건을 추가할 수 있습니다.

```php theme={null}
use App\Models\Order;

// 등가 필터
$orders = Order::search('Star Trek')->where('user_id', 1)->get();

// 배열 내의 어느 하나에 일치
$orders = Order::search('Star Trek')->whereIn('status', ['open', 'paid'])->get();

// 배열 내의 어느 것에도 일치하지 않음
$orders = Order::search('Star Trek')->whereNotIn('status', ['closed'])->get();
```

<Warning>
  Meilisearch를 사용하는 경우, `where`를 사용하기 전에 [필터링 가능한 속성](#인덱스-설정-meilisearch)을 설정할 필요가 있습니다.
</Warning>

`query` 메서드로 Eloquent 쿼리를 커스터마이즈할 수도 있습니다.

```php theme={null}
use Illuminate\Database\Eloquent\Builder;

$orders = Order::search('Star Trek')
    ->query(fn (Builder $query) => $query->with('invoices'))
    ->get();
```

## Eager Loading

Scout를 사용하면, 검색 엔진으로부터 ID의 일람을 취득한 후, Eloquent로 모델을 취득합니다. N+1 문제를 피하려면, `query` 메서드에서 `with()`을 사용한 Eager Loading을 지정합니다.

```php theme={null}
use App\Models\Order;
use Illuminate\Database\Eloquent\Builder;

$orders = Order::search('Star Trek')
    ->query(fn (Builder $query) => $query->with(['invoices', 'user']))
    ->get();
```

배치 임포트 시에 릴레이션을 Eager Load하려면, `makeAllSearchableUsing` 메서드를 정의합니다.

```php theme={null}
use Illuminate\Database\Eloquent\Builder;

protected function makeAllSearchableUsing(Builder $query): Builder
{
    return $query->with('author');
}
```

<Warning>
  `makeAllSearchableUsing`은 큐를 사용한 배치 임포트에서는 사용할 수 없는 경우가 있습니다. 큐 잡에서 모델 컬렉션이 처리될 때 릴레이션은 복원되지 않습니다.
</Warning>

## 소프트 삭제

인덱스된 모델이 [소프트 삭제](/ko/eloquent)를 사용하고 있고, 삭제된 모델도 검색하고 싶은 경우는, `config/scout.php`의 `soft_delete` 옵션을 `true`로 설정합니다.

```php theme={null}
'soft_delete' => true,
```

활성화하면, `withTrashed`나 `onlyTrashed`로 삭제된 레코드를 검색할 수 있습니다.

```php theme={null}
// 삭제된 것을 포함해 검색
$orders = Order::search('Star Trek')->withTrashed()->get();

// 삭제된 것만 검색
$orders = Order::search('Star Trek')->onlyTrashed()->get();
```

## 커스텀 엔진

조립된 검색 엔진이 니즈에 맞지 않는 경우는, 독자적인 커스텀 엔진을 구현할 수 있습니다. 커스텀 엔진은 `Laravel\Scout\Engines\Engine` 추상 클래스를 상속하고, 이하의 8개의 메서드를 구현해야 합니다.

```php theme={null}
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` 클래스를 확인해 주세요.

작성한 커스텀 엔진은, `App\Providers\AppServiceProvider`의 `boot` 메서드에서 Scout에 등록합니다.

```php theme={null}
use App\ScoutExtensions\MySqlSearchEngine;
use Laravel\Scout\EngineManager;

public function boot(): void
{
    resolve(EngineManager::class)->extend('mysql', function () {
        return new MySqlSearchEngine;
    });
}
```

등록 후, `config/scout.php`에서 드라이버로서 지정합니다.

```php theme={null}
'driver' => 'mysql',
```

## 관련 페이지

<Card title="Eloquent ORM" icon="database" href="/ko/eloquent">
  Eloquent 모델의 기본적인 사용법을 확인합니다.
</Card>

<Card title="Eloquent 릴레이션" icon="link" href="/ko/eloquent-relationships">
  릴레이션의 정의와 Eager Loading을 확인합니다.
</Card>

<Card title="큐" icon="clock" href="/ko/queues">
  Scout는 큐와 함께 인덱스를 백그라운드에서 업데이트할 수 있습니다.
</Card>


## Related topics

- [검색](/ko/search.md)
- [MongoDB](/ko/mongodb.md)
- [Laravel Sail](/ko/sail.md)
- [2026년 3월 Laravel 업데이트](/ko/blog/changelog/202603.md)
- [Laravel Telescope](/ko/telescope.md)
