Files
XRLib/Assets/Scripts/NHN/CLAUDE.md
2026-01-08 20:15:57 +09:00

5.9 KiB

NHN 모듈 가이드

NHN은 uGUI 기반 무한 스크롤(InfiniteScroll) 컴포넌트 라이브러리입니다. 대량 데이터 목록을 효율적으로 렌더링하기 위해 아이템을 재활용(recycling)합니다.

참고: 새 UI는 UI Toolkit의 ListView/TreeView 가상화 기능을 권장합니다. 이 모듈은 uGUI 기반 레거시 프로젝트용입니다.


파일 구조

NHN/
├── InfiniteScroll.cs            # 메인 클래스 (partial)
├── InfiniteScroll.ItemContainer.cs  # 아이템 컨테이너 관리
├── InfiniteScroll.ItemData.cs   # 데이터 컨텍스트
├── InfiniteScroll.Layout.cs     # 레이아웃 계산
├── InfiniteScroll.Scroll.cs     # 스크롤 처리
├── InfiniteScrollItem.cs        # 아이템 베이스 클래스
├── InfiniteScrollData.cs        # 데이터 베이스 클래스
├── ScrollLayout.cs              # 레이아웃 설정
├── ScrollMoveTo.cs              # 스크롤 이동 유틸리티
├── LayoutUpdater.cs             # 레이아웃 업데이트
├── DraggableRect.cs             # 드래그 가능한 영역
├── DragEventHandler.cs          # 드래그 이벤트 처리
└── ContentSizeSetter.cs         # 콘텐츠 크기 설정

핵심 클래스

InfiniteScroll

uGUI ScrollRect 기반의 무한 스크롤 컴포넌트입니다.

namespace Gpm.Ui
{
    public partial class InfiniteScroll : MonoBehaviour
    {
        // 이벤트
        public ChangeValueEvent onChangeValue;      // 스크롤 값 변경
        public ItemActiveEvent onChangeActiveItem;  // 아이템 활성화 변경
        public StateChangeEvent onStartLine;        // 시작점 도달
        public StateChangeEvent onEndLine;          // 끝점 도달
    }
}

기본 사용법

1. 데이터 삽입

// 단일 데이터 삽입
InfiniteScrollData myData = new MyScrollData();
infiniteScroll.InsertData(myData, immediately: true);

// 다중 데이터 삽입
InfiniteScrollData[] dataArray = new InfiniteScrollData[100];
infiniteScroll.InsertData(dataArray, immediately: true);

// 특정 인덱스에 삽입
infiniteScroll.InsertData(myData, insertIndex: 0, immediately: true);

2. 데이터 제거

// 데이터 객체로 제거
infiniteScroll.RemoveData(dataToRemove, immediately: true);

// 인덱스로 제거
infiniteScroll.RemoveData(dataIndex: 0, immediately: true);

3. 데이터 초기화

// 데이터만 초기화 (아이템 객체 유지)
infiniteScroll.ClearData(immediately: true);

// 완전 초기화 (아이템 객체도 제거)
infiniteScroll.Clear();

4. 데이터 업데이트

// 단일 데이터 업데이트
InfiniteScrollData existingData = infiniteScroll.GetData(0);
// ... 데이터 수정 ...
infiniteScroll.UpdateData(existingData);

// 전체 새로고침
infiniteScroll.UpdateAllData(immediately: true);

이벤트 처리

// 스크롤 값 변경 (첫 번째/마지막 보이는 인덱스)
infiniteScroll.onChangeValue.AddListener((first, last, isStart, isEnd) => {
    Debug.Log($"Visible: {first}~{last}, Start: {isStart}, End: {isEnd}");
});

// 아이템 활성화 상태 변경
infiniteScroll.onChangeActiveItem.AddListener((dataIndex, isActive) => {
    Debug.Log($"Item {dataIndex} is {(isActive ? "active" : "inactive")}");
});

// 시작/끝 도달
infiniteScroll.onStartLine.AddListener((isAtStart) => {
    if (isAtStart) LoadPreviousPage();
});

infiniteScroll.onEndLine.AddListener((isAtEnd) => {
    if (isAtEnd) LoadNextPage();
});

필터링

// 필터 설정
infiniteScroll.SetFilter(data => {
    MyCustomData myData = data as MyCustomData;
    return myData.score > 50; // 점수 50 이상만 표시
});

// 필터 적용 후 업데이트
infiniteScroll.UpdateAllData(immediately: true);

스크롤 제어

// 특정 아이템 위치로 이동
float position = infiniteScroll.GetItemPosition(itemIndex: 10);

// 뷰포트/콘텐츠 크기
float viewportSize = infiniteScroll.GetViewportSize();
float contentSize = infiniteScroll.GetContentSize();
float contentPos = infiniteScroll.GetContentPosition();

// 수동 새로고침
infiniteScroll.RefreshScroll();
infiniteScroll.ResizeScrollView();

커스텀 데이터/아이템 구현

데이터 클래스

public class MyScrollData : InfiniteScrollData
{
    public string title;
    public int score;
    public Sprite icon;
}

아이템 클래스

public class MyScrollItem : InfiniteScrollItem
{
    [SerializeField] private Text titleText;
    [SerializeField] private Image iconImage;

    public override void UpdateData(InfiniteScrollData scrollData)
    {
        base.UpdateData(scrollData);

        if (scrollData is MyScrollData myData)
        {
            titleText.text = myData.title;
            iconImage.sprite = myData.icon;
        }
    }
}

성능 최적화

항목 설명
아이템 재활용 화면에 보이는 아이템만 생성, 나머지는 풀에서 재활용
즉시 업데이트 immediately: true로 즉시 레이아웃 갱신
필터링 서버 필터링 권장, 클라이언트 필터는 대량 데이터 시 주의
아이템 프리팹 가볍게 유지, 복잡한 UI는 지연 로딩 고려

UI Toolkit 마이그레이션

새 프로젝트에서는 UI Toolkit의 ListView 사용을 권장합니다:

// UI Toolkit ListView (권장)
var listView = new ListView();
listView.makeItem = () => new Label();
listView.bindItem = (element, index) => {
    (element as Label).text = items[index].title;
};
listView.itemsSource = items;

InfiniteScroll → ListView 마이그레이션 시:

  • InfiniteScrollData → 일반 데이터 클래스
  • InfiniteScrollItemmakeItem/bindItem 콜백
  • 이벤트 → ListView 이벤트 (selectionChanged 등)