4. 장고 모델과 데이터베이스 이용하기

등록일 : 2020.12.15. 19:28



이전 시간 돌아보기

이전 시간에는 파이썬 개발 툴인 파이참을 사용하여 실습을 위한 프로젝트를 만들고 파이썬 가상환경에 장고를 설치했다. 설치 후에는 mysite라는 이름의 장고 프로젝트를 시작하고 polls라는 앱을 추가했다. 이후 views.py라는 파일을 최초로 수정해보고, urls.py에서 접속 경로를 지정해주었다. 그리고 개발 서버를 실행하고 실제 웹 브라우저에서 개발 서버 주소를 입력해 응답 텍스트를 출력 하는 것까지 확인했다.

기본 흐름이 어느 정도 이해가 되었다면 이제 다음 단계로 나아가자.

Database 이용하기

Database 또는 DB(디비)라는 말을 들어 봤을 것이다. 여러분이 어떤 사이트에 회원 가입을 했다면, 서비스 제공자는 아이디와 비밀번호를 어딘가에 저장해두어야 한다. 그래야 다음번에 다시 로그인 할 때, 해당 데이터를 비교해보고 아이디와 비번이 맞으면 로그인이 될 것이다.

이러한 데이터들은 서로 섞이지 않고, 쉽게 찾아볼 수 있도록 데이터를 잘 관리 해야 한다. 데이터가 없어지거나, 내 비밀번호가 다른 아이디에 매핑 되거나 아이디가 중복된다거나 하는 현상이 발생하면 얼마나 치명적이겠는가? 이런 것들을 잘 관리해주자는 관점으로 생겨난 것들이 바로 데이터베이스 시스템이다. 편의상 DB[디비]라고 부른다. 여러분이 사용하고 있는 많은 앱, 웹은 대부분 DB를 사용하고 있다.

우리도 이 DB를 사용할 것이다.

투표 앱을 위한 데이터베이스 만들기

다음의 투표 예시를 통해 DB를 만들어 보자.

투표 예시
Q1. 선호하는 떡볶이 맵기 강도는?  -2020. 11. 24일 배포-
A1   순한맛 :  0 명 투표
A2   보통맛 :  0 명 투표
A3   매운맛 :  0 명 투표
A4  아주 매운맛 : 0 명 투표

위와 같은 투표를 만들기로 했다면, 어떤 정보를 저장해야 할지 생각해보자.

투표 제목과 투표 날짜를 저장해야 할 것이다. 선택지들은 각 선택지의 내용과 몇 명이 투표했는지 기록해야 할 것이다.

우선은 컴퓨터가 아닌 현실 세계에서 투표라는 것이 어떻게 이루어지는지 생각해보자. 여러분이 종이로 투표한다면 크게 고민하지 않을 것이다. 위의 투표지를 표로 만들어 선택지마다 한 칸씩 부여하여 V 표를 체크 할 수 있게 만들 것이다. 그리고 여러 사람에게 나누어 줄 것이다. 그리고 투표를 받아 어느 선택지가 몇 번 선택되었는지 각 결과를 취합해 결과용 리포트를 작성할 것이다.

이를 컴퓨터 세상에서 웹으로 한다면 종이가 아닌 웹 사이트 화면에 DB에서 투표 정보와 선택지를 불러와 보여줄 수 있다. 투표가 완료 되면 그 숫자를 취합하여 저장해 두었다가 결과를 보고 싶을 때 꺼내 보면 된다. 현실 세계와 컴퓨터 세계가 다를 뿐, 그 방식은 거의 유사하다.

종이 투표를 위한 표를 어떻게 그릴지 구상하듯, DB에서도 유사한 작업을 하는데 이런 개념들을 추상화하고 정리하는 것을 모델링 한다고 한다. 여기서는 가장 널리 쓰이는 관계형 DB(Relational Database)로 사용한다. 무슨 말인지 잘 몰라도 된다. 구조 만들 때 '관계'의 개념으로 만든다고 생각하면 된다. 관계의 개념을 쓰지 않는 DB도 물론 존재한다.

투표를 위해서는 다음과 같이 관계형 DB를 모델링 하였다. 이 부분은 그냥 따라 하자.

투표를 위한 질문 제목과 배포일을 Question 모델로, Question과 연관된 답변 제목과 투표수를 Answer 모델로 각각 정의하고자 한다. Question 모델은 질문과 관련된 것들을 모아둔다. 질문 내용과 배포일 두 가지를 다룬다. 질문 내용은 텍스트이고, 배포일은 날짜 형식을 가진다.

Choice 모델은 투표에 대한 선택지와 관련된 것들을 모아 둔다. 선택 문구는 선택 문항으로 텍스트이다. 투표된 선택지는 숫자가 카운트 될 것이다. 숫자(정수) 형식을 가진다. 또한 Choice는 특정 Question과 연관되어 있으므로 어떤 하나의 Question을 바라보게 하자.

위와같이 모델을 설계했다. 모델 설계는 좀 생소할 수 있지만, 현재로는 이런 식으로 하는구나 정도만 알고 지나가면 되겠다.

이렇게 구성한 모델은 models.py에 기술하면 된다. 해당 파일을 다음과 같이 수정해 보자.

polls/models.py

from django.db import models


class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')


class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)

잘 이해가 가지 않겠지만 ,천천히 알아가 보자.

Question 모델에는 질문을 입력할 question_text로 최대 글자를 200자 입력할 수 있다. Question 모델에는 배포된 날짜를 pub_date로 날짜 형식으로 입력할 수 있다. Choice 모델에는 해당 선택지가 어떤 Question에 종속되는지 Question 모델을 참조할 수 있는 question 필드가 있다. Choice 모델에는 선택지 텍스트를 입력할 수 있게 choice_text로 200자까지 입력할 수 있다. Choice 모델에는 투표된 숫자를 알 수 있는 Votes가 있고 시작 값은 0임을 알 수 있다.

이렇게 만들어진 구조에 데이터를 넣으면 표와 유사한 형식으로 만들어진다고 생각하면 편하다. 그래서 DB에서는 DB table이라고 부른다. Excel, Numbers같은 스프레드시트 프로그램에 각 시트 이름을 Question, Choice로 변경하고 각각의 데이터 칼럼의 이름을 넣고 데이터들을 입력하는 것과 비슷한 개념으로 DB에 저장된다고 생각하면 된다.

눈에 잘 보이지 않으므로 어떻게 생겼는지 표 형식으로 그려 보면 다음과 같이 나타낼 수 있다.

이런 구조로 되어 있다. id는 우리가 필드로 추가하지 않았지만, 이는 편의를 위해 자동으로 생성해준다. 뒤에서 더 자세히 설명하겠다. 데이터는 아직 아무것도 넣지 않았고 구조(모델)만 만들었다. 이것도 실제 설계도만 만든 것으로 models.py에 기술되어 있다. 이것을 실제 설계가 아닌 구축을 하려면 DB에 해당 구조대로 만들도록 적용하는 명령을 요청해야 한다. 그래야 DB가 알 수 있다. 다시 한번 말하지만, 컴퓨터는 모든 것을 일일이 알려줘야 한다.

사용할 데이터베이스 설정하기

DB의 종류는 매우 다양하다. 개발 편의를 위해 django는 기본적으로 sqlite3를 기본적으로 사용하게 되어 있다. mysite/mysite/settings.py 파일을 열어보자.

mysite/mysite/settings.py 열기

# Database
# https://docs.djangoproject.com/en/3.1/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}

항목에 자동으로 기본 설정이 되어 있어 신경 쓸 것이 없다. 특별히 바꿀 것은 없다.

설치된 앱 등록하기

위에서 현재 장고 프로젝트에서 polls 앱을 만들었지만, 앱은 추후에 언제든 넣고 뺄 수 있도록 장고에서 설계 되어 있다. 따라서 polls앱을 만들고 등록하는 과정이 필요하다. 설정 파일은 앱을 생성할 때 자동으로 polls/apps.py 파일에 설정되었다. apps.py 파일을 열어보자.

apps.py 열기

from django.apps import AppConfig


class PollsConfig(AppConfig):
    name = 'polls'

장고 앱의 Appconfig를 활용해 설정 이름을 앱 이름과 같이 polls로 기본으로 만들어 주었다. 특별히 변경할 부분은 없으니 이대로 넘어가자.

이제 프로젝트에 나의 앱을 등록을 해보자. 등록은 mysite/settings.py이다. 위에 DB설정을 확인했던 파일과 같은 파일이다.

settings.py 수정

# ...생략...
INSTALLED_APPS = [
    'polls.apps.PollsConfig',  # 이 부분을 추가해준다.
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]
# ...생략...

settings.py의 INSTALLED_APPS의 [ ] 사이에 'polls.apps.PollsConfig',를 추가하자. 여기서 polls.apps는 polls/apps.py를 의미하고 뒤의 PollsConfig는 apps.py내에 정의되어 있는 PollsConfig라는 이름을 의미한다. 기존에 있는 내용을 삭제하지 말고 맨 윗줄에 한 줄 추가하도록 한다. 문장 앞에는 4칸의 공백이 있다. 이는 파이썬의 문법 규칙 때문에 꼭 정확히 입력하면 된다. 스페이스바를 4번 입력해주면 된다. 참고로 파이참의 경우는 Tab을 눌러도 자동으로 스페이스바 4칸이 적용된다. 앞뒤는 ''로 감싸고 지금 추가된 앱 외에 이미 들어있는 앱들과의 구분은 , 콤마로 되어있다. 문장 끝의 , 도 잘 입력해두자.

위와 같이 추가했다면 터미널로 이동한다. 파이참이 편리한 부분은 수정 후 파일 저장 동작을 할 필요 없이 자동 저장 된다. 따라서 변경하고 바로바로 적용하는데 편리하다. 때에 따라 원치 않은 저장이 될 수도 있으나 이는 실행 취소(보통 Ctrl + Z 또는 Command + Z)를 반복해서 사용하면 어느 정도 해결된다. 파이참에서 제공하는 이보다 더 강력한 Local history 기능도 있다. 시간대별로 수정한 내용들의 히스토리 정보를 어느 정도 제공하고 현재 문서와 비교도 및 되돌리기 기능도 있으니 한번씩 사용해보길 바란다.

models.py 변경사항을 DB에 적용하기 위한 migration 스크립트 만들기

python manage.py makemigrations polls 명령어를 터미널에서 수행하자.

(venv) $ python manage.py makemigrations polls
          Migrations for 'polls':
           polls\migrations\0001_initial.py
             - Create model Qeustion
             - Create model Choice
(venv) $

model.py의 변경사항에 해당하는 스크립트를 생성했다.

makemigrations로 만든 내용 확인하기

polls/migrations/0001_initial.py 를 열어보자.

# Generated by Django 3.1.4 on 2020-12-14 11:21

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

    initial = True

    dependencies = [
    ]

    operations = [
        migrations.CreateModel(
            name='Question',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('question_text', models.CharField(max_length=200)),
                ('pub_date', models.DateTimeField(verbose_name='date published')),
            ],
        ),
        migrations.CreateModel(
            name='Choice',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('choice_text', models.CharField(max_length=200)),
                ('votes', models.IntegerField(default=0)),
                ('question', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='polls.question')),
            ],
        ),
    ]

정확히 이해하긴 어렵지만, Models.py에서 간략하게 기술한 부분에 자동으로 많은 부분이 추가되었다. 이는 사용의 편리성을 위해 자동으로 생성되는 부분도 있고, 작성의 어려움을 도와주는 부분도 있다. 위의 내용은 지금 당장 이해하려고 하지 말고 전체적인 구성만 훑어 보도록 하자.

DB 데이터들을 어떻게 구별할까?

컴퓨터로 어떤 사이트에 가입하려면 id를 설정하는 것을 자주 보았을 것이다. 그리고 한 번쯤 내가 만들려고 했던 아이디가 이미 누가 사용 중이어서 만들 수 없다는 경우를 겪어봤을 것이다. 일반적으로 아이디는 서로 중복되지 않게 구성하여 DB에 설정되어 있다. 먼저 만드는 사람이 임자이며 같은 아이디로 등록하려고 하면 그 아이디는 등록이 거부된다. 아이디만 모든 회원이 중복되지 않고 고유하다면 이름도 같고 생년월일도 같은 회원이 나타나도 id로 모두 구분되기 때문에 식별 가능한 중요한 데이터가 되기 때문이다. 학창시절 한 반에 이름이 같은 친구도 행정상 번호로 구분할 수 있다. 실제로는 새로운 닉네임을 부여하겠지만 말이다.

이런 개념은 아이디 구분뿐만 아니라 관계형 DB에서 모든 데이터를 관리하고 구분하는 방법으로도 필수적으로 쓰인다. id와 유사한 개념을 DB에서는 Primary_key(PK 또는 Key)로 불린다. 위 스크립트를 보면 알겠지만 Question모델에는 id필드, question_text, pub_date를 만들기로 명시되어 있다. 그런데 우리는 model.py에서 분명 따로 id를 명시한 적이 없다. Django model을 통해 makemigrations을 수행할 때 우리가 입력하지 않아도 해당 필드는 자동으로 추가된 스크립트가 만들어 진다.

makemigrations로 만든 스크립트를 실제 DB에 적용해 보기

이제 스크립트도 모두 만들어졌고, 실제 DB에 만들어 달라고 요청할 시간이다. 스크립트는 설계도 정도로 생각하고, DB에 집을 지어 달라고 할 것이다. 집이 완성되면 요청한 구조에 맞춰 그 안의 방이나 거실 구조에 맞춰 가구나 살림살이(실제 데이터)를 채워 넣을 것이다. 명령어는 다음과 같다.

터미널 이동 - mysite 폴더에서 실행

(venv) $ python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, polls, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying auth.0010_alter_group_name_max_length... OK
  Applying auth.0011_update_proxy_permissions... OK
  Applying auth.0012_alter_user_first_name_max_length... OK
  Applying polls.0001_initial... OK
  Applying sessions.0001_initial... OK
(venv) $

우리가 생성한 polls.0001가 실제 적용되었다. 이것뿐만 아니라 장고에서 기본적으로 활용되는 다양한 DB Table들이 생성되었다. 이 부분을 모두 다루진 않지만 다음 장에서는 이 DB 데이터를 이제 웹에서 쉽고 편하게 다룰 수 있는 기능을 소개하도록 하겠다.