React/Next.js

[Next.js] AWS S3 이미지 업로드 예제 (2) - presigned URL

bonevillain 2024. 3. 25. 11:22

[Next.js] AWS S3 이미지 업로드 예제 (1) 글에서 소스 수정

 

전체 소스

https://github.com/kimfame/next-s3/tree/825116b3274bc549f67e3661e54bde836f725a34

 

패키지 설치

yarn add @aws-sdk/s3-request-presigner

 

 

S3 이미지 업로드 API에서 코드 추가

/src/app/api/upload/route.js

import {
  GetObjectCommand,
  PutObjectCommand,
  S3Client,
} from '@aws-sdk/client-s3'
import { getSignedUrl } from '@aws-sdk/s3-request-presigner'
import { NextResponse } from 'next/server'
import { v4 as uuidv4 } from 'uuid'

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

async function uploadFileToS3(file, fileName, fileType) {
  const key = fileName
  const body = file
  const contentType = fileType

  const parmas = {
    Bucket: process.env.AWS_S3_BUCKET_NAME,
    Key: key,
    ACL: 'public-read',
    ContentType: contentType,
    Body: body,
  }
  const command = new PutObjectCommand(parmas)
  await s3Client.send(command)
}

// 추가된 함수
async function getImageUrl(fileName) {
  const command = new GetObjectCommand({
    Bucket: process.env.AWS_S3_BUCKET_NAME,
    Key: fileName,
  })
  // 서명된 URL은 60초만 접근 가능
  const url = await getSignedUrl(s3Client, command, { expiresIn: 60 })
  return url
}

export async function POST(req) {
  try {
    const formData = await req.formData()
    const file = formData.get('file')

    if (!file) {
      return NextResponse.json({ error: 'File is required.' }, { status: 400 })
    }

    const fileExtension = file.name.split('.').slice(-1)[0]
    const newFileName = `${uuidv4()}.${fileExtension}`
    const fileType = file.type
    const buffer = Buffer.from(await file.arrayBuffer())

    await uploadFileToS3(buffer, newFileName, fileType)
	
    // imageUrl 생성 방식 변경
    // const imageUrl = `${process.env.AWS_S3_OBJECT_URL}/${newFileName}`
    const imageUrl = await getImageUrl(newFileName)

    return NextResponse.json(imageUrl)
  } catch (error) {
    return NextResponse.json({ error })
  }
}

- 이전 소스에서 새로 추가 및 변경된 부분은 getImageUrl 함수 추가와 imageUrl 변수 할당하는 값 변경

- getSignedUrl 파라미터 중 expiresIn의 단위는 '초'이다. 3600으로 설정하면 1시간 동안 이미지 URL 접근 가능

 

 

이미지 업로드폼에서 URL 주소 표기

/src/components/UploadForm.jsx

'use client'

import Image from 'next/image'
import { useState } from 'react'

export default function Home() {
  const [file, setFile] = useState(null)
  const [uploading, setUploading] = useState(false)
  const [imageUrl, setImageUrl] = useState('')

  function handleFileChange(event) {
    setFile(event.target.files[0])
  }

  async function handleSubmit(event) {
    event.preventDefault()
    if (!file) return

    setUploading(true)
    const formData = new FormData()
    formData.append('file', file)

    try {
      const response = await fetch('/api/upload', {
        method: 'POST',
        body: formData,
      })

      const data = await response.json()
      setImageUrl(data)
      setUploading(false)
    } catch (error) {
      setUploading(false)
    }
  }

  return (
    <>
      <h1>Upload Images to AWS S3</h1>

      <form onSubmit={handleSubmit}>
        <input type="file" accept="image/*" onChange={handleFileChange} />
        <button type="submit" disabled={!file || uploading}>
          {uploading ? 'Uploading...' : 'Upload'}
        </button>
      </form>
      
      <p>{imageUrl}</p>
      {imageUrl && (
        <Image src={imageUrl} width="300" height="300" alt="Image" />
      )}
      {!imageUrl && <h2>No image</h2>}
    </>
  )
}

- 이전과 동일한 코드이나 <p>{imageUrl}</p>만 추가

미리 서명된 URL을 가지고 주소창에 넣어서 접근 가능한지 테스트하기 위함 (반드시 필요한 작업은 아님)

 

 

 

[출처]

https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/Package/-aws-sdk-s3-request-presigner/

https://www.mtechzilla.com/blogs/how-to-upload-images-and-videos-to-amazon-s3-with-next-js-and-aws-sdk-v3

https://medium.com/@antoinewg/upload-a-file-to-s3-with-next-js-13-4-and-app-router-e04930601cd6

https://conermurphy.com/blog/presigned-urls-nextjs-s3-upload