
React
UI를 효율적으로 구성하고 관리할 수 있도록 설계된 JavaScript 라이브러리입니다. 컴포넌트를 기반으로 사용자 인터페이스를 구성하고, 상태(state)와 생명주기(lifecycle)를 통해 컴포넌트가 동적으로 변하도록 합니다.
훅(Hook)
2018년, React에 훅(Hook)이라는 기능이 도입되면서 함수형 컴포넌트에서도 상태와 생명주기 기능을 쉽게 사용할 수 있게 되었습니다. 훅은 React 함수형 컴포넌트에서 상태 관리와 부수 효과(Side Effect)를 처리하는 간단하면서도 강력한 도구입니다. 함수형 컴포넌트에 훅을 사용하면 컴포넌트 내에서 다양한 기능을 쉽게 구현할 수 있습니다.
훅의 규칙과 중요성
React 훅의 규칙은 컴포넌트에서 훅이 일관되게 동작하고 올바르게 작동할 수 있게 보장합니다. 두 가지 주요 규칙이 있습니다.
1. 컴포넌트의 최상위 레벨에서만 호출해야 합니다.
훅은 컴포넌트나 커스텀 훅의 최상위에서 호출해야 하며, 조건문이나 반복문 안에서 호출해서는 안 됩니다.
이렇게 해야 렌더링 사이에서 훅 호출 순서가 동일하게 유지됩니다.
2. React 함수 컴포넌트나 커스텀 훅에서만 호출해야 합니다.
훅은 일반적인 JavaScript 함수 내에서 호출할 수 없습니다.
대신, 훅은 React 함수 컴포넌트 내에서만 호출되어야 React가 훅의 상태를 추적할 수 있습니다.
💡 최적화를 위한 실전 TIP
• 의존성 배열 관리: 의존성 배열이 불필요하게 길어지지 않도록 주의하고, useCallback과 useMemo를 통해 컴포넌트가 의존하는 변수를 메모이제이션하여 의존성 업데이트를 최적화할 수 있습니다.
• useCallback과 useMemo: 컴포넌트 내부에서 함수를 정의하거나 값이 변하지 않는 경우, useCallback과 useMemo로 메모이제이션하여 불필요한 리렌더링을 줄일 수 있습니다.
이제 React의 주요 훅 중 useState와 useEffect의 작동 원리에 대해 자세히 살펴보겠습니다. useState는 상태를 관리하는 훅이고, useEffect는 컴포넌트가 렌더링될 때 특정 작업을 실행하도록 하는 훅입니다. 이 두 훅이 실제로 어떻게 작동하는지, 그리고 왜 이런 방식으로 설계되었는지 이해하면, 훅을 사용하는 컴포넌트를 더욱 효율적으로 설계할 수 있습니다.
useState
useState는 상태 관리 훅으로, 함수형 컴포넌트에서 로컬 상태를 추가할 수 있게 해줍니다. 호출 시 현재 상태 값과 상태를 업데이트할 수 있는 함수를 반환합니다. React는 상태가 변경되면 컴포넌트를 리렌더링하여 최신 상태를 반영합니다.
내부 작동 과정:
1. 컴포넌트 렌더링 중 호출: useState는 컴포넌트가 렌더링될 때마다 호출됩니다. React는 훅의 호출 순서와 위치를 추적하여 올바르게 상태를 유지합니다.
2. 배치 업데이트: 여러 상태 업데이트가 있을 경우 React는 모든 업데이트를 모아 배치로 처리하여 리렌더링 횟수를 최소화합니다. 이는 성능을 최적화하고 불필요한 리렌더링을 방지하는 방식입니다.
3. 클로저로 인한 상태 보존: useState가 반환하는 업데이트 함수는 렌더링 중 생성된 클로저로, 가장 최신 상태를 참조합니다. 이로 인해 이전 상태 값이 보존되며, 업데이트 시 올바른 상태 값이 반영됩니다.
4. 상태 업데이트의 비동기성: 상태 업데이트 함수는 기본적으로 비동기적이며, 상태가 즉시 변경되지 않고 다음 렌더링 때 반영됩니다. 상태 업데이트는 비동기적으로 처리되므로 여러 업데이트가 겹칠 경우, setState((prev) => prev + 1)와 같은 함수형 업데이트를 통해 상태를 안전하게 갱신할 수 있습니다.
useEffect
useEffect는 컴포넌트가 렌더링된 이후 특정 작업을 수행할 수 있는 기능을 제공합니다. 데이터 가져오기, DOM 수정, 구독 설정 등 부수 효과(Side Effects)를 처리하는 데 사용됩니다.
내부 작동 과정:
1. 마운트와 업데이트 시 실행: useEffect는 컴포넌트가 마운트되거나 업데이트될 때 실행됩니다. 의존성 배열이 제공되지 않으면 모든 렌더링 이후 실행되며, 의존성 배열에 따라 특정 값이 변경될 때만 실행할 수도 있습니다.
2. 의존성 배열의 역할: 의존성 배열에 포함된 값이 변경될 때만 useEffect가 실행됩니다. 의존성 배열에 빈 배열([])을 전달하면 최초 렌더링 때만 실행됩니다. 이를 통해 불필요한 효과 실행을 방지할 수 있습니다.
3. Cleanup 함수: useEffect에서 반환된 함수는 컴포넌트가 언마운트되거나 다음 렌더링이 발생하기 전에 호출됩니다. 이는 구독 취소, 타이머 클리어링 등 리소스를 정리하는 데 유용합니다.
의존성 배열로 인한 최적화
useEffect의 부수 효과가 특정 상태나 props의 변화에 반응하게 하려는 것입니다. 의존성 배열을 활용하면 부수 효과가 불필요하게 실행되지 않도록 하여 성능을 최적화할 수 있습니다.
+ 훅과 리렌더링의 상호작용
React는 훅을 통해 상태와 부수 효과를 관리하고, 최적화하여 불필요한 리렌더링을 방지합니다.
• useState와 리렌더링: 상태가 업데이트되면 React는 해당 컴포넌트를 리렌더링하여 새로운 상태를 반영합니다. 리렌더링이 발생하더라도, useState는 업데이트 후 최신 상태 값을 참조하므로 React가 상태 일관성을 유지합니다.
• useEffect와 리렌더링: useEffect는 리렌더링 이후에 실행되므로 렌더링이 완료된 상태에서 DOM 조작이 가능합니다. 의존성 배열을 통해 리렌더링 빈도를 제어하여 필요할 때만 효과를 실행할 수 있습니다.
마치며...
React 훅인 useState와 useEffect는 함수형 컴포넌트에서 상태와 생명주기를 관리하는 핵심 도구로, 내부 작동 원리를 이해하면 훨씬 효율적이고 예측 가능한 코드를 작성할 수 있습니다. 이 글에서 다룬 훅의 동작 방식과 리렌더링 원리, 그리고 훅의 규칙을 기억한다면, 실제 프로젝트에서도 React의 성능을 극대화할 수 있을 것입니다.
React 훅을 제대로 이해하고 활용하는 것은 더욱 깔끔하고 유지 보수하기 쉬운 코드를 만드는 중요한 발판이 됩니다. 앞으로 훅을 사용할 때 내부 원리를 염두에 두고, 상황에 맞게 최적화된 컴포넌트를 만들어 보세요. React와 훅의 세계에서 더욱 깊은 성장을 경험할 수 있을 것입니다.