[Spring] IoC, DI

thingzoo·2023년 6월 25일
0

Spring

목록 보기
20/54
post-thumbnail

Spring의 특징은 DI(의존성 주입) 패턴을 사용하여 IoC(제어의 역전) 설계 원칙을 구현한다는 점이다.
이게 도.대.체. 무엇인지 알아보자..!

DI(Dependency Injection)

객체를 직접 생성하는 게 아니라 외부에서 생성한 후 주입 시켜주는 방식

  • DI(의존성 주입)를 통해서 모듈 간의 결합도가 낮아지고 유연성이 높아짐

의존성

쉽게 말하자면 하나의 코드가 다른 코드를 필요로 하는 정도

강한 의존성(결합)

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 가 치킨이 아니라 피자를 먹고 싶어 한다면? 많은 수의 코드 변경이 불가피함
이 경우 Consumer 와 Chicken는 강한 결합, 강한 의존성이라고 할 수 있음

약한 의존성(결합)

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("피자를 먹는다.");
    }
}

주입 방법

필드에 직접 주입

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();
    }
}

메소드를 통한 주입

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();
    }
}

생성자를 통한 주입 ✨

객체의 불변성을 지켜줄 수 있기 때문에 가장 많이 쓰는 방법

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();
    }
}

IoC(Inversion of Control)

메소드나 객체의 호출작업을 개발자가 결정하는 것이 아니라, 외부에서 결정되는 것
IoC는 제어의 역전이라고 말하며, 간단히 말해 "제어의 흐름을 바꾼다"라고 한다.

  • 객체의 의존성을 역전시켜 객체 간의 결합도를 줄이고 유연한 코드를 작성할 수 있게 하여
  • 가독성 및 코드 중복, 유지 보수를 편하게 할 수 있게 함

예시

  • 이전에는 Consumer가 직접 Food를 만들어 먹었기 때문에 새로운 Food를 만들려면 추가적인 요리준비(코드변경)가 불가피했다.
    • 그렇기 때문에 이때는 제어의 흐름이 Consumer → Food 이었다.
  • 이를 해결하기 위해 만들어진 Food를 Consumer에게 전달해주는 식으로 변경함으로써 Consumer는 추가적인 요리준비(코드변경) 없이 어느 Food가 되었든지 전부 먹을 수 있게 되었다.
    • 결과적으로 제어의 흐름이 Food → Consumer로 역전되었다.
profile
공부한 내용은 바로바로 기록하자!

0개의 댓글