Study/Spring Framework

[Spring] IoC와 IoC 컨테이너 개념

jonghne 2023. 10. 4. 23:48

IoC (Inversion of Control) 란 ?

IoC(Inversion of Control) 란 제어의 역전이라는 뜻으로 프로그램의 제어 흐름 (객체 생성 , 객체 간 의존관계 주입, 실행 등)외부에서 관리하는 기술을 말한다.

 

IoC를 사용하지 않는 경우에는 프로그래머가 클라이언트에 객체의 생성 부터 객체 간의 연결 및 실행하는 코드를 직접 작성해서 프로그램의 제어 흐름을 조종했다.

 

이러한 방식은 의존관계를 맺은 객체의 구현체가 변경 되면 클라이언트의 코드를 수정해야 하기 때문에

 

객체지향의 단일책임 원칙(SRP), 개방-폐쇄 원칙(OCP), 의존관계 역전(DIP)을 위반할 수 있다.

 

그래서 클라이언트 내부가 아닌, 외부에서 객체의 생성과 객체 간의 연결, 실행을 제어를 하도록 하는 IoC를 사용하는 것이 좋다.

 

이 IoC를 사용하면 객체 간의 결합도를 낮춰주고, 유연하고 변경에 용이한 애플리케이션을 애플리케이션을 만들 수 있다.

 

IoC 예시

아래의 코드는 IoC를 사용하지 않고 클라이언트가 의존관계를 맺을 객체를 직접 생성해서 사용하고 있다.

 

만약 의존하고있는 userRepository 객체가 MemoryUserRepository 클래스가 아닌 JpaUserRepository 클래스로 변경해야 하는 경우, 클라이언트 소스를 수정해야 한다는 문제가 있다.

 

📌 IoC 미사용

// 의존성이 있는 클래스
public class UserService {
    private UserRepository userRepository;

    // 의존성을 직접 생성
    public UserService() {
        this.userRepository = new MemoryUserRepository();
    }

    public void getUserList() {
        List<User> userList = userRepository.getAllUsers();
        // userList를 처리하는 로직
    }
}

// 의존성을 직접 생성
public class App {
    public static void main(String[] args) {
        // UserService 객체 생성
        UserService userService = new UserService();

        // UserService 메서드 호출
        userService.getUserList();
    }
}

 

아래의 코드에서는 직접 클라이언트 내부에서 의존성을 주입하는 것이 아닌, 객체 생성 및 연결을 관리하는 용도의 AppConfig라는 클래스를 만들어서 의존관계를 외부에서 설정하고 있다.

 

이렇게 하면 클라이언트 내부에서 의존하고 있는 대상이 변경 되어도, AppConfig 설정 파일만 수정하면 되기 때문에 UserService는 수정하지 않아도 된다.

 

이렇게 제어의 흐름을 클라이언트 코드가 관리하지 않고, AppConfig와 같은 컨테이너 또는 프레임워크가 관리하는 기술을 제어의 역전 , IoC(Inversion of Control) 라고 한다.

 

📌 IoC 사용

// 의존성이 있는 클래스
public class UserService {
    private UserRepository userRepository;

    // 의존성 주입을 받는 생성자
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public void getUserList() {
        List<User> userList = userRepository.getAllUsers();
        // userList를 처리하는 로직
    }
}

// DI 컨테이너 (의존성을 주입해주는 컨테이너)
public class AppConfig {
    public UserService userService(){
        return new UserService(userRepository());
    }
    public UserRepository userRepository() {
			return new UserRepository();
		}
}

public class AppContainer {
    public static void main(String[] args) {
        // DI 컨테이너 객체 생성 
				AppConfig appConfig = new AppConfig();
        
				UserService userService = appConfig.userService();

        // UserService 메서드 호출
        userService.getUserList();
    }
}

위와 예시 외에도 우리가 평소 사용하는 프레임워크 또한 대표적으로 사용되는 IoC이다

(단, 라이브러리는 프로그래머가 직접 코드를 제어하기 때문에 IoC가 아니다.)

 

IoC 컨테이너, DI 컨테이너

IoC 컨테이너 또는 DI 컨테이너는 IoC(Inversion of Control)에서 제어하는 주체를 말한다.

 

초기에는 IoC 컨테이너라고 많이 불렸지만, 요즘엔 주로 DI 주입을 관리한다고 해서 DI 컨테이너라고 부른다.

 

Spring 에서도 IoC / DI 컨테이너 역할을 하는 ApplicationContext 라는 스프링 컨테이너가 존재한다.

 

스프링 컨테이너를 사용하지 않으면 개발자가 DI 컨테이너를 통해 직접 객체를 생성하고 DI 했지만

 

이 스프링 컨테이너를 사용하면 객체들을 스프링 빈으로 스프링 컨테이너에 등록한 뒤 찾아서 사용한다는 차이가 있다.