!!eduArray.length &&
eduArray.forEach(async (edu) => {
const eduFormData = {
post_id: postId,
user_uuid: userId,
...edu
};
const { data: eduPostDtat, error: eduPostError } = await browserClient
.from('post_detail_education')
.insert([eduFormData]);
});
!!expArray.length &&
expArray.forEach(async (exp) => {
const expFormData = {
post_id: postId,
user_uuid: userId,
...exp
};
const { data: expPostDtat, error: expPostError } = await browserClient
.from('post_detail_experience')
.insert([expFormData]);
});
!!licArray.length &&
licArray.forEach(async (lic) => {
const licFormData = {
post_id: postId,
user_uuid: userId,
...lic
};
const { data: licPostDtat, error: licPostError } = await browserClient
.from('post_detail_license')
.insert([licFormData]);
});
이번 팀프로젝트에서 이력서에 들어가는 학력, 경력, 자격증 관련 데이터뭉치들을 supabase에 올려주는 작업을 진행했다.
근데 세개를 한번에 하나씩 실행하다보니 좀 비효율적이라는 생각이 들었다.
그래서 여러 promise요청을 병렬적으로 실행시켜주는 promise.all을 사용해보고자 공식 문서를 읽어보았다.
Promise.all()의 괄호안에 배열속에 어떤 요청들을 넣어주면 병렬적으로 그 요청이 실행되는 것이다.
하지만 이런 의문이 들었다.
const a = new Promise(resolve=>resolve(3초가 걸리는 작업))
const b = new Promise(resolve=>resolve(20초가 걸리는 작업))
const c = new Promise(resolve=>resolve(3초가 걸리는 작업))
Promise.all([a,b,c]).then(...).catch(...)
이렇게 여러 작업을 한번에 진행할 때 a,c는 b보다 일찍 fulfilled될 것이다.
그렇다는 의미는 셋 다 post요청일 때 이미 a,c요청에 대한 데이터가 DB에 저장된다는 것이다.
그리고 b에서 오류가 발생해버린다면? 그런데 세 데이터가 모두 한 데이터와 연관이 있어서 하나라도 실패하면 무의미해진다면?
그럼이제 귀찮아지는거다
어쨋든 한번 화면으로 살펴보자
const a = new Promise((resolve) => {
setTimeout(() => {
resolve(console.log("a끝!"));
}, 3000);
});
const b = new Promise((resolve, reject) => {
setTimeout(() => {
reject(console.log("b는 끝내지 못했어!"));
// resolve(console.log("b끝!"));
}, 6000);
});
const c = new Promise((resolve) => {
setTimeout(() => {
resolve(console.log("c끝!"));
}, 4000);
});
Promise.all([a, b, c])
.then(() => console.log("then"))
.catch(() => console.log("catch"));
이런 코드가 있다고 가정해보자. 실패할 예정인 b로직은 6초후에 실패하고 성공할 예정인 a,c로직은 3,4초뒤에 성공한다
mdn에 애매하게 설명해둔 설명대로라면 모두 실행되지않고 b가 실패해 b는 끝내지 못했어! 만 출력될 것 같지만
실상은 그렇지 않다.
그럼 Promise.all은 도대체 어떤 녀석일까
말 그대로 모든 요청을 한번에 .then이나 .catch로 보내는 것이다.
내부에서는 따로따로 요청이 처리되고 후 로직을 기다리고 있는 것
const a =
!!eduArray.length &&
eduArray.forEach(async (edu) => {
//...
const { data: eduPostDtat, error: eduPostError } = await browserClient
.from('post_detail_education')
.insert([eduFormData]);
});
const b =
!!expArray.length &&
expArray.forEach(async (exp) => {
//...
const { data: expPostDtat, error: expPostError } = await browserClient
.from('post_detail_experience')
.insert([expFormData]);
});
const c =
!!licArray.length &&
licArray.forEach(async (lic) => {
//...
const { data: licPostDtat, error: licPostError } = await browserClient
.from('post_detail_license')
.insert([licFormData]);
});
const d = new Promise((reject) => {
setTimeout(() => reject('실패!'), 5000);
});
Promise.all([a, b, c, d])
.then((res) => console.log('res :>> ', res))
.catch((err) => console.log('err :>> ', err));
};
이렇게 supabase에 insert하는 요청을 Promise.all로 처리한다면 insert인 post요청이 각각 일어나고, 결국엔 catch로 에러처리가 될 것이다.
이 데이터를 각각의 table에 insert하는 요청은 일어나고, d라는 Promise가 reject되므로 catch로 이어지는 것이다.
실제로 데이터는 각 테이블에 들어갔지만, catch처리된 모습을 볼 수 있다.
그렇다면 저렇게 의미없어진(연결이 끊긴)데이터를 어떻게 처리해야할까?
저런 요청단위를 DB에선 Transaction이라고 부른다고 한다.
DB의 상태를 변화시키기 위해 수행하는 작업 단위라고 하는데 뭐 어쨌든 깊이 들어가지않겠다 BE의 영역이기 때문에
결국 난 위 사진과 같은 rollback의 기능이 필요하다.
const insertEducation = async (eduArray, postId, userId) => {
try {
await Promise.all(
eduArray.map(async (edu) => {
const eduFormData = {
post_id: postId,
user_uuid: userId,
...edu,
};
const { error: eduPostError } = await browserClient
.from("post_detail_education")
.insert([eduFormData]);
if (eduPostError) throw new Error("Education insert failed");
})
);
return { success: true };
} catch (error) {
return { success: false, error: "education", message: error.message };
}
};
const insertExperience = async (expArray, postId, userId) => {
try {
await Promise.all(
expArray.map(async (exp) => {
const expFormData = {
post_id: postId,
user_uuid: userId,
...exp,
};
const { error: expPostError } = await browserClient
.from("post_detail_experience")
.insert([expFormData]);
if (expPostError) throw new Error("Experience insert failed");
})
);
return { success: true };
} catch (error) {
return { success: false, error: "experience", message: error.message };
}
};
const insertLicense = async (licArray, postId, userId) => {
try {
await Promise.all(
licArray.map(async (lic) => {
const licFormData = {
post_id: postId,
user_uuid: userId,
...lic,
};
const { error: licPostError } = await browserClient
.from("post_detail_license")
.insert([licFormData]);
if (licPostError) throw new Error("License insert failed");
})
);
return { success: true };
} catch (error) {
return { success: false, error: "license", message: error.message };
}
};
const handleRollback = async (postId) => {
// 삽입 실패 시 rollback 처리
console.log("Rolling back changes...");
await browserClient.from("post_detail_education").delete().eq("post_id", postId);
await browserClient.from("post_detail_experience").delete().eq("post_id", postId);
await browserClient.from("post_detail_license").delete().eq("post_id", postId);
console.log("Rollback completed");
};
const runInserts = async () => {
const postId = "post123"; // 예시 postId
const userId = "user123"; // 예시 userId
const eduArray = [{ title: "edu1" }, { title: "edu2" }];
const expArray = [{ title: "exp1" }];
const licArray = [{ title: "lic1" }];
try {
// Promise.all로 모든 삽입 작업 실행
const results = await Promise.all([
insertEducation(eduArray, postId, userId),
insertExperience(expArray, postId, userId),
insertLicense(licArray, postId, userId),
]);
// 실패한 작업이 있는지 확인
const failed = results.find((result) => !result.success);
if (failed) {
throw new Error(`Failed on ${failed.error}: ${failed.message}`);
}
console.log("All inserts successful");
} catch (err) {
console.log("Error occurred:", err.message);
await handleRollback(postId); // 실패 시 롤백
}
};
runInserts();
뭐 이렇게 처리해야한다고 한다. 솔직히말하면 감당이 안된다. 조금 더 Promise.all을 사용해야하는지 고민을 해봐야할것같다 ㅋㅋ
'2차 공부 > TIL' 카테고리의 다른 글
24.10.19 NextJS react-hook-form (0) | 2024.10.19 |
---|---|
24.10.15 tailwind에서 제공하지 않는 style속성 사용하기 (0) | 2024.10.15 |
24.10.11 react에서 리스트 매핑중 key를 사용해야하는 이유 (0) | 2024.10.11 |
24.10.08 개인프로젝트 마무리 (2) | 2024.10.08 |
24.10.07 nextjs 에러핸들링 (0) | 2024.10.08 |