用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/SAM42-Lab/everything-claude-code-kr --skill django-patterns命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
캡처, 진단, 억제된 복구 및 인트로스펙션 보고서를 사용하여 AI 에이전트 실패에 대한 구조화된 셀프 디버깅 워크플로우를 수행합니다.
기술, 명령어, 규칙, 훅 및 기타 요소를 DAILY 대 LIBRARY 버킷으로 분류하여 특정 저장소에 대한 증거 기반의 ECC 설치 계획을 수립합니다. 전체 번들을 로드하는 대신 프로젝트에 실제로 필요한 것만 ECC를 트리밍해야 할 때 사용하세요.
리소스 명명, 상태 코드, 페이지네이션, 필터링, 에러 응답, 버전 관리 및 프로덕션 API를 위한 속도 제한을 포함한 REST API 설계 패턴.
正在显示 SKILL.md
基于 SOC 职业分类
| name | django-patterns |
| description | Django 아키텍처 패턴, DRF를 이용한 REST API 설계, ORM 모범 사례, 캐싱, 시그널, 미들웨어 및 프로덕션급 Django 앱 구축 가이드입니다. |
| origin | ECC |
확장 가능하고 유지 관리가 쉬운 애플리케이션을 위한 프로덕션급 Django 아키텍처 패턴입니다.
myproject/
├── config/
│ ├── __init__.py
│ ├── settings/
│ │ ├── __init__.py
│ │ ├── base.py # 기본 설정
│ │ ├── development.py # 개발용 설정
│ │ ├── production.py # 프로덕션용 설정
│ │ └── test.py # 테스트용 설정
│ ├── urls.py
│ ├── wsgi.py
│ └── asgi.py
├── manage.py
└── apps/
├── __init__.py
├── users/
│ ├── __init__.py
│ ├── models.py
│ ├── views.py
│ ├── serializers.py
│ ├── urls.py
│ ├── permissions.py
│ ├── filters.py
│ ├── services.py
│ └── tests/
└── products/
└── ...
# config/settings/base.py
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent.parent.parent
SECRET_KEY = env('DJANGO_SECRET_KEY')
DEBUG = False
ALLOWED_HOSTS = []
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'rest_framework.authtoken',
'corsheaders',
# 로컬 앱
'apps.users',
'apps.products',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'whitenoise.middleware.WhiteNoiseMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'config.urls'
WSGI_APPLICATION = 'config.wsgi.application'
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': env('DB_NAME'),
'USER': env('DB_USER'),
'PASSWORD': env('DB_PASSWORD'),
'HOST': env('DB_HOST'),
'PORT': env('DB_PORT', default='5432'),
}
}
# config/settings/development.py
from .base import *
DEBUG = True
ALLOWED_HOSTS = ['localhost', '127.0.0.1']
DATABASES['default']['NAME'] = 'myproject_dev'
INSTALLED_APPS += ['debug_toolbar']
MIDDLEWARE += ['debug_toolbar.middleware.DebugToolbarMiddleware']
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
# config/settings/production.py
from .base import *
DEBUG = False
ALLOWED_HOSTS = env.list('ALLOWED_HOSTS')
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_HSTS_SECONDS = 31536000
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True
# 로깅 설정
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'file': {
'level': 'WARNING',
'class': 'logging.FileHandler',
'filename': '/var/log/django/django.log',
},
},
'loggers': {
'django': {
'handlers': ['file'],
'level': 'WARNING',
'propagate': True,
},
},
}
from django.db import models
from django.contrib.auth.models import AbstractUser
from django.core.validators import MinValueValidator, MaxValueValidator
class User(AbstractUser):
"""AbstractUser를 확장한 커스텀 사용자 모델."""
email = models.EmailField(unique=True)
phone = models.CharField(max_length=20, blank=True)
birth_date = models.DateField(null=True, blank=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username']
class Meta:
db_table = 'users'
verbose_name = 'user'
verbose_name_plural = 'users'
ordering = ['-date_joined']
def __str__(self):
return self.email
def get_full_name(self):
return f"{self.first_name} {self.last_name}".strip()
class Product(models.Model):
"""적절한 필드 구성을 갖춘 제품 모델."""
name = models.CharField(max_length=200)
slug = models.SlugField(unique=True, max_length=250)
description = models.TextField(blank=True)
price = models.DecimalField(
max_digits=10,
decimal_places=2,
validators=[MinValueValidator()]
)
stock = models.PositiveIntegerField(default=)
is_active = models.BooleanField(default=)
category = models.ForeignKey(
,
on_delete=models.CASCADE,
related_name=
)
tags = models.ManyToManyField(, blank=, related_name=)
created_at = models.DateTimeField(auto_now_add=)
updated_at = models.DateTimeField(auto_now=)
:
db_table =
ordering = []
indexes = [
models.Index(fields=[]),
models.Index(fields=[]),
models.Index(fields=[, ]),
]
constraints = [
models.CheckConstraint(
check=models.Q(price__gte=),
name=
)
]
():
.name
():
.slug:
.slug = slugify(.name)
().save(*args, **kwargs)
from django.db import models
class ProductQuerySet(models.QuerySet):
"""Product 모델을 위한 커스텀 QuerySet."""
def active(self):
"""활성화된 제품만 반환."""
return self.filter(is_active=True)
def with_category(self):
"""N+1 쿼리 방지를 위해 관련 카테고리 선택(select_related)."""
return self.select_related('category')
def with_tags(self):
"""다대다 관계를 위한 태그 미리 가져오기(prefetch_related)."""
return self.prefetch_related('tags')
def in_stock(self):
"""재고가 0보다 큰 제품만 반환."""
return self.filter(stock__gt=0)
def search(self, query):
"""이름 또는 설명으로 제품 검색."""
return self.filter(
models.Q(name__icontains=query) |
models.Q(description__icontains=query)
)
class Product(models.Model):
# ... 필드 정의 ...
objects = ProductQuerySet.as_manager() # 커스텀 QuerySet 사용
Product.objects.active().with_category().in_stock()
class ProductManager(models.Manager):
"""복잡한 쿼리를 위한 커스텀 매니저."""
def get_or_none(self, **kwargs):
"""DoesNotExist 대신 객체 또는 None을 반환."""
try:
return self.get(**kwargs)
except self.model.DoesNotExist:
return None
def create_with_tags(self, name, price, tag_names):
"""연관된 태그와 함께 제품 생성."""
product = self.create(name=name, price=price)
tags = [Tag.objects.get_or_create(name=name)[0] for name in tag_names]
product.tags.set(tags)
return product
def bulk_update_stock(self, product_ids, quantity):
"""여러 제품의 재고를 한꺼번에 업데이트."""
return self.filter(id__in=product_ids).update(stock=quantity)
# 모델에서 설정
class Product(models.Model):
# ... 필드 정의 ...
custom = ProductManager()
from rest_framework import serializers
from django.contrib.auth.password_validation import validate_password
from .models import Product, User
class ProductSerializer(serializers.ModelSerializer):
"""Product 모델을 위한 시리얼라이저."""
category_name = serializers.CharField(source='category.name', read_only=True)
average_rating = serializers.FloatField(read_only=True)
discount_price = serializers.SerializerMethodField()
class Meta:
model = Product
fields = [
'id', 'name', 'slug', 'description', 'price',
'discount_price', 'stock', 'category_name',
'average_rating', 'created_at'
]
read_only_fields = ['id', 'slug', 'created_at']
def get_discount_price(self, obj):
"""할인 가격 계산(해당하는 경우)."""
if hasattr(obj, 'discount') and obj.discount:
return obj.price * (1 - obj.discount.percent / 100)
return obj.price
def validate_price(self, value):
"""가격이 음수가 아닌지 확인."""
if value < 0:
serializers.ValidationError()
value
(serializers.ModelSerializer):
:
model = Product
fields = [, , , , ]
():
data[] > data[] > :
serializers.ValidationError(
)
data
(serializers.ModelSerializer):
password = serializers.CharField(
write_only=,
required=,
validators=[validate_password],
style={: }
)
password_confirm = serializers.CharField(write_only=, style={: })
:
model = User
fields = [, , , ]
():
data[] != data[]:
serializers.ValidationError({
:
})
data
():
validated_data.pop()
password = validated_data.pop()
user = User.objects.create(**validated_data)
user.set_password(password)
user.save()
user
from rest_framework import viewsets, status, filters
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated, IsAdminUser
from django_filters.rest_framework import DjangoFilterBackend
from .models import Product
from .serializers import ProductSerializer, ProductCreateSerializer
from .permissions import IsOwnerOrReadOnly
from .filters import ProductFilter
from .services import ProductService
class ProductViewSet(viewsets.ModelViewSet):
"""Product 모델을 위한 뷰셋."""
queryset = Product.objects.select_related('category').prefetch_related('tags')
permission_classes = [IsAuthenticated, IsOwnerOrReadOnly]
filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
filterset_class = ProductFilter
search_fields = ['name', 'description']
ordering_fields = ['price', 'created_at', 'name']
ordering = ['-created_at']
def get_serializer_class(self):
"""액션에 따라 적절한 시리얼라이저 반환."""
if self.action == 'create':
return ProductCreateSerializer
return ProductSerializer
def perform_create():
serializer.save(created_by=.request.user)
():
featured = .queryset.(is_featured=)[:]
serializer = .get_serializer(featured, many=)
Response(serializer.data)
():
product = .get_object()
service = ProductService()
result = service.purchase(product, request.user)
Response(result, status=status.HTTP_201_CREATED)
():
products = .queryset.(created_by=request.user)
page = .paginate_queryset(products)
serializer = .get_serializer(page, many=)
.get_paginated_response(serializer.data)
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
@api_view(['POST'])
@permission_classes([IsAuthenticated])
def add_to_cart(request):
"""장바구니에 제품 추가."""
product_id = request.data.get('product_id')
quantity = request.data.get('quantity', 1)
try:
product = Product.objects.get(id=product_id)
except Product.DoesNotExist:
return Response(
{'error': '제품을 찾을 수 없습니다.'},
status=status.HTTP_404_NOT_FOUND
)
cart, _ = Cart.objects.get_or_create(user=request.user)
CartItem.objects.create(
cart=cart,
product=product,
quantity=quantity
)
return Response({'message': '장바구니에 추가되었습니다.'}, status=status.HTTP_201_CREATED)
# apps/orders/services.py
from typing import Optional
from django.db import transaction
from .models import Order, OrderItem
class OrderService:
"""주문 관련 비즈니스 로직을 위한 서비스 레이어."""
@staticmethod
@transaction.atomic
def create_order(user, cart: Cart) -> Order:
"""장바구니로부터 주문 생성."""
order = Order.objects.create(
user=user,
total_price=cart.total_price
)
for item in cart.items.all():
OrderItem.objects.create(
order=order,
product=item.product,
quantity=item.quantity,
price=item.product.price
)
# 장바구니 비우기
cart.items.all().delete()
return order
@staticmethod
def process_payment(order: Order, payment_data: dict) -> bool:
"""주문 결제 처리."""
# 결제 게이트웨이 연동
payment = PaymentGateway.charge(
amount=order.total_price,
token=payment_data['token']
)
if payment.success:
order.status = Order.Status.PAID
order.save()
# 확인 이메일 발송
OrderService.send_confirmation_email(order)
return True
return False
@staticmethod
def ():
from django.views.decorators.cache import cache_page
from django.utils.decorators import method_decorator
@method_decorator(cache_page(60 * 15), name='dispatch') # 15분
class ProductListView(generic.ListView):
model = Product
template_name = 'products/list.html'
context_object_name = 'products'
{% load cache %}
{% cache 500 sidebar %}
... 비용이 많이 드는 사이드바 콘텐츠 ...
{% endcache %}
from django.core.cache import cache
def get_featured_products():
"""추천 제품 정보를 캐싱과 함께 가져오기."""
cache_key = 'featured_products'
products = cache.get(cache_key)
if products is None:
products = list(Product.objects.filter(is_featured=True))
cache.set(cache_key, products, timeout=60 * 15) # 15분
return products
from django.core.cache import cache
def get_popular_categories():
cache_key = 'popular_categories'
categories = cache.get(cache_key)
if categories is None:
categories = list(Category.objects.annotate(
product_count=Count('products')
).filter(product_count__gt=10).order_by('-product_count')[:20])
cache.set(cache_key, categories, timeout=60 * 60) # 1시간
return categories
# apps/users/signals.py
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.contrib.auth import get_user_model
from .models import Profile
User = get_user_model()
@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
"""사용자 생성 시 프로필 생성."""
if created:
Profile.objects.create(user=instance)
@receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
"""사용자 저장 시 프로필 저장."""
instance.profile.save()
# apps/users/apps.py
from django.apps import AppConfig
class UsersConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'apps.users'
def ready(self):
"""앱이 준비되었을 때 시그널 임포트."""
import apps.users.signals
# middleware/active_user_middleware.py
import time
from django.utils.deprecation import MiddlewareMixin
class ActiveUserMiddleware(MiddlewareMixin):
"""활성 사용자를 추적하기 위한 미들웨어."""
def process_request(self, request):
"""들어오는 요청 처리."""
if request.user.is_authenticated:
# 마지막 활성 시간 업데이트
request.user.last_active = timezone.now()
request.user.save(update_fields=['last_active'])
class RequestLoggingMiddleware(MiddlewareMixin):
"""요청 로깅을 위한 미들웨어."""
def process_request(self, request):
"""요청 시작 시간 기록."""
request.start_time = time.time()
def process_response(self, request, response):
"""요청 소요 시간 기록."""
if hasattr(request, 'start_time'):
duration = time.time() - request.start_time
logger.info(f'{request.method} {request.path} - {response.status_code} - {duration:.3f}s')
return response
# 나쁨 - N+1 쿼리 발생
products = Product.objects.all()
for product in products:
print(product.category.name) # 각 제품마다 별도의 쿼리 발생
# 좋음 - select_related를 사용한 단일 쿼리
products = Product.objects.select_related('category').all()
for product in products:
print(product.category.name)
# 좋음 - 다대다 관계를 위한 prefetch_related
products = Product.objects.prefetch_related('tags').all()
for product in products:
for tag in product.tags.all():
print(tag.name)
class Product(models.Model):
name = models.CharField(max_length=200, db_index=True)
slug = models.SlugField(unique=True)
category = models.ForeignKey('Category', on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
indexes = [
models.Index(fields=['name']),
models.Index(fields=['-created_at']),
models.Index(fields=['category', 'created_at']),
]
# 대량 생성 (Bulk create)
Product.objects.bulk_create([
Product(name=f'Product {i}', price=10.00)
for i in range(1000)
])
# 대량 업데이트 (Bulk update)
products = Product.objects.all()[:100]
for product in products:
product.is_active = True
Product.objects.bulk_update(products, ['is_active'])
# 대량 삭제 (Bulk delete)
Product.objects.filter(stock=0).delete()
| 패턴 | 설명 |
|---|---|
| 설정 분리 | 개발/프로덕션/테스트 환경별 설정 관리 |
| 커스텀 QuerySet | 재사용 가능한 쿼리 메서드 정의 |
| 서비스 레이어 | 비즈니스 로직의 분리 및 캡슐화 |
| ViewSet | REST API 엔드포인트 구현 |
| 시리얼라이저 검증 | 요청/응답 데이터 변환 및 유효성 검사 |
| select_related | 외래 키 참조 최적화 |
| prefetch_related | 다대다 관계 참조 최적화 |
| 캐시 우선(Cache first) | 비용이 많이 드는 작업의 결과 캐싱 |
| 시그널(Signals) | 이벤트 중심의 액션 처리 |
| 미들웨어(Middleware) | 요청/응답 전처리 및 후처리 |
기억하세요: Django는 많은 지름길을 제공하지만, 프로덕션 애플리케이션에서는 간결한 코드보다 구조와 조직화가 더 중요합니다. 유지 관리를 고려하여 설계하세요.