[Spring] 커스텀 스코프와 빈 생명주기 콜백

Jiwoo Jung·2024년 10월 4일
0

GDGoC Spring 스터디

목록 보기
5/15

GDG on Campus Backend-Spring 스터디 WIL
Week 3 - 커스텀 스코프와 빈 생명주기 콜백


1. Bean Scope

  • 생성된 빈 인스턴스가 존재할 수 있는 컨테이너 영역
  • 빈을 생성하고 소멸시키는 방법을 결정하며, 이를 통해 메모리 관리와 빈의 생성 시점 등을 세밀하게 컨트롤할 수 있다.
  • 스프링은 개발자가 빈을 생성하고 관리할 수 있도록 지원한다.

스프링의 6가지 Bean Scope

1. Singleton Scope

  • Spring IoC 컨테이너는 싱글톤으로 정의된 객체의 인스턴스를 한 개 생성
  • 생성된 단일 인스턴스는 싱글톤 빈들의 캐시에 저장되며, 이후 동일한 이름의 빈에 대한 모든 요청 및 참조는 캐시된 객체를 반환

    캐시
    여러 요청이 있을 때마다 새로 빈 인스턴스를 생성하지 않고 이미 생성된 인스턴스를 재사용할 수 있도록 저장하는 역할을 하는 저장 공간

2. Prototype Scope

  • 다른 빈에 주입되거나 해당 빈이 호출될 때 스프링 컨테이너는 getBean() 메소드를 호출하는데, 이때 캐시된 인스턴스를 가져오는 싱글톤과 달리 프로토타입 스코프로 정의된 빈은 매번 새로운 객체를 생성
  • 다른 스코프와 달리 프로토타입은 스프링 컨테이너가 모든 생명주기를 관리하지는 않으며, 호출에 의해 빈이 생성되면 컨테이너에서는 빈에 대한 기록을 유지하지 않는다.
  • 일반적으로 상태를 가진(stateful) 빈은 프로토타입 스코프를 사용하고, 상태가 없는(stateless) 빈은 싱글톤 스코프를 사용

싱글톤 빈 안에 프로토타입 빈이 주입된 경우

싱글톤 빈 내부에 프로토타입 빈이 주입되면, 프로토타입 빈은 단 한 번만 생성된다. 즉, 사용 시마다 새로운 인스턴스가 생성되는 것이 아니라, 싱글톤 빈이 생성될 때 처음 한 번만 생성된 프로토타입 빈이 계속 사용된다.

해결 방법
1. ApplicationContext를 주입받아, getBean()을 통해 직접 요청
2. ObjectProvider 타입으로 선언, getObject()를 통해 프로토타입 빈 return
이렇게 하면 매번 새로운 프로토타입 빈이 생성된다.


Web-aware Scope

request, session, application, websocket 스코프는 web-aware Spring ApplicationContext 구현체(XmlWebApplicationContext 등)를 사용할 때만 쓸 수 있다.

Spring Web MVC 에서 사용할 경우, DispatcherServlet이 요청과 관련된 정보들(request 객체, response 객체, session 정보 등) 을 전역적으로 사용할 수 있게 해주기 때문에 추가 설정이 필요 없다.

Web-aware ApplicationContext

HTTP 요청이나 세션, 서블릿 같은 웹 요소와 상호작용할 수 있도록 설계된 웹 전용 기능을 가진 ApplicationContext

DispatcherServlet

HTTP 프로토콜로 들어오는 모든 요청을 가장 먼저 받아 적합한 컨트롤러에 위임해주는 프론트 컨트롤러(Front Controller)


3. Request Scope

@RequestScope
@Component
public class LoginAction {
	// ...
}
  • 모든 로그인 Http 요청마다 LoginAction 빈에 대한 새로운 인스턴스를 만들어 request를 처리
  • 각 인스턴스가 별개로 존재하기 때문에 객체 내부의 속성 값 등을 변경해도 다른 인스턴스에는 영향을 끼치지 않는다.
  • 각 객체는 request processing 이 끝날 경우 사라진다.

4. Session Scope

  • session scope 로 정의된 빈으로부터 생성된 객체는 http session이 만료되기 전까지 메모리를 차지한다.
  • 웹 어플리케이션 세션에 대한 데이터를 유지하고 관리할 때 사용된다.
@SessionScope
@Component
public class UserPreferences {
	// ...
}

5. Application Scope

  • ServletContext 레벨에서 유일한 인스턴스를 생성한다.
  • HTTP 요청이 들어오면 웹 서버는 요청을 처리하기 위해 새로운 스레드와 서블릿을 생성한다.
  • Application Scope로 생성된 빈은 스레드마다 별도의 인스턴스가 생성되어 사용되기 때문에 멀티스레드 환경에서 안전하게 빈을 사용할 수 있다.
@ApplicationScope
@Component
public class AppPreferences {
	// ...
}

6. WebSocket Scope

  • 웹소켓은 Http와 다른 프로토콜을 사용하며, 클라이언트와 서버 간 양방향 통신을 가능케 한다.
  • 웹소켓 스코프를 사용하면 각 웹소켓 세션마다 별도의 빈 인스턴스가 생성되며 웹소켓 세션의 생명주기와 함께 관리된다.
  • @WebSocketScope 어노테이션은 Spring 의 WebSocket 구현체인 Spring Websocket에서만 사용할 수 있다.
@Component
@Scope(scopeName ="websocket", proxyMode=ScopedProxyMode.TARGET_CLASS)
public class MyWebSocketHandler {
	// ...
}

2. 빈 생명주기 콜백(Bean Lifecycle Callback)

스프링 빈 생명주기(Bean Lifecycle in Spring)

스프링 IoC 컨테이너 생성 -> 빈 생성 -> 의존성 주입 -> 초기화 콜백(init()) -> 빈 사용 -> 소멸 콜백(destroy())

콜백(callback)

  • 특정 작업이나 이벤트가 발생했을 때 호출되는 함수나 메서드
  • 콜백함수를 통해 특정 시점에서 실행할 동작을 미리 정의하고 원하는 타이밍에 실행할 수 있다.
  • 스프링은 스프링 빈이 생성되고 소멸될 때, 초기화 작업과 종료 작업을 수행할수 있도록 초기화 콜백과 소멸전 콜백을 지원한다.

빈의 생성과 초기화의 분리

  • 생성자: 객체가 생성될 때, 파라미터를 받아 메모리를 할당하여 객체 생성
  • 초기화: 생성된 객체의 값들을 활용해서 외부 연결 설정이나 데이터 로드 등 무거운 동작 수행
    .
    객체의 생성과 초기화는 분리되는 것이 좋다. 초기화 작업이 단순한 경우에는 생성자에서 처리할 수 있지만, 일반적으로 초기화 작업은 무겁기 때문에 생성자 DI 주입 과정과 초기화 콜백을 분리하는 것이 유지보수에 유리하다.

스프링의 빈 생명주기 콜백 관리

  1. 인터페이스
  2. 설정 파일
  3. 어노테이션

1. 인터페이스

InitializingBeanDisposableBean 인터페이스를 구현하여 초기화 및 소멸 시 필요한 작업을 정의할 수 있다.

  public class ExampleBean implements InitializingBean, DisposableBean {
      @Override
      public void afterPropertiesSet() {
          // 초기화 콜백
      }
      @Override
      public void destroy() {
          // 소멸 콜백
      }
  }
  • InitalizingBean 인터페이스
    afterPropertiesSet() 메소드로 초기화 지원 (의존관계 주입이 끝난 후 초기화 진행)
  • DisposableBean 인터페이스
    destory() 메소드로 소멸 지원
단점
- Spring 전용 인터페이스로, Spring에 의존
- 인터페이스의 메서드를 오버라이드하여 메서드명 변경 불가
- 소스코드를 수정할 수 없는 외부 라이브러리에는 적용 불가

2. 설정 파일

Configuration 클래스에서 @Bean 어노테이션을 사용하여 빈의 초기화 메서드와 소멸 메서드를 명시적으로 설정할 수 있다.
외부 라이브러리에서 초기화와 종료를 해야 할 경우 권장된다.

@Configuration
class LifeCycleConfig {
    @Bean(initMethod = "initialize", destroyMethod = "close")
    public ExampleBean exampleBean() {
        return new ExampleBean();
    }
}

@BeandestroyMethod 옵션

  • 기본값: Inferred(추론)
  • @Bean(initMethod = "initialize") 이렇게 destroyMethod를 따로 지정하지 않아도 close, shutdown 이라는 이름의 메소드를 종료 메소드라고 자동 인식해 호출한다.
  • 추론 기능을 사용하기 싫다면 @Bean(initMethod = "initialize", destroyMethod="") 처럼 설정하면 된다.
장점단점
- 메서드명 직접 지정 가능
- Spring 의존성 없음
- 외부 라이브러리에도 적용 가능
- Bean 지정 시 initMethoddestroyMethod를 직접 설정해야 하여 번거로움

3. 어노테이션

@PostConstruct@PreDestroy 어노테이션을 사용하여 초기화 및 소멸 작업을 처리할 수 있다.
최신 스프링에서 가장 권장하는 방법이다.

public class NetworkClient {
    @PostConstruct
    public void init() {
        System.out.println("NetworkClient.init");
        // 초기화 작업
    }
    @PreDestroy
    public void close() {
        System.out.println("NetworkClient.close");
        // 소멸 작업
    }
}
장점단점
- 어노테이션만으로 설정 가능해 편리
- 표준 자바 어노테이션으로, Spring 이외의 컨테이너에서도 동작
- 커스터마이징이 불가능한 외부 라이브러리에 적용 불가

참고자료

dev-coco.tistory.com
geeksforgeeks.org

0개의 댓글