• 외부 시스템에서 데이터를 읽거나 쓸 때 애플리케이션에서 필요한 형태로 변경하기 위해 어떻게 하든 데이터 처리가 필요할 수 있다.
  • 스프링 통합(Spring Integration)은 Enterprise Integration Patterns라는 책에서 보여준 대부분의 통합 패턴을 사용할 수 있게 구현한 것이다.
  • 각 통합 패턴은 하나의 컴포넌트로 구현되며, 이것을 통해서 파이프라인으로 메시지가 데이터를 운반한다.

간단한 통합 플로우 선언하기

  • 애플리케이션은 통합 플로우를 통해서 외부 리소스나 애플리케이션 자체에 데이터를 수신 또는 전송할 수 있다.
  • 스프링 통합은 통합 플로우를 생성할 수 있게 해준다.
  • 애플리케이션이 통합할 수 있는 그런 리소스 중 하나가 파일 시스템이다. 이에 따라 스프링 통합의 많은 컴포넌트 중에 파일을 읽거나 쓰는 채널 어댑터가 있다.
  • 의존성 추가
    • 첫 번째 의존성: 스프링 통합의 스프링 부트 스타터.
    • 두 번째 의존성: 스프링 통합의 파일 엔드포인트 모듈. 외부 시스템 통합에 사용되는 24개 이상의 엔드포인트 모듈 중 하나다.
<dependency>  
   <groupId>org.springframework.boot</groupId>  
   <artifactId>spring-boot-starter-integration</artifactId>  
</dependency>  
<dependency>  
   <groupId>org.springframework.integration</groupId>  
   <artifactId>spring-integration-file</artifactId>  
</dependency>
  • 파일에 데이터를 쓸 수 있도록 애플리케이션에서 통합 플로우로 데이터를 전송하는 게이트웨이를 생성해야 한다.
    • @MessagingGateway 애노테이션을 통해 FileWriterGateway 인터페이스의 구현체를 런타임 시에 생성하라고 스프링 통합에 알려준다.
    • @MessagingGatewaydefaultRequestChannel 속성을 통해 해당 인터페이스의 메서드 호출로 생성된 메시지가 이 속성에 지정된 메시지 채널로 전송된다는 것을 나타낸다.
    • @Header 애노테이션은 filename에 전달되는 값이 메시지 페이로드가 아닌 메시지 헤더에 있다는 것을 나타낸다.
@MessagingGateway(defaultRequestChannel = "textInChannel")  
interface FileWriterGateway {  
   fun writeToFile(  
      @Header(FileHeaders.FILENAME) filename: String,  
      data: String  
   )  
}
  • 통합 플로우 구성 방법 3가지
    • XML 구성
    • 자바 구성
    • DSL을 사용한 자바 구성

Java로 통합 플로우 구성하기

  • 아래 코드는 다음의 통합 플로우를 구성한 것이다.
  • @Transformer을 통해서 inputChanneloutputChannel을 설정할 수 있다.
  • @ServiceActivator 애노테이션을 통해서 fileWriterChannel로부터 메시지를 받아서 FileWritingMessageHandler의 인스턴스로 정의된 서비스에 넘겨준다.
    • /tmp/sia5/files 디렉토리의 파일에 쓴다.
    • 파일 이름은 해당 메시지의 file_name 헤더에 지정된 것을 사용한다.
    • setExpectReply() 메서드를 통해 해당 메서드는 서비스에서 응답 채널을 사용하지 않음을 나타낸다. (설정하지 않으면 응답 채널이 구성되지 않았다는 로그 메시지들이 나타난다.)
  • textInChannelfileWriterChannel이라는 빈이 없어도 채널은 자동 생성된다.
    • 각 채널의 구성 방법을 더 제어하고 싶으면 다음과 같이 별도의 빈으로 구성할 수 있다.
@Configuration  
class FileWriterIntegrationConfig {  
  
   @Transformer(inputChannel = "textInChannel", outputChannel = "fileWriterChannel")  
   @Bean  
   fun upperCaseTransformer(): GenericTransformer<String, String> {  
      return GenericTransformer { text -> text.uppercase() }
   }  
  
   @ServiceActivator(inputChannel = "fileWriterChannel")  
   @Bean  
   fun fileWriter(): FileWritingMessageHandler {  
      val handler = FileWritingMessageHandler(File("/tmp/sia5/files"))  
      handler.setExpectReply(false)  
      handler.setFileExistsMode(FileExistsMode.APPEND)  
      handler.setAppendNewLine(true)  
      return handler  
   }  
}

스프링 통합의 DSL 구성 사용하기

  • 각 컴포넌트를 별도의 빈으로 선언하지 않고 전체 플로우를 하나의 빈으로 선언한다.
    • MessageChannel.direct()로 메시지를 수신할 채널의 이름을 구성한다.
    • transform()으로 대문자 변환기를 구성한다.
    • Files 타입으로부터 파일 아웃바운드 채널 어댑터를 구성한다.
    • 자바 구성과 마찬가지로 채널 빈을 따로 선언할 필요없지만, 필요하다면 등록할 수 있다.
@Configuration  
class FileWriterIntegrationConfig {  
  
   @Bean  
   fun fileWriterFlow(): IntegrationFlow {  
      return IntegrationFlows  
         .from(MessageChannels.direct("textInChannel"))  
         .transform<String, String> { t -> t.uppercase() }  
         .handle(  
            Files.outboundAdapter(File("tmp/sia5/files"))  
               .fileExistsMode(FileExistsMode.APPEND)  
               .appendNewLine(true)  
         )  
         .get()  
   }  
}
  • 변환기를 아웃바운드 채널 어댑터와 연결하는 채널의 경우 이 채널을 별도로 구성할 필요가 있다면, channel 메소드를 호출해서 구성할 수 있다.

스프링 통합의 컴포넌트 살펴보기

  • 통합 플로우는 하나 이상의 컴포넌트로 구성된다.
  • 통합 플로우의 컴포넌트
    • 채널(channel): 한 요소로부터 다른 요소로 메시지를 전달한다.
    • 필터(filter): 조건에 맞는 메시지가 플로우를 통과하게 해준다.
    • 변환기(transformer): 메시지 값을 변경하거나 메시지 페이로드의 타입을 다른 타입으로 변환한다.
    • 라우터(router): 여러 채널 중 하나로 메시지를 전달하며, 대개 메시지 헤더를 기반으로 한다.
    • 분배기(splitter): 들어오는 메시지를 두 개 이상의 메시지로 분할하여, 분할된 각 메시지는 다른 채널로 전송된다.
    • 직접기(aggregator): 분배기와 상반된 것으로 별개의 채널로부터 전달되는 다수의 메시지를 하나의 메시지로 결합한다.
    • 서비스 액티베이터(service activator): 메시지를 처리하도록 자바 메서드에 메시지를 넘겨준 후 메서드의 반환값을 출력 채널로 전송한다.
    • 채널 어댑터(channel adapter): 외부 시스템에 채널을 연결한다. 외부 시스템으로부터 입력을 받거나 쓸 수 있다.
    • 게이트웨이(gateway): 인터페이스를 통해 통합 플로우로 데이터를 전달한다.

메시지 채널

  • 스프링 통합에서 제공해주는 메시지 채널 구현체
    • PublishSubscribeChannel: 이것으로 전송되는 메시지는 하나 이상의 컨슈머로 전달된다. 컨슈머가 여럿일 때는 모든 컨슈머가 해당 메시지를 수신한다.
    • QueueChannel: 이것으로 전송되는 메시지는 FIFO 방식으로 컨슈머가 가져갈 때까지 큐에 저장된다. 컨슈머가 여럿일 때는 그중 하나의 컨슈머만 해당 메시지를 수신한다.
    • PriorityChannel: QueueChannel과 유사하지만, FIFO 방식 대신 메시지의 priority 헤더를 기반으로 컨슈머가 메시지를 가져간다.
    • RendezvousChannel: QueueChannel과 유사하지만, 컨슈머가 메시지를 수신할 때까지 메시지 전송자가 채널을 차단하는 것이 다르다.
    • DirectChannel: PublishSubscribeChannel과 유사하지만, 전송자와 동일한 스레드로 실행되는 컨슈머를 호출하여 단일 컨슈머에게 메세지를 전송한다. 이 채널은 트랜잭션을 지원한다.
    • ExecutorChannel: DirectChannel과 유사하지만, TaskExecutor를 통해서 메시지가 전송된다. 전송자와 다른 스레드에서 처리된다. 이 채널 타입은 트랜잭션을 지원하지 않는다.
    • FluxMessageChannel: 프로젝트 리액터의 플럭스를 기반으로 하는 리액트 스트림즈 퍼블리셔 채널이다.
  • 기본적으로 DirectChannel을 사용하고, 다른 구현체롤 사용하고 싶다면 별도의 빈으로 선언하면 된다.
  • QueueChannel을 사용할 때는 컨슈머가 이 채널을 폴링하도록 구성하는 것이 중요하다.
    • 아래의 서비스 액티베이터는 orderChannel 이라는 채널로부터 매 1초(1000ms)당 1번씩 읽을 메시지가 있는지 확인한다.

필터

  • Java로 구성
    • @Filter
  • DSL로 구성

변환기

  • Java로 구성
    • @Transformer
  • DSL로 구성
    • 변환기는 람다로 지정할 수 있다.

라우터

  • Java로 구성
    • @Router
  • DSL로 구성
    • route 메서드 사용

분배기

  • 분배기를 사용할 수 있는 두 가지 경우
    • 메시지 페이로드가 같은 타입의 컬렉션 항목들을 포함하여, 각 메시지 페이로드 별로 처리하고자 할 때
      • 여러 가지 종류의 제품이 있으며, 제품 리스트를 전달하는 메시지는 각각 한 종류의 제품의 페이로드를 갖는 다수의 메시지로 분할될 수 있다.
    • 연관된 정보를 함께 전달하는 하나의 메시지 페이로드는 두 개이 상의 서로 다른 타입 메시지로분할할 수 있다.
  • Java로 구성
    • @Splitter
    • 수신 페이로드의 각 부분을 추출하여 컬렉션의 요소들로 반환하는 POJO를 정의하면 된다.
    • List<LineItem>을 처리하는 대신 각 LineItem을 별도로 처리하고 싶다면, 아래와 같이 Splitter를 하나 더 선언하면 된다.
  • DSL로 구성

서비스 액티베이터

  • Java로 구성
    • @ServiceActivator
    • MessageHandler 인터페이스를 구현한 빈을 선언한다.
    • 스프링 통합은 MessageHandler를 구현한 여러 여러 클래스를 제공한다. (PayloadTypeRouter 등)
    • 받은 메시지의 데이터를 처리한 후 새로운 페이로드를 반환하는 액티베이터는 GenericHandler를 사용하면 된다.
      • GenericHandler는 페이로드는 물론이고 메시지 헤더도 받는다.
  • DSL로 구성
    • handle() 메서드 인자로 MessageHandlerGenericHandler를 인자로 받을 수 있다.

게이트웨이

  • 게이트웨이는 애플리케이션이 통합 플로우로 데이터를 제출하고 선택적으로 플로의 처리 결과인 응답을 받을 수 있는 수단이다.
  • 인터페이스를 선언해서 게이트웨이를 만들 수 있다.
    • @MessagingGateway
    • 인터페이스를 구현할 필요는 없다.
    • 아래 예시에서는 uppercase()가 호출되면 지정된 문자열이 통합 플로우의 inChannel로 전달된다. 그리고 통합 플로우를 지나서 데이터가 outChannel로 도착하면 uppercase() 메서드로부터 반환한다.

채널 어댑터

  • 통합 플로우의 입구와 출구를 나타낸다.
  • 데이터는 인바운드 채널 어댑터를 통해 통합 플로우로 들어오고, 아웃바운드 채널 어댑터를 통해 통합 플로우에서 나간다.
  • Java로 구성
    • AtomicIneger로 부터 numberChannel이라는 이름의 채널로 1000ms마다 한번씩 숫자를 전달한다.
  • DSL로 구성
    • from() 메서드를 사용
  • 메시지 핸들러로 구현되는 서비스 액티베이터는 아웃바운드 채털 어댑터로 자주 사용된다.

엔드포인트 모듈

  • 스프링 통합은 다양한 외부 시스템과의 통합을 위해 채널 어댑터가 포함된 24개 이상의 엔드포인트 모듈은 제공한다.

이메일 통합 플로우 생성하기