nemobim ๋‹˜์˜ ์ƒ์„ธํŽ˜์ด์ง€ ๏ผž [2ํŒ€ ์ •๋„์€] Chapter ๐Ÿงฆ 3-1. ํ”„๋ก ํŠธ์—”๋“œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ๐Ÿงฆ

HARD

7์ฃผ์ฐจ ๊ณผ์ œ ์ฒดํฌํฌ์ธํŠธ

๊ธฐ๋ณธ๊ณผ์ œ

  • ์ด 11๊ฐœ์˜ ํŒŒ์ผ, 115๊ฐœ์˜ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋ฅผ ๋ฌด์‚ฌํžˆ ์ž‘์„ฑํ•˜๊ณ  ํ†ต๊ณผ์‹œํ‚จ๋‹ค.

์งˆ๋ฌธ

Q. handlersUtils์— ๋‚จ๊ธด ์งˆ๋ฌธ์— ๋‹ต๋ณ€ํ•ด์ฃผ์„ธ์š”.

1. ๊ฐ ํ…Œ์ŠคํŠธ๋งˆ๋‹ค ๋…๋ฆฝ์ ์ธ ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

๊ฐ ํ…Œ์ŠคํŠธ๊ฐ€ ์ž์‹ ๋งŒ์˜ events ๋ฐฐ์—ด์„ ๊ฐ€์ง

export const setupMockHandlerCreation = (initEvents = [] as Event[]) => {
  // ์›๋ณธ ๋ฐ์ดํ„ฐ๋ฅผ ๊นŠ์€ ๋ณต์‚ฌ๋กœ ๊ฒฉ๋ฆฌ
  const events = [...initEvents]; // ํ…Œ์ŠคํŠธ๋งˆ๋‹ค ๋‹ค๋ฅธ ๋ฐ์ดํ„ฐ ์‚ฌ์šฉ์„ ์œ„ํ•ด
  
  // ... ํ•ธ๋“ค๋Ÿฌ ์„ค์ •

2. ๊ฐ ํ•จ์ˆ˜๋งˆ๋‹ค ๋“ฑ๋ก๋œ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์ดˆ๊ธฐํ™” ํ•œ ํ›„ ์ƒˆ๋กœ์šด ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์„ค์ •ํ•œ๋‹ค.

// ๊ธฐ์กด ํ•ธ๋“ค๋Ÿฌ ์ดˆ๊ธฐํ™”
server.resetHandlers();

server.use(
  // ์ƒˆ๋กœ์šด ํ•ธ๋“ค๋Ÿฌ ์„ค์ •
);

์ด์ „ ํ…Œ์ŠคํŠธ์˜ ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ๋‹ค์Œ ํ…Œ์ŠคํŠธ์— ์˜ํ–ฅ์„ ์ค„ ์ˆ˜ ์žˆ๊ณ  ๋‹ค๋ฅธ ํ…Œ์ŠคํŠธ์—์„œ ์ƒ์„ฑํ•œ ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ํ˜„์žฌ ํ…Œ์ŠคํŠธ๋ฅผ ๊ฐ„์„ญํ•  ์ˆ˜๋„ ์žˆ์œผ๋‹ˆ ์ดˆ๊ธฐํ™” ํ•œ๋‹ค.

3. ํด๋กœ์ €๋ฅผ ํ™œ์šฉ

export const setupMockHandlerUpdating = () => {
  // ์ด ํ•จ์ˆ˜๋งŒ์˜ ๋…๋ฆฝ์ ์ธ events ๋ฐฐ์—ด
  const events = [
    createMockEvent(1, { title: '๊ฐœ๋ฐœ ๊ณต๋ถ€' }),
    createMockEvent(2, { title: 'ํšŒ์˜' })
  ];
  
  server.use(
    http.get('/api/events', () => {
      return HttpResponse.json({ events }); // ํด๋กœ์ €์˜ events
    }),
    
    http.put('/api/events/:id', async ({ request, params }) => {
      // ... 
      // ํด๋กœ์ €์˜ events ๋ฐฐ์—ด ์ˆ˜์ •
      events[index] = { ...events[index], ...updatedEvent };
      return HttpResponse.json(events[index]);
    })
  );
  • ํ•จ์ˆ˜ ํ˜ธ์ถœ๋งˆ๋‹ค ์ƒˆ๋กœ์šด ์Šค์ฝ”ํ”„๋ฅผ ์ƒ์„ฑํ•œ๋‹ค setupMockHandlerCreation()์ด ํ˜ธ์ถœ๋  ๋•Œ๋งˆ๋‹ค ์ƒˆ๋กœ์šด events ๋ฐฐ์—ด ์ƒ์„ฑ
  • ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ์Šค์ฝ”ํ”„๋ฅผ ๊ธฐ์–ต MSW ํ•ธ๋“ค๋Ÿฌ๋“ค์ด ํ•จ์ˆ˜ ์‹คํ–‰ ์‹œ์ ์˜ events ๋ฐฐ์—ด์„ ๊ธฐ์–ตํ•œ๋‹ค
  • ๋…๋ฆฝ์ ์ธ ์ƒํƒœ ๋ณด์žฅ ๊ฐ ํ…Œ์ŠคํŠธ๊ฐ€ ์ž์‹ ๋งŒ์˜ events ๋ฐฐ์—ด์„ ๊ฐ€์ง€๊ฒŒ ๋œ๋‹ค

๋”ฐ๋ผ์„œ ํ…Œ์ŠคํŠธ ๋“ค์ด ๋™์‹œ์— ์‹คํ–‰๋˜์–ด๋„ ์„œ๋กœ ๋‹ค๋ฅธ ์ด๋ฒคํŠธ๋ฅผ ๊ฐ€์ง€๊ธฐ์— ๋ณ‘๋ ฌ๋กœ ๋Œ์•„๋„ ์•ˆ์ •์ ์ด๊ฒŒ ๋™์ž‘ํ•œ๋‹ค.


Q. ํ…Œ์ŠคํŠธ๋ฅผ ๋…๋ฆฝ์ ์œผ๋กœ ๊ตฌ๋™์‹œํ‚ค๊ธฐ ์œ„ํ•ด ์ž‘์„ฑํ–ˆ๋˜ ์„ค์ •๋“ค์„ ์†Œ๊ฐœํ•ด์ฃผ์„ธ์š”.

MSW (Mock Service Worker) ์„ค์ •

  • ๊ฐ ํ…Œ์ŠคํŠธ๋งˆ๋‹ค ๋…๋ฆฝ์ ์ธ API ์‘๋‹ต ์„ค์ •ํ•œ๋‹ค
  • ํ…Œ์ŠคํŠธ๋“ค์ด ๋™์‹œ์— ์‹คํ–‰๋˜์–ด๋„ ์„œ๋กœ ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š๋Š”๋‹ค.
/* msw */
export const server = setupServer(...handlers); //๊ฐ€์งœ ์„œ๋ฒ„ ์ƒ์„ฑ ํ•˜๊ณ  handlers์— ์ •์˜๋œ API ๋“ฑ๋ก

beforeAll(() => {
  server.listen(); // ์„œ๋ฒ„ ์‹œ์ž‘ํ•ด์„œ ์š”์ฒญ ๋ฐ›์„ ์ค€๋น„
  vi.useFakeTimers({ shouldAdvanceTime: true }); // shouldAdvnceTime: true ๋กœ ์„ค์ •ํ•˜๋ฉด ํƒ€์ด๋จธ๊ฐ€ ์ž๋™์œผ๋กœ ์ง„ํ–‰๋จ
});

beforeEach(() => {
  expect.hasAssertions(); //์ตœ์†Œ ํ•˜๋‚˜์˜ assertion์ด ์žˆ๋Š”์ง€ ํ™•์ธ, assertion ์—†์œผ๋ฉด ํ…Œ์ŠคํŠธ ์‹คํŒจ
  vi.setSystemTime(new Date('2025-10-01')); // ์›ํ•˜๋Š” ์‹œ์ ์œผ๋กœ ๊ณ ์ •
});

afterEach(() => {
  server.resetHandlers(); //MSW ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์ดˆ๊ธฐํ™”
  vi.clearAllMocks(); //ํŠน์ • ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค์—์„œ ์‚ฌ์šฉ๋œ mock ํ•จ์ˆ˜ ์ดˆ๊ธฐํ™”
});

afterAll(() => {
  vi.resetAllMocks(); //๋ชจ๋“  ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค์—์„œ ์‚ฌ์šฉ๋œ mock ํ•จ์ˆ˜ ์ดˆ๊ธฐํ™”
  vi.useRealTimers(); // ํƒ€์ด๋จธ ๊ฐ€์งœ ํ™˜๊ฒฝ ์„ค์ • ์ข…๋ฃŒ
  server.close(); //์„œ๋ฒ„ ์ข…๋ฃŒ
});

์‹ฌํ™” ๊ณผ์ œ

  • App ์ปดํฌ๋„ŒํŠธ ์ ์ ˆํ•œ ๋‹จ์œ„์˜ ์ปดํฌ๋„ŒํŠธ, ํ›…, ์œ ํ‹ธ ํ•จ์ˆ˜๋กœ ๋ถ„๋ฆฌํ–ˆ๋Š”๊ฐ€?
  • ํ•ด๋‹น ๋ชจ๋“ˆ๋“ค์— ๋Œ€ํ•œ ์ ์ ˆํ•œ ํ…Œ์ŠคํŠธ๋ฅผ 5๊ฐœ ์ด์ƒ ์ž‘์„ฑํ–ˆ๋Š”๊ฐ€?

๊ณผ์ œ ์…€ํ”„ํšŒ๊ณ 

ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ํ•ด๋ณธ ์ ์ด ์—†์—ˆ๋Š”๋ฐ ์ด๋ฒˆ์— ์ฒ˜์Œ ์งœ๋ณด๊ฒŒ ๋์Šต๋‹ˆ๋‹ค. ์–ด์ฐจํ”ผ ์•Œ์•„์•ผ ํ•  ๋‚ด์šฉ์ด๊ณ , ํ•™์Šต์ด ๋ชฉ์ ์ด๋‹ˆ ๊ทธ๋ƒฅ hard๋กœ ๋„์ „ํ–ˆ๋Š”๋ฐ... ๋”ฐ๋ผ๊ฐ€๋ ค๋‹ค๊ฐ€ ๊ฐ€๋ž‘์ด ์ฐข๊ธฐ๋Š” ์ค„ ์•Œ์•˜์Šต๋‹ˆ๋‹ค.

์œ ๋‹› ํ…Œ์ŠคํŠธ๊นŒ์ง€๋Š” ๊ฐ„๋‹จํ•˜๊ณ  ๊ดœ์ฐฎ์•˜๋Š”๋ฐ, timer ์„ค์ •์ด๋ผ๋“ ๊ฐ€ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ์˜ ์ดˆ๊ธฐ ์„ค์ • ๋ถ€๋ถ„์€ ์ •๋ง ์–ด๋ ค์› ์–ด์š”, ์–ด๋””๋ฅผ ์–ด๋–ป๊ฒŒ ์†๋Œ€์•ผ ํ• ์ง€ ๊ฐ๋„ ์•ˆ ์žกํžˆ๊ณ  ๋ฌธ์„œ๋งŒ ๋ด์„œ๋Š” ์ดํ•ด๊ฐ€ ์•ˆ ๋˜๋Š” ๋ถ€๋ถ„๋„ ๋งŽ์•„ ์†์„ ๋Œ€๊ธฐ๊ฐ€ ๋ง‰๋ง‰ํ–ˆ์Šต๋‹ˆ๋‹ค.

๋‹คํ–‰ํžˆ ์ฃผ๋ณ€์— ๊ณ ์ˆ˜๊ฐ€ ์žˆ์–ด์„œ, ์ด๊ฒƒ์ €๊ฒƒ ๋ฌผ์–ด๊ฐ€๋ฉฐ ๊ฒจ์šฐ๊ฒจ์šฐ ์—ฌ๊ธฐ๊นŒ์ง€ ์ง„ํ–‰ํ–ˆ์Šต๋‹ˆ๋‹ค. ์œค์šฐ๋‹˜ ๊ฐ์‚ฌํ•ด์š”........

๊ธฐ์ˆ ์  ์„ฑ์žฅ

MSW ๋” ์•Œ์•„๋ณด๊ธฐ

setupServer vs setupWorker

๊ธฐ์กด ๊ณผ์ œ์—์„œ๋Š” setupWorker๋ฅผ ์‚ฌ์šฉํ•ด์„œ mock ์„ ๋ฐ›์•„์„œ ์ง„ํ–‰ํ–ˆ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ์ด๋ฒˆ ๊ณผ์ œ๋Š” setupServer ๋ฅผ ์‚ฌ์šฉํ•œ๊ฑฐ ๊ฐ™๋‹ค. ๋‘˜์˜ ์ฐจ์ด๋Š” ๋ญ˜๊นŒ?

  • ํ…Œ์ŠคํŠธ(Node ํ™˜๊ฒฝ) โ†’ setupServer() ์‚ฌ์šฉ (Jest, Vitest ๊ฐ™์€ ์œ ๋‹›ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ)
  • ๋ธŒ๋ผ์šฐ์ € ๊ฐœ๋ฐœ ํ™˜๊ฒฝ โ†’ setupWorker() ์‚ฌ์šฉ (Service Worker ๋“ฑ๋กํ•ด์„œ fetch/XHR ๊ฐ€๋กœ์ฑ„๊ธฐ)

์šฉ๋„ ์ฐจ์ด๋ผ๋Š” ๊ฑด ์•Œ๊ฒŒ ๋๋‹ค. ๊ทธ๋ ‡๋‹ค๋ฉด ์ง€๊ธˆ์€ setupWorker ์„ค์ •์ด ์—†๋Š”๊ฑฐ ๊ฐ™์€๋ฐ, ์–ด๋–ป๊ฒŒ ํ™”๋ฉด์—์„œ mock์„ ๋ฐ›์•„์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๊ฑธ๊นŒ?

server.js

MSW์˜ setupWorker ๋Œ€์ฒด์žฌ ๊ฐ™์€ ์—ญํ• . ๋ธŒ๋ผ์šฐ์ €์—์„œ ์ง์ ‘ Service Worker๋ฅผ ๋“ฑ๋กํ•ด์„œ ์š”์ฒญ์„ ๊ฐ€๋กœ์ฑ„๋Š” ๋Œ€์‹ , Node/Express ์„œ๋ฒ„๋ฅผ ๋„์›Œ์„œ /api/events ๋ผ์šฐํŠธ๋ฅผ ๊ฐ€์งœ๋กœ ๊ตฌํ˜„ํ•ด๋‘๊ณ  ํ”„๋ก ํŠธ์—”๋“œ๊ฐ€ ์—ฌ๊ธฐ์— fetch/axios ์š”์ฒญ์„ ๋ณด๋‚ด๋Š” ๊ตฌ์กฐ๋‹ค.

  • ๋ธŒ๋ผ์šฐ์ € โ†’ http://localhost:3000/api/events ์š”์ฒญ
  • Express mock ์„œ๋ฒ„ โ†’ JSON ํŒŒ์ผ(realEvents.json) ์ฝ๊ณ  ์‘๋‹ต
  • ๋”ฐ๋ผ์„œ ํ”„๋ก ํŠธ์—์„œ๋Š” ์‹ค์ œ API๊ฐ€ ์—†์–ด๋„ "์ง„์งœ์ฒ˜๋Ÿผ" ์š”์ฒญ์„ ๋ณด๋‚ผ ์ˆ˜ ์žˆ๋‹ค.

Express Mock ์„œ๋ฒ„ vs Worker ์˜ ์ฐจ์ด๋„ ๊ถ๊ธˆํ•ด์„œ ์ฐพ์•„๋ด„

  • worker๋Š” ์ธ๋ฉ”๋ชจ๋ฆฌ โ†’ ์ƒˆ๋กœ๊ณ ์นจ ์‹œ ์ดˆ๊ธฐํ™”, express๋Š” ์ง„์งœ ๋„์šด๊ฑฐ๋ผ ํŒŒ์ผ(JSON)๋กœ ์ง€์† ๊ฐ€๋Šฅ
  • server.js๋กœ ๋„์› ๊ธฐ ๋•Œ๋งค ์™ธ๋ถ€ ํด๋ผ์ด์–ธํŠธ ๋ชจ๋‘ ํ˜ธ์ถœ ๊ฐ€๋Šฅ, worker๋Š” ํ•ด๋‹น ๋ธŒ๋ผ์šฐ์ €์—์„œ๋งŒ

์ฆ‰..์‹ค์ œ ์„œ๋ฒ„์ฒ˜๋Ÿผ ์ง„์งœ ํ‰๋‚ด๋ฅผ ๋‚ผ ์ˆ˜ ์žˆ๋‹ค..

ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ๋ฌธ๋ฒ•

ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์„ฑํ•˜๋ฉด์„œ ์ž์ฃผ ์‚ฌ์šฉํ•œ ๋ฌธ๋ฒ• ์œ„์ฃผ๋กœ ์ •๋ฆฌ

  • toBe: "์ด๊ฒƒ์ดย ์ •ํ™•ํžˆ ๊ฐ™์€ย ๊ฒƒ์ธ๊ฐ€?"
  • toEqual:ย "์ด๊ฒƒ์˜ย ๋‚ด์šฉ์ดย ๊ฐ™์€๊ฐ€?"
test('toBe vs toEqual', () => {
  const a = { value: 1 };
  const b = { value: 1 };

  expect(a).toEqual(b); // โœ… ํ†ต๊ณผ: ๋‚ด์šฉ์ด ๊ฐ™์€์ง€ ํ™•์ธ โ†’ ๊ฐ์ฒด๋‚˜ ๋ฐฐ์—ด์˜ ๋‚ด๋ถ€ ๊ตฌ์กฐ ๋น„๊ต
  expect(a).toBe(b);   // โŒ ์‹คํŒจ:  ๊ฐ™์€ ๊ฐ์ฒด(์ฐธ์กฐ)์ธ์ง€ ํ™•์ธ โ†’ ===์™€ ๊ฐ™์€ ์—ญํ• 
});
  • within: ํŠน์ • DOM ์š”์†Œ ๋‚ด๋ถ€์—์„œ๋งŒ ์ฐพ๊ธฐ
render(
  <div>
    <section aria-label="A"><p>apple</p></section>
    <section aria-label="B"><p>banana</p></section>
  </div>
);

const sectionA = screen.getByLabelText('A');
expect(within(sectionA).getByText('apple')).toBeInTheDocument();

// DOM ์•ˆ์—์„œ ์ฟผ๋ฆฌ ๋ฒ”์œ„๋ฅผ ์ œํ•œํ•  ๋•Œ ์‚ฌ์šฉ
// ๋™์ผํ•œ ํ…์ŠคํŠธ๊ฐ€ ์—ฌ๋Ÿฌ ๊ณณ์— ์žˆ์„ ๋•Œ ์ •ํ™•ํ•œ ์˜์—ญ ์ง€์ • ๊ฐ€๋Šฅ
  • toHaveBeenCalledWith: ํ•จ์ˆ˜๊ฐ€ ํŠน์ • ์ธ์ž๋กœ ํ˜ธ์ถœ๋˜์—ˆ๋Š”์ง€ ํ™•์ธ
const mockFn = jest.fn();
mockFn('hello', 42);

expect(mockFn).toHaveBeenCalledWith('hello', 42); // โœ…
expect(mockFn).toHaveBeenCalledWith('bye');       // โŒ

// mock ํ•จ์ˆ˜๊ฐ€ ์–ด๋–ค ์ธ์ž์™€ ํ•จ๊ป˜ ํ˜ธ์ถœ๋˜์—ˆ๋Š”์ง€ ๊ฒ€์ฆํ•  ๋•Œ ์‚ฌ์šฉ
// ํ˜ธ์ถœ ์ˆœ์„œ์™€ ์ธ์ž ์กฐํ•ฉ์„ ์ •ํ™•ํžˆ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ์Œ
  • **vi.setSystemTime, vi.advanceTimersByTimeAsync **: ๊ฐ€์งœ ์‹œ๊ฐ„ ์„ค์ •
vi.useFakeTimers();
vi.setSystemTime(new Date('2025-01-01'));
// ํ˜„์žฌ ์‹œ๊ฐ„์„ ์ง€์ •๋œ ๋‚ ์งœ๋กœ ์„ค์ •

await vi.advanceTimersByTimeAsync(1000);
// ํƒ€์ด๋จธ ๊ธฐ๋ฐ˜ ์ฝ”๋“œ์—์„œ ์‹œ๊ฐ„ ๊ฐ•์ œ ์ง„ํ–‰

์ฝ”๋“œ ํ’ˆ์งˆ

ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ํ•ด๋ณธ ์ ์ด ์—†์—ˆ๊ธฐ์— ์ฝ”๋“œ์˜ ํ’ˆ์งˆ๋ณด๋‹ค๋Š” ์„ค๋ช…์ด ์–ผ๋งˆ๋‚˜ ์ •ํ™•ํ•œ๊ฐ€? ๋ชจํ˜ธํ•œ ํ‘œํ˜„์€ ์—†๋Š”๊ฐ€? ๋ฅผ ์ค‘์‹ฌ์œผ๋กœ ๊ฒ€ํ† ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋˜ ๋ˆ„๋ฝ๋œ ํ…Œ์ŠคํŠธ๋Š” ์—†๋Š”์ง€, ์˜๋ฏธ ์—†๋Š” ํ…Œ์ŠคํŠธ๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์ง„ ์•Š์€์ง€๋ฅผ ์‚ดํŽด๋ณด๋ฉฐ ๋ณด๋‹ค ์˜๋ฏธ ์žˆ๋Š” ํ…Œ์ŠคํŠธ๋กœ ๋ฐฉํ–ฅ์œผ๋กœ ๋ฐ”๊ฟ”๋ณด๋Š” ๊ฒƒ์— ์ง‘์ค‘ํ–ˆ์Šต๋‹ˆ๋‹ค.

๋ฌด์˜๋ฏธํ•œ ํ…Œ์ŠคํŠธ ์ƒ๋žต

  • getDaysInMonth
//์œ ํšจํ•˜์ง€ ์•Š์€ ์›”์— ๋Œ€ํ•ด ์ ์ ˆํžˆ ์ฒ˜๋ฆฌํ•œ๋‹ค < ์›๋ž˜ ๋ฌธ๊ตฌ
 it('์›”์ด 12๋ฅผ ์ดˆ๊ณผํ•˜๋ฉด ์ž๋™์œผ๋กœ ๋‹ค์Œ ํ•ด๋กœ ๋„˜์–ด๊ฐ€์„œ ๊ณ„์‚ฐํ•œ๋‹ค (2025๋…„ 13์›” โ†’ 2026๋…„ 1์›”)', () => {
    const result = getDaysInMonth(2025, 13);
    expect(result).toBe(31);
  });

ํ•ด๋‹น ํ…Œ์ŠคํŠธ๋Š” ์œ ํ‹ธํ•จ์ˆ˜ ํ…Œ์ŠคํŠธ ๋ณด๋‹ค๋Š” Date ๊ฐ์ฒด์— ๋Œ€ํ•œ ํ…Œ์ŠคํŠธ๋กœ ๋А๊ปด์ ธ ์ƒ๋žตํ–ˆ์Šต๋‹ˆ๋‹ค.

์—ฃ์ง€ ์ผ€์ด์Šค ์ถ”๊ฐ€

  • getWeeksAtMonth
it('ํ‰๋…„(2025๋…„)์˜ 2์›” 28์ผ๋กœ ๋๋‚˜๋Š” ์›”์˜ ์ฃผ ๋‹จ์˜ ๋ฐฐ์—ด์„ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ฒ˜๋ฆฌํ•œ๋‹ค', () => {
    const weeksAtMonth = getWeeksAtMonth(new Date('2025-02-28'));

    expect(weeksAtMonth).toEqual([
      [null, null, null, null, null, null, 1],
      [2, 3, 4, 5, 6, 7, 8],
      [9, 10, 11, 12, 13, 14, 15],
      [16, 17, 18, 19, 20, 21, 22],
      [23, 24, 25, 26, 27, 28, null],
    ]);
  });

์œค๋…„, ํ‰๋…„์ด ์—ฃ์ง€ ์ผ€์ด์Šค๋ผ ์ƒ๊ฐ๋˜์–ด ํ•ด๋‹น ํ…Œ์ŠคํŠธ๋ฅผ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.

ํ˜ผ๋™์˜ ์—ฌ์ง€๊ฐ€ ์žˆ๋Š” ํ‘œํ˜„์€ ์ œ๊ฑฐ

  • getWeekDates
before: ์ฃผ์˜ ์‹œ์ž‘(์›”์š”์ผ)์— ๋Œ€ํ•ด ํ•ด๋‹น ์ฃผ์˜ ์ „์ฒด ๋‚ ์งœ๋“ค์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค
after: ์›”์š”์ผ์— ๋Œ€ํ•ด ํ•ด๋‹น ์ฃผ์˜ ์ „์ฒด ๋‚ ์งœ๋“ค์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค

๊ธฐ์กด ๋ฌธ๊ตฌ์˜ "์ฃผ์˜ ์‹œ์ž‘(์›”์š”์ผ)" ํ‘œํ˜„์ด ์‹ค์ œ ๋ฐ˜ํ™˜ ๊ฒฐ๊ณผ(์ผ์š”์ผ ์‹œ์ž‘)์™€ ์–ด๊ธ‹๋‚˜ ํ˜ผ๋™์˜ ์—ฌ์ง€๊ฐ€ ์žˆ์–ด "์‹œ์ž‘"์ด๋ผ๋Š” ํ‘œํ˜„์„ ์ œ๊ฑฐํ–ˆ์Šต๋‹ˆ๋‹ค.

๋ชจํ˜ธํ•œ ํ‘œํ˜„ ๊ตฌ์ฒดํ™”

  • fetchHolidays
before: ์ฃผ์–ด์ง„ ์›”์˜ ๊ณตํœด์ผ๋งŒ ๋ฐ˜ํ™˜ํ•œ๋‹ค
after: 2025๋…„ 3์›”์˜ ๊ณตํœด์ผ๋งŒ ๋ฐ˜ํ™˜ํ•œ๋‹ค

before: ๊ณตํœด์ผ์ด ์—†๋Š” ์›”์— ๋Œ€ํ•ด ๋นˆ ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค
after: ๊ณตํœด์ผ์ด ์—†๋Š” 2025๋…„ 2์›”์— ๋Œ€ํ•ด ๋นˆ ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค
  • getUpcomingEvents
before: ์ด๋ฏธ ์•Œ๋ฆผ์ด ๊ฐ„ ์ด๋ฒคํŠธ๋Š” ์ œ์™ธํ•œ๋‹ค
after: 10์‹œ 0๋ถ„์„ ๊ธฐ์ค€์œผ๋กœ ์•Œ๋ฆผ ์‹œ๊ฐ„์ด ๋ฒ”์œ„์— ์žˆ๋Š” ์ด๋ฒคํŠธ๋งŒ ๋ฐ˜ํ™˜ํ•œ๋‹ค

before: ์•Œ๋ฆผ ์‹œ๊ฐ„์ด ์•„์ง ๋„๋ž˜ํ•˜์ง€ ์•Š์€ ์ด๋ฒคํŠธ๋Š” ๋ฐ˜ํ™˜ํ•˜์ง€ ์•Š๋Š”๋‹ค
after: 10์‹œ 15๋ถ„์„ ๊ธฐ์ค€์œผ๋กœ ์ด๋ฏธ ์•Œ๋ฆผ์„ ๋ณด๋‚ธ ์ด๋ฒคํŠธ๋Š” ์ œ์™ธํ•œ๋‹ค

์ถ”์ƒ์ ์ธ ์„ค๋ช…๋ณด๋‹ค๋Š” ์ผ€์ด์Šค์— ๋งž๊ฒŒ ๊ตฌ์ฒดํ™”ํ–ˆ์Šต๋‹ˆ๋‹ค.

ํ•™์Šต ํšจ๊ณผ ๋ถ„์„

ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•  ๋•Œ ๊ณ ๋ คํ•  ๊ธฐ์ค€

1. ๋น„์ •์ƒ ๋ฐ์ดํ„ฐ (edge case)๋Š” ํ…Œ์ŠคํŠธํ•  ๊ฒƒ์ธ๊ฐ€?

  • ๊ธฐ์ค€: ํ•จ์ˆ˜์˜ ์ฑ…์ž„๊ณผ ์‚ฌ์šฉ ๋งฅ๋ฝ์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์ง„๋‹ค

    • ์˜ˆ์™ธ ์ฒ˜๋ฆฌ๋ฅผ ์ง์ ‘ ๋‹ด๋‹นํ•˜๋Š” ํ•จ์ˆ˜๋ผ๋ฉด, ์ž˜๋ชป๋œ ์ž…๋ ฅ(์˜ˆ: ์Œ์ˆ˜, ๋นˆ ๊ฐ’, ์ž˜๋ชป๋œ ๋‚ ์งœ ๋“ฑ)์„ ๋ฐ˜๋“œ์‹œ ํ…Œ์ŠคํŠธํ•ด์•ผ ํ•œ๋‹ค.

    • ์ƒ์œ„ ๋กœ์ง์—์„œ ์ž…๋ ฅ์ด ๋ณด์žฅ๋˜๋Š” ๊ฒฝ์šฐ๋ผ๋ฉด, ํ•ด๋‹น ํ•จ์ˆ˜์—์„œ๋Š” ์ •์ƒ ์ผ€์ด์Šค๋งŒ ํ…Œ์ŠคํŠธํ•ด๋„ ๋ฌด๋ฐฉํ•˜๋‹ค.

Tip: ํŒ€ ๋‚ด์—์„œ "์ด ํ•จ์ˆ˜๋Š” ์ž…๋ ฅ ์œ ํšจ์„ฑ๊นŒ์ง€ ์ฑ…์ž„์ง€๋Š”๊ฐ€?"์— ๋Œ€ํ•œ ํ•ฉ์˜๊ฐ€ ์žˆ์œผ๋ฉด ์ข‹์Œ.

2. ๊ฒ€์ฆ ์‹œ ํ•˜๋“œ์ฝ”๋”ฉํ•  ๊ฒƒ์ธ๊ฐ€, ๋™์ ์œผ๋กœ ๊ฒ€์‚ฌํ•  ๊ฒƒ์ธ๊ฐ€?

  • ๊ธฐ์ค€:

    • expect(result).toHaveLength(3)์ฒ˜๋Ÿผ ๊ณ ์ •๋œ ์ˆซ์ž๋ฅผ ์“ฐ๋ฉด, ํ…Œ์ŠคํŠธ๊ฐ€ ๋ช…ํ™•ํ•˜๊ณ  ์˜๋„๊ฐ€ ๋ถ„๋ช…ํ•˜๊ฒŒ ๋“œ๋Ÿฌ๋‚จ.

    • expect(result).toHaveLength(events.length)์ฒ˜๋Ÿผ mock ๋ฐ์ดํ„ฐ์— ์˜์กดํ•˜๋ฉด, ํ…Œ์ŠคํŠธ์˜ ์œ ์—ฐ์„ฑ์€ ์ƒ๊ธฐ์ง€๋งŒ ํ…Œ์ŠคํŠธ ์ž์ฒด์˜ ์˜๋ฏธ๊ฐ€ ์•ฝํ•ด์งˆ ์ˆ˜ ์žˆ์Œ.

  • ์ถ”์ฒœ:

    • ๋‹จ์ˆœํ•œ ์œ ๋‹› ํ…Œ์ŠคํŠธ โ†’ ํ•˜๋“œ์ฝ”๋”ฉ๋œ ์ˆซ์ž๋ฅผ ์‚ฌ์šฉํ•ด ์˜๋„๋ฅผ ๋ช…ํ™•ํžˆ

    • ๋ณต์žกํ•œ ๋™์  ๋ฐ์ดํ„ฐ๋‚˜ ์กฐ๊ฑด๋ถ€ ๊ฒฐ๊ณผ โ†’ mock ๋ฐ์ดํ„ฐ ๊ธฐ๋ฐ˜์œผ๋กœ ์œ ์—ฐํ•˜๊ฒŒ

3. ํ…Œ์ŠคํŠธ ์„ค๋ช…์€ ๋ฒ”์šฉ์ ์œผ๋กœ vs ๊ตฌ์ฒด์ ์œผ๋กœ?

  • ๊ธฐ์ค€:

    • ๋ฒ”์šฉ ์„ค๋ช…: ๊ธฐ๋Šฅ์˜ ์ผ๋ฐ˜์ ์ธ ๋™์ž‘์„ ์„ค๋ช…ํ•  ๋•Œ ์‚ฌ์šฉ ์˜ˆ) ์ž…๋ ฅํ•œ ๋‚ ์งœ ๊ธฐ์ค€์œผ๋กœ ์ฃผ๊ฐ„ ์ผ์ •์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค

    • ๊ตฌ์ฒด์  ์„ค๋ช…: ํŠน์ • ์ž…๋ ฅ๊ฐ’๊ณผ ๊ธฐ๋Œ€ ๊ฒฐ๊ณผ๋ฅผ ๋ช…ํ™•ํžˆ ๋ณด์—ฌ์ฃผ๊ณ  ์‹ถ์„ ๋•Œ ์‚ฌ์šฉ ์˜ˆ) 2025-07-01์„ ์ž…๋ ฅํ•˜๋ฉด ์›”์š”์ผ๋ถ€ํ„ฐ ์ผ์š”์ผ๊นŒ์ง€ ๋ฐ˜ํ™˜ํ•œ๋‹ค

  • ์ถ”์ฒœ:

    • ๊ตฌ์ฒดํ™”ํ•  ์ˆ˜ ์žˆ๋Š” ์ผ€์ด์Šค๋Š” ๊ตฌ์ฒดํ™” ํ•ด์„œ ์ ๊ธฐ

๊ณผ์ œ ํ”ผ๋“œ๋ฐฑ

  • ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ฒ˜์Œ ์ž‘์„ฑํ•˜๋Š” ์‚ฌ๋žŒ๋„ ์‰ฝ๊ฒŒ ๋”ฐ๋ผ๊ฐˆ ์ˆ˜ ์žˆ๋„๋ก ๊ฐ€์ด๋“œ๊ฐ€ ์ž˜ ๋˜์–ด ์žˆ์–ด ๋ฐฐ์šฐ๊ธฐ ์ˆ˜์›”ํ–ˆ์Šต๋‹ˆ๋‹ค!
  • ํŠนํžˆ ๋‚œ์ด๋„๋ฅผ ์„ ํƒํ•  ์ˆ˜ ์žˆ๋Š” ์ ์ด ๋ถ€๋‹ด์„ ์ค„์ด๋Š” ๋ฐ ๋„์›€์ด ๋˜์–ด ์ข‹์•˜์Šต๋‹ˆ๋‹ค.

์ €๋Š” ๋ฐฐ์›€์˜ ๋ชฉ์ ์œผ๋กœ hard๋ฅผ ์„ ํƒํ–ˆ๋Š”๋ฐ ํ™•์‹คํžˆ hard๋Š” ์ •๋ง ๋ง‰๋ง‰ํ•˜๋”๋ผ๊ณ ์š”. ์กฐ๊ธˆ ๋” ๊ฐ€์ด๋“œ๋ฅผ ์ œ๊ณตํ•ด๋„ ๊ดœ์ฐฎ์ง€ ์•Š์„๊นŒ ์‹ถ์Šต๋‹ˆ๋‹ค. ํ•˜๋‹ค๊ฐ€ ๋„ˆ๋ฌด ์–ด๋ ค์›Œ์„œ ํŒ€์›์˜ ํžŒํŠธ๋ฅผ ๋ฐ›์•„ ์ž‘์„ฑํ–ˆ๋Š”๋ฐ ํžŒํŠธ๊ฐ€ ์—†์—ˆ๋”๋ผ๋ฉด ํ˜ผ์ž์„œ๋Š” ๊ฐ์„ ์žก๊ธฐ ์–ด๋ ค์› ์„ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

๋ฆฌ๋ทฐ ๋ฐ›๊ณ  ์‹ถ์€ ๋‚ด์šฉ

useNotifications ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์„ฑ ์ค‘์— ์‹œ๊ฐ„ ์„ค์ • ๊ด€๋ จํ•ด์„œ ์ดํ•ด๊ฐ€ ์ž˜ ์•ˆ ๋˜๋Š” ๋ถ€๋ถ„์ด ์žˆ์–ด์„œ ์งˆ๋ฌธ๋“œ๋ฆฝ๋‹ˆ๋‹ค.

ํ˜„์žฌ setupTest.ts์—์„œ ์ „์—ญ์œผ๋กœ ์‹œ๊ฐ„์„ ์„ค์ •ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค

//setupTest.ts
beforeEach(() => {
  expect.hasAssertions(); //์ตœ์†Œ ํ•˜๋‚˜์˜ assertion์ด ์žˆ๋Š”์ง€ ํ™•์ธ, assertion ์—†์œผ๋ฉด ํ…Œ์ŠคํŠธ ์‹คํŒจ
  vi.setSystemTime(new Date('2025-10-01')); // ์›ํ•˜๋Š” ์‹œ์ ์œผ๋กœ ๊ณ ์ •
});

๊ทธ๋Ÿฐ๋ฐ useNotifications.spec.ts์—์„œ ์•Œ๋ฆผ ์ƒ์„ฑ ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•  ๋•Œ ๊ฐœ๋ณ„ ํ…Œ์ŠคํŠธ์—์„œ ์‹œ๊ฐ„์„ ๋‹ค์‹œ ์„ค์ •ํ•˜์ง€ ์•Š์œผ๋ฉด ํ…Œ์ŠคํŠธ๊ฐ€ ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค

//medium.useNotifications.spec.ts

 // ? setupTest์—์„œ ์„ค์ •์„ ํ–ˆ๋Š”๋ฐ, ์—ฌ๊ธฐ์„œ ๋‹ค์‹œ ์„ค์ •์„ ํ•˜๋Š”๊ฑฐ์ง€? ๊ทผ๋ฐ ์•ˆํ•˜๋ฉด ํ†ต๊ณผ๊ฐ€ ์•ˆ๋œ๋‹ค.
  // > ์‹œ๊ฐ„๊นŒ์ง€ ์„ค์ •ํ–ˆ๋”๋‹ˆ ๋˜๋„ค... ๊ธฐ์กด 2025-10-01์—์„œ 2025-10-01 00:00:00 ์œผ๋กœ ์„ค์ •์„ ๋ฐ”๊ฟจ๋‹ค.
  // > ์ด๋ฒˆ์—” ์บ˜๋ฆฐ๋” ํ›… ํ…Œ์ŠคํŠธ์—์„œ ํ„ฐ์ง... ๊ทธ๋ƒฅ ์—ฌ๊ธฐ์„œ๋งŒ ์‹œ๊ฐ„ ์„ค์ •ํ•˜๋Š” ๊ฑธ๋กœ...

  //๊ทธ๋ฆฌ๊ณ  shouldAdvanceTime: true ๋กœ ์„ค์ •ํ•˜๋ฉด ํƒ€์ด๋จธ๊ฐ€ ์ž๋™์œผ๋กœ ์ง„ํ–‰๋œ๋‹ค๋ผ๋Š” ๋ง์ด ์žˆ๋˜๋ฐ, ์™œ ์•ˆ ํ๋ฅผ๊นŒ..

  beforeEach(() => {
    // ์‹œ๊ฐ„์„ 2025-10-01 00:00:00์œผ๋กœ ์„ค์ •
    vi.setSystemTime(new Date('2025-10-01 00:00:00'));
  });
  • setupTest์—์„œ 2025-10-01 00:00:00๋กœ ๋ฐ”๊พธ๋ฉด ํ•ด๋‹น ํ…Œ์ŠคํŠธ๋Š” ํ†ต๊ณผํ•˜์ง€๋งŒ ๋‹ค๋ฅธ hook ํ…Œ์ŠคํŠธ๋“ค์ด ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฐ ๊ฒฝ์šฐ ๊ฐ ํ…Œ์ŠคํŠธ ํŒŒ์ผ์—์„œ ๊ฐœ๋ณ„์ ์œผ๋กœ ์‹œ๊ฐ„์„ ์„ค์ •ํ•˜๋Š” ๊ฒƒ์ด ์˜ฌ๋ฐ”๋ฅธ ๋ฐฉ๋ฒ•์ผ๊นŒ์š”? ์•„๋‹ˆ๋ฉด ์ „์—ญ ์„ค์ •์„ ์กฐ์ •ํ•˜๋Š” ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์ด ์žˆ์„๊นŒ์š”?

  • shouldAdvanceTime: true ์˜ต์…˜์ด ์‹œ๊ฐ„์„ ์ž๋™์œผ๋กœ ํ๋ฅด๊ฒŒ ํ•ด์ค€๋‹ค๊ณ  ์ดํ•ดํ–ˆ๋Š”๋ฐ, ์‹ค์ œ๋กœ๋Š” advanceTimersByTimeAsync()๋ฅผ ์ˆ˜๋™์œผ๋กœ ํ˜ธ์ถœํ•ด์•ผ๋งŒ ํ…Œ์ŠคํŠธ๊ฐ€ ํ†ต๊ณผํ–ˆ์Šต๋‹ˆ๋‹ค. shouldAdvanceTime ์˜ต์…˜์ด ์ •ํ™•ํžˆ ์–ด๋–ค ์—ญํ• ์„ ํ•˜๋Š” ๊ฑด๊ฐ€์š”? ์–ธ์ œ ์ˆ˜๋™์œผ๋กœ ์‹œ๊ฐ„์„ ์ง„ํ–‰์‹œ์ผœ์•ผ ํ•˜๊ณ  ์ด ์˜ต์…˜๋งŒ์œผ๋กœ๋Š” ์™œ ์ถฉ๋ถ„ํ•˜์ง€ ์•Š์€ ๊ฑด์ง€ ๊ถ๊ธˆํ•ฉ๋‹ˆ๋‹ค...

ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ์—์„œ ํƒ€์ž„์กด ์ด์Šˆ

it('notificationTime์„ 10์œผ๋กœ ํ•˜๋ฉด ์ง€์ • ์‹œ๊ฐ„ 10๋ถ„ ์ „ ์•Œ๋žŒ ํ…์ŠคํŠธ๊ฐ€ ๋…ธ์ถœ๋œ๋‹ค', async () => {
  vi.stubEnv('TZ', 'UTC'); // ํƒ€์ž„์กด ์„ค์ •

  const events = [
    createMockEvent(1, {
      title: '๋งˆ์ง€๋ง‰',
      date: '2025-10-01',
      description: '๋งˆ์ง€๋ง‰ ์ผ์ •',
      startTime: '00:10',
      endTime: '00:20',
      notificationTime: 10, // 10๋ถ„ ์ „ ์•Œ๋ฆผ
    }),
  ];

  setupMockHandlerCreation(events);
  renderApp();

  // ๋จผ์ € ์ด๋ฒคํŠธ๊ฐ€ ํ‘œ์‹œ๋˜๋Š”์ง€ ํ™•์ธ
  expect(await screen.findByText('๋งˆ์ง€๋ง‰ ์ผ์ •')).toBeInTheDocument();

  act(() => {
    vi.advanceTimersByTime(1000); // 1์ดˆ๋งŒ ์ง„ํ–‰
  });

  // ์•Œ๋ฆผ์ด ํ•˜๋‚˜๋งŒ ์žˆ๋Š”์ง€ ํ™•์ธ
  expect(screen.getByText('10๋ถ„ ํ›„ ๋งˆ์ง€๋ง‰ ์ผ์ •์ด ์‹œ์ž‘๋ฉ๋‹ˆ๋‹ค.')).toBeInTheDocument();
});

์ด ํ…Œ์ŠคํŠธ์—์„œ๋งŒ ์‹œ๊ฐ„์ด 9์‹œ๊ฐ„์”ฉ ์ฐจ์ด๋‚จ -> ํƒ€์ž„์กด ์ฐจ์ด(9์‹œ๊ฐ„)๋กœ ์ƒ๊ฐ๋˜์–ด ์ด ํ…Œ์ŠคํŠธ์—์„œ๋งŒ vi.stubEnv('TZ', 'UTC') ์„ค์ •

  • ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ ์ง„ํ–‰์ค‘ notificationTime ํ…Œ์ŠคํŠธ์—์„œ๋งŒ 9์‹œ๊ฐ„ ์ฐจ์ด๊ฐ€ ๋ฐœ์ƒํ•ด์„œ vi.stubEnv('TZ', 'UTC') ์„ค์ •์ด ํ•„์š”ํ–ˆ์Šต๋‹ˆ๋‹ค. ์™œ ์ด ํ…Œ์ŠคํŠธ๋งŒ ํƒ€์ž„์กด ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธด ๊ฑธ๊นŒ์š”...

  • ์‹œ๊ฐ„ ๊ด€๋ จํ•œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๊ฐ€ ์ œ์ผ ์ด์Šˆ๊ฐ€ ๋งŽ์•˜์Šต๋‹ˆ๋‹ค. ํƒ€์ด๋จธ ๊ด€๋ จ ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ• ๋•Œ ํŒ์ด ์žˆ์„๊นŒ์š”? ์‹œ๊ฐ„ ๊ด€๋ จ ํ…Œ์ŠคํŠธ๊ฐ€ ๋ณต์žกํ•ด์„œ ํšจ์œจ์ ์ธ ์ ‘๊ทผ๋ฒ•์„ ์•Œ๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค.

ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์„ฑ์ด ์•„์ง ์ต์ˆ™ํ•˜์ง€ ์•Š์•„์„œ ์ œ๊ฐ€ ์ ‘๊ทผํ•˜๋Š” ๋ฐฉ์‹์ด ๋งž๋Š”์ง€ ํ™•์‹ ์ด ์„œ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์กฐ์–ธ ๋ถ€ํƒ๋“œ๋ฆฝ๋‹ˆ๋‹ค!

๊ณผ์ œ ํ”ผ๋“œ๋ฐฑ

์ˆ˜๊ณ ํ•˜์…จ์Šต๋‹ˆ๋‹ค! ๋„์€๋‹˜

Q. useNotifications ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์„ฑ์‹œ setSystemTime๋ฌธ์ œ

A. ๋„ค ์„ค์ •์ดํ›„ ๋ฏธ์„ธํ•œ ์‹œ๊ฐ„์˜ ์ฐจ์ด๊ฐ€ ์ƒ๊ธธ์ˆ˜ ์žˆ์œผ๋‹ˆ beforeEach๋ณด๋‹ค๋Š” ์‹ค์ œ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค ์ฆ‰ it()ํ•จ์ˆ˜์•ˆ์—์„œ ์›ํ•˜๋Š” ์‹œ๊ฐ„์„ ์ตœ๋Œ€ํ•œ ํ•„์š”ํ•œ ์œ„์น˜์—์„œ ์„ค์ •ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์„ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค :)

Q. shouldAdvanceTime์˜ ์—ญํ• 

A. ์ œ๊ฐ€ ์˜ˆ์ „์— ์ •๋ฆฌํ•œ๊ฑด shouldAdvanceTime๋งŒ์€ ํƒ€์ด๋จธ๋ฅผ ํ•œ๋ฒˆ์— ๊ฝํ•˜๊ณ  ์‹œ๊ฐ„์„ ์กฐ์ ˆํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ์กฐ๊ธˆ์”ฉ ํ๋ฅด๋Š” ๊ฒƒ์„ ์‹œ๋ฎฌ๋ ˆ์ด์…˜๋งŒ ํ•ด์ค„๋ฟ ์˜จ์ „ํ•˜๊ฒŒ ์‹œ๊ฐ„์ด ํ๋ฅด๊ณ  ํƒ€์ด๋จธ์˜ ์ฝœ๋ฐฑ๋“ค์ด ์˜จ์ „ํ•˜๊ฒŒ ์‹คํ–‰๋˜๊ฒŒ ํ•˜๋ ค๋ฉด ์‹œ๊ฐ„์„ ์ญˆ์šฑ ๋ฐ€์–ด์ฃผ๋Š” ์ž‘์—…์ด ํ•„์š”ํ•˜๋‹ค๋กœ ์ •๋ฆฌํ–ˆ์–ด์š”. ๊ทธ๊ฒŒ vi.advanceTimersByTimeAsync()์ฃ . ์ฆ‰ shouldAdvanceTime ์„ค์ •์€ ๊ทธ๋ƒฅ ํƒ€์ด๋ฉฐ ์‹œ๊ฐ„์ด ํ๋ฆ„์— ๊ด€ํ•œ ์ด์•ผ๊ธฐ๊ณ  ์˜จ์ „ํžˆ ์‹œ๊ฐ„ํžˆ ํ๋ฅธ๊ฒƒ์„ ๋ณด์žฅํ•ด ๋ชจ๋“  ์ฝœ๋ฐฑ์„ ์‹คํ–‰ํ•˜๋Š” ๊ฒƒ์€ ๋ณ„๊ฐœ์˜ ์ด์•ผ๊ธฐ ์ธ๊ฒƒ์ด์ฃ .

Q. ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ์—์„œ ํƒ€์ž„์กด ์ด์Šˆ

A. ์Œ ์ด๊ฑด ์„œ๋ฒ„์—์„œ ์ด์Šˆ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋‹ค๋Š” ๋œป์ธ๊ฐ€์š”? ์Œ ์ด์ƒํ•˜๋„ค์šฅ ํƒ€์ž„์กด ์ด์Šˆ๋Š” ๋ณดํ†ต ์—†์—ˆ๋Š”๋ฐ์š” ๋ญ”๊ฐ€ ์‹œ์Šคํ…œ ์„ค์ •์˜ ํƒ€์ž„์กด์ด ๋‹ฌ๋ผ์„œ ์ด์ง€ ์•Š์„๊นŒ์š”?

Q. ์‹œ๊ฐ„ ๊ด€๋ จํ•œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๊ฐ€ ์ œ์ผ ์ด์Šˆ๊ฐ€ ๋งŽ์•˜์Šต๋‹ˆ๋‹ค. ํƒ€์ด๋จธ ๊ด€๋ จ ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ• ๋•Œ ํŒ

A. ์•„๋งˆ ์ฒ˜์Œ์ด์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ ์‘ํ•˜๊ธฐ ํž˜๋“ค์—ˆ์ง€ ์•Š์•˜๋‚˜ ์‹ถ์–ด์š”. ์‚ฌ์‹ค vitest์™€ ๊ฐ™์€ ํ…Œ์ŠคํŠธ๋Ÿฌ๋„ˆ๊ฐ€ ์ œ๊ณตํ•˜๋Š” api ์…‹์ด ๊ทธ๋ ‡๊ฒŒ ๋งŽ์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์ œ์ƒ๊ฐ์—๋Š” ์ด๋ฒˆ์— ํ•œ๋ฒˆ ์ต์ˆ™ํ•ด์ง€์‹œ๋ฉด ๋‹ค์Œ๋ถ€ํ„ฐ๋Š” ํฌ๊ฒŒ ์–ด๋ ต์ง€ ์•Š์„ ๊ฒƒ ๊ฐ™์•„์š”. ๊ด€๋ จํ•ด์„œ ๋งˆ์šฐ์Šค ๋“œ๋ž˜๊ทธ ๊ฐ™์€ ํ…Œ์ŠคํŠธ๋„ ์ฒ˜์Œ์—๋Š” ์–ด๋–ป๊ฒŒ ํ•˜๋ผ๋Š”๊ฑด์ง€ ๋‚œ๊ฐํ•œ๋ฐ์š”. ๋ช‡๋ฒˆ ์‹œํ–‰์ฐฉ์˜ค๋ฅผ ๊ฒช๊ณ  ๋‚˜๋ฉด ๋ณ„๊ฒƒ ์•„๋‹ˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. :) ์‚ฌ์‹ค ์ €๋„ ์–ด๋–ค ์œ ํ˜•์˜ ํ…Œ์ŠคํŠธ๋ฅผ ์˜ค๋žœ๋งŒ์— ํ•˜๋ฉด ํ•œ๋™์•ˆ ํ•ด๋งค์š” ใ…Žใ…Ž