본문 바로가기

Eloquent: 팩토리

번역일: 2026년 6월 21일

Eloquent: 팩토리

소개

테스트를 작성하거나 데이터베이스 시딩을 할 때, 여러 레코드를 직접 DB에 삽입해야 하는 경우가 자주 있습니다. 이때 각 컬럼 값을 일일이 지정하는 대신, Laravel의 모델 팩토리를 활용하면 Eloquent 모델별로 기본 속성값 세트를 한 번만 정의해두고 필요할 때마다 재사용할 수 있습니다.

팩토리 작성 방법은 애플리케이션에 기본으로 포함된 database/factories/UserFactory.php 파일을 참고하면 됩니다. 새 Laravel 프로젝트에는 아래와 같은 UserFactory가 이미 포함되어 있습니다:

namespace Database\Factories; use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Str; /** * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\User> */ class UserFactory extends Factory { /** * 팩토리에서 현재 사용 중인 비밀번호 */ protected static ?string $password; /** * 모델의 기본 상태를 정의합니다. * * @return array<string, mixed> */ public function definition(): array { return [ 'name' => fake()->name(), 'email' => fake()->unique()->safeEmail(), 'email_verified_at' => now(), 'password' => static::$password ??= Hash::make('password'), 'remember_token' => Str::random(10), ]; } /** * 이메일 미인증 상태로 지정합니다. */ public function unverified(): static { return $this->state(fn (array $attributes) => [ 'email_verified_at' => null, ]); } }

팩토리는 Laravel의 기본 Factory 클래스를 상속하고, definition 메서드를 정의하는 클래스입니다. definition 메서드는 팩토리로 모델을 생성할 때 적용할 기본 속성값 배열을 반환합니다.

fake() 헬퍼를 통해 Faker PHP 라이브러리를 사용할 수 있으며, 테스트 및 시딩에 필요한 다양한 더미 데이터를 손쉽게 생성할 수 있습니다.

NOTE

Faker의 로케일은 config/app.phpfaker_locale 옵션으로 변경할 수 있습니다. 예를 들어 'faker_locale' => 'ko_KR'로 설정하면 한국어 더미 데이터를 생성합니다.

모델 팩토리 정의하기

팩토리 생성

팩토리를 새로 만들려면 make:factory Artisan 명령어를 실행합니다:

php artisan make:factory PostFactory

생성된 팩토리 파일은 database/factories 디렉터리에 위치합니다.

모델과 팩토리의 자동 연결 규칙

팩토리를 정의한 후에는 모델에 Illuminate\Database\Eloquent\Factories\HasFactory 트레이트가 제공하는 정적 factory 메서드를 사용해 팩토리 인스턴스를 가져올 수 있습니다.

HasFactory 트레이트의 factory 메서드는 다음 규칙에 따라 적절한 팩토리를 자동으로 탐색합니다. Database\Factories 네임스페이스 안에서 모델명 뒤에 Factory가 붙은 클래스를 찾습니다. 예를 들어 App\Models\Post 모델이라면 Database\Factories\PostFactory를 찾습니다.

이 규칙이 프로젝트 구조와 맞지 않는 경우, 모델에서 newFactory 메서드를 직접 오버라이드해 원하는 팩토리 인스턴스를 반환하면 됩니다:

use Database\Factories\Administration\FlightFactory; /** * 모델에 대응하는 팩토리 인스턴스를 생성합니다. */ protected static function newFactory() { return FlightFactory::new(); }

그리고 해당 팩토리에서는 model 프로퍼티를 지정합니다:

use App\Administration\Flight; use Illuminate\Database\Eloquent\Factories\Factory; class FlightFactory extends Factory { /** * 이 팩토리가 대응하는 모델 클래스 * * @var class-string<\Illuminate\Database\Eloquent\Model> */ protected $model = Flight::class; }

팩토리 상태(State)

상태(State) 메서드를 사용하면 기본 팩토리에 조합 가능한 부분 변경 사항을 정의할 수 있습니다. 예를 들어 Database\Factories\UserFactorysuspended 상태 메서드를 추가해, 특정 속성만 바꾼 변형 모델을 만들 수 있습니다.

상태 변환 메서드는 보통 Laravel 기본 팩토리 클래스의 state 메서드를 호출합니다. state 메서드는 팩토리의 원본 속성 배열을 받아 변경할 속성 배열을 반환하는 클로저를 인수로 받습니다:

use Illuminate\Database\Eloquent\Factories\Factory; /** * 사용자를 정지(suspended) 상태로 표시합니다. */ public function suspended(): Factory { return $this->state(function (array $attributes) { return [ 'account_status' => 'suspended', ]; }); }

"Trashed" 상태

Eloquent 모델이 소프트 삭제를 지원한다면, 내장된 trashed 상태 메서드를 사용해 생성된 모델이 이미 소프트 삭제된 상태로 만들 수 있습니다. trashed 상태는 별도로 정의하지 않아도 모든 팩토리에서 자동으로 사용 가능합니다:

use App\Models\User; $user = User::factory()->trashed()->create();

팩토리 콜백

팩토리 콜백은 afterMakingafterCreating 메서드로 등록하며, 모델을 인스턴스화하거나 저장한 직후에 추가 작업을 실행할 수 있습니다. 이 콜백은 팩토리 클래스의 configure 메서드 안에서 등록해야 하며, configure 메서드는 팩토리가 인스턴스화될 때 Laravel이 자동으로 호출합니다:

namespace Database\Factories; use App\Models\User; use Illuminate\Database\Eloquent\Factories\Factory; class UserFactory extends Factory { /** * 팩토리를 설정합니다. */ public function configure(): static { return $this->afterMaking(function (User $user) { // 모델 인스턴스화 후 실행 })->afterCreating(function (User $user) { // 모델 DB 저장 후 실행 }); } // ... }

특정 상태에 한정된 후처리 작업이 필요하다면 상태 메서드 안에서도 콜백을 등록할 수 있습니다:

use App\Models\User; use Illuminate\Database\Eloquent\Factories\Factory; /** * 사용자를 정지 상태로 표시합니다. */ public function suspended(): Factory { return $this->state(function (array $attributes) { return [ 'account_status' => 'suspended', ]; })->afterMaking(function (User $user) { // 인스턴스화 후 실행 })->afterCreating(function (User $user) { // 저장 후 실행 }); }

팩토리로 모델 생성하기

모델 인스턴스화

팩토리를 정의했다면 HasFactory 트레이트가 제공하는 정적 factory 메서드로 팩토리 인스턴스를 얻을 수 있습니다. make 메서드는 모델을 데이터베이스에 저장하지 않고 인스턴스만 생성합니다:

use App\Models\User; $user = User::factory()->make();

count 메서드를 사용하면 여러 모델을 컬렉션으로 생성할 수 있습니다:

$users = User::factory()->count(3)->make();

상태 적용

팩토리 상태를 하나 또는 여러 개 조합해서 적용할 수 있습니다:

$users = User::factory()->count(5)->suspended()->make();

속성 덮어쓰기

기본값의 일부를 바꾸고 싶다면 make 메서드에 배열을 전달합니다. 지정한 속성만 교체되고 나머지는 팩토리 기본값이 그대로 사용됩니다:

$user = User::factory()->make([ 'name' => '홍길동', ]);

또는 팩토리 인스턴스에서 state 메서드를 직접 호출해 인라인 상태 변환을 적용할 수도 있습니다:

$user = User::factory()->state([ 'name' => '홍길동', ])->make();

NOTE

팩토리로 모델을 생성할 때는 대량 할당 보호가 자동으로 비활성화됩니다.

모델 저장

create 메서드는 모델 인스턴스를 생성하고 Eloquent의 save 메서드를 통해 데이터베이스에 저장합니다:

use App\Models\User; // 단일 User 인스턴스 생성 및 저장 $user = User::factory()->create(); // 3개의 User 인스턴스 생성 및 저장 $users = User::factory()->count(3)->create();

create 메서드에 속성 배열을 전달하면 기본값을 덮어쓸 수 있습니다:

$user = User::factory()->create([ 'name' => '김철수', ]);

시퀀스(Sequence)

여러 모델을 생성할 때 특정 속성값을 순서대로 번갈아 지정하고 싶은 경우 Sequence를 활용합니다. 예를 들어 생성되는 사용자마다 admin 컬럼 값을 'Y''N'으로 번갈아 설정하려면:

use App\Models\User; use Illuminate\Database\Eloquent\Factories\Sequence; $users = User::factory() ->count(10) ->state(new Sequence( ['admin' => 'Y'], ['admin' => 'N'], )) ->create();

이 예시에서는 admin'Y'인 사용자 5명과 'N'인 사용자 5명이 생성됩니다.

클로저를 시퀀스 값으로 사용할 수도 있습니다. 클로저는 새 값이 필요할 때마다 호출됩니다:

use Illuminate\Database\Eloquent\Factories\Sequence; $users = User::factory() ->count(10) ->state(new Sequence( fn (Sequence $sequence) => ['role' => UserRoles::all()->random()], )) ->create();

클로저 안에서는 주입된 Sequence 인스턴스의 $index(현재까지 반복된 횟수)와 $count(전체 호출 횟수) 프로퍼티에 접근할 수 있습니다:

$users = User::factory() ->count(10) ->sequence(fn (Sequence $sequence) => ['name' => '사용자 '.$sequence->index]) ->create();

편의를 위해 sequence 메서드를 체인으로 직접 호출할 수도 있습니다. 이 메서드는 내부적으로 state를 호출하며, 클로저나 속성 배열을 인수로 받습니다:

$users = User::factory() ->count(2) ->sequence( ['name' => '첫 번째 사용자'], ['name' => '두 번째 사용자'], ) ->create();

팩토리 관계(Relationships)

Has Many 관계

Laravel 팩토리의 메서드 체인으로 Eloquent 관계를 손쉽게 구성할 수 있습니다. 예를 들어 User 모델이 PosthasMany 관계를 맺고 있다면, has 메서드를 사용해 게시글 3개를 가진 사용자를 한 번에 생성할 수 있습니다:

use App\Models\Post; use App\Models\User; $user = User::factory() ->has(Post::factory()->count(3)) ->create();

규칙에 따라 Post 모델을 has 메서드에 전달하면 Laravel은 User 모델에 posts 메서드가 있다고 자동으로 판단합니다. 관계명을 직접 지정하려면 두 번째 인수로 전달합니다:

$user = User::factory() ->has(Post::factory()->count(3), 'posts') ->create();

관련 모델에 상태 변환을 적용할 수도 있으며, 부모 모델에 접근이 필요한 경우 클로저를 사용합니다:

$user = User::factory() ->has( Post::factory() ->count(3) ->state(function (array $attributes, User $user) { return ['user_type' => $user->type]; }) ) ->create();

매직 메서드 활용

Laravel의 매직 팩토리 메서드를 사용하면 코드를 더 간결하게 작성할 수 있습니다. 아래 예시는 User 모델의 posts 관계를 통해 게시글 3개를 생성합니다:

$user = User::factory() ->hasPosts(3) ->create();

매직 메서드로 관계를 생성할 때 관련 모델의 속성을 배열로 전달해 덮어쓸 수 있습니다:

$user = User::factory() ->hasPosts(3, [ 'published' => false, ]) ->create();

부모 모델 접근이 필요한 경우 클로저를 사용합니다:

$user = User::factory() ->hasPosts(3, function (array $attributes, User $user) { return ['user_type' => $user->type]; }) ->create();

Belongs To 관계

hasMany 관계의 역방향인 belongsTo 관계를 설정하려면 for 메서드를 사용합니다. 예를 들어 동일한 사용자에게 속하는 게시글 3개를 생성하려면:

use App\Models\Post; use App\Models\User; $posts = Post::factory() ->count(3) ->for(User::factory()->state([ 'name' => '박지수', ])) ->create();

이미 생성된 부모 모델 인스턴스가 있다면 그대로 전달할 수 있습니다:

$user = User::factory()->create(); $posts = Post::factory() ->count(3) ->for($user) ->create();

매직 메서드 활용

belongsTo 관계도 매직 메서드로 간결하게 표현할 수 있습니다. 아래 예시는 Post 모델의 user 관계를 자동으로 찾아 설정합니다:

$posts = Post::factory() ->count(3) ->forUser([ 'name' => '박지수', ]) ->create();

다대다(Many to Many) 관계

Has Many 관계와 마찬가지로 has 메서드로 다대다 관계를 생성할 수 있습니다:

use App\Models\Role; use App\Models\User; $user = User::factory() ->has(Role::factory()->count(3)) ->create();

피벗 테이블 속성

두 모델을 연결하는 피벗(중간) 테이블에 속성을 설정해야 한다면 hasAttached 메서드를 사용합니다. 두 번째 인수로 피벗 테이블 속성명과 값의 배열을 전달합니다:

use App\Models\Role; use App\Models\User; $user = User::factory() ->hasAttached( Role::factory()->count(3), ['active' => true] ) ->create();

관련 모델 접근이 필요한 경우 클로저를 사용합니다:

$user = User::factory() ->hasAttached( Role::factory() ->count(3) ->state(function (array $attributes, User $user) { return ['name' => $user->name.' 역할']; }), ['active' => true] ) ->create();

이미 생성된 모델 인스턴스를 연결할 수도 있습니다. 아래 예시에서는 동일한 역할 3개가 사용자 3명 모두에게 연결됩니다:

$roles = Role::factory()->count(3)->create(); $user = User::factory() ->count(3) ->hasAttached($roles, ['active' => true]) ->create();

매직 메서드 활용

다대다 관계도 매직 메서드로 간결하게 표현할 수 있습니다. 아래 예시는 User 모델의 roles 관계를 자동으로 찾아 설정합니다:

$user = User::factory() ->hasRoles(1, [ 'name' => '편집자' ]) ->create();

폴리모픽(Polymorphic) 관계

폴리모픽 관계도 팩토리로 생성할 수 있습니다. 폴리모픽 "morph many" 관계는 일반 "has many" 관계와 동일한 방식으로 생성합니다. 예를 들어 Post 모델이 Comment 모델과 morphMany 관계를 맺고 있다면:

use App\Models\Post; $post = Post::factory()->hasComments(3)->create();

Morph To 관계

morphTo 관계는 매직 메서드로 생성할 수 없으며, 반드시 for 메서드를 직접 사용하고 관계명을 명시적으로 지정해야 합니다. 예를 들어 Comment 모델에 commentable이라는 morphTo 관계 메서드가 있다면:

$comments = Comment::factory()->count(3)->for( Post::factory(), 'commentable' )->create();

폴리모픽 다대다 관계

폴리모픽 다대다(morphToMany / morphedByMany) 관계는 일반 다대다 관계와 동일하게 생성할 수 있습니다:

use App\Models\Tag; use App\Models\Video; $videos = Video::factory() ->hasAttached( Tag::factory()->count(3), ['public' => true] ) ->create();

매직 has 메서드도 사용할 수 있습니다:

$videos = Video::factory() ->hasTags(3, ['public' => true]) ->create();

팩토리 내부에서 관계 정의하기

팩토리 안에서 관계를 정의할 때는 보통 외래 키(foreign key)에 팩토리 인스턴스를 할당합니다. 이 방식은 belongsTomorphTo 같은 역방향 관계에 주로 사용됩니다. 예를 들어 게시글을 생성할 때 새 사용자도 함께 생성하려면:

use App\Models\User; /** * 모델의 기본 상태를 정의합니다. * * @return array<string, mixed> */ public function definition(): array { return [ 'user_id' => User::factory(), 'title' => fake()->title(), 'content' => fake()->paragraph(), ]; }

관계의 컬럼 값이 팩토리의 다른 속성에 의존하는 경우, 클로저를 사용해 평가된 속성 배열을 참조할 수 있습니다:

/** * 모델의 기본 상태를 정의합니다. * * @return array<string, mixed> */ public function definition(): array { return [ 'user_id' => User::factory(), 'user_type' => function (array $attributes) { return User::find($attributes['user_id'])->type; }, 'title' => fake()->title(), 'content' => fake()->paragraph(), ]; }

관계에서 기존 모델 재사용하기

여러 관계가 동일한 모델 인스턴스를 공유해야 하는 경우 recycle 메서드를 활용하면 팩토리가 생성하는 모든 관계에서 단일 인스턴스를 재사용할 수 있습니다.

예를 들어 항공사(Airline), 항공편(Flight), 티켓(Ticket) 모델이 있고, 티켓은 항공사와 항공편 모두에 속하며 항공편도 항공사에 속한다고 가정합니다. 티켓을 생성할 때 티켓과 항공편에 동일한 항공사가 사용되도록 하려면 recycle에 항공사 인스턴스를 전달합니다:

Ticket::factory() ->recycle(Airline::factory()->create()) ->create();

recycle 메서드는 동일한 사용자나 팀에 속하는 여러 모델을 생성할 때 특히 유용합니다.

recycle 메서드는 기존 모델 컬렉션도 받을 수 있습니다. 컬렉션이 전달되면 팩토리가 해당 타입의 모델이 필요할 때마다 컬렉션에서 무작위로 하나를 선택합니다:

Ticket::factory() ->recycle($airlines) ->create();

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

번역일: 2026년 6월 21일