강의

멘토링

커뮤니티

Intro

개발환경 셋업 : Pycharm

프로젝트 시작

pycharm 다운로드

터미널에 pip install django -> cd .. -> django-admin startproject [프로젝트명]

새로 생성된 프로젝트 open하기 

환경 세팅

Preferences - Python Interpreter - 톱니바퀴 눌러 venv 추가 후 설정

터미널 껐다 키고 다시 django install

 

django 개발 패턴

장고의 개발 패턴 : MVC? MVT?

  • Model : sql 없이 database와 객체를 주고 받고~
    • 객체 하나 -> Row 하나, 멤버 변수 -> Columns
  • View : 계산, 유저와 서버 간의 소통( request, response )
  • Template( Controller ) : frontend ( html, css, js ) 구성

Django Tutorial

첫 앱 시작, 그리고 기본적인 view 만들기

 앱 추가하기

1. python manage.py startapp accountapp

2. 프로젝트 폴더의 settings.py 에 INSTALLED_APPS에 'accountapp' 등록

views.py

하나의 기능 -> 하나의 함수

request를 인자로 받고 HttpResponse('hello world') return

alt+enter로 자동 import 가능

urls.py

  1. 프로젝트 폴더의 urls.py -> path('account/', include('accountapp.urls')), 추가
  2. app 폴더에 urls.py 추가
  3.  app_name = "accountapp" -> 앱 이름 명시
  4. path('hello_world/', hello_world, name='hello_world'),

서버 실행

python manage.py runserver 

[local host ip]/account/hello_world/ 

Git 의 소개

GIT이란?

-> Version Control 시스템, 따라서 오류가 났을 때 rollback 가능! + 협업에 편리한 기능 다수 존재

Branch : 다른 개발 버전 ( 기존 버전에 영향 X )

Merge 기능을 통해 branch들을 합칠 수도 있다.

Gitignore 설정, 환경변수 분리, 첫 커밋

Gitignore란 

깃에서 추적되지 않을 파일을 지정해놓는 파일

project 루트에서 .gitignore file 추가

https://github.com/github/gitignore/blob/master/Global/JetBrains.gitignore -> pycharm 프로젝트에 맞는 파일

 가상환경 폴더를 무시하기 위해 venv/ 추가 

db.sqlite3와 .idea/ 추가

환경 변수 분리

git에 올리기 전에 settings.py 에 담겨있는 secret key 등 민감한 정보를 분리해야 함

https://django-environ.readthedocs.io/en/latest/ -> 내부적으로 secret key를 읽어오는 라이브러리 

  1. pip install django-environ
  2. 문서를 따라 최상위 폴더에 .env 추가 후 시크릿키 따옴표 없이 넣어주기
  3.  settings.py에 import os, environ 
  4. 문서 처음에 있는 내용 붙여넣고  read_env 부분 괄호 안에 env_file = os.path.join( BASE_DIR, '.env' ) 추가
  5. 원래 있던 secret key 부분도 env('SECRET_KEY')로 대체
  6. .gitignore에  .env 추가

--

추가적으로 아래 모듈 다운로드

pip install python-memcached

pip install django-redis

GIT 활성화

  1. pycharm 메뉴 - VCS - enable version control ... 
  2. 터미널에서 cd PycharmProjects/almighty/
  3. git status -> 현재 올라가지 않은 추적 파일  상태
  4. git add . -> 모든 추적 파일 스테이지에 올림
  5. git commit -m "Initial commit" 

장고 Template의 extends, include 구문과 render 함수

gitignore에 pycache 추가

  1. 프로젝트 폴더와 앱 폴더에 있는 pycache 폴더 삭제 ( 파인더로 )
  2. git add . -> git commit -m "Delete redundant files"
  3. .gitignore 파일에 __pycache__/ 추가

Hyper Text -> 문서 간 이동 가능

Markup Language

extends / include 구문

  • extends : pre-made html 바탕 파일을 가져오는 것
  • include : 작은 html 조각을 템플릿에 가져오는 것

Template base 

  1. 최상위 폴더에 templates 폴더 생성 
  2. base.html 추가하고 이전에 있던 views.py의 메서드를 render( request, 'base.html' )
  3. settings.py의 templates 부분에 DIR 추가

include / extends / block 구문을 이용한 뼈대 html 만들기

include 구문 사용하기

  1. head 부분 잘라내서 templates 폴더에 head.html 추가
  2. head 있던 부분에 {% include 'head.html' %}

앱 내부에 템플릿 만들기

accountapp 폴더 내에 templates/accountapp 디렉토리 생성 후 그 안에 extend할 helloworld.html 추가

Static 설정 및 CSS 파일 분리

static 관리

static -> 자주 변경되지 않는 파일, 자원들을 뜻함

  1. settings.py에 STATIC_ROOT = 'staticfiles' 추가
  2. 앱에 종속되지 않는 static 파일들을 관리하기 위해 바로 아래에 STATICFILES_DIRS = [ BASE_DIR / "static", ] 추가

CSS 분리하기

static 폴더에 base.css 생성, class를 주로 사용 !

head.html에 {% load static %} 추가하고 css 파일 링크 추가

CSS 간단 핵심

Display 속성

  • Block : 부모 태그의 width 100%를 차지, 높이는 따로 설정하지 않으면 내용물에 따름
  • Inline : 내용물의 크기 만큼만 자리를 차지
  • Inline-block : block처럼 보이지만 인라인처럼 내용물만큼의 width를 가짐
  • None : 존재하긴 하지만 브라우저에 나타나지 않음
    • visibillity : hidden과는 달리 자리도 차지하지 않게 됨

SIZE 척도

-> related with font-size

  • px : 부모 관계 없이 px 단위의 절대값
  • em : 부모 요소( 조상 전체 곱해서 ) 폰트 사이즈의 배수
  • rem : 최상위 요소( root html, 기본 16px ) 폰트 사이즈의 배수, 주로 사용
  •  %: 바로 위의 부모 요소 폰트 사이즈의 배수

CSS display 속성, rem 단위 실습

CSS 적용 우선 순위

별도 CSS 파일 < html파일의 Style 태그 < html 태그 인라인

인라인 태그의 경우 너비 지정을 하더라도 display 지정 하지 않으면 적용되지 않는다. 

GIT 수정사항 없애기

git reset --hard HEAD

  • HEAD : 가장 최근 (상위) 커밋 삭제
  • --hard : 파일 자체 삭제

Model, DB 연동

Model

-> DB 알 필요 없이 장고가 DB와 연동

  1. models.py에 DB 아이템으로 쓰일 class 생성 ( models.Model 상속 )
  2. python manage.py makemigrations 
  3. python manage.py migrate

HTTP 프로토콜 GET, POST

GET / POST

USER --request--> <--response-- Server

'무엇'을 주고받는지 추가적인 정보를 보내는 방식

  • GET : url에 파라미터로 데이터를 담아 전송, 양이 적고 치명적이지 않은 데이터 조회 등에 사용.
  • POST : 정보를 body에 넣어 전송. create, update, user 관련 등에 사용

-> https가 아닌 http라면 암호화 X

GET, POST 프로토콜 실습

post 사용 시 form 태그 내부에 {% csrf_token %} 반드시 쓰기

POST 통신을 이용한 DB 데이터 저장 실습

DB SAVE 과정

  1. Send POST data
  2. Recieve POST data
  3. Save DB
  • request.POST.get('input name')
  • model 객체 생성 후 값 담고 .save()
  • render 함수의 인자로 객체 보내기
  • html에서 사용

DB 정보 접근 및 장고 템플릿 내 for loop

READ

Model클래스.objects.all() 를 통해 리스트로 모든 행을 가져올 수 있음

단순히 페이지 새로고침 -> HttpResponseRedirect()

 django.urlsreverse('앱이름:함수이름')으로 url 대체 가능

Pycharm 디버깅 설정

DEBUG 환경 설정

상단 Run 메뉴 - Edit Configurations... 

  1. templates 중 python 선택 
  2. script path를 [프로젝트최상위폴더]/venv/bin 으로 설정
  3. parameter를 runserver로 설정  
  4. manage.py 우클릭 - debug 'manage'

Django 의 CRUD, Class Based View 소개

Account -> 인증( Authentication )

sign up, login&logout, view info, change info, quit...

Django의 CRUD : Class-Based View

Create Read Update Delete 각각의 view 클래스를 제공

Function-Based View -> hello_world 처럼 함수 기반, 완성물이 엄청나게 길어지고 가독성이 낮아짐

 

Accountapp implementation

CreateView를 통한 회원가입 구현

CreateView() 이용하여 View 만들기

class AccountCreateView(CreateView):

# 주요 파라미터 지정

model = User

form_class = UserCreationForm

success_url = reverse_lazy("accountapp:hello_world")

-> reverse()는 함수형, reverse_lazy()는 클래스형

template_name = 'accountapp/create.html'

-> path에 AccountCreateView.as_view() 이용하여 등록

HTML에서 연결하기

  • action="{% url 'accountapp:create' %}"로 라우팅
  • {{ form }} 으로 UserCreationForm 자동 생성

Login / Logout 구현

LOGIN & LOGOUT

로그인, 로그아웃은 따로 함수 만들지 않고 직접 파라미터를 넣어도 ok

path('login/', LoginView.as_view(template_name='acountapp/login.html'), name='login'),

path('logout/', LogoutView.as_view(), name='logout'),

LOG IN & OUT Redirect Mechanism

next -> LOGIN_REDIRECT_URL이 없으면 default로 가게 됨

  1. html에서 login, logout 페이지 링크를 if문으로 생성 후 href="{% url 'accountapp:login' %}?next={{ request.path }}"
  2. settings.py에 LOGIN_REDIRECT_URL = reverse_lazy('accountapp:hello_world')과 LOGOUT_REDIRECT_URL = reverse_lazy('accountapp:login') 등록

Bootstrap 을 이용한 Form 디자인 정리

장고 FORM 부트 스트랩 라이브러리 

>> pip install django-bootstrap4

settings.py의 INSTALLED_APPS에 'bootstrap4' 추가 

html에서 {% load bootstrap4 %}

{% bootstrap_form form %}

font 파일로 폰트 넣기

static/fonts 폴더에 otf 파일 넣어두기

<style>@font-face문</style>

DetailView를 이용한 개인 페이지 구현

UserDetailView

  • class AccountDetailView(DetailView) 생성, 파라미터로 model과 template_name
  • DetailView 조회를 위해서는 PrimaryKey 정보가 필요하므로 'detail/<int:pk>'의 url을 가짐
  • href="{% url 'accountapp:detail" pk=user.pk %}"
  • 본인의 정보만 볼 수 있게끔 class에 context_object_name = 'target_user' 파라미터 추가하고 html에서 user 대신 target_user 사용

UpdateView를 이용한 비밀번호 변경 구현

정보 수정 페이지 

pk가 필요한 것을 제외하면 CreateView와 거의 동일함!

  1. class AccountUpdateView(UpdateView)
  2. url은 detail과 비슷하게 /<int:pk> 추가

form 파일 만들기

class AccountUpdateForm(UserCreationForm):

def __init__(self, *args, **kwargs):

super.__init__(*args, **kwargs)

self.fields['username'].disabled = True 

-> 이후 views.py의 form_class 파라미터 수정

DeleteView기반 회원탈퇴 구현

회원 탈퇴 기능

DeleteView를 이용해서 다른 view와 마찬가지로 작성, pk 필요

target_user 사용도 추가

Authentication

Authentication 인증시스템 구축

return HttpResponseForbidden()   ->  오류 페이지 return

Decorator를 이용한 코드 간소화

Decorator 패턴

@login_required -> 로그인 체크, 반환까지 그대로 해줌

@method_decorator(데코레이터, '메서드이름') -> 일반 function을 사용하는 데코레이터를 method( 클래스 내의 함수 )에 사용할 수 있도록 해주는 데코레이터

사용자 정의 데코레이터 만들기

프로젝트 폴더 내에 decorators.py 추가 후 def

원래 함수에 필요한 인자 받아서 내용 구현 ! 

마찬가지로 method decorator 이용하면 된다

Decorator 구문 줄이기

상단에서 묶음 이름 = [ 데코레이터 이름 배열 ] 으로 묶어두면

@method_decorator( 묶음 이름, '필요한 함수 이름')으로 한 번에 사용 가능 !

superuser, media 관련 설정

Admin 계정

localhost/admin 에서 관리자 페이지 확인 

python manage.py createsuperuser 로 계정 생성

Media 파일 관련 설정

  1. settings.py에서 static처럼 경로를 지정해준다
    • MEDIA_URL = '/media/'
    • MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
  2. media 파일 관리에 필요한 라이브러리 설치
    • pip install pillow 

Profileapp Implementation

Profileapp 시작 그리고 ModelForm

Profile 객체 생성

models.OneToOneField -> 다른 객체에 1 대 1 연결

on_delete=models.CASCADE -> user 삭제 시 함께 삭제

related_name = profile -> user.profile로 접근 가능

ModelForm

-> model을 기반으로 form 자동 생성 ! 

ModelForm 상속한 클래스 생성 후 class meta:

model=모델이름

fields=[] 

Profileapp 구현 시작

생성된 Model DB 적용

python manage.py makemigrations

python manage.py migrate

form에서 이미지 사용

form 태그 내에 enctype="multipart/form-data" 명시

Profileapp 마무리

이미지 사용하기

<img src="{{ target_user.profile.image.url }}"

이미지 라우팅 추가

프로젝트 urls.py의 urlpatterns 배열 뒤에 + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) 추가 

-> 모두 django.conf에서 import

get_success_url 함수 그리고 리팩토링

프로필 생성 / 수정 후 디테일 페이지 띄우기

def get_success_url(self):

return reverse('accountapp:detail', kwargs={'pk': self.object.user.pk})

Articleapp Implementation

MagicGrid 소개 및 Articleapp 시작

Magic Grid

임의의 높이를 가진 카드형 레이아웃을 위한 javascript 라이브러리

https://github.com/e-oj/Magic-Grid

  1. dist/magic-grid.cjs.js의 내용을 statics/js 폴더에 생성
  2. JSFIDDLE 페이지의 Sample Usage에 있는 js 내용 추가
  3. html & css 작성하고 link 

Lorem Picsum : https://picsum.photos/ -> 일정한 크기의 랜덤 이미지 url 반환 

Article 모델 생성 오류 수정

model에 created_at = models.DateField(auto_now_add=True, null=True)

ListView, Pagination 소개 및 적용

List View

게시글, 회원 정보 등 -> Single Object

Article List -> Multiple Object 를 표현하기 위한 view 

Pagination

-> 객체 리스트를 페이지화 

Infinite Scroll -> 스크롤하면 자동으로 추가 로딩

article_List와 page_obj 객체가 html에서 주요하게 쓰임

  • for문을 이용해 article in article_List로 객체 불러옴
  • 카드 내용(이미지)는 snippets에 따로 빼서 include문으로 불러오고, with article=article로 객체를 함께 넘겨줌
  • 마찬가지로 pagination으로 page_obj를 넘겨 include
  • page_obj.has_previous : 이전 페이지 유무
  • page_obj.previous_page_number : 이전 페이지 num
  • page_obj.number : 현재 페이지 num
  • page_obj.has_next : 다음 페이지 유무
  • page_obj.next_page_number : 다음 페이지

Commentapp Implementation

Mixin 소개 및 Commentapp 구현

Mixin

Create는 No object <-> Detail은 No form

-> View에서 mixin 다중 상속을 통해 object와 form 동시에 사용 가능  ! 

Commentapp 마무리

댓글 가져오기

{% for comment in target_article.comment.all %}

{% include 'commentapp/detail.html' with comment=comment %}

{% endfor %}

Mobile Responsive Layout

모바일 디버깅, 반응형 레이아웃

모바일로 local server 접속

python manage.py runserver = python manage.py runserver 127.0.0.1:8000

-> 컴퓨터에서 서버를 돌리고, 컴퓨터로만 들어갈 수 있는 local host

따라서 0.0.0.0:8000 으로 구동하면 ip 기반으로 포트를 열게 되므로 모바일에서도 접속 가능해짐

CMD에서 ipconfig 명령어로 IP를 확인하고 이를 통해 접속! ( 같은 공유기 사용 시에만 가능 )

접속 설정

settings.py의 ALLOWED_HOSTS = []에 '*' 추가 ( 모든 호스트 허용 )

화면 크기 맞추기

  • <head> 태그 안에 <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> 추가
  • .container div { width 삭제 } 
  • .container a { width: 45%; max-width: 250px; }
  • .container { padding: 0; margin: 0 auto; }
  • magicgrid.js에서 gutter: 12,  로 지정
  • css에서 @media screen and (max-width:500px) 을 통해 스크린 사이즈가 500이하일 때 html { font-size: 12px } 적용 

Projectapp Implementation

ProjectApp 구현

글자 수 많으면 ...으로 줄이기

{{ project.title | truncatechars:8 }}

MultipleObjectMixin을 통한 ProjectApp 마무리

DB에 수정사항 있을 때

models.py 수정 -> form 수정 ( 필요시 ) -> 다시 migration 

Subscribeapp Implementation

RedirectView을 통한 SubscribeApp시작

RedirectView

-> 구독 버튼을 누르면 해당 페이지에서 바로 처리 

  • def get_redirect_url()
  • def get()

  

Field Lookup을 사용한 구독 페이지 구현

Field Lookup

Model.object.filter(pk='', user='') -> AND function

OR function, WHERE function -> Field Lookups
project__in=projects -> SELECT ... WHERE project in ()

https://docs.djangoproject.com/en/3.1/ref/models/querysets/#field-lookups

Django Wrap-up

WYSIWYG 의 소개 및 적용

WYSIWYG ( 이지윅 에디터 )

-> 보이는 대로 쓰인다 ! 

https://github.com/yabwe/medium-editor

script link 복붙 -> var editor = new Medium ... script 복붙

글 작성 내용이 태그 포함해서 나올 때 

{{ target_article.content | safe }}

프로젝트 정리 및 다듬기

객체 이름 str로 만들기

models.py에서 def __str__(self):

return f'{self.pk} : {self.title}'

Home DIR 만들기

프로젝트 urls.py에 path('', 사용할view.as_view(), name='home')

Material Icon

https://material.io/resources/icons/?style=baseline

https://github.com/google/material-design-icons

스타일 시트 link 복붙, class에 material-icons 주고 아이콘 이름을 a 태그 사이에 값으로 넣으면 끝!

What is DOCKER? : Service Deployment

Why Docker? 서비스 배포로 들어가며

1. Django Container에 app을 넣어서

2. 이를 도커 시스템에 넣어줘야 함

3. Vultr라는 가상 서버를 통해 올려주기

Docker?

다른 시스템 위에 다른 시스템을'가상화'할 때 container를 이용한 방법 사용

  • 기존보다 훨씬 빠른 속도
  • 다양한 분야에서 이용됨
  • 환경 규격화 가능 -> 환경을 Image에 모두 담아두고 Container에 담아서 구성 ( 클래스 - 객체의 관계와 비슷함 ! )

VPS 대여

VULTR

-> VPS( 가상 사설 서버 ) 대여 플랫폼

실제 서버는 물리적인 컴퓨터가 필요, 대여를 위해서는 많은 비용 필요함! 

VULTR 사용하기

https://www.vultr.com/?ref=8741024-6G

정보 입력 후 일정 크레딧을 무료로 받을 수 있음

1. + 버튼 누르고 가상 서버 추가, Cloud Compute 선택

2. 서버 위치를 고르고 서버 타입은 Application - Docker - Ubuntu 선택, 사이즈는 가장 작게 ! ( 과금 단위는 사용 시간 )

3. 나머지 설정은 나중에! 하고 deploy하면 설치됨 

가상 서버 접속

터미널에서 ssh 명령어를 통해 원격의 서버에 접속 가능

-> ssh라고 쳤을 때 나오는 게 없다면 openSSH 설치

서버 정보 참고, ssh root@ip 명령어 입력 후 암호 입력

docker 설치 확인을 위해 docker 명령어 실행

docker container ls 명령어로 현재 실행중인 컨테이너의 목록 확인 가능

Docker Container, Image

Docker GUI Portainer 컨테이너 생성

dockerhub : 전세계에서의 도커 이미지 공유 사이트

portainer.io : Docker CLI -> GUI 소프트웨어

portainer 설치

dockerhub(https://hub.docker.com/r/portainer/portainer-ce  )에서 portainer-ce 이미지를 docker에서 사용하기 위한 커맨드 입력

  • docker volume create portainer_data
  • docker run -d -p 9000:9000 --name=portainer --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer-ce

ip주소:9000 접속 후 portainer 구동 확인 가능

나오는 창에서 비번 설정하여 create user -> docker connect

Port의 이해 그리고 Nginx 컨테이너 생성

PORT

서버와 통신할 때 사용되는 출입구 번호! 

- Vultr 가상 서버 안에 docker 시스템과 portainer시스템이 존재, 여기에는 특정한 port가 존재

- 외부의 port와 portainer의 port를 연결

Port 80 -> HTTP protocol 기본 포트 

Nginx 컨테이너

  1. portainer.io에서 Containers 탭 선택 - add container
  2. name과 image에 nginx 입력
  3. publish a new port 클릭 -> 80, 80 ( 테스트용 )
  4. deploy 
  5. 서버 ip를 포트 없이 그대로 입력하면 연결 확인 가능

django 소스코드 Github 업로드

Django Container

컨테이너 안에 소스를 넣고 구동해야 하므로 복잡할 수 있음! 

  1. Upload source to Github
  2. Write Dockerfile -> 이미지 설계도 같은 느낌 ! 
  3. Build Image
  4. Run Container

Upload Source to Github

  1. new repository 생성
  2. 생성 후 나오는 페이지의 설명(2)에 따라 업로드
    • git remote add origin "주소"
    • git branch -M main ( master보단 main... )
    • git push -u origin main
  3.  pip freeze >> requirements.txt로 의존성 정보 남기기

Dockerfile 구문

Dockerfile 구문

FROM : base image를 가져옴 ( 부모 이미지 )

RUN : 실행 명령어 ( pip list, install, git, run ... )

WORKDIR : cd...의 역할, 절대 경로 사용

EXPOSE : 가상 서버와 연결시킬 수 있도록 django port를 노출

CMD : 컨테이너를 실행할 때 필요한 커맨드들 입력 ( python manage,py runserver 0.0.0.0:8000 )

Dockerfile 작성 및 Image, Container 생성

Dockerfile 작성

portainer 페이지에 images - build a image에서 web editor를 사용하거나 pycharm 등에서 만들어서 파일을 올릴 수 있음 

# 베이스 이미지 가져오기 -> Python 공식 이미지 ( dockerhub에 존재 )
FROM python:3.9.0

WORKDIR /home/

RUN git clone https://github.com/isdiscodead/likelion_django_study.git

WORKDIR /home/likelion_django_study/

# requirements에 있는 라이브러리들 모두 설치
RUN pip install -r requirements.txt

# db 연동
RUN python manage.py migrate

EXPOSE 8000

CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]

그런데 이렇게는 실행이 안 됨 ( 환경 변수 분리해놨기 때문 )

-> RUN 커맨드 두 개 사이에 .env 파일에 있는 시크릿 키를 echo 커맨드로 넣어서 테스트 ! ( 실제로는 이렇게 ㄴㄴ )

RUN echo "SECRET_KEY=시크릿키값" > .env

Django Container 만들기 

django_test_image:1 처럼 이름을 짓고 이미지 빌드 

이미지 탭에서 생성된 이미지 확인 후, 컨테이너 탭에서 add

django_container 이름으로 생성, 이미지 이름에 django_test_image:1 넣기

포트는 8000 - 8000 연결 ( 장고 컨테이너 - 외부 ) 후 deploy 

Gunicorn 설치 및 runserver 명령어 대체

Gunicorn

Django Container로 runserver 시 문제! runserver는 개발용

따라서 runserver를 대체하고, Nginx와 Django를 연결해주는 인터페이스인 Gunicorn 라이브러리를 Django Container 안에 넣어주어야 함

Gunicorn 설치

개발 터미널에서 pip install gunicorn 후 pip freeze > requirements.txt로 넣어주고, 깃에 업로드

Dockfile 수정 

1. pip install -r requirements.txt 뒤에 pip install gunicorn 추가 ( 캐시 지우기 용 )

2. CMD 부분 수정

# 수정 전
CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]

# 수정 후 
CMD ["gunicorn", "almighty.wsgi", "--bind", "0.0.0.0:8000"]

3. 버전 :2로 만들어서 image 생성 후 django_container_gunicorn으로, 8080 - 8000 포트 연결해서 새로운 컨테이너 생성 ( 8000번은 이미 사용 중이므로 외부 포트를 8080으로 하는 것 )

그런데 이 상태에서는 static 파일들을 가져오지 못하므로 Nginx를 연결해주어야 한다 ! 

Docker Network, Volume

Docker Network의 이해 및 구현

외부 ip + 80 -> Nginx 내부 80 -> Django 내부 8000 

Django 내부 8000 -> ?? 경로를 알 수 없음 !

Docker Network 

Container들을 하나의 네트워크로 묶어줌 ! 내부에 있는 Container들끼리 Container Name을 기반으로 요청 주고받기가 가능해짐 ( http://django_container_gunicorn:8000 과 같은 방식 )

Docker Network : Django Container

  1. 먼저 원래 있던 Container들을 portainer 빼고 모두 지워줌
  2. 네트워크 탭에서 add -> nginx-django으로 생성
  3. django_container_gunicorn을 port 없는 채로, 네트워크 탭에서 nginx-django 선택해서 deploy

Docker Network : Nginx Container

Nginx 컨테이너는 먼저 설정 파일을 만들어줘야 함

nginx.conf -> gunicorn( https://gunicorn.org/#deployment  )에서 베이스 가져온 뒤 아래처럼 수정

 worker_processes auto;

 events {
 }

  http {
      server {
        listen 80;

        location / {
            proxy_pass http://django_container_gunicorn:8000;
            proxy_set_header Host $host;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
      }
  }

Filezilla 

위의 설정 파일을 서버에 올려주기 위해 Filezilla( https://filezilla-project.org/ ) 라는 프로그램을 이용해야 함

  1. filezilla에서 vultr에 있는 ip, root, 비밀번호, 22번 포트를 이용해서 가상 서버 안의 파일에 접근할 수 있음
  2. home 폴더 안에 django_course 폴더 생성 후 로컬 폴더에 있는 설정 파일을 옮겨주면 끝! 
  3. 이후 nginx 이미지를 이용한 컨테이너 생성, 포트는 80 - 80으로 연결하고 Network 탭에서 nginx-django 선택
  4. volume 탭에서 container : /etc/nginx/nginx.conf 로 하고 bind 체크, host : /home/django_course/nginx.conf로 deploy
  5. 이제 그냥 ip주소만으로 서버 접속이 가능해짐! 

Static 의 이해

Static, 정적 파일이란 

  1. 초기의 웹 : Static ( 정적인 html )으로만 이루어짐 
  2. 공간적 부족을 이유로 동적인 파일들이 생기기 시작
  3. Static Content는 Server에서, Dynamic Content는 Application에서 관리하는 방식으로 분리됨

왜 Django - Gunicorn이 static을 사용하지 못할까?

-> Nginx가 server에 해당, Django와 Gunicorn이 Application에 해당하기 때문에 static 파일들을 사용하려면 Nginx가 반드시 필요함!

static 사용하기

  1. Collect static content from Django container
  2. Synchronize static contents with Nginx container

Collectstatic 명령을 통한 Static 파일 취합

Collect Static

  1. python manage.py collectstatic 으로 static 파일들을 취합 
  2. 터미널에 출력된 경로에서 모든 static 파일들을 확인 가능 -> 경로 기억해두기 
  3. Dockerfile에 RUN python manage.py collectstatic 명령어를 추가하고 새로운 image 빌드 

✔ static이 collect 되는 경로는 settings.py에서 STATIC_URL과 STATIC_ROOT에서 설정됨 

Docker Volume의 이해

Docker의 Volume이란

1. Bind Volume : Host Server( Vultr )의 Nginx.conf와 Nginx Container의 Nginx.conf를 연동

2. Named Volume : docker 안에서 하나의 Named Volume 생성 후 각각의 Container들에 붙여서 동기화 ! -> Container가 사라지더라도 volume은 남아있음 

Docker Volume 생성 및 Container 적용

Docker Volume 사용하기

  1. Container들 전부 삭제 ( portainer 빼고 )
  2. image와 network는 그대로 사용, volume 탭에서 add volume
  3. static 볼륨 생성, media 볼륨 생성 ( 설정 X )
  4. django_container_gunicorn 생성 ( 이미지와 포트는 그대로, network : nginx-django, volume : /home/프로젝트명/staticfiles/와 static - local, /home/프로젝트명/media/와 media - local )
  5. nginx 컨테이너생성 ( 이미지와 포트 그대로 80-80, network : nginx-django, volume : /data/static/과 static - local, /data/media/와 media - local , bind : etc/nginx/nginx.conf - /home/django_course/nginx.conf ) 

컨테이너 생성 전에 nginx.conf를 수정하기 !!

listen 80; 아래에 코드 추가 후 저장 및 filezilla로 업로드

include mime.types;

location /static/ {
    alias /data/static/;
}

location /media/ {
    alias /data/media/;
}

Local , Remote environment detachment

MariaDB 컨테이너를 이용한 DB 분리

현재는 Django Container에 모두 Data가 들어있는 상태

-> 안정성을 위해 분리 작업이 필요함! bind와 sqlite를 사용해도 되지만 DB 성능을 위해 분리하는 것이 좋음

DB 분리 작업

  • MariaDB Container 안에 Volume 존재 -> Container의 생애 주기와 관련 없이 Data를 유지
  • local 환경에선 sqlite를 사용했지만 배포 환경에서는 MariaDB를 사용

Maria DB Container 

mariaDB도 dockerhub에 공식 이미지가 존재 ( https://hub.docker.com/_/mariadb )

  1. add container 누른 다음에 mariadb 컨테이너를 mariadb:10.5 이미지로 생성
  2. 생성 시 환경 변수 사용을 위해 Env 탭에서 add env variable -> 문서에 있는 MARIADB_ROOT_PASSWORD 을 추가 

개발/배포 설정 분리

개발 환경과 배포 환경의 설정 분리

  1. 프로젝트명의 폴더( settings.py가 있는 ) 안에서 settings라는 이름으로 new python package 생성
  2.  settings.py를 해당 폴더로 옮기고 base.py로 이름 변경, local.py와 deploy.py 추가 
  3. env 부분과 Allowed_host 부분을 잘라내서 두 군데 모두에 복사하고 from .base import *로 임포트
  4. DATABASES, CACHES 부분도 잘라내서 옮겨줌

설정 파일 수정

deploy.py

DEBUG = False 

DATABASES 밑에 있는 링크를 들어가서 내용 복붙 후 sqlite3 -> mysql로 수정, 아래 차례대로 django, django, password1234, HOST mariadb, PORT 3306으로 설정

base.py

BASE_DIR 에 .parent를 한번 더 써주어야 함! ( 폴더가 하나 더 생겼기 때문 )

manage.py 

os.environ.setdefault 부분에서 경로에 .local을 추가해주어야 함

MariaDB 컨테이너 설정 및 Django 연동

MariaDB 컨테이너 설정

  1. 원래 있던 mariadb 컨테이너 삭제
  2. database 라는 이름으로 volume 생성
  3. nginx-django network 연결, volume은 dockerhub의 mariadb 공식 문서 Caveats 부분 참고,  /var/lib/mysql/ 사용해서 database - local 생성
  4. deploy.py에 써두었던 database 정보를 토대로 env 부분 작성( MYSQL_ROOT_PASSWORD, MYSQL_DATABASE, MYSQL_USER, MYSQL_PASSWORD )

Django Container Dockerfile 수정

  1. 변경 사항 모두 git에 push
  2. Dockfile의 git clone 명령어 앞에 RUN echo "testing" 추가하여 캐시 변경
  3. RUN pip install mysqlclient 추가
  4.  CMD 부분 ["bash", "-c", "python manage.py migrate --settings=almigthy.settings.deploy  &&   gunicorn almighty.wsgi --env DJANGO_SETTINGS_MODULE=almigthy.settings.deploy --bind 0.0.0.0:8000"] 으로 변경
  5. django_test_image:4로 이미지 생성 

Django Container 수정

4번 이미지로 django_container_gunicorn 생성

network, volume 설정 이전과 같이 해주기!

-> Django 컨테이너를 삭제했다가 다시 추가해도 멀쩡해짐

Docker Swarm, Stack, Secret

Container의 한계, Docker Stack의 이해

Container의 한계

1. Repetitive Configuration

모든 컨테이너마다 Port, Volume, Network 설정 반복

-> Total STACK Settings 파일 만들기 !  = Docker STACK

  • Docker Compose라는 유사한 기능이 있지만 배포용 X 다른 기능
  • yml 파일에 작성

2. Container shutdown

24시간 모니터링을 하며 리부팅 불가능! 따라서 Service라는 상위 개념으로 복사하여 관리 ( 설정 파일을 통한 Automatic REBOOT, 컨테이너 여러개로 복제도 가능 -> Scale out Containers )

Docker Swarm 의 이해

Docker Swarm

가상 서버 하나를 Node라고 함

이 여러가지 서버를 하나의 서버(서비스)인 것처럼 묶어주는 역할. 현재는 1개의 노드만 사용 !

노드 전체에 걸쳐 Service들이 존재 ( 컨테이너 종류마다 하나씩 ) -> Container Orchestration

Stack을 위한 yml 파일 작성

먼저 swarm 모드를 키기 위해 서버에 접속

  1. ssh root@ip주소 로 서버에 접속
  2. cd ..
  3. cd home/django_course/
  4. docker swarm init

가장 먼저 만들어진 노드가 manager node가 되게 됨! 

이렇게 추가를 하고 나면 portainer에서 Swarm, Secrets, Services 탭을 확인할 수 있음

yml 파일 작성하기

root 폴더 안에 docker-compose.yml 생성

version: "3.7"

services: 

    django: 

        image: django_test_image:3 ( 테스트용이기 때문에 DB 분리 이전 이미지 사용 )

        ports: 

            - 8000:8000

stack 탭 - add stack : django_stack

yml 파일을 upload하여 deploy

-> service 탭에서 scale을 늘리는 것만으로도 container 복제 가능 !

통합 yml 파일 작성

통합 yml 파일 작성

  • 일종의 도메인으로 사용되는 '이름'을 반드시 유의해서 다른 설정과 동일하게 적어주기!! 
  • 이미지의 버전은 명시해주는 것이 좋음
  • 추가적으로 networks와 volumes에 관한 내용도 따로 작성
version: "3.7"

services:
  nginx:
    image: nginx:1.19.5
    networks:
      - network
    volumes:
      - /home/django_course/nginx.conf:/etc/nginx/nginx.conf
      - static-volume:/data/static
      - media-volume:/data/media
    ports:
      - 80:80

  django_container_gunicorn:
    image: django_test_image:1
    networks:
      - network
    volumes:
      - static-volume:/home/likelion_django_study/staticfiles
      - media-volume:/home/likelion_django_study/media

  mariadb:
    image: mariadb:10.5
    networks:
      - network
    volumes:
      - maria-database:/var/lib/mysql
    environment:
      MARIADB_ROOT_PASSWORD: password1234
      MYSQL_DATABASE: django
      MYSQL_PASSWORD: password1234
      MYSQL_USER: django

networks:
  network:

volumes:
  static-volume:
  media-volume:
  maria-database:

run 되고 있지 않는 컨테이너의 경우 컨테이너 간의 의존성 때문에 발생함! 이런 컨테이너를 삭제하기 위한 방법도 존재하긴 함.  그냥 지워도 문제 X 

Docker Secret을 이용한 보안

Docker Secrets

보안되어야 할 정보( secretkey, password 등 )를 docker 내에서 따로 관리해주는 기능

  1. Secrets tap - add secret
  2. 이름 정해주기 ( DJANGO_SECRET_KEY )
  3. dockerfile에 적어두었던 secretkey 옮기기

마찬가지로 db 관련 환경 변수들도 옮겨주고 기존 내용은 지워줘도 OK!

Secret 제공하기 : yml 파일 수정

  1. docker-compose 파일에서 서비스 내용에 secrets: 를 추가
  2. evironment에서 _FILE 접미사 이용 -> /run/secrets/mysql/secret이름
  3. volumes나 networks와 마찬가지로 하단에서 따로 정의 추가, external: true 옵션 추가! 
  django_container_gunicorn:
    image: django_test_image:5
    networks:
      - network
    volumes:
      - static-volume:/home/likelion_django_study/staticfiles
      - media-volume:/home/likelion_django_study/media
    secrets:
      - MYSQL_PASSWORD
      - DJANGO_SECRET_KEY

  mariadb:
    image: mariadb:10.5
    networks:
      - network
    volumes:
      - maria-database:/var/lib/mysql
    secrets:
      - MYSQL_PASSWORD
      - MYSQL_ROOT_PASSWORD
    environment:
      MYSQL_USER: django
      MYSQL_DATABASE: django
      MYSQL_PASSWORD_FILE: /run/secrets/MYSQL_PASSWORD
      MYSQL_ROOT_PASSWORD_FILE: /run/secrets/MYSQL_ROOT_PASSWORD

secrets:
  DJANGO_SECRET_KEY:
    external: true
  MYSQL_PASSWORD:
    external: true
  MYSQL_ROOT_PASSWORD:
    external: true

Secret 제공하기 : settings/deploy 파일 수정

  1. secret을 읽어오는 함수 정의
  2. secret 정보들을 read_secret(secret_name)으로 불러오기
def read_secret(secret_name):
file = open('/run/secrets/' + secret_name)
secret = file.read()
secret = secret.rstrip().lstrip()
file.close()

return secret
SECRET_KEY = read_secret('DJANGO_SECRET_KEY')
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'django',
'USER': 'django',
'PASSWORD': read_secret('MYSQL_PASSWORD'),
'HOST': 'mariadb',
'PORT': '3306',
}
}

Secret 제공하기 : Dockerfile 수정

manage.py collectstatic을 CMD 부분으로 미뤄줌 ( 현재 처음 상태에서는 secret_key가 불러와지지 않기 때문, --noinput 옵션과 --settings 옵션 추가! )

CMD ["bash", "-c", "python manage.py collectstatic --noinput --settings=almighty.settings.deploy && python manage.py migrate --settings=almighty.settings.deploy && gunicorn almighty.wsgi --env DJANGO_SETTINGS_MODULE=almighty.settings.deploy --bind 0.0.0.0:8000"]

-> image 생성 후 stack도 image 이름 바꾼 후 재생성

보강 1: AWS / HTTPS / 좋아요 시스템

AWS 과금 관련 주의사항

AWS 서비스 주의사항

vultr에서는 과금 요소는 instance 대여비가 대부분, 초반 크레딧 제공으로 부담 낮음! 그러나 AWS는 과금 부담이 있을 수 있음 ㅜㅜ

보강 개요

AWS와 Vultr의 차이

  1. 서버 접속 시 VULTR는 id/pw 이용, AWS는 Key File( Pem ) 이용
  2. AWS에는 docker가 깔려있지 않음
  3. AWS는 기본적으로 port들이 firewall로 막혀있음

AWS EC2 인스턴스 생성

AWS EC2 인스턴스 생성하기

  1. 가입 후 로그인 - 콘솔에 로그인
  2. 서비스 탭에서 EC2 선택
  3. 네트워크 및 보안 탭에서 키 페어 선택 후 생성
  4. 이름 설정 aws_pragmatic_key, 파일 형식 pem 선택
  5. 다운로드된 키파일은 프로젝트 폴더에 넣어줌
  6. 인스턴스 탭에서 생성 선택, Ubuntu Server 18.04 선택
  7. 인스턴스 유형 선택 후 구성, 스토리지 크기 등은 그대로! 태그 Name:webserver로 추가( 예시 ), 보안 그룹은 그때그때 열어주면 됨
  8. 시작하기 누르면 기존 키페어 선택하기

인스턴스 접속하기

인스턴스 정보에서 확인 가능한 퍼블릭 IPv4 주소를 통해 ssh 접속과 기타 기능 사용 가능!

  1. 인스턴스 상태가 실행중임을 확인
  2. 터미널에서 cd를 통해 프로젝트 폴더로 이동
  3. ssh -i key이름.pem ubuntu@ip주소 로 접속

AWS Docker 설치

AWS 인스턴스에 Docker 설치하기

https://docs.docker.com/engine/install/ubuntu/

위 링크 참고, 보통 우클릭으로 복붙 가능! docker 명령어로 확인 가능

$ sudo apt-get update

$ sudo apt-get install \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg \
    lsb-release
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
 echo \
  "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
  $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
 sudo apt-get update
 $ sudo apt-get install docker-ce docker-ce-cli containerd.io

Portainer 설치하기

https://documentation.portainer.io/v2.0/deploy/ceinstalldocker/

sudo docker volume create portainer_data

sudo docker run -d -p 9000:9000 --name=portainer --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer-ce

AWS 방화벽 뚫어주기

  1. ASW 인스턴스 콘솔에서 보안 탭 - 보안 그룹 
  2. 인바운드 규칙 편집 - 규칙 추가
  3. 사용자 지정 TCP, 포트 범위 9000 , 소스 0.0.0.0/0 

이후 ip:9000으로 portainer 접속 가능, sudo docker swarm init 명령어로 swarm 사용 가능 !

AWS 기반 Stack 재배포

AWS EC2 인스턴스에 Stack 배포

  1. Secrets에서 MYSQL_ROOT_PASSWORD, MYSQL_PASSWORD, DJANGO_SECRET_KEY 추가
  2. filezilla로 nginx.conf도 다시 올려주어야 함! 그런데 AWS는 pem 키파일로 접속해야 하므로 좌측 상단의 사이트 관리자 열기 버튼으로 서버를 설정해야 함
  3. New site 추가 후 SFTP 프로토콜, 호스트에 ip, 포트는 22, 로그온 유형은 키파일 선택, 사용자 ubuntu, 키파일 업로드 후 연결 
  4. 마찬가지로 파일을 넣을 디렉터리를 만들어야 하는데, 권한이 필요하므로 터미널에서 cd .. 로 home 디렉터리로 간 다음 sudo mkdir django_course로 디렉터리 생성
  5. cd ..로 다시 홈으로 간 다음 sudo chmod 777 django_course/ 로 권한 부여한 다음 파일을 옮겨줌
  6. image도 dockerfile 업로드로 추가! ( django_test_image:5 )
  7. add stack으로 다시 스택도 deploy 해줌
  8. AWS 콘솔의 보안 그룹에서 인바운드 규칙을 80번 포트로 새로 추가

AWS 도메인 연결

도메인 연결

업체 상관 없이 도메인 구매 -> AWS Route53 서비스와 연결 -> EC2 서버와 연결

  1. 서비스 탭에서 네트워킹 및 콘텐츠 전송 중 Route 53 선택 -> DNS 관리의 호스팅 영역 생성 클릭
  2.  도메인 입력퍼블릭 호스팅 영역 선택 후 생성
  3. 레코드 중 유형이 NS인 부분이 name server이므로 이 정보를 도메인 관리 시스템에 입력
  4. EC2 인스턴스와 연결을 위해 A유형 레코드 생성 -> 레코드 이름( www 또는 생략... ), 값에 인스턴스 ip 입력 후 생성

AWS HTTPS 설정

HTTPS란

HTTP + SECURE! 

본래 HTTP의 POST 방식은 BODY 안에 추가적인 내용들을 넣어서 보내는데, 경로 중간에 있는 서버에서 정보를 볼 수 있게 됨 -> HTTPS는 BODY 내의 내용을 암호화하여 보안 ✔

LOAD Balancer 

서버에 부하가 많을 때 요즘은 Scale Out으로 클러스터링하는 추세, 이때 부하를 알맞게 분산시켜주는 것이 LOAD Balancer!

로드 밸런서 / HTTPS 설정하기

  1. EC2 대시보드에서 로드 밸런싱 탭 - 로드 밸런서 생성
  2. HTTP / HTTPS 유형 선택
  3. 이름 pragmatic, 인터넷 경계, IPv4 선택
  4. HTTP(80), HTTPS(443) 리스너 추가
  5. 가용 영역 선택 ( 4개 전부 선택 )
  6. ACM에서 인증서 선택 -> AWS에서 자동 발급/관리
  7. 인스턴스에서 사용 중인 보안 그룹 선택( 생성 가능 )
  8. 대상 그룹 이름 webserver, 프로토콜 HTTP
  9. 설정할 인스턴스를 선택하고 로드 밸런서 생성
  10. Route53에서 도메인에 HTTPS를 연결해줄 A 레코드 생성( 레코드 이름 www, 별칭 LoadBalancer-서울-이름 선택 
  11. 인스턴스의 보안 그룹 - 인바인드 규칙에 TCP - 443번 포트 추가

Private Github Repo , 그리고 RSA 키 등록

Github Private Repository

이미지를 생성할 때 git clone을 이용함 -> public이어야 함

private repo의 경우 자격 증명이 필요해짐 -> RSA Key 등록

RSA Key 생성 / 등록

  1. 서버 접속 후 ssh-keygen -t rsa -b 4096 -C "사용 중인 이메일 주소" -> rsa 타입의 암호화 척도 4096인 키 생성
  2. 키를 저장할 경로 ( 생략 가능), passphrase도 생략 가능
  3. id_rsa( 개인키), id_rsa.pub( 공개키 ) 생성 확인 가능
  4. cat id_rsa.pub 명령어로 출력된 내용 복사
  5. git profile - settings - SSH and GPG keys 
  6. new SSH key - 이름 clone key, 복사한 내용 붙여넣기
  7. touch ~/home/ubuntu/.ssh/known_hosts -> 현재 호스트를 알려줄 파일 생성
  8. ssh-keyscan github.com >> /home/ubuntu/.ssh/known_hosts
  9. git clone 시 링크를 SSH 타입으로 복사하여 사용

 

Dockerfile 수정 및 이미지 빌드

Private Repo를 위한 Dockerfile 수정

WORKDIR 이전에 작성 ! 개인 키 파일은 보여지면 안 되기 때문에 이미지 파일의 보안에도 신경써야 함!

  1. RUN mkdir /root/.ssh/ ( /home/ubuntu/.ssh/ 와 동일하지만 컨테이너 내부에서는 root를 사용하기 때문 )
  2. ADD ./.ssh/id_rsa /root/.ssh/id_rsa -> 상대 경로를 이용해 호스트의 key 파일 복사 
  3. RUN chmod 600 /root/.ssh/id_rsa
  4. RUN touch /root/.ssh/known_hosts
  5. RUN ssh-keyscan github.com >> /root/.ssh/known_hosts
  6. GIT 링크를 SSH 프로토콜로 가져오기

서버 컨테이너 안에서 이미지 생성하기

  1. Dockerfile을 filezilla로 root 폴더( /home/ubuntu/ )에 올리기
  2. sudo docker image build -t django_test_image:6 . ( .은 현재 디렉터리에서 만들 것이라는 뜻 )

Likeapp 모델 설정

Like app 생성 및 모델 등 수정

  1. articleapp의 models.py에서 like = models.IntegerField(default=0) 추가
  2. startapp으로 likeapp 추가 ( 굳이 만들지 않고 article 앱에서 기능 추가해도 OK ) 후 settings와 url에 앱 등록
  3. likeapp에 urls.py 추가하여
  4. models.py에 LikeRecord 생성 ( user,  article ) + class Meta에 unique_together('user', 'article') 추가
  5. makemigrations, migrate 명령어로 변경사항 저장
  6. article 앱의 detail.html 부분에 Like 관련 내용 추가

Likeapp View 구현

Like View 만들기

views.py에서 아래 내용 작성 -> url 등록 

@method_decorator(login_required, 'get')
class LikeArticleView(RedirectView):
    def get_redirect_url(self, *args, **kwargs):
        return reverse('articleapp:detail', kwargs={'pk':kwargs['pk']})

    def get(self, *args, **kwargs):
        user = self.request.user
        article = get_object_or_404(Article, pk=kwargs['pk'])

        if LikeRecord.objects.filter(user=user, article=article).exists():
            return HttpResponseRedirect(reverse('articleapp:detail', kwargs={'pk': kwargs['pk']}))
        else:
            LikeRecord(user=user, article=article).save()
            
        article.like += 1
        article.save()

        return super(LikeArticleView, self).get(self.request, *args, **kwargs)

Django Message 적용 및 응용

Django Message란

유저가 요청 -> 요청에 대한 결과를 return ( 성공 여부 등 )

Message의 종류나 중요도 등에 따라 level을 다르게! 

https://docs.djangoproject.com/en/3.1/ref/contrib/messages/

Django Message 사용하기

  1. from django.contrib import messages로 임포트 해오기
  2. messages.add_message(self.request, messages.레벨, "메시지") 로 호출
  3. template 단에서는 {% for message in messages %}를 통해 message를 원하는 div에 불러올 수 있음 ( base.html에 작성 )
  4. level에 따라 bootstrap 디자인 다르게 -> message doc의 Message tags 부분의 MESSAGE_TAGS 부분을 settings.py에 붙여넣기 한 후 ERROR의 태그 변경
  5. {{ message.tags }}를 통해 스타일 클래스 지정

Transaction 개요

Transaction이란

여러 개의 DB 상호작용이 연관되어 있을 때 하나의 작업처럼 연결( 하나라도 성공하지 못한다면 모두 fail 처리 ) -> Django decorator 사용

Transaction 구현

Transaction으로 Like 기능 묶기

  1. 기존의 DB 관련 코드를 모두 db_transaction(user, article)에 넣어줌
  2. 이미 like가 존재할 경우 raise ValidationError()
  3. @transaction.atomic 데코레이터를 통해 트랜잭션화
  4. try-except 구문으로 db_transaction 함수를 호출하고 결과에 따라 message 내보내기
@transaction.atomic
def db_transaction(user, article):
if LikeRecord.objects.filter(user=user, article=article).exists():
raise ValidationError('Like already exists')
else:
LikeRecord(user=user, article=article).save()

article.like += 1
article.save()

def get(self, *args, **kwargs):
user = self.request.user
article = get_object_or_404(Article, pk=kwargs['pk'])

try:
db_transaction(user, article)
messages.add_message(self.request, messages.SUCCESS, "좋아요가 반영되었습니다.")
except ValidationError:
messages.add_message(self.request, messages.ERROR, "좋아요는 한 번만 가능합니다.")
return HttpResponseRedirect(reverse('articleapp:detail', kwargs={'pk': kwargs['pk']}))

return super(LikeArticleView, self).get(self.request, *args, **kwargs)