DI(Dependency Injection) - (1)

2025. 4. 18. 00:20·SpringBoot
SMALL

의존성과 DI

의존성은 우리가 무언가 행동 하기 위해 사용해야 하는 도구를 말한다. 

예를 들어 우리는 빨래하기 위해 세탁기에 의존하고, 세탁기가 없다면 빨래하기 힘들 것이다.

이처럼, 의존성은 없애기 힘들다.

 

그러면 우리는 의존성을 어떤 식으로 가지고 있을까?

 

필요한 물건을 사는 것과 빌리는 것(랜탈)을 비교해보자.

  구매 렌탈
초기 비용 모든 상품 직접 선정·비교
→ 많은 리소스 투입
1차적인 비교·선정은 렌탈 업체가
→ 적은 리소스 투입
핵심 기능 빨래 빨래
상대적 장점/단점 장점
내꺼니까 내맘대로 (당근하기, 페인트 칠하기)
단점
제품 직접 관리 필요
– 고장 수리·청소 등에 직접 개입
장점
제품 관리는 렌탈 업체에서 처리 → 유지 보수 용이
단점
당근 시 계약 위반, 총 비용은 더 비쌈
결론 빨래를 위해 보유한 세탁기!
당근할 일은 없지만, 관리(업데이트·청소 등)는 언제나 필요
보유 과정 의존성 생성 의존성 주입

 

이렇게 표로 보면 의존성을 구매하는 것보다 빌려사용하는 것이 더 효율 적이다.

 

 

Container(컨테이너)란?

  • 스프링에서 컨테이너는 애플리케이션 구동 시점에 객체(Bean)를 생성·관리·소멸하고, 필요한 곳에 주입(DI)해 주는 ‘공장’이자 ‘매니저’ 역할을 한다.
  • 예시를 들자면, 카페에서 커피 원두와 바리스타 레시피를 바탕으로 언제든 품질 좋은 커피를 만들어 제공하듯 스프링 컨테이너는 “설정 정보(원두·레시피)”를 읽고 “객체(Bean)”를 만들어 “의존성(원두, 우유 등)”을 주입해서 필요한 곳에 제공한다.

스프링 빈이란?

  • 스프링 프레임워크에 의해 생성되고, 관리되는 자바 객체(자신, 렌탈업체, 세탁기...)이다
  • IoC 컨테이너(스프링)가 이 빈을 생성하고, 필요한 의존성을 주입(DI)하며, 라이프사이클 콜백을 호출한다
    • 스프링을 빈의 컨테이너라고 한다.
    • 빈 증록 방법에는 어노테이션 방식과 XML방식이 있다.

의존관계란?

  • 어떤 객체가 비지니스로직 처리를 위해 다른 객체에 의존하는 관계(객체 간 has-a관계)
  • 의존 관계 관리를 위해 DI개념을 사용하며 이 과정에서 IOC가 발생한다.
    • 스프링을 IoC컨테이너라고 부른다.

이게 처음 들으면 이해가 안되고 헷갈린다. 코드를 보면 이해가 쉬울 것이다.

 

1) 기존 방식 (제어권이 클라이언트에 있을 때) 경우

public class LaundryService {
    // 1) 직접 new 해서 생성
    private WashingMachine wm = new WashingMachine();  

    public void doLaundry() {
        wm.start();
    }
}

코드를 보면 개발자가 직접 new를 통해서 원하는 시간에 객체를 생성하고 있다. 이것을 보고 제어 권한이 개발자한테 있는 것을 볼 수 있다.

 

2) 스프링 DI 방식 (제어권이 컨테이너에 넘어갈 때)

@Component
public class LaundryService {
    // 1) 선언만! 생성·설정·주입은 모두 컨테이너가 담당
    @Autowired
    private WashingMachine wm;  

    public void doLaundry() {
        wm.start();
    }
}

코드를 보면 개발자는 선언만 한 상태이고, 스프링 컨테이너가 알아서 객체 생성·설정해준다. 라이프사이클 제어 권한이 개발자가 아니라 컨테이너에 “역전(Inversion)”된 것을 확인 할 수 있다.

 


1. 직접 new 해서 의존(결합도 높음)

// 세탁기 클래스
public class Washer {
    public void wash() {
        System.out.println("빨래 중…");
    }
}

// 세탁기를 쓰는 사용자
public class WasherUser {
    private Washer washer = new Washer();  // 직접 생성

    public void doLaundry() {
        washer.wash();
    }

    public static void main(String[] args) {
        new WasherUser().doLaundry();
    }
}

 

  • 문제점
    • WasherUser가 Washer를 직접 new 하니까
      • 세탁기 종류 바꾸면 WasherUser 코드도 수정해야 한다.
      • 단위 테스트하거나 가짜 세탁기 쓰기 어렵다.

2. 인터페이스로 느슨한 결합(명시적 DI)

// 1) 인터페이스 정의
public interface IWasher {
    void wash();
}

// 2) 구체 구현체
public class Washer implements IWasher {
    public void wash() { System.out.println("기본 세탁기 빨래 중…"); }
}
public class LWasher implements IWasher {
    public void wash() { System.out.println("고급 세탁기 빨래 중…"); }
}

// 3) 생성자 주입
public class WasherUser {
    private IWasher washer;

    public WasherUser(IWasher washer) {
        this.washer = washer;      // 외부에서 주입
    }

    public void doLaundry() {
        washer.wash();
    }

    public static void main(String[] args) {
        // 수동으로 어떤 구현을 넣을지 결정
        IWasher w = new Washer();  
        new WasherUser(w).doLaundry();
    }
}
  • 장점
    • WasherUser는 IWasher에만 의존 → 구현 클래스 교체 쉬움
    • 테스트할 때 가짜 IWasher 넣어 테스트 가능

3. 팩토리 패턴으로 간접 주입(명시적 DI + factory)

// 1) 팩토리 클래스
public class WasherFactory {
    public static IWasher create(String type) {
        if ("L".equals(type)) return new LWasher();
        else return new Washer();
    }
}

// 2) 사용자 클래스
public class WasherUser {
    private IWasher washer;

    public WasherUser(IWasher washer) {
        this.washer = washer;
    }

    public void doLaundry() {
        washer.wash();
    }

    public static void main(String[] args) {
        // 팩토리 통해 생성 로직 캡슐화
        IWasher w = WasherFactory.create("L");
        new WasherUser(w).doLaundry();
    }
}
  • 장점/단점
    • 생성 로직을 Factory로 분리 → 더 유연해짐
    • 그래도 main()에서 직접 팩토리 호출·의존성 주입 필요

4. Spring Framework로 자동 주입(IoC 컨테이너)

// 1) 인터페이스 + 구현체
public interface IWasher { void wash(); }

@Component
public class Washer implements IWasher {
    public void wash() { System.out.println("스프링 세탁기 빨래 중…"); }
}

// 2) 의존성 주입 받을 사용자
@Component
public class WasherUser {
    private final IWasher washer;

    @Autowired          // 스프링이 타입 찾아서 자동 주입
    public WasherUser(IWasher washer) {
        this.washer = washer;
    }

    public void doLaundry() {
        washer.wash();
    }
}

// 3) 애플리케이션 구동
@Configuration
@ComponentScan("com.example")
public class AppConfig { }

public class MainApp {
    public static void main(String[] args) {
        // 1) 스프링 컨테이너 생성
        AnnotationConfigApplicationContext ctx =
            new AnnotationConfigApplicationContext(AppConfig.class);

        // 2) 빈 사용
        WasherUser user = ctx.getBean(WasherUser.class);
        user.doLaundry();

        ctx.close();  // 빈 소멸 콜백 처리
    }
}
  • 특징
    • 객체 생성·설정·주입을 모두 컨테이너가 담당 → 개발자는 **“필요하다”**만 선언
    • 설정(JavaConfig, XML, 어노테이션)만 바꾸면 주입 대상·방식 변경 가능
    • 결합도 최소화, 테스트·확장성·유지보수성 극대화

정리

  1. 직접 new → 결합도 너무 높음
  2. 인터페이스 + 생성자 주입 → 의존성 분리, 수동 주입
  3. 팩토리 패턴 → 생성 로직 캡슐화, 여전히 수동
  4. 스프링 DI → 컨테이너에게 제어권 완전 위임(IoC), 자동 주입

1. DI(Dependency Injection)란?

  • 의존성 주입 = 객체가 필요한 의존(멤버 변수)을 내부에서 new 하지 않고 외부(컨테이너)에서 공급받는 것
  • 이렇게 하면 “누가, 언제, 어떻게” 객체를 만들고 연결할지 제어권이 내 코드 → 컨테이너로 역전(IoC)돼서
    • 결합도 낮아지고
    • 설정·테스트·확장이 쉬워진다

2. 의존성 주입 방법 3가지

  1. 필드 주입 (Field Injection)
  2. @Autowired private Washer washer;
  3. 생성자 주입 (Constructor Injection)
  4. public WasherUser(Washer washer) { this.washer = washer; }
  5. 세터 주입 (Setter Injection)
  6. @Autowired public void setWasher(Washer washer) { this.washer = washer; }

3. 스프링 컨테이너의 빈 관리 흐름

스프링 IoC 컨테이너 생성 → 스프링 빈 생성 → 의존관계 주입 → 초기화 콜백 메소드 호출 → 사용 → 소멸 전 콜백 메소드 호출 → 스프링 종료
  1. POJO 빈 작성
    • 개발자가 단순 자바 클래스(POJO)로 빈(Bean) 클래스 작성
  2. 메타데이터 등록
    • @Component·@Bean·XML 등으로
    • “어느 클래스”를 “어떻게(생성자·필드·세터)” 빈으로 만들고,
    • “어떤 의존”을 주입할지 정보를 컨테이너에 제공
  3. 빈 생성 & 의존성 주입
    • 컨테이너가 메타정보 보고
    • 빈 인스턴스화 → 생성자/필드/세터 방식으로 DI
  4. 싱글톤 관리
    • 기본 스코프는 싱글톤
    • 한 번 생성된 빈을 컨테이너가 계속 재사용
  5. 빈 요청 & 반환
    • 애플리케이션에서 getBean() 또는 @Autowired로 요청 시
    • 컨테이너가 관리 중인 빈을 꺼내서 줌
  6. 소멸 콜백 처리
    • 컨테이너 종료 시 @PreDestroy, DisposableBean 등 호출
728x90

'SpringBoot' 카테고리의 다른 글

DI(Dependency Injection) - (3)  (0) 2025.04.18
DI(Dependency Injection) - (2)  (0) 2025.04.18
Spring Framework  (1) 2025.04.16
세션과 JWT의 차이  (1) 2025.01.12
SpringBoot 잡동사니  (1) 2025.01.12
'SpringBoot' 카테고리의 다른 글
  • DI(Dependency Injection) - (3)
  • DI(Dependency Injection) - (2)
  • Spring Framework
  • 세션과 JWT의 차이
알파 조
알파 조
공부 일기장
  • 알파 조
    Blue Ocean
    알파 조
  • 전체
    오늘
    어제
    • 분류 전체보기 (93)
      • Algorithm (9)
      • Data Structure (3)
      • Python (7)
      • 컴퓨터 구조 요약 (6)
      • 몰입 교육 (7)
      • JavaScript (1)
      • Vue.js (7)
      • 코딩테스트 연습 (40)
      • SpringBoot (9)
      • 데이터베이스 (2)
  • 블로그 메뉴

    • Home
    • Computer structure
    • Algorithm
    • SpringBoot
    • Vuejs
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    오블완
    Git
    항해99
    티스토리챌린지
    잔디 기부
    리그오브레전드 #롤 #LOL #60프레임 버그 #GPU #윈도우10 #롤 60프레임 고정
    Udemy#Python#Bootcamp#Object and Data Structure Basics
    MSA 기초
    잔디 기부 캠페인
  • hELLO· Designed By정상우.v4.10.3
알파 조
DI(Dependency Injection) - (1)
상단으로

티스토리툴바