장고 기초 #7 Django Admin과 built-in 인증
#1 Django 란에서 한 약속 — Admin이 무료로 따라온다 — 을 회수할 차례입니다. 이번 글은 Django가 자동으로 만들어주는 관리자 페이지와, 같은 흐름에서 따라오는 빌트인 인증 (User, 로그인, 권한)을 한 호흡으로 봅니다. 시리즈 마지막 편입니다.
Admin — 모델 등록 한 줄 #
blog/admin.py에 한 줄.
from django.contrib import admin
from .models import Post
admin.site.register(Post)이게 끝입니다. runserver 띄우고 http://127.0.0.1:8000/admin/으로 가면 — 아직 로그인 페이지입니다. superuser가 필요합니다.
Superuser 만들기 #
uv run python manage.py createsuperuser대화형으로 username / email / password를 입력하면 끝. 이제 /admin/에 그 계정으로 로그인할 수 있습니다.
로그인하면 — Auth (User, Group), Blog (Post)가 좌측 메뉴에. Post를 클릭하면 자동으로 만들어진 CRUD 화면이 보입니다. 목록 조회, 생성, 수정, 삭제, 검색까지 자동으로 갖춰집니다.
코드 한 줄도 더 안 적었습니다. 이게 Django의 가장 강력한 약속입니다.
ModelAdmin — Admin 화면 커스터마이즈 #
기본 화면은 단조롭습니다 (Post object (1), Post object (2) …). ModelAdmin으로 풍부하게 만듭니다.
from django.contrib import admin
from .models import Post, Tag, Comment
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
# 목록 화면
list_display = ("title", "author", "is_published", "created_at")
list_filter = ("is_published", "created_at", "author")
search_fields = ("title", "content")
date_hierarchy = "created_at"
ordering = ("-created_at",)
list_editable = ("is_published",)
list_per_page = 25
# 상세 화면
fields = ("title", "author", "content", "tags", "is_published")
readonly_fields = ("created_at", "updated_at")
filter_horizontal = ("tags",) # ManyToMany 위젯 개선
autocomplete_fields = ("author",) # FK가 많을 때 검색 자동완성
prepopulated_fields = {"slug": ("title",)} # title → slug 자동 생성
@admin.register(Tag)
class TagAdmin(admin.ModelAdmin):
list_display = ("name", "slug")
search_fields = ("name",)
prepopulated_fields = {"slug": ("name",)}
admin.site.register(Comment)자주 쓰는 옵션 정리:
| 옵션 | 효과 |
|---|---|
list_display | 목록에 표시할 컬럼들 |
list_filter | 우측 사이드바의 필터 |
search_fields | 상단 검색창 (대상 필드) |
date_hierarchy | 날짜로 드릴다운하는 상단 네비 |
ordering | 기본 정렬 |
list_editable | 목록에서 직접 수정 가능 |
list_per_page | 페이지당 개수 |
fields / fieldsets | 상세 화면의 필드 배치 |
readonly_fields | 읽기 전용 필드 |
filter_horizontal / filter_vertical | M2M의 두-칸 위젯 |
autocomplete_fields | FK / M2M 자동완성 (대상 모델에 search_fields 필요) |
prepopulated_fields | 다른 필드에서 자동 채움 (slug 흔함) |
@admin.register(Post) 데코레이터가 admin.site.register(Post, PostAdmin)와 같습니다. 더 깔끔합니다.
Inline — 부모 화면에서 자식 같이 편집 #
Post의 상세 화면에서 그 글의 Comment 들을 같이 보여주고 싶다면.
from django.contrib import admin
from .models import Post, Comment
class CommentInline(admin.TabularInline): # or StackedInline
model = Comment
extra = 1 # 빈 행 1개 추가
fields = ("author", "body", "created_at")
readonly_fields = ("created_at",)
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
list_display = ("title", "author")
inlines = [CommentInline]TabularInline— 테이블 형태 (한 줄에 한 댓글)StackedInline— 카드 형태 (필드 세로 나열)
장바구니 / 주문 항목 / 게시글 첨부파일 같이 부모 ↔ 자식 관계가 명확한 경우에 잘 어울립니다.
Admin의 보기보다 강한 점 #
- 검색 + 필터 + 페이징 자동
- 변경 이력 — 누가 언제 무엇을 바꿨는지 기록 (Recent Actions)
- 권한 체계 — 모델 단위의 add/change/delete/view 권한이 자동 생성
- 다국어 — 사용자의 LANGUAGE_CODE에 맞게 UI가 바뀜
- i18n + RTL 지원
작은 사내 도구라면 Admin만으로 운영 화면이 끝납니다. Admin 자체를 운영 도구로 쓰는 회사도 많습니다.
Admin 보안 — 운영의 기본 #
/admin/경로를 그대로 두지 말고 변경 (path("private/", admin.site.urls))- IP 제한 (Nginx 또는 미들웨어)
- 2단계 인증 (
django-otp,django-allauth-2fa) - superuser는 진짜 필요한 사람만
자세한 운영 보안은 고급 #7 배포 보안에서.
빌트인 인증 — django.contrib.auth
#
Admin의 기반이 바로 django.contrib.auth입니다. 이미 INSTALLED_APPS에 들어 있고, migrate 시 User, Group, Permission 테이블이 만들어졌습니다.
User 모델
#
from django.contrib.auth import get_user_model
User = get_user_model() # 컨벤션 — settings.AUTH_USER_MODEL을 따라감
user = User.objects.create_user(
username="curtis",
email="me@example.com",
password="secret123", # 자동으로 해시됨
)
user.set_password("new-password") # 비밀번호 변경
user.save()
user.check_password("new-password") # → Truecreate_user와 set_password가 비밀번호 해싱 (PBKDF2 기본)을 자동으로. 절대 평문으로 저장하지 마세요.
커스텀 User — 처음부터 권장 #
기본 User도 동작하지만, 새 프로젝트는 처음부터 커스텀 User 모델로 시작 하는 게 컨벤션입니다. 나중에 바꾸기가 굉장히 어렵습니다.
from django.contrib.auth.models import AbstractUser
from django.db import models
class User(AbstractUser):
bio = models.TextField(blank=True)
avatar = models.ImageField(upload_to="avatars/", blank=True, null=True)AUTH_USER_MODEL = "accounts.User"깊은 커스터마이징 (이메일 로그인, 권한 등)은 중급 #4 사용자/권한에서.
로그인 / 로그아웃 — 빌트인 view #
직접 만들 필요 없습니다. django.contrib.auth.urls에 다 있습니다.
from django.contrib import admin
from django.urls import include, path
urlpatterns = [
path("admin/", admin.site.urls),
path("accounts/", include("django.contrib.auth.urls")),
path("blog/", include("blog.urls")),
]include("django.contrib.auth.urls") 한 줄에 따라오는 URL:
| 이름 | 경로 | 쓰임 |
|---|---|---|
login | /accounts/login/ | 로그인 |
logout | /accounts/logout/ | 로그아웃 |
password_change | /accounts/password_change/ | 비밀번호 변경 |
password_change_done | /accounts/password_change/done/ | 변경 완료 |
password_reset | /accounts/password_reset/ | 비밀번호 재설정 (메일) |
password_reset_done / confirm / complete | … | 재설정 흐름 |
view는 다 있는데 템플릿은 직접 만들어야 합니다.
{% extends "base.html" %}
{% block content %}
<h1> 로그인</h1>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit"> 로그인</button>
</form>
{% if form.errors %}
<p>아이디나 비밀번호가 잘못되었습니다.</p>
{% endif %}
<p><a href="{% url 'password_reset' %}">비밀번호를 잊으셨나요?</a></p>
{% endblock %}이 한 파일이면 로그인이 동작합니다. logout / password_* 도 같은 흐름 (templates/registration/)에 만듭니다.
로그인 후 리디렉트 #
LOGIN_URL = "/accounts/login/"
LOGIN_REDIRECT_URL = "/blog/"
LOGOUT_REDIRECT_URL = "/blog/"회원가입은 직접 #
빌트인은 로그인 / 로그아웃 / 비밀번호만 줍니다. 회원가입은 직접 만들거나 django-allauth 같은 패키지를 씁니다.
from django.contrib.auth import login
from django.contrib.auth.forms import UserCreationForm
from django.shortcuts import redirect, render
def signup(request):
if request.method == "POST":
form = UserCreationForm(request.POST)
if form.is_valid():
user = form.save()
login(request, user)
return redirect("blog:post_list")
else:
form = UserCreationForm()
return render(request, "registration/signup.html", {"form": form})UserCreationForm도 빌트인 — username + password + password 확인 필드를 갖춘 ModelForm입니다.
@login_required — 로그인 보호
#
view 함수 위에 데코레이터 한 줄.
from django.contrib.auth.decorators import login_required
@login_required
def post_new(request):
...로그인 안 된 사용자가 접근하면 자동으로 LOGIN_URL로 리디렉트 (원래 가려던 URL은 ?next=... 쿼리로 달려서 로그인 후 돌아옴).
request.user — 현재 사용자
#
def my_page(request):
if request.user.is_authenticated:
username = request.user.username
...{% if user.is_authenticated %}
<p>{{ user.username }} 님 환영합니다.</p>
<a href="{% url 'logout' %}"> 로그아웃</a>
{% else %}
<a href="{% url 'login' %}"> 로그인</a>
{% endif %}request.user는 로그인 안 됐으면 AnonymousUser 인스턴스입니다 (is_authenticated가 False). 그래서 if request.user: 같은 검사는 의미가 없습니다 — 항상 is_authenticated로.
권한 — @permission_required / user.has_perm
#
Django는 모델마다 자동으로 4 개 권한을 만듭니다 — add_<model>, change_<model>, delete_<model>, view_<model>.
from django.contrib.auth.decorators import permission_required
@permission_required("blog.delete_post", raise_exception=True)
def post_delete(request, post_id):
...if request.user.has_perm("blog.change_post"):
...{% if perms.blog.change_post %}
<a href="...">수정</a>
{% endif %}Group으로 권한 묶음을 만들고 사용자에게 그룹을 할당하는 게 운영 패턴입니다. 깊은 권한 모델은 중급 #4 사용자/권한에서.
정리 #
이번 글에서 잡은 것:
admin.site.register(Model)한 줄로 자동 CRUD UIcreatesuperuser로 관리자 계정ModelAdmin—list_display,list_filter,search_fields,date_hierarchy, …- Inline — 부모 화면에서 자식 같이 편집
- 빌트인
User—create_user,set_password,check_password - 새 프로젝트는 처음부터 커스텀 User 모델 권장
include("django.contrib.auth.urls")로 로그인/로그아웃/비번 흐름- 템플릿은
templates/registration/에 직접 @login_required,request.user.is_authenticated- 권한 — 모델별 자동 생성,
@permission_required,user.has_perm, 템플릿perms.app.codename - Admin은 운영 도구로 그대로 써도 되는 수준 — 단, 보안 (경로 변경, 2FA, IP 제한) 필수
시리즈 마무리 #
7편을 한 호흡으로 정리하면:
- #1 Django 란 — 풀스택 모놀리스의 가치
- #2 프로젝트 셋업 — uv + startproject + startapp
- #3 Models와 ORM 기초 — 모델, 마이그레이션, QuerySet
- #4 URL과 Views — URLconf, FBV, render
- #5 Templates와 정적 파일 — 템플릿 상속, static
- #6 Forms와 ModelForm — 폼 검증, ModelForm, 파일 업로드
- #7 Admin과 인증 — 자동 CRUD, 로그인, 권한 ← 지금
이 7편이면 작은 블로그 한 채를 짓는 데 필요한 부품이 다 모였습니다. 혼자서 작은 사이드 프로젝트를 만들 수 있는 수준입니다.
다음 시리즈 장고 중급 #1 CBV 깊이에서는 FBV의 반복 패턴을 줄이는 클래스 기반 뷰를 다룹니다. ListView, DetailView, CreateView, UpdateView, DeleteView 다섯 개로 같은 CRUD를 훨씬 짧게 쓰는 흐름을 봅니다. 그 위에 중급 #2 ORM 중급의 select_related / prefetch_related, 중급 #7 테스트 까지 — 한 채를 진짜 운영할 수 있는 형태로 만드는 흐름입니다.
여기까지 따라오신 분께 감사드립니다. 풀스택 한 채를 짓는 즐거움이 시작이길.