Skip to content

Feat/128 : AWS SES 적용, 메일 발송 로그에 실패 원인 컬럼 추가#152

Merged
crocusia merged 10 commits intodevfrom
feat/128
Jun 23, 2025
Merged

Feat/128 : AWS SES 적용, 메일 발송 로그에 실패 원인 컬럼 추가#152
crocusia merged 10 commits intodevfrom
feat/128

Conversation

@crocusia
Copy link
Member

@crocusia crocusia commented Jun 23, 2025

🔎 작업 내용

  • AWS SES SDK 버전으로 메일을 발송합니다.
    (기존엔 Gmail-smtp 서버 사용)
  • MailLog에 Caused 컬럼이 추가되었습니다.
    AWS 사용 시, 왜 예외가 발생했는지 파악하기 편하도록

image

🧩 트러블 슈팅

  • Consumer가 여러 app이 아니기 때문에 RedisConsumerGroup을 사용할 필요가 없다고 생각했으나, 결국 비동기 Thread에서 어떤 메시지를 꺼낼지 식별하기 위해서는 해당 기능이 필요하였다.

close #128

Summary by CodeRabbit

  • 신규 기능

    • AWS SES를 통한 이메일 발송 기능이 도입되었습니다.
    • 메일 로그 상세 정보를 제공하는 새로운 응답 형식이 추가되었습니다.
  • 버그 수정

    • 메일 발송 실패 시 AWS SES 예외를 구체적으로 처리하여 상세한 오류 메시지가 로그에 기록되도록 개선되었습니다.
  • 리팩터

    • Spring 메일 발송 방식에서 AWS SES SDK 방식으로 전환되었습니다.
    • 메일 로그 및 관련 DTO 구조가 개선되었습니다.
    • 배치 작업의 스레드 풀 설정이 변경되었습니다.
    • Redis 스트림에서 처리한 메일 기록을 성공 여부와 관계없이 삭제하도록 변경되었습니다.
    • 일부 코드의 들여쓰기 및 정렬이 일관성 있게 조정되었습니다.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jun 23, 2025

"""

Walkthrough

이 변경 사항은 이메일 전송 방식을 Spring Mail에서 AWS SES로 전환하고, 메일 로그에 실패 원인(caused) 정보를 추가하며, Redis 스트림의 recordId를 메일 처리 전반에 걸쳐 추적 및 삭제하도록 개선합니다. 또한, 메일 로그 상세 응답 DTO가 새로 도입되고, 스레드 풀 설정이 조정되었습니다.

Changes

파일/경로 요약 변경 내용 요약
cs25-batch/build.gradle Spring Mail 의존성 제거, AWS SES SDK 및 관련 의존성 추가
cs25-batch/src/main/java/com/example/cs25batch/Cs25BatchApplication.java, .../config/RedisConsumerGroupInitalizer.java 코드 포맷팅 및 불필요한 개행 제거 (로직 변경 없음)
cs25-batch/src/main/java/com/example/cs25batch/aop/MailLogAspect.java 예외 처리 개선: SES 예외 구체적으로 처리, 실패 원인(caused) 로그에 저장
cs25-batch/src/main/java/com/example/cs25batch/batch/component/processor/MailMessageProcessor.java recordId 필드 추출 및 MailDto에 추가
cs25-batch/src/main/java/com/example/cs25batch/batch/component/reader/RedisStreamReader.java Redis 메시지에서 subscriptionId와 recordId만 추출하도록 변경
cs25-batch/src/main/java/com/example/cs25batch/batch/component/writer/MailWriter.java StringRedisTemplate 의존성 추가, 메일 처리 후 Redis 스트림에서 recordId로 항목 삭제
cs25-batch/src/main/java/com/example/cs25batch/batch/dto/MailDto.java recordId 필드 추가, subscription/quiz 필드 접근제어자 private으로 변경
cs25-batch/src/main/java/com/example/cs25batch/batch/jobs/DailyMailSendJob.java 스레드풀 설정 변경(코어/최대 스레드 수 감소, 큐 용량 증가), TaskExecutor 타입 구체화
cs25-batch/src/main/java/com/example/cs25batch/batch/service/BatchMailService.java Spring JavaMailSender → AWS SES v2 SDK로 메일 전송 방식 전환, 예외 처리 및 관련 의존성 변경
cs25-batch/src/main/java/com/example/cs25batch/config/AwsSesConfig.java AWS SES 클라이언트 빈 등록용 신규 설정 클래스 추가
cs25-entity/src/main/java/com/example/cs25entity/domain/mail/entity/MailLog.java caused(실패 원인) 필드 및 생성자 파라미터 추가
cs25-service/src/main/java/com/example/cs25service/domain/mail/controller/MailLogController.java 단일 메일로그 조회 응답타입을 MailLogDetailResponse로 변경, 파라미터명 개선
cs25-service/src/main/java/com/example/cs25service/domain/mail/dto/MailLogDetailResponse.java 메일로그 상세 응답 DTO 신규 추가
cs25-service/src/main/java/com/example/cs25service/domain/mail/dto/MailLogResponse.java quizId 필드 제거 및 관련 매핑 로직 삭제
cs25-service/src/main/java/com/example/cs25service/domain/mail/service/MailLogService.java getMailLog 반환타입을 MailLogDetailResponse로 변경

Sequence Diagram(s)

sequenceDiagram
    participant RedisStream as RedisStream
    participant RedisReader as RedisStreamReader
    participant Processor as MailMessageProcessor
    participant Writer as MailWriter
    participant SES as AWS SES
    participant Redis as Redis

    RedisStream->>RedisReader: 메시지 읽기
    RedisReader->>Processor: subscriptionId, recordId 추출
    Processor->>Writer: MailDto(subscription, quiz, recordId)
    loop 각 MailDto
        Writer->>SES: sendEmail(MailDto)
        alt 성공/실패와 무관하게
            Writer->>Redis: XDEL(quiz-email-stream, recordId)
        end
    end
Loading

Assessment against linked issues

Objective Addressed Explanation
AWS 적용하기: AWS SES를 통한 메일 전송 구현 및 기존 Gmail 방식 대체 (#128)
AWS 적용하기: 비동기 적용 및 병렬 처리 대비 AWS SES 적용 (#128)

Assessment against linked issues: Out-of-scope changes

(없음)

Possibly related PRs

  • NBC-finalProject/CS25-BE#90: 메일 발송 배치 플로우와 데이터 처리 구조를 리팩토링한 PR로, 본 PR과 동일한 컴포넌트(Processor, Reader, Writer, Job)들을 다루며 메일 처리 방식에 직접적인 코드 레벨 연관성이 있습니다.
  • NBC-finalProject/CS25-BE#125: MailMessageProcessor의 process 메서드를 수정한 PR로, 본 PR과 동일 메서드를 다루지만 본 PR은 recordId 필드 추가 및 데이터 처리 확장까지 포함합니다.

Suggested reviewers

  • HeeMang-Lee
  • Kimyoonbeom

Poem

🐇
SES로 편지 날리고,
recordId로 흔적 지우네.
로그엔 실패 사유 담고,
스레드풀은 가볍게 줄이고,
새로운 DTO로 응답도 똑똑!
오늘도 코드는 깔끔하게,
토끼는 메일을 띄웁니다.
✉️🌱
"""


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6f69a41 and 68a34d7.

📒 Files selected for processing (1)
  • cs25-entity/src/main/java/com/example/cs25entity/domain/mail/entity/MailLog.java (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • cs25-entity/src/main/java/com/example/cs25entity/domain/mail/entity/MailLog.java
✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@crocusia crocusia requested review from HeeMang-Lee, Kimyoonbeom, Ksr-ccb, jong-0126 and wannabeing and removed request for Ksr-ccb and wannabeing June 23, 2025 09:00
@crocusia crocusia self-assigned this Jun 23, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🔭 Outside diff range comments (1)
cs25-batch/src/main/java/com/example/cs25batch/config/RedisConsumerGroupInitalizer.java (1)

23-25: 로거 사용으로 출력 개선
Spring Boot 환경에서는 System.out.println 대신 SLF4J 로거를 사용하여 로그 레벨과 포맷을 관리하는 것이 좋습니다.

-import org.springframework.data.redis.RedisSystemException;
+import org.springframework.data.redis.RedisSystemException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;

 @Component
 @RequiredArgsConstructor
 public class RedisConsumerGroupInitalizer implements InitializingBean {
+    private static final Logger log = LoggerFactory.getLogger(RedisConsumerGroupInitalizer.class);
     private final StringRedisTemplate redisTemplate;
@@ -22,7 +24,7 @@ public class RedisConsumerGroupInitalizer implements InitializingBean {
         try {
             redisTemplate.opsForStream().createGroup(STREAM, ReadOffset.latest(), GROUP);
         } catch (RedisSystemException e) {
-            System.out.println("Redis Consumer Group 이미 존재: " + GROUP);
+            log.info("Redis Consumer Group 이미 존재: {}", GROUP);
         }
     }
 }
🧹 Nitpick comments (4)
cs25-batch/src/main/java/com/example/cs25batch/config/RedisConsumerGroupInitalizer.java (1)

12-12: 클래스명 오타 수정 제안
파일명과 일치하지 않는 Initalizer는 올바른 철자인 Initializer로 변경하는 것이 바람직합니다. IDE 및 Spring Component 스캔 시 혼동을 줄일 수 있습니다.

cs25-batch/src/main/java/com/example/cs25batch/config/AwsSesConfig.java (1)

23-23: 하드코딩된 AWS 리전을 설정 가능하게 만드세요.

현재 리전이 하드코딩되어 있어 다른 환경에서 유연성이 떨어집니다.

+@Value("${AWS_SES_REGION:ap-northeast-2}")
+private String region;

 @Bean
 public SesV2Client amazonSesClient() {
     AwsBasicCredentials credentials = AwsBasicCredentials.create(accessKey, secretKey);
     return SesV2Client.builder()
         .credentialsProvider(StaticCredentialsProvider.create(credentials))
-        .region(Region.AP_NORTHEAST_2)
+        .region(Region.of(region))
         .build();
 }
cs25-batch/src/main/java/com/example/cs25batch/batch/component/processor/MailMessageProcessor.java (1)

25-25: recordId 추출에 방어적 프로그래밍을 고려하세요.

RedisStreamReader의 변경사항과 일치하는 좋은 구현입니다. 하지만 recordId에 대한 null 체크를 추가하는 것을 고려해보세요.

-String recordId = message.get("recordId");
+String recordId = message.get("recordId");
+if (recordId == null) {
+    log.warn("RecordId is missing from message: {}", message);
+    // 또는 예외를 던지거나 기본값 설정
+}
cs25-batch/src/main/java/com/example/cs25batch/batch/jobs/DailyMailSendJob.java (1)

128-131: 사용하지 않는 시간 측정 코드 제거

로그가 주석 처리되었지만 시간 측정 변수는 여전히 할당되고 있습니다. 사용하지 않는 코드는 제거하는 것이 좋습니다.

-long searchStart = System.currentTimeMillis();
 List<SubscriptionMailTargetDto> subscriptions = subscriptionService.getTodaySubscriptions();
-long searchEnd = System.currentTimeMillis();
 //log.info("[1. 발송 리스트 조회] {}개, {}ms", subscriptions.size(), searchEnd - searchStart);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between df096d9 and 4100e6c.

📒 Files selected for processing (16)
  • cs25-batch/build.gradle (1 hunks)
  • cs25-batch/src/main/java/com/example/cs25batch/Cs25BatchApplication.java (2 hunks)
  • cs25-batch/src/main/java/com/example/cs25batch/aop/MailLogAspect.java (3 hunks)
  • cs25-batch/src/main/java/com/example/cs25batch/batch/component/processor/MailMessageProcessor.java (2 hunks)
  • cs25-batch/src/main/java/com/example/cs25batch/batch/component/reader/RedisStreamReader.java (1 hunks)
  • cs25-batch/src/main/java/com/example/cs25batch/batch/component/writer/MailWriter.java (3 hunks)
  • cs25-batch/src/main/java/com/example/cs25batch/batch/dto/MailDto.java (1 hunks)
  • cs25-batch/src/main/java/com/example/cs25batch/batch/jobs/DailyMailSendJob.java (4 hunks)
  • cs25-batch/src/main/java/com/example/cs25batch/batch/service/BatchMailService.java (2 hunks)
  • cs25-batch/src/main/java/com/example/cs25batch/config/AwsSesConfig.java (1 hunks)
  • cs25-batch/src/main/java/com/example/cs25batch/config/RedisConsumerGroupInitalizer.java (1 hunks)
  • cs25-entity/src/main/java/com/example/cs25entity/domain/mail/entity/MailLog.java (2 hunks)
  • cs25-service/src/main/java/com/example/cs25service/domain/mail/controller/MailLogController.java (2 hunks)
  • cs25-service/src/main/java/com/example/cs25service/domain/mail/dto/MailLogDetailResponse.java (1 hunks)
  • cs25-service/src/main/java/com/example/cs25service/domain/mail/dto/MailLogResponse.java (0 hunks)
  • cs25-service/src/main/java/com/example/cs25service/domain/mail/service/MailLogService.java (2 hunks)
💤 Files with no reviewable changes (1)
  • cs25-service/src/main/java/com/example/cs25service/domain/mail/dto/MailLogResponse.java
🧰 Additional context used
🧠 Learnings (1)
cs25-batch/src/main/java/com/example/cs25batch/batch/jobs/DailyMailSendJob.java (1)
Learnt from: crocusia
PR: NBC-finalProject/CS25-BE#141
File: cs25-batch/src/main/java/com/example/cs25batch/batch/jobs/DailyMailSendJob.java:187-195
Timestamp: 2025-06-23T01:34:30.721Z
Learning: DailyMailSendJob에서 taskExecutor bean은 mailConsumerWithAsyncStep에서만 사용되므로, ThreadShuttingJobListener로 해당 작업 완료 후 executor를 종료해도 다른 배치 작업들에 영향을 주지 않습니다.
🔇 Additional comments (11)
cs25-batch/build.gradle (1)

28-31: Maven Central에서 AWS SDK BOM의 실제 최신 버전을 다시 조회해보겠습니다.

#!/bin/bash
# 그룹 및 아티팩트 ID를 따옴표로 묶어서 AWS SDK BOM 최신 버전 조회
curl -s 'https://search.maven.org/solrsearch/select?q=g:%22software.amazon.awssdk%22%20AND%20a:%22bom%22&rows=1&wt=json' | jq '.response.docs[0]'
cs25-batch/src/main/java/com/example/cs25batch/batch/component/reader/RedisStreamReader.java (1)

47-51: 필요한 데이터만 추출하는 개선된 접근 방식입니다.

이전의 모든 키-값 쌍을 복사하는 방식에서 필요한 필드만 선별적으로 추출하는 방식으로 변경된 것이 좋습니다. subscriptionId에 대한 null 체크와 recordId 추가가 적절하게 구현되었습니다.

cs25-batch/src/main/java/com/example/cs25batch/batch/component/processor/MailMessageProcessor.java (1)

47-47: MailDtorecordId 필드 추가가 적절히 구현되었습니다.

RedisStreamReader와 일관성 있게 recordIdMailDto에 포함시키는 것이 올바르게 구현되었습니다.

cs25-batch/src/main/java/com/example/cs25batch/Cs25BatchApplication.java (1)

17-19: 코드 포맷팅이 일관성 있게 개선되었습니다.

들여쓰기와 정렬이 더 일관된 스타일로 개선되어 가독성이 향상되었습니다. 로직에는 변경사항이 없습니다.

Also applies to: 31-33, 38-38, 45-46

cs25-entity/src/main/java/com/example/cs25entity/domain/mail/entity/MailLog.java (1)

55-56: 생성자 업데이트가 올바르게 구현됨

새로운 caused 파라미터가 생성자와 필드 초기화에 올바르게 추가되었습니다.

Also applies to: 62-62

cs25-service/src/main/java/com/example/cs25service/domain/mail/service/MailLogService.java (1)

6-6: 상세 응답 DTO로의 변경이 적절함

MailLogDetailResponse로의 변경이 새로운 caused 필드를 포함한 더 상세한 메일 로그 정보 제공이라는 PR 목표와 일치합니다.

Also applies to: 38-40

cs25-batch/src/main/java/com/example/cs25batch/batch/component/writer/MailWriter.java (1)

19-19: Redis 의존성 추가가 적절함

Redis 스트림 레코드 정리를 위한 StringRedisTemplate 의존성 추가가 올바릅니다.

cs25-batch/src/main/java/com/example/cs25batch/aop/MailLogAspect.java (1)

35-35: AWS 오류 세부사항 캡처 로직이 우수함

caused 변수를 추가하여 AWS SES 오류 메시지를 캡처하고 MailLog에 저장하는 로직이 잘 구현되었습니다.

Also applies to: 42-42, 50-50

cs25-batch/src/main/java/com/example/cs25batch/batch/dto/MailDto.java (1)

12-14: 필드 캡슐화 및 recordId 추가가 적절함

필드를 private으로 변경하여 캡슐화를 개선하고, Redis 스트림 레코드 추적을 위한 recordId 필드 추가가 올바르게 구현되었습니다.

cs25-batch/src/main/java/com/example/cs25batch/batch/service/BatchMailService.java (1)

39-83: AWS SES 마이그레이션이 잘 구현되었습니다!

Spring Mail에서 AWS SES v2로의 전환이 깔끔하게 처리되었고, 이메일 구성 요소들이 적절히 설정되었습니다.

cs25-batch/src/main/java/com/example/cs25batch/batch/jobs/DailyMailSendJob.java (1)

173-183: 스레드 풀 크기 감소에 대한 성능 영향 검토 필요

스레드 풀 크기가 크게 감소했습니다 (코어: 5→2, 최대: 10→4). 이는 동시 처리 능력을 감소시킬 수 있습니다. 대신 큐 용량이 500으로 설정되어 대기열은 늘어났습니다.

이 변경이 메일 발송 성능에 미치는 영향을 테스트했는지 확인해 주세요.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

AWS 적용하기

3 participants