AWS_S3_BUCKET=
AWS_S3_REGION=
AWS_S3_ACCESS_KEY_ID=
AWS_S3_SECRET_ACCESS_KEY=
NEXT_PUBLIC_AWS_CLOUDFRONT_URL=
NEXT_PUBLIC
s3
를 입력AWS_S3_BUCKET=
옆에 있는 이름 셀 텍스트를 복사하세요.
AWS_S3_REGION=
옆에 있는 AWS Region 셀 텍스트를 복사하세요.
실제 지역 이름을 복사하세요. 설명 텍스트 바로 옆에 kebab-cased
형식으로 되어야 합니다.
예를 들어, AWS Region US East(N. Virginia) us-east-1
에서는 us-east-1
만 복사하세요. 공백과 괄호는 무시하세요.'use server';
import { PutObjectCommand, PutObjectCommandInput, S3Client } from '@aws-sdk/client-s3';
const s3Client = new S3Client({
region: process.env.AWS_REGION as string,
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID as string,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY as string,
},
});
export async function uploadFileS3(file: File, fileName: string) {
const fileBuffer = (await file.arrayBuffer()) as Buffer;
const params: PutObjectCommandInput = {
Bucket: process.env.AWS_BUCKET_NAME as string,
Key: fileName,
Body: fileBuffer,
ContentType: file.type,
};
const command = new PutObjectCommand(params);
await s3Client.send(command);
}
export const submitImage = async (formData: FormData) => {
const image = formData.get('image');
if (typeof image !== 'object' || !image || image?.size === 0) {
return '';
}
// 영숫자가 아닌 문자를 영숫자로
// 코드에서 관리하기 더 쉬운 파일명을 생성
// eslint-disable-next-line no-control-regex
const asciiName = image.name.replace(/[^\\x00-\\x7F]/g, '').replace('', '_');
const fileName = `${new Date().getTime()}-${asciiName}`;
await uploadFileS3(image, fileName);
// TODO: DB work with URL
const url = `${process.env.NEXT_PUBLIC_IMAGE_URL_END_POINT}/${fileName}`;
return url;
};
'use client';
import { useState, useTransition } from 'react';
import { submitImage } from './serverAction';
const UploadForm = () => {
// form action does not support re-rendering
// during the action is in progress
// unless it is rendered with 'useTransition'
const [pending, onTransition] = useTransition();
const [uploadState, setUploadState] = useState('idle');
const [imgUrl, setImgUrl] = useState('');
const onSubmit = async (formData: FormData) => {
// client-side javascript validation is allowed.
// use trigger() in react-hook-form
const imgFile = formData.get('image') as File | null;
if (!imgFile) return;
const url = await submitImage(formData);
if (url === '') {
setUploadState('upload fail');
return;
}
setUploadState('file uploaded');
setImgUrl(url);
};
return (
<form
action={(formData: FormData) => {
onTransition(() => {
onSubmit(formData);
});
}}
className="flex flex-col"
>
{!pending && <p>upload state: {uploadState}</p>}
{pending && <p>uploading...</p>}
{imgUrl && (
<a href={imgUrl} target="_blank">
{imgUrl}
</a>
)}
<input type="file" name="image" />
<button type="submit" disabled={pending}>
upload
</button>
</form>
);
};
export default UploadForm;
File uploading with Next.js 14 app route, AWS S3, Cloudfront for Dummies, Part 2