기본 패턴
java
CompletableFuture<String> future =
CompletableFuture.supplyAsync(() -> "hello")
.thenApply(String::toUpperCase);
String result = future.join();설명
Future가 "나중에 결과를 받을 수 있다"는 개념에 머문다면,CompletableFuture는 그 위에 "그 결과를 기반으로 다음 작업을 계속 이어 붙일 수 있다"는 조합 모델을 제공합니다.supplyAsync,runAsync로 비동기 작업을 시작하고,thenApply,thenCompose,thenCombine,allOf같은 메서드로 후속 작업을 연결합니다. 이때 중요한 것은 스레드 자체보다 작업 흐름을 값처럼 다룬다는 점입니다.thenApply는 결과를 다른 값으로 바꿀 때,thenCompose는 "비동기 결과 위에 또 비동기 작업"을 평평하게 이어 붙일 때 자주 씁니다. 이 차이가 익숙해지면 비동기 코드 구조가 훨씬 선명해집니다.- 예외 처리도 중요한 축입니다.
exceptionally,handle,whenComplete는 모두 비슷해 보여도 "복구할 것인가", "관찰만 할 것인가"에서 의도가 다릅니다. CompletableFuture의 핵심은 비동기 자체보다 조합(composition)입니다. 비동기 단계가 여러 개 연결될 때 callback 지옥 대신 데이터 흐름처럼 읽히게 만드는 것이 가장 큰 가치입니다.
빠른 정리
| 메서드 | 잘 맞는 상황 |
|---|---|
supplyAsync | 값을 반환하는 비동기 시작 |
thenApply | 결과를 동기적으로 변환 |
thenCompose | 다음 비동기 작업으로 연결 |
allOf | 여러 작업 완료까지 대기 |
join | 결과 기다리기 |
주의할 점
CompletableFuture를 써도 마지막에 바로 join()을 남발하면 결국 동기 코드처럼 병목이 생길 수 있습니다.
어디서 기다리고, 어디까지 비동기 흐름으로 유지할지 의식적으로 정하는 편이 중요합니다.
참고 링크
1 sources