프로젝트 (Java)/예약마켓

[프로젝트] 38. WebClient 활용 Rest API 구현

hihyuk 2024. 2. 2. 16:01

멀티 모듈 아키텍처를 적용한 프로젝트에서 각 모듈 간의 통신을 위해 Spring의 WebClient를 사용하는 것은 비동기적이고 논블로킹 방식으로 서비스 간 통신을 가능하게 하여, 시스템의 전체적인 반응성과 성능을 개선합니다. 이러한 접근 방식은 특히 마이크로서비스 아키텍처(MSA)에서 선호되며, 각 서비스가 독립적으로 개발 및 배포될 수 있도록 지원합니다.

 

Newsfeed 모듈과 Activity 모듈

Newsfeed 모듈과 Activity 모듈은 사용자의 활동(예: 게시물 생성, 댓글 달기, 좋아요 클릭)과 관련된 데이터를 관리하고, 이를 바탕으로 사용자의 뉴스피드를 생성합니다. 두 모듈 간의 통신은 내부 API를 통해 이루어지며, WebClient를 사용하여 구현합니다.

Newsfeed 모듈

Activity 모듈

 

SecurityConfig의 업데이트

각 모듈의 SecurityConfig에는 내부 통신을 위한 API 경로(/api/internal/**)를 인증 없이 접근할 수 있도록 설정합니다. 이는 서비스 내부에서의 상호작용을 원활하게 하며, 보안 문제를 방지합니다.

.antMatchers("/api/auth/join",
        "/api/auth/login",
        "/swagger-ui/**",
        "/swagger-resources/**",
        "/v3/api-docs/**",
        "/api/internal/**")  // 추가
.permitAll() // 인증 필요 없는 URL 설정

 

NewsFeedRestApiController와 PostRestApiController

NewsFeedRestApiControllerPostRestApiController는 내부 API를 통해 다른 모듈들과의 통신을 처리합니다. 예를 들어, 특정 사용자의 모든 알림을 삭제하거나, 특정 사용자의 게시물을 조회하는 등의 기능을 제공합니다.

NewsFeedRestApiController

@RestController
@RequestMapping("/api/internal/feeds")
@RequiredArgsConstructor
public class NewsFeedRestApiController {
    private final NewsFeedService newsFeedService;

    @DeleteMapping("/user/{userId}")
    public ResponseEntity<?> deleteNewsfeedByUserId(@PathVariable Long userId) {
        // 사용자 ID를 기반으로 게시물 삭제
        newsFeedService.deleteNewsfeedByUserId(userId);
        return ResponseEntity.ok().build();
    }

    @PostMapping("/notis")
    public ResponseEntity<?> createNotification(@RequestBody NotificationCreateRequest request) {
        newsFeedService.createNotification(request);
        return ResponseEntity.ok().build();
    }

    @DeleteMapping("/notis/type/{typeId}")
    public ResponseEntity<?> deleteNotificationByTypeId(@PathVariable Long typeId) {
        newsFeedService.deleteNotificationByTypeId(typeId);
        return ResponseEntity.ok().build();
    }

    @DeleteMapping("/notis/post/{postId}")
    public ResponseEntity<?> deleteNotificationByPostId(@PathVariable Long postId) {
        newsFeedService.deleteNotificationByPostId(postId);
        return ResponseEntity.ok().build();
    }
}

NewsFeedService

    // RestApi
    @Transactional
    public void deleteNewsfeedByUserId(Long userId) {
        notificationRepository.deleteAllByToUserId(userId);
        notificationRepository.deleteAllByFromUserId(userId);
        followRepository.deleteAllByFollowerId(userId);
        followRepository.deleteAllByFollowingId(userId);
    }

    @Transactional
    public void createNotification(NotificationCreateRequest request) {
        notificationRepository.mSave(request.getFromUserId(), request.getToUserId(),
                request.getType(), request.getPostId(), request.getTypeId());
    }

    @Transactional
    public void deleteNotificationByTypeId(Long typeId) {
        notificationRepository.deleteAllByTypeId(typeId);
    }

    @Transactional
    public void deleteNotificationByPostId(Long postId) {
        notificationRepository.deleteAllByPostId(postId);
    }

PostRestApiController

@RestController
@RequestMapping("/api/internal/posts")
@RequiredArgsConstructor
public class PostRestApiController {
    private final PostService postService;

    @DeleteMapping("/user/{userId}")
    public void deletePostsByUserId(@PathVariable Long userId) {
        postService.deleteAllByUserId(userId);
    }

    @PostMapping("/users")
    public List<PostResponseDto> getPostsByUserIds(@RequestBody List<Long> userIds) {
        return postService.getPostsByUserIds(userIds);
    }

}

 

WebClient를 활용한 REST API 통신

WebClient를 사용하여 다른 모듈의 REST API를 호출합니다. 이는 비동기적인 방식으로 서비스 간 통신을 가능하게 하며, 시스템의 전체적인 반응성을 향상시킵니다. 예를 들어, 게시물이 삭제될 때 관련된 모든 알림을 삭제하는 요청을 Newsfeed 모듈의 NewsFeedService에서 Activity 모듈로 보낼 수 있습니다.

 

        webClient.delete()
                .uri("http://localhost:8081/api/internal/feeds/notis/post/" + postId)
                .retrieve()
                .bodyToMono(Void.class)
                .block();
                
        webClient.delete()
                .uri("http://localhost:8081/api/internal/feeds/notis/type/" + like.getId())
                .retrieve()
                .bodyToMono(Void.class)
                .block();
                
        webClient.delete()
                .uri("http://localhost:8081/api/internal/feeds/notis/type/" + commentId)
                .retrieve()
                .bodyToMono(Void.class)
                .block();
        webClient.post()
                .uri("http://localhost:8081/api/internal/feeds/notis")
                .bodyValue(request)
                .retrieve()
                .toBodilessEntity()
                .block();

 

결론

이러한 방식으로, 각 모듈은 독립적으로 배포 및 운영될 수 있으며, WebClient를 사용한 REST API 통신을 통해 간결하고 유연한 서비스 간 상호작용을 구현할 수 있습니다. 이는 마이크로서비스 아키텍처(MSA)의 주요 장점 중 하나로, 개별 서비스의 개발, 배포, 확장이 용이해집니다.