PHP/Laravel

[Laravel9] 라라벨 이메일 보내기 with Google SMTP, Markdown

DSeung 2022. 10. 24. 16:00

0. 개요

구글 SMTP를 이용해서 라라벨 이메일 보내기 사이트를 만들어볼 것입니다.

이메일의 템플릿은 Blade 파일을 사용하지 않고 Markdown으로 해보려고 합니다.

  • Blade : 기존 view와 같은 방식으로 이메일 템플릿을 작성
  • Markdown : 직접 커스텀한 태그 또는 기본 태그를 사용해 이메일 템플릿을 작성

 

라라벨 공부 다른 글

바로가기

 

1. Project Setting

우선 Mail 프로젝트 만듭시다

composer create-project --prefer-dist laravel/laravel mail "9.*"
cd mail
composer install
php artisan key:generate

이제 각자의 환경에 맞춰 가상 호스트를 잡거나 DB 연결 등 부가적인 세팅을 해줍시다.

디비에는 메일을 보낼 사용자의 이메일을 저장할 것입니다.

mysql -u root -p
create database mail
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=mail
DB_USERNAME=root
DB_PASSWORD=secret

 

2. Google SMTP

메일 보내기 위해서는 특정 드라이버 및 SMTP를 사용해야 합니다.

여기서는 Google SMTP를 사용할 것이기에 아래 방법으로 설정해서 Google SMTP 비밀번호를 메모 해두길 바랍니다.

 

https://seung.tistory.com/entry/Google-SMTP-%EC%84%A4%EC%A0%95-%EB%B0%A9%EB%B2%95

 

Google SMTP 설정 방법

Google에서 지원하는 GMAIL SMTP를 사용하면 무료로 메일 보내기 기능을 만들 수 있습니다. 아래는 설정 방법입니다. 1. Gmail 페이지에 접속합니다. https://mail.google.com/ Gmail 이메일 또는 휴대전화 accoun.

seung.tistory.com

 

3. .env 세팅

.env의 메일 세팅 값을 아래와 같이 변경해줍시다.

MAIL_MAILER=smtp
MAIL_HOST=smtp.gmail.com
MAIL_PORT=465
MAIL_USERNAME=[본인이메일]
MAIL_PASSWORD=[Google SMTP 비밀번호]
MAIL_ENCRYPTION=ssl
MAIL_FROM_ADDRESS=[본인이메일]
MAIL_FROM_NAME="Mail Test"

 

4. 예제 

우선 아래와 같은 명령어들을 차례대로 입력해줍시다.

php artisan make:model EmailHistory -m
php artisan migrate
php artisan make:mail Email --markdown=emails.form
php artisan make:controller EmailHistoryController
php artisan make:request EmailSendRequest

차례대로 기능은 다음과 같습니다.

더보기
  • php artisan make:model EmailHistory -m 
    • EmailHistory 모델 및 email_histories 마이그레이션 추가 
  • php artisan migrate 
    • 테이블 생성
  • php artisan make:mail Email --markdown=emails.form 
    • Email 이름으로 Mailable 클래스 생성 및 emails.form 경로로 markdown 파일 생성
  • php artisan make:controller EmailHistoryController 
    • EmailHistoryController, Controller 클래스 생성
  • php artisan make:request EmailSendRequest 
    • EmailSendRequest, Request 클래스 생성

 

모델을 수정해줍시다.

/app/Models/EmailHisotry.php

class EmailHistory extends Model
{
    use HasFactory;
	
    // 모든 값 대용량 할당 허용
    protected $guarded = [];
}

 

우리는 Gmail에서 이메일 보내기 기능을 만들 것입니다, 여기서 이메일 보낸 이력을 쌓기 위해 만든 테이블이 email_histories입니다, 수정해줍시다.

/database/migrations/yyyy_mm_dd_000000_create_email_histories_table.php

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    public function up()
    {
        Schema::create('email_histories', function (Blueprint $table) {
            $table->id();
            $table->string('email');
            $table->string('subject');
            $table->string('message');
            $table->timestamps();
        });
    }

	...
};

수정한 마이그레이션으로 적용해줍시다

php artisan migrate:refresh

 

이제 컨트롤러를 수정해줍시다.

emailSend로 요청이 들어오면 request 값으로  이메일 전송 이력을 쌓고 성공 시 메일도 보내는 코드입니다.

/app/Http/Controllers/EmailHistoryController.php

<?php

namespace App\Http\Controllers;

use App\Http\Requests\EmailSendRequest;
use App\Mail\Email;
use App\Models\EmailHistory;
use Illuminate\Support\Facades\Mail;

class EmailHistoryController extends Controller
{
    private $emailHistory;

    // 의존성 주입
    public function __construct(EmailHistory $emailHistory)
    {
        $this->emailHistory = $emailHistory;
    }

    public function emailSend(EmailSendRequest $request){
        // 유효성 검사 오류시 422 상태 코드로 반환
        $validator = $request->safe()->all();

        // 이력 저장
        $emailHistory = $this->emailHistory->create(
            $validator
        );

        // 저장 성공시 이메일 발송
        if($emailHistory){
            Mail::to($validator['email'])->send(new Email($emailHistory));
            // 성공시 success section 으로 성공 문자 전달
            return redirect()->back()
                ->with([
                    'success' => '이메일을 성공적으로 발송했습니다.'
                ]);
        }
    }
}

 

위 컨트롤러 코드에서 EmailSendReqeust로 유효성 검사를 했습니다, 다음처럼 룰을 추가하시면 됩니다.

모든 값을 필수로 하되 email에 경우에만 이메일인지 체크하는 룰을 추가했습니다.

/app/Http/Requests/EmailSendRequest.php

class EmailSendRequest extends FormRequest
{
    public function authorize()
    {
        // 로그인 했을 때만 사용할 것인가 = false
        return true;
    }

    public function rules()
    {
        return [
            'email' => 'required|email',
            'subject' => 'required',
            'message' => 'required'
        ];
    }
}

 

이제 메일을 발송할 form을 만들어줍시다.

/resources/views 아래로 emailForm.blade.php를 만들어줍시다.

성공 시 Session에는 success 키로 문장이 오게 되고, 입력 값 실수 시 $errors로 실패한 키의 input 위로 마킹이 됩니다. 

/resources/views/emailForm.blade.php

<!DOCTYPE html>
<html>
<head>
    <title>Laravel Email Test</title>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.2/css/bootstrap.min.css" />
</head>
<body>
<div class="container">
    <div class="row mt-5 mb-5">
        <div class="col-10 offset-1 mt-5">
            <div class="card">
                <div class="card-body">
                    {{--성공시 성공 문장을 출력--}}
                    @if(Session::has('success'))
                        <div class="alert alert-success">
                            {{Session::get('success')}}
                        </div>
                    @endif

                    <form method="POST" action="{{ route('emailSend') }}">
                        @csrf

                        <div class="row">
                            <div class="col-md-12">
                                <div class="form-group">
                                    <strong>Email:</strong>
                                    <input type="text" name="email" class="form-control" placeholder="Email" value="{{ old('email') }}">
                                    @if ($errors->has('email'))
                                        <span class="text-danger">{{ $errors->first('email') }}</span>
                                    @endif
                                </div>
                            </div>
                            <div class="col-md-12">
                                <div class="form-group">
                                    <strong>Subject:</strong>
                                    <input type="text" name="subject" class="form-control" placeholder="Subject" value="{{ old('subject') }}">
                                    @if ($errors->has('subject'))
                                        <span class="text-danger">{{ $errors->first('subject') }}</span>
                                    @endif
                                </div>
                            </div>
                        </div>
                        <div class="row">
                            <div class="col-md-12">
                                <div class="form-group">
                                    <strong>Message:</strong>
                                    <textarea name="message" rows="3" class="form-control">{{ old('message') }}</textarea>
                                    @if ($errors->has('message'))
                                        <span class="text-danger">{{ $errors->first('message') }}</span>
                                    @endif
                                </div>
                            </div>
                        </div>

                        <div class="form-group text-center">
                            <button class="btn btn-success" type="reset">Reset</button>
                            <button class="btn btn-success btn-submit">Submit</button>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
</body>
</html>

 

이제 view를 만들었으니 라우팅을 수정하여 emailSend가 EmailHistoryController의 emailSend를 향하게 합시다.

/routes/web.php

<?php

use App\Http\Controllers\EmailHistoryController;
use Illuminate\Support\Facades\Route;

Route::get('/', function () {
    return view('welcome');
});

Route::get('/email', function () {
    return view('email');
})->name('emailForm');

Route::post('/emailSend', [EmailHistoryController::class, 'emailSend'])->name('emailSend');

 

이제 이전 Controller에서 사용한 Mailable 클래스의 내용을 수정해줍시다.

아래 코드를 보시면 Controller에서 아래 코드를 통해 넘긴 $emailHistory로 private 변수에 의존성 주입한 후

Mail::to($validator['email'])->send(new Email($emailHistory));

envelope, content에서 사용해서 값을 넣어주는 것을 확인할 수 있습니다.

/app/Mail/Email.php

<?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;

class Email extends Mailable
{
    use Queueable, SerializesModels;

    private $emailHistory;
    
    public function __construct($emailHistory)
    {
        $this->emailHistory = $emailHistory;
    }
    
    public function envelope()
    {
        return new Envelope(
            subject: $this->emailHistory->subject,
        );
    }
    
    public function content()
    {
        return new Content(
            markdown: 'emails.form',
            with: [
                'message' => $this->emailHistory->message,
                'url' => route('emailForm')
            ]
        );
    }

    public function attachments()
    {
        return [];
    }

    public function build()
    {
        return $this->markdown('emails.form');
    }
}

 

마지막으로 Mailable 클래스인 Email.php 마지막의 build 메소드에서 사용한 발송되는 이메일의 form을 수정해줍시다.

/resources/views/emails/form.blade.php

<x-mail::message>
# Introduction

{{$message}}

<x-mail::button :url="$url">
Home
</x-mail::button>

Thanks,<br>
{{ config('app.name') }}

</x-mail::message>

 

5. 시연 

저 같은 경우는 가상 호스트로 mail.test로 잡아 접속 주소는 다음과 같습니다.

http://mail.test/emailForm

 

개인이 설정한 것에 맞게 들어가시면 다음과 같이 작동하는 것을 확인 가능합니다.

이메일로 가시면 아래와 같이 잘 온 것을 확인할 수 있습니다.

반응형