스모크 테스트 (Smoke Test)를 이용해서 CSS가 로딩되었는지 확인한다.
functional_test/tests.py
def test_layout_and_styling(self):
# 에디스는 메인 페이지를 방문한다.
self.browser.get(self.live_server_url)
self.browser.set_window_size(1024,768)
# 그녀는 입력 상자가 가운데 배치된 것을 본다
inputbox = self.browser.find_element(By.ID,'id_new_item')
self.assertAlmostEqual(
inputbox.location['x'] + inputbox.size['width'] / 2,
512,
delta=10
)
기능 테스트 실행 시 아래와 같이 오류가 발생한다.
self.assertAlmostEqual(
AssertionError: 104.0 != 512 within 10 delta (408.0 difference)
스타일링을 적용하기 위한 적절한 솔루션을 찾기 위해 다시 원상 복귀한다.
<h1>작업 목록 시작</h1>
<form method="POST" action="/lists/new">
<!-- <p style="text-align : center;"> -->
<input name="item_text" id="id_new_item" placeholder="작업 아이템 입력" />
<!-- </p> -->
{% csrf_token %}
</form>
부트스트랩(BootStrap) 을 이용해보도록 한다.
https://getbootstrap.com/
다운로드를 마친 후, list 앱 내 "static" 폴더를 만들어서 저장하도록 한다.
공통 템플릿인 'superclass' 를 만들어서 두 개 템플릿이 이것을 상속하도록 한다.
python $ cp lists/templates/home.html lists/templates/base.html
base.html 이라는 공통 템플릿을 만들어서 상용구 코드를 추가하고 자식 템플릿이 커스터마이징 할 수 있는 blocks 을 설정하도록 한다.
<html>
<head>
<title>To-Do lists</title>
</head>
<body>
<h1>{% block header_text %}{% endblock %}</h1>
<form method="POST" action="{% block form_action %}{% endblock %}">
<!-- <p style="text-align : center;"> -->
<input name="item_text" id="id_new_item" placeholder="작업 아이템 입력" />
<!-- </p> -->
{% csrf_token %}
</form>
{% block table %}
{% endblock %}
</body>
</html>
base 템플릿에는 blocks 라는 연속 영역을 정의하는데,
자식 템플릿의 콘텐츠를 추가하거나 연동할 수 있는 영역이다.
lists/templates/home.html
{% extends 'base.html' %}
<html>
<head>
<title>To-Do lists</title>
</head>
<body>
<h1>{% block header_text %}작업 목록 시작{% endblock %}</h1>
<form method="POST" action="{% block form_action %}lists/new{% endblock %}">
<input name="item_text" id="id_new_item" placeholder="작업 아이템 입력" />
{% csrf_token %}
</form>
<table id="id_list_table">
{% for item in items %}
<tr>
<td>{{ forloop.counter }}: {{item.text}}</td>
</tr>
{% endfor %}
</table>
</body>
</html>
lists/templates/list.html
{% extends 'base.html' %}
<html>
<head>
<title>To-Do lists</title>
</head>
<body>
<h1>{% block heaer_text %}작업 목록 시작{% endblock %}</h1>
<form method="POST" action="{% block form_action %}/lists/{{list.id}}/add_item{% endblock %}">
<input name="item_text" id="id_new_item" placeholder="작업 아이템 입력" />
{% csrf_token %}
</form>
{% block table %}
<table id="id_list_table">
{% for item in list.item_set.all %}
<tr>
<td>{{forloop.counter}}: {{item.text}}</td>
</tr>
{% endfor %}
</table>
{% endblock %}
</body>
</html>
FT 실행 시, 이전과 동일한 결과가 나오는 것을 확인하였다.
커밋 후 다음 스텝으로 넘어가본다.
CSS 적용해보도록 한다.
lists/templates/base.html
<!DOCTYPE html>
<html lang="en">
<head>
<title>To-Do lists</title>
<meta name="viewport" content="width=device-width, initial-scale = 1.0">
<link href="css/bootstrap.min.css" rel="stylesheet" media="screen">
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md">
<div class="text-center">
<h1>{% block header_text %}{% endblock %}</h1>
<form method="POST" action="{% block form_action %}/lists/new{% endblock %}">
<input name="item_text" id="id_new_item" placeholder="작업 아이템 입력" />
{% csrf_token %}
</form>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6 col-md-offset-3">
{% block table %}
{% endblock %}
</div>
</div>
</body>
</html>
CSS 가 동작하지 않는 것을 확인 할 수 있다.
웹 서버에서 정적 파일 (Static File)을 다루기 위해서 다음 두 가지 사항을 고려해야 한다.
정적 파일은 특정 URL을 디스크 상에 있는 파일과 매칭시키는 역할을 한다.
접두사는 /static/으로 초기 설정돼 있다. setting.py 을 통해 조정할 수 있다.
/static/이라는 URL 접두사를 사용하지 않았기 때문에, 동작하지 않았다.
base.html에 있는 CSS 파일 링크 부분을 아래와 같이 변경한다.
lists/templates/base.html
<head>
<title>To-Do lists</title>
<meta name="viewport" content="width=device-width, initial-scale = 1.0">
<link href="/static/bootstrap/css/bootstrap.min.css" rel="stylesheet" media="screen">
</head>
수동으로 진입했을때, 변경되었음을 확인할 수 있다.
FT를 실행했을때, 그래도 실패하는 것을 확인할 수 있는데 이는 LiveServerTest는 자동으로 정적파일을 찾을 수 없기 때문이다.
이에 StaticLiveServerTestCase로 변경해줘야한다.
from django.contrib.staticfiles.testing import StaticLiveServerTestCase
class NewVisitorTest(StaticLiveServerTestCase):
jumbotron 이라는 클래스를 이용하여 레이아웃을 변경해주도록 한다.
아래 코드는 모든 스텝을 다 한 이후의 코드이다.
base.html
<div class="container">
<div class="row">
<div class="col-md jumbotron">
<div class="text-center">
<h1>{% block header_text %}{% endblock %}</h1>
<form method="post" action="{% block form_action %}{% endblock %}">
<input name="item_text" id="id_new_item" class = "form-control input-lg" placeholder="작업 아이템 입력" />
{% csrf_token %}
</form>
</div>
</div>
</div>
</div>
list.html
<table id="id_list_table" class = "table">
사용자 지정 CSS를 설정하여 타이틀과 입력상자 사이에 간격을 만들어 준다.
base.html
<head>
<title>To-Do lists</title>
<meta name="viewport" content="width=device-width, initial-scale = 1.0">
<link href="/static/bootstrap/css/bootstrap.min.css" rel="stylesheet" media="screen">
<link href="/static/base.css" rel="stylesheet" media="screen">
</head>
lists/static/base.css
#id_new_item {
margin-top : 2ex;
}
개발 단계에서는 이렇게 사용해도 크게 문제가 되지 않지만, 실제 운영 중인 웹 서버에서 Django가 정적 콘텐츠를 제공하도록 하는 것은 매우 느리며 비효율적이다.
이에 여러 앱에 존재하는 모든 정적 파일을 한 곳에 모아서 배포용으로 만들어 둘 필요가 있다.
이 작업을 해주는 것이 collectistatic이다.
수집된 정적 파일이 모이는 위치는 settings.py 의 STATIC_ROOT 항목을 통해 설정한다.
실습 한번하고, 해당 항목 값을 리포지토리 밖에 있는 폴더로 지정한다.
settings.py
STATIC_URL = '/static/'
STATIC_ROOT = os.path.abspath(os.path.join(BASE_DIR,'lists/static'))
lists/static 폴더에 해당 내용을 생성하였다.
관리사이트는 Django가 가진 강력한 기능이나, 현재단계에서는 필요없으므로 꺼준 후 커밋한다.
이번에는 레이아웃 관련된 부분이여서 관련된 지식이 없는 나로써는
하나씩 부딪혀보며 고쳐보았단 것 같다.
특히 text-center 관련해서 조금 애먹었다.
도서에서 나오는 그대로 따라하긴했는데 실제로 적용했을 때는 가운데 정렬이 되지 않았었다.
참고 도서 : 클린 코드를 위한 테스트 주도 개발 (비제이퍼블릭,2015)