Summary of Two Scoops of Django

1. 코딩 스타일

  • 읽기 쉬운 코드

    • 축약적이거나 함축적인 변수명은 피한다.
    • 함수 인자의 이름들은 꼭 써 준다.
    • 클래스와 메서드를 문서화한다.
    • 코드에 주석은 꼭 달도록 한다.
    • 재사용 가능한 함수 또는 메서드 안에서 반복되는 코드들은 리팩토링을 해둔다.
    • 함수와 메서드는 가능한 한 작은 크기를 유지한다. 어림잡아 스크롤없이 읽을 수 있는 길이가 적합하다.
  • PEP8를 따른다.

  • 임포트 (import)

    • 임포트 순서

      • 일반적인 경우

        • 표준 라이브러리
        • 연관 외부 라이브러리
        • 로컬 애플리케이션 또는 라이브러리에 한정된 임포트
      • Django

        • 표준 라이브러리
        • 코어 장고 임포트
        • 장고와 무관한 외부 앱 임포트
        • 프로젝트 앱 임포트
  • 임포트 방식

    • 절대 임포트 : from app.module import something
    • 명시적 상대 : from .module import something
    • 암묵적 상대 : from module import something
    • 가급적 명시적 상대 임포트를 사용
  • Never use import *

  • 장고 코드 스타일

    • URL 패턴 이름엔 dash 대신 underscore
    • 템플릿 블록 이름에 dash 대신 underscore
  • HTML, CSS, JS 스타일은 경우에 따라 적당한걸 사용한다.

  • IDE나 Editor에 종속되는 코딩 스타일은 지양한다.


2. 최적화된 장고 환경 꾸미기

  • 개발과 운영환경에서 같은 DB를 이용
    • 서로 다른 DB는 데이터와 타입 호환이 완벽하지 않음.
    • PostgreSQL 추천
  • pip와 virtualenv 사용
    • pip를 이용해 장고와 의존 패키지 설치
  • VCS 활용
  • (선택) 개발과 운영의 환경을 동일하게 구성
    • VirtualBox, Docker, ...
    • 단점
      • 필요하지 않는 기능들까지 제공되어 복잡해짐. OS레벨에서 고려해야할 필요가 없는 경우, 안하는 편이 더 편리하다.


3. 장고 프로젝트 구성

<repository_root>/
    <django_project_root>/
        <configuration_root>/
  • repository_root : README.md, docs/, .gitignore, requirements.txt, 배포 관련 파일 등등
  • django_project_root : Django project 소스들
  • configuration_root : settings, uris.py 등등. (유효한 파이썬 패키지)
  • Django cookiecutter를 사용할 수도 있음.


4. 장고 앱 디자인의 기본

  • 장고 앱 용어
    • 장고 프로젝트 : 장고 웹 프레임워크를 기반으로 한 웹 애플리케이션
    • 장고 앱 : 프로젝트의 한 기능을 표현하기 위해 디자인된 작은 라이브러리
    • INSTALLED_APPS : 프로젝트에 이용하기 위해 INSTALLED_APPS 세팅에 설정한 장고 앱들
    • 서드 파티 장고 패키지 : 파이썬 패키지 도구들에 의해 패키지화된, 재사용 가능한 플러그인 형태의 장고 앱
  • 각 앱은 그 앱의 주어진 임무에만 집중할 수 있어야함
  • 앱 이름 정하기
    • 가능한 한 한 단어
    • 일반적으로 앱의 중심이 되는 모델의 복수 형태 (blog등은 예외)
    • URL의 주소가 어떻게 되는지도 고려
    • PEP8 규약에 맞게
  • 되도록이면 앱들을 작게 유지
  • 공통 앱 모듈 / 비공통 앱 모듈
  • 공통 앱 모듈의 이름 규약은 가급적 따르자.


5. settings과 requirements 파일

  • (최선의) 장고 설정 방법
    • 버전 컨트롤 시스템으로 모든 설정 파일을 관리 (운영 환경에서 중요)
    • 반복되는 설정을 없앰
    • 암호나 비밀키 등은 안전하게 보관 (버전 컨트롤 시스템에서 제외)
  • 버전 관리되지 않는 로컬 세팅은 피한다.
    • 로컬 세팅 방식은 모든 곳에 버전 컨트롤되지 않은 코드가 존재하게 됨
    • 각기 다른 로컬 세팅을 가질 경우, 어느 쪽에서 문제가 생기면 재현하기도 어렵고 원인 추적도 어려워짐
    • 팀원들이 서로의 로컬 세팅을 복사하고 붙여쓰는데에 반복적인 일이 생기고 비효율적이게 됨
  • 여러 개의 settings 파일 이용
    • settings 아래에 base.py, local.py, staging.py, test.py, production.py로 각기 다른 세팅 파일을 둔다. (각 세팅 모듈은 독립적인 requirements가 필요)
    • 규모에따라 추가적인 세팅 파일이 필요할 수도 있음 (ex. CI서버 환경 파일)
    • 만약 local.py를 이용하여 셸을 시작할 경우, python manage.py shell --settings=twoscoops.settings.local를 쓴다
    • --settings를 사용하는 대신 DJANGO_SETTINGS_MODULE 환경 변수를 조건에 맞는 세팅 모듈 패스로 설정할 수도 있음
  • 다중 개발 환경 세팅
    • 팀원별로 각기 다른 개발 환경 세팅 파일을 사용할 경우, dev_<name>.py 형식의 파일을 VCS에서 관리하며 사용할 수 있다.
  • 코드에서 설정 분리
    • 비밀키들은 설정값들이지, 코드가 아니다.
    • 환경 변수 패턴
      • bash등의 설정 파일에 설정값들을 export해놓고 코드에서 환경 변수값을 불러와 사용한다.
      • 만약 비밀키가 존재 하지 않을 경우 Django 코어 익셉션의 ImproperlyConfigured를 임포트하여 예외를 발생시킨다.
    • 환경 변수를 사용할 수 없을 때
      • Apache, Nginx 환경에서 환경 변수 사용을 못할 수 있다. 이 땐 비밀 파일 패턴을 사용한다
    • 비밀 파일 패턴
      • JSON, Config, YAML 또는 XML중 한 가지 포맷을 선택해 비밀 파일을 생성
      • 비밀 파일을 관리하기 위한 비밀 파일 로더를 추가
      • 비밀 파일의 이름을 .gitignore 또는 .hgignore에 추가
  • 여러 개의 requirements 파일 이용
    • requirements 아래에 base.txt, local.txt, staging.txt, production.txt로 각기 다른 requirements 파일을 둔다.
    • base.txt는 공통 의존 패키지
    • 나머지의 경우 상단에 -r base.txt로 공통 패키지를 포함하고 아래에 추가 패키지를 적는다.
  • settings에서 파일 경로 처리
    • Fixes path가 아닌 Base path 기준의 Relative path를 사용하자.


6. 장고 모델

  • 모델이 너무 많으면 앱을 나눈다.
  • 모델 상속

파이썬 표준 라이브러리의 abc에서의 추상화 기초 클래스와 장고에서의 추상화 기초 클래스는 서로 목적이 전혀 다르다.

모델의 상속 스타일 장점 단점
상속 없음 : 모델들 사이에 공통 필드가 존재할 경우, 두 모델에 전부 해당 필드를 만들어 줌 테이블 매핑에 신경을 안써도되며, 간단함 모델간의 중복 테이블이 많을 경우 관리 어려움
추상화 기초 클래스 : 오직 상속받아 생성된 모델들의 테이블만 생성 공통 부분 재사용 가능. 추가 테이블 필요 없음. 조인으로 인한 성능 저하 없음. 부모 클래스를 독립적으로 이용 못함
멀티테이블 상속 : 부모와 자식 모델에 대해서 모두 테이블이 생성됨. OneToOneField는 부모와 자식 간에 적용됨. 각 모델에 대해 매칭되는 테이블이 생성됨. 따라서 부모 또는 자식 모델 어디로든지 쿼리 가능. 부모 객체로부터 자식 객체 호출 가능. 자식 테이블에 대한 각 쿼리에 대해 부모 테이블로의 조인이 필요해 부하 발생. 권하지 않음
프록시 모델 : 원래 모델에 대해서만 테이블이 생성됨. 각기 다른 파이썬 작용(behavior)을 하는 모델들의 별칭을 가질 수 있음 모델 필드 변경 불가
  • DB 마이그레이션
    • 마이그레이션 생성 팁
      • 새로운 앱이나 모델이 생성되면 새 모델에 대해 makemigrations 실행
      • 생성된 마이그레이션 코드를 실행하기 전에 생성된 코드를 한 번 봄. sqlmigrate를 통해 SQL문이 어떻게 생성되었는지도 확인 가능
      • 외부 앱에 대해 마이그레이션을 처리할 때에는 MIGRATION_MODULES 세팅 이용
    • 마이그레이션 배포 및 관리
      • 배포 전에 마이그레이션이 rollback 가능한지 확인하자.
      • 데이터가 많이 존재한다면, 실제 마이그레이션 실행 전에 스테이징 서버에서 비슷한 규모의 데이터에 대해 충분히 테스트하자. (운영 환경에선 많은 시간이 걸림)
      • MySQL를 사용한다면
        • 스키마 변환전에 DB 백업 필수. MySQL은 스키마 변경에 대한 트랜잭션 미지원. (rollback 불가능)
        • DB를 변환하기 이전에 프로젝트를 읽기 전용모드로 변경
        • 상당히 큰 테이블의 경우 스키마 변경에 상당한 시간이 소요됨
  • 모델 디자인
    • DB 정규화
    • 캐시의 비정규화 (성능 향상)
    • 반드시 필요한 경우에만 비정규화 수행
  • null과 공백
필드 타입 null=True blank=True
CharField, TextField, SlugField, EmailField, CommaSeparatedIntegerField, UUIDField X O. 위젯이 빈 값을 허용하기를 원한다면 설정한다.
FileField, ImageField X O
BooleanField X. 대신 NullBooleanField 이용 X
IntegerField, FloatField, DecimalField, DurationField 해당 값이 DB에 NULL로 들어가도 상관없다면 이용 위젯에서 해당 값이 빈 값을 받아와도 상관없다면 null=True와 같이 이용
DateTimeField, DateField, TimeField DB에서 해당 값들을 NULL로 설정하는게 가능하다면 이용 위젯에서 해당 값이 빈 값을 받아와도 문제가 없다거나 auto_now나 auto_now_add를 이용하고 있다면 null=True와 같이 이용
ForeignKey, ManyToManyField, OneToOneField DB에서 해당 값들을 NULL로 설정하는게 가능하다면 이용 위젯에서 해당 값이 빈 값을 받아와도 괜찮다면 이용
GenericIPAddressField DB에서 해당 값을 NULL로 설정하는게 가능하다면 이용 위젯에서 해당 값이 빈 값을 받아와도 괜찮다면 이용
IPAddressField X X
  • BinaryField 이용
    • 메시지팩 형식의 콘텐츠
    • 원본 센서 데이터
    • 압축된 데이터. ex. 센트리(Sentry)가 블롭(BLOB)으로 저장했지만 레거시 이슈등으로 base64로 인코딩된 데이터들의 형식
    • 이를 이용하여 파일을 직접 서비스하지는 않는다. (성능 이슈)
  • 범용 관계(generic relations)는 피하자.
    • 모델 간의 인덱싱이 존재하지 않으면 쿼리 속도에 손해를 가져옴
    • 다른 테이블에 존재하지 않는 레코드를 참조할 수 있는 데이터 충돌의 위험성이 존재
  • PostgreSQL에만 존재하는 필드에 대한 null과 공백의 사용 여부
필드 타입 null=True blank=True
ArrayField O O
HStoreField O O
IntegerRangeField, BigIntegerField, FloatRangeField DB에서 해당 값들을 NULL로 설정할 수 있다면 이용 가능 위젯에서 해당하는 폼이 빈 값을 허용하기를 원한다면 null=True와 함께 사용
DatetimeRangeField, DateRangeField DB에서 해당 값들을 NULL로 설정할 수 있다면 이용 가능 위젯에서 해당 값이 빈 값을 허용하기를 원할 경우 또는 auto_now나 auto_now_add를 이용하고 있다면 null=True와 같이 이용
  • 모델의 _meta API
    • 사용 이유
      • 모델 필드의 리스트를 가져올 때
      • 모델의 특정 필드의 클래스를 가져올 때 (또는 상속 관계나 상속 등을 통해 생성된 정보를 가져올 때)
      • 앞으로의 장고 버전들에서 이러한 정보를 어떻게 가져오게 되었는지 확실하게 상수로 남기기를 원할 때
    • 사용 예시
      • 장고 모델의 자체 검사 도구
      • 라이브러리를 이용해서 특별하게 커스터마이징된 자신만의 장고를 만들 때
      • 장고의 모델 데이터를 조정하거나 변경할 수 있는 일종의 관리도구를 만들 때
      • 시각화 또는 분석 라이브러리를 제작할 때, 예를 들어, 'foo'라는 단어로 시작하는 필드에 대한 분석 정보
  • 모델 매니저

    • 기존 모델 매니저를 교체하는 경우의 주의점

      • 추상화 기초 클래스의 자식들은 부모 모델의 모델 매니저를 받게 되고, 접합 기반 클래스들의 자식들은 그렇지 못함
      • 모델 클래스에 적용되는 첫번 째 매니저는 장고가 기본값을 취급하는 매니저이다. 따라서 쿼리셋으로부터 결과를 예상할 수 없게 만든다.
    • 모델 매니저의 적용 순서 : objects = modles.Manager()는 항상 새로운 이름의 커스텀 모델 매니저 위에 두도록 한다.

  • 거대 모델
    • 데이터 관련 코드를 뷰와 템플릿이 아닌 모델의 메서드 또는 매니저 메서드에 넣어 캡슐화
    • 재사용성 개선 가능
    • 그러나, 신의 객체 수준으로 증가시킨다면 모델이 매우 복잡해짐
    • 복잡함을 낮추는 기술
      • 모델 행동 (믹스인) : 모델 행동은 믹스인을 통한 캡슐화와 구성화의 개념으로 이루어짐. 모델은 추상화 모델로부터 로직들을 상속받음.
      • 상태 없는 헬퍼 함수 : 로직을 모델로부터 떼어내 독립적인 유틸리티 함수로 넣음. 유닛 테스트가 편해짐. 다만 stateless하기 때문에 더 많은 인자를 필요로 함


7. 쿼리와 데이터베이스 레이어

  • 단일 객체에서 get_object_or_404() 사용
    • 단, 뷰에서만 이용
  • 쿼리 예외 처리
    • ObjectDoesNotExist : 어떤 모델 객체에도 사용 가능 (except ObjectDoesNotExist)
    • DoesNotExist : 특정 모델에서만 사용 가능 (except ModelName.DoesNotExist)
    • MultipleObjectsReturned : 여러 개의 객체가 반환될 수 있는 경우 사용 (except ModelName.MultipleObjectsReturned)
  • 코드 가독성과 유지보수의 용이성을 위해 지연 연산 활용
  • 고급 쿼리 도구
    • 데이터 처리를 언어 차원에서 하지않고 DB 내부에서 처리하게 함으로써 성능 향상을 얻을 수 있으며, 경합 상황(race condition)을 피할 수 있음
    • DB 함수를 사용함으로써 성능 향상을 얻을 수 있음
  • Raw SQL은 지양
    • Raw SQL을 직접 이용함으로써 파이썬 코드나 ORM을 통해 생성된 코드가 월등히 간결해지고 단축되는 경우에만 사용
  • 인덱스를 사용해야할 경우
    • 인덱스가 빈번하게 (10~25%) 이용될 때
    • 실제 데이터 또는 실제와 비슷한 데이터가 존재해서 인덱싱 결과에 대한 분석이 가능할 때
    • 인덱싱을 통해 성능이 향상되는지 테스트할 수 있을 때
  • 트랜잭션
    • 각각의 HTTP요청을 트랜잭션으로 처리
      • 'ATOMIC_REQUESTS': True
      • 모든 쿼리가 보호되어 안전성 확보 가능
      • 그러나 성능 저하를 발생시킬 수 있음
      • DB가 아닌 아이템에 대해 데이터를 변형하는 뷰를 만들 때에는 해당 뷰를 transaction.non_atomic_requests()로 데코레이팅함을 고려
    • 명시적인 트랜잭션 선언
      • 성능 문제가 심각하지 않는 한 ATOMIC_REQUESTS를 이용
    • 트랜잭션 가이드라인
      • DB에 변경이 생기지 않는 작업은 트랜잭션으로 처리하지 않는다.
      • DB에 변경이 생기는 작업은 반드시 트랜잭션으로 처리한다.
      • DB 읽기 작업을 수반하는 변경 작업 또는 DB 성능에 관련된 특별한 경우에는 앞의 두 가이드라인을 모두 고려한다.
목적 ORM 메서드 트랜잭션 이용?
데이터 생성 .create(), .bulk_create(), .get_or_create() Yes
데이터 가져오기 .get(), .filter(), .count(), .iterate(), .exists(), .exclude(), .in_bulk() 등
데이터 수정하기 .update() Yes
데이터 지우기 .delete() Yes
  • 독립적인 ORM 메서드 호출을 트랜잭션으로 처리하지 말자.
  • django.http.StreamingHttpResponse와 트랜잭션
    • 뷰가 django.http.StreamingHttpResponse를 반환한다면 응답이 시작된 이상 트랜잭션 에러를 중간에 처리하기란 불가능
    • ATOMIC_REQUESTS를 False로 설정 후, 명시적인 트랜잭션 선언을 고려
  • 참고자료


8. 함수 기반 뷰(FBV)와 클래스 기반 뷰(CBV)

  • 범용적인 CBV로 가능하거나 복잡한 뷰 처리가 필요하지 않을 경우 CBV를 사용하며, CBV를 사용했을 경우 더 복잡해지거나 커스텀 에러가 필요할 경우 FBV
  • URLConf로부터 뷰 로직 분리 (느슨한 결합 유지)
  • URL 네임스페이스 활용
    • 서드파티 라이브러리와의 상호 운용성 증가
    • 검색, 업그레이드, 리팩토링이 쉬움
  • URLConf에서 뷰를 문자열로 지목하지 말자. (< 1.8)
  • 뷰에서 비즈니스 로직 분리 (재사용 가능한 비즈니스 로직 컴포넌트)
  • locals()를 뷰 컨텍스트에 이용하지 말자.
    • 명시적 컨텐츠를 이용


9. 함수 기반 뷰(FBV)의 모범적인 이용

  • 모든 함수는 HttpRequest객체를 받고 HttpResponse객체를 반환한다.
  • FBV 가이드라인
    • 뷰 코드는 작을수록 좋다.
    • 뷰에서 절대 코드를 반복해서 사용하지 말자.
    • 뷰는 프레젠테이션(presentation) 로직을 처리해야 한다. 비즈니스 로직은 가능한 한 모델 로직에 적용시키고 만약 해야 한다면 폼 안에 내재시켜야 한다.
    • 뷰를 가능한 한 단순하게 유지하라.
    • 403, 404, 500을 처리하는 커스텀 코드를 쓰는 데 이용하라.
    • 복잡하게 중첩된 if 블록 구문은 피하라.
  • HttpRequest객체를 활용한 코드 단순화 및 재사용
  • 데코레이터를 잘 활용하되 남용하지는 말라.
  • HttpResponse객체 또한 함수간 전달이 가능하다.

results matching ""

    No results matching ""