728x90
반응형

● IoC(Inversion of Control)란?

- IoC는 제어의 역전이라는 뜻으로 프로그램의 제어의 흐름을 직접 제어하는 것이 아니라 외부에서 관리하는 것을 말한다. 이전에는 개발자가 객체를 생성하고 관리하며 프로그램의 제어 흐름을 스스로 조종했다. 하지만 Spring을 이용하면 스프링 컨테이너가 프로그램의 제어흐름을 제어하게 된다.

스프링 컨테이너(Spring Container)란?

 - 스프링 컨테이너는 스피링의 빈(Bean)을 생성하고 관리한다. 스프링 컨테이너는 IoC Container 혹은 DI Container라고 불리는데, 이는 스프링 컨테이너가 IoC 혹은 DI를 도맡아 진행하기 때문이다. 즉, 스프링 컨테이너는 스프링 Bean들을 생성하고, 이들의 의존 관계를 연결해 주는 역할을 한다.

BeanFactory와 ApplicationContext(인프런 - 김영한님 강의)

 이러한 스프링 컨테이너는 BeanFctory와 Application Context로 나뉘는데 둘의 내용은 다음과 같다.

BeanFactory

 - 스프링 컨테이너의 최상위 인터페이스이다.

 - 스프링 빈을 관리하고 조회하는 역할을 담당한다.

ApplicationContext

 - BeanFactory 기능을 모두 상속받아 제공한다.

 - 다음과 같은 부가기능들을 제공한다.

  • 메시지 소스를 활용한 국제화 기능
  • 환경변수 - 로컬, 개발, 운영 등을 구분해서 처리
  • 애플리케이션 이벤트 관리
  • 편리한 리소스 조회

보통 스프링 컨테이너라 하면 ApplicationContext를 뜻한다. BeanFactory의 모든 기능을 상속 받는데다 편리한 부가기능을 제공하기 때문에 BeanFactory 보다는 ApplicationContext를 사용한다.

스프링 빈(Bean)이란?

스프링 공식문서에서 확인해 보자면

 스프링에서는, 스프링 IoC컨테이너에 의해 관리되고 애플리케이션의 핵심을 이루는 객체들을 Bean이로고 부른다. Bean은 스프링 IoC 컨테이너에 의해 인스턴스화되어 조립되거나 관리되는 객체를 말한다.

즉, 스프링 빈은 스프링 컨테이너에 의해 만들어지고 관리되는 객체라는 뜻이다.

[Spring] IoC,DI, 스프링 컨테이너(Container), 스프링 빈(Bean)이란? (tistory.com)

 

[Spring] IoC,DI, 스프링 컨테이너(Container), 스프링 빈(Bean)이란?

IoC(Inversion of Control)란? IoC는 제어의 역전이라는 뜻으로 프로그램의 제어 흐름을 직접 제어하는 것이 아니라 외부에서 관리하는 것을 말한다. 이전에는 개발자가 객체를 생성하고 관리하며 프로

code-lab1.tistory.com

 

728x90
728x90
반응형

● 의존관계(Dependency)란?

- 의존관계는 의존 대상 B가 변하면, 그것이 A에 영향을 미칠 때 A는 B와 의존관계라고 한다.

예를 들어 피자 가게의 요리사는 피자 레시피에 의존한다. 만약 피자 레시피가 변경된다면, 요리사는 피자를 새로운 방법으로 만들게 될 것이다. 레시피의 변화가 요리사에게 미쳤기 때문에 요리사는 레시피에 의존한다라고 할 수 있다.

public class PizzaChef {
	private PizzaRecipe pizzaRecipe;
    
    public PizzaChef() {
    	this.pizzaRecipe = new PizzaRecipe();
    }
}

PizzaChef 객체는 PizzaRecipe 객체에 의존 관계가 있다. 이러한 구조는 다음과 같은 문제점을 가지고 있다.

 -  두 클래스의 결합성이 높다.

 PizzaChef 클래스는 PizzaRecipe 클래스와 강하게 결합되어 있다는 문제점을 가지고 있다. 만약 PizzaChef가 새로운 레시피인 CheezePizzaRecipe 클래스를 이용해야 한다면 PizzaChef 클래스의 생성자를 변경해야만 한다. 마약 이후 레시피가 계속해서 바뀐다면 매번 생성자를 바꿔줘야 하는 등, 유연성이 떨어지게 된다.

 - 객체들 간의 관계가 아닌 클래스 간의 관계가 맺어진다.

객체 지향 5원칙(SOLID) 중 "추상화에 의존해야지, 구체화에 의존하면 안된다."라는 DIP원치기 존재한다. 현재 PizzaChef 클래스는 PizzaRecipe 클래스와 의존관계가 있다. 즉, PizzaChef는 클래스에 의존하고 있다. 이는 객체 지향 5원칙(SOLID)원칙을 위반하는 것이므로 PizzaChef 클래스의 변경이 어려워지게 된다.

이러한 문제점을 해결할 수 있는 것이 바로 의존관계 주입(Dependency Injection)이다.

의존관계 주입(Dependency Injection 이하 DI)이란?

 - DI는 의존관계를 외부에서 결정(주입) 하는 것을 말한다. 스프링에서는 이러한 DI를 담당하는 DI 컨테이너가 존재한다. 이 DI 컨테이너가 객체들 간의 의존관계를 주입한다.

 위의 문제점을 DI를 이용해 해결해 보자. 우선 다양한 피자 레시피를 추상화 하기 위해 PizzaRecipe를 interface로 만들자. 이후 다양한 종류의 피자 레시피는 PizzaRecipe 인터페이스를 구현하는 식으로 작성하면 된다.

public interface PizzaRecipe {}

public class CheesePizzaRecipe implements PizzaRecipe {}

이제 PizzaChef 클래스의 생성자에서 외부로부터 피자 레시피를 주입(Injection) 받도록 변경하자.

public class PizzaChef {

	private PizzaRecipe pizzaRecipe;
    
    public PizzaChef(PizzaRecipe pizzaRecipe) {
    	this.pizzaRecipe = pizzaRecipe;
    }
}

이때 스프링의 DI 컨테이너가 애플리케이션 실행 시점에 필요한 객체를 생성하여 PizzaChef클래스에 주입해주는 역할을 한다.

// DI 컨테이너에서의 동작
PizzaChef = new PizzaChef(new CheesePizzaRecipe());

// 치즈 피자 레시피에서 베이컨 피자 레시피로 바뀐다면
PizzaChef = new PizzaChef(new BaconPizzaRecipe());

이렇게 하면 피자 셰프는 피자 레시피가 바뀌더라도 생성자를 변경하지 않아도 된다. 그저 레시피가 바뀐다면 외부에서 바뀐 레시피를 주입해주기만 하면 된다.

이처럼 의존관계를 외부에서 결정하여 주입하는 것이 DI이다.

의존 관계 주입 방법

 -  스프링 공식문서에 따르면 DI는 2가지의 주요방법이 있다.

  1. 생성자 주입(Constructor-based Dependency Injection)

  2. 수정자 주입(Setter-based Dependency Injection)

 생성자 주입은 위에서 본 것 처럼 생성자를 이용하여 의존관계를 주입한 것이다. 생성자 주입은 생성자의 호출 시점에 1회 호출되는 것이 보장된다. 따라서 주입받은 객체가 변하지 않거나, 반드시 객체의 주입이 필요한 경우에 사용 할 수 있다.

public class PizzaChef {
	private PizzaRecipe pizzaRecipe;
    
    public setPizzaRecipe(PizzaRecipe pizzaRecipe) {
    	this.pizzaRecipe = pizzaRecipe;
    }
}

PizzaChef pizzaChef = new PizzaChef();

pizzaChef.setPizzaRecipe(new CheesePizzaRecipe());

이러한 수정자 주입 방법은 생성자 주입과 다르게 주입받는 객체가 변경될 가능성이 있는 경우에 사용한다.(인프런 김영한님에 의하면 거의 없다고 한다.)

이외에도 필드 주입, 일반 메서드 주입 등 방법이 있지만, 스프링은 생성자 주입을 사용하기를 권장한다.

 의존 관계 주입의 장점

- DI를 사용하면 다음과 같은 장점들이 존재한다.

 1. 결합도가 줄어든다.

 어떤 객체가 다른 객체에 의존한다는 것은, 그 의존 대상의 변화에 취약하다는 뜻이다. DI를 이요하면 주입받는 대상이 바뀔지 몰라도 해당 객체의 구현 자체를 수정할 일은 없어진다.

 2. 유연성이 높아진다.

 기존 PizzaChef 클래스는 피자 레시피를 바꾸는 것이 쉽지 않았다. 생성자 코드 자체를 변경해주어야 했지만, DI를 이용하면 생성자의 인수만 다른 피자 레시피로 바꿔주면 된다.

 3. 테스트하기 쉬워진다.

 DI를 이용한 객체는 자신이 의존하고 있는 인터페이스가 어떤 클래스로 구현되어 있는지 몰라도 된다. 따라서 테스트하기 더 쉬워진다.

 4. 가독성이 높아진다.

 

[Spring] 의존관계 주입(Dependency Injection), 의존성 주입, DI란? (tistory.com)

 

[Spring] 의존관계 주입(Dependency Injection), 의존성 주입, DI란?

의존관계(Dependency)란? 의존관계 주입(Dependency Injection)에 대하여 알아보기 전에 의존관계가 무엇인지 알아야 한다. 의존관계는 의존 대상 B가 변하면, 그것이 A에 영향을 미칠 때 A는 B와 의존관계

code-lab1.tistory.com

 

728x90
728x90
반응형

● 클린코드로 유명한 로버트 마틴의 좋은 객체 지항 설계의 5가지 원칙

 - SRP : 단일 책임 원칙(Single Reponsibility Principle)

 - OCP : 개방-폐쇄 원칙(Open/Closed Priciple)

 - LSP : 리스코프 치환 원칙(Liskvo subsititution Principle)

 - ISP : 인터페이스 분리 원칙(Interface Segregation Principle)

 - DIP : 의존관계 역전 원칙(Dependency Inversion Principle)

 

 ● SRP 단일 책임 원칙

 - 한 클래스는 하나의 책임만 져야 한다.

 -  하나의 책임이라는 것은 모호하다.(문맥과 상황에 따라 다르다.)

 -  중요한 기준은 변경이다. 변경이 있을 때 파급 효과가 적으면 단일 책임 원칙을 따라야한다.

  OCP 개방-폐쇄 원칙

 - 확장에는 열려 있어야 하나 변경에는 닫혀 있어야 한다.

 - 다형성을 활용하여 가능하다.

 - 인터페이스를 구현한 새로운 클래스를 하나 만들어서 새로운 기능을 구현해야 한다.

  LSP 리스코프 치환 원칙

 - 프로그램의 객체는 프로그램의 정확성을 깨트리지 않으면서 하위 타입의 인스턴스로 변경 할수 있어야 한다.

 - 다형성에서 하위 클래스는 인터페이스 규약을 다 지켜야한다는 것, 다형성을 지원하기 위한 원칙, 인터페이스를 구현한 구현체를 믿고 사용하려면, 이 원칙이 필요하다.

(ex. 자동차의 엑셀은 앞으로 가는 기능이지만 뒤로가게 구현하면 LSP 위반이다.)

ISP 인터페이스 분리 원칙

- 특정 클라이언트를 위한 인터페이스 여러 개가 범용 인터페이스 하나보다 낫다.

(ex. 자동차인터페이스 -> 운전 인터페이스, 정비 인터페이스)

(ex. 사용자 클라이언트 -> 운전자 인터페이스, 정비사 인터페이스)

- 인터페이스가 명확해 지고, 대체 가능성이 높아진다.

  DIP 의존관계 역전 원칙

 - 프로그래머는 "추상화에 의존 해야한다. 구체화에 의존하면 안된다." 의존성 주입은 이 원칙을 따르는 방법 중 하나이다.

 - 즉, 인터페이스에 의존 하라는 것이다.

 - 구현체에 의존하게 되면 변경이 힘들어진다.

 

정리

 - 객체 지향의 핵심은 다형성

 - 다형성 만으로는 쉽게 부품을 갈아 끼울듯 개발할 수가 없다.

 - 다형성 만으로 구현 객체를 변경 할 때 클라이언트 코드도 함께 변경된다.

- 다형성 만으로 OCP, DIP를 지킬 수 없다.

728x90

+ Recent posts