본문 바로가기
카테고리 없음

[Django를 활용한 TDD workflow (1) 필수프로그램 설치] 내일배움캠프 AI트랙

by lovvepearl 2022. 2. 16.

백엔드 개발자의 숙명은

최대한으로 기능을 분할하여

독립적으로 관리하기 용이한 코드를 짜는게 아닐까?

 

각각의 기능들은 수많은 변수를 고려하여

구현해야한다.

 

 

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
pyproject.toml 파일에 아래 코드를 추가하여
black을 사용하고 있다는 것을 isort에게 알려준다. 
[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;
manage.py 가 있는 폴더에서 서버실행 시 로컬호스트가 정상적으로 보이면
DB연결에 성공한 것이다.
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를 위한 준비를 마쳤다.

 

자동화테스트가 필요한 이유

앱의 일부분을 수정했을 때 버그없이 수정될 수 있도록 함이고

결함률이 높아지지 않기 때문에 기술부채를 줄여주는 역할도 한다. 

 

경우의 수를 커버하기 어려워질수록 코딩은 두려워진다.

코드로 코드를 검증하는 방법으로

개발자의 멘탈을 지키자🥳