Java - Spring &&n SpringBoot

자바 Spring 개념 잡기 : IoC 제어의 역전, 의존성 주입(Dependency Injection) 와 컨테이너, 스프링 빈

TerianP 2021. 12. 13.
728x90

공부하면서 정리한 내용입니다. 틀린 부분이 있을 수 있으니 언제든지 지적 환영합니다.

1. DI(Dependency Injection) : 의존성 주입

의존성 주입에 대해서 설명하기 이전에 '의존성, 의존관계' 이 무엇인지 부터 생각해야한다.

의존 관계 Dependency 즉 "A 가 B 를 의존한다 " 의 의미는 다음과 같다.

 

의존대상 B가 변하면, 그것이 A에 영향을 미친다.
즉 B의 기능이 추가 또는 변경되거나 형식이 바뀌면 그 영향이 A에 미치는것을 의미한다. 

 

- 의존적인 객체 관계

의존성 주입에 대해 설명하기 위해 커피를 마시는 아침을 예시로 들어보겠다. Cafe_A 클래스와 Cafe_B 클래스가 있다. Cafe_A 에서는 아메리카노와 카푸치노를 팔고, Cafe_B에서는 모카와 라떼를 판다. 먼저 Cafe_A 에서 서로 객체간 의존성이 강한 코드를 작성해보겠다.

 

  • Cafe_A
// Cafe_A

public class Cafe_A {
    public void where(){
        System.out.println("여기는 CAFE_A");
    }
}


class Capuuccino extends Cafe_A {  // 카페 A 안에 있는 카푸치노

    public void drink(){
        System.out.println("Cafe_A 나는 카푸치노를 마셔요");
    }

}

class Americano extends Cafe_A { // 카페 A 안에 있는 아메리카노
    public void drink(){
        System.out.println("Cafe_A 나는 아메리카노를 마셔요");
    }


}

 

  • 그런데 뭔가 이상하다. 분명 A 카페에서 아메리카나랑 카푸치노랑 둘 다 판다고 들었는데 실제로 메인 메소드에서 실행해보니 Morning 생성자 에 Americano 객체만 들어와 있다. 이때문에 결국 카푸치노가 아닌 아메리카노를 시켰다.
// 메인 Morning 클래스

public class Morning {
    private Cafe_A cafe_a;

    public static void main(String args[]){

        Morning morning = new Morning();

        morning.cafe_a.where(); // 어느 카페에서 시켰지?
        morning.cafe_a.drink(); // 어떤 것을 마실까?

    }

    public Morning() {
        this.cafe_a = new Americano(); 
        // 주인장 왈 : 오늘은 아메리카노만 팔아요 딴 거 마시려면 객체 바꿔주세요

    }

}

결국 아메리카노를 시켰어요ㅠ

 

  • 그렇다면 만약에 내가 카푸치노를 꼭! 먹고 싶다면 어떻게 해야할까? 
  • 그러면 Morning 생성자에서 new Americano() 가 아닌 new Capuuccino() 로 바꿔야 할 것이다. 지금은 간단하게 메뉴만 바꿔서 그렇지만, new Americano 에서 new Capuuccino 로 바꿔야하는게 10개라면 어떨까? 만약 100개라면? 그것들을 모두 하나하나 new Capuuccino 로 바꿔주어야 할 것이다. 
  • 이 의미는 결국 Morning 객체가 생성 될 때 기본 생성자 Morning 가 실행되고, 이때 private cafe_A cafe_a 로 설정된 인스턴스 변수에 new Americano() 객체가 들어오는 것이다.
  • 즉, cafe_a 에 어떤 객체가 들어오는지 따라서  Morning 클래스를 통해서 먹을 커피가 결정되는 것이고, 이는  Morning 이 cafe_a 에 따라서 변하게 됨으로 Morning 이 cafe_a 에 의존적이다 라고 이야기 할 수 있다.
    public Morning() {
        this.cafe_a = new Capuuccino(); 
    }

바꾸면 변한다!

- So What Is DI?

의존성 주입(Dependency Injection)은 이렇게 Morning이 Cafe_A 에게서 받는 영향과 객체간의 의존성(종속성)과 결합도를 줄이기 위해 사용하는 기법이다. 스프링 자바에서 특히 자주 사용되는 개념이며 거의 스프링으로 코딩을 하기 위해서 사용되는 기본적인 개념 중 하나이다. 이번에는 의존성 주입을 통해서 Cafe_B에서 커피를 마셔보겠다.

  • Cafe_B
package HJproject.Hellospring.Practice;

class Latte extends Cafe_B {

    void drink() {
        System.out.println("Cafe_B 나는 라떼를 마셔요");
    }
}

class Moca extends Cafe_B{
    void drink() {
        System.out.println("Cafe_B 나는 모카를 마셔요");
    }
}

public class Cafe_B{

    void where(){
        System.out.println("여기는 Cafe_B");
    }

    void drink(){
        System.out.println("나는 Cafe_B");
    }

}

 

  • 이번에도 카페를 시켜보겠다. 하지만 이번에는 카페를 시키는 모양이 조금 다르다. 이전에는 Morning 생성자 안에서 객체를 만들어서 생성 한 후 Morning 객체를 생성했다면, 이번에는 Morning 객체를 생성하면서 내가 어떤 커피를 먹을지 삽입(주입)해서 객체를 생성하였다.
  • 이전 코드와 가장 큰 차이점이 바로 이 부분이다. Morning 이 어떤 커피를 먹을지 미리 객체가 삽입되어서 정해지는 것이 아니라 추후에 객체를 생성 할 때 필요한 객체를 주입해줌으로써 객체간의 결합도를 줄이고 보다 유연한 코드를 작성 할 수 있게 한다.
// Morning 클래스

public class Morning {
    private Cafe_B cafe_b;

    public static void main(String args[]){


        Latte latte = new Latte(); // 외부에서 라떼 객체 생성
        Morning morning = new Morning(latte); // 여기 객체는 라떼 객체 주입
        morning.cafe_b.where();
        morning.cafe_b.drink();

        Moca moca = new Moca(); // 외부에서  모카 객체 생성
        Morning morning2 = new Morning(moca); // 이번에 객체는 모카 객체 주입
        morning2.cafe_b.where();
        morning2.cafe_b.drink();

    }


    public Morning(Cafe_B cafe_b) {
        this.cafe_b = cafe_b;
        // 주인장 왈 : 먹고 싶은 커피는 객체 만들때 알아서 결정하쇼
    }
}

이번에는 Morning 에 의존하지 않고 내 맘대로 먹을 수 있다

 


여기서 정리!! DI 란 무엇인가? 장점은 무엇인가?

  • DI 의존 관계 주입은 어떤 객체가 사용하는 의존 객체를 직접 만들어 사용하는게 아니라 외부에서 생성된 객체를 주입 받아 사용하는 방법이다. => new 연상자를 이용해서 객체 생성 후 주입받음
  • 쉽게 생각하면 A 객체 생성시 생성자를 통해 외부에서 만들어진 B 객체를 주입받는 방법이다.
  • DI의 장점은 다음으로 요약 할 수 있다.
    • 객체간 결합도가 낮기 때문에 추후 코드 테스트가 쉬워진다 => 종속성과 결합도 모두 낮아진다.
    • 코드 재사용과 코드의 유연성이 높아진다.
    • 코드의 중복을 막을 수 있고, 보다 가독성이 좋아진다.
  • 여기서는 '생성자를 이용한 의존성 주입' 만 보았다. 이 방법 외에도 Field 변수를 이용하는 방법, setter 을 이용하는 방법 등이 있다. 이 중 Field 를 이용한 방법은 추후 스프링 관련 글에서 다시 설명하겠다.

2. IoC(Inversion of Control) : 제어의 역전

  • 제어의 역전이란 오브젝트(객체) 생성, 관계 설정, 사용, 제거 등 오브젝트 전반에 걸친 모든 제어권을 애플리케이션이 갖는게 아니라 프레임워크의 컨테이너에게 넘기는 개념! 을 의미한다 => 스프링에서 관리하는 객체를 '빈(Bean)' 이라고 한다
  • 앞에서 보았던 의존성 주입 DI이 IoC의 일종이라고 생각하면 된다. 보통 스프링에서 의존성 주입은 스프링 IoC 컨테이너에서 해주게 된다.

즉, 우리가 객체를 직접 생성하고, 의존성을 주입하고 하는 것이 아니라 스프링 컨테이너에서 객체의 생명 주기(생성, 주입 소멸 등)를 전담 관리해주게 된다. => 개발자에게 객체 생명 주기 제어권이 있는게 아니라 스프링 컨테이너에 제어권이 있다! 이것이 바로 IoC 제어의 역전이다.

 


3. 스프링 컨테이너 - 빈 팩토리와 어플리케이션 컨텍스트

스프링 컨테이너란 객체의 생명 주기를 관리하며 생성된 인스턴스들에게 다양한 기능을 제공하는 담당자이다. 스프링 컨테이너는 자바 객체를 담고 있으며, 컨테이너 안에 있는 필요한 객체를 가져와 사용 할 수 있다. 이때 제어의 역전 IoC를 통해 스프링 컨테이너 안에서 관리되는 빈(객체)의 생명주기를 관리한다.

 

스프링 컨테이너의 종류는 크게 2가지 - 빈팩토리 BeanFactory 와 어플리케이션 컨텍스트 - 로 나뉜다.

 

1) 빈 팩토리 BeanFactory

  • 빈 팩토리는 말 그대로 빈 Bean 의 공장이다. 이곳에서 빈을 등록하고, 생성하고 조회하고 호출해주고 등등 다양한 기능을 담당한다.
  • 빈 팩토리는 빈의 정의(빈의 내용) 은 즉시 로딩해오지만 빈 자체가 필요해서 사용되기 전까지는 인스턴스화 하지 않는다.
  • getBean() 을 통해 호출되면 팩토리는 의존성 주입 DI 를 통해 빈을 인스턴스화하고 빈의 특성을 설정하기 시작한다. 이때부터 빈의 생명이 시작된다.
  • 스프링 빈을 스프링 IoC 컨테이너에 등록하기 위해서는 자바 어노테이션(@Anootation, @Component 등) 을 사용하거나 Bean Configuration File 에 직접 Bean 을 등록하는 방법이 있다.

2) 어플리케이션 컨텍스트 ApplicationContext

  • 어플리케이션 컨텍스트는 빈 팩토리를 상속, 확장한 향상된 컨테이너라고 생각하면 된다.
  • 기본적인 기능은 빈 팩토리와 동일하나 스프링이 제공하는 각종 부가 기능을 추가로 제공한다.
    • 대표적으로 트랜젝션 관리, 메시지 기반의 다국어 처리, AOP 처리 등
  • 어플리케이션 컨텍스트는 컨텍스트 초기화 시점에 미리 빈을 생성해놓아서 실제로 빈이 필요할 때 즉시 사용할 수 있도록 한다.

3) 빈 팩토리와 어플리케이션 컨텍스트 차이점

두 스프링 컨테이너의 가장 큰 가장 큰 차이점은 미리 만들어지는가 아니면 호출된 시점에 만들어지는가? 이다.

  • 빈 팩토리 BeanFactory 의 경우에는 처음으로 getBean() 이 호출된 시점에서야 해당 빈을 생성한다 -> lazy loading
  • 반면 어플리케이션 컨텍스트는 컨테이너가 구동되는 시점에 미리 빈을 로드 & 생성 Pre-loading 한 후 필요할 때 즉시 사용할 수 있도록 한다.

 


참고한 글

https://jobjava00.github.io/language/java/framework/spring/container/

 

[Spring] 컨테이너

Web programmer

jobjava00.github.io

https://velog.io/@ehdrms2034/Spring-MVC-Application-Context.xml

 

[Spring MVC] 스프링 컨테이너에 대한 요약

본 글은 스프링 MVC에 대해 지식을 정리하고 나중에 헷갈릴 때 다시 보기 위한 글입니다 👀스프링 프레임워크는 스프링의 빈을 생성하고 관리하는 컨테이너를 가지고 있다. 이를 통해서 스프링

velog.io

https://bamdule.tistory.com/174

 

[Spring] 의존성 주입(Dependency Inject,DI)이란?

의존성 주입을 이해하기 전에 의존성에 대해서 먼저 알아보겠습니다. 1. 의존성이란? 의존의 사전적 의미는 "스스로 하지 못하고 누군가에게 도움을 받음" 입니다. 그렇다면 의존성은 무엇일까

bamdule.tistory.com

https://devlog-wjdrbs96.tistory.com/165

 

[Spring] 스프링 의존성 주입(DI) 이란?

Spring 삼각형 스프링의 기반이 되는 설계 개념을 표현한 것 스프링이란 IoC와 AOP를 지원하는 경량의 컨테이너 프레임워크이다. 1. DI(Dependency Injection)이란? Inversion of Control 이라고도 하는 의존 관..

devlog-wjdrbs96.tistory.com

https://velog.io/@wlsdud2194/what-is-di

 

[DI] Dependency Injection이란 무엇일까?

Dependency Injection, 의존성 주입이 무엇이고 어떤 이점이 있는지 예시를 통해 정리한 글입니다.

velog.io

 

댓글