회원 가입을 하면 응당 축하와 환영의 인사를 새 사용자에게 보내야 하는 바, 이메일을 보내기로 했다. 이메일 템플릿 편집과 발송은 MailChimp와 Mandrill로 뚝딱하니 금방 붙더라. Mandrill은 Djrill이라고 Django 라이브러리를 공식지원해주니 거침 없다. 자, 이제 회원이 들어오면 이메일을 발송하면 끝이다.
이런 경우에 회원 가입 함수 끝자락에 이메일 발송 코드를 넣기도 하는데 경험상 차라리 이벤트를 발행하고 핸들러는 다른 곳에 따로 두는 편이 여러 모로 좋다. 굳이 이유를 말하자면
- 함수 하나에 너무 많은 기능이 들어가면 유지보수하기 힘들다
- 감사 기록, 이메일 발송 등 서비스가 커졌을 때 비동기 처리하면 좋은 코드는 일찌감치 떼놓아야 나중에 리팩터링한다고 고생하지 않는다.
그래서 Spring Framework에 있는 Application Event에 대응하는 녀석을 찾았더니 Django Signals라는 게 있네.
Django 1.8.2로 개발하는데 대부분은 공식 문서에 나온 예제 그대로 하면 잘 된다.
우선 이벤트, 아니지 시그널을 정의한다. 회원이 가입했을 때 new_user
라는 시그널이 울릴 예정이다.
# `users/signals/__init__.py`
new_user = django.dispatch.Signal(providing_args=['user'])
시그널이 울렸으면 누군가 이 신호를 받아서 이메일을 보내줘야 한다. 핸들러도 적당히 잘 짠다.
# `users/signals/handlers.py`
@receiver(new_user)
def on_email_Registered(sender, user, **kwargs):
email = UserEmail()
email.welcome(user.username, user.email)
여기까지 하면 끝!… 인 줄 알았지. Spring Framework에 길들여졌나? 자꾸 마법처럼 작동할 줄 착각한다. 하지만 아무리 기능 테스트를 돌려도 이메일은 올 생각을 안 한다. “왜 이러나?” 잠시 고민하다 원인을 알았다. Django 공식 문서를 자세히 읽으면 이런 대목이 있다.
In practice, signal handlers are usually defined in a signals submodule of the application they relate to. Signal receivers are connected in the ready() method of your application configuration class. If you’re using the receiver() decorator, simply import the signals submodule inside ready().
말이 좀 어려운데 그냥 쉽게 말해 어디선가 초기화를 해줘야 작동한다는 말이다. 어떻게?
Django 1.7, Signals & AppConfig에 잘 정리되어 있는데 나는 내 나름대로 글을 소화한 후에 아래와 같이 짰다.
# `users/__init.py`
default_app_config = 'users.apps.UsersConfig'
# `users/apps.py`
from django.apps import AppConfig
class UsersConfig(AppConfig):
name = 'users'
verbose_name = "Users"
def ready(self):
from .signals import handlers
이렇게 하고 나니 아주 잘 작동한다. 당장은… 미래를 생각하면 더 손볼 곳이 몇 개나 떠오르지만 일단 여기까지!
참고 문헌
- Django Signals
- Django 1.7, Signals & AppConfig
- Where should signal handlers live in a django project?