- 외부 시스템에서 데이터를 읽거나 쓸 때 애플리케이션에서 필요한 형태로 변경하기 위해 어떻게 하든 데이터 처리가 필요할 수 있다.
- 스프링 통합(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로 구성
@ServiceActivatorMessageHandler 인터페이스를 구현한 빈을 선언한다.
- 스프링 통합은
MessageHandler를 구현한 여러 여러 클래스를 제공한다. (PayloadTypeRouter 등) - 받은 메시지의 데이터를 처리한 후 새로운 페이로드를 반환하는 액티베이터는
GenericHandler를 사용하면 된다.GenericHandler는 페이로드는 물론이고 메시지 헤더도 받는다.
- DSL로 구성
handle() 메서드 인자로 MessageHandler와 GenericHandler를 인자로 받을 수 있다.

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