효과 처리와 비동기 작업 (useEffect), 비동기 데이터 요청 및 API 연동

효과 처리와 비동기 작업 (useEffect), 비동기 데이터 요청 및 API 연동

리액트(React)는 사용자 인터페이스를 구성하기 위한 라이브러리로서, 단일 페이지 애플리케이션(SPA)을 쉽게 구축할 수 있게 해줍니다. 리액트의 주요 개념 중 하나는 컴포넌트입니다. 이 컴포넌트는 UI의 두뇌 역할을 하며, 각 컴포넌트는 자신의 상태(state)와 속성(props)을 관리합니다. 이 글에서는 효과 처리에 널리 사용되는 useEffect 훅과 비동기 작업에 대해 알아보고, 이를 활용하여 API로부터 데이터를 요청하는 방법을 심도 깊게 설명하겠습니다.

1. useEffect 훅 소개

useEffect는 컴포넌트의 생명주기 에서 사이드 이펙트를 처리하기 위한 훅입니다. 주로 다음과 같은 경우에 사용됩니다:

  • 데이터를 가져올 때
  • 구독(subscriptions)을 설정할 때
  • DOM을 수동으로 변경할 때

1.1. 기본 사용법

useEffect는 두 개의 인자를 받습니다: 첫 번째 인자는 사이드 이펙트를 발생시키는 함수이고, 두 번째 인자는 의존성 배열입니다. 의존성 배열에 지정된 값이 변경될 때마다 효과가 다시 실행 됩니다.

import { useEffect } from 'react';

function MyComponent() {
    useEffect(() => {
        // 효과를 처리하는 코드
        document.title = 'My Component Loaded';

        // 정리(clean up) 함수
        return () => {
            document.title = 'React App';
        };
    }, []); // 빈 배열은 컴포넌트가 마운트될 때만 실행됨

    return <div>안녕하세요, 리액트 컴포넌트!</div>;
}

위 예제에서 useEffect는 컴포넌트가 처음 렌더링될 때 한 번만 실행됩니다. 그리고 컴포넌트가 언마운트될 때 제목을 원래 상태로 돌려 놓기 위해 정리(clean up) 함수를 반환합니다.

1.2. 의존성 배열의 중요성

의존성 배열을 설정하면 특정 값이 변경될 때 useEffect가 재실행됩니다. 예를 들어, 다음과 같이 count라는 상태 값을 사용할 수 있습니다:

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

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

    useEffect(() => {
        document.title = `카운트: ${count}`;
    }, [count]); // count가 변경될 때마다 실행됨

    return (
        <div>
            <p>현재 카운트: {count}</p>
            <button onClick={() => setCount(count + 1)}>카운트 증가</button>
        </div>
    );
}

이 경우, 버튼을 클릭하여 카운트를 증가시키면 제목이 변경됩니다.

2. 비동기 데이터 요청

리액트에서 API를 통해 데이터를 요청할 때는 일반적으로 fetch API를 사용합니다. 비동기 작업을 수행하기 위해 useEffect와 함께 async/await 패턴을 사용할 수 있습니다. 다음은 데이터를 API로부터 fetch하는 간단한 예제입니다.

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

function DataFetchingComponent() {
    const [data, setData] = useState(null);
    const [loading, setLoading] = useState(true);
    const [error, setError] = useState(null);

    useEffect(() => {
        const fetchData = async () => {
            try {
                const response = await fetch('https://api.example.com/data');
                if (!response.ok) {
                    throw new Error('네트워크 응답에 문제가 있습니다.');
                }
                const result = await response.json();
                setData(result);
            } catch (error) {
                setError(error);
            } finally {
                setLoading(false);
            }
        };

        fetchData(); // 비동기 함수 호출
    }, []); // 빈 배열은 컴포넌트가 마운트될 때만 실행됨

    if (loading) return <p>로딩 중...</p>;
    if (error) return <p>오류: {error.message}</p>;
    return <pre>{JSON.stringify(data, null, 2)}</pre>;
}

위 예제에서는 API 요청을 통해 데이터를 가져온 후, 로딩 상태를 관리하고, 오류 처리도 수행합니다. useEffect 내부에서 비동기 함수를 선언하고 호출하는 방식으로 진행됩니다.

3. API 연동 실습

이제 API 연동의 실제 예제를 만들어 보겠습니다. JSONPlaceholder라는 무료 API를 사용하여 사용자 목록을 가져오는 간단한 애플리케이션을 만들어 보겠습니다.

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

function UserList() {
    const [users, setUsers] = useState([]);
    const [loading, setLoading] = useState(true);
    const [error, setError] = useState(null);

    useEffect(() => {
        const fetchUsers = async () => {
            setLoading(true);
            try {
                const response = await fetch('https://jsonplaceholder.typicode.com/users');
                if (!response.ok) throw new Error('네트워크 응답에 문제가 있습니다.');
                const data = await response.json();
                setUsers(data);
            } catch (error) {
                setError(error);
            } finally {
                setLoading(false);
            }
        };

        fetchUsers();
    }, []);

    if (loading) return <p>로딩 중...</p>;
    if (error) return <p>오류: {error.message}</p>;

    return (
        <ul>
            {users.map(user => (
                <li key={user.id}>{user.name} ({user.email})</li>
            ))}
        </ul>
    );
}

위의 UserList 컴포넌트는 사용자의 목록을 가져와 화면에 표시합니다. 데이터가 로딩 중일 때는 로딩 메시지를 보여주고, 오류가 발생하면 오류 메시지를 표시합니다.

4. 정리

useEffect 훅을 활용하여 컴포넌트의 생명 주기에서 사이드 이펙트를 처리하고, 비동기 작업을 통해 외부 API와 연동하는 과정을 살펴보았습니다. 이 패턴은 리액트 애플리케이션에서 데이터 fetching 및 관리에서 필수적인 부분입니다. 다양한 API를 활용하여 데이터의 상태를 관리하고 사용자에게 더욱 풍부한 경험을 제공할 수 있습니다.

더 나아가 리액트 쿼리(React Query) 같은 라이브러리를 사용하여 API 호출 및 데이터 캐싱을 관리하면 훨씬 더 효율적인 작업이 가능합니다. 하지만 이 글에서는 가장 간단하고 기본적인 방식으로 API 연동을 다루었습니다. 앞으로 더 발전된 기술과 패턴을 배우고 적용해 보시기 바랍니다.