[Design Pattern] DI(Dependency Injection, 의존성 주입)와 IoC(Inversion of Control, 제어 반전)
11 Jan 2021 | design-pattern spring🤔 DI와 IoC?
처음 스프링을 접하면 DI (Dependency Injection, 의존성 주입)
와 IoC(Inversion of Control, 제어 반전)
라는 개념이 등장한다.
흔히 스프링의 3대 요소들 중 하나로 불리는 DI, IoC에 대해 알아보려고 한다.
DI와 IoC는 디자인 패턴
으로 스프링에만 국한되는 것 이 아니기 때문에 디자인 패턴이라는 카테고리로 정리했다.
🐥 의존성 (Dependency)
우선 가장 중요한 개념인 의존성
에 대해 정리해보도록 하자.
의존성… 뭔가 딱 와닿지 않는다.
나만 그런 것일 수 있겠지만, 난 의존성
이라는 단어가 아주 낯설게 느껴진다.
좀 익숙하게 쉽게 풀어보자. ~에 대한 의존성을 가진다
라는 것은 ~가 필요하다
라고 쉽게 풀어 쓸 수 있다.
예를 들어, Gradle(build.gradle)
이나 Maven(pom.xml)
, 더 나아가 NPM(package.json)
을 보면 dependency
는 외부 패키지, 라이브러리를 나타낸다.
즉, 해당 패키지, 라이브러리가 필요함을 명시할 때 우리는 dependency, 의존성
이라는 말을 쓴다.
A가 B에 대한 의존성을 가진다. 또는 B는 A의 의존성이다.
이 말은 A는 B가 필요하다.
라고 쉽게 풀어 쓸 수 있다.
A는 의존성을 스스로 만든다.
이 말은 A는 자기가 필요한 요소를 스스로 생성한다.
라고 쉽게 풀어 쓸 수 있다.
예를 들어보자,
public class Pizza {
void eat() {
System.out.println("피자를 먹었다. 냠냠");
}
}
public class Person {
void eat() {
Pizza pizza = new Pizza();
pizza.eat();
}
}
위의 코드에서, Person
와 Pizza
의 관계는 어떠한가?
Person
은 Pizza
를 직접 만들어 먹는다.
따라서 Person
과 Pizza
사이에는 강한 결합 (강한 의존성)
이 생긴다.
만약 피자(
Pizza
)라는게 존재하지 않는다면, 사람(Person
)은 식사(eat
)를 할 수 없다.
Pizza를 다른 음식으로 대체하려면 Person의 대부분의 코드를 수정해야한다.
↑ 피자에 강한 의존성을 가지는 강아지
그럼 어떻게 하면 요소들 사이의 결합을 약하게 할 수 있을까?
public interface Food {
void eat();
}
자바에는 Interface
가 있다.
자바가 익숙하지 않은 사람들은 도저히 인터페이스가 왜 필요한 것인지 이해하지 못한다. (나도 그랬다.)
인터페이스를 사용하면
특정 메소드
를 반드시 구현하도록 강제할 수 있다.
따라서 해당 인터페이스를implements
하는 클래스에는특정 메소드
가 존재함이 보장된다.
자, 이제 다시 작성해보자.
public class Pizza implements Food{
@Override
void eat() {
System.out.println("피자를 먹었다. 냠냠");
}
}
public class Hotdog implements Food{
@Override
void eat() {
System.out.println("핫도그를 먹었다. 냠냠");
}
}
public class Person {
void eat(Food food) {
food.eat();
}
}
이제 Person은 Pizza 외의 음식도 (Food를 implements하는 클래스라면
) 먹을 수 있다.
이 것이 바로 느슨한 결합
, 또는 약한 결합
, 약한 의존성
이다.
이제 핫도그도 먹을 수 있다.
💉 DI: 의존성을 주입한다고?
그래 이제 의존성은 알겠다.
주입? 또 뭔가 낯설다…
주입(Injection)
을 전달(Pass)
로 해석하면 이해하기 좀 더 수월할 것이다.
의존성을 주입한다
는 것은 즉, 필요한 것을 전달한다
는 것과 같다.
위의 Person, Food의 예로 쉽게 설명하자면
Person에게 Food를 전달하는 것
이다.
의존성을 전달(주입)하기 위해 사용할 수 있는 방법은 뭐가 있을까?
1. 메소드의 파라미터를 이용한 전달(주입)
public class Person {
void eat(Food food) {
food.eat();
}
}
이게 바로 파라미터를 이용한 전달이다.
하지만 Food를 두고두고 조금씩 먹고싶다면 어떨까?
Person의 필드(Field, 멤버 변수)
로 설정한다면 재사용
할 수 있을것이다.
public class Person {
Food food;
void setFood(Food food) {
this.food = food;
}
void eat() {
if (food == null) {
System.out.println("먹을게 없어..");
return;
}
food.eat();
}
}
이렇게 필드를 설정하는 setFood와 같은 메소드를 Setter
라고 한다.
(필드 값을 반환하는 getFood와 같은 메소드는 Getter
라고 한다.)
위처럼 Setter를 이용해 의존성을 전달(주입)하는 방식을 설정자를 이용한 주입 (Setter Injection)
이라고 한다.
2. 생성자를 이용한 전달(주입)
Person 인스턴스를 생성할 때 Food를 전달하는 방식이다.
생성자의 매개변수로 받으면 된다.
public class Person {
Food food;
public Person(Food food) {
if (food == null) {
throw IllegalArgumentException("음식을 내놔라 😡!!");
}
this.food = food;
}
void eat() {
food.eat();
}
}
이런식으로 만들 수 있겠다. 😏
🙃 IoC: 제어를 반전한다고?
웹툰 신의탑
자, DI에 대해 알아보았다.
이제 IoC(Inversion of Control, 제어 반전)
에 대해 알아보자!
프로그래머가 작성한 프로그램이 재사용 라이브러리의 흐름 제어를 받게 되는 소프트웨어 디자인 패턴
Wiki
한국말이 맞나 싶다… 다른 정의를 찾아보자!
프레임워크가 정의한 인터페이스를 클라이언트 코드가 구현을 하고, 구현된 객체를 프레임워크에 전달(주입)하여
프레임워크가 제어를 가지게 하는 것
Inversion of Control 컨테이너 (IoC Container)란?
좀더 가볍게 정의해보자!
인터페이스를 구현하고, 구현한 객체를 외부에 전달하여 제어를 외부로 넘기는것
이렇게까지 가볍게 정의하면, 앞서 공부한 DI
의 개념과 IoC
의 개념이 거의 일치한다는 것을 알 수 있다.
Food
를 구현한Pizza
를Person
에게 전달하여 제어를Person
에게 넘기는 것
Person
이 Pizza
를 생성하던 기존의 제어 흐름에서 반전된
Food
를 구현하는 Pizza
를 생성하여 Person
에게 전달하는 역전된 제어 흐름
이 바로 IoC
의 개념이다.
😎 즉, DI ~= IoC
😱 이게 IoC였어?!!?!?
Java로 쓰레드를 구현해본 적이 있다면 다음과 같은 코드를 한번쯤 사용해봤을 것이다.
new Thread(new Runnable() {
@Override
public void run() {
// Do something
}
}).start();
그래서 이게 IoC랑 무슨 상관인데! 싶다면 이 글을 천천히 다시 읽어보자!
(설명이 부족했다 싶으면 다른 포스트를 참고해봐도 좋다.)
Thread
의 생성자에 Runnable 인터페이스를 구현한 객체
를 넘겨준다.
그리고 start
메소드를 호출하면 내부적으로 Runnable의 run 메소드를 호출
한다.
이 것이 바로 DI를 사용한 전달이며, IoC를 사용한 제어이다.
그 외에도 만약 Android
개발을 해보았다면?
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Do something
}
});
리스너로 인터페이스, 콜백을 전달하는 방식도 DI, IoC의 개념이 적용된 것이다!
📕 마무리
DI (Dependency Injection, 의존성 주입)
와 IoC(Inversion of Control, 제어 반전)
에 대해 깊게 정리해보았다!
우리가 일상속에서 당연한 것으로 받아들이고 개발했던 것들에 이런 개념이 숨어 있었다니… 참 놀랍지 않은가?! (ㅔ)
🚀 참고
- https://moonscode.tistory.com/58
- https://vandbt.tistory.com/44
- https://velog.io/@wickedev/IoC-DIP-IoC-Container-DI-DI-Framework-%EB%8F%84%EB%8C%80%EC%B2%B4-%EA%B7%B8%EA%B2%8C-%EB%AD%94%EB%8D%B0