F-LAB JAVA · 5주차 · Phase 7 · 제어의 역전 (IoC)
🏆 Phase 7 완주 — IoC 의 실체, 컨테이너
이 Unit을 끝내면 다음을 답할 수 있어야 한다.
IoC 컨테이너는 객체의 생성·연결·생명주기를 대신 관리하는 외부 주체로, Spring 의 ApplicationContext 가 그 실체이며, 컨테이너 없이도 main 이나 팩토리에서 IoC 를 구현할 수 있지만 컨테이너는 이를 체계적·자동으로 처리한다.
IoC 컨테이너는 IoC 의 "외부 주체" 를 실체화한 것으로, 객체 (빈) 를 생성 하고, 의존성을 연결 (주입) 하며, 생성부터 소멸까지 생명주기 를 관리한다.
Spring 에서는 ApplicationContext (또는 BeanFactory) 가 IoC 컨테이너 역할을 하며, 설정 (@Configuration/@Bean, 컴포넌트 스캔) 을 읽어 빈을 만들고 조립한다.
IoC 컨테이너 없이도 IoC 는 가능하다 — main 이나 DaoFactory 가 직접new로 객체를 만들고 주입하면 그것도 IoC 다.
다만 객체가 많아지면 수동 조립이 복잡해지므로, 컨테이너가 설정 기반으로 체계적이고 자동으로 생성·주입·관리하며, Spring 외에도 Google Guice, Java EE CDI 등이 IoC 컨테이너 역할을 한다.
IoC 컨테이너 = 오케스트라 지휘자:
컨테이너 없이 (자율 연주):
- 각 연주자가 알아서 시작
- main 이 일일이 "너 시작, 너 다음"
- 곡 커지면 혼란
컨테이너 (지휘자):
- 지휘자(컨테이너)가 전체 조율
- 연주자(빈) 생성·배치
- 누가 누구와 협연(의존성)
- 시작·종료(생명주기)
컨테이너가 하는 일:
1. 연주자 모으기 (빈 생성)
2. 자리 배치 (의존성 연결)
3. 지휘 (생명주기)
ApplicationContext = 지휘자
빈 = 연주자
지휘자 없이도 (main):
- 가능하지만
- 소규모만
- 커지면 지휘자 필요
→ IoC 컨테이너 = 객체 생성·연결·생명주기 관리 (지휘자), Spring ApplicationContext.
1. IoC 컨테이너란
2. 객체 생성·연결·생명주기
3. ApplicationContext
4. 컨테이너 없이 IoC
5. 컨테이너가 하는 일
6. Spring 외 IoC 컨테이너
7. 컨테이너의 이점
8. Phase 7 완주 정리
9. 면접 + 자기 점검
IoC 컨테이너:
객체의 생성·연결·생명주기를
대신 관리하는 외부 주체.
- IoC 의 "외부" 실체화
- 객체 관리 전담
IoC 의 주체:
Unit 7.1:
- 제어가 외부로
외부 = ?
- 다른 객체
- 프레임워크
- IoC 컨테이너 ← 체계적
→ 컨테이너가 그 외부
컨테이너 역할:
1. 생성: 객체 만듦
2. 연결: 의존성 주입
3. 관리: 생명주기
→ 객체 관리 전담
// IoC 컨테이너 (Spring)
@Configuration
public class AppConfig {
@Bean
public ConnectionMaker connectionMaker() {
return new CustomerAConnectionMaker();
}
@Bean
public ShipmentDao shipmentDao() {
return new ShipmentDao(connectionMaker()); // 연결
}
}
// 컨테이너가 생성·연결·관리
// ApplicationContext ctx =
// new AnnotationConfigApplicationContext(AppConfig.class);
// ShipmentDao dao = ctx.getBean(ShipmentDao.class);
class ShipmentDao {
ShipmentDao(ConnectionMaker cm) { }
}
interface ConnectionMaker { Connection makeConnection(); }
class CustomerAConnectionMaker implements ConnectionMaker {
public Connection makeConnection() { return null; }
}
IoC 컨테이너란 무엇인가?
답:
1. 컨테이너:
IoC 주체:
역할:
전담:
세 가지 관리:
1. 생성 (Creation)
- 객체 인스턴스화
2. 연결 (Wiring)
- 의존성 주입
3. 생명주기 (Lifecycle)
- 생성 ~ 소멸
생성:
컨테이너가:
- 빈 정의 읽음
- 객체 생성
- new 대신
→ 객체 생성 권한
연결 (의존성 주입):
컨테이너가:
- 의존성 파악
- 자동 주입
- 조립
→ A 가 B 필요하면 B 주입
생명주기:
컨테이너가:
- 생성 (@PostConstruct)
- 사용
- 소멸 (@PreDestroy)
→ 전 과정 관리
@Component
public class ShipmentService {
private final ShipmentDao shipmentDao;
// 1. 생성 + 2. 연결 (컨테이너가)
public ShipmentService(ShipmentDao shipmentDao) {
this.shipmentDao = shipmentDao; // 컨테이너가 주입
}
// 3. 생명주기 (컨테이너가 호출)
@PostConstruct
public void init() {
log.info("서비스 초기화"); // 생성 후
}
@PreDestroy
public void cleanup() {
log.info("서비스 종료"); // 소멸 전
}
}
// 컨테이너가 생성·연결·생명주기 모두
객체 생성·연결·생명주기 관리의 의미는?
답:
1. 생성:
연결:
생명주기:
컨테이너:
ApplicationContext:
Spring 의 IoC 컨테이너:
- 빈 생성·관리
- DI
- 부가 기능 (i18n, 이벤트)
BeanFactory vs ApplicationContext:
BeanFactory:
- 기본 컨테이너
- 빈 생성·DI
ApplicationContext:
- BeanFactory 확장
- + 부가 기능
- 실무 표준
// ApplicationContext 생성
ApplicationContext ctx =
new AnnotationConfigApplicationContext(AppConfig.class);
// 빈 가져오기
ShipmentDao dao = ctx.getBean(ShipmentDao.class);
// 컨테이너가 생성·연결한 빈
설정 방식:
- @Configuration + @Bean (자바 설정)
- @ComponentScan (자동 스캔)
- XML (전통)
→ 컨테이너가 설정 읽고 조립
// ApplicationContext (ILIC)
@Configuration
@ComponentScan("com.ilic")
public class IlicConfig {
@Bean
public ConnectionMaker connectionMaker() {
return new CustomerAConnectionMaker();
}
}
// 컨테이너 생성
public class IlicApp {
public static void main(String[] args) {
ApplicationContext ctx =
new AnnotationConfigApplicationContext(IlicConfig.class);
// 컨테이너가 조립한 빈 사용
ShipmentDao dao = ctx.getBean(ShipmentDao.class);
// dao 는 ConnectionMaker 주입된 상태
}
}
// Spring Boot 는 자동 (SpringApplication.run)
@Component
class ShipmentDao {
ShipmentDao(ConnectionMaker cm) { }
}
interface ConnectionMaker { Connection makeConnection(); }
class CustomerAConnectionMaker implements ConnectionMaker {
public Connection makeConnection() { return null; }
}
IoC 컨테이너 = Spring ApplicationContext인가?
답:
1. ApplicationContext:
BeanFactory:
생성:
설정:
컨테이너 없이 IoC:
main 이나 팩토리:
- 직접 new
- 직접 주입
- 그것도 IoC
→ 컨테이너는 필수 X
// main 에서 IoC (컨테이너 없이)
public class App {
public static void main(String[] args) {
// 직접 생성·주입 (IoC)
ConnectionMaker cm = new NConnectionMaker();
ShipmentDao dao = new ShipmentDao(cm); // 주입
// main 이 외부 주체 (IoC)
}
}
// 컨테이너 없어도 제어 역전
// 팩토리에서 IoC
public class DaoFactory {
public ShipmentDao shipmentDao() {
return new ShipmentDao(connectionMaker()); // 조립
}
public ConnectionMaker connectionMaker() {
return new NConnectionMaker();
}
}
// DaoFactory 가 외부 주체 (IoC)
컨테이너 필요성:
객체 적을 때:
- 수동 조립 OK
객체 많을 때 (102 테이블, 431 API):
- 수동 복잡
- 컨테이너 자동화 필요
// 컨테이너 없이 IoC (수동)
public class ManualIoC {
public static void main(String[] args) {
// 수동 조립 (IoC, 컨테이너 X)
ConnectionMaker cm = new CustomerAConnectionMaker();
ShipmentDao shipmentDao = new ShipmentDao(cm);
BookingDao bookingDao = new BookingDao(cm);
InvoiceDao invoiceDao = new InvoiceDao(cm);
ShipmentService service =
new ShipmentService(shipmentDao, bookingDao, invoiceDao);
// ... 수십 개 객체 수동 조립
// → 복잡 (컨테이너 필요)
}
}
// 컨테이너 (자동)
@Configuration
@ComponentScan("com.ilic")
class IlicConfig { }
// 컨테이너가 자동 조립 (수동 X)
class ShipmentDao { ShipmentDao(ConnectionMaker cm) {} }
class BookingDao { BookingDao(ConnectionMaker cm) {} }
class InvoiceDao { InvoiceDao(ConnectionMaker cm) {} }
class ShipmentService { ShipmentService(ShipmentDao a, BookingDao b, InvoiceDao c) {} }
interface ConnectionMaker { Connection makeConnection(); }
class CustomerAConnectionMaker implements ConnectionMaker {
public Connection makeConnection() { return null; }
}
IoC 컨테이너 없이 IoC를 구현하면?
답:
1. 컨테이너 없이도:
main:
팩토리:
필요성:
컨테이너가 하는 일:
1. 빈 정의 읽기 (설정)
2. 빈 생성
3. 의존성 주입
4. 생명주기 관리
5. 빈 제공 (getBean)
빈 정의 읽기:
설정 소스:
- @Configuration/@Bean
- @Component 스캔
- XML
→ 어떤 빈, 어떻게 조립
의존성 해결:
컨테이너가:
- 의존성 파악 (생성자/필드)
- 필요한 빈 찾음
- 자동 주입
→ 조립 자동
// 빈 등록·제공
ApplicationContext ctx = ...;
// 타입으로
ShipmentDao dao = ctx.getBean(ShipmentDao.class);
// 이름으로
Object bean = ctx.getBean("shipmentDao");
// 컨테이너가 관리하는 빈 제공
// 컨테이너가 하는 일 (ILIC)
@Configuration
@ComponentScan("com.ilic")
public class IlicConfig {
// 1. 빈 정의
@Bean
public ConnectionMaker connectionMaker() {
return new CustomerAConnectionMaker();
}
}
@Service
public class ShipmentService {
// 2. 의존성 (컨테이너가 해결)
private final ShipmentDao shipmentDao;
public ShipmentService(ShipmentDao shipmentDao) {
this.shipmentDao = shipmentDao; // 컨테이너 주입
}
}
// 컨테이너 작업:
// 1. IlicConfig + 컴포넌트 스캔 읽기
// 2. ConnectionMaker, ShipmentDao, ShipmentService 빈 생성
// 3. 의존성 자동 주입 (ShipmentDao → Service)
// 4. 생명주기 관리
// 5. getBean 으로 제공
@Component
class ShipmentDao { ShipmentDao(ConnectionMaker cm) {} }
interface ConnectionMaker { Connection makeConnection(); }
class CustomerAConnectionMaker implements ConnectionMaker {
public Connection makeConnection() { return null; }
}
컨테이너가 하는 일은?
답:
1. 하는 일:
빈 정의:
의존성 해결:
제공:
Spring 외 IoC 컨테이너:
- Google Guice
- Java EE CDI (Weld)
- Dagger (안드로이드)
- PicoContainer
Google Guice:
- 경량 DI 프레임워크
- 애노테이션 기반
- @Inject
→ IoC 컨테이너
Java EE CDI:
Contexts and Dependency Injection:
- 표준 (JSR)
- @Inject, @Named
- Java EE/Jakarta EE
→ 표준 IoC 컨테이너
Dagger:
- 컴파일 타임 DI
- 안드로이드 주로
- 런타임 오버헤드 ↓
- 코드 생성
공통점:
모두 IoC 컨테이너:
- 객체 생성·주입
- 의존성 관리
- 제어 역전
→ 같은 정신
// Spring DI (ILIC 사용)
@Service
public class ShipmentService {
private final ShipmentDao dao;
public ShipmentService(ShipmentDao dao) { // Spring DI
this.dao = dao;
}
}
// 비교: Guice 스타일 (개념)
// public class ShipmentService {
// @Inject
// public ShipmentService(ShipmentDao dao) { } // Guice
// }
// 비교: CDI 스타일 (개념)
// @ApplicationScoped
// public class ShipmentService {
// @Inject ShipmentDao dao; // CDI
// }
// 모두 IoC 컨테이너 (제어 역전, 자동 주입)
// ILIC 는 Spring 사용
class ShipmentDao { }
Spring 외 IoC 컨테이너 예는?
답:
1. 다른 컨테이너:
Guice:
CDI:
공통:
컨테이너 이점:
1. 자동 조립
2. 설정 중앙화
3. 생명주기 관리
4. 부가 기능
5. 테스트
자동 조립:
수동 (main):
- 수십 개 new
- 순서 관리
컨테이너:
- 자동 생성·주입
- 의존성 해결
설정 중앙화:
빈 설정:
- @Configuration 한 곳
- 또는 스캔
→ 조립 한눈에
부가 기능 (ApplicationContext):
- AOP
- 트랜잭션
- 이벤트
- i18n
- 프로파일
→ 컨테이너가 제공
// 컨테이너 이점 (ILIC)
@SpringBootApplication
public class IlicApplication {
public static void main(String[] args) {
SpringApplication.run(IlicApplication.class, args);
// 컨테이너가:
// 1. 102 테이블 관련 DAO 자동 조립
// 2. 431 API 컨트롤러 자동 등록
// 3. 서비스 의존성 자동 주입
// 4. 트랜잭션, AOP 적용
// 5. 생명주기 관리
// → 수동이면 불가능한 규모
}
}
// 빈은 그냥 선언만
@Service
class ShipmentService {
private final ShipmentDao dao;
ShipmentService(ShipmentDao dao) { this.dao = dao; }
// 컨테이너가 알아서 조립
}
@Repository
class ShipmentDao { }
컨테이너의 이점은?
답:
1. 이점:
자동 조립:
부가 기능:
규모:
Phase 7 — 제어의 역전 (IoC)
Unit 7.1 — IoC 개념 ★깊이
- 제어 역전 (외부가 결정)
- 전통 vs IoC
Unit 7.2 — 프레임워크 vs 라이브러리
- 제어권 (IoC 여부)
- Hollywood Principle
Unit 7.3 — IoC 컨테이너
- 생성·연결·생명주기
- ApplicationContext
Phase 7 핵심 메시지:
"IoC 는 객체 제어를 외부로 넘기는 것.
프레임워크가 그 외부 (호출당함).
IoC 컨테이너 (ApplicationContext) 가
객체 생성·연결·생명주기를 관리한다."
진화 과정:
전략 주입 (Phase 6)
↓ 개념화
IoC (Phase 7.1)
↓ 프레임워크
프레임워크 IoC (Phase 7.2)
↓ 실체
IoC 컨테이너 (Phase 7.3)
↓ (다음)
Spring 컨테이너 상세 (Phase 8)
Phase 7 → Phase 8:
- IoC 컨테이너 → Spring 상세
Phase 8 — Spring 컨테이너 (8.3·8.4 ★깊이):
- 빈팩토리/ApplicationContext
- getBean
- 싱글톤 레지스트리 ★깊이
- DI ★깊이
Phase 7의 종합은?
답:
1. 3 Unit:
메시지:
진화:
다음:
| Q | 핵심 답변 |
|---|---|
| IoC 컨테이너? | 객체 생성·연결·생명주기 관리 |
| 생성·연결·생명주기? | 3가지 관리 |
| ApplicationContext? | Spring IoC 컨테이너 |
| 컨테이너 없이 IoC? | main/팩토리도 IoC |
| 컨테이너가 하는 일? | 빈 정의/생성/주입/관리 |
| Spring 외? | Guice, CDI, Dagger |
| 이점? | 자동 조립, 부가 기능 |
| 빈? | 컨테이너 관리 객체 |
| 다음? | Spring 컨테이너 상세 |
| IoC 정신? | 제어 외부로 |
답:
답:
답:
답:
답:
1. IoC 컨테이너
2. 컨테이너 없이도 IoC
3. 컨테이너의 일과 종류
🌱 Phase 7 — 제어의 역전 (IoC)
✅ Unit 7.1 IoC 개념 ★깊이
✅ Unit 7.2 프레임워크 vs 라이브러리
✅ Unit 7.3 IoC 컨테이너의 역할 ← 여기, Phase 7 완주
→ IoC (제어 외부로)
→ 프레임워크 (호출당함)
→ IoC 컨테이너 (ApplicationContext)
Phase 8 — Spring 컨테이너 (8.3·8.4 ★깊이)
Unit 8.1 — 빈 팩토리와 ApplicationContext
Unit 8.2 — getBean()의 동작
Unit 8.3 — 싱글톤 레지스트리 ★깊이
Unit 8.4 — 의존관계 주입 DI ★깊이 (+ 종합 졸업 시험)
✅ Part A — 동시성 마무리 (7 Unit)
🌱 Part B — 토비의 스프링
✅ Phase 3~6 (12 Unit)
✅ Phase 7 — IoC (3 Unit) ← 완주
⏭ Phase 8 — Spring 컨테이너 (4 Unit, 마지막)
총: 22/26 Unit
🏆 Phase 7 완주 — IoC 의 실체, 컨테이너