[개발] React Hooks를 활용한 상태 관리와 라이프사이클 관리

React Hooks의 소개와 기본 개념

React Hooks는 리액트 16.8 버전부터 도입된 기능으로, 함수형 컴포넌트에서도 상태 관리와 라이프사이클 관리 등의 기능을 사용할 수 있게 해줍니다. Hooks를 사용하면 클래스 컴포넌트를 사용하지 않고도 더 간단하고 가독성 좋은 코드를 작성할 수 있습니다.

useState Hook

useState Hook은 상태 값을 저장하고 업데이트할 수 있는 기능을 제공합니다. 함수 컴포넌트에서도 상태 값을 관리할 수 있게 해주며, useState Hook을 사용하기 위해서는 다음과 같은 형식으로 사용합니다.

const [state, setState] = useState(initialState);

state는 상태 값을 의미하고, setState는 상태 값을 업데이트하는 함수입니다. initialState는 상태의 초기 값을 설정하는 값이며, 여기에는 상태가 들어갑니다.

useEffect Hook

useEffect Hook은 컴포넌트의 라이프사이클에 효과를 주기 위해 사용됩니다. 컴포넌트가 렌더링된 후, 업데이트되었을 때, 혹은 사라질 때 등 다양한 시점에서 원하는 동작을 수행할 수 있습니다. useEffect Hook을 사용하기 위해서는 다음과 같은 형식으로 사용합니다.

useEffect(() => {
  // 원하는 동작을 수행하는 코드 작성
}, [dependency]);

여기서 dependency는 의존성 배열이며, 이 배열에 포함된 값이 변경될 때만 useEffect의 코드 블록이 실행됩니다. 만약 의존성 배열을 비워둔다면, 컴포넌트가 렌더링되거나 업데이트될 때마다 useEffect의 코드 블록이 실행됩니다.


useState Hook을 활용한 상태 관리

useState Hook은 함수형 컴포넌트에서 상태 값을 저장하고 업데이트할 수 있게 도와줍니다. 상태 값은 컴포넌트가 렌더링될 때 유지되며, 상태 값을 업데이트할 때는 해당 상태의 setter 함수를 호출하여 업데이트합니다.

상태 값 저장하기

useState Hook을 사용하여 상태 값을 저장하려면, 다음과 같은 형식으로 사용합니다.

const [state, setState] = useState(initialState);

위의 코드에서 state는 상태 값을 의미하고, setState는 해당 상태 값을 업데이트하는 함수입니다. initialState는 상태의 초기 값을 설정하는 값으로, 여기에는 원하는 상태가 들어갑니다.

상태 값 업데이트하기

상태 값을 업데이트할 때는 해당 상태의 setter 함수를 호출하여 값을 변경합니다. setter 함수에는 새로운 값을 전달하면 됩니다.

setState(newValue);

useState Hook을 사용하면 여러 개의 상태 값을 가질 수도 있습니다. 각 상태 값에 대해서는 별도의 useState Hook을 사용하면 됩니다.

다음은 useState Hook을 사용하여 카운터 기능을 구현한 예시 코드입니다.

import React, { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);

  const increment = () => {
    setCount(count + 1);
  };

  const decrement = () => {
    setCount(count - 1);
  };

  return (
    

Count: {count}

); }

useEffect Hook을 활용한 라이프사이클 관리

useEffect Hook을 사용하면 컴포넌트의 라이프사이클에 효과를 부여할 수 있습니다. 컴포넌트가 마운트될 때, 언마운트될 때, 혹은 특정 값을 감시하고자 할 때 원하는 동작을 수행할 수 있습니다.

useEffect Hook 사용하기

useEffect Hook을 사용하여 컴포넌트의 라이프사이클에 효과를 부여하려면, 다음과 같은 형식으로 사용합니다.

useEffect(() => {
  // 원하는 동작을 수행하는 코드 작성
}, [dependency]);

dependency는 의존성 배열이며, 여기에 포함된 값이 변경될 때만 useEffect의 코드 블록이 실행됩니다. 만약 의존성 배열을 비워둔다면, 컴포넌트가 렌더링되거나 업데이트될 때마다 useEffect의 코드 블록이 실행됩니다.

마운트될 때 효과 주기

컴포넌트가 마운트된 후에 원하는 동작을 수행하려면, useEffect Hook에 빈 의존성 배열을 전달합니다.

useEffect(() => {
  // 컴포넌트가 마운트된 후에 수행할 동작
}, []);

특정 값 감시하기

특정 값을 감시하고 그 값이 변경될 때마다 원하는 동작을 수행하려면, 해당 값이 의존성 배열에 포함되도록 설정합니다.

useEffect(() => {
  // 특정 값을 감시하고 그 값이 변경될 때 수행할 동작
}, [value]);

다음은 useEffect Hook을 사용하여 타이틀이 변경될 때마다 문서 제목을 업데이트하는 예시 코드입니다.

import React, { useState, useEffect } from 'react';

function TitleUpdater() {
  const [title, setTitle] = useState('');

  useEffect(() => {
    document.title = title;
  }, [title]);

  const handleChange = (e) => {
    setTitle(e.target.value);
  };

  return (
    

Enter a new title:

); }

useContext Hook을 활용한 전역 상태 관리

useContext Hook을 사용하면 전역 상태를 관리할 수 있습니다. 이를 통해 컴포넌트 간에 상태를 공유하고 쉽게 전역 상태를 업데이트할 수 있습니다.

전역 상태 관리하기

useContext Hook을 사용하여 전역 상태를 관리하려면, 다음과 같은 형식으로 사용합니다.

const value = useContext(MyContext);

MyContext는 전역 상태를 저장하고 있는 Context 객체를 의미합니다. 이때, useContext는 해당 Context 객체의 현재 값으로 초기 값을 설정합니다.

전역 상태를 업데이트하려면, 해당 Context 객체의 Provider를 사용하여 값을 변경해야 합니다.

전역 상태 사용 예시

다음은 useContext Hook을 사용하여 전역 상태를 관리하는 예시 코드입니다. Counter 컴포넌트에서는 count 값이 전역 상태로 사용되고, Control 컴포넌트에서는 count 값을 증가시키거나 감소시킬 수 있습니다.

import React, { useState, useContext } from 'react';

const CountContext = React.createContext();

function Counter() {
  const count = useContext(CountContext);

  return (
    

Count: {count}

); } function Control() { const count = useContext(CountContext); const increment = () => { // count 값 증가 }; const decrement = () => { // count 값 감소 }; return (
); } function App() { const [count, setCount] = useState(0); return ( ); }

useReducer Hook을 활용한 복잡한 상태 관리

useReducer Hook을 사용하면 복잡한 상태를 효과적으로 관리할 수 있습니다. 이를 통해 상태를 업데이트하는 로직을 분리하고, 액션을 사용하여 상태 변경을 관리할 수 있습니다.

복잡한 상태 관리하기

useReducer Hook을 사용하여 복잡한 상태를 관리하려면, 다음과 같은 형식으로 사용합니다.

const [state, dispatch] = useReducer(reducer, initialState);

state는 상태 객체, dispatch는 상태를 업데이트하는 함수입니다. reducer는 현재 상태와 액션을 받아 새로운 상태를 반환하는 함수이며, initialState은 초기 상태를 나타내는 값입니다.

dispatch 함수를 호출하여 액션을 전달하면, reducer가 호출되고 새로운 상태가 반환됩니다.

복잡한 상태 관리 사용 예시

다음은 useReducer Hook을 사용하여 복잡한 상태를 관리하는 예시 코드입니다. Counter 컴포넌트에서는 count 값에 따라 버튼의 활성화 상태가 변경되고, Control 컴포넌트에서는 count 값을 증가시키거나 초기화할 수 있습니다.

import React, { useReducer } from 'react';

const initialState = {
  count: 0,
};

function reducer(state, action) {
  switch (action.type) {
    case 'INCREMENT':
      return { ...state, count: state.count + 1 };
    case 'RESET':
      return { ...state, count: 0 };
    default:
      throw new Error();
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    

Count: {state.count}

); } function Control() { const [state, dispatch] = useReducer(reducer, initialState); return (
); } function App() { return (
); }

useMemo Hook을 활용한 성능 최적화

useMemo Hook을 사용하면 계산 비용이 큰 함수의 결과를 기억하고, 필요할 때 재사용할 수 있어 성능을 향상시킬 수 있습니다. 이를 통해 렌더링 과정에서 불필요한 연산을 줄일 수 있습니다.

성능 최적화를 위한 useMemo 사용

useMemo Hook을 사용하여 성능을 최적화하려면, 다음과 같은 형식으로 사용합니다.

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

computeExpensiveValue는 계산 비용이 큰 함수로, useMemo Hook에서 호출된 결과를 memoizedValue에 저장합니다. useMemo의 두 번째 인자로는 의존성 배열을 전달합니다. 의존성 배열 안의 값이 변경되지 않은 경우에는 이전에 계산된 결과를 재사용하게 됩니다.

성능 최적화 사용 예시

다음은 useMeno Hook을 사용하여 성능을 최적화하는 예시 코드입니다. Square 컴포넌트에서는 숫자를 제곱한 값을 계산하고, Button 컴포넌트에서는 Square 컴포넌트를 사용하면서 값이 변경될 때만 결과를 계산합니다.

import React, { useMemo, useState } from 'react';

function Square({ number }) {
  const computedValue = useMemo(() => {
    console.log('computeValue called');
    return number * number;
  }, [number]);

  return 
{computedValue}
; } function Button() { const [value, setValue] = useState(0); const increment = () => { setValue(value + 1); }; return (
); } function App() { return

위의 코드에서는 Square 컴포넌트를 render할 때 number 값이 변경될 때만 computeValue 함수가 호출되고, 값이 변경되지 않은 경우에는 이전에 계산된 결과를 재사용하여 성능을 향상시킵니다.


useCallback Hook을 활용한 함수 최적화

useCallback Hook을 사용하면 함수를 캐싱하여 불필요한 함수 재생성을 방지하여 성능을 최적화할 수 있습니다. 이를 통해 자식 컴포넌트에 전달되는 함수가 불필요하게 재생성되는 것을 방지할 수 있습니다.

함수 최적화를 위한 useCallback 사용

useCallback Hook을 사용하여 함수를 최적화하려면, 다음과 같은 형식으로 사용합니다.

const memoizedCallback = useCallback(() => {
  // 함수 본문
}, [dependencyArray]);

useCallback Hook은 첫 번째 매개변수로 콜백 함수를 받으며, 두 번째 매개변수로 의존성 배열을 전달합니다. 의존성 배열 안의 값이 변경되지 않은 경우에는 이전에 생성된 함수를 재사용합니다.

함수 최적화 사용 예시

다음은 useCallback Hook을 사용하여 함수를 최적화하는 예시 코드입니다. Parent 컴포넌트에서는 Child 컴포넌트를 렌더링하면서 handleClick 함수를 전달합니다. handleClick 함수가 변경되지 않은 경우, MemoizedChild 컴포넌트에서 이전에 생성된 함수를 재사용하여 성능을 향상시킵니다.

import React, { useCallback, useState } from 'react';

function Parent() {
  const [count, setCount] = useState(0);

  const handleClick = useCallback(() => {
    console.log('handleClick called');
    setCount(count + 1);
  }, [count]);

  return (
    
); } function Child({ handleClick }) { return ; } const MemoizedChild = React.memo(Child); function App() { return ; }

위의 코드에서는 Parent 컴포넌트에서 handleClick 함수를 useCallback으로 감싸서 MemoizedChild 컴포넌트에 전달합니다. MemoizedChild 컴포넌트는 handleClick 함수가 변경되지 않은 경우에는 이전에 생성된 함수를 재사용하여 성능을 향상시킵니다.


useRef Hook을 활용한 DOM 접근 및 값 보존

useRef Hook을 사용하면 DOM 요소에 접근하고, 값이 변경되어도 컴포넌트가 재렌더링될 때 이전 값을 보존할 수 있습니다. 또한, useRef를 사용하여 생성한 객체는 컴포넌트의 생명주기 동안 유지됩니다.

DOM 접근과 값 보존을 위한 useRef 사용

useRef Hook은 useRef 함수를 호출하여 객체를 생성합니다. 생성된 객체를 사용하여 DOM 요소에 접근할 수 있으며, 컴포넌트의 생명주기 동안 유지되기 때문에 값이 변경되어도 이전 값을 보존할 수 있습니다.

const refContainer = useRef(initialValue);

위의 코드에서 initialValue는 초기 값으로 사용될 값입니다. refContainer.current를 통해 현재 값을 읽거나 수정할 수 있습니다.

DOM 접근 및 값 보존 사용 예시

다음은 useRef Hook을 사용하여 DOM 접근 및 값 보존을 하는 예시 코드입니다. useRef를 사용하여 input 요소에 접근하고 입력된 값을 보존하여 버튼을 클릭할 때마다 이전에 입력된 값들을 출력합니다.

import React, { useRef, useState } from 'react';

function App() {
  const inputRef = useRef();
  const [values, setValues] = useState([]);

  const handleClick = () => {
    const value = inputRef.current.value;
    setValues(prevValues => [...prevValues, value]);
    inputRef.current.value = '';
  };

  return (
    
    {values.map((value, index) => (
  • {value}
  • ))}
); }

위의 코드에서는 useRef를 사용하여 input 요소에 접근하고, 값을 보존하고 있습니다. 버튼을 클릭할 때마다 input 요소의 입력 값은 이전 값들과 함께 배열에 추가되고, 출력되는 리스트에 이전에 입력된 값들이 순차적으로 출력됩니다.


useLayoutEffect Hook과 useEffect의 차이점

useLayoutEffect Hook과 useEffect Hook은 React 컴포넌트의 생명주기 동안 특정 동작을 수행할 때 사용됩니다. 이 두 가지 Hook의 주요한 차이점은 동작이 실행되는 시점입니다.

useEffect

useEffect Hook은 컴포넌트가 렌더링되고, 화면에 업데이트된 후 비동기적으로 동작합니다. useEffect의 콜백 함수는 렌더링 과정이 완료된 이후에 실행되기 때문에, 화면이 업데이트된 후에 특정 작업을 수행하는 데 적합합니다.

useEffect(() => {
  // 비동기 동작
}, [dependencyArray]);

useLayoutEffect

useLayoutEffect Hook은 컴포넌트가 렌더링되고, 화면에 업데이트되기 전에 동기적으로 동작합니다. useLayoutEffect에서 실행된 동작은 화면이 업데이트되기 전에 처리되므로, 화면이 업데이트되기 직전에 발생하는 레이아웃 변경을 처리하는 데 주로 사용됩니다.

useLayoutEffect(() => {
  // 동기 동작
}, [dependencyArray]);

useLayoutEffect와 useEffect 사용 예시

다음은 useLayoutEffect와 useEffect를 사용하는 예시 코드입니다. useLayoutEffect를 사용하면서 setState 함수를 사용하면 경고 메시지가 표시되고 useEffect를 사용하면 경고 메시지가 표시되지 않습니다.

import React, { useLayoutEffect, useEffect, useState } from 'react';

function App() {
  const [count, setCount] = useState(0);

  useLayoutEffect(() => {
    if (count === 0) {
      alert('Layout effect: count is 0');
    }
  }, [count]);

  useEffect(() => {
    if (count === 0) {
      alert('Effect: count is 0');
    }
  }, [count]);

  return (
    

Count: {count}

); }

위의 코드에서는 useLayoutEffect와 useEffect를 사용하여 count 값이 0인 경우에 대해 각각 다른 동작을 수행하도록 설정했습니다. useLayoutEffect의 경우 count가 0인 경우에 렌더링이 되기 전에 alert 창이 열리고, useEffect의 경우 렌더링 후에 alert 창이 열립니다.


Custom Hook을 만들어 재사용 가능한 로직 구현하기

Custom Hook은 React Hook의 조합을 사용하여 로직을 추출하고, 해당 로직을 여러 컴포넌트에서 재사용할 수 있는 함수입니다. Custom Hook은 “use”로 시작하는 함수 이름을 사용하여 작성하며, 기존의 Hook을 사용하거나 다른 Custom Hook을 호출하여 로직을 구현할 수 있습니다.

Custom Hook을 만드는 방법

Custom Hook은 예제 코드와 같이 일반적인 함수로 작성됩니다. 다음은 Custom Hook의 작성 방법입니다.

import { useState, useEffect } from 'react';

const useCustomHook = (initialValue) => {
  const [value, setValue] = useState(initialValue);

  useEffect(() => {
    // 로직 구현
  }, [value]);

  const handleChange = (newValue) => {
    setValue(newValue);
  };

  return {
    value,
    handleChange,
  };
};

위의 코드에서는 “useCustomHook”이라는 Custom Hook을 작성하였습니다. useState와 useEffect를 사용하여 로직을 구현하고, handleChange 함수를 반환합니다.

Custom Hook 사용 예시

다음은 Custom Hook을 사용하여 값을 관리하는 예시 코드입니다. useCustomHook을 호출하여 value와 handleChange 함수를 사용하여 값의 상태를 관리합니다.

import React from 'react';
import useCustomHook from './useCustomHook';

function App() {
  const { value, handleChange } = useCustomHook('');

  return (
    
handleChange(e.target.value)} />

Value: {value}

); }

위의 코드에서는 useCustomHook을 호출하여 value와 handleChange를 반환받고, 이를 사용하여 input 요소의 값을 관리하고 출력합니다.


Leave a Comment