이번 포스트에서는 초심으로 돌아가서 장고 프레임워크를 사용하기 전에 꼭 숙지해야하는 표준 코딩 스타일에 대해 알아보고자 한다.


읽기 쉬운 코드는 어떤 것인가?

가독성이 좋은 코드는 로직이 한 눈에 보이고 유지 관리가 쉬우며 리펙터링이 용이한 코드다. 이러한 코드는 다음의 규칙을 따른다.

  • 축약되어 있거나 함축적인 함수명은 사용하지 않는다.
  • 함수 인자의 이름을 작성한다.
  • 클래스와 메서드를 문서화해놓는다.
  • 코드에 주석이 포함되어 있다.
  • 재사용 가능한 메서드는 반드시 리펙터링을 한다.
  • 함수와 메서드는 스크롤 없이 읽을 수 있을 정도의 길이로 작성한다.

코드는 언제든지 재사용될 수 있다. 그 때 함수명을 난해하게 축약해놓았다면 함수를 구별하는 데의 기술적 부채가 긴 코드를 작성하는 것보다 더 클 수 있다. 위와 같은 규칙을 따르는 것이 다소 귀찮을지라도 예전에 작성한 코드라도 다시 열어봤을 때 바로 식별할 수 있도록 하기 위한 것이니 시간을 투자해도 아깝지 않을 것이다.


1. PEP 8

PEP 8은 파이썬 공식 스타일 가이드이다. 파이썬의 대표적인 스타일 가이드는 다음과 같다.

  • 들여쓰기는스페이스 4칸
  • 최상위 함수와 클래스 선언 사이에는 2 줄을 띄운다.
  • 클래스 안에서의 메서드 간 간격은 1 줄을 띄운다.

PEP 8을 직접 찾아보고 일일히 지킬 자신이 없다면 코드를 작성하면서 가이드를 확인해주는 PEP 8 패키지를 설치하자.

PEP 8 Python style guide checker


진행중인 프로젝트의 관례를 먼저 생각하자

이미 진행중인 프로젝트가 PEP 8 가이드를 따르고 있지 않다면 기존에 지키고 있는 관례를 따르는 것이 좋다.


코드품질 관리도구, flake8

flake8은 코딩 스타일과 코드 품질, 논리적 에러를 점검하는 데 매우 유용한 도구로 알려져 있다. 해당 도구는 파이썬 패키지 관리자인 pip으로 설치하여 사용한다. 프로젝트 작업 중 대화형 인터프리터를 열고check.py를 실행하면 표준 코딩스타일에 어긋나는 에러를 검사해주며, 에러를 고치거나 무시할 수 있는 기능이 있다.

flake8 소개페이지 바로가기 flake8 문서


한 줄의 글자 수는 79자 이내로

PEP 8에서는 한 줄의 코드가 79자를 넘지 않도록 제한하고 있다. 이는 텍스트 줄바꿈을 지원하는 텍스트 편집기나 개발 관례상 코드의 이해도를 떨어뜨리지 않는 수준의 줄 길이이기 때문이다.

특히 오픈소스 프로젝트처럼 많은 사람들이 코드를 건드릴 수 있는 경우에는 더더욱 이 규칙을 지키는 것이 좋다. 개인 및 비공개 프로젝트의 경우에는 최대 99자까지 사용해도 무방하다.

앞서 설명한 표준 코딩스타일 가이드에서는 함수명을 축약하지 않는 것이 좋다고 했다. 하지만 긴 함수명 때문에 글자수가 79자를 넘길 경우에는 어떻게 해야할까 고민했던 사람들이 많을 것이다. 장고 코어 개발자인 애머릭은 이에 대해 “수십 년 전 하드웨어를 기준으로 만들어진 말도 안되는 숫자를 지키기보다는 읽기 쉽고 의미있는 변수명을 만드는 것이 더 중요하다”라고 말한 바 있다. 가독성의 측면에서 봤을 때 어떤 규칙을 지키는 것이 더 효율적인지 판단해보면 답은 이미 알고 있을 것이다.


2. 임포트

PEP 8에서는 리소스를 임포트할 때 다음과 같은 순서로 그룹을 지으라고 권장한다.

  1. 표준 라이브러리 임포트
  2. 연관 외부 라이브러리 임포트
  3. 로컬 앱 또는 로컬 라이브러리에 한정된 임포트

이를 장고 프로젝트에 대입해보면 다음 예제와 같다.

# 표준 라이브러리 임포트 - 장고에 내장되어 있는 라이브러리들
from __future__ import absolute_import
from math import sqrt
from os.path import abspath

# 코어 장고 임포트 - 장고의 코어 파일들
from django.db import models
from django.utils.translation import ugettext_lazy as _

# 서드파티(외부) 앱 임포트 - pip, brew 등으로 설치한 (장고와 무관한) 라이브러리들
from django_extensions.db.models import TimeStampedModel

# 프로젝트 앱 임포트 - 프로젝트 내에 직접 생성한 파일들
from flavors import FlavorListCreateView


파이썬 임포트 유형

파이썬에서 모듈을 임포트하는 방식은 다음 3가지가 있다. 각각 목적에 맞게 사용한다.

from core.views import FlavorMixin

  • 절대 임포트 방식. 외부에서 임포트해서 현재 앱에서 이용할 때


from .models import Flavor

  • 명시적 상대 임포트 방식. 다른 모듈에서 임포트해서 현재 앱에서 이용할 때


from models import Flavor

  • 암묵적 상대 임포트 방식. 종종 다른 모듈에서 임포트해서 현재 앱에서 이용할 때 쓰지만 추천하지 않는 방식이다.


명시적 성격의 상대 임포트를 사용하라

프로젝트 작업을 하다보면 점점 구조를 갖춰갈수록 코드 간의 연결관계가 복잡해진다. 이 때 코드를 옮기거나 이름을 변경하거나 버전을 나눌 경우 임포트해준 파일명을 일일이 수정해줘야하는 불상사가 발생한다. 이를 대비하여 파이썬에서는 명시적 성격의 상대 임포트(Explicit Relative Import)를 사용하여 패키지명을 하드코딩하지 않고도 원하는 소스를 임포트할 수 있다.

다음 예제를 보면 하드코딩된 임포트 스타일(위)은 flavorsflavor로 변경할 경우 일일이 수정을 해줘야한다.

# flavors/views.py

from flavors.models import FlavorListCreateView
from flavors.forms import FlavorForm
from flavors.views import FlavorMixin

이는 코드의 재사용성 측면에서 문제가 되므로 명시적 성격의 상대 임포트 스타일로 바꿔보면 다음과 같다.

# flavors/views.py

from .models import FlavorListCreateView
from .forms import FlavorForm
from .views import FlavorMixin

여기서 .은 파일이 위치한 폴더를 가리킨다. 따라서 앱 이름을 굳이 명시하지 않아도 폴더 내의 모듈들을 알아서 찾아준다. 이러한 방식은 파이썬 패키지를 하나의 코드로 유닛화해주기도 한다.


from __future__ import absolute_import

__future__ 모듈에서 임포트한 absolute_import는 항상 최상위 패키지명을 찾아 임포트해준다. 만약 현재 해당 임포트문을 사용하고 있는 파일 내에 flavor 모듈을 임포트할 경우에는 최상위 패키지에 같은 이름의 모듈이 있는지 먼저 살피고 없으면 해당 모듈을 임포트해준다.


import *을 지양하자

import *은 해당 패키지에 있는 모든 네임스페이스를 불러온다. 이 방법이 위험한 이유는 다른 파이썬 모듈의 네임스페이스들이 임포트해온 파일의 네임스페이스에 추가로 로딩되거나 기존 것들을 덮어쓸 수 있기 때문이다.

네임스페이스(namespace)란?

  • 개체를 구분할 수 있는 범위를 나타내는 말로 일반적으로 하나의 이름 공간에서는 하나의 이름이 단 하나의 개체만을 가리키게 된다.
from django.forms import *
from django.db.models import *

# 위의 임포트문은 아래의 상황을 유발할 수 있다.

from django.forms import CharField
from django.db.models import CharField

즉, form의 CharField를 models의 CharField로 덮어쓸 수 있기 때문에 필요한 부분만 임포트하는 습관이 필요하다.


3. 장고 코딩 스타일

장고는 내부적으로 PEP 8을 확장한 장고만의 스타일 가이드라인을 가지고 있다. 장고의 표준 스타일은 앞서 소개한 flake8 플러그인으로 따를 수 있다. 다음은 대표적인 가이드라인이다.

장고는 기본적으로 파이썬 스타일을 따른다.

  • 들여쓰기는 스페이스 4칸으로 표시한다.
  • camelCase를 지양하고 언더스코어로 변수, 함수, 메서드 이름을 표기한다. ex_hello_world()
  • 클래스명에는 첫 글자를 대문자로 표기한다.
  • 가능하다면 편의 임포트를 사용한다.
from django.views.generic import View  # (o)
from django.views.generic.base import View  # (x)
  • docstring에는 동사를 쓴다.
  • 장고의 템플릿 스타일은 중괄호와 변수명 사이에 띄어쓰기를 준수한다.
  • 장고의 view 메서드에 첫번째 매개변수는 반드시 request로 작성한다.
def my_view(request, foo):  # (o)
	pass
	
def my_view(req, foo):  # (x)
	pass
  • 장고의 모델 필드명은 반드시 소문자로 작성하고 띄어쓰기는 언더스코어를 사용한다.
class Person(models.Model):
	first_name = models.CharField(max_length=20)  # (o)
	FirstName = models.CharField(max_length=20)  # (x)
	First_Name = models.CharField(max_length=20)  # (x)
  • 클래스 내의 메타클래스는 반드시 필드 선언 후에 한 줄 띄우고 작성한다.
  • 추가로, 모델 내의 내부 클래스와 표준 메서드는 다음 예제의 순서를 따른다.
class Person(models.Model):
	# 데이터베이스 필드들
	first_name = models.CharField(max_length=20)
	last_name = models.CharField(max_length=30)
	
	# 커스텀 매니저 속성 정의
	objects = UserManager()

	# 메타클래스 정의
	class Meta:
		pass
		
	# 객체 출력용 표준 메서드 정의
	def __str__(self):
		return self.last_name + self.first_name
	
	# 오버라이드 메서드 및 커스텀 메서드 정의
	def save()
		pass
		
	def get_absolute_url()
		pass
  • 코드 작성을 완료한 후 사용하지 않는 임포트문은 모두 지워준다. 개인적으로 장고 내의 키바인딩에서 cmd+L로 단축키를 설정하여 지워주면 편리하다.

  • URL 패턴명은 언더스코어(_)를 사용한다.

urlpatterns = [
	url(
	# 주소 URL에는 대시를 사용해도 무관하다.
	r'^add-flavor/$',   
	views.add_flavor,
	name='add_flavor',
	),
]

더 자세한 가이드는 링크-장고문서를 참고하기 바란다.


4. 자바스크립트, 마크업 코딩 스타일

자바스크립트는 공식 스타일가이드가 따로 없다. 따라서 비공식 스타일을 따르거나, 자바스크립트 프레임워크에서 제공하는 스타일가이드를 따르면 된다.

HTML과 CSS 같은 마크업 언어의 스타일가이드는 다음 링크를 참고하여 작성하면 된다. 특히 CSS의 경우에는 CSScomb라는 CSS용 코딩스타일 포맷도구가 있어 사용자 지정 스타일에 따라 가이드를 제공해준다.

@mdo가 쓴 HTML과 CSS 코드 가이드

CSScomb 살펴보기


5. 현업에서 코딩하기

항상 주변의 개발자들이 다른 도구를 사용한다고 생각하고 가이드를 선택할 줄 알아야한다. 즉, 열린 자세로 코드 스타일을 받아들여야 한다는 이야기다. 아무리 작은 코드를 작성하더라도 작업내용과 코드 위치를 금방 찾을 수 있게 프로젝트 구조를 명료히 작성해야 한다.


Julia Hwang

디발자를 꿈꾸는 웹개발자의 블로그입니다.