View

스프링 통합은 Enterprise Integration Patterns에 나오는 패턴들을 스프링 프레임워크에 구현해놓았다. Enterprise Integration Patterns은 엔터프라이즈 환경에서 사용하고 있는 다양한 분야(예. 결제, 메일, 각 부서에 필요한 서비스)의 애플리케이션을 통합, 즉 유기적으로 연결해서 효율적으로 적절하게 통합하는 방법을 여러 패턴을 통해 제시했다(고한다, 위키발..).

SI라는 말을 처음 들었을 때도 그렇고 통합, integration이라는 단어의 의미가 참 햇갈렸는데, 여기서 통합이란 일반적으로 내가 알고있던 '독립적이였던 둘 이상의 어떤 것을 하나로 합치는 것'이 아니라 '융화'의 의미로 해석하는 것이 적절한 것 같다. Enterprise Integration Pattern도 앞서 설명했다시피 엔터프라이즈 환경에서 사용하고있는 다양한 서비스를 필요에 따라 잘 융화시키기 위해 파일 전송, 데이터베이스 공유, 원격 프로시저, 메세징을 핵심 과제로 다양한 방법, 즉 패턴을 제시하고 있는 것이다.

예를들어 누군가가 회사 대표메일로 이메일을 보내면 메일을 읽고(a) 분석해서(b) 고객응대가 필요할 경우 CS팀과 홍보팀에 메일을 전달(c)하고 정규화된 양식으로 변경(c)한 다음, 해당 정보를 데이터베이스에 저장(d)하는 플로우를 구성한다고 하면, a 단계에선 메일을 읽을 수 있는 파트, b에선 메일을 분석하는 엔진으로 보내는 파트, c에선 정규화된 양식으로 변환하는 파트, 나머지 d에선 데이터베이스에 저장하는 파트로 각 모듈을 구성하고 내/외부 서비스들과 접촉하는 부분을 쉽게 구현할 수 있도록 spring integration이 제공한다고 보면 된다.

spring integration의 공식 문서에서 말하는 메인 goal은 아래와 같이 설명하고 있다.

  • 모듈화와 테스트성을 높인 loosely coupled components
  • 프레임워크 레벨에서 제공하는 비즈니스 로직과 통합 로직의 분리
  • 재사용성과 이식성을 위한 추상화
  • spring integration은 이러한 개별 모듈들의 이식성, 테스트성을 제공하기 위해서 pipes-and-filters 모델을 컨셉으로 잡았는데, 여기서 filter란 메세지를 producing, consuming할 컴포넌트를 가르키며 pipe란 메시지를 필터-컴포넌트 간 또는 컴포넌트-컴포넌트 간 전송할 수 있는 컴포넌트를 의미한다.

3개의 메인 Component

spring integration은 'pipes and filters' 모델을 구현하기 위해 3가지 핵심 개념으로 구성되어 있다.

  • Message : header, payload로 구성되어 있는, 내용을 포함하고 있는 generic wrapper. 컴포넌트 간에 이동되는 실제 데이터이다.
    Message
  • Message Channel : pipes-and-filters 모델의 pipe에 해당. 컴포넌트간의 메세지 중간 통로 역할을 함으로써 컴포넌트간 디커플링을 유지할 수 있도록 하며 interception, monitoring 포인트가 될 수 있다. 메세지 채널의 모델은 크게 두가지가 있는데, 하나의 consumer가 메세지를 받을 수 있는 point-to-point, 어려 subscriber가 메세지를 broadcast 받을 수 있는 publish-subscribe 모델이 있다. 여기서 중점적으로 봐야될 것은 두 가지 모델이 있지만 메세지 채널의 다른 하나의 중요 기능은 메세지 버퍼 역할을 할 수 있는 큐로써 동작한다는 것이다.
    Mssage Channel
  • Message Endpoint : spring integration 상에서 채널을 통해서 메세지를 받고 소비하는 주체이다. 여기서 말하는 엔드포인트란 spring integration이 구성할 파이프라인의 최종 끝단이 아니라, 파이프라인의 중간에서 메세지를 변경하거나 필터링하거나 또는 여러 다른 채널로 라우팅하는 요소이다.

Message Endpoint

spring integration에서 일련의 작업들을 정의한 플로우를 integration flow라고 하는데 이 플로우가 message endpoint로 구성되어 있다. 엔드포인트는 작업 타입에 따라 크게 그 종류를 나눌 수 있는데 Transformer(변형), Filter(필터링), Router(메세지를 특정 채널로 전송), Splitter(메세지를 분리하여 여러 채널로 전송), Aggregator(splitter의 반대), Service Activator(메세지로 특정 작업을 수행할 수 있는 핸들러를 붙일 수 있는 엔드포인트), Channel Adapter(외부 시스템과 입출력이 가능)가 있다.

Getting Started

spring integration은 mail, message queue, file 등 다양한 외부 서비스들과 연결할 수 있는 수많은 라이브러리를 제공하지만 본 예지는 간단한 file read/write로 어떤 방식으로 spring integration이 구동되는지 살펴본다.

  • 시나리오
    1. 특정 디렉토리에 티켓(파일 이름이 ticket.txt으로 끝나는 텍스트 파일)을 생성한다.
    2. 파일을 변형한다 : 여기선 예제로 파일의 내용을 대문자로 바꾸고
    3. 다른 특정 디렉토리에 변형된 파일 제목에 timestamp를 붙이고 이전 티켓 파일을 삭제한다.
@Bean
@InboundChannelAdapter(value = "fileChannel", poller = @Poller(fixedDelay = "3000"))
public MessageSource<File> fileReadingMessageSource() {
  FileReadingMessageSource sourceReader = new FileReadingMessageSource();
  sourceReader.setDirectory(new File(INPUT_DIR));
  sourceReader.setFilter(new SimplePatternFileListFilter(FILE_PATTERN));
  return sourceReader;
}

특정 input 폴더를 read용 디렉토리로 지정하고 확인할 딜레이, 파일을 읽을 경우 보낼 채널 이름 fileChannel을 지정하였다.

@Bean
@Transformer(
  inputChannel = "fileChannel"
  , outputChannel = "fileWriterChannel")
public GenericTransformer<File, File> upperCaseTransformer() {
  return file -> {
     // 대충 파일을 읽어서 대문자로 바꾸고 기존 파일을 지우는 부분
    return newFile;
  };
}

애노테이션으로 해당 transformer의 입력 채널과 출력 채널을 정의하였다. 위에서 선언했던 InboundChannelAdapter의 정보가 inputChannel로 연결된다.

@Bean
@ServiceActivator(inputChannel = "fileWriterChannel")
public MessageHandler fileWritingMessageHandler() {
  FileWritingMessageHandler handler =
    new FileWritingMessageHandler(new File(OUTPUT_DIR));
  handler.setFileExistsMode(FileExistsMode.REPLACE);
  handler.setFileNameGenerator(message -> {
    return LocalDateTime.now().toString().replace(":", "") + "__" + message.getPayload();
  });
  handler.setExpectReply(false);
  return handler;
}

Message를 핸들링할 수 있는 Adapter로 앞서 선언했던 출력 채널을 inputChannel로 설정해서 메세지를 받을 수 있도록 하였고 파일 이름에 timestamp를 추가하였다.

위의 경우 모두 애노테이션기반으로 설정했고 아래와 같이 코드로 설정한 부분을 보면 전체적인 파이프라인 구조를 파악할 수 있다.

@Bean
public IntegrationFlow flow() {
  return IntegrationFlows
    .from(fileReadingMessageSource())
    .transform(upperCaseTransformer())
    .handle(fileWritingMessageHandler())
    .get();
  }
}

전체 코드는 spring-integration/spring_integration_getting_started 참고

Conclusion

spring integration은 엔터프라이즈 환경에서 여러 서비스, 애플리케이션 간 발생할 수 있는 작업 플로우를 파이프라인 형식으로 구성할 수 있으며 주어진 시나리오를 커버할 수 있는 적절한 컴포넌트를 적절한 단계에 사용함으로써 재사용성과 테스트성을 높인 프레임워크이다.

Reference

Share Link
reply