diff --git a/src/main/resources/static/js/common.js b/src/main/resources/static/js/common.js
index f423d22..425ae80 100644
--- a/src/main/resources/static/js/common.js
+++ b/src/main/resources/static/js/common.js
@@ -168,3 +168,204 @@ class FormValidator {
}
}
}
+
+/**
+ * Summernote 에디터 공통 초기화 함수
+ * - 이미지 붙여넣기 시 data-uri(Base64) 방식으로 처리
+ * - 이미지 리사이징 지원
+ * @param {string} selector - 에디터 selector (예: '#contents')
+ * @param {object} customOptions - 추가 옵션 (선택사항)
+ */
+function initSummernote(selector, customOptions) {
+ customOptions = customOptions || {};
+
+ // 기본 설정
+ var defaultOptions = {
+ placeholder: customOptions.placeholder || '여기에 내용을 입력하세요.',
+ height: customOptions.height || 300,
+ lang: 'ko-KR',
+ toolbar: customOptions.toolbar || [
+ ['style', ['style']],
+ ['font', ['bold', 'underline', 'clear']],
+ ['color', ['color']],
+ ['para', ['ul', 'ol', 'paragraph']],
+ ['table', ['table']],
+ ['insert', ['link', 'picture']],
+ ['view', ['fullscreen', 'codeview', 'help']]
+ ],
+ // 이미지 팝오버 설정 - 리사이징 지원
+ popover: {
+ image: [
+ ['resize', ['resizeFull', 'resizeHalf', 'resizeQuarter', 'resizeNone']],
+ ['float', ['floatLeft', 'floatRight', 'floatNone']],
+ ['remove', ['removeMedia']]
+ ]
+ },
+ callbacks: {
+ onInit: function() {
+ var $editable = $(selector).next('.note-editor').find('.note-editable');
+ $editable.addClass('editor-content');
+ $editable.on('keydown', function(e) {
+ if (e.keyCode === 8) {
+ e.stopPropagation();
+ }
+ });
+
+ // 커스텀 onInit 콜백 호출
+ if (customOptions.callbacks && customOptions.callbacks.onInit) {
+ customOptions.callbacks.onInit.call(this);
+ }
+ },
+ // 이미지 업로드 처리 - data-uri(Base64) 방식
+ onImageUpload: function(files) {
+ for (var i = 0; i < files.length; i++) {
+ insertImageAsDataUri(files[i], $(selector));
+ }
+ },
+ // 붙여넣기 시 이미지 처리 (MS Office 등에서 복사한 이미지 포함)
+ onPaste: function(e) {
+ var clipboardData = e.originalEvent.clipboardData;
+ if (!clipboardData || !clipboardData.items) return;
+
+ var items = clipboardData.items;
+ var imageFile = null;
+
+ // 1. 먼저 순수 이미지 파일이 있는지 확인
+ for (var i = 0; i < items.length; i++) {
+ if (items[i].type.indexOf('image') !== -1) {
+ imageFile = items[i].getAsFile();
+ break;
+ }
+ }
+
+ // 2. 이미지 파일이 있으면 data-uri로 변환하여 삽입
+ if (imageFile) {
+ e.preventDefault();
+ e.stopPropagation();
+ insertImageAsDataUri(imageFile, $(selector));
+ return;
+ }
+
+ // 3. HTML 붙여넣기인 경우 (MS Office 등) - 이미지 태그 정리
+ var htmlData = clipboardData.getData('text/html');
+ if (htmlData && htmlData.indexOf(' maxSize) {
+ alert('이미지 크기는 5MB를 초과할 수 없습니다.');
+ return;
+ }
+
+ // 이미지 타입 확인
+ if (!file.type.match(/image\/(jpeg|jpg|png|gif|bmp|webp)/i)) {
+ alert('지원하지 않는 이미지 형식입니다. (jpeg, png, gif, bmp, webp만 지원)');
+ return;
+ }
+
+ var reader = new FileReader();
+ reader.onload = function(e) {
+ var dataUri = e.target.result;
+ $editor.summernote('insertImage', dataUri, function($image) {
+ // 이미지 스타일 설정
+ $image.css({
+ 'max-width': '100%',
+ 'height': 'auto'
+ });
+ $image.attr('data-filename', file.name);
+ });
+ };
+ reader.onerror = function() {
+ alert('이미지 읽기에 실패했습니다.');
+ };
+ reader.readAsDataURL(file);
+}
+
+/**
+ * 붙여넣기된 HTML 정리 (MS Office 등에서 복사한 내용)
+ * - 불필요한 Office 속성 제거
+ * - 이미지 태그 정리 (alt, v:shapes 등 제거)
+ * @param {string} html - 원본 HTML
+ * @returns {string} 정리된 HTML
+ */
+function cleanPastedHtml(html) {
+ // 임시 div에서 HTML 파싱
+ var $temp = $('