Home 제어의 역전(IoC)과 의존관계 주입(DI)
Post
Cancel

제어의 역전(IoC)과 의존관계 주입(DI)

제어의 역전(IoC, Inversion of Control)

Don’t call us, we will call you.
- 헐리우드 원칙(Hollywood Principle)

과거 프레임워크 없이 개발할 때에는 객체의 생성, 설정, 초기화, 메소드 호출, 소멸 등 객체의 생명주기를 프로그래머가 직접 관리했다. 또한 전통적인 프로그래밍에서는 외부 라이브러리를 사용할 때, 개발자가 직접 외부 라이브러리를 호출하는 형태로 이용했다.

하지만, 프레임워크를 사용하면 객체의 생명 주기를 모두 프레임워크에 위임할 수 있다. 즉, 외부 라이브러리가 프로그래머가 작성한 코드를 호출하고, 흐름을 제어한다. 이와 같이 개발자가 작성한 객체나 메서드의 제어를 개발자가 아니라 외부에 위임하는 설계 원칙을 제어의 역전이라고 한다. 즉, 프레임워크는 제어의 역전 개념이 적용된 대표적인 기술이라고 할 수 있다.

IoC, Inversion of Control Image IoC, Inversion of Control Image

내용을 정리한다면 전통적인 방식으로 라이브러리를 사용하는 것은 우리의 프로젝트의 일부분으로서 라이브러리를 가져와 우리가 직접 제어하는 것이다. 반면 IoC는 우리의 코드가 프레임워크의 일부분이 되어 프레임워크에 의해 제어되는 것이다.

애플리케이션의 제어 책임이 프로그래머에서 프레임워크로 위임되므로, 개발자는 핵심 비즈니스 로직에 더 집중할 수 있다는 장점이 있다.

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

DI 는 IoC 원칙을 실현하기 위한 여러 디자인패턴 중 하나이다. 외부로부터 메모리에 올라가 있는 인스턴스의 레퍼런스를 인터페이스 타입의 파라미터로 의존관계를 설정하는 것을 말한다. 스프링에선 IoC 라는 용어만 가지고는 개념이 너무 추상적이라 그 핵심을 짚는 용어가 필요했는데, 이때 몇몇 사람들의 제안으로 만든 용어가 바로 DI 인 것이다.

강한 결합 (Tight Coupling)

다른 객체에 대한 상당히 많은 정보를 필요로 하고 보통 두 객체 간의 인터페이스들은 서로 높은 의존성을 가지게 된다. 타이트하게 연결된 애플리케이션에서 하나의 객체를 변경하는 것은 많은 다른 객체들의 변경을 요구한다. 이러한 이유로 작은 프로젝트와 달리 비교적 큰 프로젝트에서는 변경점을 찾기가 쉽지 않다.

1
2
3
4
5
6
7
8
9
10
11
12
class Subject {
    Topic t = new Topic();
    public void reading() {
        t.understand();
    }
}

class Topic {
    public void understand() {
        System.out.println("Tight coupling concept!");
    }
}

위 프로그램에서 Topic 클래스는 Subject 클래스에 종속됩니다. Tocpic 클래스의 understand() 메소드가 gotIt() 메소드로 변경되면 reading() 메소드를 변경해야 하며 understand() 메소드를 호출하는 대신 gotIt() 메소드를 호출하여야 합니다.

느슨한 결합 (Loose Coupling)

느슨한 결합은 하나의 콤포넌트의 변경이 다른 콤포넌트들의 변경을 요구하는 위험을 줄이는 것을 목적으로 하는 시스템에서 콤포넌트 간의 내부 의존성을 줄이는 것을 추구하는 디자인 목표다. 느슨한 결합은 시스템을 더욱 유지 할 수 있도록 만들고, 전체 프레임워크를 더욱 안정적으로 만들고 시스템의 유연성을 증가하게 하려는 의도를 가진 포괄적인 개념이다.

스프링 프레임워크는 객체 간의 강한결합 문제를 극복하기 위해 POJO / POJI 모델의 도움으로 DI 를 통해 느슨한 결합을 달성할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public interface Topic() {
  void understand();
}

class Topic_1 implements Topic {
    public void understand() {
        System.out.println("Got It!");
    }
}

class Topic_2 implements Topic {
    public void understand() {
        System.out.println("Understand!");
    }
}

public class Subject {
    public static void main(String[] args) {
      Topic t = new Topic_1();
      t.understand();
    }
}

Topic은 인터페이스이고 런타임에 구현 된 클래스를 삽입 할 수 있으며 최종 사용자에게 서비스를 제공 할 수 있음을 의미한다.

Dependency injection form of the spring framework.

필드를 이용한 의존관계 주입 (Field Injection)

1
2
3
4
5
6
7
8
9
interface Repository {
  void findAll();
}

@Service
class Service {
    @Autowired
    private Repositoy repositoy;
}

Field injection is not recommended … Always use constructor based dependency injection in your beans
Beans

수정자메서드를 이용한 의존관계 주입 (Setter Injection)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
interface Service {
    void doSomething();
}

class ServiceImpl implements Service {
    public void doSomething() {
        System.out.println("ServiceImpl is doing something");
    }
}

class Controller {
    private Service service;
    public void setService(Service service) {
        this.service = service;
    }
    public void callService() {
        service.doSomething();
    }
}

public class Main {
    public static void main(String[] args) {
       Controller controller = new Controller();
       controller.setService(new ServiceImpl()); // setter injection
       controller.callService();
    }
}

생성자를 이용한 의존관계 주입 (Constructor Injection)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
interface Service {
    void doSomething();
}

class ServiceImpl implements Service {
    public void doSomething() {
        System.out.println("ServiceImpl is doing something");
    }
}

class Controller {
    // private final Service service; // final 선언 가능.
    private Service service;
    public Controller(Service service) {
        this.service = service;
    }
    public void callService() {
        service.doSomething();
    }
}

public class Main {
    public static void main(String[] args) {
       Controller controller = new Controller(new ServiceImpl()); // constructor injection
       controller.callService();
    }
}

Reference

This post is licensed under CC BY 4.0 by the author.

-

Spring Bean