diff --git a/actions/news.ts b/actions/news.ts index 2d5dd10b4..333cf2955 100644 --- a/actions/news.ts +++ b/actions/news.ts @@ -7,16 +7,19 @@ import { deleteNews, patchNews, postNews } from '@/apis/news'; import { getPath } from '@/utils/page'; import { news } from '@/utils/segmentNode'; +import { decodeFormDataFileName } from '@/utils/string'; const newsPath = getPath(news); export const postNewsAction = async (formData: FormData) => { - await postNews(formData); + decodeFormDataFileName(formData, 'attachments'); + const resp = await postNews(formData); revalidateNewsTag(); - redirect(newsPath); + redirect(`${newsPath}/${resp.id}`); }; export const patchNewsAction = async (id: number, formData: FormData) => { + decodeFormDataFileName(formData, 'newAttachments'); await patchNews(id, formData); revalidateNewsTag(); redirect(`${newsPath}/${id}`); diff --git a/actions/notice.ts b/actions/notice.ts index 935a74f20..71f6635d8 100644 --- a/actions/notice.ts +++ b/actions/notice.ts @@ -14,16 +14,19 @@ import { import { errorToStr } from '@/utils/error'; import { getPath } from '@/utils/page'; import { notice } from '@/utils/segmentNode'; +import { decodeFormDataFileName } from '@/utils/string'; const noticePath = getPath(notice); export const postNoticeAction = async (formData: FormData) => { - await postNotice(formData); + decodeFormDataFileName(formData, 'attachments'); + const resp = await postNotice(formData); revalidateNoticeTag(); - redirect(noticePath); + redirect(`${noticePath}/${resp.id}`); }; export const patchNoticeAction = async (id: number, formData: FormData) => { + decodeFormDataFileName(formData, 'newAttachments'); await patchNotice(id, formData); revalidateNoticeTag(); redirect(`${noticePath}/${id}`); diff --git a/actions/seminar.ts b/actions/seminar.ts index d93c94f5c..f579cda4b 100644 --- a/actions/seminar.ts +++ b/actions/seminar.ts @@ -7,16 +7,19 @@ import { deleteSeminar, patchSeminar, postSeminar } from '@/apis/seminar'; import { getPath } from '@/utils/page'; import { seminar } from '@/utils/segmentNode'; +import { decodeFormDataFileName } from '@/utils/string'; const seminarPath = getPath(seminar); export const postSeminarAction = async (formData: FormData) => { - await postSeminar(formData); + decodeFormDataFileName(formData, 'attachments'); + const resp = await postSeminar(formData); revalidateSeminarTag(); - redirect(seminarPath); + redirect(`${seminarPath}/${resp.id}`); }; export const patchSeminarAction = async (id: number, formData: FormData) => { + decodeFormDataFileName(formData, 'newAttachments'); await patchSeminar(id, formData); revalidateSeminarTag(); redirect(`${seminarPath}/${id}`); diff --git a/apis/news.ts b/apis/news.ts index c40d5ce02..cb0e5d3c5 100644 --- a/apis/news.ts +++ b/apis/news.ts @@ -18,7 +18,7 @@ export const getNewsDetail = (id: number, params?: PostSearchQueryParams) => // POST export const postNews = async (formData: FormData) => { - await postRequest(newsPath, { body: formData }); + return postRequest(newsPath, { body: formData }) as Promise<{ id: number }>; }; // PATCH diff --git a/apis/notice.ts b/apis/notice.ts index d59fca15a..ee652a9be 100644 --- a/apis/notice.ts +++ b/apis/notice.ts @@ -18,7 +18,7 @@ export const getNoticePostDetail = (id: number, params: PostSearchQueryParams) = // POST export const postNotice = async (formData: FormData) => { - await postRequest('/notice', { body: formData }); + return postRequest('/notice', { body: formData }) as Promise<{ id: number }>; }; // PATCH diff --git a/apis/seminar.ts b/apis/seminar.ts index f09aedb44..f9e73617c 100644 --- a/apis/seminar.ts +++ b/apis/seminar.ts @@ -10,17 +10,21 @@ const seminarPath = '/seminar'; // GET export const getSeminarPosts = async (params: PostSearchQueryParams) => { - return await getRequest(seminarPath, params, { next: { tags: ['seminar'] } }); + return getRequest(seminarPath, params, { + next: { tags: ['seminar'] }, + }) as Promise; }; export const getSeminarPost = async (id: number, params: PostSearchQueryParams) => { - return await getRequest(`${seminarPath}/${id}`, params, { next: { tags: ['seminar'] } }); + return getRequest(`${seminarPath}/${id}`, params, { + next: { tags: ['seminar'] }, + }) as Promise; }; // POST export const postSeminar = async (formData: FormData) => { - await postRequest(seminarPath, { body: formData }); + return postRequest(seminarPath, { body: formData }) as Promise<{ id: number }>; }; // PATCH diff --git a/app/[locale]/admin/page.tsx b/app/[locale]/admin/page.tsx index 0faf91fd8..47d2a93d1 100644 --- a/app/[locale]/admin/page.tsx +++ b/app/[locale]/admin/page.tsx @@ -35,6 +35,7 @@ const getAdminData = async (selectedMenu: string, pageNum: number) => { export default function AdminPage({ searchParams: { selected, page } }: AdminPageProps) { const selectedMenu = selected ? replaceDashWithSpace(selected) : DEFAULT_MENU; const pageNum = (page && parseInt(page)) || 1; + const { data } = useSWR(['/admin', selectedMenu, pageNum], () => getAdminData(selectedMenu, pageNum), ); diff --git a/app/[locale]/community/news/[id]/edit/EditNewsPageContent.tsx b/app/[locale]/community/news/[id]/edit/EditNewsPageContent.tsx index 7efbca4b3..bc89d3bf7 100644 --- a/app/[locale]/community/news/[id]/edit/EditNewsPageContent.tsx +++ b/app/[locale]/community/news/[id]/edit/EditNewsPageContent.tsx @@ -21,6 +21,7 @@ import { News } from '@/types/news'; import { validateNewsForm } from '@/utils/formValidation'; import { getPath } from '@/utils/page'; import { news } from '@/utils/segmentNode'; +import { encodeFormDataFileName } from '@/utils/string'; const newsPath = getPath(news); @@ -110,13 +111,12 @@ const contentToFormData = (prevNews: News, content: PostEditorContent) => { ), ); + // TOOD: 이미지 이름 깨지는지 확인 if (mainImage) { formData.append('newMainImage', mainImage); } - for (const attachment of localAttachments) { - formData.append('newAttachments', attachment); - } + encodeFormDataFileName(formData, 'newAttachments', localAttachments); return formData; }; diff --git a/app/[locale]/community/news/create/page.tsx b/app/[locale]/community/news/create/page.tsx index 686104ecc..b2a2202ba 100644 --- a/app/[locale]/community/news/create/page.tsx +++ b/app/[locale]/community/news/create/page.tsx @@ -12,6 +12,7 @@ import { NEWS_TAGS } from '@/constants/tag'; import { validateNewsForm } from '@/utils/formValidation'; import { getPath } from '@/utils/page'; import { news } from '@/utils/segmentNode'; +import { encodeFormDataFileName } from '@/utils/string'; const newsPath = getPath(news); @@ -72,9 +73,11 @@ const contentToFormData = (content: PostEditorContent) => { formData.append('mainImage', content.mainImage.file); } - for (const attachment of content.attachments.filter(isLocalFile).map((x) => x.file)) { - formData.append('attachments', attachment); - } + encodeFormDataFileName( + formData, + 'attachments', + content.attachments.filter(isLocalFile).map((x) => x.file), + ); return formData; }; diff --git a/app/[locale]/community/notice/[id]/edit/EditNoticePageContent.tsx b/app/[locale]/community/notice/[id]/edit/EditNoticePageContent.tsx index 3c26fe85a..d0e679a7b 100644 --- a/app/[locale]/community/notice/[id]/edit/EditNoticePageContent.tsx +++ b/app/[locale]/community/notice/[id]/edit/EditNoticePageContent.tsx @@ -20,6 +20,7 @@ import { Notice } from '@/types/notice'; import { validateNoticeForm } from '@/utils/formValidation'; import { getPath } from '@/utils/page'; import { notice } from '@/utils/segmentNode'; +import { encodeFormDataFileName } from '@/utils/string'; import { errorToast, successToast } from '@/utils/toast'; const noticePath = getPath(notice); @@ -109,9 +110,7 @@ const contentToFormData = (prevNotice: Notice, content: PostEditorContent) => { ), ); - for (const attachment of localAttachments) { - formData.append('newAttachments', attachment); - } + encodeFormDataFileName(formData, 'newAttachments', localAttachments); return formData; }; diff --git a/app/[locale]/community/notice/create/page.tsx b/app/[locale]/community/notice/create/page.tsx index b39fe6776..f0a14f5f7 100644 --- a/app/[locale]/community/notice/create/page.tsx +++ b/app/[locale]/community/notice/create/page.tsx @@ -13,6 +13,7 @@ import { NOTICE_TAGS } from '@/constants/tag'; import { validateNoticeForm } from '@/utils/formValidation'; import { getPath } from '@/utils/page'; import { notice } from '@/utils/segmentNode'; +import { encodeFormDataFileName } from '@/utils/string'; const noticePath = getPath(notice); @@ -66,10 +67,11 @@ const contentToForm = (content: PostEditorContent) => { ), ); - content.attachments - .filter(isLocalFile) - .map((x) => x.file) - .forEach((attachment) => formData.append('attachments', attachment)); + encodeFormDataFileName( + formData, + 'attachments', + content.attachments.filter(isLocalFile).map((x) => x.file), + ); return formData; }; diff --git a/app/[locale]/community/seminar/[id]/edit/EditSeminarPageContent.tsx b/app/[locale]/community/seminar/[id]/edit/EditSeminarPageContent.tsx index 5109f44aa..9f954494e 100644 --- a/app/[locale]/community/seminar/[id]/edit/EditSeminarPageContent.tsx +++ b/app/[locale]/community/seminar/[id]/edit/EditSeminarPageContent.tsx @@ -14,6 +14,7 @@ import { Seminar } from '@/types/seminar'; import { validateSeminarForm } from '@/utils/formValidation'; import { getPath } from '@/utils/page'; import { seminar } from '@/utils/segmentNode'; +import { encodeFormDataFileName } from '@/utils/string'; import { errorToast } from '@/utils/toast'; const seminarPath = getPath(seminar); @@ -124,9 +125,7 @@ const contentToFormData = (prevSeminar: Seminar, content: SeminarEditorContent) formData.append('newMainImage', image); } - for (const attachment of localAttachments) { - formData.append('newAttachments', attachment); - } + encodeFormDataFileName(formData, 'newAttachments', localAttachments); return formData; }; diff --git a/app/[locale]/community/seminar/create/page.tsx b/app/[locale]/community/seminar/create/page.tsx index af8473115..859fc82c8 100644 --- a/app/[locale]/community/seminar/create/page.tsx +++ b/app/[locale]/community/seminar/create/page.tsx @@ -12,6 +12,7 @@ import PageLayout from '@/components/layout/pageLayout/PageLayout'; import { validateSeminarForm } from '@/utils/formValidation'; import { getPath } from '@/utils/page'; import { seminar } from '@/utils/segmentNode'; +import { encodeFormDataFileName } from '@/utils/string'; const seminarPath = getPath(seminar); @@ -80,9 +81,7 @@ const contentToFormData = (content: SeminarEditorContent) => { formData.append('mainImage', image); } - for (const attachment of attachments) { - formData.append('attachments', attachment); - } + encodeFormDataFileName(formData, 'attachments', attachments); return formData; }; diff --git a/tsconfig.json b/tsconfig.json index 962d02c4d..f7cf3b890 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "es5", + "target": "es6", "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, "skipLibCheck": true, diff --git a/utils/string.ts b/utils/string.ts index c9637cea1..a35be4cd5 100644 --- a/utils/string.ts +++ b/utils/string.ts @@ -1,3 +1,25 @@ export const replaceSpaceWithDash = (words: string) => words.replace(/\s+/g, '-'); export const replaceDashWithSpace = (words: string) => words.replace(/-/g, ' '); + +// server action에서 한글 파일이 깨지는 문제가 있어 수동으로 해결 +// TODO: 없어도 되게 +export const decodeFormDataFileName = (formData: FormData, key: FormDataFileName) => { + const list = formData.getAll(key); + formData.delete(key); + + list.filter(isFile).forEach((file) => formData.append(key, file, decodeURI(file.name))); +}; + +// TODO: 없어도 되게 +export const encodeFormDataFileName = ( + formData: FormData, + key: FormDataFileName, + fileList: File[], +) => { + fileList.forEach((file) => formData.append(key, file, encodeURI(file.name))); +}; + +type FormDataFileName = 'attachments' | 'newAttachments'; + +const isFile = (x: unknown): x is File => x instanceof File;