Skip to content

[FEATURE] RabbitMQ 연동#124

Merged
fervovita merged 6 commits into
devfrom
feat/#122-rabbitmq
Jul 16, 2026
Merged

[FEATURE] RabbitMQ 연동#124
fervovita merged 6 commits into
devfrom
feat/#122-rabbitmq

Conversation

@fervovita

@fervovita fervovita commented Jul 15, 2026

Copy link
Copy Markdown
Collaborator

🚀 Related issue

Closes #122

#️⃣ Summary

  • 메일 발송을 RabbitMQ 기반으로 전환했습니다.

🔧 Changes

  • RabbitMQ 브로커 추가 (docker-compose local/prod) 및 배포 환경변수(RABBITMQ_*) 구성
  • NotificationOutbox를 subject/template 저장 -> NotificationType(enum) 저장으로 전환
  • 인증 메일: Outbox 없이 RabbitMQ 직접 발행, 발행 실패 시 AmqpException -> 도메인 예외 변환
  • 트랜잭션 메일: NotificationOutboxScheduler에서 직접 발송 -> Outbox 릴레이 발행으로 변경
  • EmailSender, 이메일 템플릿, spring-boot-starter-mail/thymeleaf (워커로 이관)

📸 Test Evidence

image image

💬 Reviewer Notes

  • 실제 발송 로직은 별도 레포 Ditda-Notification-Worker에 구현되어 있고, 같은 EC2·Docker 네트워크(ditda-net)에서 구동됩니다.
    현재 단계에서는 서버 분리는 과하다고 판단해, 관심사/의존성만 분리했습니다.

  • 환경변수에 변경사항들이 있어서 노션 참고 부탁드립니다.

  • notification_outboxes에 subject/template_name 대신 notification_type을 활용해 제목/템플릿을 판단하도록 했습니다.

Summary by CodeRabbit

  • 새로운 기능

    • 이메일 알림 전송이 RabbitMQ 기반 메시지 처리 방식으로 변경되었습니다.
    • 로컬 및 운영 환경에서 RabbitMQ를 사용할 수 있도록 서비스와 데이터 영속성이 추가되었습니다.
    • 메시지 전달 확인 및 라우팅 실패 감지가 지원됩니다.
  • 개선 사항

    • 이메일 인증 발송 실패 시 서비스 이용 불가 오류가 표시됩니다.
    • 알림 재시도 시 실패 원인이 기록됩니다.
    • 환불 알림에 취소 여부 대신 구체적인 처리 사유가 표시됩니다.
  • 변경 사항

    • 기존 이메일 템플릿 기반 직접 발송 기능이 제거되었습니다.
    • RabbitMQ 연결 정보가 필수 환경 설정으로 변경되었습니다.

@fervovita fervovita self-assigned this Jul 15, 2026
@fervovita
fervovita requested a review from Jong0128 as a code owner July 15, 2026 13:22
@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro Plus

Run ID: 3018e34a-a235-4d44-8807-39dcb7d7ef58

📥 Commits

Reviewing files that changed from the base of the PR and between eb66bde and 741e489.

📒 Files selected for processing (4)
  • src/main/java/ditda/backend/global/config/RabbitConfig.java
  • src/main/java/ditda/backend/global/notification/NotificationOutboxScheduler.java
  • src/main/resources/db/migration/V260715212830__add_type_to_notification_outboxes.sql
  • src/test/resources/application-test.yaml
💤 Files with no reviewable changes (1)
  • src/main/java/ditda/backend/global/config/RabbitConfig.java
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/java/ditda/backend/global/notification/NotificationOutboxScheduler.java

📝 Walkthrough

Walkthrough

SMTP 기반 메일 발송을 제거하고 RabbitMQ 메시지 발행 방식으로 전환했습니다. RabbitMQ 실행·배포 설정, 메일 메시지 계약, 아웃박스 확인 처리, 인증 메일 및 환불 알림 데이터가 변경되었습니다.

Changes

RabbitMQ 메일 알림 파이프라인

Layer / File(s) Summary
RabbitMQ 인프라 및 실행 설정
.env.example, build.gradle, docker-compose*.yaml, scripts/deploy.sh, src/main/resources/application.yaml, src/test/resources/application-test.yaml
RabbitMQ 의존성, 연결 환경변수, Compose 서비스·볼륨·헬스체크, 배포 필수 변수 검증을 추가하고 기존 SMTP 설정과 메일 의존성을 제거했습니다.
RabbitMQ 메시지 계약 및 발행
src/main/java/ditda/backend/global/notification/MailMessage.java, MailPublisher.java, MailRabbitConfig.java, src/main/java/ditda/backend/global/config/RabbitConfig.java
메일 메시지 record와 교환기·라우팅 키를 정의하고, JSON 변환 및 publisher confirm/returns 콜백을 구성했습니다.
알림 아웃박스 모델 및 발행 처리
src/main/java/ditda/backend/global/notification/NotificationType.java, NotificationOutbox.java, NotificationOutboxScheduler.java, src/main/resources/db/migration/*, src/main/resources/templates/email/*
아웃박스가 notification_type을 저장하도록 변경하고, RabbitMQ confirm 결과에 따라 발송 완료 또는 재시도를 기록하도록 수정했습니다. 기존 Thymeleaf 이메일 템플릿과 EmailSender 구현은 제거되었습니다.
알림 생산자 및 인증 발송 변경
src/main/java/ditda/backend/domain/auth/..., src/main/java/ditda/backend/domain/commission/core/notification/*
인증 메일을 RabbitMQ로 발행하고 AMQP 실패 오류 코드를 추가했으며, 환불 알림 변수에서 isCancelled 대신 사유 문자열을 전달하도록 변경했습니다.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant EmailVerificationMailer
  participant MailPublisher
  participant RabbitMQ
  EmailVerificationMailer->>MailPublisher: MailMessage 발행
  MailPublisher->>RabbitMQ: 메일 메시지 전송
  RabbitMQ-->>MailPublisher: confirm 또는 returns 결과
Loading

Possibly related PRs

Suggested reviewers: jong0128

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 22.22% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목이 RabbitMQ 연동이라는 핵심 변경을 간결하게 요약하며 변경 내용과 일치합니다.
Linked Issues check ✅ Passed 직접 이슈 #122의 RabbitMQ 연동 요구를 Docker, 설정, 발행/전송 로직 변경으로 충족합니다.
Out of Scope Changes check ✅ Passed 메일 전송과 Notification 인프라 변경 외에 명확한 무관 변경은 보이지 않습니다.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/#122-rabbitmq

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

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/main/java/ditda/backend/global/notification/NotificationOutboxScheduler.java (1)

40-58: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

발행 확인(Publisher Confirm) 대기를 일괄 처리(Batching)하여 성능을 개선할 것을 권장합니다.

반복문 내에서 매 메시지마다 .get(5, TimeUnit.SECONDS)를 호출하여 동기적으로 대기하면, 한 번에 처리할 메시지 수가 많거나 브로커 응답이 지연될 경우 스케줄러 스레드가 지나치게 오랫동안 블로킹될 수 있습니다. (예: 최대 100건 * 5초 = 최대 500초 블로킹)

먼저 반복문을 돌며 메시지를 모두 발행하여 반환된 Future를 리스트로 수집한 후, 다음 루프에서 한꺼번에 결과를 대기하도록 리팩토링하면 발송 전체 처리량(Throughput)을 크게 향상시킬 수 있습니다.

💡 리팩토링 예시
// 발행과 결과 확인을 두 번의 루프로 분리하여 대기 시간을 최적화합니다.
List<Map.Entry<NotificationOutbox, CorrelationData>> pendingConfirms = new ArrayList<>();

for (NotificationOutbox outbox : pendingAlerts) {
    try {
        MailMessage message = new MailMessage(
            outbox.getType().name(),
            outbox.getRecipientEmail(),
            outbox.getTemplateVariables()
        );
        CorrelationData correlationData = new CorrelationData(outbox.getId().toString());
        mailPublisher.publish(message, correlationData);
        
        pendingConfirms.add(new java.util.AbstractMap.SimpleEntry<>(outbox, correlationData));
    } catch (Exception e) {
        log.error("아웃박스 알림 발행 중 에러 발생. outboxId={}", outbox.getId(), e);
        outbox.recordRetry(e.getMessage());
        outboxRepository.save(outbox);
    }
}

for (Map.Entry<NotificationOutbox, CorrelationData> entry : pendingConfirms) {
    NotificationOutbox outbox = entry.getKey();
    CorrelationData correlationData = entry.getValue();
    try {
        CorrelationData.Confirm confirm = correlationData.getFuture().get(5, TimeUnit.SECONDS);
        if (confirm.ack()) {
            outbox.markSent();
        } else {
            outbox.recordRetry("Broker Not Confirmed");
        }
        outboxRepository.save(outbox);
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
        log.warn("아웃박스 알림 발행 인터럽트. outboxId={}", outbox.getId(), e);
        break;
    } catch (Exception e) {
        log.error("아웃박스 알림 발행 실패. outboxId={}", outbox.getId(), e);
        outbox.recordRetry(e.getMessage());
        outboxRepository.save(outbox);
    }
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/ditda/backend/global/notification/NotificationOutboxScheduler.java`
around lines 40 - 58, Refactor the notification processing loop around
mailPublisher.publish and CorrelationData so all pending messages are published
first and their correlation futures are collected, then await confirmations in a
separate loop. Preserve the existing ack handling, retry recording, and
outboxRepository.save behavior while preventing each message from blocking
publication of subsequent messages.
docker-compose.prod.yaml (1)

68-70: 📐 Maintainability & Code Quality | 🔵 Trivial

운영 팁: RabbitMQ 계정 정보 변경 시 주의사항

RABBITMQ_DEFAULT_USERRABBITMQ_DEFAULT_PASS 환경 변수는 데이터 볼륨(rabbitmq-data)이 비어 있는 최초 컨테이너 실행 시에만 적용됩니다.
이후 운영 환경의 파라미터 스토어(SSM)에서 비밀번호를 변경하고 재배포하더라도 기존 컨테이너의 비밀번호는 갱신되지 않으므로, 비밀번호 변경이 필요한 경우 rabbitmqctl change_password 명령어를 직접 사용하거나 볼륨을 초기화해야 합니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docker-compose.prod.yaml` around lines 68 - 70, Document that
RABBITMQ_DEFAULT_USER and RABBITMQ_DEFAULT_PASS are applied only when the
rabbitmq-data volume is initialized, and do not update existing credentials on
redeploy. Include the required credential-rotation action using rabbitmqctl
change_password or volume reinitialization.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@src/main/resources/db/migration/V20260715212830__add_type_to_notification_outboxes.sql`:
- Around line 8-15: Update the migration to add notification_type as nullable
first, backfill existing rows from template_name, validate that all rows have a
valid type, then enforce NOT NULL. Do not drop subject or template_name in this
migration; defer those column removals to a separate migration after the
rollback window for the previous deployment has ended.

---

Nitpick comments:
In `@docker-compose.prod.yaml`:
- Around line 68-70: Document that RABBITMQ_DEFAULT_USER and
RABBITMQ_DEFAULT_PASS are applied only when the rabbitmq-data volume is
initialized, and do not update existing credentials on redeploy. Include the
required credential-rotation action using rabbitmqctl change_password or volume
reinitialization.

In
`@src/main/java/ditda/backend/global/notification/NotificationOutboxScheduler.java`:
- Around line 40-58: Refactor the notification processing loop around
mailPublisher.publish and CorrelationData so all pending messages are published
first and their correlation futures are collected, then await confirmations in a
separate loop. Preserve the existing ack handling, retry recording, and
outboxRepository.save behavior while preventing each message from blocking
publication of subsequent messages.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro Plus

Run ID: 24ad8a5a-e61a-4986-b291-1996763cb7df

📥 Commits

Reviewing files that changed from the base of the PR and between d60dedc and eb66bde.

⛔ Files ignored due to path filters (1)
  • src/main/resources/email-images/logo.png is excluded by !**/*.png
📒 Files selected for processing (40)
  • .env.example
  • build.gradle
  • docker-compose.prod.yaml
  • docker-compose.yaml
  • scripts/deploy.sh
  • src/main/java/ditda/backend/domain/auth/exception/AuthErrorCode.java
  • src/main/java/ditda/backend/domain/auth/notification/EmailVerificationMailer.java
  • src/main/java/ditda/backend/domain/commission/core/notification/ApplicationDeadlineClosedNotifier.java
  • src/main/java/ditda/backend/domain/commission/core/notification/FirstDraftDeadlineClosedNotifier.java
  • src/main/java/ditda/backend/global/config/RabbitConfig.java
  • src/main/java/ditda/backend/global/notification/EmailSender.java
  • src/main/java/ditda/backend/global/notification/MailMessage.java
  • src/main/java/ditda/backend/global/notification/MailPublisher.java
  • src/main/java/ditda/backend/global/notification/MailRabbitConfig.java
  • src/main/java/ditda/backend/global/notification/NotificationOutbox.java
  • src/main/java/ditda/backend/global/notification/NotificationOutboxScheduler.java
  • src/main/java/ditda/backend/global/notification/NotificationType.java
  • src/main/resources/application.yaml
  • src/main/resources/db/migration/V20260715212830__add_type_to_notification_outboxes.sql
  • src/main/resources/templates/email/admin-payout-request.html
  • src/main/resources/templates/email/admin-refund-request.html
  • src/main/resources/templates/email/commission-cancelled.html
  • src/main/resources/templates/email/commission-finalized-designer.html
  • src/main/resources/templates/email/commission-finalized-instructor.html
  • src/main/resources/templates/email/commission-matched-designer.html
  • src/main/resources/templates/email/commission-matched-instructor.html
  • src/main/resources/templates/email/commission-shortfall-instructor.html
  • src/main/resources/templates/email/deposit-notification.html
  • src/main/resources/templates/email/designer-signup-notification.html
  • src/main/resources/templates/email/final-cancelled-designer.html
  • src/main/resources/templates/email/final-cancelled-instructor.html
  • src/main/resources/templates/email/first-draft-all-submitted-instructor.html
  • src/main/resources/templates/email/first-draft-missed-designer.html
  • src/main/resources/templates/email/first-draft-rejected-designer.html
  • src/main/resources/templates/email/first-draft-selected-designer.html
  • src/main/resources/templates/email/first-draft-shortfall-instructor.html
  • src/main/resources/templates/email/first-draft-zero-instructor.html
  • src/main/resources/templates/email/revision-requested-designer.html
  • src/main/resources/templates/email/revision-submitted-instructor.html
  • src/main/resources/templates/email/verification-code.html
💤 Files with no reviewable changes (22)
  • src/main/resources/templates/email/commission-matched-instructor.html
  • src/main/resources/templates/email/commission-matched-designer.html
  • src/main/resources/templates/email/final-cancelled-instructor.html
  • src/main/resources/templates/email/first-draft-all-submitted-instructor.html
  • src/main/resources/templates/email/final-cancelled-designer.html
  • src/main/resources/templates/email/deposit-notification.html
  • src/main/resources/templates/email/first-draft-missed-designer.html
  • src/main/resources/templates/email/admin-payout-request.html
  • src/main/resources/templates/email/revision-requested-designer.html
  • src/main/resources/templates/email/first-draft-shortfall-instructor.html
  • src/main/resources/templates/email/commission-cancelled.html
  • src/main/resources/templates/email/verification-code.html
  • src/main/resources/templates/email/first-draft-selected-designer.html
  • src/main/resources/templates/email/commission-finalized-instructor.html
  • src/main/resources/templates/email/commission-finalized-designer.html
  • src/main/resources/templates/email/first-draft-zero-instructor.html
  • src/main/resources/templates/email/admin-refund-request.html
  • src/main/resources/templates/email/designer-signup-notification.html
  • src/main/resources/templates/email/first-draft-rejected-designer.html
  • src/main/resources/templates/email/revision-submitted-instructor.html
  • src/main/java/ditda/backend/global/notification/EmailSender.java
  • src/main/resources/templates/email/commission-shortfall-instructor.html

@Jong0128 Jong0128 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

수고하셨습니다!! 😄
아래 코멘트 확인 한번만 부탁드립니다!

@Jong0128 Jong0128 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

수고하셨습니다!
바로 merge해도 좋을꺼같습니다!! 👍

@fervovita
fervovita merged commit 3378e8b into dev Jul 16, 2026
2 checks passed
@fervovita
fervovita deleted the feat/#122-rabbitmq branch July 16, 2026 07:13
@fervovita fervovita mentioned this pull request Jul 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE] RabbitMQ 연동

2 participants