본문 바로가기

더 나은 엔지니어가 되기 위해/파이썬을 파이썬스럽게

파이썬 클린 코드 2 - 클린 코드와 코딩 가이드라인

클린 코드란

클린 코드란 한 마디로 말해, 쉽게 이해 가능하며 지속적으로 개발하기 용이한 코드를 말한다.
코드는 누구나 짤 수 있다. 하지만 보기 좋고 심플하면서도 핵심 로직을 잘 풀어낸 코드를 짜내는 건 아무나 할 수 없다.
지속해서 성장하는 시스템에 유연하게 코드를 바꿀 수 있도록 설계해야 하고, 올바른 자료구조와 로직으로 하려는 일을 간단하게 드러내야 한다. 무엇보다 코드만 보고도 무엇을 하려는지 이해 가능해야 한다.
클린 코드는 개발자에게 일종의 '선'과 같은 개념이다. 현실적으로 불가능하더라도, 이러한 이상향이 있다는 것을 인지하고 내 코드를 평가할 수 있어야 한다. 적어도 내 코드가 별로인지 괜찮은지는 알아야 한다.

클린 코드는 특히 엔지니어간의 커뮤니케이션과 코드의 유지 관리성을 강조한다.

우리는 일반적으로 코드를 작성하는 것보다
읽는데 훨씬 많은 시간을 소비한다.

우리는 혼자 코딩하지 않으며 협업하며 일을 한다. 내 코드는 결국 누군가가 보게 되고, 그 누군가는 내 코드를 이해해야 한다. 따라서 애초에 "이해하기 좋은 코드" 를 짜는 것이 후에 누군가가 읽기에도 편하고, 유지 보수하기 좋다는 것이다.


코딩 가이드라인

참고로, "코딩 가이드라인"은 책에는 없는, 내가 지은 제목이다.
여기서는 코딩할 때 어떤 코딩 스타일을 따라야 하는지, 그러기 위해 어떤 기능들이 있는지 등을 간략히 살펴본다. 자세히 다루지는 않고 간단히 소개만 한다. 더 좋고 자세한 내용은 다른 블로그 분들의 포스팅을 링크해두었다.

아래 내용들이 클린 코드 그 자체는 아니다. 다만, 클린 코드가 말하는 "이해하기 좋은 코드" 가 되기 위한 권장 조건들이며, 클린 코드를 향하는 과정이라고 할 수 있겠다.
권장 조건이니 만큼, "필수적인 것"은 아니다. 실제 적용할 지는 팀마다 다르며, 자기가 속한 팀의 가이드라인이 있다면 그것을 준수하는 것이 먼저라고 생각한다. 하지만 아직 정해진 게 없다면, 아래 내용들을 숙지 후에 팀과 협의하여 가이드라인을 세워나가는 것도 좋다고 생각한다.

1. 코딩 스타일과 포매팅

코딩 스타일

좋은 코드의 레이아웃에서 가장 필요한 특성은 일관성이다. 코드가 일관되게 구조화되어 있으면 가독성이 높아지고 이해하기 쉬워진다. 코딩 스타일은 이처럼 일관된 코드 구조를 위한 관습(컨벤션) 이다.
파이썬에는 PEP-8 이라는 표준 코딩 스타일 표준 문서가 있다. 따라서 대부분의 파이썬 개발자들은 이 코딩 스타일을 따른다. PEP-8 코딩 스타일에는 예를 들어 다음과 같은 것이 있다.

# 키워드 인자에 값을 할당할 때는 띄어쓰기를 사용하지 않는다.
def func(a=None, b=None):  
    pass

# 변수에 값을 할당할 때는 띄어쓰기를 사용한다.
a = 5  
b = 3

# 키워드 인자에 값을 할당할 때는 띄어쓰기를 사용하지 않는다.
func(a=5, b=3)

이렇게 하면 a = 1 로 검색하면 함수의 정의부를 찾게 되고, a=1 로 검색하면 이 파라미터가 호출부에서 사용되는 곳을 찾게 된다. 즉 검색할 때 효율적이다.

이런 류의 일종의 '룰' 을 정의해놓은 문서가 PEP-8 이다. 구체적인 게 궁금하면 아래 링크를 참고하자.

[PEP-8 공식 도큐먼트]

https://www.python.org/dev/peps/pep-0008/

실제로 코딩할 때는 pylint 라는 린터를 설치하여 코딩한다.
린터는 언어 문법과 코드 스타일이 올바르게 짜였는지 검사하는 프로그램인데, pylint 는 내가 작성한 파이썬 코드가 문법 오류는 없는지, PEP-8 에 부합하는지 등을 검사해준다. 보통 VS code 나 Pycharm 등 IDE 내부 플러그인에 포함되어있다. 보통은 이 방식으로 PEP-8 스타일을 준수하게 된다.

 

코드 포매팅

린터가 검사만 한다면, 포매터는 검사를 기반으로 올바른 코드 스타일로 수정까지 해준다. 즉 내 코드를 바꾼다. 그런데 올바른 코드 스타일도 종류가 있다. PEP-8 문서를 보면 알겠지만, 좀 더 러프하게 방향성을 제시해줄 뿐, 어떤 정답 코드가 딱 하나가 있는 게 아니다. 그래서 포매터도 어떻게 수정할 것이냐에 따라 여러 종류가 있다.

Autopep8, yapf, black 등이 각각 나름의 스타일로 포매팅해주는 코드 포매터다.
이에 대한 자세한 내용은 아래 링크를 참고하자.

[파이썬 코드 체커와 포매터 종류]

city7310 님 블로그 - 파이썬 코드 스타일 이야기 - (1) Style Checker, Formatter들 구경하기

실제로 회사에서는 이런 코드 포매터를 도입하여 팀 내부의 코드 스타일을 엄하게 표준화한다. 그렇게 해야 팀 내 코드 스타일의 일관성을 확실하게 유지할 수 있고, 무엇보다 다른 스타일로 인한 git conflict 가 뜨지 않는다.

 

2. Docstring

파이썬은 유독 "읽고 이해하기 좋음" 을 언어적으로 강조한다. 프로그래밍을 처음 하는 사람도 C 나 Java 보다 파이썬을 더 배우기 쉬운 이유도 이 때문이라고 생각한다. 다른 언어보다, 문법이 사람의 말과 닮아있다.

이러한 철학 때문인지 파이썬에서는 문서화를 중시한다. 그리고 Docstring 이라는 개념이 문법에 등장한다. Docstring 은 코드와 로직에 대한 설명을 적어두는 것이다.
주석과 다르다. 주석은 어쩔 수 없는 보충설명의 기능이고, 되도록이면 지양해야 한다.
Docstring 은 보충설명이 아닌 문서다. 코드와 컴포넌트에 대한 전반적인 설명이고, 내용은 코드 이해를 충분히 도울만큼이면 좋다.

오픈소스 pandas 내 DataFrame 의 소스. docstring 이 아주 세세하게 잘 써져있다.

클래스나 함수 아래에 """ 를 감싼 뒤, 이 안에다가 적으면 된다.
이렇게 적은 내용은 실제로 String 객체 안에 담겨, __doc__ 을 통해 코드에서 접근할 수 있다.
예를 들어 DataFrame 의 위 Docstring 은 DataFrame.__doc__ 에 담겨있다.

Docstring 에도 유명한 스타일들이 있다.
sphinx, google, numpy 등의 스타일들이 대표적인 예다.
이 스타일들에 대한 간단한 설명은 다음 링크를 참고하자.

[파이썬 Docstring 스타일 예제]

Mo Kweon 님 블로그 - 파이썬 (doc) 스타일 가이드에 대한 정리

이렇게 docstring 을 잘 짜 놓으면 이후 sphinx 를 이용하여 코드 문서화를 웹 페이지로 퍼블리싱할 수 있다. 우리가 오픈소스 공식문서를 볼 때 흔히 보는 그 페이지들이 바로 이 sphinx 를 이용하여 만든 페이지들이다.
sphinx 를 사용하는 예제는 다음 링크를 참고하자.

[파이썬 Sphinx 로 웹페이지 문서 만드는 예제]

Kei Choi 님 블로그 - 파이썬 소스코드 문서화하기

 

3. 타입 힌팅

어노테이션

파이썬은 동적으로 타입을 결정하기 때문에, 변수의 타입이 무엇인지 알기 어려운 경우가 많다. 특히 내가 짠 코드가 아닌 남의 코드를 볼 때 더욱 그렇다.
어노테이션은 이를 해결하기 위해 변수 옆에 타입을 명시해주는 기능이다.
예를 들면 다음과 같다.

def func(a: int,  b: str = None, c: MyClass = None) -> None:
    pass

위 함수는 int, str, Myclass 타입의 변수를 입력받고, 아무것도 반환하지 않는다.
어노테이션은 명시만 해줄 뿐 실제로 그 타입인지 검사하지는 않는다. 그저 단순히 '힌트' 를 줄 뿐이다.
(다만 Pycharm 과 같은 IDE 에서는 코딩할 때 올바른 타입을 할당했는지, 반환받았는지 등을 어노테이션 기반으로 알려주기도 한다. 되도록 어노테이션을 활용해야 하는 이유이기도 하다.)

파이썬 3.6 부터는 다음과 같이 변수에다가도 사용할 수도 있다.

>>> a: int = 1
>>> b: float = 2
>>> print(a, b)
1, 2

좀 더 복잡한 List 나 Dict 같은 컬렉션 타입이나 Class 타입은 어떻게 해야 할까?
이를 위해서는 typing 모듈을 사용해야 한다.
예를 들면 다음과 같다.

from typing import List

def func(a: List[int]) -> None:
    pass

a: List[int] = [1, 2, 3, 4]
func(a)

typing 모듈을 써서 일관성 있게 타입을 명시해주는 게 타입 힌팅의 정석이긴 하다.
다만 자료구조 복잡해질수록 타입 힌팅도 길어지게 되고, 과연 이게 읽기 좋은 코드인지는 생각해봐야 한다.

 

Mypy

mypy는 어노테이션을 기반으로 정말 그 타입이 사용되었는지 검사하는 도구다. 어노테이션만 사용하면 힌트만 줄 뿐, 검사하지는 않는다고 했는데, mypy 는 검사해서 잘못사용된 부분을 알려준다.
예를 들면 다음과 같다.

# test.py
a: int = "heumsi"
$ mypy test.py
test.py:1: error: Incompatible types in assignment (expression has type "str", variable has type "int")
Found 1 error in 1 file (checked 1 source file)

mypy 는 위처럼 동적인 파이썬 코딩을 좀 더 Type safe 할 수 있게 도와준다.
다만 가끔 잘못 탐지하는 경우도 있으므로 주의하자.
좀 더 자세한 내용은 아래 링크를 참고하자.

[타입 힌팅과 mypy 소개]

Seorenn님 블로그 - Python 3 정적 타이핑 소개 및 소감(?)

 

4. 자동 검사 설정

리눅스 개발환경에서 빌드를 자동화하는 가장 일반적인 방법은 makefile 을 사용하는 것이다.
빌드 외에도 포매팅 검사나 스타일 검사, 타입 검사, 테스트 등을 이 안에서 자동화할 수 있다.
예를 들면 다음과 같다.

# makefile

lint:
pylint src/ test/

typehint:
mypy src/ test/

test:
pytest src/ test/

checklist: lint typehint test

.PHONY: typehint test lint checklist
$ make checklist

위 커맨드는 코드 스타일, 타입, 테스트 검사를 차례대로 수행한다.
만약 이 중 하나라도 실패하면 전체 프로세스가 실패한 것으로 간주한다.


정리

클린 코드가 무엇인지 잠깐 설명하고, 클린 코드를 위한 최소한의 코딩 가이드라인을 적어보았다.
주요 키워드를 적어보면 다음과 같다.

  • 코드 스타일
    • PEP-8 으로 일관된 코드 레이아웃 유지
    • 이를 위한 체커(린터)와 포매터
  • Docstring
    • 코드 문서화는 코드를 쉽게 이해하게 한다.
    • sphinx 로 쉽게 API 웹 문서를 만들 수 있다.
  • 타입 힌팅
    • 어노테이션으로 변수가 어떤 타입인지 명시
    • mypy 로 올바른 타입이 할당되었는지 검사
  • 자동 검사 설정
    • 프로젝트 빌드 시에 팀의 코드 스타일을 따랐는지, 타입 오류는 없는지 등 검사
    • 이 외에도 테스트 수행 등 기능

저번 글이 너무 길었어서, 이번에는 좀 나누어서 가려고 한다.
다음 글은 좋은 코드의 일반적인 특징 등 좀 더 코드 단위로 살펴보는 글이 될 듯하다.