테마 제작¶
기본 테마를 수정하거나 새로운 테마를 만들어 사용할 수 있습니다.
테마 생성¶
1. 테마 복사¶
새로운 테마를 만들기 위한 제일 쉬운 방법은 기본 테마를 복사하는 것입니다.
기본 테마(templates/basic
) 를 동일한 경로에 이름을 변경해서 복사합니다.
아래에서는 user
라는 이름으로 복사했다고 가정합니다. 그럼 아래와 비슷한 구조로 되어 있을겁니다.
.
├─ templates/
│ ├─ basic/
│ └─ user/
│ ├─ ...
│ ├─ member/
│ │ ├─ ...
│ │ └─ register_form.html
│ ├─ ...
│ ├─ LICENSE
│ ├─ readme.txt
│ └─ screenshot.png
└─ ...
관리자에서는 아래와 비슷한 화면이 보일겁니다.
2. 테마 정보 수정¶
기본 테마를 그대로 복사했기 때문에 테마 정보를 수정해야 합니다.
readme.txt
파일의 내용을 수정하고 screenshot.png
를 교체합니다.
아래는 readme.txt
파일 내용 예시입니다.
Theme Name: 사용자
Theme URI: http://user-domain.com/gnuboard6/theme/user
Maker: User Company
Maker URI: http://user-domain.com
Version: 1.0.0
Detail: 사용자 테마는 사용자님이 만든 테마입니다.
License: BSD
3. 테마 적용¶
관리자에서 원하는 테마를 적용하면 사용 중인 테마가 바뀌게 되며, 사용자 페이지에서 확인할 수 있습니다.
테마 수정¶
복사한 테마를 수정하여 나만의 테마를 만들 수 있습니다.
1. 기본 코드 설명 (Jinja Template)¶
테마 기초에서 회원가입 시 HTML 파일은 아래와 같은 순서로 실행된다고 했습니다.
flowchart LR
A[main.py] --> B[bbs/register.py];
B --> Template;
subgraph Template
C[base_sub.html] --> D[base.html];
D[base.html] --> E[member/register_form.html];
end
Template --> F[브라우저 출력];
파이썬(bbs/register.py
)에서 return templates.TemplateResponse("user.html", context)
로 값을 넘겨주면 HTML에서 이 값을 받아 Jinja Template 문법을 통해 명령문과 출력문의 조합으로 완성된 HTML 코드를 출력하게 됩니다.
아래는 필수로 알아야할 Jinja Template 개념 & 문법입니다.
더 많은 정보를 확인하시려면 Jinja Template Designer Documentation를 참고하세요.
1. 구분기호¶
Jinja 구분 기호에는 몇 가지 종류가 있습니다.
{% ... %}
: 주로 블록이나 if, for 문등의 명령문에 사용합니다.{{ ... }}
: 주로 프린트문에 사용합니다.{# ... #}
: 주석문에 사용합니다.
2. 변수¶
Jinja 템플릿에서는 {{ 변수명 }}
구문을 사용하여 변수 값을 출력할 수 있습니다. Python 코드에서 템플릿으로 변수를 전달하면, 해당 변수를 템플릿 내에서 사용할 수 있습니다.
3. 필터¶
변수에 적용되는 변환을 정의합니다. 예를 들어, {{ 변수명|대문자 필터 함수 }}
는 변수의 값을 대문자로 변환하여 출력합니다. Jinja는 다양한 내장 필터를 제공하며, 사용자 정의 필터도 생성할 수 있습니다.
4. 제어문¶
if, for 같은 제어 구조를 제공하여 템플릿 내에서 조건부 로직이나 반복을 수행할 수 있게 합니다. 예를 들어, {% if 사용자_상태 == '활성' %}
또는 {% for 항목 in 항목_리스트 %}
와 같은 구문을 사용할 수 있습니다.
{# if문 #}
{% if board.bo_table %}
...
{% else %}
...
{% endif %}
{# for문 #}
{% for board in boards %}
{# board 변수가 있을 경우 #}
{% else %}
{# boards가 없을 경우 #}
{% endfor %}
5. 상속과 포함¶
템플릿 파일 간의 상속을 지원하여 기본 템플릿의 블록을 재정의할 수 있게 해줍니다.
- 상속은
extends
와block
태그를 사용하여 상위 템플릿을 사용할 수 있습니다. - 포함은
include
태그를 사용하여 다른 템플릿의 내용을 현재 템플릿에 포함시킬 수 있습니다. (그누보드5의 include와 비슷합니다.)
{# 상속을 통해 상위 템플릿 사용 #}
{% extends "base.html" %}
{# 상위 템플릿의 head block에 내용 추가 #}
{% block head %}
{# datepicker.html 파일 포함 #}
{% include "datepicker.html" %}
{% endblock head %}
6. 매크로¶
매크로를 사용하면 재사용 가능한 코드 블록을 정의할 수 있습니다.
이는 함수와 유사하게 작동하며, 코드를 재사용하여 템플릿을 더 깔끔하게 유지할 수 있게 해줍니다.
{# 정의 #}
{% macro anchor(type, fr_date='', to_date='') -%}
<ul class="anchor">
<li {% if type == 'list' %}class="active"{% endif %}><a href="./visit_list?fr_date={{ fr_date }}&to_date={{ to_date }}">접속자</a></li>
...
<li {% if type == 'year' %}class="active"{% endif %}><a href="./visit_year?fr_date={{ fr_date }}&to_date={{ to_date }}">년</a></li>
</ul>
{%- endmacro %}
{# 사용 #}
{% block content %}
...
{{ form.anchor('date', fr_date, to_date) }}
{% endblock %}
7. 공백 제어¶
템플릿에서 생성된 HTML의 공백을 제어하기 위해 Jinja는 공백 제어 구문을 제공합니다.
{%-
또는 -%}
를 사용하여 태그 주변의 공백을 제거할 수 있습니다.
-
공백제어 기호가 없는 경우
latest\basic.html{# 입력 #} <li class="basic_li"> {% if write.icon_secret %} <i class="fa fa-lock" aria-hidden="true"></i> <span class="blind">비밀글</span> {% endif %} <a href="{{ url_for('read_post', bo_table=bo_table, wr_id=write.wr_id) }}"> {% if write.is_notice %}<strong>{{ write.subject }}</strong>{% else %}{{ write.subject }}{% endif %} </a> {# 출력 #} <li class="basic_li"> <i class="fa fa-lock" aria-hidden="true"></i> <span class="blind">비밀글</span> <a href="..."> 4535 </a>
-
공백제어 기호가 있는 경우
latest\basic.html{# 입력 #} <li class="basic_li"> {% if write.icon_secret -%} <i class="fa fa-lock" aria-hidden="true"></i> <span class="blind">비밀글</span> {%- endif -%} <a href="{{ url_for('read_post', bo_table=bo_table, wr_id=write.wr_id) }}"> {% if write.is_notice %}<strong>{{ write.subject }}</strong>{% else %}{{ write.subject }}{% endif %} </a> {# 출력 #} <li class="basic_li"> <i class="fa fa-lock" aria-hidden="true"></i> <span class="blind">비밀글</span><a href="..."> 4535 </a>
2. 예시¶
{# base.html을 확장합니다. base.html을 이 HTML의 기본틀로 사용한다는 것입니다. #}
{% extends "base.html" %}
{# 템플릿에서 변수를 선언하는 것입니다. #}
{% set title = "회원정보 수정" if member.mb_id else "회원가입" %}
{# base.html에 block head에 이 스크립트를 넣으라는 것입니다. #}
{% block head %}
<script src="//t1.daumcdn.net/mapjsapi/bundle/postcode/prod/postcode.v2.js" async></script>
{% endblock %}
{# base_sub.html의 block title, base.html의 block subtitle의 영역을 "title" 변수로 치환하라는 것입니다. #}
{% block title %}{{ title }}{% endblock title %}
{% block subtitle %}{{ title }}{% endblock subtitle %}
{# base.html에 block content에 아래 내용으로 변경합니다. #}
{% block content %}
<div class="register">
{# 컨트롤러에서 전달된 form.action_url 출력 #}
<form id="fregisterform" name="fregisterform" action="{{ form.action_url }}"
onsubmit="return fregisterform_submit(this)" method="post" enctype="multipart/form-data" autocomplete="off">
...
<div id="register_form" class="form_01">
<div class="register_form_inner">
<h2>사이트 이용정보 입력</h2>
<ul>
...
<li>
<input type="hidden" name="old_email" value="">
{# 필터 함수로 처리된 회원 이메일 값 추가 #}
<input type="text" name="mb_email" value="{{ member.mb_email|default_if_none('') }}"
id="reg_mb_email" required class="frm_input email full_input required" size="70"
maxlength="100" placeholder="">
<label for="reg_mb_email" class="text_input">E-mail (필수)</label>
{# if문으로 조건부 출력 #}
{% if config.cf_use_email_certify %}
{% if is_register %}
<span class="frm_info">E-mail 로 발송된 내용을 확인한 후 인증하셔야 회원가입이 완료됩니다.</span>
{% else %}
<span class="frm_info">E-mail 주소를 변경하시면 다시 인증하셔야 합니다.</span>
{% endif %}
{% endif %}
</li>
...
</ul>
</div>
</div>
<div class="btn_confirm">
<a href="/" class="btn_close">취소</a>
<button type="submit" id="btn_submit" class="btn_submit" accesskey="s">{% if is_register %}회원가입{% else %}정보수정{% endif %}</button>
</div>
</form>
</div>
<script>
...
</script>
{% endblock %}