fix: 큰 첨부파일이 서버에서 잡히지 않는 오류로 끝나던 문제
## 확인한 것 임시 Server Action에 파일을 크기별로 보내 재현했다. - 100KB · 9MB → 정상 도달 - 10.5MB · 12MB · 21MB → 500 `Unexpected end of form` 21MB에서도 "Body exceeded 20mb limit."이 아니라 같은 오류가 나므로 `bodySizeLimit`의 판정이 아니다 — Next 16.2.12(Turbopack) dev에서 Server Action의 multipart 본문이 **10MB 부근에서 잘리고**, 그러면 액션 본문이 실행되지 않아 서버에서 손쓸 방법이 없다. ## 고침 막을 수 있는 곳이 브라우저뿐이라 첨부 input에서 크기를 먼저 잰다. 상한을 넘으면 선택을 비우고 문구를 띄운다 — 종전에는 그대로 전송돼 처리 불가한 500으로 끝났다. `bodySizeLimit`은 20mb로 올려 두었다. 이번 절단과는 무관하지만, 앱 상한(첨부 10MB)과 같은 값이면 초과 파일이 우리 검증 대신 전송 단계에서 먼저 잘려 "10MB 이하만" 문구를 영영 보여줄 수 없다. Co-Authored-By: Claude Opus 5
@fc6e1268c9146c1f97925c3ae7e6758d4f56e82c
--- app/(protected)/(basic)/boards/_components/board-attachment-field.tsx
+++ app/(protected)/(basic)/boards/_components/board-attachment-field.tsx
... | ... | @@ -1,6 +1,9 @@ |
| 1 | 1 |
'use client'; |
| 2 | 2 |
|
| 3 |
+import { useState } from 'react';
|
|
| 4 |
+import { FoxHelperText } from '@fox/core/components/fox-helper-text';
|
|
| 3 | 5 |
import { Field } from '@/components/ui/field';
|
| 6 |
+import { MAX_ATTACHMENT_BYTES } from '@/lib/data/repositories/file-repository';
|
|
| 4 | 7 |
|
| 5 | 8 |
interface BoardAttachmentFieldProps {
|
| 6 | 9 |
label?: string; |
... | ... | @@ -23,10 +26,16 @@ |
| 23 | 26 |
* 새 파일을 고르면 기존 첨부는 교체된다(백엔드 필드가 `atchFileId` 하나뿐이다). 파일을 고르지 |
| 24 | 27 |
* 않으면 기존 첨부가 그대로 유지된다. |
| 25 | 28 |
*/ |
| 29 |
+const OVERSIZE_MESSAGE = `첨부파일은 ${Math.floor(
|
|
| 30 |
+ MAX_ATTACHMENT_BYTES / (1024 * 1024) |
|
| 31 |
+)}MB 이하만 올릴 수 있습니다.`; |
|
| 32 |
+ |
|
| 26 | 33 |
export function BoardAttachmentField({
|
| 27 | 34 |
label = '첨부파일', |
| 28 | 35 |
attachmentId, |
| 29 | 36 |
}: BoardAttachmentFieldProps) {
|
| 37 |
+ const [error, setError] = useState<string | undefined>(undefined); |
|
| 38 |
+ |
|
| 30 | 39 |
return ( |
| 31 | 40 |
<Field label={label}>
|
| 32 | 41 |
{/* 기존 첨부 id를 함께 제출해 "파일을 안 골랐으면 유지"를 서버가 판단할 수 있게 한다. */}
|
... | ... | @@ -45,11 +54,23 @@ |
| 45 | 54 |
</p> |
| 46 | 55 |
) : null} |
| 47 | 56 |
|
| 57 |
+ {/* 이슈: 상한을 넘는 파일은 Server Action 본문이 잘려 500으로 끝나고 액션이 실행되지
|
|
| 58 |
+ 않는다. 서버에서 잡을 수 없어 브라우저에서 미리 막는다. */} |
|
| 48 | 59 |
<input |
| 49 | 60 |
type="file" |
| 50 | 61 |
name="attachment" |
| 51 | 62 |
className="w-full text-body-sm text-foreground file:mr-3 file:rounded-md file:border file:border-border file:bg-surface file:px-3 file:py-1.5 file:text-body-sm file:text-foreground" |
| 63 |
+ onChange={(event) => {
|
|
| 64 |
+ const file = event.target.files?.[0]; |
|
| 65 |
+ if (file && file.size > MAX_ATTACHMENT_BYTES) {
|
|
| 66 |
+ event.target.value = ''; |
|
| 67 |
+ setError(OVERSIZE_MESSAGE); |
|
| 68 |
+ return; |
|
| 69 |
+ } |
|
| 70 |
+ setError(undefined); |
|
| 71 |
+ }} |
|
| 52 | 72 |
/> |
| 73 |
+ {error && <FoxHelperText type="danger" message={error} />}
|
|
| 53 | 74 |
</Field> |
| 54 | 75 |
); |
| 55 | 76 |
} |
--- next.config.ts
+++ next.config.ts
... | ... | @@ -22,7 +22,9 @@ |
| 22 | 22 |
const nextConfig: NextConfig = {
|
| 23 | 23 |
experimental: {
|
| 24 | 24 |
serverActions: {
|
| 25 |
- bodySizeLimit: '10mb', |
|
| 25 |
+ // 이슈: 앱의 파일 상한(첨부 10MB)보다 반드시 커야 한다. 같거나 작으면 초과 파일이 413으로 |
|
| 26 |
+ // 잘려 액션이 실행되지 않고, 우리가 준비한 "10MB 이하만" 메시지 대신 처리 불가한 오류가 뜬다. |
|
| 27 |
+ bodySizeLimit: '20mb', |
|
| 26 | 28 |
}, |
| 27 | 29 |
}, |
| 28 | 30 |
|
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?