장고 기초 #7 Django Admin과 built-in 인증

6 분 소요

#1 Django 란에서 한 약속 — Admin이 무료로 따라온다 — 을 회수할 차례입니다. 이번 글은 Django가 자동으로 만들어주는 관리자 페이지와, 같은 흐름에서 따라오는 빌트인 인증 (User, 로그인, 권한)을 한 호흡으로 봅니다. 시리즈 마지막 편입니다.

Admin — 모델 등록 한 줄 #

blog/admin.py에 한 줄.

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 만들기 #

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으로 풍부하게 만듭니다.

blog/admin.py
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_verticalM2M의 두-칸 위젯
autocomplete_fieldsFK / M2M 자동완성 (대상 모델에 search_fields 필요)
prepopulated_fields다른 필드에서 자동 채움 (slug 흔함)

@admin.register(Post) 데코레이터가 admin.site.register(Post, PostAdmin)와 같습니다. 더 깔끔합니다.

Inline — 부모 화면에서 자식 같이 편집 #

Post의 상세 화면에서 그 글의 Comment 들을 같이 보여주고 싶다면.

Inline
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에 들어 있고, migrateUser, Group, Permission 테이블이 만들어졌습니다.

User 모델 #

기본 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")  # → True

create_userset_password가 비밀번호 해싱 (PBKDF2 기본)을 자동으로. 절대 평문으로 저장하지 마세요.

커스텀 User — 처음부터 권장 #

기본 User도 동작하지만, 새 프로젝트는 처음부터 커스텀 User 모델로 시작 하는 게 컨벤션입니다. 나중에 바꾸기가 굉장히 어렵습니다.

accounts/models.py — 커스텀 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)
config/settings.py
AUTH_USER_MODEL = "accounts.User"

깊은 커스터마이징 (이메일 로그인, 권한 등)은 중급 #4 사용자/권한에서.

로그인 / 로그아웃 — 빌트인 view #

직접 만들 필요 없습니다. django.contrib.auth.urls에 다 있습니다.

config/urls.py
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는 다 있는데 템플릿은 직접 만들어야 합니다.

templates/registration/login.html
{% 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/)에 만듭니다.

로그인 후 리디렉트 #

config/settings.py
LOGIN_URL = "/accounts/login/"
LOGIN_REDIRECT_URL = "/blog/"
LOGOUT_REDIRECT_URL = "/blog/"

회원가입은 직접 #

빌트인은 로그인 / 로그아웃 / 비밀번호만 줍니다. 회원가입은 직접 만들거나 django-allauth 같은 패키지를 씁니다.

accounts/views.py — 직접 만든 signup
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 함수 위에 데코레이터 한 줄.

blog/views.py
from django.contrib.auth.decorators import login_required


@login_required
def post_new(request):
    ...

로그인 안 된 사용자가 접근하면 자동으로 LOGIN_URL로 리디렉트 (원래 가려던 URL은 ?next=... 쿼리로 달려서 로그인 후 돌아옴).

request.user — 현재 사용자 #

view 안에서
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_authenticatedFalse). 그래서 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 UI
  • createsuperuser로 관리자 계정
  • ModelAdminlist_display, list_filter, search_fields, date_hierarchy, …
  • Inline — 부모 화면에서 자식 같이 편집
  • 빌트인 Usercreate_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. #1 Django 란 — 풀스택 모놀리스의 가치
  2. #2 프로젝트 셋업 — uv + startproject + startapp
  3. #3 Models와 ORM 기초 — 모델, 마이그레이션, QuerySet
  4. #4 URL과 Views — URLconf, FBV, render
  5. #5 Templates와 정적 파일 — 템플릿 상속, static
  6. #6 Forms와 ModelForm — 폼 검증, ModelForm, 파일 업로드
  7. #7 Admin과 인증 — 자동 CRUD, 로그인, 권한 ← 지금

이 7편이면 작은 블로그 한 채를 짓는 데 필요한 부품이 다 모였습니다. 혼자서 작은 사이드 프로젝트를 만들 수 있는 수준입니다.

다음 시리즈 장고 중급 #1 CBV 깊이에서는 FBV의 반복 패턴을 줄이는 클래스 기반 뷰를 다룹니다. ListView, DetailView, CreateView, UpdateView, DeleteView 다섯 개로 같은 CRUD를 훨씬 짧게 쓰는 흐름을 봅니다. 그 위에 중급 #2 ORM 중급의 select_related / prefetch_related, 중급 #7 테스트 까지 — 한 채를 진짜 운영할 수 있는 형태로 만드는 흐름입니다.

여기까지 따라오신 분께 감사드립니다. 풀스택 한 채를 짓는 즐거움이 시작이길.

X