Skip to content

๐Ÿ‘Š ์›น์†Œ์ผ“์˜ ์ฑ„ํŒ… ๋ฐ์ดํ„ฐ์™€ REST API์˜ ์ฑ„ํŒ… ๋ฐ์ดํ„ฐ๋ฅผ ํ•จ๊ป˜ ๊ด€๋ฆฌํ•˜๊ธฐ

baegyeong edited this page Dec 5, 2024 · 5 revisions
๋ถ„์•ผ ์ž‘์„ฑ์ž ์ž‘์„ฑ์ผ
FE ์กฐ๋ฐฐ๊ฒฝ 24๋…„ 12์›” 04์ผ

์ฑ„ํŒ… ์ •๋ ฌ ๊ธฐ๋Šฅ ๋„์ž… ์ด์ „์— ์ฑ„ํŒ… ๋ฐ์ดํ„ฐ ๊ด€๋ฆฌ ๋ฐฉ๋ฒ•

์šฐ๋ฆฌ ์„œ๋น„์Šค์˜ ์ฑ„ํŒ…์€, ์›น์†Œ์ผ“์œผ๋กœ๋Š” ์‹ค์‹œ๊ฐ„์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์†ก์ˆ˜์‹ ํ•˜๊ณ  rest api๋กœ๋Š” ์ด์ „ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋‹ค.

๊ฐ๊ฐ ๋ฐ›๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ํ•œ๋ฒˆ์— ๊ด€๋ฆฌํ•˜๊ณ ์ž ์ฑ„ํŒ… ๋ฐ์ดํ„ฐ์˜ ์ƒํƒœ๋ฅผ chatData์— ์ €์žฅํ–ˆ๋‹ค.

์ด chatData์—๋Š” ์›น์†Œ์ผ“์ด ์ œ๊ณตํ•˜๋Š” ์ตœ์‹  ์ฑ„ํŒ… 20๊ฐœ์™€, ๋ฌดํ•œ์Šคํฌ๋กคํ•ด์„œ ๊ฐ€์ ธ์˜ค๋Š” rest api์˜ ์ฑ„ํŒ… ๋ฐ์ดํ„ฐ๋ฅผ ๋ˆ„์ ํ•˜์—ฌ ์ €์žฅํ•œ๋‹ค.


์ด๋•Œ, ์›น์†Œ์ผ“์€ ๊ณ„์†ํ•ด์„œ ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๊ณ , ์ตœ์‹  ๋ฐ์ดํ„ฐ 20๊ฐœ๋ฅผ ์œ ์ง€ํ•œ๋‹ค.

๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ๋ฅผ ์†ก์ˆ˜์‹ ํ•  ๋•Œ๋งˆ๋‹ค rest api๋Š” ์›น์†Œ์ผ“์ด ๊ฐ–๊ณ  ์žˆ๋˜ ๋ฐ์ดํ„ฐ ์ค‘ ๊ฐ€์žฅ ๋งˆ์ง€๋ง‰์— ์žˆ๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๊ธฐ์–ตํ•˜๊ณ , ์ด๋ฅผ ๊ฐ€์ ธ์™€์•ผํ•œ๋‹ค.

image

์ฆ‰ ์›น์†Œ์ผ“ ๋ฐ์ดํ„ฐ๋ฅผ ์ƒˆ๋กœ ๋ฐ›์•„์˜ฌ ๋•Œ๋งˆ๋‹ค latestChatId๋ฅผ ์—…๋ฐ์ดํŠธํ•˜์—ฌ, ํ•ด๋‹น ์ง€์ ๋ถ€ํ„ฐ rest api๋กœ ๋‹ค์‹œ ๊ฐ€์ ธ์˜จ๋‹ค.

๋‹จ์ˆœํžˆ ์ฑ„ํŒ… ์†ก์ˆ˜์‹ , ๋ฌดํ•œ์Šคํฌ๋กค, ์ข‹์•„์š” ๊ธฐ๋Šฅ๋งŒ ์žˆ์„ ๋•Œ์—๋Š” ์ด ๋ฐฉ๋ฒ•์ด ๊ฐ€๋Šฅํ–ˆ๋‹ค.


API์— ์ฑ„ํŒ… ์ •๋ ฌ ๊ธฐ๋Šฅ์ด ์ถ”๊ฐ€๋œ๋‹ค๋ฉด?

ํ•˜์ง€๋งŒ ์ฑ„ํŒ…์„ ์ข‹์•„์š”์ˆœ์œผ๋กœ ์ •๋ ฌํ•˜๋Š” api๋ฅผ ์ถ”๊ฐ€ํ•ด๋ณธ๋‹ค๋ฉด ์–ด๋–จ๊นŒ?

chatData๊ฐ€ ๋ชจ๋‘ ์ •๋ ฌ์ด ์ ์šฉ๋ผ์•ผ ํ•˜๋ฏ€๋กœ, chatData์— ์†ํ•œ ์›น์†Œ์ผ“์œผ๋กœ ๋ฐ›์•„์˜จ ๋ฐ์ดํ„ฐ๋„ ์ •๋ ฌ์ด ๋˜์–ด์•ผ ํ•œ๋‹ค.

ํ˜„์žฌ ๊ตฌ์กฐ๋กœ๋Š” ์ฃผ์–ด์ง„ api๋Š” api๋กœ ๋ฐ›์•„์˜จ ๋ฐ์ดํ„ฐ์— ํ•œํ•ด์„œ๋งŒ ์ •๋ ฌ์ด ๋  ๊ฒƒ์ด๋‹ค.

chatData์˜ ์ƒ์œ„ 20๊ฐœ๋Š” ํ•ญ์ƒ ์›น์†Œ์ผ“์ด๊ธฐ ๋•Œ๋ฌธ์—, ์ด ๋ฐฉ์‹๋Œ€๋กœ๋ผ๋ฉด ์›น์†Œ์ผ“ ๋ฐ์ดํ„ฐ๋Š” ์ •๋ ฌ์ด ๋˜์ง€ ์•Š์•„ ๊ธฐ๋Œ€์™€๋Š” ๋‹ค๋ฅธ ํ™”๋ฉด์ด ๋‚˜์˜ฌ ๊ฒƒ์ด๋‹ค.


๋ฐ›์•„์˜ค๋Š” ๋ฐ์ดํ„ฐ์˜ ์—ญํ• ์„ ๋ช…ํ™•ํžˆ ํ•˜์ž.

ํ˜„ ์ƒํ™ฉ์œผ๋กœ ๊ฐ ๋ฐ์ดํ„ฐ์˜ ์—ญํ• ์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  • ์›น์†Œ์ผ“ ๋ฐ์ดํ„ฐ: ์ƒ์œ„ 20๊ฐœ ๋ฐ์ดํ„ฐ ๋ชฉ๋ก + ์‹ค์‹œ๊ฐ„์œผ๋กœ ๋ฐ์ดํ„ฐ ์†ก์ˆ˜์‹ 
  • rest api์˜ ๋ฐ์ดํ„ฐ: ๋ฌดํ•œ์Šคํฌ๋กค๋กœ ๋ฐ›๋Š” ์ด์ „ ์ฑ„ํŒ… ๋ฐ์ดํ„ฐ ๋ชฉ๋ก

๋ฐ์ดํ„ฐ ๋ชฉ๋ก์„ ์›น์†Œ์ผ“๊ณผ rest api ๋ชจ๋‘ ๋ฐ›๊ณ  ์žˆ๋Š”๋ฐ, rest api์—๋งŒ ์ฑ…์ž„์„ ๋ถ€์—ฌํ•˜์ž.


๊ทธ๋ ‡๊ฒŒ ํ•ด์„œ ๋‹ค์‹œ ์ƒ๊ฐํ•œ ์—ญํ• ์„ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  • ์›น์†Œ์ผ“ ๋ฐ์ดํ„ฐ: ์‹ค์‹œ๊ฐ„์œผ๋กœ ๋ฐ์ดํ„ฐ ์†ก์ˆ˜์‹ (1๊ฐœ์”ฉ ๋ฐ›๊ฒŒ๋จ)
  • rest api์˜ ๋ฐ์ดํ„ฐ: ์‹ค์‹œ๊ฐ„์„ ์ œ์™ธํ•œ ๋ชจ๋“  ์ฑ„ํŒ… ๋ฐ์ดํ„ฐ ๋ชฉ๋ก

์ด๋ ‡๊ฒŒ ์—ญํ• ์„ ๋‚˜๋ˆ„๋ฉด, ์ข‹์•„์š”์ˆœ์œผ๋กœ ๋‹ค์‹œ ์ •๋ ฌ ์š”์ฒญ์„ ํ•œ๋‹ค๋ฉด ๋ชจ๋“  ๋ฐ์ดํ„ฐ์— ๋ฐ˜์˜๋  ๊ฒƒ์ด๋‹ค.

image


useInfiniteQuery์˜ ๊ธฐ๋Šฅ ํ™œ์šฉํ•˜๊ธฐ

๊ธฐ์กด์—๋Š”, useInfiniteQuery๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ชฝ์—์„œ ์ปค์Šคํ…€ํ•˜์—ฌ latestChatId๋ฅผ ๋ณด๋‚ด์คฌ๋Š”๋ฐ, useInfiniteQuery์˜ ๊ธฐ๋Šฅ์„ ํ™œ์šฉํ•ด์„œ ๋ฐ”๊ฟ”๋ณด์ž.

๋ฐ›์•„์™€์•ผํ•˜๋Š” ์ฑ„ํŒ… ๋ชฉ๋ก ์ค‘ ๊ฐ€์žฅ ์ตœ์‹ ์˜ ์ฑ„ํŒ…์˜ id๋ฅผ page ์ฒ˜๋Ÿผ ์“ฐ๊ณ  ์žˆ์œผ๋‹ˆ, ์ด๋ฅผ ๋…น์—ฌ๋‚ด์ž.

export const useGetChatList = ({
  stockId,
  latestChatId,
  pageSize,
  order,
}: GetChatListRequest) => {
  return useInfiniteQuery({
    queryKey: ['chatList', stockId, order],
    queryFn: ({ pageParam }) =>
      getChatList({
        stockId,
        latestChatId: pageParam?.latestChatId,
        pageSize,
        order,
      }),
    // lastestChatId๋ฅผ nextPageParam์œผ๋กœ์จ ๋„˜๊ธฐ๊ณ ์ž ํ•จ
    getNextPageParam: (lastPage) => 
      lastPage.hasMore
        ? {
            latestChatId: lastPage.chats[lastPage.chats.length - 1].id,
          }
        : undefined,
    // initialPageParam ์ง€์ •
    initialPageParam: { latestChatId },
    // select ์˜ต์…˜์„ ํ†ตํ•ด ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋งŒ ๋ฝ‘์•„์„œ ์‚ฌ์šฉ
    select: (data) => ({
      pages: [...data.pages].flatMap((page) => page.chats),
      pageParams: [...data.pageParams],
    }),
  });
};

์ปดํฌ๋„ŒํŠธ์—์„œ๋Š” ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ•˜๋ฉด ๋ ๊นŒ?

const { fetchNextPage, data, status, isFetching, hasNextPage } =
  useGetChatList({
    stockId,
    order,
  });

// IntersectionObserver์— ์˜ํ•ด ๊ฐ์ง€๋˜๋ฉด ์‹คํ–‰
const fetchMoreChats = () => {
  fetchNextPage(); // ๋‹ค์Œ ํŽ˜์ด์ง€ ์‹คํ–‰

  // ์‘๋‹ต์ด ์„ฑ๊ณต์ ์ด๋ฉด ๋ฐ์ดํ„ฐ ์ €์žฅ
  if (status === 'success') {
    setChatData(data.pages);
  }
};

const { ref } = useInfiniteScroll({
  onIntersect: fetchMoreChats,
  hasNextPage,
});
  • useGetChatList๋ฅผ ๋ถˆ๋Ÿฌ์˜จ๋‹ค.
  • observer๋ฅผ ํ†ตํ•ด ํŠน์ • ์˜์—ญ์— ๋‹ฟ๋Š” ๊ฒƒ์„ ๊ฐ์ง€ํ•˜๊ณ  ๋ฐ์ดํ„ฐ๋ฅผ ํŒจ์นญํ•œ๋‹ค.
// ์ •๋ ฌ์„ ํ•˜์—ฌ ๋ฐ์ดํ„ฐ๊ฐ€ ๋ณ€ํ•œ๋‹ค๋ฉด ์ƒˆ๋กญ๊ฒŒ ์ €์žฅํ•ด์•ผํ•œ๋‹ค.
useEffect(() => {
  if (status === 'success') {
    setChatData(data.pages.flat());
  }
}, [data, status]);
  • ๋ณดํ†ต ๋ฌดํ•œ์Šคํฌ๋กค์€ observer๋กœ ๊ฐ์ง€๋˜๋ฉด fetchNextPage๋ฅผ ์‹คํ–‰ํ•˜๋Š”๋ฐ, ์ •๋ ฌ๋กœ ์ธํ•œ ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ์€ ๋”ฐ๋กœ ๊ฐ์ง€ํ•˜์ง€ ๋ชปํ•œ๋‹ค.
  • ๋”ฐ๋ผ์„œ useEffect๋กœ ๊ฐ์‹ธ์„œ ๋ฐ์ดํ„ฐ๊ฐ€ ๋ณ€ํ™”ํ•˜๋ฉด ์ƒˆ๋กœ ์ €์žฅํ•œ๋‹ค.
const handleChat = useCallback((message: ChatDataResponse | ChatData) => {
	// chats ๋ฐฐ์—ด๋กœ ์˜ค๋Š” ์‘๋‹ต์€ return
  if ('chats' in message) return; 

	// ์‹ค์‹œ๊ฐ„์œผ๋กœ ์‘๋‹ต์˜ค๋Š” ๋‹จ์ผ ๋ฉ”์‹œ์ง€๋งŒ chatData์— ์ €์žฅ
  const validatedSingleChat = ChatDataSchema.parse(message);
  if (validatedSingleChat) {
    setChatData((prev) => [{ ...validatedSingleChat } as ChatData, ...prev]);
  }
}, []);
  • ์›น์†Œ์ผ“์—์„œ ์ฑ„ํŒ… ๋ธŒ๋กœ๋“œ์บ์ŠคํŠธ ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ์‹คํ–‰๋˜๋Š” ํ•จ์ˆ˜์ด๋‹ค.
  • ์ƒ์œ„ 20๊ฐœ ๋ชฉ๋ก์€ chats ๋ฐฐ์—ด๋กœ ์˜ค๊ณ , ๋‹จ์ผ ๋ฉ”์‹œ์ง€ ์†ก์ˆ˜์‹ ์€ ๋‹จ์ผ ๊ฐ์ฒด๋กœ ์˜ค๊ธฐ ๋•Œ๋ฌธ์— ์ด ์ ์„ ๊ฐ€์ง€๊ณ  ๊ฒ€์‚ฌ๋ฅผ ํ–ˆ๋‹ค.

๐Ÿœ ํŒ€ ๊ฐœ๋ฏธ

๐Ÿ›๏ธ ํŒ€ ๋ฌธํ™”

๊ฐœ๋ฐœ ์œ„ํ‚ค

FE

BE

Infra

๐Ÿ—ฃ๏ธ ๋ฐœํ‘œ

๐Ÿ“š ํšŒ์˜๋ก

๐Ÿ”ด ์ธํ„ฐ๋ฏธ์…˜
๐ŸŸ  1์ฃผ์ฐจ
๐ŸŸก 2์ฃผ์ฐจ
๐ŸŸข 3์ฃผ์ฐจ
๐Ÿ”ต 4์ฃผ์ฐจ
๐ŸŸฃ 5์ฃผ์ฐจ
๐ŸŸค 6์ฃผ์ฐจ

๐Ÿ’ญ ํšŒ๊ณ 

๐Ÿง‘โ€๐Ÿคโ€๐Ÿง‘ ๋ฉ˜ํ† ๋ง

Clone this wiki locally