Eloquent: 팩토리
업데이트됨번역일: 2026년 6월 21일
이 페이지는 원문이 업데이트되어 번역이 갱신되었습니다.
- 원문 수정
- 2026년 6월 20일
- 번역 갱신
- 2026년 6월 21일
Eloquent: 팩토리
소개
테스트를 작성하거나 데이터베이스에 시드 데이터를 채울 때, 레코드를 직접 하나하나 삽입하는 방식은 번거롭습니다. Laravel은 각 Eloquent 모델에 대해 기본 속성값 세트를 정의할 수 있는 모델 팩토리 기능을 제공합니다. 팩토리를 사용하면 테스트나 시딩에 필요한 더미 데이터를 간결하게 만들 수 있습니다.
팩토리가 어떻게 생겼는지 확인하려면 database/factories/UserFactory.php 파일을 열어보세요. 이 파일은 새 Laravel 애플리케이션에 기본으로 포함되어 있습니다:
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 라이브러리를 활용할 수 있으며, 이름, 이메일, 문단 등 다양한 형태의 임의 데이터를 쉽게 생성할 수 있습니다.
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를 붙인 클래스를 탐색합니다. 예를 들어 Post 모델이라면 Database\Factories\PostFactory를 찾습니다.
이 규칙이 프로젝트 구조와 맞지 않는 경우, 모델에 UseFactory 어트리뷰트를 지정해 팩토리 클래스를 명시적으로 연결할 수 있습니다:
use Illuminate\Database\Eloquent\Attributes\UseFactory;
use Database\Factories\Administration\FlightFactory;
#[UseFactory(FlightFactory::class)]
class Flight extends Model
{
// ...
}또는 모델의 newFactory 메서드를 오버라이드해서 직접 팩토리 인스턴스를 반환할 수도 있습니다:
use Database\Factories\Administration\FlightFactory;
/**
* 이 모델에 해당하는 팩토리 인스턴스를 생성합니다.
*/
protected static function newFactory()
{
return FlightFactory::new();
}이 경우 팩토리에는 UseModel 어트리뷰트로 대상 모델을 명시해야 합니다:
use App\Administration\Flight;
use Illuminate\Database\Eloquent\Factories\Attributes\UseModel;
use Illuminate\Database\Eloquent\Factories\Factory;
#[UseModel(Flight::class)]
class FlightFactory extends Factory
{
// ...
}팩토리 상태(State)
상태(State) 메서드를 사용하면 팩토리의 기본값 일부를 변경하는 조각을 정의하고, 이를 조합해서 다양한 형태의 모델을 만들 수 있습니다. 예를 들어 UserFactory에 suspended 상태를 추가해 계정 상태 컬럼을 변경할 수 있습니다.
상태 메서드는 보통 기본 Factory 클래스의 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 상태 메서드를 통해 이미 소프트 삭제된 모델을 생성할 수 있습니다. 별도로 정의하지 않아도 모든 팩토리에서 자동으로 사용할 수 있습니다:
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) {
// make() 후 실행할 작업
})->afterCreating(function (User $user) {
// create() 후 실행할 작업
});
}
// ...
}특정 상태 메서드 안에서도 콜백을 등록해, 해당 상태에만 적용되는 추가 로직을 처리할 수 있습니다:
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* 사용자가 정지(suspended) 상태임을 나타냅니다.
*/
public function suspended(): Factory
{
return $this->state(function (array $attributes) {
return [
'account_status' => 'suspended',
];
})->afterMaking(function (User $user) {
// make() 후 실행할 작업
})->afterCreating(function (User $user) {
// create() 후 실행할 작업
});
}팩토리로 모델 생성
모델 인스턴스 생성
팩토리를 정의했다면 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();이 예시에서는 5명은 admin = Y, 나머지 5명은 admin = N으로 생성됩니다.
시퀀스 값으로 클로저를 사용하면 매번 새 값이 필요할 때마다 클로저가 호출됩니다:
use Illuminate\Database\Eloquent\Factories\Sequence;
$users = User::factory()
->count(10)
->state(new Sequence(
fn (Sequence $sequence) => ['role' => UserRoles::all()->random()],
))
->create();클로저 안에서 $sequence->index 속성으로 현재까지 몇 번 반복됐는지 확인할 수 있습니다:
$users = User::factory()
->count(10)
->state(new Sequence(
fn (Sequence $sequence) => ['name' => '사용자 '.$sequence->index],
))
->create();sequence 메서드를 사용하면 state(new Sequence(...)) 없이 더 간결하게 작성할 수 있습니다. 클로저와 배열 모두 지원합니다:
$users = User::factory()
->count(2)
->sequence(
['name' => '첫 번째 사용자'],
['name' => '두 번째 사용자'],
)
->create();팩토리 연관관계
Has Many 관계
팩토리의 유창한(Fluent) 메서드를 사용해 Eloquent 연관관계를 가진 모델도 쉽게 생성할 수 있습니다. User 모델이 Post 모델과 hasMany 관계를 가질 때, 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();각 모델마다 다른 속성을 지정하고 싶다면 배열을 여러 개 전달하세요. Laravel이 순서대로 각 모델에 적용합니다:
$user = User::factory()
->hasPosts(
['title' => '첫 번째 글'],
['title' => '두 번째 글'],
['title' => '세 번째 글'],
)
->create();부모 모델에 접근이 필요한 경우 클로저를 사용합니다:
$user = User::factory()
->hasPosts(3, function (array $attributes, User $user) {
return ['user_type' => $user->type];
})
->create();Belongs To 관계
Has Many의 반대 방향 관계를 생성하려면 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 메서드로 생성할 수 있습니다:
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();연관 모델마다 다른 피벗 데이터를 지정하려면 피벗 배열을 여러 개 전달합니다:
$user = User::factory()
->hasAttached(
Role::factory(),
[
['active' => true],
['active' => false],
]
)
->create();이미 생성된 모델 인스턴스를 연결하고 싶다면 인스턴스를 직접 전달합니다. 아래 예시에서는 동일한 3개의 역할이 3명의 사용자 모두에게 연결됩니다:
$roles = Role::factory()->count(3)->create();
$users = User::factory()
->count(3)
->hasAttached($roles, ['active' => true])
->create();매직 메서드 활용
다대다 관계도 매직 메서드로 간편하게 생성할 수 있습니다. 아래 예시는 User 모델의 roles 관계를 통해 역할을 생성합니다:
$user = User::factory()
->hasRoles(1, [
'name' => '에디터'
])
->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;
$video = Video::factory()
->hasAttached(
Tag::factory()->count(3),
['public' => true]
)
->create();매직 has 메서드도 동일하게 사용할 수 있습니다:
$video = Video::factory()
->hasTags(3, ['public' => true])
->create();팩토리 내부에서 연관관계 정의
팩토리의 definition 메서드 안에서 연관관계를 정의할 수도 있습니다. 주로 belongsTo나 morphTo처럼 외래 키를 가진 쪽의 팩토리에서 사용합니다. 예를 들어 게시글을 생성할 때 새 사용자도 함께 만들려면:
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 메서드는 모델 컬렉션도 받을 수 있습니다. 컬렉션을 전달하면 해당 타입의 모델이 필요할 때마다 컬렉션에서 무작위로 하나를 선택해 사용합니다:
Ticket::factory()
->recycle($airlines)
->create();