@tanstack/react-virtual 사투 2

jingjinge·2025년 1월 27일

TroubleShooting

목록 보기
3/5
post-thumbnail

지난 화를 읽고 와주세요.

문제를 해결했다.

기존 코드

    function PrintList() {
        return (
            <div
                ref={parentRef}
                style={{
                    height: `200px`,
                    width: `100%`,
                    overflow: "auto",
                }}
            >
                {status === "pending" ? (
                    <li>Loading</li>
                ) : status === "error" ? (
                    <div>{`error`}</div>
                ) : (
                    <ul
                        style={{
                            height: `${rowVirtualizer.getTotalSize()}px`,
                            width: "100%",
                            position: "relative",
                        }}
                    >
                        {rowVirtualizer.getVirtualItems().map((virtualRow) => (
                            <SearchedBooks
                                key={virtualRow.index}
                                book={allRows[virtualRow.index]}
                                onClick={onClick}
                                virtual={virtualRow}
                            />
                        ))}
                    </ul>
                )}
            </div>
        );
    }

    return (
        <div className="space-y-4">
            <div className="flex space-x-2">
                <Input
                    placeholder="Enter book title"
                    className="flex-grow"
                    onChange={(e) => {
                        inputTitle.current = e.currentTarget.value;
                    }}
                    onKeyDown={(e) => {
                        if (e.key === "Enter") fetch();
                    }}
                />
                <Button onClick={fetch}>Search</Button>
            </div>
            <PrintList />
        </div>
    );
};

Print를 하는 곳이 너무 보기 안좋아서, return을 깔끔하게 남기고 싶어 컴포넌트 내에 컴포넌트로 따로 선언해서 불러왔는데, 이에 대한 결과는 아래와 같았다.

Image

스크롤을 내리면 계속해서 렌더링 되는 상황이었다.

해치운 코드

진짜 혹시나 싶어서 컴포넌트 내부 컴포넌트를 삭제하고 넣어봤다. 코드는 아래와 같다.

딱히 자세히 볼 것도 없는게, PrintList 컴포넌트 return값만 빼서 그자리에 넣은거다..

return (
        <div className="space-y-4">
            <div className="flex space-x-2">
                <Input
                    placeholder="Enter book title"
                    className="flex-grow"
                    onChange={(e) => {
                        inputTitle.current = e.currentTarget.value;
                    }}
                    onKeyDown={(e) => {
                        if (e.key === "Enter") fetch();
                    }}
                />
                <Button onClick={fetch}>Search</Button>
            </div>
            <div
                ref={parentRef}
                style={{
                    height: `200px`,
                    width: `100%`,
                    overflow: "auto",
                }}
            >
                {status === "pending" ? (
                    <li>Loading</li>
                ) : status === "error" ? (
                    <div>{`error`}</div>
                ) : (
                    <ul
                        style={{
                            height: `${rowVirtualizer.getTotalSize()}px`,
                            width: "100%",
                            position: "relative",
                        }}
                    >
                        {rowVirtualizer.getVirtualItems().map((virtualRow) => (
                            <SearchedBooks
                                key={virtualRow.index}
                                book={allRows[virtualRow.index]}
                                onClick={onClick}
                                virtual={virtualRow}
                            />
                        ))}
                    </ul>
                )}
            </div>
        </div>
    );

결과는?

Image

아직 무한스크롤을 구현하지 않아서 다 밑에까진 안내려갔지만 잘 된다.

이유

컴포넌트 내부에서 상태가 변경되면서, 아예 새로운 컴포넌트라고 인식을 해버린 것이다.

Reconcliation 과정에서, li만 바꾸는 것이 아닌 컴포넌트 자체가 바뀌었으니 div부터 렌더링을 하자! 라고 인식한 것이다.

그럼으로 인해서 계속해서 새롭게 바뀐 컴포넌트를 출력하게 되었고

난 밑으로 내려가면 다시 렌더링된 PrintList라는 컴포넌트를 계속 마주하게 된 것이다.

트러블슈팅을 통해 깨달은 것

진짜 얼토당토 없는 오류고, 몰랐던 내가 바보가 맞다. 이걸 몰랐던 내가 하수인 것이 분명하다.

하지만, @tanstack/react-virtaul은 렌더링을 효과적으로 할 수 있게끔 도와주는 툴이란 것을 확실하게 깨달았다.

내 코드에서 안되니, Docs에서 제공하는 StackBlits로 실험을 많이 해보았는데, 이를 통해 구조를 확실하게 이해하게 되었다.

그래도 다행인 점은 적어도 저 라이브러리 상에서 내가 틀린 점은 없었다는 점이다.

그래도 불행인 점은 가장 기본적인 렌더링 패턴도 모르면서 고급 기술을 익히고 있던 내 자신을 모르고 있던 점이다.

처음으로 간단한 로직을 라이브러리를 사용해 어렵게 구현하면서 렌더링을 최적화하는 과정이었는데, 다시 하려면 쉽지 않을 것 같다.

빠른 시일 내에 두어번 더 사용해 볼 생각이다.(내일부터 진행할 프로젝트에 일부 껴있기 때문에)

깨달았으니, 기본을 더 탄탄하게 쌓고 하나하나 의심하는 과정을 익히려 한다.

최종 결과물

Image

맨 밑의 index가 현재 보이는 index가 됐으며, 다음 페이지가 존재하고, 다음 페이지를 fetching해오지 않을 때, 다음 페이지를 가져오게 설계했다.

이로 인해 사용자는 많은 도서의 li를 한번에 렌더링 하는 것이 아닌, 스크롤을 내림으로써 조금씩 렌더링하는 최적화를 일구어냈다.

0개의 댓글