함수 useEffect는 어떤 값이 변경될 때마다 특정 코드를 실행하는 리액트 훅입니다. 이를 “특정 값을 검사한다”라고 표현합니다. 예컨대 useEffect를 이용하면 컴포넌트의 State 값이 바뀔 때마다 변경된 값을 콘솔에 출력하게 할 수 있습니다.
첫 번째 프로젝트로 만든 카운터 앱을 수정하면서 함수 useEffect를 어떻게 사용하는지 알아보겠습니다.
하나의 값 검사하기
카운터 앱의 App 컴포넌트에서 State 변수 count의 값이 바뀌면, 변경된 값을 콘솔에 출력하겠습니다. 비주얼 스튜디오 코드에서 첫 번째 프로젝트로 만든 project1 폴더를 엽니다.
App.js를 다음과 같이 수정합니다.
코드를 불러오는 중 입니다 ...코드를 불러오는 중 입니다 ...① 함수 useEffect를 사용하기 위해 react 라이브러리에서 불러옵니다. ② useEffect를 호출하고 두 개의 인수를 전달합니다. 첫 번째 인수로 콜백 함수를, 두 번째 인수로 배열을 전달합니다
두 번째 인수로 전달한 배열을 의존성 배열(Dependency Array, 줄여서 deps)이라고 하는데, useEffect는 이 배열 요소의 값이 변경되면 첫 번째 인수로 전달한 콜백함수를 실행합니다.
코드에서 useEffect의 의존성 배열 요소에 State 변수 count가 있으므로, 이 값이 바뀌면 콜백 함수가 실행됩니다. 콜백 함수는 콘솔에 ‘count 업데이트’라는 문자열과 함께 변경된 State 값을 출력합니다.
저장하고 개발자 도구의 콘솔을 확인합니다.

count 업데이트: 0이 콘솔에 출력됩니다. 아직 State 값을 변경한 적이 없음에도 콘솔에서 문자열을 출력한 이유는 State 값을 초기화할 때도 useEffect가 이 변화를 감지하기 때문입니다.
이번에는 <+10> 버튼을 5번 누른 다음 개발자 도구의 콘솔을 확인합니다.

버튼을 클릭할 때마다 useEffect에 인수로 전달한 콜백 함수가 실행되어 변경된 State 값을 콘솔에 출력합니다. 이렇듯 useEffect를 이용하면 특정 값이 바뀔 때마다 여러분이 원하는 코드를 실행하도록 만들 수 있습니다.
여러 개의 값 검사하기
useEffect의 의존성 배열 요소가 여러 개 있어도 마찬가지입니다. 즉, 배열 요소 중 하나가 변경되어도 useEffect는 콜백 함수를 실행합니다.
현재 카운터 앱의 App 컴포넌트에는 State 변수 count 외에는 변경할 수 있는 값이 없습니다. 따라서 임시로 입력 폼을 추가하고, 이 폼에 입력한 데이터를 처리하는 text라는 이름의 State 변수를 하나 더 만들겠습니다.
App 컴포넌트를 다음과 같이 변경합니다.
코드를 불러오는 중 입니다 ...① useState를 이용해 State 변수 text를 만듭니다. ② onChange 이벤트 핸들러 함수 handleTextChange를 만듭니다. ③ 새로운 <section> 태그로 페이지의 영역을 나누고, 텍스트 입력 폼을 생성합니다. 사용자 입력을 처리하기 위한 value 속성에 변수 text를 전달하고, onChange 이벤트 핸들러로 함수 handleChangeText를 지정합니다.
저장하고 새롭게 만든 입력 폼을 페이지에서 잘 렌더링하는지 확인합니다.

정상적으로 입력 폼이 만들어졌습니다.
이제 text 값이 변경되어도 useEffect가 콜백 함수를 실행해야 합니다. App 컴포넌트를 다음과 같이 수정합니다.
코드를 불러오는 중 입니다 ...① useEffect의 콜백 함수가 실행되면 State 변수 text와 count 값을 콘솔에 출력합니다. ② useEffect에 전달하는 의존성 배열에 변수 text를 요소로 추가합니다.
이제 State 변수 text의 값이 바뀌어도 콜백 함수를 실행할 겁니다.
저장하고 새로 만든 텍스트 입력 폼에서 ‘hi’라는 문자열을 입력합니다. 그리고 <+10> 버튼을 두 번 클릭합니다. 2개의 State 값이 변할 때, 개발자 도구의 콘솔에서는 어떤 변화가 있는지 직접 확인합니다.

두 개의 State 값이 변할 때마다 콘솔에 값을 출력합니다.
useEffect로 라이프 사이클 제어하기
이번에는 useEffect로 컴포넌트 라이프 사이클을 어떻게 제어하는지 살펴보겠습니다.
컴포넌트의 3단계 라이프 사이클 중 업데이트(Update)가 발생하면 특정 코드를 실행하겠습니다.
App 컴포넌트를 다음과 같이 수정합니다.
코드를 불러오는 중 입니다 ...① 앞서 작성했던 함수 useEffect 코드는 삭제하고 새롭게 useEffect를 생성합니다. 이때 두번째 인수인 의존성 배열에는 아무것도 전달하지 않습니다.
저장하고 <+10> 버튼을 2번 누른 다음, 개발자 도구의 콘솔을 확인합니다.

두 번째 요소인 의존성 배열에 아무것도 전달하지 않으면, useEffect는 컴포넌트를 렌더링할 때마다 콜백 함수를 실행합니다. 세 번에 걸친 문자열 출력은 컴포넌트를 처음 페이지에 렌더링하는 마운트 시점 한 번과 컴포넌트를 리렌더하는 업데이트 시점 두 번의 결과입니다.
이번에는 useEffect에서 마운트 시점은 제외하고 업데이트 시점에만 콜백 함수를 실행하겠습니다. 즉, 페이지에 처음 렌더링할 때는 콜백 함수를 실행하지 않고 리렌더될 때만 실행하겠다는 뜻입니다. 이를 위해 함수 useRef도 이용합니다.
App.js를 다음과 같이 수정합니다.
코드를 불러오는 중 입니다 ...① useRef를 사용하기 위해 react 라이브러리에서 불러옵니다. ② 현재 App 컴포넌트를 페이지에 마운트했는지 판단하는 변수 didMountRef를 Ref 객체로 생성합니다. 초깃값으로 false를 설정합니다. Ref 객체는 돔 요소를 참조하는 것뿐만 아니라 컴포넌트의 변수로도 자주 활용됩니다. ③ 컴포넌트 마운트 시점에는 콘솔에 ‘컴포넌트 업데이트’ 문자열을 출력하지 않도록 조건문을 추가 합니다.
useEffect의 콜백 함수에 조건문을 추가했습니다. 이 코드를 좀 더 자세히 살펴보겠습니다.
코드를 불러오는 중 입니다 ...① 의존성 배열로 아무것도 전달하지 않았으므로, 콜백 함수는 마운트 시점에도 실행되어야 합니다. ② 조건문에서 변수 didMountRef의 값을 검사합니다. 이 변수는 컴포넌트가 마운트했는지 아닌지 판단할 때 사용하는데, 초깃값으로 false를 설정했습니다. 따라서 콜백 함수를 처음 렌더링하는 마운트 시점에는 조건식이 참(!false=true)이 되어 if 문을 수행합니다. if 문에서는 변수 didMountRef의 값을 true로 바꾸고(마운트가 됐음을 표시), return 문으로 함수를 종료합니다. ③ 변수 didMountRef의 값이 false가 아니라면(true라면), 콜백 함수의 호출은 마운트 시점이 아닙니다. 따라서 콘솔에서 ‘컴포넌트 업데이트’ 문자열을 출력합니다.
정리하자면 useEffect에서 의존성 배열을 인수로 전달하지 않으면 마운트, 업데이트 시점 모두 콜백 함수를 호출합니다. 그러나 코드처럼 콜백 함수 내부에서 조건문과 Ref 객체로 특정 시점에만 코드를 실행하게 만들 수 있습니다. 즉, 마운트 시점(didMountRef=false)에 호출하면 아무것도 출력하지 않고 함수를 종료하고, 업데이트 시점(didMountRef=true)에 호출하면 문자열을 콘솔에 출력합니다.
<+10> 버튼을 2번 누른 다음 개발자 도구의 콘솔을 확인합니다.

업데이트 시점에만 콘솔에 ‘컴포넌트 업데이트’ 문자열을 출력합니다.
페이지에서 새로고침(<F5>) 키를 누르면 프로젝트 페이지 와 콘솔 모두 초기화됩니다. 실습 과정에서 적절히 새로고침 하여 기존에 쌓인 작업을 제거 하는 게 보기 편합니다.
컴포넌트의 마운트 제어하기
이번에는 컴포넌트의 마운트 시점에 실행되는 코드를 작성하겠습니다. 이를 “컴포넌트의 마운트를 제어한다”라고 표현합니다. App 컴포넌트를 다음과 같이 수정합니다.
코드를 불러오는 중 입니다 ...① 함수 useEffect를 하나 더 만들고 의존성 배열에는 빈 배열을 전달합니다 useEffect에서 빈 배열을 전달하면 컴포넌트의 마운트 시점에만 콜백 함수를 실행합니다.
저장하고 <+100> 버튼을 3번 누른 다음, 개발자 도구의 콘솔을 확인합니다.

컴포넌트 언마운트 제어하기
라이프 사이클의 마지막 단계인 언마운트는 컴포넌트가 페이지에서 제거될 때입니다. 컴포넌트 언마운트 시점에 필요한 코드 실행 방법을 살펴보겠습니다.
클린업
리액트 컴포넌트의 언마운트 시점을 제어하기 위해서는 먼저 클린업(Cleanup) 기능을 이해해야 합니다. 클린업이란 원래 ‘청소’라는 뜻입니다. 프로그래밍에서 이 개념은 특정 함수가 실행되고 종료된 후에, 미처 정리하지 못한 사항을 처리하는 일입니다. 개념을 완벽히 이해하지 않아도 괜찮습니다. 실습하면서 어떤 때 클린업 개념이 필요한지 정도만 이해해도 충분합니다.
먼저 App 컴포넌트에서 다음과 같이 함수 useEffect를 한 번 더 호출합니다.
코드를 불러오는 중 입니다 ...① useEffect를 호출하고 의존성 배열은 전달하지 않습니다. 따라서 App 컴포넌트를 렌더링할 때마 다 첫 번째 인수로 전달한 콜백 함수가 실행됩니다. ② useEffect의 콜백 함수는 다시 함수 setInterval을 호출합니다. setInterval은 자바스크립트 내장 함수로 두 번째 인수인 밀리초 시간이 경과하면 첫 번째 인수로 전달한 콜백 함수를 실행합니다. 즉 인터벌(Interval, 시간 간격)을 설정하는 함수입니다. 결과적으로 1초마다 콘솔에 문자열 깜빡 을 출력합니다.
저장한 다음, 개발자 도구의 콘솔을 확인하면 1초마다 문자열 깜빡이 출력되는 걸 볼 수 있습니다.

페이지에서 <+1> 또는 <-1> 버튼을 빠른 속도로 연속 클릭합니다. 빠르게 연속 클릭해State(count) 값을 변경하면, App 컴포넌트가 여러 번 리렌더됩니다.
그런데 함수 setInterval에서 정한 인터벌(1초)이 아닌 매우 빠른 속도로 ‘깜빡’ 문자열이 콘솔에 출력되는 현상을 볼 수 있습니다.

분명 1초마다 문자열을 콘솔에 출력하도록 인터벌을 정했는데, 갑자기 빠르게 출력하는이유가 무엇일까요? 두 가지 이유가 복합적으로 얽혀 있기 때문입니다.
하나는 App 컴포넌트를 렌더링할 때마다 useEffect의 콜백 함수는 새로운 setInterval 함수를 만들고 새 인터벌 간격을 생성한다는 점입니다. useEffect의 두 번째 인수로 아무것도 전달하지 않았기 때문에, 버튼을 클릭해 State를 변경하면 새 인터벌 함수를 생성합니다.
또 하나는 함수 setInterval에서 인터벌을 생성한 다음에 이를 종료하지 않았기 때문입니다. 인터벌을 종료하는 clearInterval이라는 또 다른 내장 함수를 호출하지 않으면 문자열 출력은 멈추지 않습니다.
버튼을 클릭해 State 값을 업데이트하면 App 컴포넌트를 리렌더할 때마다 새로운 인터벌이 생성됩니다. 그러나 기존 인터벌을 종료하지 않았기 때문에 여러 개의 인터벌이 중복으로 만들어져 출력 속도가 빨라지게 됩니다. 이럴 때 요긴하게 사용하는 기능이 바로 useEffect의 클린업 기능입니다.
앞서 작성한 useEffect를 다음과 같이 수정합니다.
코드를 불러오는 중 입니다 ...① 함수 setInterval은 새 인터벌을 생성하면 인터벌 식별자(id)를 반환합니다. 이 id를 변수 intervalID에 저장합니다. ② useEffect에 인수로 전달한 콜백 함수가 새 함수를 반환하도록 합니다. 이 함수는 클린업 함수로 서 useEffect의 콜백 함수가 실행되기 전이나 컴포넌트가 언마운트하는 시점에 실행됩니다. ③ 클린업 함수는 clearInterval을 호출합니다. 인수로 ①에서 생성한 인터벌 식별자를 전달해 앞서 생성한 인터벌을 삭제합니다.
정리하면 useEffect의 콜백 함수가 반환하는 함수를 클린업 함수라고 합니다. 이 함수는 콜백 함수를 다시 호출하기 전에 실행됩니다. 따라서 컴포넌트를 렌더링할 때 마다 새 인터벌을 생성하고 기존 인터벌은 삭제합니다.
이제 코드를 저장하고 페이지를 새로고침합니다. <+1> 또는 <-> 버튼을 빠르게 클릭해 App 컴포넌트를 리렌더해도 인터벌이 중복해서 만들어지지 않는지 확인합니다.

이렇듯 useEffect의 콜백 함수가 또 다른 함수를 반환하는 클린업 기능을 이용하면 인터벌같이 종료 이후에도 남아 있는 작업을 청소할 수 있습니다. 다음 실습을 위해 이번 단원에서 작성한 useEffect는 모두 삭제 또는 주석 처리합니다.
클린업을 이용해 컴포넌트 언마운트 제어하기
클린업 기능을 살펴보았으니 이를 이용해 컴포넌트가 페이지에서 사라질 때 원하는 코드를 실행하는 ‘컴포넌트 언마운트’에 대해 살펴보겠습니다.
현재 카운터 앱은 페이지에 렌더링한 컴포넌트를 사라지게 하는 기능이 없기 때문에 언마운트를 제어할 수 없습니다. 따라서 카운터 앱에 컴포넌트를 하나 새롭게 만들겠습니다. 이 컴포넌트는 count 값이 짝수면 특정 문자열을 페이지에 렌더링합니다. 이 기능을 조건부 렌더링으로 구현하겠습니다.
먼저 새 컴포넌트를 만듭니다. component 폴더에 Even.js를 만들고 다음과 같이 작성합니다.
코드를 불러오는 중 입니다 ...Even 컴포넌트를 만들었다면, count 값이 짝수일 때 이 컴포넌트를 페이지에 렌더링합니다. App.js를 다음과 같이 수정합니다.
코드를 불러오는 중 입니다 ...① component 폴더의 Even.js에서 Even 컴포넌트를 불러옵니다. ② AND 단축 평가를 이용해 count 값을 2로 나눈 나머지가 0일 때, 즉 State 값이 짝수일 때 Even 컴포넌트를 페이지에 렌더링합니다.
AND 단축 평가를 이용하면 조건부 렌더링 코드를 간결하게 작성할 수 있습니다.
예컨대
count % 2 === 0 && <Even/>
과 같은 코드에서 AND 연산자 앞의 식이 참이면 연산자 뒤의 Even 컴포넌트를 값으로 반환합니다. 만약 count % 2 === 0의 값이 거짓(false)이면 단축 평가가 이루어져 페이지에는 아무것도 렌더링하지 않습니다.저장하고 <+1> 버튼을 계속 클릭 하면서 count 값이 짝수면 Even 컴포넌트를 페이지에 렌더링하는지 확인합니다.

State 값이 짝수일 때만 Even 컴포넌트를 페이지에 렌더링하여 현재 카운트는 짝수입니다라는 문자열을 표시합니다. 홀수면 아무런 값도 페이지에 렌더링하지 않습니다.
다음에는 Even 컴포넌트에서 useEffect를 사용해 이 컴포넌트가 언마운트될 때 콘솔에 특정 문자열을 출력하겠습니다. Even 컴포넌트를 다음과 같이 수정합니다.
코드를 불러오는 중 입니다 ...① useEffect를 호출하고 의존성 배열로 빈 배열을 전달합니다. 그다음 콜백 함수가 화살표 함수를 반환하게 합니다.
함수 useEffect에 의존성 배열로 빈 배열을 전달하고, 콜백 함수가 함수를 반환하면 이 함수는 컴포넌트의 언마운트 시점에 실행됩니다.
페이지를 새로고침하고 <+1> 버튼 클릭해 개발자 도구의 콘솔을 확인합니다.

State 변수 count의 초깃값은 0(짝수)이므로 App의 마운트 시점에 Even 컴포넌트 역시 마운트됩니다. 이 상태에서 <+1> 버튼을 클릭하면 State 값이 1(홀수)로 변경됩니다. 따라서 Even 컴포넌트를 언마운트하면서 콘솔에 Even 컴포넌트 언마운트라는 문자열을 출력합니다.
지금까지 함수 useEffect를 이용해 하나 또는 여러 값을 검사하고, 컴포넌트 라이프 사이클까지 제어했습니다. useEffect는 앞으로도 자주 사용할 리액트 훅입니다. 아직 이해하지 못한 내용이 있다면 천천히 다시 읽으며 숙지하기를 바랍니다.