DI(Dependency Injection) 부터 DIP 까지 예제로 익히자!!

의존성 ??

의존성은 객체간의 의존 관계입니다. 객체(모듈)가 원하는 동작을 하기 위해 다른 객체(모듈)이 필요한 경우에 의존 관계에 있다고 표현합니다. Mysql DB에 연결해 데이터를 다루는 User 객체를 만들어 보겠습니다. User 객체는 Msqyl DB에 접속하기 위해 MysqlDAL 데이터 접근 계층이 필요한 의존 관계에 있습니다. 코드로 작성하면 아래 코드처럼 소스 내부에서 MysqlDAL 이라는 객체를 생성(인스턴스 화)합니다.

<?php

class User
{
    protected $dal; // DB Access Layer

    public function __construct()
    {
        $this->dal = new MySqlDAL();
    }
}

class MySqlDAL{}
의존 관계

의존 관계는 많은 문제를 내포하고 있습니다. 만약 MySqlDAL 이라는 객체가 수정된다면 User 객체도 수정해하는 상황이 오게됩니다. 하나 객체를 수정하는데 다른 의존 관계에 있는 객체까지 수정해야하는 것은 객체 지향 원칙 중 단일 책임 원칙 (모든 클래스는 하나의 책임만 가지며, 클래스는 그 책임을 완전히 캡슐화해야 함을 일컫는다)에 위배 됩니다. 단일 책임 원칙에 위배 되지 않는 코드를 작성하기 위해 의존성 주입(Dependency Injection) 이라는 디자인 패턴을 사용합니다.

의존성 주입 (Dependency Injection)

의존성 주입은 객체 외부에서 의존 관계를 주입 받는 것입니다. 의존성을 주입 받는 방법으로는 생성자, Setter 메소드에서 주입받는 것이 보통입니다. 여기서는 생성자를 통해 MySQLDAL 모듈을 주입받겠습니다.

<?php

class MySqlDAL{}

class User
{
    protected $dal;

    public function __construct(MySqlDAL $dal)
    {
        $this->dal = $dal;
    }
}
의존성 주입

MySqlDAL 모듈을 외부에서 주입하는 의존성 주입을 구현했습니다. 기존 User 객체는 DAL 설정을 위해 Port 번호, DB명, Password 등 DataSource 설정을 User 객체 내부에서 했어야 했습니다. 의존성 주입 후 MySQLDAL 를 인스턴스화 해야하는 책임에서 벗어날 수 있습니다.

이제 의존성 주입이 완벽하게 구현되었을까요?? 단어 그대로의 의존성 주입은 맞지만 또다른 문제점들이 있습니다. MySqlDAL Class의 명세가 바뀌는 경우, MySQLDAL 대신 OrcaleDAL 등 다른 Data Acess Layer 사용하고 싶은 경우에 의존성 주입 전과 마찬가지로 User 객체를 수정해야합니다. 단일 책임 원칙을 여전히 위배하고 있습니다. 그렇다면 어떻게 의존성 주입을 해야할까요? 우리에게 인터페이스가 있습니다.

의존성 역전 원칙 (Dependency Inversion Principle)

<?php

namespace App\Http\Controllers;

interface DAL{}

class MySqlDAL implements DAL{}

class User
{
    protected $dal;

    public function __construct(DAL $dal)
    {
        $this->dal = $dal;
    }
}
Dependency Inversion Principle

의존성 주입이 되는 MySqlDAL 구현체를 인터페이스로 바꾸어 주입했습니다. 어떤 점이 변화했을까요?

첫 번째로 MySQLDAL의 명세가 바뀌어도 User 객체에 영향을 미치지 못합니다. DAL 인터페이스를 통해 구현 클래스에게 기능 구현(implement)을 강제하기 때문에 MySQLDAL, OracleDAL 등 구현 내용을 User 객체가 더 이상 알 필요가 없어졌습니다.

두 번째로 추상화된 DAL 모듈을 사용하면서 다형성을 적극 활용할 수 있게 되었습니다. MySQL, Oracle, Mongo 등 어떤 구현체가 오더라도 DAL 인터페이스에 명세된 기능만 구현한다면 User 객체에 영향을 끼치지 않습니다.

인터페이스로 주입하는 과정을 거치면서 우리는 객체 지향 5원칙 중 하나인 의존성 역전 원칙 (Dependency Inversion Principle) 를 지킬 수 있게 되었습니다. 의존성 역전 원칙은 위키에서 다음과 같이 정의하고 있습니다.

  • 첫째, 상위 모듈은 하위 모듈에 의존해서는 안된다. 상위 모듈과 하위 모듈 모두 추상화에 의존해야 한다.
  • 둘째, 추상화는 구현체에 의존해서는 안된다. 구현체가 추상화에 의존해야 한다.

간단히 설명하면 변경이 잦은 구현체 클래스에 의존하지 말고 추상화에 의존하는 원칙입니다. 모듈 간의 의존(결합)을 최대한 느슨하게 만들어 재사용성을 높이려는 원칙입니다. 우리의 예제를 살펴보면 상위 모듈(객체)인 User는 더이상 하위 모듈 DAL에 의존하지 않는 형태이면서 DAL 인터페이스라는 추상화에 의존하고 있습니다.

TL;DR

이 글 전부
  • CASE 1, 객체 지향 프로그래밍에서 한 객체에서 다른 객체를 사용해하는 의존 관계가 발생한다.
  • CASE 2, [의존성 주입, DI] 의존 관계를 해결하기 위해 생성자, Setter 등에서 객체를 주입 받는다.
  • CASE 3, [의존성 역전 원칙, DIP] 객체 간의 의존 관계를 추상화하여 구현하면 객체간의 결합도를 낮출 수 있다.

마침

라라벨 포스팅을 하던 도중 Service Container 에 대한 포스팅하던 중 DI 에 대한 정리가 필요하여 이 글을 작성했습니다. 프레임 워크에서 컨테이너, IOC 와 더불어 자주 등장하는 개념이라 익숙해질 필요가 있다고 생각하여 라라벨 포스팅을 잠시 멈추고 작성해보았습니다.

포스팅에 많은 그림을 넣을 려고 노력 중입니다. 글 작성 전에 손으로 그림, 그래프를 그려보고 파워 포인트로 그림을 그리면서 작성하고 있습니다. 텍스트 많은 글들은 손가락으로 툭툭 밀면서 대충 보는 경향이 많은 거 같아서 텍스트를 최소화하는 방향으로 글을 작성하고자 합니다. (물론 저같은 경우에) 좋은 글 = 읽기 쉬운 글 = 텍스트 보단 그림 많은 글 이라고 생각하기 때문에 좋은 글을 작성 할 수 있도록 열심히 포스팅하겠습니다. 짝짝짝… 뉴비 글이라 피드백은 적극 환영합니다 🙂

DI(Dependency Injection) 부터 DIP 까지 예제로 익히자!!”의 2개의 댓글

  1. pathas 댓글달기

    정리를 잘 해주셔서 이해하는 데에 큰 도움이 되었습니다. 감사합니다!

답글 남기기

이메일 주소는 공개되지 않습니다.