[Camel in Action] 3-4. Camel 타입 컨버터 메커니즘과 커스텀 컨버터 작성

타입 컨버터가 왜 필요한가?

Camel 라우트에서 메시지 본문은 다양한 타입일 수 있습니다. 어떤 컴포넌트는 String을 기대하고, 다른 컴포넌트는 InputStream을 기대합니다. 타입 컨버터(Type Converter)는 이런 타입 불일치를 자동으로 해결해줍니다.

타입 컨버터 동작 원리

Camel은 시작 시 클래스패스를 스캔해 @Converter 어노테이션이 달린 메서드들을 TypeConverterRegistry에 등록합니다. 이후 getBody(TargetType.class)가 호출되면 적합한 컨버터를 찾아 자동 변환합니다.

내장 타입 컨버터 활용

// 자동 변환 - 내장 컨버터 사용
from("direct:test")
    .process(e -> {
        // String → Integer 자동 변환
        Integer num = e.getIn().getBody(Integer.class);

        // String → InputStream 자동 변환
        InputStream is = e.getIn().getBody(InputStream.class);

        // byte[] → String 자동 변환
        String text = e.getIn().getBody(String.class);
    });

// convertBodyTo() DSL
from("direct:input")
    .convertBodyTo(String.class)  // 본문을 String으로 변환
    .convertBodyTo(String.class, "UTF-8")  // 인코딩 지정
    .log("${body}");

커스텀 타입 컨버터 작성

// 1. @Converter 클래스 정의
@Converter(generateBulkLoader = true)  // 성능 최적화
public class OrderTypeConverter {

    @Converter
    public static Order fromString(String csv) {
        String[] parts = csv.split(",");
        return new Order(parts[0], parts[1], Double.parseDouble(parts[2]));
    }

    @Converter
    public static String toString(Order order) {
        return order.getId() + "," + order.getCustomer() + "," + order.getAmount();
    }

    @Converter
    public static Order fromMap(Map<String, String> map) {
        return new Order(map.get("id"), map.get("customer"),
                        Double.parseDouble(map.get("amount")));
    }
}
// 2. TypeConverterLoader 파일 생성
// src/main/resources/META-INF/services/org/apache/camel/TypeConverterLoader
// 내용: com.example.OrderTypeConverter

@Converter 어노테이션 옵션

  • @Converter: 기본 컨버터 메서드 표시 (static 또는 instance 메서드 모두 가능)
  • @FallbackConverter: 매칭되는 컨버터가 없을 때 최후 수단으로 호출
@Converter
public class FallbackConverter {
    @FallbackConverter
    public static <T> T convertTo(Class<T> type, Exchange exchange, Object value,
                                   TypeConverterRegistry registry) {
        // 커스텀 폴백 로직
        if (type == Order.class && value instanceof Map) {
            return (T) new Order((Map) value);
        }
        return null;
    }
}

TypeConverter 성능 최적화

// 벌크 로더 방식 (시작 시 빠른 등록)
@Converter(generateBulkLoader = true)
public class FastConverter {
    @Converter
    public static Order fromJson(String json, Exchange exchange) {
        ObjectMapper mapper = exchange.getContext()
            .getRegistry().lookupByNameAndType("objectMapper", ObjectMapper.class);
        try {
            return mapper.readValue(json, Order.class);
        } catch (Exception e) {
            return null;
        }
    }
}

TypeConverterRegistry 직접 접근

@Autowired
CamelContext camelContext;

void registerConverter() {
    TypeConverterRegistry registry = camelContext.getTypeConverterRegistry();
    registry.addTypeConverter(Order.class, String.class,
        (type, exchange, value) -> Order.fromCsv((String) value));
}

Leave a Comment