개발 지식

개발 지식

PHP PHP에서 커스텀 DI 컨테이너 만들기

페이지 정보

profile_image
영삼이
0건 174회 25-03-28 23:13

본문

✅ PHP에서 커스텀 DI 컨테이너 만들기

대형 프레임워크(Laravel, Symfony 등)는 강력한 DI 컨테이너(Dependency Injection Container)를 제공합니다. 하지만 작거나 독립적인 프로젝트에서는 직접 DI 컨테이너를 구현하는 것이 더 가볍고 유연할 수 있습니다. 이 글에서는 PHP로 심플하면서 강력한 DI 컨테이너를 직접 만드는 방법을 다룹니다.


📌 DI 컨테이너 핵심 기능

  • 클래스 인스턴스를 자동 생성

  • 의존성(생성자 인자)을 자동 주입

  • 싱글톤 관리 가능

  • 인터페이스 → 구현체 바인딩 가능


🧱 컨테이너 기본 구현

[code=php]
class Container {
    private array $bindings = [];
    private array $instances = [];

    public function bind(string $abstract, string|callable $concrete, bool $singleton = false): void {
        $this->bindings[$abstract] = [
            'concrete' => $concrete,
            'singleton' => $singleton
        ];
    }

    public function make(string $abstract): object {
        // 이미 싱글톤 인스턴스가 있다면 반환
        if (isset($this->instances[$abstract])) {
            return $this->instances[$abstract];
        }

        $concrete = $this->bindings[$abstract]['concrete'] ?? $abstract;

        if (is_callable($concrete)) {
            $object = $concrete($this);
        } else {
            $object = $this->build($concrete);
        }

        if (($this->bindings[$abstract]['singleton'] ?? false) === true) {
            $this->instances[$abstract] = $object;
        }

        return $object;
    }

    private function build(string $class): object {
        $ref = new ReflectionClass($class);
        $constructor = $ref->getConstructor();

        if (!$constructor) {
            return new $class;
        }

        $params = $constructor->getParameters();
        $deps = [];

        foreach ($params as $param) {
            $type = $param->getType()?->getName();
            if ($type === null) {
                throw new Exception("타입 힌트 없는 의존성: {$param->getName()}");
            }
            $deps[] = $this->make($type);
        }

        return $ref->newInstanceArgs($deps);
    }
}
[/code]

🧪 사용 예시

[code=php]
interface LoggerInterface {
    public function log(string $msg): void;
}

class FileLogger implements LoggerInterface {
    public function log(string $msg): void {
        file_put_contents('log.txt', $msg . PHP_EOL, FILE_APPEND);
    }
}

class UserService {
    public function __construct(private LoggerInterface $logger) {}

    public function run() {
        $this->logger->log("UserService 실행됨");
    }
}

$container = new Container();

$container->bind(LoggerInterface::class, FileLogger::class);
$container->bind(UserService::class, UserService::class);

$service = $container->make(UserService::class);
$service->run();
[/code]

🧠 고급 기능 추가 팁

  • 컨트롤러 자동 주입Router와 연동

  • 환경별 바인딩 → 개발/운영 Logger 분기

  • 태그 시스템 → 여러 서비스 일괄 호출


✅ 요약

  • DI 컨테이너는 클래스 간 결합을 줄이고, 테스트 가능성과 확장성을 높이는 핵심 도구

  • PHP로도 심플하고 유연한 컨테이너를 직접 만들 수 있음

  • 작은 프로젝트, 마이크로서비스, CLI 앱 등에 특히 유용


댓글목록

등록된 댓글이 없습니다.