라라벨에서는 Notification 기능을 이용해 알림을 구현할 수 있다. 즉, 어떤 상황이나 이벤트 시점에서 알림을 위한 메시지를 발송할 수 있는데 이메일이나 SMS 또는 Slack과 같은 여러 가지 채널을  사용할 수 있다. 


여기서는 새로운 태스크를 등록했을 때나 등록한 태스크를 완료처리했을 때 등록한 사람의 이메일 또는 완료된 태스크의 등록자에게 이메일이나 Slack으로 새로운 태스크가 등록 또는 완료처리되었다는 알림 메일을 발송해보도록 하자. 


이를 위해 로그인한 사용자가 새로운 태스크를 등록하고 등록한 태스크가 완료되었을 때 이를 완료처리하는 기능의 프로젝트를 개발해야 한다. 테스트 하기에 적절한 샘플 프로젝트는 여기를 참고해서 만들어보면 좋다.


프로젝트를 만들면 아래 화면에서처럼 신규 태스크를 등록할 수도 있고 등록된 태스크를 완료처리할 수도 있다.



매우 단순한 기능을 가진 프로젝트이다. 가입하고 로그인을 해야만 사용이 가능하고 이를 위해 라라벨에서 기본적으로 제공하는 인증기능을 그대로 가져다 사용하였다. 





1. 알림 발송하기


알림을 발송하기 위해서는 먼저 알림이 필요한 시점에서 Notifiable 트레이트의 notify 메소드를 사용하거나, Notification 파사드를 사용한다. 


태스크를 등록하는 경우 등록이 완료된 것을 알리기 위해 등록자의 이메일로 메일을 발송한다고 해보자. TaskController에서 태스크를 등록하는 메소드가 store()라고 하면 아래와 같이 이 메소드에서 새로운 태스크를 등록하고 알림을 발송하면 된다.

    /**
     * Store a newly created resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function store(Request $request)
    {
        // validate the given request
        $data = $this->validate($request, [
            'title' => 'required|string|max:255'
        ]);

        // create a new incomplete task with the given title
        $task = Auth::user()->tasks()->create([
            'title' => $data['title'],
            'is_complete' => false
        ]);

        session()->flash('status', 'Task Created!');

        // 새로운 태스크가 등록되었을 때 알림
        $request->user()->notify(new NewTaskRegistered($task));

        return redirect('/tasks');
    }

notify라는 메소드는 Notifiable 트레이트에서 참조하고 있는 메소드로 이를 통해 알림을 실행한다. 이때 어떤 알림을 사용할 것인지 알려주기 위해 알림 클래스를 하나 만들고 해당 알림클래스의 객체를 전송하면 된다. 


여기서는 새로운 태스크가 등록되었을 때 사용할 알림이므로 NewTaskRegistered라는 이름의 알림을 생성한다. 알림을 생성하는 방법은 아래와 같다. 


php artisan make:notification NewTaskRegistered


그러면 Notifications 폴더 안에 NewTaskRegistered라는 이름의 알림 클래스가 생성된다. 이 클래스에서 어떤 채널을 사용하여 알림을 사용할 것인지를 정의하면 된다. 생성된 알림 클래스의 코드를 보면 아래와 같다. 

<?php

namespace App\Notifications;

use App\Models\Task;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;

    class NewTaskRegistered extends Notification
{
    use Queueable;

    public $task;

    /**
     * Create a new notification instance.
     *
     * @return void
     */
    public function __construct(Task $task)
    {
        $this->task = $task;
    }

    /**
     * Get the notification's delivery channels.
     *
     * @param  mixed  $notifiable
     * @return array
     */
    public function via($notifiable)
    {
        return ['mail'];
    }

    /**
     * Get the mail representation of the notification.
     *
     * @param  mixed  $notifiable
     * @return \Illuminate\Notifications\Messages\MailMessage
     */
    public function toMail($notifiable)
    {
        $subject = sprintf('%s : %s님의 신규 태스크 등록 알림', config('app.name'), $this->task->user->name);

        $greeting = sprintf('안녕하세요 %s님!', $this->task->user->name);

        return (new MailMessage)
                    ->subject($subject)
                    ->greeting($greeting)
                    ->line('작성하신 태스크가 등록되었습니다.')
                    ->action('내 태스크 보기 ', url('/tasks'))
                    ->line('사용해주셔서 감사합니다!');
    }

    /**
     * Get the array representation of the notification.
     *
     * @param  mixed  $notifiable
     * @return array
     */
    public function toArray($notifiable)
    {
        return [
            //
        ];
    }
}
?>


via 메소드를 이용해 알림으로 어떤 수단을 사용할 것인지를 정의한다. 여기서는 메일을 사용할 것이므로 이 하나만을 지정하였는데 메일 외에도 database, broadcast, nexmo 그리고 slack 등 사용할 수 있는 여러 채널이 존재하며 여러 개의 채널을 함께 사용할 수 있다. 


메일을 사용하기로 했으면 toMail() 메소드를 통해 발송할 메일의 제목과 내용 등을 정의하면 된다. 그러면 새로운 태스크가 등록될 때마다 메일을 통해 알림이 발송된다. 이때 발송되는 메일은 로그인한 사용자 객체의 email 속성에 정의된 메일로 발송된다. 


다음과 같이 새로운 태스크를 등록해보자.



그러면 알림에 의해 태스크를 등록한 사용자에게 알림 메일이 발송된다.






2. Slack 채널로 알림하기


두번째로 등록한 태스크를 완료처리했을 때 메일 뿐 아니라 슬랙(Slack) 채널을 이용해 알림을 발송해보자.


TaskController에서 태스크 완료처리는 update() 메소드에서 정의한다. 

    /**
     * Update the specified resource in storage.
     *
     * @param  \Illuminate\Http\Request $request
     * @param  int $id
     * @return \Illuminate\Http\Response
     * @throws \Illuminate\Auth\Access\AuthorizationException
     */
    public function update(Task $task)
    {
        // check if the authenticated user can complete the task
        $this->authorize('complete', $task);

        // mark the task as complete and save it
        $task->is_complete = true;
        $task->save();

        // flash a success message to the session
        session()->flash('status', 'Task Completed!');

//        request()->user()->notify(new TaskCompleted($task));

        Notification::send($task, new TaskCompleted($task) );

        // redirect to tasks index
        return redirect('/tasks');
    }

알림을 생성하는 방법으로는 앞서의 예에서처럼 notify 메소드를 사용할 수도 있고 위의 예에서처럼 Notification 파사드의 send() 메소드를 사용할 수 도 있다. 이 메소드의 첫번째 인자는 Notifiable 객체이다. 여기서는 로그인한 사용자의 객체가 아닌 Task 모델객체를 지정하였다. 따라서 이 Task 모델 객체는 Notifiable 트레이트를 사용할 수 있어야 한다. 

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;

class Task extends Model
{
    use Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = ['title', 'is_complete'];

    /**
     * The attributes that should be cast to native types.
     *
     * @var array
     */
    protected $casts = ['is_complete' => 'boolean'];

    /**
     * The relationship to the owning user.
     *
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function user()
    {
        return $this->belongsTo(User::class);
    }


    /**
     * Get the notification routing information for the given driver.
     *
     * @param  string  $driver
     * @param  \Illuminate\Notifications\Notification|null  $notification
     * @return mixed
     */
    public function routeNotificationFor($driver, $notification = null)
    {
        if (method_exists($this, $method = 'routeNotificationFor'.Str::studly($driver))) {
            return $this->{$method}($notification);
        }

        switch ($driver) {
            case 'database':
                return $this->notifications();
            case 'mail':
//                return $this->email;
                return env('MAIL_FROM_ADDRESS');
            case 'nexmo':
                return $this->phone_number;
        }
    }

    /**
     * Route notifications for the mail channel.
     *
     * @param  \Illuminate\Notifications\Notification  $notification
     * @return string
     */
    public function routeNotificationForMail($notification)
    {
        Log::debug('수신처 이메일 : ' . $this->user->email);
        return $this->user->email;
    }

    /**
     * Route notifications for the Slack channel.
     *
     * @param  \Illuminate\Notifications\Notification  $notification
     * @return string
     */
    public function routeNotificationForSlack($notification)
    {
        Log::info("slack : " . env('SLACK_WEBHOOK_URL'));
        return env('SLACK_WEBHOOK_URL');
    }
}
?>

Task 모델 객체에 Notifiable 트레이트를 선언해준다. 그리고 슬랙과 메일 채널을 사용할 것이므로 관련된 메소드를 오버라이드한다. 


먼저 메일과 관련해서는 메일 수신자의 이메일 주소를 커스터마이징하기 위해 routeNotificationForMail 메소드를 오버라이드하였다. 여기서는 태스크를 등록한 사용자의 이메일주소로 메일이 발송되도록 하였다.


다음으로 메일과 함께 슬랙 채널을 사용할 것인데 이를 위해 routeNotificationForSlack() 메소드를 재정의하였고 여기서 환경설정 파일에 정의된 슬랙의 웹훅 주소(WebHook Url)를 가져와 해당 슬랙채널로 푸시할 수 있도록 하였다. (슬랙 웹훅은 각자 가진 슬랙의 계정에서 웹훅 URL을 미리 생성해두고 사용하면 된다)


이제 태스크를 완료처리할 때 사용할 알림을 생성한다. 


php artisan make:notification TaskCompleted


생성된 알림 클래스에서는 via 메소드를 통해 이메일과 슬랙 채널 2개를 사용하겠다고 정의하고 각각 이메일과 슬랙을 위한 메소드를 정의한다.

<?php

namespace App\Notifications;

use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\SlackMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;

class TaskCompleted extends Notification
{
    use Queueable;

    public $task;

    /**
     * Create a new notification instance.
     *
     * @return void
     */
    public function __construct($task)
    {
        $this->task = $task;
    }

    /**
     * Get the notification's delivery channels.
     *
     * @param  mixed  $notifiable
     * @return array
     */
    public function via($notifiable)
    {
        return ['mail','slack'];
    }

    /**
     * Get the mail representation of the notification.
     *
     * @param  mixed  $notifiable
     * @return \Illuminate\Notifications\Messages\MailMessage
     */
    public function toMail($notifiable)
    {
        $subject = sprintf('%s : %s님의 태스크가 완료되었습니다.', config('app.name'), $this->task->user->name);
        $greeting = sprintf('안녕하세요 %s님!', $this->task->user->name);

        return (new MailMessage)
            ->subject($subject)
            ->greeting($greeting)
            ->line(sprintf('태스크 [%s]가 %s에 완료처리되었습니다.', $this->task->title, \Carbon\Carbon::now()->format('Y-m-d H:i:s')))
            ->action('내 태스크 보기 ', url('/tasks'))
            ->line('사용해주셔서 감사합니다!');
    }

    /**
     * Get the array representation of the notification.
     *
     * @param  mixed  $notifiable
     * @return array
     */
    public function toArray($notifiable)
    {
        return [
            //
        ];
    }

    /**
     * Get the Slack representation of the notification.
     *
     * @param  mixed  $notifiable
     * @return SlackMessage
     */
    public function toSlack($notifiable)
    {
        $message = sprintf('%s님의 태스크 [%s]가 %s에 완료처리되었습니다.', $this->task->user->name, $this->task->title, \Carbon\Carbon::now()->format('Y-m-d H:i:s'));
        return (new SlackMessage)
            ->from('매니저', ':ghost:')
            ->to('#scouter')
            ->content($message);
    }
}
?>

특별할 것이 없는 코드다. 태스크가 완료처리되면 슬랙 웹훅을 통해 지정된 채널로 알림이 수신된다. 



정리하자면 알림을 생성하고 생성된 알림 클래스에서 어떤 채널을 이용해 알림을 사용할 것인지를 정의하고 관련된 메소드를 통해 발송되는 내용을 정의한다. 그리고 실제 알림을 해야 하는 시점에서 Notifiable 트레이트를 사용할 수 있는 모델 객체의 notify() 메소드나 Notification 파사드를 사용해 알림을 발생시키면 된다. 물론 알림채널은 여러 개를 함께 사용할 수 있다. 

Posted by 라스모르
,