Menu
Coddy logo textTech

의존성 주입

Coddy PHP 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨. 91개 중 57번째.

이전 레슨에서는 컴포지션을 사용하면 CarEngine 객체를 포함할 수 있다는 것을 살펴보았습니다. 하지만 클래스 내부에서 의존성을 생성하면 문제가 발생합니다. 코드가 경직되고 테스트하기 어려워지기 때문입니다. 의존성 주입은 의존성을 내부에서 생성하는 대신 외부에서 전달하여 이 문제를 해결합니다.

다음 두 접근 방식을 비교해 보세요:

<?php
// DI 없이 - 의존성이 내부에서 생성됨
class Car {
    private Engine $engine;
    
    public function __construct() {
        $this->engine = new Engine(); // 강하게 결합됨
    }
}

// DI 사용 - 의존성이 전달됨
class Car {
    public function __construct(private Engine $engine) {}
}

$engine = new Engine();
$car = new Car($engine); // 외부에서 주입됨

두 번째 접근 방식은 의존성 주입입니다. Car는 자체 engine을 생성하지 않고 하나를 전달받습니다. 이 간단한 변경에는 강력한 이점이 있습니다. 서로 다른 engine 유형을 전달하고, 테스트를 위해 구현을 교체할 수 있으며, class가 더욱 유연해집니다.

더욱 유연하게 하려면 구체 클래스 대신 인터페이스를 주입하세요:

<?php
interface EngineInterface {
    public function start(): string;
}

class Car {
    public function __construct(private EngineInterface $engine) {}
    
    public function start(): string {
        return $this->engine->start();
    }
}

이제 CarEngineInterface를 구현하는 어떤 class와도 함께 작동합니다. 가스 엔진, 전기 모터 또는 테스트용 mock과도 사용할 수 있습니다. 이러한 결합도 분리가 바로 dependency injection이 유지 관리와 테스트가 용이한 PHP 애플리케이션을 작성하는 데 fundamental한 이유입니다.

challenge icon

챌린지

쉬움

dependency injection의 강력한 기능을 보여 주는 알림 시스템을 만들어 보겠습니다. 메시지를 보내는 방법을 하드코딩하는 대신, 전달 방식을 쉽게 교체할 수 있는 유연한 시스템을 만들게 됩니다.

코드를 네 개의 파일로 구성합니다.

  • MessageSenderInterface.php: 하나의 send(string $recipient, string $message): string method를 포함하는 MessageSenderInterface라는 interface를 Define합니다. 이 interface는 모든 메시지 sender가 따라야 하는 계약을 설정합니다.
  • EmailSender.php: MessageSenderInterface를 implements하는 EmailSender class를 Create합니다. interface 파일을 포함합니다. send() method는 "Email to [recipient]: [message]"를 반환해야 합니다.
  • NotificationService.php: constructor를 통해 dependency를 받는 NotificationService class를 Create합니다. interface 파일을 포함합니다. 이 class는 다음을 수행해야 합니다.
    • constructor promotion을 사용하여 constructor에서 MessageSenderInterface를 Accept합니다.
    • 주입된 sender에 delegates하고 그 결과를 반환하는 notify(string $recipient, string $message) method를 have합니다.
    NotificationService가 구체적인 class가 아니라 interface에 의존한다는 점에 주목하세요. 즉, service를 변경하지 않고도 interface를 implements하는 어떤 sender든 Inject할 수 있습니다.
  • main.php: EmailSender 및 NotificationService 파일을 포함합니다. recipient와 message라는 두 개의 입력을 받습니다. EmailSender를 Create하고, 이를 NotificationService에 Inject한 다음 notify()를 Call하여 결과를 print합니다.

이 패턴은 NotificationService를 유연하게 유지합니다. email, SMS 또는 아직 만들지 않은 무언가 등 Inject하는 어떤 sender와도 작동합니다. service는 자체 dependency를 Create하지 않고, outside에서 전달받습니다.

직접 해보기

<?php
require_once 'EmailSender.php';
require_once 'NotificationService.php';

// 입력 읽기
$recipient = trim(fgets(STDIN));
$message = trim(fgets(STDIN));

// TODO: EmailSender 인스턴스 생성
// TODO: NotificationService에 주입
// TODO: notify()를 호출하고 결과 출력

?>
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

객체 지향 프로그래밍의 모든 레슨

직접 연습해 보세요: 온라인 PHP 컴파일러