누구나 로고
オンライン講座企業・団体研修ライブラリトレンドインサイト無料コースお知らせお問い合わせ採用サイト
모든 강의를 무료로 볼 수 있어요. 회원가입 없이도 학습 가능합니다.
로그인무료 회원가입
강좌리액트 입문 — 컴포넌트로 만드는 웹26강

26강 / 전체 28강

에러 처리와 경계

5분 읽기 조회 0

React Testing Library와 Vitest로 컴포넌트를 테스트하는 방법, 좋은 테스트의 기준, 사용자 관점 테스트 철학을 실전 코드로 배웁니다.

🧪 왜 컴포넌트 테스트가 필요한가

"테스트 없이도 앱이 잘 돌아가는데 왜 시간 들여 테스트를 작성해야 하나?" 이 질문의 답은 리팩토링 자신감에 있습니다. 코드를 변경할 때마다 손으로 모든 기능을 확인하는 건 비효율적이고 빠뜨리기 쉽습니다. 테스트가 있으면 실수를 즉시 발견할 수 있습니다.

리액트 컴포넌트 테스트의 핵심 철학: "사용자처럼 테스트하라." DOM 내부 구현보다 사용자가 보고 클릭하는 것을 테스트합니다.

📦 설치 및 설정

npm install -D vitest @testing-library/react @testing-library/jest-dom @testing-library/user-event jsdom
// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

export default defineConfig({
  plugins: [react()],
  test: {
    globals: true,
    environment: 'jsdom',
    setupFiles: './src/test/setup.ts',
  },
})
// src/test/setup.ts
import '@testing-library/jest-dom'

✅ 첫 번째 테스트 — Counter 컴포넌트

// Counter.tsx
import { useState } from 'react'

function Counter({ initialCount = 0 }: { initialCount?: number }) {
  const [count, setCount] = useState(initialCount)
  return (
    <div>
      <p data-testid="count">현재 값: {count}</p>
      <button onClick={() => setCount((c) => c + 1)}>증가</button>
      <button onClick={() => setCount((c) => c - 1)}>감소</button>
      <button onClick={() => setCount(0)}>초기화</button>
    </div>
  )
}
// Counter.test.tsx
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import Counter from './Counter'

describe('Counter', () => {
  it('초기값이 0으로 렌더링된다', () => {
    render(<Counter />)
    expect(screen.getByTestId('count')).toHaveTextContent('현재 값: 0')
  })

  it('initialCount prop을 받아 그 값으로 시작한다', () => {
    render(<Counter initialCount={10} />)
    expect(screen.getByTestId('count')).toHaveTextContent('현재 값: 10')
  })

  it('증가 버튼 클릭 시 값이 1 올라간다', async () => {
    const user = userEvent.setup()
    render(<Counter />)
    await user.click(screen.getByRole('button', { name: '증가' }))
    expect(screen.getByTestId('count')).toHaveTextContent('현재 값: 1')
  })

  it('초기화 버튼 클릭 시 0으로 돌아간다', async () => {
    const user = userEvent.setup()
    render(<Counter initialCount={5} />)
    await user.click(screen.getByRole('button', { name: '증가' }))
    await user.click(screen.getByRole('button', { name: '초기화' }))
    expect(screen.getByTestId('count')).toHaveTextContent('현재 값: 0')
  })
})

🔎 쿼리 우선순위 — 어떻게 요소를 찾을까

React Testing Library는 사용자가 요소를 찾는 방식을 모방합니다:

쿼리예시사용 시점
getByRolegetByRole('button', {name: '로그인'})접근성 역할로 — 가장 권장
getByLabelTextgetByLabelText('이메일')폼 입력 — label과 연결
getByPlaceholderTextgetByPlaceholderText('검색...')placeholder로 찾기
getByTextgetByText('제출')텍스트 내용으로
getByTestIdgetByTestId('count')최후 수단 — 구현에 의존적

📝 폼 테스트

it('로그인 폼을 올바르게 제출한다', async () => {
  const handleSubmit = vi.fn()
  const user = userEvent.setup()
  render(<LoginForm onSubmit={handleSubmit} />)

  await user.type(screen.getByLabelText('이메일'), 'test@example.com')
  await user.type(screen.getByLabelText('비밀번호'), 'password123')
  await user.click(screen.getByRole('button', { name: '로그인' }))

  expect(handleSubmit).toHaveBeenCalledWith({
    email: 'test@example.com',
    password: 'password123',
  })
})

✅ 정리

  • React Testing Library + Vitest: 컴포넌트를 사용자 관점에서 테스트합니다.
  • 요소 찾기: getByRole > getByLabelText > getByText > getByTestId 순서로 우선합니다.
  • userEvent: 실제 사용자 동작(타이핑, 클릭)을 시뮬레이션합니다.
  • 구현이 아닌 동작(behavior)을 테스트하세요 — 내부 state가 아닌 화면에 무엇이 보이는지.

관련 주제

  • 에러 바운더리 개념
  • 폼 검증
  • 사용자 친화적 에러 표시
  • 개발·프로그래밍
  • 개발·프로그래밍 강의
  • 리액트 입문 — 컴포넌트로 만드는 웹
  • 무료강의
  • 무료 온라인 강의
  • NUGUNA
  • 누구나
이전 25강성능 기초 — memo다음 27강 환경변수와 빌드

댓글

0/1000

불러오는 중...

목차

  1. 1.리액트란 무엇인가
  2. 2.개발 환경 만들기 — Vite
  3. 3.JSX 기초
  4. 4.컴포넌트 만들기
  5. 5.Props로 데이터 전달
  6. 6.State와 useState
  7. 7.이벤트 처리
  8. 8.조건부 렌더링
  9. 9.리스트 렌더링과 key
  10. 10.폼 다루기
  11. 11.useEffect 기초
  12. 12.데이터 가져오기(fetch)
  13. 13.상태 끌어올리기
  14. 14.컴포넌트 합성과 재사용
  15. 15.useRef와 DOM 접근
  16. 16.커스텀 훅 만들기
  17. 17.Context API
  18. 18.useReducer
  19. 19.리스트 앱 실전 ①
  20. 20.리스트 앱 실전 ②
  21. 21.라우팅 — React Router
  22. 22.스타일링 방법들
  23. 23.리스트·상세 페이지 연결
  24. 24.비동기 상태 관리 패턴
  25. 25.성능 기초 — memo
  26. 26.에러 처리와 경계
  27. 27.환경변수와 빌드
  28. 28.배포하기

カスタマーサポート

  • お知らせ
  • よくある質問
  • お問い合わせ
  • コミュニティ

ご利用案内

  • 利用規約
  • プライバシーポリシー
  • 返金ポリシー

サービス

  • 会社紹介
  • 新規登録
  • 新着コース
  • 無料コース
누구나 로고
利用規約プライバシーポリシー返金ポリシー

상호명: NUGUNA  |  대표자: 정우진  |  사업자등록번호: 392-32-01817  |  통신판매업신고: 제 2026-서울양천-0564 호

주소: 서울특별시 양천구 목동서로 100  |  이메일: nugunapass@gmail.com  |  전화: 010-6395-3043

© 2026 NUGUNA. All rights reserved.

KB예금주인증관리자
ホームコースライブラリお問い合わせMY