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

Rails Action Mailbox로 인바운드 이메일 통합하고 트러블슈팅하기: 완벽 실전 가이드

이메일 발송과 수신과 관련된 RFC(Request for Comments) 문서를 한 번이라도 들여다본 적이 있다면, 메일함에서 '보내기' 버튼을 누르는 단순해 보이는 동작 뒤에 얼마나 복잡한 기술이 숨어 있는지 실감하게 됩니다.

다행히도 개발자를 위해 SMTP(Simple Mail Transfer Protocol) 서비스를 제공해 주는 도구가 이미 많이 존재합니다. 직접 운영하는 Postfix 서버부터 SendGrid, Amazon SES, Postmark처럼 확장성이 뛰어난 발송 서비스까지 선택지는 다양합니다. 하지만 전송 성공률(deliverability)이나 비용 문제로 서비스 제공업체를 옮길 때마다 각 서비스의 특수한 방식에 맞춰 앱 코드를 재작성하거나 리팩토링해야 하는 골칫거리가 따릅니다.

바로 이 지점에서 Rails가 Action Mailbox라는 해결책을 내놓습니다. 이 글에서는 Action Mailbox를 활용해 인바운드 이메일을 통합하고 문제를 해결하는 방법을 깊이 있게 살펴보겠습니다.

그 전에 먼저 Action Mailbox가 정확히 무엇인지 간단히 짚고 넘어가겠습니다.

Rails의 Action Mailbox란 무엇인가?

Action Mailbox는 Ruby on Rails에서 이메일 수신을 위한 개념적 압축(conceptual compression)을 제공합니다. 개념적 압축이란 각 SMTP 서비스 사이의 세세한 차이점을 모두 캡슐화하여, 인바운드 처리 코드를 단 한 번만 작성하면 되도록 만드는 것을 의미합니다. 심지어 새로운 이메일 서비스를 위한 커스텀 프로바이더를 직접 작성할 수도 있습니다.

ActionMailbox의 핵심 개념은 수신자(recipient) 주소 기반 라우팅입니다. 인바운드 이메일 프로바이더를 설정하면 특정 도메인으로 들어오는 메일이 앱으로 라우팅되며, 수신자 주소를 확인해 각 메일 메시지를 어떻게 처리할지 판단할 수 있습니다.

rails conductor 액션을 통해 테스트 이메일을 발송하면 다음과 같은 화면을 볼 수 있습니다.

Rails Action Mailbox로 인바운드 이메일 통합하고 트러블슈팅하기: 완벽 실전 가이드

이렇게 생성된 인바운드 이메일에는 To, CC, BCC, X-Original-To 필드의 수신자 정보가 모두 담깁니다.

각 주소는 어디로 라우팅될지 검사되지만, 메일 메시지 자체는 딱 한 번만 라우팅됩니다.

개발 과정에서 중요한 요소 중 하나는 바로 실제 이메일을 테스트하는 것입니다. Rails는 /rails/conductor/ 경로 아래에 개발용 페이지를 제공하여 로컬 환경에서 이메일을 손쉽게 입력하고 시험해 볼 수 있게 해 줍니다.

위 예시처럼 이메일을 직접 타이핑해서 입력할 수도 있고, 모든 헤더가 포함된 이메일 파일을 업로드할 수도 있습니다.

헤더, 본문, 첨부 파일이 모두 갖춰진 완전한 이메일을 확보하는 좋은 방법은 Thunderbird 같은 이메일 클라이언트를 활용하는 것입니다. 개별 이메일을 .eml 형식으로 저장한 뒤 텍스트 편집기로 파일을 열어 전체 내용을 conductor 페이지에 붙여넣으면 됩니다.

이제 훨씬 복잡한 이메일 처리 로직도 테스트할 준비가 끝났습니다.

Rails 앱 게시글·댓글 데모 만들기

전체 흐름이 어떻게 작동하는지 보여드리기 위해 작은 데모를 함께 만들어 보겠습니다. 저는 37Signals의 열렬한 팬인데, 특히 Hey World라는 블로그 서비스가 마음에 듭니다. 다만 댓글 기능이 없다는 아쉬움이 있어서, 각 블로그 포스트에 댓글을 달 수 있는 클론을 직접 만들어 보겠습니다.

이 글의 코드를 그대로 따라 해 보세요.

먼저 새 앱을 생성합니다(저는 Tailwind CSS를 사용하지만, 본인에게 익숙한 스택을 선택하면 됩니다). 그리고 PostComment 모델에 Action Text도 추가합니다.

스캐폴딩 덕분에 Posts와 Comments를 빠르게 확인할 수 있습니다. post.rb에 연관 관계(association)를 추가하면 포스트 화면에 관련 댓글이 함께 표시됩니다.

posts/_post.html.erb에는 comments partial을 추가합니다.

이제 간결한 포스트·댓글 뷰가 완성되었습니다. 블로그에 게시글을 등록할 인바운드 이메일을 설정해 보겠습니다.

이 명령은 ApplicationMailbox를 생성합니다. blog@로 향하는 모든 메일이 Post Mailbox로 라우팅되어 새 포스트를 생성하도록 라우트를 구성합니다.

https://localhost:3000/rails/conductor/action_mailbox/inbound_emails에 접속해 서비스로 몇 통의 이메일을 발송하면 즉시 테스트할 수 있습니다. blog@whatever.com으로 메일을 보내면 해당 이메일이 앱의 inbox로 전달됩니다. 반대로 다른 주소로 보내면 메시지가 반송(bounce)됩니다.

Post Mailbox로 이메일 받아 게시하기

이번에는 Post Mailbox를 설정해 이메일을 수신하고 블로그에 게시해 보겠습니다. 각 Mailbox는 원본 inbound_email 객체와 mail 객체에 접근할 수 있습니다. InboundEmail은 Rails 전반에서 사용되는 mail 클래스를 감싸는 래퍼(wrapper)입니다.

우리에게 필요한 것은 이메일의 발신자, 제목, 본문입니다. 이 세 가지를 추출해 블로그 첫 페이지에 표시될 Post 레코드를 생성할 수 있습니다.

블로그 주소로 이메일을 한 번 더 보낸 뒤 index 페이지를 새로 고침해 보세요. 새 포스트가 화면에 나타날 것입니다!

이제 포스트에 달리는 댓글 기능을 추가해 보겠습니다. 우선 이메일로 댓글을 남기려면 발신자가 올바른 포스트를 참조할 수 있어야 합니다. 가장 간단한 방법은 포스트 ID를 인바운드 이메일 주소 자체에 인코딩하는 것입니다(예: comment+123@whatever.com — 여기서 123은 Post 레코드를 가리킵니다).

CommentMailbox를 생성합니다.

그리고 Action Mailbox에 comment+123 형태의 이메일을 CommentMailbox로 보내는 라우트를 추가합니다.

_post.html.erb에는 이메일 주소를 자동으로 만들어 주는 링크를 추가해, 방문자가 자신의 메일 앱을 열고 바로 댓글 메일을 보낼 수 있게 합니다.

수신된 이메일은 CommentMailbox로 라우팅되어, 올바른 블로그 포스트에 연결된 댓글로 파싱됩니다.

process 메서드는 이메일 본문과 발신자 주소로 댓글을 생성합니다. 이 메서드는 post 메서드에서 조회한 Post를 참조하며, 첫 번째 수신자의 이메일 주소를 가져온 뒤 정규표현식으로 포스트 ID를 추출합니다.

만약 해당 Post가 존재하지 않거나 토큰을 파싱할 수 없으면 이메일이 반송되고 처리가 중단됩니다.

이제 Rails conductor 폼에서 각 포스트의 주소로 댓글 메일을 보내 보세요. index 페이지의 포스트 아래에 댓글이 나타나는 것을 확인할 수 있습니다!

Action Mailbox를 활용한 더 복잡한 예제

현실의 이메일은 생각보다 훨씬 복잡합니다. 애플리케이션 모니터링(APM) 도구를 설치하고 위 코드를 배포한 뒤, 대시보드에서 에러가 잔뜩 쏟아지는 상황을 상상해 보세요.

파싱 에러가 보이거나, 포스트와 댓글에 이상한 포맷 오류가 우후죽순 생겨날 수 있습니다.

원인은 앱이 HTML 이메일을 수신한 뒤 raw body 소스를 그대로 웹사이트에 게시하기 때문입니다. 다행히 mail 젬을 활용하면 수신 이메일에 HTML 본문이 포함되어 있는지 확인하고, 메시지에서 필요한 부분만 골라 가져올 수 있습니다.

CommentMailboxPostMailbox를 수정해 멀티파트(multipart) 이메일을 검사하고 HTML 파트를 추출하도록 바꿔 보겠습니다. HTML 파트가 없다면 텍스트 파트로 대체(fallback)합니다.

각 이메일은 파트가 하나도 없거나 여러 개로 구성됩니다. 선호 순서는 먼저 HTML 파트가 있는지 확인해 사용하고, 없으면 텍스트 파트를 가져오는 것입니다. 파싱된 HTML이나 텍스트 섹션이 전혀 없다면 기존처럼 이메일 본문을 그대로 사용합니다.

수정된 PostMailbox는 조금 더 복잡해졌습니다.

CommentMailbox 역시 새로운 process 메서드를 갖습니다.

이제 스마트폰에서 보내온 이메일도 안정적으로 처리할 수 있습니다.

Rails 앱에 Action Mailbox 도입하기

Action Mailbox 덕분에 이메일을 Rails 앱의 또 하나의 I/O 채널로 자연스럽게 바라볼 수 있습니다. 개념적 압축을 통해 특정 이메일 서비스 제공업체에 종속되지 않는 코드를 작성할 수 있습니다. 저 역시 기반 인프라를 신경 쓸 필요 없이 최소한의 작업만으로 이메일 제공업체를 마이그레이션한 경험이 있습니다.

AppSignal 같은 APM 도구는 발신 ActionMailer를 모두 모니터링하고 전달 가능성(deliverability)을 한눈에 살필 수 있는 편리한 대시보드도 제공합니다.

다음은 이메일을 대량으로 주고받는 제 앱의 실제 예시입니다.

Rails Action Mailbox로 인바운드 이메일 통합하고 트러블슈팅하기: 완벽 실전 가이드

이런 모니터링을 통해 앱 내부에서 벌어지는 일을 훨씬 명확하게 파악할 수 있습니다.

마무리

이 글에서는 먼저 Rails의 Action Mailbox가 제공하는 기능을 정의했습니다. 이어서 데모 프로젝트를 구축하며 인바운드 이메일을 통합하고, 수신 메일을 파싱해 블로그 포스트를 자동 생성하는 과정까지 함께 살펴봤습니다.

이 글이 여러분의 개발에 도움이 되었기를 바랍니다. 즐거운 코딩 되세요!

P.S. Ruby Magic의 글을 발행 즉시 읽어보고 싶다면 Ruby Magic 뉴스레터를 구독하세요. 어떤 글도 놓치지 않을 수 있습니다!

Rails Action Mailbox로 인바운드 이메일 통합하고 트러블슈팅하기: 완벽 실전 가이드

John Beatty

게스트 저자 John은 2010년 iPhone 앱용 API를 구축하기 위해 Rails를 배운 이후 다시 뒤돌아본 적이 없습니다. 현재는 고등학교 학생들에게 프로그래밍을 가르치고, 학교를 위한 맞춤형 소프트웨어를 개발하며, 블로그를 운영하고 있습니다.

John Beatty의 모든 글 보기