🔐 Django Custom User + 이메일 인증 시스템 구현 가이드
📌 목표
- AbstractBaseUser 또는 AbstractUser + PermissionsMixin을 이해하고 커스텀 유저 구현하기
- Manager 클래스의 개념을 이해하고 커스텀 유저 매니저 구현하기
- CBV (Class-Based View) 방식으로 회원가입, 로그인, 로그아웃 구현하기
- django.core.signing을 이용한 이메일 인증 흐름 이해 및 구현
- send_mail 기능을 활용한 SMTP 기반 이메일 전송 구현
- 환경변수를 통한 민감 정보 보호 및 설정 적용하기
✅ 요구사항 요약
- CBV 기반으로 회원가입/로그인/이메일 인증 구현
- AbstractBaseUser 기반 CustomUser + Manager 구현
- signing + send_mail 활용한 인증메일 발송 기능 구현
1️⃣ 환경변수 설정 (.secret_config/secret.json)
민감 정보(SECRET_KEY, SMTP 정보)는 외부 파일로 관리하여 보안을 확보합니다.
secret.json 예시:
{
"DJANGO_SECRET_KEY": "your_secret",
"EMAIL": {
"HOST_USER": "your_email@example.com",
"PASSWORD": "your_email_password"
}
}
- secret.json은 .secret_config 폴더에 위치시킵니다.
- settings.py에서 json.load()로 불러와 사용합니다.
2️⃣ models.py: Custom User & Manager
from django.contrib.auth.base_user import AbstractBaseUser, BaseUserManager
from django.contrib.auth.models import PermissionsMixin
class UserManager(BaseUserManager):
def create_user(self, email, password=None):
if not email:
raise ValueError('이메일 필수')
user = self.model(email=self.normalize_email(email))
user.set_password(password)
user.is_active = False
user.save(using=self._db)
return user
def create_superuser(self, email, password):
user = self.create_user(email, password)
user.is_admin = True
user.is_active = True
user.save(using=self._db)
return user
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(unique=True)
nickname = models.CharField(max_length=20, unique=True)
is_active = models.BooleanField(default=False)
is_admin = models.BooleanField(default=False)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
objects = UserManager()
@property
def is_staff(self):
return self.is_admin
3️⃣ forms.py: 회원가입 & 로그인 폼
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm
class SignupForm(UserCreationForm):
class Meta:
model = User
fields = ('email', 'nickname')
widgets = {
'email': forms.EmailInput(attrs={'class': 'form-control'}),
'nickname': forms.TextInput(attrs={'class': 'form-control'})
}
class LoginForm(AuthenticationForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['username'].widget.attrs.update({'class': 'form-control'})
self.fields['password'].widget.attrs.update({'class': 'form-control'})
4️⃣ cb_views.py: CBV 방식 회원가입/로그인/인증
📩 SignupView (이메일 인증 포함)
class SignupView(FormView):
template_name = 'auth/signup.html'
form_class = SignupForm
def form_valid(self, form):
user = form.save(commit=False)
user.nickname = form.cleaned_data['nickname']
user.save()
signer = TimestampSigner()
signed_email = signer.sign(user.email)
dump = signing.dumps(signed_email)
url = f"{self.request.scheme}://{self.request.META['HTTP_HOST']}/verify/?code={dump}"
subject = '[Pystagram] 이메일 인증을 완료해주세요'
message = f'다음 링크를 클릭해주세요: <a href="{url}">이메일 인증</a>'
send_email(subject, message, user.email)
return render(self.request, 'auth/signup_done.html', {'user': user})
🔐 verify_email
def verify_email(request):
code = request.GET.get('code', '')
signer = TimestampSigner()
try:
decoded = signing.loads(code)
email = signer.unsign(decoded, max_age=1800)
except (TypeError, SignatureExpired):
return render(request, 'auth/verify_failed.html')
user = get_object_or_404(User, email=email, is_active=False)
user.is_active = True
user.save()
return render(request, 'auth/verify_success.html')
🔓 LoginView
class LoginView(FormView):
template_name = 'auth/login.html'
form_class = LoginForm
success_url = reverse_lazy('todo_list')
def form_valid(self, form):
user = form.get_user()
login(self.request, user)
next_page = self.request.GET.get('next')
if next_page:
return HttpResponseRedirect(next_page)
return super().form_valid(form)
5️⃣ urls.py 설정
# users/urls.py
urlpatterns = [
path('signup/', SignupView.as_view(), name='signup'),
path('login/', LoginView.as_view(), name='login'),
path('logout/', LogoutView.as_view(), name='logout'),
path('verify/', verify_email, name='verify_email'),
]
# config/urls.py
urlpatterns = [
path('users/', include('users.urls')),
]
6️⃣ templates 구조
📁 templates/
├─ base.html
├─ auth/
├─ signup.html
├─ signup_done.html
├─ login.html
├─ verify_success.html
└─ verify_failed.html
- 모든 템플릿은 base.html을 extend 하도록 설정
- verify_success.html: 이메일 인증 성공 시 메시지 + 리다이렉트 JS
- verify_failed.html: 링크 오류/만료 시 메시지 출력
✅ 이 구조를 통해 실무에서도 적용 가능한 유저 인증 시스템을 구현할 수 있습니다!
'과제' 카테고리의 다른 글
[To do list] Django Custom User + 이메일 인증 시스템 구현 가이드 2/2 (0) | 2025.05.13 |
---|---|
📌 Django Todo 프로젝트: 이미지 업로드 + 썸네일 생성 + Summernote 적용 전체 흐름 정리 (0) | 2025.05.12 |
📝 Django Todo 프로젝트: 이미지 처리 + Summernote + Bootstrap5 통합 실습 정리 (0) | 2025.05.12 |
📚 Django 프로젝트 시작하기 - 가상환경 구축부터 서버 실행까지 (0) | 2025.04.30 |
Flask practice blog 구축 인터페이스(UI) posts.html (3/3) (0) | 2025.04.22 |