이메일 받기

이메일 메시지는 HTTP 요청의 형태로 애플리케이션으로 전송됩니다. 수신 이메일 메시지를 처리하려면 애플리케이션 구성에서 이메일 주소를 서블릿과 연결한 후 앱과 함께 서블릿 코드를 포함해야 합니다. 수신 이메일은 처리에 사용되는 적절한 서블릿에 전달되는 HTTP 요청을 생성합니다.

이메일을 수신하도록 애플리케이션 구성

새로운 애플리케이션을 만들면 이메일 수신은 기본적으로 사용 중지되어 있습니다. 이를 명시적으로 사용 설정하지 않으면 애플리케이션으로 전송되는 수신 이메일 메시지는 무시됩니다.

수신 이메일 서비스를 사용 설정하려면 appengine-web.xmlweb.xml 구성 파일을 수정합니다.

appengine-web.xml에서 이메일 사용 설정하기

수신 이메일 서비스를 사용 설정하는 inbound-services 섹션을 추가하여 appengine-web.xml을 수정합니다.

<inbound-services>
  <!-- Used to handle incoming mail. -->
  <service>mail</service>
  <!-- Used to handle bounced mail notifications. -->
  <service>mail_bounce</service>
</inbound-services>

이메일 메시지는 다음 URL을 사용하여 HTTP POST 요청의 형태로 애플리케이션으로 전송됩니다.

/_ah/mail/<ADDRESS>

여기서 <ADDRESS>는 도메인 이름이 포함된 전체 이메일 주소입니다. 참고: 앱이 커스텀 도메인에 배포되어 있어도 해당 도메인의 주소로 보낸 이메일은 받을 수 없습니다.

web.xml에서 이메일 사용 설정하기

이메일 URL을 서블릿에 매핑하여 web.xml을 수정합니다.

<servlet>
  <servlet-name>mailhandler</servlet-name>
  <servlet-class>com.example.appengine.mail.MailHandlerServlet</servlet-class>
</servlet>
<servlet-mapping>
  <servlet-name>mailhandler</servlet-name>
  <url-pattern>/_ah/mail/*</url-pattern>
</servlet-mapping>
<security-constraint>
  <web-resource-collection>
    <web-resource-name>mail</web-resource-name>
    <url-pattern>/_ah/mail/*</url-pattern>
  </web-resource-collection>
  <auth-constraint>
    <role-name>admin</role-name>
  </auth-constraint>
</security-constraint>

위 스니펫에서 /_ah/mail/*은 앱으로 주소가 지정된 모든 이메일과 일치합니다. 메일 서블릿은 App Engine에서 현재 제공되는 앱 버전에서 실행됩니다.

수신 메시지의 패턴 기반 발송

앱에서 패턴 일치를 사용하는 경우 다음 코드 스니펫을 기반으로 하는 필터 기반 방식을 사용하는 것이 좋습니다.

구체적 핸들러

public class HandleDiscussionEmail extends MailHandlerBase {

  private static final Logger log = Logger.getLogger(HandleDiscussionEmail.class.getName());
  public HandleDiscussionEmail() { super("discuss-(.*)@(.*)"); }

  @Override
  protected boolean processMessage(HttpServletRequest req, HttpServletResponse res)
    throws ServletException
  {
    log.info("Received e-mail sent to discuss list.");
    MimeMessage msg = getMessageFromRequest(req);
    Matcher match = getMatcherFromRequest(req);
    // ...
    return true;
  }
}

위 구체적 핸들러는 web.xml의 다음 스니펫을 통해 등록됩니다.

<filter>
  <filter-name>HandleDiscussionEmail</filter-name>
  <filter-class>com.example.appengine.mail.HandleDiscussionEmail</filter-class>
</filter>
<filter-mapping>
  <filter-name>HandleDiscussionEmail</filter-name>
  <url-pattern>/_ah/mail/*</url-pattern>
</filter-mapping>

security-constraint 지시문은 필터에 사용될 수 없습니다. 핸들러에 대한 보안 정책을 다른 방식으로 도입해야 합니다.

추상 핸들러

public abstract class MailHandlerBase implements Filter {

  private Pattern pattern = null;

  protected MailHandlerBase(String pattern) {
    if (pattern == null || pattern.trim().length() == 0)
    {
      throw new IllegalArgumentException("Expected non-empty regular expression");
    }
    this.pattern = Pattern.compile("/_ah/mail/"+pattern);
  }

  @Override public void init(FilterConfig config) throws ServletException { }

  @Override public void destroy() { }

  /**
   * Process the message. A message will only be passed to this method
   * if the servletPath of the message (typically the recipient for
   * appengine) satisfies the pattern passed to the constructor. If
   * the implementation returns false, control is passed
   * to the next filter in the chain. If the implementation returns
   * true, the filter chain is terminated.
   *
   * The Matcher for the pattern can be retrieved via
   * getMatcherFromRequest (e.g. if groups are used in the pattern).
   */
  protected abstract boolean processMessage(HttpServletRequest req, HttpServletResponse res) throws ServletException;

  @Override
  public void doFilter(ServletRequest sreq, ServletResponse sres, FilterChain chain)
      throws IOException, ServletException {

    HttpServletRequest req = (HttpServletRequest) sreq;
    HttpServletResponse res = (HttpServletResponse) sres;

    MimeMessage message = getMessageFromRequest(req);
    Matcher m = applyPattern(req);

    if (m != null && processMessage(req, res)) {
      return;
    }

    chain.doFilter(req, res); // Try the next one

  }

  private Matcher applyPattern(HttpServletRequest req) {
    Matcher m = pattern.matcher(req.getServletPath());
    if (!m.matches()) m = null;

    req.setAttribute("matcher", m);
    return m;
  }

  protected Matcher getMatcherFromRequest(ServletRequest req) {
    return (Matcher) req.getAttribute("matcher");
  }

  protected MimeMessage getMessageFromRequest(ServletRequest req) throws ServletException {
    MimeMessage message = (MimeMessage) req.getAttribute("mimeMessage");
    if (message == null) {
      try {
        Properties props = new Properties();
        Session session = Session.getDefaultInstance(props, null);
        message = new MimeMessage(session, req.getInputStream());
        req.setAttribute("mimeMessage", message);

      } catch (MessagingException e) {
        throw new ServletException("Error processing inbound message", e);
      } catch (IOException e) {
        throw new ServletException("Error processing inbound message", e);
      }
    }
    return message;
  }
}

수신 이메일 처리

JavaMail API에는 수신 이메일 메시지를 파싱하는 데 사용할 수 있는 MimeMessage 클래스가 포함되어 있습니다. MimeMessage에는 빈 구성을 가질 수 있는 java.io.InputStream 및 JavaMail 세션을 허용하는 생성자가 있습니다.

다음과 같이 MimeMessage 인스턴스를 만듭니다.

import java.io.IOException;
import java.util.logging.Logger;
import java.util.Properties;

import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.internet.MimeMessage;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class MailHandlerServlet extends HttpServlet {

  private static final Logger log = Logger.getLogger(MailHandlerServlet.class.getName());

  @Override
  public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
    Properties props = new Properties();
    Session session = Session.getDefaultInstance(props, null);
    try {
      MimeMessage message = new MimeMessage(session, req.getInputStream());
      log.info("Received mail message.");
    } catch (MessagingException e) {
      // ...
    }
    // ...
  }
}

그런 다음 다양한 메서드를 사용하여 message 객체를 파싱할 수 있습니다.

  • getFrom()을 호출하여 메시지 발신자를 반환합니다.
  • getContentType()을 호출하여 메시지 콘텐츠 유형을 추출합니다. getContent() 메서드는 Multipart 인터페이스를 구현하는 객체를 반환합니다.
  • getCount()를 호출하여 부분 수를 확인합니다.
  • getBodyPart(int index)를 호출하여 특정 본문 부분을 반환합니다.

수신 이메일을 처리하도록 애플리케이션을 설정한 후 개발 서버 콘솔을 사용하여 수신 이메일을 시뮬레이션할 수 있습니다. 개발 서버를 시작하는 방법을 비롯한 자세한 내용은 자바 개발 서버를 참조하세요. 로컬 개발 서버에서 애플리케이션을 시작한 후 URL http://localhost:8888/_ah/admin/을 방문하여 애플리케이션에 액세스할 수 있습니다. 로컬 개발 서버의 기본 포트를 사용하지 않는 경우 8888 값은 사용 중인 포트로 대체합니다.

개발 서버에서 좌측의 수신 메일을 클릭하여 나타나는 양식을 작성한 다음 이메일 보내기를 클릭합니다.