토리맘의 한글라이즈 프로젝트 logo 토리맘의 한글라이즈 프로젝트

스프링 인티그레이션 공식 레퍼런스를 한글로 번역한 문서입니다.

전체 목차는 여기에 있습니다.


Spring Integration은 HTTP를 지원하기 때문에, HTTP 요청을 실행하거나, HTTP 요청을 받아 무언가를 처리할 수 있다. HTTP 지원은 게이트웨이 구현체, HttpInboundEndpointHttpRequestExecutingMessageHandler로 이루어진다. WebFlux 지원도 함께 참고해라.

프로젝트에는 아래 의존성을 추가해야 한다:

Maven Gradle
<dependency>
    <groupId>org.springframework.integration</groupId>
    <artifactId>spring-integration-http</artifactId>
    <version>5.5.15</version>
</dependency>
compile "org.springframework.integration:spring-integration-http:5.5.15"

타겟 서블릿 컨테이너는 반드시 javax.servlet:javax.servlet-api 의존성을 가지고 있어야 한다.

목차


21.1. Http Inbound Components

HTTP를 통해 메시지를 수신하려면 HTTP 인바운드 채널 어댑터나 HTTP 인바운드 게이트웨이를 이용해야 한다. 그러려면 Apache Tomcat이나 Jetty같은 서블릿 컨테이너 안에 HTTP 인바운드 어댑터를 배포해야 한다. 가장 쉽게는 스프링의 HttpRequestHandlerServlet을 사용해 web.xml 파일에 아래와 같이 서블릿을 정의할 수 있다:

<servlet>
    <servlet-name>inboundGateway</servlet-name>
    <servlet-class>o.s.web.context.support.HttpRequestHandlerServlet</servlet-class>
</servlet>

서블릿의 이름이 빈 이름과 일치하는 것에 주목해라. HttpRequestHandlerServlet에 대한 자세한 내용은 스프링 프레임워크 레퍼런스 문서에서 스프링을 이용한 원격 처리와 웹 서비스를 확인해봐라.

스프링 MVC 애플리케이션을 개발하고 있다면, 위에서 보여준대로 서블릿을 직접 정의할 필요는 없다. 이 경우 게이트웨이 빈의 이름은 스프링 MVC 컨트롤러 빈에서처럼 URL 경로로 매칭시킬 수 있다. 자세한 내용은 스프링 프레임워크 레퍼런스 문서에 있는 웹 MVC 프레임워크를 참고해라.

샘플 애플리케이션과 거기 필요한 설정들은 Spring Integration Samples 레포지토리를 참고해라. 이곳에선 Spring Integration의 HTTP 지원을 활용한 HTTP 샘플 애플리케이션도 확인할 수 있다.

다음은 HTTP 인바운드 엔드포인트를 정의하는 빈 예시다:

<bean id="httpInbound"
  class="org.springframework.integration.http.inbound.HttpRequestHandlingMessagingGateway">
  <property name="requestChannel" ref="httpRequestChannel" />
  <property name="replyChannel" ref="httpReplyChannel" />
</bean>

HttpRequestHandlingMessagingGatewayHttpMessageConverter 인스턴스 리스트를 설정할 수도 있고, 디폴트 리스트를 사용해도 좋다. 이 컨버터를 사용하면 HttpServletRequest로부터 Message를 매핑하는 일을 커스텀할 수 있다. 디폴트 컨버터들은 컨텐츠 타입이 text로 시작하는 POST 요청에 대해 String 메시지를 생성하는 간단한 전략들을 캡슐화하고 있다. 좀 더 자세한 내용은 Javadoc을 참고해라. 커스텀 컨버터 뒤에 디폴트 컨버터들도 추가하려면, 커스텀 HttpMessageConverter 목록과는 별도로 플래그(mergeWithDefaultConverters)를 설정해주면 된다. 이 플래그의 기본값은 false로, 커스텀 컨버터를 추가하면 디폴트 목록을 대체해버린다.

메시지를 변환할 때는 (optional) requestPayloadType 프로퍼티와 전달받은 Content-Type 헤더를 활용한다. 4.3 버전부터는 요청에 컨텐츠 타입 헤더가 없으면 RFC 2616에서 권장하는 대로 application/octet-stream을 가정한다. 이전에는 이런 메시지에 있는 body는 무시했었다.

Spring Integration 2.0은 멀티파트 파일도 구현하고 있다. 디폴트 컨버터들을 사용할 때 MultipartHttpServletRequest로 감싸진 요청을 받으면, 이 요청을 Message 페이로드로 변환할 땐 MultiValueMap을 생성한다. 이 MultiValueMap은 각 파트의 컨텐츠 타입에 따라 바이트 배열이나 문자열, 또는 스프링의 MultipartFile 인스턴스를 값으로 가지고 있다.

HTTP 인바운드 엔드포인트에선 컨텍스트에 이름이 multipartResolver인 빈이 있다면 그 빈을 가져온다 (스프링의 DispatcherServlet에서 사용하는 이름과 동일하다). 이 빈을 가져오고 나면 인바운드 request 매퍼의 멀티파트 파일 지원이 활성화된다. 멀티파트 파일 지원을 활성화하지 않은 채로 멀티파트 파일 요청을 Spring Integration의 Message에 매핑하려고 하면 에러가 발생한다. 스프링의 MultipartResolver 지원에 대한 자세한 내용은 스프링 레퍼런스 매뉴얼을 참고해라.

multipart/form-data 요청을 다른 서버로 전달만하는 프록시를 원한다면, 원래 형식을 그대로 유지하는 것이 좋다. 그러려면 컨텍스트에 multipartResolver 빈을 추가하지 않아야 한다. 엔드포인트에선 byte[] 요청을 받도록 구성하고, 메시지 컨버터를 커스텀해 ByteArrayHttpMessageConverter를 추가하고, 디폴트 멀티파트 컨버터를 비활성화해라. 응답을 전송하기 위한 다른 컨버터도 필요할 수 있다. 아래 예시를 참고해라:

<int-http:inbound-gateway
               channel="receiveChannel"
               path="/inboundAdapter.htm"
               request-payload-type="byte[]"
               message-converters="converters"
               merge-with-default-converters="false"
               supported-methods="POST" />

<util:list id="converters">
 <beans:bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter" />
 <beans:bean class="org.springframework.http.converter.StringHttpMessageConverter" />
 <beans:bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" />
</util:list>

클라이언트에 응답을 전송할 때는, 다양한 방법으로 게이트웨이의 동작을 커스텀할 수 있다. 기본적으로 게이트웨이는 status code 200을 돌려보냄으로써 요청을 잘 받았음을 알린다. 이때 보내는 응답은 스프링 MVC ViewResolver로 리졸브하는 ‘viewName’을 제공해서 커스텀할 수 있다. 게이트웨이가 Message를 전송한 뒤 응답을 받아야 한다면, expectReply 플래그(생성자 인자)를 설정하면 HTTP 응답을 생성하기 전에 reply Message를 기다리게 만들 수 있다. 다음은 view name을 이용해 스프링 MVC 컨트롤러처럼 요청을 서빙하는 게이트웨이를 설정하는 예시다:

<bean id="httpInbound"
  class="org.springframework.integration.http.inbound.HttpRequestHandlingController">
  <constructor-arg value="true" /> <!-- indicates that a reply is expected -->
  <property name="requestChannel" ref="httpRequestChannel" />
  <property name="replyChannel" ref="httpReplyChannel" />
  <property name="viewName" value="jsonView" />
  <property name="supportedMethodNames" >
    <list>
      <value>GET</value>
      <value>DELETE</value>
    </list>
  </property>
</bean>

여기서는 constructor-arg 값이 true이기 때문에 응답을 기다린다. 위 예제를 보면 게이트웨이에서 허용할 HTTP 메소드를 커스텀하는 방법도 알 수 있다 (기본값은 POSTGET이다).

model 맵 안에는 응답 메시지가 담겨있다. 기본적으로 응답 메시지는 ‘reply’란 키로 저장하지만, 엔드포인트를 구성할 때 ‘replyKey’ 프로퍼티를 설정하면 기본값을 재정의할 수 있다.

21.1.1. Payload Validation

5.2 버전부터 HTTP 인바운드 엔드포인트에 Validator를 함께 설정하면 채널로 메시지를 전송하기 전에 페이로드를 검증할 수 있다. 이때 페이로드는 payloadExpression을 적용해서 변환과 추출을 마친 값으로, 진짜 필요한 데이터만 검증할 수 있다. 유효성 검사에 실패했을 때의 처리 동작은 스프링 MVC의 에러 핸들링과 완전히 동일하다.


21.2. HTTP Outbound Components

이번 섹션에선 Spring Integration의 HTTP 아웃바운드 컴포넌트들을 설명한다.

21.2.1. Using HttpRequestExecutingMessageHandler

HttpRequestExecutingMessageHandler를 설정할 땐 다음과 같은 빈을 정의하면 된다:

<bean id="httpOutbound"
  class="org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandler">
  <constructor-arg value="http://localhost:8080/example" />
  <property name="outputChannel" ref="responseChannel" />
</bean>

여기서 정의한 빈은 RestTemplate에 HTTP 요청을 위임한다. RestTemplate은 가지고 있는 HttpMessageConverter 인스턴스 목록에 차례대로 위임해서 Message 페이로드로부터 HTTP 요청에 사용할 body를 생성한다. 이때 사용할 컨버터나 ClientHttpRequestFactory 인스턴스는 다음과 같이 설정할 수 있다:

<bean id="httpOutbound"
  class="org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandler">
  <constructor-arg value="http://localhost:8080/example" />
  <property name="outputChannel" ref="responseChannel" />
  <property name="messageConverters" ref="messageConverterList" />
  <property name="requestFactory" ref="customRequestFactory" />
</bean>

HTTP 요청은 기본적으로 JDK HttpURLConnection을 사용하는 SimpleClientHttpRequestFactory 인스턴스를 통해 생성한다. (앞에서 보여준 방법대로) CommonsClientHttpRequestFactory를 주입하면 Apache Commons HTTP 클라이언트도 이용할 수 있다.

아웃바운드 게이트웨이의 경우, 게이트웨이에서 생성하는 응답 메시지엔 요청 메시지에 존재하는 모든 메시지 헤더가 담긴다.

21.2.2. Using Cookies

기본적인 쿠키는 아웃바운드 게이트웨이의 transfer-cookies 속성을 통해 지원한다. true로 설정하면 (기본값은 false다), 서버로부터 수신한 Set-Cookie 헤더를 응답 메시지에선 Cookie라는 헤더로 변환한다. 이후 메시지를 전송할 때 이 헤더를 활용하면 다음과 같이 간단한 상태를 활용한stateful 상호 작용이 가능해진다:

…→logonGateway→…→doWorkGateway→…→logoffGateway→…

transfer-cookiesfalse이면, 응답 메시지에도 수신한 모든 Set-Cookie 헤더가 그대로 남아 있으며, 이후 메시즈를 전송할 때 제거된다.

Empty Response Bodies

HTTP는 요청-응답 프로토콜이다. 하지만 응답에는 body 없이 헤더만 존재할 수도 있다. 이 경우 HttpRequestExecutingMessageHandler는 지정한 expected-response-type에 관계없이 org.springframework.http.ResponseEntity를 페이로드로 가지고 있는 응답 Message를 생성한다. HTTP RFC 상태 코드 정의에 따르면 응답에 message-body가 포함되지 않아야 하는 상태 코드도 다양하다 (ex. 204 No Content). 그 외에도, 같은 URL을 호출했을 때 어떨 땐 응답 body를 반환하고 어떨 땐 반환하지 않는 경우도 있다. 예를 들어 특정 HTTP 리소스에 처음으로 요청을 보내면 컨텐츠를 반환하지만 두 번째 요청부턴 컨텐츠를 반환하지 않을 수 있다 (304 Not Modified). 하지만 어떤 경우라도 메시지 헤더 http_statusCode는 채워진다. HTTP 아웃바운드 게이트웨이 이후 실행하는 라우팅 로직이 있다면 이 값을 활용해도 좋다. <payload-type-router/>를 사용하면 ResponseEntity를 가지고 있는 메시지를, body가 있는 응답과는 다른 플로우로 라우팅할 수도 있다.

expected-response-type

앞의 내용에 조금 덧붙이자면, 응답에 body가 포함된 경우엔 반드시 적절한 expected-response-type 속성을 제공해야 한다. 그렇지 않으면 body가 없는 ResponseEntity를 돌려받게 된다. expected-response-type은 반드시 HttpMessageConverter 인스턴스(직접 설정했든, 디폴트 컨버터를 사용하던지 관계 없이)와 응답의 Content-Type 헤더와 호환돼야 한다. 여기에는 추상 클래스를 사용할 수도 있고, 인터페이스여도 상관 없다 (예를 들어 자바 직렬화와 Content-Type: application/x-java-serialized-object를 사용한다면 java.io.Serializable일 수 있다).

5.5 버전부터 HttpRequestExecutingMessageHandlerextractResponseBody 플래그에 따라 (기본값은 true다), expectedResponseType과는 상관 없이 응답 메시지 페이로드로 ResponseEntity를 통째로 반환하거나, 응답 body만 반환할 수 있다. ResponseEntity 안에 body가 없으면 이 플래그는 무시하고 전체 ResponseEntity를 반환한다.


21.3. HTTP Namespace Support

Spring Integration은 http 네임스페이스와 전용 스키마 정의를 제공하고 있다. 설정에 포함시키려면 애플리케이션 컨텍스트 설정 파일에 아래와 같은 네임스페이스를 선언해주면 된다:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:int="http://www.springframework.org/schema/integration"
  xmlns:int-http="http://www.springframework.org/schema/integration/http"
  xsi:schemaLocation="
    http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/integration
    https://www.springframework.org/schema/integration/spring-integration.xsd
    http://www.springframework.org/schema/integration/http
    https://www.springframework.org/schema/integration/http/spring-integration-http.xsd">
    ...
</beans>

21.3.1. Inbound

XML 네임스페이스에선 HTTP 인바운드 요청을 처리하기 위한 두 가지 컴포넌트, inbound-channel-adapterinbound-gateway를 제공한다. 별도 응답을 반환하지 않고 요청을 처리하려면 inbound-channel-adapter를 사용해라. 설정 방법은 아래 예시를 참고해라:

<int-http:inbound-channel-adapter id="httpChannelAdapter" channel="requests"
    supported-methods="PUT, DELETE"/>

응답이 필요한 요청을 처리하려면 inbound-gateway를 사용해라. 설정 방법은 아래 예시를 참고해라:

<int-http:inbound-gateway id="inboundGateway"
    request-channel="requests"
    reply-channel="responses"/>

21.3.2. Request Mapping Support

Spring Integration 3.0에선 IntegrationRequestMappingHandlerMapping을 도입해서 REST 지원을 좀 더 개선했다. 이 구현체는 스프링 프레임워크 3.1에서 개선시킨 REST 기능을 이용한다.

HTTP 인바운드 게이트웨이나 HTTP 인바운드 채널 어댑터를 파싱할 땐, integrationRequestMappingHandlerMapping 빈이 등록되어 있지 않으면 IntegrationRequestMappingHandlerMapping 타입 빈을 하나 등록한다. 이 클래스는 HandlerMapping의 구현체로, RequestMappingInfoHandlerMapping에 로직을 위임한다. 이 구현체는 스프링 MVC의 어노테이션 org.springframework.web.bind.annotation.RequestMapping과 유사한 기능을 제공한다.

자세한 정보는 @RequestMapping을 통해 요청 매핑하기를 확인해봐라.

Spring Integration 3.0에선 요청 매핑을 위한 <request-mapping> 요소를 도입했다. 이 요소는 생략할 수 있으며, <http:inbound-channel-adapter><http:inbound-gateway>에 추가할 수 있다. 동작할 땐 path 속성과 supported-methods 속성과 함께 동작한다. 다음은 인바운드 게이트웨이 안에 설정을 추가하는 예시다:

<inbound-gateway id="inboundController"
    request-channel="requests"
    reply-channel="responses"
    path="/foo/{fooId}"
    supported-methods="GET"
    view-name="foo"
    error-code="oops">
   <request-mapping headers="User-Agent"
     params="myParam=myValue"
     consumes="application/json"
     produces="!text/plain"/>
</inbound-gateway>

위 설정에선 네임스페이스 파서는 IntegrationRequestMappingHandlerMapping(없으면)과 HttpRequestHandlingController 빈 인스턴스를 생성하고 RequestMapping 인스턴스에 연결한다. 이 RequestMapping 인스턴스는 결국 스프링 MVC의 RequestMappingInfo로 변환된다.

<request-mapping> 요소는 다음과 같은 속성들을 제공한다:

<http:inbound-channel-adapter>, <http:inbound-gateway>path 속성과 supported-methods 속성을 사용하면, <request-mapping> 속성들은 스프링 MVC의 org.springframework.web.bind.annotation.RequestMapping 어노테이션이 제공하는 각각의 옵션들로 직접 변환된다.

<request-mapping> 요소를 활용하면 여러 개의 Spring Integration HTTP 인바운드 엔드포인트를 동일한 path로 구성하고 (심지어는 supported-methods도 동일하게), 들어오는 HTTP 요청을 기반으로 서로 다른 다운스트림 메시지 플로우를 제공할 수 있다.

아니면 HTTP 인바운드 엔드포인트는 하나만 선언하고 Spring Integration 플로우 내에서 라우팅과 필터링 로직을 적용해도 같은 결과를 얻을 수 있다. 이렇게 하면 준비되는 즉시 플로우로 Message를 가져올 수 있다. 다음은 그 방법을 보여주는 예시다:

<int-http:inbound-gateway request-channel="httpMethodRouter"
    supported-methods="GET,DELETE"
    path="/process/{entId}"
    payload-expression="#pathVariables.entId"/>

<int:router input-channel="httpMethodRouter" expression="headers.http_requestMethod">
    <int:mapping value="GET" channel="in1"/>
    <int:mapping value="DELETE" channel="in2"/>
</int:router>

<int:service-activator input-channel="in1" ref="service" method="getEntity"/>

<int:service-activator input-channel="in2" ref="service" method="delete"/>

핸들러 매핑에 대한 자세한 내용은 스프링 프레임워크 웹 서블릿 문서스프링 프레임워크 웹 리액티브 문서를 참고해라.

IntegrationRequestMappingHandlerMapping은 스프링 MVC의 RequestMappingHandlerMapping 클래스를 확장한 것이기 때문에, 대부분의 로직을 상속한다. 특히 어떠한 이유로 매핑 정보를 찾을 수 없을 때 4XX 응답으로 에러를 던지는 handleNoMatch(Set, String, HttpServletRequest)도 마찬가지다. 그 덕분에 애플리케이션 컨텍스트에 있는 나머지 매핑 핸들러는 호출하지 않는다. 그렇기 때문에 Spring Integration과 스프링 MVC에 동일한 요청 path를 매핑하는 세팅은 지원하지 않는다 (e.g. 하나는 POST이고 다른 하나는 GET). 같은 path를 설정했더라도 MVC 매핑 정보는 조회할 수 없을 거다.

21.3.3. Cross-origin Resource Sharing (CORS) Support

4.2 버전부터 <http:inbound-channel-adapter><http:inbound-gateway><cross-origin> 요소를 함께 설정할 수 있다. <cross-origin>@Controller 어노테이션에 사용하는 스프링 MVC의 @CrossOrigin과 동일한 옵션을 가지고 있으며, Spring Integration HTTP 엔드포인트에 CORSCross-Origin Resource Sharing를 설정해준다:

자바에서 CORS 설정은 org.springframework.integration.http.inbound.CrossOrigin 클래스로 표현하며, CrossOrigin 인스턴스는 HttpRequestHandlingEndpointSupport 빈에 주입할 수 있다.

21.3.4. Response Status Code

<http:inbound-channel-adapter>는 4.1 버전부터 status-code-expression을 설정해서 디폴트 200 OK status를 재정의할 수 있다. 여기 사용하는 표현식은 반드시 enum org.springframework.http.HttpStatus로 변환 가능한 객체를 반환해야 한다. evaluationContextBeanResolver를 가지고 있으며, 5.1부터는 RequestEntity<?>를 루트 객체로 함께 제공한다. 표현식을 활용하면 특정 스코프에 속해있는, status code를 반환하는 빈을 런타임에 리졸브할 수 있다. 하지만 보통은 status-code=expression="204"(No Content)나 status-code-expression="T(org.springframework.http.HttpStatus).NO_CONTENT"와 같이 고정 값을 설정하는 경우가 많다. status-code-expression의 기본값은 null로, 일반 ‘200 OK’ 응답을 반환한다. RequestEntity<?>를 루트 객체로 사용하면 status code를 요청 메소드, 헤더, URI 컨텐츠, 심지어는 요청 body에 따라서도 다르게 설정할 수 있다. 다음은 status code를 ACCEPTED로 설정하는 예시다:

<http:inbound-channel-adapter id="inboundController"
       channel="requests" view-name="foo" error-code="oops"
       status-code-expression="T(org.springframework.http.HttpStatus).ACCEPTED">
   <request-mapping headers="BAR"/>
</http:inbound-channel-adapter>

<http:inbound-gateway>는 응답 Message에 있는 http_statusCode 헤더를 이용해 ‘status code’를 리졸브한다. 4.2부터 reply-timeout 내에 응답을 받지 못하면 500 Internal Server Error를 디폴트 응답 status code로 사용한다. 이 동작은 두 가지 방법으로 변경할 수 있다:

ErrorMessage의 페이로드에는 MessageTimeoutException이 담긴다. MessageTimeoutExceptionString과 같이 게이트웨이에서 HTTP 응답으로 컨버팅할 수 있는 것으로 변환해야 한다. expression을 활용할 때처럼 exception의 message 프로퍼티를 사용하는 것도 좋은 방법이다.

메인 플로우에서 타임아웃이 발생한 다음 에러 플로우 역시 타임아웃되면 500 Internal Server Error를 반환하며, reply-timeout-status-code-expression이 존재한다면 이 표현식을 평가한다.

이전에는 타임아웃이 발생하면 200 OK가 디폴트였다. 이때의 동작으로 돌아가려면 reply-timeout-status-code-expression="200"을 설정하면 된다.

또한 5.4 버전부터 요청 메시지를 준비하는 동안 에러가 발생하면 에러 채널(제공했다면)로 보내진다. 적당한 예외를 던질지 결정하는 일은 에러 플로우에서 예외를 확인한 뒤 진행하는 것이 좋다. 이전에는 예외가 발생하면 무조건 던져서 HTTP 500에 해당하는 서버 에러로 응답을 전송했지만, 경우에 따라서는 요청 파라미터가 잘못된 것일 수도 있기 때문에 클라이언트 에러 4xx에 해당하는 ResponseStatusException을 던져야 할 수도 있다. 자세한 내용은 ResponseStatusException을 참고해라. 에러 채널로 전송된 ErrorMessage의 페이로드 안에는 기존의 예외가 담겨 있어서 여기서 예외를 분석할 수 있다.

21.3.5. URI Template Variables and Expressions

path 속성을 payload-expression 속성이나 header 요소와 함께 사용하면, 인바운드 요청 데이터를 훨씬 더 유연하게 매핑할 수 있다.

아래 설정 예시에선, 다음과 같은 URI로 들어온 요청을 수락하는 인바운드 채널 어댑터를 하나 설정한다:

/first-name/{firstName}/last-name/{lastName}

아래와 같이 payload-expression 속성을 정의하면, URI 템플릿 변수 Fisher는 메시지 헤더 lname에 매핑되는 반면, URI 템플릿 변수 MarkMessage 페이로드에 매핑되게 만들 수 있다:

<int-http:inbound-channel-adapter id="inboundAdapterWithExpressions"
    path="/first-name/{firstName}/last-name/{lastName}"
    channel="requests"
    payload-expression="#pathVariables.firstName">
    <int-http:header name="lname" expression="#pathVariables.lastName"/>
</int-http:inbound-channel-adapter>

URI 템플릿 변수에 대해 자세히 알아보려면 스프링 레퍼런스 매뉴얼에서 uri 템플릿 패턴을 찾아 읽어봐라.

페이로드와 헤더 표현식에 사용할 수 있는 기존 변수 #pathVariables, #requestParams 외에도, Spring Integration 3.0에선 다른 유용한 표현식 변수들을 추가했다:

참고로, 단일 스레드(요청 스레드)로 동작하는 메시지 플로우라면, 이 값들은 모두 다운스트림 플로우의 표현식 내에서 ThreadLocal org.springframework.web.context.request.RequestAttributes 변수를 통해 액세스할 수 있다. 다음은 expression 속성을 사용하는 트랜스포머 설정 예시다:

<int-:transformer
    expression="T(org.springframework.web.context.request.RequestContextHolder).
                  requestAttributes.request.queryString"/>

21.3.6. Outbound

아웃바운드 게이트웨이를 설정할 땐 네임스페이스를 이용하면 된다. 다음은 아웃바운드 HTTP 게이트웨이에서 사용할 수 있는 설정 옵션들을 나타낸 코드다:

<int-http:outbound-gateway id="example"
    request-channel="requests"
    url="http://localhost/test"
    http-method="POST"
    extract-request-payload="false"
    expected-response-type="java.lang.String"
    charset="UTF-8"
    request-factory="requestFactory"
    reply-timeout="1234"
    reply-channel="replies"/>

가장 중요한 속성은 ‘http-method’와 ‘expected-response-type’이다. 보통은 이 두 가지를 가장 많이 설정한다. 디폴트 http-methodPOST이며, response type은 null이 디폴트다. response type이 null일 땐 HTTP status가 성공을 나타내기만 한다면 응답 Message의 페이로드에 ResponseEntity를 담는다 (성공이 아닐 땐 예외를 던진다). String 등의 다른 타입을 받아야 하는 경우엔 클래스의 풀 네임fully qualified name을 지정해라 (위 예제에선 java.lang.String). HTTP 아웃바운드 컴포넌트에서 다룬 empty response bodies에 대한 내용도 함께 참고해라.

Spring Integration 2.1부터 HTTP 아웃바운드 게이트웨이의 request-timeout 속성은, 본래 의도가 더 잘 드러나도록 reply-timeout으로 이름을 변경했다.

Spring Integration 2.2 이후로 HTTP를 통한 자바 직렬화는 더 이상 기본으로 활성화되지 않는다. 이전에는 expected-response-type 속성을 Serializable 객체로 설정하면 Accept 헤더가 제대로 세팅되지 않았었다. 이제 Spring Integration 2.2부터 Accept 헤더를 application/x-java-serialized-object로 설정하도록 SerializingHttpMessageConverter를 수정했다.

하지만 이로 인해 기존 애플리케이션과 호환이 안 될 수도 있으므로, 더는 SerializingHttpMessageConverter를 자동으로 HTTP 엔드포인트에 추가하지 않기로 했다. 자바 직렬화를 사용하고 싶다면 message-converters 속성이나 (XML 설정) setMessageConverters() 메소드를 이용해 (자바 설정), 적당한 엔드포인트들에 SerializingHttpMessageConverter를 추가해주면 된다. 아니면 JSON을 사용하는 것을 검토해봐도 좋다. JSON은 클래스패스에 Jackson 라이브러리가 있으면 활성화된다.

Spring Integration 2.2부터는 SpEL과 http-method-expression 속성을 이용해 HTTP 메소드를 동적으로 결정할 수도 있다. 단, 이 속성은 http-method와는 함께 사용할 수 없다. expected-response-type 대신 expected-response-type-expression 속성을 사용해, 응답 타입을 결정할 수 있는 유효한 SpEL 표현식을 제공할 수도 있다. 아래 예시에선 expected-response-type-expression을 사용한다:

<int-http:outbound-gateway id="example"
    request-channel="requests"
    url="http://localhost/test"
    http-method-expression="headers.httpMethod"
    extract-request-payload="false"
    expected-response-type-expression="payload"
    charset="UTF-8"
    request-factory="requestFactory"
    reply-timeout="1234"
    reply-channel="replies"/>

아웃바운드 어댑터를 단방향으로 사용한다면 이 대신 outbound-channel-adapter를 사용하면 된다. 이땐 성공 응답을 받아도 응답 채널에 메시지를 전송하지 않는다. 응답 status code가 성공이 아닐 땐 예외를 던진다. 설정 자체는 아래 보이는 것처럼 게이트웨이와 매우 유사하다:

<int-http:outbound-channel-adapter id="example"
    url="http://localhost/example"
    http-method="GET"
    channel="requests"
    charset="UTF-8"
    extract-payload="false"
    expected-response-type="java.lang.String"
    request-factory="someRequestFactory"
    order="3"
    auto-startup="false"/>

URL을 지정할 땐 ‘url’ 이나 ‘url-expression’ 속성을 사용할 수 있다. ‘url’에는 간단한 문자열을 지정한다 (URI 변수에는 아래에서 설명하는 플레이스 홀더를 사용한다). ‘url-expression’은 Message를 루트 객체로 사용하는 SpEL 표현식으로, url을 동적으로 결정할 수 있게 해준다. 표현식을 평가해서 얻은 URL에는 여전히 URI 변수를 위한 플레이스 홀더가 있을 수 있다.

이전 릴리즈에서는 플레이스 홀더를 이용해 URL 전체를 URI 변수로 치환하는 사용자들도 있었다. 하지만 이제는 스프링 3.1의 변경 사항으로 인해 ‘?’와 같이 이스케이프가 필요한 문자를 사용하면 문제가 발생할 수 있다. 따라서 런타임에 URL을 통으로 생성하려면 ‘url-expression’ 속성을 사용하는 게 좋다.

21.3.7. Mapping URI Variables

사용하는 URL에 URI 변수가 들어있다면, ‘uri-variable’ 요소를 사용해 변수들을 매핑할 수 있다. 이 요소는 HTTP 아웃바운드 게이트웨이와 HTTP 아웃바운드 채널 어댑터에 사용할 수 있다. 다음은 URI 변수 zipCode를 표현식에 매핑하는 예시다:

<int-http:outbound-gateway id="trafficGateway"
    url="https://local.yahooapis.com/trafficData?appid=YdnDemo&amp;zip={zipCode}"
    request-channel="trafficChannel"
    http-method="GET"
    expected-response-type="java.lang.String">
    <int-http:uri-variable name="zipCode" expression="payload.getZip()"/>
</int-http:outbound-gateway>

uri-variable 요소에선 nameexpression이라는 두 가지 속성을 정의한다. name 속성으론 URI 변수의 이름을 식별한다면, expression 속성은 실제 값을 세팅하는 데 사용한다. expression 속성을 사용하면 SpELSpring Expression Language이 가진 모든 기능을 활용할 수 있으며, 완전히 동적으로 메시지 페이로드와 헤더에 접근할 수 있다. 예를 들어, 위 설정에선 Message의 페이로드 객체에서 getZip() 메소드를 호출하고, 그 실행 결과를 ‘zipCode’라는 URI 변수 값으로 사용한다.

Spring Integration 3.0부터 HTTP 아웃바운드 엔드포인트들은 uri-variables-expression 속성을 지원하므로, 평가하고 싶은 expression을 지정할 수 있다. 이 표현식에선 URL 템플릿 내 모든 URI 변수 플레이스 홀더를 가지고 있는 Map을 만든다. 덕분에 아웃바운드 메시지에 따라 다양한 변수 표현식을 사용할 수 있다. 이 속성은 <uri-variable/> 요소와는 함께 사용할 수 없다. 다음은 uri-variables-expression 속성의 사용법을 보여주는 예시다:

<int-http:outbound-gateway
     url="https://foo.host/{foo}/bars/{bar}"
     request-channel="trafficChannel"
     http-method="GET"
     uri-variables-expression="@uriVariablesBean.populate(payload)"
     expected-response-type="java.lang.String"/>

uriVariablesBean은 다음과 같이 정의할 수 있다:

public class UriVariablesBean {
    private static final ExpressionParser EXPRESSION_PARSER = new SpelExpressionParser();

    public Map<String, ?> populate(Object payload) {
        Map<String, Object> variables = new HashMap<String, Object>();
        if (payload instanceOf String.class)) {
            variables.put("foo", "foo"));
        }
        else {
            variables.put("foo", EXPRESSION_PARSER.parseExpression("headers.bar"));
        }
        return variables;
    }

}

uri-variables-expression은 반드시 Map으로 평가되어야 한다. Map에 있는 값들은 반드시 String이나 Expression의 인스턴스여야 한다. 이 MapExpressionEvalMap에 넘겨서, 아웃바운드 Message의 컨텍스트에서 표현식들을 사용해 URI 변수 플레이스 홀더를 리졸브한다.

주의 사항

uriVariablesExpression 프로퍼티를 잘 활용하면 다양한 방법으로 URI 변수를 만들 수 있다. 대부분은 위에 있는 예시처럼 매우 단순한 표현식만 사용할 거다. 하지만 variables.put("thing1", EXPRESSION_PARSER.parseExpression(message.getHeaders().get() "thing2", String.class)));와 같은 표현식을 map에 담아 반환하고, "@uriVariablesBean.populate(#root)"와 같이 설정하는 것도 가능하다. 이 표현식은 thing2라는 메시지 헤더를 통해 동적으로 제공된다. 단, 헤더는 신뢰할 수 없는 소스에서 가져온 값일 수도 있으므로, HTTP 아웃바운드 엔드포인트는 이러한 표현식들을 평가할 때 SimpleEvaluationContext를 사용한다. SimpleEvaluationContext는 SpEL 기능의 일부만 사용한다. 메시지의 출처를 신뢰할 수 있으며, 다른 SpEL 기능들을 사용하고 싶다면 아웃바운드 엔드포인트의 trustedSpel 속성을 true로 설정해라.

url-expression을 커스텀하면서 유틸리티를 사용해 URL 파라미터를 빌드, 인코딩하면 메시지별로 URI 변수 셋을 각각 동적으로 제공할 수 있다. 그 방법은 아래 예시를 참고해라:

url-expression="T(org.springframework.web.util.UriComponentsBuilder)
                           .fromHttpUrl('https://HOST:PORT/PATH')
                           .queryParams(payload)
                           .build()
                           .toUri()"

queryParams() 메소드는 인자로 MultiValueMap<String, String>을 받으므로, 요청을 수행하기 전에 미리 실제 URL 쿼리 파라미터 셋을 준비해두면 된다.

전체 queryString 역시 다음과 같이 uri-variable을 사용해 표현할 수 있다:

<int-http:outbound-gateway id="proxyGateway" request-channel="testChannel"
              url="http://testServer/test?{queryString}">
    <int-http:uri-variable name="queryString" expression="'a=A&amp;b=B'"/>
</int-http:outbound-gateway>

단, 이때는 반드시 직접 URL 인코딩을 수행해야 한다. 예를 들어 여기서는 org.apache.http.client.utils.URLEncodedUtils#format()을 사용할 수 있다. 앞에서처럼 MultiValueMap<String, String>을 수동으로 빌드했다면, 아래와 같이 자바 스트림을 사용해 format() 메소드의 인자 List<NameValuePair>로 변환할 수 있다:

List<NameValuePair> nameValuePairs =
    params.entrySet()
            .stream()
            .flatMap(e -> e
                    .getValue()
                    .stream()
                    .map(v -> new BasicNameValuePair(e.getKey(), v)))
            .collect(Collectors.toList());

21.3.8. Controlling URI Encoding

기본적으로 URL 문자열은 요청을 전송하기 전에 URI 객체로 인코딩된다 (UriComponentsBuilder 참고). 하지만 비표준 URI(ex. RabbitMQ REST API)를 사용하는 경우 인코딩을 수행하는 걸 바라지 않을 수도 있다. <http:outbound-gateway/><http:outbound-channel-adapter/>encoding-mode 속성을 제공한다. URL 인코딩을 비활성화하려면 이 속성을 NONE으로 설정하면 된다 (기본값은 TEMPLATE_AND_VALUES다). URL의 일부만 인코딩하고 싶다면 다음과 같이 <uri-variable/> 안에서 expression을 사용해라:

<http:outbound-gateway url="https://somehost/%2f/fooApps?bar={param}" encoding-mode="NONE">
          <http:uri-variable name="param"
            expression="T(org.apache.commons.httpclient.util.URIUtil)
                                             .encodeWithinQuery('Hello World!')"/>
</http:outbound-gateway>

Java DSL에선 BaseHttpMessageHandlerSpec.encodingMode()를 통해 이 옵션을 제어할 수 있다. WebFlux 모듈웹 서비스 모듈에 있는 아웃바운드 구성 요소들에도 같은 설정을 적용할 수 있다. 훨씬 더 복잡한 케이스엔 외부에서 RestTemplate을 제공하고 UriTemplateHandler를 설정하는 것이 좋다 (WebFlux의 경우 UriBuilderFactoryWebClient).


21.4. Configuring HTTP Endpoints with Java

다음은 Java 코드로 인바운드 게이트웨이를 설정하는 예시다:

Example 5. 자바 설정을 이용한 인바운드 게이트웨이

@Bean
public HttpRequestHandlingMessagingGateway inbound() {
    HttpRequestHandlingMessagingGateway gateway =
        new HttpRequestHandlingMessagingGateway(true);
    gateway.setRequestMapping(mapping());
    gateway.setRequestPayloadType(String.class);
    gateway.setRequestChannelName("httpRequest");
    return gateway;
}

@Bean
public RequestMapping mapping() {
    RequestMapping requestMapping = new RequestMapping();
    requestMapping.setPathPatterns("/foo");
    requestMapping.setMethods(HttpMethod.POST);
    return requestMapping;
}

다음은 Java DSL을 사용해 인바운드 게이트웨이를 설정하는 예시다:

Example 6. Java DSL을 이용한 인바운드 게이트웨이

@Bean
public IntegrationFlow inbound() {
    return IntegrationFlows.from(Http.inboundGateway("/foo")
            .requestMapping(m -> m.methods(HttpMethod.POST))
            .requestPayloadType(String.class))
        .channel("httpRequest")
        .get();
}

다음은 Java 코드로 아웃바운드 게이트웨이를 설정하는 예시다:

Example 7. 자바 설정을 이용한 아웃바운드 게이트웨이

@ServiceActivator(inputChannel = "httpOutRequest")
@Bean
public HttpRequestExecutingMessageHandler outbound() {
    HttpRequestExecutingMessageHandler handler =
        new HttpRequestExecutingMessageHandler("http://localhost:8080/foo");
    handler.setHttpMethod(HttpMethod.POST);
    handler.setExpectedResponseType(String.class);
    return handler;
}

다음은 Java DSL을 사용해 아웃바운드 게이트웨이를 설정하는 예시다:

Example 8. Java DSL을 이용한 아웃바운드 게이트웨이

@Bean
public IntegrationFlow outbound() {
    return IntegrationFlows.from("httpOutRequest")
        .handle(Http.outboundGateway("http://localhost:8080/foo")
            .httpMethod(HttpMethod.POST)
            .expectedResponseType(String.class))
        .get();
}

21.5. Timeout Handling

HTTP 구성 요소와 관련해서는 두 가지 타이밍을 고려해봐야 한다:

이 구성 요소들은 메시지 채널과도 상호 작용하는데, 메시지 채널은 타임아웃을 지정할 수 있다. 예를 들어, HTTP 인바운드 게이트웨이는 연결된 HTTP 클라이언트에서 수신한 메시지를 메시지 채널(request 타임아웃 사용)로 전달하고, 그 결과, 응답 채널(reply 타임아웃 사용)에서 HTTP 응답을 생성하는 데 사용하는 응답 메시지를 받아간다. 다음은 이 흐름 시각적으로 나타낸 그림이다:

http inbound gateway

Figure 8. HTTP 인바운드 게이트웨이에서 타임아웃 설정이 적용되는 방식

아웃바운드 엔드포인트의 경우, 원격 서버와 상호 작용하는 동안에 타이밍이 어떻게 동작할지를 생각해봐야 한다. 다음은 이 시나리오를 보여주는 이미지다:

http outbound gateway

Figure 9. 아웃바운드 게이트웨이에서 타임아웃 설정이 적용되는 방식

HTTP 아웃바운드 게이트웨이나 HTTP 아웃바운드 채널 어댑터를 사용해 HTTP 요청을 수행할 때는, HTTP 관련 타임아웃 동작을 설정해주고 싶을 수 있다. 참고로, 이 두 가지는 스프링의 RestTemplate을 사용해 HTTP 요청을 실행한다.

HTTP 아웃바운드 게이트웨이와 HTTP 아웃바운드 채널 어댑터에 타임아웃을 설정하려면 RestTemplate 빈을 직접 참조하거나 (rest-template 속성) ClientHttpRequestFactory 빈에 대한 참조를 제공하면 된다 (request-factory 속성). 스프링은 다음과 같은 ClientHttpRequestFactory 인터페이스 구현체들을 제공한다:

request-factoryrest-template 속성을 명시하지 않으면 디폴트 RestTemplate 인스턴스를 생성한다 (디폴트 RestTemplateSimpleClientHttpRequestFactory를 사용한다).

JVM 구현체에 따라, URLConnection 클래스에 의한 타임아웃 처리가 일관적이지 않을 수도 있다.

예를 들어 Java™ 플랫폼에서 setConnectTimeout에 관한 Standard Edition 6 API 스펙을 살펴보면 다음과 같이 설명하고 있다:

  • 일부 비표준 구현체에서 이 메소드는 지정된 타임아웃을 무시할 수 있다. 커넥션 타임아웃 세팅을 확인하려면 getConnectTimeout()을 호출해라.

정확한 요구 사항이 있다면 타임아웃이 잘 동작하는지 테스트해보는 것이 좋다. JVM에서 제공하는 구현체에 의존하기 보다는 Apache HttpComponents HttpClient를 이용하는 HttpComponentsClientHttpRequestFactory를 사용하는 것을 고려해봐라.

Apache HttpComponents HttpClient를 풀링 커넥션 매니저와 함께 사용한다면, 커넥션 매니저는 기본적으로 주어진 라우트 당 동시 커넥션을 2개 이상으로는 생성하지 않고, 전체 커넥션 수도 20개 이하로 유지한다는 점을 알아둬야 한다. 실제 애플리케이션에 쓰기에는 대부분 너무 과도한 제약일 거다. 이 컴포넌트에 관한 세부 세팅 방법은 Apache 문서를 참고해라.

다음은 커넥션 타임아웃과 read 타임아웃을 각각 5초로 설정한 SimpleClientHttpRequestFactory를 사용해 HTTP 아웃바운드 게이트웨이를 구성하는 예시다:

<int-http:outbound-gateway url="https://samples.openweathermap.org/data/2.5/weather?q={city}"
                           http-method="GET"
                           expected-response-type="java.lang.String"
                           request-factory="requestFactory"
                           request-channel="requestChannel"
                           reply-channel="replyChannel">
    <int-http:uri-variable name="city" expression="payload"/>
</int-http:outbound-gateway>

<bean id="requestFactory"
      class="org.springframework.http.client.SimpleClientHttpRequestFactory">
    <property name="connectTimeout" value="5000"/>
    <property name="readTimeout"    value="5000"/>
</bean>

21.5.1. HTTP Outbound Gateway

HTTP 아웃바운드 게이트웨이의 경우 XML 스키마는 reply-timeout만 정의한다. 이 reply-timeoutorg.springframework.integration.http.outbound.HttpRequestExecutingMessageHandler 클래스의 sendTimeout 프로퍼티에 매핑된다. 더 정확하게는, 이 프로퍼티는 AbstractReplyProducingMessageHandler를 상속한 클래스에 세팅돼서, 궁극적으로 MessagingTemplate에 있는 프로퍼티에 저장된다.

sendTimeout 프로퍼티의 기본값은 “-1”이며, 이 값은 연결돼있는 MessageChannel에 적용된다. 즉, 메시지 채널의 구현체에 따라 send 메소드가 무한정 블로킹될 수 있다는 뜻이기도 하다. 참고로, sendTimeout 프로퍼티는 실제 MessageChannel 구현체에서 메시지 전송이 블로킹되는 경우에만 사용한다 (유한bounded QueueChannel이 가득 찬 경우 등).

21.5.2. HTTP Inbound Gateway

HTTP 인바운드 게이트웨이의 경우, XML 스키마는 HttpRequestHandlingMessagingGateway 클래스(MessagingGatewaySupport 클래스를 상속하고 있는)의 requestTimeout 속성에 사용되는 request-timeout 속성을 정의한다. reply-timeout 속성을 사용하면 같은 클래스의 replyTimeout 속성도 매핑할 수 있다.

두 타임아웃 프로퍼티의 기본값은 모두 1000ms이다 (즉, 1000밀리세컨드=1초). request-timeout 프로퍼티는 궁극적으로 MessagingTemplate 인스턴스의 sendTimeout을 설정하는 데 사용한다. 반면 replyTimeout 프로퍼티는 MessagingTemplate 인스턴스의 receiveTimeout 프로퍼티를 설정하는 데 사용한다.

커넥션 타임아웃을 시뮬레이션해보려면, 10.255.255.10과 같이 라우팅이 불가능한 IP 주소에 연결해보면 된다.


21.6. HTTP Proxy configuration

앞에 프록시가 있어서, HTTP 아웃바운드 어댑터나 게이트웨이에 프록시 설정을 구성해야 하는 경우, 두 가지 방법 중 하나를 적용할 수 있다. 대부분의 경우 프록시 설정을 제어하는 표준 Java 시스템 프로퍼티를 이용하면 된다. 그 외는 HTTP 클라이언트 request 팩토리 인스턴스를 스프링 빈으로 직접 설정해주면 된다.

21.6.1. Standard Java Proxy configuration

세 가지 시스템 프로퍼티를 통해 HTTP 프로토콜 핸들러에서 사용할 프록시를 구성할 수 있다:

HTTPS에는 다음과 같은 프로퍼티들을 사용할 수 있다:

자세한 정보는 docs.oracle.com/javase/8/docs/technotes/guides/net/proxies.html을 확인해봐라.

21.6.2. Spring’s SimpleClientHttpRequestFactory

프록시를 직접 세팅하고 싶다면 다음과 같이 스프링의 SimpleClientHttpRequestFactory를 사용해 proxy 프로퍼티를 설정해주면 된다.

<bean id="requestFactory"
    class="org.springframework.http.client.SimpleClientHttpRequestFactory">
    <property name="proxy">
        <bean id="proxy" class="java.net.Proxy">
            <constructor-arg>
                <util:constant static-field="java.net.Proxy.Type.HTTP"/>
            </constructor-arg>
            <constructor-arg>
                <bean class="java.net.InetSocketAddress">
                    <constructor-arg value="123.0.0.1"/>
                    <constructor-arg value="8080"/>
                </bean>
            </constructor-arg>
        </bean>
    </property>
</bean>

21.7. HTTP Header Mappings

Spring Integration은 HTTP 요청과 HTTP 응답 모두, HTTP 헤더 매핑을 지원한다.

기본적으로, 별도 설정이 없어도 메시지에 있는 모든 표준 HTTP 헤더는 HTTP 요청/응답의 헤더로 매핑된다. 하지만 추가적으로 커스텀할 게 있다면, 네임스페이스를 이용해 설정을 추가해주면 된다. 헤더 이름들의 목록을 콤마로 구분해서 제공해주면 되는데, 와일드카드 역할을 하는 ‘*’ 문자가 들어있는 패턴도 사용할 수 있다. 헤더 값을 지정하면 기본 동작을 재정의하게 된다. 이 시점에는 사용자가 모든 것을 직접 제어한다고 가정한다. 하지만 표준 HTTP 헤더도 전부 포함시키고 싶다면 HTTP_REQUEST_HEADERSHTTP_RESPONSE_HEADERS도 함께 패턴으로 정의하면 된다. 다음은 두 가지 설정 예시다 (첫 번째 설정은 와일드카드를 사용한다):

<int-http:outbound-gateway id="httpGateway"
    url="http://localhost/test2"
    mapped-request-headers="thing1, thing2"
    mapped-response-headers="X-*, HTTP_RESPONSE_HEADERS"
    channel="someChannel"/>

<int-http:outbound-channel-adapter id="httpAdapter"
    url="http://localhost/test2"
    mapped-request-headers="thing1, thing2, HTTP_REQUEST_HEADERS"
    channel="someChannel"/>

어댑터와 게이트웨이는 DefaultHttpHeaderMapper를 사용하는데, DefaultHttpHeaderMapper는 이제 인바운드와 아웃바운드 어댑터를 위한 스태틱 팩토리 메소드를 각각 제공하므로, 각자에 맞는 방향으로 매핑해줄 수 있다 (HTTP 요청/응답을 상황에 맞게 in/out으로 매핑).

다른 것들을 더 커스텀해야 한다면, 별도로 DefaultHttpHeaderMapper를 설정해서 header-mapper 속성을 통해 어댑터에 주입해주는 것도 가능하다.

DefaultHttpHeaderMapper는 5.0 버전 이전에는 사용자가 정의하는 비표준 HTTP 헤더에 X-를 디폴트 프리픽스로 사용했었다. 5.0에선 이 디폴트 프리픽스를 비어있는 문자열로 변경했다. RFC-6648에 따르면 이런 프리픽스를 사용하는 것을 이제는 권장하지 않는다고 한다. 물론, DefaultHttpHeaderMapper.setUserDefinedHeaderPrefix() 프로퍼티를 설정하면 이 옵션을 커스텀할 수 있다. 다음은 HTTP 게이트웨이를 위한 헤더 매퍼를 설정하는 예시다:

<int-http:outbound-gateway id="httpGateway"
    url="http://localhost/test2"
    header-mapper="headerMapper"
    channel="someChannel"/>

<bean id="headerMapper" class="o.s.i.http.support.DefaultHttpHeaderMapper">
    <property name="inboundHeaderNames" value="thing1*, *thing2, thing3"/>
    <property name="outboundHeaderNames" value="a*b, d"/>
</bean>

현재 필요한 것이 DefaultHttpHeaderMapper의 지원 범위를 벗어난다면, 전략 인터페이스 HeaderMapper를 직접 구현해서, 구현체에 대한 참조를 제공해주면 된다.


21.8. Integration Graph Controller

HTTP 모듈은 4.3 버전부터 클래스 설정 어노테이션 @EnableIntegrationGraphController와 XML 요소 <int-http:graph-controller/>를 통해 REST 서비스로 IntegrationGraphServer를 노출해준다. 자세한 내용은 통합 그래프를 참고해라.


21.9. HTTP Samples

이번 섹션에선 몇 가지 예제와 함께 Spring Integration의 HTTP 지원에 대한 내용을 마무리하려 한다.

21.9.1. Multipart HTTP Request — RestTemplate (Client) and Http Inbound Gateway (Server)

이 예제를 통해, 스프링의 RestTemplate을 사용해 HTTP 멀티파트 요청을 보내고 Spring Integration의 HTTP 인바운드 어댑터로 수신하는 것이 얼마나 간단한지를 알아보려 한다. 여기서는 MultiValueMap을 생성해 멀티파트 데이터를 채운다. 나머지는 RestTemplateMultipartHttpServletRequest로 변환해서 처리해준다. 이 예제에선, 클라이언트가 회사의 이름과 이미지 파일(회사 로고)이 포함된 HTTP 멀티파트 요청을 전송한다고 가정한다:

RestTemplate template = new RestTemplate();
String uri = "http://localhost:8080/multipart-http/inboundAdapter.htm";
Resource s2logo =
   new ClassPathResource("org/springframework/samples/multipart/spring09_logo.png");
MultiValueMap map = new LinkedMultiValueMap();
map.add("company", "SpringSource");
map.add("company-logo", s2logo);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(new MediaType("multipart", "form-data"));
HttpEntity request = new HttpEntity(map, headers);
ResponseEntity<?> httpResponse = template.exchange(uri, HttpMethod.POST, request, null);

클라이언트에서 필요한 건 이게 전부다.

서버 측에선 다음과 같은 설정을 사용할 수 있다:

<int-http:inbound-channel-adapter id="httpInboundAdapter"
    channel="receiveChannel"
    path="/inboundAdapter.htm"
    supported-methods="GET, POST"/>

<int:channel id="receiveChannel"/>

<int:service-activator input-channel="receiveChannel">
    <bean class="org.springframework.integration.samples.multipart.MultipartReceiver"/>
</int:service-activator>

<bean id="multipartResolver"
    class="org.springframework.web.multipart.commons.CommonsMultipartResolver"/>

‘httpInboundAdapter’는 요청을 수신해서 LinkedMultiValueMap을 페이로드로 갖는 Message로 변환한다. 그런 다음엔 아래 보이는 service-activator ‘multipartReceiver’에서 페이로드를 파싱한다:

public void receive(LinkedMultiValueMap<String, Object> multipartRequest){
    System.out.println("## Successfully received multipart request ###");
    for (String elementName : multipartRequest.keySet()) {
        if (elementName.equals("company")){
            System.out.println("\t" + elementName + " - " +
                ((String[]) multipartRequest.getFirst("company"))[0]);
        }
        else if (elementName.equals("company-logo")){
            System.out.println("\t" + elementName + " - as UploadedMultipartFile: " +
                ((UploadedMultipartFile) multipartRequest
                    .getFirst("company-logo")).getOriginalFilename());
        }
    }
}

다음과 같은 문구가 출력되는 걸 볼 수 있을 거다:

## Successfully received multipart request ###
   company - SpringSource
   company-logo - as UploadedMultipartFile: spring09_logo.png

Next :
Apache Kafka Support
메시지 채널로부터 카프카 메시지를 발행하고, 카프카 메시지를 컨슘해 인티그레이션 플로우 실행하기

전체 목차는 여기에 있습니다.

<< >>

TOP