> ## 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 Wayfinder 소개

> Ziggy에서 Inertia 스타터 킷으로 채택된 대체 패키지, Laravel Wayfinder를 설명합니다. Laravel 백엔드와 TypeScript 프론트엔드를 타입 안전하게 연결하는 구조부터, Ziggy와의 차이, 설치 · 기본 사용법, next 브랜치에서 진화하는 차세대 기능까지 자세히 소개합니다.

## Wayfinder란

**Laravel Wayfinder**는 Laravel 백엔드와 TypeScript 프론트엔드를 제로 프릭션으로 연결하는 패키지입니다. 컨트롤러와 라우트에서 완전히 타입이 부여된 TypeScript 함수를 자동 생성하므로, 프론트엔드 코드에서 Laravel의 엔드포인트를 함수로서 직접 호출할 수 있습니다.

URL의 하드 코딩, 라우트 파라미터의 수동 관리, 백엔드 변경의 수작업 동기화 — 이것들이 모두 불필요해집니다.

<Info>
  Wayfinder는 Beta 버전입니다 (현재 v0.1.x). v1.0.0 릴리스까지 API가 변경될 가능성이 있습니다. 중요한 변경은 모두 [CHANGELOG](https://github.com/laravel/wayfinder/blob/main/CHANGELOG.md)에 기록됩니다.
</Info>

***

## Ziggy와 Wayfinder의 차이

### Ziggy란

[Ziggy](https://github.com/tighten/ziggy)는 오랜 세월에 걸쳐 Laravel 에코시스템에서 널리 사용되어 온 라우트 헬퍼입니다. Laravel의 라우트 정의를 JavaScript 측에 공개하고, `route('posts.show', { id: 1 })`과 같은 형식으로 URL을 생성할 수 있었습니다.

### 왜 Wayfinder로 대체되었는가

Ziggy는 라우트 이름과 파라미터를 문자열로 다루므로, TypeScript와의 궁합에 한계가 있었습니다. 라우트 이름의 오타나 잘못된 파라미터 이름은 런타임 오류밖에 되지 않습니다.

Wayfinder는 TypeScript 퍼스트 설계로, 컨트롤러의 메서드를 **임포트 가능한 함수**로서 생성합니다.

| 비교 항목     | Ziggy                            | Wayfinder                              |
| --------- | -------------------------------- | -------------------------------------- |
| 라우트 참조 방법 | `route('posts.show', { id: 1 })` | `import { show } from "@/actions/..."` |
| 타입 안전성    | 타입 정의가 제한적                       | 완전한 TypeScript 타입                      |
| IDE 지원    | 자동완성이 약함                         | 자동완성 · 타입 체크 완전 대응                     |
| 트리 셰이킹    | 전 라우트를 번들에 포함                    | 사용한 라우트만 번들                            |
| 생성 타이밍    | 런타임에 주입                          | 빌드 타임에 정적 생성                           |

Inertia 기반의 Laravel 스타터 킷(React · Vue · Svelte)에서는 Wayfinder가 표준으로 채택되어 있습니다.

***

## 설치

### 1. Composer로 서버사이드 패키지 설치

```bash theme={null}
composer require laravel/wayfinder
```

### 2. NPM으로 Vite 플러그인 설치

```bash theme={null}
npm i -D @laravel/vite-plugin-wayfinder
```

### 3. `vite.config.js`에 플러그인 추가

```ts theme={null}
import { wayfinder } from "@laravel/vite-plugin-wayfinder";
import { defineConfig } from "vite";
import laravel from "laravel-vite-plugin";

export default defineConfig({
    plugins: [
        laravel({
            input: ["resources/js/app.ts"],
            refresh: true,
        }),
        wayfinder(),
    ],
});
```

Vite 플러그인을 추가하면, 개발 서버 기동 중에 PHP 파일이나 라우트 파일이 변경될 때마다 자동으로 TypeScript 파일이 재생성됩니다.

***

## TypeScript 정의 파일의 생성

`wayfinder:generate` 명령으로 TypeScript 파일을 생성합니다.

```bash theme={null}
php artisan wayfinder:generate
```

기본으로는 `resources/js` 이하에 3개의 디렉터리가 생성됩니다.

```
resources/js/
├── actions/         # 컨트롤러 액션의 함수
│   └── App/Http/Controllers/
│       └── PostController.ts
├── routes/          # 이름 있는 라우트의 함수
│   └── post.ts
└── wayfinder/       # 타입 정의 파일
    └── types.ts
```

<Tip>
  생성되는 파일은 빌드할 때마다 완전히 재생성되므로, `.gitignore`에 추가할 것을 권장합니다. `wayfinder`, `actions`, `routes`의 3디렉터리를 함께 제외해 주세요.
</Tip>

출력처를 변경하고 싶은 경우는 `--path` 옵션을 사용합니다.

```bash theme={null}
php artisan wayfinder:generate --path=resources/js/api
```

컨트롤러 액션만, 또는 라우트만 생성할 수도 있습니다.

```bash theme={null}
php artisan wayfinder:generate --skip-actions  # 라우트만 생성
php artisan wayfinder:generate --skip-routes   # 액션만 생성
```

***

## 기본 사용법

### 액션의 임포트와 사용

`PostController`의 `show` 메서드에 대응하는 URL을 생성하는 예입니다.

```ts theme={null}
import { show } from "@/actions/App/Http/Controllers/PostController";

show(1);
// { url: "/posts/1", method: "get" }
```

URL만 필요한 경우에는 `.url()`을 사용합니다.

```ts theme={null}
show.url(1); // "/posts/1"
```

특정 HTTP 메서드를 지정할 수도 있습니다.

```ts theme={null}
show.head(1); // { url: "/posts/1", method: "head" }
```

### 파라미터의 전달 방법

Wayfinder의 함수는 다양한 형식의 파라미터를 받아들입니다.

```ts theme={null}
import { update } from "@/actions/App/Http/Controllers/PostController";

// 단일 파라미터
show(1);
show({ id: 1 });

// 여러 파라미터
update([1, 2]);
update({ post: 1, author: 2 });
update({ post: { id: 1 }, author: { id: 2 } });
```

라우트에 키 바인딩이 지정되어 있는 경우(`/posts/{post:slug}`) 그 값을 사용할 수 있습니다.

```ts theme={null}
// 라우트가 /posts/{post:slug}인 경우
show("my-new-post");
show({ slug: "my-new-post" });
```

### 컨트롤러 전체의 임포트

컨트롤러 전체를 임포트하여 메서드를 호출할 수도 있습니다.

```ts theme={null}
import PostController from "@/actions/App/Http/Controllers/PostController";

PostController.show(1);
PostController.index();
```

<Warning>
  컨트롤러 전체를 임포트하면 트리 셰이킹이 작동하지 않아, 모든 액션이 번들에 포함됩니다. 개별로 임포트하는 편이 최종 번들 크기를 작게 유지할 수 있습니다.
</Warning>

### 단일 액션 컨트롤러

단일 액션 컨트롤러(Invokable Controller)는 임포트한 함수를 그대로 호출합니다.

```ts theme={null}
import StorePostController from "@/actions/App/Http/Controllers/StorePostController";

StorePostController(); // { url: "/posts", method: "post" }
```

### 이름 있는 라우트의 임포트

라우트 이름으로 접근하려면 `routes/` 아래의 파일을 사용합니다.

```ts theme={null}
import { show } from "@/routes/post";

// 라우트명이 `post.show`인 경우
show(1); // { url: "/posts/1", method: "get" }
```

### 쿼리 파라미터

모든 Wayfinder 함수는 `query` 옵션으로 쿼리 파라미터를 추가할 수 있습니다.

```ts theme={null}
import { show } from "@/actions/App/Http/Controllers/PostController";

show(1, { query: { page: 1, sort_by: "name" } });
// { url: "/posts/1?page=1&sort_by=name", method: "get" }
```

현재 URL의 쿼리 파라미터와 병합하려면 `mergeQuery`를 사용합니다.

```ts theme={null}
// 현재 URL: /posts/1?page=1&sort_by=category&q=shirt

show.url(1, { mergeQuery: { page: 2, sort_by: "name" } });
// "/posts/1?page=2&sort_by=name&q=shirt"

// 파라미터를 삭제하려면 null을 지정
show.url(1, { mergeQuery: { sort_by: null } });
// "/posts/1?page=1&q=shirt"
```

### 폼 배리언트

기존 HTML 폼에서 사용할 경우에는 `--with-form` 옵션을 붙여 생성하고, `.form` 배리언트를 사용합니다.

```bash theme={null}
php artisan wayfinder:generate --with-form
```

```tsx theme={null}
import { store, update } from "@/actions/App/Http/Controllers/PostController";

// React 예시
const Page = () => (
    <form {...store.form()}>
        {/* <form action="/posts" method="post"> */}
    </form>
);

const EditPage = () => (
    <form {...update.form(1)}>
        {/* <form action="/posts/1?_method=PATCH" method="post"> */}
    </form>
);
```

***

## Inertia와 Wayfinder의 조합

Inertia의 폼 헬퍼와 Wayfinder를 조합하면, URL 문자열을 일절 쓰지 않고 폼 송신을 할 수 있습니다.

```ts theme={null}
import { useForm } from "@inertiajs/react";
import { store } from "@/actions/App/Http/Controllers/PostController";

const form = useForm({ name: "My Post" });

form.submit(store()); // POST /posts에 송신
```

`Link` 컴포넌트에서도 마찬가지로 사용할 수 있습니다.

```tsx theme={null}
import { Link } from "@inertiajs/react";
import { show } from "@/actions/App/Http/Controllers/PostController";

const Nav = () => (
    <Link href={show(1)}>게시물 보기</Link>
);
```

***

## 스타터 킷에서의 채택

`laravel new`로 신규 프로젝트를 만들고 React · Vue · Svelte를 선택하면, Wayfinder가 자동 셋업된 구성이 제공됩니다. 스타터 킷에는 이하가 포함됩니다.

* Composer 패키지 `laravel/wayfinder`
* NPM 패키지 `@laravel/vite-plugin-wayfinder`
* `vite.config.js`에 플러그인 설정 완료
* `.gitignore`에 생성 디렉터리 추가 완료

기존 프로젝트로의 수동 도입도 위의 절차로 대응할 수 있습니다.

***

## 예약어와 경합하는 메서드명의 처리

`delete`나 `import` 등 JavaScript의 예약어와 같은 이름의 컨트롤러 메서드에는 `Method` 서픽스가 붙습니다.

```ts theme={null}
// 컨트롤러에 delete 메서드가 있는 경우
import { deleteMethod } from "@/actions/App/Http/Controllers/PostController";

deleteMethod(1); // { url: "/posts/1", method: "delete" }
```

***

## 현재의 상황 (v0.1.x)

현재 안정판은 `v0.1.x` 브랜치로 제공되고 있습니다. 2026년 3월 시점의 최신 버전은 **v0.1.15**입니다.

### v0.1.x 계의 주요 변경 이력

| 버전      | 주요 내용                             |
| ------- | --------------------------------- |
| v0.1.15 | Laravel 13 대응, Blade 뷰 크래시 수정     |
| v0.1.13 | 쿼리 파라미터의 TypeScript strict 호환성 개선 |
| v0.1.7  | URL 기본 파라미터의 프론트엔드 지정 대응          |
| v0.1.6  | Vite 플러그인 추가                      |
| v0.1.5  | PHP 8.2 지원, 캐시된 라우트로의 대응          |
| v0.1.0  | 초기 릴리스                            |

***

## next 브랜치에서 개발 중인 차세대 기능

`next` 브랜치에서는 현재의 v0.1.x에서 대폭 기능이 확장된 차기 버전이 개발되고 있습니다.

<Warning>
  `next` 브랜치는 `dev-next` 제약으로 설치할 수 있지만, API가 크게 바뀔 가능성이 있습니다. 프로덕션 환경에서의 사용은 권장되지 않습니다.
</Warning>

```bash theme={null}
composer require laravel/wayfinder:dev-next
```

### 생성되는 TypeScript의 범위가 대폭 확대

v0.1.x가 라우트와 컨트롤러 액션만을 대상으로 하고 있는 것에 비해, 차기 버전은 이하를 모두 TypeScript로 생성합니다.

```mermaid theme={null}
graph TD
    A["Laravel 애플리케이션"] --> B["wayfinder:generate"]
    B --> C["Routes & Actions<br>라우트 URL 함수"]
    B --> D["Form Requests<br>유효성 검증 타입"]
    B --> E["Eloquent Models<br>모델 인터페이스"]
    B --> F["PHP Enums<br>TypeScript 상수"]
    B --> G["Inertia Page Props<br>페이지 prop 타입"]
    B --> H["Broadcast Channels<br>채널 타입"]
    B --> I["Broadcast Events<br>이벤트 페이로드 타입"]
    B --> J["Environment Variables<br>import.meta.env 타입"]
```

### Form Request의 TypeScript 타입 생성

```php theme={null}
class StorePostRequest extends FormRequest
{
    public function rules(): array
    {
        return [
            'title'   => ['required', 'string', 'max:255'],
            'content' => ['required', 'string'],
            'tags'    => ['nullable', 'array'],
            'tags.*'  => ['string'],
        ];
    }
}
```

위 Form Request로부터 이하의 타입이 생성됩니다.

```ts theme={null}
export type Request = {
    title: string;
    content: string;
    tags?: string[] | null;
};
```

### Eloquent 모델의 타입 생성

```php theme={null}
class User extends Model
{
    protected $casts = [
        'email_verified_at' => 'datetime',
        'is_admin' => 'boolean',
    ];

    public function posts(): HasMany
    {
        return $this->hasMany(Post::class);
    }
}
```

위 모델로부터 `types.d.ts`에 타입이 생성됩니다.

```ts theme={null}
export namespace App.Models {
    export type User = {
        id: number;
        name: string;
        email: string;
        email_verified_at: string | null;
        is_admin: boolean;
        posts: App.Models.Post[];
    };
}
```

### PHP Enum의 TypeScript 변환

```php theme={null}
enum PostStatus: string
{
    case Draft = 'draft';
    case Published = 'published';
    case Archived = 'archived';
}
```

타입과 상수 양쪽이 생성됩니다.

```ts theme={null}
// 타입 정의 (types.d.ts)
export namespace App.Enums {
    export type PostStatus = "draft" | "published" | "archived";
}

// 상수 (App/Enums/PostStatus.ts)
export const Draft = "draft";
export const Published = "published";
export const Archived = "archived";

export const PostStatus = { Draft, Published, Archived } as const;
```

### 출력 디렉터리의 변경

v0.1.x에서는 `actions/`, `routes/`, `wayfinder/`의 3디렉터리로 나뉘어 있었지만, 차기 버전에서는 `resources/js/wayfinder` 이하에 정리됩니다.

```
resources/js/wayfinder/
├── App/Http/Controllers/
│   └── PostController.ts    # 액션 함수 (actions/가 폐지)
├── routes/
│   └── post.ts              # 이름 있는 라우트 (동일)
├── broadcast-channels.ts    # 브로드캐스트 채널
├── broadcast-events.ts      # 브로드캐스트 이벤트
└── types.d.ts               # 전 타입 정의 (types.ts에서 변경)
```

### v0.1.x에서 next로의 주요 변경점

* 임포트 경로가 `@/actions/...`에서 `@/wayfinder/...`로 변경
* `--skip-actions`, `--skip-routes`, `--with-form` 플래그가 폐지되고, 설정 파일로 이관
* `types.ts`가 `types.d.ts`로 변경

***

## 정리

Laravel Wayfinder는 Ziggy가 제공하고 있던 "Laravel의 라우트를 JavaScript에서 참조하는" 기능을, TypeScript 퍼스트로 재설계한 패키지입니다. 생성된 함수를 임포트해서 사용하는 접근에 의해 타입 안전성 · IDE 지원 · 트리 셰이킹 모두에서 크게 개선되어 있습니다.

현재의 v0.1.x에서도 라우트와 컨트롤러 액션의 타입 안전한 참조가 실현되어 있어, Inertia 기반의 Laravel 스타터 킷에서 표준 채택되어 있습니다. `next` 브랜치에서 개발 중인 차기 버전에서는 Form Request · Eloquent 모델 · Enum · Inertia 페이지 prop까지 TypeScript로 생성하는, 보다 포괄적인 타입 안전 기반으로 진화할 예정입니다.

<Card title="Laravel Wayfinder GitHub" icon="github" href="https://github.com/laravel/wayfinder">
  소스 코드, CHANGELOG, Issue는 이곳에서 확인하실 수 있습니다.
</Card>

<Card title="Vite Plugin Wayfinder" icon="bolt" href="https://github.com/laravel/vite-plugin-wayfinder">
  Vite 플러그인의 설정 옵션 상세는 이곳에서 확인하실 수 있습니다.
</Card>


## Related topics

- [Blaze 패키지 소개](/ko/blog/blaze-introduction.md)
- [Laravel Telescope 실전 테크닉](/ko/blog/telescope-introduction.md)
- [Laravel Pennant 실전 유스케이스](/ko/blog/laravel-pennant.md)
- [Pest PHP로 시작하는 Laravel 테스트](/ko/blog/pest-introduction.md)
- [Laravel Folio](/ko/folio.md)
