Django, 저는 이렇게 씁니다. 
알파카코믹스를 개발하면서.
안녕하세요. 풀 스택 개발자 파이입니다. 
진짜로 서버 조립에서 디자인까지 해봄 
스마트스터디에서 일하고 있습니다.
Django로 먹고 살아온 세월 5년 
5년이면 많이 알겠네요?! 
해본 것도 기억 못해요…
발표 왜 하세요? 
일 만하다 보니 제가 뭐하고 있나 싶었어요. 
기억력이 안 좋아서 방금 짠 코드도 까먹습니다. 
잊기 전에 뭐라도 남기고 싶었습니다. 
아는 것도 별로 없지만 막상 적으려면 더 없어요. OTL 
해본 거 
탐험이 필요한 
미지의 영역 
아는 거 
발표
Django 왜 쓰나요? 
할 일은 많고 사람은 없을 때. 
시간도 없을 때. 
귀찮을 때.
알파카코믹스를 개발하면서 
만들면서 겪은 문제들을 
어떻게 해결(땜빵)했는지 
간략하게 이야기해보겠습니다.
프로젝트를 시작할 때 
신경 쓰는 게 신상에 이롭습니다.
모델이 제일 중요합니다. 
모든 것의 근원, 데이터의 흐름 
가장 오랫동안 고민해도 부족하지 않습니다. 
많은 연습을 필요로 합니다.
모듈을 용도 별로 나누세요. 
천 줄 짜리 코드를 나중에 다시 열면 
내가 짠 거 알면서도 욕이 나옵니다. 
모델에 기초해서 어떻게 하든 나누세요.
예) 프로젝트 레이아웃 
/alpaca 
/comic 
/profile 
/... 
/conf 
/production 
/settings.py 
/testing 
앱 내부 
배포 환경에 따른 세팅
Python 가상 환경은 선택이 아닌 필수 
pyenv 
https://github.com/yyuu/pyenv 
virtualenv wrapper 
https://pypi.python.org/pypi/virtualenvwrapper
Front-end는 Bower로 관리 
bower.json 
{ 
"name": "alpacacomics", 
"dependencies": { 
"jquery": "1.9.1", 
….. 
} 
} 
.bowerrc 
{"directory":"alpaca/static/components"} 
$ bower update
User에 cash 넣을래요.
Profile Model과 OneToOneField 
profile/models.py 
class Profile(models.Model): 
user = models.OneToOneField(settings.AUTH_USER_MODEL) 
cash = models.IntegerField(defalut=0) 
user.profile.cash 로 접근 
http://perhapsspy.wordpress.com/2013/02/18/a-simple-way-how-to-extend-user-model-in-django-1-5/
Profile 모델에 관련 함수 모으기 
캐시 충전 
캐시 환불 
스토어 별 캐시 
ex) user.profile.cash_charge(...)
장점 
다른 라이브러리와 충돌 걱정 없다. 
관리도 쉽다. 
구현이 제일 쉽다. 
2가지 방법이 더 있으나 
쉬운 게 최고…..는 아니고 상황에 맞게 찾아 쓰세요.
소셜 로그인이 필요해요.
Django-allauth 
이름처럼 모든 것을 해줌. 
일반 가입, 소셜 로그인 및 관리, 
이메일 인증, 비밀번호 찾기, 등. 
단, 한글은 직접 .po 파일을 만들어야...(or template) 
잘 쓰려면 adepter도 고치고...
template을 적당히 고쳐서 쓰면 그럴 듯합니다.
스크롤을 내리면 이어지게 
ajax 지옥...
HTML을 조각조각 따따따 
template_name = ‘item_list.html’ 
if request.is_ajax(): 
template_name = ‘_item.html’ 
javascript는 최대한 단순하게. 
하지만 아무래도 귀찮다. 맘에 들게 널 다시 조립할거야
CBV로 한번 만들고 계속 쓰자. 
반복 작업을 싫어하시는 당신을 위한 
CBV - Class Based View 
만들어둔 Class를 조립해서 쓴다. 
https://docs.djangoproject.com/en/1.7/topics/class-based-views/
Mixin을 써보자 
class ChangeTemplateMixin(object): 
# ajax 요청이 들어오면 template 변경 
class MoreListMixin(ChangeTemplateMixin): 
# 무한 스크롤 구현 
class CashUseLogView(MoreListMixin, ListView): 
# 위의 믹스인을 합쳐서 날로 먹기 
class CashChargeLogView(MoreListMixin, ListView): 
# 계속해서 날로 먹기
이렇게 됩니다.
RESTful API가 필요한데요.
Django REST framework 
기능이 엄청 많습니다. 
CBV로 API를 만들 수 있습니다. 
즉, 모델만 잘 짜두면 순식간에 만듭니다. 
단순한 API는 1~2시간이면 뚝딱. 
하지만 속도가 느리다는 게 함정. 
자세한 설명은 문서 참고하세요. 최근에 3.0까지 나왔습니다. 
http://www.django-rest-framework.org/
사이트가 느려요. 
DB와 Cache로 해결해봅시다.
DB 쿼리 최적화 
두 가지만 기억하세요. 
222쿼리가 2쿼리로 줄어드는 마법이 벌어집니다. 
select_related - Foreign-key 관계 
prefetch_related - Many to Many 관계 
여러 쿼리를 join으로 합쳐줍니다.
예) DB 쿼리 최적화 
Comic.objects.all().select_related(‘author’)  
.prefetch_related(‘tags’) 
Product.objects.get(id=1).select_related( 
‘episode’, ‘episode__comic’)
CBV는 어떻게 쿼리 최적화 하나요. 
class CashUseLogView(MoreListMixin, UserFilterMixin, ListView): 
model = CashUseLog 
template_name = 'cash/use_log.html' 
ajax_template_name = 'cash/_use_log.html' 
def get_queryset(self): 
return super(CashUseLogView, self).get_queryset()  
.select_related('product', 'product__episode__comic', 'product__episode') 
class ComicDetail(MoreListMixin, ProductsMixin, SingleObjectMixin, ListView): 
def get(self, request, *args, **kwargs): 
queryset = Comic.objects.all()  
.select_related('author', 'category')  
.prefetch_related('tags') 
self.object = self.get_object(queryset=queryset) 
return super(ComicDetail, self).get(request, *args, **kwargs) 
이렇게
Cache를 바릅니다. 
View, Template 등을 상황에 맞춰 적당히(!?!) 
Cache의 핵심 
어느 타이밍에 갱신할 것인가?
데이터가 변하면 갱신하자. 
class Comic(models.Model): 
… 
def save(....): 
cash_update()
이것도 Class로 만들어 두고 조립. 
class Comic(CacheDeleteModel, models.Model): 
… 
def save(....): 
cash_update()
Django-compressor 
staic 파일들을 압축해준다. 
STATICFILES_FINDERS = ( 
... 
"compressor.finders.CompressorFinder", 
) 
COMPRESS_OFFLINE = True 
COMPRESS_CSS_FILTERS = {'compressor.filters.cssmin.CSSMinFilter',} 
COMPRESS_OFFLINE_CONTEXT = {'STATIC_URL': "/static/",} 
$ python manage.py compress 
https://github.com/django-compressor/django-compressor
Admin 페이지 어떻게 써요?
예) admin.py 
def release_display(instance): 
if instance.release: 
return instance.release.strftime('%y.%m.%d %H:%M') 
return instance.release 
release_display.admin_order_field = 'release' 
release_display.short_description = u'출시일시' 
class ComicAdmin(admin.ModelAdmin): 
list_display = ['id', 'category', 'name', 'author', 'publisher', 'rating', 
'view_count', 'purchase_count', 
'active', 'end', release_display, 'recommended', 'weight', 'last_weight', 
'login_required_episode', 'episode_count'] 
list_filter = ['active', 'category', 'rating', 'recommended', 'service_ext'] 
list_editable = ['recommended', 'weight', 'last_weight', 'login_required_episode'] 
raw_id_fields = ['author', 'publisher', 'tags'] 
readonly_fields = ['view_count', 'purchase_count', 'episode_count'] 
def get_queryset(self, request): 
return super(ComicAdmin, self).queryset(request)  
.select_related('category', 'author', 'publisher') 
admin.site.register(Comic, ComicAdmin) 
쿼리 최적화
예) Admin 화면 
아래와 같이 대충 쓸 만해집니다.
기타 다른 Admin 설정 
list_display_links : 목록에서 누를 수 있는 링크가 되는 필드 
list_per_page : 목록의 아이템 수 
date_hierarchy : 지정한 날짜 필드 기준 필터 생성 
ordering : 순서 
search_fields : 검색 대상 필드
배포는?
제가 자주 씁니다. 
OS : Ubuntu 
Web Server : Nginx 
App Server : uWSGI 
Django Version : 1.7+ 
Database : Mysql or Postgresql 
설정하기 쉽다는 공통점이 있습니다.
간단한 스크립트. 
git pull 
cp nginx.conf /etc/nginx/site-enabled/app 
pip install -r requirements.txt 
bower update 
python manage.py collectstatic 
service nginx reload 
service uwsgi reload
사실 Docker 쓰고 있어요. 
방금 전 스크립트는 Dockerfile 흉내. 
Docker registry (회사 전용) 
Gadget (배포 도구)
요약하겠습니다. 
힘들어서 여기까지만.
그러니까, 
● 모델이 제일 중요합니다. 
● User 모델 확장은 OneToOne 
● 소셜 로그인은 Django-allauth 
● 반복 작업은 CBV 
● DB최적화는 select_related, prefetch_related 
● Cache는 컨트롤이 중요 
● Admin는 django 핵심 기능입니다.
질문 받습니다. 
더 많은 것을 담아보려다 
체력이 고갈 되어 질문으로 넘어갑니다.
감사합니다. 
끝.

Django, 저는 이렇게 씁니다.

  • 1.
    Django, 저는 이렇게씁니다. 알파카코믹스를 개발하면서.
  • 2.
    안녕하세요. 풀 스택개발자 파이입니다. 진짜로 서버 조립에서 디자인까지 해봄 스마트스터디에서 일하고 있습니다.
  • 3.
    Django로 먹고 살아온세월 5년 5년이면 많이 알겠네요?! 해본 것도 기억 못해요…
  • 4.
    발표 왜 하세요? 일 만하다 보니 제가 뭐하고 있나 싶었어요. 기억력이 안 좋아서 방금 짠 코드도 까먹습니다. 잊기 전에 뭐라도 남기고 싶었습니다. 아는 것도 별로 없지만 막상 적으려면 더 없어요. OTL 해본 거 탐험이 필요한 미지의 영역 아는 거 발표
  • 5.
    Django 왜 쓰나요? 할 일은 많고 사람은 없을 때. 시간도 없을 때. 귀찮을 때.
  • 6.
    알파카코믹스를 개발하면서 만들면서겪은 문제들을 어떻게 해결(땜빵)했는지 간략하게 이야기해보겠습니다.
  • 7.
    프로젝트를 시작할 때 신경 쓰는 게 신상에 이롭습니다.
  • 8.
    모델이 제일 중요합니다. 모든 것의 근원, 데이터의 흐름 가장 오랫동안 고민해도 부족하지 않습니다. 많은 연습을 필요로 합니다.
  • 9.
    모듈을 용도 별로나누세요. 천 줄 짜리 코드를 나중에 다시 열면 내가 짠 거 알면서도 욕이 나옵니다. 모델에 기초해서 어떻게 하든 나누세요.
  • 10.
    예) 프로젝트 레이아웃 /alpaca /comic /profile /... /conf /production /settings.py /testing 앱 내부 배포 환경에 따른 세팅
  • 11.
    Python 가상 환경은선택이 아닌 필수 pyenv https://github.com/yyuu/pyenv virtualenv wrapper https://pypi.python.org/pypi/virtualenvwrapper
  • 12.
    Front-end는 Bower로 관리 bower.json { "name": "alpacacomics", "dependencies": { "jquery": "1.9.1", ….. } } .bowerrc {"directory":"alpaca/static/components"} $ bower update
  • 13.
  • 14.
    Profile Model과 OneToOneField profile/models.py class Profile(models.Model): user = models.OneToOneField(settings.AUTH_USER_MODEL) cash = models.IntegerField(defalut=0) user.profile.cash 로 접근 http://perhapsspy.wordpress.com/2013/02/18/a-simple-way-how-to-extend-user-model-in-django-1-5/
  • 15.
    Profile 모델에 관련함수 모으기 캐시 충전 캐시 환불 스토어 별 캐시 ex) user.profile.cash_charge(...)
  • 16.
    장점 다른 라이브러리와충돌 걱정 없다. 관리도 쉽다. 구현이 제일 쉽다. 2가지 방법이 더 있으나 쉬운 게 최고…..는 아니고 상황에 맞게 찾아 쓰세요.
  • 17.
  • 18.
    Django-allauth 이름처럼 모든것을 해줌. 일반 가입, 소셜 로그인 및 관리, 이메일 인증, 비밀번호 찾기, 등. 단, 한글은 직접 .po 파일을 만들어야...(or template) 잘 쓰려면 adepter도 고치고...
  • 19.
    template을 적당히 고쳐서쓰면 그럴 듯합니다.
  • 20.
  • 21.
    HTML을 조각조각 따따따 template_name = ‘item_list.html’ if request.is_ajax(): template_name = ‘_item.html’ javascript는 최대한 단순하게. 하지만 아무래도 귀찮다. 맘에 들게 널 다시 조립할거야
  • 22.
    CBV로 한번 만들고계속 쓰자. 반복 작업을 싫어하시는 당신을 위한 CBV - Class Based View 만들어둔 Class를 조립해서 쓴다. https://docs.djangoproject.com/en/1.7/topics/class-based-views/
  • 23.
    Mixin을 써보자 classChangeTemplateMixin(object): # ajax 요청이 들어오면 template 변경 class MoreListMixin(ChangeTemplateMixin): # 무한 스크롤 구현 class CashUseLogView(MoreListMixin, ListView): # 위의 믹스인을 합쳐서 날로 먹기 class CashChargeLogView(MoreListMixin, ListView): # 계속해서 날로 먹기
  • 24.
  • 25.
  • 26.
    Django REST framework 기능이 엄청 많습니다. CBV로 API를 만들 수 있습니다. 즉, 모델만 잘 짜두면 순식간에 만듭니다. 단순한 API는 1~2시간이면 뚝딱. 하지만 속도가 느리다는 게 함정. 자세한 설명은 문서 참고하세요. 최근에 3.0까지 나왔습니다. http://www.django-rest-framework.org/
  • 27.
    사이트가 느려요. DB와Cache로 해결해봅시다.
  • 28.
    DB 쿼리 최적화 두 가지만 기억하세요. 222쿼리가 2쿼리로 줄어드는 마법이 벌어집니다. select_related - Foreign-key 관계 prefetch_related - Many to Many 관계 여러 쿼리를 join으로 합쳐줍니다.
  • 29.
    예) DB 쿼리최적화 Comic.objects.all().select_related(‘author’) .prefetch_related(‘tags’) Product.objects.get(id=1).select_related( ‘episode’, ‘episode__comic’)
  • 30.
    CBV는 어떻게 쿼리최적화 하나요. class CashUseLogView(MoreListMixin, UserFilterMixin, ListView): model = CashUseLog template_name = 'cash/use_log.html' ajax_template_name = 'cash/_use_log.html' def get_queryset(self): return super(CashUseLogView, self).get_queryset() .select_related('product', 'product__episode__comic', 'product__episode') class ComicDetail(MoreListMixin, ProductsMixin, SingleObjectMixin, ListView): def get(self, request, *args, **kwargs): queryset = Comic.objects.all() .select_related('author', 'category') .prefetch_related('tags') self.object = self.get_object(queryset=queryset) return super(ComicDetail, self).get(request, *args, **kwargs) 이렇게
  • 31.
    Cache를 바릅니다. View,Template 등을 상황에 맞춰 적당히(!?!) Cache의 핵심 어느 타이밍에 갱신할 것인가?
  • 32.
    데이터가 변하면 갱신하자. class Comic(models.Model): … def save(....): cash_update()
  • 33.
    이것도 Class로 만들어두고 조립. class Comic(CacheDeleteModel, models.Model): … def save(....): cash_update()
  • 34.
    Django-compressor staic 파일들을압축해준다. STATICFILES_FINDERS = ( ... "compressor.finders.CompressorFinder", ) COMPRESS_OFFLINE = True COMPRESS_CSS_FILTERS = {'compressor.filters.cssmin.CSSMinFilter',} COMPRESS_OFFLINE_CONTEXT = {'STATIC_URL': "/static/",} $ python manage.py compress https://github.com/django-compressor/django-compressor
  • 35.
  • 36.
    예) admin.py defrelease_display(instance): if instance.release: return instance.release.strftime('%y.%m.%d %H:%M') return instance.release release_display.admin_order_field = 'release' release_display.short_description = u'출시일시' class ComicAdmin(admin.ModelAdmin): list_display = ['id', 'category', 'name', 'author', 'publisher', 'rating', 'view_count', 'purchase_count', 'active', 'end', release_display, 'recommended', 'weight', 'last_weight', 'login_required_episode', 'episode_count'] list_filter = ['active', 'category', 'rating', 'recommended', 'service_ext'] list_editable = ['recommended', 'weight', 'last_weight', 'login_required_episode'] raw_id_fields = ['author', 'publisher', 'tags'] readonly_fields = ['view_count', 'purchase_count', 'episode_count'] def get_queryset(self, request): return super(ComicAdmin, self).queryset(request) .select_related('category', 'author', 'publisher') admin.site.register(Comic, ComicAdmin) 쿼리 최적화
  • 37.
    예) Admin 화면 아래와 같이 대충 쓸 만해집니다.
  • 38.
    기타 다른 Admin설정 list_display_links : 목록에서 누를 수 있는 링크가 되는 필드 list_per_page : 목록의 아이템 수 date_hierarchy : 지정한 날짜 필드 기준 필터 생성 ordering : 순서 search_fields : 검색 대상 필드
  • 39.
  • 40.
    제가 자주 씁니다. OS : Ubuntu Web Server : Nginx App Server : uWSGI Django Version : 1.7+ Database : Mysql or Postgresql 설정하기 쉽다는 공통점이 있습니다.
  • 41.
    간단한 스크립트. gitpull cp nginx.conf /etc/nginx/site-enabled/app pip install -r requirements.txt bower update python manage.py collectstatic service nginx reload service uwsgi reload
  • 42.
    사실 Docker 쓰고있어요. 방금 전 스크립트는 Dockerfile 흉내. Docker registry (회사 전용) Gadget (배포 도구)
  • 43.
  • 44.
    그러니까, ● 모델이제일 중요합니다. ● User 모델 확장은 OneToOne ● 소셜 로그인은 Django-allauth ● 반복 작업은 CBV ● DB최적화는 select_related, prefetch_related ● Cache는 컨트롤이 중요 ● Admin는 django 핵심 기능입니다.
  • 45.
    질문 받습니다. 더많은 것을 담아보려다 체력이 고갈 되어 질문으로 넘어갑니다.
  • 46.