HARD
7μ£Όμ°¨ κ³Όμ 체ν¬ν¬μΈνΈ
κΈ°λ³Έκ³Όμ
- μ΄ 11κ°μ νμΌ, 115κ°μ λ¨μ ν μ€νΈλ₯Ό 무μ¬ν μμ±νκ³ ν΅κ³Όμν¨λ€.
μ§λ¬Έ
Q. handlersUtilsμ λ¨κΈ΄ μ§λ¬Έμ λ΅λ³ν΄μ£ΌμΈμ. // ! Hard // ! μ΄λ²€νΈλ μμ±, μμ λλ©΄ fetchλ₯Ό λ€μ ν΄ μνλ₯Ό μ λ°μ΄νΈ ν©λλ€. μ΄λ₯Ό μν μ μ΄κ° νμν κ² κ°μλ°μ. μ΄λ»κ² μμ±ν΄μΌ ν μ€νΈκ° λ³λ ¬λ‘ λμλ μμ μ μ΄κ² λμν κΉμ? // ! μλ μ΄λ¦μ μ¬μ©νμ§ μμλ λλ, λ 립μ μ΄κ² ν μ€νΈλ₯Ό ꡬλν μ μλ λ°©λ²μ μ°Ύμ보μΈμ. κ·Έλ¦¬κ³ μ΄ λ‘μ§μ PRμ μ€λͺ ν΄μ£ΌμΈμ.
useEventOperationsλ₯Ό νμΈν΄λ³΄λ©΄ saveEvent, deleteEventμμ μ²λ¦¬ νμ fetchEventλ₯Ό ν΅ν΄ μλ‘ μ΄λ²€νΈλ₯Ό λΆλ¬μ€κ² λλλ°μ. μ΄ κ²½μ°μ μμ , μμ , μΆκ° μ΄λ²€νΈμ λν ν
μ€νΈκ° λ³λ ¬λ‘ μ²λ¦¬λμμ κ²½μ°μ λ°μ΄ν°κ° λΆμμ ν κ²μΌλ‘ νλ¨λμμ΅λλ€.
κ·Έλμ
// setupMockHandlerUpdating - μμ ν
μ€νΈμ©
export const setupMockHandlerUpdating = () => {
const events: Event[] = [
{
id: '1',
title: 'μμ ν
μ€νΈμ
λλΉ',
date: '2025-10-17',
// ... κΈ°λ³Έ λ°μ΄ν°
}
];
server.use(
http.put('/api/events/:id', async ({ params, request }) => {
const updatedEvent = (await request.json()) as Event;
const index = events.findIndex((event) => event.id === params.id);
if (index === -1) {
return new HttpResponse(null, { status: 404 });
}
events[index] = { ...events[index], ...updatedEvent };
return HttpResponse.json(events[index]);
})
);
};
// setupMockHandlerDeletion - μμ ν
μ€νΈμ©
export const setupMockHandlerDeletion = () => {
const events: Event[] = [/* μμ μ© κΈ°λ³Έ λ°μ΄ν° */];
server.use(
http.delete('/api/events/:id', ({ params }) => {
const index = events.findIndex((event) => event.id === params.id);
if (index === -1) {
return new HttpResponse(null, { status: 404 });
}
events.splice(index, 1);
return new HttpResponse(null, { status: 204 });
})
);
};
μμ κ°μ΄ MSW νΈλ€λ¬ μ체μ ꡬ쑰λ₯Ό κ°μ νμ΅λλ€. κΈ°μ‘΄μλ νλμ μ μ λ°°μ΄μ μ¬λ¬ ν μ€νΈκ° 곡μ νλ ννμλ€λ©΄, μ΄μ λ κ° νΈλ€λ¬ μ€μ ν¨μκ° νΈμΆλ λλ§λ€ μμ ν μλ‘μ΄ λ°°μ΄μ μμ±νλλ‘ λ³κ²½νμ΅λλ€. setupMockHandlerCreation ν¨μμμλ μ λ¬λ°μ μ΄κΈ° λ°μ΄ν°λ₯Ό λ°©μ΄μ 볡μ¬λ₯Ό ν΅ν΄ λ 립μ μΈ λ°°μ΄λ‘ λ§λ€κ³ , μ΄ λ°°μ΄λ§μ μ¬μ©νλ GET, POST νΈλ€λ¬λ₯Ό μ€μ νλλ‘ κ΅¬ννμμ΅λλ€.
Q. ν μ€νΈλ₯Ό λ 립μ μΌλ‘ ꡬλμν€κΈ° μν΄ μμ±νλ μ€μ λ€μ μκ°ν΄μ£ΌμΈμ.
μμμ νΈλ€λ¬λ₯Ό λ³κ²½νκ²μ μΆκ°μ μΌλ‘ setupTests.tsνμΌμμλ μΆκ°μ μΌλ‘ μ€μ μ νλλ°μ.
import { setupServer } from 'msw/node';
import '@testing-library/jest-dom';
import { handlers } from './__mocks__/handlers';
/* msw */
export const server = setupServer(...handlers);
beforeAll(() => {
server.listen();
vi.useFakeTimers({ shouldAdvanceTime: true });
});
beforeEach(() => {
expect.hasAssertions();
vi.setSystemTime(new Date('2025-10-15'));
});
afterEach(() => {
server.resetHandlers();
vi.clearAllMocks();
vi.clearAllTimers();
});
afterAll(() => {
vi.resetAllMocks();
server.close();
vi.useRealTimers();
});
κ° ν μ€νΈκ° λλ ν μ΄μ ν μ€νΈμ μν₯μ μμ ν μ κ±°νλ κ²μ΄λΌκ³ νλ¨νμ΅λλ€. λ°λΌμ setupTests.ts νμΌμμ ν μ€νΈ μλͺ μ£ΌκΈ°λ₯Ό 체κ³μ μΌλ‘ κ΄λ¦¬νλλ‘ μ€μ νμ΅λλ€. beforeAll λ¨κ³μμλ MSW μλ²λ₯Ό μμνκ³ FakeTimerλ₯Ό νμ±νν©λλ€. FakeTimerλ₯Ό μ¬μ©νλ μ΄μ λ μκ°μ μμ‘΄μ μΈ ν μ€νΈλ€μ΄ μ€ν νκ²½μ κ΄κ³μμ΄ μΌκ΄λ κ²°κ³Όλ₯Ό μ»κΈ° μν΄μμ λλ€. νΉν shouldAdvanceTime μ΅μ μ trueλ‘ μ€μ νμ¬ νμ΄λ¨Έκ° μ€μ λ‘ μ§νλλλ‘ νμ΅λλ€. (νμμμλ μ΄ μ€μ μ λν μΈκΈμ΄ μμ΅λλ€)
beforeEach λ¨κ³μμλ λ κ°μ§ μ€μν μ€μ μ ν©λλ€. μ²«μ§Έλ‘ expect.hasAssertions()λ₯Ό νΈμΆνμ¬ λͺ¨λ ν μ€νΈμ μ΅μ νλ μ΄μμ assertionμ΄ μλλ‘ κ°μ ν©λλ€. μ΄λ λΉ ν μ€νΈλ μ€μλ‘ assertionμ΄ μλ ν μ€νΈλ₯Ό λ°©μ§νκΈ° μν¨μ΄μ§λ§, λΉλκΈ°μ μΌλ‘ μ½λ°±μ΄ μλ κ²½μ°μ μ΄λ₯Ό μ€ννμ§ μκ³ λμ΄κ° μ μλ μν©μ 미리 μ°¨λ¨νλ μν λ ν©λλ€. μλ₯Ό λ€μ΄, Promiseκ° rejectλκ±°λ μ½λ°± ν¨μκ° νΈμΆλμ§ μλ κ²½μ°, ν μ€νΈλ μ±κ³΅μΌλ‘ νμλμ§λ§ μ€μ λ‘λ μ°λ¦¬κ° μλν κ²μ¦ λ‘μ§μ΄ μ ν μ€νλμ§ μμ μνκ° λ μ μμ΅λλ€. expect.hasAssertions()λ₯Ό μ¬μ©νλ©΄ μ΄λ° κ²½μ° ν μ€νΈκ° μ€ν¨νλλ‘ νμ¬ false positiveλ₯Ό λ°©μ§ν μ μμ΅λλ€. (μ΄ λΆλΆλ νμμ λ΄μ©μ 리 ν΄λμμ΅λλ€.) λμ§Έλ‘ vi.setSystemTimeμ μ¬μ©νμ¬ λͺ¨λ ν μ€νΈκ° λμΌν μκ°(2025-10-15)μμ μ€νλλλ‘ κ³ μ ν©λλ€. μ΄λ κ² νλ©΄ λ μ§λ μκ°μ μμ‘΄μ μΈ λ‘μ§μ ν μ€νΈν λ νκ²½μ κ΄κ³μμ΄ μΌκ΄λ κ²°κ³Όλ₯Ό μ»μ μ μμ΅λλ€. κ°μ₯ μ€μν λΆλΆμ afterEach λ¨κ³μ λλ€. μ¬κΈ°μ server.resetHandlers()λ₯Ό νΈμΆνμ¬ μ΄μ ν μ€νΈμμ server.use()λ‘ μ€μ ν λͺ¨λ MSW νΈλ€λ¬λ₯Ό κΈ°λ³Έ μνλ‘ λ³΅μνλλ‘ κ΅¬ννμμ΅λλ€. λν vi.clearAllMocks()λ‘ λͺ¨λ mock ν¨μμ νΈμΆ μ΄λ ₯μ μ΄κΈ°ννκ³ , vi.clearAllTimers()λ‘ νμ΄λ¨Έ μνλ₯Ό μ 리νλλ‘ νμ΅λλ€.
λ§μ§λ§μΌλ‘ afterAll λ¨κ³μμλ λͺ¨λ mockμ μμ ν 리μ νκ³ MSW μλ²λ₯Ό μ’ λ£ν ν μ€μ νμ΄λ¨Έλ‘ 볡μν©λλ€.
λν μ€μ ν
μ€νΈ νμΌμμλ κ° ν
μ€νΈ μΌμ΄μ€λ§λ€ μ μ ν νΈλ€λ¬ μ€μ ν¨μλ₯Ό νΈμΆνμ¬ λ
립μ μΈ νκ²½μ ꡬμ±νλλ‘ νμ΅λλ€. useEventOperations ν
μ€νΈμμλ μλ‘μ΄ μ΄λ²€νΈλ₯Ό μ μ₯νλ ν
μ€νΈμμ setupMockHandlerCreation([])μ νΈμΆνμ¬ λΉ λ°°μ΄λ‘ μμνλ μμ ν μλ‘μ΄ νκ²½μ λ§λλλ€. μ΄ ν
μ€νΈκ° μ€νλλ λμ λ€λ₯Έ μ΄λ€ ν
μ€νΈμ λ°μ΄ν°λ μν₯μ μ£Όμ§ μμ΅λλ€. μμ , μμ λ±μ ν
μ€νΈμμλ λ§μ°¬κ°μ§λ‘ μλ‘μ΄ νκ²½μμ λμν μ μλλ‘ κ΅¬ννμμ΅λλ€.
enqueueSnackbarμ κ°μ μΈλΆ λΌμ΄λΈλ¬λ¦¬ ν¨μλ€λ λ
립μ±μ 보μ₯ν΄μΌ ν©λλ€. notistack λΌμ΄λΈλ¬λ¦¬λ₯Ό mockingν λ vi.fn()μΌλ‘ μμ±ν mock ν¨μλ₯Ό μ¬μ©νκ³ , κ° ν
μ€νΈ ν vi.clearAllMocks()λ‘ νΈμΆ μ΄λ ₯μ μ΄κΈ°ννμ΅λλ€.
μ¬ν κ³Όμ
- App μ»΄ν¬λνΈ μ μ ν λ¨μμ μ»΄ν¬λνΈ, ν , μ νΈ ν¨μλ‘ λΆλ¦¬νλκ°?
- ν΄λΉ λͺ¨λλ€μ λν μ μ ν ν μ€νΈλ₯Ό 5κ° μ΄μ μμ±νλκ°?
κ³Όμ μ ννκ³
ν
μ€νΈμ½λλ μΈμ μ§λ €κ³ ν΄λ λͺ
ννκ² ν΄λ΅μ΄ μλ λ¬Έμ κ° μλλΌμ μ΄λ €μ΄ κ² κ°μ΅λλ€. κ·Έλλ μ΄λ²μ£Όλ μμν μ€ μμλλ°... λ°νλΌλ 볡λ³μ΄...! μ°νν... κ·Έλλ μ΄μ§Έμ μ§Έ μ°λ¬μ§μ§ μκ³ μ λ§λ¬΄λ¦¬νμΌλκΉ λ κ±° κ² μ£ ...?
κ³Όμ μ€μ μ μκ² νμ μ£Όμ ...μ€μΌλμ ν¬μ¬λλΆμ μλ²½μ μ μ΄ μ£½κ² λλ°λ μ΄κ²¨λμ΅λλ€...(μ§μ§μ,,,λμκ² κ³ μμ΄κ°μ...ν¬μ΄μλ€)
κΈ°μ μ μ±μ₯
1. test() vs it()
Jestλ₯Ό ν΅ν΄μ ν
μ€νΈμ½λλ₯Ό μμ±ν λ testμ itμ΄ μ‘΄μ¬ν¨μ μκ²λμμ΅λλ€. μ΄ λμ μ΄λ»κ² λ€λ₯Έ κ²μΌκΉμ?
Jestμμ testμ itμ κΈ°λ₯μ μΌλ‘ μμ ν λμΌν ν¨μμ
λλ€. λ λ€ κ°λ³ ν
μ€νΈλ₯Ό μ μνλ λ° μ¬μ©λλ©°, λ¨μ§ alias κ΄κ³μΌ λΏμ
λλ€. μ°¨μ΄μ μ μ€νμΌκ³Ό κ°λ
μ±, κ·Έλ¦¬κ³ νμ 컨벀μ
μ μμ΅λλ€.
// μ΄ λμ μ νν κ°μ ν¨μλ₯Ό κ°λ¦¬ν΅λλ€
test('calculation works correctly', () => {
expect(2 + 2).toBe(4);
});
it('calculation works correctly', () => {
expect(2 + 2).toBe(4);
});
Jest 곡μ λ¬Έμμμ νμΈν μ μλ―μ΄, test λ©μλλ itμΌλ‘λ aliasedλμ΄ μμ΅λλ€. λ΄λΆμ μΌλ‘λ μμ ν λμΌν ν¨μλ₯Ό νΈμΆνλ―λ‘ μ±λ₯μ΄λ κΈ°λ₯μμ μ°¨μ΄λ μ ν μμ΅λλ€.
λν λ ν¨μ λͺ¨λ Jestμ λͺ¨λ κΈ°λ₯μ λμΌνκ² μ§μν©λλ€.
// λ λ€ timeout μ§μ
test('async operation', async () => { /* ... */ }, 10000);
it('async operation', async () => { /* ... */ }, 10000);
// λ λ€ .only, .skip λ±μ modifier μ§μ
test.only('focused test', () => { /* ... */ });
it.only('focused test', () => { /* ... */ });
// λ λ€ .eachλ‘ λ°μ΄ν° μ£Όλ ν
μ€νΈ μ§μ
test.each([[1, 2, 3]])('parameterized test', (a, b, expected) => {
expect(a + b).toBe(expected);
});
it.each([[1, 2, 3]])('parameterized test', (a, b, expected) => {
expect(a + b).toBe(expected);
});
What is the difference between 'it' and 'test' in Jest?μ λ°λ₯΄λ©΄, testμ itμ μ°¨μ΄λ₯Ό λ€μκ³Ό κ°μ΄ μ€λͺ
ν©λλ€.
describe('UserService', () => {
test('μ¬μ©μ μμ±μ΄ μ¬λ°λ₯΄κ² λμνλ€', () => {
const user = createUser('κΉμ² μ', 'kim@example.com');
expect(user.name).toBe('κΉμ² μ');
});
test('μλͺ»λ μ΄λ©μΌλ‘ μ¬μ©μ μμ± μ μλ¬κ° λ°μνλ€', () => {
expect(() => createUser('κΉμ² μ', 'invalid-email')).toThrow();
});
});
testλ μ°μ ν¨μλͺ
μμ²΄κ° "ν
μ€νΈ"λΌλ λͺ©μ μ λΆλͺ
ν νμ¬ μ§μ μ μ΄κ³ λͺ
νν©λλ€.
λ°λ©΄μ itμ BDD(Behavior Driven Development) μ€νμΌμ μμ°μ΄μ ννμ
λλ€.
describe('UserService', () => {
it('should create user with valid data', () => {
const user = createUser('κΉμ² μ', 'kim@example.com');
expect(user.name).toBe('κΉμ² μ');
});
it('should throw error when email is invalid', () => {
expect(() => createUser('κΉμ² μ', 'invalid-email')).toThrow();
});
});
μ μ½λμ²λΌ "it should..." ν¨ν΄μΌλ‘ μμ°μ΄μ κ°κΉμ΄ ννμ΄κ³ , μꡬμ¬νμ ν μ€νΈλ‘ μ§μ λ²μνκΈ° μ©μ΄ν©λλ€.
describe('Calculator', () => {
it('should add two numbers correctly', () => {
expect(add(2, 3)).toBe(5);
});
it('should handle negative numbers', () => {
expect(add(-1, 1)).toBe(0);
});
});
μμ΄μμλ itμ΄ μμ°μ€λ½κ² μ½νλλ€: "Calculator should add two numbers correctly"
describe('κ³μ°κΈ°', () => {
// μ΄μν νν
it('λ μ«μλ₯Ό μ¬λ°λ₯΄κ² λν΄μΌ νλ€', () => {
expect(add(2, 3)).toBe(5);
});
// μμ°μ€λ¬μ΄ νν
test('λ μ«μλ₯Ό μ¬λ°λ₯΄κ² λνλ€', () => {
expect(add(2, 3)).toBe(5);
});
});
νκ΅μ΄λ‘ ν
μ€νΈλ₯Ό μμ±ν λλ testκ° λ μμ°μ€λ¬μ΄ κ²½μ°κ° λ§μ΅λλ€.
// μΌκ΄μ± μλ μ¬μ© (Good)
describe('AuthService', () => {
test('λ‘κ·ΈμΈ μ±κ³΅ μ ν ν°μ λ°ννλ€', () => { /* ... */ });
test('μλͺ»λ λΉλ°λ²νΈλ‘ λ‘κ·ΈμΈ μ μλ¬κ° λ°μνλ€', () => { /* ... */ });
test('ν ν° λ§λ£ μ μλμΌλ‘ κ°±μ νλ€', () => { /* ... */ });
});
// νΌμ¬λ μ¬μ© (Bad)
describe('AuthService', () => {
test('λ‘κ·ΈμΈ μ±κ³΅ μ ν ν°μ λ°ννλ€', () => { /* ... */ });
it('μλͺ»λ λΉλ°λ²νΈλ‘ λ‘κ·ΈμΈ μ μλ¬κ° λ°μνλ€', () => { /* ... */ });
test('ν ν° λ§λ£ μ μλμΌλ‘ κ°±μ νλ€', () => { /* ... */ });
});
μ€μ νλ‘μ νΈμμλ test, it μ€ μ΄λ€ κ²μ μ¬μ©ν΄λ λμ§λ§, νμ μν©κ³Ό 컨벀μ
μ λ§κ² μ¬μ©νλ κ²μ΄ μ€μν©λλ€. μμ λ§νλ― testμ itμ μ°¨μ΄λ κ°λ
μ±μ μκΈ° λλ¬Έμ
λλ€.
κ²°κ΅ μ€μν κ²μ testλ itμ΄λκ° μλλΌ, μλ―Έμκ³ λͺ
νν ν
μ€νΈλ₯Ό μμ±νλ κ²μ΄λΌκ³ ν μ μμ΅λλ€.
μΆμ²
- https://jestjs.io/docs/api#testname-fn-timeout
- https://stackoverflow.com/questions/45778192/what-is-the-difference-between-it-and-test-in-jest
2. expect.hasAssertions()
Jestμ expect.hasAssertions()μ μ£Όλ‘ λΉλκΈ° ν
μ€νΈμμ assertionμ΄ μ€μ λ‘ μ€νλμλμ§ νμΈνκΈ° μν΄ μ¬μ©λ©λλ€. μ¬κΈ°μ λ§νλ assertionμ μ°λ¦¬κ° ν
μ€νΈλ₯Ό μ€νν λ μμ±νλ expect().toBe(), expect().toEqual() κ°μ κ²μ¦ μ½λλ€μ μλ―Έν©λλ€.
λΉλκΈ° ν μ€νΈμμ κ°μ₯ μνν μν© μ€ νλλ ν μ€νΈκ° ν΅κ³Όνμ§λ§ μ€μ λ‘λ μ무κ²λ κ²μ¦νμ§ μμ κ²½μ°μ λλ€.
function fetchUserData(userId, callback) {
if (userId > 0) {
api.getUser(userId).then(user => {
callback(user); // μ μ μΌμ΄μ€λ§ callback νΈμΆ
});
// .catch()κ° μμ΄μ μλ¬ μ callbackμ΄ νΈμΆλμ§ μμ
}
// userId <= 0μΈ κ²½μ° callbackμ΄ μ ν νΈμΆλμ§ μμ
}
// μ΄ ν
μ€νΈλ μλͺ» ν΅κ³Όν μ μμ
test('fetchUserData handles invalid userId', () => {
fetchUserData(-1, (user) => {
expect(user).toBeNull(); // μ΄ assertionμ΄ μ€νλμ§ μμ
});
// Jestλ ν
μ€νΈ ν¨μκ° λλλ©΄ μ±κ³΅μΌλ‘ κ°μ£Ό
});
ν μ€νΈλ₯Ό μ€ννλ©΄ 'fetchUserData handles invalid userId'μΈ κ²μ²λΌ 보μ΄μ§λ§ μ€μ λ‘λ assertionμ΄ μ€νλμ§ μμ΅λλ€. κ·Έλ κΈ° λλ¬Έμ ν΄λΉ ν μ€νΈλ κ±°μ§λ ν μ€νΈκ° λ©λλ€.
κ·Έλ κΈ° λλ¬Έμ μ΄λ₯Ό expect.hasAssertions()λ₯Ό μΆκ°νμ¬ μ€ννλ©΄ λ€μκ³Ό κ°μ΄ λμν μ μμ΅λλ€.
# hasAssertions()λ₯Ό μΆκ°ν ν μ€ν κ²°κ³Ό
β fetchUserData handles invalid userId (5ms)
Error: Expected at least one assertion to be called but received zero assertion calls.
μ΄λ κ² μμ±ν΄μ£Όλ©΄ ν μ€νΈκ° μ¬λ°λ₯΄κ² μ€ν¨ν©λλ€. callbackμ΄ νΈμΆλμ§ μμλ€λ κ²μ λͺ νν μ μ μμ΅λλ€.
3. λ―ΈμΉ 1μκ° λ°λμμ νΈλ¬λΈ μν - Fake Timersμ λΉλκΈ° μμ κ°μ νμ΄λ° μ΄μ
μ κ³Όμ μμ setupTest νμΌμ λ€μκ³Ό κ°μ΄ ꡬμ±λμ΄ μμμ΅λλ€.
/* msw */
export const server = setupServer(...handlers);
beforeAll(() => {
server.listen();
vi.useFakeTimers();
});
beforeEach(() => {
expect.hasAssertions();
vi.setSystemTime(new Date('2025-10-15'));
});
afterEach(() => {
server.resetHandlers();
vi.clearAllMocks();
vi.clearAllTimers();
});
afterAll(() => {
vi.resetAllMocks();
server.close();
vi.useRealTimers();
});
κ·Έλ°λ° κ³Όμ λ₯Ό νλ€λ³΄λ μ΄μν μ μ λ°κ²¬ν©λλ€.
μ΄κ±Έ 1μκ° λ°λμ μ½μ§μ νμλλ°μ. κ²°λ‘ μ μΌλ‘λ https://github.com/testing-library/dom-testing-library/issues/1218 μ λ΄μ©μ νμΈν΄λ³΄λ©΄ vi.useFakeTimers({ shouldAdvanceTime: true }); ν μ€λ§ μΆκ°ν΄μ£Όλ©΄ λλ λ¬Έμ μμ΅λλ€. μλ
μ΄κ² μ κ·Έλ°μ§μ λν΄μλ μΌλ¨ shouldAdvanceTime μ΅μ
μ λν΄μ μ΄ν΄ν΄μΌν κ² κ°μ΅λλ€.
https://github.com/vitest-dev/vitest/blob/main/docs/config/index.md#faketimersshouldadvancetime λ₯Ό 보면
shouldAdvanceTime μ΅μ
μ
- @sinonjs/fake-timersμκ² μ€μ μμ€ν μκ° λ³νμ λ°λΌ μλμΌλ‘ κ°μ§ μκ°μ μ¦κ°μν€λΌκ³ μ§μν©λλ€. (μ: μ€μ μκ°μ΄ 20ms λ³ν λλ§λ€ κ°μ§ μκ°λ 20msμ© μ¦κ°) λΌκ³ λμμλλ°μ.
λ§μ½ shouldAdvanceTimeκ° false(κΈ°λ³Έκ°)μΈ κ²½μ°μλ κ°λ°μκ° λͺ
μμ μΌλ‘ vi.advanceTimersByTime()μΌλ‘ μλ μ‘°μν΄μΌλ§ μκ°μ΄ νλ₯΄κ² λ©λλ€. λλ¬Έμ λΉλκΈ° λΌμ΄λΈλ¬λ¦¬(μ κ³Όμ μμμ findByText)μ νΈνμ± λ¬Έμ κ° λ°μν μ μμ΅λλ€.
λ°λλ‘ shouldAdvanceTimeκ° trueμΈ κ²½μ°μλ μμμ μΈκΈλ κ²μ²λΌ μ€μ μκ°μ΄ νλ₯΄λ©΄ κ°μ§ μκ°λ λ°λΌμ νλ₯΄κΈ° λλ¬Έμ λΉλκΈ° λΌμ΄λΈλ¬λ¦¬λ€κ³Ό μ μλν©λλ€.
λ€μ μ κ³Όμ λ‘ λμκ°μ
vi.useFakeTimers(); // shouldAdvanceTime: false (κΈ°λ³Έκ°)
await act(async () => {
setup(<App />);
vi.advanceTimersByTime(100); // β μ΄λκΉμ§λ§ μκ°μ΄ νλ¦
});
// νμ§λ§ μ΄ μμ λΆν° μκ°μ΄ μμ ν λ©μΆ€
await screen.findByText('μΌμ λ‘λ© μλ£!'); // β νμμμ!
shouldAdvanceTimeκ° falseμ΄λ λͺ
μμ μΌλ‘ μκ°μ λλ €μ£Όμ§ μμΌλ©΄ μκ°μ΄ νλ₯΄μ§ μκ³
// findByText λ΄λΆ ꡬ쑰 (λ¨μν)
const findByText = (text) => {
return new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
reject(new Error('νμμμ!')); // β μ΄ νμ΄λ¨Έκ° μ€νλμ§ μμ
}, 1000);
const poll = () => {
const element = queryByText(text);
if (element) {
resolve(element);
} else {
setTimeout(poll, 50); // β μ΄ νμ΄λ¨Έλ μ€νλμ§ μμ
}
};
poll();
});
};
findByTextκ° λ΄λΆμ μΌλ‘ νμ΄λ¨Έλ₯Ό μ€ννλλ°, μ΄λ νμ΄λ¨Έκ° λ©μΆ°μκ² λμ΄ νμμμμ΄ λ°μν...κ²μ΄μμ΅λλ€.
νμκ° λ°μ΄λ ν¬μνμΌλ λ€μλ²μ μ κΈ°μ΅νκ² μ§ νκ³ ... λμμ£Όμμ΅λλ€.
μ½λ νμ§
1. λΆνμν ν μ€νΈ
λΆνμν ν μ€νΈλΌκ³ μ¬κ²¨μ§λ λΆλΆμ λν΄μ μλ¨μ μ£Όμμ λ¬μλμμ΅λλ€.
// getDaysInMonthμ λν ν
μ€νΈ μ€
// NOTE: μ΄ ν
μ€νΈλ λΆνμν ν
μ€νΈλΌκ³ νλ¨νμμΌλ μ΅λν νμ¬ κΈ°λ₯μ μ μ¬νκ² κ΅¬ννμμ΅λλ€.
// ν
μ€νΈλ₯Ό λΆνμνλ€κ³ νλ¨ν μ΄μ λ, getDaysInMonth ν¨μκ° JavaScript λ΄μ₯ κΈ°λ₯ ν
μ€νΈμ μ§λμΉμ§ μλλ€κ³ νλ¨νκΈ° λλ¬Έμ
λλ€.
describe('μλͺ»λ μ μ
λ ₯ μ²λ¦¬', () => {
test('13μ μ
λ ₯ μ μ΄μ λ¬(12μ)μ μΌμμΈ 31μΌμ λ°ννλ€', () => {
expect(getDaysInMonth(2025, 13)).toBe(31);
});
test('0μ μ
λ ₯ μ μ΄μ λ¬(12μ)μ μΌμμΈ 31μΌμ λ°ννλ€', () => {
expect(getDaysInMonth(2025, 0)).toBe(31);
});
test('-1μ μ
λ ₯ μ μ΄μ λ¬(11μ)μ μΌμμΈ 30μΌμ λ°ννλ€', () => {
expect(getDaysInMonth(2025, -1)).toBe(30);
});
});
// getTimeErrorMessageμ λν ν
μ€νΈ μ€
describe('μ
λ ₯κ°μ΄ λΉμ΄μλ κ²½μ°', () => {
test('μμ μκ°μ΄ λΉμ΄μμ λ μλ¬ λ©μμ§λ₯Ό λ°ννμ§ μλλ€', () => {
const result = getTimeErrorMessage('', '15:00');
expect(result.startTimeError).toBeNull();
expect(result.endTimeError).toBeNull();
});
test('μ’
λ£ μκ°μ΄ λΉμ΄μμ λ μλ¬ λ©μμ§λ₯Ό λ°ννμ§ μλλ€', () => {
const result = getTimeErrorMessage('14:00', '');
expect(result.startTimeError).toBeNull();
expect(result.endTimeError).toBeNull();
});
// NOTE: ν΄λΉ ν
μ€νΈμ κ²½μ° μμμ λ ν
μ€νΈμμ κ²μ¦μ΄ κ°λ₯νλ―λ‘ λΆνμν ν
μ€νΈλ‘ μκ°λ©λλ€.
test('μμ μκ°κ³Ό μ’
λ£ μκ°μ΄ λͺ¨λ λΉμ΄μμ λ nullμ λ°ννλ€', () => {
const result = getTimeErrorMessage('', '');
expect(result.startTimeError).toBeNull();
expect(result.endTimeError).toBeNull();
});
});
μ κ° νλ¨ν 'λΆνμν¨'μ κΈ°μ€μ λ€μκ³Ό κ°μ΅λλ€.
- ꡬνμ²΄κ° μ΄λ―Έ λΈλΌμ°μ apiλ‘ μΆ©λΆν νμ κ°λ₯ν ν μ€νΈλ€.
- νμ ν μ€νΈμμ μΆ©λΆν κ²μ¦ κ°λ₯ν ν μ€νΈλ€.
- μμ ν μ€νΈμμ μΆ©λΆν κ²μ¦ λμλ€.
λλ¬Έμ μκΈ°ν ν μ€νΈλ€μ λΆνμνλ€κ³ νλ¨νμ΅λλ€.
2. ν μ€νΈλ₯Ό μμ±νλ κ·μΉ
μ κ° μ΄λ²μ ν μ€νΈλ₯Ό μμ±ν¨μ μμ΄μ μ€μ€λ‘ κ·μΉμ μΈμ ν μ€νΈλ₯Ό μμ±νμλλ°, κ·Έ κ·μΉμ λ€μκ³Ό κ°μ΅λλ€.
- Given When Then λ°©μμΌλ‘ ν μ€νΈλ₯Ό ꡬνν κ²
- describeκ° κ΅¬μ²΄μ μΌκ²
- λͺ¨λ ꡬνμ 컀λ²νμ§ μμλ λκ³ , νμν λΆλΆμ λν΄μ ν μ€νΈλ₯Ό μμ±ν κ²
- 무μλ―Ένν μ€νΈλ₯Ό μμ±νμ§ μμκ²
- μ μλ²μμ λν΄μλ ν μ€νΈκ° νλλ§ μκ³ , κ²½κ³κ°μ΄ μλ€λ©΄ κ·Έμ λν ν μ€νΈλ₯Ό μμ±ν κ². κ³Όλν μ€λ²μμ§λμ΄λ§ κΈμ§ μ΄μ μλ ν μ€νΈ 컀λ²λ¦¬μ§κ° λμ ν μ€νΈ === μ’μ ν μ€νΈλΌλ μκ°μ νμλλ° μ΄λ²μ ν μ€νΈλ₯Ό ꡬννλ©΄μλ μ΄λ€ κ²μ΄ μ’μ ν μ€νΈμΌκΉλ₯Ό λ μκ°νλ©΄μ ꡬννλ κ² κ°μ΅λλ€.(λ¬Όλ‘ μμ§λ μ½μ§ μμ΅λλ€)
3. QnAλ λ€μλ ννΈμ κΈ°λ°ν΄μ ν μ€νΈ ꡬννκΈ°
μ΄λΆλΆμ μ¬μ€ μ κ° κ°νΌλ₯Ό μ μ‘μκ² λ§λ μΆμ μκ°μ΄ λλλ°μ. μΌλ¨ μ κ° μ§λ¬Έλλ Έλ μ§λ¬Έ μ€
μ΄λ€ μ λ ν μ€νΈμ eventμ λν΄μ λͺ©λ°μ΄ν°λ₯Ό μμΉλ₯Ό κ°κΉκ² λ¬μΌν κΉμ? μλλ©΄ λ©λ¦¬ λλκ² μ’μκΉμ? κ°κΉμ΄ λλ€λ μλ―Έλ ν μ€νΈ νμΌ νΉμ κ° λͺ¨λ λ΄λΆμ λͺ©λ°μ΄ν°λ₯Ό λλκ², λ©λ¦¬ λλ€λ κ²μ λ°λ‘ fixtureλ₯Ό λμ΄ κ³΅ν΅νλ λ°μ΄ν°λ₯Ό μ¬μ©ν¨μ μκ°νμ΅λλ€. κ°κΉκ² λλ©΄ μλκ° λΆλͺ νλ€λ μ΄μ μ΄ μμ κ² κ°μλ°μβ¦κ·ΈμΉλ§ μΆνμ μΈν°νμ΄μ€κ° λ³κ²½λλ©΄ λͺ¨λ κ³³μμ λ³κ²½ν΄μ£Όμ΄μΌ ν΄μ κ΄λ¦¬νκΈ°κ° μ΄λ €μΈ μ μμκ±°κ°μ΅λλ€. μ΄λ€κ² λ λμ λ°©λ²μΌκΉμ?
λΌλ μ§λ¬Έμ ν΅ν΄μ, μΌλ°μ μΈ μΌμ΄μ€λ€μ λν΄μλ κ΄ν΅νλ fixtureλ₯Ό λκ³ νΉμν μΌμ΄μ€μ λν΄μλ§ νΉμν mockEventsλ₯Ό λλλ‘ λ³κ²½νμ΅λλ€.
κ°μ₯ μ κ²½μΌλ λΆλΆμ 'ν μ€νΈλ ν΅κ³Όνμ§λ§ μ€μ λ‘λ ν΅κ³Όνμ§ μμ μλ리μ€λ₯Ό μ°ΎμλΌ'λΌλ μ§μ§ μμκ»λΌ κ°μ λ§μμ΄μλλ°μ. κ·Έλ₯Ό κ³μ μ κ²½μ°λ©΄μ μ§λ €κ³ νλλ° μ΄λ€ λΆλΆμμ κ·Έ λΆλΆμ΄ νμν κ²μΈμ§ νμ μ΄ μ΄λ €μ μ΅λλ€.
κ·Έλ°λ° setupTests.tsλ₯Ό ꡬννλ€κ° hasAsstionsλΌλ ν¨μμ λν΄μ μμλ³΄κ² λμμλ 'μ£Όλ‘ λΉλκΈ° ν μ€νΈμμ assertionμ΄ μ€μ λ‘ μ€νλμλμ§ νμΈνκΈ° μν΄ μ¬μ©νλ€' λΌλ λΆλΆμ΄ μμκ³ QnAμμλ μ½μΉλκ»μ μ€λͺ ν λΆλΆμ΄ λ°μ΄ν°λ₯Ό λΆλ¬μ¬λ λΉλκΈ°μ μΌλ‘ λ°μ΄ν°λ₯Ό λΆλ¬μ€κ² λλλ° μ΄μ λν ν μ€νΈλ₯Ό μμ±ν΄μΌνλ€? λΌλ λ¬μμ€κ° μλ κ²μΌλ‘ κΈ°μ΅νλλ°μ. μ΄λ² ν μ€νΈμ κ²½μ°μ λ°μ΄ν°λ₯Ό λ‘λ©νκ³ λμ 'μΌμ λ‘λ© μλ£!'λΌλ μ€λ΅λ°κ° 보μ¬μ§κ² λμ΄ μ΄λ₯Ό ν΅ν©ν μ€νΈμμ κ²μ¦νλλ‘ νμ΅λλ€.
test('μλ³ λ·°μ μΌμ μ΄ μμΌλ©΄, μΌμ μ΄ νμλμ§ μμμΌ νλ€.', async () => {
// Given: ν΄λΉ μμ μΌμ μ΄ μλ μνλ‘ μ± μμ
vi.setSystemTime(new Date('2019-01-01'));
setup(<App />);
await screen.findByText('μΌμ λ‘λ© μλ£!');
// When: μλ³ λ·°λ₯Ό νμΈ (κΈ°λ³Έκ°)
// Then: κ²μ κ²°κ³Όκ° μλ€λ λ©μμ§κ° νμλμ΄μΌ ν¨
const eventList = within(screen.getByTestId('event-list'));
expect(eventList.getByText('κ²μ κ²°κ³Όκ° μμ΅λλ€.')).toBeInTheDocument();
});
μΌμ μ΄ μλ κ²½μ°μλ μΌμ μ΄ λ§κ·Έλλ‘ νμλμ§ μμΌλ―λ‘ μΌμ μ΄ μ 체μ μΌλ‘ λ‘λ©λμλμ§μ λν΄ νλ¨νκΈ°κ° μ΄λ €μ°λ―λ‘ μ΄λ₯Ό 'μΌμ λ‘λ© μλ£!' ν μ€νΈκ° λ°μνλμ§λ₯Ό νμ νλλ‘ κ΅¬ννκ³ κ·Έλ₯Ό ν΅ν΄μ μ’ λ μμ μ μΈ ν μ€νΈ ꡬνμ΄ κ°λ₯νλ€κ³ μκ°ν©λλ€.
κ°μκΈ° μμ±νλ©΄μ λ κΆκΈμ¦μΈλ°, κ·Έλ λ€λ©΄ λ§μ½ λ‘λ©νμ μ λ° ν μ€νΈ λ©μμ§κ° μλ κ²½μ°μλ μ΄λ»κ² λΉλκΈ°μ μΌλ‘ λ°μ΄ν°κ° λΆλ¬μμ‘μμ μ μ μμκΉμ? μμ±νλ©΄μ κΆκΈν΄μ§λλ€.
νμ΅ ν¨κ³Ό λΆμ
μ¬μ€μμ...
κ³Όμ λ₯Ό νλ€κ° λͺ¨λ¬ κ΄λ¦¬νλ λΆλΆμ λν΄μ useOverlayλΌλ ν μ λ§λ€μ΄λ³ΌκΉ μκ°νλλ°, κ·Έλ κ² λλ©΄ ν μ€νΈμ½λλ₯Ό μ§μΌν κ±°κ°λ€λ μλ°κ°μ΄ λ€μ΄ ν μ€μ overlay-kitλ₯Ό...μ€μΉνμ΅λλ€...γ γ μ΄λΆλΆμ Prμλ€κ° 'μ λ ν μ€νΈλ₯Ό μ λ΅μ μΌλ‘ λΌμ΄λΈλ¬λ¦¬μ μμνμ΅λλ€'νκ³ μΆμλλ° μ¬μ€ μ’ λ νμ€ν 보μ¦(?)μ΄ μμΌλ©΄ μ’κ² λ€κ³ μκ°νλ μ°°λμμ΅λλ€.
μ€νμ½μΉλμ΄ κ°μΈμ μΌλ‘ μ§λ¬Έλλ¦° λ΄μ©μ λ¬ΈμκΈμ μ¬λ¦¬μ ¨λλ°
μ΄ λΆλΆμ λν κ²μ¦μ html, reactκ° μ ꡬνμ΄ λμ΄μμΌλ©΄ μ λ κ±°λ€! μ λλ‘ λ³Ό μλ μλ λΆλΆμΌμλ
λΌκ³ νμ
μ μ΄? μ΄κ±° λνν
νμν λ¬Έμ₯μΈλ° μΆμ΄μ μ΄λλ₯Ό λμΉλ©΄ μλλ€κ³ μκ°ν΄ λ°λ‘ μ§λ¬Έμ λλ Έμ΅λλ€.
μλ νλ½λ³΄λ€ μ©μκ° λΉ λ₯΄κ³ , overlay-kit μ μ¨λ λλ μ¬μ€λ³΄λ©΄ μ§μ ꡬννκ³ ν
μ€νΈ μμ±νλΌλ λ§μμ νμ€ κ² κ°μμ γ
γ
μμ¬μ€λ³΄κ³ μ§ννλ €κ³ νλλ° κΈ°νλ μμ μ°Ύμμ€λλΌκ³ μ.
μΌλ¨ λΉμ΄λΌλ κΈμλ₯Ό 보μλ§μ 'μ΄λλ€' μΆμμ΅λλ€. λ€λ₯Έ κΈμλ λμ 보μ΄μ§λ μκ³ λ΄κ° λΌμ΄λΈλ¬λ¦¬λ₯Ό μ¬μ©ν κ²μ λν΄μ νλ½μ λκ° μλλΌ νλΉν μ΄μ λ₯Ό λ§λ€μ΄ μ£Όμ¬μ λ무 κ°μ¬νμ΅λλ€ γ
γ
μ κ° λ¬ΈμκΈμ μ§λ¬Έν λ΄μ© μ€ κ°μ₯ μ¬λ°λ μ§λ¬Έμ΄μλ€κ³ μκ°ν©λλ€. γ
γ
(μνΌ μ λ΅μ μ΄μμ~)
μνΌ λΉμ·νκ², μ¬μ€ μ€λ¬΄μ μΌλ‘ ν μ€νΈμ½λλ₯Ό κ³μ ν΄μ κ΄λ¦¬νλ μ‘°μ§μ΄ μλλΌλ©΄ ν μ€νΈ μ½λλ e2eκ° μλλλΌλ κ°λΉμΌ μ½λλΌκ³ μκ°μ΄ λλλ°μ. μ΄λ° λΆλΆμ λΌμ΄λΈλ¬λ¦¬μ μμν΄μ κ²μ¦ν λ‘νλ λ°©μμ΄ μ μλ―Ένλ€λ κ²μ μκ²λμλ€κ³ μκ°ν©λλ€.
λ μ΄λ²μ μ€μΌλμ΄ μλ €μ£Όμ https://www.npmjs.com/package/fast-check μ λΌμ΄λΈλ¬λ¦¬λ₯Ό μ΄λ² κ³Όμ μ μ¨λ³΄κ³ μΆμλλ° κ·Έλ¬μ§ λͺ»ν΄μ μ’ μμ¬μμ΄ λ¨μ΅λλ€. μ΄λΆλΆμ λλ€μ μΌλ‘ λ°μνλ μ«μμ λν κ²μ¦μ©λλ‘ μ¬μ©ν μ μμμ κ² κ°μλ°...μλ¬΄νΌ λ€μμ μ€λ¬΄μ μ μ©ν΄ λ³Ό μ μλ€λ©΄ μ¨λ΄μ§ν μνμ΄λΌκ³ μκ°ν©λλ€.
κ³Όμ νΌλλ°±
μ€νμ½μΉλκ»μ μ¨κ²¨λμΌμ ¨λ€λ μ΄μ€ν°μκ·Έ(?)λ€μ΄ λ무λ무 κΆκΈν©λλ€...μ κ° μλλ₯Ό μ λλ‘ νμ νλκ² λ§λκ²μΈμ§..λ무λ무λ무 κΆκΈν©λλ€... μ΄λ²μ£Ό λ°μ λ μλ €μ£Όμ ¨μΌλ©΄ μ’κ² μ΄μ ... π₯Ή
리뷰 λ°κ³ μΆμ λ΄μ©
useEventFormμ΄λΌλ μ κ° λ무 μ± μμ΄ κ±°λνκ² κ°μμ λ΄λΆμ μΌλ‘ μν μ λΆλ¦¬νκ² λμμ΅λλ€. κ·Έμ€μuseTimeValidationμ΄λΌλ ν μ λ§λ€μμ΅λλ€. μ΄ ν μ λ΄λΆμ μΌλ‘getTimeErrorMessageλ₯Ό μ°λλ° ν΄λΉ μ νΈμ μ΄λ―Έ μ λν μ€νΈκ° μ‘΄μ¬ν©λλ€. μΌλ¨ μ λ μ΄getTimeErrorMessageμ λν κ²μ¦μuseTimeValidationμμ νλκ² μλ―Έκ° μλ€κ³ μκ°νλλ°μ.
κ·Έλ λ€λ©΄ useTimeValidationμμ ν μμλ ν
μ€νΈλ ꡬνμ λν λΆλΆλ§ 보면 μν μ
λ°μ΄νΈ, μΈν°νμ΄μ€κ΄λ ¨, νΈλ€λ¬λ₯Ό μμ±νλ λΆλΆμ λν κ² μ λκ° λ κ±°κ°μλ° μ΄λ° λΆλΆμ λν κ²μ¦μ΄ μλ―Έκ° μλ κ²μΌκΉμ?
μ 리νμλ©΄ μ νΈν¨μμ λν ν μ€νΈκ° ꡬνλμ΄μκ³ , μ΄λ₯Ό λ¨μν μ΄μ©ν΄μ μνλ₯Ό κ΄λ¦¬νλ ν μ΄ μλ€κ³ νλ©΄ μ΄ ν μ λν ν μ€νΈκ° μ μλ―Ένλ€κ³ μκ°νμλμ? 'μ€λ₯ μνμμ μ μμ μΈ κ°μΌλ‘ λ³κ²½νλ©΄ μ€λ₯κ° μ΄κΈ°νλμ΄μΌ νλ€' μ κ°μ΄ μν μ λ°μ΄νΈ μ λλ κ²μ¦ν μ μκ² λ€λ μκ°μ΄ λλλ°, μ΄λκΉμ§ κ²μ¦μ ν΄μΌνλμ§ μ’ λͺ¨νΈν κ² κ°μ΅λλ€.
-
λ¨μν presentation μ»΄ν¬λνΈλ‘ λ·°μμν μ νλ μ»΄ν¬λνΈκ° μμλ propsλ‘ νΈλ€λ¬λ€μ΄ λμ΄μ¨λ€λ μ΄μ λ‘ μ΄λ€ μ΄λ²€νΈκ° λ°μνμλ μ΄λ²€νΈ νΈλ€λ¬λ₯Ό λͺ¨νΉν΄μ ν μ€νΈ νλκ² μλ―Έκ° μμκΉμ?
-
λ°μ΄ν°κ° μ λΆ λΉμ΄μ Έμλ μν©μΈ κ²½μ°μ λ‘λ©νμ 'μΌμ λ‘λ© μλ£!'μ κ°μ ν μ€νΈ λ©μμ§κ° μλ€λ©΄ μ΄λ»κ² λ°μ΄ν°κ° λΆλ¬μμ‘μμ μμ 차릴 μ μμκΉμ?
κ³Όμ νΌλλ°±
μλ νμΈμ μμλ! 7μ£Όμ°¨ κ³Όμ μ μ§νν΄μ£Όμ ¨λ€μ γ γ μ¬λ¬κ°μ§ μΌμ΄ κ²Ήμ³μ νμ κ·Ήνμ μ±λ¦°μ§(?)λ₯Ό νκ³ μλλ° κ·ΈλΌμλ λΆκ΅¬νκ³ λλ±λλ± μ ν΄λ΄λ λͺ¨μ΅μ΄ λ©μμΌλ©΄μλ κ±±μ λλ€μ γ γ λ무 무리νμ§ μμμΌλ©΄ μ’κ² μλλ€..
test() vs it()
μ¬μ€ μ΄μ λν΄ κΉκ² κ³ λ―Όν΄λ³Έμ μ΄ μμλλ°, μμ±ν΄μ£Όμ λ΄μ©μ λ³΄κ³ "μν! κ·Έλ ꡬλ!" λΌλ μκ°μ νμ΄μ! κ΅Ώκ΅Ώ
expect.hasAssertions()
μ΄κ²λ μ²μ 보λλ° λλΆμ μλ‘μ΄ APIλ₯Ό μμκ°λλ€ γ γ κ°μ¬ν΄μ
λ―ΈμΉ 1μκ° λ°λμμ νΈλ¬λΈ μν - Fake Timersμ λΉλκΈ° μμ κ°μ νμ΄λ° μ΄μ
γ γ γ μ λ 2κΈ° λ κ³Όμ μ루μ λ§λ€λ©΄μ λμΌν λ¬Έμ λ₯Ό κ²ͺμλλ°μ, 1μκ°λ§μ ν΄κ²°νμ ¨λ€λ!!! λ©μλ€μ..
κ°μκΈ° μμ±νλ©΄μ λ κΆκΈμ¦μΈλ°, κ·Έλ λ€λ©΄ λ§μ½ λ‘λ©νμ μ λ° ν μ€νΈ λ©μμ§κ° μλ κ²½μ°μλ μ΄λ»κ² λΉλκΈ°μ μΌλ‘ λ°μ΄ν°κ° λΆλ¬μμ‘μμ μ μ μμκΉμ? μμ±νλ©΄μ κΆκΈν΄μ§λλ€.
μ΄κ±΄ λ.. λ΄λΆ λ‘μ§μ΄ μ΄λ»κ² ꡬνλμλκ°μ λ°λΌ λ€λ₯΄λ€κ³ μκ°ν΄μ γ γ νΉμ ν¨μμ νΈμΆλ‘ νμΈν΄λ³Ό μλ μκ³ , νΉμ tanstack-queryλ₯Ό μ¬μ©νλ€κ³ νμ λ κ°μ λ‘ onSuccess νΉμ onError κ°μ μ΄λ²€νΈλ₯Ό λ°μμ μ£Όμ ν μλ μκ³ !?
useEventFormμ΄λΌλ μ κ° λ무 μ± μμ΄ κ±°λνκ² κ°μμ λ΄λΆμ μΌλ‘ μν μ λΆλ¦¬νκ² λμμ΅λλ€. κ·Έμ€μ useTimeValidationμ΄λΌλ ν μ λ§λ€μμ΅λλ€. μ΄ ν μ λ΄λΆμ μΌλ‘ getTimeErrorMessageλ₯Ό μ°λλ° ν΄λΉ μ νΈμ μ΄λ―Έ μ λν μ€νΈκ° μ‘΄μ¬ν©λλ€. μΌλ¨ μ λ μ΄ getTimeErrorMessageμ λν κ²μ¦μ useTimeValidationμμ νλκ² μλ―Έκ° μλ€κ³ μκ°νλλ°μ. κ·Έλ λ€λ©΄ useTimeValidationμμ ν μμλ ν μ€νΈλ ꡬνμ λν λΆλΆλ§ 보면 μν μ λ°μ΄νΈ, μΈν°νμ΄μ€κ΄λ ¨, νΈλ€λ¬λ₯Ό μμ±νλ λΆλΆμ λν κ² μ λκ° λ κ±°κ°μλ° μ΄λ° λΆλΆμ λν κ²μ¦μ΄ μλ―Έκ° μλ κ²μΌκΉμ? μ 리νμλ©΄ μ νΈν¨μμ λν ν μ€νΈκ° ꡬνλμ΄μκ³ , μ΄λ₯Ό λ¨μν μ΄μ©ν΄μ μνλ₯Ό κ΄λ¦¬νλ ν μ΄ μλ€κ³ νλ©΄ μ΄ ν μ λν ν μ€νΈκ° μ μλ―Ένλ€κ³ μκ°νμλμ? 'μ€λ₯ μνμμ μ μμ μΈ κ°μΌλ‘ λ³κ²½νλ©΄ μ€λ₯κ° μ΄κΈ°νλμ΄μΌ νλ€' μ κ°μ΄ μν μ λ°μ΄νΈ μ λλ κ²μ¦ν μ μκ² λ€λ μκ°μ΄ λλλ°, μ΄λκΉμ§ κ²μ¦μ ν΄μΌνλμ§ μ’ λͺ¨νΈν κ² κ°μ΅λλ€.
κΉκ² λ€μ΄κ°μ κ³ λ―Όν΄λ³΄μλ©΄.. νμν ꡬκ°λ§ μ λ³ν΄μ κ²μ¦νμ! λΌκ³ μ΄μΌκΈ°ν μ μκ² μ§λ§ μμ§ν κ·Έκ±Έ μ λ³νλ κ³Όμ μ΄ λ μ€λκ±Έλ¦°λ€κ³ μκ°ν΄μ γ γ μμΈλ¦¬ λ€ κ²μ¦ν΄λ²λ¦¬λκ² μνΈνμ§ μλ? λΌλ μκ°μ λλ€. μ λ κ·Έλ₯ λ€ κ²μ¦νλ νΈμ΄λλλ€.
λ¨μν presentation μ»΄ν¬λνΈλ‘ λ·°μμν μ νλ μ»΄ν¬λνΈκ° μμλ propsλ‘ νΈλ€λ¬λ€μ΄ λμ΄μ¨λ€λ μ΄μ λ‘ μ΄λ€ μ΄λ²€νΈκ° λ°μνμλ μ΄λ²€νΈ νΈλ€λ¬λ₯Ό λͺ¨νΉν΄μ ν μ€νΈ νλκ² μλ―Έκ° μμκΉμ?
μ κ° μ’ μ’ μ΄μΌκΈ° νμλλ°, μ λ μ»΄ν¬λνΈμ λν ν μ€νΈλ μλ―Έ μλ κ²½μ°κ° λ§λ€κ³ μκ°ν΄μ. μ»΄ν¬λνΈλ λ°μ΄ν°λ₯Ό νννλ μλ¨μ΄κ³ , κ·Έλ λ€λ©΄ λ°μ΄ν°μ λν΄ κ·Έλ¦¬κ³ λ°μ΄ν°λ₯Ό λ§λ€μ΄λ΄λ ν¨μμ λν΄ κ²μ¦ν μ μλ€λ©΄ μ»΄ν¬λνΈμ λν κ²μ¦λ μ΄λμ λλ λλκ² μλκΉ!? λΌκ³ μκ°ν©λλ€.
λ€λ§, μ»΄ν¬λνΈλ μΈν°λμ μ μ°κ²°νλ μλ¨μ΄ λ μ μκΈ° λλ¬Έμ μΈν°λμ μ΄ μ λ°μνλκ°!? μ λλ ν μ€νΈν΄λ³Ό μ μκ² μ£ γ γ μ΄ κ²½μ°μλ κ·Έλ₯ e2e ν μ€νΈλ₯Ό.... ν΄λ³΄λ©΄ μ΄λ¨κΉ!? λΌκ³ λ μ°κ²°μ΄ λλ€μ γ γ
λ°μ΄ν°κ° μ λΆ λΉμ΄μ Έμλ μν©μΈ κ²½μ°μ λ‘λ©νμ 'μΌμ λ‘λ© μλ£!'μ κ°μ ν μ€νΈ λ©μμ§κ° μλ€λ©΄ μ΄λ»κ² λ°μ΄ν°κ° λΆλ¬μμ‘μμ μμ 차릴 μ μμκΉμ?
μ΄μΌκΈ°λλ¦° κ² μ²λΌ λͺ© ν¨μλ₯Ό μ΄λ²€νΈμ λκ²¨μ€ λ€μμ λͺ© ν¨μκ° νΈμΆλλμ§ νμΈν΄λ³΄λκ±°μ£ . λ€λ§ μ΄κ±΄ μ΄λ€μμΌλ‘ μ½λκ° μμ±λμ΄μλμ§μ λ°λΌ λ¬λΌμ§ μ μλ€κ³ μκ°ν΄μ!