[Spring] IoC와 객체 결합도 낮추기

JeongEun Kim·2023년 4월 24일
1

Spring

목록 보기
2/8

IoC (Inversion of Control)

Inversion of Control : 제어의 역전
프레임워크 없이 개발할 때, 객체의 생성, 설정, 초기화, 메소드 호출, 소멸을 프로그래머가 직접 관리한다. 또한 전통적인 프로그래밍에서는 외부 라이브러리를 사용할 때, 개발자가 직접 외부 라이브러리를 호출하는 방식으로 사용된다.
하지만, 프레임워크를 사용하면 객체의 생명 주기를 모두 프레임워크에 위임할 수 있다. 즉, 외부 라이브러리가 프로그래머가 작성한 코드를 호출하고 흐름을 제어한다. 이와 같이 개발자가 작성한 객체나 메서드의 제어를 개발자가 아니라 외부에 위임하는 설계 원칙을 제어의 역전이라고 한다. 즉, 프레임워크는 제어의 역전 개념이 적용된 대표적인 기술이다.
IoC의 구현 방식 중 하나로 DI(Dependency Injection)이 존재한다.


  • 객체 제어 방식
    • 기존 : 필요한 위치에서 개발자가 필요한 객체 생성 로직 구현
    • IoC : 객체 생성을 Container에 위임하여 처리
  • IoC 사용에 따른 장점
    • 객체 간의 결합도를 떨어뜨릴 수 있음(loose coupling)
  • 객체 간 결함도가 높다면?
    • 해당 클래스가 유지보수 될 때 그 클래스와 결합된 다른 클래스도 같이 유지보수 되어야 할 가능성이 높다.

IoC 유형

Dependency Lookup

컨테이너가 lookup context를 통해 필요한 Resource나 Object를 얻는 방식이다.
JNDI 이외의 방법을 사용한다면, JNDI 관련 코드를 오브젝트 내에서 일일이 변경해주어야 한다. Lookup한 Object를 필요한 타입으로 Casting 해주어야 하며, Naming Exception을 처리하기 위한 로직이 필요하다.

Dependency Injection

Object에 lookup 코드를 사용하지 않고 컨테이너가 직접 의존 구조를 Object에 설정할 수 있도록 지정해 주는 방식이다.
Object가 컨테이너의 존재 여부를 알 필요가 없으며, Lookup과 관련된 코드가 Object 내에서 사라진다.
Setter Injection과 Constructor Injection이 존재한다.


Container

Container란?

객체의 생성, 사용, 소멸에 해당하는 라이프사이클을 담당한다. 라이프사이클을 기본으로 애플리케이션 사용에 필요한 주요 기능을 제공한다.

Container 기능

  • 라이프사이클 관리
  • Dependency 객체 제공
  • Thread 관리
  • 기타 애플리케이션 실행에 필요한 환경 설정

Container의 필요성

  • 비즈니스 로직 외 부가적인 기능에 대해서는 독립적으로 관리되도록 하기 위해서
  • 서비스 look up이나 Configuration에 대한 일관성을 갖기 위해서
  • 서비스 객체를 사용하기 위해 각각 Factory 또는 Singleton 패턴을 직접 구현하지 않기 위해서

IoC Container

  • 오브젝트의 생성과 관계설정, 사용, 제거 등의 작업을 애플리케이션 코드 대신 독립된 컨테이너가 담당한다.
  • 컨테이너가 코드 대신 오브젝트에 대한 제어권을 갖고 있어 IoC라고 불린다.
  • 따라서, 스프링 컨테이너를 IoC 컨테이너라 부르기도 한다.
  • 스프링에서 IoC를 담당하는 컨테이너에는 BeanFactory, Application Context가 있다.

Spring DI Container

Spring DI Container가 관리하는 객체를 빈(Bean)이라 하고, 이 빈들의 생명주기(Life-Cycle)를 관리하는 의미로 빈팩토리(BeanFactory)라 한다.
BeanFactory에 여러가지 컨테이너 기능을 추가한 것을 Application Context라 한다.

Bean Factory

  • Bean을 등록, 생성, 조회, 반환, 관리
  • 일반적으로 Bean Factory보다는 이를 확장한 ApplicationContext를 사용한다
  • getBean() method가 정의되어 있다

Application Context

  • Bean을 등록, 생성, 조회, 반환, 관리 기능은 BeanFactory와 같다
  • Spring의 각종 부가 서비스를 추가로 제공한다
  • Spring이 제공하는 Application Context 구현 클래스는 여러 종류가 있다


객체 결합도

객체 간 강한 결합

  • 클래스 호출 방식
  • 클래스 내에 선언과 구현이 모두 되어 있기 때문에 다양한 형태로 변화가 불가능

MemberService 구현체와 AdminService 구현체를 HomeController에서 직접 생성하여 사용. MemberService 또는 AdminService가 교체되거나 내부 코드가 변경되면 HomeController까지 수정해야 할 가능성이 있다.

다형성 이용

객체 간의 강한 결합을 다형성을 통해 결합도를 낮출 수 있다.

  • 인터페이스 호출 방식
  • 구현 클래스 교체가 용이하여 다양한 형태로 변화 가능
  • 인터페이스 교체 시 호출 클래스 수정 필요

MemberService와 AdminService는 CommonService를 상속받는다. HomeController에서 각 서비스를 이용할 시 MemberService와 AdminService는 CommonService Type으로 사용 가능하다.

Factory 이용

객체 간의 강한 결합을 Factory를 통해 결합도를 낮출 수 있다.

  • 팩토리 호출 방식
  • 팩토리 방식은 팩토리가 구현 클래스를 생성하므로 클래스는 팩토리를 호출
  • 인터페이스 변경 시 팩토리만 수정하면 됨. 호출 클래스에는 영향 미치지 않음.
  • 클래스에 팩토리 호출 소스 들어가야 함.(팩토리에 의존)

각 Service를 생성하여 반환하는 Factory를 만들었다. Service를 이용하는 쪽에서는 Interface만 알고 있으면 어떤 구현체가 어떻게 생성되는지 알 필요가 없다.
이 Factory 패턴이 적용된 것이 Container의 기능이며 이 Container의 기능을 제공해주고자 하는 것이 IoC 모듈이다.

Assembler 이용

객체 간의 강한 결합을 Assembler를 통해 결합도를 낮출 수 있다.

  • IoC 호출 방식
  • 팩토리 패턴의 장점을 더하여 어떤 것에도 의존하지 않는 형태가 됨
  • 실행 시점(Runtime)에 클래스 간의 관계가 형성됨

각 Service(객체)의 라이프사이클을 관리하는 Assembler를 사용한다. Spring Container가 외부 조립기(Assembler) 역할을 한다.

0개의 댓글