별거아닌에러해결 - 이름을 잘짓자 & 스코프체이닝, 호이스팅

junamee·2023년 1월 20일
0

🧩리팩터링

목록 보기
5/5

- before

const onSelectTeacher = (input: {idx: string; value: boolean}) => {
    const {idx, value} = input
    //...(중략)
    
    if (value) {
      //...
    } else {
      const idx = originSelectedList.findIndex((teacher: TeacherInfoType) => {
        return teacher.idx === idx
      })
      originSelectedList.splice(idx, 1)
      return originSelectedList
    }
  }

여러개의 카드들을 선택하고 선택을 취소하는 로직이다.
x표를 누르면 해당 카드의 인덱스를 받아 splice를 통해 제거하려고 한다.
그러나 자꾸 내가 선택한 카드가 삭제되는 것이 아니라 마지막 카드가 삭제되는 이슈였다.

무엇이 잘못되었는지 알고보니까 매우 거슬리는 코드라는 생각이 들지만,
내가 작성한 부분이 아니었기에 바로 이유를 파악하지 못했다.

꼼꼼하게나 보자며 리팩토링을 시작했고,
갑자기 문제가해결되었다.

- after

const onSelectTeacher = (input: {idx: string; value: boolean}) => {
    const {idx, value} = input
    //...(중략)
    
    if (value) {
      //...
    } else {
      const selectedIndex = originSelectedList.findIndex((teacher: TeacherInfoType) => teacher.idx === idx)
      originSelectedList.splice(selectedIndex, 1)
      return originSelectedList
    }
  }

else 문의 idx 변수를 selectedIndex로 수정하였다. 변수명에 의미를 담고 싶어 수정했는데 이게 문제 해결이었다. 이상하네... 이게 왜 되지... 🧐

같이 문제를 얘기해보며 이해할 수 있었는데,
before의 idx로 작성했을 때는 findIndex 콜백함수 내의 idx와 변수 idx가 겹치는 것이 문제였다.

콜백함수의 idx는 parameter로 받아온 input의 idx가 될것이라고 생각 (착각)
실행컨텍스트가 실행되면 현재의 컨텍스트(LexicalEnvironment)에서 값을 찾고, 찾을 수 없다면 그 위의 스코프 (outerEnvironmentRecord의 LexicalEnvironment)를 탐색하며 값을 찾는다. (스코프 체이닝)
여기서는 콜백함수의 idx가 외부까지 탐색할 필요없이 else문의 블록 내에서 idx변수를 바로 찾게 되었다.

그렇지만 함수 스코프 내에 동일한 이름의 idx가 선언되었고,
따라서 콜백함수의 idx는 가장 가까이 있는 변수타입의 idx를 참조하게 된다.
하지만 이때 idx는 아직 값이 정해지지않은 undefined 상태이다. (*TDZ - 초기화상태)
그러므로 콜백함수를 실행하고나서의 idx는 값이 존재하지 않기 때문에 -1 을 반환하고
splice를 실행할땐 가장 마지막 요소(-1)의 요소 하나를 삭제하게 되는 것이다.

어려운 로직이 아니었는데 이를 캐치하는데 시간이 걸렸다.
결국은 의미를 담은, 좋은 코드를 작성하는 것이 불필요한 에러를 발생시키지 않는 것 같다.

profile
아티클리스트 - bit.ly/3wjIlZJ

0개의 댓글