[내일배움캠프] TIL(5) Spring 입문주차🌱 - IoC, DI, Bean, IoC Container

Euiyeon Park·2026년 10월 7일

내일배움캠프

목록 보기
6/9
post-thumbnail

IoC와 DI

스프링 IoC와 DI의 핵심

객체를 내가 직접 생성하고 연결하지 않고,
스프링 컨테이너가 객체 생성과 의존관계를 관리하도록 맡기는 것

🍀 IoC(Inversion of Control, 제어의 역전)

예제 코드

public class OrderService {

    private PaymentService paymentService = new PaymentService();
}

OrderService가 직접:

  • PaymentService 객체 생성
  • 객체 관리
  • 어떤 구현체를 사용할지 결정
    즉, 제어권이 개발자 코드에 있다.

예제 코드

@Service
public class OrderService {

    private final PaymentService paymentService;

    public OrderService(PaymentService paymentService) {
        this.paymentService = paymentService;
    }
}
@Service
public class PaymentService {
}
  • PaymentService 객체를 누가 만드는가?(생성) → Spring Container
  • OrderService에 누가 넣어주는가?(주입) → Spring Container

IoC 정리

객체에 대한 제어권이 개발자 → 프레임워크로 넘어갔기 때문에 IoC(제어의 역전)라고 한다.

🍀 DI(Dependency Injection, 의존성 주입)

DI는 IoC를 구현하는 대표적인 방법으로,
필요한 객체를 내부에서 직접 생성하지 않고 외부에서 전달받는 것이다.

예제코드

public class OrderService {

    private PaymentService paymentService;
}
  • OrderService가 정상적으로 동작하려면 PaymentService가 필요하다.
  • OrderService → (의존관계) → PaymentService

예제코드

public OrderService(PaymentService paymentService) {
    this.paymentService = paymentService;
}
  • PaymentService를 외부에서 넣어준다.

🍀 IoC와 DI의 관계

개념의미
IoC객체 생성/관리의 제어권을 Spring에 넘기는 설계원칙
DI필요한 객체를 외부에서 주입하는 방식
Spring ContainerIoC와 DI를 실제로 수행하는 주체

Bean과 IoC Container

🍀 Bean

  • 스프링(Spring IoC Container)이 생성하고 관리하는 객체
  • 관련 어노테이션:
    • @Service, @Component, @Repository, @Controller이 붙은 클래스는
      Component Scan의 대상이 되어 Spring Bean으로 등록된다.

🍀 IoC Container

  • 애플리케이션에서 사용할 Bean(객체)을 생성하고, 보관하고, 필요한 곳에 주입해주는
    스프링의 객체 관리 공간
  • 대표적으로 ApplicationContext가 IoC Container 역할을 한다.

✨ [참고] BeanFactory와 ApplicationContext

 IoC Container
   │
   ├── 📦 BeanFactory
   │      └─ Bean 관리의 가장 기본 기능
   │
   └── 📦 ApplicationContext
          └─ BeanFactory 기능 + 여러 부가 기능
  • BeanFactory : 기본적인 Bean 생성/조회/관리 기능을 제공
  • ApplicationContext : BeanFactory를 확장하여 그 기능을 포함하면서 여러 부가 기능 제공

Spring Bean 등록

간만에 스프링 공부하니까 개ㅐ어지럽다.
다양한 어노테이션과 개념들이 연달아 쏟아지듯 등장하면서 뇌가 지끈거렸는데
각각 따로 보면 헷갈리지만
결국 모두 스프링이 객체를 생성하고 관리하면서 필요한 객체끼리 연결하는 과정과 관련된 개념이다.

📍 전체 흐름 먼저 보기

@SpringBootApplication
        ↓
@ComponentScan
        ↓
@Component 계열 클래스 탐색
        ↓
Bean 생성 및 등록
        ↓
ApplicationContext가 Bean 관리
        ↓
생성자 주입 / @Autowired
        ↓
필요한 Bean을 다른 Bean에 주입

🍀 @Component

  • 해당 클래스를 스프링이 관리하는 객체인 Bean으로 등록할 대상임을 표시
  • 스프링은 @Component가 붙은 클래스를 발견하면 객체를 생성하고 스프링 Container에 Bean으로 등록한다.

🍀 @Component 계열 어노테이션

@Controller

@Service

@Repository

🍀 @ComponentScan

  • 특정 패키지를 탐색하면서 @Component 계열 어노테이션이 붙은 클래스를 찾아 Bean으로 등록한다.
  • 스프링 부트에서 ComponentScan을 직접 작성하지 않는다.
  • 왜? @SpringBootApplication안에 @ComponentScan이 포함되어 있으니까 !!!
    • @SpringBootApplication이 있는 현재 패키지+하위 패키지 스캔
  • 그래서 메인 클래스를 최상위 패키지에서 두는 것이 일반적

✨ [참고] @SpringBootApplication의 핵심 어노테이션

@SpringBootApplication
   ├─ @SpringBootConfiguration
   │      └─ @Configuration
   ├─ @EnableAutoConfiguration
   └─ @ComponentScan
  • @SpringBootConfiguration : 클래스가 Spring Boot의 설정 클래스임을 나타냄
    • @Configuration : 클래스가 Spring의 설정 클래스임을 나타냄
  • @EnableAutoConfiguration : 클래스 패스와 설정값을 바탕으로 필요한 설정을 자동 구성
  • @ComponentScan : 현재 패키지와 하위 패키지의 @Component 계열 클래스를 탐색해 Bean 등록

✨ [참고] @Component vs @Configuration + @Bean

둘 다 Bean을 등록하는 방법이지만 누가 Bean 등록을 결정하느냐가 다름

구분@Component@Configuration + @Bean
붙이는 위치클래스설정 클래스 + 메서드
등록 대상해당 클래스 자체@Bean 메서드가 반환한 객체
등록 방식Component Scan으로 자동 발견개발자가 직접 Bean 생성 방법 정의
주로 사용내가 만든 클래스외부 라이브러리 객체, 생성 과정 커스터마이징 필요 시

@Component

@Component
public class PaymentService {
}

@Cofiguration + @Bean

@Configuration
public class AppConfig {

    @Bean
    public PaymentService paymentService() {
        return new PaymentService();
    }
}

의존성 주입(DI)

여러 어노테이션을 통해 Bean을 등록한 다음에는
Bean끼리 연결을 해야하는데 이때 사용하는 것이 의존성 주입(DI)이다.

🍀 @Autowired

  • @Autowired는 스프링 컨테이너에서 필요한 타입의 Bean을 찾아 자동으로 주입하도록 요청한다.
  • 객체를 새로 생성하는 것이 아니라 컨테이너가 관리하는 빈을 찾아 연결해준다.

예제 코드

@RestController
public class UserController {

    @Autowired
    private UserService userService;
}
  • 스프링은 UserService타입의 Bean을 찾아 userService 필드에 넣어준다.

의존성 주입 방법

  1. 생성자 주입
  2. 필드 주입
  3. Setter 주입

🍀 생성자 주입

  • 생성자를 통해 필요한 Bean을 전달받는 방식
  • 객체 생성 시 필요한 의존성을 주입
  • final 필드 사용 가능
  • 필수 의존성이 누락되면 객체를 정상적으로 생성 불가
  • 생성자가 하나라면 @Autowired 생략 가능
@RestController
public class MemoController {

    private final MemoService memoService;

	@Autowired
    public MemoController(MemoService memoService) {
        this.memoService = memoService;
    }
}
  • Controller 객체가 생성되는 시점에 Serivce를 전달받는다.

✨ [참고] @RequiredArgsConstructor

  • @RequiredArgsConstructor는 final 필드와 @NonNull 필드를 매개변수로 가지는 생성자를 자동으로 만들어준다.
@RestController
@RequiredArgsConstrucutor
public class MemoController {

    private final MemoService memoService;
    
}

🍀 필드 주입

  • 필드에 직접 @Autowired를 붙여 주입받는 방식
  • final 필드에 일반적인 필드 주입을 적용 불가
    • 필드 의존성 주입은 객체 생성 후 수행(리플렉션)되므로
      객체가 생성될 때 초기화되어야 하는 final과 충돌
  • 의존성이 클래스 내부에 숨겨져 있음 - 생성자에 노출X, 필드 내부에 선언
  • 따라서 스프링 없이 객체를 직접 생성하면 해당 필드가 null
@RestController
public class MemoController {

    @Autowired
    private MemoService memoService;
}
MemoController controller = new MemoController(); // 이때 memoService는 null

🍀 Setter 주입

  • Setter 메서드를 통해 의존성을 전달받는 방식
  • 스프링이 객체를 생성한 후 set..()를 호출해서 주입
  • 객체 생성 이후 의존성 주입되므로 의존성을 나중에 변경할 수 있음
  • final 사용 불가
  • 필수가 아닌 선택적 의존성을 설정할 때 활용할 수 있음
@RestController
public class MemoController {

    private MemoService memoService;

    @Autowired
    public void setMemoService(MemoService memoService) {
        this.memoService = memoService;
    }
}

생성자 주입이 권장되는 이유

1. 필수 의존성 보장

필수 의존성 없이 객체를 생성하는 것을 방지할 수 있음

2. final 사용 가능

final 필드는 한 번 초기화되면 다른 객체로 재할당이 불가해,
의존성이 실수로 변경되는 것을 방지 할 수 있음 - Setter 주입은 변경이 가능

3. 기타

  • 테스트 편리
  • 순환 참조 조기 발견

📍 전체 흐름 다시 정리

@SpringBootApplication
        │
        └── @ComponentScan
                  │
                  ↓
        Component Scan 수행
                  │
                  ↓
@Component / @Controller / @RestController
@Service / @Repository 클래스 발견
                  │
                  ↓
              Bean 생성
                  │
                  ↓
          ApplicationContext
          ┌─────────────────┐
          │ Bean 관리       │
          │ Bean 조회       │
          │ 의존성 관리     │
          └─────────────────┘
                  │
                  ↓
       생성자 주입 / @Autowired
                  │
                  ↓
           Bean 간 의존성 연결
profile
"개발자는 해결사이자 발견자이다✨" - Michael C. Feathers

0개의 댓글