PHP

PHP에서 의존성 주입(DI) 사용하기

thebasics 2024. 9. 27. 10:00

목차
1. 의존성 주입(DI)이란 무엇인가?
   - DI의 개념
   - DI의 장점
2. PHP-DI 라이브러리 사용법
   - PHP-DI 설치 및 설정
   - 기본적인 의존성 주입 구현
3. DI 컨테이너 설정 및 사용
   - 컨테이너 설정
   - 컨테이너를 사용한 객체 생성 및 관리
4. 코드 예제
5. 결론 및 추가 학습 자료


1. 의존성 주입(DI)이란 무엇인가?

DI의 개념

의존성 주입(Dependency Injection, DI)은 객체가 그 동작에 필요한 의존성을 스스로 생성하지 않고 외부에서 주입받는 디자인 패턴입니다. 이를 통해 코드의 결합도를 낮추고, 유지보수성을 높이며, 테스트가 용이한 코드를 작성할 수 있습니다.

DI의 장점

- 결합도 감소: 객체 간의 강한 결합을 제거하여 코드의 유연성과 재사용성을 높입니다.
- 테스트 용이성: 모의 객체(Mock Object)를 사용한 단위 테스트가 가능해집니다.
- 코드 가독성 향상: 객체의 의존성이 명확하게 드러나므로 코드의 이해도가 높아집니다.
- 유지보수성 향상: 의존성을 쉽게 교체할 수 있어 코드의 유지보수가 용이해집니다.


2. PHP-DI 라이브러리 사용법

PHP-DI 설치 및 설정

PHP-DI는 PHP에서 의존성 주입을 쉽게 구현할 수 있도록 도와주는 강력한 라이브러리입니다. Composer를 통해 설치할 수 있습니다.

Composer를 통한 설치:

composer require php-di/php-di

설치가 완료되면, PHP-DI를 사용하여 의존성 주입을 구현할 수 있습니다.

기본적인 의존성 주입 구현

의존성 주입을 구현하기 위해 먼저 필요한 인터페이스와 클래스들을 정의하고, PHP-DI 컨테이너를 통해 의존성을 주입받아 객체를 생성합니다.

예제:

// src/LoggerInterface.php
<?php

interface LoggerInterface
{
    public function log(string $message);
}

?>
// src/FileLogger.php
<?php

class FileLogger implements LoggerInterface
{
    public function log(string $message)
    {
        file_put_contents('app.log', $message . PHP_EOL, FILE_APPEND);
    }
}

?>
// src/UserService.php
<?php

class UserService
{
    private $logger;

    public function __construct(LoggerInterface $logger)
    {
        $this->logger = $logger;
    }

    public function registerUser(string $username)
    {
        // 사용자 등록 로직
        $this->logger->log("User {$username} registered.");
    }
}

?>

이 예제에서 'UserService' 클래스는 'LoggerInterface'에 의존하고 있으며, 'FileLogger' 클래스가 실제 구현체로 사용됩니다. 의존성을 외부에서 주입받아 'UserService'가 필요한 'LoggerInterface' 구현체를 사용할 수 있습니다.


3. DI 컨테이너 설정 및 사용

컨테이너 설정

PHP-DI 컨테이너는 의존성을 관리하고, 필요한 객체를 생성하여 주입하는 역할을 합니다. 설정 파일을 통해 컨테이너에 매핑 정보를 제공하여, 객체 생성과 의존성 주입을 자동화할 수 있습니다.

컨테이너 설정 예제:

// config/di.php
<?php

use DI\ContainerBuilder;
use function DI\create;
use function DI\get;

return function (ContainerBuilder $containerBuilder) {
    $containerBuilder->addDefinitions([
        LoggerInterface::class => create(FileLogger::class),
        UserService::class => create(UserService::class)
            ->constructor(get(LoggerInterface::class)),
    ]);
};

?>

이 설정 파일은 'LoggerInterface'가 'FileLogger'로 매핑되도록 설정하고, 'UserService'가 'LoggerInterface'를 의존성으로 주입받도록 설정합니다.

컨테이너를 사용한 객체 생성 및 관리

설정된 컨테이너를 사용하여 객체를 생성하고 의존성을 주입할 수 있습니다.

// public/index.php
<?php

require '../vendor/autoload.php';

// 컨테이너 빌더 생성 및 설정 파일 로드
$containerBuilder = new DI\ContainerBuilder();
$containerBuilder->addDefinitions(require '../config/di.php');

// 컨테이너 빌드
$container = $containerBuilder->build();

// UserService 객체 생성 및 사용
$userService = $container->get(UserService::class);
$userService->registerUser('john_doe');

?>

이 예제에서는 'UserService' 객체를 DI 컨테이너에서 생성하며, 'FileLogger'가 'LoggerInterface'의 구현체로 자동 주입됩니다. 이렇게 생성된 'UserService' 객체를 통해 사용자 등록 작업을 수행합니다.


4. 코드 예제

다음은 PHP-DI를 사용하여 의존성 주입을 구현하는 종합적인 예제입니다.

1. Logger 인터페이스 및 구현체:

// src/LoggerInterface.php
<?php

interface LoggerInterface
{
    public function log(string $message);
}

?>
// src/FileLogger.php
<?php

class FileLogger implements LoggerInterface
{
    public function log(string $message)
    {
        file_put_contents('app.log', $message . PHP_EOL, FILE_APPEND);
    }
}

?>

2. UserService 클래스:

// src/UserService.php
<?php

class UserService
{
    private $logger;

    public function __construct(LoggerInterface $logger)
    {
        $this->logger = $logger;
    }

    public function registerUser(string $username)
    {
        // 사용자 등록 로직
        $this->logger->log("User {$username} registered.");
    }
}

?>

3. DI 컨테이너 설정:

// config/di.php
<?php

use DI\ContainerBuilder;
use function DI\create;
use function DI\get;

return function (ContainerBuilder $containerBuilder) {
    $containerBuilder->addDefinitions([
        LoggerInterface::class => create(FileLogger::class),
        UserService::class => create(UserService::class)
            ->constructor(get(LoggerInterface::class)),
    ]);
};

?>

4. 메인 애플리케이션 파일:

// public/index.php
<?php

require '../vendor/autoload.php';

// 컨테이너 빌더 생성 및 설정 파일 로드
$containerBuilder = new DI\ContainerBuilder();
$containerBuilder->addDefinitions(require '../config/di.php');

// 컨테이너 빌드
$container = $containerBuilder->build();

// UserService 객체 생성 및 사용
$userService = $container->get(UserService::class);
$userService->registerUser('john_doe');

?>

코드 분석:
- 첫 번째 예제는 'LoggerInterface'와 그 구현체인 'FileLogger'를 정의합니다.
- 두 번째 예제는 'UserService' 클래스에서 'LoggerInterface'를 의존성으로 주입받아 사용자 등록을 처리합니다.
- 세 번째 예제는 PHP-DI 컨테이너 설정 파일로, 의존성을 정의하고 매핑합니다.
- 네 번째 예제는 설정된 컨테이너를 사용하여 'UserService' 객체를 생성하고, 이를 통해 사용자 등록 작업을 수행합니다.


5. 결론 및 추가 학습 자료

이번 글에서는 PHP에서 의존성 주입(DI)을 사용하는 방법에 대해 알아보았습니다. DI를 통해 코드의 결합도를 낮추고, 유지보수성과 테스트 용이성을 높일 수 있습니다. PHP-DI 라이브러리를 사용하여 DI 컨테이너를 설정하고, 객체 생성을 자동화함으로써 코드의 가독성과 유연성을 향상시킬 수 있습니다.

추가 학습 자료:
- [PHP-DI 공식 문서](https://php-di.org/doc/) PHP-DI 라이브러리의 설치 및 사용법에 대한 자세한 문서입니다.
- [Dependency Injection Explained](https://martinfowler.com/articles/injection.html) 마틴 파울러의 의존성 주입에 대한 설명과 다양한 패턴을 소개합니다.
- [PHP The Right Way - 의존성 주입](https://phptherightway.com/#dependency_injection) PHP에서 의존성 주입을 사용하는 올바른 방법에 대한 가이드입니다.


이제 의존성 주입을 사용하여 더욱 유연하고 유지보수성이 높은 PHP 애플리케이션을 개발할 수 있습니다. 실습을 통해 DI 패턴을 프로젝트에 적용해보세요!

반응형

'PHP' 카테고리의 다른 글

PHP에서 자주 사용되는 디자인 패턴  (3) 2024.09.29
PHP에서 이벤트와 리스너 사용하기  (0) 2024.09.28
PHP에서 라우팅 구현하기  (4) 2024.09.26
PHP로 MVC 패턴 구현하기  (6) 2024.09.25
PHP에서 PSR 표준 적용하기  (2) 2024.09.24