티스토리 뷰

배포

AWS S3에 이미지 업로드하기 (Nextjs)

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

AWS S3에 이미지를 업로드할 때 사용하는 패키지 종류가 2개가 있어서 비교해봤는데요. 

 

@aws-sdk/client-s3와 @aws-sdk는 모두 AWS SDK를 사용하는 패키지이지만, 사용하는 방식과 포함된 기능이 달랐습니다.

 

@aws-sdk/client-s3와 @aws-sdk의 차이

 

패키지 목적 특징
@aws-sdk/client-s3 S3 전용 클라이언트 S3 객체 업로드, 삭제 등 S3 관련 기능만 포함
@aws-sdk AWS SDK v3의 상위 모듈 S3뿐만 아니라 DynamoDB, Lambda 등 다양한 AWS 서비스 지원

 

aws-sdk/client-s3 설치

 

저는 S3에 대한 기능만 사용할거라서 @aws-sdk/client-s3를 설치하기로 했습니다.

 

 

@aws-sdk/client-s3

AWS SDK for JavaScript S3 Client for Node.js, Browser and React Native. Latest version: 3.750.0, last published: 5 days ago. Start using @aws-sdk/client-s3 in your project by running `npm i @aws-sdk/client-s3`. There are 3851 other projects in the npm regi

www.npmjs.com

 

 

IAM 설정

 

보안 강화를 위해 IAM 사용자 계정을 생성하여 S3 접근 권한만 부여하는 것이 좋습니다.

 

 

  • AWS IAM 콘솔에서 새 사용자 생성
  • S3 권한만 포함된 정책 (AmazonS3FullAccess 또는 커스텀 정책) 할당
  • 액세스 키 및 시크릿 키 발급 후 .env에 저장

 

 

 

 

그리고 보안 자격 증명에 보면 액세스키 만들기 탭이 있는데 생성해줍니다.

 

액세스 키를 외부에서 사용할 거라서 외부에서 실행되는 애플리케이션을 선택해줬습니다.

S3 버킷 정책 설정 

그리고 S3의 정책을 변경해줘야했는데요. 아래의 정책을 추가해줬습니다. 계정 아이디는 AWS의 오른쪽 위에서 확인할 수 있었어요

 

업로드 및 삭제 권한을 부여하기 위해 S3 버킷 정책을 추가해야 합니다.

 {
      "Sid": "2",
      "Effect": "Allow",
      "Principal": {
	    "AWS": "arn:aws:iam::<계정아이디>:root"
		},
      "Action": ["s3:PutObject", "s3:DeleteObject"],
      "Resource": "arn:aws:s3:::your-s3-bucket-name/*"
    }

 

S3 CORS 설정

 

프론트엔드에서 직접 S3에 업로드하려면 CORS 설정이 필요합니다.

 

 

  • AllowedMethods: 허용할 HTTP 메서드 지정
  • AllowedOrigins: 특정 도메인만 허용하여 보안 강화
  • ExposeHeaders: 클라이언트가 확인할 수 있는 응답 헤더 지정

 

[{
  "AllowedHeaders": [
    "*"
  ],
  "AllowedMethods": [
    "PUT",
    "POST",
    "GET"
  ],
  "AllowedOrigins": [
    "http://localhost:3000",
    "https://portfolio.coddink.com",
    "https://www.portfolio.coddink.com"
  ],
  "ExposeHeaders": [
    "ETag"
  ]
}
]

 

 

Access 키 발급 (IAM 이용 안할때)

액세스키랑 시크릿키도 필요했는데요. 계정에서 보안 자격 증명을 클릭한 뒤 액세스 키를 만들면 되었습니다. 단 한번만 볼 수 있으니깐 복사해두고 잘 관리해야 해요.

 

 

 

AWS_ACCESS_KEY=
AWS_SECRET_ACCESS_KEY=

 

 

AWS 코드 적용하기

.env에 아래에 해당하는 코드를 작성했습니다.

AWS_REGION=ap-northeast-2
AWS_BUCKET_NAME=
AWS_ACCESS_KEY=
AWS_SECRET_ACCESS_KEY=

 

 

client에서 사용하는 업로드 파일. 저는 파일 이름을 prisma로 따로 저장해서 보여줬기 때문에 파일 이름을 반환하도록 했어요.

export const uploadFile = async (file: File): Promise<string> => {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();

    reader.readAsDataURL(file);
    reader.onloadend = async () => {
      if (reader.result) {
        try {
          const base64data = (reader.result as string).split(',')[1];

          const res = await fetch('/api/files', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({
              file: base64data,
              name: file.name,
              type: file.type,
            }),
          });

          const data = await res.json();

          if (res.ok) {
            resolve(data.id); // 성공 시 파일 ID 반환
          } else {
            reject(new Error(`Upload failed: ${data.error || 'Unknown error'}`));
          }
        } catch (error) {
          reject(error);
        }
      } else {
        reject(new Error('FileReader result is null'));
      }
    };

    reader.onerror = () => reject(new Error('File reading failed'));
  });
};

 

사용 예시 코드.

  const bannerFinishClick = async () => {
    if (loading) return;
    if (!banner.fileData) return;

    setIsBannerClick(false);
    setIsBannerLoading(true);
    const imageSrc = await uploadFile(banner.fileData);

    updateProfile({ banner: { ...banner, imageSrc } });
    setIsBannerLoading(false);
  };

 

 

api/files.ts , 서버에서 사용하는 코드입니다. 기존의 cloudflare보다 업로드 속도가 빨라진 것 같습니다.

 

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' });
  }
}
export default withApiSession(
  withHandler({ methods: ['GET', 'POST'], handler })
);

 

 

그리고 이미지를 보여줄 때의 코드인데요, cloudflare에서 옮긴 파일에서는 확장자명이 안붙어있어서 뒤에 .jpg를 붙여줘야하고, S3에서 업로드할 때는 확장자명이 붙어있기 때문에 그대로 보여주도록 했습니다.

 

export function makeImageURL(id: string) {
  if (!id) return 'https://d3319kcxpye19t.cloudfront.net/images/default.jpg';

  if (id.match(/\.\w+$/)) {
    return `https://d3319kcxpye19t.cloudfront.net/images/${id}`;
  }

  return `https://d3319kcxpye19t.cloudfront.net/images/${id}.jpg`;
}

 

 

 

이번 포스팅에서는 Nextjs에서 AWS S3에 이미지를 업로드하는 방법과 @aws-sdk/client-s3를 사용해 파일을 처리하는 과정을 살펴봤는데요.

 

파일 관리를 Cloudflare에서 S3로 이동하면서 AWS에 대해 더 많이 알게 되어서 좋았습니다.

 

 

 

AWS S3 이미지 업로드하기 (with NextJS)

안녕하세요. 오랜만에 쓰는 포스팅이네요. 코드드림 어드민 프로젝트가 거의 마무리되어 가고 있습니다. 저희는 AWS S3를 사용해 이미지를 저장하게 되었는데, 이때 배우게 된 내용을 복기하고자

tech.codedream.co.kr

 

 

 

공지사항
최근에 올라온 글
최근에 달린 댓글
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
글 보관함