diff --git a/backend/src/main/java/com/opensource/docgrid/domain/document/config/DocumentUploadProperties.java b/backend/src/main/java/com/opensource/docgrid/domain/document/config/DocumentUploadProperties.java index b7593b5d..1b3f34d3 100644 --- a/backend/src/main/java/com/opensource/docgrid/domain/document/config/DocumentUploadProperties.java +++ b/backend/src/main/java/com/opensource/docgrid/domain/document/config/DocumentUploadProperties.java @@ -13,5 +13,5 @@ @ConfigurationProperties(prefix = "document.upload") public class DocumentUploadProperties { - private DataSize maxFileSize = DataSize.ofMegabytes(10); + private DataSize maxFileSize = DataSize.ofMegabytes(20); } diff --git a/backend/src/main/java/com/opensource/docgrid/global/exception/ErrorCode.java b/backend/src/main/java/com/opensource/docgrid/global/exception/ErrorCode.java index edfcc1d2..b5c1af08 100644 --- a/backend/src/main/java/com/opensource/docgrid/global/exception/ErrorCode.java +++ b/backend/src/main/java/com/opensource/docgrid/global/exception/ErrorCode.java @@ -69,7 +69,7 @@ public enum ErrorCode { HttpStatus.INTERNAL_SERVER_ERROR, "DOCUMENT-STATUS-001", "문서 인덱싱 상태를 조회할 수 없습니다." ), EMPTY_FILE(HttpStatus.BAD_REQUEST, "DOCUMENT-FILE-001", "빈 파일은 업로드할 수 없습니다."), - FILE_SIZE_EXCEEDED(HttpStatus.BAD_REQUEST, "DOCUMENT-FILE-002", "파일 크기 제한을 초과했습니다."), + FILE_SIZE_EXCEEDED(HttpStatus.BAD_REQUEST, "DOCUMENT-FILE-002", "파일 용량이 큽니다."), UNSUPPORTED_FILE_EXTENSION(HttpStatus.BAD_REQUEST, "DOCUMENT-FILE-003", "지원하지 않는 파일 확장자입니다."), UNSUPPORTED_FILE_CONTENT_TYPE(HttpStatus.BAD_REQUEST, "DOCUMENT-FILE-004", "지원하지 않는 파일 형식입니다."), INVALID_FILE_NAME(HttpStatus.BAD_REQUEST, "DOCUMENT-FILE-005", "유효하지 않은 파일명입니다."), diff --git a/backend/src/main/resources/application.yml b/backend/src/main/resources/application.yml index d242d2b7..99501572 100644 --- a/backend/src/main/resources/application.yml +++ b/backend/src/main/resources/application.yml @@ -7,8 +7,9 @@ spring: active: ${SPRING_PROFILES_ACTIVE:local} servlet: multipart: - max-file-size: 10MB - max-request-size: 11MB + max-file-size: 20MB + # Multipart 메타데이터를 포함할 수 있도록 파일 제한보다 요청 제한을 넉넉하게 둔다. + max-request-size: 25MB jpa: # 전역 OSIV를 끄고, WebMvcConfig에서 /mcp를 제외한 나머지 경로에만 다시 등록한다. # /mcp는 MCP Streamable HTTP 응답 처리 방식과 OSIV가 충돌해 DB 커넥션이 누수됐다(#120). @@ -42,7 +43,7 @@ dashboard: document: upload: - max-file-size: 10MB + max-file-size: 20MB chunking: chunk-size: ${DOCUMENT_CHUNK_SIZE:1000} overlap: ${DOCUMENT_CHUNK_OVERLAP:200} diff --git a/frontend/app/lib/api.ts b/frontend/app/lib/api.ts index fb2f8003..62cdcd6c 100644 --- a/frontend/app/lib/api.ts +++ b/frontend/app/lib/api.ts @@ -60,7 +60,9 @@ export async function apiRequest(path: string, options: RequestOptions = {}): const payload = text ? safeJson(text) : null; if (!response.ok) { const error = (payload ?? {}) as ApiErrorBody; - throw new ApiError(response.status, error.message ?? "요청을 처리하지 못했습니다.", error.code); + // 4. Convert a plain-text proxy rejection into an actionable upload message. + const fallbackMessage = response.status === 413 ? "파일 용량이 큽니다." : "요청을 처리하지 못했습니다."; + throw new ApiError(response.status, error.message ?? fallbackMessage, error.code); } return (payload as ApiEnvelope).data; diff --git a/frontend/next.config.ts b/frontend/next.config.ts index e9ffa308..13b79c99 100644 --- a/frontend/next.config.ts +++ b/frontend/next.config.ts @@ -1,7 +1,12 @@ import type { NextConfig } from "next"; const nextConfig: NextConfig = { - /* config options here */ + experimental: { + // Vinext가 multipart POST를 Route Handler보다 먼저 검사하므로 백엔드 요청 제한과 맞춘다. + serverActions: { + bodySizeLimit: "25mb", + }, + }, }; export default nextConfig;