5주차 Unit 8.4 — 의존관계 주입 (DI)

Psj·2026년 5월 27일

F-lab

목록 보기
181/239

Unit 8.4 — 의존관계 주입 (DI)

F-LAB JAVA · 5주차 · Phase 8 · Spring 컨테이너
★ 깊이 파기 + 🏆 Phase 8 완주 + 🎓 5주차 전체 완주 + 종합 졸업 시험 20문항


📌 학습 목표

이 Unit을 끝내면 다음을 답할 수 있어야 한다.

  • DI (의존관계 주입) 의 정의는?
  • 런타임 의존관계 연결 의 의미는?
  • 의존 / 인터페이스 의존 / 외부 주입 의 3요소는?
  • 3가지 주입 방식 (생성자/Setter/필드) 은?
  • DI 와 IoC 의 관계 는?
  • 생성자 주입 권장 이유 3가지 는?
  • Setter/필드 주입의 문제 는?
  • 5주차 전체 의 종합은?
  • 종합 졸업 시험 20문항 은?

🎯 핵심 한 문장

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 구현), 생성자 주입 권장.


🧭 9개 섹션 로드맵

1. DI의 정의
2. 런타임 의존관계 연결
3. 3요소 (의존/인터페이스/주입)
4. 3가지 주입 방식
5. DI와 IoC 관계
6. 생성자 주입 권장 이유
7. Setter/필드 주입 문제
8. 5주차 완주 + 종합 졸업 시험
9. 면접 + 자기 점검

1️⃣ DI의 정의

1.1 DI

DI (Dependency Injection):

  의존관계 주입.

  객체가 의존하는 대상을
  외부에서 연결·주입.

  - 직접 생성 X
  - 외부가 주입

1.2 의존관계

의존관계:

  A 가 B 사용:
    - A 는 B 에 의존
    - B 없으면 A 동작 X

  ShipmentDao → ConnectionMaker

1.3 주입

주입 (Injection):

  외부가:
    - 의존 객체 생성
    - A 에 넣어줌

  → A 는 받아서 사용

1.4 ILIC 의 맥락

// DI (ILIC)
@Service
public class ShipmentService {
    private final ShipmentDao shipmentDao;   // 의존
    
    // 생성자 주입 (외부가 주입)
    public ShipmentService(ShipmentDao shipmentDao) {
        this.shipmentDao = shipmentDao;   // 받음
    }
    // Spring 컨테이너가 ShipmentDao 주입
}
class ShipmentDao { }

1.5 자기 점검 답변

DI (의존관계 주입) 의 정의는?

:
1. DI:

  • 의존 대상 외부 주입
  1. 의존관계:

    • A 가 B 사용
  2. 주입:

    • 외부가 넣어줌
  3. 결과:

    • 받아서 사용

2️⃣ 런타임 의존관계 연결

2.1 런타임 연결

런타임 의존관계 연결:

  코드(컴파일)에는:
    - 인터페이스 의존

  런타임에:
    - 구체 구현 연결
    - 동적 결정

2.2 컴파일 vs 런타임

컴파일 vs 런타임:

컴파일 의존:
  - ShipmentDao → ConnectionMaker (인터페이스)

런타임 의존:
  - ShipmentDao → NConnectionMaker (구체)
  - 주입으로 결정

2.3 동적 연결

동적 연결:

  같은 코드:
    - 런타임에 다른 구현
    - 설정/환경 따라

  → 유연

2.4 ILIC 의 맥락

// 런타임 의존관계 연결
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; }
}

2.5 자기 점검 답변

런타임 의존관계 연결의 의미는?

:
1. 런타임 연결:

  • 구체를 런타임에
  1. 컴파일 vs 런타임:

    • 인터페이스 vs 구체
  2. 동적:

    • 설정/환경 따라
  3. 유연:

    • 같은 코드, 다른 구현

3️⃣ 3요소 (의존/인터페이스/주입)

3.1 3요소

DI 3요소:

1. 의존
   - A 가 B 사용

2. 인터페이스 의존
   - A 는 B 인터페이스에

3. 외부 주입
   - 구체는 외부가

3.2 의존

의존:

  A 가 B 필요:
    - B 없으면 A 동작 X
    - 의존관계

3.3 인터페이스 의존

인터페이스 의존:

  A 는 B 의 인터페이스만:
    - 구체 모름
    - 추상에 의존 (DIP)

3.4 외부 주입

외부 주입:

  구체 구현:
    - 외부(컨테이너)가 생성
    - A 에 주입

3.5 ILIC 의 맥락

// 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.6 자기 점검 답변

의존 / 인터페이스 의존 / 외부 주입의 3요소는?

:
1. 의존:

  • A 가 B 사용
  1. 인터페이스 의존:

    • 추상에 의존
  2. 외부 주입:

    • 구체 외부가
  3. 3요소:

    • 함께 DI

4️⃣ 3가지 주입 방식

4.1 세 방식

3가지 주입:

1. 생성자 주입 ✅ (권장)
2. Setter 주입
3. 필드 주입

4.2 생성자 주입

// 생성자 주입 (권장)
@Service
public class ShipmentService {
    private final ShipmentDao shipmentDao;   // final
    
    public ShipmentService(ShipmentDao shipmentDao) {
        this.shipmentDao = shipmentDao;
    }
}
// 생성 시 주입, final, 불변

4.3 Setter 주입

// Setter 주입
@Service
public class ShipmentService {
    private ShipmentDao shipmentDao;   // final X
    
    @Autowired
    public void setShipmentDao(ShipmentDao shipmentDao) {
        this.shipmentDao = shipmentDao;
    }
}
// setter 로 주입, 변경 가능

4.4 필드 주입

// 필드 주입 (권장 X)
@Service
public class ShipmentService {
    @Autowired
    private ShipmentDao shipmentDao;   // 필드 직접
}
// 간편하나 단점 (테스트, final X)

4.5 비교

방식final테스트권장
생성자쉬움
Setter보통
필드어려움

4.6 ILIC 의 맥락

// 생성자 주입 (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 { }

4.7 자기 점검 답변

3가지 주입 방식은?

:
1. 3방식:

  • 생성자/Setter/필드
  1. 생성자:

    • final, 권장
  2. Setter:

    • 변경 가능
  3. 필드:

    • 간편하나 권장 X

5️⃣ DI와 IoC 관계

5.1 관계

DI와 IoC:

  IoC (넓은 개념):
    - 제어 역전

  DI (구체 방법):
    - 의존성 주입
    - IoC 의 한 형태

  IoC ⊃ DI

5.2 IoC 의 형태

IoC 의 형태:

  DI (의존성 주입):
    - 외부가 주입 (수동)

  DL (의존성 조회):
    - 객체가 조회 (능동, getBean)

  → 둘 다 IoC

5.3 DI 가 깔끔

DI 가 깔끔:

  DL (조회):
    - 컨테이너 의존
    - getBean 명시

  DI (주입):
    - 컨테이너 모름
    - 자동 주입

→ DI 권장

5.4 ILIC 의 맥락

// 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 { }

5.5 자기 점검 답변

DI와 IoC의 관계는?

:
1. 관계:

  • DI 는 IoC 의 한 형태
  • IoC ⊃ DI
  1. IoC 형태:

    • DI (주입)
    • DL (조회)
  2. DI 권장:

    • 컨테이너 모름
  3. DL:

    • getBean (지양)

6️⃣ 생성자 주입 권장 이유

6.1 세 가지 이유

생성자 주입 권장 3이유:

1. final (불변)
2. 순환참조 감지
3. 테스트 용이성

6.2 final 불변

// 1. final 불변
@Service
public class ShipmentService {
    private final ShipmentDao shipmentDao;   // final 가능
    
    public ShipmentService(ShipmentDao dao) {
        this.shipmentDao = dao;
    }
    // 생성 후 변경 X (불변, 안전)
}
// Setter/필드는 final X

6.3 순환참조 감지

2. 순환참조 감지:

  A → B, B → A:
    - 생성자 주입: 기동 시 오류 (감지)
    - Setter/필드: 런타임 늦게 발견

  → 생성자가 조기 감지

6.4 테스트 용이성

// 3. 테스트 용이성
@Test
void test() {
    ShipmentDao mock = mock(ShipmentDao.class);
    ShipmentService service = new ShipmentService(mock);   // 생성자 주입
    // Spring 없이 Mock 주입, 단위 테스트
}
// 필드 주입은 reflection 필요 (테스트 어려움)

6.5 필수 의존성 명확

필수 의존성 명확:

  생성자 주입:
    - 생성 시 필수
    - 누락 시 컴파일/기동 오류

  → 의존성 명시적

6.6 ILIC 의 맥락

// 생성자 주입 권장 (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); }

6.7 자기 점검 답변

생성자 주입 권장 이유 3가지는?

:
1. final:

  • 불변 보장
  1. 순환참조 감지:

    • 기동 시
  2. 테스트 용이:

    • Mock 주입 쉬움
  3. + 필수 의존성:

    • 명확

7️⃣ Setter/필드 주입 문제

7.1 Setter 주입 문제

Setter 주입 문제:

  - final X (변경 가능)
  - 필수 의존성 불명확
  - 주입 누락 가능
  - 불완전 객체 (NPE)

7.2 필드 주입 문제

필드 주입 문제:

  - final X
  - 테스트 어려움 (reflection)
  - 컨테이너 강결합
  - 의존성 숨김

7.3 필드 주입 위험

// 필드 주입 위험
@Service
public class ShipmentService {
    @Autowired
    private ShipmentDao shipmentDao;   // 숨겨진 의존성
    
    // 테스트 시:
    // ShipmentService s = new ShipmentService();
    // shipmentDao 주입 안 됨 → NPE
    // reflection 으로 주입해야 (번거로움)
}

7.4 언제 Setter

언제 Setter:

  - 선택적 의존성
  - 변경 가능 의존성
  - (드묾)

→ 대부분 생성자

7.5 ILIC 의 맥락

// 권장 (생성자)
@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 { }

7.6 자기 점검 답변

Setter/필드 주입의 문제는?

:
1. Setter:

  • final X, 누락 가능
  1. 필드:

    • 테스트 어려움
    • 의존성 숨김
  2. 필드 위험:

    • NPE, reflection
  3. 권장:

    • 생성자

8️⃣ 5주차 완주 + 종합 졸업 시험

8.1 Phase 8 완주

🏆 Phase 8 — Spring 컨테이너
  ✅ Unit 8.1 빈 팩토리와 ApplicationContext
  ✅ Unit 8.2 getBean()의 동작
  ✅ Unit 8.3 싱글톤 레지스트리 ★깊이
  ✅ Unit 8.4 의존관계 주입 DI ★깊이 ← 완주

8.2 5주차 전체 완주

🎓 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 ✅

8.3 5주차 진화 여정

5주차 진화 여정:

Part A (동시성):
  스레드 풀 → 가시성/원자성 → CAS

Part B (객체 설계):
  전통 DAO (혼재)
    → 관심사 분리
    → 디자인 패턴 (템플릿/팩토리)
    → 인터페이스 + 합성 (전략)
    → OCP
    → IoC
    → DI (Spring)

→ "한 줄의 DAO 가 Spring 까지"

8.4 종합 자기 점검 20문항 (졸업 시험)

Part A — 동시성 (5문항)

Q1. 스레드 많을수록 빠른가?
  → X (스위칭 비용, 적정 수)

Q2. 가시성 vs 원자성?
  → 가시성(안 보임)/원자성(덮어씀)

Q3. synchronized/volatile/Atomic 비교?
  → sync(둘다,락)/volatile(가시성)/Atomic(둘다,CAS)

Q4. CAS 4단계?
  → 읽기(A)/계산(B)/비교/교체or재시도

Q5. ABA 문제?
  → A→B→A 못 감지, AtomicStampedReference

Part B — 객체 설계 (5문항)

Q6. DAO 가 분리하는 것?
  → 비즈니스 vs 데이터 접근

Q7. 관심사 분리?
  → 같은 것 모음, 다른 것 분리

Q8. 템플릿/팩토리 메소드?
  → 흐름(부모)+변동(자식), 객체 생성 위임

Q9. 상속의 한계?
  → 단일상속/컴파일타임/깨지기쉬움

Q10. 인터페이스로 OCP?
  → 새 구현(확장), 기존 불변(닫힘)

Part B — 디자인 원칙 (3문항)

Q11. OCP?
  → 확장 열림, 변경 닫힘

Q12. 전략 패턴 3요소?
  → Context/Strategy/ConcreteStrategy

Q13. 전략 vs 템플릿 메소드?
  → 합성(주입) vs 상속(서브클래스)

Part B — IoC/DI (7문항)

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)

8.5 채점

20 / 20 → 5주차 마스터 (동시성 + Spring)
17-19   → 거의 마스터
14-16   → 복습 권장
< 14    → 5주차 재학습

8.6 자기 점검 답변

5주차의 종합은?

:
1. Part A:

  • 동시성 마무리 (CAS)
  1. Part B:

    • DAO → Spring (DI)
  2. 진화:

    • 혼재 → IoC/DI
  3. 완주:

    • 26/26 Unit

9️⃣ 면접 + 자기 점검

9.1 면접 단골 질문 매핑

Q핵심 답변
DI?의존 대상 외부 주입
런타임 연결?구체 런타임 결정
3요소?의존/인터페이스/주입
3방식?생성자/Setter/필드
DI-IoC?DI 는 IoC 형태
생성자 권장?final/순환참조/테스트
Setter 문제?final X, 누락
필드 문제?테스트 어려움
싱글톤 stateless?동시성 (4주차)
IoC 컨테이너?ApplicationContext

9.2 자기 점검 체크리스트

DI

  • 정의
  • 런타임

3요소

  • 의존/인터페이스/주입

3방식

  • 생성자/Setter/필드

DI-IoC

  • 관계

생성자 권장

  • 3이유

5주차 완주

  • 종합

9.3 추가 심화 질문

Q1: @Autowired 생략?

답:

  • Spring 4.3+ 생성자 1개면 생략
  • 단일 생성자 자동 주입
  • 명시적 가독성도 OK
  • 권장 (간결)

Q2: @Qualifier?

답:

  • 같은 타입 여러 빈
  • 이름으로 구분
  • @Qualifier("name")
  • @Primary 도 우선순위

Q3: 순환참조 해결?

답:

  • 설계 재검토 (근본)
  • @Lazy (지연)
  • Setter 주입 (임시)
  • 이벤트/중간 객체

Q4: Lombok @RequiredArgsConstructor?

답:

  • final 필드 생성자 자동
  • 생성자 주입 간결
  • 보일러플레이트 제거
  • 실무 흔히 사용

Q5: DI 컨테이너 없이 DI?

답:

  • 수동 DI (main/팩토리)
  • new 로 주입
  • 패턴은 DI
  • 컨테이너는 자동화

🎯 핵심 요약 — 3줄 정리

1. DI

  • 의존 대상을 외부에서 런타임에 주입 (IoC 의 구현)
  • 3요소: 의존 / 인터페이스 의존 / 외부 주입

2. 3가지 주입과 권장

  • 생성자 / Setter / 필드 중 생성자 권장
  • 이유: final(불변) / 순환참조 감지 / 테스트 용이성

3. DI-IoC + 5주차 완주

  • DI 는 IoC 의 한 형태 (IoC ⊃ DI)
  • 전통 DAO → 관심사 분리 → 패턴 → OCP → IoC → DI (Spring 정신)

🏆 Phase 8 완주 — Spring 컨테이너

🌱 Phase 8 — Spring 컨테이너
  ✅ Unit 8.1 빈 팩토리와 ApplicationContext
  ✅ Unit 8.2 getBean()의 동작
  ✅ Unit 8.3 싱글톤 레지스트리 ★깊이
  ✅ Unit 8.4 의존관계 주입 DI ★깊이 ← 여기, Phase 8 완주

🎓 5주차 전체 완주!

🎓 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주차를 완주했습니다. 다음 학습으로 이어갈 수 있습니다:

  • 6주차 (다음 주차 커리큘럼)
  • 특정 Unit 심화 복습
  • 종합 졸업 시험 풀이
  • 실전 프로젝트 적용 (ILIC)

★ 깊이 파기 — DI 완료
🏆 Phase 8 완주 + 🎓 5주차 전체 완주 (26/26 Unit)

profile
Software Developer

0개의 댓글