
해당 포스팅은 유투버 Bucky의 『Pro Django Tutorial』 1강의 내용을 바탕으로 작성 했습니다.
동영상 바로가기
동영상 속 깃헙 바로가기
Tutorial_2는 Styling, Linting tools 관련 튜토리얼이다.
프로젝트 최상위 디렉토리에 위치한 가상환경 디렉토리 하위에 파일을 생성한 뒤, 다음과 같은 스타일링 규칙을 작성해 준다;
# .venv/.editorconfig
root = true
[*.{html, py}]
charset = utf-8
indent_size = 4
indent_style = space
max_line_length = 119 # 한 줄에 작성할 수 있는 최대 문자 수
VSCode가 최신버전이라면 저장만 해도 적용이 된다(고 한다.)
그렇지 않은 경우 EditorConfig for VS Code 익스텐션을 설치해야 한다(고 한다.)
그럼에도 불구하고 적용이 되지 않는 경우 VSCode 설정파일을 들여다 봐야 한다(고 한다.)
테스트 목적으로 "root = true [*.css] indent_style = space indent_size = 2" 설정 후 새로운 css 파일을 만들어 확인해 봤는데 적용이 안 됐다
vscode settings에서 detect indentation: false, default css formatter: false 설정을 줘도 안되길래 stackoverflow에 찾아봤더니 "npm install -g editorconfig"를 해줘야 한다는데..
분명 EditorConfig 공식에서는 "No Plugin Necessary"라면서 VSCode 아이콘이 있었는데... 왜 안되지?!
응... 실패했다. 그냥 넘어간다.poetry add -D flake8
-D 옵션은 개발서버에서만 적용되는 의존성 패키지임을 명시하는 옵션이다.
(flake8은 운영서버에서는 필요가 없다.)
poetry add [패키지명] 실행시 pyproject.toml에 패키지가 add 될 뿐만 아니라 install도 된다.
실제로 pyproject.toml 파일 하단에 아래와 같이 추가된 것을 볼 수 있다.
[tool.poetry.group.dev.dependencies]
flake8 = "^7.2.0"
테스트를 위해 core/manage.py 파일 아무데서나 공백 라인을 많이 만들어 준다.
이후 poetry run flake8 core/manage.py 명령어로 flake8을 실행시키면 다음과 같은 결과가 찍힌다.

이때 E303은 flake8에 설정된 오류코드이다.
개발 과정에서 파일 하나하나에 flake8 명령어를 실행시킬 수는 없다.
전체 소스코드 파일에 대해 flake8을 실행시키려면 우선 최상위 디렉토리에 flake8 설정파일을 다음과 같이 만들어 준다.
# flake8 설정 파일
[flake8]
# ignore = C101,D403,F403,F405,I100,I201,W503,W504 # 무시할 오류들
exclude =
**/migrations/*,
.git,
__pycache__,
.venv
filename = *.py
accept-encodings = utf-8
inline-quotes = single
# 하나의 함수가 갖을 수 있는 최대 복잡도. 워닝 나면 쪼개서 리팩토링 필요함.
max-complexity = 12
max-linenumber = 500
max-line-length = 119
multiline-quotes = double
# 이 특정 파일은 무시함
per-file-ignores =
cooking_core/project/settings/templates/settings.dev.py:F821
이후 poetry run flake8을 실행시키면 다음과 같은에러가 뜨는 것을 볼 수 있다.
W293은 flake8 패키지에 정의된 오류코드 이다.

안좋은 코딩 습관 덕분에 유독 whitespace와 blank line 관련 피드백이 많은 것을 볼 수 있다 (허허..)
이들 중 F821 코드는 django-split-setting(Tutorial_1)에서 쪼개놓은 설정 때문이다. 결과적으로 파이썬 인터프리터가 컴파일시 모든 설정들이 유기적으로 연관되도록 세팅파일을 나누어 놓았지만, 이를 모르는 IDE가 보기에는 broken setting file인 것이다.
(이를 모르는 IDE가 무시할 수 있도록 # type: ignore을 붙여준 부분에 뒤에) # type: ignore # noqa: F821를 추가해 준다.

이후 다시 Poetry를 통해 flake8을 실행하면 모든 오류가 해결된 것을 볼 수 있다!
poetry add -D pre-commit 이후 .pre-commit-config.yaml 파일을 다음과 같이 작성해 준다.
# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
# repo에서 hooks_id를 읽어 적용한다
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v3.2.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files
위 설정파일은 명시된 repo에서 일치하는 hooks_id를 찾아 코드조각을 실행한다는 내용이다.
예를 들어 명시된 repo에는 모든 문장 말단의 공백을 정리해주는 trailing-whitespace 코드 스니펫이 있고, 깃 커밋 전에 이 코드 스니펫이 자동으로 우선 실행되도록 설정하고 있다.
poetry run pre-commit install 명령어를 통해 hook들을 우선 다운받아 준다.
(해당 명령어를 실행하려면 .git 파일이 존재해야 한다. 없으면 git init만 우선 해주자.)
이후 깃 커밋시 hooks에 정의된 코드 스니펫들이 자동으로 실행될 것이다.
+) 관련 설정 없이도 이게 됨?
우선 pre-commit이 잘 작동하는지 확인을 위해 다음 명령어로 pre-commit을 수동으로 실행해 본다.
poetry run pre-commit run --all-files
훅 실행이 건너띄워졌다. 다음 경우에 skipped가 될 수 있다고 한다;
1. 커밋된 파일이 없거나
2. .pre-commit-config.yaml에 정의된 훅이 적용되는 파일(예: .py, .js, .md 등)이 현재 프로젝트에 없거나
3. 변경된 파일이 없기 때문에 훅을 적용할 파일이 없는 경우
2번은 위에서 진행해 주었기 때문에 1번과 3번을 의심했다.
git init은 했지만, git add로 파일을 Git에 등록하지 않은 상태인 경우, (당연히) Git이 추적하고 있는 파일은 없다. 따라서 pre-commit이 검사할 파일이 없는 것이다.
git add . 를 해준 뒤 재시도 했다.
우선 pre-commit이 잘 동작하는지 보기 위해 아무 파일에 whitespace를 추가해 줬다.
이후 깃이 내 파일을 추적하도록 git add . 를 해준다.
그리고 poetry run pre-commit run --all-files 를 실행하니 .pre-commit-config.yaml에 정의된 훅들이 정상적으로 작동하면서 whitespace를 자동으로 수정하는 것을 확인했다.

위에서는 pre-commit의 요지를 설명하기 위해 pre-commit에서 제공하는 샘플 레포와 훅들을 다운로드하여 수동으로 실행시켜 보았다.
이제 실제 코드에 적용할 스타일 규칙들을 적용하기 위한 hook들을 아래와 같이 추가해 보자 (pre-commit에서 제공하는 샘플 훅들은 삭제했다.);
repos:
- repo: https://github.com/pycqa/isort
rev: 5.12.0
hooks:
- id: isort
name: isort (python)
# yapf(?)
- repo: https://github.com/pre-commit/mirrors-yapf
rev: v0.31.0
hooks:
- id: yapf
additional_dependencies: [ toml ]
# flake8가: 깃 커밋 전 자동 실행
- repo: https://github.com/PyCQA/flake8
rev: 4.0.1
hooks:
- id: flake8
# hook이 의존하는 패키지
additional_dependencies:
- flake8-bugbear
- flake8-builtins
- flake8-coding
- flake8-import-order
- flake8-polyfill
- flake8-quotes
# mypy(?)
- repo: https://github.com/pre-commit/mirrors-mypy
rev: 'v0.910'
hooks:
- id: mypy
additional_dependencies: [ types-requests, types-PyYAML, types-toml ]
yapf, flake8, mypy의 훅들을 추가해 주었다.
pre-commit-config.yaml 파일에서는 어떤 hook들을 어디(repo)에서 다운받아 사용해야 할지를 정의한다.
그리고 다운받은 툴들의 세부 설정값은 .toml 파일에 다음과 같이 정의해 준다;
[tool.isort]
multi_line_output = 5
line_length = 119
[tool.yapf]
based_on_style = "google"
align_closing_bracket_with_visual_indent = true
coalesce_brackets = true
column_limit = 119
dedent_closing_brackets = true
⚠️ 삽질: 버전 충돌
yaml-stubs/emitter.pyi:11: error: Positional-only parameters are only supported in Python 3.8 and greater
이후 poetry run pre-commit run --all-files 명령어 실행 후 모두 Passed가 떠 있음을 확인한다.
처음에는 flake8을 수동으로 실행했다.
이후 pre-commit을 통해 flake8이 실행되도록 자동화 설정을 추가했다.
따라서 개발환경에서 flake8이 필요 없게 되었으니 의존성을 삭제해 주자;
poetry remove -D flake8
이후 poetry run pre-commit run --all-files 를 다시 해보면 여전히 flake8에서 검사가 이루어지고 있는 것을 확인할 수 있다.
이로서 pre-commit이라는 parent tool을 사용하여 flake8을 비롯한 여러 child tool들을 자동으로 실행되도록 설정했다.
pre-commit을 추가했으니 Makefile에 다음 내용을 추가해 줬다;
# hook에 변경사항이 있을 때 pre-commit 도구를 통해 기존 hook 전체 삭제 및 재다운로드
.PHONY: install-pre-commit
install-pre-commit:
poetry run pre-commit uninstall; poetry run pre-commit install
# pre-commit 툴을 사용하여 코드 스타일 수동 확인
.PHONY: lint
lint:
poetry run pre-commit run --all-files
또한 Makefile에서 기존의 update 명령어도 수정했다;
.PHONY: update
update: install migrate install-pre-commit ;
전체적인 흐름
이제 다른 개발팀원들이 프로젝트를 git pull 하면
make update 를 통해 다음 세 가지 사항이 자동으로 실행된다.
이후 오늘의 업무(코드 수정)를 수행한 뒤 make lint 로 코드 스타일을 체크한 뒤 다시 git push를 하게 되는 흐름인 것 같다.