백엔드 로직의 모든 부분을 동기 방식으로 처리하게 되면,
해당 결과가 도출될 때까지 무한정 대기해야한다.
심지어 처리한 로직 부분이 대규모 트래픽을 다루는 API라면 스레드 설정에 따라
Exception을 내뱉거나 스레드 락에 걸릴 수 있다.
위 이미지에서 보듯이, 동기적으로 데이터를 처리할 때는 1~4번의 스레드가 순차적으로 처리되며
작업이 종료되어야 다음 스레드의 처리 순서가 온다.
하지만, 요청된 결과를 받기 전에 다음 처리를 할 수 있는 비동기 처리를 통해 로직을 개선할 수 있다.
JAVA 8 버전 이하에서는 [ Future ] 라는 내장 인터페이스를 통해 비동기 처리를 지원했는데, 해당 인터페이스를 이용해서
구현하려면 스레드의 맥스 타임아웃을 설정하거나 구현 인터페이스 내에서 Exception을 처리하는 작업 등
사용하기 까다로운 경향이 있다.
이에, JAVA 8 버전에서 [CompletableFuture] 클래스를 사용할 수 있게 되고
이전의 Future 보다 더 간결한 코드를 작성할 수 있도록 도와준다.
▼ CompletableFuture 클래스를 구현하여 비동기 처리 진행
CompletableFuture<String> future = new CompletableFuture<>();
int[] arr = {1,2,3,4,5};
new Thread(() -> {
System.out.println("thread start");
for(int a : arr){
if(a == arr[arr.length-1])
future.complete("배열의 마지막은 역시 "+ a);
}
}).start();
System.out.println(future.get());
처리 작업 이후 CompletableFuture 내장 메소드 complete를 통해 담긴 값을 출력하는 예제다.
기본적으로 complete 메소드를 통해 future의 값을 꺼내 사용할 수 있다.
결과 ▼
▼ Thread 객체를 생성하지 않고 처리 진행
int[] arr = {1,2,3,4,5};
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
System.out.println("thread start");
String data = "없네";
for(int a : arr){
if(a == arr[arr.length-1])
data = "배열의 마지막은 역시 "+ a;
}
return data;
}).thenApply((result)->{
System.out.println("result complete");
return result + "0";
});
System.out.println(future.get());
해당 구문에서 supplyAsync 함수를 사용하여 Thread를 직접 생성하지 않고 비동기 처리를 진행했다.
해당 처리에서 thenApply의 역할은 비동기 처리의 결과를 return 할 때
리턴받은 결과를 처리하거나 가공할 때 사용된다.
결과 ▼
또한, 아래와 같이 에러 처리에 대한 handle 메소드도 제공된다.
int[] arr = {1,2,3,4,5};
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
System.out.println("thread start");
String data = "없네";
for(int a : arr){
if(a == arr[arr.length-1])
data = "배열의 마지막은 역시 "+ a;
}
return data;
}).thenApply((result)->{
System.out.println("result complete");
return result + "0";
}).handle((result,throwable) -> {
System.out.println("에러 발생 ! :" + throwable);
return "에러 문구 ! : "+throwable;
});
System.out.println(future.get());
▼ 리스트 CompletableFuture 클래스를 선언해 사용
List<String> arr = new ArrayList<>();
arr.add("게임");
arr.add("축구");
arr.add("공부");
arr.add("식사");
arr.add("야구");
arr.add("양치");
List<CompletableFuture<String>> futureList =
arr.stream()
.map(work -> CompletableFuture.supplyAsync(()->{
System.out.println(work + " 하는 중입니다.");
return work;
})).map(future -> future.thenApply(apply -> {
System.out.println(apply + " 끝냈습니다.");
return apply;
})).toList();
비동기 처리의 특징이 드러나면서 아래와 같이 뒤죽박죽 인간이 만들어질 수 있다.
결과 ▼
중규모 정도의 푸시 메세지 발송이나 적당한 데이터 처리의 경우 비동기 처리를 통해
백엔드 로직을 우선 개선해보고 난 다음에
서버 분리나 RabbitMq 같은 큐 형식의 메세지 브로커를 사용해보면 좋을 것 같다.
'개발 > BACK' 카테고리의 다른 글
사람들이 많이 실수하는 개발 실수들 (0) | 2024.07.08 |
---|---|
[RDB MariaDB] 자주 실수하는 쿼리 오류들 모음 (0) | 2023.11.01 |
[MariaDB] SQL 오류 (1242): Subquery returns more than 1 row (0) | 2023.10.26 |
[Tableau] 데이터 분석 툴 연동 방법 [개발자 VER] (0) | 2023.01.09 |
[Mysql] 실행계획 (Explain)에 따른 쿼리튜닝 방법 (0) | 2023.01.06 |