Jeen - Yet anothere techlog

STFUAWSC

Perl - Email Send

회사 선배이신 @mintegrals 께서 최근에 사내 버그질라에 올려놓은 내용을 보고 약간 코드를 수정해봤습니다.

내용인즉슨, 파일 첨부한 메일을 어떻게 보낼 수 있냐라는 것이었지요. 기존에 써놨던 메일송신을 하는 모듈이 파일첨부를 고려하지 않았던 것이었는데, 그런 상황등을 고려해서 좀 더 수정해봤습니다.

MyApp::Mail

~~~ perl package MyApp::Mail; use Moose; use namespace::autoclean; use Email::MIME; use Email::Sender::Simple ‘sendmail’; use Email::Sender::Transport::SMTP; use Encode; use MIME::Types ();

has to => (

is      => 'ro',
isa     => 'Str',
default => sub {
    Encode::encode( "MIME-Header", $_\[0\] );
},
required => 1,

);

has from => (

is      => 'ro',
isa     => 'Str',
default => sub {
    Encode::encode( "MIME-Header", $_\[0\] );
},
required => 1,

);

has subject => (

is      => 'ro',
isa     => 'Str',
default => sub {
    Encode::encode( "MIME-Header", $_\[0\] );
},
required => 1,

);

has body => (

is  => 'ro',
isa => 'Str',

);

has transport => (

is  => 'ro',
isa => 'HashRef'

);

has attachments => (

is => 'ro',
isa => 'ArrayRef\[Path::Class::File\]',
default => sub { \[\] },

);

sub _get_parts {

my $self = shift;

my @parts;
push @parts, 
    Email::MIME->create(
        attributes => {
            content_type => 'text/plain',
            charset      => 'utf8',
        },
        body => Encode::encode_utf8($self->body),
    );

for my $attachment (@{ $self->attachments }) {
    confess "File Not Found : $attachment" unless -f $attachment;
    my ($mime_type, $encoding) = MIME::Types::by_suffix($attachment->basename);
    $mime_type ||= 'multipart/mixed';
    my $file_body = $attachment->slurp;
    push @parts, Email::MIME->create(
        attributes => {
            content_type => $mime_type,
            name         => $attachment->basename,
            filename     => $attachment->basename,
            encoding     => 'base64',
            disposition  => 'attachment',
        },
        body => $file_body,
    );
}
\@parts;

}

sub send {

my $self = shift;

my $opt = {};
if ($self->transport) {
    $opt->{transport} = Email::Sender::Transport::SMTP->new( $self->transport );
}

my $email = Email::MIME->create(
    header => \[
        From    => $self->from,
        To      => $self->to,
        Subject => $self->subject
    \],
    parts => $self->_get_parts, 
);
sendmail($email, $opt);

}

PACKAGE–>meta->make_immutable;

1; ~~~

파일첨부를 위한 attachments 접근자를 추가로 지정합니다. 첨부파일은 하나가 아닌 여러개가 가능하기에 ArrayRef 를 기본형식으로 합니다. 각 요소는 반드시 Path::Class::File 로 받도록 했습니다.

MIME::Types 로 첨부파일의 확장자를 통해서 MIME-TYPE 을 뽑아내도록 하고, 알 수 없다면 multipart/mixed 로 지정했습니다.

transport 레이어를 별도로 지정할 수 있으며, transport 의 지정이 없을 경우는 당연히 로컬호스트의 smtp 를 이용하도록 합니다.

Using MyApp::Mail

위의 MyApp::Mail 을 이용하기 위해서 아래와 같이 sendmail.pl 이라는 스크립트를 준비했습니다.

~~~ perl

sendmail.pl

use strict; use warnings; use MyApp::Mail; use Path::Class::File;

my $to = ‘your@email.com’; my $from = ‘my@email.com’; my $subject = ‘MailSender Test Subject’; my $body = ‘MailSender Test Body’;

my $mailer = MyApp::Mail->new(

to => $to,
from => $from,
subject => $subject,
body    => $body,
attachments => \[
  Path::Class::File->new('test.doc'),
  Path::Class::File->new('text.pdf')
\],
transport => {
    host => 'smtp.gmail.com',
    port => 465,
    sasl_username => 'YOUR_GOOGLE EMAIL',
    sasl_password => 'YOUR_GOOGLE_PASS',
    ssl  => 1,
},

);

$mailer->send; ~~~

위에서 언급한 transport 레이어에 지메일의 smtp 를 사용하도록 합니다. 이를 위해서 물론 Google 계정정보를 입력할 필요가 있습니다.

How to Test?

Email::Sender::Manual::QuickStart 문서를 확인 해보면 Testing 관련 내용이 나옵니다.

~~~ perl use Test::More; BEGIN { $ENV{EMAIL_SENDER_TRANSPORT} = ‘Test’ } use YourCode;

YourCode->run;

my @deliveries = Email::Sender::Simple->default_transport->deliveries; ~~~

위와같은 샘플코드가 제시되어 있는데,

perl BEGIN { $ENV{EMAIL_SENDER_TRANSPORT} = ‘Test’ }

가 지정되면, 실제로 메일 송신이 이뤄지지 않습니다.

perl my @deliveries = Email::Sender::Simple->default_transport->deliveries;

에서 그동안 송신한 메일건수당 성공실패를 판별할 수 있고, 작성된 메일 내용 또한 확인이 가능합니다. 테스트코드를 작성함에 있어서 위의 두 부분이 절대적으로 필요합니다.

위처럼 테스트 코드를 작성할 시에는 실제로 메일 송신을 하지 않았기에 transport 레이어에서 발생하는 어떤 불상사는 감지할 수 없습니다.

Conclusion

이전에 메일보내기 관련해서 SSMTP + MIME::Lite 를 사용하는 그런 코드를 2008년쯤에 썼었네요. 부끄럽습니다. 2009년부터는 아마 MIME::Lite 를 안 썼던 것 같네요.

뭐 당장 필요로 하는 부분만 구현을 한 것일 뿐인지라, 차후에 다른 어떤 요구사항이 있다면 그에 맞춰서 다시 한번 소개를 해볼까 합니다.

Comments