๐Ÿ’พ 05. WebSocket, SSE ๋“ฑ ์‹ค์‹œ๊ฐ„ ํ†ต์‹ ๋ง๊ณผ ์บ์‹œ ๋™๊ธฐํ™”

2026๋…„ 3์›” 4์ผ ์ˆ˜์ •๋จ

๐Ÿ“‹ ๊ฐœ์š”

์„œ๋ฒ„๊ฐ€ ํ‘ธ์‹œํ•˜๋Š” ์–‘๋ฐฉํ–ฅ ์‹ค์‹œ๊ฐ„ ์ด๋ฒคํŠธ(Socket/SSE)๋ฅผ React Query์˜ QueryCache์— ๊ฐ€์žฅ ๊ฒฝ๋Ÿ‰ํ™”๋˜๊ณ  ์•ˆ์ „ํ•˜๊ฒŒ ์ฃผ์ž…ํ•˜๋Š” ๋‘ ๊ฐ€์ง€ ๊ฐˆ๋ฆผ๊ธธ์„ ๋ฐฐ์›๋‹ˆ๋‹ค.

๐Ÿ“‹ ๋ชฉ์ฐจ

"์˜์ฒ  ๋‹˜, ์›น์†Œ์ผ“์œผ๋กœ '1๋ฒˆ ๊ฒŒ์‹œ๊ธ€ ์ง€์›Œ์ง' ์‹ ํ˜ธ ๋ฐ›์•˜๋‹ค๊ณ  ๊ตณ์ด ๋‹ค์‹œ API fetch ๋ฅผ ๋‚ ๋ฆฌ๋ฉด ๊ทธ ๋น ๋ฅธ ์›น์†Œ์ผ“ ์„œ๋ฒ„๋ฅผ ํŠผ ์ด์œ ๊ฐ€ ์—†์ž–์•„์š”!"

โ˜•๏ธ ์˜์ฒ ์ด์˜ ๊ณ ๋ฏผ: "ํ‘ธ์‹œ(Push)๊ฐ€ ์™”๋Š”๋ฐ ๋˜ ๋‹น๊ธฐ(Pull)๋‹ˆ๊นŒ ๋А๋ ค์š”"

(ํ™”์š”์ผ ์˜คํ›„, ์‹ค์‹œ๊ฐ„ ํŠธ๋ ˆ์ด๋”ฉ ๋Œ€์‹œ๋ณด๋“œ ํ™”๋ฉด ๊ฐ€๊ฒฉ์ด ํŠ€๋Š” ๊ฑธ ๋ณด๋ฉฐ ๋‹นํ™ฉํ•˜๋Š” ์˜์ฒ )

๐Ÿฃ ์˜์ฒ : ์ €๊ธฐ, ๋ฆฌ๋“œ ๋‹˜! ํŒ€์žฅ๋‹˜์ด "์ฆ๊ถŒ ์•ฑ์ฒ˜๋Ÿผ ๋น„ํŠธ์ฝ”์ธ ์‹œ์„ธ๋‚˜ ์‹ค์‹œ๊ฐ„ ๋‹ฌ๋ฆฐ ๋Œ“๊ธ€์€ ์ƒˆ๋กœ๊ณ ์นจ ์•ˆ ํ•ด๋„ ํŒํŒ ๋ฐ”๋€Œ๊ฒŒ ํ•ด์ฃผ์„ธ์š”" ๋ผ๊ณ  ํ•˜์…”์„œ ์–ด์ œ ๋ฐค์ƒˆ WebSocket(์›น์†Œ์ผ“) ์—ฐ๊ฒฐ์„ ๋งˆ์ณค๊ฑฐ๋“ ์š”!

์†Œ์ผ“ ์ฑ„๋„์—์„œ {"event":"updated", "id":100} ์ด๋ ‡๊ฒŒ ๋ฉ”์‹œ์ง€๊ฐ€ ๋˜‘ ๋–จ์–ด์ง€๋ฉด,
์ œ๊ฐ€ ๋ƒ‰ํผ queryClient.invalidateQueries({ queryKey: ['posts'] }) ์œผ๋กœ ์บ์‹œ ํŒ ๋ฌดํšจํ™” ์ณ์„œ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ๋ฆฌํŒจ์นญ(GET /posts)์„ ๋บ‘๋บ‘ ๋Œ๊ฒŒ ์งฐ์Šต๋‹ˆ๋‹ค! ์–ด๋•Œ์š”? ์‹ค์‹œ๊ฐ„ ์ฉ”์ฃ ?

๐Ÿฆ ์˜ํ˜ธ: (์ด๋งˆ๋ฅผ ์งš์œผ๋ฉฐ) ์˜์ฒ  ๋‹˜... ์ง€๊ธˆ 1์ดˆ์— ๋Œ“๊ธ€์ด 50๊ฐœ์”ฉ ๋‹ฌ๋ฆฌ๋Š” ํ•ซํ•œ ์ปค๋ฎค๋‹ˆํ‹ฐ ๊ธ€์— ๋งค์ดˆ๋งˆ๋‹ค ์›น์†Œ์ผ“ ์‹ ํ˜ธ(updated)๊ฐ€ ๋‚ด๋ ค์˜ฌ ํ…๋ฐ, ๊ทธ๋•Œ๋งˆ๋‹ค ๋ฐฑ๊ทธ๋ผ์šด๋“œ fetch /posts ๋ฅผ 50๋ฒˆ์”ฉ ๋‚ ๋ฆฌ์‹œ๊ฒ ๋‹ค๊ณ ์š”...? ์šฐ๋ฆฌ ์˜์ˆ˜(๋ฐฑ์—”๋“œ)๋‹˜์ด ๋””๋„์Šค(DDoS) ๊ณต๊ฒฉ ์˜๋ƒ๊ณ  ๋ฉฑ์‚ด ์žก์œผ๋Ÿฌ ์˜ต๋‹ˆ๋‹ค.
WebSocket์˜ ๋ชฉ์ ์€ "์„œ๋ฒ„๊ฐ€ ์ด๋ฏธ ์ตœ์‹  ๋ฐ์ดํ„ฐ๋ฅผ ์ž…์— ๋ฌผ๊ณ  ํ‘ธ์‹œ(Push)ํ•ด ์คฌ์œผ๋‹ˆ, ํด๋ผ์ด์–ธํŠธ๊ฐ€ API๋ฅผ ์˜์ง€ ๋ง๊ณ (Pull) ๊ทธ ๋ฐ์ดํ„ฐ๋กœ ๋„ค ์บ์‹œ ํ†ต๋งŒ ์‚ด์ง ์—…๋ฐ์ดํŠธ์ณ๋ผ" ์— ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฌดํšจํ™”(invalidate)๋ฅผ ๋‚จ๋ฐœํ•˜๋ฉด ์ตœ์•…์˜ ๋ณ‘๋ชฉ์ด ํ„ฐ์ ธ์š”.


๐Ÿค” ์™œ ์•Œ์•„์•ผ ํ•˜๋Š”๊ฐ€: ์บ์‹œ ์„œ๋ฒ„ ๋™๊ธฐํ™”์˜ ๋”œ๋ ˆ๋งˆ

ํ˜„๋Œ€ ์›น ํŠธ๋ Œ๋“œ(ํŠนํžˆ ์ฑ„ํŒ…, ์•Œ๋ฆผ, ์ฃผ์‹, ํ˜‘์—… ํˆด) ์—์„œ๋Š” REST API (๋‚ด๊ฐ€ ๋‹น๊น€, Pull) ์™€ WebSocket/SSE (์„œ๋ฒ„๊ฐ€ ์ด์คŒ, Push) ๋‘ ๊ฐ€์ง€ ํŒŒ์ดํ”„๋ผ์ธ์ด ํ•˜๋‚˜์˜ ํ™”๋ฉด(UI) ์•ˆ์—์„œ ๊ฒฉ๋ ฌํ•˜๊ฒŒ ์ถฉ๋Œํ•ฉ๋‹ˆ๋‹ค.

์„œ๋ฒ„๊ฐ€ "์•ผ! 1๋ฒˆ ๊ธ€ ๋ฐ”๊ผˆ๋‹ค!" ๋ผ๊ณ  Push ์‹ ํ˜ธ๋ฅผ ์คฌ์„ ๋•Œ React Query๊ฐ€ ํ•  ์ˆ˜ ์žˆ๋Š” ํ–‰๋™์€ ๋‘ ๊ฐ€์ง€ ๊ฐˆ๋ฆผ๊ธธ ์ž…๋‹ˆ๋‹ค.

  1. ์†Œ๊ทน์  ์ˆ˜๋น„ (Invalidation): "์˜ค ์‹ ํ˜ธ ๋•กํ. ๊ทผ๋ฐ ๋„ˆ๊ฐ€ ์ค€ ์ด๋ฒคํŠธ ๋ฐ์ดํ„ฐ ๋„ˆ๋ฌด ์ชผ๋งŒํ•ด์„œ ๋ชป ๋ฏฟ๊ฒ ์–ด. ๊ท€์ฐฎ์ง€๋งŒ ๋‚ด๊ฐ€ ์„œ๋ฒ„์— REST API ๋‹ค์‹œ ์ด์„œ ์ตœ์‹  ๋ฆฌ์ŠคํŠธ ํ†ต์งธ๋กœ ๋‹ค ๋Œ์–ด์˜ฌ๊ฒŒ."
  2. ๊ณต๊ฒฉ์  ์ˆ˜์šฉ (setQueryData): "์˜ค ์‹ ํ˜ธ ๋•กํ! ๋„ˆ๊ฐ€ ๋ณ€๊ฒฝ๋œ ๋ฐ์ดํ„ฐ({likes: 5}) ๊ตฌ์›Œ์คฌ์œผ๋‹ˆ๊นŒ, ๋‚ด๊ฐ€ ๋„คํŠธ์›Œํฌ ์•ˆ ์˜๊ณ  ๋‚ด ๋กœ์ปฌ ๋ฉ”๋ชจ๋ฆฌ ์บ์‹œํ†ต ์กฐ๋ฆฝํ•ด์„œ ์‹น ๊ฐ–๋‹ค ๋ถ™์ผ๊ฒŒ!"

์–ด๋–ค ์ƒํ™ฉ์— ๋ฌด์—‡์„ ํƒํ•˜๋Š”์ง€๊ฐ€ ์‹ค์‹œ๊ฐ„ ์•„ํ‚คํ…์ฒ˜์˜ ํผํฌ๋จผ์Šค๋ฅผ ๊ฐ€๋ฆ…๋‹ˆ๋‹ค. (TkDodo ์•„ํ‹ฐํด #7)


1. 1๋ฒˆ ์ฑ„๋„: ๋ถ€๋ถ„ ์ด๋ฒคํŠธ (์†Œ๊ทน์  ์ˆ˜๋น„ - Invalidate)

๋งŒ์•ฝ ์›น์†Œ์ผ“ ์ด๋ฒคํŠธ ๋ฉ์–ด๋ฆฌ๊ฐ€ ๋งค์šฐ ๋นˆ์•ฝํ•˜๊ฒŒ ๋„˜์–ด์˜ค๋Š” ๊ฒฝ์šฐ์ž…๋‹ˆ๋‹ค.
๊ฐ€๋ น Payload๊ฐ€ ๋‹จ์ง€ {"type": "POST_UPDATED", "postId": 5} ์ •๋„๋งŒ ์ฐํ˜€์„œ ์™”์Šต๋‹ˆ๋‹ค. "๋ฌด์—‡"์ด ๋ฐ”๋€Œ์—ˆ๋Š”์ง€๋Š” ์•ˆ ์•Œ๋ ค์ฃผ๊ณ  "๋ฐ”๋€Œ์—ˆ์Œ!" ์ด๋ผ๋Š” ํŒŒ๋‹ฅ๊ฑฐ๋ฆผ๋งŒ ์˜ค๋Š” ๊ฑฐ์ฃ .

์ด๋Ÿด ๋• ์–ด์ฉ” ์ˆ˜ ์—†์ด ๋ฌดํšจํ™”๋ฅผ ์ณ์•ผ ํ•˜์ง€๋งŒ, ๋””๋„์Šค(DDoS) ๋ฐฉ์–ด ๋งค์ปค๋‹ˆ์ฆ˜ ์„ ์„ธํŒ…ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

import { useEffect } from 'react';
import { useQueryClient } from '@tanstack/react-query';
 
function usePostSocketHandling() {
  const queryClient = useQueryClient();
 
  useEffect(() => {
    // ์†Œ์ผ“์—์„œ ์ด๋ฒคํŠธ๊ฐ€ ํŒŒ๋ฐ”๋ฐ•- ์ˆ˜์‹ ๋  ๋•Œ
    socket.on('POST_UPDATED', (eventMessage) => {
      // โš ๏ธ ์˜์ฒ ์ด์˜ ์ˆœ์ง„ํ•œ ํญ๊ฒฉ
      // 1์ดˆ์— 10๋ฒˆ ์†Œ์ผ“์ด ์˜ค๋ฉด fetch GET ์š”์ฒญ์ด ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ 10๋ฒˆ ํ„ฐ์ง„๋‹ค ๐Ÿ’ฅ
      // queryClient.invalidateQueries({ queryKey: ['posts'] });
 
      // ๐Ÿš€ ์˜ํ˜ธ ๋ฆฌ๋“œ์˜ ๋””๋„์Šค ๋ฐฉ์–ด๋ง‰ (Throttling Invalidation)
      queryClient.invalidateQueries({ 
        queryKey: ['posts'],
        // "๋„คํŠธ์›Œํฌ ํ†ต์‹ ์ด ์—ฐ์‡„์ ์œผ๋กœ ๋“ค์–ด์™€๋„ ๋‹ค ๋ฌด์‹œํ•˜๊ณ , ๋”ฑ 1๋ฒˆ๋งŒ ์ทจํ•ฉํ•ด์„œ ๋‹ค์‹œ ๊ฐ€์ ธ์™€๋ผ!" 
        // 1์ดˆ ๊ฐ„๊ฒฉ ์•ˆ์— ๋ฐœ์ƒํ•œ ๋ฌดํšจํ™”๋Š” ๋ณ‘ํ•ฉ(Batch) ์ฒ˜๋ฆฌ๋จ.
        cancelRefetch: false 
      });
    });
 
    return () => socket.off('POST_UPDATED');
  }, [queryClient]);
}

2. 2๋ฒˆ ์ฑ„๋„: ์™„์ „ํ•œ ์ด๋ฒคํŠธ (๊ณต๊ฒฉ์  ์ˆ˜์šฉ - ๋‚™๊ด€์  ์บ์‹œ ์ฃผ์ž…)

์„œ๋ฒ„๊ฐ€ ๋„ˆ๋ฌด๋‚˜ ์นœ์ ˆํ•˜๊ฒŒ ๋ฐ”๋€ ๋ฐ์ดํ„ฐ ๊ป๋ฐ๊ธฐ๋ฅผ ํ†ต์งธ๋กœ ๋‹ค ์ด์ฃผ๋Š” ๊ฒฝ์šฐ(์˜ˆ: { "id": 5, "title": "์ˆ˜์ •๊ธ€", "likes": 12 })์—” ์ ˆ๋Œ€๋กœ fetch๋ฅผ ๋‹ค์‹œ ์  ์ด์œ ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค!

๋ฉ”๋ชจ๋ฆฌ ์•ˆ์˜ QueryCache ์ฃผ๋จธ๋‹ˆ๋ฅผ ์ง์ ‘ ์—ด๊ณ , ํ•€์…‹์œผ๋กœ ํ•ด๋‹น ๊ฐ์ฒด๋งŒ ์ฐฉ! ๊ฐˆ์•„๋ผ์šฐ๋Š” setQueryData (ํ”„๋ก ํŠธ ์กฐ๋ฆฝ ๋ฐฉ์‹) ์ด ์„œ๋ฒ„ ๋น„์šฉ์„ 0์›์œผ๋กœ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

// ์†Œ์ผ“์ด ์˜จ์ „ํ•œ ์ตœ์‹  ๊ฒŒ์‹œ๊ธ€(Post) ๋ฐ์ดํ„ฐ๋ฅผ ํ‘ธ์‹œํ•˜๋Š” ๊ฒฝ์šฐ
type Post = { id: number; title: string; likes: number };
 
function useLivePostUpdates() {
  const queryClient = useQueryClient();
 
  useEffect(() => {
    socket.on('INCOMING_POST_UPDATE', (newPost: Post) => {
      
      // ๐Ÿš€ ๋ฐ์ดํ„ฐ ์กฐ๋ฆฝ์‹ ์ฃผ์ž… (๋„คํŠธ์›Œํฌ ์š”์ฒญ ๋ฐœ์ƒ๋ฅ  0%)
      queryClient.setQueryData<Post[]>(['posts'], (oldPosts) => {
        // ๋งŒ์•ฝ ๋‚ด ํ™”๋ฉด ์บ์‹œ ๊ธˆ๊ณ ๊ฐ€ ํ…… ๋น„์—ˆ๋‹ค๋ฉด? ๋ฌด์‹œํ•ด๋ฒ„๋ฆผ (ํ™”๋ฉด์— ์•ˆ ๋„์›Œ๋†จ์œผ๋‹ˆ ์•Œ ๋ฐ” ์•„๋‹˜)
        if (!oldPosts) return; 
 
        // ์บ์‹œ ์†์— ์žˆ๋Š” 100๊ฐœ ๋ฆฌ์ŠคํŠธ ์ค‘, ๋ฐฉ๊ธˆ ์†Œ์ผ“์œผ๋กœ ๋‚ ์•„์˜จ ๋…€์„๋งŒ ๊ฐˆ์•„๋ผ์šด๋‹ค!
        return oldPosts.map((post) => 
          post.id === newPost.id ? newPost : post
        );
      });
      
    });
 
    return () => socket.off('INCOMING_POST_UPDATE');
  }, [queryClient]);
}

์ด ํŒจํ„ด์ด ๋“ค์–ด๊ฐ€๋ฉด, ๋ˆ„๊ตฐ๊ฐ€๊ฐ€ ๊ฒŒ์‹œํŒ์— ๊ธ€์„ ์˜ฌ๋ฆฌ๊ฑฐ๋‚˜ ๋Œ“๊ธ€์„ ๋‹ค๋Š” ์ฆ‰์‹œ ๋‚ด ํ™”๋ฉด์— 1ms์˜ ๋กœ๋”ฉ ์Šคํ”ผ๋„ˆ์˜ ํ”์ ๋„ ์—†์ด ์Šค๋ฌด์Šคํ•˜๊ฒŒ ์ตœ์‹  ๊ธ€์ด '์Šคํฐ(Spawn)' ๋ฉ๋‹ˆ๋‹ค. ์ง„์ •ํ•œ ์‹ค์‹œ๊ฐ„(Realtime) ํ†ต์‹ ์˜ ์œ„๋ ฅ์ด์ฃ !


3. SSE (Server-Sent Events) ์™€์˜ ๊ฒฐํ•ฉ ์•„ํ‚คํ…์ฒ˜

์ฑ„ํŒ…์ด ์•„๋‹ˆ๋ผ ๋‹จ์ˆœ ๋‹จ๋ฐฉํ–ฅ ์•Œ๋ฆผ(์•Œ๋ฆผ ์ข…๋‹ฌ๊ธฐ์žฅ ์ˆซ์ž ๋“ฑ) ์ด๋ผ๋ฉด ๋ฌด๊ฑฐ์šด WebSocket ๋Œ€์‹  HTTP ๊ธฐ๋ฐ˜์˜ SSE(Server-Sent Events) ๋กœ ๊ฐ€๋ณ๊ฒŒ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ํŠธ๋ Œ๋“œ์ž…๋‹ˆ๋‹ค. React Query์— SSE๋ฅผ ๋ถ™์ผ ๋• ๋ณดํ†ต ์ปดํฌ๋„ŒํŠธ ์ƒ๋ช…์ฃผ๊ธฐ์™€ ์ „์—ญ ํ›…(QueryClient ํŒฉํ† ๋ฆฌ) ์ค‘ ํ›„์ž์— ๋ถ™์ด๋Š” ๊ฒƒ์ด ๋ฉ”๋ชจ๋ฆฌ ์•ˆ์ „์„ฑ์— ์ข‹์Šต๋‹ˆ๋‹ค.

// ๐Ÿ“ providers/get-query-client.ts ์˜ ์ดˆ๊ธฐํ™” ๋ธ”๋ก
let sseConnection: EventSource | null = null;
 
export function getQueryClient() {
  const client = new QueryClient({ ... });
 
  // ํด๋ผ์ด์–ธํŠธ(๋ธŒ๋ผ์šฐ์ €)์—์„œ ๋‹จ ์ตœ์ดˆ 1ํšŒ๋งŒ SSE ๋ฐฑ๊ทธ๋ผ์šด๋“œ ํ„ฐ๋„์„ ์˜๊ตฌ ๊ฐœํ†ต!
  if (typeof window !== 'undefined' && !sseConnection) {
    sseConnection = new EventSource('/api/stream/notifications');
    
    // ๋ฐ์ดํ„ฐ๊ฐ€ ์Ÿ์•„์งˆ ๋•Œ๋งˆ๋‹ค ์บ์‹œ ๊ธˆ๊ณ ์— ์ฆ‰๊ฐ ๊ฝ‚์•„๋ฒ„๋ฆผ
    sseConnection.onmessage = (event) => {
      const liveData = JSON.parse(event.data);
      client.setQueryData(['notifications'], liveData);
    };
  }
 
  return client;
}

์ด๋Ÿฌ๋ฉด ๋‹น์‹ ์˜ ์•ฑ์€ ์ ‘์†ํ•˜์ž๋งˆ์ž ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ์˜ ์ถ•๋ณต์„ ๋ฐ›๋Š” ๊ณ ๋„๋กœ ์ตœ์ ํ™”๋œ ์ŠคํŠธ๋ฆฌ๋ฐ ์•ฑ์ด ๋ฉ๋‹ˆ๋‹ค.


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

์™€, ๋‚˜ ๊ทธ๋™์•ˆ ์›น์†Œ์ผ“ ์ด๋ฒคํŠธ ๋–จ์–ด์งˆ ๋•Œ๋งˆ๋‹ค ์ƒ๊ฐ์—†์ด Invalidate ๋‚ ๋ ธ์—ˆ๋Š”๋ฐ, ์˜์ˆ˜ ๋‹˜์ด ๋‚˜ํ•œํ…Œ ๊ฟ€๋ฐค ๋•Œ๋ฆฌ๋Ÿฌ ์˜จ ์ด์œ ๋ฅผ ์•Œ๊ฒ ๋‹ค...
์ดˆ๋‹น 10๋ฒˆ ๋ฐ”๋€Œ๋Š” ๋น„ํŠธ์ฝ”์ธ ์‹œ์„ธ ๋ฐ์ดํ„ฐ๋ฅผ 10๋ฒˆ GET ์š”์ฒญ์œผ๋กœ ๋‹ค์‹œ ์˜๊ณ  ์žˆ์—ˆ์œผ๋‹ˆ ์„œ๋ฒ„ CPU๊ฐ€ ํ„ฐ์ง€์ง€;;

๐Ÿ’ก ์˜ค๋Š˜์˜ ๊ตํ›ˆ: "์„œ๋ฒ„๊ฐ€ ํ‘ธ์‹œ(Push) ํ•˜๋Š” ์ด๋ฒคํŠธ๊ฐ€ Partial(์ผ๋ถ€) ์ด๋ผ๋ฉด invalidate ๋น”์„ ์˜๋˜ Batch/๋””๋„์Šค ๋ฐฉ์–ด๋ฅผ ๊ผญ ๊ฑธ์–ด๋‘๊ณ ! ์„œ๋ฒ„๊ฐ€ ํ‘ธ์‹œ ํ•˜๋Š” ์ด๋ฒคํŠธ๊ฐ€ Full(์ „์ฒด) ์ด๋ผ๋ฉด ์ ˆ๋Œ€ ๋„คํŠธ์›Œํฌ๋ฅผ ๋‹ค์‹œ ์˜์ง€ ๋ง๊ณ  setQueryData ํ•€์…‹ ์กฐ๋ฆฝ์œผ๋กœ ์šฐ์•„ํ•˜๊ฒŒ ์บ์‹œ๋ฅผ ๊ฐˆ์•„ ๋ผ์šฐ์ž!"

์‚ฌ์‹ค setQueryData ์จ์„œ ๋กœ์ปฌ ๋ฉ”๋ชจ๋ฆฌ ๋ฐฐ์—ด map ๋Œ๋ฆฌ๋Š” ๋กœ์ง ์งœ๋Š” ๊ฒŒ ์กฐ๊ธˆ ๊ท€์ฐฎ๊ธด ํ•œ๋ฐ, ์ด๊ฑฐ ๋ฐ˜์˜ํ•˜๊ณ  ๋‚˜๋‹ˆ๊นŒ ๋„คํŠธ์›Œํฌ ํƒญ์— ๋ถˆํ•„์š”ํ•œ HTTP ํ†ต์‹ ์ด ๋‹จ 1๊ฑด๋„ ์•ˆ ์ฐํžˆ๊ณ  ํด๋ผ์ด์–ธํŠธ ์ž์ฒด์ ์œผ๋กœ ์Šค๋ฌด์Šคํ•˜๊ฒŒ ํ™”๋ฉด์ด ๊นœ๋นก์ด๋Š” ๊ฑฐ ๋ณด๊ณ  ์พŒ๊ฐ ์˜ค์กŒ๋‹ค... ๋‚ด์ผ์€ ์œ ์ € ์ธํ’‹ ๋‹ค๋ฃจ๋Š” ํผ(Form) ์ž‘์—… ํ•ด๋ด์•ผ์ง€! ๐Ÿ’ป


๐Ÿ“ ๋ฐฐ์šด ๋‚ด์šฉ ์ ๊ฒ€ํ•˜๊ธฐ (Quiz)

Q. ์˜์ฒ ์ด๋Š” ์‹ค์‹œ๊ฐ„ ์ฑ„ํŒ…๋ฐฉ ํ™”๋ฉด์„ ๋งŒ๋“ค๋ฉด์„œ, "์ƒˆ๋กœ์šด ๋ฉ”์‹œ์ง€(new_chat)" ์›น์†Œ์ผ“ ์ด๋ฒคํŠธ๊ฐ€ ์˜ค๋ฉด setQueryData๋กœ ๋ฌดํ•œ ์Šคํฌ๋กค(useInfiniteQuery)์˜ ๊ณผ๊ฑฐ ๋ฐ์ดํ„ฐ ๋ฐฐ์—ด ๋์— ๋ฉ”์‹œ์ง€๋ฅผ ๊ฐ์ฒด๋ฅผ ํ•˜๋‚˜ ํ‘ธ์‹œ(Push) ํ•ด์ฃผ๋Š” ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ ์น˜๋ช…์ ์ธ ๋ฒ„๊ทธ๋ฅผ ๋ฐœ๊ฒฌํ–ˆ์Šต๋‹ˆ๋‹ค. ์œ ์ €๊ฐ€ ์ฑ„ํŒ…๋ฐฉ ํ™”๋ฉด์„ ๋‹ซ์•˜๋‹ค๊ฐ€(๋‹ค๋ฅธ ๋ฉ”๋‰ด ์ด๋™ ํ›„ ์–ธ๋งˆ์šดํŠธ), ์ฆ‰์‹œ 1๋ถ„(staleTime ๊ธฐ๋ณธ๊ฐ’์ธ 0์ดˆ ์ƒํƒœ, gcTime ์ƒ์กด ์ƒํƒœ) ๋’ค ์ฑ„ํŒ…๋ฐฉ์— ๋‹ค์‹œ ์ง„์ž…ํ–ˆ๋”๋‹ˆ ํ™”๋ฉด์— ๋ฐฉ๊ธˆ ์›น์†Œ์ผ“์œผ๋กœ ์—ด์‹ฌํžˆ ์ถ”๊ฐ€ํ•ด ๋„ฃ์—ˆ๋˜ ๋ฉ”์„ธ์ง€๊ฐ€ ์‚ฌ๋ผ์กŒ๋‹ค๊ฐ€ ๊นœ๋นก ํ•˜๋ฉด์„œ ์ตœ์‹  ๋ฐ์ดํ„ฐ๋กœ ๋‹ค์‹œ ๋ Œ๋”๋ง ๋ฉ๋‹ˆ๋‹ค. ์™œ ์ด ํ˜„์ƒ์ด ํ„ฐ์ง€๋Š” ๊ฑธ๊นŒ์š”?

  • A) ๋ธŒ๋ผ์šฐ์ € ํƒญ์„ ๋ฐ”๊ฟจ๋”๋‹ˆ ์›น์†Œ์ผ“ ์—ฐ๊ฒฐ์ด ์Šค์Šค๋กœ ๋Š์–ด์ ธ ๋ฒ„๋ ค์„œ ์บ์‹œ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ๊ฐ™์ด ์ฆ๋ฐœํ–ˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.
  • B) ์†Œ์ผ“์ด ์•„๋ฌด๋ฆฌ ์บ์‹œ(QueryCache)๋ฅผ ์ •์„ฑ์Šค๋ ˆ ์ˆ˜์ž‘์—…์œผ๋กœ ์ฑ„์›Œ๋†จ์–ด๋„, staleTime: 0 (๊ธฐ๋ณธ๊ฐ’) ์ƒํƒœ์—์„œ ์œ ์ €๊ฐ€ ์ปดํฌ๋„ŒํŠธ์— ๋‹ค์‹œ ๋งˆ์šดํŠธ(์ง„์ž…)ํ•˜๋Š” ์ˆœ๊ฐ„ "์–ด? ์ด๊ฑฐ ์ƒํ•œ๋นต์ด๋„ค?" ํ•˜๊ณ  ์ฆ‰๊ฐ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ๋ฆฌํŒจ์นญ(GET /chats) ์„ ์ด๋ฒ„๋ ค, ์ˆ˜์ž‘์—… ์ด๋ ฅ์ด ์„œ๋ฒ„ ์ดˆ๊นƒ๊ฐ’ ํŠธ๋žœ์žญ์…˜์œผ๋กœ ๋‚ ์•„๊ฐ”๊ธฐ(๋ฎ์–ด์”Œ์›Œ์กŒ๊ธฐ) ๋•Œ๋ฌธ์ด๋‹ค.
  • C) setQueryData๋Š” ๋ฉ”๋ชจ๋ฆฌ ํœ˜๋ฐœ์„ฑ์ด๋ผ์„œ ํŽ˜์ด์ง€๋ฅผ ์ด๋™ํ•˜๋Š” ์ˆœ๊ฐ„ ์ฆ‰์‹œ Garbage Collect(์‚ญ์ œ ๋จ) ์ฒ˜๋ฆฌ๋˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

โœ… ์ •๋‹ต: B

๐Ÿ’ก ์ƒ์„ธ ํ•ด์„ค:

  • ์›๋ฆฌ ์„ค๋ช…: TkDodo ์•„ํ‹ฐํด #7์—์„œ ์งš์–ด์ฃผ๋Š” ๊ฐ€์žฅ ํ”ํ•œ WebSocket ์—ฐ๋™ ํ•จ์ •์ž…๋‹ˆ๋‹ค. ๋‹น์‹ ์ด setQueryData ๋กœ ์•„๋ฌด๋ฆฌ ์—ด์‹ฌํžˆ ํ”„๋ก ํŠธ ์บ์‹œ์— ์ตœ์‹  ๋ฐ์ดํ„ฐ๋ฅผ ์šฑ์—ฌ๋„ฃ์–ด๋„... ํ•ด๋‹น ๋ฐ์ดํ„ฐ์˜ ์œ ํ†ต๊ธฐํ•œ(staleTime)์ด ์งง๊ฑฐ๋‚˜ 0์ด๋ฉด ์œ ์ €๊ฐ€ ๋‹ค๋ฅธ ํƒญ์„ ์ฐ๊ณ  ๋‹ค์‹œ ์ปดํฌ๋„ŒํŠธ์— ํฌ์ปค์‹ฑ(refetchOnWindowFocus ํ˜น์€ Mount) ํ•˜๋Š” ์ˆœ๊ฐ„ React Query๋Š” "์ƒํ•œ ๊ฑฐ๋‹ˆ๊นŒ ๋ฒ„๋ฆฌ๊ณ  ์„œ๋ฒ„์—์„œ ํ†ต์งธ๋กœ ์Ž„๋ฒผ์˜ค์ž!" ๋ฐœ๋™์„ ๊ฒ๋‹ˆ๋‹ค. ์ด๋•Œ ์•„์ฃผ ์ฐฐ๋‚˜์˜ ์ˆœ๊ฐ„ ๋™์•ˆ ๊นœ๋นก์ž„์ด๋‚˜ ๋ฐ์ดํ„ฐ ๋ฏธ์Šค๋งค์น˜๊ฐ€ ํ„ฐ์ง‘๋‹ˆ๋‹ค.
  • ์˜ค๋‹ต ํ”ผ๋“œ๋ฐฑ: "์˜์ฒ  ๋‹˜, ์›น์†Œ์ผ“์œผ๋กœ ์—ด์‹ฌํžˆ ์บ์‹œ๋ฅผ ๋ฐฅ ๋จน์—ฌ ๋†จ์œผ๋ฉด, ์ ์–ด๋„ ๊ทธ ๋ฐ์ดํ„ฐ๊ฐ€ ๋‹น๋ถ„๊ฐ„์€ ์‹ ์„ ํ•˜๋‹ค๋Š” ๊ฑธ React Queryํ•œํ…Œ ์ธ์ง€์‹œ์ผœ์ค˜์•ผ ํ•ฉ๋‹ˆ๋‹ค. WebSocket ์—ฐ๋™ ๋ฐ์ดํ„ฐ๋ฅผ ์œ ์ง€ํ•  ๋•Œ๋Š” ํ•ด๋‹น useQuery์˜ staleTime์„ Infinity ๋“ฑ์œผ๋กœ ๋„‰๋„‰ํžˆ ๊ฑธ์–ด ์‹ ์„ ๋„ ๊ด€๋ฆฌ๋ฅผ ์Šค์Šค๋กœ ํ•˜๊ฒŒ๋” ํ†ต์ œ๊ถŒ์„ ์™„์ „ํžˆ ๋„˜๊ฒจ๋ฐ›์œผ์…”์•ผ ํ•ฉ๋‹ˆ๋‹ค!"
  • ๐Ÿ“Œ ํ•ต์‹ฌ ๊ธฐ์–ต๋ฒ•: ์›น์†Œ์ผ“ setQueryData ๋ฎ์–ด์“ฐ๊ธฐ ๋กœ์ง ์งค ๋•Œ๋Š”, ๋ฌด์กฐ๊ฑด ์Œ๋‘ฅ์ด์ฒ˜๋Ÿผ staleTime: Infinity ์„ธํŒ…๋„ ์ฑ™๊ฒจ๋ผ!