> ## 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 Telescope 실전 테크닉

> Telescope를 로컬 개발에서 최대한 활용하는 실전 패턴을 소개합니다. N+1 검출, 태그를 사용한 추적, 커스텀 워처, Dump 워처 활용 등 공식 문서만으로는 알기 어려운 노하우를 정리했습니다.

기본적인 설치와 설정은 [가이드 페이지](/ko/telescope)를 참고하세요. 이 페이지에서는 공식 문서에 실려 있지 않은, 로컬 개발에서의 실전 활용 패턴을 소개합니다.

Telescope는 **로컬 개발 전용** 도구입니다. 프로덕션 환경의 모니터링에는 [Laravel Pulse](/ko/pulse)나 [Laravel Nightwatch](/ko/blog/nightwatch-introduction)를 사용해 주세요.

***

## N+1 문제를 체계적으로 발견하기

Telescope를 사용한 N+1 디버그의 실전 흐름을 설명합니다. 단순히 "같은 SELECT가 반복되고 있다"고 알아채는 것뿐만 아니라, 수정 후 확인까지 일련의 워크플로우로 수행할 수 있습니다.

```mermaid theme={null}
sequenceDiagram
    participant 브라우저
    participant Laravel
    participant Telescope
    participant DB

    브라우저->>Laravel: GET /posts
    Laravel->>DB: SELECT * FROM posts (1쿼리)
    loop 각 게시물에 대해
        Laravel->>DB: SELECT * FROM users WHERE id = ? (N 쿼리)
    end
    Laravel-->>브라우저: 응답
    Laravel-->>Telescope: 쿼리 이력 기록
    Note over Telescope: 같은 SELECT 문이 N회<br>slow 태그가 붙는다
```

<Steps>
  <Step title="Requests에서 느린 요청 찾기">
    `/telescope`를 열고, Requests 탭에서 실행 시간이 긴 요청을 특정합니다.
  </Step>

  <Step title="Queries에서 발행된 SQL 확인">
    요청의 상세를 열면, 그 요청 중에 실행된 모든 SQL 쿼리가 표시됩니다. 비슷한 SELECT 문이 반복되고 있다면 N+1입니다.
  </Step>

  <Step title="Eager Loading을 추가하여 수정">
    코드에 `with()`를 추가하고 쿼리를 재확인합니다. 쿼리 수가 대폭 감소했다면 수정 완료입니다.
  </Step>
</Steps>

```php theme={null}
// N+1이 발생하는 코드
$posts = Post::all();
foreach ($posts as $post) {
    echo $post->user->name; // 게시물의 수만큼 추가 쿼리
}

// Eager Loading으로 해결
$posts = Post::with('user')->get();
```

디버그용 `dd()`나 로그를 코드에 추가하지 않고, 이 일련의 흐름을 브라우저 상에서 완결시킬 수 있습니다.

***

## 태그를 사용한 특정 요청의 추적

Telescope에는 **태그** 기능이 있습니다. `Telescope::tag()`로 엔트리에 임의의 태그를 붙이면, 대시보드의 필터에서 그 태그의 엔트리만을 재빨리 좁힐 수 있습니다.

특정 사용자 ID나 주문 ID에 관련된 처리만을 추적하고 싶은 케이스에서 매우 유용합니다.

```php theme={null}
use Laravel\Telescope\IncomingEntry;
use Laravel\Telescope\Telescope;

// TelescopeServiceProvider의 tags 메서드를 오버라이드한다
Telescope::tag(function (IncomingEntry $entry) {
    if ($entry->type === 'request') {
        return array_filter([
            'status:' . $entry->content['response_status'],
            auth()->check() ? 'user:' . auth()->id() : null,
        ]);
    }

    return [];
});
```

이제 `/telescope/requests` 화면의 Search에 `user:42`를 입력하는 것만으로, 사용자 ID=42의 요청만을 일람할 수 있습니다.

### 모델에 태그를 자동 부여

`TelescopeServiceProvider`의 `tags` 메서드로, 특정 모델 ID를 모든 엔트리에 부여할 수 있습니다.

```php theme={null}
// TelescopeServiceProvider
Telescope::tag(function (IncomingEntry $entry) {
    return $entry->tags();
});
```

또한 `HasTags` 컨트랙트를 모델에 구현함으로써, 그 모델이 기록될 때 자동으로 태그가 붙습니다.

```php theme={null}
use Laravel\Telescope\Contracts\EntriesRepository;

class Order extends Model implements \Laravel\Telescope\Contracts\HasTags
{
    public function telescopeTags(): array
    {
        return ['order:' . $this->id];
    }
}
```

***

## Dump 워처 활용

`dump()`를 사용하면 HTML 응답에 출력이 섞여 들어가 API의 디버그가 어려워질 수 있습니다. Telescope의 Dump 워처를 사용하면 `dump()`의 출력을 브라우저의 응답에서 분리하여 대시보드에 기록할 수 있습니다.

사용법은 심플합니다. `/telescope`의 "Dump" 탭을 연 상태에서 `dump()`를 호출하기만 하면 됩니다.

```php theme={null}
// 컨트롤러
public function index()
{
    $users = User::with('posts')->get();
    dump($users->first()->toArray()); // Telescope의 dump 탭에 표시됨
    return response()->json($users);
}
```

페이지를 연 상태에서만 캡처되므로, 필요할 때만 감시할 수 있습니다. `dd()`와는 달리 앱의 실행을 멈추지 않으므로, 연속된 요청을 흘려보내면서 디버그할 경우에 편리합니다.

***

## Mailpit 연동으로 메일 디버그를 쾌적하게

Mail 워처와 로컬 SMTP 서버인 [Mailpit](https://mailpit.axllent.org/)을 조합함으로써, 메일 개발의 흐름이 대폭 개선됩니다.

```ini theme={null}
# .env
MAIL_MAILER=smtp
MAIL_HOST=127.0.0.1
MAIL_PORT=1025
```

Telescope의 Mail 탭에서는 송신 메일의 HTML/텍스트 양쪽을 프리뷰할 수 있습니다. Mailpit에서는 수신한 메일을 브라우저에서 확인할 수 있고, 게다가 첨부 파일 확인이나 스팸 스코어 확인도 가능합니다.

<Tip>
  테스트 중에 실수로 외부에 메일을 보내지 않도록, 로컬에서는 반드시 Mailpit이나 Mailhog 등의 로컬 SMTP를 사용합시다.
</Tip>

***

## 이벤트와 리스너의 디버그

이벤트 드리븐한 구현의 디버그는 종종 어려워집니다. "이벤트는 발화되고 있는가", "어느 리스너가 호출되고 있는가"를 로그를 보면서 쫓는 것은 번거롭습니다.

Telescope의 Events 워처를 사용하면, 발행된 이벤트와 그 리스너가 일람으로 확인할 수 있습니다.

리스너가 호출되지 않는 경우, Events 탭에서 이벤트 엔트리를 확인합니다.

* 이벤트는 발행되어 있지만 리스너가 표시되지 않음 → 리스너가 등록되어 있지 않음 (`EventServiceProvider` 확인)
* 이벤트 자체가 발행되어 있지 않음 → `event()` 호출 부분 확인

```php theme={null}
// 테스트 대신 로컬에서 확인하는 흐름
event(new OrderShipped($order));
// → /telescope/events에서 OrderShipped가 표시되는지 확인
// → 리스너 SendShippingConfirmation이 호출되고 있는지 확인
```

***

## 큐 잡의 디버그

비동기 처리의 디버그는 로그를 보는 것만으로는 원인 특정이 어렵습니다. Telescope의 Jobs 워처는 잡의 디스패치부터 실행 결과까지 기록합니다.

실패한 잡의 엔트리를 클릭하면 스택 트레이스와 예외 메시지를 확인할 수 있습니다. `queue:failed` 테이블과는 별도로 Telescope에서도 확인할 수 있으므로, 실패의 원인 조사를 재빨리 수행할 수 있습니다.

```php theme={null}
// 잡의 디버그 시에는 sync 드라이버로 동기 실행하면 추적하기 쉽다
// .env
QUEUE_CONNECTION=sync
```

sync 드라이버로 함으로써, 잡이 요청 내에서 동기 실행되고, Requests 워처에 잡의 실행 결과도 포함됩니다.

***

## HTTP 클라이언트의 디버그

외부 API와의 통신을 디버그할 때, HTTP Client Watcher가 유용합니다. `Http::` 파사드에서 이루어진 요청과, 그 응답이 기록됩니다.

```php theme={null}
$response = Http::get('https://api.example.com/users');
// → /telescope/client-requests에서 확인할 수 있음
```

응답의 내용, 상태 코드, 실행 시간이 한눈에 보입니다. `dd($response->json())`을 쓰지 않아도 대시보드에서 확인할 수 있습니다.

***

## 정리

| 테크닉         | 효과                              |
| ----------- | ------------------------------- |
| N+1 워크플로우   | dd() 불필요하게 쿼리 문제를 체계적으로 발견 · 수정 |
| 태그 부여       | 특정 사용자 · 모델의 기록을 즉시 좁히기         |
| Dump 워처     | API 응답을 더럽히지 않고 dump 출력을 확인     |
| Mailpit 연동  | 로컬 메일 개발 쾌적화                    |
| Events 워처   | 이벤트/리스너의 발화를 시각적으로 확인           |
| Jobs + sync | 비동기 처리를 동기 실행하여 디버그             |
| HTTP 클라이언트  | 외부 API 통신의 기록과 확인               |

<Columns cols={2}>
  <Card title="Laravel Telescope 가이드" icon="telescope" href="/ko/telescope">
    설치와 워처 설정의 상세는 가이드 페이지를 참고하세요.
  </Card>

  <Card title="Laravel Nightwatch" icon="moon" href="/ko/blog/nightwatch-introduction">
    프로덕션 환경의 모니터링은 Nightwatch에서 수행합니다.
  </Card>
</Columns>


## Related topics

- [Laravel Nightwatch 입문](/ko/blog/nightwatch-introduction.md)
- [Laravel Telescope](/ko/telescope.md)
- [Laravel Pennant 실전 유스케이스](/ko/blog/laravel-pennant.md)
- [Laravel Console Starter](/ko/packages/laravel-console-starter/index.md)
- [Ask user](/ko/packages/laravel-copilot-sdk/ask-user.md)
