본 포스트는 공식 레퍼런스를 참고해 GraphQL을 공부하며 직접 작성한 가이드 입니다. 본 포스트는 2021년 7월 최신 버전인 v16.2를 기준으로 작성되어 있습니다.
비동기로 요청 처리하기
기본적으로 GraphQLResolver
는 동기적으로 작업을 처리하도록 설계되었다. 만약 내려줘야하는 데이터에서 시간이 오래걸리는 작업이 있다고 하자. 이런 작업은 비동기로 처리하도록 설계하는게 어플리케이션 성능에 긍정적인 영향을 미칠 확률이 높다. 이번에는 요청을 비동기로 처리하는 방법을 알아보자.
먼저 가져오는데 시간이 오래걸리는 데이터인 Bgm
을 WebToon
에 추가한다.
1 2 3 4 5 6 7 8 9 10 11 type WebToon { id: ID! webToonType: WebToonType title: String image: Image bgm: Bgm } type Bgm { url: String! }
그리고 이에따라 BgmResolver
도 추가해준다. 데이터가 가져오는데 시간을 오래걸림을 표현하기위해 3초간 지연을 시킨다. Image
또한 3초간 지연을 시키도록 추가한다.
1 2 3 4 5 6 7 8 9 10 11 @Slf4j @Component public class BgmResolver implements GraphQLResolver <WebToon> { public Bgm bgm (WebToon webToon, DataFetchingEnvironment env) { log.info("requesting bgm data for webtoon id: {}" , webToon.getId()); ThreadUtils.ThreadStop(3 ); return Bgm.builder().url("Bgm Url Resource" ).build(); } }
그리고 요청을 보낸 후 로그를 살펴보자.
1 2 3 4 2021-07-25 19:00:16.185 INFO 15392 --- [nio-8080-exec-2] c.s.g.resolver.webtoon.ImageResolver : get image data from local context. thumbnail : thumbnail URL 2021-07-25 19:00:16.185 INFO 15392 --- [nio-8080-exec-2] com.sumfi.graphql.utils.ThreadUtils : Stop http-nio-8080-exec-2 Thread 5 seconds 2021-07-25 19:00:21.194 INFO 15392 --- [nio-8080-exec-2] c.s.g.resolver.webtoon.BgmResolver : requesting bgm data for webtoon id: 3568e088-ec83-11eb-9a03-0242ac130003 2021-07-25 19:00:21.194 INFO 15392 --- [nio-8080-exec-2] com.sumfi.graphql.utils.ThreadUtils : Stop http-nio-8080-exec-2 Thread 5 seconds
하위 리졸버들은 모두 같은 스레드인 nio-8080-exec-2
에서 돌고있는걸 볼 수 있다. 이제 이 요청을 비동기로 돌려보자. 방법은 간단한데 CompletableFuture
를 반환하도록 하면 된다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 @Slf4j @Component @RequiredArgsConstructor public class BgmResolver implements GraphQLResolver <WebToon> { private final Executor webtoonTaskExecutor; public CompletableFuture<Bgm> bgm (WebToon webToon, DataFetchingEnvironment env) { log.info("requesting bgm data for webtoon id: {}" , webToon.getId()); return CompletableFuture.supplyAsync( () -> { log.info("getting bgms for webtoon id: {}" , webToon.getId()); ThreadUtils.ThreadStop(3 ); return Bgm.builder().url("Bgm Url Resource" ).build(); }, webtoonTaskExecutor); } } @Slf4j @Component @RequiredArgsConstructor public class ImageResolver implements GraphQLResolver <WebToon> { private final Executor webtoonTaskExecutor; public CompletableFuture<DataFetcherResult<Image>> image (WebToon webToon, DataFetchingEnvironment env) { log.info("requesting image data for webtoon id: {}" , webToon.getId()); return CompletableFuture.supplyAsync(() -> { Image image = env.getLocalContext(); log.info("get image data from local context. thumbnail : {}" , image.getThumbnail()); ThreadUtils.ThreadStop(3 ); return DataFetcherResult.<Image>newResult() .data(image) .build(); }, webtoonTaskExecutor); } }
그리고 요청을 비동기로 돌릴 executor bean
을 등록해주자.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Configuration public class AsyncConfiguration { @Bean(name = "webtoonTaskExecutor") public Executor threadPoolTaskExecutor () { ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor (); taskExecutor.setCorePoolSize(5 ); taskExecutor.setMaxPoolSize(10 ); taskExecutor.setQueueCapacity(100 ); taskExecutor.setThreadNamePrefix("webtoonTaskExecutor-" ); taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor .CallerRunsPolicy()); taskExecutor.initialize(); return taskExecutor; } }
이제 요청을 보내보고 디버깅을 해보자.
1 2 3 4 2021-07-25 19:30:38.514 INFO 15588 --- [nTaskExecutor-1] c.s.g.resolver.webtoon.ImageResolver : get image data from local context. thumbnail : thumbnail URL 2021-07-25 19:30:38.514 INFO 15588 --- [nTaskExecutor-1] com.sumfi.graphql.utils.ThreadUtils : Stop webtoonTaskExecutor-1 Thread 3 seconds 2021-07-25 19:30:38.515 INFO 15588 --- [nTaskExecutor-2] c.s.g.resolver.webtoon.BgmResolver : getting bgms for webtoon id: 3568e088-ec83-11eb-9a03-0242ac130003 2021-07-25 19:30:38.515 INFO 15588 --- [nTaskExecutor-2] com.sumfi.graphql.utils.ThreadUtils : Stop webtoonTaskExecutor-2 Thread 3 seconds
ImageResolver
와 BgmResolver
가 각각 1번, 2번 스레드에서 돌고 있음을 볼 수 있다.
Repository
모든 가이드의 예제 코드는 SongHayoung/springboot-graphql-tutorial 에서 확인할 수 있습니다.