Computer >> 컴퓨터 >  >> 프로그래밍 >> Ruby

Incoming! – 모든 랙(Rack) 애플리케이션에서 인바운드 이메일 처리하기

고객에게서 전화가 왔습니다...

고객은 자신의 앱에서 수신 이메일(inbound email)을 처리하기를 원합니다. Basecamp처럼요.

"문제없지. 이메일을 POST 방식으로 앱에 전달해 주는 서비스가 많으니 금방 하겠네."라고 생각하셨나요?

안타깝게도 그렇지 않습니다.

각 서비스마다 제각각인 독자적 형식을 사용하기 때문입니다. 어떤 서비스는 파싱된 이메일을 보내고, 어떤 서비스는 원본(raw) 데이터를 그대로 전달합니다. 인증 서명(authentication signature)을 함께 보내는 곳도 있는가 하면, 그렇지 않은 곳도 있습니다.

이런 번거로움을 모두 건너뛰고 표준적이며 널리 알려진 Ruby 객체만으로 작업할 수 있다면 얼마나 좋을까요?

게다가 코드 한 줄도 고치지 않고 이메일 벤더를 자유롭게 변경할 수 있다면 더욱 좋겠죠?

Incoming!을 사용하면 그 모든 것이 가능합니다.

TL;DR (한 줄 요약)

Incoming!은 Rack::Request 객체를 받아 표준 Mail::Message 객체로 변환해 줍니다.

지원 서비스

Incoming!은 다음과 같은 주요 이메일 서비스들을 지원합니다:

  1. SendGrid

  2. Mailgun

  3. Postmark

  4. CloudMailin

  5. 직접 구축·운영하는 자체 메일 서버까지도 (와우)

무엇보다 확장이 매우 쉽다는 점이 강점입니다. 덕분에 아직 존재하지 않는 새로운 서비스와도 사실상 호환됩니다! (물론 문자 그대로는 아니지만, 구조적으로는 그렇습니다)

인바운드 이메일, 왜 필요할까요?

사용자에게 "이메일을 보내는 것만으로" 원하는 작업을 할 수 있는 옵션을 제공하면, 실제로 그 행동을 할 가능성이 눈에 띄게 높아집니다.

저희는 honeybadger.io를 위해 Incoming!을 만들었습니다. 에러 관련 논의를 더 원활하게 진행할 방법이 필요했기 때문입니다. 예를 들어, 알림 메일에 답장만 하면 해당 에러에 바로 코멘트가 달립니다. 실제로 저희 코멘트 대부분이 이 방식으로 들어오기 때문에, 큰 성과라고 평가하고 있습니다.

이 글을 작성하는 시점 기준으로, 이 젬은 Honeybadger 팀 내부에서 약 6개월간 사용되어 왔습니다.

코드를 보여주세요!

CloudMailin을 사용하는 간단한 예제입니다.

# 먼저 이메일 수신기(receiver) 클래스를 정의합니다
class EmailReceiver < Incoming::Strategies::CloudMailin
  def receive(mail)
    puts %(Got message from #{mail.to.first} with subject "#{mail.subject}")
  end
end

# 그다음 Rack::Request 객체를 넣어주면 끝입니다.
req = Rack::Request.new(env)
result = EmailReceiver.receive(req) # => Got message from whoever@wherever.com with subject "hello world"

더 자세한 내용은 공식 Readme 문서를 확인해 주세요.

Rails에서는 어떻게 사용하나요?

Rails에서 Incoming!을 사용하는 것도 마찬가지로 간단합니다. 샘플 앱을 하나 만들어 보겠습니다.

요구 사항: 저는 문자 메시지를 보내는 일이 딱히 좋지 않습니다. 개인적인 취향이지만, 수업 중인 약혼녀에게 러브레터를 보낼 방법이 꼭 필요합니다.

당연히 해답은 이메일-SMS 브릿지를 만드는 것입니다!

첫째, 이메일 수신기를 설정합니다:

# app/email_receivers/incoming.rb
class EmailToSmsReceiver < Incoming::Strategies::Postmark
  def receive(mail)
    send_sms([mail.subject, mail.body].join(": "))
  end
  private
    def send_sms(message)
      # 여기에 twilio 연동 로직을 넣습니다
    end
end

둘째, 요청을 수신기로 전달하는 컨트롤러를 추가합니다:

# app/controllers/emails_controller.rb
class EmailsController < ActionController::Base
  def create
    if EmailToSmsReceiver.receive(request)
      render :json => { :status => 'ok' }
    else
      render :json => { :status => 'rejected' }, :status => 403
    end
  end
end

아 맞다, 라우트도 필요합니다:

# config/routes.rb
Rails.application.routes.draw do
  post '/emails' => 'emails#create'
end

이게 전부입니다. 정말로요.

Incoming!은 다른 젬(gem)과 비교하면 어떤가요?

이 분야에는 다른 젬들도 존재합니다. 특히 Thoughtbot의 Griddler가 가장 잘 알려져 있습니다.

하지만 Incoming!이 두드러지는 몇 가지 차별점이 있습니다:

  1. 여러 메일 서비스를 하나의 인터페이스로 지원합니다

  2. Rails뿐 아니라 어떤 랙(Rack) 애플리케이션에서도 동작합니다

  3. 표준 Mail::Message 객체를 그대로 전달해 줍니다

  4. 특정 사용 사례에 얽매이지 않는 유연한 설계를 갖추고 있습니다

밈(meme) 사진도 있나요?

네, 물론 준비되어 있습니다.

Incoming! – 모든 랙(Rack) 애플리케이션에서 인바운드 이메일 처리하기