F-LAB JAVA · 5주차 · Phase 8 · Spring 컨테이너
★ 깊이 파기 + 🏆 Phase 8 완주 + 🎓 5주차 전체 완주 + 종합 졸업 시험 20문항
이 Unit을 끝내면 다음을 답할 수 있어야 한다.
DI (Dependency Injection, 의존관계 주입) 는 객체가 의존하는 대상을 외부에서 런타임에 연결해 주입하는 것으로 IoC 의 구체적 구현 방법이며, 생성자 주입·Setter 주입·필드 주입 중 생성자 주입이 final·순환참조 감지·테스트 용이성 때문에 권장된다.
DI 는 A 가 B 를 사용할 때 A 가 B 를 직접 생성하지 않고, 외부 (컨테이너) 가 B 를 만들어 A 에 런타임에 연결 (주입) 하는 것이다.
핵심 3요소는 (1) A 가 B 에 의존 하고, (2) A 는 B 의 인터페이스에 의존 하며, (3) 구체 구현은 외부에서 주입 된다는 것이다.
주입 방식은 생성자 주입 (생성자로), Setter 주입 (setter 로), 필드 주입 (@Autowired 필드) 세 가지이며, DI 는 IoC 의 한 형태 (IoC ⊃ DI) 다.
생성자 주입이 권장 되는 이유는 (1)final로 불변 보장, (2) 순환참조를 컴파일/기동 시 감지, (3) 테스트 시 Mock 주입이 쉬워 테스트가 용이하기 때문이다.
DI = 부품 런타임 조립:
의존:
- 자동차(A)는 엔진(B) 필요
- A 가 B 에 의존
인터페이스 의존:
- "엔진" 규격에 의존
- 어떤 엔진이든 (가솔린/전기)
외부 주입:
- 공장(컨테이너)이 엔진 장착
- 자동차가 직접 만들지 X
3가지 주입:
생성자 (조립 시 장착):
- 출고 시 엔진 (final, 권장)
Setter (나중 장착):
- 교체 가능 (선택적)
필드 (직접 끼움):
- 간편하나 권장 X
생성자 권장:
- 출고 시 엔진 확정 (final)
- 엔진 없이 출고 X (순환참조 감지)
- 테스트용 엔진 쉽게 (Mock)
→ DI = 의존 대상을 외부에서 런타임 주입 (IoC 구현), 생성자 주입 권장.
1. DI의 정의
2. 런타임 의존관계 연결
3. 3요소 (의존/인터페이스/주입)
4. 3가지 주입 방식
5. DI와 IoC 관계
6. 생성자 주입 권장 이유
7. Setter/필드 주입 문제
8. 5주차 완주 + 종합 졸업 시험
9. 면접 + 자기 점검
DI (Dependency Injection):
의존관계 주입.
객체가 의존하는 대상을
외부에서 연결·주입.
- 직접 생성 X
- 외부가 주입
의존관계:
A 가 B 사용:
- A 는 B 에 의존
- B 없으면 A 동작 X
ShipmentDao → ConnectionMaker
주입 (Injection):
외부가:
- 의존 객체 생성
- A 에 넣어줌
→ A 는 받아서 사용
// DI (ILIC)
@Service
public class ShipmentService {
private final ShipmentDao shipmentDao; // 의존
// 생성자 주입 (외부가 주입)
public ShipmentService(ShipmentDao shipmentDao) {
this.shipmentDao = shipmentDao; // 받음
}
// Spring 컨테이너가 ShipmentDao 주입
}
class ShipmentDao { }
DI (의존관계 주입) 의 정의는?
답:
1. DI:
의존관계:
주입:
결과:
런타임 의존관계 연결:
코드(컴파일)에는:
- 인터페이스 의존
런타임에:
- 구체 구현 연결
- 동적 결정
컴파일 vs 런타임:
컴파일 의존:
- ShipmentDao → ConnectionMaker (인터페이스)
런타임 의존:
- ShipmentDao → NConnectionMaker (구체)
- 주입으로 결정
동적 연결:
같은 코드:
- 런타임에 다른 구현
- 설정/환경 따라
→ 유연
// 런타임 의존관계 연결
public class ShipmentDao {
private final ConnectionMaker connectionMaker; // 컴파일: 인터페이스
public ShipmentDao(ConnectionMaker cm) {
this.connectionMaker = cm; // 런타임: 구체 주입
}
}
// 런타임에 구체 결정
@Configuration
public class Config {
@Bean
public ConnectionMaker connectionMaker() {
// 런타임에 어떤 구현?
return new CustomerAConnectionMaker(); // 여기서 결정
}
}
// 컴파일: ShipmentDao → ConnectionMaker (인터페이스)
// 런타임: ShipmentDao → CustomerAConnectionMaker (구체)
interface ConnectionMaker { Connection makeConnection(); }
class CustomerAConnectionMaker implements ConnectionMaker {
public Connection makeConnection() { return null; }
}
런타임 의존관계 연결의 의미는?
답:
1. 런타임 연결:
컴파일 vs 런타임:
동적:
유연:
DI 3요소:
1. 의존
- A 가 B 사용
2. 인터페이스 의존
- A 는 B 인터페이스에
3. 외부 주입
- 구체는 외부가
의존:
A 가 B 필요:
- B 없으면 A 동작 X
- 의존관계
인터페이스 의존:
A 는 B 의 인터페이스만:
- 구체 모름
- 추상에 의존 (DIP)
외부 주입:
구체 구현:
- 외부(컨테이너)가 생성
- A 에 주입
// DI 3요소
public class ShipmentService {
// 1. 의존 (ShipmentDao 필요)
// 2. 인터페이스 의존 (구체 모름)
private final ShipmentDao shipmentDao;
// 3. 외부 주입 (생성자)
public ShipmentService(ShipmentDao shipmentDao) {
this.shipmentDao = shipmentDao;
}
}
// 1. 의존: ShipmentService → ShipmentDao
// 2. 인터페이스: (ShipmentDao 가 인터페이스면) 추상 의존
// 3. 주입: Spring 이 구체 주입
class ShipmentDao { }
의존 / 인터페이스 의존 / 외부 주입의 3요소는?
답:
1. 의존:
인터페이스 의존:
외부 주입:
3요소:
3가지 주입:
1. 생성자 주입 ✅ (권장)
2. Setter 주입
3. 필드 주입
// 생성자 주입 (권장)
@Service
public class ShipmentService {
private final ShipmentDao shipmentDao; // final
public ShipmentService(ShipmentDao shipmentDao) {
this.shipmentDao = shipmentDao;
}
}
// 생성 시 주입, final, 불변
// Setter 주입
@Service
public class ShipmentService {
private ShipmentDao shipmentDao; // final X
@Autowired
public void setShipmentDao(ShipmentDao shipmentDao) {
this.shipmentDao = shipmentDao;
}
}
// setter 로 주입, 변경 가능
// 필드 주입 (권장 X)
@Service
public class ShipmentService {
@Autowired
private ShipmentDao shipmentDao; // 필드 직접
}
// 간편하나 단점 (테스트, final X)
| 방식 | final | 테스트 | 권장 |
|---|---|---|---|
| 생성자 | ✅ | 쉬움 | ✅ |
| Setter | ❌ | 보통 | △ |
| 필드 | ❌ | 어려움 | ❌ |
// 생성자 주입 (ILIC 권장)
@Service
public class ShipmentService {
private final ShipmentDao shipmentDao; // final
private final FreightCalculator calculator; // final
// 생성자 주입 (Spring 4.3+ @Autowired 생략 가능)
public ShipmentService(ShipmentDao shipmentDao,
FreightCalculator calculator) {
this.shipmentDao = shipmentDao;
this.calculator = calculator;
}
// 불변, 필수 의존성 명확, 테스트 쉬움
}
class ShipmentDao { }
class FreightCalculator { }
3가지 주입 방식은?
답:
1. 3방식:
생성자:
Setter:
필드:
DI와 IoC:
IoC (넓은 개념):
- 제어 역전
DI (구체 방법):
- 의존성 주입
- IoC 의 한 형태
IoC ⊃ DI
IoC 의 형태:
DI (의존성 주입):
- 외부가 주입 (수동)
DL (의존성 조회):
- 객체가 조회 (능동, getBean)
→ 둘 다 IoC
DI 가 깔끔:
DL (조회):
- 컨테이너 의존
- getBean 명시
DI (주입):
- 컨테이너 모름
- 자동 주입
→ DI 권장
// IoC ⊃ DI
// IoC (제어 역전) — 넓은 개념
// : 객체 생성·연결을 외부가
// DI (의존성 주입) — IoC 구현
@Service
public class ShipmentService {
private final ShipmentDao shipmentDao;
public ShipmentService(ShipmentDao dao) { // DI (주입)
this.shipmentDao = dao;
}
}
// DL (의존성 조회) — IoC 의 다른 형태 (지양)
// public class ShipmentServiceDL {
// private final ShipmentDao shipmentDao;
// public ShipmentServiceDL(ApplicationContext ctx) {
// this.shipmentDao = ctx.getBean(ShipmentDao.class); // 조회
// }
// }
// DI 가 깔끔 (컨테이너 의존 X)
class ShipmentDao { }
DI와 IoC의 관계는?
답:
1. 관계:
IoC 형태:
DI 권장:
DL:
생성자 주입 권장 3이유:
1. final (불변)
2. 순환참조 감지
3. 테스트 용이성
// 1. final 불변
@Service
public class ShipmentService {
private final ShipmentDao shipmentDao; // final 가능
public ShipmentService(ShipmentDao dao) {
this.shipmentDao = dao;
}
// 생성 후 변경 X (불변, 안전)
}
// Setter/필드는 final X
2. 순환참조 감지:
A → B, B → A:
- 생성자 주입: 기동 시 오류 (감지)
- Setter/필드: 런타임 늦게 발견
→ 생성자가 조기 감지
// 3. 테스트 용이성
@Test
void test() {
ShipmentDao mock = mock(ShipmentDao.class);
ShipmentService service = new ShipmentService(mock); // 생성자 주입
// Spring 없이 Mock 주입, 단위 테스트
}
// 필드 주입은 reflection 필요 (테스트 어려움)
필수 의존성 명확:
생성자 주입:
- 생성 시 필수
- 누락 시 컴파일/기동 오류
→ 의존성 명시적
// 생성자 주입 권장 (ILIC)
@Service
public class ShipmentService {
// 1. final (불변)
private final ShipmentDao shipmentDao;
private final FreightCalculator calculator;
private final ApplicationEventPublisher publisher;
// 생성자 주입 (3 이유 모두)
public ShipmentService(ShipmentDao shipmentDao,
FreightCalculator calculator,
ApplicationEventPublisher publisher) {
this.shipmentDao = shipmentDao;
this.calculator = calculator;
this.publisher = publisher;
}
// 1. final → 불변 (안전)
// 2. 순환참조 → 기동 시 감지
// 3. 테스트 → Mock 3개 주입 쉬움
// + 필수 의존성 3개 명확
}
class ShipmentDao { }
class FreightCalculator { }
interface ApplicationEventPublisher { void publishEvent(Object e); }
생성자 주입 권장 이유 3가지는?
답:
1. final:
순환참조 감지:
테스트 용이:
+ 필수 의존성:
Setter 주입 문제:
- final X (변경 가능)
- 필수 의존성 불명확
- 주입 누락 가능
- 불완전 객체 (NPE)
필드 주입 문제:
- final X
- 테스트 어려움 (reflection)
- 컨테이너 강결합
- 의존성 숨김
// 필드 주입 위험
@Service
public class ShipmentService {
@Autowired
private ShipmentDao shipmentDao; // 숨겨진 의존성
// 테스트 시:
// ShipmentService s = new ShipmentService();
// shipmentDao 주입 안 됨 → NPE
// reflection 으로 주입해야 (번거로움)
}
언제 Setter:
- 선택적 의존성
- 변경 가능 의존성
- (드묾)
→ 대부분 생성자
// 권장 (생성자)
@Service
public class GoodService {
private final ShipmentDao dao;
public GoodService(ShipmentDao dao) {
this.dao = dao; // 생성자 (권장)
}
}
// 지양 (필드)
@Service
public class BadService {
@Autowired
private ShipmentDao dao; // 필드 (지양)
// 테스트 어려움, final X, 의존성 숨김
}
// ILIC 는 생성자 주입 일관 사용
class ShipmentDao { }
Setter/필드 주입의 문제는?
답:
1. Setter:
필드:
필드 위험:
권장:
🏆 Phase 8 — Spring 컨테이너
✅ Unit 8.1 빈 팩토리와 ApplicationContext
✅ Unit 8.2 getBean()의 동작
✅ Unit 8.3 싱글톤 레지스트리 ★깊이
✅ Unit 8.4 의존관계 주입 DI ★깊이 ← 완주
🎓 5주차 전체 완주!
✅ Part A — 동시성 마무리 (7 Unit)
✅ Phase 1 — 스레드 풀 필요성 (3)
✅ Phase 2 — 동시성 안전 도구 (4, 2.4 ★마스터)
✅ Part B — 토비의 스프링 (19 Unit)
✅ Phase 3 — 전통 DAO (3)
✅ Phase 4 — 관심사 분리 (3, ★깊이)
✅ Phase 5 — 디자인 패턴 (3)
✅ Phase 6 — OCP & 전략 (3, 6.2 ★깊이)
✅ Phase 7 — IoC (3, 7.1 ★깊이)
✅ Phase 8 — Spring 컨테이너 (4, 8.3·8.4 ★깊이)
총: 26/26 Unit ✅
5주차 진화 여정:
Part A (동시성):
스레드 풀 → 가시성/원자성 → CAS
Part B (객체 설계):
전통 DAO (혼재)
→ 관심사 분리
→ 디자인 패턴 (템플릿/팩토리)
→ 인터페이스 + 합성 (전략)
→ OCP
→ IoC
→ DI (Spring)
→ "한 줄의 DAO 가 Spring 까지"
Q1. 스레드 많을수록 빠른가?
→ X (스위칭 비용, 적정 수)
Q2. 가시성 vs 원자성?
→ 가시성(안 보임)/원자성(덮어씀)
Q3. synchronized/volatile/Atomic 비교?
→ sync(둘다,락)/volatile(가시성)/Atomic(둘다,CAS)
Q4. CAS 4단계?
→ 읽기(A)/계산(B)/비교/교체or재시도
Q5. ABA 문제?
→ A→B→A 못 감지, AtomicStampedReference
Q6. DAO 가 분리하는 것?
→ 비즈니스 vs 데이터 접근
Q7. 관심사 분리?
→ 같은 것 모음, 다른 것 분리
Q8. 템플릿/팩토리 메소드?
→ 흐름(부모)+변동(자식), 객체 생성 위임
Q9. 상속의 한계?
→ 단일상속/컴파일타임/깨지기쉬움
Q10. 인터페이스로 OCP?
→ 새 구현(확장), 기존 불변(닫힘)
Q11. OCP?
→ 확장 열림, 변경 닫힘
Q12. 전략 패턴 3요소?
→ Context/Strategy/ConcreteStrategy
Q13. 전략 vs 템플릿 메소드?
→ 합성(주입) vs 상속(서브클래스)
Q14. IoC 무엇이 역전?
→ 제어 흐름 (객체→외부)
Q15. 프레임워크 vs 라이브러리?
→ 호출당함(IoC) vs 내가 호출
Q16. ApplicationContext 역할?
→ 빈 생성·연결·생명주기 (싱글톤 레지스트리)
Q17. getBean 100번 1객체?
→ 싱글톤 (1개 공유)
Q18. 싱글톤 stateless (4주차 연결)?
→ 멀티스레드 공유, 상태=동시성 문제
Q19. 생성자 주입 이유 3?
→ final/순환참조 감지/테스트 용이
Q20. DI-IoC 관계?
→ DI 는 IoC 의 한 형태 (IoC⊃DI)
20 / 20 → 5주차 마스터 (동시성 + Spring)
17-19 → 거의 마스터
14-16 → 복습 권장
< 14 → 5주차 재학습
5주차의 종합은?
답:
1. Part A:
Part B:
진화:
완주:
| Q | 핵심 답변 |
|---|---|
| DI? | 의존 대상 외부 주입 |
| 런타임 연결? | 구체 런타임 결정 |
| 3요소? | 의존/인터페이스/주입 |
| 3방식? | 생성자/Setter/필드 |
| DI-IoC? | DI 는 IoC 형태 |
| 생성자 권장? | final/순환참조/테스트 |
| Setter 문제? | final X, 누락 |
| 필드 문제? | 테스트 어려움 |
| 싱글톤 stateless? | 동시성 (4주차) |
| IoC 컨테이너? | ApplicationContext |
답:
답:
답:
답:
답:
1. DI
2. 3가지 주입과 권장
3. DI-IoC + 5주차 완주
🌱 Phase 8 — Spring 컨테이너
✅ Unit 8.1 빈 팩토리와 ApplicationContext
✅ Unit 8.2 getBean()의 동작
✅ Unit 8.3 싱글톤 레지스트리 ★깊이
✅ Unit 8.4 의존관계 주입 DI ★깊이 ← 여기, Phase 8 완주
🎓 F-LAB JAVA 5주차 완주!
✅ Part A — 동시성 마무리 (7 Unit)
✅ Phase 1 — 스레드 풀 필요성 (3)
✅ Phase 2 — 동시성 안전 도구 (4, 2.4 ★마스터)
✅ Part B — 토비의 스프링 (19 Unit)
✅ Phase 3 — 전통 DAO (3)
✅ Phase 4 — 관심사 분리 (3, ★깊이)
✅ Phase 5 — 디자인 패턴 (3)
✅ Phase 6 — OCP & 전략 (3, 6.2 ★깊이)
✅ Phase 7 — IoC (3, 7.1 ★깊이)
✅ Phase 8 — Spring 컨테이너 (4, 8.3·8.4 ★깊이)
총: 26/26 Unit ✅
마스터 1개 (2.4 CAS) + 깊이 6개 (4.1~4.3, 6.2, 7.1, 8.3, 8.4)
종합 졸업 시험 20문항
→ 동시성 (스레드 풀 ~ CAS)
→ 객체 설계 진화 (DAO ~ DI)
→ Spring 의 정신 (IoC/DI) 완전 이해
5주차를 완주했습니다. 다음 학습으로 이어갈 수 있습니다:
★ 깊이 파기 — DI 완료
🏆 Phase 8 완주 + 🎓 5주차 전체 완주 (26/26 Unit)