Skip to content

[3ํŒ€ ์ด๊ฐ€์€] Chapter ๐Ÿฆ 3-1. ํ”„๋ก ํŠธ์—”๋“œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ๐Ÿฆ#53

Open
tooth-is-silver wants to merge 26 commits intohanghae-plus:mediumfrom
tooth-is-silver:medium
Open

[3ํŒ€ ์ด๊ฐ€์€] Chapter ๐Ÿฆ 3-1. ํ”„๋ก ํŠธ์—”๋“œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ๐Ÿฆ#53
tooth-is-silver wants to merge 26 commits intohanghae-plus:mediumfrom
tooth-is-silver:medium

Conversation

@tooth-is-silver
Copy link

@tooth-is-silver tooth-is-silver commented Aug 21, 2025

๋‚œ์ด๋„์— ๋งž๋Š” ํ…œํ”Œ๋ฆฟ์„ ์„ ํƒํ•ด์„œ ์ž‘์„ฑํ•ด์ฃผ์„ธ์š”!


Medium

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

๊ธฐ๋ณธ๊ณผ์ œ

Medium

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

์งˆ๋ฌธ

Q. medium.useEventOperations.spec.ts > ์•„๋ž˜ enqueueSnackbarFn๊ณผ mock๊ณผ ์ด fn์€ ๋ฌด์—‡์„ ํ•ด์ค„๊นŒ์š”?

const enqueueSnackbarFn = vi.fn();

vi.mock('notistack', async () => {
  const actual = await vi.importActual('notistack');
  return {
    ...actual,
    useSnackbar: () => ({
      enqueueSnackbar: enqueueSnackbarFn,
    }),
  };
});

React Notistack - https://notistack.com/
notistack์ด๋ผ๋Š” React ์•Œ๋ฆผ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์—์„œ ๊ฐ€์ƒ์œผ๋กœ ๋™์ž‘์‹œํ‚ค๊ธฐ ์œ„ํ•œ ๋ชจํ‚น ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค.
vi.fn()์œผ๋กœ ์ƒ์„ฑ๋œ enqueueSnackbarFn์€ ์‹ค์ œ ์•Œ๋ฆผ์„ ํ™”๋ฉด์— ๋„์šฐ์ง€ ์•Š์œผ๋ฉด์„œ๋„ ํ•จ์ˆ˜ ํ˜ธ์ถœ ์—ฌ๋ถ€, ํ˜ธ์ถœ ํšŸ์ˆ˜, ์ „๋‹ฌ๋œ ๋ฉ”์‹œ์ง€ ๋“ฑ์„ ์ถ”์ ํ•  ์ˆ˜ ์žˆ๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.
vi.mock์„ ํ†ตํ•ด notistack ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ useSnackbar ํ›…๋งŒ ๊ฐ€์งœ ํ•จ์ˆ˜๋กœ ๊ต์ฒดํ•˜๊ณ , ๋‚˜๋จธ์ง€ ๊ธฐ๋Šฅ๋“ค์€ ์›๋ณธ์„ ๊ทธ๋Œ€๋กœ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค. ์ดํ›„ ํ…Œ์ŠคํŠธ ์ง„ํ–‰์‹œ enqueueSnackbarFn๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํ…Œ์ŠคํŠธ ์‹คํ–‰ ์ค‘ ์‹ค์ œ ํŒ์—…์ด ๋œจ์ง€ ์•Š์•„ ํ…Œ์ŠคํŠธ ์ง„ํ–‰์— ๋ฐฉํ•ด๋ฐ›์ง€ ์•Š์œผ๋ฉด์„œ๋„, expect๋ฅผ ์‚ฌ์šฉํ•ด ์•Œ๋ฆผ ๊ธฐ๋Šฅ์ด ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ํ˜ธ์ถœ๋˜์—ˆ๋Š”์ง€ ํ™•์ธ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

Q. medium.integration.spec.ts > ์—ฌ๊ธฐ์„œ Provider๋กœ ๋ฌถ์–ด์ฃผ๋Š” ๋™์ž‘์€ ์˜๋ฏธ์žˆ์„๊นŒ์š”? ์žˆ๋‹ค๋ฉด ์–ด๋–ค ์˜๋ฏธ์ผ๊นŒ์š”?

Provider ๋ž˜ํ•‘์€ ์‹ค์ œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ํ™˜๊ฒฝ๊ณผ ๋™์ผํ•œ ์ปจํ…์ŠคํŠธ๋ฅผ ํ…Œ์ŠคํŠธ์—์„œ ์ œ๊ณตํ•˜๊ธฐ ์œ„ํ•ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

  • ThemeProvider: MUI ์ปดํฌ๋„ŒํŠธ๋“ค์ด ์ผ๊ด€๋œ ํ…Œ๋งˆ๋ฅผ ์ ์šฉ๋ฐ›์„ ์ˆ˜ ์žˆ๋„๋ก ํ•จ
  • CssBaseline: ๋ธŒ๋ผ์šฐ์ €๋ณ„ ๊ธฐ๋ณธ ์Šคํƒ€์ผ ์ฐจ์ด๋ฅผ ์ œ๊ฑฐํ•˜์—ฌ ์ผ๊ด€๋œ ์‹œ์ž‘์  ์ œ๊ณต
  • SnackbarProvider: ์ „์—ญ ์•Œ๋ฆผ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜์—ฌ ์ปดํฌ๋„ŒํŠธ๋“ค์ด ์•Œ๋ฆผ์„ ํ‘œ์‹œํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•จ

์ด๋Ÿฌํ•œ Provider๋“ค ์—†์ด๋Š” ํ…Œ์ŠคํŠธ ์ค‘์ธ ์ปดํฌ๋„ŒํŠธ๋“ค์ด ํ•„์š”ํ•œ ์ปจํ…์ŠคํŠธ๋ฅผ ์ฐพ์ง€ ๋ชปํ•ด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๊ฑฐ๋‚˜ ์˜ˆ์ƒ๊ณผ ๋‹ค๋ฅด๊ฒŒ ๋™์ž‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Q. handlersUtils > ์•„๋ž˜ ์—ฌ๋Ÿฌ๊ฐ€์ง€ use ํ•จ์ˆ˜๋Š” ์–ด๋–ค ์—ญํ• ์„ ํ• ๊นŒ์š”? ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉ๋  ์ˆ˜ ์žˆ์„๊นŒ์š”?

๊ฐ ํ•จ์ˆ˜๋Š” ํ…Œ์ŠคํŠธ์šฉ Mock ์„œ๋ฒ„์—์„œ CREAT,UPDATE,DELETE ๊ธฐ๋Šฅ์„ ๋‹ด๋‹นํ•ฉ๋‹ˆ๋‹ค.

  • setupMockHandlerCreation: ์ƒˆ๋กœ์šด ์ด๋ฒคํŠธ๋ฅผ ์ƒ์„ฑํ•  ๋•Œ ์‚ฌ์šฉ. ๋‹จ์ผ ์ด๋ฒคํŠธ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์—ฌ๋Ÿฌ ์ด๋ฒคํŠธ๋ฅผ ์ง์ ‘ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์–ด, ์ดˆ๊ธฐ ๋ฐ์ดํ„ฐ ์„ค์ •์ด๋‚˜ ๋นˆ ์ƒํƒœ(์ผ์ •์ด ์—†๋Š” ์ƒํƒœ)์—์„œ์˜ ํ…Œ์ŠคํŠธ์— ํ™œ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • setupMockHandlerUpdating: ๊ธฐ์กด mockEvents ๋ฐ์ดํ„ฐ๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์ˆ˜์ • ์ž‘์—…์„ ์ฒ˜๋ฆฌ. ๊ธฐ์กด ์ผ์ • ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ๋Š” ์ƒํƒœ์—์„œ์˜ ๊ธฐ์กด ๋ฐ์ดํ„ฐ์˜ ์—…๋ฐ์ดํŠธ ํ…Œ์ŠคํŠธ์— ํ™œ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • setupMockHandlerDeletion: mockEvents์—์„œ ํŠน์ • ์ด๋ฒคํŠธ๋ฅผ ์‚ญ์ œ. ์‚ญ์ œ ํ›„์˜ ์ƒํƒœ ๋ณ€ํ™”๋ฅผ ํ™•์ธํ•˜๋Š” ํ…Œ์ŠคํŠธ์— ํ™œ์šฉํ•ฉ๋‹ˆ๋‹ค.

Q. setupTests.ts > ์™œ ์ด ์‹œ๊ฐ„์„ ์„ค์ •ํ•ด์ฃผ๋Š” ๊ฑธ๊นŒ์š”?

beforeEach(() => {
  expect.hasAssertions();
  vi.setSystemTime(new Date('2025-10-01'));
});

beforeEach์—์„œ ์ด ์„ค์ •๋“ค์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ชจ๋“  ํ…Œ์ŠคํŠธ๊ฐ€ ๋™์ผํ•œ ์กฐ๊ฑด์—์„œ ์‹คํ–‰๋˜๋„๋ก ์ผ๊ด€์„ฑ์„ ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค.

  • vi.setSystemTime: ์‹œ์Šคํ…œ ์‹œ๊ฐ„์„ ๊ณ ์ •ํ•˜์—ฌ Mock ๋ฐ์ดํ„ฐ์™€ ๋‚ ์งœ ๋น„๊ต ์‹œ ํ•ญ์ƒ ๋™์ผํ•œ ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์‹ค์ œ๋กœ ์‹œ๊ฐ„ ์„ค์ • ์—†์ด ํ…Œ์ŠคํŠธํ•˜๋ฉด ์‹คํ–‰ ๋‚ ์งœ์— ๋”ฐ๋ผ ๊ฒฐ๊ณผ๊ฐ€ ๋‹ฌ๋ผ์งˆ ์ˆ˜ ์žˆ์–ด ๋ถˆ์•ˆ์ •ํ•œ ํ…Œ์ŠคํŠธ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค. ์‹ค์ œ๋กœ ์ฑ•ํ„ฐ2 ์ง„ํ–‰์‹œ ํ™”์š”์ผ์ด ์•„๋‹Œ ๋‚ ์—๋Š” ํ…Œ์ŠคํŠธ๊ฐ€ ํ†ต๊ณผ๋˜์ง€ ์•Š๋Š” ์ด์Šˆ๊ฐ€ ๋ฐœ์ƒ๋์—ˆ์Šต๋‹ˆ๋‹ค.
  • expect.hasAssertions: ๋ชจ๋“  ํ…Œ์ŠคํŠธ์—์„œ ๋ฐ˜๋“œ์‹œ expect๋ฅผ ์‚ฌ์šฉํ•œ ๊ฒ€์ฆ์ด ์ด๋ฃจ์–ด์ง€๋„๋ก ๊ฐ•์ œํ•˜๋Š” ๊ทœ์น™์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. ๊ฒ€์ฆ ์—†์ด ํ†ต๊ณผํ•˜๋Š” ๋ฌด์˜๋ฏธํ•œ ํ…Œ์ŠคํŠธ๋ฅผ ์ œํ•œํ•ฉ๋‹ˆ๋‹ค.

์‹ฌํ™” ๊ณผ์ œ

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

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

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

ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ฒ˜์Œ ์ ‘ํ–ˆ๋Š”๋ฐ ์ด์ œ ๋งŽ์ด ์นœํ•ด์กŒ์Šต๋‹ˆ๋‹ค. ํŠนํžˆ ๋น„๋™๊ธฐ/๋™๊ธฐ ํ…Œ์ŠคํŠธ ๋ฐฉ์‹์˜ ์ฐจ์ด์ , API ์š”์ฒญ์„ Mock ๋ฐ์ดํ„ฐ๋กœ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•, ๊ทธ๋ฆฌ๊ณ  ๋‹ค์–‘ํ•œ ํ…Œ์ŠคํŠธ ๋ฉ”์„œ๋“œ๋“ค์˜ ์‚ฌ์šฉ๋ฒ•๊ณผ ์ฐจ์ด์ ์„ ์ดํ•ดํ•˜๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
toBe๋Š” ์ฐธ์กฐ ๋น„๊ต์™€ ์›์‹œ๊ฐ’ ๋น„๊ต์— ์‚ฌ์šฉํ•˜๊ณ , toEqual์€ ๊ฐ์ฒด์˜ ๋‚ด๋ถ€ ๋‚ด์šฉ๊นŒ์ง€ ๊นŠ์€ ๋น„๊ต๋ฅผ ํ•  ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค๋Š” ์ ์„ ๋ฐฐ์› ์Šต๋‹ˆ๋‹ค.
queryByTestId๋Š” ์š”์†Œ๊ฐ€ ์žˆ์„์ง€ ์—†์„์ง€ ๋ชจ๋ฅผ ๋•Œ ์‚ฌ์šฉํ•˜๋ฉฐ null์„ ๋ฐ˜ํ™˜ํ•˜๊ณ , getByTestId๋Š” ๋ฐ˜๋“œ์‹œ ์กด์žฌํ•  ๊ฒƒ์œผ๋กœ ์˜ˆ์ƒ๋˜๋Š” ์š”์†Œ์— ์‚ฌ์šฉํ•œ๋‹ค๋Š” ์ฐจ์ด์ ๋„ ์•Œ์•„๋ƒˆ์Šต๋‹ˆ๋‹ค.

ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ฒซ ๋งŒ๋‚จ์ธ๋ฐ ๋„ˆ๋ฌด ์žฌ๋ฐ‹๊ณ  ์–ด๋ ต๊ณ  ์‹ ๊ธฐํ–ˆ์Šต๋‹ˆ๋‹ค. ์ €ํ•œํ… ์œ ๋‹ˆ์ฝ˜๐Ÿฆ„ ๊ฐ™์€ ์กด์žฌ์˜€๋Š”๋ฐ ์‹ค์ œ๋กœ ๋งŒ๋‚˜์„œ ๋ฟ”๋„ ๋งŒ์ ธ๋ณด๊ณ  ๋ง๋„ ๊ฑธ์–ด๋ณด๊ณ  ์ƒ๊น€์ƒˆ๋ฅผ ๊ด€์ฐฐํ•ด๋ณธ ๋А๋‚Œ์ด์—ˆ์Šต๋‹ˆ๋‹ค.
๊ด€์ฐฐํ•ด๋ณธ ํ›„๊ธฐ๋ผ๊ณ  ํ•œ๋‹ค๋ฉด ์•„์ง์€ ์žฌ๋ฐ‹๊ณ  ํฅ๋ฏธ์ง„์ง„ํ•œ๋ฐ ์ฃผ๋ณ€ ์‚ฌ๋žŒ๋“ค์€ ๋‹ค๋“ค ์žฌ๋ฏธ์—†์–ดํ•ด์„œ ๋ฉ‹์ฉ์€ ๋А๋‚Œ์ž…๋‹ˆ๋‹ค. ์™œ ๋‹ค๋“ค ์žฌ๋ฏธ์—†๋Š”์ง€ ์œ ์ถ”ํ•ด๋ณด์ž๋ฉด ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋Š” ๊ธฐํš, UX์ ์ธ ์ธก๋ฉด์—์„œ ๊ณ ๋ คํ•ด์•ผํ•  ๊ฒƒ์ด ๋งŽ๊ณ  ๊ทธ ๋ถ€๋ถ„์„ ์ฝ”๋“œ๋กœ ํ’€์–ด์•ผ ํ•˜๋‹ค๋ณด๋‹ˆ UX์™€ ์นœํ•˜์ง€ ์•Š๋‹ค๋ฉด ์ฝ”๋“œ๋ฅผ ์งค ๋•Œ๋งˆ๋‹ค ๊ณ ๋  ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

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

๋…ธ๋ ฅํ•œ ๋ถ€๋ถ„

  • test description๋ฅผ ๊ธฐํš ์นœํ™”์ ์œผ๋กœ ํ’€์–ด์„œ ์ƒ์„ธํ•˜๊ฒŒ ์ ์œผ๋ ค๊ณ  ๋…ธ๋ ฅํ–ˆ์Šต๋‹ˆ๋‹ค.
  • timeValidation.ts์— ํ•จ์ˆ˜์— ์ „๋‹ฌ๋˜๋Š” ์ธ์ž๊ฐ€ string ์›์‹œ ํƒ€์ž…์ด๋‹ค๋ณด๋‹ˆ ์ข€ ๋” ๋ช…ํ™•ํ•œ ๋กœ์ง์ด ํ•„์š”ํ•˜๋‹ค๊ณ  ์ƒ๊ฐ๋˜์–ด ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋‹ค๊ฐ€ ์˜ˆ์™ธ์ฒ˜๋ฆฌ ๋กœ์ง์„ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค. (regex๋กœ string ํฌ๋ฉง ํ™•์ธ + ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ถ”๊ฐ€)

๋ฆฌํŒฉํ† ๋ง์„ ์ง„ํ–‰ํ•˜๋ฉด์„œ ์ˆœ์ˆ˜ํ•จ์ˆ˜์ด๊ฑฐ๋‚˜ ์ฝ”๋“œ๊ฐ€ ๊ฐ„๊ฒฐํ•˜๊ณ  ์งง์„์ˆ˜๋ก, ๊ทธ๋ฆฌ๊ณ  ์‘์ง‘๋„๊ฐ€ ๋†’๊ณ  ๊ฒฐํ•ฉ๋„๊ฐ€ ๋‚ฎ์€ ์ฝ”๋“œ์ผ์ˆ˜๋ก ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์„ฑ์ด ํ›จ์”ฌ ์ˆ˜์›”ํ•˜๋‹ค๋Š” ๊ฒƒ์„ ์ฒด๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค.
๋ฐ˜๋ฉด ํ†ตํ•ฉํ…Œ์ŠคํŠธ์ฒ˜๋Ÿผ ๊ด€๋ฆฌํ•ด์•ผ ํ•  ์ƒํƒœ๊ฐ€ ๋งŽ์•„์ง€๊ฑฐ๋‚˜ ๊ธฐ๋Šฅ์ด ๋ณต์žกํ•ด์งˆ์ˆ˜๋ก ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์˜ ๋ณต์žก๋„๋„ ํ•จ๊ป˜ ์ฆ๊ฐ€ํ•˜๋ฉฐ, ๊ณ ๋ คํ•ด์•ผ ํ•  ๋ถ€๋ถ„์ด ๋งŽ์•„์ ธ ๋†“์น˜๋Š” ๋ถ€๋ถ„์ด ๋ฐœ์ƒํ•  ๊ฐ€๋Šฅ์„ฑ๋„ ๋†’์•„์ง„๋‹ค๋Š” ์ ์„ ๊นจ๋‹ฌ์•˜์Šต๋‹ˆ๋‹ค.
ํ…Œ์ŠคํŠธ ๋ชฉ ๋ฐ์ดํ„ฐ ๊ด€๋ฆฌ์— ๋Œ€ํ•ด์„œ๋„ ๊ณ ๋ฏผํ•ด๋ณด์•˜์Šต๋‹ˆ๋‹ค. ๋ณ„๋„์˜ dummies.ts ํŒŒ์ผ๋กœ ๋ถ„๋ฆฌํ•˜๋Š” ๊ฒƒ์„ ๊ณ ๋ฏผํ–ˆ๋Š”๋ฐ, ์ค‘๋ณต ์‚ฌ์šฉ๋˜๋Š” ๋ถ€๋ถ„์ด ํฌ๊ฒŒ ์—†๊ณ  ๋ฐ์ดํ„ฐ์–‘์ด ๋งŽ์ง€ ์•Š์•„์„œ ํ•œ ํŒŒ์ผ ๋‚ด์—์„œ ํ…Œ์ŠคํŠธ๋ณ„๋กœ ํ•„์š”ํ•œ ๋”๋ฏธ๋ฐ์ดํ„ฐ๋ฅผ ํ™•์ธํ•˜๋Š” ๊ฒƒ์ด ๊ฐ€๋…์„ฑ ๋ฉด์—์„œ ๋” ์ข‹๋‹ค๊ณ  ํŒ๋‹จํ–ˆ์Šต๋‹ˆ๋‹ค.
์‹ค์ œ๋กœ handlerUtils.ts๋‚ด๋ถ€์— ๋ชฉ ๋ฐ์ดํ„ฐ๊ฐ€ ๋“ค์–ด๊ฐ€์žˆ์–ด ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์„ฑ์‹œ ๋งค๋ฒˆ ํ•ด๋‹น ํŒŒ์ผ๋กœ ์ด๋™ํ•˜์—ฌ ๋ชฉ ๋ฐ์ดํ„ฐ๋ฅผ ํ™•์ธํ•˜๋ฉด์„œ ์ง„ํ–‰ํ–ˆ์—ˆ์Šต๋‹ˆ๋‹ค.

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

๊ฐ€์žฅ ํฐ ๋ฐฐ์›€์€ TDD(Test-Driven Development)์˜ ์ค‘์š”์„ฑ์„ ๋А๋‚€ ๊ฒƒ ์ž…๋‹ˆ๋‹ค. UI๋ฅผ ๊ธฐ์ค€์œผ๋กœ ๋‚˜์ค‘์— ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ์€ ๊ณ ๋ฏผ ํ•  ๊ฒƒ์ด ๋งŽ์•„ ๋„ˆ๋ฌด ์–ด๋ ค์› ๊ณ , ์™„์„ฑ๋œ ๊ธฐ๋Šฅ์— ๋Œ€ํ•ด ํ…Œ์ŠคํŠธ๋ฅผ ๋งŒ๋“ค๋‹ค ๋ณด๋‹ˆ ๋†“์น˜๋Š” ๋ถ€๋ถ„๋“ค์ด ์ƒ๊ฒผ์Šต๋‹ˆ๋‹ค.
ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ๋จผ์ € ์ž‘์„ฑํ•˜๊ณ  ๊ทธ์— ๋งž์ถฐ ๊ฐœ๋ฐœ์„ ์ง„ํ–‰ํ•˜๋Š” TDD ๋ฐฉ์‹์ด ํ›จ์”ฌ ํšจ์œจ์ ์ผ ๊ฒƒ ๊ฐ™๋‹ค๋Š” ์ƒ๊ฐ์ด ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. ํŠนํžˆ ์ƒ์„ธํ•œ UX ๊ธฐํš์„œ๋ฅผ ๊ธฐ์ค€์œผ๋กœ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•œ๋‹ค๋ฉด ํ›จ์”ฌ ์ฒด๊ณ„์ ์ด๊ณ  ์™„์„ฑ๋„ ๋†’์€ ํ…Œ์ŠคํŠธ๊ฐ€ ๊ฐ€๋Šฅํ•  ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.
๋˜ํ•œ ๊ฐœ๋ฐœ์ž๋“ค์ด ์™œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์„ฑ์„ ์‹ซ์–ดํ•˜๋Š”์ง€๋„ ์ดํ•ดํ•˜๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ด์ „ ํšŒ์‚ฌ์—์„œ ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ์ž๊ฐ€ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์˜ ์ค‘์š”์„ฑ์„ ์•Œ๊ณ  ํ™€๋กœ ๋„์ž…ํ–ˆ์œผ๋‚˜, ํ”„๋กœ๋•ํŠธ ์ฝ”๋“œ๊ฐ€ ํด๋ฆฐํ•˜์ง€ ์•Š์•„ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ๋ชจ๋‘ ํ†ต๊ณผํ–ˆ๋Š”๋ฐ๋„ ๋ถˆ๊ตฌํ•˜๊ณ  ํฌ๊ฒŒ ์—๋Ÿฌ๊ฐ€ ํ„ฐ์ง„ ์ ์ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ํ”„๋กœ๋•ํŠธ๋ฅผ ๋น ๋ฅด๊ฒŒ ๊ฐœ๋ฐœํ•œ ํ›„์— ๋งŒ๋“œ๋Š” ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋Š” ๋„์ž… ์˜๋ฏธ๊ฐ€ ํฌ์„๋  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ด ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์™„๋ฒฝ์— ๊ฐ€๊น๊ฒŒ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด์„œ๋Š” ์ƒ๋‹นํ•œ ์‹œ๊ฐ„ ํˆฌ์ž๊ฐ€ ํ•„์š”ํ•˜๋‹ค๋Š” ๊ฒƒ์„ ๊ณผ์ œํ•˜๋ฉด์„œ ๋˜ ๋‹ค์‹œ ๋А๊ผˆ์Šต๋‹ˆ๋‹ค.

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

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

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

ํ…Œ์ŠคํŠธ ๋”๋ฏธ๋ฐ์ดํ„ฐ ๊ด€๋ฆฌ ๊ณ ๋ฏผ
ํ˜„์žฌ๋Š” ๊ฐ ํ…Œ์ŠคํŠธ ํŒŒ์ผ ๋‚ด์—์„œ ๋”๋ฏธ๋ฐ์ดํ„ฐ๋ฅผ ๊ด€๋ฆฌํ•˜๊ณ  ์žˆ๋Š”๋ฐ, ํ”„๋กœ์ ํŠธ ๊ทœ๋ชจ๊ฐ€ ์ปค์งˆ ๋•Œ๋ฅผ ๋Œ€๋น„ํ•œ ํšจ๊ณผ์ ์ธ ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ ๊ด€๋ฆฌ ๋ฐฉ๋ฒ•์ด ์žˆ๋Š”์ง€ ๊ถ๊ธˆํ•ฉ๋‹ˆ๋‹ค. ์–ธ์ œ ๋ถ„๋ฆฌํ•˜๊ณ  ์–ด๋–ค ๊ธฐ์ค€์œผ๋กœ ๊ตฌ์กฐํ™”ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์„๊นŒ์š”?
์ด์ „ ๋ฉ˜ํ† ๋ง๋•Œ ์ฝ”์น˜๋‹˜๊ป˜์„œ ์‹ค๋ฌด์—์„œ ๋”๋ฏธ๋ฐ์ดํ„ฐ๋ฅผ ๋”ฐ๋กœ ๋ถ„๋ฆฌํ•ด์„œ ์‚ฌ์šฉํ•˜์‹œ๋Š” ๊ฒƒ์„ ๋ณด์—ฌ์ฃผ์…จ๋Š”๋ฐ ๊ทธ ๋ฐฉ๋ฒ•์ด ์‹ค์ œ๋กœ ๊ดœ์ฐฎ์€ ๋ฐฉ๋ฒ•์ด์–ด์„œ ์ฑ„ํƒ๋๋Š”์ง€ ์•„๋‹ˆ๋ฉด ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•๋„ ์žˆ์—ˆ๋Š”๋ฐ ๊ทธ๋ ‡๊ฒŒ ๋ฐ”๋€Œ๊ฒŒ ๋œ๊ฑด์ง€๋„ ๊ถ๊ธˆํ•ฉ๋‹ˆ๋‹ค.

ํ†ตํ•ฉํ…Œ์ŠคํŠธ vs ๋‹จ์œ„ํ…Œ์ŠคํŠธ ๋น„์œจ๊ณผ ์ „๋žต
ํ†ตํ•ฉํ…Œ์ŠคํŠธ๊ฐ€ ๋‹จ์œ„ํ…Œ์ŠคํŠธ๋ณด๋‹ค ์ž‘์„ฑํ•˜๊ธฐ ์–ด๋ ต๊ณ  ๋ณต์žกํ•˜๋‹ค๋Š” ๊ฒƒ์„ ๊ฒฝํ—˜ํ–ˆ์Šต๋‹ˆ๋‹ค. ์‹ค์ œ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ์ด ๋‘˜์˜ ์ ์ ˆํ•œ ๋น„์œจ์ด๋‚˜ ์šฐ์„ ์ˆœ์œ„๋ฅผ ์–ด๋–ป๊ฒŒ ์ •ํ•˜๋Š”์ง€, ๊ทธ๋ฆฌ๊ณ  ํ†ตํ•ฉํ…Œ์ŠคํŠธ ์—†์ด ๋‹จ์œ„ํ…Œ์ŠคํŠธ๋งŒ ์ง„ํ–‰ํ•  ๊ฒฝ์šฐ ํฌ๊ฒŒ ๋ฌธ์ œ๊ฐ€ ์žˆ์„๊นŒ์š”?
ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋ฉด์„œ ์ผ๋ถ€ ๋‹จ์œ„ํ…Œ์ŠคํŠธ์˜ ๋‚ด์šฉ์ด ํ†ตํ•ฉํ…Œ์ŠคํŠธ์—๋„ ๋น„์Šทํ•˜๊ฒŒ ์‚ฌ์šฉ๋˜์—ˆ๋Š”๋ฐ ๋‹จ์œ„ํ…Œ์ŠคํŠธ์—์„œ ์ตœ๋Œ€ํ•œ ์ปค๋ฒ„ํ•˜๊ณ  ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ์—์„œ๋Š” ๋”ฑ! ๋‹จ์ผ ui์ ์ธ ๋ถ€๋ถ„๋งŒ ํ…Œ์ŠคํŠธํ•˜๋ฉด ๋˜์ง€ ์•Š์„๊นŒ..ํ•˜๊ณ  ์ƒ์ƒํ•ด๋ดค๋Š”๋ฐ ๋งž๋Š”์ง€ ๋ชจ๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค!


Copy link

@susmisc14 susmisc14 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

์•ˆ๋…•ํ•˜์„ธ์š”! AI ์ฝ”๋“œ ๋ฆฌ๋ทฐ์–ด์ž…๋‹ˆ๋‹ค. ๐Ÿค–
[FE] 7์ฃผ์ฐจ ๊ณผ์ œ: ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ๊ธฐ์ดˆ Pull Request์— ๋Œ€ํ•œ ๋ฆฌ๋ทฐ๋ฅผ ์™„๋ฃŒํ–ˆ์–ด์š”. ํ•จ๊ป˜ ์ฝ”๋“œ๋ฅผ ๋” ๋ฐœ์ „์‹œ์ผœ ๋ณผ๊นŒ์š”?

Note

โœจ ์ด๋ฒˆ PR ํ•œ ์ค„ ์š”์•ฝ
๊ฑฐ๋Œ€ํ•œ App ์ปดํฌ๋„ŒํŠธ๋ฅผ ํ…Œ์ŠคํŠธ ์šฉ์ด์„ฑ์ด ๋†’์€ ์ปดํฌ๋„ŒํŠธ์™€ ํ›…์œผ๋กœ ์„ฑ๊ณต์ ์œผ๋กœ ๋ถ„๋ฆฌํ•˜๊ณ , ๊ฒฌ๊ณ ํ•œ ๋‹จ์œ„/ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ๋ฅผ ๊ตฌ์ถ•ํ•˜์—ฌ ์ฝ”๋“œ์˜ ์‹ ๋ขฐ์„ฑ๊ณผ ์œ ์ง€๋ณด์ˆ˜์„ฑ์„ ํฌ๊ฒŒ ํ–ฅ์ƒ์‹œ์ผฐ์Šต๋‹ˆ๋‹ค.


๐ŸŽฏ ํ•ต์‹ฌ ๋ฆฌ๋ทฐ ์š”์•ฝ

๊ฐ€์žฅ ์ค‘์š”ํ•œ ๊ฐœ์„  ํฌ์ธํŠธ๋ถ€ํ„ฐ ๋น ๋ฅด๊ฒŒ ํ™•์ธํ•ด ๋ณด์„ธ์š”.

  • ์•„ํ‚คํ…์ฒ˜ ๊ฐœ์„ : 'God Component'์˜€๋˜ App.tsx๋ฅผ ๋ชฉ์ ์— ๋”ฐ๋ผ ์ปดํฌ๋„ŒํŠธ์™€ ํ›…์œผ๋กœ ๋ถ„๋ฆฌํ•œ ๊ฒƒ์€ ์ด๋ฒˆ ๊ณผ์ œ์˜ ๊ฐ€์žฅ ํฐ ์„ฑ๊ณผ์ž…๋‹ˆ๋‹ค. ์ด๋Š” ํ…Œ์ŠคํŠธ ์šฉ์ด์„ฑ์„ ๊ทน์ ์œผ๋กœ ํ–ฅ์ƒ์‹œ์ผฐ์Šต๋‹ˆ๋‹ค.
  • ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ ๊ด€๋ฆฌ: ํ…Œ์ŠคํŠธ์˜ ๊ฐ€๋…์„ฑ๊ณผ ์œ ์ง€๋ณด์ˆ˜์„ฑ์„ ํ•œ ๋‹จ๊ณ„ ๋” ๋Œ์–ด์˜ฌ๋ฆด ์ˆ˜ ์žˆ๋Š” 'ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ ํŒฉํ† ๋ฆฌ(Factory) ํŒจํ„ด' ๋„์ž…์„ ์ œ์•ˆํ•ฉ๋‹ˆ๋‹ค.
  • ํ…Œ์ŠคํŠธ ์ „๋žต: ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ์™€ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ์˜ ์ด์ƒ์ ์ธ ๊ท ํ˜•์— ๋Œ€ํ•œ ๊ณ ๋ฏผ์„ 'ํ…Œ์ŠคํŒ… ํŠธ๋กœํ”ผ(The Testing Trophy)' ๊ฐœ๋…์„ ํ†ตํ•ด ํ•ด๊ฒฐํ•ด ๋ณด์„ธ์š”.
  • ํ…Œ์ŠคํŠธ ๊ฐ€๋…์„ฑ: it.skip๊ณผ ๊ฐ™์ด ๋น„ํ™œ์„ฑํ™”๋œ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋Š” ๊ทธ ์ด์œ ๋ฅผ ๋ช…ํ™•ํžˆ ๋ฌธ์„œํ™”ํ•˜์—ฌ ํŒ€์˜ ํ˜ผ๋ž€์„ ๋ฐฉ์ง€ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

๐Ÿ” ์ƒ์„ธ ๋ฆฌ๋ทฐ

๊ฐ ํ•ญ๋ชฉ์— ๋Œ€ํ•œ ๊ตฌ์ฒด์ ์ธ ์„ค๋ช…๊ณผ ์ฝ”๋“œ ์˜ˆ์‹œ๋ฅผ ์ค€๋น„ํ–ˆ์–ด์š”.

1. ๊ฒฝ์ด๋กœ์šด ๋ฆฌํŒฉํ† ๋ง: God Component์˜ ํ•ด์ฒด์™€ ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅํ•œ ์•„ํ‚คํ…์ฒ˜ ๊ตฌ์ถ•

  • ๐Ÿ‘ ์ข‹์€ ์ : App.tsx ํ•˜๋‚˜์— ๋ชจ๋“  ์ƒํƒœ, ๋กœ์ง, UI๊ฐ€ ๋’ค์„ž์—ฌ ์žˆ๋˜ ๊ตฌ์กฐ๋ฅผ useEventHandlers, useCalendarView ๊ฐ™์€ ์ปค์Šคํ…€ ํ›…๊ณผ EventForm, CalendarView ๋“ฑ์˜ ํ”„๋ ˆ์  ํ…Œ์ด์…”๋„ ์ปดํฌ๋„ŒํŠธ๋กœ ์™„๋ฒฝํ•˜๊ฒŒ ๋ถ„๋ฆฌํ•ด๋‚ด์…จ์Šต๋‹ˆ๋‹ค. ์ด ๊ฒฐ์ • ๋•๋ถ„์— ๊ฐ ๋ชจ๋“ˆ์„ ๋…๋ฆฝ์ ์œผ๋กœ ํ…Œ์ŠคํŠธํ•˜๊ณ  ์œ ์ง€๋ณด์ˆ˜ํ•˜๋Š” ๊ฒƒ์ด ๋งค์šฐ ์šฉ์ดํ•ด์กŒ์Šต๋‹ˆ๋‹ค. ์…€ํ”„ ํšŒ๊ณ ์—์„œ "์‘์ง‘๋„๊ฐ€ ๋†’๊ณ  ๊ฒฐํ•ฉ๋„๊ฐ€ ๋‚ฎ์€ ์ฝ”๋“œ์ผ์ˆ˜๋ก ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์„ฑ์ด ํ›จ์”ฌ ์ˆ˜์›”ํ•˜๋‹ค"๊ณ  ๋ง์”€ํ•˜์‹  ๋ถ€๋ถ„์„ ์ฝ”๋“œ๋กœ ์ง์ ‘ ์ฆ๋ช…ํ•ด๋‚ด์…จ๋„ค์š”.
  • ๐Ÿ’ก ๊ฐœ์„  ์ œ์•ˆ: ํ˜„์žฌ ๊ตฌ์กฐ์˜ ์šฐ์ˆ˜์„ฑ์„ ๋ช…ํ™•ํžˆ ์ดํ•ดํ•˜๊ณ , ์•ž์œผ๋กœ๋„ ์ด๋Ÿฐ ์„ค๊ณ„๋ฅผ ์œ ์ง€ํ•˜๊ธฐ ์œ„ํ•ด '์‘์ง‘๋„'์™€ '๊ฒฐํ•ฉ๋„'์˜ ๊ฐœ๋…์„ ๋‹ค์‹œ ํ•œ๋ฒˆ ์งš์–ด๋ณด๋Š” ๊ฒƒ์„ ์ถ”์ฒœํ•ฉ๋‹ˆ๋‹ค.
  • ๐Ÿค” ์ด๋ ‡๊ฒŒ ์ œ์•ˆํ•˜๋Š” ์ด์œ :
    • ๋†’์€ ์‘์ง‘๋„ (High Cohesion): useEventOperations๋Š” ์„œ๋ฒ„ ํ†ต์‹ ๋งŒ, useEventForm์€ ํผ ์ƒํƒœ ๊ด€๋ฆฌ๋งŒ ์ฑ…์ž„์ง‘๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋‚˜์˜ ๋ชจ๋“ˆ์ด ํ•˜๋‚˜์˜ ์ฑ…์ž„๋งŒ ๊ฐ–๊ฒŒ ๋˜๋ฉด ์ฝ”๋“œ๋ฅผ ์ดํ•ดํ•˜๊ณ  ํ…Œ์ŠคํŠธํ•˜๊ธฐ๊ฐ€ ๋งค์šฐ ์‰ฌ์›Œ์ง‘๋‹ˆ๋‹ค.
    • ๋‚ฎ์€ ๊ฒฐํ•ฉ๋„ (Low Coupling): EventList ์ปดํฌ๋„ŒํŠธ๋Š” ์ด์ œ events ๋ฐ์ดํ„ฐ๊ฐ€ ์–ด๋–ป๊ฒŒ ์ƒ์„ฑ๋˜๊ณ  ๊ด€๋ฆฌ๋˜๋Š”์ง€ ์•Œ ํ•„์š” ์—†์ด, ๊ทธ์ € props๋กœ ๋ฐ›์€ ๋ฐ์ดํ„ฐ๋ฅผ ํ™”๋ฉด์— ๊ทธ๋ฆฌ๊ณ  ์ƒํ˜ธ์ž‘์šฉ(์ˆ˜์ •/์‚ญ์ œ)์„ ๋ถ€๋ชจ์—๊ฒŒ ์•Œ๋ฆฌ๊ธฐ๋งŒ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. ์ด๋Š” EventList์˜ ์žฌ์‚ฌ์šฉ์„ฑ์„ ๋†’์ด๊ณ , App.tsx์˜ ๋ณ€๊ฒฝ์ด EventList์— ๋ฏธ์น˜๋Š” ์˜ํ–ฅ์„ ์ตœ์†Œํ™”ํ•ฉ๋‹ˆ๋‹ค.
  • ๐Ÿ’ป ์•„ํ‚คํ…์ฒ˜ ๋น„๊ต (AS-IS vs TO-BE)

AS-IS: ํ•˜๋‚˜์˜ ๊ฑฐ๋Œ€ํ•œ App ์ปดํฌ๋„ŒํŠธ

graph TD
    A[App.tsx]
    A --> B{State (useState)}
    A --> C{API Logic (fetch, etc.)}
    A --> D{UI Rendering (JSX)}
    A --> E{Event Handlers}
    A --> F{Side Effects (useEffect)}
Loading

TO-BE: ์—ญํ• ๊ณผ ์ฑ…์ž„์— ๋”ฐ๋ผ ๋ถ„๋ฆฌ๋œ ๊ตฌ์กฐ

graph TD
    subgraph Components
        D1[EventForm]
        D2[CalendarView]
        D3[EventList]
    end
    subgraph Hooks
        B1[useEventForm]
        B2[useCalendarView]
        C1[useEventOperations]
        E1[useEventHandlers]
    end
    A[App.tsx] --> B1 & B2 & C1 & E1
    A --> D1 & D2 & D3
    B1 --> D1
    B2 & C1 --> D2
    C1 --> D3
Loading

์ด์ฒ˜๋Ÿผ ์—ญํ• ์— ๋”ฐ๋ผ ๋ชจ๋“ˆ์„ ๋ถ„๋ฆฌํ•˜๋Š” ๊ฒƒ์€ ํ…Œ์ŠคํŠธ ์šฉ์ด์„ฑ์„ ๋†’์ด๋Š” ์ฒซ๊ฑธ์Œ์ด์ž ๊ฐ€์žฅ ์ค‘์š”ํ•œ ๋‹จ๊ณ„์ž…๋‹ˆ๋‹ค. ํ›Œ๋ฅญํ•œ ๋ฆฌํŒฉํ† ๋ง์ž…๋‹ˆ๋‹ค!

2. ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ ๊ด€๋ฆฌ: ๋” ์ด์ƒ ํ•˜๋“œ์ฝ”๋”ฉ์€ ๊ทธ๋งŒ, 'ํŒฉํ† ๋ฆฌ ํŒจํ„ด'์œผ๋กœ ์ง„ํ™”ํ•˜๊ธฐ

  • ๐Ÿ‘ ์ข‹์€ ์ : ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋งˆ๋‹ค ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ์ง์ ‘ ์ •์˜ํ•˜์—ฌ ๊ฐ ํ…Œ์ŠคํŠธ์˜ ๋…๋ฆฝ์„ฑ์„ ํ™•๋ณดํ•˜๋ ค๋Š” ๋…ธ๋ ฅ์ด ๋ณด์ž…๋‹ˆ๋‹ค. "ํ…Œ์ŠคํŠธ๋ณ„๋กœ ํ•„์š”ํ•œ ๋”๋ฏธ๋ฐ์ดํ„ฐ๋ฅผ ํ™•์ธํ•˜๋Š” ๊ฒƒ์ด ๊ฐ€๋…์„ฑ ๋ฉด์—์„œ ๋” ์ข‹๋‹ค๊ณ  ํŒ๋‹จํ–ˆ๋‹ค"๋Š” ๊ณ ๋ฏผ์€ ๋งค์šฐ ์ค‘์š”ํ•˜๊ณ  ์ข‹์€ ์ถœ๋ฐœ์ ์ž…๋‹ˆ๋‹ค.

  • ๐Ÿ’ก ๊ฐœ์„  ์ œ์•ˆ: ํ”„๋กœ์ ํŠธ๊ฐ€ ์ปค์งˆ ๊ฒƒ์„ ๋Œ€๋น„ํ•ด, ๋ฐ˜๋ณต์ ์ธ ๋ชจํ‚น(Mocking) ๋ฐ์ดํ„ฐ ์ƒ์„ฑ์„ ์ถ”์ƒํ™”ํ•˜๋Š” ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ ํŒฉํ† ๋ฆฌ(Test Data Factory) ํŒจํ„ด์„ ๋„์ž…ํ•ด ๋ณด์„ธ์š”. ์ด ํŒจํ„ด์€ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์˜ ๊ฐ€๋…์„ฑ๊ณผ ์œ ์ง€๋ณด์ˆ˜์„ฑ์„ ๊ทน์ ์œผ๋กœ ํ–ฅ์ƒ์‹œํ‚ต๋‹ˆ๋‹ค.

  • ๐Ÿค” ์ด๋ ‡๊ฒŒ ์ œ์•ˆํ•˜๋Š” ์ด์œ :

    • ๊ฐ€๋…์„ฑ ํ–ฅ์ƒ: createMockEvent({ title: '์ค‘์š”ํ•œ ํšŒ์˜' })์ฒ˜๋Ÿผ ํ…Œ์ŠคํŠธ์— ํ•„์ˆ˜์ ์ธ ๋ฐ์ดํ„ฐ๋งŒ ๋ช…์‹œํ•˜๋ฉด, ์ด ํ…Œ์ŠคํŠธ๊ฐ€ ๋ฌด์—‡์„ ๊ฒ€์ฆํ•˜๋ ค๋Š”์ง€ ์˜๋„๊ฐ€ ๋ช…ํ™•ํ•ด์ง‘๋‹ˆ๋‹ค. ๋‚˜๋จธ์ง€ ์†์„ฑ์€ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ์ฑ„์›Œ์ง€๋ฏ€๋กœ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๊ฐ€ ๊ฐ„๊ฒฐํ•ด์ง‘๋‹ˆ๋‹ค.
    • ์œ ์ง€๋ณด์ˆ˜ ์šฉ์ด์„ฑ: Event ํƒ€์ž…์— ์ƒˆ๋กœ์šด ํ•„๋“œ(์˜ˆ: priority)๊ฐ€ ์ถ”๊ฐ€๋  ๋•Œ, ๋ชจ๋“  ํ…Œ์ŠคํŠธ ํŒŒ์ผ์„ ์ˆ˜์ •ํ•  ํ•„์š” ์—†์ด ํŒฉํ† ๋ฆฌ ํ•จ์ˆ˜ ํ•œ ๊ณณ๋งŒ ์ˆ˜์ •ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. ์ด๋Š” ์ˆ˜์‹ญ, ์ˆ˜๋ฐฑ ๊ฐœ์˜ ํ…Œ์ŠคํŠธ๋ฅผ ๊ด€๋ฆฌํ•  ๋•Œ ์—„์ฒญ๋‚œ ์‹œ๊ฐ„ ์ ˆ์•ฝ์œผ๋กœ ์ด์–ด์ง‘๋‹ˆ๋‹ค.
  • ๐Ÿ’ป ์ฝ”๋“œ ์˜ˆ์‹œ (AS-IS vs TO-BE)

    AS-IS (medium.useEventOperations.spec.ts)

    it('์ด๋ฒคํŠธ ์ •๋ณด๋ฅผ ๋ฐ›์•„ ๊ธฐ์กด ์ด๋ฒคํŠธ ๋ฐฐ์—ด์— ์ •์ƒ์ ์œผ๋กœ ์ €์žฅ์ด ๋œ๋‹ค', async () => {
      // ...
      const newEvent: Event = {
        id: '1',
        title: '์ƒˆ๋กœ์šด ํšŒ์˜',
        date: '2025-07-16',
        startTime: '12:00',
        endTime: '13:00',
        description: '์ƒˆ๋กœ์šด ํŒ€ ๋ฏธํŒ…',
        location: 'ํšŒ์˜์‹ค D',
        category: '์—…๋ฌด',
        repeat: { type: 'none', interval: 0 },
        notificationTime: 15,
      };
    
      // ...
    });
    ```

    
    TO-BE (ํ…Œ์ŠคํŠธ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํŒŒ์ผ์— ํŒฉํ† ๋ฆฌ ํ•จ์ˆ˜ ์ƒ์„ฑ)
    
```typescript
    // src/__tests__/utils/factory.ts
    import { Event } from '../../types';

    export const createMockEvent = (overrides: Partial<Event> = {}): Event => {
      return {
        id: 'default-id-' + Math.random(),
        title: '๊ธฐ๋ณธ ์ œ๋ชฉ',
        date: '2025-10-01',
        startTime: '10:00',
        endTime: '11:00',
        description: '๊ธฐ๋ณธ ์„ค๋ช…',
        location: '๊ธฐ๋ณธ ์œ„์น˜',
        category: '์—…๋ฌด',
        repeat: { type: 'none', interval: 1 },
        notificationTime: 10,
        ...overrides, // ์—ฌ๊ธฐ์„œ ํ•„์š”ํ•œ ์†์„ฑ๋งŒ ๋ฎ์–ด์”๋‹ˆ๋‹ค.
      };
    };
    ```


    (ํ…Œ์ŠคํŠธ ํŒŒ์ผ์—์„œ ์‚ฌ์šฉ)
    
```typescript
    // medium.useEventOperations.spec.ts
    import { createMockEvent } from '../utils/factory';

    it('์ œ๋ชฉ์ด ๋‹ค๋ฅธ ์ƒˆ๋กœ์šด ์ด๋ฒคํŠธ๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ์ €์žฅ๋œ๋‹ค', async () => {
      // ...
      // ์ด์ œ ์ด ํ…Œ์ŠคํŠธ์˜ ํ•ต์‹ฌ์ธ 'title'๊ณผ 'description'๋งŒ ์‹ ๊ฒฝ์“ฐ๋ฉด ๋ฉ๋‹ˆ๋‹ค.
      const newEvent = createMockEvent({ 
        id: '1', 
        title: '์ƒˆ๋กœ์šด ํšŒ์˜',
        description: '์ƒˆ๋กœ์šด ํŒ€ ๋ฏธํŒ…',
      });
    
      // ...
    });
    ```


**3. ํ…Œ์ŠคํŠธ ์ „๋žต ์ˆ˜๋ฆฝ: Kent C. Dodds์˜ 'ํ…Œ์ŠคํŒ… ํŠธ๋กœํ”ผ'๋ฅผ ์•„์‹œ๋‚˜์š”?**
- **๐Ÿ‘ ์ข‹์€ ์ **: "ํ†ตํ•ฉํ…Œ์ŠคํŠธ vs ๋‹จ์œ„ํ…Œ์ŠคํŠธ ๋น„์œจ๊ณผ ์ „๋žต"์— ๋Œ€ํ•ด ๊นŠ์ด ๊ณ ๋ฏผํ•˜๊ณ  ์งˆ๋ฌธํ•ด์ฃผ์‹  ์ ์ด ๋งค์šฐ ์ธ์ƒ ๊นŠ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ๋ชจ๋“  ๊ฐœ๋ฐœ์ž๊ฐ€ ๋ถ€๋”ชํžˆ๋Š” ํ˜„์‹ค์ ์ธ ๊ณ ๋ฏผ์ด๋ฉฐ, ์ด ๊ณ ๋ฏผ ์ž์ฒด๊ฐ€ ์„ฑ์žฅ์˜ ์ฆ๊ฑฐ์ž…๋‹ˆ๋‹ค.
- **๐Ÿ’ก ๊ฐœ์„  ์ œ์•ˆ**: ๋‹จ์œ„ ํ…Œ์ŠคํŠธ์™€ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ์˜ ๊ด€๊ณ„๋ฅผ ๊ฒฝ์Ÿ์ด ์•„๋‹Œ ํ˜‘๋ ฅ ๊ด€๊ณ„๋กœ ๋ฐ”๋ผ๋ณด๋Š” **'ํ…Œ์ŠคํŒ… ํŠธ๋กœํ”ผ(The Testing Trophy)'** ๋ชจ๋ธ์„ ์†Œ๊ฐœํ•ด ๋“œ๋ฆฌ๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. ์ด ๋ชจ๋ธ์€ '์–ผ๋งˆ๋‚˜ ๋งŽ์€ ๋น„์šฉ์„ ๋“ค์—ฌ ์–ผ๋งˆ๋‚˜ ํฐ ์ž์‹ ๊ฐ์„ ์–ป์„ ๊ฒƒ์ธ๊ฐ€?'์— ๋Œ€ํ•œ ํ›Œ๋ฅญํ•œ ๊ฐ€์ด๋“œ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
- **๐Ÿค” ์ด๋ ‡๊ฒŒ ์ œ์•ˆํ•˜๋Š” ์ด์œ **:
    - **๋‹จ์œ„ ํ…Œ์ŠคํŠธ(Unit Tests)**: ๋น ๋ฅด๊ณ , ์ €๋ ดํ•˜๋ฉฐ, ํŠน์ • ๋กœ์ง์˜ ์ •ํ™•์„ฑ์„ ๊ฒ€์ฆํ•ฉ๋‹ˆ๋‹ค. `dateUtils`์˜ ํ•จ์ˆ˜๋“ค์ด ๋Œ€ํ‘œ์ ์ธ ์˜ˆ์‹œ์ž…๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๊ฐ ๋ถ€ํ’ˆ์ด ์ •์ƒ์ด๋ผ๊ณ  ํ•ด์„œ ์™„์„ฑ๋œ ์ž๋™์ฐจ๊ฐ€ ์ž˜ ๊ตด๋Ÿฌ๊ฐ„๋‹ค๋Š” ๋ณด์žฅ์€ ์—†์Šต๋‹ˆ๋‹ค.
    - **ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ(Integration Tests)**: ์—ฌ๋Ÿฌ ๋ชจ๋“ˆ(์ปดํฌ๋„ŒํŠธ, ํ›…, API ๋“ฑ)์ด ํ•จ๊ป˜ ๋™์ž‘ํ•˜๋Š” ๊ฒƒ์„ ๊ฒ€์ฆํ•ฉ๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ ๊ฒฝํ—˜ํ•˜๋Š” ์‹ค์ œ ์‹œ๋‚˜๋ฆฌ์˜ค์™€ ๊ฐ€์žฅ ๊ฐ€๊น๊ธฐ ๋•Œ๋ฌธ์— **๊ฐ€์žฅ ๋†’์€ ROI(ํˆฌ์ž ๋Œ€๋น„ ํšจ๊ณผ)**๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. "์ผ์ • ์ถ”๊ฐ€ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด, API ์š”์ฒญ์ด ๊ฐ€๊ณ , ๋ชฉ๋ก์ด ๊ฐฑ์‹ ๋œ๋‹ค"์™€ ๊ฐ™์€ ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ํ…Œ์ŠคํŠธํ•˜๋Š” ๊ฒƒ์ด์ฃ .
    - ํšŒ๊ณ ๋ก์—์„œ ์–ธ๊ธ‰ํ•˜์‹  "ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ๋ชจ๋‘ ํ†ต๊ณผํ–ˆ๋Š”๋ฐ๋„ ๋ถˆ๊ตฌํ•˜๊ณ  ํฌ๊ฒŒ ์—๋Ÿฌ๊ฐ€ ํ„ฐ์ง„ ์ "์ด ๋ฐ”๋กœ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋งŒ์œผ๋กœ๋Š” ๋ถ€์กฑํ•œ ๋ถ€๋ถ„์„ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ๊ฐ€ ์ฑ„์›Œ์ฃผ๋Š” ์ด์œ ๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” ์™„๋ฒฝํ•œ ์‚ฌ๋ก€์ž…๋‹ˆ๋‹ค.
- **๐Ÿ’ป ๋‹ค์ด์–ด๊ทธ๋žจ: The Testing Trophy**
    
      /\\
     /  \\  <- E2E (๊ฐ€์žฅ ์ ๊ฒŒ)
    /____\\
   /      \\
  /        \\ <- Integration (๊ฐ€์žฅ ๋งŽ์ด, ํ•ต์‹ฌ)
 /__________\\
/            \\

/ \ <- Unit (์ค‘๊ฐ„)
/________________\
/ \
Static Analysis (๊ธฐ๋ณธ)
```

์ด ๋ชจ๋ธ์ฒ˜๋Ÿผ, ๋Œ€๋ถ€๋ถ„์˜ ๋…ธ๋ ฅ์„ **ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ**์— ์Ÿ๋Š” ๊ฒƒ์ด ๋ฒ„๊ทธ๋ฅผ ์ค„์ด๊ณ  ์•ˆ์ •์ ์ธ ์„œ๋น„์Šค๋ฅผ ๋งŒ๋“œ๋Š” ๋ฐ ๊ฐ€์žฅ ํšจ๊ณผ์ ์ž…๋‹ˆ๋‹ค. ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋กœ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ๋ฅผ ๋Œ€์ฒดํ•˜๋Š” ๊ฒƒ์€ ์œ„ํ—˜ํ•˜๋ฉฐ, "๋‹จ์œ„ ํ…Œ์ŠคํŠธ์—์„œ ์ตœ๋Œ€ํ•œ ์ปค๋ฒ„ํ•˜๊ณ  ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ์—์„œ๋Š” UI์ ์ธ ๋ถ€๋ถ„๋งŒ ํ…Œ์ŠคํŠธ"ํ•˜๋Š” ์ ‘๊ทผ๋ณด๋‹ค๋Š”, ์‚ฌ์šฉ์ž์˜ ํ•ต์‹ฌ ํ๋ฆ„์„ ๊ฒ€์ฆํ•˜๋Š” ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ๋ฅผ ์šฐ์„ ์œผ๋กœ ์ž‘์„ฑํ•˜๊ณ , ๋ณต์žกํ•œ ์ˆœ์ˆ˜ ํ•จ์ˆ˜ ๋กœ์ง์„ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋กœ ๋ณด์ถฉํ•˜๋Š” ์ „๋žต์„ ์ถ”์ฒœํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ—บ๏ธ ์ „์ฒด ๊ฐœ์„  ๋กœ๋“œ๋งต (์šฐ์„ ์ˆœ์œ„ ๊ฐ€์ด๋“œ)

ํ”ผ๋“œ๋ฐฑ์˜ ์ค‘์š”๋„ ์ˆœ์„œ๋Œ€๋กœ ์ •๋ ฌํ–ˆ์–ด์š”. ๋†’์€ ์ˆœ์œ„๋ถ€ํ„ฐ ์ฐจ๊ทผ์ฐจ๊ทผ ํ•ด๊ฒฐํ•ด ๋‚˜๊ฐ€๋Š” ๊ฑธ ์ถ”์ฒœํ•ด์š”.

  • CRITICAL ๐Ÿšจ: ํ˜„์žฌ ์ฝ”๋“œ์—๋Š” ์‹ฌ๊ฐํ•œ ๋ฒ„๊ทธ๋‚˜ ๋ฌธ์ œ๋Š” ๋ณด์ด์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ํ›Œ๋ฅญํ•ด์š”!
  • WARNING โš ๏ธ: ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ ํŒฉํ† ๋ฆฌ ํŒจํ„ด์„ ๋„์ž…ํ•˜์—ฌ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์˜ ์œ ์ง€๋ณด์ˆ˜์„ฑ์„ ํ™•๋ณดํ•˜๋Š” ๊ฒƒ์„ ์ ๊ทน ๊ถŒ์žฅํ•ด์š”. ์ง€๊ธˆ ๋‹น์žฅ์€ ๊ดœ์ฐฎ์ง€๋งŒ, ํ”„๋กœ์ ํŠธ๊ฐ€ ๋ณต์žกํ•ด์ง€๋ฉด ๊ธฐ์ˆ  ๋ถ€์ฑ„๋กœ ๋Œ์•„์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • OPTIONAL ๐Ÿ’ก: it.skip์œผ๋กœ ๋น„ํ™œ์„ฑํ™”๋œ ํ…Œ์ŠคํŠธ(easy.eventUtils.spec.ts)์— ๋Œ€ํ•ด, ์™œ ์Šคํ‚ตํ–ˆ๋Š”์ง€ ์ฃผ์„(// ์ƒ์œ„ ํ…Œ์ŠคํŠธ์™€ ์ค‘๋ณต๋˜์–ด ๋น„ํ™œ์„ฑํ™”)์„ ๋‚จ๊ฒจ๋‘๋ฉด ๋ฏธ๋ž˜์˜ ๋‚˜ ๋˜๋Š” ๋™๋ฃŒ๊ฐ€ ์ฝ”๋“œ๋ฅผ ์ดํ•ดํ•˜๋Š” ๋ฐ ํฐ ๋„์›€์ด ๋ฉ๋‹ˆ๋‹ค.

๐Ÿค” ํ•œ ๊ฑธ์Œ ๋”: ์ฝ”๋“œ์™€ ์˜๋„ ๋Œ์•„๋ณด๊ธฐ

PR ๋ณธ๋ฌธ์„ ํ†ตํ•ด ๊นŠ์€ ๊ณ ๋ฏผ์„ ์—ฟ๋ณผ ์ˆ˜ ์žˆ์—ˆ์–ด์š”. ํ…Œ์ŠคํŠธ๋ผ๋Š” '์œ ๋‹ˆ์ฝ˜'๊ณผ ๋งŽ์ด ์นœํ•ด์ง€์‹  ๊ฒƒ ๊ฐ™์•„ ์ €๋„ ๊ธฐ์˜๋„ค์š”! ์ฝ”๋“œ์™€ ํšŒ๊ณ ๋ฅผ ๋น„๊ตํ•˜๋ฉฐ ๋ช‡ ๊ฐ€์ง€ ์งˆ๋ฌธ์„ ์ค€๋น„ํ–ˆ์Šต๋‹ˆ๋‹ค.

  • ๋ชฉํ‘œ์™€ ๊ตฌํ˜„์˜ ์—ฐ๊ฒฐ๊ณ ๋ฆฌ: ๋ณธ๋ฌธ์— "App ์ปดํฌ๋„ŒํŠธ ์ ์ ˆํ•œ ๋‹จ์œ„์˜ ์ปดํฌ๋„ŒํŠธ, ํ›…, ์œ ํ‹ธ ํ•จ์ˆ˜๋กœ ๋ถ„๋ฆฌ"๋ผ๋Š” ๋ชฉํ‘œ๋ฅผ ์™„๋ฒฝํ•˜๊ฒŒ ๋‹ฌ์„ฑํ•˜์…จ๋„ค์š”. ํ˜น์‹œ useEventHandlers ํ›…์„ ์„ค๊ณ„ํ•  ๋•Œ, ์–ด๋–ค ๋กœ์ง๊นŒ์ง€ ์ด ํ›…์˜ ์ฑ…์ž„์œผ๋กœ ๋‘˜์ง€ ๊ณ ๋ฏผํ–ˆ๋˜ ์ง€์ ์ด ์žˆ๋‚˜์š”? ์˜ˆ๋ฅผ ๋“ค์–ด findOverlappingEvents ํ˜ธ์ถœ์„ useEventHandlers ๋‚ด๋ถ€์—์„œ ํ• ์ง€, ์™ธ๋ถ€์—์„œ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ›์•„์˜ฌ์ง€ ๊ฐ™์€ ๊ฒฐ์ •๋“ค์ด์š”.
  • ์ˆจ๊ฒจ์ง„ ์˜๋„ ํŒŒ์•…: timeValidation.ts์— TIME_FORMAT_REGEX๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ์‹œ๊ฐ„ ํ˜•์‹ ์ž์ฒด๋ฅผ ๊ฒ€์ฆํ•˜๋Š” ๋กœ์ง์„ ์ถ”๊ฐ€ํ•˜์…จ๋”๊ตฐ์š”. ์•„๋งˆ ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•˜์‹œ๋ฉด์„œ ๋ฐœ๊ฒฌํ•œ ์—ฃ์ง€ ์ผ€์ด์Šค ๊ฐ™์€๋ฐ, ์ด๋Ÿฐ ๋””ํ…Œ์ผ์ด ์ฝ”๋“œ์˜ ์•ˆ์ •์„ฑ์„ ํฌ๊ฒŒ ๋†’์ž…๋‹ˆ๋‹ค. ์–ด๋–ค ๊ณ„๊ธฐ๋กœ ์ด ๋ถ€๋ถ„์„ ์ถ”๊ฐ€ํ•˜๊ฒŒ ๋˜์…จ๋Š”์ง€ ๊ถ๊ธˆํ•˜๋„ค์š”!
  • ์Šค์Šค๋กœ์—๊ฒŒ ๋˜์ง€๋Š” ์งˆ๋ฌธ:
    • ํšŒ๊ณ ์—์„œ TDD์˜ ์ค‘์š”์„ฑ์„ ๋А๋ผ์…จ๋‹ค๊ณ  ํ–ˆ๋Š”๋ฐ, ๋งŒ์•ฝ useEventHandlers ํ›…์„ TDD ๋ฐฉ์‹์œผ๋กœ ๊ฐœ๋ฐœํ–ˆ๋‹ค๋ฉด ์ง€๊ธˆ์˜ ์ฝ”๋“œ์™€ ์–ด๋–ป๊ฒŒ ๋‹ฌ๋ผ์กŒ์„๊นŒ์š”? ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋ฅผ ๋จผ์ € ์ƒ์ƒํ•ด๋ณด๋Š” ์—ฐ์Šต์„ ํ•ด๋ณด๋Š” ๊ฑด ์–ด๋–จ๊นŒ์š”?
    • Toss Frontend Fundamental์˜ ๊ฒฐํ•ฉ๋„(Coupling) ์›์น™์„ ๋ณด๋ฉด "์–ด๋–ค ๋ชจ๋“ˆ์˜ ๋‚ด์šฉ์„ ๋ฐ”๊ฟจ์„ ๋•Œ ๋‹ค๋ฅธ ๋ชจ๋“ˆ์„ ๋˜ ๊ณ ์ณ์•ผ ํ•œ๋‹ค๋ฉด, ๊ทธ ๋‘˜์€ ๊ฐ•ํ•˜๊ฒŒ ๊ฒฐํ•ฉ๋˜์–ด ์žˆ๋‹ค"๊ณ  ๋งํ•ฉ๋‹ˆ๋‹ค. ์ง€๊ธˆ์˜ ๊ตฌ์กฐ์—์„œ ๋งŒ์•ฝ notistack ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๋‹ค๋ฅธ ์•Œ๋ฆผ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ ๊ต์ฒดํ•œ๋‹ค๋ฉด, ๋ช‡ ๊ฐœ์˜ ํŒŒ์ผ์„ ์ˆ˜์ •ํ•ด์•ผ ํ• ๊นŒ์š”? ์ด ์งˆ๋ฌธ์ด ๊ฒฐํ•ฉ๋„๋ฅผ ์ธก์ •ํ•˜๋Š” ์ข‹์€ ์ฒ™๋„๊ฐ€ ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • "์ด ์ฝ”๋“œ๋ฅผ ์ฒ˜์Œ ๋ณด๋Š” ๋™๋ฃŒ๋Š” handlerUtils.ts์˜ ์—ญํ• ๊ณผ setupMockHandlerCreation ๊ฐ™์€ ํ•จ์ˆ˜๋“ค์˜ ๋™์ž‘ ๋ฐฉ์‹์„ ์‰ฝ๊ฒŒ ์ดํ•ดํ•  ์ˆ˜ ์žˆ์„๊นŒ?"
    • ์ด๋ฒˆ์— ์ ์šฉํ•œ ์ปดํฌ๋„ŒํŠธ/ํ›… ๋ถ„๋ฆฌ ์•„ํ‚คํ…์ฒ˜๊ฐ€ "๋ฐ˜๋ณต ์ผ์ •" ๊ฐ™์€ ๋” ๋ณต์žกํ•œ ๊ธฐ๋Šฅ์ด ์ถ”๊ฐ€๋  ๋•Œ๋„ ์œ ์—ฐํ•˜๊ฒŒ ํ™•์žฅ๋  ์ˆ˜ ์žˆ์„๊นŒ์š”?

๐Ÿ“š ์ฐธ๊ณ  ์ž๋ฃŒ

๋ฌธ์ œ ํ•ด๊ฒฐ์— ๋„์›€์ด ๋  ๋งŒํ•œ ์ž๋ฃŒ๋“ค์„ ๋ชจ์•„๋ดค์–ด์š”.

๐ŸŒ ์ถ”์ฒœ ์•„ํ‹ฐํด


์ด๋ฒˆ ๋ฆฌ๋ทฐ๊ฐ€ ์„ฑ์žฅ์— ๋„์›€์ด ๋˜์—ˆ์œผ๋ฉด ์ข‹๊ฒ ์Šต๋‹ˆ๋‹ค! ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์— ๋Œ€ํ•œ ์—ด์ •๊ณผ ๊นŠ์€ ๊ณ ๋ฏผ, ์ •๋ง ํ›Œ๋ฅญํ•ฉ๋‹ˆ๋‹ค. ๊ณ„์† ๋‚˜์•„๊ฐ€์„ธ์š”

Copy link

@ldhldh07 ldhldh07 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ ํ˜ธ์ถœํ•  ๋•Œ ์ธ๋ฑ์Šค๋ฅผ ์ด์šฉํ•ด์„œ ์–ด๋–ค ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜๋Š”์ง€ ์–ด๋ ต์ง€ ์•Š๋‚˜ ์‹ถ๊ธฐ๋„ ํ–ˆ๋Š”๋ฐ, ๊ทธ ํ…Œ์ŠคํŠธ ๋กœ์ง์ด ์ผ๊ด€์„ฑ์ด ์žˆ์–ด์„œ ์ฝ์œผ๋ฉด ์ฝ์„์ˆ˜๋ก ๋น ๋ฅด๊ฒŒ ํ๋ฆ„์„ ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ์–ด์„œ ์ข‹์•˜์Šต๋‹ˆ๋‹ค!

๋‹ค์–‘ํ•œ vitest ๋ฉ”์†Œ๋“œ ์‚ฌ์šฉํ•ด์ฃผ์…”์„œ ๋งŽ์ด ๋ฐฐ์šฐ๊ณ  ๊ฐ‘๋‹ˆ๋‹น


it("์ด๋ฒคํŠธ ๋กœ๋”ฉ ์‹คํŒจ ์‹œ '์ด๋ฒคํŠธ ๋กœ๋”ฉ ์‹คํŒจ'๋ผ๋Š” ํ…์ŠคํŠธ์™€ ํ•จ๊ป˜ ์—๋Ÿฌ ํ† ์ŠคํŠธ๊ฐ€ ํ‘œ์‹œ๋˜์–ด์•ผ ํ•œ๋‹ค", async () => {});
it('์ €์žฅ๋˜์–ด์žˆ๋Š” ์ดˆ๊ธฐ ์ด๋ฒคํŠธ๊ฐ€ ์žˆ์„ ๊ฒฝ์šฐ ๋ฐ์ดํ„ฐ๋ฅผ ์ ์ ˆํ•˜๊ฒŒ ๋ถˆ๋Ÿฌ์˜จ๋‹ค', async () => {
const createEvents: Event[] = [

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

๋ฐ์ดํ„ฐ๋Š” ๋ณ€์ˆ˜๋ช…์„ ๋ช…์‚ฌ๋กœ ํ•˜๋Š”๊ฒŒ ์ข‹๋‹ค ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

์•— ๊ทธ๋ ‡๋„ค์š” ์ฝ”๋“œ ๋ด์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค ๐Ÿ˜Š


// Date ๊ฐ์ฒด์ธ์ง€ ํ™•์ธ
expect(targetDateTime).toBeInstanceOf(Date);
expect(String(targetDateTime)).toBe('Invalid Date');

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

์•„ ์ด๊ฑฐ ์ด๋ ‡๊ฒŒ ํ•˜๋Š”๊ฑฐ์˜€๊ตฐ์š”. ์ œ๊บผ์— ์ด์ƒํ•˜๊ฒŒ ํ•ด๋ฒ„๋ ธ๋„ค

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

์ œ๊ฐ€ ์ด์ƒํ•˜๊ฒŒ ํ•œ ๊ฒƒ์ผ์ˆ˜๋„ ... ๐Ÿฅน

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants