클라이언트 사이드 동작 확장하기¶
많은 종류의 일반적인 사용자 정의는 자바스크립트에 손대지 않고도 수행할 수 있지만, 활용하거나 사용자 정의하려는 클라이언트 측 상호 작용의 부분에 따라 React, Stimulus 또는 일반(vanilla) JS를 사용해야 할 수도 있습니다.
React는 사이드바, 댓글 시스템, Draftail 리치 텍스트 편집기와 같이 Wagtail의 더 복잡한 부분에 사용됩니다. 기본적인 자바스크립트 기반 상호 작용을 위해 Wagtail은 Stimulus로 마이그레이션하고 있습니다.
요소에 사용자 정의 동작을 추가하기 위해 이러한 라이브러리를 알거나 사용할 필요는 없으며, 많은 경우 간단한 자바스크립트로도 충분하지만, 더 복잡한 사용 사례에는 Stimulus가 권장되는 접근 방식입니다.
이러한 라이브러리를 기반으로 구축된 많은 사용자 정의를 위해 사용자 정의 Wagtail 설치에 Node.js 도구를 실행할 필요는 없지만, 패키지 빌드와 같은 일부 경우에는 더 복잡한 개발을 더 쉽게 만들 수 있습니다.
참고
jQuery 및 문서화되지 않은 jQuery 플러그인은 향후 Wagtail 버전에서 제거될 예정이므로 사용을 피하세요.
사용자 정의 자바스크립트 추가하기¶
Wagtail의 관리자 인터페이스 내에서 자바스크립트를 추가하는 몇 가지 방법이 있습니다.
가장 간단한 방법은 훅을 통해 전역 자바스크립트 파일을 추가하는 것입니다. insert_editor_js 및 insert_global_admin_js를 참조하세요.
특정 위젯이 사용될 때 추가되는 자바스크립트의 경우, 내부 Media 클래스를 추가하여 위젯이 사용될 때 파일이 로드되도록 할 수 있습니다. 양식 Media 클래스에 대한 Django의 문서를 참조하세요.
비슷한 방식으로 Wagtail의 템플릿 컴포넌트는 렌더링될 때 스크립트를 추가하기 위해 media 속성 또는 Media 클래스를 제공합니다.
이렇게 하면 핵심 자바스크립트 관리자 파일이 이미 로드된 후 추가된 파일이 관리자에서 사용되도록 할 수 있습니다.
DOM 이벤트로 확장하기¶
클라이언트 측 사용자 정의나 새 구성 요소 채택에 접근할 때, 먼저 구현을 간단하게 유지하려고 노력하세요. 목표를 달성하기 위해 Stimulus, React, 자바스크립트 모듈 또는 빌드 시스템에 대한 지식이 필요하지 않을 수 있습니다.
브라우저에 동작을 연결하는 가장 간단한 방법은 DOM 이벤트와 일반(vanilla) 자바스크립트를 통하는 것입니다.
Wagtail의 사용자 정의 DOM 이벤트¶
Wagtail은 사용자 정의 DOM 이벤트를 수신하거나 전달하여 일부 사용자 정의 동작을 지원합니다.
업로드 시 이미지 제목 생성 참조.
업로드 시 문서 제목 생성 참조.
Stimulus로 확장하기¶
Wagtail은 관리자 인터페이스 내에서 가벼운 클라이언트 측 상호 작용이나 사용자 정의 자바스크립트 위젯을 제공하는 방법으로 Stimulus를 사용합니다.
Stimulus 사용의 주요 이점은 모달, InlinePanel 또는 StreamField 패널과 같이 위젯이 동적으로 나타날 때 코드가 수동 초기화의 필요성을 피할 수 있다는 것입니다.
Stimulus 핸드북은 Stimulus를 사용하고 이해하는 방법에 대한 최고의 자료입니다.
사용자 정의 Stimulus 컨트롤러 추가하기¶
Wagtail은 Stimulus를 사용하기 위해 두 개의 클라이언트 측 전역 변수를 노출합니다.
window.wagtail.app핵심 관리자 Stimulus 애플리케이션 인스턴스.window.StimulusModule@hotwired/stimulus에서 내보낸 Stimulus 모듈.
먼저, 자바스크립트 클래스 상속을 사용하여 기본 window.StimulusModule.Controller 를 확장하는 사용자 정의 Stimulus 컨트롤러를 만드세요. 빌드 도구를 사용하는 경우 import { Controller } from '@hotwired/stimulus'; 를 통해 기본 컨트롤러를 가져올 수 있습니다.
사용자 정의 컨트롤러를 만든 후에는 window.wagtail.app.register 메서드를 통해 Stimulus 컨트롤러를 수동으로 등록해야 합니다.
간단한 컨트롤러 예제¶
먼저, Wagtail 관리자 내 어딘가에 나타나도록 HTML을 만듭니다.
<!-- 콘솔에 'My controller has connected: hi'를 기록합니다. -->
<div data-controller="my-controller">Hi</div>
<!-- 콘솔에 'My controller has connected: hello'를 span 요소와 함께 기록합니다. -->
<div data-controller="my-controller">
Hello <span data-my-controller-target="label"></span>
</div>
둘째, 컨트롤러 코드를 포함할 자바스크립트 파일을 만듭니다. 이 컨트롤러는 connect 시 간단한 메시지를 기록하는데, 이는 컨트롤러가 생성되고 일치하는 data-controller 속성을 가진 HTML 요소에 연결되었을 때 한 번 발생합니다.
// myapp/static/js/example.js
class MyController extends window.StimulusModule.Controller {
static targets = ['label'];
connect() {
console.log(
'My controller has connected:',
this.element.innerText,
this.labelTargets,
);
}
}
window.wagtail.app.register('my-controller', MyController);
마지막으로, 훅을 사용하여 자바스크립트 파일을 Wagtail 관리자에 로드합니다.
# myapp/wagtail_hooks.py
from django.templatetags.static import static
from django.utils.html import format_html
from wagtail import hooks
@hooks.register('insert_global_admin_js')
def global_admin_js():
return format_html(
f'<script src="{static("js/example.js")}"></script>',
)
이제 HTML을 표시하던 관리자를 새로고침하면 콘솔에 두 개의 로그가 표시되는 것을 볼 수 있습니다.
더 복잡한 컨트롤러 예제¶
이제 입력된 단어 수를 보여주는 작은 output 요소를 제어되는 input 요소 옆에 추가하는 WordCountController 를 만들겠습니다.
// myapp/static/js/word-count-controller.js
class WordCountController extends window.StimulusModule.Controller {
static values = { max: { default: 10, type: Number } };
connect() {
this.setupOutput();
this.updateCount();
}
setupOutput() {
if (this.output) return;
const template = document.createElement('template');
template.innerHTML = `<output name='word-count' for='${this.element.id}' class='output-label'></output>`;
const output = template.content.firstChild;
this.element.insertAdjacentElement('beforebegin', output);
this.output = output;
}
updateCount(event) {
const value = event ? event.target.value : this.element.value;
const words = (value || '').split(' ');
this.output.textContent = `${words.length} / ${this.maxValue} words`;
}
disconnect() {
this.output && this.output.remove();
}
}
window.wagtail.app.register('word-count', WordCountController);
이렇게 하면 데이터 속성 data-word-count-max-value 가 이 컨트롤러의 ‘구성’을 결정하고, 데이터 속성 작업이 출력 요소 업데이트의 ‘트리거’를 결정하게 됩니다.
# models.py
from django import forms
from wagtail.admin.panels import FieldPanel
from wagtail.models import Page
class BlogPage(Page):
# ...
content_panels = Page.content_panels + [
FieldPanel('subtitle', classname="full"),
FieldPanel(
'introduction',
classname="full",
widget=forms.TextInput(
attrs={
'data-controller': 'word-count',
# 속성을 사용하여 최대 수를 결정할 수 있도록 허용
# 여기서 파이썬 값을 사용할 수 있으며, 장고가 문자열 변환을 처리합니다 (해당되는 경우 이스케이프 포함)
'data-word-count-max-value': 5,
# 데이터 액션으로 카운트를 업데이트할 시점 결정
# (예: 'blur->word-count#updateCount'는 필드가 포커스를 잃을 때만 업데이트됨)
'data-action': 'word-count#updateCount paste->word-count#updateCount',
}
)
),
#...
다음 코드 스니펫은 향후 컨트롤러를 위해 추가 스크립트를 추가하도록 설정된 insert_editor_js 훅 사용법의 더 고급 버전을 보여줍니다.
# wagtail_hooks.py
from django.utils.html import format_html_join
from django.templatetags.static import static
from wagtail import hooks
@hooks.register('insert_editor_js')
def editor_js():
# 필요에 따라 더 많은 컨트롤러 코드 추가
js_files = ['js/word-count-controller.js',]
return format_html_join('\n', '<script src="{0}"></script>',
((static(filename),) for filename in js_files)
)
이제 블로그 페이지에서 소개 필드에 사용된 단어 수와 최대 단어 수를 보여주는 작은 output 요소가 표시되는 것을 볼 수 있습니다.
더 복잡한 위젯 예제¶
더 복잡한 위젯의 경우, 이제 위젯이 렌더링된 HTML에 나타날 때마다, 초기 로드 시 또는 동적으로 인라인 script 요소 없이 추가 라이브러리를 통합할 수 있습니다.
이 예에서는 사용자 정의 위젯 옵션을 지원하는 Coloris 자바스크립트 라이브러리를 사용하여 색상 선택기 위젯을 빌드합니다.
먼저, Wagtail이 FieldPanel 및 FieldBlock 에 대해 지원하는 Django 위젯 시스템을 기반으로 HTML부터 시작하겠습니다. build_attrs 메서드를 사용하여 컨트롤러에 전달되는 일반적인 데이터 구조를 지원하기 위해 적절한 Stimulus 데이터 속성을 구성합니다.
복잡한 값(이 경우 문자열 목록)에 대해 json.dumps 를 사용하고 있음을 확인하세요. Django는 렌더링될 때 이러한 값을 자동으로 이스케이프하여 안전하지 않은 클라이언트 측 코드의 일반적인 원인을 피합니다.
# myapp/widgets.py
import json
from django.forms import Media, TextInput
from django.utils.translation import gettext as _
class ColorWidget(TextInput):
"""
https://coloris.js.org/ 참조
"""
def __init__(self, attrs=None, swatches=[], theme='large'):
self.swatches = swatches
self.theme = theme
super().__init__(attrs=attrs);
def build_attrs(self, *args, **kwargs):
attrs = super().build_attrs(*args, **kwargs)
attrs['data-controller'] = 'color'
attrs['data-color-theme-value'] = self.theme
attrs['data-color-swatches-value'] = json.dumps(swatches)
return attrs
@property
def media(self):
return Media(
js=[
# UI 라이브러리 로드
"https://cdn.jsdelivr.net/gh/mdbassit/Coloris@latest/dist/coloris.min.js",
# 컨트롤러 JS 로드
"js/color-controller.js",
],
css={"all": ["https://cdn.jsdelivr.net/gh/mdbassit/Coloris@latest/dist/coloris.min.css"]},
)
Stimulus 컨트롤러의 경우, this.element.id 를 통해 제어되는 요소에 대한 참조를 포함하여 값을 자바스크립트 라이브러리로 전달합니다.
// myapp/static/js/color-controller.js
class ColorController extends window.StimulusModule.Controller {
static values = { swatches: Array, theme: String };
connect() {
// 생성
Coloris({ el: `#${this.element.id}` });
// 초기 생성 후 옵션 설정
setTimeout(() => {
Coloris({ swatches: this.swatchesValue, theme: this.themeValue });
});
}
}
window.wagtail.app.register('color', ColorController);
이제 이 위젯을 모든 FieldPanel 또는 StreamFields의 모든 FieldBlock 에서 사용할 수 있으며, 필드의 요소에 자바스크립트를 자동으로 인스턴스화합니다.
# blocks.py
# ... 기타 가져오기
from django import forms
from wagtail.blocks import FieldBlock
from .widgets import ColorWidget
class ColorBlock(FieldBlock):
def __init__(self, *args, **kwargs):
swatches = kwargs.pop('swatches', [])
theme = kwargs.pop('theme', 'large')
self.field = forms.CharField(widget=ColorWidget(swatches=swatches, theme=theme))
super().__init__(*args, **kwargs)
# models.py
# ... 기타 가져오기
from django import forms
from wagtail.admin.panels import FieldPanel
from .blocks import ColorBlock
from .widgets import ColorWidget
BREAD_COLOR_PALETTE = ["#CFAC89", "#C68C5F", "#C47647", "#98644F", "#42332E"]
class BreadPage(Page):
body = StreamField([
# ...
('color', ColorBlock(swatches=BREAD_COLOR_PALETTE)),
# ...
], use_json_field=True)
color = models.CharField(blank=True, max_length=50)
# ... 기타 필드
content_panels = Page.content_panels + [
# ... 기타 패널
FieldPanel("body"),
FieldPanel("color", widget=ColorWidget(swatches=BREAD_COLOR_PALETTE)),
]
빌드 시스템 사용하기¶
빌드 출력이 ES6/ES2015 이상인지 확인해야 합니다. window.StimulusModule 에서 노출된 전역 모듈을 사용하거나 npm 모듈 @hotwired/stimulus 를 사용하여 직접 제공할 수 있습니다.
// myapp/static/js/word-count-controller.js
import { Controller } from '@hotwired/stimulus';
class WordCountController extends Controller {
// ... 위와 동일
}
window.wagtail.app.register('word-count', WordCountController);
자바스크립트 출력에 Stimulus를 번들로 포함하는 것을 피하고 전역을 외부/별칭 모듈로 처리하고 싶을 수 있습니다. 이 작업을 수행하는 방법에 대한 지침은 빌드 시스템 설명서를 참조하세요.
React로 확장하기¶
React 컴포넌트를 사용자 정의하거나 확장하려면 React뿐만 아니라 다른 관련 라이브러리도 사용해야 할 수 있습니다.
이를 더 쉽게 하기 위해 Wagtail은 React 관련 종속성을 관리자 내에서 전역 변수로 노출합니다. 사용 가능한 패키지는 다음과 같습니다.
// 'focus-trap-react'
window.FocusTrapReact;
// 'react'
window.React;
// 'react-dom'
window.ReactDOM;
// 'react-transition-group/CSSTransitionGroup'
window.CSSTransitionGroup;
Wagtail은 또한 자체 React 컴포넌트 중 일부를 노출합니다. 다음을 재사용할 수 있습니다.
window.wagtail.components.Icon;
window.wagtail.components.Portal;
리치 텍스트 편집기를 포함하는 페이지는 다음에도 액세스할 수 있습니다.
// 'draft-js'
window.DraftJS;
// 'draftail'
window.Draftail;
// Wagtail의 Draftail 관련 API 및 컴포넌트.
window.draftail;
window.draftail.DraftUtils;
window.draftail.ModalWorkflowSource;
window.draftail.ImageModalWorkflowSource;
window.draftail.EmbedModalWorkflowSource;
window.draftail.LinkModalWorkflowSource;
window.draftail.DocumentModalWorkflowSource;
window.draftail.Tooltip;
window.draftail.TooltipEntity;