# Keep (이번 주에 잘한 일)
- 1. 전날 구현내용 / 오늘 구현내용을 프론트와 매일 공유함
- 2. 구현중에 필요없다 생각드는 부분은 프론트와 협의하여 바로바로 제거 및 추가함
- 3. 멘토링 지시사항을 반영하여 최적화를 실시함
# Problem (고쳐야 할 점)
- 1. 이번주는 철저하게 잘했다고 생각함
# Try (Keep과 Problem을 기반으로 시도해볼 것)
- 1. 프론트와 대화를 좀 늘리니 충돌이 거의 없었음
구현 현황
- 기획문서 작성 및 프로젝트 세팅
- 내가 한 일
- git organization 구성 / git webhook 연결 / ci 설정 / 도커세팅
- README.md 작성 / EMS 구현 / swagger세팅 / ERD작성
- 경과시간
- 각자 맡은 파트 구현
- 나의 파트
- 커뮤니티 리뷰CRUD
- 댓글CRUD
- "좋아요"기능
- ai 리뷰요약 시스템(비동기/욕설1차필터)
- 경과시간
- 2026/01/12 ~ 2026/01/22 구현 끝
결과적으로 2주정도 시간이 남았기 때문에 개인프로젝트를 진행함
user
AbstractBaseUser
- "가장 기초적인 유저 모델의 뼈대"
- Django가 기본적으로 제공하는 유저 모델(User)이 있지만
- 이를 그대로 쓰기보다 현재 서비스에 맞게 커스터마이징하고 싶을 때 사용함
특징
사용 이유
- Django 기본 유저는 username(아이디)이 필수임
- 하지만 요즘 서비스는 email로 로그인하는 경우가 많음
- 이때 AbstractBaseUser를 상속받아 유저 모델을 만들면
- 아이디 필드를 없애고 이메일을 ID로 쓰도록 완전히 뜯어고칠 수 있음
주의점
- 권한 관리(Permissions) 기능이 없으므로
- 보통 PermissionsMixin이라는 클래스와 함께 상속받아 사용함
TimeStampedModel
from django.db import models
class TimeStampedModel(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
"""
abstract=True로 설정하면 이 모델은 DB에 테이블을 만들지 않고,
이를 상속받는 자식 모델들에게 필드만 물려주는 '추상 클래스'가 됨
"""
- "생성 시간과 수정 시간을 자동으로 기록하는 틀"
- 이건 Django 내장 기능이 아니라
- 개발자들이 관례적으로 많이 만드는(혹은 라이브러리로 쓰는) 추상 모델
기능
- 데이터가 언제 생성되었는지(created_at)
- 언제 마지막으로 수정되었는지(updated_at)를 자동으로 기록
사용 이유
- 회원가입 날짜, 글 쓴 날짜, 프로필 수정 날짜 등은 거의 모든 데이터에 필요
- 모든 모델마다 이 두 필드를 일일이 적는 것은 비효율적(코드 중복)
- 이 모델을 만들어두고 다른 모델들이 상속받게 하면, 자동으로 시간 기록 기능이 생김
UserManager (BaseUserManager)
class UserManager(BaseUserManager):
def create_user(self, email, password=None, **extra_fields):
if not email:
raise ValueError('이메일은 필수입니다.')
user = self.model(email=self.normalize_email(email), **extra_fields)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, password):
user = self.create_user(email, password)
user.is_staff = True
user.is_superuser = True
user.save(using=self._db)
return user
- "유저 생성(create_user)과 관리자 생성(create_superuser)을 담당합니다."
- AbstractBaseUser로 유저 모델을 새로 정의했다면,
- Django는 "그래서 유저는 어떻게 생성하는데?"라고 묻게 됨
역할
- create_user()
- 일반 유저를 만드는 로직 (비밀번호 암호화 저장 등)
- create_superuser()
필요한 이유
- 우리가 유저 모델에서 username을 없애고 email을 쓰기로 했다면
- 유저를 생성할 때도 username 대신 email을 받아야함, 그 로직을 여기서 코딩해줘야 함
커스텀 유저 모델 정의
class User(AbstractBaseUser, PermissionsMixin, TimeStampedModel):
email = models.EmailField(unique=True)
nickname = models.CharField(max_length=30)
is_staff = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
objects = UserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['nickname']
def __str__(self):
return self.email
User
managers.py
from django.contrib.auth.base_user import BaseUserManager
class UserManager(BaseUserManager):
def create_user(self, email, nickname, password=None, **extra_fields):
if not email:
raise ValueError("이메일은 필수 입력값입니다.")
email = self.normalize_email(email)
user = self.model(email=email, nickname=nickname, **extra_fields)
if password:
user.set_password(password)
else:
user.set_unusable_password()
user.save(using=self._db)
return user
def create_superuser(self, email, nickname, password, **extra_fields):
extra_fields.setdefault("is_staff", True)
extra_fields.setdefault("is_superuser", True)
extra_fields.setdefault("is_active", True)
if extra_fields.get("is_staff") is not True:
raise ValueError("Superuser must have is_staff=True.")
if extra_fields.get("is_superuser") is not True:
raise ValueError("Superuser must have is_superuser=True.")
return self.create_user(email, nickname, password, **extra_fields)
- 데이터의 생성(Create) 로직을 담당하는 특수한 매니저
- 이 코드의 존재이유는 "비밀번호 암호화"와 "필수 필드 전처리" 때문
- Django 기본 User 모델을 쓰지 않고 커스텀 유저 모델을 만들 때는
- "유저를 DB에 저장할 때 비밀번호를 그냥 텍스트로 넣으면 안 되고, 반드시 해시(암호화) 필요"
- 라는 규칙을 이 매니저 파일(create_user 메서드)에 정의해야 함
커스텀 유저 매니저의 표준 패턴
normalize_email
- 이메일 주소의 대소문자 문제로 인한 중복 가입을 방지하는 표준 방식
set_password
- 보안의 핵심, 절대 직접 user.password = password라고 쓰면 안 됨
set_unusable_password
- 요즘 트렌드인 소셜 로그인(구글, 카카오 등)까지 고려하여 유연하게 작성되었습니다.
using=self._db
- 나중에 데이터베이스가 여러 개로 늘어날 경우(Replication 등)를 대비한 안전한 코드
참조
import
- Django에서는 모델 간에 관계(ForeignKey, ManyToManyField)를 맺을 때
- 직접 클래스를 import 하지 않고 '앱이름.모델이름' 문자열로 적는 것을 강력히 권장
from django.db import models
from apps.post.models.post import Post
class PostTag(models.Model):
...
post = models.ForeignKey(Post, on_delete=models.CASCADE)
——————————————————————————————————————[비교]—————————————————————————————————————————
from django.db import models
class PostTag(models.Model):
...
post = models.ForeignKey('post.Post', on_delete=models.CASCADE)
login
view