라라벨 커스텀 이메일 인증
갑자기 라라벨?
PHP의 진영에서 모던 프레임워크로 열심히 발빠르게 날리고 있는 라라벨을 다루게 된 계기는 현 회사에서 인턴과제로 라라벨 프레임워크를 활용한 개발을 진행중이기 때문이다.
라라벨을 사용하는 사람이 없기도하며, 워낙 커뮤니티도 적고 한글화된 문서를 확인해보기는 어렵기 때문에 여러가지 상황에 맞는 개발을 진행기에 무리가 많지만 한글화 된 자료나 튜토리얼들은 도움을 크게 줄 수 있을 거라고 생각한다.
내가 스스로 고생하면서 얻은 지식들과 함께 라라벨에서 제공했던 프레임워크에서 제공되는 구조를 잘 활용해서 개발할 수 있게 하는게 제일 베스트라고 생각했고, 결국 라라벨을 쓰는 이점을 살리는 개발을 쭉 진행해보고 싶었다.
회원 이메일 인증
라라벨에서 기본적으로 이메일 인증을 받기위해서는 Vue 스케폴딩을 해줘야한다. (8.0 버전이상 되면서 php artisan make:auth
기능을 활용할 수 가 없고, laravel-ui를 컴포저를 통해서 설치해야지 회원가입 기능을 더 활용 할 수 있다.)
기본 이메일 인증을 받는 경우 User모델에 존재하는 MustVerifiedEmail
가 이미 구현되어있으나, 실제로는 코드로 사용되지 않고 있어 모델 클래스에 implements MustVerifyEmail
를 선언함으로써 회원가입시 인증메일이 전송되게 된다.
이 기본 이메일의 경우 라라벨에서 .env파일에 설정되있는 MAIL_MAILER에서 smtp를 직접 연결함으로써, 메일 서버를 별도 연결하면 자기가 그 메일을 직접 메일 서버에 전송을 하여, 보내는 방식으로 구현이 되어있다.
문제는 현 프로젝트를 SMTP 서버가 아닌 API 방식으로 메일 전달을 해야하는 상황이 생겨버린 것이다.
커스텀 이메일 인증
SMTP서버를 타지 못하면 사실상 라라벨에서 제공하는 이메일 인증 기능 자체를 완전히 사용 불가능 하다는 뜻이 된다. 내부 로직을 다 뜯어 고치지 않는 이상 라라벨에서 제공되는 이메일 인증 서비스를 구현하기라는 것은 쉽지가 않았다.
나는 최대한 라라벨이 제공하는 기능을 사용하면서 코드 구현을 해보고 싶었다. 결국 오버라이딩하면 되는 문제 아닌가라고 생각했었고, 내부 로직에 대해서 구체적으로 파악 해보려했었다.
이러한 이메일을 발송하는 방식은 event 패턴을 통한 이메일 발송이 진행된다. 라라벨에서는 이벤트 객체와 이벤트 리스너를 통해서 직접적으로 이메일을 전송하는 메소드를 활성화 시키게한다.
trait RegistersUsers
public function register(Request $request)
{
$this->validator($request->all())->validate();
event(new Registered($user = $this->create($request->all()))); //이렇게 회원가입 이벤트 객체를 만들어서 유저를 넣어 이벤트를 발생시키면...
....
class SendEmailVerificationNotification
public function handle(Registered $event)
{
if ($event->user instanceof MustVerifyEmail && ! $event->user->hasVerifiedEmail()) { //이메일 인증이 됬는지 확인해보고,
$event->user->sendEmailVerificationNotification(); // 안됬으면 발송한다.
}
}
이런 구조로 되어있기 때문에 커스텀 이메일 인증시 우리는 sendEmailVerificationNotification()
를 잘 오버라이딩 하기만 해도 커스텀이 가능해지는 것이다.
sendEmailVerificationNotification()
는 다음과 같은 코드로 구성이 되어있다.
public function sendEmailVerificationNotification()
{
$this->notify(new VerifyEmail);
}
notify를 통해 라라벨 notification 기능을 통해 이메일이 전송되는 방식이다. verfiyEmail
을 확인해보자면..
핵심은 두가지로 볼 수 있다.
via
를 통해서 어떤 채널로 발송 할 것인지?toMail
을 통해서 어떤 메세지를 보낼것인지?
로 요약이 가능하다. 나머지 코드들은 인증과 관련된 인증 url, 메일에 담길 화면을 구성하는 메소드라서 생략했다.
public function via($notifiable)
{
return ['mail'];
}
public function toMail($notifiable)
{
$verificationUrl = $this->verificationUrl($notifiable);
if (static::$toMailCallback) {
return call_user_func(static::$toMailCallback, $notifiable, $verificationUrl);
}
return $this->buildMailMessage($verificationUrl);
}
결국 커스텀을 위한 개발은 이 핵심코드를 어떤식으로 수정하느냐에 따라서 커스텀을 할 수 있을 것이다.
커스텀 채널을 만들자
via
의 ['mail']
도 역시 라라벨에서 제공하는 메일 채널 즉, SMTP를 통한 메일 전송이므로, 결국 따로 채널을 만들어야하는데,
다음과 같은 채널에서 send 메소드를 통해서 메일 전송이 되는것이다. 그럼 다음과 같은 메일을 전송한다.
class OOOChannel
{
public function send($notifiable, Notification $notification)
{
$message = $notification->toVoice($notifiable);
// Send notification to the $notifiable instance...
}
}
즉, 이 send
메소드만 재구성하면 된다. 우리는 API를 통한 전송을 해야하므로, API 전송에 대한 코드로 재구성할 것이다.
Laravel에서는 기본적으로 API를 통한 전달을 제공한다. GuzzleHttp를 기본으로 포함하고 있어서 HTTP 파사드를 통해 HTTP 전달을 제공하게 된다.
public function send($notifiable, Notification $notification)
{
$message = $notification->to000($notifiable);
$response = Http::asForm()->post(url, $message);
//리스폰스에 대한 예외처리는 아래로...
}
이렇게 구성하면 채널을 통한 API 전송은 구현한 것이다. 그러면 이 채널을 장착을 하기만 하면된다.
마무리 작업들
notification
을 재설계해야하므로, 새로 만든 notification
을 via
를 교체해줘야한다.
public function via($notifiable)
{
return [OOOChannel::class];
}
과 같이 교체해주면 메일이 정상적으로 API콜을 통해서 발송 될것이다.
그리고 이 만들었던 notification
을 User모델에 다시 삽입하는 과정이 필요한데 이 메소드를 오버라이딩 한 후에
public function sendEmailVerificationNotification()
{
$this->notify(new customEmail);//customEmail이라는 새로운 이름으로 재구성한 Notification을 추가한다.
}
참고로 유저모델에 삽입하는 이유는 다음과 같다.
회원가입시 이벤트로 이메일이 발송되므로 유저 모델에 삽입하는 것이다. 만약 이메일 인증을 다른 곳에서(유저가 아니라 로깅 모델, 기타 등등) 필요하면 다른 이벤트를 발생시켜 그 모델에 notify를 추가해주면된다.
다음과 같은 기능을 추가하면 라라벨의 제공하는 이메일 전송 로직은 그대로 살아 있으면서, 내부적 로직을 변경을 통해서 커스텀하게 이메일 전송을 가능하게 만들 수 있을 것이다.
물론, 라라벨에서 제공하는 이메일 형태 즉, MailMessage
객체를 통해서 전달은 불가능할 것인데, 왜냐하면 API콜의 경우 post로 전달해야하므로 그 형태를 body안에 넣어서 처리해야하기때문에 결국 buildMailMessage
과 같은 기능들을 API콜에 맞추어서 변형은 해야하지만, 그래도 1차적 목표인 API 콜로 이메일 인증을 보낼 수는 있다.
결론
라라벨이 가지고 있는 프레임워크를 최대한 잘 활용하며 개발하는것이 내가 이번 개발을 하면서 가장 먼저 세웠었던 목표였다. 백엔드에서 결국 우리는 언어만 다를뿐이지, 프레임워크가 가지고 있는 로직들 그리고 이 프레임워크가 작동하는 방식을 재대로 활용하면 모든걸 직접 구현해야하는 번거로움을 줄일 수 있다고 생각한다. 그리고 이러한 번거로움을 줄이는 것은 공수에서도 시간 단축에도 큰 도움을 줄 수 있다고 생각한다.
P. S.
Spring을 통해서 개발을 몰두하다보면, 타 프레임워크의 완성도, 혹은 이건 왜 이렇지와 같은 개발 방향성에 대해서 종종 무시하게 된다. Spring에서는 당연히 이게 맞다싶은 것들이 타 프레임워크에서는 다른 방향으로 개발하게끔 유도하는데, 나는 여러 프레임워크들을 두루두루 다뤄봤던 경험이 개발 방식에 큰 도움을 받은 사례라고 생각한다. 프레임워크가 추구했었던 골격과 논리를 잘 들여다보면, 모던프레임워크에서 원하는 이벤트, 리스너 방식(옵저버 패턴)과 같은 디자인패턴을 통해 훨씬 의존성을 줄일 수 있는 방향에 대해서 다시 생각해볼 수 있는 계기가 된것 같아서 매우 좋았다.
현 프레임워크에서 주요하게 사용되는 방식 혹은 패턴을 Spring으로 옮겨서 개발하는 방법도 현재 글쓰기 목록에 올려놓았다. 개인적으로 이후에도 포스팅하겠으나, 옵저버 패턴은 확실히 Spring에서는 한번도 생각해보지도 못했던 개발 방식이었고 라라벨 개발시에 큰 도움이 되어서 적용해볼 예정이다.
'Backend > PHP&Laravel' 카테고리의 다른 글
라라벨의 비동기를 활용한 실시간 검증 (0) | 2021.08.19 |
---|