Eloquent 팩토리
번역일: 2026년 6월 21일
Eloquent 팩토리
소개
애플리케이션을 테스트하거나 데이터베이스에 초기 데이터를 심을 때, 여러 레코드를 직접 하나하나 입력하는 것은 번거롭습니다. Laravel의 모델 팩토리를 사용하면 각 Eloquent 모델에 대한 기본 속성값을 미리 정의해두고, 필요할 때 손쉽게 모델 인스턴스를 만들 수 있습니다.
팩토리가 어떻게 생겼는지 확인하려면 프로젝트 내 database/factories/UserFactory.php 파일을 열어보세요. 모든 새 Laravel 프로젝트에는 다음과 같은 UserFactory가 기본으로 포함되어 있습니다.
namespace Database\Factories;
use Illuminate\Support\Str;
use Illuminate\Database\Eloquent\Factories\Factory;
class UserFactory extends Factory
{
/**
* 모델의 기본 상태를 정의합니다.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'name' => fake()->name(),
'email' => fake()->unique()->safeEmail(),
'email_verified_at' => now(),
'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
'remember_token' => Str::random(10),
];
}
}보시다시피 팩토리는 Laravel의 기본 Factory 클래스를 상속하고 definition 메서드를 구현하는 클래스입니다. definition 메서드는 팩토리로 모델을 생성할 때 적용될 기본 속성값 배열을 반환합니다.
fake() 헬퍼를 통해 Faker PHP 라이브러리를 사용할 수 있으며, 이름·이메일·주소·문단 등 다양한 종류의 가짜 데이터를 편리하게 생성할 수 있습니다.
NOTE
config/app.php에 faker_locale 옵션을 추가하면 Faker의 로케일을 변경할 수 있습니다. 한국어 데이터가 필요하다면 '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 Illuminate\Database\Eloquent\Factories\Factory;
use Database\Factories\Administration\FlightFactory;
/**
* 이 모델에 대응하는 팩토리 인스턴스를 반환합니다.
*/
protected static function newFactory(): Factory
{
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) 메서드를 사용하면 기본값의 일부만 변경한 변형 팩토리를 정의할 수 있습니다. 여러 상태를 조합해서 사용하는 것도 가능합니다.
예를 들어 UserFactory에 계정이 정지된 상태를 나타내는 suspended 상태를 추가할 수 있습니다. 상태 메서드는 기본적으로 state 메서드에 클로저를 전달하는 방식으로 작성합니다. 클로저는 현재 팩토리의 속성 배열을 받아 변경할 속성만 반환합니다.
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* 계정이 정지된 상태를 나타냅니다.
*/
public function suspended(): Factory
{
return $this->state(function (array $attributes) {
return [
'account_status' => 'suspended',
];
});
}"Trashed" 상태
Eloquent 모델이 소프트 삭제를 지원하는 경우, 내장된 trashed 상태 메서드를 호출하면 생성된 모델이 이미 소프트 삭제된 것처럼 처리됩니다. 별도로 정의할 필요 없이 모든 팩토리에서 바로 사용할 수 있습니다.
use App\Models\User;
$user = User::factory()->trashed()->create();팩토리 콜백
팩토리 콜백을 사용하면 모델을 만들거나(afterMaking) 저장한 후(afterCreating) 추가 작업을 수행할 수 있습니다. 팩토리 클래스의 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) {
// 데이터베이스 저장 후 실행
});
}
// ...
}특정 상태에서만 실행할 콜백이 필요하다면, 상태 메서드 안에서도 등록할 수 있습니다.
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' => '김철수',
]);시퀀스(Sequences)
여러 모델을 한꺼번에 생성할 때 특정 속성값을 번갈아가며 지정하고 싶다면 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 관계
User 모델이 Post 모델과 hasMany 관계를 갖는다고 가정해봅시다. has 메서드에 팩토리 인스턴스를 전달하면 관련 모델을 함께 생성할 수 있습니다.
use App\Models\Post;
use App\Models\User;
$user = User::factory()
->has(Post::factory()->count(3))
->create();Laravel은 Post 모델이 전달된 경우, 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();매직 메서드 사용하기
더 간결하게 작성하려면 매직 메서드를 활용할 수 있습니다. 아래 예시는 User 모델의 posts 관계 메서드 이름을 자동으로 추론합니다.
$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 관계
이번에는 반대 방향, 즉 belongsTo 관계를 살펴보겠습니다. for 메서드를 사용하면 생성할 모델이 속할 부모 모델을 지정할 수 있습니다. 아래 예시는 하나의 사용자에 속하는 Post 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.' Role'];
}),
['active' => true]
)
->create();이미 생성된 모델 인스턴스들을 첨부하려면 그대로 전달하면 됩니다. 아래 예시에서는 동일한 세 개의 권한(Role)이 세 명의 사용자 모두에게 연결됩니다.
$roles = Role::factory()->count(3)->create();
$user = User::factory()
->count(3)
->hasAttached($roles, ['active' => true])
->create();매직 메서드 사용하기
다대다 관계도 매직 메서드를 사용할 수 있습니다. 아래 예시는 User 모델의 roles 관계를 자동으로 추론합니다.
$user = User::factory()
->hasRoles(1, [
'name' => 'Editor'
])
->create();다형성(Polymorphic) 관계
다형성 관계도 팩토리로 만들 수 있습니다. morphMany 관계는 일반 hasMany와 동일한 방식으로 생성됩니다. 예를 들어 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();팩토리 내부에서 관계 정의하기
belongsTo나 morphTo 같은 역방향 관계는 팩토리의 definition 메서드에서 외래 키에 팩토리 인스턴스를 직접 할당하여 정의하는 방식이 일반적입니다. 예를 들어 게시글을 생성할 때 새 사용자도 함께 생성하려면 다음과 같이 작성합니다.
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 모델이 있고, Ticket은 Airline과 Flight 모두에 속하며, Flight도 Airline에 속한다고 가정합시다. 티켓과 항공편에 동일한 항공사가 연결되어야 한다면 recycle 메서드에 항공사 인스턴스를 전달하세요.
Ticket::factory()
->recycle(Airline::factory()->create())
->create();recycle 메서드는 공통 사용자나 팀에 속하는 모델들을 생성할 때 특히 유용합니다.
컬렉션을 전달하면, 팩토리가 해당 타입의 모델이 필요할 때마다 컬렉션에서 무작위로 하나를 선택하여 사용합니다.
Ticket::factory()
->recycle($airlines)
->create();