๐พ 05. WebSocket, SSE ๋ฑ ์ค์๊ฐ ํต์ ๋ง๊ณผ ์บ์ ๋๊ธฐํ
๐ ๊ฐ์
์๋ฒ๊ฐ ํธ์ํ๋ ์๋ฐฉํฅ ์ค์๊ฐ ์ด๋ฒคํธ(Socket/SSE)๋ฅผ React Query์ QueryCache์ ๊ฐ์ฅ ๊ฒฝ๋ํ๋๊ณ ์์ ํ๊ฒ ์ฃผ์ ํ๋ ๋ ๊ฐ์ง ๊ฐ๋ฆผ๊ธธ์ ๋ฐฐ์๋๋ค.
๐ ๋ชฉ์ฐจ
- โ๏ธ ์์ฒ ์ด์ ๊ณ ๋ฏผ: "ํธ์(Push)๊ฐ ์๋๋ฐ ๋ ๋น๊ธฐ(Pull)๋๊น ๋๋ ค์"
- ๐ค ์ ์์์ผ ํ๋๊ฐ: ์บ์ ์๋ฒ ๋๊ธฐํ์ ๋๋ ๋ง
- 1. 1๋ฒ ์ฑ๋: ๋ถ๋ถ ์ด๋ฒคํธ (์๊ทน์ ์๋น - Invalidate)
- 2. 2๋ฒ ์ฑ๋: ์์ ํ ์ด๋ฒคํธ (๊ณต๊ฒฉ์ ์์ฉ - ๋๊ด์ ์บ์ ์ฃผ์ )
- 3. SSE (Server-Sent Events) ์์ ๊ฒฐํฉ ์ํคํ ์ฒ
- ๐ ๋ฐฐ์ด ๋ด์ฉ ์ ๊ฒํ๊ธฐ (Quiz)
"์์ฒ ๋, ์น์์ผ์ผ๋ก '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๊ฐ ํ ์ ์๋ ํ๋์ ๋ ๊ฐ์ง ๊ฐ๋ฆผ๊ธธ ์ ๋๋ค.
- ์๊ทน์ ์๋น (Invalidation): "์ค ์ ํธ ๋กํ. ๊ทผ๋ฐ ๋๊ฐ ์ค ์ด๋ฒคํธ ๋ฐ์ดํฐ ๋๋ฌด ์ชผ๋งํด์ ๋ชป ๋ฏฟ๊ฒ ์ด. ๊ท์ฐฎ์ง๋ง ๋ด๊ฐ ์๋ฒ์
REST API๋ค์ ์ด์ ์ต์ ๋ฆฌ์คํธ ํต์งธ๋ก ๋ค ๋์ด์ฌ๊ฒ." - ๊ณต๊ฒฉ์ ์์ฉ (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์ธํ ๋ ์ฑ๊ฒจ๋ผ!