PromleeBlog
sitemap
aboutMe

posting thumbnail
비동기 로직 테스트 - React로 시작하는 TDD 8편
Testing Asynchronous Logic - TDD with React Part 8

📅

🚀

들어가기 전에 🔗

지금까지 우리는 버튼을 누르면 숫자가 즉시 변하는
동기
(Synchronous) 코드를 테스트했습니다.
하지만 실제 웹사이트는 그렇지 않습니다.

배달 앱에서 음식을 주문한다고 상상해 보세요.
주문 버튼을 누른다고 해서 음식이 뿅 하고 나타나지 않습니다.
  1. "주문 접수 중..." (로딩)
  2. 배달 기사님 이동 (대기)
  3. "도착!" (성공) 또는 "주문 취소" (에러)

웹 개발에서도 서버에 데이터를 요청하면 이런 기다림의 시간이 필요합니다.
오늘은 이
비동기
(Asynchronous)를 테스트 코드로 어떻게 다루는지,
RTL
이 제공하는 강력한 도구인 findBy를 통해 알아보겠습니다.

🚀

시나리오: 사용자 목록 조회 🔗

새로운 요구사항을 정의합니다.
요구사항:
  1. 컴포넌트가 렌더링 되면 서버에서 사용자 목록을 가져온다.
  2. 데이터를 가져오는 동안에는 "불러오는 중..."이라는 글자를 보여준다.
  3. 데이터를 성공적으로 가져오면 사용자 이름(예: Leanne Graham)을 화면에 표시한다.
이 과정에는 시간의 흐름이 포함되어 있습니다.
테스트 코드가 이 흐름을 따라갈 수 있도록 만들어야 합니다.

🚀

1단계: 테스트 작성 (Red) 🔗

src/components/UserList.test.tsx 파일을 생성합니다.
이번 테스트의 핵심은
가짜 API
(Mock)를 만드는 것과
기다리는 쿼리
(findBy)를 사용하는 것입니다.

(참고: 이번 편에서는 간단한 global.fetch 모킹을 사용하고, 다음 9편에서 전문적인 MSW 도구를 다룹니다.)
src/components/UserList.test.tsx
import { render, screen } from '@testing-library/react';
import { vi, describe, test, expect, beforeEach, afterEach } from 'vitest';
// 아직 UserList 컴포넌트가 없으므로 import 에러가 날 수 있습니다.
import UserList from './UserList';
 
describe('UserList 컴포넌트 비동기 테스트', () => {
  
  // [설정] 테스트 전에 가짜 fetch 함수를 만듭니다.
  beforeEach(() => {
    // global.fetch를 스파이(Spy)로 심어서 가로챕니다.
    // 실제 서버 대신 우리가 정의한 가짜 데이터(JSON)를 반환하도록 합니다.
    global.fetch = vi.fn().mockResolvedValue({
      json: async () => [
        { id: 1, name: 'Leanne Graham' },
        { id: 2, name: 'Ervin Howell' },
      ],
    });
  });
 
  // [정리] 테스트가 끝나면 가짜 함수를 초기화합니다.
  afterEach(() => {
    vi.restoreAllMocks();
  });
 
  test('데이터를 불러오는 동안 로딩 문구가 뜨고, 이후 데이터가 표시된다', async () => {
    render(<UserList />);
 
    console.log('[Step 1] 렌더링 직후 확인');
    screen.debug();
 
    // 1. 로딩 상태 확인 (동기)
    // 렌더링 되자마자 바로 보여야 하므로 getBy를 씁니다.
    expect(screen.getByText('불러오는 중...')).toBeInTheDocument();
 
    console.log('[Step 2] 데이터 수신 대기');
    
    // 2. 데이터 표시 확인 (비동기)
    // 데이터는 나중에 뜨므로 getBy를 쓰면 에러가 납니다.
    // findBy는 요소가 나타날 때까지(기본 1초) 기다려줍니다.
    const userItem = await screen.findByText('Leanne Graham');
    
    console.log('[Step 3] 데이터 수신 완료 후 디버깅');
    screen.debug();
 
    expect(userItem).toBeInTheDocument();
  });
});

getBy vs findBy 🔗

비동기 테스트에서 가장 중요한 차이점입니다.

🚀

2단계: 테스트 실행 (Red) 🔗

테스트를 실행해 봅시다.
Terminal
npm test -- src/components/UserList.test.tsx

실행 결과 분석 🔗

당연히 실패합니다.
UserList 컴포넌트가 없기 때문입니다.
하지만 우리는 테스트 코드를 통해
무엇을 구현해야 하는가
를 명확히 알게 되었습니다.

🚀

3단계: 기능 구현 (Green) 🔗

이제 src/components/UserList.tsx를 만들고 기능을 구현합니다.
useEffectfetch를 사용하여 실제 데이터를 가져오는 로직을 작성합니다.
src/components/UserList.tsx
import { useEffect, useState } from 'react';
 
// 사용자 데이터 타입 정의
interface User {
  id: number;
  name: string;
}
 
export default function UserList() {
  const [users, setUsers] = useState<User[]>([]);
  const [loading, setLoading] = useState(true);
 
  useEffect(() => {
    // 실제 API 호출 (테스트에서는 가짜 함수가 실행됨)
    fetch('https://jsonplaceholder.typicode.com/users')
      .then((response) => response.json())
      .then((data) => {
        setUsers(data);
        setLoading(false); // 로딩 끝
      })
      .catch((error) => {
        console.error('Error fetching data:', error);
        setLoading(false);
      });
  }, []);
 
  if (loading) {
    return <p>불러오는 중...</p>;
  }
 
  return (
    <ul>
      {users.map((user) => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
}

🚀

4단계: 테스트 재실행 및 결과 분석 🔗

다시 테스트를 실행합니다.
Terminal
npm test -- src/components/UserList.test.tsx

성공 로그 확인 및 분석 🔗

터미널에 찍힌 로그를 통해 시간의 흐름을 확인해 보세요.
성공 로그 확인
성공 로그 확인
[Step 1] 렌더링 직후 확인
<body>
  <div>
    <p>불러오는 중...</p>
  </div>
</body>
 
[Step 2] 데이터 수신 대기
 
[Step 3] 데이터 수신 완료 후 디버깅
<body>
  <div>
    <ul>
      <li>Leanne Graham</li>
      <li>Ervin Howell</li>
    </ul>
  </div>
</body>
 
PASS src/components/UserList.test.tsx

🚀

결론 🔗

소스 코드는 GitHub - Promleeblog/react-tdd-setup 에서 확인할 수 있습니다.
Terminal
git clone https://github.com/PROMLEE/my-tdd-app.git
cd my-tdd-app
git checkout part8
오늘은 프론트엔드 테스트의 난관인 비동기 로직을 다루었습니다.

하지만 global.fetch를 직접 가로채는 방식은 API가 많아지면 관리가 힘들어집니다.
다음 시간에는 현업에서 가장 많이 사용하는 네트워크 Mocking 도구인
9편. API Mock 전략과 MSW 활용
에 대해 알아보겠습니다.

참고 🔗