토비의 스프링 Vol.2 - 7장 스프링의 기타 기술과 효과적인 학습 방법

2020-11-04

스프링의 기타 기술과 효과적인 학습 방법

스프링은 자바 엔터프라이즈 개발에 사용되는 다양한 기술 영역을 지원하는 방대한 기능을 제공한다. 스프링의 자체 기술은 물론이고, 표준 기술과 주요 오픈소스 기술에 대한 지원 기능도 제공된다.


스프링 기술과 API를 효과적으로 학습하는 방법

스프링은 일관된 방식으로 개발된 프레임워크다. 모든 코드와 API가 동일한 원리에 기반을 두고 만들어져 있다. 바로 DI다.

빈으로 등록되는 스프링 클래스와 DI

어떤 오브젝트가 빈으로 사용된다의 의미
  1. 다른 빈에 의해 DI 돼서 사용되는 서비스다.
  2. 다른 빈이나 정보에 의존하고 있다.
ex) DataSourceTransactionManager

스프링이 빈으로 등록하는 방법을 통해 제공하는 기능인 DataSourceTransactionManager를 제대로 공부하기

구현 인터페이스 분석
구현 인터페이스가 무엇인지 파악하는 방법
  1. 스프링 API 문서에서 DataSourceTransactionManager 항목을 찾아본다.
  2. IDE에서 클래스의 타입 계층구조를 보는 기능을 사용한다.
    프로퍼티 분석

    빈의 클래스의 설정 방법과 학장 방법을 살펴봐야한다. 빈 클래스의 활용 방법, 확장 방법, 디폴트와 다른 동작을 가능하게 하는 방법에 관한 지식은 대부분 클래스의 프로퍼티를 살펴보면 알 수 있다.

    DI/확장 포인트 분석

    DataSourceTransactionManager가 DataSource라는 인터페이스를 통해 다른 빈을 참조한다는 건 DI의 모든 기능을 활용할 수 있는 확장 포인트를 갖고 있다고 이해할 수 있다.

스프링이 실제 DB 커넥션을 지원하는 DataSource와 DataSource 타입의 프로퍼티를 갖는 빈 사이에 데코레이터 패턴이나 프록시 패턴을 이용해 부가기능 또는 접근제어 기능을 제공해주는 빈을 만들려고 할 때 유용한 기반 클래스 DelegatingDataSource를 제공해준다.

  • 스프링이 제공하는 DelegatingDataSource를 기반으로 하는 다양한 데코레이터 또는 프록시
    • LazyConnectionDataSourceProxy
      • LazyConnectionDataSourceProxy는 트랜잭션 매니저와 실제 DataSource 사이에서 DB 커넥션 생성을 최대한 지연시켜주는 기능을 제공해준다.
    • AbstractRoutingDataSource
      • AbstractRoutingDataSource는 추상 클래스이므로 상속을 통해 기능을 추가하고 사용해야 한다. 스프링은 AbstractRoutingDataSource의 서브클래스로 IsolationLevelDataSourceRouter를 제공하고 있다.
      • AbstractRoutingDataSource는 다중 DataSource에 대한 라우팅을 제공하는 프록시다. 여러 개의 DataSource가 존재하지만 DAO나 트랜잭션 매니저에는 하나의 DataSource만 존재하는 것처럼 사용하도록 만들어야 할 때 사용한다.

IoC 컨테이너와 DI

DispatcherServlet과 마찬가지로 스프링 IoC/DI 컨테이너, 즉 애플리케이션 컨텍스트도 그 자체로는 빈은 아니지만 DI를 받는다. 직접 <property>를 사용해 설정해줄 수는 없지만 애플리케이션 컨텍스트는 자신의 확장 포인트 인터페이스를 구현한 빈을 찾아서 스스로 DI 한다. 엄밀히 말하면 DL 이라고 볼 수 있다.

BeanPostProcessor와 BeanFactoryPostProcessor

가장 많이 사용되는 IoC 컨테이너의 확장 포인트는 바로 빈 후처리기와 빈 팩토리 후처리기다. 빈 후처리기는 BeanPostProcessor 인터페이스로 정의되어 있고 실제 빈 오브젝트를 생성하는 시점에서 사용된다. 빈 팩토리 후처리기는 BeanFactoryPostProcessor 인터페이스를 사용하고 빈 설정 메타데이터가 준비된 시점에서 사용된다.

이 두 가지 확장 포인트 외에도 컨테이너 레벨의 이벤트와 리스너 구현, XML의 프로퍼티 값에 대한 프로퍼티 에디터나 컨버전 서비스, 국제화 지원과 같은 IoC의 확장 포인트도 사용할 수 있다.


SpEL

자바에는 다양한 종류의 표현식 언어가 존재한다. JSP에서 사용되는 JSP EL을 비롯해서, 오픈소스 프로젝트의 제품인 MVEL이나, JBoss가 제공하는 JBoss EL도 있다. 스프링은 지금까지 다양한 표준 EL이나 오픈소스 EL을 사용해왔는데, 스프링 3.0부터는 좀 더 강력하고 유연하면서도 스프링에 잘 접목돼서 사용할 수 있는 스프링 전용 EL을 직접 제공하기 시작했다. 스프링 EL은 보통 SpEL이라는 약자로 표기한다.

SpEL 사용 방법

SpEL을 코드에서 사용하려면 기본적으로 두 가지 인터페이스를 활용해야 한다.

  1. ExpressionParser
    • 표현식을 파싱하는 기능이 정의되어 있는 인터페이스다.
  2. Expression
    • 파서에 의해 해석된 표현식 정보를 가진 오브젝트의 타입이다.

      한 번 파싱한 Expression 오브젝트를 재사용할 수 있기 때문에 파서로부터 바로 파싱을 해서 결과 값을 받지 않고, 일단 Expression 타입의 오브젝트로 만든 다음 값을 가져온다.


OXM

스프링 OXM은 서비스 추상화가 적용된 기술이다. 트랜잭션이나 OXM 같은 서비스 추상화는 먼저 스프링이 제공하는 인터페이스의 사용 방법을 익히고, 각 기술별로 제공되는 어댑터 빈의 설정 방법을 살펴봐야 한다. 서비스 추상화를 통해 OXM 구현 기술이 바뀌더라도 이를 사용하는 코드에는 영향을 주지 않으므로, 익숙한 구현 기술 하나를 선택해서 집중적으로 분석하고 실제 코드에 적용해보면서 사용 방법을 익히는 것이 중요하다.

스프링에서 OXM 추상화를 적용하는 방법은 코드에서 직접 마샬러 빈을 가져와 Marshaller 인터페이스를 이용해서 XML과 오브젝트 사이의 변경 작업을 수행하는 방법과 웹 환경에서 XML 뷰나 XML 메시지 컨버터를 만들 때 활용하는 방법이 있다.

Marshaller/Unmarshaller 인터페이스

  • Marshaller는 오브젝트를 XML로 변환하는 기능을 추상화한 인터페이스다.
  • Unmarshaller는 XML 소스로부터 오브젝트를 변환해주는 기능을 정의한 인터페이스다.

    OXM 기술 어댑터 클래스

  • Marshaller와 Unmarshaller 추상 인터페이스를 구현해서 각 OXM 기술과 연결해주는 어댑터 클래스는 총 다섯가지가 있다. 기본 지원 기술은 Castor, JAXB, XMLBeans, JiBX, XStream이다. 각 기술 이름 뒤에 Marshaller를 붙이면 지원 클래스다.
  • 하나의 어댑터 클래스가 Marshaller와 Unmarshaller를 모두 구현하고 있다. 비록 같은 오브젝트이지만 각각 접근하는 인터페이스가 다르고 용도가 다르므로 별개의 프로퍼티로 주입받는 것이 좋다.

리모팅과 웹 서비스, EJB

자바 엔터프라이즈에는 다양한 종류의 리모팅 기술이 존재한다. 당연히 스프링에서도 이러한 리모팅 기술을 지원한다.

  • 스프링이 직접 지원하는 리모팅 기술
    • RMI
    • 스프링 HTTP Invoker
    • Hessian
    • Burlap
    • JAX-RPC
    • JAX-WS
    • JMS
    • RESTful

      리모팅은 원격 시스템과 스프링 애플리케이션이 연동해서 동작하게 해주는 기술이다. 스프링 애플리케이션이 클라이언트 시스템에게 원격 서비스를 제공하는 것과 다른 원격 시스템의 서비스를 이용하는 것, 두 가지로 구분할 수 있다.

      익스포터와 프록시

      익스포터
  • 서비스를 제공할 때는 원격 요청을 받아서 특정 인터페이스를 구현한 서비스 빈에게 요청을 전달해주는 빈을 이용해야 하는데, 이를 익스포터: exporter라고 한다. 이 익스포터가 원격 클라이언트의 오브젝트인 것처럼 서비스를 제공하는 빈을 인터페이스를 통해 호출해주도록 구성된다.
  • 보통 익스포터는 원격 요청을 처리하는 서블릿 등을 통해 HTTP 요청을 전달받고 이를 해석한 후에 미리 설정을 통해서 등록된 인터페이스를 이용해 서비스 빈을 호출한다.
  • 익스포터 빈은 대부분 서비스 빈의 존재와 구현 인터페이스를 모르기 때문에 서비스 인터페이스를 함께 제공해줘서 어떤 식으로 메소드를 호출해야 하는 지 파악할 수 있게 해줘야 한다.
    프록시
  • 원격 시스템에 있는 오브젝트를 대신해서 클라이언트 오브젝트의 호출을 받고, 이를 원격 오브젝트에 전송해서 결과를 가져와 클라이언트 오브젝트에게 돌려주는 역할을 맡은 빈 오브젝트를 프록시: proxy라고 부른다. 전형적인 원격 프록시 패턴이 적용된 예라고 볼 수 있다.
  • 이 프록시는 원격 서비스 내용이 정의된 인터페이스를 구현하고 있어야 한다. 그래서 이를 사용하는 빈 입장에서는 원격 호출이 일어나는지를 신경 쓰지 않고, 같은 컨테이너 안의 빈 오브젝트를 사용하듯 쓰는 것이다. 모든 리모팅 호출의 공통적인 동작원리다.

    모든 스프링의 리모팅 지원 기능은 익스포터와 프록시 방식을 사용한다.

    RESTful 서비스 템플릿

  • RESTful 클라이언트 기능은 여타 리모팅 기술과 사용 방법이 다르다. RESTful 서비스는 스프링 MVC를 통해 구현하므로, 리모팅에서는 원격 RESTful 서비스 사이트를 이용해 결과를 가져오는 클라이언트 기능만 제공한다.
  • RESTful 서비스 사이트를 이용할 때는 서비스 인터페이스 타입의 프록시 대신 템플릿/콜백 방식의 템플릿을 이용한다.
  • RESTful 클라이언트는 HTTP 메소드 GET, POST, PUST, DELETE, HEAD, OPTIONS를 모두 지원한다. 결과는 문자열로 그대로 받을 수도 있고, 메시지 컨버터를 이용해 오브젝트로 변환할 수도 있다.

    RESTful 스타일의 서비스는 직렬화를 이용하는 프로토콜이나 복잡한 요청 메시지를 작성하지 않고도 손쉽게 URL과 파라미터를 이용해 서비스에 접근할 수 있기 때문에 그만큼 사용 방법도 간단한다.

    EJB 서비스 이용

    EJB 2나 EJB 3로 만들어진 컴포넌트가 있고, EJB 컨테이너에서 서비스되고 있다면 이를 스프링에서 사용할 수 있다. 스프링은 EJB도 리모팅의 프록시와 비슷한 방식으로 접근하도록 해준다. 로컬 세션빈과 리모트 세션빈 모두 동일한 방법으로 사용 가능하다.


태스크 실행과 스케줄링

TaskExecutor 서비스 추상화

java.lang.Runnable은 run()이라는 단순한 메소드를 가진 인터페이스로서 독립적인 스레드에 의해 실행되도록 의도된 오브젝트를 만들 때 주로 사용된다. 이렇게 독립적인 스레드 안에서 동작하도록 만들어진 오브젝트를 독립적으로 실행 가능한 작업이라는 의미로 태스크: task라고 부른다. 스프링은 이런 태스크를 다양한 방법으로 실행하도록 만들어진 오브젝트 특징을 추상화한 태스크 실행기: TaskExecutor라는 인터페이스를 제공한다.

  • 스프링이 제공하는 주요한 TaskExecutor 구현 기술과 클래스
    • ThreadPoolExecutor
    • SimpleThreadPoolTaskExecutor
    • WorkManagerTaskExecutor

      스프링 애플리케이션이 동작하는 자바 엔터프라이즈 환경은 제한된 크기의 스레드풀을 사용한다고 하더라도 비동기 작업을 함부로 적용하는 건 위험하다.

      TaskScheduler

      자바 엔터프라이즈 환경에서 사용되는 태스크는 일정한 간격 또는 시간 기준에 따라 실행되는 스케줄링 방식으로 동작하는 경우가 대부분이다. 스프링은 TaskExecutor와 마찬가지로 서비스 추상화 기법을 이용해서 스케줄링 기술에 독립적인 사용이 가능한 추상화 서비스 인터페이스인 TaskScheduler를 제공한다. TaskScheduler 인터페이스는 주어진 태스크를 조건에 따라 실행하거나 반복하는 작업을 수행한다.

  • TaskScheduler의 주요 구현 클래스와 사용 기술
    • ThreadPoolTaskScheduler
    • TimerManagerTaskScheduler

      task 네임스페이스

      스프링은 task 스키마에 정의된 전용 태그를 통해 태스크 실행기와 스케줄러를 간편하게 등록할 수 있는 방법을 제공한다.

  • <task:executor>
    • <task:executor>는 ThreadPoolTaskExecutor 타입의 TaskExecutor 빈을 등록해준다.
  • <task:scheduler>
    • TaskScheduler 타입의 ThreadPoolTaskScheduler 빈을 등록해준다.
  • <task:scheduled-tasks><task:scheduled>
    • 스케줄러를 적용하려면 태스크마다 Runnable을 구현한 클래스를 만들어 빈으로 등록해야하고, 스케줄을 등록해주는 코드를 작성하고, 자동으로 스케줄 등록 기능이 실행되도록 만들어야 하는 등의 번거로움이 있다. 스프링에서는 <task:sheduled-task> 태그를 이용해 일반 빈의 메소드를 태스크로 활용하는 스케줄을 등록할 수 있다.
    • 스케줄은 <task:scheduled> 태그를 이용해 등록한다.

      애노테이션을 이용한 스케줄링과 비동기 태스크 실행

      @Scheduled

      @Scheduled는 XML 설정 대신 태스크 역할을 맡을 메소드에 직접 스케줄 정보를 애노테이션을 통해 부여해서 스케줄이 적용되게 해준다.

  • @Scheduled는 세 가지 종류의 트리거 설정을 지원한다.
    1. fixedDelay
      • 이전 작업이 끝난 시점부터 일정 시간이 지난 후에 동작하도록 설정한다.
    2. fixedRate
      • 밀리초로 설정된 일정한 시간 간격으로 메소드가 실행되게 해준다. fixedDelay와 다르게 이전 메소드가 호출된 시점으로부터의 시간이다.
    3. cron
      • cron 포맷을 사용해 스케줄을 지정할 수 있다. 가장 유연하게 스케줄을 지정할 수 있는 방법이다.

        @Scheduled가 부여되는 메소드는 파라미터를 가질 수 없으며 반드시 void형의 리턴타입이어야 한다.

        @Async

        @Async가 부여된 메소드는 자동으로 비동기 방식으로 실행된다. TaskExecutor를 코드로 사용하지 않고도 비동기 실행이 가능하게 해준다. 비동기로 동작하기 때문에 메소드 내의 작업이 오랜 시간이 걸리더라도 메소드를 호출하면 바로 리턴된다. 메소드는 별도의 스레드에서 동작하게 된다. 리턴 타입은 void 또는 Future 타입이어야 한다. 메소드는 다른 코드에 의해 직접 호출되므로 파라미터는 가질 수 있다.


캐시 추상화(스프링 3.1)

스프링 3.1은 빈의 메소드에 캐시 서비스를 적용할 수 있는 기능을 제공한다. 캐시 서비스는 트랜잭션과 마찬가지로 AOP를 이용해 메소드 실행 과정에 투명하게 적용된다. 또한 캐시 서비스 구현 기술에 종속되지 않도록 추상화 서비스를 제공하기 때문에 환경이 바뀌거나 적용할 기술을 변경해서 캐시 서비스의 종류가 달라지더라도 애플리케이션 코드에 영향을 주지 않는다.

  • 캐시는 기본적으로 성능의 향상을 위해 사용한다. 캐시: cache는 임시 저장소라는 뜻이다. 복잡한 계산이나 DB 작업, 원격 요청의 처리 결과 등을 임시 저장소인 캐시에 저장해뒀다가 동일한 요청이 들어오면 복잡한 작업을 수행해서 결과를 만드는 대신 캐시에 보관해뒀던 기존 결과를 바로 돌려주는 방식이다.
  • 캐시는 반복적으로 동일한 결과가 돌아오는 작업에만 이용할 수 있다.
  • 캐시를 사용할 때는 캐시에 저장해둔 내용이 바뀌는 상황을 잘 파악해야 한다.(데이터 불일치 문제)

    애노테이션을 이용한 캐시 속성 부여

    스프링의 캐시 서비스 추상화는 AOP를 이용한다. 캐시 기능을 담은 어드바이스는 스프링이 제공한다.

    @Cacheable

    캐시 서비스는 보통 메소드 단위로 지정한다. 캐시에 저장할 내용과 캐시 속성 정보로 메소드의 리턴 값과 메소드 파라미터를 사용하기 때문이다. 캐시에 저장되는 정보를 구분하기 위해 캐시 이름을 사용한다. @Cacheable("product") 하나의 캐시에는 키가 다른 여러 개의 오브젝트를 넣을 수 있다. @Cacheable(value="product", key="#productNo") key 엘리먼트는 SpEL을 이용해 키 값을 지정한다. 파라미터 값이 특정 조건을 만족시키는 경우에만 캐시를 적용하고, 그 외의 경우에는 캐시 서비스를 적용하지 않아야 한다면 condition 엘리먼드를 이용하면 된다. @Cacheable(value="user", condition="#user.type=='ADMIN'")

    @CacheEvict와 @CachePut
  • 적절한 시점에 제거돼야 한다. 캐시는 캐시의 제거에도 캐시 서비스 AOP 기능이 적용된 메소드를 이용한다. 캐시 제거에 사용될 메소드에 간단히 @CacheEvict 애노테이션을 붙여주면 된다. @CacheEvict(value="bestProduct") @CacheEvict는 기본적으로 메소드의 키 값에 해당하는 캐시만 제거한다. @CacheEvict(value="product", key="#product.productNo") 캐시에 저장된 값을 모두 제거할 필요가 있다면 allEntries 엘리먼트를 true로 지정해주면 된다. @CacheEvict(value="product", allEntries=true)
  • 드물지만 메소드를 캐시에 값을 저장하는 용도로만 사용하기 위해 @CachePut을 이용한다. 메소드 실행 결과를 캐시에 저장하지만, 저장된 캐시의 내용을 사용하지는 않고 항상 메소드를 실행한다. 한 번에 캐시에 많은 정보를 저장해두는 작업이나, 다른 사용자가 참고할 정보를 생성하는 용도로만 사용되는 메소드에 이용할 수 있다.
    애노테이션을 이용한 캐시 기능 설정
  • XML 설정
    • <cache:annotation-driven />
  • @Configuration 클래스를 사용
    • @EnableCaching 애노테이션을 추가해주기만 하면 된다.
      @Configuration
      @EnableCaching
      public Class AppConfig {
      

      캐시 매니저

  • 스프링의 캐시 서비스는 AOP를 이용해 애플리케이션 코드를 수정하지 않고도 캐시 부가기능을 메소드에 적용할 수 있게 해준다. 동시에 캐시 기술의 종류와 상관없이 추상화된 스프링 캐시 API를 이용할 수 있게 해주는 서비스 추상화를 제공한다.
  • 캐시 추상화에서는 적용할 캐시 기술을 지원하는 캐시 매니저를 빈으로 등록해줘야 한다.
  • 캐시 추상화 API인 캐시 매니저는 org.springframework.cache 패키지의 CacheManager 인터페이스를 구현해서 만든다. 스프링 3.1은 기본적으로 다섯 가지 CacheManager 구현 클래스를 제공한다.
    • ConcurrentMapCacheManager
    • SimpleCacheManager
    • EhCacheCacheManager
    • CompositeCacheManager, NoOpCacheManager

@Enable 애노테이션을 이용한 빈 설정정보 모듈화

모듈화의 가장 큰 이유는 효과적인 재사용이다. 빈 설정정보를 모듈화하는 이유는 반복적으로 사용되는 복잡한 빈 설정을 독립시켜서 편리하게 재사용할 필요가 있기 때문이다. 모듈화를 할 때 중요한 것은 모듈이 시스템의 다른 부분과 적절히 분리되어 있는 것이다. 모듈을 추가하고 나서 모듈 외부의 변화가 생겼다고 모듈 내부 코드를 수정해야 한다거나, 반대로 모듈 내부 구현이 수정됐다고 모듈을 사용하는 외부 코드도 같이 수정해야 한다면 모듈화가 잘 된 것이 아니다. 모듈은 자기 책임을 명확히 갖고 있고, 간결한 인터페이스를 통해서만 연결돼야 한다.

스프링의 XML 전용 태그는 이런 모듈화 조건을 잘 충족한다.

XML 전용 태그를 만드는 데는 손쉽게 접근하기도 힘들고 상당한 시간을 들여야 한다. 반면에 자바 코드를 이용한 빈 설정 방식은 재사용 가능한 설정정보 모듈로 만들기가 훨씬 쉽다. 스프링 3.1은 자바 코드 설정을 모듈화하는 데 필요한 다양한 기법을 제공하고, 스프링 스스로도 @Enable로 시작하는 다양한 설정용 애노테이션을 만들어서 제공하고 있다. @Enable 설정 애노테이션과 자바 코드를 이용한 설정 방법을 이용하면 XML 전용 태그보다 훨씬 간단한 방법으로 설정정보 재사용 모듈을 만들어낼 수가 있다.

@Import와 @Configuration 상속

@Import를 이용한 단순 재사용

@Configuration 클래스로 만든 설정정보를 추가할 때는 @Import를 이용하면 된다. 재사용하고 싶은 빈 설정을 @Configuration 클래스로 미리 만들어두고 이를 @Import로 가져오는 방식이다. 하지만 이 방법은 설정정보를 바꿀 수가 없다.

@Configuration 클래스 상속과 오버라이딩을 이용한 확장 방법

@Configuration 클래스의 빈 정보는 @Bean 메소드에 담겨 있다. 따라서 @Bean 메소드를 상속받으면 슈퍼클래스의 빈 설정정보도 함께 상속받게 된다. 상속을 이용해 설정정보를 가져왔을 때의 장점은 메소드 오버라이딩을 이용해 슈퍼 클래스의 @Bean 메소드를 재정의 할 수 있다는 것이다. 하지만 상속은 한 개의 클래스만 상속할 수 있다는 단점이 있다.

@Enable 전용 애노테이션과 ImportAware

@Enable로 시작하는 애노테이션은 모듈화된 빈 설정정보를 추가하면서 엘리먼트 값을 이용해 옵션 정보를 제공할 수 있게 해준다.

ImportAware 인터페이스를 이용한 옵션 지정

@Configuration 클래스가 자신을 @Import하는 애노테이션의 엘리먼트 값을 참조하려면 애노테이션 정보를 제공받기 위해 ImportAware 인터페이스를 구현해야 한다.

애노테이션을 정의해서 설정정보를 추가하면서 엘리먼트 값을 이용해 옵션 정보를 제공하여 빈 설정정보를 가져와 필요한 설정 항목을 자유롭게 변경할 수 있다.

빈 설정자

재사용하려는 빈 설정정보의 양이 많거나 확장 방법이 다양하고 복잡할 경우에는 애노테이션의 엘리먼트만으로는 충분하지 않을 수 있다. 애노테이션의 엘리먼트에는 고정된 값만 넣을 수 있는데, 때로는 코드를 이용해 복잡한 설정을 추가해야 할 필요가 있다. 이럴 때는 @Enable 애노테이션과 함께 자바 코드를 이용한 설정정보의 확장 포인트가 필요한데 이를 빈 설정자라고 한다.

빈 설정자는 인터페이스로 정의된다. 보통 @Configuration 클래스가 빈 설정자를 구현해서 사용한다. 빈 설정자는 @Configuration 클래스 상속과 오버라이딩을 통한 확장 방법처럼 자바 코드를 이용해 빈 설정 코드를 추가할 수 있다는 장점이 있다. 반면에 상속과 달리 여러 개의 빈 설정자를 동시에 구현해서 적용할 수 있고, @Bean 메소드를 직접 노출하지 않기 때문에 오버라이딩을 잘못 사용해서 설정정보를 엉망으로 만들 위험은 없다.

따라서 자바 코드를 이용한 빈 설정정보 확장이 필요한 경우 @Configuration 상속보다는 @Enable 애노테이션과 빈 설정자를 활용하는 방법이 낫다.

ImportSelector와 ImportBeanDefinitionRegistrar

옵션에 따라 빈의 종류나 구성이 아예 바뀌어야 한다면 @Import 기반의 방법으로는 충분하지 않다. @Enable 애노테이션은 @Import를 기반으로 하고 있고, @Import는 가져와 사용할 @Configuration 클래스를 직접 지정하고 있기 때문에 @Configuration 클래스의 종류가 고정돼야 할 테지만 @Enable 애노테이션을 이용해서 옵션에 따라 다른 @Configuration 클래스를 사용할 수 있는 이유는 스프링이 ImportSelector를 제공하기 때문이다. ImportSelector는 애노테이션 메타정보를 제공받아서 스트링 배열을 리턴하는 selectImport() 메소드를 갖고 있다. ImportSelector는 @Eable 애노테이션의 메타정보를 이용해 @Import에 적용할 @Configuration 클래스를 결정해주는 오브젝트다.

빈 설정정보를 @Configuration 단위로 만들어두고 선택하는 수준을 넘어서 옵션에 따라 복잡한 빈 설정 조합을 만들어내야 하는 경우라면 ImportBeanDefinitionRegistrar를 이용할 수 있다. ImportBeanDefinitionRegistrar는 XML 전용 태그를 만드는 방법과 비슷하게 빈 메타정보를 생성하는 코드를 직접 작성해야 한다.


정리

스프링은 기능이 매우 방대한 프레임워크다. 누구도 스프링의 모든 기능을 다 알고 있을 수는 없기 때문에 필요할 때마다 관련 문서를 찾아보는 습관을 들이는 게 중요하다. 레퍼런스 문서의 항목을 참고해보고 핵심 클래스를 중심으로 API 문서를 찾아보면서 사용방법을 익히자. 학습 테스트를 만들어가면서 공부한다면 새로운 기능의 사용 방법을 빠르게 익힐 수 있다.

출처 : https://github.com/Masssidev/toby-vol2