프로그래밍/ReactJS

React 애플리케이션에서 비동기 코드 테스트: 완벽 가이드

shimdh 2025. 9. 30. 09:04
728x90

소프트웨어 개발의 세계에서 테스팅은 애플리케이션이 예상대로 작동하는지 확인하는 데 필수적인 요소입니다. 특히 React 애플리케이션은 컴포넌트의 동적인 특성과 상태 관리로 인해 비동기 코드를 테스트하는 것이 까다로운 과제가 될 수 있습니다. 하지만 걱정하지 마세요! 이 블로그 게시물은 React 애플리케이션에서 비동기 작업을 효과적으로 테스트하는 방법에 대한 깊이 있는 이해를 제공하고, 여러분이 자신감을 가지고 테스트를 작성할 수 있도록 도울 것입니다.


728x90

비동기 코드, 왜 중요할까요?

비동기 코드는 함수가 메인 스레드를 차단하지 않고 실행될 수 있도록 합니다. React 애플리케이션에서는 주로 다음과 같은 상황에서 비동기 코드를 접하게 됩니다.

  • 데이터 가져오기: 사용자 정보를 검색하기 위해 API 호출을 하는 경우.
  • 타이머: 지연된 작업을 위해 setTimeout 또는 setInterval을 사용하는 경우.

이러한 작업은 즉시 반환되지 않으므로, 테스트 시 특별한 처리가 필요합니다.


비동기 코드를 테스트해야 하는 이유

그렇다면 왜 비동기 코드를 굳이 테스트해야 할까요? 여기에는 몇 가지 핵심적인 이유가 있습니다.

  1. 정확성 보장: 비동기 작업이 완료된 후에도 컴포넌트가 올바르게 작동하는지 확인해야 합니다.
  2. 회귀 방지: 향후 변경 사항이 비동기 동작과 관련된 기존 기능을 손상시키지 않도록 방지합니다.
  3. 신뢰성 향상: 자동화된 테스트는 개발 프로세스 초기에 버그를 찾아 수정하는 데 큰 도움을 줍니다.

비동기 코드 테스트를 위한 핵심 도구

React 애플리케이션에서 비동기 코드를 테스트하기 위해 일반적으로 두 가지 인기 있는 도구가 사용됩니다.

  • Jest: 테스트 실행 및 어설션을 위한 유틸리티를 제공하는 강력한 JavaScript 테스팅 프레임워크입니다.
  • React Testing Library (RTL): 사용자 상호 작용을 시뮬레이션하여 React 컴포넌트 테스트를 위해 특별히 설계된 라이브러리입니다.

이 두 도구는 함께 동기 및 비동기 동작 모두에 대한 효과적인 테스트를 작성할 수 있도록 합니다.


실전! 비동기 코드 테스트 작성하기

이제 실제 예시를 통해 어떻게 비동기 코드를 테스트하는지 살펴보겠습니다. 컴포넌트가 마운트될 때 API에서 사용자 데이터를 가져오는 간단한 UserComponent를 가정해 봅시다.

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

const UserComponent = () => {
  const [userData, setUserData] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const fetchUserData = async () => {
      const response = await fetch('https://api.example.com/user');
      const data = await response.json();
      setUserData(data);
      setLoading(false);
    };

    fetchUserData();
  }, []);

  if (loading) return <div>Loading...</div>;

  return <div>{userData.name}</div>;
};

export default UserComponent;

이제 Jest와 RTL을 사용하여 이 컴포넌트가 로딩 단계에서 그리고 데이터가 가져와진 후에 올바르게 동작하는지 확인하는 테스트 케이스를 작성해 보겠습니다.

import { render, screen } from '@testing-library/react';
import UserComponent from './UserComponent';

// 전역 fetch 함수 모의 (Mocking)
global.fetch = jest.fn(() =>
  Promise.resolve({
    json: () => Promise.resolve({ name: 'John Doe' }),
  })
);

test('초기에 로딩 메시지를 렌더링한다', () => {
  render(<UserComponent />);

  // 초기에 "Loading..."이 표시되는지 확인
  expect(screen.getByText(/loading/i)).toBeInTheDocument();
});

test('데이터를 가져온 후 사용자 이름을 렌더링한다', async () => {
  render(<UserComponent />);

  // 프로미스가 해결될 때까지 기다린다
  expect(await screen.findByText(/john doe/i)).toBeInTheDocument();
});

테스트 분석

위 테스트 코드를 단계별로 분석해 봅시다.

  1. Fetch 기능 모의 (Mocking):
    컴포넌트가 렌더링 중에 fetch를 호출할 때 실제 네트워크 요청을 하는 대신 미리 정의된 응답을 받도록 fetch를 전역적으로 모의(mock) 합니다. 이는 테스트의 속도를 높이고 외부 의존성을 제거하는 데 중요합니다.
  2. 초기 상태 테스트:
    첫 번째 테스트는 render(<UserComponent />)를 사용하여 컴포넌트를 렌더링하고, "Loading..." 텍스트가 초기 렌더링 시 문서에 표시되는지 expect(screen.getByText(/loading/i)).toBeInTheDocument()를 통해 확인합니다.
  3. 비동기 동작 테스트:
    두 번째 테스트는 findByText 를 사용합니다. 이 함수는 모의된 API 호출에서 반환된 프로미스가 해결된 후 지정된 텍스트가 화면에 나타날 때까지 자동으로 기다립니다. 이 메서드는 비동기 업데이트와 관련된 모든 타이밍 문제를 원활하게 처리하여 테스트를 안정적으로 만듭니다.

모범 사례

비동기 코드 테스트를 작성할 때 다음 모범 사례를 따르면 더욱 견고하고 유지보수하기 쉬운 테스트 코드를 만들 수 있습니다.

  • 설명적인 테스트 이름 사용: 각 테스트가 무엇을 검증하는지 명확하게 알 수 있도록 테스트에 설명적인 이름을 사용하십시오.
  • 모의 정리: Jest가 제공하는 afterEach 또는 유사한 메서드를 사용하여 테스트 간에 모의를 정리하십시오. 이는 테스트 간의 간섭을 방지합니다.
  • 오류 처리 고려: 오류 처리와 같은 예외적인 경우를 항상 고려하십시오. API가 실패하면 어떻게 될까요? 오류 메시지가 제대로 표시되는지, 컴포넌트가 올바르게 작동하는지 테스트해야 합니다.

결론

비동기 코드 테스트는 React 애플리케이션 개발에서 중요한 부분입니다. Jest와 React Testing Library 같은 강력한 도구를 효과적으로 활용하고, 위에서 설명한 모범 사례를 따른다면, 여러분은 React 애플리케이션 내에서 비동기 코드 테스트를 자신감 있게 처리하고 더욱 견고하고 신뢰할 수 있는 애플리케이션을 구축할 수 있을 것입니다.

728x90