🌟 Java 초보 개발자를 위한 비동기 체이닝 클래스 만들기: AsyncChain<T> 완전 분석

2025. 7. 2. 18:32Spring

반응형

이번에는 CompletableFuture를 더 쉽게, 더 깔끔하게 쓸 수 있도록 도와주는 AsyncChain<T> 클래스를 소개하고, 그 내부 동작을 하나하나 설명해드리겠습니다.

Java를 처음 배우다 보면 비동기 처리, 콜백 헬, 체이닝 같은 개념이 헷갈릴 수 있는데요. AsyncChain<T>는 이 복잡한 흐름을 마치 레고 조립하듯 직관적으로 이어갈 수 있도록 도와주는 클래스입니다.

 

 

🧩 AsyncChain<T>가 왜 필요할까?

Java에는 이미 CompletableFuture라는 강력한 비동기 API가 있지만...

  • 체이닝할 때 코드가 복잡해지고
  • 예외 처리, 병렬 처리 등에서 코드가 산으로 갈 수 있어요.

👉 그래서 만든 게 바로 AsyncChain<T>. CompletableFuture를 래핑(wrapping)해서 더 읽기 쉽고, 유지보수가 쉬운 방식으로 만들었어요.

 

 

🔨 기본 구조

public class AsyncChain<T> {
    private final CompletableFuture<T> future;
    private final ExecutorService executor;

    // 기본 스레드 풀
    private static final ExecutorService DEFAULT_EXECUTOR = ForkJoinPool.commonPool();
}

//설명: 
T: 작업 결과의 타입입니다.
future: 실제 비동기 작업이 들어 있는 CompletableFuture.
executor: 스레드 실행을 담당하는 ExecutorService입니다.

 

🚀 시작하는 법

//1. 값으로 시작하기
AsyncChain.of(10)

//→ 10이라는 값을 가진 체인을 만들어줍니다. 마치 Future.completedFuture(10) 과 비슷하죠.

//2. 비동기 Supplier로 시작하기
AsyncChain.supply(() -> {
    return 10 * 2;
})

//→ 내부적으로 CompletableFuture.supplyAsync(...)를 사용합니다. 계산은 비동기로 진행돼요.

 

🔗 체이닝 메서드

// 1. map(Function): 값 변환

AsyncChain.of(10)
    .map(x -> x * 2) // 20
// → 동기적으로 값을 변환합니다. thenApply를 쓰고 있어요.


// 2. flatMap(Function): 체인을 이어붙이기
AsyncChain.of(10)
    .flatMap(x -> AsyncChain.of(x * 3)) // 30
    
//→ 중첩된 AsyncChain을 펼쳐줍니다. thenCompose 사용.


// 3. peek(Consumer): 사이드 이펙트용
AsyncChain.of(10)
    .peek(x -> System.out.println("결과: " + x))

// → 디버깅할 때 유용합니다. 값을 바꾸지는 않고, 그냥 엿보기만 해요.

// 4. doIf(Predicate, Consumer): 조건에 따라 실행
AsyncChain.of(10)
    .doIf(x -> x > 5, x -> System.out.println("5보다 큽니다"))
    
    
// 5. filter(Predicate): 조건 필터링
AsyncChain.of(10)
    .filter(x -> x > 5)
// → Optional<T>을 반환하는 체인으로 바뀝니다.

 

 

 

 

⛓️ 병렬 작업 (Parallel Execution)

// 1. 여러 개를 병렬로 실행하기
AsyncChain.supplyAll(() -> 1, () -> 2, () -> 3)
//→ 각 Supplier를 병렬로 실행한 뒤, 결과 리스트로 반환합니다.

// 2. 리스트에 대해 병렬로 map 하기
AsyncChain.ofAll(1, 2, 3)
    .mapParallel(x -> x * 10) // [10, 20, 30]
// → mapParallel은 내부적으로 각 요소를 별도의 스레드로 실행합니다.

 

 

🛠️ 예외 처리

// 1. 실패 시 기본값 반환
AsyncChain.supply(() -> 1 / 0)
    .orElse(-1) // 예외 발생 시 -1 반환
    
// 2. 실패 시 복구 함수 적용
.orElse(...) 대신 recover(...) 사용 가능

// 3. 재시도
AsyncChain.supply(() -> {
    if (Math.random() < 0.7) throw new RuntimeException();
    return "성공!";
}).retry(3, 1000) // 최대 3번 시도, 각 1초 간격
// → 실패하면 일정 시간 후 재시도합니다. 재시도 횟수가 남아 있으면 계속 해봐요.

// 4. 타임아웃
.timeout(2, TimeUnit.SECONDS)
// → 주어진 시간 내 완료되지 않으면 TimeoutException 발생.

 

 

🔚 결과 가져오기

.get() // 블로킹
.getNow(defaultValue) // 논블로킹
.onSuccess(result -> { ... }) // 성공 콜백
.onFailure(e -> { ... }) // 실패 콜백

 

 

🧠 핵심 요약

기능 메서드
기본 생성 of, supply, supplyAll
변환 map, flatMap, mapParallel
조건 처리 filter, doIf, peek
예외 처리 recover, orElse, retry, timeout
종료 처리 get, getNow, onSuccess, onFailure

 

 

🎁 마치며

이 AsyncChain<T> 클래스는 실무에서도 비동기 흐름을 깔끔하게 표현할 수 있도록 설계된 유틸리티입니다. 초보 개발자분들도 체이닝 방식에 익숙해지면, 복잡한 로직도 훨씬 명확하게 표현할 수 있어요.

반응형