주문서 페이지 새로고침 시 데이터 유지 로직

67e71c23-e88f-41ba-bb74-0f0634338bed.MOV

페이지를 새로고침함으로서 발생하는 이벤트 중 하나인 beforeunload를 활용하여 현재 선택된 데이터를 세션스토리지에 저장합니다.

function useSaveStateOnUnload(storageKey: string, stateToSave: InitialUIStateType) {
    useEffect(() => {
        const handleBeforeUnload = () => {
            sessionStorage.setItem(storageKey, JSON.stringify(stateToSave))
        }

        window.addEventListener('beforeunload', handleBeforeUnload)

        return () => {
            window.removeEventListener('beforeunload', handleBeforeUnload)
        }
    }, [storageKey, stateToSave])
}

이후 API를 호출하여 데이터를 불러오고, 해당 데이터와 스토리지에 저장된 데이터를 비교합니다. 이 둘이 만약 다르다면, 사용자에게 데이터를 원복할건지 묻는 컨펌창을 띄우고, 수락시 스토리지에 저장된 데이터로 데이터를 원복합니다.

실제 API 요청 순서를 모사하기 위해 fetchOrderSheet를 먼저 디스패치한 뒤, postPointsReward를 디스패치합니다. 그 이유는 포인트 혜택에 대한 정보를 요청할 때 실제로는 주문서 정보를 가져온 뒤 그 안에 있는 아이템 id, 주문서 id 등을 활용하기 때문입니다.

useEffect(() => {
  const initialize = async () => {
    const storageData = loadStateFromSessionStorage(FORM_STATE_KEY);
    if (!storageData) {
        return
    }
    const orderSheetAction = await dispatch(fetchOrderSheet());
    const pointsRewardAction = await dispatch(postPointsReward())

asyncThunk를 통해 불러온 비동기 데이터가 에러인지 아닌지 확인합니다. 만약 에러인 경우, 스토리지 값을 제거하고 경고창을 띄워 에러 발생을 알립니다.

const orderSheetPayload = orderSheetAction.payload
const pointsRewardPayload = pointsRewardAction.payload
if (
    !orderSheetPayload ||
    !pointsRewardPayload ||
    checkErrorBaseType(orderSheetPayload) ||
    checkErrorBaseType(pointsRewardPayload)
) {
    removeStateFromSessionStorage(FORM_STATE_KEY)
    alert('데이터를 불러오는데 에러가 발생했습니다.')
    return
}

서버에서 가져온 데이터들을 스토리지에 저장된 데이터와 비교할 수 있도록 동일한 포맷으로 맞춥니다.

const selectedMemo = orderSheetPayload.deliveryAddress.deliveryMemos.find((memo) => memo.reuseMemo) ?? null

const initialUiStateFromServer: UiStateFromServer = {
    useVirtualPhoneNumber: false,
    selectedMemo,
    reuseMemo: !!selectedMemo,
    useAllCoupon: true,
}

깊은 비교인 isEqual을 통해 스토리지에 저장된 이전 데이터와 서버로부터 받아온 데이터를 비교합니다. 만약 사용자가 원복을 원하면 데이터들을 원복합니다. 이후 스토리지 값은 초기화됩니다.

if (
    !isEqual(storageData, initialUiStateFromServer) &&
    window.confirm('이전에 입력한 내용이 있습니다. 이어서 작성하시겠습니까?')
) {
    dispatch(restoreDeliveryAddressState(storageData))
    dispatch(restoreUseAllCouponState(storageData))
}

removeStateFromSessionStorage(FORM_STATE_KEY)