| 요약 | Discourse Bounce Guard은 하드 이메일 전달 실패를 감지하여 유효하지 않은 주소로의 메일 전송을 중단하고, 영향을 받은 사용자를 비활성화하여 작동하는 주소를 확인하도록 할 수 있습니다. | |
| 저장소 링크 | https://github.com/overgrow/discourse-bounce-guard | |
| 설치 가이드 | Discourse에 플러그인 설치 방법 |
라이선스: MIT
&tldr; 얻을 수 있는 것: 더 깔끔한 에러 로그(유효하지 않은 주소에 대한 시간당 재시도가 중단됩니다). Discourse는 수신할 수 없는 메일 전송을 중단합니다. 그리고 더 만족스러운 사용자 경험: 유효하지 않은 주소가 제때 작동하는 주소로 교체되므로 사용자는 계정에 다시 접근할 수 있습니다.
나가는 메일이 자체 릴레이(Postfix, Exim, 대부분의 셀프 호스팅 설정)를 통해 전송되는 경우, /logs에 다음과 같은 쌍이 쌓이는 것을 본 적이 있을 것입니다:
SMTP Error Net::SMTPServerBusy with message: 450 4.1.2 <someone@gone-domain.com>: Recipient address rejected: Domain not found
Job exception: Net::SMTPServerBusy
해당 도메인은 사라졌으며 돌아오지 않습니다. 하지만 릴레이는 이론상 DNS 조회가 일시적으로 실패할 수 있기 때문에 일시적 에러 코드(450)로 응답합니다. Discourse는 모든 일시적 에러를 "1시간 후 다시 시도"로 해석하며, Sidekiq는 몇 주 동안 계속 시도합니다. 코어의 바운스 감지는 절대 트리거되지 않습니다. 코어는 이메일(VERP) 또는 메일 제공자로부터의 웹훅으로 바운스 메시지가 돌아올 때만 반응하는데, 여기서는 메시지가 현장에서 거부되므로 바운스 메시지가 생성되지 않습니다. 바운스 점수는 0으로 유지되고, 재시도는 계속되며, 사용자는 비밀번호 재설정 포함 모든 것을 수신할 수 없는 주소를 계속 보유하게 됩니다.
이 문제는 내장된 해결책 없이 여기에서 몇 번이나 언급되었는데, 예를 들어 존재하지 않는 도메인으로의 이메일 처리, 하드 바운스 사용자의 비활성화, 이메일을 수신하지 못하는 사용자의 계정 비활성화 방법 등이 있습니다. 우리 포럼에서도 동일한 문제를 겪었기 때문에 플러그인을 만들었습니다.
기능
Bounce Guard는 전송 경로 자체에 후킹하여 모든 SMTP 거부를 분류합니다:
- 잘못된 수신자 강화 상태(
5.1.1,5.1.2,5.2.1등)를 가진 5xx 응답은 하드 실패로 계산됩니다. - 구성 가능한 구문 목록(“domain not found”, “user unknown” 등)과 일치하는 모든 응답은 코드와 관계없이 하드 실패로 계산됩니다. 이는 450 코드로 영구적 상태를 소프트 실패로 처리하는 릴레이를 포착합니다.
- 그레이리스트, 꽉 찬 메일박스, 레이트 리밋은 그대로 두어 코어의 재시도 동작에 영향을 주지 않습니다.
기록된 하드 실패는 다음과 같은 단계로 진행됩니다:
- 각 실패는 코어의 바운스 점수에 하드 바운스로 반영됩니다(기본값: 켜짐). 두 번의 실패 후, 코어의 자체 임계값이 트리거되어 Discourse가 해당 사용자에게 이메일을 보내는 것을 중단합니다. 더 이상의 설정을 활성화하지 않더라도 로그 노이즈는 여기서 끝납니다.
- 구성 가능한 시간 범위(기본값: 최소 48시간 간격으로 2회 실패, 따라서 짧은 장애로 인해 아무도 제외되지 않도록 함)에 걸쳐 구성 가능한 횟수의 실패가 발생하면 플러그인이 작동합니다. 기본 동작은
log_only로, 사용자가 비활성화되었을 것이라는 내용을 기록하는 스태프 액션 로그 항목을 생성합니다. 신뢰할 수 있다고 판단되면deactivate로 전환하십시오. - 비활성화는 사용자의 다음 로그인 시 표준 활성화 흐름으로 유도하며, 여기서 주소 변경이 내장되어 있습니다. 사용자는 작동하는 이메일을 확인하고 계정을 온전히 유지한 채 계속 사용할 수 있습니다. 핵심은 복구 가능성입니다: 유일한 복구 채널이 죽은 메일박스인 활성 계정은 곧 닥칠 잠금(lockout) 상태입니다.
사이트가 바운스(VERP 또는 웹훅)를 수신하는 경우, 코어의 바운스 점수가 코어가 전송을 중단하는 점수보다 높은 사용자가 선택한 수준을 넘을 때 플러그인이 작동하도록 설정할 수도 있습니다. 모든 곳에 몇 가지 안전 규칙이 적용됩니다. 스태프와 봇은 절대 영향을 받지 않습니다. 1시간마다 재시도되는 막힌 이메일 한 통은 쿨다운 윈도우 덕분에 단일 실패로 계산됩니다. 실패는 서버가 거부한 주소가 해당 사용자의 현재 주소일 때만 사용자에게 불리하게 계산됩니다. 그리고 기록된 모든 실패는 무슨 일이 있었는지 확인할 수 있도록 90일간 보관됩니다(README의 Data Explorer 쿼리 참조).
설치
표준 플러그인 설치:
hooks:
after_code:
- exec:
cd: $home/plugins
cmd:
- git clone https://github.com/discourse/docker_manager.git
- git clone https://github.com/overgrow/discourse-bounce-guard.git
bounce_guard_enabled를 활성화하고, bounce_guard_action을 1~2주 동안 log_only로 유지한 후, 플래그가 지정된 내용을 검토하고 비활성화에 대해 결정하십시오. 설정 참조는 README에 있습니다.
서버 사이드 전용으로, 테마나 JS 컴포넌트가 없습니다. 현재 코어(2026.8)에 대해 빌드 및 테스트되었으며, 31개의 스펙이 있고 CI는 표준 discourse-plugin 워크플로우를 실행합니다. 피드백을 환영하며, 특히 기본 구문 목록이 놓치는 다른 릴레이의 거부 구문에 대한 피드백을 기다립니다.