프로그래밍/ReactJS

⚛️ ReactJS 테스팅 완벽 가이드: Jest와 React Testing Library로 견고한 앱 만들기

shimdh 2025. 9. 29. 07:57
728x90

React 애플리케이션을 개발하는 과정에서 우리는 항상 사용자에게 최고의 경험을 제공하고자 합니다. 하지만 복잡한 UI를 구축하다 보면 예상치 못한 버그나 기능 오작동이 발생할 수 있습니다. 이때 바로 '테스팅' 이 빛을 발합니다. 테스팅은 애플리케이션이 의도한 대로 작동하는지 확인하고, 개발 초기 단계에서부터 잠재적인 문제를 식별하여 궁극적으로 더 안정적이고 유지보수하기 쉬운 코드를 만드는 데 필수적인 과정입니다.

이번 블로그 게시물에서는 React 테스팅의 중요성을 강조하고, 특히 JestReact Testing Library(RTL) 라는 두 가지 강력한 도구를 사용하여 React 구성 요소를 효과적으로 테스트하는 방법을 심층적으로 다룰 것입니다.


728x90

🧐 테스팅의 중요성: 왜 테스트를 해야 할까요?

테스팅은 단순히 버그를 찾는 것 이상의 의미를 가집니다. 이는 소프트웨어 개발 수명 주기 전반에 걸쳐 여러 가지 이점을 제공합니다.

  1. 품질 보증: 다양한 시나리오에서 구성 요소가 올바르게 작동하는지 확인하여 애플리케이션의 전반적인 품질을 높입니다. 사용자가 겪을 수 있는 모든 경우의 수를 시뮬레이션하여 예상대로 동작하는지 검증합니다.
  2. 회귀 방지: 새로운 변경 사항이나 기능을 추가할 때 기존 코드에 오류가 발생하는 것을 '회귀'라고 합니다. 테스트는 이러한 회귀를 조기에 포착하여 새로운 기능이 기존 기능에 부정적인 영향을 미치지 않도록 방지합니다.
  3. 문서화: 테스트 코드는 구성 요소가 어떻게 작동해야 하는지에 대한 살아있는 문서 역할을 합니다. 새로운 개발자가 프로젝트에 참여할 때, 테스트 코드를 통해 해당 구성 요소의 의도된 동작과 사용법을 빠르게 이해할 수 있습니다.

🛠️ React 테스팅 도구: Jest와 React Testing Library (RTL)

React 생태계에는 다양한 테스팅 도구가 있지만, JestReact Testing Library는 가장 널리 사용되고 강력한 조합입니다.

도구 설명 역할
Jest Facebook에서 개발한 JavaScript 테스팅 프레임워크. 테스트 실행 환경, 어설션 라이브러리, 모의(mocking) 기능 제공. 올인원 솔루션.
React Testing Library (RTL) 구현 세부 사항보다는 '사용자가 구성 요소와 상호 작용하는 방식' 에 중점을 둔 라이브러리. 실제 사용자의 관점에서 DOM에 렌더링된 요소를 쿼리하고 이벤트를 시뮬레이션하여 테스트. 견고하고 리팩토링에 강한 테스트 작성에 유용.

🚀 Jest 및 RTL 시작하기

React 애플리케이션에 Jest와 RTL을 설정하는 것은 매우 간단합니다.

1. 설치

프로젝트 터미널에서 다음 명령어를 실행하여 필요한 패키지를 설치합니다.

npm install --save-dev jest @testing-library/react @testing-library/jest-dom
  • jest: 테스팅 프레임워크 자체입니다.
  • @testing-library/react: React 구성 요소 테스트를 위한 RTL 패키지입니다.
  • @testing-library/jest-dom: Jest에 DOM 관련 매처(matcher) 를 추가하여 DOM 요소를 더 쉽게 테스트할 수 있도록 합니다.

2. 구성

대부분의 Create React App 설정은 Jest와 함께 사전 구성되어 제공되므로, 별도의 복잡한 설정 없이 바로 사용할 수 있습니다. 필요에 따라 package.json 파일에 추가 구성을 추가할 수 있습니다.


✍️ 첫 번째 테스트 작성하기

간단한 Greeting.js 구성 요소를 예시로 테스트 코드를 작성해 보겠습니다.

// Greeting.js
import React from 'react';

const Greeting = ({ name }) => {
  return <h1>Hello, {name}!</h1>;
};

export default Greeting;

이제 Jest와 RTL을 사용하여 이 구성 요소에 대한 테스트를 작성해 보겠습니다. 일반적으로 테스트 파일은 테스트할 구성 요소와 같은 디렉토리에 .test.js 또는 .spec.js 접미사를 붙여 생성합니다.

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

test('renders greeting message', () => {
  // 1. Greeting 구성 요소를 가상 DOM에 렌더링합니다.
  render(<Greeting name="John" />);

  // 2. 렌더링된 출력에서 "Hello"를 포함하는 요소를 쿼리합니다 (대소문자 무시).
  const greetingElement = screen.getByText(/hello/i);

  // 3. 쿼리된 요소가 문서에 있는지 확인합니다.
  expect(greetingElement).toBeInTheDocument();
});

테스트 코드 분석

  • render(<Greeting name="John" />): RTL은 실제 브라우저 환경과 유사하게 구성 요소를 렌더링하여 사용자 관점에서 테스트할 수 있도록 합니다.
  • screen.getByText(/hello/i): screen 객체를 사용하여 렌더링된 DOM을 쿼리합니다. RTL은 사용자가 접근할 수 있는 방식으로 요소를 쿼리하는 것을 권장합니다 (getByRole, getByLabelText, getByText 등).
  • expect(greetingElement).toBeInTheDocument(): @testing-library/jest-dom에서 제공하는 매처로, 쿼리된 요소가 현재 문서에 존재하는지 확인합니다.

💡 구성 요소 테스팅의 일반적인 관행

단순한 렌더링 테스트 외에도, React 구성 요소를 테스트할 때 유용한 몇 가지 일반적인 관행이 있습니다.

1. Props 상호 작용 테스트

다양한 props가 구성 요소의 렌더링 또는 동작에 어떤 영향을 미치는지 확인하는 테스트입니다.

test('renders different names', () => {
  const { rerender } = render(<Greeting name="Alice" />);
  expect(screen.getByText(/alice/i)).toBeInTheDocument();

  // rerender 함수를 사용하여 props를 변경하고 다시 렌더링할 수 있습니다.
  rerender(<Greeting name="Bob" />);
  expect(screen.getByText(/bob/i)).toBeInTheDocument();
});

2. 사용자 이벤트 시뮬레이션

RTL은 클릭, 양식 제출 등 사용자가 구성 요소와 상호 작용하는 이벤트를 시뮬레이션할 수 있는 fireEvent 함수를 제공합니다.

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

// Example Button Component
const Button = ({ onClick }) => (
  <button onClick={onClick}>Click Me</button>
);

// Test Case for Button Click Event
test('calls onClick prop when clicked', () => {
  // jest.fn()은 mock 함수를 생성하여 함수 호출 여부, 횟수 등을 추적합니다.
  const handleClick = jest.fn();

  render(<Button onClick={handleClick} />);

  // 'Click Me' 텍스트를 가진 버튼을 찾고 클릭 이벤트를 발생시킵니다.
  fireEvent.click(screen.getByText(/click me/i));

  // handleClick 함수가 정확히 한 번 호출되었는지 확인합니다.
  expect(handleClick).toHaveBeenCalledTimes(1);
});

3. 스냅샷 테스팅 (Jest-Specific)

스냅샷 테스트는 UI가 시간이 지남에 따라 예기치 않게 변경되지 않도록 하는 데 도움이 됩니다. 이 테스트는 구성 요소의 렌더링된 출력을 파일로 저장하고, 이후 테스트 실행 시 이 스냅샷과 현재 출력을 비교합니다.

import renderer from 'react-test-renderer';
import Greeting from './Greeting';

test('matches snapshot', () => {
  // React Test Renderer를 사용하여 구성 요소를 JSON 트리로 렌더링합니다.
  const tree = renderer
    .create(<Greeting name="Snapshot User" />)
    .toJSON();

  // tree 객체를 저장된 스냅샷 파일과 비교합니다.
  expect(tree).toMatchSnapshot(); 
});

스냅샷은 대규모 UI 변경이 없을 때 회귀 테스트로 매우 유용하지만, 테스트가 너무 쉽게 통과하거나 실패할 수 있으므로, 사용자 관점의 상호 작용 테스트와 병행하여 사용하는 것이 가장 좋습니다.

728x90