Action Mailbox 기본 사항

이 가이드는 귀하의 애플리케이션에서 이메일을 수신하는 데 필요한 모든 것을 제공합니다.

이 가이드를 읽고 나면 다음을 알게 될 것입니다:

  • Rails 애플리케이션 내에서 이메일을 수신하는 방법.
  • Action Mailbox를 구성하는 방법.
  • 이메일을 메일박스로 생성 및 라우팅하는 방법.
  • 수신 이메일을 테스트하는 방법.

Action Mailbox란 무엇인가?

Action Mailbox는 들어오는 이메일을 Rails 애플리케이션의 컨트롤러와 유사한 메일박스로 라우팅하여 처리합니다. Action Mailbox는 이메일을 수신하는 데 사용되며, Action Mailer는 이메일을 보내는 데 사용됩니다.

들어오는 이메일은 Active Job을 사용하여 비동기적으로 라우팅되며, 하나 이상의 전용 메일박스로 전송됩니다. 이 이메일은 Active Record를 사용하여 InboundEmail 레코드로 변환되며, 이를 통해 도메인 모델과 직접 상호 작용할 수 있습니다.

InboundEmail 레코드는 수명 추적, Active Storage를 통한 원본 이메일 저장, 기본적으로 활성화된 소각을 통한 책임감 있는 데이터 처리 기능도 제공합니다.

Action Mailbox에는 Mailgun, Mandrill, Postmark, SendGrid와 같은 외부 이메일 공급자로부터 이메일을 수신할 수 있는 인그레스(ingress)가 포함되어 있습니다. 또한 내장된 Exim, Postfix, Qmail 인그레스를 통해 들어오는 이메일을 직접 처리할 수 있습니다.

설정

Action Mailbox에는 여러 가지 구성 요소가 있습니다. 먼저 설치 프로그램을 실행합니다. 그 다음 들어오는 이메일을 처리할 인그레스를 선택하고 구성합니다. 그러면 Action Mailbox 라우팅을 추가하고, 메일박스를 만들고, 들어오는 이메일 처리를 시작할 준비가 됩니다.

시작하려면 Action Mailbox를 설치합니다:

$ bin/rails action_mailbox:install

이렇게 하면 application_mailbox.rb 파일이 생성되고 마이그레이션이 복사됩니다.

$ bin/rails db:migrate

이렇게 하면 Action Mailbox와 Active Storage 마이그레이션이 실행됩니다.

Action Mailbox 테이블 action_mailbox_inbound_emails에는 들어오는 메시지와 처리 상태가 저장됩니다.

이 시점에서 Rails 서버를 시작하고 http://localhost:3000/rails/conductor/action_mailbox/inbound_emails를 확인할 수 있습니다. 로컬 개발 및 테스트에 대해 자세히 알아보세요.

다음 단계는 들어오는 이메일을 수신하는 방법을 지정하기 위해 Rails 애플리케이션에서 인그레스를 구성하는 것입니다.

인그레스 구성

인그레스 구성에는 선택한 이메일 서비스에 대한 자격 증명 및 엔드포인트 정보 설정이 포함됩니다. 각 지원되는 인그레스에 대한 단계는 다음과 같습니다.

Exim

Action Mailbox가 SMTP 릴레이에서 이메일을 수락하도록 알려줍니다:

# config/environments/production.rb
config.action_mailbox.ingress = :relay

Action Mailbox가 릴레이 인그레스에 대한 요청을 인증하는 데 사용할 수 있는 강력한 비밀번호를 생성합니다.

bin/rails credentials:edit를 사용하여 애플리케이션의 암호화된 자격 증명에 비밀번호를 action_mailbox.ingress_password로 추가합니다. Action Mailbox가 자동으로 찾을 수 있습니다:

action_mailbox:
  ingress_password: ...

또는 RAILS_INBOUND_EMAIL_PASSWORD 환경 변수에 비밀번호를 제공할 수 있습니다.

Exim을 구성하여 들어오는 이메일을 bin/rails action_mailbox:ingress:exim으로 파이프하고, 릴레이 인그레스의 URL과 이전에 생성한 INGRESS_PASSWORD를 제공합니다. 애플리케이션이 https://example.com에 있다면 전체 명령은 다음과 같습니다:

$ bin/rails action_mailbox:ingress:exim URL=https://example.com/rails/action_mailbox/relay/inbound_emails INGRESS_PASSWORD=...

Mailgun

Action Mailbox에 Mailgun 서명 키(Mailgun의 Settings -> Security & Users -> API security에서 찾을 수 있음)를 제공하여 Mailgun 인그레스에 대한 요청을 인증할 수 있습니다.

bin/rails credentials:edit를 사용하여 애플리케이션의 암호화된 자격 증명에 서명 키를 action_mailbox.mailgun_signing_key로 추가합니다. Action Mailbox가 자동으로 찾을 수 있습니다:

action_mailbox:
  mailgun_signing_key: ...

또는 MAILGUN_INGRESS_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를 지정해야 합니다.

Mandrill

Action Mailbox에 Mandrill API 키를 제공하여 Mandrill 인그레스에 대한 요청을 인증할 수 있습니다.

bin/rails credentials:edit를 사용하여 애플리케이션의 암호화된 자격 증명에 API 키를 action_mailbox.mandrill_api_key로 추가합니다. Action Mailbox가 자동으로 찾을 수 있습니다:

action_mailbox:
  mandrill_api_key: ...

또는 MANDRILL_INGRESS_API_KEY 환경 변수에 API 키를 제공할 수 있습니다.

Action Mailbox가 Mandrill에서 이메일을 수락하도록 알려줍니다:

# config/environments/production.rb
config.action_mailbox.ingress = :mandrill

Mandrill을 구성하여 들어오는 이메일을 /rails/action_mailbox/mandrill/inbound_emails로 라우팅합니다. 애플리케이션이 https://example.com에 있다면 완전한 URL https://example.com/rails/action_mailbox/mandrill/inbound_emails를 지정해야 합니다.

Postfix

Action Mailbox가 SMTP 릴레이에서 이메일을 수락하도록 알려줍니다:

# config/environments/production.rb
config.action_mailbox.ingress = :relay

Action Mailbox가 릴레이 인그레스에 대한 요청을 인증하는 데 사용할 수 있는 강력한 비밀번호를 생성합니다.

bin/rails credentials:edit를 사용하여 애플리케이션의 암호화된 자격 증명에 비밀번호를 action_mailbox.ingress_password로 추가합니다. Action Mailbox가 자동으로 찾을 수 있습니다:

action_mailbox:
  ingress_password: ...

또는 RAILS_INBOUND_EMAIL_PASSWORD 환경 변수에 비밀번호를 제공할 수 있습니다.

Postfix를 구성하여 들어오는 이메일을 bin/rails action_mailbox:ingress:postfix로 파이프하고, Postfix 인그레스의 URL과 이전에 생성한 INGRESS_PASSWORD를 제공합니다. 애플리케이션이 https://example.com에 있다면 전체 명령은 다음과 같습니다:

$ bin/rails action_mailbox:ingress:postfix URL=https://example.com/rails/action_mailbox/relay/inbound_emails INGRESS_PASSWORD=...

Postmark

Action Mailbox가 Postmark에서 이메일을 수락하도록 알려줍니다:

# config/environments/production.rb
config.action_mailbox.ingress = :postmark

Action Mailbox가 Postmark 인그레스에 대한 요청을 인증하는 데 사용할 수 있는 강력한 비밀번호를 생성합니다.

bin/rails credentials:edit를 사용하여 애플리케이션의 암호화된 자격 증명에 비밀번호를 action_mailbox.ingress_password로 추가합니다. Action Mailbox가 자동으로 찾을 수 있습니다:

action_mailbox:
  ingress_password: ...

또는 RAILS_INBOUND_EMAIL_PASSWORD 환경 변수에 비밀번호를 제공할 수 있습니다.

Postmark 수신 웹훅을 구성하여 들어오는 이메일을 /rails/action_mailbox/postmark/inbound_emails로 전달하고, 사용자 이름을 actionmailbox와 이전에 생성한 비밀번호를 사용합니다. 애플리케이션이 https://example.com에 있다면 다음과 같은 완전한 URL로 Postmark를 구성해야 합니다:

https://actionmailbox:PASSWORD@example.com/rails/action_mailbox/postmark/inbound_emails

참고: Postmark 수신 웹훅을 구성할 때 “JSON 페이로드에 원시 이메일 콘텐츠 포함” 옵션을 선택해야 합니다. Action Mailbox에는 원시 이메일 콘텐츠가 필요합니다.

Qmail

Action Mailbox가 SMTP 릴레이에서 이메일을 수락하도록 알려줍니다:

# config/environments/production.rb
config.action_mailbox.ingress = :relay

Action Mailbox가 릴레이 인그레스에 대한 요청을 인증하는 데 사용할 수 있는 강력한 비밀번호를 생성합니다.

bin/rails credentials:edit를 사용하여 애플리케이션의 암호화된 자격 증명에 비밀번호를 action_mailbox.ingress_password로 추가합니다. Action Mailbox가 자동으로 찾을 수 있습니다:

action_mailbox:
  ingress_password: ...

또는 RAILS_INBOUND_EMAIL_PASSWORD 환경 변수에 비밀번호를 제공할 수 있습니다.

Qmail을 구성하여 들어오는 이메일을 bin/rails action_mailbox:ingress:qmail로 파이프하고, 릴레이 인그레스의 URL과 이전에 생성한 INGRESS_PASSWORD를 제공합니다. 애플리케이션이 https://example.com에 있다면 전체 명령은 다음과 같습니다:

$ bin/rails action_mailbox:ingress:qmail URL=https://example.com/rails/action_mailbox/relay/inbound_emails INGRESS_PASSWORD=...

SendGrid

Action Mailbox가 SendGrid에서 이메일을 수락하도록 알네, 계속해서 한국어 번역을 제공하겠습니다.

SendGrid

Action Mailbox가 SendGrid에서 이메일을 수락하도록 알려줍니다:

# config/environments/production.rb
config.action_mailbox.ingress = :sendgrid

Action Mailbox가 SendGrid 인그레스에 대한 요청을 인증하는 데 사용할 수 있는 강력한 비밀번호를 생성합니다.

bin/rails credentials:edit를 사용하여 애플리케이션의 암호화된 자격 증명에 비밀번호를 action_mailbox.ingress_password로 추가합니다. Action Mailbox가 자동으로 찾을 수 있습니다:

action_mailbox:
  ingress_password: ...

또는 RAILS_INBOUND_EMAIL_PASSWORD 환경 변수에 비밀번호를 제공할 수 있습니다.

SendGrid Inbound Parse를 구성하여 들어오는 이메일을 /rails/action_mailbox/sendgrid/inbound_emails로 전달하고, 사용자 이름을 actionmailbox와 이전에 생성한 비밀번호를 사용합니다. 애플리케이션이 https://example.com에 있다면 다음과 같은 URL로 SendGrid를 구성해야 합니다:

https://actionmailbox:PASSWORD@example.com/rails/action_mailbox/sendgrid/inbound_emails

참고: SendGrid Inbound Parse 웹훅을 구성할 때 “원시, 전체 MIME 메시지 게시” 옵션을 선택해야 합니다. Action Mailbox에는 원시 MIME 메시지가 필요합니다.

들어오는 이메일 처리

들어오는 이메일을 처리하는 것은 일반적으로 이메일 내용을 사용하여 모델을 생성하고, 뷰를 업데이트하고, 백그라운드 작업을 큐에 넣는 등의 작업을 수행하는 것을 의미합니다.

들어오는 이메일을 처리하기 전에 Action Mailbox 라우팅을 설정하고 메일박스를 만들어야 합니다.

라우팅 구성

구성된 인그레스를 통해 수신된 들어오는 이메일은 실제 처리를 위해 메일박스로 전달되어야 합니다. Rails 라우터가 URL을 컨트롤러로 디스패치하는 것과 마찬가지로, Action Mailbox의 라우팅은 어떤 이메일이 어떤 메일박스로 전송되어 처리되는지 정의합니다. 정규 표현식을 사용하여 application_mailbox.rb 파일에 라우트를 추가합니다:

# app/mailboxes/application_mailbox.rb
class ApplicationMailbox < ActionMailbox::Base
  routing(/^save@/i     => :forwards)
  routing(/@replies\./i => :replies)
end

정규 표현식은 들어오는 이메일의 to, cc 또는 bcc 필드와 일치합니다. 예를 들어, 위의 코드는 save@로 보낸 모든 이메일을 “forwards” 메일박스로 보냅니다. 이메일을 라우팅하는 다른 방법이 있습니다. 자세한 내용은 ActionMailbox::Base를 참조하세요.

이제 “forwards” 메일박스를 만들어야 합니다.

메일박스 생성

# 새 메일박스 생성
$ bin/rails generate mailbox forwards

이렇게 하면 app/mailboxes/forwards_mailbox.rb가 생성되며, ForwardsMailbox 클래스와 process 메서드가 포함됩니다.

이메일 처리

InboundEmail을 처리할 때 InboundEmail#mail을 사용하여 이메일의 구문 분석된 버전을 Mail 객체로 가져올 수 있습니다. 또한 #source 메서드를 사용하여 원시 소스를 직접 가져올 수 있습니다. 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
=> "This is the body of the email message."
# mail.decoded, a shorthand for mail.body.decoded, also works
irb> mail.decoded
=> "This is the body of the email message."
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">

수신 이메일 상태

이메일이 일치하는 메일박스로 라우팅되고 처리되는 동안 Action Mailbox는 action_mailbox_inbound_emails 테이블에 저장된 이메일 상태를 다음 값 중 하나로 업데이트합니다:

  • pending: 인그레스 컨트롤러에서 수신되어 라우팅을 위해 예약되었습니다.
  • processing: 특정 메일박스의 process 메서드가 실행되는 동안 활성 처리 중입니다.
  • delivered: 특정 메일박스에 의해 성공적으로 처리되었습니다.
  • failed: 특정 메일박스의 process 메서드 실행 중 예외가 발생했습니다.
  • bounced: 특정 메일박스에 의해 처리가 거부되어 발신자에게 반송되었습니다.

이메일이 delivered, failed 또는 bounced로 표시되면 “처리된” 것으로 간주되며 소각을 위해 표시됩니다.

예시

프로젝트에 대한 “forwards"를 생성하는 Action Mailbox의 예시입니다.

before_processing 콜백은 process 메서드가 호출되기 전에 특정 조건이 충족되도록 합니다. 이 경우 before_processing은 사용자에게 프로젝트가 하나 이상 있는지 확인합니다. 다른 지원되는 Action Mailbox 콜백after_processingaround_processing입니다.

"forwarder"에게 프로젝트가 없는 경우 bounced_with를 사용하여 이메일을 반송할 수 있습니다. "forwarder"는 mail.from과 동일한 이메일 주소를 가진 User입니다.

"forwarder"에게 프로젝트가 하나 이상 있는 경우 record_forward 메서드는 이메일 데이터 mail.subjectmail.decoded를 사용하여 애플리케이션의 Active Record 모델을 생성합니다. 그렇지 않은 경우 Action Mailer를 사용하여 "forwarder"에게 프로젝트를 선택하도록 요청하는 이메일을 보냅니다.

# app/mailboxes/forwards_mailbox.rb
class ForwardsMailbox < ApplicationMailbox
  # 처리 전 필수 조건을 지정하는 콜백
  before_processing :require_projects

  def process
    # 한 프로젝트에 forward 기록, 또는...
    if forwarder.projects.one?
      record_forward
    else
      # ...두 번째 Action Mailer를 사용하여 어떤 프로젝트에 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

로컬 개발 및 테스트

실제로 이메일을 보내고 받지 않고도 개발 환경에서 들어오는 이메일을 테스트할 수 있는 것이 도움이 됩니다. 이를 위해 /rails/conductor/action_mailbox/inbound_emails에 마운트된 conductor 컨트롤러가 있으며, 시스템의 모든 InboundEmail, 처리 상태, 새 InboundEmail을 생성할 수 있는 폼을 제공합니다.

다음은 Action Mailbox 테스트 헬퍼를 사용하여 수신 이메일을 테스트하는 예입니다.

class ForwardsMailboxTest < ActionMailbox::TestCase
  test "직접 한 프로젝트에 해당하는 forwarder와 forwardee에 대한 클라이언트 forward 기록" 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

ActionMailbox::TestHelper API에서 추가 테스트 헬퍼 메서드를 참조하세요.

InboundEmail 소각

기본적으로 처리된 InboundEmail은 30일 후에 소각됩니다. InboundEmail은 상태가 delivered, failed 또는 bounced로 변경될 때 처리된 것으로 간주됩니다.

실제 소각은 IncinerationJob을 통해 수행되며, 이는 config.action_mailbox.incinerate_after 시간 후에 실행되도록 예약됩니다. 이 값은 기본적으로 30.days로 설정되어 있지만 production.rb 구성에서 변경할 수 있습니다. (이 먼 미래의 소각 예약은 작업 큐가 그 기간 동안 작업을 보유할 수 있다는 것을 전제로 합니다.)

기본 데이터 소각은 사용자가 계정을 취소하거나 콘텐츠를 삭제한 후에도 불필요하게 사람들의 데이터를 보유하지 않도록 보장합니다.

Action Mailbox 처리의 의도는 이메일을 처리할 때 이메일에서 필요한 모든 데이터를 추출하고 애플리케이션의 도메인 모델에 지속적으로 저장하는 것입니다. InboundEmail은 디버깅 및 포렌식을 위해 구성된 시간 동안 시스템에 남아 있다가 삭제됩니다.