모델 팩토리

프로젝트를 진행하다 보면 테스트를 위한 데이타가 필요한 때가 많습니다.

  • 성능 테스트를 위해 대규모의 중복되지 않은 데이타
  • 작성한 쿼리 로직이 잘 동작하는지 확인하기 위해 다양한 검색 조건을 사용할 수 있도록 고르게 분포된 데이타
  • 페이징 및 뷰 단에서 데이타가 잘 표시되는지 확인

 

이런 테스트 데이타는 실제 데이타와 비슷하게 의미있어야 합니다. 즉 만드는 서비스가 전 세계 우편 번호를 저장하고 검색하는 기능이 있다면 테스트 데이타를 이를 반영하여 전 세계의 실제 우편 번호를 고르게 삽입해야 합니다.

 

이렇게 의미있는 테스트 데이타를 만드는 것은 매우 중요하지만 개발 단계에서 개발과 테스트 용도로만 필요하므로 실제로 시간을 많이 쓸 수는 없습니다.

이런 문제를 해결하기 위해 의미있는 테스트 데이타를 만들어주는 Faker(https://github.com/fzaninotto/Faker) 라는 유명한 PHP 라이브러리가 있고 개발 단계에서 유용하게 사용할 수 있습니다.

모델 팩토리(Model Factory)는 라라벨 5 에 추가된 기능으로  Faker 라이브러리를 사용하여 Model 클래스에서 바로 테스트 데이타를 만들수 있는 기능으로 손쉽게 데이타를 생성하고 입력할 수 있습니다.

 

에디터로 database/factories/ModelFactory.php 을 열어서 모델 팩토리가 무엇인지 알아 봅시다.

database/factories/ModelFactory.php

$factory->define(App\User::class, function (Faker\Generator $faker) {
    return [
        'name' => $faker->name,
        'email' => $faker->email,
        'password' => str_random(10),
        'remember_token' => str_random(10),
    ];
});
PHP

모델 팩토리는 define() 메소드를 사용하여 정의할 수 있으며 첫 번째 파라미터로 모델클래스, 두 번째 파라미터로 클로저를 받습니다.

클로저는 faker 객체를 변수로 받으며 사전에 정의된 다양한 형식을 사용하여 모델의 컬럼에 맞는 다양한 데이타를 생성할 수 있습니다. 위 예제에서 생성하는 데이타 의미는 다음과 같습니다.

  • 'name' => $faker->name :  Faker 라이브러리에 미리 정의된 이름중 하나를 랜덤하게 가져옵니다.
  • 'email' => $faker->email :  Faker 라이브러리에 미리 정의된 이메일중 하나를 랜덤하게 가져옵니다.
  • 'password' => str_random(10) :  암호로 사용할 10자의 문자를 랜덤하게 생성합니다.

 

모델 팩토리 생성

이제 프로젝트 모델 팩토리를 만들어 보겠습니다. 위 소스 뒤에 이어서 다음 내용을 추가합니다.

database/factories/ModelFactory.php

$factory->define(App\Project::class, function (Faker\Generator $faker) {
    // 집계함수를 사용하여 id 의 최소, 최대값을 가져옴
	$min = App\User::min('id');	
	$max = App\User::max('id');
    return [
        'user_id' => $faker->numberBetween($min, $max),
        'name' => $faker->word,        
        'created_at' => $faker->dateTimeBetween($startDate = '-2 years', $endDate = '-1 years'),
        'updated_at' => $faker->dateTimeBetween($startDate = '-1 years', $endDate = 'now'),
    ];
});
PHP
  • 'user_id' => $faker->numberBetween($min, $max)  : user_id 는 user 테이블을 참조해야 하므로 유효한 id 의 최소, 최대 값을 가져와서 구간내의 임의의 값을 설정합니다.

  • 'name' => $faker->word : Faker 라이브러리에 미리 정해진 문자열을 프로젝트 이름에 할당합니다.

  • 'create_at' => $faker->dateTimeBetween : 생성일은 현재부터 과거 2년에서 1년 사이의 임의의 날자를 생성합니다.

  • 'update_at' => $faker->dateTimeBetween : 갱신일은 과거 1년에서 현재 사이의 임의의 날자를 생성합니다.

 

태스크 모델 팩토리도 프로젝트 모델 팩토리뒤에 이어 정의해 주면 됩니다.

$factory->define(App\Task::class, function (Faker\Generator $faker) {
	$min = App\Project::min('id');    
	$max = App\Project::max('id');
    return [
    	'project_id' => $faker->numberBetween($min, $max),
        'name' => substr($faker->sentence, 0, 49),
        'description' => $faker->text,
        'created_at' => $faker->dateTimeBetween($startDate = '-2 years', $endDate = '-1 years'),
        'updated_at' => $faker->dateTimeBetween($startDate = '-1 years', $endDate = 'now'),
		'due_date' => $faker->dateTimeBetween($startDate = '-2 weeks', $endDate = '+1 months'),
    ];
});
CODE
  • 'project_id' => $faker->numberBetween : 모든 Task 는 프로젝트에 포함되어야 하므로 유효한 project id 의 최소/최대 값을 가져온 후에 그 사이의 임의의 숫자를 할당합니다.

  • 'name' =>  substr($faker→sentence, 0, 49): Task 이름은 랜덤 문자열을 사용하며 컬럼의 길이를 넘지 않도록 substr 로 잘라냅니다.

  • 'description' => $faker->text : Task 에 대한 상세 기술은 긴 랜덤 문자열을 사용합니다.

  • 'due_date' => $faker->dateTimeBetween : 완료 기한은 만료일이 최근인 태스크를 찾는 기능 테스트를 위해 2주전(-2 weeks) 과 1달후(+1 months) 사이의 랜덤한 날자를 할당합니다.

 

이제 생성한 모델 팩토리를 전 절에서 설명한 REPL 인 tinker 를 사용하여 확인해 봅시다.

$ php artisan tinker
CODE

모델 팩토리는 factory() 헬퍼 함수를 사용하여 만들 수 있습니다. 첫번째 파라미터는 생성할 모델의 이름을 주고 make() 메소드로 모델을 생성합니다.

>>> factory(App\Task::class)->make();
=> <App\Task #000000002f923f6c00000000094e0474> {
       project_id: 2,
       name: "Quos accusantium dolores laborum velit.",
       description: "Repellat atque dolorem ut vel.",
       created_at: "2013-07-28 07:00:33",
       updated_at: "2014-12-13 08:13:43",
	   due_date: "2016-03-13 08:13:43"
   }
CODE

여러 개를 생성하려면 다음 예제처럼 두 번째 파라미터로 갯수를 지정하면 됩니다.

>>> factory(App\Task::class, 10)->make();
CODE

 

Persistence 데이타 생성

make() 는 임시 데이타만 생성하므로 DB 에 직접 입력하려면 create() 를 사용하면 되며 다음은 100개의 Task 레코드를 만드는 예제입니다.

>>> factory(App\Task::class, 100)->create();
CODE

 

모델 팩토리에 대해 더 자세한 내용은 필자의 블로그(http://lesstif.com/x/7QKOAQ)를 참고하세요.