타입스크립트 코드 리펙토링

요즘 Vue로 개발중인데 아직 타입스크립트에 익숙해지지 못했다. 그 와중에 기능 개발 요청이 있어서 코드를 구현하고 리펙토링 까지 한 경험을 얘기해보려고 한다.

요구사항

예약 프로그램에서 예약인원을 설정해야하는데 기존에는 단순히 성인, 아동, 유아 의 인원을 설정할 수 있고 합이 9명까지만 설정이 가능했다. 이 부분을 다음과 같은 요구사항으로 변경이 필요하다.

  1. 성인은 기본이 1명이고 나머지는 기본이 0명이다.
  2. 유아의 인원수는 성인의 인원수를 넘을 수 없다.
  3. 위 조건에 따라 마이너스, 플러스 버튼이 상황에 맞게 활성,비활성화 처리가 되어야 한다.

개발진행

보기에는 간단해보였는데 구현중에 사이드 이펙트가 많이 발생했다. 처음에 대충 생각해서 진행했더니 생각지 못한 부분들의 오류가 많이 발생해서(예를들면, 성인 유아수가 동일한데 아동수를 꽉체웠다가 풀었더니 마이너스 버튼이 전부 활성화 된다던지…) 힘들었다. 시행착오 끝에 아래와 같은 코드가 완성됬다.

const adultMinusDisabled = ref(true);
const adultPlusDisabled = ref(false);
const childMinusDisabled = ref(true);
const childPlusDisabled = ref(false);
const babyMinusDisabled = ref(true);
const babyPlusDisabled = ref(false);

const maxPersonCount = 9;

const currentCount = ref(departSeatAdult.value + departSeatChild.value + departSeatBaby.value);

watch(currentCount, () => {

  // 1. 성인 인원은 1보다 작을수 없다.
  // 2. 소아, 유아 인원이 0보다 작을 수 없다.
  // 3. 합산 값이 9보다 클수 없다.
  // 4. 성인 인원을 유아 인원이 넘을수 없다. 

  if(currentCount.value === 1) {
    adultMinusDisabled.value = true;
    adultPlusDisabled.value = false;
    childMinusDisabled.value = true;
    childPlusDisabled.value = false;
    babyMinusDisabled.value = true;
    babyPlusDisabled.value = false;
  } else if(currentCount.value === maxPersonCount) {
    if(departSeatAdult.value === departSeatBaby.value) {
      adultMinusDisabled.value = true;
      adultPlusDisabled.value = true;
      childMinusDisabled.value = false;
      childPlusDisabled.value = true;
      babyMinusDisabled.value = false;
      babyPlusDisabled.value = true;
    } else {
      adultMinusDisabled.value = false;
      adultPlusDisabled.value = true;
      childMinusDisabled.value = false;
      childPlusDisabled.value = true;
      babyMinusDisabled.value = false;
      babyPlusDisabled.value = true;
    }
  } else {
    if(departSeatAdult.value === departSeatBaby.value) {
      adultMinusDisabled.value = true;
      adultPlusDisabled.value = false;
      if(departSeatChild.value === 0) {
        childMinusDisabled.value = true;
      } else {
        childMinusDisabled.value = false;
      }
      childPlusDisabled.value = false;
      babyMinusDisabled.value = false;
      babyPlusDisabled.value = true;
    } else {
      if(departSeatAdult.value === 1) {
        adultMinusDisabled.value = true;
      } else {
        adultMinusDisabled.value = false;
      }
      adultPlusDisabled.value = false;
      if(departSeatChild.value === 0) {
        childMinusDisabled.value = true;
      } else {
        childMinusDisabled.value = false;
      }
      childPlusDisabled.value = false;
      if(departSeatBaby.value === 0) {
        babyMinusDisabled.value = true;
      } else {
        babyMinusDisabled.value = false;
      }
      babyPlusDisabled.value = false;
    }
  }

});

정말 모든 케이스에서 잘 동작을 한다. 그런데 뭔가 길다? 중복되는 코드도 많은것 같고…

리펙토링

코드 최적화가 필요해 보였다. 최적화를 할때 어떤걸 생각해야할까?

  1. 조건문의 중첩을 최소화하고 간결하게 작성
  2. 변수 초기화 선언을 한번에 처리
  3. 상수값과 변수값을 직접비교하지말고 상수값을 변수로 대입하게 수정
  4. 중복되는 코드를 함수로 만들어서 재사용

위 정도 생각하면서 개선을 진행했다. 개선된 코드는 아래와 같다.

interface DisabledState {
  adultMinus: boolean;
  adultPlus: boolean;
  childMinus: boolean;
  childPlus: boolean;
  babyMinus: boolean;
  babyPlus: boolean;
}

const defaultDisabledState: DisabledState = {
  adultMinus: false,
  adultPlus: false,
  childMinus: false,
  childPlus: false,
  babyMinus: false,
  babyPlus: false,
};

const maxPersonCount = 9;

const disabledState = ref<DisabledState>(defaultDisabledState);

const setCurrentCount = (): void => {
  const currentCount = departSeatAdult.value + departSeatChild.value + departSeatBaby.value;

  const isAdultOne = departSeatAdult.value === 1;
  const isChildZero = departSeatChild.value === 0;
  const isBabyZero = departSeatBaby.value === 0;
  const isAdultBabyEqual = departSeatAdult.value === departSeatBaby.value;

  if (currentCount === 1) {
    disabledState.value = { 
      ...defaultDisabledState , 
      adultMinus : true,
      childMinus : true,
      babyMinus : true
    };
  } else if (currentCount === maxPersonCount) {
    if (isAdultBabyEqual) {
      disabledState.value = {
        ...defaultDisabledState,
        adultMinus: true,
        adultPlus: true,
        childPlus: true,
        babyPlus: true,
      };
    } else {
      disabledState.value = {
        ...defaultDisabledState,
        adultMinus: isAdultOne,
        adultPlus: true,
        childMinus: isChildZero,
        childPlus: true,
        babyMinus: isBabyZero,
        babyPlus: true,
      };
    }
  } else {
    disabledState.value = {
      ...defaultDisabledState
    };
    if (isAdultBabyEqual) {
      disabledState.value.adultMinus = true;
      if (isChildZero) {
        disabledState.value.childMinus = true;
      }
      disabledState.value.babyPlus = true;
    } else {
      if (isAdultOne) {
        disabledState.value.adultMinus = true;
      }
      if (isChildZero) {
        disabledState.value.childMinus = true;
      }
      if (isBabyZero) {
        disabledState.value.babyMinus = true;
      }
    }
  }
  console.log(disabledState.value);
};

watch(
  [departSeatAdult, departSeatChild, departSeatBaby],
  setCurrentCount,
  { immediate: true }
);

위 타입스크립트 코드에 맞춰서 화면도 몇가지 변경사항이 있었는데 Vue사용자라고 하면 대충 알것으로 생각한다.

결론

정확히 코드 리펙토링 전과 후의 코드는 같은 동작을 한다. 하지만 코드는 변수명등 알아듣기 쉬운것을 사용함으로써 직관적이고 알기 더 쉽게 바뀌었다. 아마 짧은코드로만 접근한다면 위 코드보다 더 코드를 적게 짤수 있을 것이다. 하지만 짧다고 좋은코드라고 볼순 없을 것이다. 어느정도 다음 개발자를 생각해서 조절해야 맞지 않나 싶다.