Dependency Injection(DI) : 의존관계 주입

후추·2023년 4월 14일
0

들어가기

Dependency Injection(DI) 은 의존관계 주입을 의미한다.

Dependency Injection 을 이해하기 위해 우선 Dependency(의존관계)가 무엇인지 알아보자.

Dependency 의존관계

정의

자바 프로그래밍에서 Dependency는 주로 객체 사이의 의존관계를 표현하는 용어이다.

흔히 A와 B 두 객체가 있을 때, A가 B를 의존한다는 말을 들어보았을 것이다.

의존한다는 것이 무엇을 의미할까.

토비의 스프링은 다음과 같이 서술한다.

의존한다는 건 의존대상 B가 변하면, 그것이 A에 영향을 미친다는 뜻이다.

  • 이일민, 토비의 스프링 3.1, 에이콘(2012), p113

즉, 의존관계변경에 의한 영향을 받는 관계라고 다시 표현할 수 있다.

B의 변화에 A가 영향을 받는다면 A가 B에 의존하는 것이다.

이를 그림으로 표현하면 아래와 같다.

예시 코드

스쿼트가 가능한 파워랙과 운동인 후추, 네오를 코드로 작성했다.

후추와 네오 모두 스쿼트를 할 수 있다.

public class PowerRack {

    private Huchu huchu;

    public PowerRack() {
        this.huchu = new Huchu();
    }

    public void squat() {
        huchu.squat();
    }
}
public class Huchu{

    public void squat() {
        System.out.println("후추가 스쿼트를 한다.");
    }
}
public class Neo{

    public void squat() {
        System.out.println("네오가 스쿼트를 한다.");
    }
}

위 코드에서 파워랙(PowerRack)은 후추(Huchu)가 변화하면 영향을 받는다.

가령, 후추의 생성자 시그니처가 변경된다면 파워랙의 코드도 반드시 변해야 한다.

따라서 파워랙이 후추를 의존하는 관계이다.

주목할 점은 파워랙과 후추의 의존관계가 어떻게 설정되었는가 이다.

코드를 보면 파워랙(PowerRack)은 후추(Huchu)를 인스턴스 변수로 선언하고, 의존관계를 생성자 내부에서 직접 설정했다.

이러한 방식으로 인해 두 클래스의 관계는 단순히 의존할 뿐 아니라 강하게 결합되어 있는데,

똑같이 스쿼트를 할 수 있는 네오(Neo)는 파워랙을 사용할 수 없다.

파워랙 클래스가 매우 유연하지 않은 경우이다.

추상화

파워랙 클래스에 유연성을 확보하기 위해 후추(Huchu)와 네오(Neo)를 헬스장 회원(GymMember)으로 추상화해보자.

GymMember를 인터페이스로 두고 후추와 네오가 GymMember를 구현하게 설정했다.

public interface GymMember {
    void squat();
}


public class Huchu implements GymMember{

	@Override
    public void squat() {
        System.out.println("후추가 스쿼트를 한다.");
    }
}

public class Neo implements GymMember{

    @Override
    public void squat() {
        System.out.println("네오가 스쿼트를 한다.");
    }
}

파워랙 클래스는 GymMember를 인스턴스 변수로 갖게 했다.

public class PowerRack {

    private GymMember gymMember;

    public PowerRack() {
        this.gymMember = new Huchu();
        // this.gymMember = new Neo();
    }

    public void squat() {
        gymMember.squat();
    }
}

추상화를 통해 후추와 네오 모두 파워랙에서 스쿼트를 할 수 있게 됐다.

이렇듯 의존하는 대상을 인터페이스로 추상화하게 되면, 의존관계를 다양하게 맺을 수 있으며 결합도가 낮아진다.

그러나 파워랙은 여전히 this.gymMember = new Huchu() 코드로 내부에서 직접 의존관계를 설정하고 있다.

만약 네오가 파워랙에서 스쿼트를 할 수 있게 하려면, this.gymMember = new Neo()로 코드를 수정해야 한다.

결국 파워랙이 스스로 의존관계를 설정하는 방식은 유연성이 떨어지고 유지보수에 불리하다.

의존관계 주입(Dependency Injection)

두 객체 사이의 의존관계를 객체 외부에서 설정해볼 수 있다.

파워랙을 관리하는 GymMaster(헬스장 주인)을 새로 만들어보자.

그리고 GymMaster가 파워랙을 이용하는 회원을 결정하게 하자.

public class PowerRack {

    private GymMember gymMember;

    public PowerRack(final GymMember gymMember) {
        this.gymMember = gymMember;
    }

    public void squat() {
        gymMember.squat();
    }
}

public class GymMaster {
    public static void main(String[] args) {
        final PowerRack powerRack = new PowerRack(new Huchu());
        powerRack.squat();
    }
}

그렇다면 파워랙이 오직 GymMember 인터페이스에 의존하게 되고,

어떤 구현체를 의존하는지는 파워랙의 외부(GymMaster)에서 결정하게 된다.

이처럼 객체의 의존관계를 외부에서 설정하는 것이 Dependency Injection (의존관계 주입)이다.

토비의 스프링은 의존관계 주입이 다음 세 가지 조건을 충족한다고 말한다.

  • 클래스 모델이나 코드에는 런타임 시점의 의존관계가 드러나지 않는다. 그러기 위해서는 인터페이스만 의존하고 있어야 한다.
  • 런타임 시점의 의존관계는 컨테이너나 팩터리 같은 제 3의 존재가 결정한다.
  • 의존관계는 사용할 오브젝트에 대한 레퍼런스를 외부에서 제공해줌으로써 만들어진다.

Dependency Injection 의 장점

이러한 의존관계 주입의 장점은 무엇일까.

1. 의존성이 줄어든다.

기본적으로 의존관계는 변화에 의한 영향을 받는 관계이다.

의존관계를 외부에서 주입한다면 주입 대상이 달라지는 것에서 자유로워진다.

즉, 의존성이 줄어들게 되고 상대적으로 변화의 영향을 덜 받게 된다.

2. 재사용성이 높아진다.

외부에서 의존관계를 설정할 때 같은 타입의 여러 객체를 주입할 수 있다.

따라서 기존 코드를 수정하지 않더라도, 구현체를 달리함으로써 다양한 기능을 사용할 수 있게 된다.

3. 테스트하기 좋아진다.

의존관계 주입을 활용하는 경우, 인터페이스를 통해 mock 객체를 만들 수 있다.

이러한 mock 객체로 인해 테스트가 용이해진다.

4. 가독성이 높아진다.

의존관계 주입을 적용하면 여러 객체를 하나의 인터페이스로 추상화하면서 각 객체의 동작을 인터페이스에 정리할 수 있게 된다.

이러한 추상화는 구현체의 가독성을 높인다.

정리

  • 의존관계란 변화에 의해 영향을 받는 관계이다.
  • Dependency Injection 이란, 객체 사이의 의존관계를 외부에서 설정해주는 방식이다.
  • DI 는 유지보수 및 테스트 등에서 여러 이점을 갖는다.

0개의 댓글