hyeonga_code
Project_15_CompletableFuture 적용하기 본문
Url을 저장하는 다이어리가 주제인 프로젝트를 작업 중이다.
CompletableFuture를 사용하여 비동기 작업을 처리하는 것은 주로 서비스 계층에서 데이터베이스 접근 및 캐시 작업과 관련된 부분에서 사용할 수 있다.
데이터베이스의 저장, 조회, 업데이트, 삭제 작업을 비동기로 처리하여 응답 시간을 단축하여 서비스의 성능을 향상시키고 대기 시간을 줄일 수 있다.
현재 프로젝트에서 캐시를 사용하여 카테고리 관련 기능을 구현했는데 카테고리 수정 시 캐시를 업데이트하고 데이터베이스에 수정 사항을 저장하는 작업을 비동기로 처리할 수 있다.
CategoryService
> 이전 코드
@CachePut(value = "category", key = "#userId")
public Category updateCategory(Category category, Long userId) {
Logger.servicelogging("save");
Category existCategory = categoryRepository.findByUserId(userId)
.orElseThrow(CategoryNotFoundException::new);
existCategory.setCategoryTree(category.getCategoryTree() != null ?
category.getCategoryTree() : existCategory.getCategoryTree());
return categoryRepository.save(existCategory);
}
> 수정 코드
@CachePut(value = "category", key = "#userId")
public CompletableFuture<Category> updateCategory(Category category, Long userId) {
Logger.servicelogging("save");
log.info("비동기 처리 전");
CompletableFuture<Category> result = CompletableFuture.supplyAsync(() -> {
log.info("비동기 처리 시작");
Category existCategory = categoryRepository.findByUserId(userId)
.orElseThrow(CategoryNotFoundException::new);
log.info("비동기 처리 중 : categoryRepository.findByUserId()");
existCategory.setCategoryTree(category.getCategoryTree() != null ?
category.getCategoryTree() : existCategory.getCategoryTree());
log.info("비동기 처리 완료 : existCategory.setCategoryTree()");
return categoryRepository.save(existCategory);
});
log.info("비동기 처리 후");
return result;
}
CategoryController
> 이전 코드
@PutMapping
public CategoryResponseDto updateCategory(@RequestBody CategoryRequestDto categoryRequest) {
Logger.logging("updateCategory");
return CategoryResponseDto
.fromEntity(categoryService.updateCategory(
categoryRequest.toEntity(), tokenService.getUserId()));
}
> 수정 코드
@PutMapping
public CompletableFuture<CategoryResponseDto> updateCategory(
@RequestBody CategoryRequestDto categoryRequest) {
Logger.logging("updateCategory");
return categoryService.updateCategory(
categoryRequest.toEntity(), tokenService.getUserId()
).thenApply(CategoryResponseDto::fromEntity);
}
CategoryServiceTest
> 수정 코드
@Test
@DisplayName("update_success") // CompletableFuture 적용
void updateCategory_success() throws ExecutionException, InterruptedException {
when(categoryRepository.findByUserId(anyLong())).thenReturn(Optional.ofNullable(category));
when(categoryRepository.save(any(Category.class))).thenReturn(category);
CompletableFuture<Category> result
= categoryService.updateCategory(categoryRequest.toEntity(), 1L);
Category update = result.get();
assertNotNull(update);
assertEquals(categoryTree, update.getCategoryTree());
verify(categoryRepository, times(1)).findByUserId(anyLong());
verify(categoryRepository, times(1)).save(any(Category.class));
}
> 실행
// Test worker >> Main 스레드
[Test worker] INFO -- ----- save -----
[Test worker] INFO -- 비동기 처리 전
// 비동기 작업 이후 코드가 먼저 출력된다.
[Test worker] INFO -- 비동기 처리 후
// ForkJoinPool.commonPool-worker-1 >> Main 스레드가 아닌 다른 스레드에서 실행
[ForkJoinPool.commonPool-worker-1] INFO -- 비동기 처리 시작
[ForkJoinPool.commonPool-worker-1] INFO -- 비동기 처리 중 : categoryRepository.findByUserId()
[ForkJoinPool.commonPool-worker-1] INFOe -- 비동기 처리 완료 : existCategory.setCategoryTree()
1. Test worker > 기존에 사용되고 있는 메인 스레드이다.
2. ForkJoinPool.commonPool-worker-1 > CompletableFuture에서 사용하는 스레드로 ForkJoinPool의 스레드이다.
3. 메인 스레드는 비동기 작업이 완료되기 전에 다음 코드를 실행한다.
즉, 데이터베이스 쿼리가 실행되기 전, 즉 비동기 작업이 시작되기 전에 "비동기 처리 후" 로그가 출력된다.
4. 비동기 작업을 다른 스레드에서 실행한다.(메인스레드가 ForkJoinPool의 스레드에 요청)
로그 확인하기
비동기 처리 전 : 비동기 작업이 시작되기 전에 출력된다.
비동기 처리 후 : 비동기 작업이 시작되기 전에 출력된다.
비동기 처리 시작 : 비동기 작업이 새로운 스레드에서 시작되었음을 나타낸다.
비동기 처리 중 :데이터베이스 조회가 실행되고 있음을 나타낸다.
비동기 처리 완료 : 비동기 작업이 완료되었음을 나타낸다.
'Project_HYEONGARL' 카테고리의 다른 글
Project_16_ForkJoinPool (0) | 2024.07.12 |
---|---|
Sync/Async Blocking/Non-Blocking 가 대체 뭐야? 이해하기 (0) | 2024.06.27 |
Project_14_CompletableFuture는 무엇인가 (0) | 2024.06.25 |
Project_13_카테고리 기능 구현 Cache 적용하기 (0) | 2024.06.21 |
Project_12_카테고리 기능 구현 (0) | 2024.06.18 |