임동욱 임동욱 08-19
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
+++ app/(protected)/(basic)/boards/_actions.ts
@@ -14,7 +14,6 @@
 } from '@/lib/data/repositories/file-repository';
 import {
   ANSWER_STATUS_DONE,
-  BOARD_FILE_MODULE_ID,
   type BoardType,
 } from '@/lib/domain/board-post';
 import {
@@ -24,6 +23,7 @@
   type InquiryAnswerFormState,
 } from '@/lib/domain/board-post-form';
 import { BOARD_PATHS } from '@/lib/domain/board-post-query';
+import { FILE_MODULE_ID } from '@/lib/domain/file-module';
 
 /**
  * 게시판(공지사항·1:1문의·FAQ) 등록/수정/삭제/답변 Server Action.
@@ -86,7 +86,7 @@
     throw new Error(OVERSIZE_MESSAGE);
   }
 
-  return uploadAttachment(file, BOARD_FILE_MODULE_ID);
+  return uploadAttachment(file, FILE_MODULE_ID.board);
 }
 
 /** 등록·수정이 공유하는 입력 읽기. */
lib/data/repositories/decoration-item-repository.ts
--- lib/data/repositories/decoration-item-repository.ts
+++ lib/data/repositories/decoration-item-repository.ts
@@ -8,6 +8,7 @@
   DecorationItemQuery,
   DecorationItemSearchField,
 } from '@/lib/domain/decoration-item-query';
+import { FILE_MODULE_ID } from '@/lib/domain/file-module';
 
 /**
  * 꾸미기 아이템 Repository — 이 도메인을 백엔드에서 "어떻게 읽고 쓰는지"만 안다(엔드포인트·
@@ -52,9 +53,6 @@
 const DECORATION_ITEM_PATH = '/api/v1/mngr/item';
 const FILE_UPLOAD_PATH = '/api/v1/common/file/upload';
 const FILE_IMAGE_PATH = '/api/v1/common/file/image';
-
-/** 파일 업로드 경로 변수 — 아이템 이미지용 모듈 ID(사용자 확정 사항). */
-const ITEM_FILE_MODULE_ID = 'MODULE_ITEM';
 
 /** 화면·URL의 유형 값 ↔ 백엔드 `itemType` 코드값(사용자 확정 사항). */
 const ITEM_TYPE_CODE: Record<DecorationItemType, string> = {
@@ -350,7 +348,7 @@
   multipart.set('file', file);
 
   const result = await backendFetch<string>(
-    `${FILE_UPLOAD_PATH}/${ITEM_FILE_MODULE_ID}`,
+    `${FILE_UPLOAD_PATH}/${FILE_MODULE_ID.decorationItem}`,
     {
       method: 'POST',
       multipart,
lib/data/repositories/file-repository.ts
--- lib/data/repositories/file-repository.ts
+++ lib/data/repositories/file-repository.ts
@@ -31,9 +31,9 @@
 /**
  * 파일 하나를 업로드하고 `atchFileId`를 돌려준다.
  *
- * `moduleId`는 백엔드에서 파일 저장 설정(`FileStrgStngVo.strgStngId`)을 가리키며, 설정에 없는
- * 값이면 `UNKNOWN` 경로로 저장된다(EgovFileMngUtil) — 즉 잘못된 값이어도 실패하지 않고 조용히
- * 다른 곳에 저장되므로, 실제 설정 ID가 확정되면 반드시 맞춰야 한다.
+ * `moduleId`는 백엔드에서 파일 저장 설정(`FileStrgStngVo.strgStngId`)을 가리킨다. 설정에 없는
+ * 값이면 기본 저장소(`FILE_STORAGE`)로 폴백해 **실패하지 않고 조용히 다른 곳에 저장된다**
+ * (`EgovFileMngUtil.parseFileInf`). 쓸 수 있는 값은 `lib/domain/file-module.ts`가 모아 둔다.
  */
 export async function uploadAttachment(
   file: File,
lib/domain/board-post.ts
--- lib/domain/board-post.ts
+++ lib/domain/board-post.ts
@@ -32,13 +32,6 @@
   faq: 'TEMP_STNG_ID_FAQ',
 };
 
-/**
- * 파일 업로드 API(`POST /api/v1/common/file/upload/{moduleId}`)의 moduleId — **임시값이다.**
- * 백엔드에서 이 값은 파일 저장 설정(`FileStrgStngVo.strgStngId`)을 가리키며, 설정에 없는 값이면
- * `UNKNOWN` 경로로 떨어진다(EgovFileMngUtil). 실제 설정 ID가 정해지면 여기만 고친다.
- */
-export const BOARD_FILE_MODULE_ID = 'MODULE_BBS';
-
 export type BoardPost = {
   /** 백엔드 `bbsId`. 행의 key이자 단건 조회·수정·삭제의 입력값이다. */
   id: string;
 
lib/domain/file-module.ts (added)
+++ lib/domain/file-module.ts
@@ -0,0 +1,26 @@
+/**
+ * 파일 업로드 API(`POST /api/v1/common/file/upload/{moduleId}`)가 받는 moduleId 모음.
+ *
+ * **전부 임시값이다.** 이 값은 백엔드에서 파일 저장 설정(`FileStrgStngVo.strgStngId`)을 가리키고,
+ * 그 설정이 저장 폴더·허용 확장자·최대 크기·파일 개수 제한·원본 파일명 보존 여부를 정한다
+ * (`EgovFileMngUtil.parseFileInf`). 그런데 백엔드 **코드**에 실제로 등장하는 모듈은
+ * `MODULE_POCKET`·`MODULE_USER_PROFILE` 둘뿐이고, 아래 값들이 저장 설정 테이블에 있는지는
+ * 저장소만 봐서는 알 수 없다(DB에만 있다).
+ *
+ * 틀린 값을 보내도 **업로드가 실패하지는 않는다** — 설정을 못 찾으면 기본 저장소(`FILE_STORAGE`)로
+ * 폴백해 조용히 다른 곳에 저장된다. 그래서 잘못돼도 티가 나지 않는 것이 이 값의 위험한 점이다.
+ * (기본 저장소 설정마저 없으면 그때는 NPE로 500이 되어 화면에 업로드 실패로 뜬다.)
+ *
+ * **백엔드가 모듈 목록 조회 API를 만들어 주기로 했다(2026-08-19 합의).** 그 API가 나오면 이 파일의
+ * 상수를 지우고 응답으로 대체한다 — 업로드 호출부는 모두 여기만 참조하므로 바꿀 곳은 여기 하나다.
+ * 그때 화면이 모듈을 고르게 할 필요는 없다. 각 화면이 쓰는 모듈은 고정이고, 조회 API는 "그 이름이
+ * 실재하는지"를 확인하는 용도다.
+ */
+export const FILE_MODULE_ID = {
+  /** 꾸미기 아이템 썸네일. */
+  decorationItem: 'MODULE_ITEM',
+  /** 게시판 첨부파일. */
+  board: 'MODULE_BBS',
+} as const;
+
+export type FileModuleId = (typeof FILE_MODULE_ID)[keyof typeof FILE_MODULE_ID];
Add a comment
List