WEB/FE(HTML,CSS,JS | React)

[React] 무한루프 예방을 위한 주의사항(useEffect, Event Handler)

tedium._.dev 2023. 2. 14. 12:40

리액트를 이용해 개발을 하다가, 처음 보는 오류를 마주하게 되었다. 기존의 동적인 요소와 상호작용이 되지도 않을 뿐더러, 사이트 자체가 아주 느려진 느낌이었다. 창이 꺼지는 데에도 많은 시간이 걸렸다.

추후에 어찌저찌 개발 툴을 열어 오류를 확인해 보니, 아래와 같은 오류 창이 떠 있었다.

 

무서움 9806배

음.. 누가 봐도 어딘가에서 코드를 잘못 써서 무한루프에 걸린 문제였다.

 

처음에는 'useEffect 내부에서 setState를 호출했으나, useEffect가 두 번째 인자인 dependency 배열을 받지 않았거나 렌더 시마다 dependency가 변경되었을 때 발생할 수 있는 오류'라는 내용을 보고 이 부분을 확인해보고자 하였다.

 

실제로, 아래 두 가지의 상황에서는 무한루프가 발생할 수 있다.

 


 

1. useEffect의 세 번째 인자, 즉 dependency가 주어지지 않았을 때

 

useEffect의 두 번째 인자, 즉 dependency에 아무것도 넘겨주지 않을 경우, dependency는 JS 자료형 중 하나인 'undefined' 상태가 된다. 

문제는 dependency로 비교할 대상이 undefined가 아니라, dependency 자체가 undefined인 상태이기 때문에 렌더링 이전과 이후에 올바른 비교를 할 수 없게 되어 무한루프에 빠질 수 있다.

 

2. useEffect의 dependency가 내부에서 변경될 때

 

간단한 예시를 보자.

 

useEffect의 내부는 dependency가 변경될 때마다 실행된다. 그리고 그 실행은 다시 dependency의 변경을 유발한다.

이 과정이 반복되어 무한 루프에 빠지게 된다.


위의 사항 때문에 문제가 생기는 것인지 확인하기 위해, 코드 내부의 useEffect문을 확인해 보았다.

 

useEffect 내부에서 변경되는 state를 dependency로 가지고 있지도 않았고, 또한 빈 배열을 dependency로 명시적으로 넘겨 주었다. useEffect 부분에는 문제의 원인이 없는 것 같아 보였다.

 

그렇다면 문제는? 답은 이벤트 핸들러에 있었다.

 

문제가 되었던 코드 부분

문제는 24번째 줄, 현재는 주석 처리된 코드였다.

 

평소에는 이벤트가 발생했을 때 처리할 함수를 직접 넘겨주는 대신 해당 함수를 실행하는 콜백 함수를 넘겨 주었는데, 이번에는 코드를 간결화하고자 이와 같이 작성해서 오류가 발생한 것이었다.

 

내가 작성한 두 가지 방식에는 큰 차이가 있는데, 함수를 즉시 실행하느냐에 대한 여부이다.

 

제대로 작성된 코드인 25-27번째 줄의 콜백 함수는

        onClick={function () {
          navigate("/");
        }}

과 같다. 즉, 즉시 함수를 실행하는 것이 아니라, click 이벤트가 발생했을 때 실행할 콜백 함수를 넘겨 준 것이다.

 

그러나 24번째 줄은 click 이벤트가 발생했을 때의 이벤트 핸들러를 선언하는 과정에서 함수를 이미 호출해 버리면서, 이 함수의 호출이 리렌더링을 발생시키면서 다시 호출의 반복, 무한루프에 빠지게 된 것이었다.

 

코드 간결화도 잘 알아보고 쓰자는 교훈을 얻게 된 뿌듯한 하루였다.

아, 콜백 함수의 인자와 body 내부로 넘겨주는 매개변수가 동일할 경우 전체를 생략할 수 있는 방법이 있긴 한데, 현재 상황에서는 그 둘이 서로 다르기에 생략하기에는 무리가 있어 보인다.

 

생략 예시

 

오히려 해당 코드에서는, button의 onClick으로 navigate를 사용하는 것 대신 react-router-dom 라이브러리의 다른 메소드 중 하나인 Link를 이용하는 것이 더욱 나아 보인다.

 

 

마지막으로, 무한루프에 빠지는 것을 방지하기 위한 중요 사항에 대해 요약하자면 다음과 같다.

useEffect 사용 시의 무한루프를 방지하려면
useEffect의 dependency는 넘겨 주기(빈 배열이라도)
useEffect의 dependency가 useEffect의 내부에서 변경되지 않도록 하기

 

이벤트 핸들링에서 무한루프를 방지하려면
이벤트 발생 시 처리 함수를 콜백함수로 넘겨 주기

 

정도가 될 것 같다.