Code maturity - minnie0531/fastapi-template GitHub Wiki

๊ฐœ๋ฐœ์ž์™€ ๊ฐœ๋ฐœ ํ”„๋กœ์ ํŠธ๊ฐ€ ๋งŽ์•„ ์ง์— ๋”ฐ๋ผ ํ‘œ์ค€ํ™”๊ฐ€ ์ค‘์š”ํ•ด ์ง‘๋‹ˆ๋‹ค. ๊ทธ ์ค‘์—์„œ ์—ฌ๊ธฐ์„œ ๋‹ค๋ค„๋ณผ ๋‚ด์šฉ์€ ์ฝ”๋“œ์— ๋Œ€ํ•œ ๊ฒƒ์œผ๋กœ ์–ด๋–ป๊ฒŒ ์ฝ”๋“œ์˜ ์„ฑ์ˆ™๋„๋ฅผ ์˜ฌ๋ฆฌ๊ณ , ์œ ์ง€๋ณด์ˆ˜์™€ ๋””๋ฒ„๊น…์ด ์‰ฌ์›Œ์งˆ ์ˆ˜ ์žˆ๋Š”์ง€์— ๋Œ€ํ•ด ์ด์•ผ๊ธฐ ํ•˜๋ ค ํ•ฉ๋‹ˆ๋‹ค. ์ฝ”๋“œ์— ๋Œ€ํ•œ ๋ถ„์„์€ ์ •์  ๋ถ„์„๊ณผ ๋™์  ๋ถ„์„์œผ๋กœ ๋‚˜๋ˆŒ ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ •์  ๋ถ„์„์œผ๋กœ formatter์™€ linter๋ฅผ, ๋™์  ๋ถ„์„์œผ๋กœ code coverage์— ๋Œ€ํ•ด ๋‹ค๋ฃจ๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

formatter and linter

์—ฌ๋Ÿฌ ์‚ฌ๋žŒ์ด ํ˜‘์—…ํ•˜์—ฌ ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๋Š” ๊ฒฝ์šฐ ์ฝ”๋“œ์Šคํƒ€์ผ์— ๋Œ€ํ•œ ์ •์˜๊ฐ€ ์—†๋‹ค๋ฉด ๊ฐœ์ธ ๋งˆ๋‹ค ๋‹ค๋ฅธ ํ˜•์‹์œผ๋กœ ๊ฐœ๋ฐœ๋  ๊ฐ€๋Šฅ์„ฑ์ด ํฝ๋‹ˆ๋‹ค. ๋ˆ„๊ตฐ๊ฐ€๋Š” if ๋‹ค์Œ {} ์˜ ์œ„์น˜๋ฅผ ๋ฐ”๋กœ ์“ฐ๋Š” ๊ฒƒ์„ ์„ ํ˜ธํ•˜๊ณ  ์–ด๋–ค ์‚ฌ๋žŒ์€ ๋‹ค์Œ ๋ผ์ธ์— ์œ„์น˜ํ•˜๋Š” ๊ฒƒ์„ ์„ ํ˜ธ ํ•ฉ๋‹ˆ๋‹ค. ํ•จ์ˆ˜ ์ •์˜ ํ•œ ๋‹ค์Œ ๋‹ค๋ฅธ ํ•จ์ˆ˜์˜ ์ •์˜๊ฐ€ ์˜ฌ ๋•Œ blank line์— ์–ผ๋งŒํผ ์‚ฌ์šฉํ• ์ง€ ๋“ฑ ๋‹ค์–‘ํ•œ ์„ ํ˜ธ๊ฐ€ ์žˆ์„ ๊ฒƒ์ด๊ณ  ์ด๋Š” ๊ณ ์Šค๋ž€ํžˆ ์ฝ”๋“œ์— ๋ฐ˜์˜๋˜๊ฒŒ ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋‹ค์–‘ํ•œ ์ฝ”๋“œ ์Šคํƒ€์ผ์€ ํ”„๋กœ์ ํŠธ์— ๋Œ€ํ•œ ์œ ์ง€๋ณด์ˆ˜๋ฅผ ์–ด๋ ต๊ฒŒ ๋งŒ๋“ญ๋‹ˆ๋‹ค. ๋‹ค์‹œ ๋งํ•˜๋ฉด ์ฝ”๋“œ์— ๋Œ€ํ•œ ๊ฐ€๋…์„ฑ์ด ๋–จ์–ด์ง€๊ณ  ๊ทธ ๊ฒฐ๊ณผ ์—๋Ÿฌ๋ฅผ ๋ฐœ๊ฒฌํ•˜๋Š” ๊ฒƒ์ด ์‰ฝ์ง€ ์•Š์•„ ๋””๋ฒ„๊น…์ด ์–ด๋ ค์›Œ ์งˆ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋ชจ๋‘๊ฐ€ ๋™์ผํ•œ ์ฝ”๋“œ ์Šคํƒ€์ผ์„ ์‚ฌ์šฉํ•˜๊ฒŒ ๋œ๋‹ค๋ฉด ์œ„์˜ ๋‹จ์ ์ด ๋ณด์™„ ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์šฐ๋ฆฌ๋Š” formatter์™€ linter๋ฅผ ์ด์šฉํ•˜์—ฌ code format์— ๋Œ€ํ•œ violation์„ ์ฒดํฌํ•˜๊ณ  ๋ฐ”๋กœ ์žก์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • PEP8

pep8์€ python ๊ณต์‹ ์Šคํƒ€์ผ ๊ฐ€์ด๋“œ ์ž…๋‹ˆ๋‹ค. ์•„๋ž˜์™€ ๊ฐ™์ด ์ฝ”๋“œ ์Šคํƒ€์ผ์— ๋Œ€ํ•ด ์ •์˜๊ฐ€ ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

Spaces are the preferred indentation method.
Use 4 spaces per indentation level.
Limit all lines to a maximum of 79 characters.
Separate top-level function and class definitions with two blank lines.
Method definitions inside a class are surrounded by a single blank line.
Imports should be grouped in the following order:
  - Standard library imports.
  - Related third party imports.
  - Local application/library specific imports.
  - A blank line between each group of imports.

Formatter์™€ linter ์˜ ์ข…๋ฅ˜

linter์™€ formatter์˜ ์ข…๋ฅ˜๋Š” ๋งค์šฐ ๋‹ค์–‘ํ•ด์„œ ๊ฐœ๋ฐœ๊ทธ๋ฃน์—์„œ ์„ ํƒ์ ์œผ๋กœ ์ ์šฉ ๊ฐ€๋Šฅ ํ•ฉ๋‹ˆ๋‹ค.

  • pycodestyle

    Pycodestyle์€ ๊ณต์‹ linter๋กœ PEP8์˜ ์Šคํƒ€์ผ ์ปจ๋ฒค์…˜์„ ๊ธฐ๋ฐ˜์œผ๋กœ python ์ฝ”๋“œ๋ฅผ ์ฒดํฌํ•ฉ๋‹ˆ๋‹ค.

  • pylint

    pylint๋Š” python linter์ค‘ ํ•˜๋‚˜๋กœ ์†Œ์Šค์ฝ”๋“œ ์ฒดํฌ ๋ฐ ๋ฒ„๊ทธ, qulaity ์ฒดํฌ๋ฅผ ํ•ฉ๋‹ˆ๋‹ค. ๋‹จ์ˆœํžˆ PEP8์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ ๋ณด๋‹ค ๋งŽ์€ verificiation checks์™€ ์˜ต์…˜์„ ๊ฐ–๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ๊ฐ ๋ผ์ธ์˜ ๊ธธ์ด๋ฅผ ์ฒดํฌ ํ•œ๋‹ค๊ฑฐ๋‚˜ ๋ณ€์ˆ˜ ๋ช…์ด ํ”„๋กœ์ ํŠธ์˜ ๊ธฐ์ค€์— ๋งž๊ฒŒ ์ ์šฉ ๋˜์–ด ์žˆ๋Š”์ง€ ์„ ์–ธ๋œ ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ์ •๋ง๋กœ ๊ตฌํ˜„ ๋˜์–ด ์žˆ๋Š” ์ง€ ๋“ฑ PEP8์— ์—†๋Š” ์ถ”๊ฐ€์ ์ธ ๋‚ด์šฉ๋„ ๊ฐ–๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐ€์žฅ ๋งŽ์ด ์‚ฌ์šฉํ•˜๋Š” linter๋กœ VSCode์˜ ๊ธฐ๋ณธ linter์ž…๋‹ˆ๋‹ค.

  • pyflakes

    pyflakes๋Š” ์•ž์˜ ๋‹ค๋ฅธ linter ๋‹ค๋ฅด๊ฒŒ ์Šคํƒ€์ผ์— ๋Œ€ํ•œ ์ฒดํฌ๊ฐ€ ์•„๋‹ˆ๋ผ ํ”„๋กœ์ ํŠธ์˜ ๊ฐ ํŒŒ์ผ์—์„œ ๋ฐœ์ƒ๋  ์ˆ˜ ์žˆ๋Š” logistic erorrs , syntax eorror ๋“ฑ์„ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

  • flake8

    flake8์€ pyflakes, pycodlestyle ๊ณผ Mccabe script ๋ฅผ ๊ฐ์‹ธ๊ณ  ์žˆ๋Š” wrapper ์ž…๋‹ˆ๋‹ค. McCabe script์˜ ๋Š” ์ฝ”๋“œ์˜ ๋ณต์žก์„ฑ์„ ์ค„์ด๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

  • black

    black์€ python code auto-formatter๋กœ ์ „์ฒด ํŒŒ์ผ์— ๋Œ€ํ•ด reformattingํ•ด์ฃผ๊ณ  string์˜ ๊ฒฝ์šฐ " ์„๊ฐ–๊ณ ๋„๋ก ๋งŒ๋“ญ๋‹ˆ๋‹ค. black์€ python ๊ณต์‹ community์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด๋ฉฐ line-length๋ฅผ ์ œ์™ธํ•˜๊ณ  format์— ๋Œ€ํ•œ ์ถ”๊ฐ€์ ์ธ configuration์€ ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

  • autopep8

    autopep8์€ ์ž๋™์œผ๋กœ python code๋ฅผ style๋กœ reformatting ํ•ด ์ค๋‹ˆ๋‹ค. autopep8์€ pycodesstyle ์—์„œ ๋ฆฌํฌํŠธ ๋˜๋Š” ๋Œ€๋ถ€๋ถ„์˜ ์ด์Šˆ๋ฅผ ๋ฐ”๋กœ ๊ณ ์นฉ๋‹ˆ๋‹ค.

  • yapf

    yapf์€ python formatter์ค‘ ํ•˜๋‚˜๋กœ google์—์„œ ์‚ฌ์šฉํ•˜๊ณ  ์œ ์ง€๋ณด์ˆ˜ ํ•ฉ๋‹ˆ๋‹ค. configuration์ด ์ž์œ ๋กœ์šด ๊ฒƒ์ด ํŠน์ง•์ž…๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์—์„œ๋Š” ๋‹ค์–‘ํ•˜๊ฒŒ linter๋ฅผ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋Š” flake8๊ณผ ์šฐ๋ฆฌ์˜ configuration์ด ์ถ”๊ฐ€์ ์œผ๋กœ ๋“ค์–ด๊ฐ€์ง€ ์•Š์•„๋„ ๋˜๋Š” black์„ ์‚ฌ์šฉํ•ด ๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

IDE์—์„œ lint ์ ์šฉํ•˜๊ธฐ

vscode https://code.visualstudio.com/docs/python/linting

  • command palette ์—์„œย python: select lint๋ฅผ ์ž…๋ ฅํ•˜์—ฌ flake8 ์„ ํƒ
  • command palette ์—์„œย python: run linting์„ ํ†ตํ•˜์—ฌ lint ํ™œ์„ฑํ™”

pre-commit์„ ์ด์šฉํ•œ commit check & ci improvement.

pre-commit์„ installํ•˜๊ฒŒ ๋˜๋ฉด ./git/hook/pre-commitํด๋”๊ฐ€ ์ƒ์„ฑ๋˜๊ณ  ์ด๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ git commit์‹œ .pre-commit-config.yaml ์— ์ •์˜๋œ ๋ชจ๋“ˆ์„ ์‹คํ–‰ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

flake8 pre-commit : https://flake8.pycqa.org/en/latest/user/using-hooks.html

repos:
  - repo: https://github.com/ambv/black
    rev: 21.7b0
    hooks:
      - id: black
        language_version: python3.9
  - repo: https://github.com/pycqa/flake8
    rev: 3.9.2
    hooks:
      - id: flake8

Configuration for flake8

[flake8]
    ignore = E203, E266, E501, W503, F403, F401 # set ignore errors
    max-line-length = 89
    max-complexity = 18
    select = B,C,E,F,W,T4,B9
# select
# C90 : Allย C90ย class violations are reported when the user specifies  
# E : llย Eย class violations are โ€œerrorsโ€ reported by pycodestyle
# F : Allย Fย class violations are reported by pyflakes
# W : Allย Wย class violations are โ€œwarningsโ€ reported by pycodestyle

flake8ย --max-complexity

pre-commit์„ ์ˆ˜ํ–‰ํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ๋˜๊ณ , git commit ์ปค๋งจ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด๋„ ๋™์ผํ•œ ์ ˆ์ฐจ๋ฅผ ๊ฑฐ์นฉ๋‹ˆ๋‹ค.

pre-commit run --all-files
black....................................................................Passed
flake8...................................................................Passed

Error and violation code

์•„๋ž˜ ๋งํฌ๋Š” PEP8์— ์ •์˜๋œ ERROR ์™€ Violation code์— ๋Œ€ํ•œ ์„ค๋ช…์ž…๋‹ˆ๋‹ค. ์œ„ ignoreํ•ญ๋ชฉ์—์„œ ํ•„์š”์— ๋”ฐ๋ผ ๋„ฃ์–ด์ฃผ๋ฉด, ํ•ด๋‹น ํ•ญ๋ชฉ์€ ์ฒดํฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

https://flake8.pycqa.org/en/latest/user/error-codes.html

https://pep8.readthedocs.io/en/latest/intro.html

Code coverage

์ฝ”๋“œ์˜ Quality๋ฅผ ๋†’์ด๋Š” ๋ฐฉ๋ฒ•์ค‘ ํ•˜๋‚˜๋Š” ํ…Œ์ŠคํŠธ์˜ ์ž‘์„ฑ์ž…๋‹ˆ๋‹ค. Code coverage๋Š” ์–ผ๋งˆ๋‚˜ ํ…Œ์ŠคํŠธ๊ฐ€ ์ž˜๋˜์–ด ์žˆ๋Š”์ง€๋ฅผ ์•Œ์•„ ๋ณด๋Š” ์ง€ํ‘œ๋กœ, coverage๊ฐ€ ๋†’์„ ์ˆ˜๋ก ํ”„๋กœ๊ทธ๋žจ์ด ๋™์ž‘ํ•  ๋•Œ ์˜ค๋ฅ˜๋ฅผ ์ค„์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์‹œ์Šคํ…œ์ด ๋ณต์žกํ•  ์ˆ˜๋ก, ๋งŽ์€ ๊ฐœ๋ฐœ์ž๊ฐ€ ๊ฐœ๋ฐœ์— ์ฐธ์—ฌํ•  ์ˆ˜๋ก ํ…Œ์ŠคํŠธ์™€ ํ…Œ์ŠคํŠธ coverage check๋Š” ๊ฐœ๋ฐœ์— ๋ฐ˜๋“œ์‹œ ํ•„์š”ํ•œ ์š”์†Œ ์ž…๋‹ˆ๋‹ค.

์„ค์น˜

pip install coverage

run

coverage run --omit "venv/*" test_file_path

report

coverage report

coverage reportName       Stmts   Miss  Cover
--------------------------------------------
app/main.py                  8      1    88%
app/routers/query.py         5      1    80%
test/test_endpoints.py      15      6    60%
--------------------------------------------
TOTAL                       28      8    71%

HTML report

coverage html

coverage result in index.html

main.html coverage