IoC, DI, Spring Container

루미·2022년 7월 26일
0

Spring

목록 보기
1/11

Class란?

  • 객체를 만들기 위한 설계도 혹은 틀
  • 연관되어 있는 변수와 메서드의 집합

Object란?

  • 소프트웨어 세계에 구현할 대상
  • 물리적으로 존재하거나 추상적으로 생각할 수 있는 것 중에서 자신의 속성을 갖고있고, 다른것과 식별 가능한 것을 말한다.
  • 객체는 속성과 동작으로 구분되어있으며 자바에서는 이 속성과 동작을 Field와 Method라 부른다.

Instance(인스턴스)란?

  • 설계도를 바탕으로 소프트웨어 세계에 구현된 구체적인 실체. 객체를 소프트웨어화 시키면 그것을 인스턴스라고 부른다.
  • 실체화된 인스턴스는 메모리에 할당된다.
  • OOP의 관점에서 보면 객체가 메모리에 할당되어 실제로 사용될 때 '인스턴스'라고 부른다.

Ex)
A a = new A();
a.b;
-> A클래스의 객체 b를 인스턴스 a를 이용해 다른 클래스에서 호출할 수 있다.


IoC(Inversion of Control) 제어의 역전 이라고 해석된다. 객체의 생성과 생명주기관리까지 모든 객체에 대한 제어권이 역전됨을 의미한다.
-> 개발자가 객체를 직접 생성하고 삭제하는것이 아니라 spring이 대신 해주는것을 말함

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

기존에는 [객체생성 -> 의존성 객체 생성(클래스 내부에서) -> 의존성 객체 메서드 호출]의 방식이었지만 스프링에서는 [객체 생성 -> 의존성 객체 주입(스스로가 만드는것이 아니라 제어권을 스프링에게 위임하여 스프링이 만들어놓은 객체(bean)를 주입시킨다) -> 의존성 객체 메서드 호출]

비슷해보이지만 객체 '생성'과 '주입'은 엄연히 다르다. 스프링이 모든 의존성 객체를 서버가 실행될 때 만들어주고, 필요한곳에 주입시켜줌으로써 Bean들은 Singleton pattern을 가진다.

  • 싱글톤 패턴(Singleton pattern) 이란?
    하나의 인스턴스만 생성하여 사용하는 디자인 패턴. 인스턴스가 필요할 때 똑같은 인스턴스를 한번 더 만들지 말고
    기존의 인스턴스를 활용하는것을 말한다. 생성자가 여러번 호출되어도 실제로 생성되는 객체는 하나이며 최초로 생성
    된 이후에 호출된 생성자는 이미 생성된 객체를 반환시키도록 만드는것이다.
    
    * 왜쓰는가 ?
    1. 메모리 낭비 줄이기
    2. 생성된 인스턴스는 '전역' 이기 때문에 다른클래스의 인스턴스들이 데이터를 공유하는것이 가능한 장점이 있다.
    3. 주로 공통된 객체를 여러개 생성해서 사용해야 하는 상황에 주로 사용한다. (데이터베이스에서 커넥션풀, 스레드풀, 캐시, 
    로그 기록 객체 등)

DI(Dependency Injection)란 스프링이 다른 프레임워크와 차별화되어 제공하는 의존관계 주입기능이다. 객체를 직접 생성하는것이 아닌 외부에서 생성한 후 주입시켜주는 방식이다.
이를통해 모듈간의 결합도가 낮아지고 유연성이 높아진다.

첫번째 방법은 A객체가 B와 C객체를 new 생성자를 통해 직접 생성하는 방법이고

두번째 방법은 외부에서 생성된 객체를 setter()/생성자를 통해 사용하는 방법이다.

두번째 방식이 의존성 주입의 예시인데 이렇게 했을경우 왜 유연성이 높아지고 결합력이 낮아질 수 있는가?

다음과 같이 Store 객체가 Pencil 객체를 사용하고 있는 경우 Store객체가 Pencil 객체에 의존성이 있다고 표현한다.

그리고 두 객체간의 관계를 맺어주는것을 의존성 주입이라고 하며 다양한 주입방법이 존재한다.

public class Store {

private Pencil pencil;

public Store() {
    this.pencil = new Pencil();
}

}

예를 들어 연필이라는 상품과 1개의 연필을 판매하는 Store 클래스가 있다고 하자.
위와 같은 예시는 두가지 문제점이 존재한다.

1. 두 클래스가 강하게 결합되어있음
2. 객체들간 관계가 아닌 클래스간 관계가 맺어져 있음

위와 같은 Store 클래스는 현재 Pencil 클래스와 강하게 결합되어 있다는 문제점을 가지고 있다. 두 클래스가 강하게 결합되어 있어서 만약 Store에서 Pencil이 아닌 Food와 같은 다른 상품을 판매하고자 한다면 Store 클래스의 생성자에 변경이 필요하다. 즉, 유연성이 떨어진다. 각각의 다른 상품들을 판매하기 위해 생성자만 다르고 나머지는 중복되는 Store 클래스들이 파생되는 것은 좋지 못하다. 이에 대한 해결책으로 상속을 떠올릴 수 있지만, 상속은 제약이 많고 확장성이 떨어지므로 피하는 것이 좋다.

위의 Store와 Pencil은 객체들 간의 관계가 아니라 클래스들 간의 관계가 맺어져 있다는 문제가 있다. 올바른 객체지향적 설계라면 객체들 간에 관계가 맺어져야 한다. 객체들 간에 관계가 맺어졌다면 다른 객체의 구체 클래스(Pencil인지 Food 인지 등)를 전혀 알지 못하더라도, (해당 클래스가 인터페이스를 구현했다면) 인터페이스의 타입(Product)으로 사용할 수 있다.

[ 의존성 주입(Dependency Injection)을 통한 문제 해결 ]
위와 같은 문제를 해결하기 위해서는 우선 다형성이 필요하다. Pencil, Food 등 여러 가지 제품을 하나로 표현하기 위해서는 Product 라는 Interface가 필요하다. 그리고 Pencil에서 Product 인터페이스를 우선 구현해주도록 하자.

public interface Product {}

public class Pencil implements Product {}

이제 우리는 Store와 Pencil이 강하게 결합되어 있는 부분을 제거해주어야 한다. 이를 제거하기 위해서는 다음과 같이 외부에서 상품을 주입(Injection)받아야 한다. 그래야 Store에서 구체 클래스에 의존하지 않게 된다.

public class Store {

private Product product;

public Store(Product product) {
    this.product = product;
}

}

여기서 Spring이 DI 컨테이너를 필요로 하는 이유를 알 수 있는데, 우선 Store에서 Product 객체를 주입하기 위해서는 애플리케이션 실행 시점에 필요한 객체(빈)를 생성해야 하며, 의존성이 있는 두 객체를 연결하기 위해 한 객체를 다른 객체로 주입시켜야 하기 때문이다.
예를 들어 다음과 같이 Pencil 이라는 객체를 만들고, 그 객체를 Store로 주입시켜주는 역할을 위해 DI 컨테이너가 필요하게 된 것이다.

public class BeanFactory {

public void store() {
    // Bean의 생성
    Product pencil = new Pencil();

    // 의존성 주입
    Store store = new Store(pencil);
}

}

그리고 이러한 개념은 제어의 역전(Inversion of Control, IoC)라고 불리기도 한다. 어떠한 객체를 사용할지에 대한 책임은 프레임워크에게 넘어갔고, 자신은 수동적으로 주입받는 객체를 사용하기 때문이다.

profile
Backend 개발자가 되어보자!!

0개의 댓글

관련 채용 정보