[Pro Django] Tutorial_2: EditorConfig, Flake8, Pre-Commit

Saemi An·2025년 5월 7일
post-thumbnail

해당 포스팅은 유투버 Bucky의 『Pro Django Tutorial』 1강의 내용을 바탕으로 작성 했습니다.
동영상 바로가기
동영상 속 깃헙 바로가기


Tutorial_2는 Styling, Linting tools 관련 튜토리얼이다.

  • Linting 이란?
    작성된 소스코드를 분석하여 개발자가 사전에 정의한 기준에서 벗어나는 오류나 스타일 등을 체크하는 과정이다.
    코딩 컨벤션 규칙을 따로 정해 놓더라도 다수의 개발팀원들이 프로젝트에 참여하는 경우 코드의 일관성이 유지되기 힘들다.
    이때 Linting Tool을 활용하면 개발자의 로컬 머신에서 검사를 거친 후 커밋이 이루어지기 때문에 보다 매끄러운 협업이 이루어진다.

🕹️ EditorConfig

EditorConfig 웹사이트 링크

프로젝트 최상위 디렉토리에 위치한 가상환경 디렉토리 하위에 파일을 생성한 뒤, 다음과 같은 스타일링 규칙을 작성해 준다;

# .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 아이콘이 있었는데... 왜 안되지?!

응... 실패했다. 그냥 넘어간다.

🕹️ Flake8

🎮 설치

poetry add -D flake8

-D 옵션은 개발서버에서만 적용되는 의존성 패키지임을 명시하는 옵션이다.
(flake8은 운영서버에서는 필요가 없다.)

poetry add [패키지명] 실행시 pyproject.toml에 패키지가 add 될 뿐만 아니라 install도 된다.
실제로 pyproject.toml 파일 하단에 아래와 같이 추가된 것을 볼 수 있다.

[tool.poetry.group.dev.dependencies]
flake8 = "^7.2.0"

🎮 하나의 파일에 flake8 사용

테스트를 위해 core/manage.py 파일 아무데서나 공백 라인을 많이 만들어 준다.
이후 poetry run flake8 core/manage.py 명령어로 flake8을 실행시키면 다음과 같은 결과가 찍힌다.

이때 E303은 flake8에 설정된 오류코드이다.

🎮 전체 소스코드에 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 관련 피드백이 많은 것을 볼 수 있다 (허허..)

  • W293 blank line contains whitespace - 가독성을 위해 코드블럭들 사이에 공백 라인들을 둘 때가 있다. 이때 공백 라인의 커서는 맨 앞에 가있어야 한다. 즉, 공백 라인에 공백(화이트스페이스)가 있으면 안된다.
  • W292 no newline at end of file - 파일의 최하단은 공백 라인으로 한 줄로 끝나야 한다.
  • E302 expected 2 blank lines, found 1 - (보통 함수와 함수 사이에 혹은 import 섹션과 함수 사이에) 공백 라인은 2줄이어야 한다.
  • W291 trailing whitespace - 소스코드 뒤에 불필요한 공백은 없어야 한다.

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

이후 다시 Poetry를 통해 flake8을 실행하면 모든 오류가 해결된 것을 볼 수 있다!

🕹️ pre-commit

🎮 설치 및 설정

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
  • pre-commit hook이란?
    커밋 직전에 자동으로 실행되는 스크립트이다. 코드검토, 스타일 확인, 테스트 실행 등을 자동화 할 수 있다.

위 설정파일은 명시된 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

훅 실행이 건너띄워졌다. 다음 경우에 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를 자동으로 수정하는 것을 확인했다.

🎮 hook & toml 업데이트

위에서는 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
mypy와 mypy가 의존하는 패키지인 types-PyYAML 사이의 파이썬 버전 차이 때문에 충돌이 일어났다.
pre-commit-config.yaml 파일에서 types-PyYAML 버전을 다음과 같이 다운그레이드하여 해결했다.
- types-PyYAML==5.4.3

이후 poetry run pre-commit run --all-files 명령어 실행 후 모두 Passed가 떠 있음을 확인한다.

🎮 flake8 삭제

처음에는 flake8을 수동으로 실행했다.
이후 pre-commit을 통해 flake8이 실행되도록 자동화 설정을 추가했다.

따라서 개발환경에서 flake8이 필요 없게 되었으니 의존성을 삭제해 주자;
poetry remove -D flake8

이후 poetry run pre-commit run --all-files 를 다시 해보면 여전히 flake8에서 검사가 이루어지고 있는 것을 확인할 수 있다.

이로서 pre-commit이라는 parent tool을 사용하여 flake8을 비롯한 여러 child tool들을 자동으로 실행되도록 설정했다.

🎮 Makefile 업데이트

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 를 통해 다음 세 가지 사항이 자동으로 실행된다.

  • Poetry 변경 사항을 적용
  • DB migration 진행
  • hook 업데이트

이후 오늘의 업무(코드 수정)를 수행한 뒤 make lint 로 코드 스타일을 체크한 뒤 다시 git push를 하게 되는 흐름인 것 같다.

profile
하나씩 차근차근 천천히

0개의 댓글