๐Ÿš€ 19. ์ƒํƒœ ๊ด€๋ฆฌ์˜ ๊ถŒ๋ ฅ ๋ถ„๋ฆฝ: ์„œ๋ฒ„(Server) ์ƒํƒœ vs ํด๋ผ์ด์–ธํŠธ(Client) ์ƒํƒœ

๐Ÿ“‹ ๊ฐœ์š”

Redux ํ•˜๋‚˜๋กœ ๋ชจ๋“  ๋ฐ์ดํ„ฐ๋ฅผ ์šฑ์—ฌ๋„ฃ๋˜ ์‹œ๋Œ€๋ฅผ ์ง€๋‚˜, ์™œ ํ˜„๋Œ€ ํ”„๋ก ํŠธ์—”๋“œ๋Š” ์„œ๋ฒ„ ๋ฐ์ดํ„ฐ(TanStack Query)์™€ UI ๋ฐ์ดํ„ฐ(Zustand)๋ฅผ ์ฒ ์ €ํžˆ ๋ถ„๋ฆฌํ•˜๋Š”์ง€ ์•„ํ‚คํ…์ฒ˜ ๊ด€์ ์—์„œ ํ•ด๋ถ€ํ•ฉ๋‹ˆ๋‹ค.

๐ŸŽฏ ์ด ์„น์…˜์„ ์ฝ๊ณ  ๋‚˜๋ฉด:

  • ๊ณผ๊ฑฐ Redux-Thunk๋‚˜ Saga๋กœ ๊ฒŒ์‹œ๊ธ€ ๋ชฉ๋ก์„ ๋ถˆ๋Ÿฌ์˜ค๊ณ  ์บ์‹ฑํ•˜๋˜(๋ณด์ผ๋Ÿฌํ”Œ๋ ˆ์ดํŠธ ์ง€์˜ฅ) ์‹œ๋Œ€๊ฐ€ ์™œ ์ข…๋ง์„ ๋งž์ดํ–ˆ๋Š”์ง€ ์ดํ•ดํ•œ๋‹ค.
  • ์ƒํƒœ(State)์˜ '์„ฑ์งˆ'์„ ํŒŒ์•…ํ•˜์—ฌ, ๋‚ด ๋ฐ์ดํ„ฐ๋ฅผ ์–ด๋””(Zustand vs TanStack Query)์— ๋ณด๊ด€ํ•ด์•ผ ํ• ์ง€ 1์ดˆ ๋งŒ์— ํŒ๋ณ„ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ๋ฐฑ์—”๋“œ DB์˜ ์บ์‹œ ๋ณต์‚ฌ๋ณธ(์„œ๋ฒ„ ์ƒํƒœ)์„ ํ”„๋ก ํŠธ์— ์˜ˆ์˜๊ฒŒ ๋™๊ธฐํ™”ํ•˜๋Š” ์บ์‹ฑ(Caching) ์ „๋žต์˜ ๊ธฐ์ดˆ๋ฅผ ๋‹ค์ง„๋‹ค.

๐Ÿ“‹ ๋ชฉ์ฐจ


๐Ÿ“Œ ์ด ๋ฌธ์„œ๋ฅผ ์ฝ๊ธฐ ์ „์—

โฑ๏ธ ์˜ˆ์ƒ ์ฝ๊ธฐ ์‹œ๊ฐ„: 10๋ถ„ / ํ•ต์‹ฌ ํŒŒํŠธ: 5๋ถ„

๐Ÿ—บ๏ธ ์ด ๋ฌธ์„œ์˜ ๋ฐฐ๊ฒฝ ์„ธ๊ณ„๊ด€: '์˜์ˆ˜๋„ค ์ปค๋ฎค๋‹ˆํ‹ฐ'

  • ์˜์ˆ˜(PM): "์˜์ฒ  ๋‹˜, ์šฐ๋ฆฌ ์•ฑ์€ ๊ฒŒ์‹œ๊ธ€ ๋ชฉ๋ก ์บ์‹ฑ๋„ ํ•ด์•ผ ํ•˜๊ณ , ๋‹คํฌ๋ชจ๋“œ ์„ค์ •๋„ ์ „์—ญ์œผ๋กœ ๊ธฐ์–ตํ•ด์•ผ ํ•˜๊ณ , ์œ ์ €๊ฐ€ ์–ด๋–ค ๋ชจ๋‹ฌ์ฐฝ์„ ๋„์›Œ๋†จ๋Š”์ง€๋„ ๋‹ค ๊ธฐ์–ตํ•ด์•ผ ํ•˜๋Š”๋ฐ ์–ด๋–ป๊ฒŒ ํ•ฉ์ณ ๋†“์œผ์…จ๋‚˜์š”?"
  • ์˜์ฒ (์‹ ์ž…): "๊ฑฑ์ • ๋งˆ์„ธ์š”! ๋งŒ๋Šฅ ๋ณด๋”ฐ๋ฆฌ Redux Store ๋ผ๋Š” ๊ฑฐ๋Œ€ํ•œ ์Šคํ† ์–ด ํ•˜๋‚˜ ํŒ ๋‹ค์Œ์— ๊ฑฐ๊ธฐ์—๋‹ค๊ฐ€ { isDarkMode, isModalOpen, postsList, isLoading, errorMsg } ์‹น ๋‹ค ๋•Œ๋ ค ๋ฐ•์•„ ๋†จ์Šต๋‹ˆ๋‹ค! ์•ก์…˜(Action) ์ˆ˜์‹ญ ๊ฐœ๋ž‘ ๋ฆฌ๋“€์„œ(Reducer) ์ง€์˜ฅ๋งŒ ๊ฑฐ์น˜๋ฉด ๊บผ๋‚ด ์“ธ ์ˆ˜ ์žˆ์–ด์š” ใ…Žใ…Ž"
  • ์˜ํ˜ธ(๋ฆฌ๋“œ): "์˜์ฒ  ๋‹˜, ๊ทธ๋ž˜์„œ ๋‹น์‹ ์˜ ์Šคํ† ์–ด ์ฝ”๋“œ๊ฐ€ 5,000์ค„์ด ๋„˜๋Š” ๊ฒ๋‹ˆ๋‹ค. ์ € ๋ฐ์ดํ„ฐ๋“ค์€ ํƒœ์ƒ๋ถ€ํ„ฐ๊ฐ€ ๋‹ค๋ฅธ ๋‚จ๋‚จ์ž…๋‹ˆ๋‹ค. ํด๋ผ์ด์–ธํŠธ(UI) ์ƒํƒœ์™€ ์„œ๋ฒ„(API) ์ƒํƒœ๋ฅผ ํ•œ ๋ฐ”๊ตฌ๋‹ˆ์— ๋‹ด์œผ๋ฉด ๋ถ€ํŒจํ•ฉ๋‹ˆ๋‹ค. ์˜ค๋Š˜ ๋‹น์žฅ ๊ถŒ๋ ฅ ๋ถ„๋ฆฝ์„ ์‹œ์ž‘ํ•ฉ์‹œ๋‹ค."

๐Ÿค” ์™œ ์•Œ์•„์•ผ ํ•˜๋Š”๊ฐ€: '์ „์—ญ ์Šคํ† ์–ด ๋งŒ๋Šฅ์ฃผ์˜'์˜ ํํ•ด

2010๋…„๋Œ€ ํ›„๋ฐ˜ ํ”„๋ก ํŠธ์—”๋“œ๋Š” "๋ชจ๋“  ์ค‘์š”ํ•œ ๋ฐ์ดํ„ฐ๋Š” ์ „์—ญ(Global - Redux)์œผ๋กœ ๋นผ์ž!"๋ผ๋Š” ์‚ฌ์ƒ์— ๋ฏธ์ณ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ธฐ๊ดดํ•œ ํ˜•ํƒœ์˜ ์Šคํ† ์–ด๊ฐ€ ํƒ„์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.

// โŒ 3๋…„ ์ „ ํ”ํ–ˆ๋˜ ์˜์ฒ ์ด์˜ "์žกํƒ•๋ฐฅ ์ „์—ญ ์ƒํƒœ"
const globalRootStore = {
  // ๐ŸŸข ํด๋ผ์ด์–ธํŠธ(UI) ๊ณ ์œ  ์ƒํƒœ - ์ƒˆ๋กœ๊ณ ์นจํ•˜๋ฉด ๋‚ ์•„๊ฐ€๊ฑฐ๋‚˜ ๋‚ด ๊ธฐ๊ธฐ ๊ธฐ์–ต์—๋งŒ ์˜์กด
  theme: 'dark',
  isSidebarOpen: false,
  cartFloatingXPosition: 150,
 
  // ๐Ÿ”ด ์„œ๋ฒ„(Server) ์ƒํƒœ์˜ ํ”„๋ก์‹œ ๋ณต์‚ฌ๋ณธ - ๋‚จ(DB)์˜ ๋ฐ์ดํ„ฐ๋ฅผ ํ›”์ณ์˜จ ๊ฒƒ
  users: [{ id: 1, name: "์˜ํ˜ธ" }, { id: 2, name: "์˜์ˆ™" }],
  isUsersLoading: false,
  usersError: null,
};

์ด ์ฝ”๋“œ๋Š” ์™œ ๋”์ฐํ• ๊นŒ์š”?

  • **์บ์‹ฑ(Caching)**์˜ ํ•œ๊ณ„: ์˜์ˆ™์ด๊ฐ€ ๋ณธ๋ช… ๋‹‰๋„ค์ž„์„ "์˜์ˆ™_ํ”„๋กœ"๋กœ ๋ฐ”๊ฟจ๋‹ค๊ณ  ์นฉ์‹œ๋‹ค. ๋‚ด users ๋ฐฐ์—ด ๊ฐ์ฒด(ํ”„๋ก ํŠธ ์ „์—ญ ์Šคํ† ์–ด)๋Š” 10์‹œ๊ฐ„ ์ „ ์„œ๋ฒ„์—์„œ ํ›”์ณ์˜จ "๋‚ก๊ณ  ์ฉ์€ ๊ณผ๊ฑฐ์˜ ๋ฐ์ดํ„ฐ(Stale Data)"์ž…๋‹ˆ๋‹ค. ์ด๊ฑธ ์–ธ์ œ ๋ฌดํšจํ™”ํ•˜๊ณ  ๋‹ค์‹œ Fetch ํ• ์ง€, Redux์˜ ์ˆœ์ˆ˜ ํ•จ์ˆ˜ ๋ฉ์–ด๋ฆฌ ์•ˆ์—์„œ ์ง์ ‘ ๋กœ์ง ๊ตฌํ˜„์„ ํ•˜๋ ค๋ฉด ํ”ผ๋ฅผ ํ† ํ•ฉ๋‹ˆ๋‹ค.
  • ์ˆ˜๋™ ์ƒํƒœ ๊ด€๋ฆฌ(Boilerplate):isUsersLoading, usersError ๊ฐ™์€ ์“ฐ๋ ˆ๊ธฐ ํ”Œ๋ž˜๊ทธ ๊ฐ’๋“ค์„ ์•ก์…˜๋งˆ๋‹ค ์ˆ˜๋™์œผ๋กœ ๊ป๋‹ค ์ผฐ๋‹ค(dispatch FETCH_START, FETCH_SUCCESS)ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์•ก์…˜ ํƒ€์ž…๋ช… ์ง“๋‹ค๊ฐ€ ๋‚ ์„ ์ƒ™๋‹ˆ๋‹ค.

๐Ÿ—๏ธ ๋น„์œ ๋กœ ๋จผ์ € ์ดํ•ดํ•˜๊ธฐ

๐Ÿง’ 5์‚ด์—๊ฒŒ ์„ค๋ช…ํ•œ๋‹ค๋ฉด?

  1. ํด๋ผ์ด์–ธํŠธ ์ƒํƒœ(UI State): ๋‚ด ์„œ๋ž์— ์žˆ๋Š” ์ผ๊ธฐ์žฅ์ด๋‚˜ ๊ฑฐ์šธ ๊ฐ๋„์ž…๋‹ˆ๋‹ค. **๋‚˜(Front-end ๋ธŒ๋ผ์šฐ์ €)**๋งŒ์˜ ์†Œ์œ ๊ถŒ๊ณผ ํ†ต์ œ๊ถŒ์„ ์ฅ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋‚ด๊ฐ€ ๊ฐ๋„๋ฅผ ํ‹€๋ฉด ํ‹€์–ด์ง€๊ณ , ๋‹คํฌ๋ชจ๋“œ ์Šค์œ„์น˜๋ฅผ ๋„๋ฉด ๊บผ์ง‘๋‹ˆ๋‹ค. ๋‚ด ๋งˆ์Œ๋Œ€๋กœ ๋น ๋ฆฟ๋น ๋ฆฟํ•˜๊ฒŒ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  2. ์„œ๋ฒ„ ์ƒํƒœ(Server State): ๋„์„œ๊ด€์—์„œ ์–ด์ œ ๋นŒ๋ ค์˜จ ๋ฐฑ๊ณผ์‚ฌ์ „์ž…๋‹ˆ๋‹ค. ์›๋ณธ ์†Œ์œ ๊ถŒ์€ ๋‚˜(ํ”„๋ก ํŠธ)์—๊ฒŒ ์—†๊ณ  **๋„์„œ๊ด€(Backend DB)**์ด ์ฅ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
    ๋‚ด๊ฐ€ ๊ทธ ์ฑ…์„ ์ง‘์— ๋“ค๊ณ  ์™€์„œ ์ฝ๋Š” ๋™์•ˆ, ๋„์„œ๊ด€์žฅ์ด ์ฑ…์˜ ์›๋ž˜ ๋‚ด์šฉ(์˜คํƒ€)์„ ์ˆ˜์ •ํ–ˆ๋‹ค๋ฉด? ๋‚ด ์ฑ…์€ ์ฆ‰์‹œ ๊ฑฐ์ง“(Stale) ์ •๋ณด๊ฐ€ ๋˜์–ด๋ฒ„๋ฆฝ๋‹ˆ๋‹ค. ์ด๊ฑธ ๊ณ„์† ๋ˆˆ์น˜์ฑ„๊ณ  ์ƒˆ ๋ฒ„์ „์œผ๋กœ ๊ฐˆ์•„์น˜์›Œ ์ค˜์•ผ ํ•˜๋Š” ์—„์ฒญ๋‚˜๊ฒŒ ํ”ผ๊ณคํ•œ ๊ด€๋ฆฌ ๋Œ€์ƒ์ž…๋‹ˆ๋‹ค.

์ด์ฒ˜๋Ÿผ "๋‚˜ ํ˜ผ์ž๋งŒ์˜ ์ฐฐ๋‚˜์˜ ๊ธฐ์–ต(UI)"๊ณผ "๋‚จ์˜ ๊ฑด๋ฌผ์—์„œ ํ›”์ณ์˜จ ๋‚ก์€ ๊ธฐ์–ต(Server API)"์„ ๋˜‘๊ฐ™์€ ์„œ๋ž ๋ฐ”๊ตฌ๋‹ˆ(Zustand, Redux)์— ๋„ฃ์–ด ๊ณ ์ธ๋ฌผ๋กœ ์ฉ๊ฒŒ ๋†”๋‘˜ ์ˆœ ์—†์Šต๋‹ˆ๋‹ค. ๋ฐฉ์‹์„ ์™„์ „ํžˆ ๋ถ„๋ฆฌํ•ด์•ผ๋งŒ ํ•ฉ๋‹ˆ๋‹ค.


๐Ÿงฉ ๊ทน์˜ ์ฒด๋“: ๋‘ ์™•์ขŒ์˜ ๋ถ„๋ฆฌ (Zustand & TanStack Query)

์ตœ์‹  ์‹ค๋ฌด ํŒ€(์Šคํƒ€ํŠธ์—…~ํ† ์Šค, ๋‹น๊ทผ, ๋ฐฐ๋ฏผ ๋“ฑ)์˜ ๋Œ€๋ถ€๋ถ„์€ ์ „์—ญ ์ƒํƒœ๋ฅผ ์œ„ํ•ด ๋”ฑ ๋‘ ๊ฐ€์ง€์˜ ๋ฌด๊ธฐ๋งŒ์„ ๊บผ๋‚ด ๋“ญ๋‹ˆ๋‹ค. ์˜์ฒ ์ด์˜ ๊ฑฐ๋Œ€ํ•œ ์žกํƒ• ์Šคํ† ์–ด๊ฐ€ 2๊ฐœ๋กœ ์ฐข์–ด์ง€๋Š” ๊ธฐ์ ์„ ๋ด…์‹œ๋‹ค.

โœ… 1. ์˜ค์ง "๋‚ด(Client)" ๋ฐ์ดํ„ฐ๋งŒ ๊ด€๋ฆฌํ•˜๋Š” ์ดˆ๊ฒฝ๋Ÿ‰ ์Šคํ† ์–ด (Zustand)

๋‹คํฌ๋ชจ๋“œ ๋„๊ณ  ์ผœ๊ธฐ, ํ˜„์žฌ ์ผœ์ ธ ์žˆ๋Š” ๋ชจ๋‹ฌ ID ์ €์žฅ, ๋“œ๋ž˜๊ทธ ์•ค ๋“œ๋กญํ•œ ๋ธ”๋ก์˜ X,Y ์ขŒํ‘œ. ์ด๋“ค์€ ์„œ๋ฒ„๋กœ ๋‚ ์•„๊ฐˆ ์ผ๋„ ์—†๊ณ  ์บ์‹œ ๋  ํ•„์š”๋„ ์—†๋Š” ์˜์›ํ•œ UI ํœ˜๋ฐœ์„ฑ/ํด๋ผ์ด์–ธํŠธ ์ƒํƒœ์ž…๋‹ˆ๋‹ค.
๋‹จ ๋ช‡ ์ค„์˜ ์ฝ”๋“œ๋กœ ์ „์—ญ ๋ณ€์ˆ˜๋ฅผ ์กฐ์ข…ํ•  ์ˆ˜ ์žˆ๋Š” Zustand๊ฐ€ ์ตœ๊ณ  ์กด์—„์œผ๋กœ ๋“ฑ๊ทนํ–ˆ์Šต๋‹ˆ๋‹ค.

// ๐ŸŽฏ ์˜ค์ง ๋ธŒ๋ผ์šฐ์ € UI ์กฐ์ž‘๋งŒ์„ ๋‹ด๋Š” ๊น”๋”ํ•œ Zustand 
import { create } from 'zustand';
 
export const useUIStore = create((set) => ({
  theme: 'dark',
  toggleTheme: () => set((state) => ({ theme: state.theme === 'dark' ? 'light' : 'dark' })),
  
  activeHoverId: null,
  setHoverId: (id) => set({ activeHoverId: id }),
}));

(์ด ๊ณต๊ฐ„์—” ์ ˆ๋Œ€ ๋„คํŠธ์›Œํฌ fetch ๊ฒฐ๊ณผ๋ฌผ์„ ๋„ฃ์ง€ ์•Š์Šต๋‹ˆ๋‹ค!)

โœ… 2. ๊ณจ์น˜ ์•„ํ”ˆ "๋‚จ(Server)"์˜ ๋ฐ์ดํ„ฐ ์บ์‹ฑ ๋งค๋‹ˆ์ € (TanStack Query)

"์„œ๋ฒ„์•ผ ๋ˆ ์ข€ ์ค˜"๋ผ๊ณ  ์š”์ฒญํ•˜๋Š” ์ˆœ๊ฐ„ ๋ฒŒ์–ด์ง€๋Š” ๋กœ๋”ฉ ์ฒ˜๋ฆฌ, ์‹คํŒจ ์‹œ 3๋ฒˆ ์ž๋™ ์žฌ์š”์ฒญ(Retry), ์œ ์ €๊ฐ€ ์ฐฝ์„ ๋‚ด๋ ธ๋‹ค๊ฐ€ 3๋ถ„ ๋’ค ์ผฐ์„ ๋•Œ (Focus) ๋’ค์—์„œ ๋ชฐ๋ž˜ ์ƒˆ ๋ฐ์ดํ„ฐ ๊ฐฑ์‹ ํ•˜๊ธฐ(Stale-While-Revalidate)...

์ด ๋”์ฐํ•œ ์žก๋ฌด๋“ค์„ ์•Œ์•„์„œ ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ์บ์‹ฑ ์ œ๊ตญ์„ ๊ฑด์„คํ•˜์—ฌ ๋‹ค์ด๋ ‰ํŠธ๋กœ ๊ด€๋ฆฌํ•ด ์ฃผ๋Š” ๊ฒƒ์ด **React Query(TanStack Query)**์ž…๋‹ˆ๋‹ค. 16๊ฐ•์˜ Suspense์™€๋„ ์˜ํ˜ผ์˜ ๋‹จ์ง์ž…๋‹ˆ๋‹ค.

// ๐ŸŽฏ ์„œ๋ฒ„ ์ƒํƒœ: ํ”„๋ก ํŠธ๋Š” ๊ทธ์ € Query Key๋กœ ๊ตฌ๋…(Subscribe)๋งŒ ํ•˜๊ณ  ๊ฟ€์„ ๋นค๋‹ค.
import { useQuery } from '@tanstack/react-query';
 
function UserProfile({ userId }) {
  // Redux์˜ Dispatch, useSelector ๊ทธ๋”ด ๊ฑด ์žŠ์œผ์„ธ์š”.
  // ์ด ํ›… ํ•˜๋‚˜๊ฐ€ "์บ์‹ฑ, ๋กœ๋”ฉ, ์—๋Ÿฌ, ์žฌ๊ฐฑ์‹ , ๋ฉ”๋ชจ๋ฆฌ ํ๊ธฐ"๋ฅผ ์‹น ํ˜ผ์ž ๊ด€๋ฆฌํ•ด๋ฒ„๋ฆผ.
  const { data: user, isLoading, error } = useQuery({
    queryKey: ['user', userId], // ๋งˆ์น˜ ์บ์‹œ ์ฐฝ๊ณ ์˜ ๋ฐ”์ฝ”๋“œ(์ด๋ฆ„ํ‘œ) ๊ฐ™์€ ๊ฒƒ!
    queryFn: () => fetch(`/api/users/${userId}`).then(res => res.json()),
    staleTime: 1000 * 60 * 5, // "5๋ถ„ ๋™์•ˆ์€ ๋„์„œ๊ด€์— ๋˜ ์•ˆ ๋ฌผ์–ด๋ณด๊ณ , ๋‚ด ์บ์‹œ ๋ณต์‚ฌ๋ณธ ์“ธ๊ฒŒ!"
  });
 
  if (isLoading) return <Spinner />;
  if (error) return <div>์—๋Ÿฌ๋‚จ!</div>;
 
  return <div>{user.name} ๋‹˜ ํ™˜์˜!</div>;
}

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋ธŒ๋ผ์šฐ์ €๋Š” "์„œ๋ฒ„ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด๊ด€ํ•˜๊ธฐ ์œ„ํ•œ ์ƒํƒœ ๊ด€๋ฆฌ ๋„๊ตฌ"๋ผ๋Š” ๋ง๋ น์—์„œ ์™„์ „ํžˆ ํ•ด๋ฐฉ๋ฉ๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๊ฐ€ ํ”„๋ก ํŠธ์—”๋“œ์—์„œ ๋ฐ”๋ผ๋ณด๋Š” ์„œ๋ฒ„ ๋ฐ์ดํ„ฐ๋Š”, ์ƒํƒœ(State)๋กœ ๋‹ค๋ฃจ๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ๊ทธ๋ƒฅ Query์—๊ฒŒ"์บ์‹œ ๋ฐ์ดํ„ฐ ์ตœ์‹ ๋ฒ„์ „ ๋ณต์‚ฌ๋ณธ ์ข€ ๋Œ€์‹  ๊บผ๋‚ด์„œ ์ž ์‹œ ๋ณด์—ฌ์ค˜"ํ•˜๊ณ  ๋นŒ๋ ค ์“ฐ๋Š” ๋ Œ๋”๋ง ์Šค๋ƒ…์ƒท์— ๋ถˆ๊ณผํ•˜๊ฒŒ ๋˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.


๐Ÿ ์ด๋ฒˆ์— ๋ฐฐ์šด ๋‚ด์šฉ ์ด์ •๋ฆฌ

๊ด€์ UI ์ƒํƒœ (ํด๋ผ์ด์–ธํŠธ ์ƒํƒœ)API ์„œ๋ฒ„ ์ƒํƒœ (Server State)
์†Œ์œ ๊ถŒ (Truth)ํ”„๋ก ํŠธ์—”๋“œ(Client/Browser)๋ฒก์—”๋“œ(DB)
ํŠน์ง•์ฆ‰๊ฐ์ , ๋™๊ธฐ์ , ์ผ์‹œ์  (ํ™”๋ฉด ๋‹ซ์œผ๋ฉด ํœ™ ๋‚ ์•„๊ฐ)๋น„๋™๊ธฐ์ , ์–ธ์ œ๋“ ์ง€ ๊ตฌํ˜• ๋ฐ์ดํ„ฐ(Stale)๋กœ ์ฉ์–ด๋ฒ„๋ฆด ์œ„ํ—˜ ์กด์žฌ
์ ํ•ฉํ•œ ๋ฐ์ดํ„ฐ ์˜ˆ์‹œ๋ชจ๋‹ฌ ์—ด๋ฆผ ์—ฌ๋ถ€, ๋‹คํฌ ๋ชจ๋“œ, ์ธํ’‹ ์ž…๋ ฅ๊ฐ’๊ฒŒ์‹œํŒ ๊ธ€ ๋ชฉ๋ก, ๋‚ด ํ”„๋กœํ•„ ์„ค์ •, JWT ๊ถŒํ•œ ์ •๋ณด
๋Œ€ํ‘œ์ ์ธ ์‹ค๋ฌด ํˆดZustand, Jotai, Recoil, Redux(๋ฌด๊ฑฐ์›€)TanStack Query (๊ตฌ React Query), SWR

๐Ÿ’ก ํ•œ ์ค„๋กœ ๊ธฐ์–ตํ•˜๊ธฐ
"๋‚ด ๋ฐ์ดํ„ฐ๋Š” Zustand์— ์ˆจ๊ธฐ๊ณ , ๋‚จ(์„œ๋ฒ„)์˜ ๋ฐ์ดํ„ฐ๋Š” Query์—๊ฒŒ ์™ธ์ฃผ(Outsourcing) ์ค˜๋ฒ„๋ ค๋ผ."
์ด ๊ฑฐ๋Œ€ํ•œ ๋‘ ์ถ•๋งŒ ๋ช…ํ™•ํžˆ ๊ฐˆ๋ผ๋‚ด๋„, ๋‹น์‹ ์˜ ํ”„๋ก ํŠธ์—”๋“œ ์„ค๊ณ„ ์•„ํ‚คํ…์ฒ˜๋Š” ํ† ์Šค๋‚˜ ์นด์นด์˜ค๊ธ‰ ์‹œ๋‹ˆ์–ด์˜ ์‹œ์•ผ๋ฅผ ๊ฐ–์ถ˜ ๊ฒƒ๊ณผ ๋‹ค๋ฆ„์—†๋‹ค.


๐Ÿฃ ์˜์ฒ ์ด์˜ ํ‡ด๊ทผ ์ผ๊ธฐ

Redux ํ•˜๋‚˜์— ๋ชจ๋“  ์„ธ์ƒ์˜ ์ง์„ ๋‹ค ์งŠ์–ด์ง€๊ฒŒ ํ•˜๋˜ ๋‚ด ์ฝ”๋“œ๊ฐ€ ์–ผ๋งˆ๋‚˜ ๋ฏธ๋ จํ–ˆ๋Š”์ง€ ๊นจ๋‹ฌ์•˜๋‹ค. ํด๋ผ์ด์–ธํŠธ ๋ฐ์ดํ„ฐ๋ž‘ ์„œ๋ฒ„ ๋ฐ์ดํ„ฐ๊ฐ€ ์• ์ดˆ์— ์œ ํšจ๊ธฐ๊ฐ„๋ถ€ํ„ฐ ๋‹ค๋ฅธ ๋‚จ๋‚จ์ด์—ˆ๋‹ค๋‹ˆ.

๐Ÿ’ก "๋ช…์‹ฌํ•˜์ž. ๋‚ด ์•ฑ์˜ ์ˆœ์ˆ˜ UI ์ƒํƒœ(Zustand)์™€ ์„œ๋ฒ„์—์„œ ๋นŒ๋ ค์˜จ ์žฅ๋ถ€ ๋ณต์‚ฌ๋ณธ(TanStack Query)์€ ์™„์ „ํžˆ ๋ถ„๋ฆฌํ•ด์„œ ๊ด€๋ฆฌํ•ด์•ผ ํ•œ๋‹ค."

์ด '๊ถŒ๋ ฅ ๋ถ„๋ฆฝ' ํŒจํ„ด๋Œ€๋กœ๋ฉด ๋ณด์ผ๋Ÿฌํ”Œ๋ ˆ์ดํŠธ ์ฝ”๋“œ ์ˆ˜๋ฐฑ ์ค„์ด ๊ทธ๋ƒฅ ๋‚ ์•„๊ฐ„๋‹ค. ๋‹น์žฅ ๋‚ด์ผ ์ถœ๊ทผํ•ด์„œ ๊ทธ ๋ฌด๊ฒ๊ณ  ๋А๋ฆฐ Redux ๊ฑท์–ด๋‚ด๊ณ  Zustand๋ž‘ React Query ๋„์ž…ํ•˜์ž๊ณ  ๊ธฐ์•ˆ์„œ๋ถ€ํ„ฐ ์˜ฌ๋ ค์•ผ๊ฒ ๋‹ค. ํ† ์Šค๋‚˜ ๋‹น๊ทผ์—์„œ ์™œ ์ด ์กฐํ•ฉ์„ ์“ฐ๋Š”์ง€ ์•Œ ๊ฒƒ ๊ฐ™์•„ ์†์ด ๋ปฅ ๋šซ๋ฆฐ๋‹ค.


๐Ÿ“ ๋งˆ๋ฌด๋ฆฌ ํ€ด์ฆˆ

Q1. ๋‹น์‹ ์ด ์‡ผํ•‘๋ชฐ ์žฅ๋ฐ”๊ตฌ๋‹ˆ ํŽ˜์ด์ง€๋ฅผ ์„ค๊ณ„ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ฐ ์žฅ๋ฐ”๊ตฌ๋‹ˆ ์•„์ดํ…œ์˜ ์ˆ˜๋Ÿ‰์„ '์ฆ๊ฐ€(+)/๊ฐ์†Œ(-)'์‹œํ‚ค๋Š” ๋ฒ„ํŠผ์„ ๋ˆŒ๋ €์„ ๋•Œ ๋ฒŒ์–ด์ง€๋Š” ์ผ์„ ์„ค๊ณ„ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. "UI ์ƒํƒœ"์™€ "์„œ๋ฒ„ ์ƒํƒœ"์˜ ๋ถ„๋ฆฌ ์›์น™์— ๋น„์ถ”์–ด ๋ณผ ๋•Œ, ๊ฐ€์žฅ ์˜ฌ๋ฐ”๋ฅธ ๋ฉ˜ํƒˆ ๋ชจ๋ธ์ด๋‚˜ ํ–‰๋™ ๋ฐฉ์‹์€ ๋ฌด์—‡์ธ๊ฐ€์š”?

  • A) [+] ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅธ ์ฆ‰์‹œ Zustand ์ƒํƒœ ๋ณ€์ˆ˜๋กœ cartItems[0].quantity + 1์„ ๋•Œ๋ ค ๋ฐ•์•„ ์ „์—ญ ์ƒํƒœ๋ฅผ ์˜ค์—ผ์‹œํ‚ค๊ณ , ๊ตณ์ด DB ์„œ๋ฒ„๋กœ ์žฌ์ „์†กํ•˜์ง€ ์•Š์€ ์ฑ„ ์œ ์ €๊ฐ€ ๊ฒฐ์ œ ์ฐฝ์œผ๋กœ ๋„˜์–ด๊ฐˆ ๋•Œ๋งŒ API๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.
  • B) [+] ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ์ด๊ฑด ๋ช…๋ฐฑํžˆ ๋ฐฑ์—”๋“œ ์žฅ๋ฐ”๊ตฌ๋‹ˆ DB ํ…Œ์ด๋ธ”(Server State)์„ ์ˆ˜์ •ํ•ด์•ผ ํ•˜๋Š” ์ง€์‹œ์ด๋ฏ€๋กœ, TanStack Query์˜ Mutation ๊ธฐ๋Šฅ ๋“ฑ์„ ํ™œ์šฉํ•ด ์„œ๋ฒ„๋กœ ๋ช…๋ น์„ ๋‚ ๋ฆฐ๋‹ค. ์„ฑ๊ณต ์‘๋‹ต์ด ๋–จ์–ด์ง€๋ฉด, ์–ฝํ˜€์žˆ๋Š” ํ•ด๋‹น '์žฅ๋ฐ”๊ตฌ๋‹ˆ ์ฟผ๋ฆฌ ๋ฌดํšจํ™”(Invalidation)'๋ฅผ ๋•Œ๋ ค์„œ ์„œ๋ฒ„์˜ ์ตœ์‹  ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค์‹œ ์Šค๋ฌผ์Šค๋ฌผ ๋ฐ›์•„์™€ ํ™”๋ฉด์— ์ƒˆ ์ˆซ์ž๋ฅผ ๋ Œ๋”๋งํ•œ๋‹ค.
  • C) ์ด ์ƒํƒœ๋Š” ํด๋ผ์ด์–ธํŠธ์˜ ํ†ต์ œ๋ฅผ ๋”ฐ๋ฅด๋ฏ€๋กœ LocalStorage์— ์ €์žฅํ•˜์—ฌ ์บ์‹œ ๋งค๋‹ˆ์ € ๋Œ€์‹  ๋ธŒ๋ผ์šฐ์ € ์—”์ง„์— ์˜จ์ „ํžˆ ์œ„์ž„ํ•ด์•ผ ํ•œ๋‹ค.

โœ… ์ •๋‹ต: B

๐Ÿ’ก ์ƒ์„ธ ํ•ด์„ค: ํ›Œ๋ฅญํ•œ ๊ถŒ๋ ฅ ๋ถ„๋ฆฝ ์„ค๊ณ„์ž…๋‹ˆ๋‹ค. ์žฅ๋ฐ”๊ตฌ๋‹ˆ ์ˆ˜๋Ÿ‰์€ ๊ฒฐ๊ตญ ์œ ์ €์˜ ์ง€๊ฐ‘(๊ฒฐ์ œ)๊ณผ ์—ฐ๊ฒฐ๋  "๋‚จ์˜ ํ†ต์ œ(์„œ๋ฒ„ ์žฅ๋ถ€)" ๋ฐ์ดํ„ฐ์ด๋ฏ€๋กœ, ๋‚ด๊ฐ€ ๋กœ์ปฌ ์ƒํƒœ(UI)๋กœ ํ•จ๋ถ€๋กœ ์ฆ‰์‹œ ์ˆ˜์ •ํ•˜์—ฌ ๋–ต๋–ต๊ฑฐ๋ฆฌ๋ฉด ์•ˆ ๋ฉ๋‹ˆ๋‹ค. ๋ฌด์กฐ๊ฑด ์„œ๋ฒ„์—๊ฒŒ "ํ•˜๋‚˜ ๋” ๋Š˜๋ ค์ฃผ์„ธ์š”"๋ผ๊ณ  ํ—ˆ๋ฝ(mutate)์„ ๊ตฌํ•˜๊ณ , ์›๋ณธ ์žฅ๋ถ€(DB)๊ฐ€ ์ˆ˜์ •๋œ ํ›„ ๊ทธ ์ตœ์‹  ๋ฐ์ดํ„ฐ(Query)๋ฅผ ์ƒˆ๋กœ๊ณ ์นจ(Invalidate)์‹œ์ผœ ํ™”๋ฉด์— ๋ฐ˜์˜ํ•˜๋Š” ํ๋ฆ„์ด ์•ˆ์ „ํ•œ ๋™๊ธฐํ™”์˜ ๊ธฐ๋ณธ์ž…๋‹ˆ๋‹ค. (๋ฌผ๋ก  Optimistic Update๋ผ๋Š” ๊ธฐ๋ฒ•๋„ ์žˆ์ง€๋งŒ, ์ด๋…์  ๋ณธ์งˆ์€ ์ผ๋งฅ์ƒํ†ตํ•ฉ๋‹ˆ๋‹ค.)

Q2. ์‹ ์ž…์ธ ์˜์ฒ ์ด๊ฐ€ Context API์˜ ๋‚ด์žฅ ๊ธฐ๋Šฅ๋งŒ์œผ๋กœ ์‡ผํ•‘๋ชฐ ์ „์ฒด์˜ ์ƒํ’ˆ ๋ชฉ๋ก(10๋งŒ ๊ฐœ ๋ฐ์ดํ„ฐ)์„ ๋กœ๋”ฉ/์บ์‹ฑ/๊ธฐ์–ตํ•˜๋Š” ์‹œ์Šคํ…œ์„ ๊ฐœ๋ฐœํ•ด ์™”์Šต๋‹ˆ๋‹ค. ์˜ํ˜ธ ์‹œ๋‹ˆ์–ด๊ฐ€ ์ด๋ฅผ ๋ณด๊ณ  ํ˜ธํ†ต์น˜๋ฉฐ TanStack Query๋กœ ๋ฐ”๊พธ๋ผ๊ณ  ํ•œ ์น˜๋ช…์ ์ธ ๋‹จ์ ๋“ค์„ ๋ชจ๋‘ ๋‚˜์—ดํ•˜๊ณ , Query๋„๊ตฌ๋“ค์ด ์ด๋Ÿฐ ๋ฌธ์ œ ๋ฉ์–ด๋ฆฌ๋ฅผ ์–ด๋–ป๊ฒŒ ํ•ด๊ฒฐํ•ด์ฃผ๋Š”์ง€ ์ง์ ‘ ์ฃผ๊ด€์‹์œผ๋กœ ์„ค๋ช…ํ•ด๋ณด์„ธ์š”.

โœ… ์ •๋‹ต ๋ฐ ์ฃผ๊ด€์‹ ํ•ด์„ค:
"์˜์ฒ ์ด ๋ฐฉ์‹์˜ ์น˜๋ช…์ ์ธ ๋‹จ์ ์€, Context API์— ๋‚ด๋ ค๋ฐ›์€ 10๋งŒ ๊ฐœ ๋ฐ์ดํ„ฐ ๋ฐฐ์—ด์€ ์‹œ๊ฐ„์ด ์ง€๋‚˜๋ฉด '์‹ ์„ ๋„'๋ฅผ ์žƒ์–ด๋ฒ„๋ฆฌ๋Š” ์ผํšŒ์„ฑ ๋ฐ”๊ตฌ๋‹ˆ์— ๋ถˆ๊ณผํ•˜๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ์œ ์ €๊ฐ€ ์žฌ๊ณ ๋ฅผ ๋‹ค ์‚ฌ๊ฐ€์„œ 0๊ฐœ๊ฐ€ ๋˜์—ˆ๋Š”๋ฐ, ์˜์ฒ ์ด์˜ ์บ์‹œ(Context state)๋Š” 100๊ฐœ๊ฐ€ ๋‚จ์•˜๋‹ค๊ณ  **๊ฑฐ์ง“๋ง(Stale)**์„ ์น  ๊ฒƒ์ž…๋‹ˆ๋‹ค.
์ด๊ฑธ ํ•ด๊ฒฐํ•˜๋ ค๋ฉด ์˜์ฒ ์ด๊ฐ€ ์ง์ ‘ setTimeOut์ด๋‚˜ ์Šคํฌ๋กค ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์ด๋ฒคํŠธ ๋กœ์ง ๋“ฑ์„ ๋ชจ์กฐ๋ฆฌ ์ˆ˜๋ฐฑ ์ค„ ์†์œผ๋กœ ์งœ์•ผ ํ•˜์ฃ . TanStack Query๋Š” ๋‹จ์ง€ staleTime: 5000 ํ•œ ์ค„ ์˜ต์…˜๋งŒ ๋„˜๊ฒจ์ฃผ๋ฉด, ํฌ์ปค์Šค๊ฐ€ ๋Œ์•„์™”์„ ๋•Œ๋‚˜, 5์ดˆ๊ฐ€ ์ง€๋‚ฌ์„ ๋•Œ ์ž๊ธฐ ํ˜ผ์ž์„œ ์•Œ์•„์„œ ๋’ค์—์„œ ๋ชฐ๋ž˜ ํ†ต์‹ ํ•˜๊ณ (Background Fetching) ์ฉ์€ ๋ถ€๋ถ„์„ ์ตœ์‹ ํ™”๋œ ์„œ๋ฒ„ ๋ฐ์ดํ„ฐ๋กœ ์™„๋ฒฝํ•˜๊ฒŒ ๋ฒ„๋ฌด๋ ค ์บ์‹ฑ(Caching)๊นŒ์ง€ ๋Œ€ํ–‰ํ•ด์ฃผ๋Š” ์„ ์–ธ์  ๊ฐฑ์‹ (Revalidate)์˜ ์•…๋งˆ์ด๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค."