서버에게 부하를 최대한 줄이기 위해, 클라이언트에서 이미지를 업로드 하자!
클라이언트에서 Object Storage 업로드 하려고 찾아보니까 계속 CORS 오류가 뜸..
(=브라우저에서 바로 storage에 업로드 할 수 없음. 무조건 서버를 거쳐가야함)
찾아보니 클라이언트 측에서 직접 업로드 하는 방식은 서버로부터 인증된 url(signed require url)을 받아서, 업로드 하는 것 같다.
https://medium.com/@cpatarun/client-side-vs-server-side-file-upload-to-amazon-s3-5c46ac0f4c7f
curl
로 확인해도 잘 받아옴 (참고: https://aws.amazon.com/ko/premiumsupport/knowledge-center/s3-configure-cors/)
multer
라는 미들웨어를 사용
단점. 업로드가 전적으로 서버에게 맡겨지는 것이기 때문에, 서버의 부하가 존재할 수 있을 것 같다.
https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#getSignedUrl-property
export const makeSignedURL = (S3:AWS.S3, contentHash:String, musicName:String):string =>{
const params = {Bucket: 'xoxo', Key: `${contentHash}/${musicName}`, Expires: 60};
const url = S3.getSignedUrl('putObject', params);
return url;
}
const getSignedURL = async (request: Request, response: Response, next: NextFunction) => {
const { contentTypes } = request.body;
const presignedData = await Promise.all(
contentTypes
.filter((type: string) => ['png', 'jpg', 'jpeg', 'webp'].includes(String(extension(type))))
.map(async (type: string) => {
const fileExtension = extension(type);
const key = `${uuid()}.${fileExtension}`;
const presignedURL = await s3.getSignedUrlPromise('putObject', {
Bucket: process.env.NCP_BUCKET_NAME,
Key: `raw/${key}`,
Expires: 60,
ContentType: type,
ACL: 'public-read',
});
return { presignedURL, key };
}),
);
response.json(succeed(presignedData));
}
이런식으로 서버가 signed url을 만들어서 클라이언트가 요청하면 내려주는 것.
(서버가 올릴 파일에 대한 경로와 이름을 다 설정한 url을 내려주고, 클라이언트는 그걸 받아서 파일만 업로드 하는 것임!)
단점. 아무래도 보안에 있어서 좀 취약해질 수도 있을 것 같다. (누군가가 url을 탈취한다면 기존 파일의 이름과 경로가 동일한 파일을 업로드 할 수 있음⇒파일이 덮어써질 가능성이 있음)
https://campkim.tistory.com/66
https://aws.amazon.com/ko/blogs/compute/uploading-to-amazon-s3-directly-from-a-web-or-mobile-application/