๐Ÿ“ ๊ฐœ๋ฐœ

TanStack Query์˜ ๊ตฌ์กฐ์  ๊ณต์œ ์™€ React ๋ฆฌ๋žœ๋”๋ง์— ๋Œ€ํ•œ ํ•™์Šต๊ณผ ๊ด€์ฐฐ


TanStack Query๋ฅผ ์“ฐ๋‹ค๊ฐ€ ๋ฌธ๋“ ์ด๋Ÿฐ ์˜๋ฌธ์ด ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.

"์„œ๋ฒ„์—์„œ ๋ฐ›์•„์˜ค๋Š” ๋ฐ์ดํ„ฐ๋Š” ๋งค๋ฒˆ ์ƒˆ๋กœ์šด JSON ๊ฐ์ฒด๋กœ ๋‚ด๋ ค์™€ ์ฐธ์กฐ๊ฐ’์ด ๋งค๋ฒˆ ๋‹ฌ๋ผ์งˆํ…๋ฐ, ์ด๋Ÿฌ๋ฉด TanStack Query๊ฐ€ ๋ฆฌํŽ˜์น˜ํ•  ๋•Œ๋งˆ๋‹ค ๋ชจ๋“  ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ฆฌ๋ Œ๋”๋ง๋˜๋Š” ๊ฑธ๊นŒ??"

์ด ๊ธ€์€ ์ด๋Ÿฐ ์˜๋ฌธ์„ ํ•ด์†Œํ•˜๊ธฐ ์œ„ํ•ด ๊ณต๋ถ€ํ•˜๊ณ  ํ…Œ์ŠคํŠธํ–ˆ๋˜ ๊ณผ์ •์„ ์ •๋ฆฌํ•œ ๊ธฐ๋ก์ž…๋‹ˆ๋‹ค.

Tanstack query์˜ ๊ตฌ์กฐ์  ๊ณต์œ 

TanStack Query๋Š” ๋‚ด์šฉ์ด ๊ฐ™์œผ๋ฉด ์ฐธ์กฐ ๋™์ผ์„ฑ์„ ์œ ์ง€ํ•˜๋„๋ก ์„ค๊ณ„๋˜์–ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ์ฐธ์กฐ ๋™์ผ์„ฑ์ด๋ž€ ๋ฌด์—‡์ผ๊นŒ์š”? ์•„๋ž˜ ์˜ˆ์‹œ๋ฅผ ํ†ตํ•ด ์„ค๋ช…ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

JS์—์„œ๋Š” ๋ฉ”๋ชจ๋ฆฌ ์ฃผ์†Œ๋ฅผ ์ง์ ‘ ๋ณผ ์ˆ˜ ์—†์ง€๋งŒ, ์„ค๋ช… ํŽธ์˜๋ฅผ ์œ„ํ•ด ์ž„์‹œ๋กœ ๋ฉ”๋ชจ๋ฆฌ ์ฃผ์†Œ๋ฅผ ํ‘œ๊ธฐํ–ˆ์Šต๋‹ˆ๋‹ค.

[
  { "id": 1, "title": "์ฑ„ํŒ… 1" }, //@memory_address: 1
  { "id": 2, "title": "์ฑ„ํŒ… 2" }, //@memory_address: 2
]

๋งŒ์•ฝ id๊ฐ€ 1์ธ ์ฑ„ํŒ…์˜ title์ด ์ฑ„ํŒ… 1 -> ์ฑ„ํŒ… 123์œผ๋กœ ๋ฐ”๋€Œ๋Š” ๊ฒฝ์šฐ,

[
-    { "id": 1, "title": "์ฑ„ํŒ… 1" }, //@memory_address: 1
+    { "id": 1, "title": "์ฑ„ํŒ… 123" }, //@memory_address: 3
     { "id": 2, "title": "์ฑ„ํŒ… 2" }, //@memory_address: 2
]

์œ„ ์˜ˆ์‹œ์—์„œ id:2 ํ•ญ๋ชฉ์€ ๋‚ด์šฉ์ด ๋ฐ”๋€Œ์ง€ ์•Š์•„ ์ด์ „ ์ฐธ์กฐ๊ฐ’์ด ์œ ์ง€๋ฉ๋‹ˆ๋‹ค. ๋ฐ˜๋ฉด id:1 ํ•ญ๋ชฉ์€ ๋‚ด์šฉ์ด ๋ฐ”๋€Œ์–ด ์ƒˆ ์ฐธ์กฐ๋กœ ๊ต์ฒด๋ฉ๋‹ˆ๋‹ค. TanStack Query๋Š” ๊ฐ’์ด ๋™์ผํ•  ๋•Œ ๊ธฐ์กด ๊ฐ์ฒด์˜ ์ฐธ์กฐ๋ฅผ ์žฌ์‚ฌ์šฉํ•˜๋Š” ๊ตฌ์กฐ์  ๊ณต์œ ๋กœ ๋‚ด๋ถ€ ์บ์‹œ๊ฐ€ ์ตœ์ ํ™”๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

์‹ค์ œ ๋žœ๋”๋ง ์‚ดํŽด๋ณด๊ธฐ

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

// ์‚ฌ์šฉ๋œ API ์‘๋‹ต ๊ตฌ์กฐ
[
  // ๋ฆฌํŒจ์น˜๊ฐ€ ๋ฐœ์ƒํ•  ๋•Œ ๋งˆ๋‹ค id:1์˜ ์ฑ„ํŒ…๋ฐฉ์˜ ์ด๋ฆ„์ด ๋ณ€๊ฒฝ๋˜๋Š” ๋™์ž‘์œผ๋กœ ๊ตฌํ˜„๋จ.
  { "id": 1, "title": "์ฑ„ํŒ… 1" },
  { "id": 2, "title": "์ฑ„ํŒ… 2" },
]
 
// ์ปดํฌ๋„ŒํŠธ ๋žœ๋”๋ง ์ฝ”๋“œ
function ChatList() {
  const { data: chatList } = useGetChatsSuspenseQuery(undefined, { refetchInterval: 500 });
 
  return (
    <div>
      <h3>์ฑ„ํŒ… ๋ชฉ๋ก</h3>
      <div className='flex flex-col gap-2 p-2 border max-h-70 overflow-y-auto'>
        {chatList.map((chat) => (
          <ChatItem key={chat.id} chat={chat} />
        ))}
      </div>
    </div>
  );
}
 
type Props = {
  chat: ChatResponseDto;
};
 
function ChatItem({ chat }: Props) {
  return <div className='flex flex-col gap-2 rounded border p-2'>{chat.title}</div>;
}

์ฑ„ํŒ…๋ฐฉ ๋ชฉ๋ก์„ ์กฐํšŒํ•˜๋Š” ๊ฐ„๋‹จํ•œ ์ปดํฌ๋„ŒํŠธ์ž…๋‹ˆ๋‹ค. refetchInterval: 500์„ ์‚ฌ์šฉํ•ด 0.5์ดˆ๋งˆ๋‹ค API๊ฐ€ ๋ฆฌํŽ˜์น˜๋˜๋„๋ก ์„ค์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. TanStack Query์˜ ๊ตฌ์กฐ์  ๊ณต์œ ์— ๋”ฐ๋ฅด๋ฉด, id:1 ์ฑ„ํŒ…๋ฐฉ๋งŒ ๋ฆฌ๋ Œ๋”๋ง๋˜๊ณ  id:2 ์ฑ„ํŒ…๋ฐฉ์€ ๋ฆฌ๋ Œ๋”๋ง๋˜์ง€ ์•Š์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ์ƒ๊ณผ ๋‹ฌ๋ฆฌ React DevTools๋กœ ์ปดํฌ๋„ŒํŠธ ๋ฆฌ๋ Œ๋”๋ง์„ ์‹œ๊ฐ์ ์œผ๋กœ ํ™•์ธํ•ด๋ณด๋‹ˆ, ๋ฆฌํŽ˜์น˜๊ฐ€ ๋ฐœ์ƒํ•  ๋•Œ๋งˆ๋‹ค ๋ชจ๋“  ChatItem์ด ๋ฆฌ๋ Œ๋”๋ง๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ–ˆ์Šต๋‹ˆ๋‹ค.

์™œ ๊ทธ๋Ÿด๊นŒ์š”? ๋ฐ”๋กœ id:1์„ ์ œ์™ธํ•œ ํ•ญ๋ชฉ๋“ค์€ ๊ฐ ์•„์ดํ…œ์˜ ์ฐธ์กฐ๋Š” ์œ ์ง€๋˜์ง€๋งŒ, ์ƒ์œ„์˜ chatList ๋ฐฐ์—ด ์ž์ฒด๋Š” ์ƒˆ ๊ฐ์ฒด์ด๊ธฐ ๋•Œ๋ฌธ์ธ๋ฐ์š”.

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

/**
 * This function returns `a` if `b` is deeply equal.
 * If not, it will replace any deeply equal children of `b` with those of `a`.
 * This can be used for structural sharing between JSON values for example.
 */
export function replaceEqualDeep<T>(a: unknown, b: T): T
export function replaceEqualDeep(a: any, b: any): any {
  if (a === b) {
    return a
  }
 
  const array = isPlainArray(a) && isPlainArray(b)
 
  if (array || (isPlainObject(a) && isPlainObject(b))) {
    const aItems = array ? a : Object.keys(a)
    const aSize = aItems.length
    const bItems = array ? b : Object.keys(b)
    const bSize = bItems.length
    const copy: any = array ? [] : {}  // copy๋ฅผ ์ƒ์„ฑํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ฐธ์กฐ ๋‹ฌ๋ผ์ง
    const aItemsSet = new Set(aItems)
 
    let equalItems = 0
 
    for (let i = 0; i < bSize; i++) {
      const key = array ? i : bItems[i]
      if (
        ((!array && aItemsSet.has(key)) || array) &&
        a[key] === undefined &&
        b[key] === undefined
      ) {
        copy[key] = undefined
        equalItems++
      } else {
        copy[key] = replaceEqualDeep(a[key], b[key])
        if (copy[key] === a[key] && a[key] !== undefined) {
          equalItems++
        }
      }
    }
 
    return aSize === bSize && equalItems === aSize ? a : copy
  }
 
  return b
}

์ด๋กœ ์ธํ•ด ChatList๊ฐ€ ๋ฆฌ๋ Œ๋”๋˜๊ณ , React์—์„œ ๋ถ€๋ชจ๊ฐ€ ๋ฆฌ๋ Œ๋”๋  ๊ฒฝ์šฐ React.memo ๋“ฑ ๋ณ„๋„์˜ ์ตœ์ ํ™”๊ฐ€ ์ ์šฉ๋˜์–ด ์žˆ์ง€ ์•Š์œผ๋ฉด ์ž์‹ ์ปดํฌ๋„ŒํŠธ๋“ค์ด ๋ชจ๋‘ ๋‹ค์‹œ ๋ Œ๋”๋ฉ๋‹ˆ๋‹ค. ๊ฒฐ๊ณผ์ ์œผ๋กœ ๋ชจ๋“  ChatItem ์ปดํฌ๋„ŒํŠธ์— ๋ฆฌ๋žœ๋”๋ง์ด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋ ‡๋‹ค๋ฉด ๊ตฌ์กฐ์  ๊ณต์œ ๊ฐ€ ์†Œ์šฉ ์—†๋Š”๊ฑฐ ์•„๋‹Œ๊ฐ€?

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

React.Memo๋ฅผ ์‚ฌ์šฉํ•œ ๋ฆฌ๋žœ๋”๋ง ์ตœ์ ํ™”

function ChatList() {
  const { data: chatList } = useGetChatsSuspenseQuery(undefined, { refetchInterval: 500 });
 
  return (
    <div>
      <h3>์ฑ„ํŒ… ๋ชฉ๋ก</h3>
      <div className='flex flex-col gap-2 p-2 border max-h-70 overflow-y-auto'>
        {chatList.map((chat) => (
          <ChatMemo key={chat.id} chat={chat} />
        ))}
      </div>
    </div>
  );
}
 
type Props = {
  chat: ChatResponseDto;
};
 
const ChatMemo = React.memo(function Chat({ chat }: Props) {
  return <div className='flex flex-col gap-2 rounded border p-2'>{chat.title}</div>;
});

ChatItem์„ React.memo๋กœ ๊ฐ์‹ธ๋ฉด, ๋ถ€๋ชจ(ChatList)๊ฐ€ ๋ฆฌ๋ Œ๋”๋˜๋”๋ผ๋„ ์ž์‹์€ ์ „๋‹ฌ๋œ props์˜ ์–•์€ ๋น„๊ต ๊ฒฐ๊ณผ๊ฐ€ ๊ฐ™์„ ๋•Œ๋งŒ ๋ Œ๋”๋ฅผ ๊ฑด๋„ˆ๋œ๋‹ˆ๋‹ค. ๊ตฌ์กฐ์  ๊ณต์œ  ๋•๋ถ„์— ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์€ ํ•ญ๋ชฉ์˜ ์ฐธ์กฐ๋Š” ์œ ์ง€๋˜๋ฏ€๋กœ, ์‹ค์ œ๋กœ๋Š” id:1์ฒ˜๋Ÿผ ์ฐธ์กฐ๊ฐ€ ๋ฐ”๋€ ํ•ญ๋ชฉ๋งŒ ๋ฆฌ๋ Œ๋”๋ฉ๋‹ˆ๋‹ค.

์œ„ ์ด๋ฏธ์ง€๋Š” ChatItem์„ React.memo๋กœ ๊ฐ์ŒŒ์„ ๋•Œ์˜ ๋™์ž‘์ž…๋‹ˆ๋‹ค. ChatList๊ฐ€ ๋ฆฌ๋ Œ๋”๋˜๋”๋ผ๋„ props๊ฐ€ ๋™์ผํ•œ ํ•ญ๋ชฉ๋“ค์€ ๋ Œ๋”๋ฅผ ๊ฑด๋„ˆ๋›ฐ๊ณ , id:1์ฒ˜๋Ÿผ props๊ฐ€ ๋ฐ”๋€ ํ•ญ๋ชฉ๋งŒ ๋‹ค์‹œ ๋ Œ๋”๋ง๋ฉ๋‹ˆ๋‹ค.

select๋ฅผ ์‚ฌ์šฉํ•œ ์„ ํƒ์  ๊ตฌ๋…

function ChatList() {
  const { data: chatList } = useGetChatsSuspenseQuery(undefined, {
    select: (list) => list.map((c) => c.id),
  });
 
  return (
    <div className='p-4'>
      <h3>์ฑ„ํŒ… ๋ชฉ๋ก (select)</h3>
      <div className='flex flex-col gap-2 p-2 border max-h-70 overflow-y-auto'>
        {/* ๋ถ€๋ชจ๋Š” ids๋งŒ ๊ณ„์‚ฐํ•ด์„œ ๋„˜๊น€ */}
        {chatList.map((id) => (
          <ChatBySelect key={id} id={id} />
        ))}
      </div>
    </div>
  );
}
 
type Props = {
  chat: ChatResponseDto;
};
 
/**
 * ChatBySelect: ์ž์‹ ๋‚ด๋ถ€์—์„œ useGetChatsSuspenseQuery์˜ select๋กœ ์ž๊ธฐ ํ•ญ๋ชฉ๋งŒ ๊ตฌ๋…
 * - select๋Š” ์›๋ณธ ๊ฐ์ฒด๋ฅผ ๊ทธ๋Œ€๋กœ ๋ฐ˜ํ™˜ํ•ด์•ผ ์ฐธ์กฐ ๋ณด์กด์˜ ์ด์ ์ด ์žˆ๋‹ค.
 */
function ChatBySelect({ id, render }: { id: number; render: (key: string) => React.ReactNode }) {
  const { data: chat } = useGetChatsSuspenseQuery(undefined, {
    select: (list) => list.find((c) => c.id === id),
  });
 
  if (!chat) return null;
  return <div className='flex flex-col gap-2 rounded border p-2'>{chat.title}</div>;
}

select๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ChatList๋Š” id ๋ชฉ๋ก๋งŒ ๊ตฌ๋…ํ•˜๊ณ , ์ž์‹ ์ปดํฌ๋„ŒํŠธ(ChatBySelect)๋Š” ์ž์‹ ์ด ์‚ฌ์šฉํ•˜๋Š” ํ•ญ๋ชฉ๋งŒ select๋กœ ๊ตฌ๋…ํ•˜๋„๋ก ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

React.Memo๋ฅผ ์‚ฌ์šฉํ–ˆ๋˜ ์˜ˆ์‹œ์™€ ๋‹ค๋ฅด๊ฒŒ ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ์—์„œ๋„ ๋ฆฌ๋ Œ๋”๋ง์ด ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์™œ๋ƒํ•˜๋ฉด chatList ๋ฐฐ์—ด ์ž์ฒด์˜ ์ฐธ์กฐ๊ฐ€ ๋ Œ๋” ์ „ํ›„์— ๋™์ผํ•˜๊ฒŒ ์œ ์ง€๋˜๊ธฐ ๋•Œ๋ฌธ์ด์ฃ . ๊ทธ ๊ฒฐ๊ณผ ์ž์‹ ์ปดํฌ๋„ŒํŠธ๋Š” ๋ณ€๊ฒฝ๋œ ํ•ญ๋ชฉ๋งŒ ๊ฐ์ง€ํ•ด ๋‹ค์‹œ ๋ Œ๋”๋งํ•˜๋ฏ€๋กœ, ์ด ๊ฒฝ์šฐ์—๋Š” id:1์ธ ์ฑ„ํŒ… ํ•ญ๋ชฉ๋งŒ ๋ฆฌ๋ Œ๋”๋ง๋ฉ๋‹ˆ๋‹ค.

๊ฐ๊ฐ์˜ ์„ฑ๋Šฅ์„ ๋น„๊ตํ•ด ๋ณผ๊นŒ์š”?

100๊ฐœ, 50000๊ฐœ์˜ chat item์„ ๋ถˆ๋Ÿฌ์™€์„œ ย React Profiler๋กœ ๋ Œ๋”๋ง ์†๋„๋ฅผ ์ธก์ •ํ•ด ๋ณด์•˜์Šต๋‹ˆ๋‹ค. ๊ฒฐ๊ณผ ์›์‹œ ๋ฐ์ดํ„ฐ๋Š” ์•„๋ž˜ ์ด๋ฏธ์ง€์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

๋žœ๋”๋ง ์†๋„ ์ธก์ • RAW ๋ฐ์ดํ„ฐ

์ด ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ์ƒ์„ฑํ•œ ์ฐจํŠธ๋กœ ํ˜„์ƒ์„ ๋ถ„์„ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

๋‹จ์ผ item ๋žœ๋”๋ง ๋น„๊ต

๋‹จ์ผ item ๋žœ๋”๋ง ์‹œ๊ฐ„ ๋น„๊ต ๋‹จ์ผ item ๊ด€์ ์—์„œ๋Š” ์•„๋ฌด๊ฒƒ๋„ ํ•˜์ง€ ์•Š์Œ < React.Memo ์ ์šฉ < select ์‚ฌ์šฉ ์ˆœ์œผ๋กœ ํ‰๊ท  ๋ Œ๋” ์‹œ๊ฐ„์ด ๊ธธ์–ด์ง€๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค๋ฆฌ์ŠคํŠธ ํฌ๊ธฐ๊ฐ€ 100๊ฐœ์—์„œ 50k๋กœ ๋Š˜์–ด๋‚˜๋ฉด ์ฐจ์ด๊ฐ€ ๋” ํ™•์—ฐํžˆ ๋“œ๋Ÿฌ๋‚ฌ์Šต๋‹ˆ๋‹ค.

์™œ๋ƒํ•˜๋ฉด memo๋Š” props ์ฐธ์กฐ๋ฅผ ์–•๊ฒŒ ๋น„๊ตํ•˜๋Š” ๋น„์šฉ์ด ์ถ”๊ฐ€๋กœ ๋“ค๊ณ , select๋Š” find() ๊ฐ™์€ ํƒ์ƒ‰์„ ์ˆ˜ํ–‰ํ•˜๋ฏ€๋กœ ์•„์ดํ…œ ์ˆ˜์— ๋น„๋ก€ํ•œ ์ถ”๊ฐ€ ๋น„์šฉ์ด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

์ „์ฒด ๋ฆฌ์ŠคํŠธ ๋žœ๋”๋ง ๋น„๊ต

์ „์ฒด ๋ฆฌ์ŠคํŠธ ๋žœ๋”๋ง ์‹œ๊ฐ„ ๋น„๊ต ์ „์ฒด ๋ฆฌ์ŠคํŠธ ๊ด€์ ์—์„œ๋Š” ๋‹ค๋ฅธ ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

size: 100

select < memo < nothing ์ˆœ์œผ๋กœ ์ „์ฒด ์†Œ์š” ์‹œ๊ฐ„์ด ์ž‘์•˜์Šต๋‹ˆ๋‹ค.

์ด์œ ๋Š” select์˜ ๊ฒฝ์šฐ ๋ถ€๋ชจ๋Š” ๋ฆฌ๋žœ๋”๋˜์ง€ ์•Š๊ณ  ๋ณ€๊ฒฝ๋œ ์ž์‹ ์ปดํฌ๋„ŒํŠธ์—์„œ 100๊ฐœ ์ •๋„์˜ find() ๋น„์šฉ์ด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ์ด ๋น„์šฉ์ด memo๋กœ ์ธํ•œ ๋น„๊ต ๋น„์šฉ๊ณผ ๋ถ€๋ชจ ๋ฆฌ๋ Œ๋” ์˜ํ–ฅ์„ ํ•ฉ์นœ ๋น„์šฉ๋ณด๋‹ค ์ž‘๊ฒŒ ์ž‘์šฉํ–ˆ๊ธฐ ๋•Œ๋ฌธ์ด์ฃ .

size: 50k

๋‹จ, ํ•ญ๋ชฉ์ด ๋งค์šฐ ๋งŽ์€(5๋งŒ๊ฐœ ์ •๋„)์ธ ๊ฒฝ์šฐ select๊ฐ€ ์›”๋“ฑํžˆ ๋А๋ ค์ง„ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์—ˆ๋Š”๋ฐ์š”. ์ด๋Š” find() ๋น„์šฉ์ด ๋ฐ์ดํ„ฐ ์ˆ˜์— ๋”ฐ๋ผ ๊ธ‰๊ฒฉํžˆ ์ปค์กŒ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๋งˆ์šดํŠธ ์‹œ์ ์„ ์ œ์™ธํ•˜๋ฉด ์ƒํ™ฉ์ด ๋‹ฌ๋ผ์กŒ์Šต๋‹ˆ๋‹ค. ์ดˆ๊ธฐ ๋งˆ์šดํŠธ ์‹œ์—๋Š” item์˜ ๊ฐœ์ˆ˜์˜ ์ œ๊ณฑ๋งŒํผ ์ฆ‰ O(N^2)์˜ ์‹œ๊ฐ„๋ณต์žก๋„๊ฐ€ ์†Œ์š”๋˜์–ด ๋น„์šฉ์ด ํฌ๊ฒŒ ๋“ค์–ด๊ฐ€์ง€๋งŒ, ๋ฆฌ๋žœ๋”๋ง ๋‹จ๊ณ„์—์„œ๋Š” ๋ณ€๊ฒฝ๋œ ๋ถ€๋ถ„๋งŒ ๋ฐ˜์˜๋˜๋ฏ€๋กœ ์ƒ๋Œ€์ ์œผ๋กœ ์ €๋ ดํ•ด์ง‘๋‹ˆ๋‹ค.

๊ทธ๋Ÿผ ์–ธ์ œ memo๋ฅผ ์“ฐ๊ณ , ์–ธ์ œ select๋ฅผ ์“ธ๊นŒ?

React.memo๋งŒ์œผ๋กœ๋Š” ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ์˜ ๋ฆฌ๋ Œ๋”๋ง์„ ๋ง‰์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๋ถ€๋ชจ๊ฐ€ ๋ฆฌ๋ Œ๋”๋˜๋ฉด ๊ธฐ๋ณธ์ ์œผ๋กœ ์ž์‹๋“ค๋„ ๋‹ค์‹œ ํ‰๊ฐ€๋˜๋ฉฐ, ์ด๋•Œ ์ž์‹์ด React.memo๋กœ ๊ฐ์‹ธ์ ธ ์žˆ๊ณ  ์ „๋‹ฌ๋œ props์˜ ์ฐธ์กฐ๊ฐ€ ๋™์ผํ•  ๋•Œ์—๋งŒ ๋ Œ๋”๋ฅผ ๊ฑด๋„ˆ๋›ธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ถ€๋ชจ๊ฐ€ ๋ฆฌ๋žœ๋”๋ง๋  ๋•Œ ์ž์‹์˜ memo๋Š” ์ƒ๊ฐ๋ณด๋‹ค ๊นจ์ง€๊ธฐ ์‰ฝ์Šต๋‹ˆ๋‹ค. props ์Šคํ”„๋ž˜๋“œ, JSX children ์ „๋‹ฌ, ์ฝœ๋ฐฑ ํ•จ์ˆ˜ ๋“ฑ์„ ์•ˆ์ •ํ™”ํ•˜์ง€ ์•Š์€ ๊ฒฝ์šฐ์ด์ฃ . ๋”ฐ๋ผ์„œ props์˜ ์ฐธ์กฐ ์•ˆ์ •ํ™”๋ฅผ ์‹ ๊ฒฝ ์จ์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋ฐ˜๋ฉด select๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๊ตฌ๋… ๋ฒ”์œ„๋ฅผ item ๋‹จ์œ„๋กœ ์ขํ˜€ ๋ถ€๋ชจ๊ฐ€ ์•„์˜ˆ ๋ฆฌ๋žœ๋”๋˜์ง€ ์•Š๋„๋ก ํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋ถ€๋ชจ ๋ฆฌ๋ Œ๋”๋กœ ์ธํ•œ ํ•˜์œ„ ํŠธ๋ฆฌ์˜ ์‚ฌ์ด๋“œ ์ดํŽ™ํŠธ๋ฅผ ์›์ฒœ์ ์œผ๋กœ ์ค„์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค๋งŒ, ์•ž์„œ ์–ธ๊ธ‰ํ–ˆ๋“ฏ์ด select๋Š” ํ•ญ๋ชฉ์„ ์ฐพ๋Š” ๋“ฑ์˜ ๋น„์šฉ์ด ์ถ”๊ฐ€๋  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ํŠธ๋ ˆ์ด๋“œ์˜คํ”„๋ฅผ ๊ณ ๋ คํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

  • React.memo
    • ์žฅ์ : ์–•์€ ๋น„๊ต ๋น„์šฉ์ด ์ž‘๊ณ  ๊ตฌํ˜„์ด ๊ฐ„๋‹จํ•ฉ๋‹ˆ๋‹ค.
    • ๋‹จ์ : ๋ถ€๋ชจ๊ฐ€ ๋„˜๊ธฐ๋Š” props๋ฅผ ์•ˆ์ •ํ™”ํ•ด์•ผ ํ•ด์„œ ์œ ์ง€๋ณด์ˆ˜ ๋ณต์žก๋„๊ฐ€ ์˜ฌ๋ผ๊ฐ‘๋‹ˆ๋‹ค.
  • select
    • ์žฅ์ : ๊ตฌ๋… ๋ฒ”์œ„๋ฅผ ์ถ•์†Œํ•ด ๋ถ€๋ชจ ๋ ˆ๋ฒจ์˜ ๋ถˆํ•„์š”ํ•œ ๋ฆฌ๋ Œ๋”๋ฅผ ํ”ผํ•ฉ๋‹ˆ๋‹ค.
    • ๋‹จ์ : ๊ฒ€์ƒ‰/๋ณ€ํ™˜ ๋น„์šฉ(์˜ˆ: find, map ์ƒ์„ฑ ๋“ฑ)์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋งŒ์•ฝ ์„œ๋ฒ„ ์‘๋‹ต์„ ์ •๊ทœํ™”ํ•ด์„œ ์ €์žฅํ•˜๋ฉด, select์˜ ๊ฒ€์ƒ‰ ๋น„์šฉ์„ O(1)์œผ๋กœ ํฌ๊ฒŒ ์ค„์ผ ์ˆ˜ ์žˆ์–ด ๋” ํšจ์œจ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๊ฒ ์ฃ . ์ด๋Ÿฐ ์ด์œ ๋กœ api ์‘๋‹ต์ด obejct์ธ ๊ฒฝ์šฐ select๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ์‹์ด ๋” ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.

object์˜ ๊ฒฝ์šฐ

/**
 * ์„œ๋ฒ„ ์‘๋‹ต ๊ฐ€์ •
 */
type ServerResponse = {
  id: number;
  title: string;
  user: {
    id: number;
    name: string;
  };
};
 
function ChatMetaInfoWidget({ id }: { id: number }) {
  return (
    <section className='chat-meta-widget'>
      <ChatTitle id={id} />
      <ChatParticipants id={id} />
    </section>
  );
}
 
/**
 * ์ž์‹ 1: ์ œ๋ชฉ๋งŒ ๊ตฌ๋…
 * - select๋กœ title๋งŒ ๊ณจ๋ผ์„œ ๊ตฌ๋… -> title์ด ๋ฐ”๋€” ๋•Œ๋งŒ ๋ฆฌ๋ Œ๋”
 */
function ChatTitle({ id }: { id: number }) {
  const { data: title } = useGetChatMetaByIdSuspenseQuery(
    { id },
    {
      select: (chat) => chat.title,
    },
  );
 
  return <h3>{title}</h3>;
}
 
/**
 * ์ž์‹ 2: ์ฐธ์—ฌ์ž ์ด๋ฆ„
 * - select๋กœ user.name๋งŒ ๊ณจ๋ผ์„œ ๊ตฌ๋… -> user.name์ด ๋ฐ”๋€” ๋•Œ๋งŒ ๋ฆฌ๋ Œ๋”
 */
function ChatParticipants({ id }: { id: number }) {
  const { data: user } = useGetChatMetaByIdSuspenseQuery(
    { id },
    {
      select: (chat) => chat.user,
    },
  );
 
  return <div>{user.name}</div>;
}

์œ„ ์˜ˆ์‹œ์ฒ˜๋Ÿผ ๋ถ€๋ชจ๊ฐ€ ์„œ๋ฒ„ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์™€์„œ ์ง์ ‘ props๋กœ ๋‚ด๋ ค์ฃผ๋Š” ๊ตฌ์กฐ์˜€๋‹ค๋ฉด, ์ œ๋ชฉ ํ•˜๋‚˜๋งŒ ๋ฐ”๋€Œ์–ด๋„ ๋ถ€๋ชจ๊ฐ€ ๋ฆฌ๋ Œ๋”๋˜๋ฉฐ ๋ชจ๋“  ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋‹ค์‹œ ํ‰๊ฐ€๋ฉ๋‹ˆ๋‹ค. React.memo๋กœ ์ž์‹์„ ๊ฐ์‹ธ๋ฉด ์ผ๋ถ€ ๋ฐฉ์–ด๋Š” ๋˜์ง€๋งŒ, ๊ทธ๋Ÿด ๊ฒฝ์šฐ ๋ชจ๋“  ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ์— ๋Œ€ํ•ด ๋ฉ”๋ชจ๋ฅผ ์ ์šฉํ•˜๊ฑฐ๋‚˜ ์ „๋‹ฌ๋˜๋Š” props๋ฅผ ๊ผผ๊ผผํžˆ ์•ˆ์ •ํ™”ํ•ด์ค˜์•ผ ํ•˜๋ฏ€๋กœ ์ฝ”๋“œ ๋ณต์žก๋„๊ฐ€ ํฌ๊ฒŒ ์˜ฌ๋ผ๊ฐ‘๋‹ˆ๋‹ค.

๋ฐ˜๋ฉด select๋ฅผ ์‚ฌ์šฉํ•ด ์ž์‹์ด ํ•„์š”ํ•œ ํ•„๋“œ๋งŒ ๊ตฌ๋…ํ•˜๊ฒŒ ๊ตฌ์„ฑํ•˜๋ฉด, ์ œ๋ชฉ ๋ณ€๊ฒฝ์€ ChatTitle๋งŒ, ์ด๋ฆ„ ๋ณ€๊ฒฝ์€ ChatParticipants๋งŒ ๋ฆฌ๋ Œ๋”๋˜๋„๋ก ๋ฒ”์œ„๋ฅผ ์ถ•์†Œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

React.memo๋Š” ์–•์€ ๋น„๊ต ๋น„์šฉ + ์œ ์ง€๋ณด์ˆ˜ ๋น„์šฉ์„ ์น˜๋ฅด๋ฉฐ ์–ป๋Š” ์ตœ์ ํ™”์ž…๋‹ˆ๋‹ค. ๋ฐ˜๋ฉด select๋Š” ๊ตฌ๋… ๋ฒ”์œ„๋ฅผ ์ถ•์†Œํ•ด ๋ถ€๋ชจ ๋ ˆ๋ฒจ์˜ ๋ถˆํ•„์š”ํ•œ ๋ฆฌ๋ Œ๋”๋ฅผ ํ”ผํ•˜๋Š” ์ „๋žต์ž…๋‹ˆ๋‹ค.

๋žœ๋”๋ง ์ตœ์ ํ™”๋Š” ๊ผญ ํ•ด์•ผํ• ๊นŒ?

๊ฒฐ๋ก ๋ถ€ํ„ฐ ๋งํ•˜๋ฉด, ๋ฆฌ๋žœ๋”๋ง ์ž์ฒด๋Š” ๋‚˜์œ ๊ฒƒ์ด ์•„๋‹ˆ๋‹ค๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ฆฌ๋ Œ๋”๋œ๋‹ค๋Š” ๊ฑด ๋‹จ์ง€ ์ปดํฌ๋„ŒํŠธ ํ•จ์ˆ˜๊ฐ€ ๋‹ค์‹œ ์‹คํ–‰๋˜๋Š” ๊ฒƒ์ผ ๋ฟ์ด๊ณ , ์‹ค์ œ DOM ๋ณ€๊ฒฝ์€ ๊ฐ€์ƒ๋”์„ ์‚ฌ์šฉํ•ด ์‹ค์ œ ๋ณ€๊ฒฝ๋œ ๋ถ€๋ถ„๋งŒ ๋ฐ˜์˜๋ฉ๋‹ˆ๋‹ค. ๋ฌธ์ œ๋Š” ๋ฆฌ๋žœ๋”๋ง ์ž์ฒด๊ฐ€ ์•„๋‹ˆ๋ผ ํ•œ ๋ฒˆ์˜ ๋ Œ๋”๊ฐ€ ๋А๋ ค์„œ ํ”„๋ ˆ์ž„์„ ์ง€์—ฐ์‹œํ‚ค๋Š” ๊ฒฝ์šฐ์ž…๋‹ˆ๋‹ค. ์ฆ‰, โ€œ์ž์ฃผ ๋ Œ๋”๋˜๋”๋ผ๋„ ๊ดœ์ฐฎ๊ฒŒโ€ ์„ค๊ณ„ํ•˜๋Š” ๊ฒƒ์ด ๋” ์˜ฌ๋ฐ”๋ฅธ ๋ฐฉํ–ฅ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

๋˜ํ•œ ๋Œ€๋ถ€๋ถ„์˜ ๊ฒฝ์šฐ ์„ฃ๋ถ€๋ฅธ ์ตœ์ ํ™”๋Š” ๋…์ด ๋ฉ๋‹ˆ๋‹ค. LABjs์˜ ์‚ฌ๋ก€์ฒ˜๋Ÿผ ์ตœ์ ํ™”๊ฐ€ ์˜คํžˆ๋ ค ์„ฑ๋Šฅ ๋ฌธ์ œ๋ฅผ ์ผ์œผํ‚ค๋Š” ๊ฒฝ์šฐ๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์ธก์ • ์—†์ด ์ตœ์ ํ™”ํ•˜๋ฉด ์„ฑ๋Šฅ์ด ๊ฐœ์„ ๋˜๋Š”์ง€์กฐ์ฐจ ์•Œ๊ธฐ ์–ด๋ ต๊ณ , ์˜คํžˆ๋ ค ์•…์˜ํ–ฅ์„ ์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿผ ์–ธ์ œ ์ตœ์ ํ™”๋ฅผ ํ•˜๋ฉด ์ข‹์„๊นŒ?

Performance๋กœ ๋А๋ฆฐ ๋žœ๋”๋ง ํ™•์ธํ•˜๊ธฐ

๋ธŒ๋ผ์šฐ์ € Performance ํƒญ์œผ๋กœ ๋ Œ๋”๋ง ์‹œ๊ฐ„์„ ์ธก์ •ํ•˜์„ธ์š”. 60fps ๊ธฐ์ค€ ํ•œ ํ”„๋ ˆ์ž„์€ ์•ฝ 16.7ms์ž…๋‹ˆ๋‹ค. ๋งŒ์•ฝ ํ•œ ํ”„๋ ˆ์ž„์ด 16.7ms๋ฅผ ๊พธ์ค€ํžˆ ๋„˜๋Š”๋‹ค๋ฉด โ€œ์‚ฌ์šฉ์ž๊ฐ€ ์ฒด๊ฐํ•˜๋Š” ๋А๋ฆฐ ๋ Œ๋”๋งโ€์ž…๋‹ˆ๋‹ค.

์ด๋•Œ๋Š” ์ปดํฌ๋„ŒํŠธ ํ•จ์ˆ˜๊ฐ€ ๋А๋ ค์„œ ๋ฐœ์ƒํ•œ ๋ฌธ์ œ์ธ์ง€, ์•„๋‹ˆ๋ฉด ๋ ˆ์ด์•„์›ƒ/์Šคํƒ€์ผ/ํŽ˜์ธํŠธ ๋“ฑ ๋ธŒ๋ผ์šฐ์ € ๋ Œ๋” ๋‹จ๊ณ„์˜ ๋ณ‘๋ชฉ์ธ์ง€๋ฅผ ๋จผ์ € ๊ตฌ๋ถ„ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๊ตฌ์กฐ์  ๊ฐœ์„ ์„ ๋จผ์ € ์‹œ๋„ํ•˜๊ธฐ

Performance์—์„œ ๋ณ‘๋ชฉ์ด ์ž์ฃผ ๋ฐœ์ƒํ•˜๋Š” ์ง€์ ์ด ๋‚˜์™”์–ด๋„ memo๋‚˜ select ๊ฐ™์€ ๊ธฐ๋ฒ•์„ ๋ฐ”๋กœ ๋„์ž…ํ•˜์ง€ ๋งˆ์„ธ์š”. ๋‹จ์ˆœํžˆ ๊ตฌ์กฐ๋ฅผ ๋ฐ”๊พธ๋Š” ๊ฒƒ ๋งŒ์œผ๋กœ๋„ ๊ทผ๋ณธ ์›์ธ์ด ํ•ด๊ฒฐ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ์ปดํฌ๋„ŒํŠธ ๋ถ„๋ฆฌ: ๋ฌด๊ฑฐ์šด ์—ฐ์‚ฐ์ด ์žˆ๋Š” ๋ถ€๋ถ„์„ ๋ณ„๋„์˜ ์ปดํฌ๋„ŒํŠธ๋กœ ๋ถ„๋ฆฌํ•ด์„œ ๋ฆฌ๋žœ๋” ์ตœ์ ํ™”
  • ์ฟผ๋ฆฌ ํ‚ค/์บ์‹œ ์žฌ์„ค๊ณ„: ๋ถˆํ•„์š”ํ•œ invalidate ๋ฐฉ์ง€

์ด๋Ÿฐ ๊ตฌ์กฐ์  ๊ฐœ์„ ์œผ๋กœ ๋ฌธ์ œ๊ฐ€ ํ’€๋ฆฌ๋ฉด ์ฝ”๋“œ๋„ ๊น”๋”ํ•ด์ง€๊ณ  ์œ ์ง€๋ณด์ˆ˜ ๋น„์šฉ๋„ ๋Š˜๋ฆฌ์ง€ ์•Š์œผ๋ฉด์„œ ์„ฑ๋Šฅ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

React Profiler๋กœ React ์ปดํฌ๋„ŒํŠธ ๋žœ๋”๋ง ์‹œ๊ฐ„ ํ™•์ธํ•˜๊ธฐ

๊ตฌ์กฐ์  ๊ฐœ์„  ํ›„์—๋„ ๋ฆฌ๋žœ๋”๋ง์ด ๋ณ‘๋ชฉ์ด๋ฉด React Profiler๋ฅผ ์ผœ์„œ ์–ด๋–ค ์ปดํฌ๋„ŒํŠธ๊ฐ€ render์— ์‹œ๊ฐ„์„ ๋งŽ์ด ์“ฐ๋Š”์ง€ ํ™•์ธํ•˜์„ธ์š”. React Profiler๋ฅผ ํ†ตํ•ด ์ปดํฌ๋„ŒํŠธ์˜ ๋ฆฌ๋žœ๋”๋ง ํšŸ์ˆ˜์™€ ์‹œ๊ฐ„์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์„œ ์ค‘์š”ํ•œ ์ ์€ ๋ธŒ๋ผ์šฐ์ €์˜ Performance๋กœ ํ™•์ธํ•˜๋Š” ๊ฒƒ์€ ์‹ค์ œ DOM์— ๋ฐ˜์˜๋˜๋Š” ๋น„์šฉ์ด๊ณ , React Profiler๋ฅผ ํ†ตํ•ด ํ™•์ธํ•˜๋Š” ๊ฒƒ์€ React ๋‚ด๋ถ€์˜ render/commit ๋‹จ๊ณ„์ž…๋‹ˆ๋‹ค. ์ฆ‰ "์ปดํฌ๋„ŒํŠธ ํ•จ์ˆ˜๊ฐ€ ์‹คํ–‰๋˜๋Š” ๊ฒƒ"์— ๋Œ€ํ•œ ์„ฑ๋Šฅ ์ธก์ •์ด๊ณ  DOM ๋ฐ˜์˜๊ณผ ๋‹ค๋ฅธ ์˜๋ฏธ์ž…๋‹ˆ๋‹ค.

๋งˆ๋ฌด๋ฆฌ

TanStack Query๋ฅผ ์“ฐ๋‹ค๊ฐ€ ๋“  ์˜๋ฌธ์—์„œ ์‹œ์ž‘ํ•ด ๊ณต๋ถ€๋ฅผ ์ง„ํ–‰ํ•˜๋‹ค ๋ณด๋‹ˆ, ๊ตฌ์กฐ์  ๊ณต์œ ๋ถ€ํ„ฐ React ๋ฆฌ๋ Œ๋”๋ง ์ตœ์ ํ™” ๋ฐฉ๋ฒ•, ๊ทธ๋ฆฌ๊ณ  ์–ธ์ œ ์ตœ์ ํ™”๋ฅผ ํ•ด์•ผ ํ•˜๋Š”์ง€๊นŒ์ง€ ์ •๋ฆฌํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ํ•™์Šตํ•˜๋‹ค๋ณด๋‹ˆ ๊ฐœ์ธ์ ์œผ๋กœ ํ•œ ๊ฐ€์ง€ ํŒ๋‹จ์ด ๋“ค์—ˆ๋Š”๋ฐ์š”. ๋ฐ”๋กœ โ€œ๋ฆฌ๋ Œ๋”๋ง ํšŸ์ˆ˜ ์ž์ฒด๋ฅผ ์ค„์ด๋Š” ๊ฒƒโ€์€ ์šฐ์„ ์ˆœ์œ„๊ฐ€ ๋†’์ง€ ์•Š๋‹ค ๋ผ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

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

๋ฌผ๋ก  ์ƒํ™ฉ์— ๋”ฐ๋ผ ๋‹ค๋ฆ…๋‹ˆ๋‹ค๋งŒ, ์ œ ๊ธฐ์ค€์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  • ์„ฑ๋Šฅ ๊ฐœ์„ ์€ ์‹ค์ œ ๋ฌธ์ œ๊ฐ€ ์žˆ์„ ๋•Œ ํ•˜์ž. ์ธก์ • ์—†์ด ๋ฏธ๋ฆฌ ์†๋Œ€๋ฉด ๋ถˆํ•„์š”ํ•œ ๋ณต์žก๋„๋งŒ ์Œ“์ธ๋‹ค.
  • ๊ตฌ์กฐ ๋ฌธ์ œ๋ฅผ ๋จผ์ € ๊ณ ์น˜์ž. ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ž˜ ๋ถ„๋ฆฌํ•˜๊ณ  ํ•ฉ์น˜๋Š” ๊ฒƒ์€ ์„ค๊ณ„ ์ˆ˜์ค€์˜ ๋ฌธ์ œ๋ผ์„œ, ๊ตฌ์กฐ๊ฐ€ ์ž˜๋ชป๋˜๋ฉด ์–ด๋–ค ๋ฏธ์„ธ ์ตœ์ ํ™”๋„ ๊ทผ๋ณธ์  ํ•ด๊ฒฐ์ด ๋˜์ง€ ์•Š๋Š”๋‹ค.
  • ๊ทธ๋ž˜๋„ ์—ฌ์ „ํžˆ ๋А๋ฆฌ๋‹ค๋ฉด select, React.memo, API ์‘๋‹ต ์ •๊ทœํ™” ๊ฐ™์€ ๋ฐฉ๋ฒ•์„ ์“ฐ์ž.

๊ธด ๊ธ€ ์ฝ์–ด์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ๊ถ๊ธˆํ•œ ์ ์ด๋‚˜ ํ”ผ๋“œ๋ฐฑ์€ ์–ธ์ œ๋“ ์ง€ ํ™˜์˜ํ•ฉ๋‹ˆ๋‹ค.

์ฐธ๊ณ ์ž๋ฃŒ

TanStack Query์˜ ๊ตฌ์กฐ์  ๊ณต์œ ์™€ React ๋ฆฌ๋žœ๋”๋ง์— ๋Œ€ํ•œ ํ•™์Šต๊ณผ ๊ด€์ฐฐ