[Django] summernote, 에디터 사용하기
일반적으로 브라우저에서 지원해주는 Input 은 기능이 부족하다고 생각해서 다른 에디터를 사용하고자 하였습니다. 기존에 CKEditor
를 알고 있어서 사용해보았는데 결정적으로 file 에 대해서 부분 유료라서 어쩔 수 없이 다른 걸 찾아보았습니다. 그러던 중 summernote
를 찾게 되었고 적용이나 기능 면에서 더 효율적이라 생각되어 사용 방법을 정리해보았습니다.
WYSIWYG 란?
WYSIWYG 는 위지위그라고 읽으며 What You See Is What You Get 의 줄임말로서 '보는대로 얻는다' 라는 의미입니다. 다시 말해서 html 처럼 따로 css 문법을 적용해서 디자인을 편집하는 것이 아니라 문서 편집 과정에서 화면에 포맷된 낱말, 문장이 출력물과 동일하게 나오는 방식을 말합니다. summernote 도 WYSIWYG 의 일종이라고 볼 수 있습니다.
설치 및 기본 설정
summernote 를 CDN 방식으로 jQuery 를 이용해서 적용할 수 있지만 django app으로 개발이 되어있어서 쉽게 설치가 가능합니다. pip 를 통해 설치해주시고,
pip install django-summernote
INSTALLED_APPS
에 추가해줍니다.
# settings.py
INSTALLED_APPS = [
# 생략
'django_summernote',
]
다음으로는 전체 urls 에 summernote 를 사용할 수 있도록 include 해줍니다.
# config/urls.py
from django.contrib import admin
from django.urls import path,include
urlpatterns = [
path('admin/', admin.site.urls),
# 생략
path('summernote/', include('django_summernote.urls')),
]
이미지 업로드를 위해서 media 설정도 해주도록 합시다.
# settings.py
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media/')
summernote 적용
Post 라는 모델을 정의하고 이에 대한 예시를 살펴보겠습니다.
# models.py
from django.db import models
class Post(models.Model):
title = models.CharField(max_length=10)
content = models.TextField()
def __str__(self):
return self.title
summernote 를 적용하기 위해서는 forms.py 를 사용합니다. forms 이 낯설다면 이전 글을 참고해주시길 바랍니다.
# forms.py
from django import forms
from .models import Post
from django_summernote.widgets import SummernoteWidget
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ['title', 'content']
widgets = {
'content': SummernoteWidget(),
}
이제 form 을 띄워주기만 하면
<form action="" method="POST">
{% csrf_token %}
<table>
{{form.as_table}}
</table>
<input type="submit" value="제출">
</form>
다음과 같이 여러 기능을 가진 editor를 사용하실 수 있습니다.
글을 쓰고 저장을 하면 스타일이 html 로 바뀌어 있는 그대로 보여주게 됩니다.
따라서 templates filter, safe 를 사용해서 tag escape 를 방지할 수 있습니다.
<h1>메인 화면</h1>
<a href="{% url 'post_create' %}">글쓰기</a>
<hr>
{% for post in post_list %}
<h3>
<a href="{% url 'post_detail' post.id %}">
{{ post.title }}
</a>
</h3>
<div>{{ post.content | safe }}</div>
<a href="{% url 'post_edit' post.pk %}">수정</a>
<hr>
{% endfor %}
summernote custom
settings.py 에서 SUMMERNOTE_CONFIG
에 다음과 같은 설정사항이 default 로 있습니다. 이를 오버라이딩 하여 사용할 수 있습니다.
# settings.py
SUMMERNOTE_CONFIG = {
# Using SummernoteWidget - iframe mode, default
'iframe': True,
# Or, you can set it as False to use SummernoteInplaceWidget by default - no iframe mode
# In this case, you have to load Bootstrap/jQuery stuff by manually.
# Use this when you're already using Bootstraip/jQuery based themes.
'iframe': False,
# You can put custom Summernote settings
'summernote': {
# As an example, using Summernote Air-mode
'airMode': False,
# Change editor size
'width': '100%',
'height': '480',
# Use proper language setting automatically (default)
'lang': None,
# Or, set editor language/locale forcely
'lang': 'ko-KR',
...
# You can also add custom settings for external plugins
'print': {
'stylesheetUrl': '/some_static_folder/printable.css',
},
},
# Need authentication while uploading attachments.
'attachment_require_authentication': True,
# Set `upload_to` function for attachments.
'attachment_upload_to': my_custom_upload_to_func(),
# Set custom storage class for attachments.
'attachment_storage_class': 'my.custom.storage.class.name',
# Set custom model for attachments (default: 'django_summernote.Attachment')
'attachment_model': 'my.custom.attachment.model', # must inherit 'django_summernote.AbstractAttachment'
# You can disable attachment feature.
'disable_attachment': False,
# Set `True` to return attachment paths in absolute URIs.
'attachment_absolute_uri': False,
# You can add custom css/js for SummernoteWidget.
'css': (
),
'js': (
),
# You can also add custom css/js for SummernoteInplaceWidget.
# !!! Be sure to put {{ form.media }} in template before initiate summernote.
'css_for_inplace': (
),
'js_for_inplace': (
),
# Codemirror as codeview
# If any codemirror settings are defined, it will include codemirror files automatically.
'css': (
'//cdnjs.cloudflare.com/ajax/libs/codemirror/5.29.0/theme/monokai.min.css',
),
'codemirror': {
'mode': 'htmlmixed',
'lineNumbers': 'true',
# You have to include theme file in 'css' or 'css_for_inplace' before using it.
'theme': 'monokai',
},
# Lazy initialize
# If you want to initialize summernote at the bottom of page, set this as True
# and call `initSummernote()` on your page.
'lazy': True,
# To use external plugins,
# Include them within `css` and `js`.
'js': {
'/some_static_folder/summernote-ext-print.js',
'//somewhere_in_internet/summernote-plugin-name.js',
},
}
혹은 다른 방법으로 forms.py 에서도 이를 바꿔 줄 수 있습니다.
# forms.py
from django import forms
from . models import Post
from django_summernote.widgets import SummernoteWidget
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ['title', 'content']
widgets = {
'content': SummernoteWidget(attrs={'summernote': {'width': '100%', 'height': '400px'}}),
}
Django 3 부터 변경 사항
django 3 부터 클릭재킹에 대한 기본 설정이 변경되어서 추가적인 설정이 필요해졌습니다.
X_FRAME_OPTIONS 값을 변경하여 summernote의 로드를 허용할 수 있습니다.
# config/settings.py
X_FRAME_OPTIONS = 'SAMEORIGIN'