image
를 storage와 db에 생성 및 업로드하고, 생성된 객체의 id들을 돌려준다.club
DB에 생성한다. (연결하기 위해)1-2-3번이 동기적으로 실행되어야하는데,
작업이 조금 걸리는 2번이 어째서인지 동기적으로 실행하지 못하여 3번이 받는 2번의 리턴값은 빈 배열이다.
//controller.ts
@Post()
async createClub(
@UploadedFiles() files: File[],
@Body() createClubDto: CreateClubDto,
) {
let imageIds = [];
if (files.length > 0) {
imageIds = await this.imageService.uploadImageOnClub(files);
}
console.log('[2] imageIds' + imageIds);
const createdClub = await this.clubService.createClub(
createClubDto,
imageIds,
);
return await this.findClubById(createdClub.id);
}
// service.ts
async uploadImageOnClub(images: File[], clubId?: number): Promise<number[]> {
let imageIds: number[] = [];
if (images.length !== 0) {
//현 이미지 업로드
images.map(async (image) => {
const file = await this.cloudStorageService.uploadFile(image, '');
const createdImage = await this.prisma.image.create({
data: {
imageUrl: file.publicUrl,
imageName: file.name,
type: 'CLUB',
},
});
console.log('[0] createdImage', createdImage.id);
imageIds.push(createdImage.id);
});
} else {
throw new BadRequestException(`Image did not transferred`);
}
console.log('[1] imageIds' + imageIds);
return imageIds;
}
내가 원하는 결과는 0 > 1 > 2
순서대로 출력되는 것을 기대하였다
하지만 실제 결과는 위 사진과 같이 1 > 2 > 0
으로 출력되어 [1]과 [2]의 배열에는 숫자25가 들어가지 않은! 빈 배열이 들어가게 되어 결과에 영향을 끼친다.
imageIds
에 빈 배열을 선언해주어서? ▶ 하지만 이 변수는 동기적인 흐름과는 전혀 관계가 없다..map()
이 동기적인 역할을 하지 못해서? ▶ 잘 모르겠지만 알아 볼 필요가 있다.map()
을 쓴 이유는 무엇인가?for
문도 위의 역할이 가능한데 왜 map
을 사용하였는가?for
문은 고안하고 있지 않았다. ▶ 이 대답은 정말 치명적인 문제인 것 같다.map()
의 역할은 무엇인가? for
과의 차이점은?for
문은 반환값이 없고, map
은 새로운 배열값을 반환(return) 한다 //현 이미지 업로드
const imageId = images.map(async (image) : Promise<number>=> {
...
const data = await this.prisma.image.create({
data: {
...
},
});
return data.id;
});
console.log('i',imageId)
결과값은 오른쪽과 같았다.
Promise 자체가 리턴된다.
왜 그런가, 찾아봤더니 map 안에서 async/await
함수가 들어갈 경우, Promise의 배열을 반환한다고 한다.
즉 for문을 사용하던가, map을 쓰겠다면 then으로 받아주던가..
그렇다면 for과 Promise 에 초점을 잡고, 테스트를 해보았다.
promise에 대한 글은 이 포스팅에서 이해하는데 도움을 많이 받았다.
const generatePromise = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(`generate Promise`)
}, 1000)
})
}
function generateError() {
return new Promise((resolve, reject) => {
setTimeout(() => { reject("[X] Error promise") }, 1000);
})
}
generatePromise().then((x) => console.log(x));
console.log("[1] start");
결과는 내 예상대로 1 > promise
출력되었다
하나씩 돌리면서 promise 객체를 받아보자 (시간도 같이 재기)
for문 안에서 도는 것 또한 동기적으로 하나씩 돌고 있는데, all을 통해 promise 작업을 병렬적으로 받을 수 있다.
allSettled는 실패한 작업이 있어도 무조건 실행하고, 구분된 반환값을 내뱉는다.
위 테스트한 내용을 기반으로 map 대신 for..of
를 사용했다.
async uploadImageOnClub(clubId: number , images: File[]){
const imageIds: number[] = [];
//현 이미지 업로드
for(let image of images){ //map 대신 for..of
const file = await this.cloudStorageService.uploadFile(image, '');
const data = await this.prisma.image.create({
data: {
imageUrl: file.publicUrl,
imageName: file.name,
type: 'CLUB',
ClubImage: {
create: {
clubId
}
}
},
});
console.log('[0] createdImage', data.id);
imageIds.push(data.id);
}
console.log('[1] imageIds' + imageIds);
return imageIds;
}
결과는!!! 0 > 1
순서대로 성공!!!
그런데 시간이 꽤 걸린다
이번엔 allSettled를 활용하여 코드를 작성해보았다.
async uploadImageOnClub(clubId: number , images: File[]){
//현 이미지 업로드
const upload: number[] = await this.saveClubImage(clubId, images); //하단
const result = await Promise.allSettled(upload)
const imageIds = result.filter((f) => f.status === 'fulfilled');
return imageIds;
}
async saveClubImage(clubId: number, images: File[]) {
const imageIds: number[] = [];
for (const image of images) {
const file = await this.cloudStorageService.uploadFile(image, '');
const data = await this.prisma.image.create({
data: {
imageUrl: file.publicUrl,
imageName: file.name,
type: 'CLUB',
ClubImage: {
create: {
clubId,
},
},
},
});
imageIds.push(data.id);
return imageIds;
}
}
1번과 동일한 파일을 업로드하였는데, 1번의 상황보다는 시간이 1/7보다도 적게 소모된다!!!!!!!
이 문제를 맞닥뜨리고 원인을 찾았을때, 나의 부족함도 그대로 마주했다..
js의 기본문법을 제대로 이해하고 있었다면, map안에서 async를 사용할 경우 Promise를 반환해 준다는 사실은 금방 알 수 있었을 것 같은 문제였다.
또한, 이 글은 2-3일 정도 혼자 끙끙 앓다가 선배에게 물어보며 힌트를 얻고,
인프콘 강연 및 여러 게시글들을 보며 에러를 해결하기 위해 생각해야할 것, 에러 해결 후 글 쓰는 법을 조금은 터득한 후 처음 쓴 글이었다.
이 전 블로그에서는 단순히 에러발생 현상과 결과만 작성하였는데
왜?라는 질문을 하며 짚어보니 혼자 끙끙 앓던 2-3일보다 훨씬 쉽게 원인에 닿을 수 있었다.
하.. 이 문제 해결 후에 도서관에서 자바스크립트 코딩의 기술
책을 빌려왔다.