presigned url 은 단어 그대로 미리 서명된 url을 받는 것이다.
AWS에서 제공하는 S3 스토리지에 Bucket Policy나 acl에 관계없이 자격증명이 된 사용자가 접근할 수 있도록 url을 만들어 제공하는 것이다.
일단 S3 스토리지에 올라가는 프로세스를 확인 해 보자.
위 과정에서 문제점

(나의 경우에는 업로드 요청 시 fileType을 뽑아내서 서버에 요청했다. 파일 타입 검증을 위해)
const addProduct = async () => {
try {
if (files) {
const fileTypes = files.map((file) => {
return file.type
})
const presignedData = await getPresignedUrl(fileTypes, 'product')
const result = await Promise.all(
files.map((file, idx) => {
// 새로운 formData를 만들어주고 presigned로 받은 데이터들 추가
const formData = new FormData()
const { presigned } = presignedData[idx] //
for (const key in presigned.fields) {
formData.append(key, presigned.fields[key])
console.log(formData.get(key))
}
formData.append('Content-Type', file.type)
formData.append('file', file)
return axios.post(presigned.url, formData)
})
)
/**
* @Todo: 받은 url로 사진 보낼 때 403 에러 해결해야함 -
* @Done: presigned url 받아오는 것 까진 문제가 없음
* @Todo: files를 map 돌렸을 때 같은 값이 formData에 들어가고 있다. 왜?
* @해결: formData 를 map 돌릴때마다 새로 만들어줘야 하는데 map 부분 위에서 만들어서 중복 추가 되는거였다.
*/
}
} catch (error) {
console.error(error)
} 첫 번째 방법으로 받아온 presigned 값
두 번째 코드
const addProduct = async () => {
try {
if (files) {
const fileTypes = files.map((file) => {
return file.type
})
const presignedData = await getPresignedUrl(fileTypes, 'product')
// 아래와 같은 presigned 값의 배열이 오게 되는데 물음표 전 까지의 url만 뽑아내서 쓰면된다
const res = await Promise.all(
files.map((file, idx) => {
// url을 split을 통해 뽑아낸다
const url = presignedData[idx].signenUrlPut.split('?')[0]
return axios.put(url, file, {
headers: { 'Content-Type': file.type },
})
})
)
}
} catch (error) {
console.error(error)
}
}
두 번째 방법으로 받아온 presigned 값

"https://kt-first-bucket.s3.ap-northeast-2.amazonaws.com/product/ba3df5b1-5f2e-41aa-9ddd-81f6fcbb2043.png
?X-Amz-Algorithm=AWS4-HMAC-SHA256
&X-Amz-Credential=AKIAXZ4K2VPPHG6IQ5HU%2F20230817%2Fap-northeast-2%2Fs3%2Faws4_request
&X-Amz-Date=20230817T073135Z&X-Amz-Expires=3600
&X-Amz-Signature=9f5cfca82c47497316c280c5f884690fcac7c79de352a5baaaf749cc18763c17
&X-Amz-SignedHeaders=host"
// aws.controller.ts
@Post('/presignedurl')
async getPresignedUrl(@Body() { types }: { types: string[] }) {
return this.awsService.getPresignedUrl(types);
}
// aws.service.ts
export class AwsService {
private readonly bucketName: string;
private readonly s3: S3;
constructor(private configService: ConfigService) {
this.bucketName = this.configService.get('AWS_BUCKET_NAME');
// s3 환경 설정
this.s3 = new S3({
accessKeyId: this.configService.get('AWS_ACCESS_KEY_ID'),
secretAccessKey: this.configService.get('AWS_SECRET_ACCESS_KEY'),
region: this.configService.get('AWS_BUCKET_REGION'),
});
}
// 파일 타입들을 받아서 key를 만들어 createPresigned 함수의 키로 넘겨준다
async getPresignedUrl(fileTypes: string[]) {
return Promise.all(
fileTypes.map(async (fileType) => {
const extension = fileType.split('/')[1]; // 파일 확장자 ex) png
const imageKey = `${randomUUID()}.${extension}`; // image 이름
const key = `product/${imageKey}`; // 경로 및 image 이름
const presigned = await this.createPresigned(key);
return { imageKey, presigned };
}),
);
// key를 인수로 받아 Fields에 설정 및 Bucket, 만료 시간, conditions(파일에 대한 정보) 작성
async createPresigned(key) {
return this.s3.createPresignedPost({
Bucket: this.bucketName,
Fields: {
key,
},
Expires: 60 * 60,
Conditions: [
['content-length-range', 0, 20 * 1000 * 1000], // 0 ~ 20MB
['starts-with', '$Content-Type', 'image/'],
],
});
}
}async getPresignedUrl(fileTypes: string[], bucket: string) {
const result = await Promise.all(
fileTypes.map(async (fileType) => {
const extension = fileType.split('/')[1];
const imageKey = `${randomUUID()}.${extension}`;
const key = `${bucket}/${imageKey}`;
const signenUrlPut = await this.s3.getSignedUrlPromise('putObject', {
Bucket: this.bucketName,
Key: key,
Expires: 60 * 60,
});
return signenUrlPut;
}),
);
return result;
}
/**
* 'getObject': 객체를 가져오는 작업을 나타냅니다. 이 작업은 S3 버킷의 객체를 읽어오는 데 사용됩니다.
'putObject': 객체를 업로드하는 작업을 나타냅니다. 이 작업은 파일을 S3 버킷에 업로드하는 데 사용됩니다.
'deleteObject': 객체를 삭제하는 작업을 나타냅니다. 이 작업은 S3 버킷에서 객체를 삭제하는 데 사용됩니다.
'listObjects': 객체 목록을 나열하는 작업을 나타냅니다. 이 작업은 S3 버킷 내의 객체들의 목록을 가져오는 데 사용됩니다.
'copyObject': 객체를 복사하는 작업을 나타냅니다. 이 작업은 S3 버킷 내에서 객체를 복사하는 데 사용됩니다.
// */