Laravel 의 메일 기능이란
Laravel의 메일 기능은 Symfony Mailer를 베이스로 구축되어 있으며, SMTP·Mailgun·Postmark·Resend·Amazon SES·sendmail 등의 드라이버에 대응하고 있습니다.
이전 Laravel에서 사용되었던 SwiftMailer는 폐지되어 있습니다. Laravel 9 이후는 Symfony Mailer를 사용합니다.
메일 설정은 config/mail.php에서 관리합니다. .env 파일에서 사용할 드라이버를 전환할 수 있습니다.
MAIL_MAILER=smtp
MAIL_HOST=smtp.example.com
MAIL_PORT=587
MAIL_USERNAME=your-username
MAIL_PASSWORD=your-password
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS="[email protected]"
MAIL_FROM_NAME="${APP_NAME}"
Mailable 클래스의 작성
Laravel에서는 애플리케이션이 전송하는 각 메일을 “Mailable” 클래스로 표현합니다. make:mail Artisan 명령어로 클래스를 생성합니다.
php artisan make:mail OrderShipped
생성된 클래스는 app/Mail/ 디렉터리에 배치됩니다.
Mailable 의 설정
생성된 Mailable 클래스에는 envelope(), content(), attachments()의 3가지 메서드가 있습니다.
<?php
namespace App\Mail;
use App\Models\Order;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
class OrderShipped extends Mailable
{
use Queueable, SerializesModels;
public function __construct(
public Order $order,
) {}
public function envelope(): Envelope
{
return new Envelope(
subject: '주문이 발송되었습니다',
);
}
public function content(): Content
{
return new Content(
view: 'mail.orders.shipped',
);
}
public function attachments(): array
{
return [];
}
}
송신자의 설정
envelope() 메서드로 송신자를 지정할 수 있습니다.
use Illuminate\Mail\Mailables\Address;
use Illuminate\Mail\Mailables\Envelope;
public function envelope(): Envelope
{
return new Envelope(
from: new Address('[email protected]', 'Shop Support'),
subject: '주문이 발송되었습니다',
);
}
config/mail.php에 글로벌한 송신자 주소를 설정해 두면, 개별 Mailable에서의 지정을 생략할 수 있습니다.
'from' => [
'address' => env('MAIL_FROM_ADDRESS', '[email protected]'),
'name' => env('MAIL_FROM_NAME', 'Example'),
],
메일 본문의 설정
content() 메서드로 Blade 템플릿을 지정합니다. 생성자에서 public 프로퍼티에 설정한 데이터는 자동으로 템플릿에서 이용할 수 있습니다.
public function content(): Content
{
return new Content(
view: 'mail.orders.shipped',
);
}
대응하는 Blade 템플릿(resources/views/mail/orders/shipped.blade.php)을 작성합니다.
<!DOCTYPE html>
<html>
<body>
<h1>귀하의 주문이 발송되었습니다</h1>
<p>주문 번호: {{ $order->id }}</p>
<p>합계 금액: ¥{{ number_format($order->total) }}</p>
<p>주문해 주셔서 감사합니다.</p>
</body>
</html>
데이터를 with 파라미터로 명시적으로 전달할 수도 있습니다. 이 경우는 프로퍼티를 protected 또는 private로 합니다.
public function __construct(
protected Order $order,
) {}
public function content(): Content
{
return new Content(
view: 'mail.orders.shipped',
with: [
'orderId' => $this->order->id,
'total' => $this->order->total,
],
);
}
메일의 전송
Mail 파사드의 to() 메서드로 수신자를 지정하고, send()로 메일을 전송합니다.
<?php
namespace App\Http\Controllers;
use App\Mail\OrderShipped;
use App\Models\Order;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Mail;
class OrderShipmentController extends Controller
{
public function store(Request $request): RedirectResponse
{
$order = Order::findOrFail($request->order_id);
Mail::to($request->user())->send(new OrderShipped($order));
return redirect('/orders');
}
}
CC·BCC도 지정할 수 있습니다.
Mail::to($request->user())
->cc($ccUsers)
->bcc($bccUsers)
->send(new OrderShipped($order));
특정 메일러를 사용해 전송하려면 mailer() 메서드를 사용합니다.
Mail::mailer('postmark')
->to($request->user())
->send(new OrderShipped($order));
큐를 사용한 메일 전송
메일 전송은 응답 타임에 영향을 미치기 때문에, 큐를 사용해 백그라운드에서 전송하는 것을 권장합니다.
Mail::to($request->user())->queue(new OrderShipped($order));
큐를 사용하려면 사전에 큐의 설정과 워커의 기동이 필요합니다.
지연 전송도 가능합니다.
Mail::to($request->user())
->later(now()->plus(minutes: 10), new OrderShipped($order));
Mailable 클래스에 ShouldQueue를 구현하면, send()를 호출해도 항상 큐에 들어가게 됩니다.
use Illuminate\Contracts\Queue\ShouldQueue;
class OrderShipped extends Mailable implements ShouldQueue
{
// ...
}
큐 접속과 큐 이름을 PHP 속성으로 지정
PHP 속성을 사용해 Mailable 클래스에 큐 접속과 큐 이름을 직접 지정할 수 있습니다. on()->queue() 메서드 체인 대신에 클래스 레벨에서 선언적으로 설정할 수 있습니다.
use Illuminate\Queue\Attributes\Connection;
use Illuminate\Queue\Attributes\Queue;
#[Connection('sqs')]
#[Queue('emails')]
class OrderShipped extends Mailable implements ShouldQueue
{
// ...
}
이 방법으로는 이 Mailable이 항상 SQS 접속의 emails 큐로 전송됨을 클래스 정의에서 명시할 수 있습니다.
Markdown Mail 의 작성
Markdown 형식의 메일을 사용하면 Laravel이 제공하는 아름다운 반응형 HTML 템플릿을 활용할 수 있습니다.
Markdown Mailable 의 생성
--markdown 옵션을 붙여 Mailable을 생성합니다.
php artisan make:mail OrderShipped --markdown=mail.orders.shipped
content() 메서드에서 markdown 파라미터를 사용합니다.
use Illuminate\Mail\Mailables\Content;
public function content(): Content
{
return new Content(
markdown: 'mail.orders.shipped',
with: [
'url' => route('orders.show', $this->order),
],
);
}
Markdown 템플릿의 기술
Laravel이 제공하는 Blade 컴포넌트를 사용해 메일 본문을 작성합니다.
<x-mail::message>
# 귀하의 주문이 발송되었습니다
주문이 발송되었으므로 알려 드립니다.
<x-mail::button :url="$url">
주문을 확인하기
</x-mail::button>
이용해 주셔서 감사합니다.<br>
{{ config('app.name') }}
</x-mail::message>
이용 가능한 컴포넌트는 다음과 같습니다.
| 컴포넌트 | 설명 |
|---|
<x-mail::button> | 버튼 링크(color에 primary·success·error를 지정 가능) |
<x-mail::panel> | 눈에 띄는 패널 영역 |
<x-mail::table> | Markdown 형식의 테이블 |
Markdown 메일은 동시에 플레인 텍스트 버전도 자동 생성됩니다. HTML 메일에 대응하지 않는 메일 클라이언트에서도 읽을 수 있습니다.
메일의 프리뷰
라우트에서 Mailable 인스턴스를 반환하면 브라우저에서 메일을 프리뷰할 수 있습니다. 개발 중의 확인에 편리합니다.
// routes/web.php
Route::get('/mailable', function () {
$order = App\Models\Order::first();
return new App\Mail\OrderShipped($order);
});
로컬 개발에서의 메일
운영 환경에서 메일을 잘못 전송하지 않도록, 로컬 개발에서는 Mailpit 등의 도구를 사용하면 편리합니다.
# .env (Mailpit)
MAIL_MAILER=smtp
MAIL_HOST=127.0.0.1
MAIL_PORT=1025
Laragon이나 Laravel Herd에서는 Mailpit이 기본적으로 포함되어 있습니다.
log 드라이버를 사용하면 메일의 내용이 로그 파일에 기록됩니다. 메일 전송의 확인만이라면 가장 간편한 방법입니다.
| 하고 싶은 것 | 방법 |
|---|
| Mailable을 작성 | php artisan make:mail ClassName |
| 송신자를 설정 | envelope()의 from 파라미터 |
| Blade 템플릿을 사용 | content()의 view 파라미터 |
| Markdown 템플릿을 사용 | content()의 markdown 파라미터 |
| 메일을 전송 | Mail::to()->send() |
| 큐로 전송 | Mail::to()->queue() |
| 지연 전송 | Mail::to()->later() |