STATIC 파일 분리
1. static 파일을 분리하는 이유
일반적으로 css와 자바스크립트는 html 파일에 포함시키지 않습니다. css와 자바스크립트는 서버에 매번 렌더링 할 필요 없이 모든 사용자에게 동일하게 적용됩니다. css나 자바스크립트는 데이터베이스에 저장된 값을 적용하 거나 템플릿태그를 적용할 일이 없다는 말입니다. 서버에서 렌터링해야할 필요가 있는 경우도 있지만 예외적인 부분입니다. 반대로 html은 매번 새로 생성해야 하는 부분이기 때문에 어쩔 수 없이 매 요청마다 렌더링 후 전송을 해야 합니다.
매번 새로 생성해줘야 하는 코드를 동적코드 dynamic코드라고 부르며 새로 생성할 필요가 없는 코드는 정적코드 static코드라고 합니다.
정적 코드는 각 종류에 따라 css, js 등의 파일로 관리를 하고 정적 파일은 서버와 브라우저에서 관리합니다.
장고나 웹서버에서 1차적으로 정적파일을 전송할 때 Last-Modifield해더에 파일의 최종 수정된 시간을 표시합니다. 2차적으로 브라우저는 전송받은 정적파일을 로컬에 저장했다가 다음번에 동일한 파일을 서버에 요청할 때 If-Modifield-Since헤더에 Last-Modified헤더의 값을 표시해서 요청하고, 서버는 요청 헤더에 if-Modifield가 있으면 해당 파일의 최종 수정시간이 과거일 경우 파일을 다시 전송하고 수정된 시간을 표시해 줍니다.
서버의 입장에서는 파일 전송에 허비하는 시간을 줄이고 브라우저 입장에서는 조금 더 빠르게 화면을 표현할 수 있습니다.
2. STATIC 설정
장고에서는 모든 요청을 urlpatterns에 등록된 핸들러에 전달합니다. 브라우저가 static파일을 다운로드 요청을 하더라도 이것에 대한 핸들러가 있어야 하는데 설정파일의 STATIC_URL값이 설정되어 있으며 장고에 내장되어 있는 static 핸들러에 라우팅이 등록됩니다.
STATIC_URL로 기본설정된 값은 변경하지 않고 앞으로 static 파일들은 각 앱의 static 디렉터리에 저장하고 요청하는 url도 /static/으로 시작하도록 설정하겠습니다.
# djangotutorial/settings.py
# 생략
STATIC_URL = 'static/'
STATIC_ROOT = "/프로젝트 이름/프로젝트 이름/static"
STATICFILES_FINDERS = ['django.contrib.staticfiles.finders.FileSystemFinder','django.contrib.staticfiles.finders.AppDirectoriesFinder']
# 생략
static관련 설정
- STATIC_URL : 이 경로로 시작되는 요청은 static핸들러로 라우팅
- STATIC_ROOT : collectstatic 커맨드로 static 파일들을 모을 때 저장될 디렉터리 경로
- STATICFILES_DIRS : collectstatic 또는 findstatic 커맨드 실행시 검색하는 디렉터리 경로들의 리스트, 주로 앱 내부의 static 디렉토리가 아닌 다른 곳에 저장되어 있을 경우 설정함
STATIC_ROOT는 현재 존재하는 디렉토리 경로를 설정해야 합니다. 윈도우는 예외지만 리눅스 또는 맥에서는 manage.py를 실행하는 권한으로 디렉터리 권한이 설정되어 있어야 합니다.
(test-venv-36) $ sudo mkdir -p /var/www/static
(test-venv-36) $ sudo mkdir -p /var/www/static
(test-venv-36) $ sudo chown `whoami`:`id -g -n` /var/www/static/
이제 앞으로 /static/으로 시작하는 url은 장고의 static 핸들러가 자동으로 처리해 줄 것입니다.
css 분리
각 템플릿의 css 블록 안에 있는 css 코드들을 css 파일로 따로 분리합니다. 각 앱 별로 css는 따로 관리할 수 있고 자바스크립트와 이미지 등의 파일도 모드 각 앱의 static이라는 디렉터리 안에서 관리합니다. 모든 앱들의 static파일들을 앱 구분업싱 하나의 디렉터리에서 관리할 수도 있습니다. 하지만 이렇게 관리하면 다른 프로젝트에서 재사용하기 힘들어집니다.
STATIC_URL = 'static/'
STATIC_ROOT = "/DjangoTutorial/DjangoTutorial/static"
STATICFILES_DIRS = (
os.path.join(BASE_DIR, '/DjangoTutorial/DjangoTutorial/bbs/static'),
os.path.join(BASE_DIR, '/DjangoTutorial/DjangoTutorial/user/static'),
)
STATICFILES_FINDERS = ['django.contrib.staticfiles.finders.FileSystemFinder','django.contrib.staticfiles.finders.AppDirectoriesFinder']
각 앱 별로 static 디렉터리를 생성하고 그 안에 앱 이름의 디렉터리를 다시 생성하고 그 안에 css, js, img라는 디렉터리를 생성합니다.
<!-- bbs/templates/article_list.html -->
{% extends 'base.html' %}
{% load static %}
{% block title %}<title>게시글 목록</title>{% endblock title %}
{% block css %}
{{ block.super }}
<link rel="stylesheet" href="{% static 'css/bbs.css' %}">
{% endblock css %}
{% block content %}
<table class="table table-hover table-responsive">
<thead>
<th>번호</th><th>제목</th><th>작성자</th>
</thead>
<tbody>
{% for article in articles %}
<tr onclick="location.href='/article/{{ article.pk }}/'">
<td>{{ article.pk }}</td><td>{{ article.title }}</td><td>{{ article.author }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<a href="/article/create/"><button class="btn btn-primary" type="button">새 게시글 작성</button></a>
{% endblock content %}
헤더에 <link rel="stylesheet" href="{% static 'css/bbs.css' %}>라고 표기해서 css파일을 사용한다는 선언을 해주었습니다.
장고에서는 설정파일의 static_url 설정을 신경 쓰지 않고 유연하게 static 파일을 설정할 수 있는 템플릿 태그를 제공합니다. static이라는 태그인데 href 속성에 static 태그를 이용해서 설정 파일의 static_url + 'css/bbs.css'로 변환됩니다. 즉, {% load static %}을 추가해서 템플릿엔진에서 static 템플릿 태그를 사용할 수 있게 로딩해줘야 합니다.
분리한 css 내용은 bbs/static/css/bbs.css 파일에 그대로 넣어줍니다.
/* bbs/static/css/bbs.css */
tbody > tr {cursor: pointer;}
잘 적용되었는지 확인 후 다음으로 넘어가겠습니다.
user앱의 css를 분리합니다. login_form.html, resend_verify_email.html, user_model.html 세 탬플릿이 거의 비슷한 css를 가지고 있습니다. 이것을 하나로 만들어주겠습니다.
/* user/static/css/user.css */
.user-panel {
width: 360px;
margin: 0 auto;
}
.registration {
width: 360px;
margin: 0 auto;
}
.registration .form-actions > button {
width: 100%;
}
p {
text-align: center;
}
label {
width: 50%;
text-align: left;
}
.control-label {
width: 100%;
}
.user-panel .form-actions > button {
width: 100%;
}
.link-below-button { margin-top: 10px; text-align: right;}
이제 각 템플릿의 {{ block.super }}, 와 css를 연결해 주는 코드를 남기고 모든 css를 제거하여 줍니다. 그리고 {% extends 'base.html' %} 바로 밑에 {% load static %}를 추가하여 줍니다.
템플릿 분리
회원가입과 로드인 템플릿을 하나의 템플릿으로 만들고 로그인 상태에 따라 if else 문을 사용하여 형태를 달라지게 하였는데 이것을 분리하여 줍니다.
1. 템플릿 include
user 앱에서 템플릿의 공통된 부분들을 따로 분리하는데 extends 템플릿태그로 상속받는 것이 아니라 include탬플릿 태그를 이용해서 템플릿의 일부를 분리시키는 방법을 사용하겠습니다.
user_model.html 템플릿의 for 블록을 분리합니다. form_field.html 템플릿 파일을 생성해서 분리한 내용을 저장합니다.
<!-- user/templates/user/form_field.html -->
{% for field in form %}
<div class="form-group {% if field.errors|length > 0 %}has-error{%endif %}">
<label for="{{ field.id_for_label }}">{{ field.label }}</label>
<input name="{{ field.html_name }}" id="{{ field.id_for_label }}" class="form-control" type="{{ field.field.widget.input_type }}" value="{{ field.value|default_if_none:'' }}">
{% for error in field.errors %}
<label class="control-label" for="{{ field.id_for_label }}">{{ error }}</label>
{% endfor %}
</div>
{% endfor %}
이후 user_model.html 템플릿에서 분리된 내용을 삭제하고 include 템플릿 태그를 이용해서 분리한 템플릿을 불러와줍니다.
<!-- user/templates/user_model.html -->
<!-- 생략 -->
<form action="." method="post">
{% csrf_token %}
{% include 'form_field.html' with form=form %}
<div class="form-actions">
<button class="btn btn-primary btn-large" type="submit">가입하기</button>
</div>
</form>
<!-- 생략 -->
삽입되는 템플릿에 데이터를 전달할 때는 with구문을 인자로 넘겨줄 수 있습니다. 예를 들어 form=form이라고 값을 넘겨주면 form_field.html 에서 form이라는 데이터를 동일한 이름의 피라미터로 사용하도록 전달한다는 의미입니다.
마지막으로 템플릿 이름을 조금 더 알아보기 쉽고 일관성 있게 정리해 주었습니다. user_model.html을 registration_form.html으로 변경하고 resend_verify_email.html을 resend_verify_form.html로 변경하고 각 뷰에도 변경된 템플릿 이름으로 설정하도록 하였습니다. 또한 뷰에서 template_name 변수도 변경하여 주었습니다.
파일은 아래와 같이 정리되어 있습니다.
STATIC 파일 모으기
collectstatic 커맨드를 이용해 각 앱의 static 파일들을 하나의 디렉터리에 모아 둘 수 있습니다.
과거 admin 사이트를 만들면서 css가 적용되지 않았는데 아래의 방법으로 해결했습니다.
[Project] 장고 admin 페이지 css 오류
[Project] 장고 admin 페이지 css 오류 📖 개요 장고를 이용하여 포트폴리오 웹사이트를 만드는 중 admin 사이트의 css가 적용이 안 되는 버그를 발견하다. 본인은 "0 static files copied to" 오류가 계속 뜨고
jongseoung.tistory.com
위 글에서 정리된 오류 말고 몇 가지가 더 있었다.
모이는 파일들이 경로와 이름이 겹치게 되면 아래와 같이 오류가 발생한다.
Found another file with the destination path 'css/common.css'. It will be ignored since only the first encountered file is collected. If this is not what you want,
make sure every static file has a unique path.
STATICFILES_DIRS 설정을 할 때 디렉터리들을 튜플로 설정하는데 만일 추가할 디렉터리가 1개일 때 쉼표(,)를 생략할 경우 오류가 발생한다.
ERRORS:
?: (staticfiles.E001) The STATICFILES_DIRS setting is not a tuple or list.
HINT: Perhaps you forgot a trailing comma?
'Django > DRF' 카테고리의 다른 글
[Django tutorial] 사용자 인증 - 소셜 로그인 (2) (0) | 2023.01.29 |
---|---|
[Django tutorial] 사용자 인증 - 소셜 로그인 (1) (0) | 2023.01.21 |
[Django tutorial] 사용자 인증 - 이메일 인증, 로그아웃 (0) | 2023.01.17 |
[Django Tutorial] 사용자 인증 - 세션, 인증 관리 (0) | 2023.01.15 |
[Django Tutorial] 사용자 인증 - 로그인 기능 생성 (0) | 2023.01.15 |