async/await문제(return의 중요성)

FAST FOX·2023년 5월 11일
0

DressCode개발일지

목록 보기
3/6
post-thumbnail

코드 설명

새로운 옷에 대한 정보를 firebase의 database에 저장하는 과정 중에 생긴 문제가 생겼다. 데이터는 객체 형태로 저장하려고 했고 다음과 같이 표현하였다.

const { uploadUrl_0, uploadUrl_1 } = imgData;
const { season, part, brand, price, details } = clothesData;

const newClothes = {
	docName: uuidv4(),
	favorite: false,
    id: user.uid,
    imageUrl_1: null,
    imageUrl_2: null,
    season: season,
    part: part,
    brand: brand,
    price: price,
    details: details,
};

const storage = getStorage();
const spaceRef_1 = ref(storage,`${user.uid}/clothes/${uuidv4()}`);
console.log("start upload first image");
await uploadBytes(spaceRef_1, uploadUrl_0).then((snapshot) => {
	getDownloadURL(snapshot.ref).then((url) => {
      	console.log("1",url)
		newClothes.imageUrl_1 = url;
	});
});
if (uploadUrl_1) {
	const spaceRef_2 = ref(storage, `${user.uid}/clothes/${uuidv4()}`);
  	console.log("start upload second image")
	await uploadBytes(spaceRef_2, uploadUrl_1)
		.then((snapshot) => {
			getDownloadURL(snapshot.ref).then((url) => {
              console.log("2",url)
              newClothes.imageUrl_2 = url;
            });
		})
}
console.log("start store data")
const collectionRef = collection(db, "clothes");
await setDoc(doc(collectionRef, newClothes.docName), newClothes);

문제점

내 의도는 이미지 두개가 정상적으로 값이 있다면 firebase의 storage에 저장하고 다운로드 받은 url을 newClothes 객체에 재할당하고 database에 저장했지만 1번 이미지의 url만 저장되고 2번 이미지의 url은 저장되지 않는 문제가 발생하였다.

문제 원인 생각하기

  1. url 자체가 정상적으로 들어오지 않는가??

  2. 내가 async/await의 상용 방법을 잘못 알고 있나??

문제 해결 시도

두 번째 이미지를 업로드하고 getDownloadURL를 해줬을 때 반환 값인 url은 console.log로 찍어봤을 때 정상적으로 반환되는걸 확인했다.

그렇다면 내가 async/await의 흐름 자체를 잘못 이해하고 사용하고 있다는 말이었다. 코드가 실행되는 중간중간에 임의의 문자열을 콘솔에 나타내며 순서를 알아봤는데 다음과 같이 코드가 실행되었다.

내가 예상하기로는 await가 사용된 코드가 끝나기 전까지는 다음 코드가 실행되지 않는다고 생각했는데 그것과 별개로 두 번재 image의 url을 다운받기 전에 데이터가 저장되는 결과가 나왔다.

기존의 async/awiat의 사용법과 내 코드가 다른 점을 찾아본다면 then 메소드에서 반환하는 값이 없다는 것이다. 내 생각으로는 newClothes라는 객체에 데이터를 저장하기만 하면 되는거라 굳이 반환값이 필요한가 싶은 생각에 굳이 반환값을 지정하지 않은 것이다.

반환 값이 있고 없고의 차이를 찾아보니 return의 존재 유•무 자체에 차이가 있다는 걸 알 수 있었다.

return은 우리가 흔히 아는 것처럼 해당 함수가 값을 반환하고 함수를 종료시키는 역할을 가지고 있다. 따라서 return을 사용하면, 해당 함수가 끝나기 전까지 다음 코드가 실행되지 않는 것이다.
하지만 내가 만든 코드에서 비동기 함수인 getDownloadURL()에 return이 없기 때문에 await가 작동되지 않고 다음 코드로 넘어가게 되는 것이었다.

문제 해결

기존의 코드에서 getDonwloadURL메소드 앞에 return을 주는 방식으로 코드를 작성했다.

await uploadBytes(spaceRef_1, uploadUrl_0).then((snapshot) => {
	return getDownloadURL(snapshot.ref).then((url) => {
		newClothes.imageUrl_1 = url;
	});
});
if (uploadUrl_1) {
	const spaceRef_2 = ref(storage, `${user.uid}/clothes/${uuidv4()}`);
	await uploadBytes(spaceRef_2, uploadUrl_1).then((snapshot) => {
		return getDownloadURL(snapshot.ref).then((url) => {
			newClothes.imageUrl_2 = url;
		});
	});
}

추가 개선

await uploadBytes(spaceRef_1, uploadUrl_0).then((snapshot) => {
	return getDownloadURL(snapshot.ref).then((url) => {
		newClothes.imageUrl_1 = url;
});

위의 코드는 이미지를 firebase의 Storage에 업로드하는 코드인데 해당 컴포넌트에서도 두번 사용을 하고 있고 추후에도 사용할 코드이기 때문에 최근에 알게 된 Wrapper Function을 사용해서 코드를 정리해보기로 했다.

우선은 uploadBytes.js라는 새로운 파일을 생성해주고 다음과 같이 함수를 옮겨주었다.

그 후에는 래퍼함수에 필요한 인자들을 넣어주고 upload라는 래퍼함수가 다운로드 된 url을 반환하므로 저장할 데이터의 값으로 바로 연결시켜주었다.

코드를 위와 같이 변경하면서 추후에 다른 곳에서도 이미지를 업로드 할 때 함수를 새롭게 구현할 필요없이 기존의 래퍼함수를 호출하는게 가능해졌다.

profile
준비하는 개발자

0개의 댓글