본문 바로가기

백/django

MBIT 장고-3

구조 - user, web server, app server

-> 여태까지 했던 것처럼 단순히 runserver 하는 것은 적합하지 x

 

1. static file들을 한 곳에 모아야 함

settings.py에서 STATIC_URL밑에 아래 코드 작성

STATIC_ROOT = BASE_DIR / 'staticfiles'

후 console창에서 python manage.py collectstatic 입력.

 

2. WSGI 수정

WSGI : 웹서버와 웹 어플리케이션(장고) 직접적으로 통신하지 못하기 때문에 WSGI가 중재자 역할 해준다!

wsgi 터미널창에 깔아주고

$ uwsgi \
--http :80 : http 80 서비스로 구동할거다
--home /나의 pwd/venv/ :home directory 설정
--chdir /나의 pwd/  : 자식 디렉토리 설정
--static-map /static=/나의 pwd/staticfiles/ \
-w MBIT.wsgi

터미널 창에 이렇게 입력해주면 더이상 터미널 창에 runserver 입력안해도 port 80으로 접속하면 실행됨!

[uwsgi]
chdir = /workspace/mbittest/
module = MBIT.wsgi:application
home = /workspace/mbittest/venv/

uid = root
gid = root

http = :80

enable-threads = true
master = true
vacuum = true
pidfile = /workspace/mbittest/tmp/MBIT.pid
logto = /workspace/mbittest/log/uwsgi/@(exec://date +%%Y-%%m-%%d).log
log-reopen = true
static-map = /static=/workspace/mbittest/staticfiles/
  • chdir : django project의 manage.py가 존재하는 경로를 지정해줌
  • module : wsgi 파일을 지정해줌
  • home : 가상환경의 경로를 지정해줌
  • uid, gid : uWSGI를 실행할 사용자 및 사용자그룹을 지정해줌
  • http : http 프로토콜을 통해서 요청을 받으며 포트 번호를 정해줌
  • enable-threads : 스레드 사용 여부를 결정함
  • master : 마스터 프로세스 사용 여부를 결정함
  • vacuum : 실행 시 자동 생성되는 파일들을 삭제해줌
  • pidfile : 실행되는 프로세스의 id 값을 담고 있는 파일, pidfile의 경로를 지정해줌
  • logto : 로그파일을 작성할 위치를 설정함
  • log-reopen : 재시작할 시 로그를 다시 열어줌

uwsgi.ini에 위와 같이 입력함으로써 긴 명령어 필요없이 모든 옵션이 들어있는 파일로 연다. ( 해당 옵션들을 절대 경로로 적어주다보니 명령어 하나의 양이 적지 않다는 문제를 해결 )

 

3. 웹서버 : HTTP를 통해 웹 브라우저에서 요청하는 HTML 문서나 object(이미지 파일 등)을 전송해주는 서비스 프로그램

-> 대표적으로 NGINX, apache가 있다

 

 

4. 프로젝트 개선하기

Redirect

웹 브라우저: 요청을 보내고 응답을 받은 뒤에도 보낸 요청이 무엇인지 기억하고 있는다

- > 유저가 새로고침하면 기억하고 있던 요청을 다시 보냄.

   - > 결고 페이지에서 새로고침하면 설문데이터가 서버로 다시 전달 됨  -> 이를 방지하기 위해 redirect 사용해야한다

Redirect란?

-> 브라우저에게 어떤 특정한 url로 재요청을 하라고 명령하는 것

  설문 제출 요청 후에 결과 페이지를 받기만 하는 요청이 한 번 더 이루어지기 때문에 더 이상 처음의 설문 제출 요청은 웹   브라우저에 남지 않게됨.( 웹 브라우저는 이전 상태는 전혀 기억하지 못하게 되어있기 때문에)

 

[이전] : 설문제출 요청 -> 처리 후 결과페이지 렌더링
[이후] : 설문 제출 요청 -> 처리 -> 결과 페이지 요청 -> 결과 페이지 렌더링 

 

A라는 유저가 처음 submit이라는 B 페이지를 들어가고 result라는 C페이지로 redirect

 

def submit(request):  # 10개의 문항을 돌면서 각 문항에서 선택한 항목이 어떤 개발자 유형에 속하는지 count한 후 문항 다 돌았을 때 어떤 개발자유형이 가장 많이 나왔는지 알아내어 최종적으로 적합한 개발자 유형 알려주는 시스템
    
    # 문항수
    N=Question.objects.count()
    # 개발자 유형 수 
    K=Developer.objects.count()
    
    counter=[0]*(K+1)
    
    for i in range(1, N+1):
        developer_id=int(request.POST[f'question-{i}'][0])
        counter[developer_id]+=1
        
    # 최고점 개발 유형
    best_developer_id=max(range(1, K+1), key=lambda id: counter[id])
    best_developer=Developer.objects.get(pk=best_developer_id)  # 객체 가져옴
    best_developer.count+=1  #객체 요소 count에 접근
    best_developer.save()
    
    context={
        'developer':best_developer,
        'counter':counter
    }
    
    return redirect(f'result/{best_developer_id}')

def result(request):
    
    return render(request, 'result.html', context=context)

이런식으로 submit return에 redirect하고 result view 만들어준다

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', views.index), # views.py의 index라는 함수에 매핑시켜주겠다
    path('form/', views.form),
    path('submit/', views.submit),
    path('result/<int:developer_id>', views.result),
]

urls.py에도 위와 같이 추가해준다

->submit함수에서 계산된 best_developer_id가 redirect에 사용되어 알맞은 result페이지로 redirect 시켜줌

->근데 잘 보니 result view에 best_developer_id 받는 부분이 없다. 만들어주기!

def result(request, developer_id):
    developer=Developer.objects.get(pk=developer_id)
    context={
        'developer':developer,  # developer는 바로 위에서 developer다
    }
    return render(request, 'result.html', context=context)

 -> 이제 아무리 결과 페이지에서 새로고침을 해도 result함수만 호출되기 때문에 설문 참여자 수는 늘어나지 않

 

5. url 분리

여태까지 모든 url을 MBIT/urls.py에서 관리함. 실무에서는 urls.py에서 모든 url을 관리하지 않는다(url 개수 너무 많아서)

-> URL 설정을 앱별로 나누어서 관리하는 게 좋음

   -> main앱과 관련된 url을 main/urls.py에 저장

1. 기존 MBIT/urls.py 내용을 main/urls.py로 옮기기(admin은 여기 통해 들어갈 게 아니니까 관련 내용 삭제)

2. MBIT/urls.py 아래와 같이 수정

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

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('main.urls')), # 어떤 option이 붙든 main.urls.py로 연결된다

]

 

6. URL name

-URL을 변수(name)에 넣어서 사용.

-특정 URL에 우리가 알 수 있는 변수(name)을 지정하고, 이 URL을 사용해야 하는 곳에서 URL을 직접 사용하는 대신에 이 변수(name)를 사용

app_name='main'

urlpatterns = [
    path('', views.index, name='index'),
    path('form/', views.form, name='form'),
    path('submit/', views.submit, name='submit'),
    path('result/<int:developer_id>/', views.result, name='result'),
]

-> app_name : URL namespace(이름공간)으로 여러 앱들 마다 동일한 URL name을 가질 때 구별하기 위함

 

7. Template 상속

겹치는 부분은 base.html로 따로 빼내놓고 각 page마다 다른 부분만 block과 endblock으로 감싸줌

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>나의 개발 유형찾기 - final</title>
  {% block head %}
  {% endblock head %}
  <title>Document</title>
</head>
<body>
  {% block body %}
  {% endblock body %}

  {% block js %}
  {% endblock js %}
</body>
</html>

-> base.html

 

8. 개발자 유형별 데이터 Json형태로 추가하기

main > models.py의 class Developer 데이터 추가하기

-> model이 변경되었으므로 터미널 창에 makemigrations 해주기

이런 문구가 뜨게 되는데 얘는 data가 비어 있을 때 default 값을 줄건지 물어보는거다. 1을 선택하면 비어있을 때 default 값을 주게 된다.

만들어 둔 data.json파일에 다운받은 json 파일을 복사해 붙여넣고 터미널 창에 python manage.py loaddata data.json 입력해 전체 데이터를 load 한다.

python manage.py shell_plus 입력하면 데이터가 잘 들어갔는지 확인할 수 있다.

 

json 바탕으로 데이터 뿌리는 팁!

-> colab같은데에 데이터 가져와서 직접 한번 실행시켜 "어떤 데이터를 어떻게 뿌리는구나" 라고 판단하기

"model": "main.developer",
    "pk": 2,
    "fields": {
        "name": "프론트엔드 개발자",
        "count": 12,
        "data": {
            "title": "예술적 감각이 뛰어난 당신은!",
            "features": [
                "내 눈으로 결과를 확인해야 함.  바로 바로 적용되는 화면에 성취감을 느낌.",
                "어렸을 때 한번쯤은 호그와트 들어가는 상상해보지 않나? 상상력이 풍부하고 새로운 도전을 즐기는 편임.",
                "은근히 완벽주의자 성향이 있어서 1px이라도 비뚤어지거나 대칭이 맞지 않으면 참을 수 없음.",
                "자유로운 영혼이지만 내가 지금 무엇을 해야하는지 정확히 알아야 함."
            ],
            "descriptions": [
                "FE(Front-End)라고도 부르며, 웹이나 앱을 이용할 때 눈으로 보는 모든 화면들을 만드는 개발자를 의미합니다.",
                "반응형 웹이나 UI를 만들어야 하기 때문에 미적 감각이 필요합니다.",
                "웹 퍼블리셔와 프론트엔드 개발자는 공동의 영역을 공유합니다. 웹 퍼블리셔는 주로 HTML, CSS를 사용하여 화면을 설계합니다. 프론트엔드 개발자는 js, jQuery, 백엔드와의 연동 등 화면 설계와 동시에 화면 구조까지 살펴볼 수 있는 사람을 말합니다. 그러나 실무에서는 이 두 가지 직업군의 롤 모두를 수행하는 사람이 많습니다."
            ],
            "languages": {
                "list": [
                    {
                        "name": "HTML",
                        "img": "img/lang/html.png"
                    },
                    {
                        "name": "CSS",
                        "img": "img/lang/css.png"
                    },
                    {
                        "name": "JavaScript",
                        "img": "img/lang/js.png"
                    }
                ],
                "comments": [
                    "HTML, CSS, JavaScript 웹을 구성하는 3요소 입니다! (얼마 전 Web Assembly가 추가되어 4요소가 되었지만 처음에는 3개만 배우셔도 충분합니다!)",
                    "기본적인 내용이 숙지되었다면 CSS Animation, Media Query, Canvas, JSON parsing, Ajax, Node.js, jQuery를 공부해보세요!"
                ]
            },
            "lectures": [
                {
                    "name": "HTML, CSS, JS, Python 30분 요약강좌",
                    "img": "img/lec/lec_30minutes.png",
                    "url": "https://www.inflearn.com/course/제주코딩-웹개발-30분요약"
                },
                {
                    "name": "코알못에서 웹서비스 런칭까지 : 제주 코딩 베이스캠프(Django)",
                    "img": "img/lec/lec_web_fullstack.png",
                    "url": "https://www.inflearn.com/course/web_fullstack"
                },
                {
                    "name": "제주코딩베이스캠프 Code Festival: JavaScript 100제",
                    "img": "img/lec/lec_js100.png",
                    "url": "https://www.inflearn.com/course/제주코딩-자바스크립트-100제"
                }
            ]
        }
    }
},

임의로 하나의 모델의 데이터 뽑음. 이때 languages는 다른애들과 다르게 리스트형식이다. 따라서 코드 languages.list/languages.comment로 작성해야 함. 인자들은 language.name 이런식으로 리스트 안의 원소에 접근해야 한다.

<div class="explain">
    <h3 class="title">그래서 어떤 언어부터 공부해야 할까?</h3>
    <ul class="language_lists">
        {% for language in developer.data.languages.list %}
        <li>
            <div class="img_wrap">
                <img src="{% static '' %}{{ language.img }}" alt="{{ language.name }}">
            </div>
            <h3>{{ language.name }}</h3>
        </li>
        {% endfor %}
    </ul>
    {% if data.languages.comments %}
    <ul>
    {% for comment in developer.data.languages.comments %}
        <li>
            {{ comment }}
        </li>
    {% endfor %}
    </ul>
    {% endif %}
</div>

' > django' 카테고리의 다른 글

MBIT 장고-2  (0) 2023.10.11
MBIT 장고-1  (0) 2023.10.05