[Next.js] API Route로 AWS S3에 이미지 업로드

2024-01-29

  • 주의사항: 이 글은 Next.js 14버전의 page 디렉터리의 api 라우트를 전제하여 작성된 글입니다. ​

1. 연동을 위한 준비

Next.js 프로젝트를 하던 중 스토리지 서버가 필요해 가장 먼저 든 생각이 api 라우트의 한계를 경험해 보고 싶어 AWS의 S3와 연동할 생각을 하게 되었다. ​ 일단 연동을 위해서는 AWS에서 S3 버킷을 생성하고 IAMUSER까지 등록되어 있어야 한다. (모두 퍼블릭으로 설정)
그리고 IAMUSER를 생성하면 ACCESS KEY와 SECRET KEY를 기억해 둬야 한다. ​

2. Next.js 기본 설정

기본적으로 이미지를 클라이언트에서 보내려면 json 형식의 데이터가 아닌 form 데이터 형식으로 보내야 한다.
하지만 Next.js의 api 라우트는 form 데이터를 파싱할 수 없다. 따라서 기본적으로 요청을 받았을 때 데이터를 파싱하는 기능을 꺼줘야 한다. ​

// pages/api/upload-img.ts
export const config = {
  api: {
    bodyParser: false
  }
}

​ 다음으로 form 데이터를 파싱할 수 있는 라이브러리를 설치해야 한다. 이 글에선 formidable이라는 라이브러리를 사용할 것이다. ​

npm install formidable

​ 만약 타입스크립트를 사용중이라면 타입 라이브러리도 함께 설치해야 한다. ​

npm install --save @types/formidable

​ 이제 S3 라이브러리를 설치해야 한다. 이 글에선 aws-sdk를 사용하겠다. ​

npm install aws-sdk

​ 만약 타입스크립트를 사용중이라면 타입 라이브러리도 함께 설치한다. ​

npm install @aws-sdk/types

​ 다음으로 환경변수 설정이 필요하다.
프로젝트 루트에서 .env 파일을 생성해 다음과 같이 넣어준다. ​

AWS_REGION=ap-northeast-2(한국 서울 기준)
AWS_ACCESS_KEY='I AM USER ACCESS KEY'
AWS_SECRET_KEY='I AM USER 생성시 받았던 SECRET KEY'
AWS_BUCKET_NAME='S3 버킷 이름'

​ 여기까지 오면 50% 이상 끝났다. 이제 본격적으로 이미지를 받아서 S3에 업로드하는 작업을 해보자. ​

3. S3 연동 설정

// pages/api/upload-img.ts
const s3 = new S3({
  region: process.env.AWS_REGION,
  accessKeyId: process.env.AWS_ACCESS_KEY,
  secretAccessKey: process.env.AWS_SECRET_KEY,
});

​ 위에 .env에 저장했던 환경변수를 불러오면 끝이다. ​

4. FormData 파싱 후 S3에 업로드

// pages/api/upload-img.ts
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  // Method Not Allowed
  if (req.method !== 'POST') {
    return res.status(405).send('잘못된 요청 메서드');
  }

  // 바디 폼 데이터 파싱
  const form = formidable({});
  const [filed, files] = await form.parse(req);

  if (files.club_img) {
    // import fs from 'fs';를 추가해야 한다.
    const readStream = fs.createReadStream(files.club_img[0].filepath);
    const uploadParams = {
      Bucket: process.env.AWS_BUCKET_NAME,
      Key: files.club_img[0].originalFilename || '',
      Body: readStream,
    };

    try {
      const result = await s3.upload(uploadParams).promise();
      console.log(result.Location);
      return res.status(201).json({ img_url: result.Location });
    } catch (err) {
      console.error('S3 업로드 실패', err);
      return res.status(500).send('서버 관련 오류');
    }
  } else {
    res.status(500).send('알 수 없는 오류');
  }
}

​ 엄청 간단하다. 필자는 구글을 뒤져보며 이상한 코드들만 계속 해보다가 안돼서 조금 간단히 생각해 단순히 파싱하고 업로드를 해주면 되지 않을까?라는 생각에 이렇게 코드를 짜니 성공하였다. 정말 Next.js의 api 라우트의 한계는 어디까지인 것인지 의문이다...

next.jsawss3

프로필 사진
TaeWoo Kim
Junior Frontend Engineer