하루 30분 TIL

230113 30분 TIL forEach의 return

devSoo 2023. 1. 13. 14:32

1. forEach 안에서 return/ breack 처리 ?! 

 상황

아래의 코드를 리팩터링 하고자 했다. 

  useEffect(() => {
    if (!Object.keys(QUIZ_TASTE_DESCRIPTION).find((key: string) => title?.includes(key))) {
      return setTasteData(initialTasteData);
    }

    for (const [key, value] of Object.entries(QUIZ_TASTE_DESCRIPTION)) {
      if (title?.includes(key)) {
        setTasteData(value);
      }
    }
  }, [title]);

QUIZ_TASTE_DESCRIPTION을 순회하는건 데 두 번씩이나 loop 할 필요 있을까? 한번으로 되지 않을까 라는 의문에서 시작됐다. 

forEach문으로 조회시 가능하지 않을까 라는 생각이 들어 시도해 보았다.

 

  useEffect(() => {
    const QUIZ_TASTE_DESCRIPTION_KEYS_ARRAY = Object.keys(QUIZ_TASTE_DESCRIPTION);

    const handleTasteData = (taste: string, index: number) => {
      if (title?.includes(taste)) {
        console.log('@@here!');
        return setTasteData(QUIZ_TASTE_DESCRIPTION[taste]);
      }

      if (index + 1 === QUIZ_TASTE_DESCRIPTION_KEYS_ARRAY.length) {
        console.log('@@here2!');
        return setTasteData(initialTasteData);
      }
    };

    QUIZ_TASTE_DESCRIPTION_KEYS_ARRAY.forEach(handleTasteData);
  }, [title]);

의도한 건 includes 조건이 걸렸을 때 멈추고 마지막 index에서 tasteData를 initalTasteData로 리셋시키는 것이었다.

하지만 의도와 달리 if문에서 조건이 걸리더라고 멈추지 않았고 tasteData가 initalTasteData로 리셋이 됐다.

 

 

그래서 forEach 메소드가 얼리 리턴이 되는 줄 알고 그렇게 썼던 것 같은데 작동이 되지 않아 찾아봤다

 

아래 코드를 보자 

 const arr = [1,2,3,4,5];
 
 arr.forEach((value) => {
 	if(value == 3) return;
    console.log(value);
    });
  // 기대 실제
  // 1    1
  // 2    2
  // 3    4
  //      5

 

 

기대한 바는 3에서 멈추는 것인데 4, 5까지 나오는 것을 알 수 있다.

 

아 생각해보니 이전에 썼을 때 해당 차수(index)에서 리턴 시키고 싶을 때 사용을 했구나! 기억이 났다.

 

그렇다면 위의 경우 어떻게 해야할 까?

 

for - of를 쓴다면 ?

 

    for(const taste of Object.keys(QUIZ_TASTE_DESCRIPTION)) {
        if (title?.includes(taste)) {
          setTasteData(QUIZ_TASTE_DESCRIPTION[taste]);
          break;
        }
        setTasteData(initialTasteData);
    }

 

이렇게 쓸 때의 문제는 얼리 break 되지 않은 이상 setTasteData가 매번 실행되어 리렌더링이 계속될 것이다.

 

그렇다면 if문에서 만났을때 조기 breack도 되면서 마지막 차수일 때만 가능하게 하는 유일한 방법은 for문 밖에 없다!

 

    const QUIZ_TASTE_DESCRIPTION_KEYS_ARRAY = Object.keys(QUIZ_TASTE_DESCRIPTION);

    for (let i = 0; i < QUIZ_TASTE_DESCRIPTION_KEYS_ARRAY.length; i++) {
      if (title?.includes(QUIZ_TASTE_DESCRIPTION_KEYS_ARRAY[i])) {
        setTasteData(QUIZ_TASTE_DESCRIPTION[QUIZ_TASTE_DESCRIPTION_KEYS_ARRAY[i]]);
        break;
      }

      if (i + 1 === QUIZ_TASTE_DESCRIPTION_KEYS_ARRAY.length) {
        setTasteData(initialTasteData);
      }
    }

의도했던 대로 동작을 한다! 

 

하지만 효율성을 위해 더 어려운 코드가 된 느낌을 지울 수 없다.... ㅠ 

그래서 처음의 코드로 다시 해야할 것 같다 

 

라고 생각을 해서 동료와 얘기를 해보니  for of문 안의 조건문 안에서 return을 쓰면 해당 loop 뿐 아니라 밑의 코드까지 가지 않고 return이 된다고 한다. 

 

  useEffect(() => {
    for (const [key, value] of Object.entries(QUIZ_TASTE_DESCRIPTION)) {
      if (title?.includes(key)) {
        setTasteData(value);
        return;
      }
    }

    setTasteData(initialTasteData);
  }, [title]);

오호 훨씬 간단해졌다!!!

 

오늘의 결론

 

1. forEach 안에서 return이 되지 않는다. 

2. for - of 문 안에서 break시 loop만 종료되고 return 시 일반 return 처럼 벗어날 수 있다.