ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ ์ž‘์„ฑ - NavigationBar

date
2026-01-29
order
8

NavigationBar ์ปดํฌ๋„ŒํŠธ

NavigationBar์ปดํฌ๋„ŒํŠธ์˜ ๊ธฐ๋Šฅ๋“ค์„ ์ •๋ฆฌํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค

  • ๋กœ๊ณ  ํด๋ฆญ์‹œ ๋ฉ”์ธ ํŽ˜์ด์ง€๋กœ ์ด๋™
  • ๋น„๋กœ๊ทธ์ธ์‹œ
    • ๋กœ๊ทธ์ธ ๋ฒ„ํŠผ ๋…ธ์ถœ, ํด๋ฆญ์‹œ ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€๋กœ ์ด๋™
  • ๋กœ๊ทธ์ธ์‹œ
    • ์žฅ๋ฐ”๊ตฌ๋‹ˆ ๋ฒ„ํŠผ
      • ์žฅ๋ฐ”๊ตฌ๋‹ˆ์— ๋‹ด๊ธด ์ƒํ’ˆ์ˆ˜๊ฐ€ ๋‚˜ํƒ€๋‚จ
      • ํด๋ฆญ์‹œ ์žฅ๋ฐ”๊ตฌ๋‹ˆ ํŽ˜์ด์ง€๋กœ ์ด๋™
    • ๋กœ๊ทธ์•„์›ƒ ๋ฒ„ํŠผ
      • ํด๋ฆญ์‹œ "๋กœ๊ทธ์•„์›ƒ ํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?" ๋ชจ๋‹ฌ ๋…ธ์ถœ
      • ๋ชจ๋‹ฌ์˜ ํ™•์ธ ๋ฒ„ํŠผ ํด๋ฆญ์‹œ ๋กœ๊ทธ์•„์›ƒ

์ด์ œ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ๋ฅผ ์–ด๋–ป๊ฒŒ ์ž‘์„ฑํ•˜๋ฉด ์ข‹์„๊นŒ? ์˜ˆ์ œ์—์„œ๋Š” ์ด๋ ‡๊ฒŒ ๊ตฌ์„ฑํ–ˆ๋‹ค.

  • (it)๋กœ๊ณ  ํด๋ฆญ์‹œ "/" ๊ฒฝ๋กœ๋กœ ์ด๋™(navigate๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค).
  • (describe)๋กœ๊ทธ์ธ์ด ๋œ ๊ฒฝ์šฐ
    • (it)์žฅ๋ฐ”๊ตฌ๋‹ˆ ๋ฒ„ํŠผ, ์žฅ๋ฐ”๊ตฌ๋‹ˆ์— ๋‹ด๊ธด ์ƒํ’ˆ ์ˆ˜, ๋กœ๊ทธ์•„์›ƒ ๋ฒ„ํŠผ(์‚ฌ์šฉ์ž ์ด๋ฆ„)์ด ๋ Œ๋”๋ง ๋œ๋‹ค
    • (it)์žฅ๋ฐ”๊ตฌ๋‹ˆ ๋ฒ„ํŠผ ํด๋ฆญ์‹œ "/cart" ๊ฒฝ๋กœ๋กœ ์ด๋™ํ•œ๋‹ค(navigate๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค).
    • (describe)๋กœ๊ทธ์•„์›ƒ ๋ฒ„ํŠผ
      • (it)๋ชจ๋‹ฌ์ด "๋กœ๊ทธ์•„์›ƒ ํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?" ๋ฌธ๊ตฌ์™€ ํ•จ๊ป˜ ๋ Œ๋”๋ง ๋œ๋‹ค.
      • (it)ํ™•์ธ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ๋กœ๊ทธ์•„์›ƒ์ด ๋˜๋ฉฐ ๋ชจ๋‹ฌ์ด ์‚ฌ๋ผ์ง„๋‹ค
      • (it)์ทจ์†Œ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ๋ชจ๋‹ฌ์ด ์‚ฌ๋ผ์ง„๋‹ค.
  • (describe)๋น„๋กœ๊ทธ์ธ ๊ฒฝ์šฐ
    • (it)๋กœ๊ทธ์ธ ๋ฒ„ํŠผ ๋ Œ๋”๋ง, ํด๋ฆญ์‹œ ํ˜„์žฌ "/login"๊ฒฝ๋กœ๋กœ pathname๊ณผ ํ•จ๊ป˜ navigate ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœ

msw์˜ useํ•จ์ˆ˜

๋กœ๊ทธ์ธ์ด ๋œ ๊ฒฝ์šฐ ํ…Œ์ŠคํŠธ๋ฅผ ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋กœ๊ทธ์ธ์ด ๋œ ์ƒํƒœ๋ฅผ ๋ชจํ‚นํ•ด์•ผ ํ•œ๋‹ค. ๊ทธ๋Ÿฌ๋ ค๋ฉด ๋กœ๊ทธ์ธ์ด ๋œ ๊ฒฝ์šฐ์˜ describe ์•ˆ์— ์…‹์—… beforeEach๋กœ ํ•ด์ค˜์•ผํ•œ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ๋ฌธ์ œ๋Š” ์ด๋Ÿฌํ•œ ์‚ฌ์šฉ์ž ์ •๋ณด๋Š” ์Šคํ† ์–ด์—์„œ ๊ฐ€์ ธ์˜ค๋Š”๊ฒƒ์ด ์•„๋‹ˆ๋ผ api ํ˜ธ์ถœ์„ ํ†ตํ•ด ๊ฐ€์ ธ์˜จ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

NavigationBar์ปดํฌ๋„ŒํŠธ์—์„œ๋Š” useProfileํ›…(๋‚ด๋ถ€์ ์œผ๋กœ ๋ฆฌ์•กํŠธ ์ฟผ๋ฆฌ๋ฅผ ์‚ฌ์šฉ)์„ ์‚ฌ์šฉํ•ด ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„์™€ setUserData๋กœ ์Šคํ† ์–ด์— ์ €์žฅํ•œ๋‹ค.

//NavigationBar.jsx
const { data, remove } = useProfile({
    config: {
      onSuccess: profile => {
        setUserData(profile);
        initCart(profile.id);
      },
      enabled: !!isLogin,
    },
  });

๊ทธ๋ž˜์„œ ์ด api๋ฅผ ๋ชจํ‚นํ•ด์•ผํ•˜๋Š”๋ฐ, ์ €๋ฒˆ ์‹œ๊ฐ„๋ถ€ํ„ฐ ์šฐ๋ฆฌ๋Š” api ๋ชจํ‚น์„ msw๋กœ ํ•ด์™”๋‹ค. ๊ทธ๋ž˜์„œ msw ์ฝ”๋“œ(handlers.js) ๋กœ ๊ฐ€๋ด์•ผ ํ•˜๋Š”๋ฐ, useProfileํ›…์ด ์š”์ฒญ์„ ๋ณด๋‚ด๋Š” ์ฃผ์†Œ์— ํ•ด๋‹นํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์ฐพ์•„๋ณด๋ฉด ์ด๋ ‡๋‹ค.

  rest.get(`${API_DOMAIN}${apiRoutes.profile}`, (req, res, ctx) => {
    return res(ctx.status(200), ctx.json(null));
  }),

๋กœ๊ทธ์ธ์ด ๋˜์–ด์žˆ์ง€ ์•Š๋Š”๊ฒŒ ๊ธฐ๋ณธ๊ฐ’์˜ ์ƒํƒœ๋ผ๊ณ  ๋ณด๋Š”๊ฒŒ ์ ์ ˆํ•˜๋‹ˆ๊นŒ ์œ„์˜ ์ฝ”๋“œ๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜๋Š” ์—†๋‹ค. ๊ทธ๋ž˜์„œ ์‚ฌ์šฉํ•˜๋Š”๊ฒŒ msw์˜ use ํ•จ์ˆ˜๋‹ค.

server ์ธ์Šคํ„ด์Šค

ํ…Œ์ŠคํŠธ ์…‹์—… ํŒŒ์ผ์— ๊ฐ€๋ณด๋ฉด server ์ธ์Šคํ„ด์Šค๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š”๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

//setupTests.js
import { setupServer } from 'msw/node';
import '@testing-library/jest-dom';

import { handlers } from '@/__mocks__/handlers';

/* msw */
export const server = setupServer(...handlers);

beforeAll(() => {
  server.listen();
});

afterEach(() => {
  server.resetHandlers();
  vi.clearAllMocks();
});

afterAll(() => {
  vi.resetAllMocks();
  server.close();
});

//...

์ด ์ธ์Šคํ„ด์Šค๋ฅผ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•ด์•ผ ๊ธฐ์กด์— ๋ชจํ‚น๋œ api์˜ ์‘๋‹ต์„ ์ •์ƒ์ ์œผ๋กœ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋‹ค.

useํ•จ์ˆ˜ ์ž‘์„ฑ

describe('๋กœ๊ทธ์ธ์ด ๋œ ๊ฒฝ์šฐ', () => {
  const userId = 10;

  beforeEach(() => {
    server.use(
      rest.get('/user', (_, res, ctx) => {
        return res(
          ctx.status(200),
          ctx.json({
            email: 'maria@mail.com',
            id: userId,
            name: 'Maria',
            password: '12345',
          }),
        );
      }),
    );
    mockUseUserStore({ isLogin: true });

useํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด handlers.js์— ์ž‘์„ฑ๋œ ๊ธฐ๋ณธ ์„ค์ •๋Œ€๋กœ๊ฐ€ ์•„๋‹ˆ๋ผ useํ•จ์ˆ˜ ๋‚ด๋ถ€์— ์ž‘์„ฑํ•œ ์‘๋‹ต์„ ๊ธฐ์ค€์œผ๋กœ ํ…Œ์ŠคํŠธ๊ฐ€ ์ง„ํ–‰๋  ์ˆ˜ ์žˆ๋‹ค. ์ด ํ™˜๊ฒฝ์—์„œ useProfileํ›…์€ ์œ„์— ์ž‘์„ฑ๋œ ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

useํ•จ์ˆ˜ ๊ฒฐ๊ณผ ์ดˆ๊ธฐํ™”

useํ•จ์ˆ˜๋กœ ๋ณ€๊ฒฝํ•œ ์„œ๋ฒ„์˜ api ์‘๋‹ต๋„ ๋‹ค๋ฅธ ๋ชจํ‚น๋“ค๊ณผ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์ดˆ๊ธฐํ™”๋ฅผ ํ•ด์ฃผ์–ด์•ผ ํ•˜๋Š”๋ฐ, ์–ด๋””์„œ ํ•ด์ฃผ๋Š”๊ฒƒ์ผ๊นŒ? ์œ„์˜ setupTests.js๋ฅผ ๋ณด๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ฝ”๋“œ์—์„œ ๋ชจ๋“  ํ…Œ์ŠคํŠธ๊ฐ€ ์™„๋ฃŒ๋œ ํ›„์— ์‹คํ–‰ํ•˜๋Š” ํ‹ฐ์–ด๋‹ค์šด์œผ๋กœ ์ž‘์„ฑ๋˜์–ด ์žˆ๋Š”๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

//setupTests.js
afterEach(() => {
  server.resetHandlers();
  vi.clearAllMocks();
});

within ํ•จ์ˆ˜

๋กœ๊ทธ์•„์›ƒ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ๋ชจ๋‹ฌ์ด ๋‚˜ํƒ€๋‚˜๋Š”์ง€์— ๋Œ€ํ•œ ํ…Œ์ŠคํŠธ์ฝ”๋“œ๋‹ค.

    it('๋ชจ๋‹ฌ์ด ๋ Œ๋”๋ง๋˜๋ฉฐ, ๋ชจ๋‹ฌ ๋‚ด์— "๋กœ๊ทธ์•„์›ƒ ํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?" ํ…์ŠคํŠธ๊ฐ€ ๋ Œ๋”๋ง๋œ๋‹ค.', () => {
      const dialog = screen.getByRole('dialog');

      expect(screen.getByRole('dialog')).toBeInTheDocument();
      expect(
        within(dialog).getByText('๋กœ๊ทธ์•„์›ƒ ํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?'),
      ).toBeInTheDocument();
    });

์—ฌ๊ธฐ ์ฝ”๋“œ๋ฅผ ๋ณด๋ฉด screen.getByText ๋Œ€์‹  within(dialog).getByText๋ฅผ ์“ฐ๊ณ ์žˆ๋‹ค. within ํ•จ์ˆ˜๋ฅผ ์“ฐ๋ฉด getBy์˜ ์กฐํšŒ ๋ฒ”์œ„๋ฅผ ํ•ด๋‹น ์š”์†Œ๋กœ ์ œํ•œํ•  ์ˆ˜ ์žˆ๋‹ค. ์—ฌ๊ธฐ์„œ๋Š” ์‚ฌ์‹ค "๋กœ๊ทธ์•„์›ƒ ํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?"๋ผ๋Š” ๋ฌธ๊ตฌ๊ฐ€ ํŽ˜์ด์ง€ ์ „์ฒด์— ํ•˜๋‚˜๋ฐ–์— ์—†์–ด์„œ ๋ฌธ์ œ๊ฐ€ ๋˜์ง€ ์•Š์ง€๋งŒ, ๊ฐ€๋ น ProductList ์ปดํฌ๋„ŒํŠธ๋ฅผ ํ…Œ์ŠคํŠธํ•  ๋•Œ ์—ฌ๋Ÿฌ ProductCard๊ฐ€ ํ™”๋ฉด์ƒ์— ์กด์žฌํ•˜๊ณ  ๊ฑฐ๊ธฐ์„œ ํŠน์ • ์ƒํ’ˆ์˜ "์žฅ๋ฐ”๊ตฌ๋‹ˆ" ๋ฒ„ํŠผ์„ ์ฐพ์œผ๋ ค๊ณ  ํ•œ๋‹ค๋ฉด ํ™”๋ฉด์ƒ์— ์—ฌ๋Ÿฌ "์žฅ๋ฐ”๊ตฌ๋‹ˆ" ๋ฒ„ํŠผ์ด ์กด์žฌํ•˜๊ธฐ ๋•Œ๋ฌธ์— screen ๋Œ€์‹  within์„ ๋ฐ˜๋“œ์‹œ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.