본문으로 건너뛰기

© 2026 Molayo

YouTube요약2026. 06. 15. 06:49

UI Toolkit: ListViews

요약

Unity의 UI Toolkit에서 대규모 데이터를 효율적으로 처리하기 위한 ListView 사용법을 설명합니다. ScrollView와 달리 ListView는 가상화(virtualization) 기술을 통해 화면에 보이는 요소만 생성하고 재사용함으로써 성능 최적화를 구현합니다.

핵심 포인트

  • ListView는 가상화 기술을 통해 대규모 데이터 세트를 효율적으로 처리함
  • ScrollView와 달리 뷰포트에 보이는 만큼의 요소만 생성하여 메모리 절약
  • makeItem을 통해 시각적 요소를 생성하고 bindItem으로 데이터를 연결
  • 데이터 소스를 기반으로 행(row)을 재사용하는 데이터 기반 컬렉션 방식

비디오: UI Toolkit: ListViews
채널: Unity
재생 시간: 9분 4초
출처: 자막 (수동, en-US)

내용:
만약 UI Toolkit에서 긴 아이템 목록을 표시해야 했던 적이 있다면, 아마 ScrollView를 사용해 보셨을 것입니다. 하지만 스크롤해야 할 아이템이 1,000개가 된다면 어떻게 될까요? 만약 UI에 랙(lag)이 발생하기 시작한다면, 이제 ListView로 전환해야 할 때입니다. ListView는 비슷해 보이지만, 대규모의 데이터 기반 컬렉션(data-driven collections)을 처리하도록 설계되었습니다. 어떻게 작동하는지 살펴보겠습니다. ListView는 ScrollView와 유사합니다. 아직 ScrollView에 대한 영상을 보지 않으셨다면, 설명란에 있는 링크를 확인해 보세요. ScrollView는 단순한 컨테이너입니다. 자식 요소(child elements)를 추가하면 각 요소가 시각적 트리(visual tree)의 일부가 됩니다.

500개의 아이템을 추가하면, 500개의 시각적 요소가 생성됩니다. 간단하지만 잠재적으로 비용이 많이 들 수 있습니다. ListView는 다르게 작동합니다. 대규모 데이터 세트(data sets)를 위해 구축되었습니다. ListView는 가시적인 뷰포트(viewport)를 채우기에 충분한 만큼의 행(row) 요소만 생성합니다. 스크롤을 하면, 해당 행들은 재사용되어 새로운 데이터에 다시 바인딩(rebound)됩니다. 따라서 수천 개의 아이템이 있더라도, 한 번에 활성화된 요소는 불과 수십 개뿐일 수 있습니다. 이것이 두 방식의 핵심적인 차이점입니다. 이러한 동작을 가상화 (virtualization)라고 부릅니다. 모든 것을 렌더링하는 대신, ListView는 볼 수 있는 부분만 렌더링합니다. 그런 다음 필요에 따라 해당 시각적 요소들을 재활용합니다.

행(Rows)은 특정 아이템에 속하는 것이 아니라, 현재 보이는 데이터를 표시하는 일시적인 컨테이너일 뿐입니다. 이 점을 이해하고 나면 ListView의 다른 모든 부분도 이해되기 시작할 것입니다. ListView를 설정하는 것은 ScrollView보다 몇 단계 더 필요하지만, 네 가지 주요 부분으로 이루어진 명확한 패턴을 따릅니다. 각 단계를 하나씩 살펴보겠습니다. 여기 코드에서 ListView를 설정하는 최소한의 예시가 있습니다. 이 UI는 정수(integers)의 간단한 목록을 표시할 것입니다. 우리는 이것을 m_Items라고 부를 것이며, 이 예제에서는 우리의 데이터 소스(data source)가 됩니다.

ListView 자체는 이미 UXML에 정의되어 있으므로, 우리는 단순히 그 참조(reference)를 가져올 것입니다. 그런 다음 모든 것을 연결하기 위해 helper method인 ConfigureListView를 사용합니다. 먼저 ListView의 itemsSource를 m_Items에 할당합니다. 이것이 여러분의 데이터(data)입니다. 이 경우에는 단순히 정수(integer) 리스트일 뿐입니다. ListView는 이 컬렉션(collection)을 읽어 항목이 몇 개 존재하는지, 그리고 무엇을 표시해야 하는지를 결정합니다. ListView는 데이터를 생성하지 않습니다. 단순히 리스트에 이미 있는 내용을 반영할 뿐입니다. 다음으로 makeItem을 정의합니다. 이 함수는 각 행(row)을 위한 시각적 요소(visual elements)를 생성합니다.

이 간단한 예제에서는 단순히 레이블(label)을 반환합니다. 이것은 데이터가 없는 행의 구조이며, 행들이 재사용되기 때문에 이 함수는 화면을 채우기에 충분할 만큼만 몇 번 실행됩니다. 그다음 bindItem을 정의합니다. 여기서 데이터가 적용됩니다. bindItem 콜백(callback)은 각 행의 시각적 요소를 데이터 소스(data source)의 항목과 연결합니다. 이 함수는 두 가지, 즉 행 요소(row element)와 표시할 항목의 인덱스(index)를 전달받습니다. 해당 인덱스를 사용하여 데이터를 조회한 다음 UI를 업데이트합니다. 이 경우에는 요소를 레이블(label)로 캐스팅(cast)한 다음, 데이터 리스트의 포맷된 문자열(formatted string)을 사용하여 텍스트를 설정합니다.

그리고 기억하세요, 행들이 재사용되기 때문에 스크롤할 때마다 이 함수가 반복적으로 실행됩니다. 따라서 이 안에 있는 모든 로직은 실행될 때마다 행의 시각적 상태(visual state)를 완전히 업데이트해야 합니다. 마지막으로 fixedItemHeight를 예를 들어 32픽셀로 설정합니다. 이는 ListView에 모든 행의 높이가 동일하다는 것을 알려주며, 이를 통해 레이아웃을 더 효율적으로 계산하고 스크롤 성능을 향상할 수 있습니다. 또한 이러한 최적화를 최대한 활용할 수 있도록 virtualization method(가상화 방법)를 FixedHeight로 설정하고, 이 예제는 표시 전용이므로 selectionType을 사용하여 선택(selection)을 비활성화합니다.

모든 설정을 마친 후, ListViews의 rebuild 메서드를 호출합니다. 이를 통해 ListView가 새로고침되고 새로운 설정이 적용됩니다. 즉, 단 몇 줄의 코드로 itemsSource는 데이터를 제공하고, makeItem은 행(row) 구조를 생성합니다. bindItem은 콘텐츠로 행을 채우며, fixedItemHeight는 성능을 향상시킵니다. 그리고 마지막으로 rebuild가 설정을 적용합니다. 이것이 모든 ListView의 기초입니다. 이제 스크롤 중에 어떤 일이 발생하는지 살펴보겠습니다. 데모에는 ListView의 콜백(callback)을 기록하는 stats 컴포넌트가 포함되어 있으며, 이는 스크롤할 때 어떤 일이 일어나는지 이해하는 데 도움이 될 것입니다.

makeItem은 몇 번만 실행되지만, bindItem은 반복적으로 실행된다는 점에 주목하세요. 그 이유는 행(row)이 재사용되기 때문입니다. 스크롤은 새로운 비주얼(visual)을 생성하는 것이 아니라, 기존의 것을 재할당할 뿐입니다. ListView는 ScrollView를 기반으로 구축됩니다. 동일한 스크롤 시스템을 사용하지만, 가상화 (virtualization)와 데이터 바인딩 (data binding) 기능이 추가되었습니다. UI Builder에서 ListView를 열고 계층 구조(hierarchy)에서 확장해 보면, 구조의 일부로서 내부의 ScrollView를 실제로 확인할 수 있으므로 동일한 규칙이 적용됩니다. 스크롤은 ListView의 크기가 제한되어 있고 콘텐츠가 가시 영역을 초과해야 할 때만 작동합니다.

만약 스크롤이 작동하지 않는다면, 일반적인 사항들을 확인해 보세요. ListView에 고정되거나 제한된 높이(fixed or constrained height)가 설정되어 있나요? 부모 컨테이너가 콘텐츠에 맞춰 확장되고 있나요? 항상 ListView가 적절히 제한되어 있고 콘텐츠가 뷰포트 (viewport)를 초과하여 스크롤할 수 있는지 확인해야 합니다. ListView를 다룰 때 가장 중요한 함정은 다음과 같습니다: 행(row)은 재활용됩니다. 스크롤할 때 동일한 비주얼 요소가 서로 다른 아이템에 재사용됩니다. 따라서 UI 상태 (UI state)는 행에 속하는 것이 아니라, 왼쪽에 있는 데이터에 속합니다. 이 예제는 그 점을 간과하고 있습니다. 만약 행을 토글(toggle)한 다음 스크롤하면, 그 상태는 소실되거나 다른 아이템에 나타나게 됩니다.

그 이유는 변경 사항이 데이터가 아닌 UI에만 존재하기 때문입니다. 오른쪽에서는 상태를 데이터에 저장함으로써 이 문제를 해결합니다. 토글 항목을 체크한 후 위로 스크롤했다가 다시 돌아와도, 체크된 상태가 유지됩니다. 스크립트에서 이것이 어떻게 설정되어 있는지 살펴보겠습니다. 이는 앞서 다룬 것과 동일한 ListView 설정을 사용합니다. 여전히 데이터 소스(data source), makeItem, 그리고 bindItem이 있지만, 이제 한 가지 핵심 요소를 추가합니다. 바로 변경 사항을 데이터에 다시 쓰는 것입니다. 우리는 이름과 활성화 상태(enabled state)를 가진 토글 항목들의 리스트라는 간단한 데이터 모델을 가지고 있습니다.

00:06:31,090 --> 00:06:32,325 ListView는 이 리스트를 읽으며, 모든 것은 makeItem과 bindItem이라는 두 메서드를 통해 동기화된 상태를 유지합니다. 이제 이를 두 부분으로 나눌 수 있습니다. 사용자가 클릭했을 때 어떤 일이 발생하는가, 그리고 각 행(row)이 어떻게 올바르게 설정되는가입니다. makeItem에서 우리는 행을 생성하고, 사용자가 토글의 상태를 변경할 때 실행될 콜백(callback)을 토글에 등록합니다. 콜백이 실행되면, UI가 즉각 반응하도록 시각적 요소(visuals)를 바로 업데이트합니다. 그런 다음 이 행이 어떤 아이템을 나타내는지 찾아냅니다. 해당 인덱스(index)는 toggle.userData에 저장됩니다. 인덱스가 유효하다면, 새로운 값을 데이터에 다시 씁니다. 그것으로 끝입니다.

사용자가 토글을 변경하면, 우리는 데이터를 업데이트합니다. 이제 두 번째 부분인 행이 자신이 어떤 아이템을 나타내는지 아는 방법에 대해 알아보겠습니다. 이는 bindItem에서 이루어집니다. 이 메서드는 행이 재사용될 때마다 실행됩니다. 우리는 토글(toggle)과 레이블(label)을 가져온 다음, 현재 인덱스에서 아이템을 가져옵니다. 레이블 텍스트를 업데이트한 다음, 인덱스를 toggle.userData에 저장합니다. 이것이 나중에 콜백에서 사용하는 값입니다. 그다음 데이터로부터 토글 값을 설정합니다. 이때 콜백이 다시 트리거되지 않도록 SetValueWithoutNotify를 사용합니다. 마지막으로 행의 시각적 요소를 업데이트합니다.

따라서 전체적인 흐름은 간단합니다. 데이터를 업데이트하면 행(row)이 재사용되고, 그 다음 bind item이 해당 데이터로부터 UI를 복원합니다. 이제 ListView가 어떻게 작동하는지, 그리고 어떻게 설정하는지 살펴보았습니다. 대규모 데이터 세트나 성능이 중요한 경우에는 ListView를 사용하세요. 작거나 정적인 콘텐츠에는 ScrollView를 사용하십시오. 설명란의 링크를 확인하여 QuizU 데모 프로젝트를 다운로드하고 직접 시도해 보세요. 관련 컨트롤인 TreeView에 대한 데모도 있습니다. TreeView는 ListView와 유사하지만 계층적 데이터 (hierarchical data) 지원이 추가되었습니다. 즉, 확장 및 축소 가능한 노드 (nodes)를 통해 항목을 부모 및 자식 관계로 구성할 수 있음을 의미합니다.

TreeView는 중첩된 구조 (nested structures)에 적용될 뿐, 데이터 바인딩 (data binding) 및 요소 재사용 (element reuse)이라는 동일한 핵심 패턴을 사용합니다. UI Toolkit의 ListView 및 TreeView에 대해 더 자세히 배우고 싶다면 공식 문서를 참조하세요. 시청해 주셔서 감사합니다.

AI 자동 생성 콘텐츠

본 콘텐츠는 YouTube Unity 공식의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0