본문 바로가기
Framework & Lib & API/스프링

[스프링] 스태틱 메서드가 아닌 스프링 싱글톤 빈을 사용해야하는 이유

by 코딩공장공장장 2024. 6. 29.

스프링의 application context에 관리되는 빈들은 기본적으로 싱글톤으로 관리된다. 

 

싱글톤으로 관리하게 됬을 때의 장점은 메모리 사용에 효율적이라는 것이다.

 

객체 사용 시점마다 인스턴스화가 된다면 가비지 컬렉터의 성능이 아무리 좋다한들 많은 양의 메모리를 점유하게 될 것이다.

 

싱글톤 인스턴스를 통해 하나만 가지고 재사용할 수 있다면 메모리 점유를 줄여 효율적인 시스템 운영이 가능하게 된다.

 

스프링이 싱글톤을 통해 얻는 장점에 대해서는 알았으나 의문점이 드는 부분이 있다.

 

싱글톤을 통해 얻는 장점이 메모리 사용의 효율성이라고 한다면 모든 메서드를 스태틱으로 선언하여 사용해도 되지 않을까?

 

위와 같은 의문에 대한 답은 객체지향적인 프로그래밍의 장점을 위해 스태틱 메서드로 이루어진 클래스를 사용하는 것보다 

 

인스턴스화하는 것이 더 좋다는 것이겠지만

 

이번 포스팅에서는 객체지향 프로그래밍의 장점보다는 스프링 프레임워크의 특징에 초점에 맞춰 그 답을 찾아보도록 하겠다.

 

1. DI (with DIP)

 

스프링에서는 DI를 통해 객체 생성과 의존성을 결정해준다.

 

이때 DIP를 통해 인터페이스 타입에 의존하는 방식으로 객체들의 의존관계를 설정하면 구현체가 변경되더라도 호출하는 객체에게는 영향도를 줄이는 낮은 결합도를 갖출 수 있게 된다.

 

허나, 스태틱 메서드로만 이루어진 클래스들의 의존관계를 설정하게 됬을 시에는 구체적인 클래스 타입에 의존하게 된다. 

(애초에 스태틱 메서드를 사용한다는 것은 오버라이딩을 사용하지 않겠다는 것이니 DIP를 적용하는 구조가 무의미하다.)

 

이는 의존하는 호출되는 클래스를 변경하게 될 시 호출하는 모든 클래스의 변경을 수반하게 되는 높은 결합도를 갖게 된다. 

 

따라서 스프링 싱글톤 빈을 사용하는 것이 static 메서드로만 이루어진 클래스를 사용하는 것보다 낮은 결합도를 갖춰

변경사항에 더욱 유연한 구조를 가질 수 있게 된다.

 

* DI를 사용하는 이유 : 객체 생성과 의존관계 설정에 대한 책임을 프레임워크에 넘길 수 있음(개발자가 하지 않아도 됨)

* DI에 DIP를 적용하는 이유 : 의존관계 변경에 유연한 구조를 갖추기 위해(결합도를 낮출 수 있음)

 

2. 프록시 aop

 

스프링의 대표적인 기술 중 하나에는 aop가 있다.

 

aop란 관점지향 프로그래밍으로 객체지향 프로그래밍으로는 결합도를 낮추고 응집도를 높이기 어려운 구조를 해결하는데 도움이 되는 기법이다.

 

스프링 aop의 대표적인 기능 중 하나로 트랜잭션이 있다.

 

@Transactional 어노테이션이 붙은 클래스의 인스턴스들은 아래와 같이 TransactionInterceptor의 invoke 메서드의 파라미터로 전달된다.

 

public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable {

  . . .


  @Nullable
  public Object invoke(final MethodInvocation invocation) throws Throwable {


      Class<?> targetClass = invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null;


      return this.invokeWithinTransaction(invocation.getMethod(), targetClass, new TransactionAspectSupport.CoroutinesInvocationCallback() {
          @Nullable
          public Object proceedWithInvocation() throws Throwable {
              return invocation.proceed();
          }

          public Object getTarget() {
              return invocation.getThis();
          }

          public Object[] getArguments() {
              return invocation.getArguments();
          }
      });
  }
}

 

MethodInvocation 타입의 invocation 객체는 타깃 객체를 래핑하고 있다.

 

invocation 객체를 통해 타깃 정보를 전달 받아 트랜잭션 기능을 제공하고 원래 실행하려는 타깃의 메서드를 실행할 수 있다.

 

TransactionInterceptor처럼 타깃 객체에 대한 접근을 제어하고 부가기능을 제공해주는 객체를 프록시라고 부른다.

 

프록시의 특징 중 하나는 타깃 객체를 참조하고 있는 것이다.

 

스프링의 aop는 주로 프록시 패턴으로 구현되어있다.

 

따라서 스프링에서 aop를 적용하기 위해서는 인스턴스화된 타깃이 필요하다.

 

static 메서드에는 스프링 aop 기능 적용 불가하다.

반응형