Akashic Records

Spring Integration의 실용 사례 - File, RestAPI 본문

Spring Integration for Beginners

Spring Integration의 실용 사례 - File, RestAPI

Andrew's Akashic Records 2024. 12. 20. 14:27
728x90
1. Spring Integration 소개
1.1 Spring Integration의 배경과 필요성
1.2 Spring Integration의 탄생과 역사
1.3 Spring Integration의 주요 특징
1.4 Spring Integration의 기본 개념
1.5 Spring Integration의 실용 사례

Spring Integration for Backend Developers

파일 처리 자동화

Spring Integration은 파일 시스템과의 연동을 간편하게 처리할 수 있도록 강력한 기능을 제공합니다. 파일 처리 자동화는 특정 디렉터리에서 파일을 읽어 데이터를 처리하거나, 파일을 생성 및 저장하는 등의 작업을 자동화하는 데 유용합니다.

 

Gradle 의존성 추가

implementation 'org.springframework.boot:spring-boot-starter-integration'
implementation 'org.springframework.integration:spring-integration-file'

1. 파일 처리 자동화의 주요 구성 요소

  • 파일 인바운드 어댑터(Inbound Adapter): 디렉터리에서 파일을 감시하고 읽어 Spring Integration 메시지로 변환합니다.
  • 파일 아웃바운드 어댑터(Outbound Adapter): 처리된 데이터를 파일로 저장합니다.
  • 메시지 처리기: 파일 내용을 처리하는 비즈니스 로직을 구현합니다.

2. 파일 처리 자동화 예제

예제 시나리오

  1. /input-directory에 새로운 .txt 파일이 생성되면 이를 자동으로 감지합니다.
  2. 파일 내용을 읽어 문자열로 변환합니다.
  3. 파일의 내용을 대문자로 변환한 후 /output-directory에 저장합니다.

2.1 파일 처리 자동화 코드 (Spring Integration 6.4)

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.config.EnableIntegration;
import org.springframework.integration.dsl.IntegrationFlow;
import org.springframework.integration.file.dsl.Files;

import java.io.File;

@Configuration
@EnableIntegration
public class FileProcessingConfig {

    // 1. 파일 읽기: Inbound Adapter 설정
    @Bean
    public IntegrationFlow fileInboundFlow() {
        return IntegrationFlow.from(Files.inboundAdapter(new File("/input-directory"))
                        .autoCreateDirectory(true) // 디렉터리 자동 생성
                        .patternFilter("*.txt"))  // .txt 파일만 감지
                .transform(Files.toStringTransformer()) // 파일 내용을 문자열로 변환
                .channel("fileProcessingChannel") // 메시지 채널로 전달
                .get();
    }

    // 2. 파일 내용 처리: 메시지 핸들러
    @Bean
    public IntegrationFlow fileProcessingFlow() {
        return IntegrationFlow.from("fileProcessingChannel")
                .transform(String.class, String::toUpperCase) // 파일 내용을 대문자로 변환
                .channel("fileOutputChannel") // 출력 채널로 전달
                .get();
    }

    // 3. 파일 저장: Outbound Adapter 설정
    @Bean
    public IntegrationFlow fileOutboundFlow() {
        return IntegrationFlow.from("fileOutputChannel")
                .handle(Files.outboundAdapter(new File("/output-directory"))
                        .fileNameGenerator(message -> "processed_" + System.currentTimeMillis() + ".txt")
                        .autoCreateDirectory(true)) // 디렉터리 자동 생성
                .get();
    }
}

3. 코드 설명

3.1 파일 인바운드 어댑터 (파일 읽기)

  • Files.inboundAdapter를 사용하여 /input-directory를 감시합니다.
  • .txt 파일만 처리하도록 필터를 설정했습니다 (patternFilter("*.txt")).
  • 새로 감지된 파일의 내용은 Transformers.fileToString()을 사용해 문자열로 변환됩니다.
Files.inboundAdapter(new File("/input-directory"))
    .autoCreateDirectory(true)
    .patternFilter("*.txt")

3.2 파일 내용 처리 (메시지 핸들러)

  • 파일 내용을 대문자로 변환하는 로직을 작성했습니다.
  • .transform(Files.toStringTransformer())를 사용하여 Payload를 변환합니다.
.transform(Files.toStringTransformer()) // 파일 내용을 문자열로 변환

3.3 파일 아웃바운드 어댑터 (파일 저장)

  • 변환된 파일 내용을 /output-directory에 저장합니다.
  • 파일 이름은 processed_타임스탬프.txt 형식으로 생성됩니다.
  • autoCreateDirectory(true)를 설정하여 디렉터리가 없을 경우 자동으로 생성됩니다.
Files.outboundAdapter(new File("/output-directory"))
    .fileNameGenerator(message -> "processed_" + System.currentTimeMillis() + ".txt")
    .autoCreateDirectory(true)

4. 실행 흐름

  1. /input-directory에 새 .txt 파일을 생성합니다.
    예: sample.txt
  2. Spring Integration의 파일 인바운드 어댑터가 새 파일을 감지하고 내용을 메시지로 변환하여 fileProcessingChannel로 전달합니다.
    예: sample.txt 파일 내용 → "Hello, Spring Integration!"
  3. 파일 내용 처리기가 메시지 내용을 대문자로 변환합니다.
    결과: "HELLO, SPRING INTEGRATION!"
  4. 변환된 내용을 파일 아웃바운드 어댑터가 /output-directory에 저장합니다.
    저장된 파일 이름: processed_1691234567890.txt
    저장된 내용: "HELLO, SPRING INTEGRATION!"

5. 테스트

테스트 파일 생성

  1. /input-directory에 example.txt 파일 생성.
  2. 파일 내용: Hello, Spring Integration!

결과 확인

  1. /output-directory에 processed_<timestamp>.txt 파일이 생성됩니다.
  2. 파일 내용: HELLO, SPRING INTEGRATION!

메시지 채널(MessageChannel)  자동생성

Spring Integration에서 메시지 채널(MessageChannel)은 코드에서 명시적으로 정의하지 않아도 자동으로 생성됩니다.

Spring Integration은 채널 이름(fileProcessingChannel, fileOutputChannel)이 Integration Flow에서 언급되면, 해당 채널을 자동으로 등록합니다. 따라서 위 코드에서 fileProcessingChannel과 fileOutputChannel을 별도로 정의하지 않아도 문제없이 실행됩니다.

왜 자동 생성이 가능한가?

Spring Integration은 다음과 같은 이유로 메시지 채널을 자동 생성합니다:

  1. 명시적 선언이 없는 채널은 기본 구현(DefaultChannel)으로 생성됩니다.
    • 기본적으로 DirectChannel 타입으로 생성됩니다.
  2. Integration Flow에 정의된 이름은 Spring 컨텍스트에 등록된 Bean으로 처리됩니다.

그러나 특별한 요구 사항(예: 메시지 버퍼링, 비동기 처리 등)이 있다면, 명시적으로 채널을 정의하는 것이 더 적합합니다.

추가적인 설명이 필요하면 말씀해주세요!


728x90

RestAPI 통합과 데이터 교환

Spring Integration을 사용하면 외부 REST API와 손쉽게 통합할 수 있습니다. API 통합은 Spring Integration의 HTTP Outbound Gateway를 활용하여 요청을 보내고 응답을 처리하는 방식으로 구현됩니다. 이 섹션에서는 HTTP POST 메서드를 사용해 JSON 구조로 데이터를 전달하고, 문자열 응답을 처리하는 실용 사례를 다룹니다.

 

의존성

Spring Boot 프로젝트에서 실행하려면 아래 의존성을 추가해야 합니다.

implementation 'org.springframework.boot:spring-boot-starter'
implementation 'org.springframework.boot:spring-boot-starter-integration'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.integration:spring-integration-http'

testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.integration:spring-integration-test'

 

요구사항

  1. API 요청
    • URL: http://localhost:8080/echo/reflection
    • HTTP 메서드: POST
    • 요청 데이터(JSON): { "name": "Andrew", "shout": "Hello World" }
  2. API 응답
    • 응답 데이터(문자열): 예: "You said: Hello World"

Spring Integration 구성 및 테스트 코드

아래는 Spring Integration을 사용하여 REST API 통합과 데이터 교환을 구현한 코드입니다.

RestAPI Code

예제코드에서 사용할 RestAPI 코드를 작성합니다. 이 API는 전달 받은 sout 문자열를 그래도 응답하게 되어 있습니다.

package kr.co.thekeytech.spring.eai.controller;

import kr.co.thekeytech.spring.eai.dto.EchoRequest;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RequiredArgsConstructor
@RestController
public class EchoController {

    @PostMapping("/echo/reflection")
    public ResponseEntity<String> reflection(@RequestBody EchoRequest echo) {
        return ResponseEntity.ok(echo.getShout());
    }
}

전송 객체

클라이언트의 RequestBody의 전송데이터를 담을 객체입니다.

package kr.co.thekeytech.spring.eai.dto;

import lombok.AllArgsConstructor;
import lombok.Getter;

@AllArgsConstructor
@Getter
public class EchoRequest {
    private String name;
    private String shout;
}

Integration Code 구현

package kr.co.thekeytech.spring.eai.api;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.annotation.Gateway;
import org.springframework.integration.annotation.MessagingGateway;
import org.springframework.integration.config.EnableIntegration;
import org.springframework.integration.dsl.IntegrationFlow;
import org.springframework.integration.http.dsl.Http;
import org.springframework.messaging.handler.annotation.Payload;

@Configuration
@EnableIntegration
public class ApiIntegrationConfig {

    // 1. HTTP Outbound Gateway 설정
    @Bean
    public IntegrationFlow apiIntegrationFlow() {
        return IntegrationFlow.from("apiRequestChannel") // 입력 채널
                .enrichHeaders(h -> h.header("Content-Type", "application/json"))
                .handle(Http.outboundGateway("http://localhost:8080/echo/reflection")
                        .httpMethod(org.springframework.http.HttpMethod.POST) // POST 메서드
                        .expectedResponseType(String.class) // 응답 타입
                        .mappedRequestHeaders("*")) // 모든 헤더 전달
                .get();
    }

    // 2. Gateway 정의 (클라이언트 인터페이스)
    @MessagingGateway
    public interface ApiGateway {
        @Gateway(requestChannel = "apiRequestChannel")
        String sendRequest(@Payload String json);
    }
}

Gateway 설명

1. Gateway

  • @MessagingGateway: API 호출을 메서드 호출처럼 사용할 수 있도록 인터페이스를 정의합니다.
  • @Gateway: IntegrationFlow의 입력 채널 명을 지정해줍니다.
  • sendRequest(String json): JSON 형식의 문자열을 입력받아 API 요청을 보냅니다. 
  • 반환값: API에서 반환된 응답 문자열.

HTTP Outbound Gateway 설명

  • .enrichHeaders(h -> h.header("Content-Type", "application/json"))
  • Http.outboundGateway(...): Spring Integration의 HTTP Outbound Gateway로 API 요청을 처리합니다.
    • httpMethod(org.springframework.http.HttpMethod.POST): HTTP POST 메서드를 사용.
    • expectedResponseType(String.class): 응답 데이터를 문자열로 처리.
    • mappedRequestHeaders("*"): 모든 헤더를 API 요청에 포함.
  • 채널: 요청은 apiRequestChannel로 전달되며, Gateway를 통해 외부 API로 전송됩니다.

테스트 예제

package kr.co.thekeytech.spring.eai.api;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class ApiIntegrationConfigTest {

    @Autowired
    private ApiIntegrationConfig.ApiGateway apiGateway;

    @Test
    void sendRequest() throws Exception {
        // JSON 요청 데이터 생성
        String json = """
                {
                    "name": "John",
                    "shout": "Hello World"
                }
                """;

        // Gateway를 통해 API 호출
        String response = apiGateway.sendRequest(json);

        // API 응답 출력
        System.out.println("API Response: " + response);
    }
}

실행 흐름

  1. JSON 데이터 생성
    Gateway를 통해 API에 전달할 JSON 데이터를 준비합니다.
  2. { "name": "John", "shout": "Hello World" }
  3. API 요청 전송
    JSON 데이터는 http://localhost:8080/echo/reflection으로 POST 요청됩니다.
  4. API 응답 처리
    API에서 반환된 응답 문자열을 Gateway에서 받아 출력합니다.
  5. 예: "You said: Hello World"

728x90
Comments