๐Ÿš€ React + Vite ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ ๊ตฌ์ถ• ๋ฐ ์‹คํ–‰ ๊ฐ€์ด๋“œ

Ma_Seokjaeยท2025๋…„ 2์›” 18์ผ
0
post-thumbnail

Vite ๊ธฐ๋ฐ˜์˜ React ํ”„๋กœ์ ํŠธ์—์„œ Vitest + React Testing Library๋ฅผ ํ™œ์šฉํ•œ ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์„ ๊ตฌ์ถ•ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ •๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
์‹ค์ œ ์„ค์ • ๋ฐ ๋ฌธ์ œ ํ•ด๊ฒฐ ๊ณผ์ •๊นŒ์ง€ ๋ชจ๋‘ ํฌํ•จํ•˜์—ฌ ์ž์„ธํ•˜๊ฒŒ ์•Œ์•„๊ฐ€๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.


๐Ÿ›  1๏ธโƒฃ ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ ๋ฐ ๊ธฐ๋ณธ ์„ค์ •

์šฐ์„ , Vite๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ƒˆ๋กœ์šด React ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

# Vite + React ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ
npm create vite@latest my-project --template react
cd my-project

# ํŒจํ‚ค์ง€ ์„ค์น˜
npm install

๐Ÿ“Œ Vite๋Š” ๋น ๋ฅธ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์„ ์ œ๊ณตํ•˜๋Š” ๋นŒ๋“œ ํˆด์ด๋ฉฐ, --template react ์˜ต์…˜์„ ํ†ตํ•ด React ํ™˜๊ฒฝ์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.


๐Ÿ“Œ 2๏ธโƒฃ ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ ๊ตฌ์ถ• (Vitest + React Testing Library)

Vite ํ”„๋กœ์ ํŠธ์—์„œ ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ•˜๋ ค๋ฉด Vitest์™€ React Testing Library๋ฅผ ์„ค์น˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

npm install -D vitest @testing-library/react @testing-library/jest-dom jsdom

โœ… ์„ค์น˜ํ•˜๋Š” ํŒจํ‚ค์ง€ ์„ค๋ช…

  • vitest โ†’ Jest์™€ ์œ ์‚ฌํ•œ ํ…Œ์ŠคํŠธ ํ”„๋ ˆ์ž„์›Œํฌ (Vite์™€ ์ตœ์ ํ™”๋จ)
  • @testing-library/react โ†’ React ์ปดํฌ๋„ŒํŠธ ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
  • @testing-library/jest-dom โ†’ DOM ๊ด€๋ จ ์ถ”๊ฐ€ ๋งค์ฒ˜(matcher) ์ œ๊ณต (toBeInTheDocument() ๋“ฑ)
  • jsdom โ†’ ๋ธŒ๋ผ์šฐ์ € ํ™˜๊ฒฝ์„ ๊ฐ€์ƒ์œผ๋กœ ์ œ๊ณตํ•˜์—ฌ React ์ปดํฌ๋„ŒํŠธ๋ฅผ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•จ

๐Ÿ“Œ 3๏ธโƒฃ package.json ์Šคํฌ๋ฆฝํŠธ ์ถ”๊ฐ€

package.json์— ํ…Œ์ŠคํŠธ ์‹คํ–‰ ๋ช…๋ น์–ด๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ“ package.json

"scripts": {
  "dev": "vite",
  "build": "vite build",
  "preview": "vite preview",
  "test": "vitest",
  "test:watch": "vitest --watch"
}

โœ… ๊ฐ ๋ช…๋ น์–ด ์„ค๋ช…

  • "test": "vitest" โ†’ ํ…Œ์ŠคํŠธ ์‹คํ–‰
  • "test:watch": "vitest --watch" โ†’ ํŒŒ์ผ์ด ๋ณ€๊ฒฝ๋  ๋•Œ ์ž๋™์œผ๋กœ ํ…Œ์ŠคํŠธ ์‹คํ–‰

๐Ÿ“Œ 4๏ธโƒฃ vite.config.js ์„ค์ •

Vite์—์„œ Vitest๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์„ค์ • ํŒŒ์ผ์„ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ“ vite.config.js

import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import { configDefaults } from "vitest/config";

export default defineConfig({
  plugins: [react()],
  test: {
    globals: true,
    environment: "jsdom",
    setupFiles: "./vitest.setup.js",
    exclude: [...configDefaults.exclude, "e2e/*"],
  },
});

โœ… ์„ค์ • ์„ค๋ช…

  • environment: "jsdom" โ†’ ๋ธŒ๋ผ์šฐ์ € ํ™˜๊ฒฝ์„ ์‹œ๋ฎฌ๋ ˆ์ด์…˜
  • setupFiles: "./vitest.setup.js" โ†’ ํ…Œ์ŠคํŠธ ์‹คํ–‰ ์ „์— ํ•„์š”ํ•œ ์„ค์ •์„ ๋กœ๋“œ
  • exclude: [...configDefaults.exclude, "e2e/*"] โ†’ e2e ํ…Œ์ŠคํŠธ ๋””๋ ‰ํ† ๋ฆฌ ์ œ์™ธ

๐Ÿ“Œ 5๏ธโƒฃ vitest.setup.js ์ถ”๊ฐ€

ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์„ ์„ค์ •ํ•˜๋Š” vitest.setup.js ํŒŒ์ผ์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ“ vitest.setup.js

import "@testing-library/jest-dom";

์ด ํŒŒ์ผ์„ ํ†ตํ•ด @testing-library/jest-dom์˜ ํ™•์žฅ ๋งค์ฒ˜๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


๐Ÿ“Œ 6๏ธโƒฃ ํ…Œ์ŠคํŠธ ํŒŒ์ผ ์ž‘์„ฑ

์ปดํฌ๋„ŒํŠธ๋ณ„๋กœ tests/ ๋””๋ ‰ํ† ๋ฆฌ๋ฅผ ์ƒ์„ฑํ•˜์—ฌ ํ…Œ์ŠคํŠธ ํŒŒ์ผ์„ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ“‚ ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ

src/
โ”‚โ”€โ”€ components/
โ”‚   โ”œโ”€โ”€ Header.jsx
โ”‚   โ”œโ”€โ”€ Footer.jsx
โ”‚   โ”œโ”€โ”€ tests/
โ”‚   โ”‚   โ”œโ”€โ”€ Header.test.jsx
โ”‚   โ”‚   โ”œโ”€โ”€ Footer.test.jsx
โ”‚โ”€โ”€ vite.config.js
โ”‚โ”€โ”€ vitest.setup.js
โ”‚โ”€โ”€ package.json

๐Ÿ“ src/components/tests/Footer.test.jsx

import { render, screen } from "@testing-library/react";
import Footer from "../Footer";

test("Footer๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ๋ Œ๋”๋ง๋˜์–ด์•ผ ํ•œ๋‹ค", () => {
  render(<Footer />);

  expect(screen.getByText("์”จ์ œ์ด์˜ฌ๋ฆฌ๋ธŒ์˜ ์ฃผ์‹ํšŒ์‚ฌ")).toBeInTheDocument();
  expect(screen.getByText("์ด์šฉ์•ฝ๊ด€ | ๋ฒ•์ ๊ณ ์ง€")).toBeInTheDocument();
  expect(screen.getByText(/ํ•˜๋‚˜์€ํ–‰ ๊ตฌ๋งค์•ˆ์ „ ์„œ๋น„์Šค/)).toBeInTheDocument();
});

test("ํšŒ์‚ฌ์†Œ๊ฐœ ๋งํฌ๊ฐ€ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์กด์žฌํ•ด์•ผ ํ•œ๋‹ค", () => {
  render(<Footer />);

  const companyLink = screen.getByText("ํšŒ์‚ฌ์†Œ๊ฐœ");
  expect(companyLink.closest("a")).toHaveAttribute("href", "https://corp.oliveyoung.com/ko");
});

test("๊ณ ๊ฐ์„ผํ„ฐ ๋ฐ ๋ฒ•์  ์ •๋ณด ๋งํฌ๋“ค์ด ์กด์žฌํ•ด์•ผ ํ•œ๋‹ค", () => {
  render(<Footer />);

  expect(screen.getByText("๊ฐœ์ธ์ •๋ณด์ฒ˜๋ฆฌ๋ฐฉ์นจ")).toBeInTheDocument();
  expect(screen.getByText("์ฒญ์†Œ๋…„๋ณดํ˜ธ๋ฐฉ์นจ")).toBeInTheDocument();
  expect(screen.getByText("์ด๋ฉ”์ผ๋ฌด๋‹จ์ˆ˜์ง‘๊ฑฐ๋ถ€")).toBeInTheDocument();
});

โœ… Header ์ปดํฌ๋„ŒํŠธ ํ…Œ์ŠคํŠธ

๐Ÿ“ src/components/tests/Header.test.jsx

import { render, screen, fireEvent } from "@testing-library/react";
import { MemoryRouter } from "react-router-dom";
import Header from "../Header";

test("Header๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ๋ Œ๋”๋ง๋˜์–ด์•ผ ํ•œ๋‹ค", () => {
  render(
    <MemoryRouter>
      <Header />
    </MemoryRouter>
  );

  expect(screen.getByAltText("์˜ฌ๋ฆฌ๋ธŒ์˜ ๋กœ๊ณ ")).toBeInTheDocument();
  expect(screen.getByPlaceholderText("์ƒํ’ˆ, ๋ธŒ๋žœ๋“œ, ์„ฑ๋ถ„ ๊ฒ€์ƒ‰")).toBeInTheDocument();
  expect(screen.getByText("ํšŒ์›๊ฐ€์ž…")).toBeInTheDocument();
});

test("๋กœ๊ณ  ํด๋ฆญ ์‹œ ํ™ˆ(/)์œผ๋กœ ์ด๋™ํ•ด์•ผ ํ•œ๋‹ค", () => {
  const { container } = render(
    <MemoryRouter>
      <Header />
    </MemoryRouter>
  );

  const logo = screen.getByAltText("์˜ฌ๋ฆฌ๋ธŒ์˜ ๋กœ๊ณ ");
  fireEvent.click(logo);

  expect(container.innerHTML).toContain("์˜ฌ๋ฆฌ๋ธŒ์˜");
});

๐Ÿ“Œ 7๏ธโƒฃ ํ…Œ์ŠคํŠธ ์‹คํ–‰ ๋ฐ ๋ฌธ์ œ ํ•ด๊ฒฐ

ํ…Œ์ŠคํŠธ ์‹คํ–‰

npm run test

โœ”๏ธ ์ •์ƒ์ ์œผ๋กœ ์„ค์ •๋˜์—ˆ๋‹ค๋ฉด ์•„๋ž˜์ฒ˜๋Ÿผ ์ถœ๋ ฅ๋ฉ๋‹ˆ๋‹ค.

โœ“ src/components/tests/Footer.test.jsx (3 tests)
โœ“ src/components/tests/Header.test.jsx (2 tests)

Test Files  2 passed (2)
      Tests  5 passed (5)


๐Ÿ” ๋ฌธ์ œ ํ•ด๊ฒฐ ๊ณผ์ •

  1. Vitest๊ฐ€ ์‹คํ–‰๋˜์ง€ ์•Š์Œ (vitest: command not found)

    npm install -D vitest
  2. @testing-library/react๋ฅผ ์ฐพ์ง€ ๋ชปํ•จ

    npm install -D @testing-library/react
  3. @testing-library/jest-dom์ด ํ•„์š”ํ•จ

    npm install -D @testing-library/jest-dom
  4. ํŒจํ‚ค์ง€ ๋ฌธ์ œ ํ•ด๊ฒฐ (node_modules ์‚ญ์ œ ํ›„ ์žฌ์„ค์น˜)

    rm -rf node_modules package-lock.json
    npm install

โœ… ์ตœ์ข… ์š”์•ฝ

ํ•„์š”ํ•œ ๊ตฌ์„ฑ ํŒŒ์ผ๋“ค

๋‹จ๊ณ„๋ณ„ ์š”์•ฝ

๋‹จ๊ณ„๋ช…๋ น์–ด
1. Vite ํ”„๋กœ์ ํŠธ ์ƒ์„ฑnpm create vite@latest my-project --template react
2. ํŒจํ‚ค์ง€ ์„ค์น˜npm install -D vitest @testing-library/react @testing-library/jest-dom jsdom
3. package.json ์„ค์ •"test": "vitest" ์ถ”๊ฐ€
4. vite.config.js ์„ค์ •environment: "jsdom" ๋“ฑ ์ถ”๊ฐ€
5. vitest.setup.js ์„ค์ •import "@testing-library/jest-dom" ์ถ”๊ฐ€
6. ํ…Œ์ŠคํŠธ ์ž‘์„ฑsrc/components/tests/*.test.jsx ์ƒ์„ฑ
7. ํ…Œ์ŠคํŠธ ์‹คํ–‰npm run test

๐ŸŽฏ ๊ฒฐ๋ก 

์ด์ œ React + Vite ํ”„๋กœ์ ํŠธ์—์„œ Vitest๋ฅผ ํ™œ์šฉํ•œ ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์„ ์™„๋ฒฝํ•˜๊ฒŒ ๊ตฌ์ถ•ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค! ๐Ÿš€

์•ž์œผ๋กœ ๋‹ค๋ฅธ ํ”„๋กœ์ ํŠธ์—์„œ๋„ ์ด ๊ฐ€์ด๋“œ๋ฅผ ์ฐธ๊ณ ํ•˜์—ฌ ๋น ๋ฅด๊ฒŒ ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์„ ๊ตฌ์ถ•ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ถ๊ธˆํ•œ ์ ์ด ์žˆ์œผ๋ฉด ์–ธ์ œ๋“ ์ง€ ์งˆ๋ฌธํ•˜์„ธ์š”! ๐Ÿ˜ƒ

profile
Why not change the code?

0๊ฐœ์˜ ๋Œ“๊ธ€