의존성 주입
Coddy PHP 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨. 91개 중 57번째.
이전 레슨에서는 컴포지션을 사용하면 Car가 Engine 객체를 포함할 수 있다는 것을 살펴보았습니다. 하지만 클래스 내부에서 의존성을 생성하면 문제가 발생합니다. 코드가 경직되고 테스트하기 어려워지기 때문입니다. 의존성 주입은 의존성을 내부에서 생성하는 대신 외부에서 전달하여 이 문제를 해결합니다.
다음 두 접근 방식을 비교해 보세요:
<?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();
}
}
이제 Car는 EngineInterface를 구현하는 어떤 class와도 함께 작동합니다. 가스 엔진, 전기 모터 또는 테스트용 mock과도 사용할 수 있습니다. 이러한 결합도 분리가 바로 dependency injection이 유지 관리와 테스트가 용이한 PHP 애플리케이션을 작성하는 데 fundamental한 이유입니다.
챌린지
쉬움dependency injection의 강력한 기능을 보여 주는 알림 시스템을 만들어 보겠습니다. 메시지를 보내는 방법을 하드코딩하는 대신, 전달 방식을 쉽게 교체할 수 있는 유연한 시스템을 만들게 됩니다.
코드를 네 개의 파일로 구성합니다.
MessageSenderInterface.php: 하나의send(string $recipient, string $message): stringmethod를 포함하는MessageSenderInterface라는 interface를 Define합니다. 이 interface는 모든 메시지 sender가 따라야 하는 계약을 설정합니다.EmailSender.php:MessageSenderInterface를 implements하는EmailSenderclass를 Create합니다. interface 파일을 포함합니다.send()method는"Email to [recipient]: [message]"를 반환해야 합니다.NotificationService.php: constructor를 통해 dependency를 받는NotificationServiceclass를 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할 수 있습니다.- constructor promotion을 사용하여 constructor에서
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()를 호출하고 결과 출력
?>이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.
객체 지향 프로그래밍의 모든 레슨
직접 연습해 보세요: 온라인 PHP 컴파일러