메인 콘텐츠로 건너뛰기

Wayfinder란

Laravel Wayfinder는 Laravel 백엔드와 TypeScript 프론트엔드를 제로 프릭션으로 연결하는 패키지입니다. 컨트롤러와 라우트에서 완전히 타입이 부여된 TypeScript 함수를 자동 생성하므로, 프론트엔드 코드에서 Laravel의 엔드포인트를 함수로서 직접 호출할 수 있습니다. URL의 하드 코딩, 라우트 파라미터의 수동 관리, 백엔드 변경의 수작업 동기화 — 이것들이 모두 불필요해집니다.
Wayfinder는 Beta 버전입니다 (현재 v0.1.x). v1.0.0 릴리스까지 API가 변경될 가능성이 있습니다. 중요한 변경은 모두 CHANGELOG에 기록됩니다.

Ziggy와 Wayfinder의 차이

Ziggy란

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

왜 Wayfinder로 대체되었는가

Ziggy는 라우트 이름과 파라미터를 문자열로 다루므로, TypeScript와의 궁합에 한계가 있었습니다. 라우트 이름의 오타나 잘못된 파라미터 이름은 런타임 오류밖에 되지 않습니다. Wayfinder는 TypeScript 퍼스트 설계로, 컨트롤러의 메서드를 임포트 가능한 함수로서 생성합니다.
비교 항목ZiggyWayfinder
라우트 참조 방법route('posts.show', { id: 1 })import { show } from "@/actions/..."
타입 안전성타입 정의가 제한적완전한 TypeScript 타입
IDE 지원자동완성이 약함자동완성 · 타입 체크 완전 대응
트리 셰이킹전 라우트를 번들에 포함사용한 라우트만 번들
생성 타이밍런타임에 주입빌드 타임에 정적 생성
Inertia 기반의 Laravel 스타터 킷(React · Vue · Svelte)에서는 Wayfinder가 표준으로 채택되어 있습니다.

설치

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

composer require laravel/wayfinder

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

npm i -D @laravel/vite-plugin-wayfinder

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

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 파일을 생성합니다.
php artisan wayfinder:generate
기본으로는 resources/js 이하에 3개의 디렉터리가 생성됩니다.
resources/js/
├── actions/         # 컨트롤러 액션의 함수
│   └── App/Http/Controllers/
│       └── PostController.ts
├── routes/          # 이름 있는 라우트의 함수
│   └── post.ts
└── wayfinder/       # 타입 정의 파일
    └── types.ts
생성되는 파일은 빌드할 때마다 완전히 재생성되므로, .gitignore에 추가할 것을 권장합니다. wayfinder, actions, routes의 3디렉터리를 함께 제외해 주세요.
출력처를 변경하고 싶은 경우는 --path 옵션을 사용합니다.
php artisan wayfinder:generate --path=resources/js/api
컨트롤러 액션만, 또는 라우트만 생성할 수도 있습니다.
php artisan wayfinder:generate --skip-actions  # 라우트만 생성
php artisan wayfinder:generate --skip-routes   # 액션만 생성

기본 사용법

액션의 임포트와 사용

PostControllershow 메서드에 대응하는 URL을 생성하는 예입니다.
import { show } from "@/actions/App/Http/Controllers/PostController";

show(1);
// { url: "/posts/1", method: "get" }
URL만 필요한 경우에는 .url()을 사용합니다.
show.url(1); // "/posts/1"
특정 HTTP 메서드를 지정할 수도 있습니다.
show.head(1); // { url: "/posts/1", method: "head" }

파라미터의 전달 방법

Wayfinder의 함수는 다양한 형식의 파라미터를 받아들입니다.
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}) 그 값을 사용할 수 있습니다.
// 라우트가 /posts/{post:slug}인 경우
show("my-new-post");
show({ slug: "my-new-post" });

컨트롤러 전체의 임포트

컨트롤러 전체를 임포트하여 메서드를 호출할 수도 있습니다.
import PostController from "@/actions/App/Http/Controllers/PostController";

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

단일 액션 컨트롤러

단일 액션 컨트롤러(Invokable Controller)는 임포트한 함수를 그대로 호출합니다.
import StorePostController from "@/actions/App/Http/Controllers/StorePostController";

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

이름 있는 라우트의 임포트

라우트 이름으로 접근하려면 routes/ 아래의 파일을 사용합니다.
import { show } from "@/routes/post";

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

쿼리 파라미터

모든 Wayfinder 함수는 query 옵션으로 쿼리 파라미터를 추가할 수 있습니다.
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를 사용합니다.
// 현재 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 배리언트를 사용합니다.
php artisan wayfinder:generate --with-form
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 문자열을 일절 쓰지 않고 폼 송신을 할 수 있습니다.
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 컴포넌트에서도 마찬가지로 사용할 수 있습니다.
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에 생성 디렉터리 추가 완료
기존 프로젝트로의 수동 도입도 위의 절차로 대응할 수 있습니다.

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

deleteimport 등 JavaScript의 예약어와 같은 이름의 컨트롤러 메서드에는 Method 서픽스가 붙습니다.
// 컨트롤러에 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.15Laravel 13 대응, Blade 뷰 크래시 수정
v0.1.13쿼리 파라미터의 TypeScript strict 호환성 개선
v0.1.7URL 기본 파라미터의 프론트엔드 지정 대응
v0.1.6Vite 플러그인 추가
v0.1.5PHP 8.2 지원, 캐시된 라우트로의 대응
v0.1.0초기 릴리스

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

next 브랜치에서는 현재의 v0.1.x에서 대폭 기능이 확장된 차기 버전이 개발되고 있습니다.
next 브랜치는 dev-next 제약으로 설치할 수 있지만, API가 크게 바뀔 가능성이 있습니다. 프로덕션 환경에서의 사용은 권장되지 않습니다.
composer require laravel/wayfinder:dev-next

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

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

Form Request의 TypeScript 타입 생성

class StorePostRequest extends FormRequest
{
    public function rules(): array
    {
        return [
            'title'   => ['required', 'string', 'max:255'],
            'content' => ['required', 'string'],
            'tags'    => ['nullable', 'array'],
            'tags.*'  => ['string'],
        ];
    }
}
위 Form Request로부터 이하의 타입이 생성됩니다.
export type Request = {
    title: string;
    content: string;
    tags?: string[] | null;
};

Eloquent 모델의 타입 생성

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에 타입이 생성됩니다.
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 변환

enum PostStatus: string
{
    case Draft = 'draft';
    case Published = 'published';
    case Archived = 'archived';
}
타입과 상수 양쪽이 생성됩니다.
// 타입 정의 (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.tstypes.d.ts로 변경

정리

Laravel Wayfinder는 Ziggy가 제공하고 있던 “Laravel의 라우트를 JavaScript에서 참조하는” 기능을, TypeScript 퍼스트로 재설계한 패키지입니다. 생성된 함수를 임포트해서 사용하는 접근에 의해 타입 안전성 · IDE 지원 · 트리 셰이킹 모두에서 크게 개선되어 있습니다. 현재의 v0.1.x에서도 라우트와 컨트롤러 액션의 타입 안전한 참조가 실현되어 있어, Inertia 기반의 Laravel 스타터 킷에서 표준 채택되어 있습니다. next 브랜치에서 개발 중인 차기 버전에서는 Form Request · Eloquent 모델 · Enum · Inertia 페이지 prop까지 TypeScript로 생성하는, 보다 포괄적인 타입 안전 기반으로 진화할 예정입니다.

Laravel Wayfinder GitHub

소스 코드, CHANGELOG, Issue는 이곳에서 확인하실 수 있습니다.

Vite Plugin Wayfinder

Vite 플러그인의 설정 옵션 상세는 이곳에서 확인하실 수 있습니다.
마지막 수정일 2026년 7월 13일