먹수의 개발일지

Django Signals 본문

back-end/Django

Django Signals

icandle 2024. 11. 10. 19:58

Intro

유저의 승인 상태 값이 변경(ex.승인대기중 → 승인)되었을때 자동으로 유저에게 승인 완료 메일이 날아가도록 구현하기 위해 사용했다. 임시적으로 django Admin에서 유저의 값을 변경했을 때 해당 기능이 실행되도록 구현해야 했기 때문에, view에서 처리할 수는 없었다.

 

signal이란

Django 공식문서 Django includes a “signal dispatcher” which helps decoupled applications get notified when actions occur elsewhere in the framework.

Signal은 분리된 어플리케이션의 작업이 발생했음을 알려주고 처리할 수 있는 기능이다. Django signal은 DB 에 값이 저장 직전(pre_save), 저장 후(post_save)에 실행될 작업을 지정 해 줄 수 있다.구현해야할 기능은 저장되기 전에 User의 기존 승인상태값과 비교해야 하기 때문에 pre_save를 사용했다.

 

한계점

  • 디버깅 등 코드 유지관리가 어려울 수 있다.
    • 정확한 docs가 되어 있지 않으면, 파일이 분산됨에 따라 signal 함수를 찾기 힘들다. 아래 코드와 같이 user model에 연동되는 signal을 구현했지만, user model에 정의하지 않는 등 찾기 어렵다.

예시 코드

user/signals.py

from django.db.models.signals import pre_save
from django.dispatch import receiver
from utils.send_mail import MailAPI 
from user.models import *

def send_approval_email(sender, instance, **kwargs):
    """회원가입 승인&반려 메일
        1) 승인 메일 전송
        - send mail when User approval HO(대기) or DENY(반려) -> AP(승인)
        2) 반려 메일 전송
        - send mail when User approval HO(대기)  -> DENY(반려)
    """
    previous_value = User.objects.get(id = instance.id).approval
    current_value = instance.approval

    #wait -> deny (반려메일 전송) 
    if previous_value == UserApproveCode.WAIT and current_value == UserApproveCode.DENY:
        email = instance.email
        mail_api = MailAPI()
        content = mail_api.content_approve(email)
        mail_api.send("회원 승인이 반려되었습니다.", content, email)  
        
    #wait or deny -> approve(승인메일 전송)
    if (previous_value == UserApproveCode.WAIT or previous_value == UserApproveCode.DENY) and current_value == UserApproveCode.APPROVE:
        email = instance.email
        mail_api = MailAPI()
        content = mail_api.content_approve(email)
        mail_api.send("승인이 완료되었습니다.", content, email)
        
        
@receiver(pre_save, sender=User)
def user_pre_save(sender, instance, **kwargs):
    # Check if the instance already exists (updating) & only expert user
    if instance.pk and instance.group == UserRoleCode.EXPERT:
        send_approval_email(sender, instance, **kwargs)

 

앱 내부에 app.py에서 ready 메소드를 아래와 같이 오버라이드하여 [앱이름].signals를 import 해준다.

어떤 앱의 AppConfig 가 준비가 되면 실행 되는 메소드가 있는데, 그것이 ready 메소드이다. ready 안에 import 를 넣으면 ready 메소드가 실행이 될 때 import 가 된다.

 

user/app.py

from django.apps import AppConfig

class UserConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'user'
    
    def ready(self) -> None:
        import user.signals
        return super().ready()
  • user_pre_save라는 함수를 생성하여 instance.pk가 존재할때만 (기존에 저장된 유저가 update 될때만)실행되도록 로직을 추가해주었다.
  • send_approval_email은 특정 경우에 승인 메일이 전송되는 함수이다.
  • from utils.send_mail import Mail → 별도로 구현한 Mail 모듈을 활용했다.
Comments