Spring Boot/제어 역전(loC), 의존성 주입(DI)

[Spring boot] 제어 역전(loC)

재윤 2025. 7. 21. 15:49
반응형
  • 일반적인 자바 개발의 경우 객체를 사용하기 위해 다음과 같은 코드를 사용함
public interface MyService {
    public String getHello();
}
@Service
public class MyServiceImpl implements MyService {
    @Override
    public String getHello() {
        return "Hello";
    }
}
@RestController
public class NoDIController {

    private MyService service = new MyServiceImpl(); // 직접 생성

    @GetMapping("/no-di/hello")
    public String getHello() {
        return service.getHello();
    }

}

 

다이어그램을 그려보면

1. 일반적인 자바 개발 경우

  • MyServiceImpI을 직접 new로 생성해서 MyService에 저장
  • 개발자가 객체 생성 제어권을 갖고 있는 방식

또 다른 예제

Engine engine = new DieselEngine();  // 내가 직접 만든다
Car car = new Car(engine);
car.drive();
  • 자동차를 만들 때, 엔진을 내가 직접 선택해서 new로 만든다
  • 바꿀 일이 생기면 코드도 수정해야 한다

개발자가 모든 걸 직접 컨트롤

  • 어떤 객체 쓸지 (new로 생성)
  • 언제 만들지
  • 어떤 구현체를 쓸지 (직접 결정)

 

2. IoC 방식

  • 제어 역전의 특징을 가진 스프링은 기존 자바 개발 방식과 다르게 동작
  • 사용할 객체를 직접 생성하지 않고 객체의 생명 주기 관리를 외부에 위임
  • IoC == 객체의 생성과 생명 주기 제어를 개발자가 아니라 외부(컨테이너가) 담당

 

예제

  • Engine을 new 하지 않음!
  • Spring이 알아서 Engine을 만들어서 Car에 "끼워줌"
@RequiredArgsConstructor
public class Car {
    private final Engine engine;  // 주입받음 (DI)

    public void drive() {
        engine.run();
    }
}

 

Spring IoC 컨테이너란?

  • 객체를 대신 만들어주고, 필요한 곳에 주입해주고, 생명 주기를 관리해주는 핵심 컴포넌트
  • 대표적으로는 ApplicationContext

 

Spring IoC 컨테이너 동작 흐름

예제 코드

public interface MyService {
    String getHello();
}
@Service
public class MyServiceImpl implements MyService {
    public String getHello() {
        return "Hello from MyServiceImpl";
    }
}
@RestController
@RequiredArgsConstructor
public class MyController {

    private final MyService service;

    @GetMapping("/di/hello")
    public String getHello() {
        return service.getHello();
    }
}

 

 

1단계 : 설정 파일/어노테이션 읽기(클래스 스캔)

  • Spring이 시작될 때 @ComponentScan이 설정된 패키지를 스캔함
  • @Service가 붙은 MyServiceImpl 발견 → Bean으로 등록
  • @RestController가 붙은 MyController도 발견 → Bean으로 등록

2단계 : Bean 정의(설계도)를 만들어 저장

  • Spring은 어떤 클래스인지, 어떤 생성자를 써야 하는지, 어떤 의존성이 필요한지를 메모리에 미리 등록

Bean 이름 타입 생성 방법 의존성

myServiceImpl MyService new MyServiceImpl() 없음
myController MyController new MyController(myService) MyService 필요함

 

💡 Bean이란?
Spring IoC 컨테이너가 생성하고 관리하는 객체(인스턴스)

 

3단계. Bean 인스턴스를 생성

  • 필요한 Bean들을 실제로 생성(new) 하고
  • 생성 시점에 필요한 의존성(=다른 객체)도 같이 만들어서 주입
  • 먼저 MyServiceImpl을 생성하고, 그다음 MyController를 생성할 때 MyServiceImpl을 자동으로 넣어줌

이 부분 의존성 주입

MyService myService = new MyService(); // spring이 생성
MyController controller = new MyController(myService); // spring이 주입

 

4단계. 싱글톤으로 관리

  • 대부분의 Bean은 하나만 만들어서 공유(Singleton)
  • 필요할 때마다 계속 new 하는 게 아니라, 만들어둔 거에서 꺼내서 씀 → 메모리에 보관

5단계. 요청 처리 중 주입된 Bean 사용

  • 사용자가 /di/hello로 요청을 보냄
  • Spring MVC가 MyController.getHello() 메서드 실행
  • 내부에서 주입된 MyServiceImpl의 getHello() 호출됨
  • 응답으로 "Hello from MyServiceImpl" 반환
    @GetMapping("/di/hello")
    public String getHello() {
        return service.getHello();
    }

 

정리

 

반응형