-
Notifications
You must be signed in to change notification settings - Fork 0
[GH-182] 인증 메일 전송 api 리펙터링 #184
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,7 +2,7 @@ | |
|
|
||
| urlpatterns = [ | ||
| path( | ||
| "/user", | ||
| "user/", | ||
| include("api.users.urls"), | ||
| name="user", | ||
| ), | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,11 +1,11 @@ | ||
| from django.urls import path | ||
|
|
||
| from api.users.views import EmailRequestView | ||
| from api.users.views import EmailVerificationAPIView | ||
|
|
||
| urlpatterns = [ | ||
| path( | ||
| "/email/send", | ||
| EmailRequestView.as_view(), | ||
| name="email-send", | ||
| "email-verification", | ||
| EmailVerificationAPIView.as_view(), | ||
| name="email-verification", | ||
| ), | ||
| ] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| # Generated by Django 6.0 on 2025-12-21 06:06 | ||
|
|
||
| from django.db import migrations, models | ||
|
|
||
|
|
||
| class Migration(migrations.Migration): | ||
|
|
||
| dependencies = [ | ||
| ("users", "0001_initial"), | ||
| ] | ||
|
|
||
| operations = [ | ||
| migrations.AlterField( | ||
| model_name="user", | ||
| name="is_active", | ||
| field=models.BooleanField(default=False, verbose_name="계정 활성화 여부"), | ||
| ), | ||
| ] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,16 +1,20 @@ | ||
| from django.db import transaction | ||
|
|
||
| from apps.users.models import User | ||
| from apps.users.tasks import send_email_verify_code | ||
| from apps.users.tasks import send_verification_code | ||
|
|
||
|
|
||
| class UserVerificationService: | ||
| class EmailVerificationService: | ||
|
|
||
| @transaction.atomic | ||
| def send_verification_code(self, email: str) -> None: | ||
| def send_verification_email( | ||
| self, | ||
| email: str, | ||
| ) -> None: | ||
| """인증 메일 발송""" | ||
|
|
||
| User.objects.get_or_create( | ||
| email=email, | ||
| defaults={"is_active": False}, | ||
| ) | ||
|
|
||
| send_email_verify_code.delay(email=email) | ||
| send_verification_code.delay(email=email) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,33 +9,35 @@ | |
| from core.cache.prefix import CacheKeyPrefix | ||
|
|
||
|
|
||
| @shared_task() | ||
| def send_email_verify_code(user_email: str) -> None: | ||
| @shared_task | ||
| def send_verification_code(email: str) -> None: | ||
| """인증 메일 전송""" | ||
|
|
||
| code = random.randint(100000, 999999) | ||
|
|
||
| Cache.set( | ||
| prefix=CacheKeyPrefix.email_verification_code, | ||
| key=user_email, | ||
| key=email, | ||
| value=code, | ||
| timeout=60 * 10, | ||
| ) | ||
|
|
||
| html_content = render_to_string( | ||
| template_name="verify_code.html", | ||
| context={ | ||
| "recipient_name": user_email, | ||
| "recipient_name": email, | ||
| "verification_code": code, | ||
| }, | ||
| ) | ||
|
|
||
| email = EmailMultiAlternatives( | ||
| email_message = EmailMultiAlternatives( | ||
| subject=f"Jusicool 메일 인증 코드", | ||
| body=html_content, | ||
| to=[user_email], | ||
| to=[email], | ||
|
||
| from_email=settings.EMAIL_HOST_USER, | ||
| ) | ||
| email.attach_alternative( | ||
| email_message.attach_alternative( | ||
| html_content, | ||
| "text/html", | ||
| ) | ||
| email.send() | ||
| email_message.send() | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,11 +3,11 @@ | |
| import pytest | ||
|
|
||
| from apps.users.factories import UserFactory | ||
| from apps.users.services import UserVerificationService | ||
| from apps.users.services import EmailVerificationService | ||
|
|
||
|
|
||
| @pytest.mark.django_db | ||
| class TestUserVerificationService: | ||
| class TestEmailVerificationService: | ||
|
|
||
| @pytest.fixture(autouse=True) | ||
| def mock_exist_user(self) -> UserFactory: | ||
|
|
@@ -16,15 +16,15 @@ def mock_exist_user(self) -> UserFactory: | |
| is_active=True, | ||
| ) | ||
|
|
||
| @patch("apps.users.services.send_email_verify_code.delay") | ||
| def test_send_verification_code( | ||
| @patch("apps.users.services.send_verification_code.delay") | ||
| def test_send_verification_email( | ||
| self, | ||
| mock_task, | ||
| ): | ||
| """메일 전송 task를 호출한다.""" | ||
|
|
||
| # Action | ||
| UserVerificationService().send_verification_code(email="[email protected]") | ||
| EmailVerificationService().send_verification_email(email="[email protected]") | ||
|
|
||
| # Assert | ||
| mock_task.assert_called_once_with(email="[email protected]") | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
서비스 인스턴스를 클래스 속성으로 정의하면 모든 요청에서 동일한 인스턴스를 공유하게 됩니다. 상태를 가지지 않는 서비스라면 문제없지만, Django의 일반적인 패턴은 메서드 내에서 인스턴스를 생성하거나 의존성 주입을 사용하는 것입니다. 매 요청마다 새로운 인스턴스가 필요한 경우
post메서드 내에서 인스턴스를 생성하는 것을 고려하세요.