티스토리 뷰

배포

Nextjs S3 업로드 에러 "Body exceeded 1mb limit" 해결 방법

YG - 96년생 , 강아지 있음, 개발자 희망 2025. 2. 26. 19:54

Next.js에서 파일을 업로드할 때, 기본적으로 요청 크기 제한이 1MB로 설정되어 있어 대용량 파일을 업로드하면 문제가 발생할 수 있습니다. 

 

저도 AWS S3에 이미지를 업로드하는 과정에서 'Body exceeded 1MB limit' 오류를 만났는데요. 🤯 이 문제를 해결하는 방법은 여러 가지가 있습니다. 

 

저는 1) Next.js의 API 요청 크기 제한을 늘리는 방법과 2) Presigned URL을 활용해 S3에 직접 업로드하는 방법을 시도해봤습니다. 각각의 방법을 비교하고, 최종적으로 어떤 방식을 선택했는지 공유해보겠습니다! 🚀

 

해결방법 1

 

Nextjs는 기본적으로 !MB의 요청 크기 제한을 가지고 있는데요. 이를 늘리려면 bodyParser 설정을 변경해주면 됩니다.

 

기존 코드에서 export 위에 옵션 설정을 변경해주면 잘 올라갔습니다.

 

// ✅ API 요청 크기 제한을 10MB로 증가
export const config = {
  api: {
    bodyParser: {
      sizeLimit: '10mb', // 기본 1MB → 10MB로 증가
    },
  },
};

 

import { NextApiRequest, NextApiResponse } from 'next';
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
import withHandler from '@libs/server/withHandler';
import { withApiSession } from '@libs/server/withSession';

const s3Client = new S3Client({
  region: process.env.AWS_REGION,
  credentials: {
    accessKeyId: process.env.AWS_ACCESS_KEY as string,
    secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY as string,
  },
});

async function handler(req: NextApiRequest, res: NextApiResponse) {
  if (req.method === 'POST') {
    try {
      const { file, name, type } = req.body; // 클라이언트에서 전송한 파일

      const buffer = Buffer.from(file, 'base64'); // base64 문자열을 버퍼로 변환합니다.

      const fileName = `${Date.now()}_${name}`;
      const Key = `images/${fileName}`;
      const uploadParams = {
        Bucket: process.env.AWS_BUCKET_NAME,
        Key, // 파일명을 고유하게 지정합니다.
        Body: buffer,
        ContentType: type,
      };

      const command = new PutObjectCommand(uploadParams);
      await s3Client.send(command);

      res
        .status(200)
        .json({ message: 'File uploaded successfully', id: fileName });
    } catch (err) {
      console.error(err);
      res.status(500).json({ error: 'Error uploading file' });
    }
  } else {
    res.status(405).json({ error: 'Method Not Allowed' });
  }
}

// ✅ API 요청 크기 제한을 10MB로 증가
export const config = {
  api: {
    bodyParser: {
      sizeLimit: '10mb', // 기본 1MB → 10MB로 증가
    },
  },
};

export default withApiSession(
  withHandler({ methods: ['GET', 'POST'], handler })
);

 

 

 

해결방법 2

S3 업로드 방식을 Presigned URL 방식으로 변경해서 해결할 수도 있습니다.

 

현재 저는 클라이언트에서 Base64로 변환해서 API로 전송하고, 서버에서 S3에서 업로드하는 구조인데요.

 

여기서 문제점은 Base64 변환 시 파일 크기가 33% 증가하게 됩니다. 그리고 다시 서버에서 Base64 => Buffer로 변환가정이 필요하게 되고, API 서버에서 큰 파일을 직접 처리하기 때문에 성능이 저하될 수 있습니다.

 

그래서 Presigned URL을 사용해서 클라이언트에서 직접 업로드하면 API 서버가 큰 파일을 처리할 필요가 없고, AWS S3에 부담을 전가할 수 있기 때문에 더 좋아보였습니다.

 

서버 코드

 

presigned 기능을 사용할 땐 아래의 패키지가 추가로 필요했습니다.

 

 

@aws-sdk/s3-request-presigner

[![NPM version](https://img.shields.io/npm/v/@aws-sdk/s3-request-presigner/latest.svg)](https://www.npmjs.com/package/@aws-sdk/s3-request-presigner) [![NPM downloads](https://img.shields.io/npm/dm/@aws-sdk/s3-request-presigner.svg)](https://www.npmjs.com/.

www.npmjs.com

 

import { NextApiRequest, NextApiResponse } from "next";
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";

const s3Client = new S3Client({
  region: process.env.AWS_REGION,
  credentials: {
    accessKeyId: process.env.AWS_ACCESS_KEY as string,
    secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY as string,
  },
});

async function handler(req: NextApiRequest, res: NextApiResponse) {
  if (req.method !== "POST") {
    return res.status(405).json({ error: "Method Not Allowed" });
  }

  try {
    const { name, type } = req.body;

    const fileName = `${Date.now()}_${name}`;
    const Key = `images/${fileName}`;

    const command = new PutObjectCommand({
      Bucket: process.env.AWS_BUCKET_NAME,
      Key,
      ContentType: type,
    });

    const signedUrl = await getSignedUrl(s3Client, command, { expiresIn: 60 }); // 60초 동안 유효한 URL

    res.status(200).json({ url: signedUrl, fileName });
  } catch (err) {
    console.error(err);
    res.status(500).json({ error: "Error generating signed URL" });
  }
}

export default handler;

 

 

클라이언트 코드

 

기존에는 파일 데이터를 넘겼다면 이름과 타입만 넘기고, URL을 받아서 URL로 파일을 패치하도록 했습니다.

 

export const uploadFile = async (file: File): Promise<string> => {
  // 1. 서버에서 Presigned URL 요청
  const res = await fetch("/api/files", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      name: file.name,
      type: file.type,
    }),
  });

  const { url, fileName } = await res.json();

  // 2. Presigned URL을 사용하여 파일을 S3에 직접 업로드
  const uploadRes = await fetch(url, {
    method: "PUT",
    headers: { "Content-Type": file.type },
    body: file,
  });

  if (!uploadRes.ok) {
    throw new Error("Upload failed");
  }

  return fileName; // 성공 시 업로드된 파일명 반환
};

 

 

 

이처럼 파일 업로드 방식에는 여러 가지가 있으며, 프로젝트 상황에 맞는 최적의 방법을 선택하는 것이 중요합니다. 저는 결국 Presigned URL 방식을 적용했는데, 서버 부담을 줄이면서도 성능을 최적화할 수 있기 때문입니다. 

 

여러분도 프로젝트 상황에 따라 적절한 방식을 선택하시길 바랍니다! 😀

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/04   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30
글 보관함