열심히 데이터 분석 모델을 만들었다고 생각해보자. 이 모델을 이런저런 방식으로 다른 사람들도 사용할 수 있도록 만들고 싶은데 그러려면 뭔가 모델과 함께 작동할 화면 프로그램이 필요해보인다. 웹 어플리케이션은 화면을 바탕으로 개발한 기능을 원하는 시나리오대로 사용할 수 있는 하나의 프로그램이다. 그럼 웹어플리케이션에 대해 알아보자.
웹어플리케이션 구조 예시
-> Django를 활용한 웹어플리케이션
먼저 웹어플리케이션 구조를 살펴보자. 파이썬 django를 사용해 만드는 웹어플리케이션 구조는 위 그림과 같다. 이 구조의 웹어플리케이션이 돌아가는 방식은 다음과 같다.
1. 사용자가 PC 웹브라우저를 통해서 웹 어플리케이션에 특정행위에 대한 요청을 보낸다.
* Web Browser(Chrome) -> Web Server(Nginx)
2. 웹서버에서 사용자의 요청을 받음(Nginx)
3. 웹서버가 WAS(Web Application Server)에 사용자들의 요청을 전달 함
* Nginx -> uWSGI
4. WAS가 어플레이션(별도 프로세스)에 들어온 요청들을 분산해 로직을 실행시킴
* uWSGI -> Django(application)
5. 어플리케이션에서 로직을 실행시키고(동적task, 비즈니스 로직), WAS(uWSGI)에 응답을 보냄(필요시 DB참조값을 보내겠지)
* Application(Django) -> WAS(uWSGI)
6. WAS가 어플리케이션(Django)으로부터 온 응답을 웹서버(Nginx)가 이해할 수 있는 형태로 변환해 전달
* Application(Django) -> WAS(uWSGI) -> Web Server(Nginx)
7. 웹서버(Nginx)가 정적 데이터와 함께, 사용자 웹브라우저상에 최종 응답결과를 제공함
왜 Web Application을 사용할까?(왜 웹에서 동작할 수 있는 App으로 만드는거지?)
-> 그럼, web appication을 사용하지 않고 어떻게 머신러닝 모델을 사용하도록 만들 수 있을끼?
$ python model.py data.csv parameter.csv
sys 모듈의 sys.argv를 활용해 모델을 돌리는데 필요한 데이터와 파라미터를 넘기는 방식으로 프로그램을 구성할 수 있다.
-> 즉, 목적에 부합하는 python파일을 만들고, 직접 커맨드를 입력해서 명령하여 동작하도록 할 수 있다.
-> 또는 python의 pyQt와 같은 라이브러리를 통해서 GUI를 만들 수 있지만 UI디자인이 깔끔하지 않다.
다만, 이렇게 만들어낸 프로그램은 내PC가 아닌 곳에서는 접근할 방법이 없기때문에, 타인이 해당 프로그램에 접근하고 사용할 수 있게끔 돕기 위해, Web Application을 사용하게 되는 것이고, 다른 컴퓨터에서도 서비스(모델)에 접근하기 위해서 서버 클라언트 구조가 필요하게 된다.)
서버와 클라이언트
1) 서버는 요청을 받아 서비스나 데이터를 제공하는 컴퓨터
2) 클라이언트는 요청을 보내는 컴퓨터 또는 어플리케이션
-> 서버와 클라이언트 구조를 갖춘 프로그램을 만들면 직접 개발한 컴퓨터가 아닌 다른 컴퓨터에서도 서비스(또는 모델)를 실행시키고 결과를 볼 수있다.
여기서 클라이언트가 서버에 요청하는 것을 "Request"라고 하고,
서버가 클라이언트에 결과를 제공하는것을 "Response"라고 한다.
다만, 웹어플리케이션의 서버와 클라언트 통신에는 정해진 프로토콜이 있다.
이 프로토콜에 맞게 요청해야 결과가 오는데, 이에 대해 자세히 알아보자
HTTP 프로토콜, REST API
Web Application 에서 서버와 클라이언트간 통신은 HTTP(HyperText Transfer Protocol)을 따른다.
HTTP프로토콜을 따라 데이터를 어떤방식으로 요청할지 정할 수 있고 이에 따라 응답이 생성된다.
1) HTTP Request
HTTP 프로토콜에서는 URI(Uniform Resource Identifier)라는 주소에 메소드를 통해 데이터를 요청한다. 하나의 자원에 대해 단순 조회를 해본다거나 그 자원을 사용해 다른 결과를 만드는 등 다양한 행위를 하고 싶을 수 있기 때문에 HTTP에서는 여러 메소드를 두어 요청을 할 수 있게 만들어놓았다.
HTTP 메소드의 종류는 다음과 같다.
GET: URI에 데이터를 조회하는 메소드
POST: URI에 데이터를 생성하기 위한 메소드. 또는, URI에 데이터를 보내고 새로운 결과를 얻기 위한 메소드.
PUT: URI 데이터 수정을 위한 메소드
DELETE: URI 데이터 삭제를 위한 메소드
이 외에도 HEAD, CONNECT, OPTIONS, TRACE, PATCH와 같은 메소드가 존재한다. 가장 많이 사용하는 메소드는 GET과 POST로 두 가지 메소드만 잘 알아둬도 많은 문제 상황을 해결할 수 있다.
2) HTTP Response
서버에 request를 보내면 response로 결과가 나온다. 크게 응답코드로 요청에 대한 성공/실패 등 수행 결과를 제공한다. 가끔 우리가 보는 404 에러 같은 메세지는 응답코드로 실패를 나타낸다. 400번대는 실패 200번대는 성공이라고 일단 알아두면 이해에 도움이 될 것이다. 요청에 대한 결과는 body에 json이나 정해진 형식으로 응답코드와 함께 제공된다.
REST(Representational State Transfer) API
REST API를 간단히 이야기하면, "위의 HTTP 메소드를URL주소로 Request를 쏠 수 있게 만든 인터페이스"이다.
대부분의 웹 API설계는 REST API방식으로 이루어지므로 웹이서 데이터를 주고 받을 수 있는 수단 정도로 이해하면 되겠다.
기타
웹어플리케이션에서 데이터의 교환은 주로 json을 이용해 이루어진다.
어플리케이션(ex.Django)
서버와 클라이언트, HTTP통신에 대해 알아봤으니, Application은 어떻게 만들어지는지 알아보자.
MTV (Model - Template -View) 패턴
-> Django MTV패턴
웹어플리케이션은 여러화면과 이에 관련된 로직, 데이터를 바탕으로 만들어지는데, 현대 웹어플리케이션에는
이런 화면, 로직, 데이터를 효과적으로 관리하기 위해 소프트웨어 패턴(규칙)을 가지고 개발된다.
파이썬 Django는 MTV패턴을 사용한다.
간단히 MTV 패턴에 대해 알아보자.
- Model: 데이터를 다루는 부분. 데이터가 정의되고 DB와 연결됨
- Template: 화면을 다루는 부분. 화면에 표시될 html을 다룸
- View: 로직을 다루는 부분. HTTP request와 연관된 로직을 관리하고 처리
1) View
- MTV 패턴에서 사용자의 요청은 View에서 로직으로 구현
- View는 Model로부터 요청을 처리하기 위한 데이터를 받아오고 Template의 html에 로직의 결과를 포함해 응답
만약 머신러닝 모델을 구현했다면, 구현한 함수나 클래스가 View에서 호출된다.(모델을 호출하는내용이 VIew에서 시작한다 정도?!)
Model로 부터 필요한 설정값이나 데이터가 있다면 받아오고, 정의된 Template을 바탕으로 화면에 표시해준다.
2) MVC패턴을 이용해 만든 Django 프로젝트 예시
.
├── config # 설정
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── core # 공통 모듈 (비슷한 기능을 다른 앱에서 참조하기 위해 사용)
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── migrations
│ ├── models.py
│ ├── tests.py
│ ├── urls.py
│ └── views.py
├── reviews # 개별 앱 (리뷰)
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── migrations
│ ├── models.py
│ ├── tests.py
│ ├── urls.py
│ └── views.py
├── rooms
│ ...
├── templates # html
│ ├── rooms
│ │ ├── room_detail.html
│ │ ├── ...
│ │ └── room_edit.html
│ │ ...
| ├── 404.html
│ └── base.html
├── static # 정적데이터 (nginx 웹서버 이용 시 nginx가 참조하는 다른 위치에 존재)
│ ├── css
│ └── image
├── db.sqlite3 # 데이터베이스 (mysql, postgresql 등 사용하면 별도로 연결)
├── manage.py
└── requirements.txt
Django 프로젝트는,
1) 설정관련 디렉토리 : settings.py, urls.py, wsgi.py 포함
2) 개별 앱 디렉토리 : reviews, rooms 등
3) Templates 디렉토리 : 화면에 표시하기 위한 html
4) static 디렉토리(정적 데이터포함)
5) 데이터베이스 등
으로 구성된다.
Django 작동방식
1) HTTP Request을 보냄
2) HTTP Request의 URL을 보고 어떤 View로 찾아가야하는지 urls.py에서 찾아 넘겨줌
3) View에 작성된 로직에 따라 Model 데이터를 조작하고 필요한 값을 읽어옴
4) Model에서 읽어온 값과 Template의 html 형식을 조합해 완성된 HTML HTTP Response 생성
작동 방식에 나온 구성 요소들의 코드 예시를 살펴보자.
urls.py
from django.urls import path
from . import views
app_name = "reviews"
urlpatterns = [
path("create/<int:room>", views.create_review, name="create")
]
어떤 URL이 어떤 View에 맵핑되어있는 지를 작성한다. path("create/<int:room>", views.create_review, name="create") 에서 "create/<int:room>" 부분이 URL, views.create_review 부분이 View를 의미한다.
model.py
from django.db import models
from django.core.validators import MinValueValidator, MaxValueValidator
from core import models as core_models
class Review(core_models.TimeStampedModel):
""" Review Model Definition"""
review = models.TextField()
accuracy = models.IntegerField(
validators=[MinValueValidator(1), MaxValueValidator(5)]
)
communication = models.IntegerField(
validators=[MinValueValidator(1), MaxValueValidator(5)]
)
cleanliness = models.IntegerField(
validators=[MinValueValidator(1), MaxValueValidator(5)]
)
location = models.IntegerField(
validators=[MinValueValidator(1), MaxValueValidator(5)]
)
check_in = models.IntegerField(
validators=[MinValueValidator(1), MaxValueValidator(5)]
)
value = models.IntegerField(
validators=[MinValueValidator(1), MaxValueValidator(5)]
)
user = models.ForeignKey("users.User", related_name="reviews", on_delete=models.CASCADE)
room = models.ForeignKey("rooms.Room", related_name="reviews", on_delete=models.CASCADE)
def __str__(self):
return f"{self.review} - {self.room}"
def rating_average(self):
avg = (
self.accuracy
+ self.communication
+ self.cleanliness
+ self.location
+ self.check_in
+ self.value
) / 6
return round(avg, 2)
rating_average.short_description = "Avg."
class Meta:
ordering = ("-created",)
어떤 객체가 어떤 방식으로 만들어져야 하는지를 규정한다. 예시에서 Review라는 객체는 텍스트로 된 리뷰 필드, accuracy 등 항목에 대한 개별 점수 필드로 정의되고 foreign key를 통해 리뷰 작성자와 리뷰가 작성되는 방에 연결된다. 파이썬 코드로 위와 같이 작성하면 ORM(Object Relational Mapping)이라는 기술을 통해 DB와 연동되어 쿼리를 사용할 필요 없이 파이썬 코드로 데이터를 조작할 수 있게 된다. Model로 만들어놓은 객체들은 추후 View에서 import해 사용한다.
view.py
from django.contrib import messages
from django.shortcuts import redirect, reverse
from rooms import models as room_models
from . import forms
def create_review(request, room):
if request.method == "POST":
form = forms.CreateReviewForm(request.POST)
room = room_models.Room.objects.get_or_none(pk=room)
if not room:
return redirect(reverse("core:home"))
if form.is_valid():
review = form.save()
review.room = room
review.user = request.user
review.save()
messages.success(request, "Room reviewed")
return redirect(reverse("rooms:detail", kwargs={"pk": room.pk}))
View에서는 로직을 정의한다. def create_review(request, room) 으로 리뷰를 생성하기 위한 로직을 정의하고 return redirect(reverse("rooms:detail", kwargs={"pk": room.pk})) 으로 원하는 Template으로 이동시킨다.
room_detail.html
{% extends "base.html" %}
{% load is_booked on_favs %}
{% load i18n %}
{% block page_title %}
{{ room.name }}
{% endblock page_title %}
{% block content %}
...
<div class="-mt-5 container max-w-full h-75vh flex mb-20">
<div class="h-full w-1/2 bg-center bg-cover" style="background-image:url({{room.first_photo}})"></div>
<div class="h-full w-1/2 flex flex-wrap">
# {% for photo in room.get_next_four_photos %}
<div style="background-image:url({{photo.file.url}})" class="w-1/2 h-auto bg-cover bg-center border-gray-500 border"></div>
# {% endfor %}
</div>
</div>
<div class="container mx-auto flex justify-around pb-56">
<div class="w-1/2">
<div class="flex justify-between">
<div class="mb-5">
<h4 class="text-3xl font-medium mb-px">{{ room.name }}</h4>
<span class="text-gray-700 font-light">{{ room.city }}</span>
</div>
<a href="{{ room.host.get_absolute_url }}" class="flex flex-col items-center">
{% include "mixins/user_avatar_info.html" with user=room.host %}
<span class="mt-3 text-gray-500">{{ room.host.first_name }}</span>
</a>
</div>
...
{% endblock content %}
Template은 django 객체를 사용해 만든 html이다. django template만의 특수문법과 View에서 나온 값들을 포함해 사용자가 볼 html 페이지를 만든다.
django도 하나의 거대한 프레임워크라 직접 사용하려면 배워야할 개념들이 많다. 처음 접한다면 Django Girls Tutorial을 따라해보자. 조금 더 실제 프로젝트에 가까운 케이스를 보고 싶다면 노마드코더 풀스택 에어비앤비 클론코딩를 보는 것을 추천한다.
데이터 분석에서 django를 주로 사용했을 때는 모델을 API 형식으로 만들 때였던 것 같다. 전체적인 어플리케이션 만드는 것보다 API 만드는 법만 알고 싶다면 Django REST Framework Full Course For Beginners을 참조해보자.
웹 서버, 웹어플리케이션 서버(WAS)
어플리케이션이 만들어졌으면 HTTP 요청을 주고 받는 것을 관리해줄 부분이 필요하다. 웹서버와 웹어플리케이션서버(WAS)가 이 역할을 수행하는데 각자의 역할을 통해 시스템으로 들어오는 사용자 요청을 분배하고 관리한다.
Web Server(예. Nginx)
이미지나 css, 단순 html 등의 정적 데이터를 처리하는 서버이다. Nginx와 Tomcat 등이 웹서버에 속한다. 클라이언트가 요청을 하면 웹서버에서 제일 먼저 HTTP 요청을 처리하며 정적 데이터는 웹서버에서 제공하고 동적으로 처리가 필요한 데이터는 WAS에 넘긴다.
WAS (Web Application Server) (예. uWSGI)
웹서버로 온 HTTP 요청을 여러 어플리케이션에 분배해 부하를 관리하는 서버이다. WAS는 웹서버로부터 받은 요청을 중간에 해석해 django 어플리케이션으로 전달한다. 설정한 프로세스 갯수만큼 어플리케이션 객체를 띄우고 이들에게 요청을 분배해 많은 요청이 들어왔을 때 장애가 일어나지 않고 응답을 보낸다.
WAS도 정적 데이터 처리가 가능해서 python [manage.py](http://manage.py) runserver 로 실행하는 django 개발 서버만으로도 웹어플리케이션을 구동시킬 수 있다. 다만, 정적 데이터는 Nginx 같은 웹서버에서 동적으로 처리가 필요한 데이터를 WAS를 통해 분배하는 구조가 더 효율적이다.
django의 WAS에는 gunicorn과 uwsgi 등이 있다. django 개발 서버를 사용하면 개발 변경 사항을 반영하기 위한 부분을 제외하면 하나의 어플리케이션이 돌아가 많은 요청을 보내면 버벅거린다. gunicorn이나 uwsgi 설정에서 프로세스 갯수를 늘리면 대기하고 있는 어플리케이션들이 각각 요청을 수행해 효과적으로 요청을 처리할 수 있다.
django의 WAS 부분이 이해하기 어려웠었다. 여러 프로세스로 django 어플리케이션을 띄우고 Nginx로부터 받은 요청을 어플리케이션에 나눠 부하를 관리하는 서버라고 생각하면 좋을 것 같다.
References
- https://bunnnybin.tistory.com/entry/웹-서비스의-구조
- https://www.altexsoft.com/blog/machine-learning-pipeline/
- https://www.fullstackpython.com/green-unicorn-gunicorn.html
- https://www.fullstackpython.com/wsgi-servers.html
- https://stackoverflow.com/questions/58860877/what-is-the-lifetime-of-a-django-wsgi-application-object-when-served-using-gunic
- https://uwsgi-docs.readthedocs.io/en/latest/WSGIquickstart.html
- https://nitro04.blogspot.com/2020/01/web-types-of-web-application-server-and.html
- https://twowix.me/85
- https://docs.djangoproject.com/en/3.2/ref/django-admin/
- https://soniacomp.medium.com/장고-django-멀티스레드-와-node-js-싱글-스레드-202b3270bf95
- https://developer.mozilla.org/ko/docs/Learn/Server-side/Django/Introduction
- https://nomadcoders.co/airbnb-clonehttps://jinwoo1990.github.io/dev-wiki/web-application/
'DEVELOP_NOTE > 그 외' 카테고리의 다른 글
[GitLab]소스코드 관리, GitLab으로 완벽 해결하기 (0) | 2023.07.21 |
---|---|
[Flower]Celery웹 모니터링 도구 'Flower'을 분해해보자. (0) | 2023.03.29 |
[Git]Branch 조회 & 기본 Branch 변경 (0) | 2023.03.24 |
[WEB&WAS]WEB서버와 WAS에 대해서 알아보자 (0) | 2023.03.23 |
[Pycharm 설정]Preference에서 Django설정 불가 이슈 (0) | 2023.03.21 |