OPEN HYPER STEP
← 목록으로 (stack-analysis)
STACK-ANALYSIS · 11 / 90
stack-analysis
CHAPTER 11 / 90
읽기 약 2
FUNCTION

테스트: Testing Library + Vitest


핵심 개념

컴포넌트 테스트·접근성 기반 쿼리·MSW 목 API — CRUD 컴포넌트 테스트.

본문

Vitest 설정

BASH📋 코드 (1줄)
pnpm add -D vitest @testing-library/react @testing-library/jest-dom @testing-library/user-event jsdom
TYPESCRIPT📋 코드 (16줄)
// vitest.config.ts
import { defineConfig } from 'vitest/config';
import react from '@vitejs/plugin-react';

export default defineConfig({
  plugins: [react()],
  test: {
    environment: 'jsdom',
    setupFiles: ['./test/setup.ts'],
    globals: true,
  },
});


// test/setup.ts
import '@testing-library/jest-dom/vitest';

컴포넌트 테스트 — 접근성 기반

TYPESCRIPT📋 코드 (48줄)
// SignupForm.test.tsx
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { SignupForm } from './SignupForm';

describe('SignupForm', () => {
  it('이메일·비밀번호 입력 후 가입 버튼 활성화', async () => {
    const user = userEvent.setup();
    render(<SignupForm />);

    // 접근성 기반 쿼리 — role + name
    const emailInput = screen.getByRole('textbox', { name: /이메일/i });
    const passwordInput = screen.getByLabelText(/비밀번호/i);
    const submitBtn = screen.getByRole('button', { name: /가입/i });

    await user.type(emailInput, 'alice@example.com');
    await user.type(passwordInput, 'Pass1234');

    expect(submitBtn).toBeEnabled();
  });

  it('이메일 형식 오류 시 에러 표시', async () => {
    const user = userEvent.setup();
    render(<SignupForm />);

    const emailInput = screen.getByRole('textbox', { name: /이메일/i });
    await user.type(emailInput, 'invalid');
    await user.tab();

    expect(await screen.findByText(/올바른 이메일/i)).toBeInTheDocument();
  });

  it('성공 시 onSuccess 콜백', async () => {
    const onSuccess = vi.fn();
    const user = userEvent.setup();

    render(<SignupForm onSuccess={onSuccess} />);

    await user.type(screen.getByLabelText(/이메일/i), 'alice@example.com');
    await user.type(screen.getByLabelText(/비밀번호/i), 'Pass1234');
    await user.click(screen.getByRole('button', { name: /가입/i }));

    expect(onSuccess).toHaveBeenCalledWith({
      email: 'alice@example.com',
      password: 'Pass1234',
    });
  });
});

MSW (Mock Service Worker)

TYPESCRIPT📋 코드 (37줄)
// pnpm add -D msw

// mocks/handlers.ts
import { http, HttpResponse } from 'msw';

export const handlers = [
  http.get('/api/users', () => {
    return HttpResponse.json([
      { id: 1, name: 'Alice' },
      { id: 2, name: 'Bob' },
    ]);
  }),

  http.post('/api/users', async ({ request }) => {
    const body = await request.json();
    return HttpResponse.json({ id: 3, ...body });
  }),

  http.get('/api/users/:id', ({ params }) => {
    return HttpResponse.json({ id: Number(params.id), name: 'Test' });
  }),
];


// mocks/node.ts
import { setupServer } from 'msw/node';
import { handlers } from './handlers';
export const server = setupServer(...handlers);


// test/setup.ts
import { server } from '../mocks/node';
import { beforeAll, afterEach, afterAll } from 'vitest';

beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());

비동기 테스트

TYPESCRIPT📋 코드 (17줄)
import { render, screen, waitFor } from '@testing-library/react';

it('사용자 목록을 가져와서 표시', async () => {
  render(<UserList />);

  // 로딩 상태
  expect(screen.getByText(/로딩/i)).toBeInTheDocument();

  // 데이터 로드 후
  expect(await screen.findByText('Alice')).toBeInTheDocument();
  expect(screen.getByText('Bob')).toBeInTheDocument();

  // 또는 명시적 waitFor
  await waitFor(() => {
    expect(screen.getByText('Alice')).toBeInTheDocument();
  });
});

쿼리 우선순위 (접근성 우선)

📋 코드 (10줄)
1. getByRole (button, textbox, heading 등)
2. getByLabelText (form 입력)
3. getByPlaceholderText
4. getByText
5. getByDisplayValue (form 값)
6. getByAltText (이미지)
7. getByTitle
8. getByTestId (마지막 수단)

→ 사용자가 보는 방식대로 테스트 = 리팩터링 안전

다음 챕터

CH.12 "Storybook" — 컴포넌트 문서화.


AI 프롬프트
🤖 AI에게 잘 물어보는 법 — 모델·전략별 프롬프트
Claude

무료: Sonnet 4.6 / Pro $20/mo: Opus 4.6

내 코드의 컴포넌트 테스트 부분을 분석해서
실전 분석 + 개선 우선순위를 알려줘.
ChatGPT

무료: GPT-5.5 / Plus $20/mo: GPT-5.5 Pro

컴포넌트 테스트 관련 인기 라이브러리/패턴 5개를
비교 분석해서 패턴 추출를 알려줘.
Gemini

무료: 2.5 Flash / Pro $19.99/mo: 3.1 Pro

내 프로젝트 전체에서 컴포넌트 테스트
최적화 가능 위치를 보고해줘.
Grok

무료: Grok 4.1 / SuperGrok $30/mo

2026년 한국 프론트엔드 시장의
컴포넌트 테스트 트렌드를 솔직히 알려줘.

⭐ 이것만 기억하세요
테스트: Testing Library + Vitest 이 3가지만 확실히 잡으세요
1.Testing Library는 접근성 기반 쿼리 — 사용자 관점 테스트로 리팩터링 안전
2.MSW로 fetch/axios 가로채기 + 환경 무관 (Node + Browser 모두)
3.다음 챕터 CH.12에서 Storybook — 디자인 시스템 문서화


공유하기
진행도 11 / 90