반응형

모달창에서 입력폼이 있는데, 추가로 옵션이라는 항목이 생기게 됐다.

기존엔 모든 옵션들이 입력하고 확인하면 끝나는데 이 옵션값은 한단계 더 들어가야하기에 생각할게 많았다.

그 모달에서 저장이 바로 되도 안되고, 확인 후 이전 모달에서 저장된 값이 보이고 그 후에 모든 내용이 저장되야 했다.

기술적 어려움보다 로직을 생각하기가 어려웠다.

 

일단 기존의 추가되있을 수 있던 옵션들이나 1번 모달에서 삭제할 수 있는 로직을 짰다

  // 옵션 삭제시 필터링
  const removerOptionHandler = () => {
    const missingInMenuOptions = origineMenuOptions.filter(item =>
      menuOptions.some(menu => menu.menu_id === item.menu_id),
    );
    const missingItems = missingInMenuOptions.filter(item => !menuOptions.some(menu => menu.id === item.id));

    if (missingItems.length > 0) {
      missingItems.forEach(async item => {
        removeChangeMenuOptionsStore(item);
        await removeMenuOption(item.id);
      });
    }
  };

 

그리고 제일 중요한 옵션들을 먼저 정리하기 전에 팀원분의 가이드를 토대로 로직을 짰다.

가이드는 다음과 같다.

// 1. option정보를 담는 state를 3개 만든다.
menuOption, originalMenuOption,  changedMenuOption

// 2 . 옵션 데이터를 불러온 후 두 state에 똑같이 담는다.
setMenuOption(data), setOriginalMenuOption(data)

// 3. 변경 사항은 menuOption state에 업데이트 한다.

// 4. 변경 사항 저장시 두 state를 비교하며 값이 다른게 하나라도 있는 것은 changedMenuOption에 담는다.

// 5. menu_option을 뽑아 menu_option에 upsert를 한다.
// 6-1. state에서 뽑은 menu_option에 id가 없는 경우는 DB에 들어가지 않은 경우이므로 upsert후 id를 받은 후 menu_option_detail을 뽑아 upsert를 한다.
// 6-2. state에서 뽑은 menu_option에 id가 있는 경우는 그 id를 이용해 menu_option_detail을 upsert한다.

 

이 내용을 토대로 짠 코드는 다음과 같다.

  const filterOptionHandler = () => {
    const differences = findDifferences(menuOptions, origineMenuOptions);

    differences.map(async item => {
      if (item.id === '') {
        // 옵션 항목 supabase에 추가
        const newOption = {
          name: item.name,
          is_use: item.is_use,
          max_detail_count: item.max_detail_count,
          menu_id: item.menu_id,
        };
        const { data: optionData } = await addMenuOption(newOption);

        // 해당 data 받아서 그 option_id로 detail들 추가
        item.menu_option_detail.map(async option => {
          const addOptionForm: Omit<Tables<'menu_option_detail'>, 'id'> = {
            name: option.name,
            option_id: optionData[0].id,
            price: option.price,
          };
          await addUpsertMenuOptionDetail(addOptionForm);
        });
        const newOptionList: MenuOptionWithDetail = {
          id: optionData[0].id,
          name: optionData[0].name,
          is_use: optionData[0].is_use,
          max_detail_count: optionData[0].max_detail_count,
          menu_id: optionData[0].menu_id,
          menu_option_detail: item.menu_option_detail,
        };
        setChangeMenuOptions([...changeMenuOptions, { ...newOptionList }]);
      } else {
        // 옵션은 있는거니까 해당 detail을 옵션 아이디로 supabase 추가
        await updateMenuOption(item);
        const newOptionList: MenuOptionWithDetail = {
          id: item.id,
          name: item.name,
          is_use: item.is_use,
          max_detail_count: item.max_detail_count,
          menu_id: item.menu_id,
          menu_option_detail: [],
        };
        // 디테일 있는것도 있고 없는것도 있으니까 upsert
        item.menu_option_detail.map(async option => {
          const addOptionForm: Omit<Tables<'menu_option_detail'>, 'id'> | Tables<'menu_option_detail'> = {
            name: option.name,
            option_id: newOptionList.id,
            price: option.price,
          };

          if (option.id !== '') (addOptionForm as Tables<'menu_option_detail'>).id = option.id;

          await addUpsertMenuOptionDetail(addOptionForm);
        });
        updateChangeMenuOptionsStore(prevMenuOptions =>
          prevMenuOptions.map(option =>
            option.id === item.id
              ? {
                  ...item,
                  name: item.name ?? '',
                  is_use: item.is_use ?? false,
                  max_detail_count: item.max_detail_count ?? 1,
                }
              : item,
          ),
        );
      }
    });
  };

 

생각보다 가진 기능에 비해 로직이 길어서 맘에 썩 들진 않지만, 시간이 된다면 이 부분도 리팩토링 기간을 가지고 싶다

반응형