๐ ์ฟ ํฐ ์ ์ฒด ๋ฐ๊ธฐ ๊ธฐ๋ฅ์ ๊ฐ์ ํด๋ณด์!
๋ฌธ์ ์ํฉ
์ํ ์์ธ ํ์ด์ง(PDP) ๋ด ์ฟ ํฐ๋ฐ๊ธฐ ํ์
์์ ์ฟ ํฐ ์ ์ฒด ๋ฐ๊ธฐ
๋ฒํผ ํด๋ฆญ์, ์ผ๋ถ ์ ์ ๊ฐ ์ฟ ํฐ ๋ค์ด๋ก๋ ์คํจ ์๋ฟ์ด ๋ฐ๋ณต์ ์ผ๋ก ๋
ธ์ถ๋๋ค๋ CS ์ ์๋์๋ค. ์ฆ, ์ฌ์ฉ์๋ ์ ์ฒด ์ฟ ํฐ ๋ฐ๊ธฐ ๋ฒํผ์ ๋๋ ๋๋ฐ ์ค์ ๋ก๋ ๋ค์ด๋ก๋๊ฐ ์ผ๋ถ ์คํจํ๊ฑฐ๋ ์๋ฌ ๋ฉ์์ง๋ง ๋ณด๋ ์ํฉ
์์ธ ๋ถ์
DataDog์์ ์ฟ ํฐ ๋ค์ด๋ก๋์ ํธ์ถ๋๋ ๋ฒ ๋คํ API ํ์ธ. ์๋ฌ๋ก๊ทธ๋ฅผ ์ดํด๋ณด๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์์ ์ 0.1์ด ๊ฐ๊ฒฉ์ผ๋ก ํ ์ธ ์ฟ ํฐ์ด ์ด๋ฏธ ๋ฐ๊ธ๋์์ต๋๋ค.
์๋ฌ๋ก๊ทธ๊ฐ ์ฐํ์๋๊ฒ์ ๋ฐ๊ฒฌํจ.(01:12:00.520
, 01:12:00.521
, 01:12:00.522
, 01:12:00.525
...) ๊ทธ๋ฌ๋ ํ๋ก๊ทธ๋จ์ ์ฌ์ฉํ์ง ์๋ ์ด์ ์ ์ ๊ฐ 0.1์ด ๊ฐ๊ฒฉ์ผ๋ก ๋ค์ด๋ก๋ ๋ฒํผ์ ๋๋ฅผ ์ ์๊ธฐ๋๋ฌธ์ ์ฟ ํฐ ์ ์ฒด ๋ฐ๊ธฐ ๋ก์ง์์ ์๋ฌ๊ฐ ๋ฐ์ํ๋ค๊ณ ํ๋จํ์ฌ ์ด๋ฅผ ๊ฐ์ ํจ
๊ฐ์ ๋ฐฉ์
Promise.allSettled
์ฌ์ฉ
- ๊ธฐ์กด:
Promise.all
→ ํ๋๋ผ๋ ์คํจํ๋ฉด ์ ์ฒด๊ฐ rejected ์ํ๋์ด ๊ฐ๊ฐ์ ์ฟ ํฐ ๋ค์ด๋ก๋ ๊ฒฐ๊ณผ๋ฅผ ์ถ์ ํ ์ ์์ - ๊ฐ์ :
Promise.allSettled
→ ๊ฐ ์ฟ ํฐ์ ์ฑ๊ณต/์คํจ ์ฌ๋ถ๋ฅผ ๊ฐ๋ณ์ ์ผ๋ก ํ์ธ ๊ฐ๋ฅ
์ด๋ฅผ ํตํด “๋ช ์ฅ์ด ์ฑ๊ณตํ๊ณ ๋ช ์ฅ์ด ์คํจํ๋์ง”๋ฅผ ์ง๊ณํ ์ ์์ต๋๋ค.
- ์ฌ์ฉ์ ์นํ์ ์ธ ์๋ฌ ๋ฉ์์ง
- ๊ธฐ์กด:
์ฟ ํฐ์ ๋ค์ด๋ก๋ํ๋๋ฐ ์คํจํ์ต๋๋ค. ์ ์ ํ ๋ค์ ์๋ํด์ฃผ์ธ์.
- ๊ฐ์ :
์ด 5์ฅ์ ์ฟ ํฐ ์ค 4์ฅ์ ๋ฐ์์ผ๋ฉฐ, 1์ฅ์ ๋ฐ๋ ๋ฐ ์คํจํ์ต๋๋ค. ํ์ธ ๋ฒํผ์ ๋๋ฌ ์ฟ ํฐ ๋ฆฌ์คํธ๋ฅผ ๋ค์ ํ์ธํด ์ฃผ์ธ์.
์ ์ ๊ฐ ์ค์ ์ํฉ์ ํฌ๋ช ํ๊ฒ ์ธ์งํ๋๋ก ์๋ฌ ๋ฉ์์ง๋ฅผ ๊ฐ์
- ์คํจ์ ์ฟ ํฐ ๋ชฉ๋ก ์ต์ ํ: ๋ค์ด๋ก๋ ์คํจํ ์ฟ ํฐ์ ์ฌ์ ํ ํด๋ฆญ ๊ฐ๋ฅํ ์ํ๋ก ๋จ์ ๋ค์ ์๋ฌ๋ฅผ ๋ฐ์์ํค๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํ์ฌ ๋ค์ด๋ก๋ ์คํจ์ refetch()๋ฅผ ํตํด ์ต์ ์ฟ ํฐ ๋ชฉ๋ก์ ๋ค์ ๋ถ๋ฌ์ค๋๋ก ์์
์ฝ๋ ๋น๊ต
// AS-IS
const handleDownloadAllExtraCoupons = useCallback(() => {
try {
await Promise.all(downloadableCouponIdList.map((couponId) => fetchCouponDownload(couponId)))
setAlert({
open: true,
message: `${downloadableCouponIdList.length}์ฅ์ ์ฟ ํฐ์ด ๋ค์ด๋ก๋ ๋์์ต๋๋ค. ์๋์ผ๋ก ์ต๋ ํํ ์ฟ ํฐ์ด ์ ์ฉ๋์์ต๋๋ค.`,
})
setCouponDefinitions((coupons) => {
const newCoupons = coupons.map((coupon) =>
downloadableCouponIdList.includes(coupon.id) ? { ...coupon, downloaded: true } : coupon,
)
return newCoupons
})
} catch {
setAlert({
open: true,
message: '์ฟ ํฐ์ ๋ค์ด๋ก๋ํ๋๋ฐ ์คํจํ์ต๋๋ค. ์ ์ ํ ๋ค์ ์๋ํด์ฃผ์ธ์.',
})
}
})
// TO-BE
const handleDownloadAllExtraCoupons = useCallback(async () => {
let successfulCouponDownloadCount = 0 // ๊ฐ์ด ๋ฐ๋์ด๋ ์ ์ ๋ทฐ์ ๋ฆฌ๋ ๋๋ง ํ์ง์์๋ ๋๊ณ , ์ปจํ
์คํธ ๋ด๋ถ์์๋ง ์ฌ์ฉํ๋ ์์๊ฐ์ด๋ฏ๋ก useState ๋์ let ์ฌ์ฉ
let failedCouponDownloadCount = 0
try {
const downloadAllCouponsResponse = await Promise.allSettled(
downloadableCouponIdList.map(async (couponId) => {
const response = await fetchCouponDownload(couponId)
if (response.status === 200) {
setCouponDefinitions((coupons) => {
const newCoupons = coupons.map((coupon) =>
coupon.id === couponId ? { ...coupon, downloaded: true } : coupon,
)
return newCoupons
})
}
return response
}),
)
successfulCouponDownloadCount = downloadAllCouponsResponse.filter(
(coupon) => coupon.status === 'fulfilled' && coupon.value.status === 200,
).length
failedCouponDownloadCount = downloadAllCouponsResponse.filter(
(coupon) =>
(coupon.status === 'fulfilled' && coupon.value.status !== 200) ||
coupon.status === 'rejected',
).length
if (failedCouponDownloadCount > 0) { // ์คํจํ ์ฟ ํฐ์ด ์กด์ฌํ๋๊ฒฝ์ฐ catch์ ๋ก ์ด๋ํ๊ธฐ์ํด throw error
throw new Error(
`Some coupons failed to download. (Total: ${downloadableCouponIdList.length}, Success: ${successfulCouponDownloadCount}, Failed: ${failedCouponDownloadCount})`,
)
}
setAlert({
open: true,
message: `${successfulCouponDownloadCount}์ฅ์ ์ฟ ํฐ์ด ๋ค์ด๋ก๋ ๋์์ต๋๋ค. ์๋์ผ๋ก ์ต๋ ํํ ์ฟ ํฐ์ด ์ ์ฉ๋์์ต๋๋ค.`,
})
} catch {
setAlert({
open: true,
message: `์ด ${downloadableCouponIdList.length}์ฅ์ ์ฟ ํฐ ์ค ${successfulCouponDownloadCount}์ฅ์ ๋ฐ์์ผ๋ฉฐ, ${failedCouponDownloadCount}์ฅ์ ๋ฐ๋ ๋ฐ ์คํจํ์ต๋๋ค.<br/>ํ์ธ ๋ฒํผ์ ๋๋ฌ ์ฟ ํฐ ๋ฆฌ์คํธ๋ฅผ ๋ค์ ํ์ธํด ์ฃผ์ธ์.`,
onConfirm: () => {
refetch() // ์ฟ ํฐ ๋ชฉ๋ก์ ์ต์ ํํ๊ธฐ์ํด refetch ์ฌ์ฉ
},
})
}
}, [downloadableCouponIdList, setAlert, setCouponDefinitions, refetch])
๊ฒฐ๋ก
- Promise.allSettled์ ์ฌ์ฉํด ๋ค์ด๋ก๋ ์ฑ๊ณต/์คํจ๋ฅผ ์ธ๋ฐํ๊ฒ ์ถ์ ํจ
- ์ฌ์ฉ์์๊ฒ๋ ์คํจํ ์ด์ ์ ํํฉ์ ์น์ ํ๊ฒ ์๋ด
- ์ฟ ํฐ ๋ค์ด๋ก๋ ์คํจ ์์๋ ์ฟ ํฐ ๋ชฉ๋ก์ ์ต์ ํํ์ฌ ๋ฐ๋ณต ์ค๋ฅ ๋ฐฉ์ง
์ด ๊ณผ์ ์ ํตํด ์ฌ์ฉ์ ๊ฒฝํ(UX)์ ๊ฐ์ ํ๊ณ , ๋ถํ์ํ CS ๊ฐ์๋ฅผ ๊ธฐ๋ ํ ์ ์์ต๋๋ค.
๋๊ธ