Django의 Proxy 모델은 하나의 데이터 모델을 가지고, 다른 인터페이스를 제공하는 클래스라고 생각하면 됩니다.
모델 상속의 경우, 각 subclass 에 대해서 새로운 데이터 테이블이 생성되지만, proxy 모델은 별도로 데이터 테이블을 생성하지 않고, 기존 테이블을 참조해서 사용하게 됩니다.
Proxy 모델의 정의
Proxy 모델은 원본 모델 클래스를 상속받아, Meta 클래스에 proxy 값을 True 로 설정해 주면 됩니다.
from django.db import models
class Person(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
class MyPerson(Person):
class Meta:
proxy = True
Proxy 모델 활용 - method 재정의
아래 예시에서는 원본 데이터 클래스인 Person 에서는, 인스턴스 출력시, DB에 저장되어 있는 이름을 그대로 출력되고, Proxy 클래스인 MyPerson 에서는, 인스턴스 출력시, 이름이 대문자로 변경되어서 출력됩니다. 같은 데이터를 참조하면서, method 를 다르게 정의해서 사용할 수 있습니다.
from django.db import models
class Person(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
def __str__(self):
return f"{self.firat_name} {self.last_name}"
class MyPerson(Person):
class Meta:
proxy = True
def __str__(self):
return super().__str__().upper()
Proxy 모델 활용 - manager: get_queryset
이번에는 proxy 모델과 manager를 이용해서, 원본 데이터를 필터링해서 관련 데이터만 사용하는 예시입니다.
먼저 SomeClass 라고 하는 데이터 모델을 정의하고, 세 개의 필드 status, title, content 를 정의합니다.이어서 정의할 manager 클래스에서는 SomeClass의 status 값에 따라 원본 데이터를 필터해서 돌려 줄 수 있게 처리합니다. 그리고 마지막으로 proxy 모델들은 원본 데이터 클래스 (SomeClass) 를 상속하고, objects 에 해당 manager 클래스의 인스턴스를 지정해 줍니다.
from django.db import models
STATUS_TYPES = (
('n', 'New'),
('i', 'In Process'),
('c', 'Completed'),
)
# 원본 데이터 클래스
class SomeClass(models.Model):
status = models.CharField(max_length=1, choices=STATUS_TYPES)
title = models.CharField(max_length=100)
content = models.TextField(blank=True, null=True)
# 매니저 클래스
class NewStatusManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(status='n')
# 원본 데이터 테이블에서 status 가 'n' 인 데이터만 가지고 오는 proxy 클래스
class NewStatusSomeClass(SomeClass):
objects = NewStatusManager()
class Meta:
proxy = True
# 매니저 클래스
class InProcessStatusManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(status='i')
# 원본 데이터 테이블에서 status 가 'i' 인 데이터만 가지고 오는 proxy 클래스
class InProcessStatusSomeClass(SomeClass):
objects = InProcessStatusManager()
class Meta:
proxy = True
# 매니저 클래스
class CompletedStatusManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(status='c')
# 원본 데이터 테이블에서 status 가 'c' 인 데이터만 가지고 오는 proxy 클래스
class CompletedStatusSomeClass(SomeClass):
objects = CompletedStatusManager()
class Meta:
proxy = True
Proxy 모델 활용 - manager: create
이번에는 proxy 클래스로 신규 레코드를 생성하는 케이스에 대해서 정의해 보겠습니다.
Proxy 모델에 연결된 manager 클래스에 create method 를 재정의해서, Proxy 형태를 구분하는 컬럼의 값을 지정해 줄 수 있습니다.
이번 포스트는 앱 배포에 관한 것으로, docker 환경에 구축된 jenkins를 이용해서, git repository 에 저장된 소스를 가지고 와서 docker 이미지를 빌드하고, private docker registry 에 배포하는 과정에 대해서 정리해 보았습니다.
서버 환경
먼저 서버를 준비해야 하는데, 어차피 docker로 배포가 될 예정이기 때문에, 테스트 환경은 Virtual Box 에 설치한 Ubuntu 22.04 LTS 상에서 docker를 설치해서 진행했습니다.
Docker 설치
대부분 application은 docker 상에서 구동될 예정이기 때문에 먼저 docker를 설치합니다.
Jenkins도 별도로 설치하지 않고, docker 컨테이너 형태로 사용을 할 예정인데요, 이런 식으로 사용하는 경우 docker 관련 플러그인을 사용할 때, docker 명령어를 찾지 못하거나 glibc 버전 문제가 발생할 수 있어서, jenkins docker 이미지에 docker를 추가로 설치해서 사용하도록 하겠습니다.
먼저 아래와 같이 Dockerfile 파일을 준비합니다.
FROM jenkins/jenkins:lts
USER root
RUN apt-get update -qq \
&& apt-get install -qqy apt-transport-https ca-certificates curl gnupg2 software-properties-common
RUN curl -fsSL https://download.docker.com/linux/debian/gpg | apt-key add -
RUN add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/debian \
$(lsb_release -cs) \
stable"
RUN apt-get update -qq \
&& apt-get -y install docker-ce
RUN usermod -aG docker jenkins
아래 명령어를 이용해서 이미지를 빌드합니다.
docker image build -t custom-jenkins-docker .
Jenkins를 띄울 때도, docker-compose.yml 을 작성해서 띄우도록 하겠습니다.
docker-compose 명령어 혹은 docker 명령어로 jenkins 서버를 시작합니다.
docker-compose 가 설치되어 있는 경우
docker-compose up -d
docker 명령어를 사용하는 경우
docker compose up -d
Jenkins 설정
웹 브라우저를 이용해서 http://localhost:8080 으로 접속합니다. 참고로 저는 virtual box의 host 네트워크 설정으로 host PC에서 IP (192.168.56.101) 로 접근할 수 있게 설정하였습니다.
처음 실행시 아래와 같은 화면이 나옵니다.
화면에 나온 설명대로 docker 컨테이너 내에서 /var/jenkins_home/secrets/initialAdminPassword 파일을 열어보면 초기 비밀번호를 확인할 수 있습니다. 해당 파일은 docker-compose 로 띄울 때, .data 폴더를 /var/jenkins_home 폴더로 매핑시켜 두었기 때문에, Ubuntu 서버 내에서 .data/secrets/initialAdminPassword 파일을 열어보아도 동일한 결과를 얻을 수 있습니다.
그 외에는 docker 컨테이너 log를 이용해서 아래와 같이 초기 비밀번호를 확인할 수도 있습니다.
docker logs jenkins
다음 화면에서는 Install suggested plugins를 클릭해서 기본 플러그인들을 설치합니다.
다음 화면에서는 Admin User를 생성해도 되지만 귀찮으시면 Skip 을 눌러서 넘길 수도 있습니다. (Skip시 initial password를 계속 사용하게 됩니다.)
Jenkins URL을 세팅하고, 시작합니다.
Jenkins 화면에서 Jenkins 관리 > 플러그인 관리 > Available plugins 를 차례로 클릭합니다.
검색창에서 docker를 입력합니다.
Docker, Docker Commons, Docker Pipeline, Docker API, docker-build-step 를 체크하고, Download now and install after restart 를 클릭합니다.
설치 후 Jenkins 재시작을 체크하면, Jenkins가 재시작 됩니다.
마지막으로 docker socket을 세팅해 줍니다.
Jenkins 관리 > 시스템 설정을 차례로 클릭합니다.
스크롤해서 Docker Builder 항목을 찾아가서 Docker server REST API URL에 아래와 같이 "unix:///var/run/docker.sock" 을 입력합니다.
빌드 테스트
이제 Jenkins 에 Pipeline 아이템을 추가해서 빌드 테스트를 진행 해보겠습니다.
Jenkins Dashboard에서 새로운 Item을 클릭합니다.
적당한 이름을 입력하고, Pipeline 을 선택하고 OK 버튼을 눌러서 넘어갑니다.
먼저, Docker 이미지에 버전을 태깅하기 위해서 BUILD_NUMBER를 인자로 받도록 합니다.
General 섹션에서 "이 빌드는 매개변수가 있습니다" 를 체크하고, String Parameter를 선택하고, 매개변수 명에 "BUILD_NUMBER" 라고 적습니다.
먼저 프로세스를 검색해 보겠습니다. 메모장(notepad)를 하나 열고 아래 명령어를 입력합니다.
Get-Process -Name Notepad
아래와 비슷한 결과가 출력될 것입니다.
이름으로 프로세스를 검색하는 경우에는, -Name은 생략이 가능합니다.
만약 프로세스 아이디를 알고 있다면 아래처럼 입력하면 됩니다. (여기서는 아이디가 4556)
Get-Process -Id 4556
정확한 프로세스 이름을 모른다면 와일드카드(*)를 이용할 수 있습니다.
Get-Process -Name Note*
위의 출력결과에서 Id만 화면에 출력하고 싶다면, 아래와 같이 입력합니다.
Get-Process -Name Notepad | Select-Object Id
여기서 | (pipe) 는 바로 앞의 커맨드의 결과를 받아서 처리하라는 의미이고, Select-Object는 프로세스 리스트에서 Id 만 선택해서 보여주라는 의미 입니다.
결과는 아래처럼 출력됩니다.
결과를 리스트로 받기 위해서는 아래와 같이 ExpandProperty 옵션을 추가합니다.
Get-Process -Name Notepad | Select-Object -ExpandProperty Id
파이프 기능을 활용하면 이전 결과를 필터 할 수도 있습니다. 아래 명령어는 Where-Object 기능을 이용해서 전체 프로세스 리스트 중에 ProcessName이 Notepad 인 것만 필터해서 출력해 줍니다. 즉, 앞에서 입력했던 Get-Process Notepad 와 같은 결과가 나옵니다. 여기서 $_ 는 이전 결과 리스트의 아이템(원소)를 의미합니다.
먼저 map 의 예시입니다. map은 리스트를 받아서 리스트를 돌려줍니다. 아래 예시는 [1, 2, 3, 4, 5, 6, 7, 8, 9] 리스트를 생성해서 각 원소에 2를 곱한 결과를 다시 리스트로 반환합니다. 결과는 [2, 4, 6, 8, 10, 12, 14, 16, 18] 이 됩니다.
1..9 | ForEach-Object -Process {$_ * 2}
위의 예시에서 -Process 는 생략가능하며, ForEach-Object 대신 %{$_ * 2} 을 사용해도 같은 결과가 리턴됩니다.
이번에는 reduce 예시입니다. reduce는 리스트를 받아서 연산한 결과값을 돌려줍니다. 아래 예시는 [1, 2, 3, 4, 5, 6, 7, 8, 9] 리스트를 생성해서 원소들의 합을 구해서 돌려줍니다. 결과는 45 가 됩니다.
먼저 polls 폴더내에 있는 views.py 파일을 열어서 아래와 같이 코드를 작성합니다.
polls/views.py
from django.shortcuts import render
from django.http import HttpResponse
# Create your views here.
def index(request):
return HttpResponse("HelloWorld")
그리고 polls 폴더에 urls.py 파일을 생성해서 아래와 같이 코드를 작성합니다.
polls/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='index')
]
마지막으로 config 폴더에 있는 urls.py 파일을 열어서 아래와 같이 코드를 입력합니다.
from django.contrib import admin
# django.urls의 include 모듈을 추가
from django.urls import include
from django.urls import path
urlpatterns = [
# polls 를 경로에 추가
path('polls/', include('polls.urls')),
path('admin/', admin.site.urls),
]
프로젝트를 다시 실행합니다.
이번에는 localhost:8000 으로 접속하면 아래와 같은 에러가 나오게 됩니다.
나중에 처리하기로 하고, localhost:8000/polls 로 접속합니다.
아래처럼 HelloWorld 가 화면에 나오면 정상입니다.
페이지가 호출되는 흐름을 설명하자면,
먼저 웹 요청이 오면, project 에 있는 config/urls.py 에서 urlpatterns 에 있는 리스트를 매치해 봅니다.
매칭 결과 "polls/"가 매치되므로, url은 polls.urls 에 있는 urlpatterns 를 찾아보게 됩니다.
다음으로 polls/urls.py 에 있는 urlpatterns에서 매치되는 path를 살펴보고,
매치되는 함수를 실행합니다. (여기서는 views.index)
polls/views.py 에 있는 index 함수에서 요청을 처리하고, HttpResponse 로 "HelloWorld" 를 리턴하게 됩니다.
프로젝트 폴더는 탐색기를 이용해도 되지만, 여기서는 커맨드 프롬프트를 이용해서 생성해 보겠습니다. 프로젝트는 C:\Projects\Django 아래 위치하며, 프로젝트 이름은 mysite 라고 가정하겠습니다.
> cd C:\Projects\Django
> mkdir mysite
> cd mysite
커맨드는 윈도우 기준으로 작성되었습니다. 첫째 줄은 C:\Projects\Django 디렉터리로 이동하라는 명령어 인데, 이전에 C:\Projects\Django 폴더가 생성되어 있어야 합니다. 그리고 두번째 줄은 프로젝트 폴더인 mysite 를 생성하는 명령어이고, 세번째 줄은 폴더 생성 후에 mysite 디렉터리로 이동하라는 의미 입니다. 이후 작업은 모두 C:\Projects\Django\mysite 에서 이루어지기 때문에 미리 이동해 두었습니다.
2. 가상환경 생성 및 실행
이전 포스트에서 가상환경에 대해서 설명을 했었는데, 가상환경은 프로젝트별로 Python 실행환경을 만들어서 가상환경 별로 모듈을 관리하기 위해서 사용합니다. C:\Projects\Django\mysite 에서 아래 명령을 실행합니다.
> python -m venv .venv
> cd .venv\Scripts
> activate
(.venv) > cd ..\..
역시 윈도우 기준으로 커맨드를 작성했습니다. 첫번째 줄에서는 .venv 라는 가상환경을 생성했고요, 두번째 줄에서는 가상환경을 실행하기 위해서 경로를 변경해 주었고, 세번째 줄에서는 가상환경을 실행하였습니다. 네번째 줄은 원래 프로젝트 루트로 돌아가기 위한 명령어 입니다.
pip 버전을 업그레이드 하라는 경고 메시지가 나온다면 아래 명령어로 업그레이드를 진행합니다.
(.venv) > python -m pip install --upgrade pip
4. Django admin 명령어로 프로젝트 생성
django-admin 명령어로 Django 프로젝트에 필요한 기본 설정들을 생성합니다.
(.venv) > django-admin startproject config .
django-admin 명령어의 첫번째 파라미터인 startproject 는 말 그대로 프로젝트를 시작하기 위한 기본 파일들을 생성하기 위해 사용합니다. 두번째 파라미터인 config 는 django 프로젝트의 설정파일들을 config 폴더에 생성하겠다는 의미이고, 마지막 파라미터인 "." 은 현재 경로에 생성을 하겠다는 의미 입니다.
명령어를 실행하고 나면, 프로젝트 루트 경로에는 manage.py 파일과 config 폴더가 생성됩니다. config 폴더에는 기본 설정 및 운영 환경에서의 실행을 위한 기본 파일들이 생성됩니다.
5. Django 서버 실행
프로젝트가 생성되었다면, 아래 명령어를 입력해서 Django 서버를 실행합니다.
(.venv) > python manage.py runserver
명령어를 실행하고 나면, 마지막에 Starting development server at http://127.0.0.1:8000/ 같은 메시지가 출력되는데, 이것이 서버 접속 정보입니다. 브라우저를 열고 http://127.0.0.1:8000 을 입력하면 Django 에서 제공해주는 기본 화면이 아래와 같이 나오게 됩니다.
Django 는 포트 충돌이 없다면 기본적으로 8000 포트로 서비스를 시작합니다. 만약 8080 포트로 서비스를 하고자 한다면 다음과 같이 입력하면됩니다.
(.venv) > python manage.py runserver 8080
혹은 서버에서 여러 개의 네트워크 인터페이스를 사용하고 있어서 특정 아이피에 서비스를 바인드 하고 싶다면 아이피도 적어줍니다.
(.venv) > python manage.py runserver 0.0.0.0:8080
0.0.0.0 은 모든 네트워크 인터페이스에서의 요청을 바인드 할 때 사용합니다. 필요에 따라 바인드할 아이피를 세팅해 주면됩니다.
원래 이번 포스트에서 HelloWorld 를 구현하려고 했는데.. 다음 포스트에 작성해 보겠습니다.
웹 프레임워크는 웹 프로그램을 만들기 위한 스타터 키트 같은 것으로, 웹 프로그램에 필요한 많은 기능들(쿠키나 세션 처리, 로그인/로그아웃 처리, 권한 관리, 데이터 베이스 연결 등)을 포함시켜서 바로 사용할 수 있게 만들어진 프레임워크 입니다.
Django 설치하기 1 - Python 설치
Django 를 이용해서 웹 개발을 위해서는 Python 이 필요합니다. Python 홈페이지에서 사용하는 컴퓨터의 OS에 맞게 설치를 합니다. 버전은 3.8 이후를 사용하는 것을 권장하는데, 2022년 10월 기준으로 3.10.4 버전을 설치하면 됩니다. 이미 Python 3.8 이후 버전이 설치되어 있다면 이 과정은 건너 뛰어도 됩니다.
설치가 끝나면, 명령 프롬프트 (커맨드 창) 등에서 아래 명령어를 이용해서 설치된 버전을 확인할 수 있습니다. (V는 대문자 입니다.)
> python -V
Django 설치하기 2 - Python 가상환경 설정
Python의 가상환경은 Python 프로젝트를 진행할 때, 각각의 프로젝트에 독립된 환경을 만들어 주기 위해서 사용합니다. 예를 들어서 Python 프로젝트를 여러개 관리 중인데, 각 프로젝트가 필요로 하는 모듈 및 모듈의 버전이 다르다면, 가상환경을 이용해서 각 프로젝트에 필요한 모듈을 각각 설치해서 사용할 수 있습니다.
가상환경은 아래 명령어로 생성할 수 있습니다.
> python -m venv <name>
원하는 위치에 가상환경을 세팅할 수 있지만, 각 프로젝트 폴더에 .venv 라는 이름으로 생성하도록 하겠습니다. 가상환경에 설치되는 모듈은 pip 명령어를 이용해서 다시 설치가 가능하기 때문에 소스 관리 툴(git 등)에서 해당 폴더를 ignore list에 추가하도록 합니다.
다음은 가상환경을 시작하는 방법입니다. 시작은 가상환경을 설치한 프로젝트 루트에서 아래 처럼 명령어를 입력해 주면 됩니다.
> cd .venv\Scripts
> activate
(.venv) > cd ..\..
위의 명령어는 윈도우 기준으로 작성되었습니다. 첫번째 라인은 script 경로로 변경해 주는 것이고, 두번째 라인은 activate.bat 파일을 실행하여 가상환경을 시작하는 것이고, 마지막 라인은 다시 프로젝트 루트 폴더로 돌아오게 하는 것입니다. 세번째 라인은 명령 프롬프트 앞에 (.venv) 가 보이는데, 이것은 .venv 라는 가상환경이 실행 중임을 표시해 주는 것입니다.
참고로 맥이나 리눅스에서는 아래 명령어를 이용하면 됩니다.
> source ./.venv/bin/activate
가상환경을 종료하기 위해서는 아래 명령어를 입력합니다.
(.venv) > deactivate
Django 설치하기 3 - Django 모듈 설치하기
지금까지는 Python 과 Python 가상환경 설치에 대해서 살펴 봤는데요, 이제는 Django를 구동하기 위한 Django 모듈을 설치합니다.
설치는 pip 명령어를 이용하게 됩니다.
가상환경이 실행된 상태인지 확인합니다. 명령 프롬프트 앞에 (.venv) 이 표시되고 있는지 확인하시고, 없다면 바로 앞에서 설명드린 명령어로 가상환경을 시작합니다.
그리고 아래 명령을 이용해서 Django 모듈을 설치합니다. 이 글을 작성하는 시점에서는 4.1.2 버전이 최신입니다.
(.venv) > pip install django==4.1.2
새로 생성된 가상환경에서 pip 를 처음 실행하거나 새로운 pip 버전이 출시 되었을 때 실행하면, pip 버전을 업그레이드하라는 경고 메시지가 나옵니다. 아래 명령어를 입력하면 pip 버전을 업그레이드 할 수 있습니다.