HOW TO Start (Python) Project

(ํŒŒ์ด์ฌ์œผ๋กœ) ํ”„๋กœ์ ํŠธ ์‹œ์ž‘ํ•  ๋•Œ ํ•„์š”ํ•œ ๊ฒƒ๋“ค

ย 

July. 4, 2020

@DoonDoony

Table of contents

๐Ÿ”จ Basic Project Settings

๐Ÿณ Docker Settings

๐Ÿ—‚ Workflow Settings

๐Ÿš€ HOW TO START (PYTHON) PROJECT

WHAT TO MAKE

๐Ÿ“ฅย  ย ๋…๋ฆฝ๋œ ํŒŒ์ด์ฌ ๋Ÿฐํƒ€์ž„ ์‚ฌ์šฉ์„ ์œ„ํ•œ ํŒŒ์ด์ฌ ๊ฐ€์ƒํ™˜๊ฒฝ ์„ค์ •

๐ŸŒธย  ย ์ข€ ๋” ์˜ˆ์œ ์ปค๋ฐ‹ ๋ฉ”์‹œ์ง€๋ฅผ ๋งŒ๋“œ๋Š” gitmoji

๐Ÿ”จย  ย ๋™์ผํ•œ ์ฝ”๋“œ ์ปจ๋ฒค์…˜์„ ์ง€ํ‚ฌ ์ˆ˜ ์žˆ๋„๋ก ์ฝ”๋“œ ํฌ๋งคํ„ฐ

๐Ÿ“ ย  ย ์˜ฌ๋ฐ”๋ฅธ ์ฝ”๋“œ ์ž‘์„ฑ์„ ์œ„ํ•œ ์ •์  ํƒ€์ž… ๋ถ„์„๊ธฐ

๐Ÿ“ฆย  ย ์ตœ์‹  ํŒจํ‚ค์ง€ ๋งค๋‹ˆ์ €๋ฅผ ํ†ตํ•œ ํ”„๋กœ์ ํŠธ ๊ด€๋ฆฌ

๐Ÿš”ย  ย ๋ณด์•ˆ/๋ฏผ๊ฐ ์ •๋ณด๋ฅผ ๋…ธ์ถœํ•˜์ง€ ์•Š๊ณ  ๊ด€๋ฆฌ

โš™๏ธย  ย ์šด์˜ ํ™˜๊ฒฝ ๊ฐ๊ฐ์˜ ์„ค์ • ํŒŒ์ผ ๊ด€๋ฆฌ

Basic Course

๐Ÿš€ HOW TO START (PYTHON) PROJECT

WHAT TO MAKE

๐Ÿณย  ย ๋น ๋ฅธ ์˜จ๋ณด๋”ฉ๊ณผ ์ผ๊ด€๋œ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ ๊ตฌ์ถ•์„ ์œ„ํ•œ ๋„์ปค ์„ค์ •

โœ…ย  ย ์ตœ์†Œํ•œ์˜ CI ํ™˜๊ฒฝ ๊ตฌ์ถ•

๐Ÿš•ย  ย ์ฝ”๋“œ๋กœ CD ํ™˜๊ฒฝ ๊ด€๋ฆฌํ•˜๊ธฐ (w/ Fabric)

๐Ÿทย  ย Git tag ๊ฐ„๋‹จํ•˜๊ฒŒ ์‚ฌ์šฉํ•ด๋ณด๊ธฐ

๐Ÿ‘ฉโ€๐Ÿ’ปย  ย ์—…๋ฌด๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•œ ์ฃผ๊ด€์ ์ธ ์ „๋žต (Work Flow)

advanced Course

๐Ÿš€ HOW TO START (PYTHON) PROJECT

Oh, Why?

๋‹ค์Œ ํ”„๋กœ์ ํŠธ๋ฅผ ๋งŒ๋“ค ๋•Œ On-Boarding ๋น„์šฉ์ด ์ ˆ๊ฐ๋ฉ๋‹ˆ๋‹ค

์‚ฌ์†Œํ•œ ๋ฌธ์ œ๋ฅผ ๋ฏธ๋ฆฌ ๋ฐœ๊ฒฌํ•˜๊ณ , ์ตœ๋Œ€ํ•œ ์ž๋™์œผ๋กœ ํ•ด๊ฒฐ๋˜๋„๋ก ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค

์˜คํ”ˆ์†Œ์Šค์˜ ํ”„๋กœ์ ํŠธ ๊ตฌ์„ฑ์— ๋Œ€ํ•ด ์ดํ•ดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค (์–ธ์–ด/ํ”„๋ ˆ์ž„์›Œํฌ๋ณ„ ์ƒ์ด)

๋‹ค๋ฅธ ์‚ฌ๋žŒ๊ณผ ํ˜‘์—…ํ•  ๋•Œ ๊ทœ์น™์„ ๊ฐ€์ง€๊ณ  ์ผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค

๊ฐœ๋ฐœ/์ด์Šˆ์— ๋Œ€ํ•œ ํžˆ์Šคํ† ๋ฆฌ ๊ด€๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค

Q. ์ด๊ฑธ ์•Œ๋ฉด ์–ด๋–ค ์žฅ์ ์ด ์žˆ๋‚˜์š”?

๐Ÿš€ HOW TO START (PYTHON) PROJECT

Oh, Why?

๋ผ๊ณ  ํ”„๋กœ์ ํŠธ ์„ธํŒ…๋งŒ ํ•ด๋†“๊ณ  ๋งจ๋‚  ์‹œ์ž‘๋„ ์•ˆํ•˜๋Š” ๋ฐฉ๋ง์ด ๊นŽ๋Š” ๋…ธ์ธ 2๋…„์ฐจ๊ฐ€ ๋งํ•ฉ๋‹ˆ๋‹ค

๐Ÿ”จ Basic project settings

Git, Python Virtual Environment, Code Formatter, Linter, ...

๐Ÿš€ HOW TO START (PYTHON) PROJECT

Create Virtual Env

Pyenv ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ€์ƒํ™˜๊ฒฝ์„ ์„ค์ •ํ•ด์š”

๐Ÿš€ HOW TO START (PYTHON) PROJECT

Create Virtual Env (1)

# MacOS์— ๊ธฐ๋ณธ์œผ๋กœ ์„ค์น˜๋œ System Python์„ ๊ธฐ์ค€์œผ๋กœ ์„ค์น˜๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค
$ python --version
Python 2.7.16

# HomeBrew๋ฅผ ์‚ฌ์šฉํ•œ ์„ค์น˜๋Š” ๊ถŒ์žฅํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค (Dependency๋กœ Python3 ๋Ÿฐํƒ€์ž„์ด ์„ค์น˜๋จ)
# pyenv-virtualenv ํ”Œ๋Ÿฌ๊ทธ์ธ ๊ฐ™์ด ์„ค์น˜๋˜์–ด์„œ ์ข‹์Šต๋‹ˆ๋‹ค
$ curl https://pyenv.run | bash

# ์•„๋ž˜ PATH์™€ Pyenv Hook์„ .zshrc์— ๋ณต๋ถ™ํ•ด์ฃผ์„ธ์š”
$ cat <<EOF >> .zshrc
$ export PATH="$HOME/.pyenv/bin:$PATH"
$ eval "$(pyenv init -)"
$ eval "$(pyenv virtualenv-init -)"
$ EOF

# Reloading .zshrc
$ source ~/.zshrc

# Check pyenv installation
$ pyenv --version
pyenv 1.2.18

๐Ÿš€ HOW TO START (PYTHON) PROJECT

Create Virtual Env (2)

# pyenv๋ฅผ ์‚ฌ์šฉํ•ด์„œ Python 3.8.3 ์„ ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค
$ pyenv install 3.8.3

# pyenv-virtualenv ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์‚ฌ์šฉํ•ด์„œ ํ”„๋กœ์ ํŠธ์šฉ ๊ฐ€์ƒํ™˜๊ฒฝ์„ ๋งŒ๋“ญ๋‹ˆ๋‹ค
$ pyenv virtualenv 3.8.3 creamheroes

# ์„ค์น˜ ๊ฐ€๋Šฅํ•œ pyenv์˜ Python ๋ฒ„์ „์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค
$ pyenv install --list

# ๋งŒ๋“ค์—ˆ๋˜ ๊ฐ€์ƒํ™˜๊ฒฝ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ œ๊ฑฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค
$ pyenv uninstall creamheroes

# ์„ค์น˜ํ–ˆ๋˜ Python ๋ฒ„์ „์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ œ๊ฑฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค
$ pyenv uninstall 3.8.3

๐Ÿš€ HOW TO START (PYTHON) PROJECT

Create Project w/ Git

ํ”„๋กœ์ ํŠธ ๋””๋ ‰ํ† ๋ฆฌ๋ฅผ ๋งŒ๋“ค๊ณ  Git ์„ค์ •์„ ํ•ด๋ณด์•„์š”

๐Ÿš€ HOW TO START (PYTHON) PROJECT

Create Project w/ Git

# ๋””๋ ‰ํ† ๋ฆฌ ์ƒ์„ฑ
~ $ mkdir -p Tutorial/creamheroes
~ $ cd Tutorial/creamheroes

# Git ์ €์žฅ์†Œ ๋งŒ๋“ค๊ธฐ
~/creamheroes $ git init

# Git local config๋กœ Author, Email ๊ฐ’ ์„ค์ •ํ•˜๊ธฐ
~/creamheroes $ git config --local user.name "DoonDoony"
~/creamheroes $ git config --local user.email "cream.doondoon@gmail.com"

# .gitignore ์ƒ์„ฑ (https://gitignore.io API๋ฅผ ์‚ฌ์šฉ)
~/creamheroes $ curl https://www.toptal.com/developers/gitignore/api/macos,python,django,pycharm,git,vim > .gitignore

# pyenv ์ž๋™ ํ™œ์„ฑํ™”๋ฅผ ์œ„ํ•œ .python-version ํŒŒ์ผ ์ƒ์„ฑ
~/creamheroes $ echo 'creamheroes' > .python-version
(creamheroes) ~/creamheroes $ python --version
Python 3.8.3

# ๋˜๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํ™œ์„ฑํ™” ํ•  ์ˆ˜ ์žˆ์Œ
~/creamheroes $ pyenv shell creamheroes
(creamheroes) ~/creamheroes $ python --version
Python 3.8.3

๐Ÿš€ HOW TO START (PYTHON) PROJECT

Create Project w/ Git

์—ฌ๊ธฐ๊นŒ์ง€ ์ž‘์—…ํ–ˆ์œผ๋‹ˆ๊นŒ Commit ํ•˜...๊ธฐ ์ „์—

์ข€ ๋” Commit Message ๋ฅผ ์˜ˆ์˜๊ฒŒ ์ ๊ธฐ ์œ„ํ•ด

๐ŸŒธ gitmoji ๋ฅผ ์„ค์น˜ํ•ด๋ด์š”

๐Ÿš€ HOW TO START (PYTHON) PROJECT

Create Project w/ Git

# NPM Global ์„ค์น˜๋กœ gitmoji-cli ์„ค์น˜
$ npm i -g gitmoji-cli
$ gitmoji --version
3.2.6

# ํ”„๋กœ์ ํŠธ ๋””๋ ‰ํ† ๋ฆฌ์—์„œ
(creamheroes) ~/creamheroes $ gitmoji --init
โœ” Gitmoji commit hook created successfully

๐Ÿš€ HOW TO START (PYTHON) PROJECT

Create Project w/ Git

Gitmoji ์„ค์น˜ํ–ˆ์œผ๋‹ˆ ์ด์ œ ์ง„์งœ๋กœ ์ฒซ Commit?

Commit ์ „์— ์—ฌ๋Ÿฌ๊ฐ€์ง€ ๋ฌธ์ œ๋ฅผ ๋ฏธ๋ฆฌ ๋ฐœ๊ฒฌํ•ด์ฃผ๋Š”

pre-commit ํ›…๋„ ๊ฐ™์ด ์ ์šฉํ•ด๋ด์š”

๐Ÿš€ HOW TO START (PYTHON) PROJECT

Create Project w/ Git

# ์‹œ์Šคํ…œ ์ „์—ญ์— ํŒŒ์ด์ฌ ํŒจํ‚ค์ง€ ๋งค๋‹ˆ์ €์ธ Poetry๋ฅผ ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค
# NOTE: ๋ฐ˜๋“œ์‹œ ์‹œ์Šคํ…œ ๊ธฐ๋ณธ Python ๋Ÿฐํƒ€์ž„์œผ๋กœ ์„ค์น˜ํ•˜๋„๋ก pyenv ์„ค์ •์„ ๋ณ€๊ฒฝํ•ด์ฃผ์„ธ์š”
$ pyenv shell system
$ python --version
Python 2.7.16

# Poetry๋Š” ๊ธฐ๋ณธ Python ํŒจํ‚ค์ง€ ๋งค๋‹ˆ์ €์ธ pip๊ฐ€ ์•„๋‹ˆ๋ผ, ๊ณต์‹ ํ™ˆํŽ˜์ด์ง€์—์„œ ๊ถŒ์žฅํ•˜๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค
$ curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python
$ poetry --version
Poetry version 1.0.5

# ํ˜„์žฌ ํ”„๋กœ์ ํŠธ์— Poetry๋ฅผ ํ™œ์„ฑํ™” ํ•˜๊ณ  pre-commit์„ ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค
$ poetry init

This command will guide you through creating your pyproject.toml config.
# ...์ดํ•˜ ์ƒ๋žต

# DevDependency๋กœ ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค
$ poetry add -D pre-commit

# pre-commit ์‹คํ–‰์— ํ•„์š”ํ•œ ํ›…์„ ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค
$ pre-commit install
pre-commit installed at .git/hooks/pre-commit

๐Ÿš€ HOW TO START (PYTHON) PROJECT

Create Project w/ Git

# ์•„๋ž˜ ์„ค์ •์„ ์ถ”๊ฐ€ํ•ด์„œ Commit ๋˜๊ธฐ ์ „์— ์•„๋ž˜ ํ•ญ๋ชฉ๋“ค์„ ์ฒดํฌํ•˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค
# XXX: Python ๊ด€๋ จ ์„ค์ •์€ ์ถ”ํ›„์— ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค
(creamheroes) ~/creamheroes $ cat <<EOF >> .pre-commit-config.yaml 
$ default_language_version:
$   python: python3.8
$ repos:
$   - repo: https://github.com/pre-commit/pre-commit-hooks
$     rev: v2.5.0
$     hooks:
$       - id: check-byte-order-marker
$       - id: trailing-whitespace
$       - id: end-of-file-fixer
$       - id: check-yaml
$       - id: check-added-large-files
$ EOF

# ์–ต์ง€๋กœ pre-commit ํ›…์— ๊ฑธ๋ฆฌ๊ฒŒ ํ•˜๋ ค๊ณ  ํŒŒ์ผ์˜ EOF๋ฅผ ์ง€์›Œ๋ณผ๊ฒŒ์š”
$ truncate -s -1 .gitignore

# ํ•ด๋‹น ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์ปค๋ฐ‹ํ•˜๋ฉด์„œ ์ •์ƒ์ ์œผ๋กœ ๋™์ž‘ํ•˜๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค
(creamheroes) ~/creamheroes $ git commit
[INFO] Initializing environment for https://github.com/pre-commit/pre-commit-hooks.
[INFO] Installing environment for https://github.com/pre-commit/pre-commit-hooks.
[INFO] Once installed this environment will be reused.
[INFO] This may take a few minutes...
Trim Trailing Whitespace.................................................Passed
Fix End of Files.........................................................Failed
- hook id: end-of-file-fixer
- exit code: 1
- files were modified by this hook

Fixing .gitignore

Check Yaml...............................................................Passed
Check for added large files..............................................Passed

# ๋‹ค์‹œ add ํ•  ๋•Œ EOF๊ฐ€ ์ž๋™์œผ๋กœ ์ถ”๊ฐ€๋œ๊ฒƒ์ด ๋ณด์ž…๋‹ˆ๋‹ค
$ git add -p
$ git commit

๐Ÿš€ HOW TO START (PYTHON) PROJECT

Create Project w/ Git

์—ฌ๊ธฐ๊นŒ์ง€ ์ž‘์—…ํ–ˆ์œผ๋‹ˆ ์ง„์งœ Commit ํ•˜๊ณ 

Github์—์„œ ๋ฆฌ๋ชจํŠธ ์ €์žฅ์†Œ๋„ ํ•˜๋‚˜ ์ƒ์„ฑํ•ด๋ณผ๊ฒŒ์š”

๐Ÿš€ HOW TO START (PYTHON) PROJECT

Create Project w/ Git

๐Ÿš€ HOW TO START (PYTHON) PROJECT

Create Project w/ Git

# ๋ฆฌ๋ชจํŠธ ์ €์žฅ์†Œ๋ฅผ ๋กœ์ปฌ์— ref๋กœ ๋“ฑ๋กํ•˜๊ณ , ํ˜„์žฌ๊นŒ์ง€์˜ ์ž‘์—…๋‚ด์šฉ์„ Push ํ•ฉ๋‹ˆ๋‹ค
(creamheroes) ~/creamheroes $ git remote add origin https://github.com/DoonDoony/creamheroes.git
(creamheroes) ~/creamheroes $ git push origin master

๐Ÿš€ HOW TO START (PYTHON) PROJECT

Code Format / Lint

Code Quality์™€ Convention์„ ๋งž์ถฐ์ฃผ๋Š”

Code Formatter, Linter, ์ •์  ๋ถ„์„๊ธฐ๋ฅผ ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค

๐Ÿš€ HOW TO START (PYTHON) PROJECT

Code Format / Lint

Code Formatter ์ธ black

์ž๋™์œผ๋กœ import ์ˆœ์„œ๋ฅผ ์ •๋ ฌํ•ด์ฃผ๋Š” isort

์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๋ณ€์ˆ˜๋‚˜ import๋ฅผ ์ž๋™์œผ๋กœ ์ •๋ฆฌํ•ด์ฃผ๋Š” autoflake

PEP Guide์— ๋งž์ง€ ์•Š๋Š” ์ฝ”๋“œ๋ฅผ ์•Œ๋ ค์ฃผ๋Š” flake8

๋งˆ์ง€๋ง‰์œผ๋กœ, ํŒŒ์ด์ฌ์˜ ์ •์ ํƒ€์ž… ๋ถ„์„๊ธฐ์ธ mypy์„ ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค

๐Ÿš€ HOW TO START (PYTHON) PROJECT

Code Format / Lint

# ์‹ค์ œ ์šด์˜์—์„œ๋Š” ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋ฏ€๋กœ, DevDependency๋กœ์จ ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค
(creamheroes) ~/creamheroes $ poetry add -D black isort autoflake flake8 flake8-django mypy django-stubs

# ์œ„์˜ ๋„๊ตฌ๋“ค์€ setup.cfgํŒŒ์ผ์˜ ์„ค์ •์„ ๋”ฐ๋ฆ…๋‹ˆ๋‹ค. ini์™€ ๋น„์Šทํ•œ ํ˜•์‹์œผ๋กœ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค 
# https://packaging.python.org/guides/distributing-packages-using-setuptools/#setup-cfg
(creamheroes) ~/creamheroes $ cat <<EOF >> setup.cfg
[flake8]
# B = bugbear
# E = pycodestyle errors
# F = flake8 pyflakes
# W = pycodestyle warnings
# B9 = bugbear opinions,
# ISC = implicit str concat
select = B, E, F, W, B9, ISC
ignore = E203, E266, E501, W503, B305, W504
max-line-length = 120
exclude = **/migrations/*

[isort]
combine_as_imports = true
default_section = THIRDPARTY
include_trailing_comma = true
line_length = 79
multi_line_output = 5
use_parentheses = true

[mypy]
python_version = 3.8
warn_return_any = True
disallow_untyped_defs = True
ignore_missing_imports = True
plugins = mypy_django_plugin.main

[mypy.plugins.django-stubs]
django_settings_module = creamheroes.conf.settings.base

[mypy_django_plugin]
ignore_missing_settings = true
ignore_missing_model_attributes = True

[mypy-*.migrations.*]
# Django migrations should not produce any errors:
ignore_errors = True

[tool:pytest]
addopts = -v -p no:warnings --nomigrations --cov=. --no-cov-on-fail
DJANGO_SETTINGS_MODULE = creamheroes.conf.settings.test
python_paths = creamheroes
console_output_style = progress
cache_dir = .pytest_cache
EOF

# Python์€ ์•„์ง๋„ ๋ฐœ์ „์ค‘์ด๊ธฐ ๋•Œ๋ฌธ์—... ์ง€๋“ค ๋ฉ‹๋Œ€๋กœ ์„ค์ •ํŒŒ์ผ์„ ์ •์˜ํ•˜๋Š” ๋ถ€๋ถ„์ด ๋‹ค๋ฆ…๋‹ˆ๋‹ค
# https://github.com/psf/black/issues/688
# 'black' ์ฝ”๋“œ ํฌ๋งคํ„ฐ๋Š” 'pyproject.toml' ํŒŒ์ผ์— ๊ทธ ์„ค์ •์„ ์ •์˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค
(creamheroes) ~/creamheroes $ : << 'END_COMMENT'
[tool.black]
line-length = 119
target-version = ['py38']
include = '\.pyi?$'
exclude = '''
(
  /(
      \.eggs         # exclude a few common directories in the
    | \.git          # root of the project
    | \.hg
    | \.mypy_cache
    | \.tox
    | \.venv
    | \.python-version
    | _build
    | buck-out
    | build
    | dist
  )/
)
'''
[build-system]
requires = ["poetry>=0.12"]
build-backend = "poetry.masonry.api"
END_COMMENT

๐Ÿš€ HOW TO START (PYTHON) PROJECT

Code Format / Lint

์„ค์ •์€ ์ •์˜ ํ–ˆ์œผ๋‹ˆ, ์•„๋ž˜ ์„ธ ๊ตฐ๋ฐ์—์„œ ์ด๊ฒƒ์„ ์‹คํ–‰ํ•˜๋ฉด ์ข‹์„๊ฒƒ ๊ฐ™์•„์š”

ย 

1. PyCharm

2. pre-commit hook

3. CI (Github Actions) ๋„๊ตฌ (๋‚˜์ค‘์—)

๐Ÿš€ HOW TO START (PYTHON) PROJECT

Code Format / Lint

๐Ÿš€ HOW TO START (PYTHON) PROJECT

Code Format / Lint

๐Ÿš€ HOW TO START (PYTHON) PROJECT

Code Format / Lint

# .pre-commit-config.yaml์— ์•„๋ž˜ ์„ค์ •๋“ค์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค
(creamheroes) ~/creamheroes $ cat <<EOF >> .pre-commit-config.yaml
  - repo: https://github.com/python/black
    rev: 19.10b0
    hooks:
      - id: black
  - repo: local
    hooks:
      - id: isort
        name: isort
        entry: python -m isort.__main__
        language: system
        types: [python]
  - repo: local
    hooks:
      - id: autoflake
        name: autoflake
        entry: autoflake
        language: system
        args: ['--in-place', '--remove-all-unused-imports']
  - repo: local
    hooks:
      - id: flake8
        name: flake8
        entry: flake8
        language: python
        types: [python]
        args: ['--config', 'setup.cfg']
  - repo: local
    hooks:
      - id: mypy
        name: mypy
        entry: mypy
        language: python
        types: [python]
        args: ['--config-file', 'setup.cfg']
EOF

๐Ÿš€ HOW TO START (PYTHON) PROJECT

Code Format / Lint

์ž‘์—…์ด ๋๋‚ฌ์œผ๋‹ˆ Commit ํ•ฉ์‹œ๋‹ค

.py ํŒŒ์ผ์ด Stage์— ์—†์œผ๋ฉด, ํ›…์€ ๋™์ž‘ํ•˜์ง€ ์•Š์•„์š”

๐Ÿš€ HOW TO START (PYTHON) PROJECT

Start Django Project

์ด์ œ Django Project ๋ฅผ ์ƒ์„ฑํ•ด๋ด์š”

์˜์กด์„ฑ์„ ์„ค์น˜ํ•˜๊ณ , CLI๋ฅผ ํ†ตํ•ด ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค

๐Ÿš€ HOW TO START (PYTHON) PROJECT

Start Django Project

# ์ตœ์‹  ๋ฒ„์ „ django๋ฅผ ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค
(creamheroes) ~/creamheroes $ poetry add django

# django-admin์„ ํ†ตํ•ด ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค
# ๋ฐ˜๋“œ์‹œ ๋งจ ๋งˆ์ง€๋ง‰์— ํ˜„์žฌ ๊ฒฝ๋กœ์— ์„ค์น˜๋ฅผ ์˜๋ฏธํ•˜๋Š” ์˜จ์  (.) ์„ ๋ถ™์—ฌ์ค๋‹ˆ๋‹ค
(creamheroes) ~/creamheroes $ django-admin startproject creamheroes .
(creamheroes) ~/creamheroes $ lt --level 2
Permissions  Size User Date Created     Name
drwxr-xr-x      - doon 2020-07-04 08:32 ๏„• .
drwxr-xr-x      - doon 2020-07-04 08:33 โ”œโ”€โ”€ ๏„• creamheroes
.rw-r--r--      0 doon 2020-07-04 08:33 โ”‚  โ”œโ”€โ”€ ๎˜† __init__.py
.rw-r--r--    399 doon 2020-07-04 08:33 โ”‚  โ”œโ”€โ”€ ๎˜† asgi.py
.rw-r--r--  3,103 doon 2020-07-04 08:33 โ”‚  โ”œโ”€โ”€ ๎˜† settings.py
.rw-r--r--    753 doon 2020-07-04 08:33 โ”‚  โ”œโ”€โ”€ ๎˜† urls.py
.rw-r--r--    399 doon 2020-07-04 08:33 โ”‚  โ””โ”€โ”€ ๎˜† wsgi.py
.rwxr-xr-x    631 doon 2020-07-04 08:33 โ”œโ”€โ”€ ๎˜† manage.py
.rw-r--r--  2,137 doon 2020-07-04 08:33 โ”œโ”€โ”€ ๏…› poetry.lock
.rw-r--r--    269 doon 2020-07-04 08:32 โ”œโ”€โ”€ ๏…› pyproject.toml
.rw-r--r--    269 doon 2020-07-04 08:32 โ””โ”€โ”€ ๏…› setup.cfg

# settings ํŒŒ์ผ์„ ์˜ฎ๊ฒจ์ค๋‹ˆ๋‹ค. settings ํŒŒ์ผ์€ ๋‹ค๋ฅธ ํ”„๋ ˆ์ž„์›Œํฌ์— ๋น„์œ ํ•˜๋ฉด...
# Lavavel์˜ Config ๋””๋ ‰ํ† ๋ฆฌ ๋˜๋Š” Spring์˜ pom.xml? application.properties? ์ž˜ ๋ชจ๋ฅด๊ฒ ๋„ค์š”
(creamheroes) ~/creamheroes $ mkdir -p creamheroes/conf/settings
(creamheroes) ~/creamheroes $ cd creamheroes
(creamheroes) ~/creamheroes/creamheroes $ mv __init__.py asgi.py settings.py urls.py wsgi.py conf/
(creamheroes) ~/creamheroes/creamheroes $ touch conf/settings/__init__.py
(creamheroes) ~/creamheroes/creamheroes $ mv conf/settings.py conf/settings/base.py

# Ripgrep ์œผ๋กœ ํ•„์š”ํ•œ ๋ถ€๋ถ„์„ ์ฐพ์•„๋ฐ”๊ฟ”์ค๋‹ˆ๋‹ค
(creamheroes) ~/creamheroes/creamheroes $ brew install ripgrep

# GNU sed ์—์„œ๋Š” ๋™์ž‘์ด ์กฐ๊ธˆ ๋‹ค๋ฆ…๋‹ˆ๋‹ค (sed -i 's/find/replace/g' ๋กœ๋„ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค)
(creamheroes) ~/creamheroes/creamheroes $ rg '\bcreamheroes.settings' -l | xargs sed -i '' 's/creamheroes.settings/creamheroes.conf.settings.base/g'
(creamheroes) ~/creamheroes/creamheroes $ rg '\bcreamheroes.urls' -l | xargs sed -i '' 's/creamheroes.urls/creamheroes.conf.urls/g'
(creamheroes) ~/creamheroes/creamheroes $ rg '\bcreamheroes.wsgi' -l | xargs sed -i '' 's/creamheroes.wsgi/creamheroes.conf.wsgi/g'

# ๊ฐœ๋ฐœ ์„œ๋ฒ„๊ฐ€ ์ •์ƒ ๋™์ž‘ ํ•˜๋Š”์ง€ ์‹คํ–‰ํ•ด ๋ด…๋‹ˆ๋‹ค
(creamheroes) ~/creamheroes/creamheroes $ cd ..
(creamheroes) ~/creamheroes/ $ ./manage.py runserver

๐Ÿš€ HOW TO START (PYTHON) PROJECT

Start Django Project

๊ทธ๋Ÿผ ์ด์ œ ํ”„๋กœ์ ํŠธ ์„ธํŒ…๋„ ํ–ˆ์œผ๋‹ˆ

Commit์„ ํ•  ๋•Œ๊ฐ€ ๋˜๊ธด ๋ญ๊ฐ€ ๋ผ!

์ค‘์š”ํ•œ ์ •๋ณด๋ฅผ ์ง€์šฐ๊ณ  ๊ณต๊ฐœ ์ €์žฅ์†Œ์— ์˜ฌ๋ ค์•ผ ํ•ฉ๋‹ˆ๋‹ค

๐Ÿš€ HOW TO START (PYTHON) PROJECT

Start Django Project

Django Settings์˜ SECRET_KEY๋Š”

์•”ํ˜ธํ™” ์„œ๋ช…๊ณผ ํ•ด์‰ฌ์— ์‚ฌ์šฉ๋˜๋Š” salt์˜ ์—ญํ• ๋„ ํ•ฉ๋‹ˆ๋‹ค

๋”ฐ๋ผ์„œ ๊ณต๊ฐœ ์ €์žฅ์†Œ์— ๋…ธ์ถœ๋˜๋ฉด ๋ณด์•ˆ์— ์œ„ํ—˜์ด ์žˆ๊ธฐ์—

์˜๋ฏธ์—†๋Š” ์ž„์˜๊ฐ’์œผ๋กœ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค

๐Ÿš€ HOW TO START (PYTHON) PROJECT

Start Django Project

์ด๋Ÿฐ ์ค‘์š”ํ•œ ์ •๋ณด๋Š” .env ํŒŒ์ผ์„ ์ƒ์„ฑํ•˜์—ฌ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค

.env ํŒŒ์ผ์—์„œ ๊ฐ’์„ ์ฝ์–ด ์ฃผ์ž…ํ•ด์ฃผ๋Š”

django-environ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์„ค์น˜ํ•˜๊ณ  ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค

๐Ÿš€ HOW TO START (PYTHON) PROJECT

Start Django Project

# django-environ ์„ ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค
(creamheroes) ~/creamheroes $ poetry add django-environ
(creamheroes) ~/creamheroes $ touch .env
(creamheroes) ~/creamheroes $ cat <<EOF >> .env
SECRET_KEY=<์‹ค์ œ SECRET_KEY>
EOF

# ๊ณต๊ฐœ ์ €์žฅ์†Œ์— .env ํŒŒ์ผ ๋˜ํ•œ ์˜ฌ๋ฆด ์ˆ˜ ์—†์œผ๋ฏ€๋กœ (์ด๋ฏธ .gitignore์— ๋“ฑ๋ก๋˜์–ด ์žˆ์„๊ฑฐ์—์š”)
# ์ด๋Ÿฐ ๊ฐ’๋“ค์ด ํ•„์š”ํ•˜๋‹ค๋Š” ํžŒํŠธ๊ฐ€ ์žˆ์–ด์•ผ ํ˜‘์—…ํ•˜๋Š” ์‚ฌ๋žŒ์ด ๊ฐ’์„ ๋ฌผ์–ด๋ณผ ์ˆ˜ ์žˆ๊ฒ ์ฃ ?
# NOTE: .env.sample ์—์„œ ์‹ค์ œ ๊ฐ’์„ ๋ฐ˜๋“œ์‹œ ์ œ๊ฑฐํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค
(creamheroes) ~/creamheroes $ cp .env .env.sample

๐Ÿš€ HOW TO START (PYTHON) PROJECT

Start Django Project

# creamheroes/conf/settings/base.py
import environ  # type: ignore

env = environ.Env()
environ.Env.read_env(os.environ.get("ENV_PATH"))

SECRET_KEY = env.str('SECRET_KEY', '********')  # ๊ฐ’์ด ์—†์–ด๋„ Default ๊ฐ’์„ ๋”ฐ๋ฅด๊ฒŒ ๋ฉ๋‹ˆ๋‹ค

๐Ÿš€ HOW TO START (PYTHON) PROJECT

Start Django Project

ENV_PATH ๋ผ๋Š” ํ™˜๊ฒฝ๋ณ€์ˆ˜๋Š” ์—†๋Š”๋ฐ ๐Ÿค” ์–ด๋–ป๊ฒŒ ์ •์˜ํ• ๊นŒ์š”?

direnv ๋ฅผ ์‚ฌ์šฉํ•ด์„œ, ํ”„๋กœ์ ํŠธ๋ณ„ ํ™˜๊ฒฝ๋ณ€์ˆ˜๋ฅผ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ์–ด์š”

๐Ÿš€ HOW TO START (PYTHON) PROJECT

Start Django Project

# direnv ๋ฅผ ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค
$ brew install direnv

# direnv ํ›…์„ ํ™œ์„ฑํ™” ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค
$ cat <<EOF >> ~/.zshrc
eval "$(direnv hook zsh)"
EOF

# direnv ๋ฅผ ํ™œ์„ฑํ™” ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” .envrc ํŒŒ์ผ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค
# .envrc ํŒŒ์ผ์— ์šฐ๋ฆฌ๊ฐ€ ์„ค์ •ํ•˜๋ ค๋Š” ํ™˜๊ฒฝ๋ณ€์ˆ˜๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค
(creamheroes) ~/creamheroes $ touch .envrc
(creamheroes) ~/creamheroes $ cat <<EOF >> .envrc
export ENV_PATH="$PWD/.env"
EOF

direnv: error /Users/doon/Tutorial/creamheroes/.envrc is blocked. Run `direnv allow` to approve its content

# direnv ๋ฅผ ํ™œ์„ฑํ™” ํ•ฉ๋‹ˆ๋‹ค
$ direnv allow .
direnv: loading ~/Tutorial/creamheroes/.envrc
direnv: export +ENV_PATH


# ํ™˜๊ฒฝ๋ณ€์ˆ˜๊ฐ€ ์ž˜ ๋“ฑ๋ก๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค
$ env | grep ENV_PATH
ENV_PATH=/Users/doon/Tutorial/creamheroes/.env

๐Ÿš€ HOW TO START (PYTHON) PROJECT

Start Django Project

ํ™˜๊ฒฝ๋ณ€์ˆ˜๋กœ ์ž˜ ์ ์šฉ๋˜์—ˆ๋Š”์ง€๋ฅผ

Django Interactive Shell ์„ ์‚ฌ์šฉํ•ด์„œ ํ™•์ธํ•ด๋ด์š”

๐Ÿš€ HOW TO START (PYTHON) PROJECT

Start Django Project

$ ./manage.py shell

Python 3.8.3 (default, May 19 2020, 23:35:46)
[Clang 11.0.0 (clang-1100.0.33.8)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from django.conf import settings
>>> settings.SECRET_KEY
'-2u)(2#bx8+#u6&cv7k$war6#)5sts+&rb6j*#)liiyxl0l#na'

๐Ÿš€ HOW TO START (PYTHON) PROJECT

Start Django Project

์ด์ œ ์ง„์งœ Commit ํ•˜๊ณ  ๋‹ค์Œ Docker ๊ตฌ์„ฑ์œผ๋กœ ๋„˜์–ด๊ฐ€๋ณด์•„์š”

๐Ÿ”ฅ Advenced project settings

Docker, CI/CD, Git tag, Workflow, Fabric...

๐Ÿš€ HOW TO START (PYTHON) PROJECT

Create Docker Image

Docker ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๊ฐœ๋ฐœ์„œ๋ฒ„๋ฅผ

์šด์˜๊ณผ ๋น„์Šทํ•œ ํ™˜๊ฒฝ์—์„œ ์‹คํ–‰๋˜๋„๋ก ์„ค์ •ํ•ด๋ณด์•„์š”

๐Ÿš€ HOW TO START (PYTHON) PROJECT

Create Docker Image

๋จผ์ €, ์‹ค์ œ๋กœ ๋ฐฐํฌ๋  Docker Image ๋ฅผ ๋งŒ๋“ค๊ธฐ์œ„ํ•ด

Dockerfile ์„ ์ž‘์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค

๐Ÿš€ HOW TO START (PYTHON) PROJECT

Create Docker Image

# Docker ๊ด€๋ จ๋œ ํŒŒ์ผ์€ ๋‹ค๋ฅธ ๋””๋ ‰ํ† ๋ฆฌ์— ์ •๋ฆฌํ•˜๋Š”๊ฑธ ์„ ํ˜ธํ•ด์š”
(creamheroes) ~/creamheroes $ mkdir .docker

# ์‹ค์ œ๋กœ ์„œ๋ฒ„ ๊ตฌ๋™์‹œ์—๋Š” ./manage.py runserver ๊ฐ€ ์•„๋‹Œ gunicorn์„ ์‚ฌ์šฉํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค
# Java Tomcat ๊ฐ™์€ ๋…€์„์ž…๋‹ˆ๋‹ค
(creamheroes) ~/creamheroes $ poetry add gunicorn

# Dockerfile ์€ ์•„๋ž˜์™€ ๊ฐ™์ด ์ž‘์„ฑํ•ด๋ดค์Šต๋‹ˆ๋‹ค
(creamheroes) ~/creamheroes $ cat <<EOF >> .docker/Dockerfile
FROM python:3.8.3-slim AS build
RUN apt-get update -y
RUN apt-get install -y --no-install-recommends build-essential gcc

RUN python -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"

RUN mkdir /code
WORKDIR /code

COPY requirements.txt .
RUN pip install -U pip
RUN pip install -r requirements.txt

FROM python:3.8.3-slim AS final
COPY --from=build /opt/venv /opt/venv
WORKDIR /code

COPY . .
ENV PATH="/opt/venv/bin:$PATH"
CMD ["gunicorn", "creamheroes.conf.wsgi", "--bind", "0.0.0.0:8000"]
EOF

๐Ÿš€ HOW TO START (PYTHON) PROJECT

Create Docker Image

Multi Stage Build ๋ฅผ ํ™œ์šฉํ•ด ์ด๋ฏธ์ง€ ์‚ฌ์ด์ฆˆ๋ฅผ ์ค„์ด๊ณ 

์บ์‹œ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์ตœ๋Œ€ํ•œ ํ™œ์šฉํžˆ ๋นŒ๋“œ์†๋„๋ฅผ ์˜ฌ๋ฆฝ๋‹ˆ๋‹ค

๐Ÿš€ HOW TO START (PYTHON) PROJECT

Create Docker Image

Docker Image ๋ฅผ ๋นŒ๋“œํ•˜๊ธฐ์ „์—

์ž์ฃผ ์“ธ ๊ฒƒ ๊ฐ™์€ ๋ช…๋ น์–ด๋ฅผ Makefile ์— ์ •์˜ํ•ด๋ณด๋ฉด

์–ด๋–จ๊นŒ์š”์ž‰ ๐Ÿค”

๐Ÿš€ HOW TO START (PYTHON) PROJECT

Create Docker Image

# Makefile ์—์„œ ์‚ฌ์šฉํ•  ๋งŒํ•œ ๋ช…๋ น์–ด ์„ธํŠธ์ž…๋‹ˆ๋‹ค
(creamheroes) ~/creamheroes $ cat <<EOF >> Makefile
.PHONY: build
build:
	@make freeze
	@docker image build . -t creamheroes:v1 -f .docker/Dockerfile

.PHONY: clean
clean:
	@docker image prune -f
	@docker container prune -f
	@docker network prune -f
	@docker volume prune -f

.PHONY: destroy
destroy:
	@docker container stop creamheroes || true
	@make clean > /dev/null

.PHONY: freeze
freeze:
	@poetry export -f requirements.txt -o requirements.txt --dev -n --without-hashes

.PHONY: log
log:
	@docker container logs -f creamheroes

.PHONY: open
open:
	@open http://localhost:8000

.PHONY: runserver
runserver:
	@docker container run --rm -d -p 8000:8000 --name creamheroes -t creamheroes:v1
EOF

๐Ÿš€ HOW TO START (PYTHON) PROJECT

Create Docker Image

์ด์ œ ์‹ค์ œ๋กœ ์ด๋ฏธ์ง€๋ฅผ ๋นŒ๋“œํ•˜๊ณ 

๋„์ปค ์ด๋ฏธ์ง€๋กœ ๊ฐœ๋ฐœ์„œ๋ฒ„๊ฐ€ ์ž˜ ๋™์ž‘ํ•˜๋Š”์ง€

ํ•œ ๋ฒˆ ํ™•์ธํ•ด๋ด์š” ๐Ÿค—

๐Ÿš€ HOW TO START (PYTHON) PROJECT

Create Docker Image

# ์ด๋ฏธ์ง€ ๋นŒ๋“œ ์‹œ์ž‘
(creamheroes) ~/creamheroes $ make build

# ์„œ๋ฒ„ ์‹œ์ž‘
(creamheroes) ~/creamheroes $ make runserver

# ๋ธŒ๋ผ์šฐ์ €์—์„œ ํ™•์ธ
(creamheroes) ~/creamheroes $ make open

# ์„œ๋ฒ„ ์ข…๋ฃŒ
(creamheroes) ~/creamheroes $ make destroy

๐Ÿš€ HOW TO START (PYTHON) PROJECT

Create Docker Image

์—ฌ๊ธฐ์„œ ํ•œ ๋ฒˆ ์ปค๋ฐ‹ํ•˜๊ณ 

์˜ค๋Š˜์€ ์—ฌ๊ธฐ๊นŒ์ง€... ๐Ÿƒ๐Ÿปโ€โ™‚๏ธ

๐Ÿš€ HOW TO START (PYTHON) PROJECT

Create Docker Image

๋‹ค์Œ์— ๋•œ๋นตํ• ์ผ ์žˆ์œผ๋ฉด ์ด์–ด์„œ ์ค€๋น„ํ•ด ๋ณผ๊ฒŒ์š”

Made with Slides.com