관리자 페이지는 무엇을 대신해 주는가
앞 장에서 Category와 Post 모델을 만들고, migration을 통해 데이터베이스에 테이블을 생성했다. 이제 남은 문제는 데이터를 어떻게 넣고 관리하느냐다.
게시글을 하나 추가하기 위해 매번 Django shell을 열고 Post.objects.create(...)를 실행할 수는 있다. 하지만 실제로는 글을 확인하고, 수정하고, 삭제하고, 검색하는 화면이 필요하다. 이때 Django Admin을 사용하면 모델을 기반으로 기본 관리 화면을 빠르게 만들 수 있다.
Django Admin은 사용자가 보는 공개 화면이 아니다. 사이트 운영자나 내부 관리자가 데이터를 관리하기 위한 도구다. 모델을 등록하면 Django가 목록 화면, 상세 수정 화면, 삭제 화면을 자동으로 만들어 준다. 처음부터 별도의 관리 페이지를 직접 만들 필요가 없기 때문에 개발 초기에 특히 유용하다.
단, Admin은 만능 도구가 아니다. 고객이 사용하는 서비스 화면은 별도로 만들어야 한다. Admin은 운영자가 데이터를 빠르게 확인하고 정리하는 내부 도구라고 이해하면 된다.
AI 생성 이미지
관리자 계정 만들기
관리자 페이지에 들어가려면 먼저 관리자 계정이 필요하다. Django는 createsuperuser 명령으로 모든 권한을 가진 계정을 만들 수 있다.
프로젝트 폴더에서 다음 명령을 실행한다.
python manage.py createsuperuser명령을 실행하면 사용자 이름, 이메일, 비밀번호를 입력하라고 나온다.
Username: admin
Email address: [email protected]
Password:
Password (again):비밀번호를 입력할 때 화면에 글자가 보이지 않는 것은 정상이다. 보안을 위해 터미널에 표시하지 않을 뿐 입력은 되고 있다.
계정을 만들었다면 개발 서버를 실행한다.
python manage.py runserver브라우저에서 다음 주소로 접속한다.
http://127.0.0.1:8000/admin/로그인 화면이 나타나면 방금 만든 계정으로 로그인한다. 로그인에 성공하면 Django 관리자 메인 화면이 보인다. 처음에는 Authentication and Authorization 영역에 사용자와 그룹 정도만 보일 수 있다. 우리가 만든 Category, Post 모델은 아직 등록하지 않았기 때문이다.
모델을 관리자 페이지에 등록하기
Django Admin은 프로젝트의 모든 모델을 자동으로 보여 주지 않는다. 어떤 모델을 관리자에서 다룰지 개발자가 명시적으로 등록해야 한다. 등록은 앱의 admin.py에서 한다.
예를 들어 boards 앱에 Category, Post 모델이 있다면 boards/admin.py를 다음처럼 작성한다.
from django.contrib import admin
from .models import Category, Post
admin.site.register(Category)
admin.site.register(Post)다시 관리자 페이지를 새로고침하면 Categorys, Posts처럼 모델 목록이 나타난다. 모델에 verbose_name과 verbose_name_plural을 설정했다면 더 자연스러운 이름으로 표시된다.
예를 들어 모델에 다음 설정이 있다면 관리자 화면에서도 이 이름을 사용한다.
class Category(models.Model):
name = models.CharField('이름', max_length=50)
class Meta:
verbose_name = '카테고리'
verbose_name_plural = '카테고리'
def __str__(self):
return self.name__str__()도 중요하다. 관리자 페이지에서 객체를 선택하거나 목록에서 표시할 때 Django는 객체를 문자열로 바꿔 보여 준다. __str__()을 정의하지 않으면 Category object (1)처럼 알아보기 어려운 이름이 나온다. 관리 화면에서 사람이 읽기 좋은 이름을 보여 주려면 모델마다 __str__()을 작성하는 습관을 들이는 것이 좋다.
기본 등록만으로 가능한 일
모델을 단순히 admin.site.register(Post)로 등록해도 많은 기능을 바로 사용할 수 있다.
관리자 목록 화면에서는 데이터 목록을 볼 수 있다. 오른쪽 위의 Add 버튼을 누르면 새 데이터를 만들 수 있고, 기존 항목을 클릭하면 상세 수정 화면으로 들어간다. 수정 화면에서는 필드 값을 바꾸고 저장할 수 있으며, 필요하면 삭제도 할 수 있다.
예를 들어 Post 모델이 다음과 같다고 하자.
class Post(models.Model):
category = models.ForeignKey(Category, on_delete=models.CASCADE)
title = models.CharField('제목', max_length=100)
content = models.TextField('내용')
is_published = models.BooleanField('공개 여부', default=True)
created_at = models.DateTimeField('작성일', auto_now_add=True)
updated_at = models.DateTimeField('수정일', auto_now=True)
def __str__(self):
return self.title관리자 페이지에서 게시글을 추가하면 title, content, category, is_published 값을 입력할 수 있다. created_at, updated_at처럼 자동으로 채워지는 필드는 일반적으로 직접 수정할 필요가 없다.
이 단계만으로도 개발자는 테스트 데이터를 쉽게 만들 수 있다. View와 Template을 만들기 전에 Admin에서 게시글을 몇 개 넣어 두면, 목록 페이지와 상세 페이지를 실제 데이터로 확인할 수 있다.
목록 화면 보기 좋게 만들기
기본 등록은 빠르지만 목록 화면이 단순하다. 게시글 목록에 제목만 보이면 운영자가 원하는 글을 찾기 어렵다. 이때 ModelAdmin 클래스를 사용해 관리자 화면을 조정한다.
boards/admin.py를 다음처럼 바꿔 보자.
from django.contrib import admin
from .models import Category, Post
@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
list_display = ['id', 'name']
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
list_display = ['id', 'title', 'category', 'is_published', 'created_at']
list_filter = ['category', 'is_published', 'created_at']
search_fields = ['title', 'content']
ordering = ['-created_at']@admin.register(Post)는 admin.site.register(Post, PostAdmin)과 같은 역할을 한다. 모델과 관리자 설정 클래스를 함께 연결하는 방식이다.
각 옵션의 역할은 다음과 같다.
list_display: 목록 화면에 보여 줄 컬럼을 정한다.list_filter: 오른쪽에 필터 영역을 만든다.search_fields: 검색창에서 검색할 필드를 정한다.ordering: 목록의 기본 정렬 순서를 정한다.
list_display에 title, category, is_published, created_at을 넣으면 게시글 목록에서 핵심 정보를 한눈에 볼 수 있다. list_filter에 category와 is_published를 넣으면 특정 카테고리의 글이나 공개된 글만 빠르게 볼 수 있다. search_fields에 title, content를 넣으면 제목과 본문을 대상으로 검색할 수 있다.
ordering = ['-created_at']은 최신 글이 위에 오도록 정렬한다. 앞의 -는 내림차순을 뜻한다. 반대로 ordering = ['created_at']이라고 쓰면 오래된 글부터 보인다.
목록에서 바로 수정하기
관리자 화면에서는 목록에서 바로 값을 바꾸도록 설정할 수도 있다. 예를 들어 게시글 공개 여부를 자주 바꾼다면 매번 상세 화면에 들어가는 것이 번거롭다.
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
list_display = ['id', 'title', 'category', 'is_published', 'created_at']
list_editable = ['is_published']
list_filter = ['category', 'is_published']
search_fields = ['title', 'content']
ordering = ['-created_at']list_editable에 is_published를 넣으면 목록 화면에서 체크박스로 값을 바로 바꿀 수 있다. 수정 후에는 목록 아래쪽의 저장 버튼을 눌러야 반영된다.
다만 모든 필드를 목록에서 수정하게 만드는 것은 좋지 않다. 제목이나 본문처럼 실수하면 영향이 큰 값은 상세 화면에서 확인하고 수정하는 편이 안전하다. list_editable은 공개 여부, 노출 순서, 승인 상태처럼 짧고 의미가 분명한 필드에 쓰는 것이 적절하다.
ForeignKey는 어떻게 보일까
Post 모델의 category는 ForeignKey다. 하나의 게시글이 하나의 카테고리를 참조한다는 뜻이다. 관리자 수정 화면에서 category 필드는 선택 상자로 표시된다.
이때 선택 상자에 보이는 이름은 Category 모델의 __str__() 결과다.
class Category(models.Model):
name = models.CharField('이름', max_length=50)
def __str__(self):
return self.name만약 __str__()이 없다면 카테고리 선택 상자에 Category object (1)처럼 표시된다. 기능적으로는 동작하지만 운영자가 어떤 카테고리인지 알기 어렵다. 관계형 모델을 Admin에서 다룰 때는 참조되는 모델의 __str__()이 특히 중요하다.
데이터가 많아지면 선택 상자도 불편해진다. 카테고리가 수십 개라면 괜찮지만 수천 개라면 긴 목록에서 찾기 어렵다. 이럴 때는 검색 가능한 선택 방식도 고려할 수 있다.
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
list_display = ['id', 'title', 'category', 'is_published', 'created_at']
search_fields = ['title', 'content']
autocomplete_fields = ['category']autocomplete_fields를 사용하면 category를 입력 검색 방식으로 선택할 수 있다. 이 기능을 쓰려면 참조 대상인 CategoryAdmin에도 검색 필드가 있어야 한다.
@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
list_display = ['id', 'name']
search_fields = ['name']이처럼 관계 필드를 다룰 때는 양쪽 관리자 설정이 함께 맞물린다.
Inline으로 관련 데이터를 한 화면에서 편집하기
카테고리를 수정하면서 그 카테고리에 속한 게시글도 함께 보고 싶을 때가 있다. 이때 inline을 사용한다. Inline은 부모 모델의 수정 화면 안에서 자식 모델을 함께 편집하게 해 주는 기능이다.
Category가 부모이고 Post가 자식이라고 보면 된다. Post가 category = ForeignKey(Category, ...)로 Category를 참조하기 때문이다.
class PostInline(admin.TabularInline):
model = Post
extra = 1
fields = ['title', 'is_published']
@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
list_display = ['id', 'name']
search_fields = ['name']
inlines = [PostInline]이제 관리자에서 카테고리 상세 화면에 들어가면 해당 카테고리에 속한 게시글을 아래쪽에서 함께 볼 수 있다. TabularInline은 표 형태로 간결하게 보여 준다. 더 자세한 폼 형태가 필요하면 StackedInline을 사용할 수 있다.
class PostInline(admin.StackedInline):
model = Post
extra = 1extra는 새로 추가할 수 있는 빈 입력 줄의 개수다. extra = 1이면 빈 게시글 입력 영역이 하나 보인다. 너무 크게 잡으면 화면이 복잡해지므로 필요한 만큼만 둔다.
Inline은 편리하지만 남용하면 관리자 화면이 무거워진다. 관련 데이터가 너무 많다면 한 화면에 모두 넣기보다 별도 목록에서 필터링해 관리하는 편이 낫다.
자동 필드는 읽기 전용으로 보여 주기
created_at, updated_at 같은 필드는 데이터의 생성 시각과 수정 시각을 기록한다. 보통 운영자가 직접 바꾸지 않는다. 하지만 관리자 상세 화면에서 값은 확인하고 싶을 수 있다.
이럴 때 readonly_fields를 사용한다.
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
list_display = ['id', 'title', 'category', 'is_published', 'created_at']
list_filter = ['category', 'is_published', 'created_at']
search_fields = ['title', 'content']
ordering = ['-created_at']
readonly_fields = ['created_at', 'updated_at']readonly_fields에 포함된 필드는 수정 화면에 표시되지만 입력할 수 없다. 자동으로 관리되는 값이나 운영자가 참고만 해야 하는 값에 적합하다.
필드가 많아지면 수정 화면을 정리할 수도 있다.
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
list_display = ['id', 'title', 'category', 'is_published', 'created_at']
readonly_fields = ['created_at', 'updated_at']
fields = ['category', 'title', 'content', 'is_published', 'created_at', 'updated_at']fields는 상세 수정 화면에서 필드를 어떤 순서로 보여 줄지 정한다. 입력 흐름에 맞춰 자주 쓰는 필드를 위에 두면 관리하기 쉽다.
Admin을 운영 도구로 볼 때의 주의점
Django Admin은 강력하지만 기본적으로 내부 관리자를 위한 화면이다. 따라서 다음 점을 기억해야 한다.
첫째, 관리자 계정은 신중하게 발급해야 한다. superuser는 모든 권한을 가진 계정이다. 모든 운영자에게 슈퍼유저 권한을 주기보다 필요한 권한만 부여하는 편이 안전하다.
둘째, Admin URL을 알고 있다고 해서 아무나 들어갈 수 있는 것은 아니지만, 로그인 정보가 노출되면 위험하다. 운영 환경에서는 강한 비밀번호를 사용하고, HTTPS를 적용해야 한다.
셋째, Admin을 사용자용 화면으로 대체하지 말아야 한다. Admin은 데이터 관리에는 좋지만 서비스의 사용 경험을 설계하기 위한 도구는 아니다. 예를 들어 블로그 독자가 글을 읽는 화면, 회원이 게시글을 작성하는 화면은 별도의 View와 Template으로 만들어야 한다.
Admin의 장점은 개발자가 데이터 관리 기능을 처음부터 모두 만들지 않아도 된다는 데 있다. 모델만 잘 설계해 두면 Django가 기본 관리 화면을 만들어 주고, ModelAdmin 설정으로 필요한 만큼 다듬을 수 있다.
정리
이번 장에서는 Django Admin으로 모델 데이터를 관리하는 방법을 살펴보았다.
먼저 createsuperuser로 관리자 계정을 만들고 /admin/ 주소로 로그인했다. 그다음 admin.py에 모델을 등록해 목록, 추가, 수정, 삭제 화면이 자동으로 만들어지는 과정을 확인했다.
기본 등록만으로도 충분히 유용하지만, 실제로 쓰기 좋은 관리 화면을 만들려면 ModelAdmin 설정이 필요하다. list_display로 목록 컬럼을 정하고, list_filter로 빠른 필터를 만들고, search_fields로 검색 대상을 지정하고, ordering으로 기본 정렬을 정리할 수 있다.
또한 ForeignKey 관계는 관리자 화면에서 선택 필드로 표시되며, 참조 대상 모델의 __str__()이 화면의 가독성을 좌우한다. 관련 데이터를 한 화면에서 다루고 싶다면 inline 편집을 사용할 수 있고, 자동 생성되는 값은 readonly_fields로 읽기 전용으로 보여 줄 수 있다.
다음 장에서는 운영자가 아니라 일반 사용자가 입력하는 데이터를 다룬다. 사용자가 웹 화면에서 제목과 내용을 입력하고 서버로 보내면, Django가 그 입력을 어떻게 검증하고 처리하는지 폼을 통해 살펴본다.
이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.