스프링 강의를 듣게 되면서 IoC와 DI라는 것을 처음 듣고 알게 되었다.
처음에 무슨 말을 하는 지몰라 다시금 복습하기 위해 적어보면서 알아보겠다.
IoC는 Inversion of Control, 제어의 역전이라는 뜻을 갖는다. IoC 컨테이너는 그 기능을 하는 것이고
프레임워크에 저어의 권한을 넘김으로써 클라이언트 코드가 신경써야할 것을 줄이는 전략으로
객체 간의 낮은 결합도를 유지하며 외부(IoC컨테이너)에서 제어를 하는 것이다.
DI는 Dependency Injection, 의존관계 주입으로 이를 통해서 IoC를 구현한다.
IoC와 DI가 세트로 불리게 되는 이유이기도 하다.
의존성에 대해 이해하는 것부터 알아보겠다.
의존성이란 예를 들어 우리가 다리를 다쳐서 목발을 사용하여 걷게되는 것에서 목발에 의존하고 있다는 것으로
이를 통해 목발에 의존성을 두게 되었다고 말할 수 있다.
코드를 예시로 보자면
강하게 결합되어있는 Consumer와 Chicken
public class Consumer {
void eat() {
Chicken chicken = new Chicken();
chicken.eat();
}
public static void main(String[] args) {
Consumer consumer = new Consumer();
consumer.eat();
}
}
class Chicken {
public void eat() {
System.out.println("치킨을 먹는다.");
}
}
위 코드를 보면 실행하는데는 아무 지장이 없지만 Consumer가 치킨이 아닌 다른 음식을 먹고싶어하면 많은 수의 코드 수정이 불가피 해진다.
이것을 해결하기위해서는 Java에서는 Interface를 활용하여 해결 할 수 가 있다.
public class Consumer {
void eat(Food food) {
food.eat();
}
public static void main(String[] args) {
Consumer consumer = new Consumer();
consumer.eat(new Chicken());
consumer.eat(new Pizza());
}
}
interface Food {
void eat();
}
class Chicken implements Food{
@Override
public void eat() {
System.out.println("치킨을 먹는다.");
}
}
class Pizza implements Food{
@Override
public void eat() {
System.out.println("피자를 먹는다.");
}
}
Interface의 다형성의 원리를 이용하여 고객이 어떤 음식을 원하더라도 쉽게 대처가 가능해진다.
이러한 관계를 약한 결합 및 약한 의존성이라고 할 수가 있다.
이제 그러면 이러한 관계에서 주입이란 무엇인가?
예를 들어 주사기를 통하여 백신을 우리몸속에 주입하듯이
코드에서도 필요로 하는 객체를 해당객체에 전달하는 것을 주입이라고 할 수 있다.
필드에 직접 주입
public class Consumer {
Food food;
void eat() {
this.food.eat();
}
public static void main(String[] args) {
Consumer consumer = new Consumer();
consumer.food = new Chicken();
consumer.eat();
consumer.food = new Pizza();
consumer.eat();
}
}
interface Food {
void eat();
}
class Chicken implements Food{
@Override
public void eat() {
System.out.println("치킨을 먹는다.");
}
}
class Pizza implements Food{
@Override
public void eat() {
System.out.println("피자를 먹는다.");
}
}
Food를 Consumer에 포함 시키고 Food에 필요한 객체를 주입받아 사용한다.
메서드를 통한 주입
public class Consumer {
Food food;
void eat() {
this.food.eat();
}
public void setFood(Food food) {
this.food = food;
}
public static void main(String[] args) {
Consumer consumer = new Consumer();
consumer.setFood(new Chicken());
consumer.eat();
consumer.setFood(new Pizza());
consumer.eat();
}
}
interface Food {
void eat();
}
class Chicken implements Food{
@Override
public void eat() {
System.out.println("치킨을 먹는다.");
}
}
class Pizza implements Food{
@Override
public void eat() {
System.out.println("피자를 먹는다.");
}
}
set 메서드를 사용하여 필요한 객체를 주입받아 사용한다.
생성자를 통한 주입
public class Consumer {
Food food;
public Consumer(Food food) {
this.food = food;
}
void eat() {
this.food.eat();
}
public static void main(String[] args) {
Consumer consumer = new Consumer(new Chicken());
consumer.eat();
consumer = new Consumer(new Pizza());
consumer.eat();
}
}
interface Food {
void eat();
}
class Chicken implements Food{
@Override
public void eat() {
System.out.println("치킨을 먹는다.");
}
}
class Pizza implements Food{
@Override
public void eat() {
System.out.println("피자를 먹는다.");
}
}
생성자를 사용하여 필요한 객체를 주입받아 사용할 수 있다.
이렇듯 필요한 객체를 주입하여 사용하는 법을 알아 보았다.
맨처음에는 Consmer가 직접 Food를 만들어 먹었기 때문에 새로운 Food를 만드려면 추가적인 코드변경이 불가피 하였어서 제어의 흐름이 Consumer → Food 였다.
하지만 이를 해결하고자 Food를 Consumer에게 전달해 주는 방식으로 변경함 으로 써 Consumer는 추가적인 코드변경 없이 다른 Food를 먹을 수 있게 되었다.
결과적으로 제어의 흐름이 Food -> Consumer 로역전 되었다.
이러한 것을 제어의 역전이라고 말한다.
오늘의 회고
이것 밖에도 IoC 컨테이너 Bean 이러한 내용들이 있지만 의존성 주입 이라는 DI만 이해하기에도 머리가 어지러워 내일 다시금 복습을 해야하는 수 밖에 없을 것 같다.
'내일배움캠프' 카테고리의 다른 글
내일배움캠프 TIL - Spring Bean (1) | 2024.01.23 |
---|---|
내일배움캠프 TIL - DTO를 클래스에서 record로 (0) | 2024.01.23 |
내일배움캠프 TIL - Spring MVC 이모저모 (0) | 2024.01.18 |
내일배움캠프 TIL - Gradle에 관해서 (0) | 2024.01.17 |
내일배움캠프 12일차 TIL - Java stream 간략하게 알아보기 (1) | 2024.01.09 |