마이크로서비스 아키텍처에서 RabbitMQ를 활용한 내부 통신: 메일 및 SMS 서비스 구현 사례
마이그레이션 전 소스코드
※실제 코드는 아니지만, 구조는 동일했습니다.
@Service
@RequiredArgsConstructor
public class SignUpService {
private final NotiService notiService;
public void signUp() {
//회원가입 로직
...
...
//노티전송 로직
...
...
}
}
위에 코드에서 signUp() 안의 내용을 들여다 보면
1)회원가입
2)노티전송
두가지 역할을 하고있습니다.
한 서비스에 여러 로직이 함께 있는 경우 몇 가지 단점이 있습니다.
● 단일 책임 원칙 위반: 각 서비스는 특정 기능 또는 역할을 담당해야 한다는 원칙입니다.
● 유연성 감소 : 서비스가 다양한 역할을 수행하면 변경이 발생했을 때 다른 부분에도 영향을 미칠 수 있습니다.
● 확장성 문제 : 노티 로직이 비즈니스 로직과 결합되어 있으면, 노티 로직을 확장하기에 어려울수 있습니다.
● 유지보수 어려움 : 여러 기능이 한 곳에 모여있으면 코드를 이해하고 수정하기가 어려울 수 있습니다.
위와 같은 이유로 노티서비스를 아예 분리를 해볼까 합니다.
모듈 분리
기존에는 한 프로젝트 안에 노티서비스도 다 들어있었지만,
노티서비스를 한 프로젝트 단위로 빼서 새로 띄울 계획입니다.
우선 아래 사진은 노티서비스 프로젝트 내부 모듈로 쪼개논 모습입니다.
● module-core, module-database : 공통으로 쓰이는 코어부분과 데이터베이스 부분입니다.
● module-redis, module-rabbitmq : 공통으로 쓰이는 redis, rabbitmq 설정 및 세팅 소스코드 부분입니다.
● module-sms, module-mail : 실제로 쓰이는 sms 모듈과 mail 모듈을 분리해놓았습니다.
내부 통신
자 이제 기존 서비스도 떠있고, 노티 서비스도 떠있습니다.
각자 서비스가 잘 떠있으니, 기존 서비스에서 필요한 노티 서비스 API를 요청해오면 됩니다.
하지만 내부 통신 시 API 호출은 단점이 분명 존재합니다.
● 성능문제 : API호출은 네트워크 오버헤드와 대기 시간을 유발 할 수 잇습니다.
● 장애 전파 : 호출되는 서비스의 장애가 호출하는 서비스에 영향을 줄 수 있습니다.
여러 문제점이 있었습니다.
메세지큐 도입 (Rabbit MQ 사용함)
그래서 위와같은 단점을 보완하기 위해 메세지큐를 사용하기로 하였습니다.
메세지큐를 사용하면 좋은점이 많습니다.
● 비동기 통신 : 응답 대기 시간을 줄이고, 서비스 간의 결합도를 낮출 수 있습니다.
● 확장성 : 분산형 아키텍처를 지원함으로 서비스 확장에 용이합니다.
● 결함 격리/데이터 신뢰성 : 메세지를 큐에 보내고 다른 서비스에서 처리할 수 있으므로, 장애가 발생해도 메세지 유실이 되지않습니다.
1. 회원가입을 담당하는 로직입니다.
2. notiService.sendNoti(); => 노티 전송을 담당하는 로직입니다.
◆ notiSerivce.sendNoti() 내부로직은 아래 그림과같이 메세지를 담아 큐에 전송하는 로직이 전부입니다.
그 후 처리는 따로 띄어있는 노티프로젝트가 임무를 완수합니다.
[NotiService]
amqpTemplate.convertAndSend(); 를 통해 메시지를 담아 큐로 전송해줍니다.
위의 그림은 따로 띄어놓았던 노티서비스 프로젝트 안의 코드입니다.
@RabbitListener를 이용하여 큐안에 있는 메세지를 받아와서 처리를 해줍니다.
결론
이러한 구조에서는 회원가입 서비스와 노티 서비스가 서로 독립적으로 존재하며, 비동기적으로 통신합니다.
이를 통해 각 서비스는 서로의 내부 동작에 대해 알 필요가 없으며, 서비스 간의 결합도를 낮추고 확장성을 높일 수 있습니다.
또한 최종적으로 시스템 전체의 안정성과 성능을 향상 시킬수 있습니다.