본문 바로가기
💻 개발/Spring

11/21 - TIL : Spring Bean과 DIP, DI, Interface

by 컴쏘 2024. 11. 21.

 

Spring에서 중요하다고 할 수 있는게 뭐가 있을까? 그것은.. Bean이다. 

  • Spring의 핵심 철학인 DI(Dependency Injection)IoC(Inversion of Control)의 중심에 있는 것이 Bean이다. 

 

Bean이 무엇이길래?! 

 

먼저, 의존 역전 원칙(DIP : Dependency Inversion Principle), Interface, DI에 대해 우선 알아보자. 

DIP (Dependency Inversion Principle)

  • DIP는 의존 역전 원칙으로 상위 수준 모듈은 하위 수준 모듈에 의존해서는 안되며, 둘 다 추상화(인터페이스)에 의존해야 한다는 것이다. 
  • 이는 추상화된 인터페이스를 통해 상호작용해야 한다는 것이다. 
    • 상위 모듈은 인터페이스에 의존 
    • 하위 모듈은 인터페이스를 구현 

Interface

  • 추상화를 위한 메커니즘으로, 메서드의 선언만 포함하고 구현은 제공하지 않는 구조 
  • 객체 간의 결합도를 낮추기 위해 사용한다. 

DI (Dependency Injection) 

  • 객체 간 의존성을 외부에서 주입받아 객체 간 결합도를 낮추는 설계 패턴 
  • 객체 생성 및 관리외부 컨테이너(Spring)가 담당 
    • 필드 주입 : @Autowired 를 필드에 사용 
    • 생성자 주입 : 생성자를 통해 의존성을 주입 
    • setter 주입 : setter 메서드를 통해 의존성을 주입 

 

그럼 Spring Bean의 역할은 무엇일까? 

  1. Spring은 IoC 컨테이너(객체의 제어권이 개발자가 아닌 컨테이너가 가짐)를 통해 Bean 객체를 생성, 관리, 소멸까지 담당한다. 
  2. DI를 통해 Bean 간의 의존성을 주입한다. 
  3. Bean은 interface와 DIP 원칙을 적용하여 설계된 객체 간 의존성을 해결한다. 

Spring Bean으로 DI와 DIP 구현 

// 1. Interface 정의
public interface PaymentService {
    void processPayment();
}

// 2. 구현 클래스
@Component
public class CreditCardPaymentService implements PaymentService {
    @Override
    public void processPayment() {
        System.out.println("신용카드 결제");
    }
}

// 3. 상위 모듈 (OrderService)
@Component
public class OrderService {
    private final PaymentService paymentService;

    // 생성자 주입
    @Autowired
    public OrderService(PaymentService paymentService) {
        this.paymentService = paymentService; // DIP 준수
    }

    public void placeOrder() {
        paymentService.processPayment();
    }
}

 

위의 예시를 보면서 생각해보자. 최근에 주문 플랫폼 프로젝트를 진행했기 때문에 Order와 Payment의 경우를 예시로 들었다. 

 

Spring에서 이 코드는 다음과 같이 동작하게 된다. 

  1. IoC 컨테이너 초기화 : 애플리케이션 실행 시 Spring IoC 컨테이너가 초기화된다. IoC 컨테이너는 애플리케이션의 모든 객체(Bean)를 관리하며, 객체의 생성과 주입을 담당한다. 
  2. Bean 스캔 및 등록 : Spring은 @Component 어노테이션이 붙은 클래스를 스캔하여 Bean으로 등록한다. 
  3. 의존성 확인 및 주입 : Spring은 OrderService의 생성자를 분석하고, PaymentService 타입의 의존성이 필요하다는 것을 파악한다. 그리고 PaymentService 인터페이스를 구현한 Bean을 검색하고 OrderService의 생성자에 주입한다. 
  4. 객체 사용 : 애플리케이션이 OrderService Bean을 요청하면 컨테이너는 의존성이 주입된 인스턴스를 반환한다. 

이를 통해서 알 수 있는 건 다음과 같다. 

  • PaymentService 인터페이스에서 CreditCardPaymentService 구현체를 자동으로 연결한다.
    • 상위 모듈(OrderService)은 PaymentService 인터페이스에 의존한다. (결제가 필요하다는 것만 알고, 어떻게 결제할 지는 모름) 
    • 하위 모듈(CreditCardPaymentService)인터페이스를 구현한다.  
    • DI에 의해 구현체가 자동으로 연결된다. 
  • Spring의 DI는 DIP를 자연스럽게 구현하고 있다.

 

Bean에 대해서 조금 더 알아보자.

 

Spring에서는 Bean의 scope를 설정하여 Bean이 생성되고 관리되는 범위를 정의할 수 있다. 

Bean의 Scope

  • Singleton : Bean의 기본 스코프이다. Spring 컨테이너 내에서 단 하나의 인스턴스만 생성되어 공유된다. 
    • 사용 시점 : 대부분의 경우, 상태를 공유하지 않는 빈(Stateless Bean)에 적합하다. 
    • 특징 : Spring 컨테이너가 시작될 때 빈이 생성된다. 모든 요청에 대해 동일한 인스턴스가 반환된다. 
  • Prototype : 요청 시마다 새로운 인스턴스를 생성한다.
    • 사용 시점 : 상태를 가지는 Bean(Stateful Bean)이나 특정 요청마다 새롭게 초기화가 필요한 경우에 적합하다. 
    • 특징 : 컨테이너가 Bean을 생성 후 초기화만 담당하고, 이후의 라이프사이클은 컨테이너가 관리하지 않는다. 
  • Request : HTTP 요청 하나마다 새로운 빈 인스턴스를 생성한다. 
    • 사용 시점 : 웹 애플리케이션에서 HTTP 요청마다 고유한 데이터를 관리할 때 적합하다. 
    • 특징 : 요청이 끝나면 빈은 소멸된다. 
  • WebSocket : 웹소켓 세션 동안 동일한 Beean 인스턴스를 사용한다.
    • 사용 시점 : 웹소켓 연결에 따라 Bean을 분리하여 관리하고 싶을 때 적합하다. 
    • 특징 : 웹 소켓 연결이 종료되면 Bean도 소멸 

... 등등 그외에도 session, application도 있다고 한다. 

 

 

+) Singleton 타입의 빈의 생애주기 

더보기

1. Spring 컨테이너 생성 : Spring 컨테이너 (Application Context)가 생성된다. 

- Spring은 애플리케이션의 설정을 읽고 Bean의 정의를 메모리에 로드한다. 

- 컨테이너는 Bean의 lifecycle을 관리할 준비를 한다. 

 

2. Spring 빈 생성 : Spring 컨테이너가 Bean을 생성한다. 

- 설정된 클래스 또는 XML 설정에 따라 Bean 객체를 인스턴스화 한다. 

- 이 단계에서는 의존관계가 주입되지 않는다. 

 

3. 의존관계 주입 : Spring 컨테이너가 Bean의 의존성을 주입한다. 

- 생성자 주입, 필드 주입, setter 주입 방식으로 다른 Bean을 주입

- 이 과정에서 Bean간의 의존성이 해결  

 

4. 초기화 콜백 : Bean이 초기화 작업을 수행한다. 

- 의존 관계 주입이 완료된 후, Bean이 application에서 사용되기 전에 필요한 초기화 작업을 수행한다. 

 

5. 사용 : application의 로직에서 Bean을 사용한다. 

- Bean은 요청에 응답하거나, application의 특정 작업을 수행한다. 

- Singleton Bean의 경우, 여러 요청이 동일한 인스턴스를 공유 

 

6. 소멸 전 콜백 : 컨테이너가 종료되기 전에 Bean이 리소스를 정리하거나 종료 작업을 수행한다. 

- 네트워크 연결 해제, 파일 닫기, 캐시 정리와 같은 작업이 필요할 수 있다. 

 

7. Spring 종료 : application context가 종료된다. 

- 컨테이너가 관리하던 모든 빈의 소멸 작업을 완료한 후, 컨테이너 자체가 종료된다. 

 

Bean이라는 이름은 귀엽지만.. Bean의 역할은 아주 중요한 것 같다...🫛🫛