1 Action Mailbox란 무엇인가?
Action Mailbox는 수신되는 이메일을 Rails 애플리케이션에서 처리하기 위해 controller와 유사한 mailbox로 라우팅합니다. Action Mailbox는 이메일을 수신하는 것이고, Action Mailer는 이메일을 전송하는 것입니다.
수신 이메일은 Active Job을 사용하여 하나 또는 여러 개의 전용 mailbox로 비동기적으로 라우팅됩니다. 이 이메일들은 Active Record를 사용하여 InboundEmail
레코드로 변환되며, 도메인 모델의 나머지 부분과 직접 상호작용할 수 있습니다.
InboundEmail
레코드는 또한 생명주기 추적, Active Storage를 통한 원본 이메일 저장, 그리고 기본적으로 활성화된 소각을 통한 책임있는 데이터 처리를 제공합니다.
Action Mailbox는 Mailgun, Mandrill, Postmark, SendGrid와 같은 외부 이메일 제공자로부터 이메일을 수신할 수 있게 해주는 ingress와 함께 제공됩니다. 또한 내장된 Exim, Postfix, Qmail ingress를 통해 직접 수신 이메일을 처리할 수도 있습니다.
2 설정
Action Mailbox는 여러 요소들로 구성되어 있습니다. 먼저, installer를 실행해야 합니다. 다음으로, 수신되는 이메일을 처리하기 위한 ingress를 선택하고 설정해야 합니다. 그런 다음, Action Mailbox routing을 추가하고, mailbox를 생성하고, 수신 이메일 처리를 시작할 수 있습니다.
시작하기 위해, Action Mailbox를 설치해봅시다:
$ bin/rails action_mailbox:install
Rails mailbox를 생성하고 migration을 복사합니다.
$ bin/rails db:migrate
Action Mailbox와 Active Storage migration이 실행됩니다.
Action Mailbox 테이블인 action_mailbox_inbound_emails
는 수신된 메시지와 처리 상태를 저장합니다.
이제 Rails 서버를 시작하고 http://localhost:3000/rails/conductor/action_mailbox/inbound_emails
를 확인할 수 있습니다. 자세한 내용은 Local Development and Testing을 참조하세요.
다음 단계는 Rails 애플리케이션에서 수신 이메일을 어떻게 받을지 지정하는 ingress를 구성하는 것입니다.
3 Ingress 설정
Ingress 설정에는 선택한 이메일 서비스에 대한 자격 증명과 엔드포인트 정보를 설정하는 작업이 포함됩니다. 각 지원되는 Ingress의 설정 단계는 다음과 같습니다.
3.1 Exim
Action Mailbox가 SMTP relay로부터 이메일을 수신하도록 설정하세요:
# config/environments/production.rb
config.action_mailbox.ingress = :relay
인바운드 이메일을 수신하기 위한 ingress를 relay로 설정합니다.
Action Mailbox가 relay ingress에 대한 요청을 인증하는 데 사용할 수 있는 강력한 비밀번호를 생성합니다.
bin/rails credentials:edit
를 사용하여 action_mailbox.ingress_password
아래 애플리케이션의 암호화된 credentials에 비밀번호를 추가하면, Action Mailbox가 자동으로 이를 찾을 수 있습니다:
action_mailbox:
ingress_password: ... # Action Mailbox ingress API를 위한 비밀번호
또는 RAILS_INBOUND_EMAIL_PASSWORD
환경변수에 패스워드를 제공할 수 있습니다.
Exim에서 수신되는 이메일을 bin/rails action_mailbox:ingress:exim
으로 파이핑하도록 설정하고, relay ingress의 URL
과 이전에 생성한 INGRESS_PASSWORD
를 제공하세요. 애플리케이션이 https://example.com
에 있다면, 전체 명령은 다음과 같을 것입니다:
$ bin/rails action_mailbox:ingress:exim URL=https://example.com/rails/action_mailbox/relay/inbound_emails INGRESS_PASSWORD=...
3.2 Mailgun
Action Mailbox가 Mailgun 인그레스로의 요청을 인증할 수 있도록 Mailgun Signing key를 제공하세요(Mailgun의 Settings -> Security & Users -> API security에서 찾을 수 있습니다).
bin/rails credentials:edit
를 사용하여 Signing key를 action_mailbox.mailgun_signing_key
아래의 애플리케이션 암호화 자격 증명에 추가하세요. Action Mailbox는 이를 자동으로 찾을 것입니다:
action_mailbox:
mailgun_signing_key: ... # Mailgun Webhook을 인증하기 위한 서명 키
또는 MAILGUN_INGRESS_SIGNING_KEY
환경 변수에 Signing key를 제공하세요.
Action Mailbox가 Mailgun에서 오는 이메일을 수락하도록 설정하세요:
# config/environments/production.rb
config.action_mailbox.ingress = :mailgun
Mailgun을 설정하여 수신되는 이메일을 /rails/action_mailbox/mailgun/inbound_emails/mime
로 전달하세요. 만약 여러분의 애플리케이션이 https://example.com
에 있다면, 전체 URL을 https://example.com/rails/action_mailbox/mailgun/inbound_emails/mime
로 지정해야 합니다.
3.3 Mandrill
Action Mailbox가 Mandrill ingress로의 요청을 인증할 수 있도록 Mandrill API key를 제공하세요.
bin/rails credentials:edit
를 사용하여 action_mailbox.mandrill_api_key
아래에 있는 애플리케이션의 암호화된 credentials에 API key를 추가하세요. Action Mailbox가 자동으로 이를 찾을 것입니다:
action_mailbox:
mandrill_api_key: ... # Mandrill로부터 수신된 이메일의 유효성을 검증하기 위한 API 키
또는 MANDRILL_INGRESS_API_KEY
환경 변수에 API key를 제공하세요.
Action Mailbox가 Mandrill에서 이메일을 받도록 설정하세요:
# config/environments/production.rb
config.action_mailbox.ingress = :mandrill
Configure
Mandrill을 설정하여 수신 이메일을 /rails/action_mailbox/mandrill/inbound_emails
로 라우팅하세요. 만약 애플리케이션이 https://example.com
에서 실행 중이라면, 전체 URL을 https://example.com/rails/action_mailbox/mandrill/inbound_emails
로 지정해야 합니다.
3.4 Postfix
Action Mailbox에게 SMTP 릴레이로부터의 이메일을 수락하도록 지시:
# config/environments/production.rb
config.action_mailbox.ingress = :relay
이 설정은 Action Mailbox가 릴레이 방식으로 수신하도록 구성합니다.
Action Mailbox가 relay ingress에 대한 요청을 인증하는 데 사용할 수 있는 강력한 비밀번호를 생성합니다.
bin/rails credentials:edit
를 사용하여 action_mailbox.ingress_password
아래의 애플리케이션 암호화된 credentials에 비밀번호를 추가하면, Action Mailbox가 자동으로 찾을 수 있습니다:
action_mailbox:
ingress_password: ... # Action Mailbox ingress 인증에 사용되는 비밀번호
또는 RAILS_INBOUND_EMAIL_PASSWORD
환경변수로 비밀번호를 제공할 수 있습니다.
Postfix를 설정하여 수신되는 이메일을 bin/rails action_mailbox:ingress:postfix
로 파이프하고, Postfix ingress의 URL
과 이전에 생성한 INGRESS_PASSWORD
를 제공하세요. 애플리케이션이 https://example.com
에 있다면, 전체 명령은 다음과 같을 것입니다:
$ bin/rails action_mailbox:ingress:postfix URL=https://example.com/rails/action_mailbox/relay/inbound_emails INGRESS_PASSWORD=...
3.5 Postmark
Action Mailbox에게 Postmark로부터 이메일을 수락하도록 지시하세요:
# config/environments/production.rb
config.action_mailbox.ingress = :postmark
Postmark ingress에 대한 요청을 인증하기 위해 Action Mailbox가 사용할 수 있는 강력한 password를 생성하세요.
bin/rails credentials:edit
를 사용하여 action_mailbox.ingress_password
아래에 있는 애플리케이션의 encrypted credentials에 password를 추가하세요. Action Mailbox는 자동으로 이를 찾을 것입니다:
action_mailbox:
ingress_password: ... # Action Mailbox ingress API를 보안하기 위한 비밀번호
또는 RAILS_INBOUND_EMAIL_PASSWORD
환경 변수로 비밀번호를 제공할 수 있습니다.
Postmark inbound webhook 설정을 통해 수신 이메일을 username actionmailbox
와 이전에 생성한 비밀번호를 사용하여 /rails/action_mailbox/postmark/inbound_emails
로 전달하세요. 만약 애플리케이션이 https://example.com
에 있다면, Postmark를 다음과 같은 전체 URL로 설정해야 합니다:
https://actionmailbox:PASSWORD@example.com/rails/action_mailbox/postmark/inbound_emails
참고: Postmark inbound webhook을 설정할 때, "Include raw email content in JSON payload"라고 표시된 체크박스를 반드시 체크하세요. Action Mailbox가 동작하려면 raw email content가 필요합니다.
3.6 Qmail
Action Mailbox가 SMTP 릴레이로부터 이메일을 수락하도록 설정하세요:
# config/environments/production.rb
config.action_mailbox.ingress = :relay
Action Mailbox가 relay ingress로 전달되는 요청을 인증하는 데 사용할 수 있는 강력한 비밀번호를 생성하세요.
bin/rails credentials:edit
를 사용하여 애플리케이션의 암호화된 credentials에 action_mailbox.ingress_password
항목으로 비밀번호를 추가하세요. Action Mailbox가 자동으로 이를 찾을 것입니다:
action_mailbox:
ingress_password: ... # Action Mailbox ingress API를 보호하기 위한 비밀번호
Qmail 패스워드를 RAILS_INBOUND_EMAIL_PASSWORD
환경변수로 제공할 수도 있습니다.
Qmail이 수신 이메일을 bin/rails action_mailbox:ingress:qmail
로 파이프하도록 설정하고, relay ingress의 URL
과 이전에 생성한 INGRESS_PASSWORD
를 제공하세요. 만약 애플리케이션이 https://example.com
에 있다면, 전체 명령어는 다음과 같습니다:
$ bin/rails action_mailbox:ingress:qmail URL=https://example.com/rails/action_mailbox/relay/inbound_emails INGRESS_PASSWORD=...
3.7 SendGrid
SendGrid로부터 이메일을 수신하도록 Action Mailbox를 설정하세요:
# config/environments/production.rb
config.action_mailbox.ingress = :sendgrid
SendGrid ingress에 대한 요청을 인증하기 위해 Action Mailbox가 사용할 수 있는 강력한 비밀번호를 생성하세요.
bin/rails credentials:edit
를 사용하여 Action Mailbox가 자동으로 찾을 수 있는 action_mailbox.ingress_password
항목 아래에 애플리케이션의 암호화된 credentials에 비밀번호를 추가하세요:
action_mailbox:
ingress_password: ... # Action Mailbox inbound email 서비스의 비밀번호
또는 RAILS_INBOUND_EMAIL_PASSWORD
환경변수에 비밀번호를 제공할 수 있습니다.
SendGrid Inbound Parse 구성을 통해 수신 이메일을 /rails/action_mailbox/sendgrid/inbound_emails
로 전달하도록 설정하세요. 이때 username은 actionmailbox
를 사용하고, 앞서 생성한 비밀번호를 사용하면 됩니다. 만약 애플리케이션이 https://example.com
에 있다면, SendGrid를 다음 URL로 구성하게 됩니다:
https://actionmailbox:PASSWORD@example.com/rails/action_mailbox/sendgrid/inbound_emails
(Note: 이는 SendGrid로부터 inbound email을 받기 위한 Action Mailbox endpoint URL입니다)
SendGrid Inbound Parse webhook을 설정할 때, "Post the raw, full MIME message" 라벨이 있는 박스를 반드시 체크하세요. Action Mailbox가 동작하기 위해서는 raw MIME 메시지가 필요합니다.
4 수신 이메일 처리하기
수신 이메일을 처리하는 것은 일반적으로 Rails 애플리케이션에서 이메일 내용을 사용하여 model을 생성하고, view를 업데이트하고, 백그라운드 작업을 큐에 추가하는 등의 작업을 포함합니다.
수신 이메일 처리를 시작하기 전에, Action Mailbox routing을 설정하고 mailbox를 생성해야 합니다.
4.1 라우팅 구성하기
설정된 ingress를 통해 수신된 이메일은 애플리케이션이 실제 처리할 수 있도록 mailbox로 전달되어야 합니다. URL을 controller에 전달하는 Rails router와 마찬가지로, Action Mailbox의 라우팅은 어떤 이메일이 처리를 위해 어떤 mailbox로 전달될지 정의합니다. 라우팅은 application_mailbox.rb
파일에 정규 표현식을 사용하여 추가됩니다:
# app/mailboxes/application_mailbox.rb
class ApplicationMailbox < ActionMailbox::Base
routing(/^save@/i => :forwards) # save@로 시작하는 이메일은 forwards mailbox로 라우팅
routing(/@replies\./i => :replies) # @replies.가 포함된 이메일은 replies mailbox로 라우팅
end
정규표현식은 수신되는 이메일의 to
, cc
또는 bcc
필드와 매칭됩니다. 예를 들어, 위의 예시는 save@
로 전송된 모든 이메일을 "forwards" mailbox로 매칭합니다. 이메일을 라우팅하는 다른 방법들도 있으며, 자세한 내용은 ActionMailbox::Base
를 참조하세요.
다음으로 "forwards" mailbox를 생성해야 합니다.
4.2 Mailbox 만들기
Mailbox는 이메일을 수신하고 해당 이메일에 대해 action을 수행하는 클래스입니다. 어플리케이션에서 Mailbox를 생성하기 위해, rails generate mailbox
command를 실행하세요:
$ bin/rails generate mailbox replies
이는 다음과 같은 mailbox 클래스를 생성합니다:
# app/mailboxes/replies_mailbox.rb
class RepliesMailbox < ApplicationMailbox
def process
# do something
end
end
Mailbox는 들어오는 이메일을 수락하여 처리할지 여부를 결정하는 routing
rule이 필요합니다. mailbox를 위한 routing rule은 ApplicationMailbox
에서 설정됩니다:
# app/mailboxes/application_mailbox.rb
class ApplicationMailbox < ActionMailbox::Base
routing(/reply@reply.com/ => :replies)
end
이는 새로운 mailbox를 reply@reply.com
으로 전송된 모든 이메일에 대한 처리자로 등록합니다.
자세한 내용은 Action Mailbox Basics 가이드를 참조하십시오.
# 새로운 mailbox 생성하기
$ bin/rails generate mailbox forwards
이는 app/mailboxes/forwards_mailbox.rb
파일에 ForwardsMailbox
클래스와 process
메서드를 생성합니다.
4.3 이메일 처리하기
InboundEmail
을 처리할 때, InboundEmail#mail
을 통해 파싱된 버전의 이메일을 Mail
객체로 얻을 수 있습니다. #source
메서드를 사용하여 raw 소스를 직접 얻을 수도 있습니다. Mail
객체를 통해 mail.to
, mail.body.decoded
등과 같은 관련 필드에 접근할 수 있습니다.
irb> mail
=> #<Mail::Message:33780, Multipart: false, Headers: <Date: Wed, 31 Jan 2024 22:18:40 -0600>, <From: someone@hey.com>, <To: save@example.com>, <Message-ID: <65bb1ba066830_50303a70397e@Bhumis-MacBook-Pro.local.mail>>, <In-Reply-To: >, <Subject: Hello Action Mailbox>, <Mime-Version: 1.0>, <Content-Type: text/plain; charset=UTF-8>, <Content-Transfer-Encoding: 7bit>, <x-original-to: >>
irb> mail.to
=> ["save@example.com"]
irb> mail.from
=> ["someone@hey.com"]
irb> mail.date
=> Wed, 31 Jan 2024 22:18:40 -0600
irb> mail.subject
=> "Hello Action Mailbox"
irb> mail.body.decoded
=> "이것이 이메일 메시지의 본문입니다."
# mail.decoded는 mail.body.decoded의 단축형으로도 작동합니다
irb> mail.decoded
=> "이것이 이메일 메시지의 본문입니다."
irb> mail.body
=> <Mail::Body:0x00007fc74cbf46c0 @boundary=nil, @preamble=nil, @epilogue=nil, @charset="US-ASCII", @part_sort_order=["text/plain", "text/enriched", "text/html", "multipart/alternative"], @parts=[], @raw_source="This is the body of the email message.", @ascii_only=true, @encoding="7bit">
4.4 Inbound Email Status
이메일이 매칭되는 mailbox로 라우팅되고 처리되는 동안, Action Mailbox는 action_mailbox_inbound_emails
테이블에 저장된 이메일 상태를 다음 값 중 하나로 업데이트합니다:
pending
: ingress 컨트롤러 중 하나에 의해 수신되어 라우팅 대기 중.processing
: 특정 mailbox가process
메서드를 실행하는 동안의 활성 처리 중.delivered
: 특정 mailbox에 의해 성공적으로 처리됨.failed
: 특정 mailbox의process
메서드 실행 중 예외가 발생함.bounced
: 특정 mailbox에 의해 처리가 거부되어 발신자에게 반송됨.
이메일이 delivered
, failed
, 또는 bounced
상태로 표시되면 "처리됨"으로 간주되고 소각을 위해 표시됩니다.
5 Example
다음은 사용자의 프로젝트에 대한 "forwards"를 생성하기 위해 이메일을 처리하는 Action Mailbox의 예시입니다.
before_processing
콜백은 process
메서드가 호출되기 전에 특정 조건이 충족되는지 확인하는 데 사용됩니다. 이 경우, before_processing
은 사용자가 최소한 하나의 프로젝트를 가지고 있는지 확인합니다. 지원되는 다른 Action Mailbox 콜백으로는 after_processing
과 around_processing
이 있습니다.
"forwarder"가 프로젝트를 가지고 있지 않은 경우 bounced_with
를 사용하여 이메일을 반송할 수 있습니다. "forwarder"는 mail.from
과 동일한 이메일을 가진 User
입니다.
"forwarder"가 최소한 하나의 프로젝트를 가지고 있다면, record_forward
메서드는 mail.subject
와 mail.decoded
이메일 데이터를 사용하여 애플리케이션에서 Active Record 모델을 생성합니다. 그렇지 않으면, Action Mailer를 사용하여 "forwarder"에게 프로젝트를 선택하도록 요청하는 이메일을 보냅니다.
# app/mailboxes/forwards_mailbox.rb
class ForwardsMailbox < ApplicationMailbox
# Callback이 처리를 위한 전제 조건을 지정
before_processing :require_projects
def process
# 하나의 project에 forward를 기록하거나...
if forwarder.projects.one?
record_forward
else
# ...두 번째 Action Mailer를 사용하여 어느 project로 forward할지 요청
request_forwarding_project
end
end
private
def require_projects
if forwarder.projects.none?
# Action Mailer를 사용하여 수신된 이메일을 발신자에게 반송 - 이는 처리를 중단시킴
bounce_with Forwards::BounceMailer.no_projects(inbound_email, forwarder: forwarder)
end
end
def record_forward
forwarder.forwards.create subject: mail.subject, content: mail.decoded
end
def request_forwarding_project
Forwards::RoutingMailer.choose_project(inbound_email, forwarder: forwarder).deliver_now
end
def forwarder
@forwarder ||= User.find_by(email_address: mail.from)
end
end
6 로컬 개발과 테스팅
실제로 이메일을 주고받지 않고도 개발 단계에서 수신되는 이메일을 테스트할 수 있으면 좋습니다. 이를 위해 /rails/conductor/action_mailbox/inbound_emails
에 마운트된 conductor 컨트롤러가 있습니다. 이 컨트롤러는 시스템의 모든 InboundEmails 목록과 그들의 처리 상태, 그리고 새로운 InboundEmail을 생성하기 위한 폼을 제공합니다.
다음은 Action Mailbox TestHelpers를 사용하여 수신 이메일을 테스트하는 예시입니다.
class ForwardsMailboxTest < ActionMailbox::TestCase
test "forwarder와 forwardee가 하나의 project에 해당하는 client forward를 직접 recording하기" do
assert_difference -> { people(:david).buckets.first.recordings.count } do
receive_inbound_email_from_mail \
to: "save@example.com",
from: people(:david).email_address,
subject: "Fwd: Status update?",
body: <<~BODY
--- Begin forwarded message ---
From: Frank Holland <frank@microsoft.com>
What's the status?
BODY
end
recording = people(:david).buckets.first.recordings.last
assert_equal people(:david), recording.creator
assert_equal "Status update?", recording.forward.subject
assert_match "What's the status?", recording.forward.content.to_s
end
end
InboundEmails의 폐기
기본적으로, 처리가 완료된 InboundEmail
은 30일 후에 폐기됩니다. InboundEmail
의 상태가 delivered
, failed
, 또는 bounced
로 변경되면 처리가 완료된 것으로 간주됩니다.
실제 폐기는 config.action_mailbox.incinerate_after
시간이 지난 후 실행되도록 예약된 IncinerationJob
을 통해 수행됩니다. 이 값은 기본적으로 30.days
로 설정되어 있지만, production.rb 설정에서 변경할 수 있습니다. (이러한 먼 미래의 폐기 일정은 작업 대기열이 그 기간 동안 작업을 유지할 수 있어야 한다는 점에 유의하세요.)
기본 데이터 폐기는 사용자가 계정을 취소하거나 콘텐츠를 삭제한 후에 불필요하게 사용자의 데이터를 보관하지 않도록 보장합니다.
Action Mailbox 처리의 의도는 이메일을 처리할 때 이메일에서 필요한 모든 데이터를 추출하여 애플리케이션의 도메인 모델에 영구적으로 저장하는 것입니다. InboundEmail
은 디버깅과 포렌식을 위해 설정된 기간 동안 시스템에 남아있다가 삭제됩니다.
추가적인 테스트 헬퍼 메서드는 ActionMailbox::TestHelper API를 참조하세요.