- 외부 시스템에서 데이터를 읽거나 쓸 때 애플리케이션에서 필요한 형태로 변경하기 위해 어떻게 하든 데이터 처리가 필요할 수 있다.
- 스프링 통합(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
인터페이스의 구현체를 런타임 시에 생성하라고 스프링 통합에 알려준다.@MessagingGateway
의 defaultRequestChannel
속성을 통해 해당 인터페이스의 메서드 호출로 생성된 메시지가 이 속성에 지정된 메시지 채널로 전송된다는 것을 나타낸다.@Header
애노테이션은 filename
에 전달되는 값이 메시지 페이로드가 아닌 메시지 헤더에 있다는 것을 나타낸다.
@MessagingGateway(defaultRequestChannel = "textInChannel")
interface FileWriterGateway {
fun writeToFile(
@Header(FileHeaders.FILENAME) filename: String,
data: String
)
}
- 통합 플로우 구성 방법 3가지
- XML 구성
- 자바 구성
- DSL을 사용한 자바 구성
Java로 통합 플로우 구성하기#
- 아래 코드는 다음의 통합 플로우를 구성한 것이다.
@Transformer
을 통해서 inputChannel
과 outputChannel
을 설정할 수 있다.@ServiceActivator
애노테이션을 통해서 fileWriterChannel
로부터 메시지를 받아서 FileWritingMessageHandler
의 인스턴스로 정의된 서비스에 넘겨준다.- /tmp/sia5/files 디렉토리의 파일에 쓴다.
- 파일 이름은 해당 메시지의
file_name
헤더에 지정된 것을 사용한다. setExpectReply()
메서드를 통해 해당 메서드는 서비스에서 응답 채널을 사용하지 않음을 나타낸다. (설정하지 않으면 응답 채널이 구성되지 않았다는 로그 메시지들이 나타난다.)
textInChannel
과 fileWriterChannel
이라는 빈이 없어도 채널은 자동 생성된다.- 각 채널의 구성 방법을 더 제어하고 싶으면 다음과 같이 별도의 빈으로 구성할 수 있다.

@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()
메서드 인자로 MessageHandler
와 GenericHandler
를 인자로 받을 수 있다.

게이트웨이#
- 게이트웨이는 애플리케이션이 통합 플로우로 데이터를 제출하고 선택적으로 플로의 처리 결과인 응답을 받을 수 있는 수단이다.
- 인터페이스를 선언해서 게이트웨이를 만들 수 있다.
@MessagingGateway
- 인터페이스를 구현할 필요는 없다.
- 아래 예시에서는
uppercase()
가 호출되면 지정된 문자열이 통합 플로우의 inChannel로 전달된다. 그리고 통합 플로우를 지나서 데이터가 outChannel로 도착하면 uppercase()
메서드로부터 반환한다. 
채널 어댑터#
- 통합 플로우의 입구와 출구를 나타낸다.
- 데이터는 인바운드 채널 어댑터를 통해 통합 플로우로 들어오고, 아웃바운드 채널 어댑터를 통해 통합 플로우에서 나간다.
- Java로 구성
AtomicIneger
로 부터 numberChannel
이라는 이름의 채널로 1000ms마다 한번씩 숫자를 전달한다.
- DSL로 구성
from()
메서드를 사용
- 메시지 핸들러로 구현되는 서비스 액티베이터는 아웃바운드 채털 어댑터로 자주 사용된다.
엔드포인트 모듈#
- 스프링 통합은 다양한 외부 시스템과의 통합을 위해 채널 어댑터가 포함된 24개 이상의 엔드포인트 모듈은 제공한다.
이메일 통합 플로우 생성하기#