효과 처리와 비동기 작업 (useEffect), useEffect의 기본 개념과 사용법

리액트(React)는 프론트엔드 개발을 위한 인기 있는 라이브러리로, 컴포넌트 기반으로 UI를 구성할 수 있도록 해줍니다. 리액트의 가장 중요한 특징 중 하나는 상태 관리와 생명주기 관리가 용이하다는 점입니다. 이 글에서는 리액트의 useEffect 훅에 관해 자세히 설명하겠습니다. useEffect는 효과(사이드 이펙트)를 처리하는 데 사용되며, 비동기 작업을 쉽게 다룰 수 있도록 해줍니다.

1. useEffect의 기본 개념

useEffect는 리액트의 훅 중 하나로서, 컴포넌트가 렌더링될 때마다 특정 작업을 수행할 수 있게 해줍니다. 주로 데이터 fetching, DOM 조작, 구독(subscription) 등을 처리할 때 사용됩니다. 이 훅은 컴포넌트의 생명주기와 밀접한 관계가 있으며, 다음과 같은 사항을 기억하는 것이 중요합니다.

1.1. 사이드 이펙트란?

사이드 이펙트는 애플리케이션에서 발생하는 예상치 못한 효과 또는 행동을 의미합니다. 예를 들어, API 호출, 타이머를 설정하거나 외부 라이브러리 사용 등은 모두 사이드 이펙트로 간주됩니다. 리액트 컴포넌트는 주로 UI를 렌더링하도록 설계되어 있지만, useEffect를 통해 사이드 이펙트를 효과적으로 처리할 수 있습니다.

2. useEffect의 기본 사용법

useEffect는 두 개의 인자를 받습니다. 첫 번째 인자는 실행할 함수이고, 두 번째 인자는 의존성 배열입니다. 이 의존성 배열을 통해 언제 이펙트를 다시 실행할지를 결정할 수 있습니다.

2.1. 기본적인 사용 예

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

const ExampleComponent = () => {
    const [data, setData] = useState(null);

    useEffect(() => {
        fetch('https://api.example.com/data')
            .then(response => response.json())
            .then(data => setData(data))
            .catch(error => console.error('Error fetching data:', error));
    }, []); // 빈 배열은 컴포넌트가 마운트될 때만 이펙트를 실행

    return (
        
{data ?
{JSON.stringify(data, null, 2)}

: 'Loading...'}

);
};

export default ExampleComponent;

위의 예제에서 useEffect는 컴포넌트가 마운트될 때 API를 호출하여 데이터를 가져옵니다. 빈 배열 []를 제공하면, 이 이펙트는 컴포넌트의 생애주기 동안 한 번만 실행됩니다.

2.2. 의존성 배열의 사용

의존성 배열은 useEffect의 실행 시점을 제어하는 중요한 역할을 합니다. 배열에 포함된 값이 변경될 때마다 이펙트가 실행됩니다.

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

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

    useEffect(() => {
        document.title = `Count: ${count}`;
    }, [count]); // count가 변경될 때마다 문서 제목 업데이트

    return (
        

Count: {count}

); }; export default Counter;

이 예제에서는 count 상태가 변경될 때마다 문서의 제목을 업데이트합니다. 이렇게 의존성 배열을 사용하는 것은 성능을 최적화하는 좋은 방법입니다.

3. 클린업 함수

useEffect는 클린업(cleanup) 함수를 반환할 수 있습니다. 이 함수는 컴포넌트가 언마운트되거나 의존성이 변경될 때 실행됩니다. 주로 구독을 해제하거나 타이머를 정리하는 데 사용됩니다.

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

const Timer = () => {
    const [count, setCount] = useState(0);

    useEffect(() => {
        const timer = setInterval(() => {
            setCount(prevCount => prevCount + 1);
        }, 1000);

        return () => clearInterval(timer); // 컴포넌트 언마운트 시 타이머 클리어
    }, []);

    return 

Timer: {count}

; }; export default Timer;

위 예제에서는 1초마다 카운트를 증가시키는 타이머를 설정합니다. 컴포넌트가 언마운트되면 clearInterval를 호출하여 메모리 누수를 방지하게 됩니다.

4. 비동기 작업 처리하기

비동기 작업을 useEffect 내에서 처리하는 것은 간단하지만 코드를 잘 구성해야 합니다. 비동기 함수를 직접 useEffect의 인자로 지정할 수는 없으므로, 일반적인 패턴은 내부에 비동기 함수를 정의하고 호출하는 것입니다.

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

const AsyncExample = () => {
    const [data, setData] = useState(null);

    useEffect(() => {
        const fetchData = async () => {
            try {
                const response = await fetch('https://api.example.com/data');
                const result = await response.json();
                setData(result);
            } catch (error) {
                console.error('Error fetching data:', error);
            }
        };

        fetchData(); // 비동기 함수 호출
    }, []);

    return (
        
{data ?
{JSON.stringify(data, null, 2)}

: 'Loading...'}

);
};

export default AsyncExample;

비동기 함수인 fetchData를 정의하고 useEffect 내에서 호출하여 API로부터 데이터를 가져오는 과정입니다.

5. 결론

리액트의 useEffect는 사이드 이펙트를 처리하기 위한 강력한 도구로, 비동기 작업을 손쉽게 관리할 수 있습니다. 이 글에서 다룬 기본 개념과 사용법을 이해하고 활용한다면, 복잡한 비즈니스 로직도 간단히 처리할 수 있는 가능성이 높아집니다. useEffect를 잘 활용하여 더욱 반응형이고 효율적인 리액트 애플리케이션을 개발해보세요!