Skip to content

๐Ÿง‘โ€๐Ÿ’ผReactโ€Query๋กœ ์•ˆ์ •์ ์ธ ์ŠคํŠธ๋ฆฌ๋ฐ์„ ์œ„ํ•œ ๋กœ๋”ฉ ๋ฐ ์—๋Ÿฌ ๊ด€๋ฆฌํ•˜๊ธฐ

Changhyun-Hong edited this page Dec 5, 2024 · 1 revision

๐Ÿšจย ๋ฌธ์ œ ์ธ์‹

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

๊ธฐ์กด์˜ ์ฝ”๋“œ

const MainLiveSection = ({ title, type }: MainLiveSectionProps) => {
  const { data = [], isLoading, error } = useRecentLive();

  if (error) {
    return <div>๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ์ค‘ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.</div>;
  }

  return (
    <MainSectionContainer>
      <MainSectionHeader>
        <p className="live_section_title">{title}</p>
        <button className="live_section_button">์ „์ฒด๋ณด๊ธฐ</button>
      </MainSectionHeader>

      {isLoading && <div>๋กœ๋”ฉ ์ค‘...</div>}

      {data.length === 0 && !isLoading && <div>๋ฐ์ดํ„ฐ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.</div>}
				// ์ฝ”๋“œ ์ƒ๋žต
    </MainSectionContainer>
  );
};

์œ„์™€ ๊ฐ™์€ ์ฝ”๋“œ๊ฐ€ ์ปค์Šคํ…€ ํ›…์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ์ปดํฌ๋„ŒํŠธ๋งˆ๋‹ค ์žˆ์—ˆ๋‹ค. isLoading๊ณผ error์ผ๋•Œ ๊ฐ๊ฐ ์—ฐ์‚ฐ์ž์™€ ์กฐ๊ฑด๋ฌธ์„ ํ†ตํ•ด์„œ ์ฒ˜๋ฆฌํ–ˆ์—ˆ๋‹ค.

๐Ÿƒย ํ•ด๊ฒฐ ๊ณผ์ •

๊ด€์‹ฌ์‚ฌ๋ฅผ ๋ถ„๋ฆฌํ•  ๊ฒƒ

์ปดํฌ๋„ŒํŠธ์˜ ์—ญํ• ์„ ๋ Œ๋”๋ง๋˜๋Š” ๊ฒƒ์—๋งŒ ์ง‘์ค‘

  • ๋น„์ง€๋‹ˆ์Šค ๋กœ์ง
  • ๋ Œ๋”๋ง ๋กœ์ง

๋กœ๋”ฉ๊ณผ ์—๋Ÿฌ์ฒ˜๋ฆฌ๋Š” ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ์˜ ๊ด€์‹ฌ์‚ฌ์—์„œ ๋ฒ—์–ด๋‚œ๋‹ค๊ณ  ํŒ๋‹จ

โ†’ ๋กœ๋”ฉ๊ณผ ์—๋Ÿฌ์ฒ˜๋ฆฌ ์—ญํ• ์„ **๋ถ€๋ชจ(์ฒ˜๋ฆฌํ•ด์•ผํ•  ๋ถ€๋ถ„)**๋กœ ์œ„์ž„

Suspense์ฒ˜๋ฆฌ๋ฅผ ์™œ ํ•ด์•ผํ•˜๋Š”๊ฐ€์— ๋Œ€ํ•œ ์˜๋ฌธ

๊ธฐ์กด์— ๊ฐ„๋‹จํ•˜๊ฒŒ ๋กœ๋”ฉ์‹œ์—๋Š”

{isLoading && <div>๋กœ๋”ฉ ์ค‘...</div>}

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

  • ๋ ˆ์ด์•„์›ƒ ์‹œํ”„ํŠธ (Layout Shift)
    • ์›น ํŽ˜์ด์ง€์˜ ์š”์†Œ๋“ค์ด ์˜ˆ์ƒ์น˜ ๋ชปํ•˜๊ฒŒ ์ด๋™ํ•˜๋Š” ํ˜„์ƒ
    • ๊ธฐ์กด์— ๊ณ„์‚ฐ๋˜์—ˆ๋˜ Layout์ด ๋‹ค์‹œ ๊ณ„์‚ฐ๋˜๋Š” ๋ฌธ์ œ๊ฐ€ ์žˆ์Œ

Layout Shift ํ˜„์ƒ์ด ์ผ์–ด๋‚˜์ง€ ์•Š๋„๋ก ํ•˜๊ธฐ ์œ„ํ•ด์„œ ๋ณด์—ฌ์ค˜์•ผํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ์™€ ํฌ๊ธฐ๊ฐ€ ๊ฐ™์€ ์ปดํฌ๋„ŒํŠธ๋ฅผ Suspense fallback์ปดํฌ๋„ŒํŠธ์— ๋ณด๋‚ด์ฃผ๊ฒŒ ๋˜๋ฉด Layout์ด ๋‹ค์‹œ ๊ณ„์‚ฐ๋˜์ง€ ์•Š์•„์„œ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜๊ณผ ์ตœ์ ํ™”๋ฅผ ๋™์‹œ์— ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์Œ

<Suspense fallback={<RecommendLiveSkeleton />}>
  <DataComponent />
</Suspense>

Suspense์˜ fallback์ปดํฌ๋„ŒํŠธ์— skeleton์„ ์ง์ ‘ ๋งŒ๋“ค์–ด์„œ ๋Œ€์‘

  • skeleton ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์„ ์ •
    • react-loading-skeleton, react-skeleton-loader : ๊ฐ„๋‹จํ•˜๊ณ  ๋น ๋ฅด๊ฒŒ ์ ์šฉ
    • react-content-loader : ๋””์ž์ธ์„ ์ปค์Šคํ„ฐ๋งˆ์ด์ง• ๊ฐ€๋Šฅ (svg ๊ธฐ๋ฐ˜)
  • ์ดˆ๊ธฐ ๋กœ๋”ฉํ™”๋ฉด

image

  • ๊ฐœ์„  ํ›„

์—๋Ÿฌ ์ปดํฌ๋„ŒํŠธ

์ถ”๊ฐ€๋กœ, ์›นํŽ˜์ด์ง€์˜ ์„ฑ๋Šฅ์ง€ํ‘œ์ธ **CLS(Cumulative Layout Shift)**๋ฅผ ํ†ตํ•ด์„œ Layout Shift ๋นˆ๋„๋ฅผ ์ธก์ •ํ•  ์ˆ˜ ์žˆ์Œ

https://wit.nts-corp.com/2020/12/28/6240

React-Query๋ฅผ ํ™œ์šฉํ•˜์—ฌ Suspense ์ฒ˜๋ฆฌํ•˜๊ธฐ

React-Query์˜ ๋ฒ„์ „์ด ๋‹ฌ๋ผ์ง€๋ฉด์„œ DefaultOptions ์†์„ฑ์—์„œ suspense ์†์„ฑ์ด ์ œ๊ฑฐ๋จ

image 1

OmitKeyof ์†์„ฑ์„ ํ†ตํ•ด suspense๊ฐ€ ๋น ์ง„ ๊ฒƒ์„ ํ™•์ธ ํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

Suspense๋ฅผ ์ง€์›ํ•˜๋Š” ์ƒˆ๋กœ์šด ํ›…์ด ์žˆ์—ˆ๋‹ค.

  • useSuspenseQuery
  • useSuspenseQueries

์œ„์˜ suspenseQuery๋Š” ๊ธฐ์กด์˜ useQuery์˜ ๊ธฐ๋Šฅ์— React์˜ Suspense๋ฅผ ์ž๋™์ ์œผ๋กœ ์ง€์›ํ•ด์ฃผ๋Š” ํ›…์ด๋‹ค.

export const useMainLive = () => {
  return useSuspenseQuery<MainLive[], Error>({
    queryKey: ['mainLive'],
    queryFn: fetchMainLive,
    refetchOnWindowFocus: false,
  });
};

Suspense์™€ ErrorBoundary์˜ ํ†ตํ•ฉ

ErrorBoundary ๋„์ž…

  • ์ปดํฌ๋„ŒํŠธ์—์„œ ๋ฐœ์ƒํ•œ ์—๋Ÿฌ ๊ด€์‹ฌ์‚ฌ๋ฅผ ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ๋กœ(ErrorBoundary) ์œ„์ž„
  • ์—๋Ÿฌ์‹œ ๋ณด์—ฌ์•ผ ์ค˜์•ผ ํ•  ์ปดํฌ๋„ŒํŠธ ํ• ๋‹น

Suspense์™€ ErrorBoundary๋ฅผ **AsyncBoundary**๋ผ๋Š” ํ•˜๋‚˜์˜ ์ปดํฌ๋„ŒํŠธ๋กœ ํ†ตํ•ฉ

โ†’ ๋น„๋™๊ธฐย Suspenseย ์ปดํฌ๋„ŒํŠธ์˜ ๋กœ๋”ฉ ์ƒํƒœ์™€ย Errorย ์ƒํƒœ๋ฅผ ๋™์‹œ์— ์ฒ˜๋ฆฌํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ

https://www.slash.page/ko/libraries/react/async-boundary/src/withAsyncBoundary.i18n

<AsyncBoundary
  pendingFallback={<RecommendLiveSkeleton />}
  rejectedFallback={(error) => <RecommendLiveError error={error} />}
>
  <RecommendLive />
</AsyncBoundary>
  • ์ธ์ž๊ฐ’
    • pendingFallback : ์ปดํฌ๋„ŒํŠธ ๋กœ๋”ฉ ์‹œ์— ๋ณด์—ฌ์•ผํ•  ์ปดํฌ๋„ŒํŠธ
    • rejectedFallback : ์—๋Ÿฌ์‹œ์— ๋ณด์—ฌ์•ผํ•  ์ปดํฌ๋„ŒํŠธ
  • ์žฅ์ 
    • ๊ตฌํ˜„์˜ ํŽธ๋ฆฌํ•จ
    • ์ถ”ํ›„์— error๊ฐ€ ๋ฐœ์ƒํ•œ ์ปดํฌ๋„ŒํŠธ์—์„œ ๋…๋ฆฝ์ ์œผ๋กœ refetch ๊ฐ€๋Šฅ

LiBoo

ํŒ€

๊ณตํ†ต

๋ฏผ์ง€

์˜๊ธธ

์ค€์„œ

์ง€์ˆ˜

์ฐฝํ˜„

๋ฐ์ผ๋ฆฌ ์Šคํฌ๋Ÿผ

ํšŒ์˜๋ก

๋ฐœํ‘œ

์ผ๊ธฐ์žฅ

Clone this wiki locally