useEffect 동작 원리와 의존성 배열 이해하기
리액트(React)로 웹 서비스를 개발할 때 가장 자주 마주치며, 동시에 초보 개발자를 가장 많이 혼란에 빠뜨리는 개념 중 하나가 바로 useEffect입니다. 화면이 그려지는 과정과 상관없이 외부 데이터를 가져오거나 브라우저 타이머를 설정하는 등, 컴포넌트의 핵심 흐름 바깥에서 일어나는 작업을 제어할 때 이 도구가 사용됩니다. 하지만 동작 원리를 정확히 모른 채 사용하면 무한 루프에 빠지거나 메모리 누수가 발생해 서비스 성능을 갉아먹는 주범이 되기도 합니다. 이 글에서는 복잡한 프로그래밍 용어를 덜어내고, 우리 일상 속 비유를 통해 useEffect의 핵심 메커니즘을 아주 쉽게 풀어보고자 합니다.
useEffect란 무엇인가: 컴포넌트 화면 뒤의 스마트 비서
리액트 컴포넌트의 주된 임무는 사용자에게 보여줄 화면(UI)을 그리는 일입니다. 상태(State)가 바뀔 때마다 컴포넌트는 새로운 모습으로 화면을 다시 그리는데, 이를 렌더링이라고 합니다. 하지만 화면을 그리는 일 외에도 서버에서 데이터를 불러오거나, 브라우저 이벤트를 감시하는 등의 추가적인 작업이 필요할 때가 있습니다. 프로그래밍 세계에서는 이를 '부수 효과(Side Effect)'라고 부릅니다.
useEffect는 바로 이 부수 효과를 안전하게 처리할 수 있도록 돕는 리액트 전용 스마트 비서입니다. 화면을 다 그린 직후에 비서에게 "화면 그리기가 끝났으니 이제 이 작업을 처리해줘" 하고 업무 지시서를 전달하는 것과 같습니다. 화면이 렌더링되는 핵심 흐름을 방해하지 않으면서 필요한 뒷작업들을 차례대로 수행할 수 있도록 조율해 주는 고마운 도구입니다.
마운트 언마운트: 컴포넌트의 일생과 연결하기
useEffect를 제대로 이해하기 위해서는 컴포넌트의 일생을 나타내는 마운트 언마운트 개념을 먼저 알아야 합니다. 컴포넌트가 브라우저 화면에 처음으로 나타나는 순간을 '마운트(Mount)'라고 하며, 반대로 사용자가 다른 페이지로 이동하는 등의 이유로 화면에서 완전히 사라지는 순간을 '언마운트(Unmount)'라고 부릅니다. 연극 무대로 비유하자면 배우가 무대 위로 처음 등장하는 것이 마운트이고, 공연을 마치고 퇴장하는 것이 언마운트입니다.
useEffect는 이 탄생과 소멸의 시점에 정확히 맞추어 특정 코드를 실행할 수 있는 기능을 제공합니다. 컴포넌트가 마운트될 때 딱 한 번만 서버에서 사용자 정보를 가져오거나, 언마운트될 때 실행 중이던 애니메이션을 정지시키는 등 화면 전환에 맞춘 정교한 제어가 가능해집니다.
의존성 배열: 비서에게 내리는 세부 실행 지침
useEffect 함수를 사용할 때는 두 번째 인자로 대괄호 모양의 '의존성 배열(Dependency Array)'을 전달할 수 있습니다. 이 배열은 비서에게 언제 업무를 다시 수행해야 하는지 알려주는 일종의 체크리스트 역할을 합니다. 의존성 배열을 어떻게 설정하느냐에 따라 useEffect의 동작 방식은 완전히 달라집니다.
- 의존성 배열이 없을 때: 컴포넌트가 렌더링될 때마다 매번 useEffect 내부 코드가 실행됩니다. 이는 성능 저하의 주원인이 될 수 있으므로 주의해야 합니다.
- 빈 배열([])을 넣었을 때: 컴포넌트가 처음 화면에 나타나는 마운트 시점에만 딱 한 번 실행됩니다. 이후 컴포넌트가 아무리 다시 그려져도 신경 쓰지 않습니다.
- 배열 내부에 특정 값을 넣었을 때([value]): 배열 안에 들어있는 값이 변경될 때마다 useEffect 내부 코드가 다시 실행됩니다. 즉, 감시 대상이 바뀔 때만 움직이는 효율적인 동작이 가능해집니다.
이를테면 "사용자 ID가 바뀔 때마다 해당 사용자의 프로필 데이터를 새로 가져와라" 같은 지시를 내릴 때 의존성 배열에 사용자 ID를 넣어 해결하는 것입니다.
useEffect 클린업: 메모리를 살리는 뒷정리 노하우
useEffect의 강력한 기능 중 하나는 바로 수행했던 작업을 깔끔하게 정리하는 'useEffect 클린업(Cleanup)'입니다. 만약 화면이 바뀔 때 이전에 등록해 둔 타이머나 이벤트 리스너를 그대로 방치하면 어떻게 될까요? 화면에는 보이지 않는데 백그라운드에서 끊임없이 계산이 돌아가며 메모리를 낭비하게 됩니다. 이를 해결하기 위해 useEffect 내부에서 함수를 반환(return)하면, 리액트는 컴포넌트가 다시 렌더링되거나 사라지기 직전에 이 반환된 함수를 먼저 실행해 줍니다.
이것이 바로 클린업 함수입니다. 마치 방을 어지럽히며 놀았던 장난감들을 다음 놀이를 시작하기 전에 제자리에 치우는 행동과 같습니다. 웹 브라우저의 스크롤 이벤트를 감시하기 위해 마운트 시점에 등록했던 리스너를, 언마운트 시점에 클린업 함수를 통해 해제해 줌으로써 메모리 누수를 원천 차단하고 최적의 브라우저 성능을 유지할 수 있습니다.
결론: 안전하고 효율적인 상태 관리를 위하여
useEffect는 리액트 컴포넌트가 외부 세계와 안전하게 소통할 수 있도록 연결해 주는 아주 유용한 도구입니다. 하지만 잘못 다루면 화면이 무한히 깜빡이거나 브라우저가 멈추는 무한 루프 현상을 만나기 쉽습니다. 컴포넌트의 생명 주기인 마운트 언마운트 흐름을 명확히 인지하고, 의존성 배열을 꼼꼼하게 설계하며, 완벽한 뒷정리를 위한 useEffect 클린업 함수를 잊지 않는다면 훨씬 가볍고 부드러운 웹 애플리케이션을 만들 수 있을 것입니다.