$(document).ready(function () {
'use strict';
// =========================
// Config & Selectors
// =========================
const API_BASE_URL = `${typeof hostURL !== 'undefined' ? hostURL : ''}/ajax/api.php`;
const SELECTOR = {
viewList: '#viewList',
viewWrite: '#viewWrite',
viewDetail: '#viewDetail',
postList: '#postList',
writeForm: '#writeForm',
categorySelect: '#categorySelect',
boardPagination: '#boardPagination',
boardCategory: '#board_catagory'
};
const urlParams = new URLSearchParams(window.location.search);
const modeParam = urlParams.get('mode');
const idParam = urlParams.get('id');
// 현제 페이지 파일명에 manager가 포함되어 있으면 current_page_manager 변수에 파일명을 저장하고 아니면 current_page 변수에 파일명을 저장한다.
const currentPageFileName = window.location.pathname.split('/').pop();
if (currentPageFileName.includes('manager')) {
var current_page_manager = currentPageFileName;
var current_page = currentPageFileName.replace('_manager', '');
} else {
var current_page = currentPageFileName;
var current_page_manager = currentPageFileName.replace('.html', '_manager.html');
}
// alert(`current_page_manager: ${current_page_manager}, current_page: ${current_page}`);
let currentDetailRaw = null;
const PAGE_SIZE = 20;
let currentPostData = [];
let currentPage = 1;
var rowListCount = 20;
var rowImageCount = 4;
// 파일 업로드 관련 상태
let dt = new DataTransfer();
let existingFiles = [];
let deletedFileIds = [];
// DOM 캐싱
const $dropZone = $('#file-upload-box');
const $fileInput = $('#fileInput');
const $fileListContainer = $('#fileListContainer');
const $fileItemsList = $('#fileItemsList');
const $fileCount = $('#fileCount');
const $fileTotalSize = $('#fileTotalSize');
// =========================
// Init & Event Binding
// =========================
initPage();
bindEvents();
function initPage() {
if ($(SELECTOR.viewList).length) {
loadPosts();
}
if ($(SELECTOR.viewWrite).length || $(SELECTOR.viewDetail).length) {
if (modeParam === 'detail' && idParam) {
showPostDetail(idParam);
} else if (modeParam === 'edit' && idParam) {
loadCategoryOptions().then(() => loadPostForEdit(idParam));
} else {
loadCategoryOptions().then(() => {
resetWriteForm();
showWriteView();
});
}
}
}
// =========================
// Init & Event Binding
// =========================
function bindEvents() {
$('#btnGoWrite').on('click', () => { location.href = `${current_page_manager}?mode=write`; });
$('#btnSearch').on('click', onClickSearch);
$('#searchForm').on('submit', onSubmitSearch);
// 체크박스 & 선택 삭제
$('#checkAll').on('change', onChangeCheckAll);
$(document).on('change', '.row-check', onChangeRowCheck);
$('#btnDeleteSelected').on('click', onClickDeleteSelected);
// 분류 추가/설정 모달
$('#categoryForm').on('submit', onSubmitCategoryForm);
$('#boardSetupModal').on('show.bs.modal', loadboardSetup);
$('#btnSaveBoardSetup').on('click', onClickSaveBoardSetup);
// 이벤트 위임
$(document)
.on('click', '#postList .title-cell', onClickTitleCell)
.on('click', '#board_catagory .category-pill-btn', onClickCategory)
.on('click', '#boardPagination .page-btn:not(.disabled):not(.active)', onClickPageBtn)
.on('click', '#btnBackToList', () => { location.href = `${current_page}`; })
.on('click', '#btnDeleteCurrentPost', onClickDeleteCurrent)
.on('click', '#btnEditCurrentPost', onClickEditCurrent);
// 작성 폼 관련
$('#btnCancelWrite, #btnCancelWriteBottom').on('click', onClickCancelWrite);
$(SELECTOR.writeForm).on('submit', onSubmitWriteForm);
// 드래그 앤 드롭 & 파일 인풋
$fileInput.on('change', function () {
if (this.files?.length) appendFiles(this.files);
});
$dropZone
.on('dragover dragenter', (e) => {
e.preventDefault();
e.stopPropagation();
$dropZone.addClass('is-dragover');
})
.on('dragleave dragend drop', (e) => {
e.preventDefault();
e.stopPropagation();
$dropZone.removeClass('is-dragover');
})
.on('drop', (e) => {
const droppedFiles = e.originalEvent.dataTransfer.files;
if (droppedFiles?.length) appendFiles(droppedFiles);
});
$fileItemsList
.on('click', '.btn-remove-file', onRemoveNewFile)
.on('click', '.btn-remove-existing-file', onRemoveExistingFile);
$('#btnClearAllFiles').on('click', onClearAllFiles);
}
// =========================
// Core & Page Init
// =========================
function getBoardName() {
return $('#board_name').val() || 'post_notice';
}
// =========================
// Util Functions
// =========================
function escapeHtml(str) {
if (!str) return '';
return String(str)
.replace(/&/g, '&')
.replace(//g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
function formatBytes(bytes) {
if (!bytes || bytes === 0) return '0 B';
const k = 1024;
const sizes = ['B', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}
function resolveFileUrl(rawPath) {
if (!rawPath) return '';
if (/^(https?:)?\/\//i.test(rawPath)) return rawPath;
const base = (typeof hostURL !== 'undefined' ? hostURL : '').replace(/\/+$/, '');
return `${base}/${String(rawPath).replace(/^\/+/, '')}`;
}
// =========================
// File Management
// =========================
function updateFileListUI() {
const newFiles = dt.files;
const totalCount = existingFiles.length + newFiles.length;
if (totalCount === 0) {
$fileItemsList.empty();
$fileListContainer.addClass('d-none');
$fileInput[0].files = dt.files;
return;
}
$fileListContainer.removeClass('d-none');
$fileCount.text(totalCount);
let totalBytes = 0;
let htmlBuffer = '';
// 1. 기존 파일 렌더링
existingFiles.forEach((file, index) => {
const size = Number(file.file_size || file.size || 0);
totalBytes += size;
const fileName = file.origin_name || file.original_name || file.name || '첨부파일';
htmlBuffer += `
${escapeHtml(fileName)}
기존 파일
(${formatBytes(size)})
`;
});
// 2. 신규 파일 렌더링
Array.from(newFiles).forEach((file, index) => {
totalBytes += file.size;
htmlBuffer += `
${escapeHtml(file.name)}
신규
(${formatBytes(file.size)})
`;
});
$fileItemsList.html(htmlBuffer);
$fileTotalSize.text(formatBytes(totalBytes));
$fileInput[0].files = dt.files;
}
function appendFiles(newFiles) {
const existingMap = new Set(Array.from(dt.files).map(f => `${f.name}_${f.size}`));
for (const file of newFiles) {
if (!existingMap.has(`${file.name}_${file.size}`)) {
dt.items.add(file);
existingMap.add(`${file.name}_${file.size}`);
}
}
updateFileListUI();
}
function onRemoveNewFile(e) {
e.stopPropagation();
const removeIdx = $(this).data('index');
const newDt = new DataTransfer();
Array.from(dt.files).forEach((file, idx) => {
if (idx !== removeIdx) newDt.items.add(file);
});
dt = newDt;
updateFileListUI();
}
function onRemoveExistingFile(e) {
e.stopPropagation();
const removeIdx = $(this).data('index');
const removedItem = existingFiles.splice(removeIdx, 1)[0];
const fileId = removedItem?.attachment_id || removedItem?.file_id || removedItem?.id;
if (fileId) deletedFileIds.push(fileId);
updateFileListUI();
}
function onClearAllFiles(e) {
e.stopPropagation();
existingFiles.forEach(f => {
const fileId = f.attachment_id || f.file_id || f.id;
if (fileId) deletedFileIds.push(fileId);
});
existingFiles = [];
dt = new DataTransfer();
updateFileListUI();
}
function loadExistingFilesToDataTransfer(attachments = []) {
existingFiles = attachments.map(item => ({
...item,
fileUrl: resolveFileUrl(item.download_url || item.file_path || '')
}));
deletedFileIds = [];
updateFileListUI();
}
// =========================
// Category & Settings Logic
// =========================
function loadCategoryOptions() {
return $.ajax({
url: `${API_BASE_URL}?action=category_list&board_name=${encodeURIComponent(getBoardName())}`,
method: 'GET',
dataType: 'json'
}).then(res => {
const list = res.boardInfo?.category_list;
const categories = (typeof list === 'string' ? list.split(',') : (list || []))
.map(c => c.trim())
.filter(Boolean);
const $select = $(SELECTOR.categorySelect);
if ($select.length) {
let options = '전체 ';
categories.forEach(cat => {
options += `${escapeHtml(cat)} `;
});
$select.html(options);
}
return categories;
}).catch(err => {
console.error('카테고리 로드 실패:', err);
return [];
});
}
function onClickSaveBoardSetup() {
$.ajax({
url: `${API_BASE_URL}?action=save_board_setup`,
method: 'POST',
data: $('#boardSetupForm').serialize(),
dataType: 'json',
success(res) {
if (!res.success) {
alert(res.message || '게시판 설정 저장에 실패했습니다.');
return;
}
alert('게시판 설정이 저장되었습니다.');
$('#boardSetupModal').modal('hide');
},
error: () => alert('게시판 설정 저장 중 통신 오류가 발생했습니다.')
});
}
function loadboardSetup() {
$.ajax({
url: `${API_BASE_URL}?action=board_setup&board_name=${encodeURIComponent(getBoardName())}`,
method: 'GET',
dataType: 'json',
success(res) {
if (!res.success || !res.boardInfo) return;
const info = res.boardInfo;
const $form = $('#boardSetupForm');
['board_id', 'board_name', 'category_list', 'function_settings', 'display_settings', 'file_attachment_policy'].forEach(name => {
$form.find(`input[name="${name}"]`).val(info[name] || '');
});
['is_active', 'use_comment_yn', 'use_category_yn', 'file_upload_yn'].forEach(name => {
$form.find(`input[name="${name}"]`).prop('checked', info[name] === 'Y');
});
},
error: () => alert('게시판 설정 조회 중 오류가 발생했습니다.')
});
}
function onSubmitCategoryForm(e) {
e.preventDefault();
$.ajax({
url: `${API_BASE_URL}?action=category_update`,
method: 'POST',
data: {
board_name: getBoardName(),
category_list: $('#newCategoryName').val() || ''
},
dataType: 'json',
success(res) {
if (res.success) {
alert('분류가 업데이트되었습니다.');
location.reload();
} else {
alert(res.message || '분류 업데이트에 실패했습니다.');
}
},
error: () => alert('서버와 통신 중 오류가 발생했습니다.')
});
}
// =========================
// View Switching & Post Edit
// =========================
function showWriteView() {
$(SELECTOR.viewDetail).hide();
$(SELECTOR.viewWrite).show();
$('#wr_board_name').val(getBoardName());
window.scrollTo(0, 0);
}
function showDetailView() {
$(SELECTOR.viewWrite).hide();
$(SELECTOR.viewDetail).show();
window.scrollTo(0, 0);
}
function setWriteMode(mode, postId = '') {
const $form = $(SELECTOR.writeForm);
$form.attr({ 'data-mode': mode, 'data-post-id': postId });
$('#write_postid').val(postId);
const isEdit = mode === 'edit';
$('#write_title').text(isEdit ? '게시글 수정' : '게시글 작성');
$('#btnSubmit').html(` ${isEdit ? '수정' : '저장'} 완료`);
}
function resetWriteForm() {
if (typeof $.fn.summernote !== 'undefined' && $('#post_content').length) {
initSummernote();
}
const $form = $(SELECTOR.writeForm);
if ($form.length) {
$form[0].reset();
const $content = $('#post_content');
$content.data('summernote') ? $content.summernote('reset') : $content.val('');
}
setWriteMode('create', '');
}
async function populateEditForm(data, postId) {
resetWriteForm();
setWriteMode('edit', postId);
await loadCategoryOptions();
$('input[name="title"]').val(data.title || '');
$('input[name="author"]').val(data.author || '');
const $content = $('#post_content');
const postContent = data.content || '';
if ($content.data('summernote')) {
$content.summernote('code', postContent);
} else {
$content.val(postContent);
}
if (data.category) {
$('#categorySelect').val(data.category);
}
loadExistingFilesToDataTransfer(data.attachments || []);
showWriteView();
}
// =========================
// List & Detail Logic
// =========================
// 게시글 목록 조회
function loadPosts(searchType, searchKeyword, category, page) {
// 검색/분류/페이지 상태를 보관해 페이지 전환 시 동일한 조건으로 재조회
const currentSearchType = searchKeyword ? (searchType || 'title') : null;
const currentSearchKeyword = searchKeyword || null;
const currentCategory = (category && category !== 'all') ? category : null;
const params = {
action: 'list',
board_name: getBoardName(),
page: page || 1,
page_size: PAGE_SIZE
};
if (currentSearchKeyword) {
params.search_type = currentSearchType;
params.search_keyword = currentSearchKeyword;
}
if (currentCategory) {
params.category = currentCategory;
}
$.ajax({
url: API_BASE_URL,
method: 'GET',
data: params,
dataType: 'json',
success(res) {
if (!res.success) {
alert(res.message || '목록 조회에 실패했습니다.');
return;
}
const currentPostData = res.data || [];
const currentPage = res.currentPage || params.page;
const totalPageCount = res.totalPages || 1;
const totalPostCount = res.totalCount || currentPostData.length;
renderPostList(currentPostData);
if (res.boardInfo) renderBoardInfo(res.boardInfo, category || 'all');
},
error: () => alert('목록 조회 중 통신 오류가 발생했습니다.')
});
}
function renderBoardInfo(boardInfo, activeCategory = 'all') {
const $categoryContainer = $(SELECTOR.boardCategory);
if (!$categoryContainer.length) return;
const categories = (boardInfo.category_list || '').split(',').map(c => c.trim()).filter(Boolean);
const counts = boardInfo.category_counts || {};
let buttons = `
전체
`;
categories.forEach(cat => {
const isActive = activeCategory === cat;
buttons += `
${escapeHtml(cat)}
${counts[cat] || 0}
`;
});
$categoryContainer.html(buttons);
}
function renderPostList(fullData, page) {
currentPostData = fullData || currentPostData || [];
const totalCount = currentPostData.length;
const totalPages = Math.max(1, Math.ceil(totalCount / PAGE_SIZE));
currentPage = Math.min(Math.max(1, page || currentPage || 1), totalPages);
const startIdx = (currentPage - 1) * PAGE_SIZE;
const data = currentPostData.slice(startIdx, startIdx + PAGE_SIZE);
const $list = $(SELECTOR.postList).empty();
$('#checkAll').prop('checked', false);
const isGallery = $('#board_table').hasClass('gallery-board');
if (!data.length) {
$list.html(isGallery
? ' 조회된 자료가 없습니다
'
: ' 조회된 자료가 없습니다 '
);
renderPagination(totalCount, currentPage, totalPages);
return;
}
if (isGallery) {
const cards = data.map(item => {
const category = escapeHtml(item.category || '전체');
const title = escapeHtml(item.title || '제목 없음');
const date = item.created_at ? item.created_at.substring(0, 10) : '-';
const thumb = item.first_image_attachment?.file_path
? resolveFileUrl(item.first_image_attachment.file_path)
: (item.content?.match(/ ]+src="([^">]+)"/i)?.[1] || '');
return `
${thumb ? '' : ' '}
${category}
${title}
${escapeHtml(item.author || '작성자 미상')}
${date}
${item.view_count || 0}
`;
}).join('');
$list.html(cards);
} else {
const rows = data.map(item => {
const attachBadge = item.attach_count > 0 ? ` ` : '';
const date = item.created_at ? item.created_at.substring(0, 10) : '-';
return `
${item.post_id}
${escapeHtml(item.category)}
${escapeHtml(item.title)} ${attachBadge}
${escapeHtml(item.author)}
${date}
${item.view_count || 0}
`;
}).join('');
$list.html(rows);
}
renderPagination(totalCount, currentPage, totalPages);
}
// 페이지 네비게이션 렌더링 함수
function renderPagination(totalCount, page, totalPages) {
const $nav =$(SELECTOR.boardPagination);
// 페이지 네비게이션이 필요 없는 경우 처리
if (!$nav.length || !totalCount || totalPages <= 1) {
$nav.empty();
return;
}
// 한 그룹에 표시할 페이지 수
const GROUP_SIZE = 5;
// 현재 그룹의 시작 페이지 번호 계산
const groupStart = Math.floor((page - 1) / GROUP_SIZE) * GROUP_SIZE + 1;
// 현재 그룹의 끝 페이지 번호 계산
const groupEnd = Math.min(groupStart + GROUP_SIZE - 1, totalPages);
// Bootstrap 표준 구조: li.page-item > a.page-link
let pageButtons = '';
for (let p = groupStart; p <= groupEnd; p++) {
const isActive = p === page ? ' active' : '';
pageButtons += `
${p}
`;
}
const isFirst = page === 1;
const isLast = page === totalPages;
$nav.html(`
Total ${totalCount} pages
`);
}
function showPostDetail(postId) {
if (!postId) return;
$.ajax({
url: `${API_BASE_URL}?action=detail&id=${postId}&board_name=${encodeURIComponent(getBoardName())}`,
method: 'GET',
dataType: 'json',
success(res) {
if (!res.success) {
alert(res.message || '게시글을 불러올 수 없습니다.');
location.href = `${current_page}`;
return;
}
const data = res.data;
currentDetailRaw = data;
$('#detailTitle').text(data.title || '');
$('#detailCategory').text(data.category || '');
$('#detailAuthor').text(data.author || '');
$('#detailDate').text(data.created_at ? data.created_at.substring(0, 10) : '-');
$('#detailViews').text(data.view_count || 0);
$('#detailPostId').text(postId);
$('#write_postid').val(postId);
$('#detailContent').html(data.content || '');
const attachments = data.attachments || [];
const $attachmentArea = $('#detailAttachmentArea');
const $attachmentList = $('#detailAttachments').empty();
if (attachments.length) {
const listItems = attachments.map(file => {
const fileName = file.origin_name || file.original_name || file.name || '첨부파일';
const fileUrl = resolveFileUrl(file.download_url || file.file_path || '');
const fileSize = file.size_formatted || (file.size ? `${(file.size / 1024).toFixed(1)} KB` : '');
return `
${fileSize ? `${escapeHtml(fileSize)} ` : ''}
`;
}).join('');
$attachmentList.html(listItems);
$attachmentArea.show();
} else {
$attachmentArea.hide();
}
showDetailView();
},
error: () => {
alert('게시글 조회 중 오류가 발생했습니다.');
location.href = `${current_page}`;
}
});
}
function loadPostForEdit(postId) {
$.ajax({
url: `${API_BASE_URL}?action=detail&id=${postId}&board_name=${encodeURIComponent(getBoardName())}`,
method: 'GET',
dataType: 'json',
success(res) {
if (!res.success) {
alert('게시글 정보를 가져오지 못했습니다.');
return;
}
currentDetailRaw = res.data;
populateEditForm(res.data, postId);
}
});
}
// =========================
// Action Events
// =========================
function onSubmitWriteForm(e) {
e.preventDefault();
const $content = $('#post_content');
if ($content.data('summernote')) {
$content.val($content.summernote('code'));
}
const postId = $('#write_postid').val();
const mode = postId ? 'update' : 'create';
const formData = new FormData(this);
formData.set('board_name', getBoardName());
if (mode === 'update') {
formData.set('write_postid', postId);
formData.set('post_id', postId);
formData.set('id', postId);
if (deletedFileIds.length) {
formData.set('delete_attach_ids', JSON.stringify(deletedFileIds));
}
}
formData.delete('attachments[]');
Array.from(dt.files).forEach(file => formData.append('attachments[]', file));
const $submitBtn = $('#btnSubmit')
.prop('disabled', true)
.html(' 처리 중...');
$.ajax({
url: `${API_BASE_URL}?action=${mode}`,
method: 'POST',
data: formData,
processData: false,
contentType: false,
dataType: 'json',
success(res) {
if (res.success) {
alert('저장되었습니다.');
location.href = `${current_page}`;
} else {
alert(res.message || '게시글 저장에 실패했습니다.');
}
},
error: () => alert('서버와 통신 중 오류가 발생했습니다.'),
complete: () => $submitBtn.prop('disabled', false)
});
}
function deletePosts(ids, onSuccess) {
$.ajax({
url: `${API_BASE_URL}?action=delete&board_name=${encodeURIComponent(getBoardName())}`,
method: 'POST',
contentType: 'application/json',
data: JSON.stringify({ ids }),
dataType: 'json',
success(res) {
if (res.success) {
onSuccess?.();
} else {
alert(res.message || '삭제에 실패했습니다.');
}
},
error: () => alert('통신 중 오류가 발생했습니다.')
});
}
function onClickDeleteSelected() {
const selected = $('.row-check:checked').map(function () { return $(this).val(); }).get();
if (!selected.length) {
alert('삭제할 게시글을 하나 이상 선택해주세요.');
return;
}
if (!confirm(`선택한 ${selected.length}건의 게시글을 삭제하시겠습니까?`)) return;
deletePosts(selected, () => loadPosts());
}
function onClickDeleteCurrent() {
const postId = $('#detailPostId').text().trim();
if (!postId || !confirm('이 게시글을 삭제하시겠습니까?')) return;
deletePosts([postId], () => {
alert('삭제되었습니다.');
location.href = `${current_page}`;
});
}
function onClickEditCurrent() {
const postId = $('#detailPostId').text().trim();
if (!postId) return;
currentDetailRaw ? populateEditForm(currentDetailRaw, postId) : loadPostForEdit(postId);
}
function onClickCancelWrite() {
const titleVal = $('input[name="title"]').val()?.trim();
if (titleVal && !confirm('작성 중인 내용이 저장되지 않습니다. 취소하시겠습니까?')) {
return;
}
location.href = `${current_page}`;
}
function onClickCategory(e) {
e.preventDefault();
const category = String($(this).data('category') || 'all').trim();
if (!category) return;
$('#board_catagory .category-pill-btn').removeClass('active');
$(this).addClass('active');
loadPosts(null, null, category);
}
function onClickPageBtn() {
const targetPage = parseInt($(this).data('page'), 10);
if (!targetPage || targetPage === currentPage) return;
renderPostList(currentPostData, targetPage);
document.getElementById('viewList')?.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
function onClickTitleCell() {
const postId = $(this).closest('[data-post-id]').data('post-id');
location.href = `${current_page_manager}?mode=detail&id=${postId}`;
}
function onClickSearch() {
const modalEl = document.getElementById('searchModal');
if (modalEl && typeof bootstrap !== 'undefined') {
bootstrap.Modal.getOrCreateInstance(modalEl).show();
$('#searchKeyword').trigger('focus');
}
}
function onSubmitSearch(e) {
e.preventDefault();
const keyword = $('#searchKeyword').val().trim();
if (!keyword) {
alert('검색어를 입력해주세요.');
$('#searchKeyword').trigger('focus');
return;
}
loadPosts($('#searchType').val(), keyword);
const modalEl = document.getElementById('searchModal');
if (modalEl && typeof bootstrap !== 'undefined') {
bootstrap.Modal.getOrCreateInstance(modalEl).hide();
}
}
function onChangeCheckAll() {
$('.row-check').prop('checked', $(this).is(':checked'));
}
function onChangeRowCheck() {
const total = $('.row-check').length;
const checked = $('.row-check:checked').length;
$('#checkAll').prop('checked', total > 0 && total === checked);
}
// =========================
// Summernote Editor
// =========================
function initSummernote() {
$('#post_content').summernote({
placeholder: '본문 내용을 입력하세요...',
height: 380,
lang: 'ko-KR',
toolbar: [
['style', ['style']],
['font', ['bold', 'underline', 'clear']],
['color', ['color']],
['para', ['ul', 'ol', 'paragraph']],
['table', ['table']],
['insert', ['link', 'picture', 'video']],
['view', ['fullscreen', 'codeview', 'help']]
],
callbacks: {
onImageUpload(files) {
Promise.all(Array.from(files).map(uploadSummernoteImage));
},
onMediaDelete($target) {
const imageUrl = $target.attr('src');
if (imageUrl) deleteSummernoteImage(imageUrl);
}
}
});
}
function uploadSummernoteImage(file) {
const data = new FormData();
data.append('file', file);
data.append('board_name', getBoardName());
return $.ajax({
url: `${API_BASE_URL}?action=upload_editor_image`,
method: 'POST',
data,
contentType: false,
processData: false,
dataType: 'json'
}).then(res => {
if (res.success && res.url) {
$('#post_content').summernote('insertImage', resolveFileUrl(res.url));
} else {
alert(res.message || '이미지 업로드에 실패했습니다.');
}
}).catch(() => {
alert('이미지 업로드 중 서버 통신 오류가 발생했습니다.');
});
}
function deleteSummernoteImage(imageUrl) {
$.ajax({
url: `${API_BASE_URL}?action=delete_editor_image`,
method: 'POST',
data: {
image_url: imageUrl,
board_name: getBoardName()
},
dataType: 'json',
error: () => console.error('이미지 파일 삭제 중 오류 발생')
});
}
});