1. IoC(Inversion of Control) :  "제어의 역전"이라는 의미로, 전통적인 프로그래밍에서 객체가 자신의 로직을 제어하는 구조에서 벗어나, 제어의 흐름을 외부(프레임워크나 컨테이너 ) 위임하는 디자인 원칙입니다. 이를 통해 객체 간의 결합도를 줄이고, 모듈 간의 독립성을 높이는데 목적이 있습니다. IoC 프레임워크가 코드의 흐름을 담당하고 개발자는 안에서 필요한 코드를 제공하는 방식으로 작동합니다.Spring 같은 경우 프레임워크가 만들어 놓은 객체를 주입, IoC 컨테이너에서 Bean으로 등록된 객체를 관리합니다.
  1. DI(Dependency Injection) : DI IoC 형태로, "의존성 주입"이라고 합니다. 객체가 서로 의존하는 상황에서, 의존성(객체가 필요로 하는 다른 객체) 직접 생성/관리하는 것이 아니라 외부(컨테이너나 프레임워크 )에서 주입하는 방식을 말합니다. 이를 통해 객체 간의 결합을 느슨하게 만들어 유지보수성과 재사용성을 향상시킬 있습니다.의존성을 주입하는 방법에는 생성자 주입, Setter 주입, 필드 주입 등이 있는데 Spring에서는 생성자 주입을 권장, 생성자 주입은 생성자의 호출 시점에 1회 호출 되는 것이 보장되기 때문에 주입받은 객체가 변하지 않거나, 반드시 객체의 주입이 필요한 경우 강제로 주입하기 위해 사용할 수 있다는 장점이 있습니다.
    객체가 생성될 때 생성자가 호출되며, 그 시점에 의존성이 주입
    -> 객체가 생성되는 시점에 필요한 모든 의존성이 주입되어야 함을 보장하며, 이후에 변경될 수 없다는 특징
    따라서, 생성자 주입은 "반드시 객체의 주입이 필요한 경우"에 주로 사용됩니다. 생성자에서 필요한 의존성을 선언하면, 해당 객체를 생성하려면 반드시 해당 의존성을 제공해야 합니다. 이렇게 하면 필요한 의존성이 없는 상태로 객체가 생성되는 것을 방지할 수 있습니다

** 예시

    1. 자동차(Car) 객체가 엔진(Engine) 객체에 의존하는 상황에, Car 객체 내부에서 Engine 객체를 직접 생성하면 Car Engine 강하게 결합되어 있게 됩니다. 이런 상황에서 Engine 변화가 Car에도 영향을 미치게 되어 유지보수가 어려워집니다. 반면, DI 사용하면 Engine 객체는 외부에서 Car 객체에 주입되므로, Car Engine 생성 생명 주기에 대해 신경 필요가 없게 되며 이는 객체의 결합도를 낮춥니다.
    2. UserService 클래스가 UserRepository 인터페이스를 사용해 DB에서 사용자를 조회하는 상황을 생각해봅시다. UserService 클래스에서 UserRepository 구현체를 직접 생성하게 되면, UserService UserRepository 특정 구현체에 의존하게 됩니다. 그러나 DI 사용하면, UserService UserRepository 인터페이스에만 의존하게 되고, 실제 사용할 구현체는 프레임워크가 런타임에 주입해줍니다. 이런 식으로 DI 통해 클래스 간의 의존성을 낮출 있고, 코드의 유연성과 재사용성을 높일 있습니다.
    3. 이와 같은 방식은 특히나 유닛 테스트에서 유용하게 사용됩니다. 의존성 주입을 통해 테스트 환경에서는 실제 객체 대신 모의 객체(mock object) 쉽게 주입할 있기 때문입니다.
    4. Java Spring 프레임워크와 같은 모던 프레임워크들은 이런 IoC DI 원칙을 광범위하게 적용하고 있습니다. 이를 통해 개발자는 더욱 모듈화된 코드를 작성하고, 쉽게 유닛 테스트를 실행할 있습니다.

IoC는 의존할 메소드나 객체의 호출을 개발자가 결정하는 것이 아닌 프레임워크에서 결정하는 것으로 “제어의 역전”라는 의미를 갖습니다. 기존에는 클래스 내부에서 의존할 객체를 직접 생성하고 객체 메소드를 호출 했다면 Spring 같은 프레임워크에서는 제어권을 프레임워크에 위임하여 프레임워크가 만들어 놓은 객체를 주입합니다. Spring 같은 경우 IoC 컨테이너에서 Bean으로 등록된 객체를 관리합니다.

DI는 IoC(제어의 역전)의 개념을 구현하기 위한 디자인 패턴이자 객체 의존관계를 외부에서 주입 시키는 방법입니다. 의존성을 주입하는 방법에는 생성자 주입, Setter 주입, 필드 주입 등이 있는데 Spring에서는 생성자 주입을 권장하고 있습니다. 생성자 주입은 생성자의 호출 시점에 1회 호출 되는 것이 보장되기 때문에 주입받은 객체가 변하지 않거나, 반드시 객체의 주입이 필요한 경우 강제로 주입하기 위해 사용할 수 있다는 장점이 있습니다.

 

DI(Dependency Injection)란 스프링이 다른 프레임워크와 차별화되어 제공하는 의존 관계 주입 기능으로, 객체를 직접 생성하는 게 아니라 외부에서 생성한 후 주입 시켜주는 방식이다. DI(의존성 주입)를 통해서 모듈 간의 결합도가 낮아지고 유연성이 높아진다. IoC(Inversion of Control)란 "제어의 역전" 이라는 의미로, 말 그대로 메소드나 객체의 호출작업을 개발자가 결정하는 것이 아니라, 외부에서 결정되는 것을 의미한다. IoC는 제어의 역전이라고 말하며, 간단히 말해 "제어의 흐름을 바꾼다"라고 한다. 객체의 의존성을 역전시켜 객체 간의 결합도를 줄이고 유연한 코드를 작성할 수 있게 하여 가독성 및 코드 중복, 유지 보수를 편하게 할 수 있게 한다.

 

 

제어의 역전 (IoC: Inversion of Control)은 프로그램의 제어 흐름 구조가 뒤바뀌는 것을 의미합니다.

  • 일반적으로 개발자가 필요한 객체를 생성하여 사용하는 것이 일반적이지만, IoC에서는 이를 뒤집어, 용도에 맞는 객체를 시스템이 제공하고 개발자는 그냥 사용합니다. 이를 "의존성 주입(Dependency Injection, DI)"이라고 부릅니다.
  • 실생활 예제로, 가위를 사용하는 경우를 들 수 있습니다. 가위를 직접 만드는 것이 아니라, 가위를 사용하는 용도에 따라 다양한 가위를 사용하는 것과 같습니다.

스프링 프레임워크와 IoC/DI:

  • 스프링 프레임워크는 DI를 지원하여, 개발자가 직접 객체를 생성하지 않고 필요한 객체를 스프링 프레임워크가 제공합니다.
  • 스프링에서 관리하는 이러한 객체를 '빈(Bean)'이라고 부르며, '빈'들은 스프링 IoC 컨테이너에 의해 관리됩니다.

스프링 '빈' 등록과 사용

  • '빈'의 등록은 @Component 어노테이션을 클래스 위에 붙여서 수행하며, 이 클래스는 스프링 서버가 시작될 때 스프링 IoC 컨테이너에 저장됩니다.
  • '빈'의 이름은 클래스의 이름을 사용하되, 첫 글자를 소문자로 변경합니다.
  • '빈'을 사용하기 위해선, @Autowired 어노테이션을 사용하여 스프링 IoC 컨테이너에 의존성 주입을 요청합니다.
  • @Autowired는 멤버변수 선언 위에 또는 '빈'을 사용할 함수 위에 붙일 수 있습니다.
  • 이 어노테이션은 스프링 IoC 컨테이너에 의해 관리되는 클래스에서만 사용 가능합니다.
  • 특정 조건에서는 @Autowired 어노테이션을 생략할 수 있습니다.

추가적인 주요 개념:

  • @ComponentScan: 스프링이 자동으로 빈을 등록할 클래스를 찾을 위치를 지정합니다.
  • @Bean: 직접 객체를 생성하여 빈으로 등록하는데 사용됩니다.
  • ApplicationContext: 스프링 IoC 컨테이너의 인터페이스로, 스프링 컨테이너의 기본적인 기능을 제공합니다. 빈을 찾거나 빈에 접근하는 등의 역할을 합니다.
  • @RequiredArgsConstructor: Lombok 라이브러리에서 제공하는 어노테이션으로, final로 선언된 모든 필드에 대한 생성자를 자동으로 생성합니다. 이는 DI를 위해 사용될 수 있습니다.

따라서 스프링에서는 IoC와 DI를 통해 개발자가 직접 객체를 생성하고 관리하는 부담을 덜고, 개발자는 비즈니스 로직에 집중할 수 있게 합니다. 이를 통해 코드의 가독성과 유지보수성이 향상됩니다.

 

2023.05.23 - [Mockterview] - DI와 IoC pt.2