ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 리팩터링 - Guard Clause를 사용하여 코드 가독성 높히기
    JS & TS 2022. 9. 13. 22:52

    리팩터링이란 소프트웨어의 겉보기 동작은 그대로 유지한 채, 코드를 이해하고 수정하기 쉽도록 내부 구조를 변경하는 기법이다 - 리팩터링 2판, 마틴 파울러

     

    수많은 리팩터링 방법 중 하나인 Guard Clause에 대해 소개합니다. Guard Clause는 무엇일까요?

    A guard clause is simply a check that immediately exits the function, either with a return statement or an exception.

    Guard Clause 는 반환 문이나 예외를 포함하여 함수를 즉시 종료하는 단순한 검사와 같은 문법입니다.

     

    아래의 코드를 보겠습니다.

    export function payAmount(employee) {
      let result;
      if (employee.isSeparated) {
        result = { amount: 0, reasonCode: "SEP" };
      } else {
        if (employee.isRetired) {
          result = { amount: 0, reasonCode: "RET" };
        } else {
          result = someFinalComputation();
          return result;
        }
      }
      return result;
    }
    

    payAmount 라는 함수에 파라미터로 employee 객체를 전달합니다. employee 내부에 뭐가 들었는지는 모르지만, isSeparated, isRetired 와 같은 내부 속성에 의해 if - else 문을 복잡하게 반복합니다.

     

    코드를 짜는 본인의 경우에는 알아볼 수 있지만 며칠만 지나도 쉽게 헷갈리게 되고 다른 사람들이 봤을 때 유지보수의 어려움을 느끼게 될 것입니다.

     

    result 라는 임시 변수를 선언하고 각 조건에 따라 result 변수에 값을 할당한 뒤 마지막에 result 를 리턴합니다. 임시 변수를 무조건 사용하자라고는 말할 수 없지만, 코드의 임시 변수가 많아질수록 가독성이 떨어지게 됩니다.

     

    이와 같은 코드 흐름을 Guard Cluase 를 사용하면 어떻게 될까요?

    export function payAmount(employee) {
      if (employee.isSeparated) {
        return { amount: 0, reasonCode: "SEP" };
      }
    
      if (employee.isRetired) {
        return { amount: 0, reasonCode: "RET" };
      }
    
      return someFinalComputation();
    }
    

    임시 변수를 사용하지 않고 각 조건에 따라 바로 리턴을 해줍니다. if else 문 또는 if else 문 내부에서 중첩된 if else 문을 사용하지 않고, 같은 스코프에서 조건에 따라 바로 반환하기 때문에 복잡하지 않고 가독성이 높아졌습니다.

     

    Guard Clause 를 사용한 다른 예제 코드를 첨부해 두겠습니다.

     

    리팩터링 이전

    export function adjustedCapital(instrument) {
      let result = 0;
      if (instrument.capital > 0) {
        if (instrument.interestRate > 0 && instrument.duration > 0) {
          result =
            (instrument.income / instrument.duration) *
            anInstrument.adjustmentFactor;
        }
      }
      return result;
    }
    

    리팩터링 이후

    export function adjustedCapital(instrument) {
      function isEligibleForAdjustedCapital(instrument) {
        return (
          instrument.capital > 0 &&
          instrument.interestRate > 0 &&
          instrument.duration > 0
        );
      }
    
      if (!isEligibleForAdjustedCapital(instrument)) {
        return 0;
      }
    
      return (
        (instrument.income / instrument.duration) * anInstrument.adjustmentFactor
      );
    }
    

    리팩터링 이전

    const onLogin = useCallback(async () => {
        if (isModerator) {
          if (deviceSelected.audioInput !== DEVICE_DISABLE) {
            setSituation(info, ROLE.MODERATOR_ROLE, STATUS.STAGE);
    
            return;
          } else {
            setSituation(info, ROLE.MODERATOR_ROLE, STATUS.BACKSTAGE);
    
            return;
          }
        } else {
          setSituation(info, ROLE.AUDIENCE_ROLE, STATUS.AUDIENCE);
    
          return;
        }
      }, [deviceSelected, info, isModerator, RTM_join, setSituation]);
    

    리팩터링 이후

    const onLogin = useCallback(async () => {
        if (isModerator && deviceSelected.audioInput !== DEVICE_DISABLE) {
          setSituation(info, ROLE.MODERATOR_ROLE, STATUS.STAGE);
          return;
        }
    
        if (isModerator && deviceSelected.audioInput === DEVICE_DISABLE) {
          setSituation(info, ROLE.MODERATOR_ROLE, STATUS.BACKSTAGE);
          return;
        }
    
        setSituation(info, ROLE.AUDIENCE_ROLE, STATUS.AUDIENCE);
        return;
      }, [deviceSelected, info, isModerator, RTM_join, setSituation]);
    

    레퍼런스

    https://deviq.com/design-patterns/guard-clause

    book_study_storage/README.md at master · junh0328/book_study_storage

    댓글

Designed by Tistory.