전략 패턴 도입 계기
이제 우리가 지금 당장 사용할 수 있는 메일 발송 서비스는 2가지가 있다.
- Gmail-SMTP
- AWS SES
현재까지 개발된 상황을 보면 Gmail-smtp를 사용하면 비동기가 필수적이고,
AWS SES를 사용하면 동기만으로도 충분히 빠른 속도를 보인다. (개수가 10000개 내외일때)
이런 상황에서 앞으로 겪을 수 있는 상황을 고려했을 때,
사용하는 메일 서비스를 바꾸는 상황이 올 수도 있을 거란 판단이 들었다.
- 프리티어 이용을 위한 AWS 계정 변경 시, SES 프로덕션 모드 변경 재신청
- 이용자 수가 500명 이하인데 AWS SES 비용이 많이 청구되는 경우
그래서 메일 서비스 발송 방식을 쉽게 추가 및 변경하기 위해서 전략 패턴을 도입하기로 결정했다.
전략 패턴 도입
1. 공통 행위 추상화
먼저 추상화할 공통 행위를 꼽았다.
- 메일 내용 구성 및 발송
- RateLimiter로 초당 요청량 제어
선별한 공통 행위를 다음과 같이 인터페이스로 추상화하였다.
public interface MailSenderStrategy {
void sendQuizMail(MailDto mailDto);
boolean tryConsume(Long num);
}
2. 각 서비스 별 구현체 생성
public class JavaMailSenderStrategy implements MailSenderStrategy{
private final JavaMailService javaMailService;
private final Bucket bucket; //Gmail 초당 요청량에 맞게 설정한 Bucket
@Override
public void sendQuizMail(MailDto mailDto) {
javaMailService.sendQuizEmail(mailDto.getSubscription(), mailDto.getQuiz()); // 커스텀 메서드로 정의
}
@Override
public boolean tryConsume(Long num){
return bucket.tryConsume(num);
}
}
public class SesMailSenderStrategy implements MailSenderStrategy{
private final SesMailService sesMailService;
private final Bucket bucket; //SES 초당 요청량에 맞춘 Bucket
@Override
public void sendQuizMail(MailDto mailDto) {
sesMailService.sendQuizEmail(mailDto.getSubscription(), mailDto.getQuiz());
}
@Override
public boolean tryConsume(Long num){
return bucket.tryConsume(num);
}
}
3. Context 클래스를 통해 적용
strategyMap에 있는 메일 전략이 맞는지 아닌지 확인한 후,
전략키에 해당하는 구현체의 메서드를 호출한다.
public class MailSenderContext {
private final Map<String, MailSenderStrategy> strategyMap;
public void send(MailDto dto, String strategyKey) {
MailSenderStrategy strategy = getValidStrategy(strategyKey);
strategy.sendQuizMail(dto);
}
public boolean tryConsume(String strategyKey, Long num) {
MailSenderStrategy strategy = getValidStrategy(strategyKey);
return strategy.tryConsume(num);
}
private MailSenderStrategy getValidStrategy(String strategyKey) {
MailSenderStrategy strategy = strategyMap.get(strategyKey);
if (strategy == null) {
throw new IllegalArgumentException("메일 전략이 존재하지 않습니다: " + strategyKey);
}
return strategy;
}
}
처음에는 메일 발송 부분만 추상화하였었는데
튜터님께서 이왕 전략 패턴을 쓰는거 RateLimiter 관련된 부분까지 함께 관리하면 더 좋을 것 같다고 하셔서
그 부분을 추가했다.
일관된 발송 주체(주소)를 유지하기 위해서 런타임 내에서 발송 방식이 변경되도록 구현하지는 않았다.
(SES로 발송이 실패하면 Gmail로 보내도록 시도하는 FallBack 구조)
'CS25' 카테고리의 다른 글
[내일배움캠프] 모의 면접 (입문) 복기 (0) | 2025.06.26 |
---|---|
[내일배움캠프 83일차] 3차 리팩토링 - 계층형 엔티티 도입 (0) | 2025.06.20 |
[내일배움캠프 82일차] AWS SES 서버 기반 메일 발송 (0) | 2025.06.19 |
[내일배움캠프 80일차] 2차 리팩토링 - 멀티모듈, 쿼리 노출 보안 (0) | 2025.06.17 |
[내일배움캠프 79일차] 트러블 슈팅 Throttle 예외 - RateLimiter 적용 (0) | 2025.06.16 |