๐Ÿ“– TIL - ํ…Œ์ŠคํŠธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํ•ต์‹ฌ ์ •๋ฆฌ

์Š˜ยท2025๋…„ 8์›” 12์ผ

๐Ÿ“– TIL

๋ชฉ๋ก ๋ณด๊ธฐ
87/90

๐Ÿ“Œ ๋ฌธ์ œ ์„ค๋ช…

๋ฌธ์ œ ์„ค๋ช…

ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๋ฐœ์—์„œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์„ฑ ์‹œ ํ•„์š”ํ•œ ๋‹ค์–‘ํ•œ ํ…Œ์ŠคํŠธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํ•จ์ˆ˜๋“ค๊ณผ ๊ฐœ๋…์„ ์ •ํ™•ํžˆ ์ดํ•ดํ•˜๊ณ  ํ™œ์šฉํ•ด๋ณด์ž.

์‹ค์ œ๋กœ Auto-Summary-AI ํ”„๋กœ์ ํŠธ๋ฅผ ๊ฐœ๋ฐœํ•˜๋ฉด์„œ ํ…Œ์ŠคํŠธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ฒ˜์Œ๋ถ€ํ„ฐ ๋๊นŒ์ง€ ์ ์šฉํ•ด๋ณด๋Š” ๊ฒฝํ—˜์„ ํ–ˆ๋‹ค. ๋…ธํŠธ ์ž‘์„ฑ, ํŽธ์ง‘, AI ์š”์•ฝ ๊ธฐ๋Šฅ๊นŒ์ง€ ํฌํ•จ๋œ ๋ณต์žกํ•œ ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋ฉด์„œ ๋งŽ์€ ์‹œํ–‰์ฐฉ์˜ค์™€ ๊นจ๋‹ฌ์Œ์„ ์–ป์„ ์ˆ˜ ์žˆ์—ˆ๋‹ค.

๊ด€๋ จ ํ”„๋กœ์ ํŠธ: Auto-Summary-AI

ํ•™์Šต ๋ชฉํ‘œ

  • Vi.js์˜ ๋ชจํ‚น ํ•จ์ˆ˜๋“ค (vi.fn, vi.mock) ์‹ค์ „ ํ™œ์šฉ
  • React Testing Library์˜ ๋‹ค์–‘ํ•œ ์ฟผ๋ฆฌ ํ•จ์ˆ˜๋“ค ์ƒํ™ฉ๋ณ„ ์ ์šฉ
  • ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์—์„œ์˜ ๋ผ์šฐํ„ฐ ์„ค์ • ๋ฐฉ๋ฒ• ์™„์ „ ์ •๋ณต
  • Jest/Vitest์˜ matcher ํ•จ์ˆ˜๋“ค์„ ํ†ตํ•œ ์ •๊ตํ•œ ๊ฒ€์ฆ

๐Ÿค” ๋ฌธ์ œ ์ ‘๊ทผ

์‹ค์ œ ํ”„๋กœ์ ํŠธ์—์„œ ํ…Œ์ŠคํŠธ๋ฅผ ๊ตฌํ˜„ํ•˜๋ฉด์„œ ๋ถ€๋”ชํžŒ ์ฃผ์š” ๊ณผ์ œ๋“ค:

๐Ÿ—๏ธ ๋ณต์žกํ•œ ํ™˜๊ฒฝ ๊ตฌ์„ฑ

  • RTK(Redux Toolkit) ์ƒํƒœ ๊ด€๋ฆฌ
  • redux-persist๋ฅผ ํ†ตํ•œ ๋กœ์ปฌ ์Šคํ† ๋ฆฌ์ง€ ์—ฐ๋™
  • React Router์˜ ๋™์  ๋ผ์šฐํŒ…
  • OpenAI API ์—ฐ๋™ ๋ฐ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ

๐ŸŽฏ ํ•ต์‹ฌ ํ…Œ์ŠคํŠธ ๋Œ€์ƒ

  • ๋…ธํŠธ ์ƒ์„ฑ/ํŽธ์ง‘ ํ”Œ๋กœ์šฐ
  • ๋ผ์šฐํ„ฐ ๊ธฐ๋ฐ˜ ํŽ˜์ด์ง€ ์ด๋™
  • AI ์š”์•ฝ ๊ธฐ๋Šฅ์˜ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ
  • ์‚ฌ์šฉ์ž ์ƒํ˜ธ์ž‘์šฉ (ํด๋ฆญ, ์ž…๋ ฅ, ํผ ์ œ์ถœ)

๐Ÿ’ก ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•

๐Ÿ”ง ๋ชจํ‚น(Mocking) ์ „๋žต

ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์˜ ํ•œ๊ณ„๋ฅผ ๊ทน๋ณตํ•˜๊ธฐ ์œ„ํ•œ ๋‹ค์–‘ํ•œ ๋ชจํ‚น ๊ธฐ๋ฒ•์„ ํ™œ์šฉํ–ˆ๋‹ค.

๐Ÿ—๏ธ ํ…Œ์ŠคํŠธ ๊ตฌ์กฐํ™”

๋ณต์žกํ•œ ๊ธฐ๋Šฅ์„ ์ฒด๊ณ„์ ์œผ๋กœ ํ…Œ์ŠคํŠธํ•˜๊ธฐ ์œ„ํ•œ ๊ตฌ์กฐ ์„ค๊ณ„

๐Ÿ›ฃ๏ธ ๋ผ์šฐํ„ฐ ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ

์‹ค์ œ ๋ธŒ๋ผ์šฐ์ € ์—†์ด๋„ ๋ผ์šฐํŒ…์„ ์™„๋ฒฝํ•˜๊ฒŒ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ• ๊ตฌ์ถ•


๐Ÿ” ์ฝ”๋“œ ์„ค๋ช…

์‹ค์ œ Auto-Summary-AI ํ”„๋กœ์ ํŠธ์—์„œ ๊ฒช์€ ๊ฒฝํ—˜๋“ค

๐ŸŽช ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์˜ ํ˜„์‹ค์  ์ œ์•ฝ๊ณผ ํ•ด๊ฒฐ์ฑ…

๊ฐ€์žฅ ํฐ ๋‚œ๊ด€: RTK + redux-persist ์กฐํ•ฉ์—์„œ์˜ localStorage ๋ฌธ์ œ

ํ”„๋กœ์ ํŠธ์—์„œ ์‚ฌ์šฉ์ž์˜ ๋…ธํŠธ๋ฅผ ๋ธŒ๋ผ์šฐ์ €์— ์ €์žฅํ•˜๊ธฐ ์œ„ํ•ด redux-persist๋ฅผ ํ™œ์šฉํ–ˆ๋Š”๋ฐ, ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์—์„œ๋Š” ์‹ค์ œ ๋ธŒ๋ผ์šฐ์ € ํ™˜๊ฒฝ์ด ์•„๋‹ˆ๋ผ์„œ localStorage๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค๋Š” ๊ฑธ ๋’ค๋Šฆ๊ฒŒ ๊นจ๋‹ฌ์•˜๋‹ค. ๐Ÿ˜…

ํ•ด๊ฒฐ ๊ณผ์ •์—์„œ ๊ฐ€์žฅ ํ—ท๊ฐˆ๋ ธ๋˜ ๋ถ€๋ถ„: getItem์—์„œ Promise.resolve(null)์„ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•œ๋‹ค๋Š” ์ ์ด์—ˆ๋‹ค. setItem/removeItem์€ ๋‹จ์ˆœํžˆ ๋™์ž‘๋งŒ ๋ชจํ‚นํ•˜๋ฉด ๋˜์ง€๋งŒ, getItem์€ ์‹ค์ œ๋กœ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜์ด๊ธฐ ๋•Œ๋ฌธ์— ์ดˆ๊ธฐ ์ƒํƒœ์—์„œ๋Š” null์„ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ์„ค์ •ํ•ด์•ผ ํ•œ๋‹ค๋Š” ๊ฑธ ์‹œํ–‰์ฐฉ์˜ค๋ฅผ ํ†ตํ•ด ํ•™์Šตํ–ˆ๋‹ค.

// ๋ธŒ๋ผ์šฐ์ € ์ „์—ญ ํ•จ์ˆ˜ ๋ชจํ‚น (์•Œ๋ฆผ ๊ธฐ๋Šฅ ํ…Œ์ŠคํŠธ์šฉ)
window.alert = vi.fn();

// HTTP ํด๋ผ์ด์–ธํŠธ ์ „์ฒด ๋ชจํ‚น
vi.mock("axios");

// OpenAI API ์‘๋‹ต ๋ชจํ‚น - ์‹ค์ œ API ์‘๋‹ต ๊ตฌ์กฐ ๊ทธ๋Œ€๋กœ ์žฌํ˜„
vi.spyOn(api, "fetchOpenAI").mockResolvedValueOnce({
  choices: [{ message: { content: "AI๊ฐ€ ์ƒ์„ฑํ•œ ์š”์•ฝ ๋‚ด์šฉ" } }]
});

// redux-persist์šฉ ์Šคํ† ๋ฆฌ์ง€ ๋ชจํ‚น (ํ•ต์‹ฌ ํฌ์ธํŠธ!)
const mockStorage = {
  getItem: vi.fn(() => Promise.resolve(null)), // ์ดˆ๊ธฐ๊ฐ’ null ๋ฐ˜ํ™˜์ด ํ•ต์‹ฌ
  setItem: vi.fn(() => Promise.resolve()),     // ์ €์žฅ ๋™์ž‘๋งŒ ๋ชจํ‚น
  removeItem: vi.fn(() => Promise.resolve())   // ์‚ญ์ œ ๋™์ž‘๋งŒ ๋ชจํ‚น
};

๐Ÿ” DOM ์š”์†Œ ํƒ์ƒ‰์˜ ์‹ค์ œ ํ™œ์šฉ ํŒจํ„ด

ํ”„๋กœ์ ํŠธ ํŠน์„ฑ: ๊ฐ™์€ ๊ธฐ๋Šฅ์˜ ๋ฒ„ํŠผ์ด ์—ฌ๋Ÿฌ ๊ณณ์— ๋ฐฐ์น˜๋จ (ํ—ค๋”, ์‚ฌ์ด๋“œ๋ฐ” ๋“ฑ)

// ์‹ค์ œ ์‚ฌ์šฉ ํŒจํ„ด: ๊ตฌ์กฐ๋ถ„ํ•ดํ• ๋‹น์œผ๋กœ ํ•„์š”ํ•œ ์ฟผ๋ฆฌ๋งŒ ์ถ”์ถœ
const {getAllByText, getByTestId, getByText, getAllByTestId} = render(
  <Provider store={testStore}>
    <PersistGate loading={<div data-testid="loading">Loading...</div>} persistor={persistor}>
      <RouterProvider router={router}/>
    </PersistGate>
  </Provider>
);

// PersistGate ๋กœ๋”ฉ ์™„๋ฃŒ๊นŒ์ง€ ๋Œ€๊ธฐ (redux-persist ํŠน์„ฑ์ƒ ํ•„์ˆ˜)
await waitFor(() => {
  expect(screen.queryByTestId("loading")).not.toBeInTheDocument();
}, { timeout: 3000 });

// Code Splitting์œผ๋กœ ์ธํ•œ lazy loading ๋Œ€๊ธฐ
await waitFor(() => {
  expect(screen.queryByText("loading...")).not.toBeInTheDocument();
}, { timeout: 5000 });

๐Ÿ‘† ์‚ฌ์šฉ์ž ํ–‰๋™ ์‹œ๋ฎฌ๋ ˆ์ด์…˜์˜ ํ˜„์‹ค์  ์ ‘๊ทผ

์‹ค์ œ ์‚ฌ์šฉ์ž ํ”Œ๋กœ์šฐ: ๋…ธํŠธ ์ž‘์„ฑ ๋ฒ„ํŠผ ํด๋ฆญ โ†’ ํŽ˜์ด์ง€ ์ด๋™ โ†’ ๋‚ด์šฉ ์ž…๋ ฅ โ†’ ์š”์•ฝ ์ƒ์„ฑ โ†’ ์ €์žฅ

// ํ˜„์‹ค์ ์ธ ๋ฌธ์ œ: "๋…ธํŠธ ์ž‘์„ฑ" ๋ฒ„ํŠผ์ด ์—ฌ๋Ÿฌ ๊ฐœ ์กด์žฌ
const noteButtons = getAllByText("๋…ธํŠธ ์ž‘์„ฑ");
expect(noteButtons).toHaveLength(2); // ํ—ค๋”์™€ ์‚ฌ์ด๋“œ๋ฐ”์— ๊ฐ๊ฐ ์กด์žฌ
await userEvent.click(noteButtons[0]); // ์ฒซ ๋ฒˆ์งธ(ํ—ค๋”) ๋ฒ„ํŠผ ํด๋ฆญ

// ์‹ค์ œ ์‚ฌ์šฉ์ž์ฒ˜๋Ÿผ ๊ธฐ์กด ๋‚ด์šฉ ์ง€์šฐ๊ณ  ์ƒˆ๋กœ ์ž…๋ ฅ
const titleEl = getByTestId('title');
await userEvent.clear(titleEl);
await userEvent.type(titleEl, "ํ…Œ์ŠคํŠธ์šฉ ์ œ๋ชฉ");

// textarea๊ฐ€ ์—ฌ๋Ÿฌ ๊ฐœ์ธ ๊ฒฝ์šฐ (์›๋ณธ ๋‚ด์šฉ, ์š”์•ฝ ๋‚ด์šฉ)
const contentEl = getAllByTestId('content');
await userEvent.clear(contentEl[0]);
await userEvent.type(contentEl[0], "30์ž ์ด์ƒ์˜ ์ถฉ๋ถ„ํžˆ ๊ธด ํ…Œ์ŠคํŠธ ๋‚ด์šฉ์ž…๋‹ˆ๋‹ค. AI ์š”์•ฝ์„ ์œ„ํ•ด ์ž‘์„ฑํ•œ ์˜ˆ์‹œ ํ…์ŠคํŠธ์ž…๋‹ˆ๋‹ค.");

๐Ÿ›ฃ๏ธ ๋™์  ๋ผ์šฐํŒ… ๊ฒ€์ฆ์˜ ์‹ค์ œ ์ผ€์ด์Šค

ํ”„๋กœ์ ํŠธ ํŠน์ง•: UUID ๊ธฐ๋ฐ˜ ๋™์  ๋…ธํŠธ ํŽ˜์ด์ง€ (/notes/12345678-1234-1234-1234-123456789abc)

// ๋…ธํŠธ ์ƒ์„ฑ ํ›„ ์ž๋™์œผ๋กœ ํ•ด๋‹น ๋…ธํŠธ ํŽ˜์ด์ง€๋กœ ์ด๋™๋˜๋Š”์ง€ ํ™•์ธ
await waitFor(() => {
  expect(router.state.location.pathname).toMatch(/^\/notes\/[a-f0-9-]{36}$/)
});

// ๋…ธํŠธ ์ €์žฅ ํ›„ ํ™ˆ์œผ๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ๋˜๋Š”์ง€ ํ™•์ธ
await waitFor(() => {
  expect(router.state.location.pathname).toBe('/')
});

๐Ÿช Redux ์ƒํƒœ ์ง์ ‘ ๊ฒ€์ฆ

RTK์˜ ์žฅ์  ํ™œ์šฉ: testStore.getState()๋กœ ์ƒํƒœ ์ง์ ‘ ์ ‘๊ทผ

// ๋…ธํŠธ๊ฐ€ ์‹ค์ œ๋กœ Redux ์Šคํ† ์–ด์— ์ €์žฅ๋˜์—ˆ๋Š”์ง€ ํ™•์ธ
await waitFor(() => {
  const notes = testStore.getState().notes.lists;
  expect(notes).toHaveLength(1);
  expect(notes[0].title).toBe("ํ…Œ์ŠคํŠธ์šฉ ์ œ๋ชฉ");
  expect(notes[0].content).toBe("30์ž ์ด์ƒ์˜ ์ถฉ๋ถ„ํžˆ ๊ธด ํ…Œ์ŠคํŠธ ๋‚ด์šฉ์ž…๋‹ˆ๋‹ค...");
  expect(notes[0].summary).toBe("AI๊ฐ€ ์ƒ์„ฑํ•œ ์š”์•ฝ ๋‚ด์šฉ");
});

๐ŸŽญ ์‹ค์ œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ๊ฒ€์ฆ

ํ”„๋กœ์ ํŠธ์˜ ํ•ต์‹ฌ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ: 30์ž ๋ฏธ๋งŒ์€ ์š”์•ฝ ๋ถˆ๊ฐ€

// ๋น„์ฆˆ๋‹ˆ์Šค ๋ฃฐ์— ๋”ฐ๋ฅธ ๋ถ„๊ธฐ ์ฒ˜๋ฆฌ
if (contentEl[0].value.length < 30) {
  expect(window.alert).toHaveBeenCalledWith('์š”์•ฝํ•˜๋ ค๋ฉด ์ตœ์†Œ 30์ž ์ด์ƒ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.');
} else {
  expect(window.alert).not.toHaveBeenCalledWith('์š”์•ฝํ•˜๋ ค๋ฉด ์ตœ์†Œ 30์ž ์ด์ƒ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.');
}

// ์š”์•ฝ ์ƒ์„ฑ ์—ฌ๋ถ€์— ๋”ฐ๋ฅธ ์ €์žฅ ๊ฐ€๋Šฅ์„ฑ ๊ฒ€์ฆ
if (window.alert.mock.calls.length > 0) {
  expect(window.alert).toHaveBeenCalledWith('์š”์•ฝ ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜์—ฌ ์š”์•ฝ ๊ฒฐ๊ณผ๋ฅผ ์ •๋ฆฌํ•ด์ฃผ์„ธ์š”.');
} else {
  // ๋ชจ๋“  ๊ฒ€์ฆ์ด ํ†ต๊ณผํ•œ ๊ฒฝ์šฐ์˜ ์„ฑ๊ณต ์‹œ๋‚˜๋ฆฌ์˜ค
}

โœจ ์ƒˆ๋กญ๊ฒŒ ๋ฐฐ์šด ์ 

๐Ÿ”ง ๋ชจํ‚น์˜ ์‹ค์ „ ํ™œ์šฉ๋ฒ•

vi.fn() vs vi.mock()์˜ ๋ช…ํ™•ํ•œ ์ฐจ์ด: ๊ฐœ๋ณ„ ํ•จ์ˆ˜๋Š” vi.fn(), ๋ชจ๋“ˆ ์ „์ฒด๋Š” vi.mock()์œผ๋กœ ์ฒ˜๋ฆฌ

๐Ÿ” ์ƒํ™ฉ๋ณ„ ์ฟผ๋ฆฌ ์„ ํƒ ์ „๋žต

  • getByText: ๋ฐ˜๋“œ์‹œ ์กด์žฌํ•ด์•ผ ํ•˜๋Š” ์š”์†Œ (์—†์œผ๋ฉด ํ…Œ์ŠคํŠธ ์‹คํŒจ๋กœ ์ฒ˜๋ฆฌ)
  • queryByText: ์กด์žฌ ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•˜๊ณ  ์‹ถ์€ ์š”์†Œ (๋กœ๋”ฉ ์ƒํƒœ ๋“ฑ)
  • findByText: ๋น„๋™๊ธฐ๋กœ ๋‚˜ํƒ€๋‚˜๋Š” ์š”์†Œ (API ์‘๋‹ต ํ›„ ๋ Œ๋”๋ง)

๐Ÿ›ฃ๏ธ ๋ฉ”๋ชจ๋ฆฌ ๋ผ์šฐํ„ฐ์˜ ์ง„๊ฐ€

createMemoryRouter์˜ ํ•ต์‹ฌ ๊ฐ€์น˜: ๋ธŒ๋ผ์šฐ์ € ํ™˜๊ฒฝ ์—†์ด๋„ ์™„์ „ํ•œ ๋ผ์šฐํŒ… ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅ

๐Ÿ“ ์š”์†Œ๋ณ„ ๊ฒ€์ฆ ๋งค์ฒ˜ ์„ ํƒ

์‹ค์ „ ๊ฒฝํ—˜: input/textarea๋Š” toHaveValue, ์ผ๋ฐ˜ ํ…์ŠคํŠธ ์š”์†Œ๋Š” toHaveTextContent

๐Ÿ—๏ธ ๋ณตํ•ฉ ํ™˜๊ฒฝ์—์„œ์˜ ์ดˆ๊ธฐํ™” ์ค‘์š”์„ฑ

Redux + Router + PersistGate: ๊ฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ์ดˆ๊ธฐํ™” ์ˆœ์„œ์™€ ๋ฐฉ๋ฒ•์ด ํ…Œ์ŠคํŠธ ์„ฑ๊ณต์˜ ํ•ต์‹ฌ

๐ŸŽฏ ์‹ค๋ฌด์—์„œ ์œ ์šฉํ•œ ๊ณ ๊ธ‰ ๊ธฐ๋ฒ•๋“ค

  • ์ •๊ทœํ‘œํ˜„์‹: UUID ๊ฐ™์€ ๋™์  ๊ฐ’ ๊ฒ€์ฆ์— toMatch() ํ™œ์šฉ
  • ์กฐ๊ฑด๋ถ€ ํ…Œ์ŠคํŠธ: mock.calls.length๋กœ ํ•จ์ˆ˜ ํ˜ธ์ถœ ์—ฌ๋ถ€ ํŒ๋‹จ ํ›„ ๋ถ„๊ธฐ
  • ํƒ€์ž„์•„์›ƒ ์ฐจ๋“ฑํ™”: lazy loading(5์ดˆ), ์ผ๋ฐ˜ ๋น„๋™๊ธฐ(3์ดˆ)๋กœ ์ƒํ™ฉ์— ๋งž๊ฒŒ ์กฐ์ ˆ

๐Ÿš€ ์‹ค์ „์—์„œ ํ„ฐ๋“ํ•œ ํšจ์œจ์ ์ธ ํŒจํ„ด๋“ค

๐Ÿ”„ ํ…Œ์ŠคํŠธ ๊ฐ„ ๊ฒฉ๋ฆฌ์˜ ์™„๋ฒฝํ•จ

beforeEach/afterEach ํ™ฉ๊ธˆ ์กฐํ•ฉ:

  • beforeEach: ๋ชจํ‚น, ์Šคํ† ์–ด, ๋ผ์šฐํ„ฐ ์ „์ฒด ์žฌ์„ค์ •
  • afterEach: persistor.purge()๋กœ ์˜์† ์ƒํƒœ๊นŒ์ง€ ์™„์ „ ์ดˆ๊ธฐํ™”

๐Ÿ“ ์ฝ”๋“œ ๊ฐ€๋…์„ฑ ํ–ฅ์ƒ ๊ธฐ๋ฒ•

๊ตฌ์กฐ๋ถ„ํ•ดํ• ๋‹น์˜ ์œ„๋ ฅ: render ๋ฐ˜ํ™˜๊ฐ’์—์„œ ์‹ค์ œ ์‚ฌ์šฉํ•  ํ•จ์ˆ˜๋“ค๋งŒ ์„ ๋ณ„์ ์œผ๋กœ ์ถ”์ถœ

โฐ ํ˜„์‹ค์ ์ธ ๋Œ€๊ธฐ ์‹œ๊ฐ„ ์ „๋žต

์ƒํ™ฉ๋ณ„ ํƒ€์ž„์•„์›ƒ ์„ค์ •:

  • Code Splitting ๋กœ๋”ฉ: 5์ดˆ
  • ์ผ๋ฐ˜ ์ƒํƒœ ๋ณ€๊ฒฝ: 3์ดˆ
  • ์ฆ‰์‹œ ๋ฐ˜์˜ ์˜ˆ์ƒ: 1์ดˆ

๐ŸŽฏ ๋ชจ๋“  ์‹œ๋‚˜๋ฆฌ์˜ค ์ปค๋ฒ„๋ง

์กฐ๊ฑด๋ถ€ ํ…Œ์ŠคํŠธ์˜ ํž˜: ์„ฑ๊ณต/์‹คํŒจ ์ผ€์ด์Šค๋ฅผ ๋ชจ๋‘ ๊ณ ๋ คํ•œ ๋ถ„๊ธฐ ์ฒ˜๋ฆฌ๋กœ ๊ฒฌ๊ณ ํ•œ ํ…Œ์ŠคํŠธ

๐Ÿ” ๋‹ค์ธต์  ๊ฒ€์ฆ ์ฒด๊ณ„

  • UI ๋ ˆ๋ฒจ: ํ™”๋ฉด์— ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ํ‘œ์‹œ๋˜๋Š”๊ฐ€?
  • ์ƒํƒœ ๋ ˆ๋ฒจ: Redux ์Šคํ† ์–ด์— ์ •ํ™•ํžˆ ์ €์žฅ๋˜๋Š”๊ฐ€?
  • ๋ผ์šฐํŒ… ๋ ˆ๋ฒจ: ํŽ˜์ด์ง€ ์ด๋™์ด ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ผ์–ด๋‚˜๋Š”๊ฐ€?
  • ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ๋ ˆ๋ฒจ: ๋„๋ฉ”์ธ ๊ทœ์น™์ด ์ •ํ™•ํžˆ ์ ์šฉ๋˜๋Š”๊ฐ€?

โญ๏ธ ์ฐธ๊ณ  ์ž๋ฃŒ

profile
์ฃผ๋‹ˆ์–ด ํ”„๋ก ํŠธ์—”๋“œ ์„ฑ์žฅ๊ธฐ ๊ธฐ๋ก๊ธฐ๋ก

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