refactor: 파일 업로드 moduleId를 한 곳으로 모음
백엔드가 **모듈 목록 조회 API를 만들어 주기로 해서**(2026-08-19 합의) 그때 바꿀 곳이 하나가 되도록 정리했다. 지금은 `MODULE_ITEM`(아이템 썸네일)이 Repository 안 사설 상수로, `MODULE_BBS` (게시판 첨부)가 게시판 도메인 파일로 흩어져 있었다. 이 값은 백엔드에서 파일 저장 설정(`FileStrgStngVo.strgStngId`)을 가리켜 저장 폴더·허용 확장자· 최대 크기를 정하는데, **틀려도 업로드가 실패하지 않는다** — 설정을 못 찾으면 기본 저장소 (`FILE_STORAGE`)로 폴백해 조용히 다른 곳에 저장된다. 티가 나지 않는 것이 이 값의 위험한 점이라 근거를 `lib/domain/file-module.ts` 주석에 모아 두었다. 곁들여 `file-repository.ts`의 설명을 사실에 맞게 고쳤다 — "설정에 없으면 `UNKNOWN` 경로로 저장된다"고 적혀 있었으나, 실제 폴백은 `FILE_STORAGE` 저장소다(`UNKNOWN`은 설정의 strgStngId가 비었을 때 쓰는 별개 값이다). Co-Authored-By: Claude Opus 5
@2a9cd565cbb84b134a1d1304d88a6e974e72e782
--- app/(protected)/(basic)/boards/_actions.ts
+++ app/(protected)/(basic)/boards/_actions.ts
... | ... | @@ -14,7 +14,6 @@ |
| 14 | 14 |
} from '@/lib/data/repositories/file-repository'; |
| 15 | 15 |
import {
|
| 16 | 16 |
ANSWER_STATUS_DONE, |
| 17 |
- BOARD_FILE_MODULE_ID, |
|
| 18 | 17 |
type BoardType, |
| 19 | 18 |
} from '@/lib/domain/board-post'; |
| 20 | 19 |
import {
|
... | ... | @@ -24,6 +23,7 @@ |
| 24 | 23 |
type InquiryAnswerFormState, |
| 25 | 24 |
} from '@/lib/domain/board-post-form'; |
| 26 | 25 |
import { BOARD_PATHS } from '@/lib/domain/board-post-query';
|
| 26 |
+import { FILE_MODULE_ID } from '@/lib/domain/file-module';
|
|
| 27 | 27 |
|
| 28 | 28 |
/** |
| 29 | 29 |
* 게시판(공지사항·1:1문의·FAQ) 등록/수정/삭제/답변 Server Action. |
... | ... | @@ -86,7 +86,7 @@ |
| 86 | 86 |
throw new Error(OVERSIZE_MESSAGE); |
| 87 | 87 |
} |
| 88 | 88 |
|
| 89 |
- return uploadAttachment(file, BOARD_FILE_MODULE_ID); |
|
| 89 |
+ return uploadAttachment(file, FILE_MODULE_ID.board); |
|
| 90 | 90 |
} |
| 91 | 91 |
|
| 92 | 92 |
/** 등록·수정이 공유하는 입력 읽기. */ |
--- lib/data/repositories/decoration-item-repository.ts
+++ lib/data/repositories/decoration-item-repository.ts
... | ... | @@ -8,6 +8,7 @@ |
| 8 | 8 |
DecorationItemQuery, |
| 9 | 9 |
DecorationItemSearchField, |
| 10 | 10 |
} from '@/lib/domain/decoration-item-query'; |
| 11 |
+import { FILE_MODULE_ID } from '@/lib/domain/file-module';
|
|
| 11 | 12 |
|
| 12 | 13 |
/** |
| 13 | 14 |
* 꾸미기 아이템 Repository — 이 도메인을 백엔드에서 "어떻게 읽고 쓰는지"만 안다(엔드포인트· |
... | ... | @@ -52,9 +53,6 @@ |
| 52 | 53 |
const DECORATION_ITEM_PATH = '/api/v1/mngr/item'; |
| 53 | 54 |
const FILE_UPLOAD_PATH = '/api/v1/common/file/upload'; |
| 54 | 55 |
const FILE_IMAGE_PATH = '/api/v1/common/file/image'; |
| 55 |
- |
|
| 56 |
-/** 파일 업로드 경로 변수 — 아이템 이미지용 모듈 ID(사용자 확정 사항). */ |
|
| 57 |
-const ITEM_FILE_MODULE_ID = 'MODULE_ITEM'; |
|
| 58 | 56 |
|
| 59 | 57 |
/** 화면·URL의 유형 값 ↔ 백엔드 `itemType` 코드값(사용자 확정 사항). */ |
| 60 | 58 |
const ITEM_TYPE_CODE: Record<DecorationItemType, string> = {
|
... | ... | @@ -350,7 +348,7 @@ |
| 350 | 348 |
multipart.set('file', file);
|
| 351 | 349 |
|
| 352 | 350 |
const result = await backendFetch<string>( |
| 353 |
- `${FILE_UPLOAD_PATH}/${ITEM_FILE_MODULE_ID}`,
|
|
| 351 |
+ `${FILE_UPLOAD_PATH}/${FILE_MODULE_ID.decorationItem}`,
|
|
| 354 | 352 |
{
|
| 355 | 353 |
method: 'POST', |
| 356 | 354 |
multipart, |
--- lib/data/repositories/file-repository.ts
+++ lib/data/repositories/file-repository.ts
... | ... | @@ -31,9 +31,9 @@ |
| 31 | 31 |
/** |
| 32 | 32 |
* 파일 하나를 업로드하고 `atchFileId`를 돌려준다. |
| 33 | 33 |
* |
| 34 |
- * `moduleId`는 백엔드에서 파일 저장 설정(`FileStrgStngVo.strgStngId`)을 가리키며, 설정에 없는 |
|
| 35 |
- * 값이면 `UNKNOWN` 경로로 저장된다(EgovFileMngUtil) — 즉 잘못된 값이어도 실패하지 않고 조용히 |
|
| 36 |
- * 다른 곳에 저장되므로, 실제 설정 ID가 확정되면 반드시 맞춰야 한다. |
|
| 34 |
+ * `moduleId`는 백엔드에서 파일 저장 설정(`FileStrgStngVo.strgStngId`)을 가리킨다. 설정에 없는 |
|
| 35 |
+ * 값이면 기본 저장소(`FILE_STORAGE`)로 폴백해 **실패하지 않고 조용히 다른 곳에 저장된다** |
|
| 36 |
+ * (`EgovFileMngUtil.parseFileInf`). 쓸 수 있는 값은 `lib/domain/file-module.ts`가 모아 둔다. |
|
| 37 | 37 |
*/ |
| 38 | 38 |
export async function uploadAttachment( |
| 39 | 39 |
file: File, |
--- lib/domain/board-post.ts
+++ lib/domain/board-post.ts
... | ... | @@ -32,13 +32,6 @@ |
| 32 | 32 |
faq: 'TEMP_STNG_ID_FAQ', |
| 33 | 33 |
}; |
| 34 | 34 |
|
| 35 |
-/** |
|
| 36 |
- * 파일 업로드 API(`POST /api/v1/common/file/upload/{moduleId}`)의 moduleId — **임시값이다.**
|
|
| 37 |
- * 백엔드에서 이 값은 파일 저장 설정(`FileStrgStngVo.strgStngId`)을 가리키며, 설정에 없는 값이면 |
|
| 38 |
- * `UNKNOWN` 경로로 떨어진다(EgovFileMngUtil). 실제 설정 ID가 정해지면 여기만 고친다. |
|
| 39 |
- */ |
|
| 40 |
-export const BOARD_FILE_MODULE_ID = 'MODULE_BBS'; |
|
| 41 |
- |
|
| 42 | 35 |
export type BoardPost = {
|
| 43 | 36 |
/** 백엔드 `bbsId`. 행의 key이자 단건 조회·수정·삭제의 입력값이다. */ |
| 44 | 37 |
id: string; |
+++ lib/domain/file-module.ts
... | ... | @@ -0,0 +1,26 @@ |
| 1 | +/** | |
| 2 | + * 파일 업로드 API(`POST /api/v1/common/file/upload/{moduleId}`)가 받는 moduleId 모음. | |
| 3 | + * | |
| 4 | + * **전부 임시값이다.** 이 값은 백엔드에서 파일 저장 설정(`FileStrgStngVo.strgStngId`)을 가리키고, | |
| 5 | + * 그 설정이 저장 폴더·허용 확장자·최대 크기·파일 개수 제한·원본 파일명 보존 여부를 정한다 | |
| 6 | + * (`EgovFileMngUtil.parseFileInf`). 그런데 백엔드 **코드**에 실제로 등장하는 모듈은 | |
| 7 | + * `MODULE_POCKET`·`MODULE_USER_PROFILE` 둘뿐이고, 아래 값들이 저장 설정 테이블에 있는지는 | |
| 8 | + * 저장소만 봐서는 알 수 없다(DB에만 있다). | |
| 9 | + * | |
| 10 | + * 틀린 값을 보내도 **업로드가 실패하지는 않는다** — 설정을 못 찾으면 기본 저장소(`FILE_STORAGE`)로 | |
| 11 | + * 폴백해 조용히 다른 곳에 저장된다. 그래서 잘못돼도 티가 나지 않는 것이 이 값의 위험한 점이다. | |
| 12 | + * (기본 저장소 설정마저 없으면 그때는 NPE로 500이 되어 화면에 업로드 실패로 뜬다.) | |
| 13 | + * | |
| 14 | + * **백엔드가 모듈 목록 조회 API를 만들어 주기로 했다(2026-08-19 합의).** 그 API가 나오면 이 파일의 | |
| 15 | + * 상수를 지우고 응답으로 대체한다 — 업로드 호출부는 모두 여기만 참조하므로 바꿀 곳은 여기 하나다. | |
| 16 | + * 그때 화면이 모듈을 고르게 할 필요는 없다. 각 화면이 쓰는 모듈은 고정이고, 조회 API는 "그 이름이 | |
| 17 | + * 실재하는지"를 확인하는 용도다. | |
| 18 | + */ | |
| 19 | +export const FILE_MODULE_ID = { | |
| 20 | + /** 꾸미기 아이템 썸네일. */ | |
| 21 | + decorationItem: 'MODULE_ITEM', | |
| 22 | + /** 게시판 첨부파일. */ | |
| 23 | + board: 'MODULE_BBS', | |
| 24 | +} as const; | |
| 25 | + | |
| 26 | +export type FileModuleId = (typeof FILE_MODULE_ID)[keyof typeof FILE_MODULE_ID]; |
Add a comment
Delete comment
Once you delete this comment, you won't be able to recover it. Are you sure you want to delete this comment?