왜 나는 Migrun을 만들었는가
Source: Dev.to
기존 PHP 마이그레이션 도구들의 문제점
저는 주로 Laravel이나 Symfony 같은 전체 프레임워크 안에 있지 않은 PHP 프로젝트를 다룹니다 — 자체 ORM 기반 마이그레이션 도구를 제공하는 프레임워크가 아닌 경우 말이죠.
독립형 마이그레이션 도구(Phinx, Phpmig, Phoenix)는 존재하지만, 수년간 저를 괴롭혀 온 문제들이 있습니다.
Phinx는 실제로 독립형이 아니다
- Cake 프레임워크의 코어를 끌어옵니다.
- 서비스 컨테이너 통합이 해킹이다.
- Phinx 마이그레이션에 서비스를 주입하는 것이 불가능합니다.
- 사람들은 전역 정적 접근이나 기타 우회 방법을 사용해 자체 DB 연결이나 로거를 마이그레이션에 넣습니다.
- 이는 현대 DI 관행에 완전히 어긋납니다.
- 저는 마이그레이션이 단순히 자신의 의존성을 선언하고 컨테이너에서 자동으로 해결되길 원했습니다.
쿼리 빌더는 불필요한 복잡성을 추가한다
마이그레이션 시스템은 종종 테이블 빌더—컬럼, 인덱스, 테이블을 데이터베이스에 독립적인 방식으로 생성하기 위한 API를 제공하는데, 이는 보통 프레임워크의 ORM을 기반으로 합니다. 이론적으로는 마이그레이션을 다시 작성하지 않고도 데이터베이스를 교체할 수 있게 해줍니다. 실제로는:
- 대부분의 프로젝트는 데이터베이스를 교체하지 않습니다. 제가 작업한 프로젝트 중 어느 하나도 교체한 적이 없습니다. 새로운 데이터베이스를 추가는 하지만 기존 것을 대체하진 않죠.
- 교체가 필요할 경우에도 데이터베이스별 기능, 데이터 타입, 동작을 따로 처리해야 합니다.
- 빌더 API는 모든 데이터베이스별 케이스를 커버할 수 없습니다(예: Laravel에서 흔히 보는 “커스텀 사이즈의 tiny integer” 문제). Phinx는 더 제한적이며, 복잡한 부분은 결국 raw SQL을 직접 작성하게 됩니다.
- 빌더는 단순히
CREATE TABLE문 하나로 충분히 해결되는 일을 추상화 레이어와 복잡성을 추가합니다.
빌더가 쓸모없다고 말하는 것은 아니지만, 많은 프로젝트에서는 불필요한 오버헤드가 됩니다.
상속 기반 설계
모든 도구가 상속을 사용합니다. 마이그레이션 클래스는 AbstractMigration 기본 클래스를 확장해야 하며, 이는 클래스 계층 구조에 얽매이게 합니다. 현대 PHP에서는 상속보다 컴포지션과 인터페이스를 선호합니다; 상속 기반 설계는 경직되고 자체 아키텍처와 통합하기 어렵습니다.
경직된 네이밍 규칙 및 디렉터리 구조
도구들은 채택자에게 네이밍 규칙과 경직된 디렉터리 구조를 강요합니다. 수백 개가 쌓인 마이그레이션을 아카이브해야 할 때? 옵션이 아니거나 매우 번거로운 작업이 됩니다.
오래된 코드베이스
PHP 생태계의 마이그레이션 도구들은 언어와 함께 진화하지 못했습니다. PHP에는 이제 readonly 클래스, enum, named arguments, union type, fiber, pipeline 등 다양한 기능이 있지만, 마이그레이션 도구들은 여전히 PHP 5 시절 패턴을 사용하고 있습니다.
I/O 통합이 해킹이다
Phinx는 Symfony Console 안에서만 동작하며, 실행기는 그것에 강하게 결합돼 있습니다. 마이그레이션 목록을 HTML 페이지로 만들고 싶나요? 행운을 빕니다.
내가 원한 것
- 마이그레이션을 위한 인터페이스.
up()을 구현하고, 필요하면down()도 구현. 끝. - 서비스 컨테이너에서 자동 주입.
PDO $db를 매개변수로 선언하면 자동으로 해결됩니다. - 확장할 기본 클래스가 없습니다.
- 설정 파일이 없습니다.
- 번들된 CLI가 없습니다. Symfony Console도 없습니다.
- 쿼리 빌더가 없습니다.
- 파일명 제약이 없습니다.
- 런타임 의존성이 전혀 없습니다 — 유틸리티 패키지조차, Cake 코어도 없습니다.
- 어떤 데이터베이스, 어떤 기술 스택, 어떤 서비스 컨테이너와도 작동합니다.
그래서 **Migrun**을 만들었습니다.
대상은 누구인가
- Laravel 또는 Doctrine 생태계 외부에서 PHP 프로젝트를 작업하는 개발자.
- 기존 아키텍처에 맞는 마이그레이션 실행기를 원하고, 아키텍처를 강제하는 것이 아닌 것을 원함.
- 빌더 API를 배우는 대신 직접 SQL을 작성하는 것을 선호함.
- 서비스 컨테이너를 사용하고, 마이그레이션이 자연스럽게 그 혜택을 받길 원함.
- 전체 프레임워크가 아닌 최소하고 해킹 가능한 것을 원함.
이미 Laravel이나 Doctrine을 사용하고 있다면, 해당 마이그레이션 시스템은 깊이 통합되어 있으므로 계속 사용해야 합니다. Migrun은 그것들을 대체하기 위해 만든 것이 아니라, 그 외의 모든 사람들을 위해 만든 것입니다.
작동 방식
PHP 파일에서 익명 클래스 인스턴스(또는 클로저)를 반환합니다. 모든 매개변수는 서비스 컨테이너에서 자동으로 해결됩니다.
간단한 마이그레이션 (익명 클래스)
// 20260326_181000_a_migration.php
exec(debug('Users table created.');
}
};되돌릴 수 있는 마이그레이션 (ReversibleMigration 구현)
// 20260326_181002_reversible_migration.php
exec('CREATE INDEX idx_users_email ON users (email)');
}
public function down(?PDO $db = null): void
{
$db->exec('DROP INDEX idx_users_email');
}
};클로저 기반 마이그레이션
// 20260326_181008_closure_migration.php
exec(directory(__DIR__ . '/migrations')
->container($container) // any PSR‑11 container for autowiring
->pdoStorage($container->get(PDO::class))
->build();
$migrun->run(); // run pending migrations
$migrun->rollback(1); // roll back the last migration이제 끝입니다—간단하고, 조합 가능하며, 프레임워크에 구애받지 않습니다.
$migrun->status(); // see what's applied and what's pendingreadme를 참조하여 완전한 CLI 예시(예: composer migrate:up)를 확인하거나, AI 도구를 사용해 1분 안에 설정할 수 있습니다.
Symfony Console을 지원하지만 필수는 아닙니다(예: php bin/console db:migrate).
The Architecture
Migrun은 네 가지 간단한 인터페이스를 중심으로 구성됩니다:
- 마이그레이션을 찾음
- 마이그레이션을 실행함
- 마이그레이션 의존성을 해결함
- 마이그레이션 히스토리를 저장함
Extensibility
- Pure SQL files? 새로운 executor (단일 메서드)와 새로운 finder (두 메서드)를 구현합니다.
- Different DI container? 단일 invoker 메서드를 구현합니다.
- Store history in Redis? 새로운 스토리지 클래스를 구현합니다.
라이브러리에는 상속이 전혀 없으며, 정적 메서드도, 전역 상태도 없습니다.
한번 사용해 보세요. 현재 베타 단계이며, 여러분의 피드백이 정식 버전을 좌우할 수 있습니다.
Repository:
Install: composer require dakujem/migrun
(or prompt “Install dakujem/migrun from Packagist using the recommended setup and add CLI scripts called via Composer.”)