ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [React-Window] 리액트-윈도우 패키지로 윈도잉 기법 써보기 (가상화 목록, 무한스크롤?)
    [TIL] 카테고리/React 2022. 6. 15. 00:46

    와 ㅠㅠ 하루 종일 골머리 무진장 썩어가면서 여튼 해냈다 ㅠㅠ

     

    사실 맞게 했는지는 아직도 잘모르겠다...... 그렇지만 해냈다..!

     

    무한스크롤이 너무 해보고 싶어서 어떻게 구현해볼까 하다가

    기술매니저님의 윈도잉 기법을 사용해보라는 조언을 받고 

    react-window 를 사용한 가상화 목록 (List Virtualization / Windowing) 구현에 도전했다.

     

    하다보니 사실 무한스크롤과는 조금 다른 개념이란 걸 알게되었지만,

    1) 일반적인 방법의 무한 스크롤은 남은 항해기간 동안에도 충분히 또 해볼 수 있을 것 같았고

    2) 백엔드분들이 너무 바빠보여서(...) 부가적인 기능으로 부담을 안겨주기가 싫었다..

    3) 결국 렌더링 비용을 줄여 페이지 초기 로딩 속도를 개선한다는 측면에서 목적과 맞았다. 

     

     

    ( 무한스크롤은 사용자가 페이지 하단에 도달했을 때, 콘텐츠가 계속 로드되는 방식이라면,
    윈도잉 기법은 단지 렌더링 할 때, viewport에 보이는 부분만 렌더링 되는 방식이다.) 

     

     

    Windowing 기법에 흔히 쓰는 라이브러리는 두 종류가 있었는데, 
    react-virtualized 와 react-window 였다. 개발하신 분은 같은 분이신 듯 했는데,
    차이는 react-virtualized를 간소화시킨 버전이 react-window 이다.
    리액트 윈도우는 그래서 당연히 기능이 더 적지만 가볍다. (더 흔히 쓰이는건 react-virtualized)

     

    대단한 기능을 바란게 아니어서 그냥 가벼운게 최고지~ 하면서

    리액트 윈도우를 택했는데....... 좀 후회했다. 그 이유는

     

    1.

    .... 이거 사용법을 검색하면 자꾸, 리액트 윈도우의 사용법이 아니라..

    윈도우에서 리액트 사용하는법 (a.k.a React 설치 방법 ^.^...)이 튀어나온다 ㅠ...
    이름을 다르게 지어줬더라면 참 좋았을 것 같다.

     

    2.

    내가 구현하고 싶은 것은 그리드(grid) 형태 목록(=2차원)을 가상화하는 것이었는데,
    대부분의 사용 예시, 사용방법 소개는 1차원 적인 형태의 List 만 다루고 있었다.

    거의 공식 문서 밖에 볼게 없을 수준으로... 볼게 없었다... 물론 공식문서는 영어다.
    개발공부 1개월차 신생아는 혼란스러웠다 @_@............

     

    3. 
    결국 패키지를 추가 설치해야했다. 가변적인 윈도우 창의 크기를 측정할 방법이 없었기 때문이다.

    (react-virtualized-auto-sizer 라는 패키지로 해결했고, 공식문서에서도 쓰라고 추천해주고 있다.)

     

     

    구현한 코드의 모양은 대충 이렇다.

     

    import { FixedSizeGrid as Grid } from 'react-window';
    import AutoSizer from 'react-virtualized-auto-sizer';
    
    const Windowing = () => {
      const posts = useSelector((state) => state.posts.list)
    
      // 아이템 데이터를 만들어 주는 콜백 함수
      const makeItem = useCallback ((data) => {
        const item = data.data[0]
        const rowLength = data.data[1]
        const columnIndex = data.columnIndex
        const rowIndex = data.rowIndex
        const style = data.style
        
        const itemIndex = columnIndex + (rowIndex*rowLength)
        
        return itemIndex < item.length ? (
            <div style={style}>
              <div>
                <h5>{item[itemIndex].onair_year}~</h5>
                <h3>{item[itemIndex].title}</h3>
                <span>❤️ {item[itemIndex].likes} </span>
                <button onClick={() => navigate('/write/' + item[itemIndex].id)}> 수정</button>
              </div>
            </div>
          ) : (null)
      })
    
    
    // 리액트 윈도우 패키지의 그리드를 사용하는 부분 (컴포넌트가 리턴하는 내용)
      return (
        <Wrap>
        <AutoSizer>
          {({ height, width }) => (
            <Grid
              columnCount={Math.floor(width/300)}
              columnWidth={320}
              height={height}
              rowCount={Math.ceil(posts.length/(width/300))}
              rowHeight={490}
              width={width}
              itemData={[posts, Math.floor(width/300)]}
            >
              {makeItem}
            </Grid>
          )}
        </AutoSizer>
        </Wrap>
      )
    }

    .

     

    ...? 막상 이렇게 보니 별게 없잖아...? 

    왤케 짧지 ㅠㅠ 하루 종일 고생했는데 진짜 ㅠㅠㅠ

     

    암튼 내용을 풀어보면 대충 이렇다.

     

    임포트 해오는 부분

     

    여긴 그리드 내용. 그리드 태그안에 온갖 속성을 붙여야한다.

    더 많은 속성은 포스팅 맨 끝에 있는 공식문서 참고.

     

     

     

    물론 맞게 한건지는 나도 잘 모르지만 22

    잘 출력이 되긴 한다  ㅎ... 행 .. 복....

     

    혹시 나처럼 골머리 앓다 찾아오는 사람 있을까봐

    진행중에 겪었던 난항들 중 일부를 기록해본다 (...)

     

     

    1) 윈도우 크기를 잡기가 힘들다! (총 넓이, 총 길이 부분)

    => react-virtualized-auto-sizer 패키지로 해결했다. 만약 패키지 다운을 받고
        알맞게 적용했는데도 뭔가 크기가 이상하게 잡힌다면 :
        <AutoSizer> 태그 바깥을 <div> 태그로 둘러주고, 이 div의 크기를 잡아주자.
        오토사이저가 크기를 재는 기준요소는 >부모요소< 이다.

     

    2) 리스트 아이템들이 보이질 않는다 (....)

      => 아마 콜백 함수 안쪽을 수정해야 할 가능성이 높을 것 (...)

       보이긴 보이는데 이상하게 보이거나 스크롤해야만 보인다면 
       리스트 아이템의 가장 바깥쪽 태그에 style={style} 태그를 적용해줘야만한다.
       가장 바깥쪽 태그에 이미 스타일이 지정되어 있어 곤란하다면 한번 더 감싸자.

     

    3) 아이템들 사이에 gap을 주고 싶은데 어쩐지 공식문서에서 소개하는 방식이 죽어도 안먹힌다.

      => 단순하게 생각해서, grid 태그 안에 지정한 아이템 크기가 실제 아이템 크기보다 크게하면 된다.

     

    4)  ?? 동일한 아이템이 여러번 보인다..?
    => 콜백함수 안쪽 내용은 자동으로 rowCount * columnCount 번 만큼 반복해서 출력된다.
      즉 이미 반복문이 돌고 있다. 만약 콜백함수 안에 map이나 반복문을 돌리는 나처럼 멍청한 

      일을 했다면.. 안해도 된다. 하하하...

     

    5)  아이템 갯수에 따라 오류가 나기도, 정상 출력이 되기도 한다.

    =>  콜백함수 안쪽 내용은 자동으로 rowCount * columnCount 번 만큼 반복해서 출력된다. <<

      아이템 갯수보다 많이 반복되지 않도록 조절하자. 나는 넘치는 횟수만큼 null을 리턴해서 해결했다.

     

    6) 레이아웃을 잡으려고 하는데 안먹힌다!
    => 모든 아이템에는 position:absolute 속성이 들어가 있는 상태이다. 생각하면서 하면 될듯.

     

     

     

    사실 이런 문제들 보다

    그냥 사용법 자체를 감잡는게 어려웠지만...

     

    그건 위에 코드 참고하면 해결될거라고 보고(...)
    (물론 맞는 코드라는 확신은 전혀 없다. 암튼 굴러는 간다.)

     

     

     

    구현해보고 테스트 해보니....

     

    1) 리스트가 길어지면 길어질수록 확실히 윈도잉을 적용을 했을 때 처음 렌더링 속도가 빠르다.


    2) 그렇지만 리스트의 길이가 짧다면 초기 렌더링조차 오히려 한번에 렌더링해오는게 빠르게 느껴진다 :
        아무래도 현재 위치 잡는 것 부터 들어갈 아이템의 갯수 계산 등 이것저것 해야하다보니......
        아마 전체 크기를 정해두지 않고 반응형으로 계산하게 만들어서 더 그런 것 같다.


    3) 당장 리스트가 별로 길지 않은 현재 프로젝트에서는.. (실제 측정한 값과 별개로..) 
        퍼포먼스 차이가 크게 와닿지 않는다. 확실히 긴~~ 목록일 때 유용한 기법인듯하다.

    4) 그러나 스크롤을 빠르게 할 때는 한번에 불러오는 방식이 빠르게 느껴진다.

     

     

     

    Mock 데이터를 약 1,000개 정도 넣어두고,

    새로고침했을 때의 퍼포먼스를 기록했다.


    (사실 이렇게 측정하면 되는건지에 대한 확신은 없지만..?)

     

     

     

     

    확실히 기존방식에 비해 윈도잉 적용한 버전이

    로딩시간/스크립팅/렌더링/페인팅 시간이 극적으로 짧다.

     

    체감은 한 요정도...

     

     

    그러나 데이터가 양을 늘리지 않고 원래 상태 (7개만 있는..)를 봤을 때는,

     

     

     

     

    윈도잉을 굳이 적용하지 않는 편이 빠르다.

    스크립팅에 시간을 더 쓰게 되기 때문에..

     

    별 차이 안나는 것 같은데도 눈으로 봤을때도 약-간 차이난다.

    뭔가 윈도잉 적용 안한 버전은 모든게 동시에 뿅! 나오는데 윈도잉 적용된 버전은 이-얍! 하고 보이는 느낌?
    영상으로는 티가 전혀 안나서 굳이 찍지는 않았다...

     

    윈도잉 적용한 쪽은 확실히 리스트 갯수가 몇 개가 되든 초기 렌더링 비용은 비슷- 한것 같다.

     

    대강 몇개쯤 부터 윈도잉이 유리해지는지는 굳이 계산해보지 않았다 (...) 


    그렇지만 리스트가 굳이 길어지지 않는다면 꼭 필요한지는 생각해봐야할지도.

     

     

    +) 윈도잉을 적용할 경우, CSS 레이아웃 잡는게 굉장히 뭔가 말을 잘 안들어서 열받았다.

    일단 아이템 하나하나가 전부 position:absolute 속성을 가지고 있기 때문에 ^^;;

     

    +) 필요하다면, 윈도잉기법을 쓰고 또 infinity-loader를 붙여서 lazy load 까지 하는 것도 가능하다.

        나는... 굳이 하지 않았다. 리스트가 그 만큼 길어질 일이 없을 것 같다 (...)

     

     

    React Window 깃 헙

    https://github.com/bvaughn/react-window

     

    GitHub - bvaughn/react-window: React components for efficiently rendering large lists and tabular data

    React components for efficiently rendering large lists and tabular data - GitHub - bvaughn/react-window: React components for efficiently rendering large lists and tabular data

    github.com

     

    사용 예시 / 속성들 설명

    https://react-window.vercel.app/#/examples/list/fixed-size

     

    react-window

     

    react-window.vercel.app

Designed by Tistory.