[SPRING] IoC & DI의 이해

정솔·2023년 9월 7일
0

SPRING

목록 보기
1/1
post-thumbnail

이번 포스팅에선 스프링을 공부하다 보면 꼭 만나게 되는
IoC 와 DI 에 대해 쉽게 알아보자.

1. IoC (Inversion of Control)

IoC란?

IoC는 '제어의 역전'이라고 번역 된다.
여전히 어렵다.

개발자가 객체의 생성과 소멸, 의존성 주입, 제어 흐름 등을
모두 수동으로 담당했던 기존의 프로그래밍과 달리
이런 제어 흐름과 객체 관리를 컨테이너나 프레임워크로 위임하는 것이다.
덕분에 개발자는 비지니스 로직에만 집중할 수 있는 것이다.

코드로 보면 이해가 더 잘 될 것이다.


IoC 사용하지 않은 코드

public class UserService {

    private UserDao userDao;

    public UserService() {
        this.userDao = new UserDao(); // UserDao 인스턴스를 직접 생성
    }

    public void saveUser(User user) {
        // 사용자 정보 저장 로직
        userDao.save(user);
    }
}

public class Main {
    public static void main(String[] args) {
        UserService userService = new UserService();
        User user = new User("John");
        userService.saveUser(user);
    }
}

IoC 사용 코드

@Service // 스프링 빈으로 등록
public class UserService {

    private final UserDao userDao;

    @Autowired // 의존성 주입
    public UserService(UserDao userDao) {
        this.userDao = userDao;
    }

    public void saveUser(User user) {
        // 사용자 정보 저장 로직
        userDao.save(user);
    }
}

public class Main {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        UserService userService = context.getBean(UserService.class);
        User user = new User("John");
        userService.saveUser(user);
    }
}

스프링 컨테이너에 제어를 위임함으로써
개발자가 직접 객체를 생성하거나 초기화하지 않아도

UserService 객체를 얻을 수 있는 것을 알 수 있다.
스프링 컨테이너가 객체를 생성 소멸, 메서드 호출과 같은 제어를 맡아주기 때문에
신경쓰지 않아도 된다. 그저 가져다 쓰면 되는 것이다



2. DI (Dependency Injection)

DI란?

DI는 '의존성 주입'이라고 번역 된다.
IoC 개념을 구현하기 위해 사용하는 디자인 패턴 중 하나이며,
객체의 의존관계를 직접 결정하는 것이 아니라 외부로부터 주입받는 것이다.
외부로부터 주입받는다는 것이 뭘까? 코드로 알아보자


DI 사용하지 않은 코드

public class OrderService {

    public void 주문(Order order) {

//        카드 card = new 신용카드();
        카드 card = new 체크카드();

        card.결제(order);
    }
}

public class 체크카드 implements 카드 {
    public void 결제(Order order) {
        // 체크카드 결제 처리 로직
    }
}

public class 신용카드 implements 카드 {
    public void 결제(Order order) {
        // 신용카드 결제 처리 로직
    }
}

public interface 카드 {
    void 결제(Order order);
}

보다시피 직접 의존 대상을 결정하고 있다.

// 카드 card = new 신용카드();
카드 card = new 체크카드();

DI를 적용한 버전의 코드를 보며
의존관계를 외부에서 주입한다는 것이 뭔지 알아보자.


DI 사용 코드

public class OrderService {

    private final 카드 card;

    public OrderService(카드 card) {
        this.card = card;
    }

    public void 주문(Order order) {
        // 주문 처리 로직
        card.결제(order);
    }
}


public class 체크카드 implements 카드 {
    public void 결제(Order order) {
        // 체크카드 결제 처리 로직
    }
}

public class 신용카드 implements 카드 {
    public void 결제(Order order) {
        // 신용카드 결제 처리 로직
    }
}

public interface 카드 {
    void 결제(Order order);
}

이 예제는 생성자 주입(나중에 자세히 다룰 예정)을 받은 예제로,
외부에서 의존 대상을 주입 받는다. 아래 처럼 말이다.

private final 카드 card;

public OrderService(카드 card) {
        this.card = card;
    }

DI를 적용함으로써 추상화(인터페이스)에 의존하며
실제로 어떤 객체(클래스)가 구현될지는 몰라도 되는 것이다.
(이는 구현 객체가 변경될 때 빛을 발한다.)

이렇게 DI는 객체간 결합도를 낮추고 확장과 변경을 용이하게 한다.

0개의 댓글