이제 아이디 대신 닉네임을 사용하고, 내 얼굴(프로필 사진)을 등록해서 커뮤니티에 한걸음 내딛어 봅시다.
[STEP 6] 회원 프로필 (닉네임 & 아바타)
이번 단계에서는 회원들에게 닉네임을 부여하고, 내 얼굴(프로필 사진)을 등록할 수 있게 합니다.
⚠️ 주의: 이미 가입된 회원이 있는 상태에서 unique=True(중복 금지) 필드를 바로 추가하면 에러가 발생합니다.
따라서 안전하게 3단계에 걸쳐서 적용하는 실무적인 방법을 사용하겠습니다.
1. 1차 모델 수정 (제약 없이 생성)
먼저 unique=True 옵션 없이 닉네임 필드를 생성합니다.
이렇게 하면 기존 회원들의 닉네임이 비어 있어도 에러가 나지 않습니다.
accounts/models.py 파일을 열고 User 클래스를 수정하세요.
from django.db import models
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
# 기존 필드
is_board_manager = models.BooleanField(default=False, verbose_name="게시판 관리자 여부")
# [추가 1] 닉네임 (일단 unique=True 없이 생성)
nickname = models.CharField(max_length=20, null=True, blank=True, verbose_name="닉네임")
# [추가 2] 프로필 사진
# upload_to: 이미지가 저장될 경로 (연/월/일 폴더 자동 생성)
avatar = models.ImageField(upload_to='accounts/avatar/%Y/%m/%d/', blank=True, null=True, verbose_name="프로필 사진")
def __str__(self):
# 닉네임이 있으면 닉네임, 없으면 아이디 리턴
return self.nickname if self.nickname else self.username
터미널에서 1차 마이그레이션을 진행합니다.
[Bash]
python manage.py makemigrations
python manage.py migrate
2. 기존 데이터 채우기 (데이터 보정)
이제 DB에는 nickname 컬럼이 생겼지만, 기존 회원들의 값은 비어있습니다.
이 상태에서 중복 금지(Unique)를 걸면 에러가 납니다. "비어있는 닉네임을 아이디와 똑같이 채워주는 작업"을 진행합니다.
터미널에 python manage.py shell을 입력하여 파이썬 쉘을 열고, 아래 코드를 복사해서 실행하세요.
python manage.py shell
from accounts.models import User
# 닉네임이 없는 유저를 찾아 아이디로 채워줌
for user in User.objects.all():
if not user.nickname:
user.nickname = user.username
user.save()
print(f"Update: {user.username} -> {user.nickname}")
# 작업이 끝나면 exit()로 나옵니다.
exit()
3. 2차 모델 수정 (제약 조건 확정)
이제 모든 회원이 닉네임을 가졌으므로, 중복 금지(Unique) 제약을 걸어도 안전합니다.
또한, 앞으로 가입할 회원들을 위해 "닉네임을 비워두면 자동으로 username으로 저장하는 로직"을 추가합니다.
accounts/models.py를 다시 수정하세요.
class User(AbstractUser):
# ... 기존 필드들 ...
# [수정] unique=True 추가 (이제 안전함!)
nickname = models.CharField(max_length=20, unique=True, null=True, blank=True, verbose_name="닉네임")
avatar = models.ImageField(upload_to='accounts/avatar/%Y/%m/%d/', blank=True, null=True, verbose_name="프로필 사진")
# [추가] 저장 시 자동 처리 로직 (오버라이딩)
def save(self, *args, **kwargs):
# 닉네임 입력 없이 저장하려 하면, 아이디를 닉네임으로 설정
if not self.nickname:
self.nickname = self.username
super().save(*args, **kwargs)
def __str__(self):
return self.nickname if self.nickname else self.username
터미널에서 최종 마이그레이션을 진행합니다.
[Bash]
python manage.py makemigrations
python manage.py migrate
4. 폼 수정 (accounts/forms.py)
회원 정보 수정 페이지에서 닉네임과 사진을 입력받을 수 있도록 폼을 수정합니다.
accounts/forms.py의 CustomUserChangeForm을 수정하세요.
class CustomUserChangeForm(forms.ModelForm):
class Meta:
model = User
# [수정] 닉네임, 아바타 필드 추가
fields = ['email', 'nickname', 'avatar', 'last_name', 'first_name']
labels = {
'email': '이메일 주소',
'nickname': '닉네임',
'avatar': '프로필 사진',
'last_name': '성',
'first_name': '이름',
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for field in self.fields.values():
field.widget.attrs['class'] = 'form-control'
5. 로직 수정 (accounts/views.py)
중요: 파일 업로드를 처리하려면 View에서 반드시 request.FILES를 받아야 합니다.
accounts/views.py의 profile_edit 함수를 수정하세요.
@login_required
def profile_edit(request):
if request.method == 'POST':
# [수정] request.FILES 추가 (필수)
form = CustomUserChangeForm(request.POST, request.FILES, instance=request.user)
if form.is_valid():
form.save()
return redirect('accounts:profile')
else:
form = CustomUserChangeForm(instance=request.user)
return render(request, 'accounts/profile_edit.html', {'form': form})
6. 화면 수정 (accounts/templates/accounts/profile_edit.html)
HTML 폼 태그에 enctype="multipart/form-data" 속성을 추가해야 파일이 전송됩니다.
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{% for field in form %}
<div class="mb-3">
<label class="form-label">{{ field.label }}</label>
{{ field }}
{% if field.errors %}
<div class="text-danger small">{{ field.errors.0 }}</div>
{% endif %}
</div>
{% endfor %}
<div class="text-end">
<a href="{% url 'accounts:profile' %}" class="btn btn-secondary">취소</a>
<button type="submit" class="btn btn-primary">저장</button>
</div>
</form>
7. 상단바 수정 (templates/base.html)
상단 메뉴바(Navbar)에 아이디 대신 "내 사진 + 닉네임"이 나오도록 수정합니다.
templates/base.html에서 로그인 정보 표시 부분(navbar-nav)을 찾아 아래 코드로 교체하세요.
<ul class="navbar-nav">
{% if user.is_authenticated %}
<li class="nav-item me-3">
<a href="{% url 'accounts:profile' %}" class="nav-link d-flex align-items-center gap-2">
{% if user.avatar %}
<img src="{{ user.avatar.url }}" alt="profile" width="32" height="32" class="rounded-circle border">
{% else %}
<i class="bi bi-person-circle" style="font-size: 1.5rem;"></i>
{% endif %}
<strong>{{ user.nickname|default:user.username }}</strong>
</a>
</li>
<li class="nav-item">
<form action="{% url 'accounts:logout' %}" method="post">
{% csrf_token %}
<button type="submit" class="nav-link btn btn-link text-white" style="text-decoration: none;">로그아웃</button>
</form>
</li>
{% else %}
<li class="nav-item">
<a class="nav-link" href="{% url 'accounts:login' %}">로그인</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'accounts:signup' %}">회원가입</a>
</li>
{% endif %}
</ul>
8. 테스트
- 정보 수정: 내 정보 -> 정보 수정 메뉴로 이동합니다.
- 등록: 닉네임을 변경하고, 프로필 사진을 등록한 뒤 **[저장]**을 누릅니다.
- 확인: 상단 메뉴바에 내 사진(원형)과 변경된 닉네임이 잘 뜨는지 확인합니다.
- 자동 채움 테스트 (선택): 관리자 페이지 등에서 새 유저를 만들 때 닉네임을 비워두면 자동으로 아이디가 들어가는지 확인해 봅니다.
'Step by Step > [phase 2] django community upgrade' 카테고리의 다른 글
| 8. 디테일 업그레이드 ( 조회수 쿠키 & Humanize) (0) | 2025.12.07 |
|---|---|
| 7. 사용자 간 1:1 쪽지 기능(DM) 구현 (0) | 2025.12.07 |
| 5. 메인 페이지(대시보드 & 데이터 조회) 구현 (0) | 2025.12.07 |
| 4. 좋아요(추천) 기능 (M:N 관계) 구현 (0) | 2025.12.02 |
| 3. 이미지 및 파일 첨부 기능 구현 (0) | 2025.12.02 |