Camel을 REST 클라이언트로 사용하기
Camel은 REST 서버뿐 아니라 클라이언트로도 탁월합니다. 외부 API를 호출하고 응답을 처리하는 복잡한 로직을 라우트로 표현하면 유지보수가 훨씬 쉬워집니다.
기본 REST API 호출
// GET 요청
from("timer:apiCall?period=60000")
.to("rest:get:https://api.example.com/users?bridgeEndpoint=true")
.unmarshal().json(JsonNode.class)
.process(exchange -> {
JsonNode users = exchange.getIn().getBody(JsonNode.class);
// 응답 처리
});
// POST 요청
from("direct:createUser")
.marshal().json()
.setHeader(Exchange.HTTP_METHOD, constant("POST"))
.setHeader(Exchange.CONTENT_TYPE, constant("application/json"))
.to("http4://api.example.com/users?bridgeEndpoint=true")
.unmarshal().json(UserResponse.class);
OAuth 2.0 인증 처리
현대 API는 대부분 OAuth 토큰 인증이 필요합니다. Camel에서 토큰을 자동으로 갱신하는 패턴입니다.
// 토큰 캐싱 빈
@Component
public class TokenCache {
private String token;
private Instant expiry;
public String getValidToken() {
if (token == null || Instant.now().isAfter(expiry)) {
refreshToken();
}
return token;
}
}
// 라우트에서 토큰 주입
from("direct:secureApiCall")
.process(exchange -> {
String token = tokenCache.getValidToken();
exchange.getIn().setHeader("Authorization", "Bearer " + token);
})
.to("http4://secure-api.example.com/data?bridgeEndpoint=true");
응답 코드별 처리 분기
API 응답 코드에 따라 다른 처리가 필요할 때는 Choice EIP를 활용합니다.
from("direct:apiCall")
.to("http4://api.example.com/resource?bridgeEndpoint=true&throwExceptionOnFailure=false")
.choice()
.when(header(Exchange.HTTP_RESPONSE_CODE).isEqualTo(200))
.to("bean:successHandler")
.when(header(Exchange.HTTP_RESPONSE_CODE).isEqualTo(404))
.log("리소스 없음")
.stop()
.when(header(Exchange.HTTP_RESPONSE_CODE).isEqualTo(429))
.log("요청 한도 초과, 재시도")
.delay(60000)
.to("direct:apiCall") // 재귀 호출로 재시도
.otherwise()
.to("direct:errorHandler")
.end();
재시도와 폴백 패턴
외부 API는 일시적으로 실패할 수 있습니다. Camel의 Retry와 Circuit Breaker로 복원력 있는 클라이언트를 구현합니다.
from("direct:resilientApiCall")
.circuitBreaker()
.resilience4jConfiguration()
.slidingWindowSize(10)
.failureRateThreshold(50)
.waitDurationInOpenState(30)
.end()
.to("http4://flaky-api.example.com/data")
.onFallback()
.setBody(simple("{"source":"cache","data":${bean:cacheService.getLatest}}"))
.end();
페이지네이션 자동 처리
대용량 데이터를 페이지 단위로 가져오는 API를 처리할 때는 동적 라우팅으로 반복 호출을 자동화합니다.
from("direct:fetchAllPages")
.setHeader("page", constant(1))
.loopDoWhile(simple("${header.hasMore} == 'true' || ${header.page} == 1"))
.toD("http4://api.example.com/items?page=${header.page}&bridgeEndpoint=true")
.process(exchange -> {
PagedResponse resp = exchange.getIn().getBody(PagedResponse.class);
exchange.getIn().setHeader("hasMore", resp.hasNextPage());
exchange.getIn().setHeader("page", resp.getPage() + 1);
})
.to("direct:processPage")
.end();