๐ Django ๋ด๋ถ ๋์ ์๋ฆฌ์ Best Practice
1. Django ORM (์ฟผ๋ฆฌ์ ์ต์ ํ)โ
Django ORM(Object-Relational Mapper)์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๊ฐ์ฒด ์งํฅ ํ๋ก๊ทธ๋๋ฐ ์ธ์ด ๊ฐ์ ํธํ๋์ง ์๋ ๋ฐ์ดํฐ๋ฅผ ๋ณํํ๋ ํ๋ก๊ทธ๋๋ฐ ๊ธฐ๋ฒ์ ๋๋ค. ORM์ ์ฌ์ฉํ๋ฉด SQL ์ฟผ๋ฆฌ๋ฅผ ์ง์ ์์ฑํ์ง ์๊ณ ๋ ํ์ด์ฌ ์ฝ๋๋ก ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ํธ์์ฉํ ์ ์์ต๋๋ค. ํ์ง๋ง ORM์ ํจ์จ์ ์ผ๋ก ์ฌ์ฉํ์ง ์์ผ๋ฉด ์ฑ๋ฅ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์์ผ๋ฉฐ, ํนํ N+1 ๋ฌธ์ ๊ฐ ๋ํ์ ์ ๋๋ค.
N+1 ๋ฌธ์ ๋?โ
N+1 ๋ฌธ์ ๋ ํ๋์ ์ฟผ๋ฆฌ๋ก ๊ฐ์ ธ์ฌ ์ ์๋ ๋ฐ์ดํฐ๋ฅผ N๊ฐ์ ์ถ๊ฐ ์ฟผ๋ฆฌ๋ฅผ ํตํด ๊ฐ์ ธ์ค๋ ๋นํจ์จ์ ์ธ ์ํฉ์ ๋งํฉ๋๋ค. ์ ๋ฅผ ๋ค์ด, ๊ฒ์๊ธ ๋ชฉ๋ก์ ๊ฐ์ ธ์จ ํ ๊ฐ ๊ฒ์๊ธ์ ์์ฑ์ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ค๊ธฐ ์ํด ๊ฒ์๊ธ ์๋งํผ ์ถ๊ฐ ์ฟผ๋ฆฌ๊ฐ ๋ฐ์ํ๋ ๊ฒฝ์ฐ์ ๋๋ค.
# N+1 ๋ฌธ์ ๋ฐ์ ์์
posts = Post.objects.all() # ๊ฒ์๊ธ ๋ชฉ๋ก ๊ฐ์ ธ์ค๊ธฐ (์ฟผ๋ฆฌ 1ํ)
for post in posts:
print(post.author.username) # ๊ฐ ๊ฒ์๊ธ์ ์์ฑ์ ์ ๋ณด ์ ๊ทผ ์๋ง๋ค ์ฟผ๋ฆฌ ๋ฐ์ (Nํ)
์ด๋ฌํ N+1 ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด Django ORM์ select_related์ prefetch_related๋ผ๋ ๋ ๊ฐ์ง ์ฃผ์ ์ต์ ํ ๋๊ตฌ๋ฅผ ์ ๊ณตํฉ๋๋ค.
select_relatedโ
select_related๋ ์ ๋ฐฉํฅ ForeignKey ๋๋ OneToOneField ๊ด๊ณ์ ๊ฐ์ฒด๋ค์ SQL JOIN์ ์ฌ์ฉํ์ฌ ํ ๋ฒ์ ์ฟผ๋ฆฌ๋ก ๊ฐ์ ธ์ต๋๋ค. ์ฆ, ๊ด๋ จ๋ ๊ฐ์ฒด๋ค์ "์ ํ์ ์ผ๋ก" ๋ฏธ๋ฆฌ ๋ก๋ํ์ฌ ์ถ๊ฐ ์ฟผ๋ฆฌ ๋ฐ์์ ๋ฐฉ์งํฉ๋๋ค.
์ฌ์ฉ ์์ :
- ํ๋์ ๊ฐ์ฒด์ ๊ด๋ จ๋ ๋ค๋ฅธ ํ๋์ ๊ฐ์ฒด๋ฅผ ๊ฐ์ ธ์ฌ ๋ (One-to-One, Foreign Key)
- JOIN์ผ๋ก ์ธํด ์ฟผ ๋ฆฌ ๊ฒฐ๊ณผ์ ํฌ๊ธฐ๊ฐ ๋๋ฌด ์ปค์ง์ง ์์ ๋
์ฌ์ฉ๋ฒ:
# Post ๋ชจ๋ธ์ด author๋ผ๋ ForeignKey ํ๋๋ฅผ ๊ฐ์ง๊ณ ์๋ค๊ณ ๊ฐ์
posts = Post.objects.select_related('author').all()
for post in posts:
print(post.author.username) # ์ถ๊ฐ ์ฟผ๋ฆฌ ๋ฐ์ ์ ํจ
์ฃผ์์ฌํญ:
- ManyToManyField๋ ์ญ๋ฐฉํฅ ForeignKey ๊ด๊ณ์๋ ์ฌ์ฉํ ์ ์์ต๋๋ค. (prefetch_related ์ฌ์ฉ)
- ๋๋ฌด ๋ง์ JOIN์ ์คํ๋ ค ์ฑ๋ฅ์ ์ ํ์ํฌ ์ ์์ผ๋ฏ๋ก ํ์ํ ๊ด๊ณ๋ง ์ง์ ํด์ผ ํฉ๋๋ค.
prefetch_relatedโ
prefetch_related๋ ManyToManyField ๋๋ ์ญ๋ฐฉํฅ ForeignKey ๊ด๊ณ, ๊ทธ๋ฆฌ๊ณ GenericForeignKey ๊ด๊ณ์ ๊ฐ์ฒด๋ค์ ๋ณ๋์ ์ฟผ๋ฆฌ๋ก ๊ฐ์ ธ์จ ํ ํ์ด์ฌ ๋ ๋ฒจ์์ ์กฐ์ธํฉ๋๋ค. ์ฆ, ๊ด๋ จ๋ ๊ฐ์ฒด๋ค์ "๋ฏธ๋ฆฌ ๊ฐ์ ธ์์" ์ฐ๊ฒฐํฉ๋๋ค.
์ฌ์ฉ ์์ :
- ํ๋์ ๊ฐ์ฒด์ ๊ด๋ จ๋ ์ฌ๋ฌ ๊ฐ์ฒด๋ฅผ ๊ฐ์ ธ์ฌ ๋ (Many-to-Many, ์ญ๋ฐฉํฅ Foreign Key)
- select_related๋ฅผ ์ฌ์ฉํ ์ ์๋ ๊ด๊ณ์ผ ๋
์ฌ์ฉ๋ฒ:
# Post ๋ชจ๋ธ์ 'tags'๋ผ๋ ManyToManyField๊ฐ ์๊ณ , User ๋ชจ๋ธ์ด 'posts'๋ผ๋ ์ญ๋ฐฉํฅ ๊ด๊ณ๋ฅผ ๊ฐ์ง๊ณ ์๋ค๊ณ ๊ฐ์
posts = Post.objects.prefetch_related('tags').all()
for post in posts:
print([tag.name for tag in post.tags.all()]) # ์ถ๊ฐ ์ฟผ๋ฆฌ ๋ฐ์ ์ ํจ (tags์ ๋ํด)
users = User.objects.prefetch_related('post_set').all() # User์ ๊ด๋ จ๋ Post๋ค์ ๋ฏธ๋ฆฌ ๊ฐ์ ธ์ด
for user in users:
print([p.title for p in user.post_set.all()]) # ์ถ๊ฐ ์ฟผ๋ฆฌ ๋ฐ์ ์ ํจ
Prefetch ๊ฐ์ฒด ํ์ฉ: prefetch_related๋ Prefetch ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ์ฌ ๋ฏธ๋ฆฌ ๊ฐ์ ธ์ฌ ๋ฐ์ดํฐ์ ๋ํ ์ธ๋ถ์ ์ธ ์ ์ด(์: ํน์ ์กฐ๊ฑด์ ๋ฐ์ดํฐ๋ง ๊ฐ์ ธ์ค๊ฑฐ๋, ์ ๋ ฌ ์์ ์ง์ )๊ฐ ๊ฐ๋ฅํฉ๋๋ค.
from django.db.models import Prefetch
# ํน์ ์กฐ๊ฑด์ ๋ง๋ ํ๊ทธ๋ง ๋ฏธ๋ฆฌ ๊ฐ์ ธ์ค๊ธฐ
posts = Post.objects.prefetch_related(
Prefetch('tags', queryset=Tag.objects.filter(is_active=True))
).all()
Best Practice:
- ํ ํ๋ฆฟ์ด๋ ์ฝ๋์์ ๊ด๋ จ๋ ๊ฐ์ฒด์ ์์ฑ์ ๋ฐ๋ณต์ ์ผ๋ก ์ ๊ทผํด์ผ ํ๋ค๋ฉด select_related ๋๋ prefetch_related ์ฌ์ฉ์ ์ ๊ทน์ ์ผ๋ก ๊ณ ๋ คํฉ๋๋ค.
- Django Debug Toolbar์ ๊ฐ์ ๋๊ตฌ๋ฅผ ์ฌ์ฉํ์ฌ ์คํ๋๋ ์ฟผ๋ฆฌ ์๋ฅผ ํ์ธํ๊ณ N+1 ๋ฌธ์ ๋ฅผ ์ง๋จํฉ๋๋ค.
- ํ์ํ ํ๋๋ง ๊ฐ์ ธ์ค๊ธฐ ์ํด
only()๋๋defer()๋ฅผ ํจ๊ป ์ฌ์ฉํ๋ ๊ฒ์ ๊ณ ๋ คํฉ๋๋ค. (๋จ,select_related์ ํจ๊ป ์ฌ์ฉํ ๋ ์ฃผ ์.select_related๋ก ๊ฐ์ ธ์จ ํ๋๋only์ ๋ช ์ํด์ผ ํจ) values()๋values_list()๋ฅผ ์ฌ์ฉํ์ฌ ๋์ ๋๋ฆฌ๋ ํํ ํํ๋ก ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋ฉด ORM ๊ฐ์ฒด ์์ฑ ๋น์ฉ์ ์ค์ผ ์ ์์ง๋ง, ์ด ๊ฒฝ์ฐselect_related๋prefetch_related์ ํจ๊ณผ๋ฅผ ์ง์ ์ ์ผ๋ก ๋๋ฆฌ๊ธฐ ์ด๋ ต์ต๋๋ค. (๋์annotate๋ฑ์ ํ์ฉ)
๊ธฐํ ์ต์ ํ ๊ธฐ๋ฒโ
- only() ๋ฐ defer(): ๋ชจ๋ธ ์ธ์คํด์ค๋ฅผ ๊ฐ์ ธ์ฌ ๋ ํน์ ํ๋๋ง ์ฆ์ ๋ก๋ํ๊ฑฐ๋ ํน์ ํ๋์ ๋ก๋๋ฅผ ์ง์ฐ์ํต๋๋ค.
# 'title'๊ณผ 'author' ํ๋๋ง ์ฆ์ ๋ก๋
posts = Post.objects.only('title', 'author').all()
# 'content' ํ๋๋ ๋์ค์ ์ ๊ทผํ ๋ ๋ก๋
posts = Post.objects.defer('content').all()
- values() ๋ฐ values_list(): ์ฟผ๋ฆฌ์ ๊ฒฐ๊ณผ๋ฅผ ๋์ ๋๋ฆฌ ๋๋ ํํ ๋ฆฌ์คํธ๋ก ๋ฐํํ์ฌ ORM ๊ฐ์ฒด ์์ฑ ์ค๋ฒํค๋๋ฅผ ์ค์ ๋๋ค. ํน์ ํ๋๋ง ์ ํํ ๋ ์ ์ฉํฉ๋๋ค.
# ์ ๋ชฉ๋ง ๋์
๋๋ฆฌ ๋ฆฌ์คํธ๋ก ๊ฐ์ ธ์ค๊ธฐ
titles = Post.objects.values('title')
# ์ ๋ชฉ๊ณผ ์์ฑ์ ID๋ง ํํ ๋ฆฌ์คํธ๋ก ๊ฐ์ ธ์ค๊ธฐ
data = Post.objects.values_list('title', 'author_id')
- count(): ๊ฐ์ฒด์ ์๋ฅผ ์
๋
len(queryset)๋์queryset.count()๋ฅผ ์ฌ์ฉํฉ๋๋ค.count()๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์์ค์์ ํจ์จ์ ์ผ๋ก ๊ฐ์๋ฅผ ๊ณ์ฐํฉ๋๋ค. - exists(): ํน์ ์กฐ๊ฑด์ ๋ง์กฑํ๋ ๊ฐ์ฒด๊ฐ ํ๋๋ผ๋ ์กด์ฌํ๋์ง ํ์ธํ ๋
queryset.exists()๋ฅผ ์ฌ์ฉํฉ๋๋ค. ์ ์ฒด ๊ฐ์ฒด๋ฅผ ๊ฐ์ ธ์ค์ง ์๊ณ ์กด์ฌ ์ฌ๋ถ๋ง ํ์ธํ๋ฏ๋ก ํจ์จ์ ์ ๋๋ค. - update() ๋ฐ delete(): ์ฌ๋ฌ ๊ฐ์ฒด๋ฅผ ํ ๋ฒ์ ์
๋ฐ์ดํธํ๊ฑฐ๋ ์ญ์ ํ ๋ ์ฟผ๋ฆฌ์
์
update()๋๋delete()๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ฉด ๊ฐ๋ณ ๊ฐ์ฒด๋ฅผ ๋ก๋ํ๊ณ ์ ์ฅํ๋ ๊ณผ์ ์ ์๋ตํ์ฌ ํจ์จ์ ์ ๋๋ค. (๋จ, ๋ชจ๋ธ์save(),delete()๋ฉ์๋๋ ๊ด๋ จ ์๊ทธ๋์ด ํธ์ถ๋์ง ์์์ ์ ์)
# is_published๊ฐ False์ธ ๋ชจ๋ ๊ฒ์๊ธ์ True๋ก ๋ณ๊ฒฝ
Post.objects.filter(is_published=False).update(is_published=True)
- annotate() ๋ฐ aggregate(): ๋ฐ์ดํฐ๋ฒ ์ด์ค ์์ค์์ ์ง๊ณ ์ฐ์ฐ(ํ๊ท , ํฉ๊ณ, ๊ฐ์ ๋ฑ)์ ์ํํฉ๋๋ค.
from django.db.models import Count, Avg
# ๊ฐ ์นดํ
๊ณ ๋ฆฌ๋ณ ๊ฒ์๊ธ ์ ๊ณ์ฐ
Category.objects.annotate(num_posts=Count('post'))
# ๋ชจ๋ ๊ฒ์๊ธ์ ํ๊ท ํ์ ๊ณ์ฐ
Post.objects.aggregate(avg_rating=Avg('rating'))
- ์ธ๋ฑ์ฑ (Indexing): ์์ฃผ ๊ฒ์๋๋ ํ๋์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ธ๋ฑ์ค๋ฅผ ์์ฑํ์ฌ ์กฐํ ์ฑ๋ฅ์ ํฅ์์ํต๋๋ค. ๋ชจ๋ธ ํ๋ ์ต์
์์
db_index=True๋ฅผ ์ค์ ํ๊ฑฐ๋Metaํด๋์ค์indexes๋ฅผ ์ ์ํฉ๋๋ค.
class Post(models.Model):
title = models.CharField(max_length=200, db_index=True) # ํ๋์ ์ง์ ์ธ๋ฑ์ค ์ค์
# ...
class Meta:
indexes = [
models.Index(fields=['created_at']), # created_at ํ๋์ ์ธ๋ฑ์ค ์์ฑ
]
- ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ ๊ด๋ฆฌ: ์ง์์ ์ธ ์ฐ๊ฒฐ(Persistent connections)์ ์ฌ์ฉํ์ฌ ๋งค ์์ฒญ๋ง๋ค ์๋ก์ด ์ฐ๊ฒฐ์ ์์ฑํ๋ ์ค๋ฒํค๋๋ฅผ ์ค์ผ ์ ์์ต๋๋ค. (Django 3.1๋ถํฐ
CONN_MAX_AGE๊ธฐ๋ณธ๊ฐ์ด 0์ด ์๋ 300์ผ๋ก ๋ณ๊ฒฝ๋์ด ๊ธฐ๋ณธ์ ์ผ๋ก ํ์ฑํ)
2. ๋ฏธ๋ค์จ์ด (Middleware)โ
๋ฏธ๋ค์จ์ด๋ Django์ ์์ฒญ/์๋ต ์ฒ๋ฆฌ ๊ณผ์ ์ ๊ฐ์ ํ์ฌ ์ ์ญ์ ์ธ ๊ธฐ๋ฅ์ ์ถ๊ฐํ ์ ์๋ "ํ (hook)" ๋๋ "ํ๋ฌ๊ทธ์ธ" ์์คํ ์ ๋๋ค. ๊ฐ ๋ฏธ๋ค์จ์ด ์ปดํฌ๋ํธ๋ ์์ฒญ์ ์ฒ๋ฆฌํ๊ฑฐ๋ ์๋ต์ ๋ฐํํ๊ธฐ ์ ์ ํน์ ์์ ์ ์ํํ ์ ์์ต๋๋ค.
๋ฏธ๋ค ์จ์ด์ ๋์ ์๋ฆฌโ
Django ์๋ฒ๊ฐ ์์ฒญ์ ๋ฐ์ผ๋ฉด, settings.py์ ์ ์๋ MIDDLEWARE ์ค์ ์ ๋ฐ๋ผ ์์๋๋ก ๋ฏธ๋ค์จ์ด๋ฅผ ํต๊ณผํฉ๋๋ค.
-
์์ฒญ ๋จ๊ณ (Request Phase):
- ๋ทฐ๊ฐ ํธ์ถ๋๊ธฐ ์ ์ ๊ฐ ๋ฏธ๋ค์จ์ด์
process_request(request)๋๋__call__(request)(์๋ก์ด ์คํ์ผ ๋ฏธ๋ค์จ์ด) ๋ฉ์๋๊ฐ ์์๋๋ก ํธ์ถ๋ฉ๋๋ค. - ์ด ๋จ๊ณ์์ ๋ฏธ๋ค์จ์ด๋ ์์ฒญ ๊ฐ์ฒด๋ฅผ ์์ ํ๊ฑฐ๋, ํน์ ์กฐ๊ฑด์ ๋ฐ๋ผ ์ฆ์
HttpResponse๋ฅผ ๋ฐํํ์ฌ ๋ทฐ ์ฒ๋ฆฌ๋ฅผ ์ค๋จ์ํฌ ์ ์์ต๋๋ค. (์: ์ธ์ฆ ๋ฏธ๋ค์จ์ด)
- ๋ทฐ๊ฐ ํธ์ถ๋๊ธฐ ์ ์ ๊ฐ ๋ฏธ๋ค์จ์ด์
-
๋ทฐ ์ฒ๋ฆฌ (View Processing):
- ๋ชจ๋ ์์ฒญ ๋ฏธ๋ค์จ์ด๋ฅผ ํต๊ณผํ๋ฉด Django๋ URLconf๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ ์ ํ ๋ทฐ๋ฅผ ์ฐพ์ ์คํํฉ๋๋ค.
- ๋ทฐ๋
HttpResponse๊ฐ์ฒด๋ฅผ ๋ฐํํฉ๋๋ค.
-
์๋ต ๋จ๊ณ (Response Phase):
- ๋ทฐ๊ฐ ์๋ต์ ๋ฐํํ๋ฉด, ๋ฏธ๋ค์จ์ด๋
MIDDLEWARE์ค์ ์ ์ญ์์ผ๋กprocess_response(request, response)๋๋__call__๋ด๋ถ์ ์๋ต ์ฒ๋ฆฌ ๋ก์ง์ด ํธ์ถ๋ฉ๋๋ค. - ์ด ๋จ๊ณ์์ ๋ฏธ๋ค์จ์ด๋ ์๋ต ๊ฐ์ฒด๋ฅผ ์์ ํ ์ ์์ต๋๋ค. (์: Content-Security-Policy ํค๋ ์ถ๊ฐ)
- ๋ทฐ๊ฐ ์๋ต์ ๋ฐํํ๋ฉด, ๋ฏธ๋ค์จ์ด๋
-
์์ธ ์ฒ๋ฆฌ ๋จ๊ณ (Exception Phase):
- ๋ทฐ ์ฒ๋ฆฌ ์ค ์์ธ๊ฐ ๋ฐ์ํ๋ฉด, Django๋
MIDDLEWARE์ค์ ์ ์ญ์์ผ๋ก ๊ฐ ๋ฏธ๋ค์จ์ด์process_exception(request, exception)๋ฉ์๋๋ฅผ ํธ์ถํฉ๋๋ค. - ์ด ๋ฉ์๋๋ ์์ธ๋ฅผ ์ฒ๋ฆฌํ๊ณ ์๋ก์ด
HttpResponse๋ฅผ ๋ฐํํ๊ฑฐ๋, ์๋ฌด๊ฒ๋ ๋ฐํํ์ง ์์ ๋ค์ ์์ธ ์ฒ๋ฆฌ ๋ฏธ๋ค์จ ์ด๋ก ๋๊ธธ ์ ์์ต๋๋ค.
- ๋ทฐ ์ฒ๋ฆฌ ์ค ์์ธ๊ฐ ๋ฐ์ํ๋ฉด, Django๋
-
์๋ก์ด ์คํ์ผ ๋ฏธ๋ค์จ์ด (Django 1.10+):
- ํด๋์ค ๊ธฐ๋ฐ ๋ฏธ๋ค์จ์ด๋
__init__๊ณผ__call__๋ฉ์๋๋ฅผ ๊ฐ์ง๋๋ค.__init__์ ์๋ฒ ์์ ์ ํ ๋ฒ ํธ์ถ๋๋ฉฐ,__call__์ ๊ฐ ์์ฒญ๋ง๋ค ํธ์ถ๋ฉ๋๋ค.
- ํด๋์ค ๊ธฐ๋ฐ ๋ฏธ๋ค์จ์ด๋
class MyNewMiddleware:
def __init__(self, get_response):
self.get_response = get_response
# ์๋ฒ ์์ ์ ์ด๊ธฐํ ์ฝ๋ (ํ ๋ฒ ์คํ)
def __call__(self, request):
# ์์ฒญ ์ฒ๋ฆฌ ์ ์ฝ๋ (๋ทฐ ํธ์ถ ์ )
# ์: request.user = ...
response = self.get_response(request) # ๋ค์ ๋ฏธ๋ค์จ์ด๋ ๋ทฐ ํธ์ถ
# ์๋ต ์ฒ๋ฆฌ ํ ์ฝ๋ (๋ทฐ ๋ฐํ ํ)
# ์: response['X-My-Header'] = 'value'
return response
์ฃผ์ ๋ด์ฅ ๋ฏธ๋ค์จ์ดโ
django.middleware.security.SecurityMiddleware: ๋ณด์ ๊ด๋ จ ์ฌ๋ฌ ์ค์ ์ ์ฒ๋ฆฌํฉ๋๋ค. (์: X-Content-Type-Options, X-XSS-Protection, Strict-Transport-Security ํค๋ ์ค์ ,SECURE_SSL_REDIRECT๋ฑ)django.contrib.sessions.middleware.SessionMiddleware: ์ธ์ ๊ด๋ฆฌ๋ฅผ ํ์ฑํํฉ๋๋ค.django.middleware.common.CommonMiddleware:APPEND_SLASH,PREPEND_WWW์ฒ๋ฆฌ, Content-Length ํค๋ ์ค์ ๋ฑ ์ผ๋ฐ์ ์ธ ์น ์์ฒญ/์๋ต ํจํด์ ์ฒ๋ฆฌํฉ๋๋ค.django.middleware.csrf.CsrfViewMiddleware: CSRF ๊ณต๊ฒฉ ๋ฐฉ์ด๋ฅผ ์ํ ํ ํฐ์ ๊ฒ์ฆํ๊ณ ์์ฑํฉ๋๋ค.django.contrib.auth.middleware.AuthenticationMiddleware:request๊ฐ์ฒด์user์์ฑ์ ์ถ๊ฐํ์ฌ ํ์ฌ ๋ก๊ทธ์ธํ ์ฌ์ฉ์๋ฅผ ๋ํ๋ ๋๋ค.django.contrib.messages.middleware.MessageMiddleware: ์ผํ์ฑ ๋ฉ์์ง(์: ์ฑ๊ณต, ์ค๋ฅ ์๋ฆผ)๋ฅผ ์ฒ๋ฆฌํฉ๋๋ค.
์ปค์คํ ๋ฏธ๋ค์จ์ด ์์ฑโ
์ ํ๋ฆฌ์ผ์ด์ ์ ๋ฐ์ ๊ฑธ์ณ ์ ์ฉํ๊ณ ์ถ์ ๋ก์ง์ด ์๋ค๋ฉด ์ปค์คํ ๋ฏธ๋ค์จ์ด๋ฅผ ์์ฑํ ์ ์์ต๋๋ค.
์์: ์์ฒญ ์ฒ๋ฆฌ ์๊ฐ ์ธก์ ๋ฏธ๋ค์จ์ด
# myapp/middleware.py
import time
class TimingMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
start_time = time.time()
response = self.get_response(request)
duration = time.time() - start_time
response['X-Page-Generation-Duration-ms'] = str(int(duration * 1000))
print(f"Request to {request.path} took {duration:.4f} seconds")
return response
# settings.py
MIDDLEWARE = [
# ... ๊ธฐ์กด ๋ฏธ๋ค์จ์ด ...
'myapp.middleware.TimingMiddleware', # ์ถ๊ฐ
# ... ๊ธฐ์กด ๋ฏธ๋ค์จ์ด ...
]
๋ฏธ๋ค์จ์ด ํ (์ ํ์ ๋ฉ์๋ - ๊ตฌํ ์คํ์ผ):
process_view(request, view_func, view_args, view_kwargs): ๋ทฐ๊ฐ ํธ์ถ๋๊ธฐ ์ง์ ์ ํธ์ถ๋ฉ๋๋ค.None์ ๋ฐํํ๋ฉด ๋ทฐ ์ฒ๋ฆฌ๊ฐ ๊ณ์๋๊ณ ,HttpResponse๋ฅผ ๋ฐํํ๋ฉด ํด๋น ์๋ต์ด ์ฌ์ฉ๋ฉ๋๋ค.process_template_response(request, response): ๋ทฐ๊ฐ ํ ํ๋ฆฟ ๋ ๋๋ง์ ํฌํจํ๋TemplateResponse(๋๋ ์ ์ฌ ๊ฐ์ฒด)๋ฅผ ๋ฐํํ์ ๋ ํธ์ถ๋ฉ๋๋ค.response.render()๊ฐ ํธ์ถ๋๊ธฐ ์ ์ ์๋ต์ ๋ณ๊ฒฝํ ์ ์์ต๋๋ค.