본문 바로가기

DEVELOPER/Dart&Flutter

[Flutter] 목록 출력 ListView, 성능 개선

안녕하세요 🥳 백엔드 개발자 제임스입니다.
오랜만에 글을 작성합니다. 난데없이 '플루터에 대한 내용'으로 인사를 드리네요. 최근에 앱 개발 프로젝트를 진행하고 있습니다. 물론 저는 서버 개발을 담당합니다. 그럼에도 팀이 사용하는 기술에 대해 안다면, 더 좋은 결과를 만들 수 있겠다고 판단하여 이렇게 취미로나마 플루터를 공부하고 있습니다.

오늘 소개할 내용은 List 목록을 출력하는 방법 그리고 성능 개선입니다. 최근에 이 부분을 공부하면서 재미와 흥미를 느껴서 이렇게 기록합니다.


1) ListView

여러 항목을 나열하는 데 사용하는 스크롤 위젯(Widget)입니다. 말 그대로 많은 양의 데이터를 연속적으로 보여줍니다. 예시로 아래와 같이 코드를 작성할 수 있습니다.

예시코드 1.

즉 ListView가 children [] 속성을 가지면서, Column 또는 Row를 지정하지 않고 특정 방향으로 항목을 모두 출력할 수 있습니다. 

이러한 ListView는 내부에 Collection for를 통해 개수를 알 수 없는 항목들도 출력할 수 있습니다.

예시코드 2.

 
body: FutureBuilder(
        future: webtoons,
        builder: (context, snapshot) {
          if (snapshot.hasData) {
            return ListView(
              children: [
                for (var webtoon in snapshot.data!) Text(webtoon.title),
              ],
            );
          }
          return const Center(
            child: CircularProgressIndicator(),
          );
        },
      ),
⬆ FutureBuilder를 통해 API 결과 목록을 갖고 오고 있습니다. 현재 future 타입은 List<Widget>입니다.
해당 결과는 Snapshot에 저장되어 있습니다. 따라서 ListView의 children[] 내부에서 for를 통해 항목 데이터에 접근하고 있습니다.

ListView로 네이버 웹툰 목록 API 출력 결과

이처럼 ListView를 통해서 List 타입의 항목을 출력하는 예시를 보았습니다. 

항목을 출력하기 위해 매우 유익한 위젯입니다. 하지만 문제는 ListView는 데이터 목록을 한 번에 가져와서 조작합니다.
가령 데이터의 용량이 큰 이미지 거나 양이 무수히 많다면, 메모리가 버티지 못하고 죽고 말 것입니다.

 

2) ListView.builder

ListView의 문제를 개선하여 동적인 목록을 생성하는 위젯입니다. 즉, 이 위젯은 사용자가 보고 있는 위치(섹션)에 해당하는 데이터만 렌더링 하여 보여줍니다.

이와 같은 기능이 가능한 이유는 인덱스를 활용하기 때문입니다. itemBuilder 함수를 사용하여 각 항목을 생성하고, 인덱스에 따라 항목을 반환합니다. 그리고 스크롤이 발생하면 화면에서 벗어나는 항목은 재사용 대기열로 이동합니다. 화면에 들어온다면, 재사용 대기열에서 항목을 가져옵니다.  이 같은 방식 덕분에 메모리 사용량을 줄이고, 성능이 향상합니다.

예시코드 1.

return ListView.builder(
              scrollDirection: Axis.horizontal,
              itemCount: snapshot.data!.length,
              itemBuilder: (context, index) {
                var webtoon = snapshot.data![index];
                print('$index, ${webtoon.title}');
                return Text(
                  '$index + ${webtoon.title}',
                );
              },
            );
1) scrollDirection : 스크롤 방향 (ListView에도 있음)
2) itemCount : build 할 item 개수
3) itemBuilder: required 인자, index를 통해 항목 출력

데이터 로딩 결과

위 결과 이미지를 보면, 화면(섹션)에 해당하는 데이터만 콘솔에 출력되는 것을 볼 수 있습니다. 

 

3) ListView.separated

separated는 ListView.builder보다 더 개선된 위젯입니다. 동적인 목록을 생성한다는 점은 builder와 목적이 같습니다. 하지만 separated는 항목 사이에 구분자를 추가할 수 있는 점에서 차이가 있습니다. 따라서 이를 구현하기 위해 separtorBuilder라는 필수인자가 추가됩니다.

즉, 메모리 성능을 개선하여 출력한 목록에서 UI 디자인까지 개선된 위젯이라고 생각하면 좋습니다.

예시코드 1.

return ListView.separated(
              scrollDirection: Axis.horizontal,
              itemCount: snapshot.data!.length,
              itemBuilder: (context, index) {
                var webtoon = snapshot.data![index];
                return Text(webtoon.title);
              },
              separatorBuilder: (context, index) => const SizedBox(
                width: 20,
              ),
            );
위에서 작성했던 ListView.builder 코드에 명칭을 separated로 변경하고, separatorBuilder로 SizedBox() 위젯을 구분자로 반환하도록 추가했습니다.

ListView.separated의 결과

 

반응형