백엔드 개발자의 숙명은
최대한으로 기능을 분할하여
독립적으로 관리하기 용이한 코드를 짜는게 아닐까?
각각의 기능들은 수많은 변수를 고려하여
구현해야한다.
Don't trust user!
'사용자를 믿지 마라!' 가 가장 중요한 포인트이다.
TDD workflow는 Test Driven Development의 약자로
실패할 수 밖에 없는 코드를 짠 뒤 테스트케이스를 반복하면서
성공하는 코드를 완성해가는 과정이다.
만든 테스트케이스를 검증할 수 있도록
여러가지 라이브러리를 설치해보자.
python version 3.9.9 환경에서 실행한다.
01. poetry 설치
curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py
vi .zshrc
vi 편집기에 진입하면 하단에 아래 코드를 붙여넣는다.
export PATH="$HOME/.poetry/bin:$PATH"
vi 편집기에서 빠져나와서 아래 코드로 설정 완료한다.
source $HOME/.poetry/env
잘 설치되었는지 아래 코드로 확인한다. >>>1.1.12 버젼으로 확인됨
poetry --version
02. Django & Django ninja 설치
poetry add django==4.0 django-ninja==0.16.1
Django ninja는 fastAPI 처럼 개발할 수 있도록 도와준다.
fake fastAPI랄까..
- urls.py 설정할 때 유용
from django.contrib import admin
from django.urls import path
from ninja import NinjaAPI
api = NinjaAPI()
@api.get("/add")
def add(request, a: int, b: int):
return {"result": a + b}
urlpatterns = [
path("admin/", admin.site.urls),
path("api/", api.urls),
]
데코레이터(@)로 url을 감싸고 실행되는 함수가 오게하여
가독성이 높다.
path 로 연결된 "api/" 가 실행되면 데코레이터로 감싸진
모든 함수가 실행된다.
localhost:8000/api/docs
위의 주소로 swagger를 활용하여 함수를 실행시켜볼 수 있다.
03. black 설치
black은 code formatter 로 일관된 코드스타일을 유지하도록 도와준다.
poetry add black==21.12b0
pyproject.toml 파일에 코드를 추가하면 세부설정을 할 수 있다.
[tool.black]
line-length = 120
실행할때에는 터미널에 아래 코드를 입력한다.
poetry run black .
04. mypy 설치
strict 설정으로 type hint를 검사한다.
(typescript의 역할)
poetry add django-stubs==1.9.0 mypy==0.931
pyproject.toml 파일에 아래 코드를 추가하여 설정한다.
[tool.mypy]
plugins = ["mypy_django_plugin.main"]
python_version = 3.9
strict = true
[[tool.mypy.overrides]]
module = "*.migrations.*"
ignore_errors = true
[[tool.mypy.overrides]]
module = "manage"
ignore_errors = true
[tool.django-stubs]
django_settings_module = "sparta.settings"
overrides 설정으로 mypy 검사를 하지 않을 항목을 추가한다.
아래 코드를 터미널에 입력하여 실행한다.
poetry run mypy .
위의 코드가 뜨면 type error가 발견되지 않은 것이다.
05. isort 설치
import 순서를 정렬해주는 라이브러리이다.
poetry add isort==5.10.1
[tool.isort]
profile = "black"
06. test.sh 생성(쉬뱅)
어떤 프로그램을 사용해서 읽을 것인지
실패할 경우 어떻게 표현할 것인지 pipefail 로 설정해준다.
test.sh 이름으로 파일생성하여 아래와 같이 코드를 작성한다.
#!/usr/bin/env zsh
set -euo pipefail
export COLOR_GREEN='\e[0;32m'
export COLOR_NC='\e[0m' # No Color
echo "Run black"
poetry run black .
echo "Run isort"
poetry run isort .
echo "Run mypy"
poetry run mypy .
echo "Run tests"
python manage.py test
echo "${COLOR_GREEN}You are good to go!${COLOR_NC}"
permission denied 된다면 아래 코드를 먼저 실행시킨다.
sudo chmod 777 ./test.sh
터미널에 아래 코드를 작성하면
test.sh 내부의 라이브러리가 실행되면서 검사한다.
./test.sh
실패할 경우 아래와 같이 뜬다.
위와 같은 error 를 수정하면서 코드를 짜는 것이 TDD 방식이다.
07. Github actions 활용
git push 할때마다 자동으로 테스트하게 설정할 수 있다.
.github 디렉토리 생성 - workflows 디렉토리 생성 - ci.yaml or ci.yml 파일 생성
name: Django CI
on:
push:
jobs:
ci:
runs-on: ubuntu-latest
steps:
- name: Check out the codes
uses: actions/checkout@v2
- name: Setup python environment
id: setup-python
uses: actions/setup-python@v2
with:
python-version: 3.9.9
- name: Install Poetry
run: |
curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python
echo "${HOME}/.poetry/bin" >> $GITHUB_PATH
- name: Install dependencies
run: |
${HOME}/.poetry/bin/poetry install
- name: Test python project
run: |
poetry run python manage.py test
on: -> 액션이 언제 실행되는지 정의
jobs -> 하나의 workflow에 속한 job은 여러개 설정 할 수 있음
runs-on -> job이 실행되는 machine
steps -> 명령을 실행하거나 다른 action을 실행
uses -> 실행할 action을 가리킴
with -> action에 전달할 변수
run -> 실행할 명령어
run:| -> '|'은 value 가 여러줄이라는 것을 의미함
더 많은 메소드는 아래 링크를 참고하자.
https://docs.github.com/en/actions
GitHub Actions Documentation - GitHub Docs
Automate, customize, and execute your software development workflows right in your repository with GitHub Actions. You can discover, create, and share actions to perform any job you'd like, including CI/CD, and combine actions in a completely customized wo
docs.github.com
on: push 로 설정하였기 때문에 push 후
깃허브 actions 에서 ci(job)를 클릭하면
실시간으로 실행되는 작업을 확인할 수 있다.

08. pycharm 에서 Django 실행
장고서버 선택 후 이름 정하고 적용한다.
디버깅을 하고자하는 줄(중단점)을 찍고
swagger를 실행시키면 작성된 테스트가 자동으로
파이참에서 디버깅된다.
09. docker를 활용한 mySQL 설치
운영체제에 맞는 docker를 설치한다.
아래 코드로 3306 포트에서 실행되고 있는 파일이 있는지 확인한다.
netstat -vanp tcp | grep 3306
- docker 와 mySQL 연결하기
spartadb 라는 이름으로 DB 생성하고
[비밀번호] 로 docker의 3306 포트와 mysql 3306 포트를 연결시킨다.
docker run --name spartadb -e MYSQL_ROOT_PASSWORD=[비밀번호] -e TZ='Asia/Seoul' -d -p 3306:3306 mysql:8.0.25
docker에서 아래 DB가 확인되면 잘 연결된 것이다.
- pycharm 과 docker 연결하기
데이터베이스 '+' 툴을 클릭하여 mySQL를 선택하고
root 사용자를 입력하여 연결해준다.
console 창에서 버젼과 현재시간을 확인한다.
10. pymysql 설치
poetry add pymysql==1.0.2 types-PyMySQL==1.0.6
settings.py 의 DATABASES 부분에 아래 코드를 작성한다.
pymysql.install_as_MySQLdb()
DATABASES = {
"default": {
"ENGINE": "django.db.backends.mysql",
"NAME": "[DB이름]",
"USER": "root",
"PASSWORD": "[비밀번호]",
"HOST": "localhost",
"PORT": "3306",
}
}
CREATE DATABASE [DB이름]으로 DB를 생성한다.
mySQL의 스패너를 클릭하여 데이터베이스에 생성한 DB 이름을 입력해주면 연결 완료!
아래 코드를 입력하여 검사하여 docker의 DB와 잘 연결되었는지 확인한다.
./test.sh
11. Github actions를 tmate 툴 활용하여 디버깅
Django 에서 모델 생성 후 마이그레이션 과정을 거치는데
마이그레이션은 데이터베이스의 형상을 관리하는 것으로
변경이력을 보존하고 데이터를 되돌리는데 중요한 역할을 한다.
django.db.utils.OperationalError: (2003, "Can't connect to MySQL server on 'localhost' ([Errno 111] Connection refused)")
94Error: Process completed with exit code 1.
마이그레이션 후
github actions에서 위와 같은 에러메세지가 발생한다면
깃허브 액션을 디버깅하는 방법으로 에러를 수정한다.
test python project 에서 문제가 생겼음으로
ci.yaml 파일에서 이전 단계에 아래 코드를 적어준다.
- name: Setup tmate session
uses: mxschmitt/action-tmate@v3
코드를 추가하고 push 를 한뒤 actions 에 접근하면
무한루프가 도는데, ssh 로 시작되는 코드를 복사하여
터미널에서 실행시키면 디버깅할 수 있는 창이 생성된다.
sudo systemctl status mysql
sudo systemctl start mysql
mysql -uroot -proot
차례대로 입력하여 mysql 을 실행시킬 수 있다.
정상적으로 실행된다면 아래와 같은 창이 뜬다.
mysql 안에는 계정정보가 저장되어있음으로
root 비밀번호를 변경할 수 있다.
use mysql;
FLUSH PRIVILEGES;
ALTER USER 'root'@'localhost' IDENTIFIED BY '[비밀번호]';
exit;
sql 밖으로 나와서 변경된 비밀번호로 접근 가능한지 확인한다.
현재 시간대로 변경해주기 위해 sql을 실행시키고 현재 시간을 조회한다.
SELECT NOW();
sql 밖에서 아래 코드를 실행한다.
sudo rm /etc/localtime
sudo ln -s /usr/share/zoneinfo/Asia/Seoul /etc/localtime
sudo systemctl restart mysql
start가 아니라 restart로 실행해야 정상반영된다!!
sql 내부에서 데이터베이스를 생성해준다.
CREATE DATABASE sparta;
poetry run python3 manage.py runserver
12. ci.yaml 파일에 디버깅하여 얻은 step 추가
name: Django CI
on:
push:
jobs:
ci:
env:
DB_DATABASE: sparta
DB_USER: root
DB_PASSWORD: [비밀번호]
runs-on: ubuntu-latest
steps:
- name: Check out the codes
uses: actions/checkout@v2
- name: Set timezone to KST
run: |
sudo rm /etc/localtime
sudo ln -s /usr/share/zoneinfo/Asia/Seoul /etc/localtime
# Start Mysql
# https://ovirium.com/blog/how-to-make-mysql-work-in-your-github-actions/
- name: Start Mysql
run: |
sudo systemctl start mysql
mysql -e "use mysql; FLUSH PRIVILEGES; ALTER USER '${{ env.DB_USER }}'@'localhost' IDENTIFIED BY '${{ env.DB_PASSWORD }}';" -uroot -proot
mysql -e 'CREATE DATABASE ${{ env.DB_DATABASE }};' -u${{ env.DB_USER }} -p${{ env.DB_PASSWORD }}
- name: Setup python environment
id: setup-python
uses: actions/setup-python@v2
with:
python-version: 3.9.9
- name: Install Poetry
run: |
curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python
echo "${HOME}/.poetry/bin" >> $GITHUB_PATH
- name: Install dependencies
run: |
${HOME}/.poetry/bin/poetry install
- name: Test python project
run: |
poetry run python manage.py test
tmate 디버깅 시 사용했던 코드로 timezone과 mysql step을 생성한다.
env(환경변수)를 만들어서 '${{ env.변수명 }}' 으로 활용한다.
13. 환경별로 다른 설정 값 사용하기
local_settins.py를 생성하여 settings.py에 덮어쓰기하여 사용한다.
기존 settings.py 와 다르게 변경하고자 하는 부분이 있을때
settings.py 하단에 아래 코드를 추가하여 덮어쓰기 한다.
try:
from sparta.local_settings import *
except ImportError:
pass
gitignore된 local_settings.py가 없어도 실행할 수 있도록 설정한다.
이렇게 TDD를 위한 준비를 마쳤다.
자동화테스트가 필요한 이유는
앱의 일부분을 수정했을 때 버그없이 수정될 수 있도록 함이고
결함률이 높아지지 않기 때문에 기술부채를 줄여주는 역할도 한다.
경우의 수를 커버하기 어려워질수록 코딩은 두려워진다.
코드로 코드를 검증하는 방법으로
개발자의 멘탈을 지키자🥳