File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
import 'server-only';
import { getSessionAccessToken } from '@/lib/auth/dal';
import { BackendRequestError, backendFetch } from '@/lib/http/backend-fetch';
/**
* 첨부파일 Repository — 업로드해서 `atchFileId`를 받아오는 것만 담당한다.
*
* POST /api/v1/common/file/upload/{moduleId} multipart/form-data, 파트 이름 `file`
* → data: "생성된 atchFileId" (문자열)
*
* 게시판 등록/수정은 이 id를 `atchFileId`로 실어 보내 게시물과 파일을 연결한다.
*
* **파일 메타데이터(파일명·용량) 조회 API가 없다.** 백엔드 파일 컨트롤러에는 업로드·다운로드·삭제·
* 이미지·스트림만 있고 "이 atchFileId에 무슨 파일이 붙어 있는가"를 알려 주는 엔드포인트가 없다.
* 그래서 시안(A_BOA_008_p)의 `pic_0001.jpg 88KB` 같은 표기는 **이미 저장된 첨부에 대해서는
* 불가능**하고, 화면은 "첨부 있음 + 다운로드 링크"까지만 보여준다. 방금 업로드한 파일은
* 브라우저가 이름·크기를 알고 있어 그 세션 동안만 표시할 수 있다.
*
* 다운로드는 이 파일이 다루지 않는다 — 파일 응답은 라우트 핸들러가 스트림으로 중계한다
* (`app/(protected)/(basic)/boards/attachments/route.ts`).
*/
const FILE_UPLOAD_PATH = '/api/v1/common/file/upload';
/** 업로드 상한. 백엔드에 명시적 제한이 보이지 않아 프론트에서 방어적으로 건다. */
export const MAX_ATTACHMENT_BYTES = 10 * 1024 * 1024;
/** 파일 생성은 일반 조회보다 오래 걸릴 수 있어 넉넉히 잡는다. */
const UPLOAD_TIMEOUT_MS = 60_000;
/**
* 파일 하나를 업로드하고 `atchFileId`를 돌려준다.
*
* `moduleId`는 백엔드에서 파일 저장 설정(`FileStrgStngVo.strgStngId`)을 가리킨다. 설정에 없는
* 값이면 기본 저장소(`FILE_STORAGE`)로 폴백해 **실패하지 않고 조용히 다른 곳에 저장된다**
* (`EgovFileMngUtil.parseFileInf`). 쓸 수 있는 값은 `lib/domain/file-module.ts`가 모아 둔다.
*/
export async function uploadAttachment(
file: File,
moduleId: string
): Promise<string> {
const accessToken = await getSessionAccessToken();
const multipart = new FormData();
multipart.set('file', file);
const result = await backendFetch<unknown>(
`${FILE_UPLOAD_PATH}/${moduleId}`,
{
method: 'POST',
multipart,
accessToken: accessToken ?? undefined,
cache: 'no-store',
timeoutMs: UPLOAD_TIMEOUT_MS,
}
);
if (!result.ok) {
throw new BackendRequestError(result);
}
// 성공 응답의 data는 atchFileId 문자열 그대로다(ApiResponseVO.success(atchFileId)).
if (typeof result.data !== 'string' || result.data.length === 0) {
throw new Error('첨부파일 업로드 응답에 파일 식별자가 없습니다.');
}
return result.data;
}