Django 프로젝트에서 Model의 테스트 코드를 작성하는 방법에 대해서 알아봅시다.


1. 개요

이제 소프트웨어 개발에서 테스트 코드 작성은 빼놓을 수 없는 부분으로 자리잡았습니다.

테스트 코드는 서비스의 품질 상승, 서비스 검증의 시간 감소 및 개발 생산성 향상 등

서비스를 제공하는 회사에 여러면에서 큰 이익을 가져다 줍니다.

 

이번 블로그 포스트에서는 Django 프로젝트에서 Model을 테스트하는 방법에 대해서 소개합니다.


2. 프로젝트 준비

다음 블로그 포스트를 참고하여 새로운 장고 프로젝트를 생성합니다.


3. 사용자 모델

우선 테스트의 대상이 되는 사용자 모델(User model)에 대해서 살펴봅시다.

이번 블로그에서 소개할 유저 모델은 다음과 같습니다.

from django.db import models
from django.contrib.auth.models import (BaseUserManager, AbstractBaseUser)
...
class User(AbstractBaseUser):
    email = models.EmailField(
        verbose_name='email',
        unique=True,
    )

    wrong_pw = models.DecimalField(max_digits=1, decimal_places=0, default= 0)
    password_lock = models.DateTimeField(blank=True, null=True)
    certificated_at = models.DateTimeField(blank=True, null=True)
    is_active = models.BooleanField(default=True)
    is_admin = models.BooleanField(default=False)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    objects = UserManager()
    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = []

    def __str__(self):
        return self.email

    def has_perm(self, perm, obj=None):
        return True

    def has_module_perms(self, app_label):
        return True

    @property
    def is_staff(self):
        return self.is_admin

위의 모델은 Django의 기본 사용자 모델을 커스터마이징한 사용자 모델 일부입니다.

Django에서 커스텀 사용자 모델을 사용하는 방법에 대해서는 아래 링크를 참고하시기 바랍니다.

이 사용자 모델은 다음과 같은 특징을 가지고 있습니다.

 

- email

: 사용자 이름 대신 이메일을 사용합니다.

 

- wrong_pw

: 암호를 틀린 횟수를 저장하기 위한 필드

 

- password_lock

: 암호를 연속해서 3번 틀렸을 경우, 1시간 동안 암호를 입력하지 못하게 하기 위한 필드

 

- certificated_at

: 이메일의 본인 인증을 한 시점을 저장하기 위한 필드

 

- created_at

: 사용자 생성 시점

 

- updated_at

: 사용자 정보의 업데이트 시점

 

그럼 이제 이런 특징을 가진 사용자 모델을 테스트 하는 방법에 대해서 알아봅시다.


4. 사용자 모델 테스트

그럼 위에서 만든 사용자 모델에 관한 테스트 코드를 작성해 봅시다.

모델에 관한 테스트 코드는 ./AppName/test_models.py을 생성하고 다음과 같이 수정합니다.

from django.test import TestCase
from unittest import mock
from datetime import datetime

from .models import User

class UserModelTest(TestCase):
    def test_default_values(self):
        mock_date = datetime(2021, 3, 4, 14, 57, 11, 703055)
        with mock.patch('django.utils.timezone.now') as mock_now:
            mock_now.return_value = mock_date
            user = User.objects.create(email='test@test.test', password='12345')

        self.assertEquals(user.email, 'test@test.test')
        self.assertEquals(user.wrong_pw, 0)
        self.assertEquals(user.password_lock, None)
        self.assertEquals(user.certificated_at, None)
        self.assertEquals(user.is_active, True)
        self.assertEquals(user.is_admin, False)
        self.assertEquals(user.created_at, mock_date)
        self.assertEquals(user.updated_at, mock_date)

    def test_updated_at(self):
        mock_date = datetime(2021, 3, 4, 14, 57, 11, 703055)
        with mock.patch('django.utils.timezone.now') as mock_now:
            mock_now.return_value = mock_date
            user = User.objects.create(email='test@test.test', password='12345')

        self.assertEquals(user.created_at, mock_date)
        self.assertEquals(user.updated_at, mock_date)
        self.assertEquals(user.updated_at.strftime("%Y-%m-%d"), '2021-03-04')

        mock_update_date = datetime(2021, 3, 5, 14, 57, 11, 703055)
        with mock.patch('django.utils.timezone.now') as mock_now:
            mock_now.return_value = mock_update_date
            user.is_admin = True
            user.save()

        self.assertEquals(user.created_at, mock_date)
        self.assertEquals(user.updated_at, mock_update_date)
        self.assertEquals(user.updated_at.strftime("%Y-%m-%d"), '2021-03-05')

 

그럼 테스트 코드를 좀 더 자세히 살펴봅시다.

class UserModelTest(TestCase):
    def test_default_values(self):
        ...
    def test_updated_at(self):
        ...

 

이 테스트 코드는 test_default_values와 test_updated_at, 두 가지 테스트 케이스를 가지고 있습니다.

 

- test_default_values

: 모델을 통해 데이터를 생성했을 때, 기본값들을 테스트

 

- test_updated_at

: 모델을 통해 데이터를 업데이트 했을 때, updated_at이 잘 동작하는지 확인하는 테스트

 

(1) test_default_values

새로운 데이터를 생성할 때, created_at은 생성 시점의 timestamp를 사용합니다.

이 값이 제대로 동작하는지 확인하기 위해서는 시스템 시간을 Mocking할 필요가 있습니다.

mock_date = datetime(2021, 3, 4, 14, 57, 11, 703055)
with mock.patch('django.utils.timezone.now') as mock_now:
    mock_now.return_value = mock_date
    user = User.objects.create(email='test@test.test', password='12345')

 

이렇게 시간을 Mocking하여 새로운 사용자를 생성한 후,

새롭게 생성된 데이터의 기본값들을 체크합니다.

self.assertEquals(user.email, 'test@test.test')
self.assertEquals(user.wrong_pw, 0)
self.assertEquals(user.password_lock, None)
self.assertEquals(user.certificated_at, None)
self.assertEquals(user.is_active, True)
self.assertEquals(user.is_admin, False)
self.assertEquals(user.created_at, mock_date)
self.assertEquals(user.updated_at, mock_date)

 

이 때, 위에서 Mocking한 시간이 created_at과 updated_at에 잘 저장되었는지도 확인합니다.

self.assertEquals(user.created_at, mock_date)
self.assertEquals(user.updated_at, mock_date)

 

(2) test_updated_at

모델을 사용하여 생성된 데이터를 업데이트하였을 때, 

updated_at이 잘 갱신되는지 확인하기 위한 테스트 케이스입니다.

mock_date = datetime(2021, 3, 4, 14, 57, 11, 703055)
with mock.patch('django.utils.timezone.now') as mock_now:
    mock_now.return_value = mock_date
    user = User.objects.create(email='test@test.test', password='12345')

self.assertEquals(user.created_at, mock_date)
self.assertEquals(user.updated_at, mock_date)
self.assertEquals(user.updated_at.strftime("%Y-%m-%d"), '2021-03-04')

 

우선 시스템 시간을 Mocking하여 새로운 유저 데이터를 생성합니다.

그리고 해당 데이터가 현재 시간을 잘 가지고 있는지 테스트합니다.

mock_update_date = datetime(2021, 3, 5, 14, 57, 11, 703055)
with mock.patch('django.utils.timezone.now') as mock_now:
    mock_now.return_value = mock_update_date
    user.is_admin = True
    user.save()

self.assertEquals(user.created_at, mock_date)
self.assertEquals(user.updated_at, mock_update_date)
self.assertEquals(user.updated_at.strftime("%Y-%m-%d"), '2021-03-05')

 

그런 다음, 시간을 새롭게 Mocking하고, 사용자 데이터를 업데이트합니다.

이렇게 업데이트 한 후, updated_at에 업데이트한 시점의 시간이 잘 기록되었는지 확인합니다.


5. 테스트 실행

이렇게 모델을 가지고 우리가 할 수 있는 동작들을 테스트 케이스로 작성한 후,

다음 명령어를 사용하여 테스트 코드를 실행합니다.

python manage.py test

 

그럼 다음과 같이 우리가 작성한 테스트 코드가 문제없이 통과되는 것을 확인할 수 있습니다.

Creating test database for alias 'default'...
System check identified no issues (0 silenced).
......................
----------------------------------------------------------------------
Ran 22 tests in 2.453s

6. 완료

이것으로 Django에서 모델을 테스트하는 방법에 대해서 알아보았습니다.

모델에 큰 로직이 포함되어있지 않기 때문에, 간단한 테스트 코드로 모델을 테스트할 수 있음을 알 수 있었습니다.

이제 여러분도, 여러분의 Django 프로젝트에 모델 테스트 코드를 작성해 보시기 바랍니다.


출처

https://dev-yakuza.posstree.com/ko/django/test/models/

 

Django 프로젝트에서 pre-commit을 사용하여 Git에 커밋을 할 때, flake8을 실행하도록 설정해 보자.


1. 개요

이전 블로그 포스트에서 파이썬의 코드 정적 분석기인 flake8을 사용하여 코드 스타일을 통일하고, 잠재적인 버그를 줄이는 방법에 대해서 알아보았습니다.

 

이번 블로그 포스트에서는 pre-commit을 사용하여 소스코드를 Git에 커밋할 때,

설정한 flake8을 자동으로 실행하는 방법에 대해서 알아봅니다.


2. pre-commit 설치

pre-commit을 사용하여 flake8을 자동으로 실행하기 위해서는, pre-commit을 설치할 필요가 있습니다.

다음 명령어를 사용하여 pre-commit을 설치합니다.

pip install pre-commit

 

설치를 완료하였다면, 잊지말고 requirements.txt에 저장해 둡니다.

pip freeze > requirements.txt

이것으로 pre-commit을 설치하는 방법에 대해서 알아보았습니다.


3. pre-commit 설정

pre-commit을 사용하여 flake8을 자동으로 실행하기 위해서는, pre-commit의 설정 파일을 작성할 필요가 있습니다.

다음 명령어를 사용하여 pre-commit의 설정 파일을 생성합니다.

pre-commit sample-config > .pre-commit-config.yaml

 

생성된 .pre-commit-config.yaml 파일을 열어보면 다음과 같습니다.

# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
repos:
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v3.2.0
    hooks:
      - id: trailing-whitespace
      - id: end-of-file-fixer
      - id: check-yaml
      - id: check-added-large-files

pre-commit에서 제공하는 샘플 내용을 확인할 수 있습니다.


4. flake8 설정하기

이제 pre-commit의 설정 파일에 flake8을 설정하는 방법에 대해서 알아봅시다.

pre-commit의 설정 파일인 .pre-commit-config.yaml은 기본적으로 다음과 같은 구조를 가지고 있습니다.

repos:
  - repo: repo-url
    rev: version
    hooks:
      - id: hook-id

- repo

: pre-commit이 제공하는 기능의 Repository URL

 

- rev

: 사용하려는 기능의 버전

 

- id

: pre-commit이 제공하는 기능

pre-commit은 기본적으로 제공하는 기능들이 있습니다. 다음 링크를 통해 어떤 기능들이 있는지 확인할 수 있습니다.

 

우리는 이중에서 flake8을 사용할 예정입니다.

그럼 .pre-commit-config.yaml 파일을 열고 다음과 같이 수정합니다.

repos:
  - repo: https://gitlab.com/pycqa/flake8
    rev: 3.8.4
    hooks:
      - id: flake8

pre-commit에서 제공하는 repo의 URL과 버전, flake8 hook을 설정하였습니다.

이 부분은 flake8 공식 문서에서도 확인할 수 있습니다.

 

- flake8 hooks

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

 

이제 이렇게 설정한 파일을

실제 Git의 Commit hook에 등록하기 위해 다음 명령어를 실행합니다.

pre-commit install

이것으로 pre-commit을 사용하여 flake8을 사용할 준비가 되었습니다.


5. pre-commit 실행

그럼 우리가 설정한 pre-commit이 잘 동작하는지 확인해 봅시다.

다음 명령어를 실행하면 우리가 설정한 .pre-commit-config.yaml 파일을 기준으로 pre-commit을 실행할 수 있습니다.

pre-commit run --all-files

 

명령어를 실행하면 다음과 같은 결과를 확인할 수 있습니다.

flake8...................................................................Passed

 

이것으로 우리가 설정한 pre-commit이 잘 동작하는 것을 확인하였습니다.

이제 지금까지 설정한 내용을 Git에 커밋합니다.

git add .
git commit -m 'Add pre-commit for flake8'
git push origin main

6. 완료

이것으로 pre-commit을 사용하여 Git에 커밋을 할 때마다, flake8을 실행하는 방법에 대해서 알아보았습니다.

주의해야할 점은, 새롭게 Repository를 Clone한 경우 잊지말고 pre-commit install을 실행하여 pre-commit의 설정 내용을 Git hooks에 등록해야 합니다.

git clone repository_url
# virtualenv venv
# source venv/bin/activate
pip install -r requirement.txt
pre-commit install

그럼 이제 자동화된 flake8으로 더욱 생산성 높은 개발을 해보시기 바랍니다.

Django 프로젝트에서 파이썬 코드 스타일을 통일하기 위한 코드 정적 분석기인 flake8을 사용하는 방법에 대해서 알아봅시다.


1. 개요

Django로 서버사이드를 개발하면서 다른 개발자분들과 협업을 하게 되었습니다.

여러 개발자가 협업을 하므로, 코드의 스타일을 통일하고, 잠재적인 버그를 줄이기 위해,

코드 정적 분석기인 flake8을 도입하기로 했습니다.

 

이번 블로그 포스트에서는 Django 프로젝트에 flake8을 설정하고 사용하는 방법에 대해서 설명합니다.


2. flake8 설치

Django에서 flake8을 사용하기 위해서는 우선, flake8을 설치할 필요가 있습니다.

다음 명령어를 사용하여 flake8을 설치합니다.

pip install flake8

 

설치를 하였다면, 잊지말고 requirements.txt에 저장해 둡니다.

pip freeze > requirements.txt

 

이것으로 flake8을 설치하는 방법에 대해서 알아보았습니다.


3. flake8 사용법

다음 명령어를 실행하여 flake8을 실행할 수 있습니다.

flake8

 

Django 프로젝트 폴더에서 flake8을 실행하면 다음과 같은 내용을 확인할 수 있습니다.

./venv/lib/python3.8/site-packages/pyflakes/checker.py:153:31: F821 undefined name 'PercentFormat'
./venv/lib/python3.8/site-packages/pyflakes/checker.py:160:9: F821 undefined name 'Generator'
./venv/lib/python3.8/site-packages/pyflakes/checker.py:160:9: F821 undefined name 'PercentFormat'
./venv/lib/python3.8/site-packages/pyflakes/checker.py:180:47: F821 undefined name 'Optional'
./venv/lib/python3.8/site-packages/pyflakes/checker.py:759:35: F821 undefined name 'List'
./venv/lib/python3.8/site-packages/pyflakes/checker.py:760:35: F821 undefined name 'Dict'

flake8의 결과를 보면 checker.py:153:31: F821 undefined name 'PercentFormat'와 같이 파일명:에러위치 에러ID 에러 내용을 확인할 수 있습니다.


4. flake8 설정하기

저는 djanog 프로젝트 폴더에 virtualenv를 생성하여 사용하고 있습니다. 그래서, flake8이 불필요하게 virtualenv 폴더까지 분석하고 있으니, flake8이 virtualenv 폴더를 분석하지 않도록 설정할 필요가 있었습니다.

falke8이 virtualenv 폴더를 무시할 수 있도록 설정하기 위해, .flake8 파일을 Django 프로젝트 폴더에 생성하고 다음과 같이 수정하였습니다.

[flake8]
exclude =
    .git,
    .gitignore,
    *.pot,
    *.py[co],
    __pycache__,
    venv,
    .env

이렇게 설정하고, 설정이 잘 적용되었는지 확인하기 위해 아래에 명령어를 사용하여 flake8을 실행해 봅니다.

flake8

그럼 이전과는 다르게 다음과 같은 결과를 확인할 수 있습니다.

./petmeeting/settings.py:98:80: E501 line too long (91 > 79 characters)
./petmeeting/settings.py:101:80: E501 line too long (81 > 79 characters)
./petmeeting/settings.py:104:80: E501 line too long (82 > 79 characters)
./petmeeting/settings.py:107:80: E501 line too long (83 > 79 characters)
./petmeeting/settings.py:132:33: W292 no newline at end of file

flake8은 많은 내용을 체크하지만, 일반적으로 생각하지 않아도 되는 부분까지 에러로 표시하는 경우가 있습니다. 이런 부분들을 무시하기 위해서 저는 다음과 같이 무시할 내용들을 추가하였습니다.

[flake8]
exclude =
    .git,
    .gitignore,
    *.pot,
    *.py[co],
    __pycache__,
    venv,
    .env

ignore =
    E121,
    E126,
    E127,
    E128,
    E203,
    E225,
    E226,
    E231,
    E241,
    E251,
    E261,
    E265,
    E302,
    E303,
    E305,
    E402,
    E501,
    E741,
    W291,
    W292,
    W293,
    W391,
    W503,
    W504,
    F403,
    B007,
    B950,

max-line-length = 200

이 내용은 Sider라는 회사에서 추천하는 규칙을 적용한 내용입니다.

 

이 규칙은 단순히 정한건 아닌거 같고, 이 회사에서 연구한 결과를 바탕으로 추천하고 있는거 같습니다.

자세한 내용은 아래에 링크를 참고하시기 바랍니다.


5. 완료

이것으로 Django 프로젝트에서 flake8을 사용하는 방법에 대해서 알아보았습니다.

또한 불필요한 규칙들과 폴더를 무시하기 위한 설정 방법에 대해서도 알아보았습니다.

앞으로 다른 개발자들과 협업하면서 이렇게 설정한 flake8이 많은 도움이 되기를 기대해 봅니다.


출처

https://dev-yakuza.posstree.com/ko/django/flake8/

django 프로젝트에서 Gmail의 smtp로 메일을 발송하는 방법과 개발하면서 겪은 문제점을 공유합니다.


1. 개요

django로 서버사이드를 개발하면서 메일을 발송할 필요가 생겼습니다.

이번 블로그 포스트에서는 django 프로젝트에서 Gmail의 smtp로 메일을 발송하는 방법을 소개하고, 메일을 발송하면서 겪은 문제를 공유합니다.


2. Google Account 설정

SMTP를 사용하여 Gmail로 메일을 발송하려면 우선 Less secure apps & your Google Account을 설정해야 합니다.

 

우선 아래에 링크를 통해 Google Account로 이동합니다.

로그인 후, Google Account로 가면 아래와 같은 화면을 볼 수 있습니다.

왼쪽의 Security 메뉴를 선택하고 조금 스크롤하면, 아래와 같은 화면을 확인할 수 있습니다.

메뉴를 선택하고 하고 들어가서, Allow less secure apps을 ON으로 상태를 변경해줍니다.

메뉴 찾기가 어려우신 분들은 아래에 링크를 사용하시기 바랍니다.

참고로 2-Step Verification(2단계 인증)을 사용하고 있는 계정은 설정이 불가합니다.


3. django 설정

이제 django 프로젝트의 [project name]/settings.py를 열고 아래와 같이 수정합니다.

# send email
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.googlemail.com'
EMAIL_USE_TLS = True
EMAIL_PORT = 587
EMAIL_HOST_USER = 'Your email address'
EMAIL_HOST_PASSWORD = 'password'

여기서, EMAIL_HOST_USER과 EMAIL_HOST_PASSWORD에 자신의 구글 계정 정보를 입력합니다.


4. 메일 발송

아래에 코드를 사용하여 HTML 메일을 발송할 수 있습니다.

from django.core.mail import send_mail
from django.conf import settings
...
send_mail(subject, message, email_from, recipient_list, html_message=message)
  • subject: 메일의 제목
  • message: 메일 내용
  • email_from: 메일을 발송하는 사람(저는 email_from = settings.EMAIL_HOST_USER 코드를 사용합니다.)
  • recipient_list: 받는 사람 리스트. 메일 주소의 문자열 리스트([email, ])
  • html_message: HTML 문자열

이렇게 하면 문제없이 메일이 발송되는 것을 확인할 수 있습니다.


5. heroku에서 SMTPAuthenticationError 에러

로컬에서 잘 테스트하다가 heroku에 프로젝트를 업로드한 후,

같은 메일 기능을 사용했을 때, 아래와 같은 에러 메세지가 나오면서 메일이 발송되지 않았습니다.

2020-03-02T04:53:02.037241+00:00 app[web.1]:     new_conn_created = self.open()
2020-03-02T04:53:02.037241+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/django/core/mail/backends/smtp.py", line 69, in open
2020-03-02T04:53:02.037242+00:00 app[web.1]:     self.connection.login(self.username, self.password)
2020-03-02T04:53:02.037242+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/smtplib.py", line 730, in login
2020-03-02T04:53:02.037242+00:00 app[web.1]:     raise last_exception
2020-03-02T04:53:02.037243+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/smtplib.py", line 721, in login
2020-03-02T04:53:02.037243+00:00 app[web.1]:     initial_response_ok=initial_response_ok)
2020-03-02T04:53:02.037243+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/smtplib.py", line 642, in auth
2020-03-02T04:53:02.037244+00:00 app[web.1]:     raise SMTPAuthenticationError(code, resp)
2020-03-02T04:53:02.037261+00:00 app[web.1]: smtplib.SMTPAuthenticationError: (534, b'....')

Gmail과 Google Account에 들어가서 확인해 보니, Heroku 서버의 IP가 차단되어 있는 것을 확인할 수 있었습니다.

이 문제를 해결하기 위해서 우선 아래에 링크로 이동합니다.

 

위에 링크로 접속하면 아래와 같은 화면을 볼 수 있습니다.

하단에 있는 Continue 버튼을 눌러 줍니다.

그리고 다시 django를 통해 메일을 발송하면 메일이 잘 발송되는 것을 확인할 수 있습니다.

 


6. 완료

이것으로 django 프로젝트에서 Gmail의 SMTP를 사용하여 메일을 발송하는 방법에 대해서 알아보았습니다.

아마 별다른 문제가 없을 거 같지만, 저처럼 구글에서 IP를 차단당하시는 분들이 있다면

위에서 설명한 내용을 시도해 보시기 바랍니다.


출처

https://dev-yakuza.posstree.com/ko/django/gmail-smtp/


장고(django) 프로젝트에서 API를 사용할때 JWT(Json Web Token) 인증을 사용하는 방법에 대해서 알아보자


1. 개요

리액트 네이티브(React Native)로 개발된 앱(App)의 로그인, 정보 갱신 등의 API 서버를 장고(django)를 사용하여 개발하려고 합니다. 이 때, 앱(App)과 API 서버에 자주 사용되는 JWT(Json Web Token) 인증을 장고(django)를 사용하여 구현하려고 합니다.

 

이번 블로그에서는 Django REST framework JWT를 사용하여 장고(django)의 JWT를 구현하는 방법에 대해서 설명합니다.

 

이 블로그에서 사용하는 소스코드는 github에 공개되어 있습니다.

아래에 링크를 통해 확인 가능합니다.


2. 장고(django) 프로젝트 준비

이전 블로그 시리즈에서 장고(django) 프로젝트를 사용하는 방법에 대해서 설명하였습니다.

장고(django)를 사용하여 프로젝트를 구성하는 방법에 대한 자세한 내용은 아래에 링크를 통해 확인하시기 바랍니다.

 

이 블로그에서는 장고(django) 설치와 프로젝트 설정등에 대해서는 설명하지 않도록 하겠습니다.

아래에 명령어를 통해 장고(django) 프로젝트를 생성합니다.

django-admin startproject django_jwt

 

아래에 블로그를 참고하여 데이터베이스 연동 및 테이블을 생성합니다.

 

데이터베이스 연동이 완료되었다면 아래에 명령어를 통해 관리자(superuser)를 생성합니다.

python manage.py createsuperuser

 

아래에 블로그를 참고하여 Blog 장고(django) 앱(App)과 Post 모델(Model)을 생성합니다.

 

그리고 아래에 블로그를 참고하여 테스트 데이터를 추가합니다.


3. Django REST framework JWT 설치

JWT 구현은 상단히 많은 부분을 신경써야하는 귀찮은 작업입니다.

그래서 대부분의 플랫폼에는 잘 만들어진 모듈 혹은 라이브러리가 존재합니다.

우리는 여기서 Django REST framework JWT을 사용하여 JWT를 구현하도록 하겠습니다.

아래에 명령어를 통해 Django REST framework JWT을 설치합니다.

pip install djangorestframework djangorestframework-jwt

djangorestframework

: 장고(django)의 Restful API를 좀 더 쉽게 사용할 수 있게 해주는 모듈입니다.

 

djangorestframework-jwt

: 장고(django)의 Restful API에서 JWT를 쉽게 구현할 수 있게 도와주는 모듈입니다.

 

설치가 완료되면 잊지 않고 requirements.txt를 갱신합니다.

pip freeze > requirements.txt

4. Django Rest framework JWT 설정

Django Rest framework JWT 설정하기 위해 

django_jwt/settings.py를 열고 아래와 같이 수정합니다.

...
REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.IsAuthenticated',
    ),
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.BasicAuthentication',
    ),
}

JWT_AUTH = {
    'JWT_SECRET_KEY': SECRET_KEY,
    'JWT_ALGORITHM': 'HS256',
    'JWT_ALLOW_REFRESH': True,
    'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7),
    'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(days=28),
}
...

 

좀 더 자세히 살펴보면,

REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.IsAuthenticated',
    ),
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
    ),
}

 

위에서 설치한 djangorestframework에 관한 설정입니다.

 

- 로그인 여부를 확인하는 클래스(DEFAULT_PERMISSION_CLASSES)를 rest_framework.permissions.IsAuthenticated로 사용하도록 설정하였습니다.

 

-로그인과 관련된 클래스(DEFAULT_AUTHENTICATION_CLASSES)를 JWT(rest_framework_jwt.authentication.JSONWebTokenAuthentication)을 사용하도록 하였습니다.

 

JWT_AUTH = {
    'JWT_SECRET_KEY': SECRET_KEY,
    'JWT_ALGORITHM': 'HS256',
    'JWT_ALLOW_REFRESH': True,
    'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7),
    'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(days=28),
}

- JWT_SECRET_KEY

: JWT의 비밀키(Secret Key)로 어떤걸 사용할지 작성합니다.

: 여기에서는 장고(django)와 같은 비밀키를 사용하였지만 사용할 때는 다른 키를 사용하시길 권장합니다.

 

- JWT_ALGORITHM

: JWT 암호화에 사용되는 알고리즘을 지정합니다.

 

- JWT_ALLOW_REFRESH

: JWT 토큰을 갱신할 수 있게 할지 여부를 결정합니다.

 

- JWT_EXPIRATION_DELTA

: JWT 토큰의 유효 기간을 설정합니다.

 

- JWT_REFRESH_EXPIRATION_DELTA

: JWT 토큰 갱신의 유효기간 입니다.

 

JWT_EXPIRATION_DELTA와 JWT_REFRESH_EXPIRATION_DELTA이 잘 이해가 되지 않는데요.

위와 같이 설정한 경우 JWT 토큰을 7일 안에 갱신하지 않으면 JWT 토큰을 사용할 수 없고 로그아웃됩니다.

또한 7일안에 열심히 갱신해도 28일 후에는 갱신할 수 없습니다.

즉, 열심히 갱신해도 28일 후에는 로그아웃 처리가 되는 것을 의미합니다.


5. Django Rest framework JWT을 위한 URL 설정

이제 JWT 인증을 위한 URL을 추가해야 합니다. 

django_jwt/urls.py를 열고 아래와 같이 수정합니다.

from django.contrib import admin
from django.urls import path, include
from rest_framework_jwt.views import obtain_jwt_token, verify_jwt_token, refresh_jwt_token

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/token/', obtain_jwt_token),		# <<<
    path('api/token/verify/', verify_jwt_token),	# <<<
    path('api/token/refresh/', refresh_jwt_token),	# <<<
    path('api/blog/', include('blog.urls'))
]

- from rest_framework_jwt.views import obtain_jwt_token, verify_jwt_token, refresh_jwt_token

: JWT 인증을 위해 필요한 요소들을 불러옵니다.

 

- obtain_jwt_token

: JWT 토큰을 발행할 때 사용합니다. (path('api/token/', obtain_jwt_token))

 

- verify_jwt_token

: JWT 토큰이 유효한지 검증할 때 사용합니다. (path('api/token/verify/', verify_jwt_token))

 

- refresh_jwt_token

: JWT 토큰을 갱신할 때 사용합니다. (path('api/token/refresh/', refresh_jwt_token))

 

- path(‘api/blog/’, include(‘blog.urls’))

: 우리가 만든 Blog 앱의 URL을 연결합니다.

우리가 만든 Blog 앱의 URL을 만들기 위해 blog/urls.py를 만들고 아래와 같이 수정합니다.

from django.urls import path
from . import views

urlpatterns = [
    path('posts/', views.posts, name='posts'),
]

6. 뷰(View) 만들기

이제 실제로 JWT 인증을 사용할 API를 만들어 봅시다. 

blog/views.py를 열고 아래와 같이 수정합니다.

from django.shortcuts import render
from django.core import serializers
from django.http import HttpResponse

from rest_framework.decorators import api_view, permission_classes, authentication_classes
from rest_framework.permissions import IsAuthenticated
from rest_framework_jwt.authentication import JSONWebTokenAuthentication

from .models import Post


@api_view(['GET'])
@permission_classes((IsAuthenticated, ))
@authentication_classes((JSONWebTokenAuthentication,))
def posts(request):
    posts = Post.objects.filter(
        published_at__isnull=False).order_by('-published_at')
    post_list = serializers.serialize('json', posts)
    return HttpResponse(post_list, content_type="text/json-comment-filtered")

 

이제까지 만들어 보던 뷰(View)와 많이 다릅니다.

간단히 살펴보도록 하겠습니다.

from django.shortcuts import render
from django.core import serializers
from django.http import HttpResponse
...
from .models import Post
...
def posts(request):
    posts = Post.objects.filter(published_at__isnull=False).order_by('-published_at')
    post_list = serializers.serialize('json', posts)
    return HttpResponse(post_list, content_type="text/json-comment-filtered")

이 부분은 이전 블로그(장고(django)의 모델(Models)을 JSON으로 응답(Response)하기)에서 이미 소개해 드렸습니다.

모델(Models)의 결과(QuerySet)을 JSON으로 응답(Response)하기 위한 부분입니다.

 

이제 새롭게 추가된 부분을 살펴봅시다.

...
from rest_framework.decorators import api_view, permission_classes, authentication_classes
from rest_framework.permissions import IsAuthenticated
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
...
@api_view(['GET'])
@permission_classes((IsAuthenticated, ))
@authentication_classes((JSONWebTokenAuthentication,))
def posts(request):
...

- from rest_framework.decorators import api_view, permission_classes, authentication_classes

: 우리가 설치한 djangorestframework이 제공하는 파이썬 데코레이터(Python Decorator)입니다.

: 데코레이터(Decorator)를 설명하자면 많이 길어질거 같아, 간단하게 이해하면 전처리로 생각하면 간단할 거 같습니다.

 

- from rest_framework.permissions import IsAuthenticated

: 로그인 여부를 확인할 때 사용합니다.

 

- from rest_framework_jwt.authentication import JSONWebTokenAuthentication

: JWT 인증을 확인하기 위해 사용합니다.

 

- @api_view(['GET'])

: 위에서 언급한 데코레이터(Decorator)입니다.

하위에 있는 함수(def posts(request):)를 실행하기 전에 이 부분이 먼저 실행됩니다.

이 데코레이터(Decorator)는 GET 요청인지 검증하고 GET이 아니면 에러를 JSON 타입으로 반환합니다.

 

- @permission_classes((IsAuthenticated, ))

: 권한을 체크합니다. 여기서는 로그인 했는지 여부만 체크하도록 하였습니다.

 

- @authentication_classes((JSONWebTokenAuthentication,))

: JWT 토큰을 확인합니다. 토큰이 이상이 있으면 에러를 JSON 형식으로 반환합니다.


7. 확인

지금까지 작업한 내용을 Postman을 통해 확인해 보도록 하겠습니다.

아래에 명령어로 장고(django)의 테스트 서버를 실행합니다.

python manage.py runserver

 

그리고 Postman을 열고 아래와 같이 

http://localhost:8000/api/blog/posts/에 GET 요청을 보냅니다.

그러면 위와 같이 에러 메세지를 확인할 수 있습니다.

우리가 만든 API가 JWT 인증으로 보호되고 있음을 확인할 수 있습니다.

 

그럼 JWT 인증으로 정보를 가져오기 위하 JWT 토큰을 발행해 봅니다.

http://localhost:8000/api/token/에 POST로 username과 password를 설정하여 요청을 보냅니다.

여기서 사용하는 username과 password는 우리가 만든 장고(django)의 superuser입니다.

(장고 관리자 페이지에 로그인할 때 사용하는 username과 password)

위와 같이 JWT 토큰이 잘 발행되는 것을 확인할 수 있습니다.

그럼 발행한 JWT 토큰으로 다시 위에 요청(http://localhost:8000/api/blog/posts/)을 보내보도록 하겠습니다.

여기에서는 요청의 Header에 Authorization을 설정해서 보냅니다. jwt [jwt key]와 같은 형식으로 작성해야 합니다.

이전과 다르게 데이터를 잘 가져오는 것을 확인할 수 있습니다.

그럼 JWT 토큰을 검증하는 방법과 갱신하는 방법에 대해서 알아보겠습니다.

JWT 토큰을 검증하는 방법은 http://localhost:8000/api/token/verify/에 POST로 위에서 발급 받은 토큰을 전송합니다.

그러면 위와 같이 문제가 없는 것을 확인할 수 있습니다.

JWT 토큰에 문제가 있으면 아래와 같이 에러 메세지를 전달 받습니다.

그럼 JWT 토큰을 갱신(Refresh)해 볼까요? 

http://localhost:8000/api/token/refresh/에 POST로 이미 발급받은 JWT 토큰을 전달합니다.

그럼 위와 같이 새로운 JWT 토큰을 발급받을 수 있습니다.

만약 JWT 토큰을 갱신하지 않거나 유효 기간 이후 갱신하게 되면 아래와 같은 에러 메세지를 확인할 수 있습니다.


8.완료

이것으로 장고(django)에서 JWT를 사용하는 방법에 대해서 알아보았습니다.

이제 JWT 토큰을 사용하여 앱과 서버간에 통신이나, 프론트엔드와 서버간 통신에 사용할 수 있습니다.


출처

https://dev-yakuza.posstree.com/ko/django/jwt/

장고(django) 프로젝트에서 API를 사용하여 정보를 전달할 때, 모델(Models)에서 가져온 정보(QuerySet)를 그대로 JSON으로 응답(Response)하는 방법에 대해서 알아봅니다.


1. 개요

장고(django)로 API 서버를 개발하다 보면,

모델(Models)에서 직접 가져온 정보(QuerySet)을 바로 JSON으로 응답하고 싶을 때가 있습니다.

 

이 블로그 포스트에서는 장고(django)를 이용하여 모델(Models)에서 가져온 정보(QuerySet)을 그대로 JSON으로 보내는 방법에 대해서 알아보겠습니다.

 

이 블로그에서 사용하는 소스코드는 github에 공개되어 있습니다. 아래에 링크를 통해 확인 가능합니다.


2. 장고(django) 프로젝트 준비

이전 블로그 시리즈에서 장고(django) 프로젝트를 사용하는 방법에 대해서 설명하였습니다.

장고(django)를 사용하여 프로젝트를 구성하는 방법에 대한 자세한 내용은 아래에 링크를 통해 확인하시기 바랍니다.

이 블로그에서는 장고(django) 설치와 프로젝트 설정등에 대해서는 설명하지 않도록 하겠습니다.

간단하게 프로젝트가 진행될 수준으로만 설명하겠습니다.

아래에 명령어를 통해 장고(django) 프로젝트를 생성합니다.

django-admin startproject django_response_model_to_json

 

아래에 블로그를 참고하여 데이터베이스 연동 및 테이블을 생성합니다.

 

데이터베이스 연동이 완료되었다면 아래에 명령어를 통해 관리자(superuser)를 생성합니다.

python manage.py createsuperuser

 

아래에 블로그를 참고하여 Blog 장고(django) 앱(App)과 Post 모델(Model)을 생성합니다.

 

그리고 아래에 블로그를 참고하여 테스트 데이터를 추가합니다.


3. URL 작성

이제 테스트 데이터를 가져오기 위한 URL을 생성합니다.

아래에 링크를 통해 자세한 방법을 확인할 수 있습니다.

 

URL을 작성하기 위해 django_response_model_to_json/urls.py을 열고 아래와 같이 수정합니다.

from django.contrib import admin
from django.urls import path
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('blog.urls')),
]

 

그리고 Blog 앱에 URL을 추가하기 위해 blog/urls.py을 생성하고 아래와 같이 수정합니다.

from django.urls import path
from . import views

urlpatterns = [
    path('posts/', views.posts, name='posts'),
]

4. 뷰(View) 작성

이제 모델(Models)에서 가져온 데이터(QuerySet)을 JSON으로 반환하기 위한 뷰(View)를 작성해야 합니다.

장고(django)의 뷰(View)에 관해서는 아래에 블로그를 참고하시기 바랍니다.

 

뷰(View)를 만들기 위해 blog/views.py를 열고 아래와 같이 수정합니다.

from django.shortcuts import render
from django.core import serializers
from django.http import HttpResponse
from .models import Post


def posts(request):
    posts = Post.objects.filter(published_at__isnull=False).order_by('-published_at')
    post_list = serializers.serialize('json', posts)
    return HttpResponse(post_list, content_type="text/json-comment-filtered")

- from django.core import serializers

: 모델(Models)을 JSON 타입으로 데이터를 직렬화 시키기 위해 serializers를 불러옵니다.

 

- from django.http import HttpResponse

: JSON으로 데이터를 반환(Response)하기 위해 HttpResponse를 사용할 예정입니다.

 

- posts = Post.objects.filter(published_at__isnull=False).order_by('-published_at')

: Post 모델(Models)을 이용하여 데이터를 가져옵니다.

 

- post_list = serializers.serialize('json', posts)

: 가져온 데이터(QuerySet)를 JSON 타입의 문자열로 변환합니다.

 

- return HttpResponse(post_list, content_type="text/json-comment-filtered")

: post_list가 JSON 타입의 문자열이므로 따옴표(")를 제거한 JSON을 반환해야 합니다.


5. 확인

지금까지 작업한 내용을 확인하기 위해 장고(django)의 테스트 서버를 실행 시킵니다.

python manage.py runserver

 

그리고 Postman에서 해당 URL(http://localhost:8000/posts/)을 GET으로 가져오면

아래와 같이 데이터가 잘 가져와지는 것을 확인할 수 있습니다.


출처

https://dev-yakuza.posstree.com/ko/django/response-model-to-json/

장고(django) 프로젝트에서 필요한 마스터 데이터 또는 테스트 데이터를 넣는 방법(data-seed)에 관해서 알아보겠습니다.


1. 개요

일반적으로 웹 서비스를 개발하다보면

초기에 기본적으로 세팅이 필요한 마스터 데이터 또는 테스트 데이터가 필요합니다.

 

이 블로그에서는 장고(django) 프로젝트에서 fixtures을 이용하여 마스터 데이터를 생성하는 방법에 대해서 알아보겠습니다.

이 블로그 포스트에 소개되는 소스코드는 github에 공개되어 있습니다. 아래에 링크를 참고하시기 바랍니다.


2. 장고(django) 프로젝트 준비

이전 블로그 시리즈에서 장고(django) 프로젝트를 사용하는 방법에 대해서 설명하였습니다.

장고(django)를 사용하여 프로젝트를 구성하는 방법에 대한 자세한 내용은 아래에 링크를 통해 확인하시기 바랍니다.

이 블로그에서는 장고(django) 설치와 프로젝트 설정등에 대해서는 설명하지 않도록 하겠습니다.

간단하게 프로젝트가 진행될 수준으로만 설명하겠습니다.

아래에 명령어를 통해 장고(django) 프로젝트를 생성합니다.

django-admin startproject django_jwt

 

아래에 블로그를 참고하여 데이터베이스 연동 및 테이블을 생성합니다.

 

데이터베이스 연동이 완료되었다면 아래에 명령어를 통해 관리자(superuser)를 생성합니다.

python manage.py createsuperuser

 

아래에 블로그를 참고하여 Blog 장고(django) 앱(App)과 Post 모델(Model)을 생성합니다.


3. 마스터 데이터 준비

위에 장고(django) 프로젝트 과정을 진행하면 

blog/models.py에 아래와 같은 내용을 확인할 수 있습니다.

from django.db import models
from django.utils import timezone


class Post(models.Model):
    author = models.ForeignKey('auth.User', on_delete=models.CASCADE)
    title = models.CharField(max_length=100)
    content = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    published_at = models.DateTimeField(blank=True, null=True)

    def publish(self):
        self.published_at = timezone.now()
        self.save()

    def __str__(self):
        return self.title

 

이제 모델(Models)에 맞는 마스터 데이터(Master data)를 준비하고 데이터를 추가(Seed)할 것입니다. 

blog/fixtures/posts-data.json 파일을 생성하고 아래와 같이 추가합니다.

[
  {
    "model": "blog.post",
    "fields": {
      "author": 1,
      "title": "test2",
      "content": "test2",
      "created_at": "2019-06-04T15:43:29.899",
      "updated_at": "2019-06-04T15:43:29.899",
      "published_at": "2019-06-04T15:43:28"
    }
  },
  {
    "model": "blog.post",
    "fields": {
      "author": 1,
      "title": "test1",
      "content": "test1\r\n\r\ntest1",
      "created_at": "2019-06-04T15:43:19.760",
      "updated_at": "2019-06-04T15:43:19.760",
      "published_at": "2019-06-04T15:43:18"
    }
  }
]

위 json 파일을 보면 알 수 있듯이

우리는 두 개의 테스트 데이터를 추가할 예정입니다.


4. 마스터 데이터 넣기(Data Seed)

아래에 명령어를 통해

우리가 만든 데이터 시드(Data Seed)를 데이터베이스에 넣습니다.

python manage.py loaddata blog/fixtures/posts-data.json

5. 확인

데이터베이스 툴을 이용하여 데이터가 잘 들어갔는지 확인해 봅니다.

 

물론, 장고(django)의 관리자 화면을 통해서도 확인이 가능합니다.

아래에 장고(django) 명령어로 테스트 서버를 실행 시킵니다.

python manage.py runserver

 

그리고 관리자 페이지의 URL(http://127.0.0.1:8000/admin)에 접속하여 추가된 데이터를 확인할 수 있습니다.


6. 완료

이것으로 장고(django)의 마스터 데이터 추가(Data seed)에 대해서 알아보았습니다.

이제 장고(django) 프로젝트를 진행할 때, 마스터 데이터 또는 테스터 데이터를 미리 준비할 수 있게 되었습니다!


출처

https://dev-yakuza.posstree.com/ko/django/data-seed/

장고(django) 프로젝트를 실제 서비스에서 사용하기 위해 헤로쿠(Heroku) 서비스에 업로드하고 웹 서비스를 제공하는 방법에 대해서 알아봅시다.


1. 개요

장고(django)로 만든 프로젝트로 웹 서비스를 시작하기 위해

헤로쿠(Heroku)에 장고(django) 프로젝트를 업로드하고 웹 서비스를 시작해 봅시다.

이 블로그는 시리즈로 작성되어 있으며, 아래에 링크를 통해 시리즈의 다른 글을 확인할 수 있습니다.

또한 이 블로그 시리즈에서 다룬 소스는 github에 공개되어 있습니다. 아래에 링크를 통해 확인 가능합니다.


2. 헤로쿠(Heroku) 회원 가입

헤로쿠(Heroku) 서비스를 이용하기 위해서는 헤로쿠(Heroku) 서비스에 회원 가입을 할 필요가 있습니다.

아래에 링크를 통해 헤로쿠(Heroku) 서비스에 회원 가입 및 로그인을 합니다.

회원 가입 및 로그인은 일반 서비스의 회원 가입 및 로그인 절차와 같으므로 자세한 설명은 생략 하도록 하겠습니다.


3. 헤로쿠(Heroku) 앱 생성

헤로쿠(Heroku)에서 웹 서비스를 운영하기 위해서는 헤로쿠(Heroku)에 앱(App)을 생성할 필요가 있습니다.

회원 가입과 로그인후 아래와 같은 대시보드(Dashboard) 화면이 보인다면 Create new app을 눌러 새로운 앱을 생성합니다.

헤로쿠(Heroku) 앱을 생성하기 위해 앱 정보를 입력합니다.

무료이기 때문에 지역은 미국(United State)과 유럽(Europe)만 선택이 가능합니다. 앱 정보를 입력하였다면 Create app을 눌러 앱을 생성합니다.


4. 헤로쿠(Heroku) CLI 설치

우리는 헤로쿠(Heroku) CLI(Command Line Interface)를 통해 우리가 만든 장고(djanog) 프로젝트를 헤로쿠(Herkou) 업로드(Deploy) 할 예정입니다. 아래에 링크를 통해 각 OS에 맞는 헤로쿠(Heroku) CLI를 다운로드 하시기 바랍니다. 여기에서는 macOS를 기준으로 설명합니다.

macOS에서는 아래에 명령어를 통해 헤로쿠(Heroku) CLI를 설치합니다.

brew tap heroku/brew && brew install heroku

5. 헤로쿠(Heroku) 로그인

헤로쿠(Heroku)에 웹 서비스를 배포(Deploy)하기 위해서는,

헤로쿠(Heroku) CLI를 이용하여 헤로쿠(Heroku)에 로그인 할 필요가 있습니다.

아래에 명령어를 통해 헤로쿠(Heroku) 서비스에 로그인 합니다.

heroku login

위에 헤로쿠(Heroku) 명령어를 실행하면 아래와 같이 로그인 화면이 웹 브라우저에 표시됩니다. 

Log in 버튼을 눌러 로그인합니다.

로그인이 완료되면 아래와 같이 로그인 성공 화면을 웹 브라우저에서 확인할 수 있으며,

헤로쿠(Heroku) 명령어를 입력한 터미널에서는 아래와 같이 성공 메세지를 확인할 수 있습니다.

heroku: Press any key to open up the browser to login or q to exit:
Opening browser to https://cli-auth.heroku.com/auth/browser/f844937e-aaaf-4620-998f-1f938ed3dea7
Logging in... done
Logged in as dev.yakuza@gmail.com

6. 장고(django) 프로젝트 수정

헤로쿠(Heroku) 서비스에 배포(Deploy) 준비가 끝났습니다.

우리는 장고(django) 프로젝트를 헤로쿠(Heroku) 서비스에서 동작하도록 수정할 필요가 있습니다. 

manage.py가 있는 폴더에 Procfile 파일을 생성한 후 아래와 같이 추가합니다.

web: gunicorn django_exercise.wsgi --log-file -

위에 내용에서 django_exercise.wsgi 부분을 자신의 프로젝트 이름에 맞게 수정합니다.

([project name].wsgi) 우리는 gunicorn이라는 파이썬 HTTP 서버(Python WSGI HTTP Server)를 사용하여 웹 서비스할 것임을 헤로쿠(Heroku)에게 알려주었습니다.

아래에 명령어로 헤로쿠(Heroku)에서 장고(django)를 사용하기 위해 필요한 모듈을 설치합니다.

# source venv/bin/activate
pip install gunicorn whitenoise django-heroku

- gunicorn

: 위에서 설명한 파이썬 HTTP 서버(Python WSGI HTTP Server)입니다.

 

- whitenoise

: 헤로쿠(Heroku)에서 정적 파일(Static files)을 사용하기 위해 필요한 모듈입니다.

 

- django-heroku

: 헤로쿠(Heroku)에 배포하는 django 프로젝트의 각종 설정을 간단하게 해주는 모듈입니다.

 

설치가 완료되면 아래에 명령어를 통해 requirements.txt를 갱신합니다.

pip freeze > requirements.txt

 

갱신이 완료되면, dajgno_exercise/settings.py를 열고 아래와 같이 수정합니다.

...
import django_heroku
...

DEBUG = False

ALLOWED_HOSTS = ['127.0.0.1', '.herokuapp.com']
...
MIDDLEWARE = [
    'whitenoise.middleware.WhiteNoiseMiddleware',
    ...
]
...
# Activate Django-Heroku.
django_heroku.settings(locals())

 

지금까지의 내용을 우리가 관리하는 git에 저장합니다.

git add .
git commit -m 'prepare to deploy'
git push origin master

7. 배포(Deploy)

이제 헤로쿠(Heroku)에 우리의 장고(django) 프로젝트를 배포(Deploy)해 봅시다.

아래에 명령어로 git의 remote에 헤로쿠(Heroku)를 추가합니다.

heroku git:remote -a djangoexercise

 

아래에 명령어로 잘 추가되었는지 확인합니다.

git remote -v

 

그러면 아래와 같이 git의 remote에 헤로쿠(Heroku)가 추가된 것을 확인할 수 있습니다.

heroku  https://git.heroku.com/djangoexercise.git (fetch)
heroku  https://git.heroku.com/djangoexercise.git (push)
origin  https://github.com/dev-yakuza/django_exercise.git (fetch)
origin  https://github.com/dev-yakuza/django_exercise.git (push)

 

이제 아래에 명령어를 통해 배포(Deploy)합니다.

git push heroku master

 

헤로쿠(Heroku)는 git을 기반으로 배포(Deploy)를 합니다.

위에 명령어로 이미 우리의 장고(django) 프로젝트는 헤로쿠(Heroku)에 배포(Deploy)되었습니다.

이제 우리가 만든 migration을 이용하여 새롭게 데이터베이스를 구축하고 관리자를 생성할 필요가 있습니다.

아래에 명령어를 실행하여 데이터베이스를 구축합니다.

heroku run python manage.py migrate

 

아래에 명령어로 장고(djagno)의 관리자를 생성합니다.

heroku run python manage.py createsuperuser

8. 확인

우리가 헤로쿠(Heroku)에 배포한 장고(django) 프로젝트를 확인해 봅시다.

아래에 명령어로 우리가 만든 사이트를 웹 브라우저에 표시할 수 있습니다.

heroku open

아래와 같이 우리가 만든 장고(django) 프로젝트가 잘 표시되는 것을 확인할 수 있습니다.

URL에 /admin을 입력하여 관리자 화면으로 이동해 봅니다.

우리가 명령어로 생성한 superuser로 로그인 해 봅니다.

우리가 개발한 Post 모델(Model)이 잘 표시되는 것을 확인할 수 있습니다.


9. 완료

이것으로 장고(django) 프로젝트를 헤로쿠(Heroku)에 배포(Deploy)하는 과정을 살펴보았습니다.

우리는 이제 장고(django)를 가지고 간단한 웹 서비스를 제공할 수 있는 능력을 가지게 되었습니다.

이후 블로그에서는 서비스를 개발하면서 알게된 내용을 정리할 예정입니다.


출처

https://dev-yakuza.posstree.com/ko/django/heroku/

장고(django)의 폼(Form)을 이용하여 데이터를 추가하거나 수정하는 방법에 대해서 알아보겠습니다.


1. 개요

지금까지는 장고(django)가 기본적으로 제공하는 관리자 페이지(Administrator)를 사용하여 데이터를 추가하거나 수정하였습니다.

 

이번 블로그에서는 장고(django)의 폼(Form)을 이용하여

데이터를 추가하거나 수정하는 방법에 대해서 알아보도록 하겠습니다.

 

이 블로그는 시리즈로 작성되어 있으며, 아래에 링크를 통해 시리즈의 다른 글을 확인할 수 있습니다.

또한 이 블로그 시리즈에서 다룬 소스는 github에 공개되어 있습니다. 아래에 링크를 통해 확인 가능합니다.


2. 폼(Form) 생성

장고(django)의 폼(Form) 기능을 사용하기 위해서 blog/forms.py 파일을 생성하고 아래와 같이 수정합니다.

from django import forms
from .models import Post

class PostCreateForm(forms.ModelForm):
    class Meta:
        model = Post
        fields = ('title', 'content')

- from django import forms

: 장고(django)가 기본적으로 제공하는 폼(form)을 불러옵니다.

 

- from .models import Post

: 폼(Form)을 통해 데이터를 추가하거나 수정할 Post 모델(Model)을 불러옵니다.

 

- class PostCreateForm(forms.ModelForm):

: PostCreateForm이라는 이름으로 클래스를 생성하고 fomrs.ModelForm을 상속받습니다.

 

- class Meta:

: 이 폼(Form)에서 다룰 내용을 작성합니다.

: 여기에서는 이 폼(Form)이 다룰 모델(Model)이 Post임을 알려줬고 (model = Post),

: 그 중에서 title과 content 필드(fields = ('title', 'content'))를 다룰 것임을 알려주었습니다.


3. 뷰(View) 생성

이제 장고(django)의 폼(Form)을 사용할 뷰(View)를 작성해야 합니다.

뷰(View) 작성은 이전 블로그(장고(django)의 뷰(View))에서 연습하였기 때문에 좀 더 쉽게 제작할 수 있을거 같습니다.

(1) HTML 파일 생성

일단 데이터를 입력 받을 웹 페이지를 작성해 봅니다.

blog/templates/post_create.html 파일을 생성하고 아래와 같이 수정합니다.

{% extends 'blog/layout.html' %}

{% block content %}
    <a href="{% url 'posts' %}">
        Post List
    </a>
    <h1>Create Blog post</h1>
    <form method="POST">
        {% csrf_token %}
        {{ form.as_p }}
        <button type="submit">Save</button>
    </form>
{% endblock %}

- {% csrf_token %}

: CSRF 공격(Cross-site Request Forgery)을 방지하기 위한 코드를 입니다.

 

- {{ form.as_p }}

: 뷰(View)로부터 전달받을 파라메터 form으로부터 단락(as_p: Paragraph - <p> 태그) 형식으로 표시합니다.

: 이외에도 테이블(as_table - <table> 태그) 형식, 리스트(as_ul - <ul> 태그) 형식으로 표시할 수 있습니다.

: 이 기능을 통해 우리가 위에서 만든 form 클래스(class)에서 지정한 fields들이 표시됩니다.

 

(2) URL 생성

다음으로, 새로운 URL을 생성하기 위해 blog/urls.py를 열고 아래와 같이 수정합니다.

from django.urls import path
from . import views

urlpatterns = [
    path('', views.posts, name='posts'),
    path('post/<int:id>/', views.post_detail, name='post_detail'),
    path('post/create/', views.post_create, name='post_create'), # <<<<<<<<<<< here
]

이 부분은 이전 블로그에서 설명하였기 때문에 설명을 생략하겠습니다.

이 부분에 대해서 이해가 잘 가시지 않는 분은 이전 블로그를 참고하시기 바랍니다.

 

장고(django)의 뷰(View)

웹 페이지를 위해 장고(django)의 뷰(View)를 사용하는 방법에 대해서 알아보겠습니다.

dev-yakuza.posstree.com

 

(3) 뷰(View) 생성

마지막으로 데이터 처리를 위해 뷰(View)를 생성합니다. 

blog/views.py를 열고 아래와 같이 수정합니다.

from django.shortcuts import render, get_object_or_404, redirect
...
from django.utils import timezone
from .forms import PostCreateForm

...

def post_create(request):
    if request.method == "POST":
        form = PostCreateForm(request.POST)
        if form.is_valid():
            post = form.save(commit=False)
            post.author = request.user
            post.published_at = timezone.now()
            post.save()
            return redirect('post_detail', id=post.id)
    else:
        form = PostCreateForm()
    return render(request, 'blog/post_create.html', {'form': form})

- redirect

: 데이터를 성공적으로 저장한 후 페이지를 다른 페이지로 리다이렉트(Redirect)시키기 위한 기능을 불러옵니다.

 

- from django.utils import timezone

: 블로그 데이터에 publised_at에 현재 날짜를 넣기 위해 기능을 불러옵니다.

- if request.method == "POST": ... else

: 우리가 선언한 함수에는 기본적으로 request 파라메터가 설정되어 있습니다.

: 이 request 파라메터의 method를 확인하여 GET/POST를 구별합니다.

: POST인 경우는 폼(Form)을 작성하여 데이터를 저장하는 경우입니다.

 

- form = PostCreateForm(request.POST)

: 우리가 폼(Form)을 통해 전송한 POST 데이터는 request.POST에 담겨있습니다.

: 이 데이터를 가지고 우리가 생성한 PostCreateForm 폼 클래스(Form class)를 통해 폼 객체(Form object)를 생성합니다.

 

- if form.is_valid()

: 장고(django)가 기본적으로 제공하는 유효성 체크(Validation) 기능을 사용하여 POST로 보내온 데이터를 확인합니다.

: 우리는 블로그 Post 모델(Model)을 생성할 때, published_at에만 blank = True을 사용하였습니다.

: 따라서 이 필드 이외에 모든 필드는 필수(required) 필드가 적용됩니다.

 

- post = form.save(commit=False)

: 폼 객체(Form object)를 통해 데이터를 저장(form.save)합니다.

: 하지만 commit=False 옵션을 통해 데이터베이스에 반영하지 않도록 설정합니다.

 

- post.author = request.user

: 위에서 바로 저장하지 않은 이유는 추가적인 데이터를 함께 저장하기 위해서 입니다. 저장할 데이터의 작성자(author)를 현재 request를 보낸 사용자로 넣습니다. 우리는 관리자 화면을 통해 이미 로그인한 상태임으로 request.user에는 관리자 화면에 로그인한 사용자 정보가 담겨져있습니다.

 

- post.published_at = timezone.now()

: 추가적으로 공개일(published_at)도 함께 넣어줌으로써 데이터가 화면에 바로 표시될 수 있도록 설정합니다.

 

- post.save()

: 최종적으로 데이터를 데이터베이스에 저장합니다.

 

- return redirect('post_detail', id=post.id)

: 그리고 우리가 만든 데이터가 잘 저장되었는지 확인하기 위해

: redirect 함수를 통해 post_detail 페이지로 리다이렉트(redirect) 시킵니다.

 

- if request.method == "POST"

: ... else: 만약 보내온 요청(request)가 POST가 아닌 경우

 

- form = PostCreateForm()

: 우리가 만든 폼(Form)을 가져와 return render(request, 'blog/post_create.html', {'form': form}) 화면에 표시합니다.

 

(4) 링크 생성

위에서 만든 페이지로 활용하기 위해

메인 페이지인 blog/posts.html 파일을 열고 아래와 같이 수정합니다.

{% extends 'blog/layout.html' %}

{% block content %}
  <a href="{% url 'post_create' %}">Create Blog Post</a>
  {% for post in posts %}
    ...
  {% endfor %}
{% endblock %}


5. 확인

지금까지 작업한 내용을 확인하기 위해 http://127.0.0.1로 접속합니다.

그러면 위와 같이 우리가 새로 추가한 Create Blog Post 링크가 보이는 메인 페이지를 확인할 수 있습니다.

Create Blog Post 링크를 눌러 블로그 작성 페이지로 이동합니다.

그러면 위와 같이 폼(Form) 작성 페이지를 확인할 수 있습니다.

여러분이 저장하길 원하는 데이터를 넣고 Save 버튼을 눌러 데이터를 저장합니다.

데이터 저장 완료후 우리는 상세 페이지로 리다이렉트(Redirect)되도록 작업하였기 때문에,

위와 같이 저장된 데이터의 상세 페이지로 이동하게 됩니다.

마지막으로 상단에 Post List 링크를 눌러 메인 페이지로 이동해 봅니다.

메인 페이지에서 위와 같이 우리가 추가한 데이터가 표시되는 걸 확인할 수 있습니다.


6. 완료

이것으로 장고(django)의 폼(Form)을 사용하는 방법에 대해서 알아보았습니다.

또한 뷰(View)에서 사용자의 요청(Request)을 GET과 POST로 구분하여 다르게 처리하는 방법에 대해서도 알아 보았습니다. 이것으로 우리는 사용자가 보내온 데이터를 저장하거나 수정할 수 있게 되었습니다.


출처

https://dev-yakuza.posstree.com/ko/django/form/

웹 페이지를 위해 장고(django)의 뷰(View)를 사용하는 방법에 대해서 알아보겠습니다.


1. 개요

지금까지 데이터를 저장할 데이터베이스와의 연동과 데이터를 생성(Create)하고 읽고(Read) 갱신(Update)하고 삭제(Delete) CRUD 그리고 사용자가 접속할 URL을 생성하는 방법 등 장고(django)로 웹 서비스를 하기 위한 많은 작업을 진행하였습니다.

 

이번 블로그에서는 사용자에게 보여줄 웹 페이지를 만들기 위해

장고(django)의 뷰(View)를 사용하는 방법에 대해서 알아보겠습니다.

이 블로그는 시리즈로 작성되어 있으며, 아래에 링크를 통해 시리즈의 다른 글을 확인할 수 있습니다.

또한 이 블로그 시리즈에서 다룬 소스는 github에 공개되어 있습니다. 아래에 링크를 통해 확인 가능합니다.


2. URL 확인

이전 블로그(장고(django)의 라우팅(Routing))에서 우리는 사용자가 접속할 URL과 정적 HTML을 연결하는 방법에 대해서 알아보았습니다. 아래에 장고(django) 명령어를 실행하여 테스트 서버를 시작한 후, http://127.0.0.1:8000/에 접속하면 Hello World가 표시되는 웹 페이지를 확인할 수 있습니다.

# source venv/bin/activate
# cd django_exercise/
python manage.py runserver

3. 데이터 전달

이제 이전 블로그(장고(django)의 ORM)에서 생성한 데이터를 웹 페이지에서 보기 위해 뷰(View)에 데이터를 전달해 보도록 하겠습니다. blog/views.py를 열고 아래와 같이 수정합니다.

from django.shortcuts import render
from .models import Post

def posts(request):
    posts = Post.objects.filter(published_at__isnull=False).order_by('-published_at')
    return render(request, 'blog/posts.html', {'posts': posts})
  • from .models import Post: 우리가 만든 Post 모델(Models)을 불러옵니다.
  • posts = Post.objects.filter(published_at__isnull=False).order_by('-published_at')
    • published_at__isnull=False: published_at 필드가 null이 아닌 경우(__isnull=False)에 데이터를 가져옵니다
    • order_by('-published_at'): 데이터를 published_at 필드로 내림차순 정렬합니다.
  • return render(request, 'blog/posts.html', {'posts': posts}): 가져온 데이터를 blog/posts.html 페이지에 {'posts': posts} posts란 변수명으로 전달합니다.

4. 데이터 표시

이제 위에서 전달한 데이터를 화면에 표시해 봅시다. 

blog/templates/blog/posts.html 파일을 열고 아래와 같이 수정합니다.

<html>
  <head><title>Hello World</title></head>
  <body>
    {% for post in posts %}
    <div>
      <h1>
        {{ post.title }}<small>(published: {{ post.published_at }})</small>
      </h1>
      <p>{{ post.content | linebreaksbr }}</p>
    </div>
    {% endfor %}
  </body>
</html>

  • {% for post in posts %}...{% endfor %}: 위에서 blog/views.py에서 전달한 posts 데이터 리스트에서 post로 데이터를 하나씩 가져와 반복문을 돌립니다.
  • {{ post.title }}<small>(published: {{ post.published_at }})</small>: post 데이터의 title / published_at 필드값을 표시합니다.
  • <p>{{ post.content | linebreaksbr }}</p>: 우리가 가지고 있는 post 데이터의 content를 가져와 줄바꿈(\n)을 줄바꿈 태그(<br/>)로 변환하여 표시합니다.

위와 같이 코드를 수정하고 http://127.0.0.1:8000/에 접속하면

아래와 같이 데이터가 표시되는 것을 확인할 수 있습니다.

 

조금 더 연습을 하기 위해 상세 페이지를 제작해 봅시다.

위에서 만든 blog/templates/blog/posts.html 파일을 아래와 같이 수정합니다.

<html>
  <head><title>Hello World</title></head>
  <body>
    {% for post in posts %}
    <a href="{% url 'post_detail' id=post.id %}"> <!-- <<<<<<<<<<<<<<<<<<<<< here -->
      <h1>
        {{ post.title }}<small>(published: {{ post.published_at }})</small>
      </h1>
      <p>{{ post.content | linebreaksbr }}</p>
    </a> <!-- <<<<<<<<<<<<<<<<<<<<< here -->
    {% endfor %}
  </body>
</html>

  • {% url 'post_detail' id=post.id %}: 우리가 정의한 django_exercise/urls.py 와 blog/urls.py에서 post_detail이라는 이름(name)을 찾고 해당 URL로 변경해 줍니다. 이때 id라는 파라메터를 만들고 Post의 id를 넣어줍니다.

아직은 post_detail과 매칭되는 URL을 만들지 않았습니다.

이제 post_detail과 대응하는 URL과 뷰(View)를 제작해 봅시다. 

blog/templates/blog/post_detail.html을 생성하고 아래와 같이 수정합니다.

<html>
  <head><title>Hello World</title></head>
  <body>
    <a href="{% url 'posts' %}">
      Post List
    </a>
    <h1>
      {{ post.title }}
    </h1>
    <p>created: {{ post.created_at }}</p>
    <p>updated: {{ post.updated_at }}</p>
    <p>published: {{ post.published_at }}</p>
    <p>author: {{ post.author.username }}</p>
    <p>{{ post.content | linebreaksbr }}</p>
  </body>
</html>

  • {% url 'posts' %}: URL 파일에서 posts라는 이름(name)을 갖는 URL을 찾고 그 URL로 대체합니다.
  • author: {{ post.author.username }}: 우리는 우리가 만든 Post 모델과 장고(django)에서 기본적으로 제공하는 auth.User과 연결하여 작성자(author) 필드를 만들었습니다. 이렇게 연결한 모델(Models)에서 username을 가져와 표시하였습니다.

이제 화면에 표시할 페이지는 준비되었습니다. 

blog/urls.py를 열고 아래와 같이 수정합니다.

from django.urls import path
from . import views

urlpatterns = [
    path('', views.posts, name='posts'),
    path('post/<int:id>/', views.post_detail, name='post_detail'), # <<<<<<<<<<<< here
]
  • post/<int:id>/: URL에 파라메터로 넘어온 숫자형 데이터 id를 표시합니다.
  • name='post_detail': URL의 이름은 post_detail이며 views.post_detail와 매핑되어있습니다.

이로써 {% url 'post_detail' id=post.id %}을 사용하여

post/<int:id>/ URL을 표시하고 views.post_detail과 매핑 시킬 준비가 되었습니다.

이제 blog/views.py 파일을 열고 아래와 같이 수정합니다.

from django.shortcuts import render, get_object_or_404 # <<<<<<<<<<<< here
from .models import Post


def posts(request):
    posts = Post.objects.filter(
        published_at__isnull=False).order_by('-published_at')
    return render(request, 'blog/posts.html', {'posts': posts})

# <<<<<<<<<<<< here
def post_detail(request, id):
    post = get_object_or_404(Post, id=id)
    return render(request, 'blog/post_detail.html', {'post': post})
  • get_object_or_404: 장고에서 기본적으로 제공하는 함수를 추가하였습니다. 이 함수는 해당 객체(Object)에서 데이터를 가져오는데 데이터가 없으면 404 에러를 발생시키는 함수입니다.
  • def post_detail(request, id):: 새롭게 추가한 URL과 매핑되는 함수입니다. 파라메터로 넘겨준 id를 별도의 변수로 전달받을 수 있습니다.

이것으로 상세 페이지를 제작해 보았습니다. 

http://127.0.0.1:8000으로 접속하면 이전과는 다르게 아래와 같이 링크가 걸린 페이지를 볼 수 있습니다.

해당 링크를 누르면 상세페이지 URL(http://127.0.0.1:8000/post/2/)로 이동하며 아래와 같이 Post의 상세 페이지를 확인할 수 있습니다.


5. 템플릿 파일

이제 뷰(View)와 URL, 그리고 데이터를 표시하는 방법은 어느정도 익숙해진거 같습니다.

하지만 HTML 파일에 중복되는 코드가 많습니다. 아직 css, js나 meta 태그를 넣지 않았지만,

이런 태그들이 많아지면 점점 중복되는 코드들이 많아지기 시작합니다.

이런 중복되는 부분을 새로운 파일로 만들고 가져오는 방법에 대해서 알아보도록 하겠습니다.

 

일단 정적 파일(css, js 등)을 추가하는 방법에 대해서 알아보겠습니다.

blog/static/css/main.css 파일을 생성하고 아래와 같이 수정합니다.

h1 {
  color: red;
}

 

 

여기에서는 디자인에 관해서는 다루지 않겠습니다.

더 이쁘게 꾸미고 싶으신 분들은 이 파일을 수정하시기 바랍니다.

장고(django)는 static이라는 이름의 폴더를 자동으로 찾아 등록하기 때문에 추가적인 등록 과정은 필요하지 않습니다.

이제 blog/templates/blog/layout.html 파일을 생성하고 아래와 같이 수정합니다.

{% load static %}
<html>
  <head>
    <title>Hello World</title>
    <link rel="stylesheet" href="{% static 'css/main.css' %}" />
  </head>
  <body>
    {% block content %} {% endblock %}
  </body>
</html>

  • {% load static %}: 이 파일에서 static 폴더를 사용할 수 있도록 static 폴더를 불러옵니다.
  • <link rel="stylesheet" href="{% static 'css/main.css' %}" />: 우리가 불러온 static 폴더에서 css/main.css를 찾아 URL로 변경합니다.
  • {% block content %} {% endblock %}: 이 레이아웃(layout) 파일에 필요한 데이터(content)를 표시할 위치를 설정합니다. 이렇게 표시하고 싶은 부분을 block [name]으로 설정하고 해당 블록(block)을 대체할 부분을 [name]을 사용하여 대체합니다.

이제 레이아웃(layout) 파일을 활용하기 위해 blog/templates/blog/posts.html 파일을 열고 아래와 같이 수정합니다.

{% extends 'blog/layout.html' %}

{% block content %}
  {% for post in posts %}
    <a href="{% url 'post_detail' id=post.id %}">
      <h1>
        {{ post.title }}<small>(published: {{ post.published_at }})</small>
      </h1>
      <p>{{ post.content | linebreaksbr }}</p>
    </a>
  {% endfor %}
{% endblock %}

  • {% extends 'blog/layout.html' %}: 이 파일(posts.html)은 우리가 만든 레이아웃(layout.html) 파일을 활용합니다.
  • {% block content %}...{% endblock %}: 우리가 레이아웃(layout.html) 파일에 만든 블록(block), 그중에 이름이 content인 블록(block)을 여기에 작성한 내용으로 대체합니다.

그리고 blog/templates/blog/post_detail.html 파일을 열고 아래와 같이 수정합니다.

{% extends 'blog/layout.html' %}

{% block content %}
  <a href="{% url 'posts' %}">
    Post List
  </a>
  <h1>
    {{ post.title }}
  </h1>
  <p>created: {{ post.created_at }}</p>
  <p>updated: {{ post.updated_at }}</p>
  <p>published: {{ post.published_at }}</p>
  <p>author: {{ post.author.username }}</p>
  <p>{{ post.content | linebreaksbr }}</p>
{% endblock %}

위에서 설명한 내용과 동일하게 레이아웃(layout.html) 파일을 활용하고 이름이 content인 블록(block)을 여기에 작성한 내용으로 대체합니다.


6. 확인

실제 작업한 내용을 확인하기 위해 http://127.0.0.1:8000/에 접속해 봅니다.

그러면 우리가 작성한 css가 적용된 화면을 아래와 같이 볼 수 있습니다.

(화면이 제대로 표시되지 않는 다면 테스트 서버를 재실행 해 보세요.)

링크를 눌러 상세 페이지(http://127.0.0.1:8000/post/2/)에 이동해도

아래와 같이 css가 적용된 것을 확인할 수 있습니다.

이것으로 우리는 장고(django)의 템플릿을 활용하여 정적 파일(css, js 등)을 가져오는 방법과 HTML의 중복 줄이기 위한 방법에 대해서 알아보았습니다.


7. 완료

이번 블로그에서는 장고(django)의 뷰(View)를 활용하는 전박적인 방법에 대해서 살펴보았습니다.

데이터를 가져와 화면에 출력하고, 출력하는 화면을 잘 관리하기 위한 장고(django)의 템플릿도 확인해 보았습니다.

또한 css와 js같은 정적 파일(static)을 가져오는 방법에 대해서도 알아 보았습니다.

이제 우리는 이것들을 활용하여 일반적인 웹 서비스 사이트를 제작할 수 있게 되었습니다!

+ Recent posts