55 KiB
ToolBar UIToolkit 마이그레이션 작업지시서
📋 개요
uGUI 기반의 ToolBar 시스템(Assets/Scripts/UVC/UI/ToolBar/)을 UI Toolkit 기반으로 마이그레이션하는 작업입니다.
기존 ToolbarModel의 데이터 구조와 Toolbar/Toolbox의 public API를 동일하게 유지하면서,
가로/세로 배치 전환 기능을 추가하고, 관심사 분리 원칙을 준수합니다.
🎯 작업 목표
- 독립적인 데이터 구조: UIToolkit 전용
UTKToolBarModel,UTKToolBarItemData별도 구현 - Controller 호환성:
Toolbar/Toolbox의 public API 동일하게 구현 - UIToolkit 전환: View를 UIToolkit 기반으로 재구현
- 가로/세로 배치:
Orientation속성으로 Horizontal/Vertical 전환 지원 - 4가지 버튼 타입: Standard, Toggle, Radio, Expandable 모두 지원
- 관심사 분리: Model, View, Controller 명확히 분리
- 메모리 관리: IDisposable 구현, 이벤트 정리, 메모리 누수 방지
- 성능 최적화: 쿼리 캐싱, GC 최소화, 불필요한 리빌드 방지
- 완전한 문서화: 모든 public/protected 멤버에 XML 주석 필수
📁 파일 구조
기존 파일 (참조용)
Assets/Scripts/UVC/UI/ToolBar/
├── Model/
│ ├── IToolbarItem.cs # 툴바 아이템 인터페이스
│ ├── ToolbarButtonBase.cs # 버튼 추상 베이스
│ ├── ToolbarStandardButton.cs # 일반 버튼
│ ├── ToolbarToggleButton.cs # 토글 버튼
│ ├── ToolbarRadioButton.cs # 라디오 버튼
│ ├── ToolbarRadioButtonGroup.cs # 라디오 그룹 관리
│ ├── ToolbarExpandableButton.cs # 확장 버튼 (서브메뉴)
│ ├── ToolbarSeparator.cs # 구분선
│ └── ToolbarModel.cs # 모델 (아이템 컬렉션)
├── View/
│ ├── IButtonViewProcessor.cs # 버튼별 UI 처리 인터페이스
│ ├── ToolbarStandardButtonViewProcessor.cs
│ ├── ToolbarToggleButtonViewProcessor.cs
│ ├── ToolbarRadioButtonViewProcessor.cs
│ ├── ToolbarExpandableButtonViewProcessor.cs
│ ├── ToggleGroupManager.cs # 라디오 그룹 UI 관리
│ ├── SubMenuHandler.cs # 확장 버튼 서브메뉴 관리
│ └── ToolbarView.cs # 메인 뷰
├── Toolbar.cs # MonoBehaviour 컨트롤러
└── Toolbox.cs # 대안 컨트롤러
신규 파일 (생성)
Assets/Scripts/UVC/UIToolkit/ToolBar/
├── UTKToolBar.cs # ⭐ 메인 View (VisualElement)
├── UTKToolBarModel.cs # ⭐ 데이터 모델
├── UTKToolBarController.cs # ⭐ MonoBehaviour 컨트롤러
├── Data/
│ ├── IUTKToolBarItem.cs # 아이템 인터페이스
│ ├── UTKToolBarButtonData.cs # 버튼 데이터 베이스
│ ├── UTKToolBarStandardButtonData.cs # 일반 버튼 데이터
│ ├── UTKToolBarToggleButtonData.cs # 토글 버튼 데이터
│ ├── UTKToolBarRadioButtonData.cs # 라디오 버튼 데이터
│ ├── UTKToolBarRadioButtonGroup.cs # 라디오 그룹 관리
│ ├── UTKToolBarExpandableButtonData.cs # 확장 버튼 데이터
│ └── UTKToolBarSeparatorData.cs # 구분선 데이터
├── Items/
│ ├── UTKToolBarButtonBase.cs # ⭐ 버튼 VisualElement 베이스
│ ├── UTKToolBarStandardButton.cs # 일반 버튼 컴포넌트
│ ├── UTKToolBarToggleButton.cs # 토글 버튼 컴포넌트
│ ├── UTKToolBarRadioButton.cs # 라디오 버튼 컴포넌트
│ ├── UTKToolBarExpandableButton.cs # 확장 버튼 컴포넌트
│ └── UTKToolBarSeparator.cs # 구분선 컴포넌트
└── README.md
Assets/Resources/UIToolkit/ToolBar/
├── UTKToolBar.uxml # 메인 툴바 구조
├── UTKToolBarUss.uss # 메인 툴바 스타일
├── UTKToolBarButton.uxml # 버튼 아이템 템플릿
├── UTKToolBarButtonUss.uss # 버튼 아이템 스타일
├── UTKToolBarToggleButton.uxml # 토글 버튼 템플릿
├── UTKToolBarToggleButtonUss.uss # 토글 버튼 스타일
├── UTKToolBarExpandableButton.uxml # 확장 버튼 템플릿
├── UTKToolBarExpandableButtonUss.uss # 확장 버튼 스타일
├── UTKToolBarSeparator.uxml # 구분선 템플릿
├── UTKToolBarSeparatorUss.uss # 구분선 스타일
├── UTKToolBarSubMenu.uxml # 서브메뉴 컨테이너
└── UTKToolBarSubMenuUss.uss # 서브메뉴 스타일
🔧 구현 상세
1. 열거형 및 인터페이스
1.1 UTKToolBarOrientation
namespace UVC.UIToolkit.ToolBar
{
/// <summary>
/// 툴바 배치 방향을 정의합니다.
/// </summary>
public enum UTKToolBarOrientation
{
/// <summary>가로 배치 (좌→우)</summary>
Horizontal,
/// <summary>세로 배치 (위→아래)</summary>
Vertical
}
}
1.2 UTKToolBarActionType
namespace UVC.UIToolkit.ToolBar
{
/// <summary>
/// 툴바 버튼 액션 타입을 정의합니다.
/// </summary>
public enum UTKToolBarActionType
{
/// <summary>일반 클릭</summary>
Standard,
/// <summary>라디오 그룹 선택</summary>
Radio,
/// <summary>토글 상태 변경</summary>
Toggle,
/// <summary>확장 버튼 서브 선택</summary>
Expandable
}
}
1.3 IUTKToolBarItem
파일 경로: Assets/Scripts/UVC/UIToolkit/ToolBar/Data/IUTKToolBarItem.cs
#nullable enable
namespace UVC.UIToolkit.ToolBar
{
/// <summary>
/// 툴바 아이템의 기본 인터페이스입니다.
/// 모든 툴바 아이템(버튼, 구분선 등)은 이 인터페이스를 구현합니다.
/// </summary>
public interface IUTKToolBarItem
{
/// <summary>아이템 고유 식별자</summary>
string ItemId { get; }
}
}
2. 데이터 레이어
2.1 UTKToolBarButtonData (추상 베이스)
파일 경로: Assets/Scripts/UVC/UIToolkit/ToolBar/Data/UTKToolBarButtonData.cs
책임:
- 모든 툴바 버튼의 공통 데이터 (Text, Icon, Enabled, Tooltip, Command)
- 상태 변경 이벤트 (OnStateChanged, OnClicked)
#nullable enable
using System;
using UVC.UI.Commands;
namespace UVC.UIToolkit.ToolBar
{
/// <summary>
/// 모든 툴바 버튼의 공통 데이터를 정의하는 추상 클래스입니다.
/// </summary>
public abstract class UTKToolBarButtonData : IUTKToolBarItem, IDisposable
{
// --- 속성 ---
/// <summary>아이템 고유 식별자</summary>
public string ItemId { get; private set; }
/// <summary>버튼 텍스트 (다국어 키)</summary>
public string Text { get; set; }
/// <summary>아이콘 리소스 경로 또는 Material Icon 유니코드</summary>
public string? IconPath { get; set; }
/// <summary>Material Icon 사용 여부 (true: 폰트 아이콘, false: 이미지)</summary>
public bool UseMaterialIcon { get; set; }
/// <summary>활성화 상태</summary>
public bool IsEnabled { get; set; }
/// <summary>툴팁 텍스트 (다국어 키)</summary>
public string? Tooltip { get; set; }
/// <summary>실행할 명령</summary>
public ICommand? ClickCommand { get; set; }
// --- 이벤트 ---
/// <summary>Text, Icon, Enabled 등 시각적 상태 변경 시 발생</summary>
public event Action? OnStateChanged;
/// <summary>버튼 클릭 시 발생</summary>
public event Action? OnClicked;
// --- 메서드 ---
/// <summary>클릭 실행 (Command 실행 + 이벤트 발생)</summary>
public virtual void ExecuteClick(object? parameter = null);
/// <summary>상태 변경 알림</summary>
public void NotifyStateChanged();
/// <summary>모든 이벤트 핸들러 해제</summary>
public void ClearEventHandlers();
// --- IDisposable ---
private bool _disposed;
public void Dispose();
protected virtual void Dispose(bool disposing);
}
}
구현 지침:
ExecuteClick()에서IUndoableCommand지원 (Execute 후 Undo 스택 관리)Dispose()에서 이벤트 핸들러 해제 + Command가 IDisposable이면 정리
2.2 UTKToolBarStandardButtonData
파일 경로: Assets/Scripts/UVC/UIToolkit/ToolBar/Data/UTKToolBarStandardButtonData.cs
#nullable enable
namespace UVC.UIToolkit.ToolBar
{
/// <summary>
/// 단순 클릭 동작의 일반 버튼 데이터입니다.
/// </summary>
public class UTKToolBarStandardButtonData : UTKToolBarButtonData
{
// ToolbarStandardButton과 동일한 구조
// 추가 속성 없음 (베이스 클래스 기능만 사용)
}
}
2.3 UTKToolBarToggleButtonData
파일 경로: Assets/Scripts/UVC/UIToolkit/ToolBar/Data/UTKToolBarToggleButtonData.cs
#nullable enable
using System;
namespace UVC.UIToolkit.ToolBar
{
/// <summary>
/// On/Off 상태를 가지는 토글 버튼 데이터입니다.
/// </summary>
public class UTKToolBarToggleButtonData : UTKToolBarButtonData
{
/// <summary>현재 선택(On) 상태</summary>
public bool IsSelected { get; private set; }
/// <summary>Off 상태 아이콘 경로</summary>
public string? OffIconPath { get; set; }
/// <summary>토글 시 콜백</summary>
public Action<bool>? OnToggle { get; set; }
/// <summary>토글 상태 변경 이벤트</summary>
public event Action<bool>? OnToggleStateChanged;
/// <summary>
/// 선택 상태를 변경합니다.
/// </summary>
/// <param name="isSelected">새로운 선택 상태</param>
/// <param name="raiseEvent">이벤트 발생 여부</param>
public void SetSelected(bool isSelected, bool raiseEvent = true);
/// <summary>
/// 클릭 시 상태를 반전시키고 Command를 실행합니다.
/// </summary>
public override void ExecuteClick(object? parameter = null);
}
}
2.4 UTKToolBarRadioButtonData
파일 경로: Assets/Scripts/UVC/UIToolkit/ToolBar/Data/UTKToolBarRadioButtonData.cs
#nullable enable
namespace UVC.UIToolkit.ToolBar
{
/// <summary>
/// 그룹 내 상호 배타적 선택을 지원하는 라디오 버튼 데이터입니다.
/// </summary>
public class UTKToolBarRadioButtonData : UTKToolBarToggleButtonData
{
/// <summary>소속 라디오 그룹 이름</summary>
public string GroupName { get; private set; }
/// <summary>라디오 그룹 참조 (모델에서 설정)</summary>
internal UTKToolBarRadioButtonGroup? RadioGroup { get; set; }
/// <summary>
/// 클릭 시 그룹 내 다른 버튼은 해제하고 이 버튼만 선택합니다.
/// </summary>
public override void ExecuteClick(object? parameter = null);
}
}
2.5 UTKToolBarRadioButtonGroup
파일 경로: Assets/Scripts/UVC/UIToolkit/ToolBar/Data/UTKToolBarRadioButtonGroup.cs
#nullable enable
using System;
using System.Collections.Generic;
namespace UVC.UIToolkit.ToolBar
{
/// <summary>
/// 라디오 버튼 그룹을 관리합니다. 하나의 버튼만 선택 상태를 유지합니다.
/// </summary>
public class UTKToolBarRadioButtonGroup : IDisposable
{
/// <summary>그룹 이름</summary>
public string GroupName { get; private set; }
/// <summary>버튼 등록</summary>
public void RegisterButton(UTKToolBarRadioButtonData button);
/// <summary>특정 버튼 선택 (나머지 해제)</summary>
public void SetSelected(UTKToolBarRadioButtonData buttonToSelect, bool raiseEvent = true);
/// <summary>모든 선택 해제</summary>
public void ClearSelection(bool raiseEvent = true);
/// <summary>그룹 내 버튼 목록 반환</summary>
public IReadOnlyList<UTKToolBarRadioButtonData> GetButtons();
/// <summary>텍스트로 버튼 검색</summary>
public UTKToolBarRadioButtonData? FindButtonByText(string text);
/// <summary>초기 선택 상태 적용</summary>
public void InitializeSelection();
// --- IDisposable ---
public void Dispose();
}
}
2.6 UTKToolBarExpandableButtonData
파일 경로: Assets/Scripts/UVC/UIToolkit/ToolBar/Data/UTKToolBarExpandableButtonData.cs
#nullable enable
using System;
using System.Collections.Generic;
namespace UVC.UIToolkit.ToolBar
{
/// <summary>
/// 서브 버튼 목록을 가진 확장 가능한 버튼 데이터입니다.
/// </summary>
public class UTKToolBarExpandableButtonData : UTKToolBarButtonData
{
/// <summary>서브 버튼 목록</summary>
public List<UTKToolBarButtonData> SubButtons { get; private set; }
/// <summary>서브 버튼 선택 시 메인 아이콘 업데이트 여부</summary>
public bool UpdateIconOnSelection { get; set; }
/// <summary>현재 선택된 서브 버튼</summary>
public UTKToolBarButtonData? SelectedSubButton { get; private set; }
/// <summary>원본 텍스트 (서브 버튼 선택 시 변경 전 저장용)</summary>
public string OriginalText { get; private set; }
/// <summary>서브 버튼 선택 콜백</summary>
public Action<UTKToolBarButtonData>? OnSubButtonSelected { get; set; }
/// <summary>서브 버튼 선택 변경 이벤트 (mainText, selectedSubText)</summary>
public event Action<string, string>? OnSubButtonSelectionChanged;
/// <summary>서브 버튼을 선택합니다.</summary>
public void SelectSubButton(UTKToolBarButtonData selectedSubButton);
/// <summary>원본 텍스트를 설정합니다.</summary>
public void SetOriginalText(string text);
}
}
2.7 UTKToolBarSeparatorData
파일 경로: Assets/Scripts/UVC/UIToolkit/ToolBar/Data/UTKToolBarSeparatorData.cs
#nullable enable
namespace UVC.UIToolkit.ToolBar
{
/// <summary>
/// 툴바 내 시각적 구분선 데이터입니다.
/// </summary>
public class UTKToolBarSeparatorData : IUTKToolBarItem
{
/// <summary>아이템 고유 식별자</summary>
public string ItemId { get; private set; }
public UTKToolBarSeparatorData(string? itemId = null);
}
}
3. 모델 레이어
3.1 UTKToolBarModel ⭐
파일 경로: Assets/Scripts/UVC/UIToolkit/ToolBar/UTKToolBarModel.cs
책임:
- 툴바 아이템 컬렉션 관리
- 라디오 그룹 자동 관리
- 팩토리 메서드로 아이템 생성
#nullable enable
using System;
using System.Collections.Generic;
using UVC.UI.Commands;
namespace UVC.UIToolkit.ToolBar
{
/// <summary>
/// UTKToolBar의 데이터 모델입니다.
/// 아이템 컬렉션을 관리하고, 라디오 그룹 등록을 자동화합니다.
/// </summary>
public class UTKToolBarModel : IDisposable
{
/// <summary>전체 아이템 목록 (순서 유지)</summary>
public List<IUTKToolBarItem> Items { get; private set; }
// --- 팩토리 메서드 (ToolbarModel과 동일 API) ---
/// <summary>일반 버튼 추가</summary>
/// <param name="text">버튼 텍스트 (다국어 키)</param>
/// <param name="iconPath">아이콘 경로 (Material Icon 또는 Resources 경로)</param>
/// <param name="command">실행할 명령</param>
/// <param name="tooltip">툴팁 (다국어 키)</param>
/// <param name="useMaterialIcon">Material Icon 사용 여부</param>
/// <returns>생성된 버튼 데이터</returns>
public UTKToolBarStandardButtonData AddStandardButton(
string text,
string? iconPath = null,
ICommand? command = null,
string? tooltip = null,
bool useMaterialIcon = true);
/// <summary>토글 버튼 추가</summary>
/// <param name="text">버튼 텍스트</param>
/// <param name="initialState">초기 On/Off 상태</param>
/// <param name="onIconPath">On 상태 아이콘</param>
/// <param name="offIconPath">Off 상태 아이콘</param>
/// <param name="onToggle">토글 콜백</param>
/// <param name="command">실행할 명령</param>
/// <param name="tooltip">툴팁</param>
/// <param name="useMaterialIcon">Material Icon 사용 여부</param>
/// <returns>생성된 토글 버튼 데이터</returns>
public UTKToolBarToggleButtonData AddToggleButton(
string text,
bool initialState = false,
string? onIconPath = null,
string? offIconPath = null,
Action<bool>? onToggle = null,
ICommand? command = null,
string? tooltip = null,
bool useMaterialIcon = true);
/// <summary>라디오 버튼 추가</summary>
/// <param name="groupName">라디오 그룹 이름</param>
/// <param name="text">버튼 텍스트</param>
/// <param name="initialState">초기 선택 상태</param>
/// <param name="onIconPath">선택 상태 아이콘</param>
/// <param name="offIconPath">비선택 상태 아이콘</param>
/// <param name="onToggle">토글 콜백</param>
/// <param name="command">실행할 명령</param>
/// <param name="tooltip">툴팁</param>
/// <param name="useMaterialIcon">Material Icon 사용 여부</param>
/// <returns>생성된 라디오 버튼 데이터</returns>
public UTKToolBarRadioButtonData AddRadioButton(
string groupName,
string text,
bool initialState = false,
string? onIconPath = null,
string? offIconPath = null,
Action<bool>? onToggle = null,
ICommand? command = null,
string? tooltip = null,
bool useMaterialIcon = true);
/// <summary>확장 버튼 추가</summary>
/// <param name="text">버튼 텍스트</param>
/// <param name="iconPath">아이콘 경로</param>
/// <param name="command">실행할 명령</param>
/// <param name="tooltip">툴팁</param>
/// <param name="updateIconOnSelection">서브 선택 시 아이콘 업데이트 여부</param>
/// <param name="useMaterialIcon">Material Icon 사용 여부</param>
/// <returns>생성된 확장 버튼 데이터</returns>
public UTKToolBarExpandableButtonData AddExpandableButton(
string text,
string? iconPath = null,
ICommand? command = null,
string? tooltip = null,
bool updateIconOnSelection = false,
bool useMaterialIcon = true);
/// <summary>구분선 추가</summary>
public UTKToolBarSeparatorData AddSeparator();
// --- 상태 관리 메서드 ---
/// <summary>라디오 그룹의 특정 버튼 선택</summary>
public void SetRadioButtonSelection(string groupName, string buttonText, bool raiseEvent = true);
/// <summary>라디오 그룹의 특정 버튼 선택 (UTKToolBarRadioButtonData로)</summary>
public void SetRadioButtonSelection(string groupName, UTKToolBarRadioButtonData button, bool raiseEvent = true);
/// <summary>라디오 그룹 선택 해제</summary>
public void ClearRadioButtonSelection(string groupName, bool raiseEvent = true);
/// <summary>토글 버튼 상태 변경</summary>
public void SetToggleButtonState(string buttonText, bool isSelected, bool raiseEvent = true);
/// <summary>토글 버튼 상태 조회</summary>
public bool GetToggleButtonState(string buttonText);
/// <summary>라디오 그룹 가져오기</summary>
public UTKToolBarRadioButtonGroup? GetRadioButtonGroup(string groupName);
// --- IDisposable ---
private bool _disposed;
public void Dispose();
protected virtual void Dispose(bool disposing);
}
}
구현 지침:
_radioGroups를Dictionary<string, UTKToolBarRadioButtonGroup>으로 관리AddRadioButton()호출 시 자동으로 그룹 생성/등록Dispose()에서 모든 아이템과 라디오 그룹 정리
4. View 레이어
4.1 UTKToolBarButtonBase (추상 VisualElement 베이스) ⭐
파일 경로: Assets/Scripts/UVC/UIToolkit/ToolBar/Items/UTKToolBarButtonBase.cs
책임:
- 모든 툴바 버튼 UI의 공통 기반
- 아이콘(Material Icon / Image) + 텍스트 표시
- 테마 연동
- 데이터 바인딩 및 상태 동기화
#nullable enable
using System;
using UnityEngine.UIElements;
namespace UVC.UIToolkit.ToolBar
{
/// <summary>
/// 툴바 버튼 VisualElement의 추상 베이스 클래스입니다.
/// 아이콘, 텍스트, 활성화 상태의 공통 UI를 제공합니다.
/// </summary>
public abstract partial class UTKToolBarButtonBase : VisualElement, IDisposable
{
// --- 캐시된 UI 요소 ---
protected VisualElement? _iconElement;
protected Label? _textLabel;
protected VisualElement? _rootButton;
// --- 데이터 참조 ---
protected UTKToolBarButtonData? _data;
// --- 이벤트 ---
/// <summary>버튼 클릭 이벤트</summary>
public event Action<UTKToolBarButtonData>? OnButtonClicked;
// --- 공통 메서드 ---
/// <summary>데이터를 바인딩합니다.</summary>
public virtual void BindData(UTKToolBarButtonData data);
/// <summary>데이터 바인딩을 해제합니다.</summary>
public virtual void UnbindData();
/// <summary>아이콘을 업데이트합니다.</summary>
protected void UpdateIcon(string? iconPath, bool useMaterialIcon);
/// <summary>텍스트를 업데이트합니다.</summary>
protected void UpdateText(string text);
/// <summary>활성화 상태를 업데이트합니다.</summary>
protected void UpdateEnabled(bool isEnabled);
/// <summary>모델 상태 변경 핸들러</summary>
protected virtual void OnDataStateChanged();
// --- 테마 ---
protected void SubscribeToThemeChanges();
private void OnAttachToPanelForTheme(AttachToPanelEvent evt);
private void OnDetachFromPanelForTheme(DetachFromPanelEvent evt);
private void OnThemeChanged(UTKTheme theme);
// --- IDisposable ---
protected bool _disposed;
public virtual void Dispose();
}
}
구현 지침:
- 아이콘 우선순위:
UseMaterialIcon == true이면UTKMaterialIcons폰트 사용, 아니면Resources.Load<Sprite>()사용 - 테마:
UTKThemeManager패턴 (AttachToPanelEvent/DetachFromPanelEvent) - 데이터 바인딩:
BindData()에서OnStateChanged구독,UnbindData()에서 해제
4.2 UTKToolBarStandardButton
파일 경로: Assets/Scripts/UVC/UIToolkit/ToolBar/Items/UTKToolBarStandardButton.cs
#nullable enable
using UnityEngine.UIElements;
namespace UVC.UIToolkit.ToolBar
{
/// <summary>
/// 단순 클릭 동작의 툴바 버튼 컴포넌트입니다.
/// </summary>
[UxmlElement]
public partial class UTKToolBarStandardButton : UTKToolBarButtonBase
{
private const string UXML_PATH = "UIToolkit/ToolBar/UTKToolBarButton";
private const string USS_PATH = "UIToolkit/ToolBar/UTKToolBarButtonUss";
public UTKToolBarStandardButton();
/// <summary>클릭 이벤트 처리</summary>
private void OnClick(ClickEvent evt);
}
}
4.3 UTKToolBarToggleButton
파일 경로: Assets/Scripts/UVC/UIToolkit/ToolBar/Items/UTKToolBarToggleButton.cs
#nullable enable
using UnityEngine.UIElements;
namespace UVC.UIToolkit.ToolBar
{
/// <summary>
/// On/Off 상태 전환이 가능한 토글 버튼 컴포넌트입니다.
/// 선택 상태에 따라 아이콘과 스타일이 변경됩니다.
/// </summary>
[UxmlElement]
public partial class UTKToolBarToggleButton : UTKToolBarButtonBase
{
private const string UXML_PATH = "UIToolkit/ToolBar/UTKToolBarToggleButton";
private const string USS_PATH = "UIToolkit/ToolBar/UTKToolBarToggleButtonUss";
public UTKToolBarToggleButton();
/// <summary>데이터 바인딩 (Toggle 전용 이벤트 추가 구독)</summary>
public override void BindData(UTKToolBarButtonData data);
/// <summary>토글 상태에 따른 시각적 업데이트</summary>
private void UpdateToggleVisuals(bool isSelected);
/// <summary>토글 상태 변경 핸들러</summary>
private void OnToggleStateChanged(bool isSelected);
/// <summary>클릭 이벤트 처리</summary>
private void OnClick(ClickEvent evt);
}
}
4.4 UTKToolBarRadioButton
파일 경로: Assets/Scripts/UVC/UIToolkit/ToolBar/Items/UTKToolBarRadioButton.cs
#nullable enable
using UnityEngine.UIElements;
namespace UVC.UIToolkit.ToolBar
{
/// <summary>
/// 라디오 그룹 내에서 상호 배타적으로 선택되는 버튼 컴포넌트입니다.
/// UTKToolBarToggleButton을 상속하여 토글 시각 효과를 재사용합니다.
/// </summary>
[UxmlElement]
public partial class UTKToolBarRadioButton : UTKToolBarToggleButton
{
public UTKToolBarRadioButton();
/// <summary>클릭 시 라디오 그룹 선택 처리</summary>
private void OnClick(ClickEvent evt);
}
}
4.5 UTKToolBarExpandableButton
파일 경로: Assets/Scripts/UVC/UIToolkit/ToolBar/Items/UTKToolBarExpandableButton.cs
#nullable enable
using System.Collections.Generic;
using UnityEngine.UIElements;
namespace UVC.UIToolkit.ToolBar
{
/// <summary>
/// 클릭 시 서브 메뉴를 펼치는 확장 버튼 컴포넌트입니다.
/// 서브 버튼 목록을 지연 생성(Lazy Loading)합니다.
/// </summary>
[UxmlElement]
public partial class UTKToolBarExpandableButton : UTKToolBarButtonBase
{
private const string UXML_PATH = "UIToolkit/ToolBar/UTKToolBarExpandableButton";
private const string USS_PATH = "UIToolkit/ToolBar/UTKToolBarExpandableButtonUss";
private const string SUBMENU_UXML = "UIToolkit/ToolBar/UTKToolBarSubMenu";
private const string SUBMENU_USS = "UIToolkit/ToolBar/UTKToolBarSubMenuUss";
/// <summary>서브 메뉴 열림 상태</summary>
public bool IsSubMenuOpen { get; private set; }
/// <summary>서브 메뉴 열림/닫힘 이벤트</summary>
public event Action<bool>? OnSubMenuToggled;
public UTKToolBarExpandableButton();
/// <summary>서브 메뉴 토글</summary>
public void ToggleSubMenu();
/// <summary>서브 메뉴 닫기</summary>
public void CloseSubMenu();
/// <summary>서브 메뉴 생성 (지연 로드)</summary>
private void CreateSubMenu();
/// <summary>서브 메뉴 위치 계산 (가로/세로 배치 대응)</summary>
private void PositionSubMenu();
/// <summary>확장 화살표 아이콘 (▼ 또는 ▶)</summary>
private VisualElement? _arrowIcon;
/// <summary>서브 메뉴 컨테이너</summary>
private VisualElement? _subMenuContainer;
/// <summary>서브 메뉴 아이템 요소 목록</summary>
private List<UTKToolBarButtonBase>? _subMenuItems;
}
}
서브 메뉴 위치 로직 (가로/세로 대응):
가로(Horizontal) 배치 시:
┌──────────────────────────┐
│ [Btn1] [Btn2] [▼Exp] ... │ ← 툴바
└──────────────────────────┘
┌──────────┐
│ SubBtn1 │ ← 서브 메뉴 (아래로 펼침)
│ SubBtn2 │
│ SubBtn3 │
└──────────┘
세로(Vertical) 배치 시:
┌──────┐
│ Btn1 │
│ Btn2 │
│▶Exp │──┐
│ Btn3 │ │ ┌──────────┐
└──────┘ └→│ SubBtn1 │ ← 서브 메뉴 (오른쪽으로 펼침)
│ SubBtn2 │
│ SubBtn3 │
└──────────┘
4.6 UTKToolBarSeparator
파일 경로: Assets/Scripts/UVC/UIToolkit/ToolBar/Items/UTKToolBarSeparator.cs
#nullable enable
using UnityEngine.UIElements;
namespace UVC.UIToolkit.ToolBar
{
/// <summary>
/// 툴바 내 시각적 구분선 컴포넌트입니다.
/// 가로 배치 시 세로선, 세로 배치 시 가로선으로 표시됩니다.
/// </summary>
[UxmlElement]
public partial class UTKToolBarSeparator : VisualElement, IDisposable
{
private const string UXML_PATH = "UIToolkit/ToolBar/UTKToolBarSeparator";
private const string USS_PATH = "UIToolkit/ToolBar/UTKToolBarSeparatorUss";
public UTKToolBarSeparator();
/// <summary>배치 방향에 따라 구분선 방향을 업데이트합니다.</summary>
public void UpdateOrientation(UTKToolBarOrientation orientation);
public void Dispose();
}
}
4.7 UTKToolBar (메인 View) ⭐⭐
파일 경로: Assets/Scripts/UVC/UIToolkit/ToolBar/UTKToolBar.cs
책임:
- 전체 툴바 UI 생성/관리
- 가로/세로 배치 전환
- 아이템 렌더링 및 이벤트 중재
- 서브 메뉴 외부 클릭 감지
#nullable enable
using System;
using System.Collections.Generic;
using UnityEngine.UIElements;
namespace UVC.UIToolkit.ToolBar
{
/// <summary>
/// UIToolkit 기반 툴바 메인 컴포넌트입니다.
/// 가로/세로 배치를 지원하며, 4가지 버튼 타입과 구분선을 렌더링합니다.
/// </summary>
[UxmlElement]
public partial class UTKToolBar : VisualElement, IDisposable
{
#region Constants
private const string UXML_PATH = "UIToolkit/ToolBar/UTKToolBar";
private const string USS_PATH = "UIToolkit/ToolBar/UTKToolBarUss";
#endregion
#region UXML Attributes
/// <summary>툴바 배치 방향</summary>
[UxmlAttribute("orientation")]
public UTKToolBarOrientation Orientation
{
get => _orientation;
set
{
_orientation = value;
ApplyOrientation();
}
}
/// <summary>아이템 간 간격 (px)</summary>
[UxmlAttribute("item-spacing")]
public float ItemSpacing { get; set; } = 2f;
/// <summary>툴바 크기 (가로 시 높이, 세로 시 너비)</summary>
[UxmlAttribute("toolbar-size")]
public float ToolBarSize { get; set; } = 40f;
#endregion
#region Fields
private UTKToolBarOrientation _orientation = UTKToolBarOrientation.Horizontal;
private VisualElement? _itemContainer;
private List<VisualElement> _itemElements;
private Dictionary<string, UTKToolBarButtonBase> _buttonMap;
private bool _disposed;
#endregion
#region Events
/// <summary>버튼 액션 이벤트</summary>
public event Action<UTKToolBarActionEventArgs>? OnAction;
#endregion
#region Constructor
/// <summary>
/// 기본 생성자. UXML에서 인스턴스화 시 사용됩니다.
/// </summary>
public UTKToolBar();
#endregion
#region Public Methods
/// <summary>모델 데이터로 툴바를 생성합니다.</summary>
public void BuildToolBar(UTKToolBarModel model);
/// <summary>툴바의 모든 아이템을 제거합니다.</summary>
public void ClearToolBar();
/// <summary>특정 버튼의 활성화 상태를 변경합니다.</summary>
public void SetButtonEnabled(string itemId, bool isEnabled);
/// <summary>배치 방향을 변경합니다.</summary>
public void SetOrientation(UTKToolBarOrientation orientation);
/// <summary>특정 버튼 요소를 가져옵니다.</summary>
public bool TryGetButtonElement(string itemId, out UTKToolBarButtonBase? button);
#endregion
#region Private Methods
/// <summary>UXML/USS 로드 및 UI 구성</summary>
private void CreateUI();
/// <summary>배치 방향을 적용합니다.</summary>
private void ApplyOrientation();
/// <summary>모델 아이템으로 개별 버튼 생성</summary>
private VisualElement CreateItemElement(IUTKToolBarItem item);
/// <summary>외부 클릭 감지 (서브 메뉴 닫기용)</summary>
private void OnPanelPointerDown(PointerDownEvent evt);
/// <summary>모든 열린 서브 메뉴 닫기</summary>
private void CloseAllSubMenus();
#endregion
#region Theme
private void SubscribeToThemeChanges();
private void OnAttachToPanelForTheme(AttachToPanelEvent evt);
private void OnDetachFromPanelForTheme(DetachFromPanelEvent evt);
private void OnThemeChanged(UTKTheme theme);
#endregion
#region IDisposable
public void Dispose();
#endregion
}
}
4.8 UTKToolBarActionEventArgs
#nullable enable
namespace UVC.UIToolkit.ToolBar
{
/// <summary>
/// 툴바 버튼 액션 이벤트 인자입니다.
/// </summary>
public class UTKToolBarActionEventArgs
{
/// <summary>버튼 텍스트 (또는 그룹 이름)</summary>
public string Text { get; set; } = "";
/// <summary>컨텍스트별 값 (Toggle: bool, Radio: 선택 텍스트, Expandable: 서브 텍스트)</summary>
public object? Value { get; set; }
/// <summary>액션 타입</summary>
public UTKToolBarActionType ActionType { get; set; }
}
}
5. Controller 레이어
5.1 UTKToolBarController ⭐
파일 경로: Assets/Scripts/UVC/UIToolkit/ToolBar/UTKToolBarController.cs
책임:
- MonoBehaviour로 UIDocument와 연동
- UTKToolBarModel과 UTKToolBar(View) 중재
Toolbar/Toolbox의 public API 호환
#nullable enable
using System;
using UnityEngine;
using UnityEngine.UIElements;
namespace UVC.UIToolkit.ToolBar
{
/// <summary>
/// UTKToolBar의 MonoBehaviour 컨트롤러입니다.
/// 기존 Toolbar/Toolbox의 public API와 호환됩니다.
/// </summary>
public class UTKToolBarController : MonoBehaviour
{
// --- 인스펙터 ---
[SerializeField] private UIDocument? _uiDocument;
[SerializeField]
[Tooltip("툴바 배치 방향")]
private UTKToolBarOrientation _orientation = UTKToolBarOrientation.Horizontal;
// --- 필드 ---
private UTKToolBarModel? _model;
private UTKToolBar? _toolBarView;
// --- 이벤트 (Toolbar/Toolbox와 동일) ---
/// <summary>버튼 액션 이벤트</summary>
public event Action<UTKToolBarActionEventArgs>? OnAction;
// --- Public 메서드 (Toolbar/Toolbox 호환 API) ---
/// <summary>데이터 모델 설정</summary>
public void SetData(UTKToolBarModel model);
/// <summary>초기화 (툴바 렌더링)</summary>
public virtual void Initialize();
/// <summary>라디오 버튼 선택 (텍스트로)</summary>
public void SetRadioButtonSelection(string groupName, string buttonText, bool raiseEvent = true);
/// <summary>라디오 버튼 선택 (데이터 참조로)</summary>
public void SetRadioButtonSelection(string groupName, UTKToolBarRadioButtonData button, bool raiseEvent = true);
/// <summary>라디오 버튼 선택 해제</summary>
public void ClearRadioButtonSelection(string groupName, bool raiseEvent = true);
/// <summary>토글 버튼 상태 설정</summary>
public void SetToggleButtonState(string buttonText, bool isSelected, bool raiseEvent = true);
/// <summary>토글 버튼 상태 조회</summary>
public bool GetToggleButtonState(string buttonText);
/// <summary>배치 방향 변경</summary>
public void SetOrientation(UTKToolBarOrientation orientation);
// --- Lifecycle ---
protected virtual void Awake();
protected virtual void OnDestroy();
}
}
6. UXML/USS 리소스
6.1 UTKToolBar.uxml
경로: Assets/Resources/UIToolkit/ToolBar/UTKToolBar.uxml
<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit.ToolBar">
<ui:VisualElement name="toolbar-root" class="utk-toolbar">
<ui:VisualElement name="toolbar-container" class="utk-toolbar__container" />
</ui:VisualElement>
</ui:UXML>
6.2 UTKToolBarUss.uss
경로: Assets/Resources/UIToolkit/ToolBar/UTKToolBarUss.uss
/* === 공통 === */
.utk-toolbar {
overflow: visible;
}
.utk-toolbar__container {
overflow: visible;
}
/* === 가로 배치 === */
.utk-toolbar--horizontal {
flex-direction: row;
align-items: center;
height: 40px;
width: auto;
background-color: var(--color-bg-secondary);
border-bottom-width: var(--border-width);
border-bottom-color: var(--color-border);
padding: 0 var(--space-s);
}
.utk-toolbar--horizontal > .utk-toolbar__container {
flex-direction: row;
align-items: center;
flex-grow: 1;
}
/* === 세로 배치 === */
.utk-toolbar--vertical {
flex-direction: column;
align-items: center;
width: 40px;
height: auto;
background-color: var(--color-bg-secondary);
border-right-width: var(--border-width);
border-right-color: var(--color-border);
padding: var(--space-s) 0;
}
.utk-toolbar--vertical > .utk-toolbar__container {
flex-direction: column;
align-items: center;
flex-grow: 1;
}
6.3 UTKToolBarButton.uxml
경로: Assets/Resources/UIToolkit/ToolBar/UTKToolBarButton.uxml
<ui:UXML xmlns:ui="UnityEngine.UIElements">
<ui:VisualElement name="button-root" class="utk-toolbar-btn">
<ui:VisualElement name="icon" class="utk-toolbar-btn__icon" />
<ui:Label name="label" class="utk-toolbar-btn__label" />
</ui:VisualElement>
</ui:UXML>
6.4 UTKToolBarButtonUss.uss
경로: Assets/Resources/UIToolkit/ToolBar/UTKToolBarButtonUss.uss
.utk-toolbar-btn {
flex-direction: column;
align-items: center;
justify-content: center;
min-width: 32px;
min-height: 32px;
padding: var(--space-xs);
margin: 0 1px;
border-radius: var(--radius-s);
border-width: 0;
background-color: transparent;
cursor: link;
transition: background-color var(--anim-fast);
}
.utk-toolbar-btn:hover {
background-color: var(--color-bg-hover);
}
.utk-toolbar-btn:active {
background-color: var(--color-bg-active);
}
.utk-toolbar-btn--disabled {
opacity: 0.4;
cursor: default;
}
.utk-toolbar-btn__icon {
width: var(--size-icon-btn);
height: var(--size-icon-btn);
-unity-background-scale-mode: scale-to-fit;
}
.utk-toolbar-btn__icon--material {
font-size: 18px;
-unity-text-align: middle-center;
color: var(--color-text-primary);
}
.utk-toolbar-btn__label {
font-size: var(--font-size-label4);
color: var(--color-text-secondary);
-unity-text-align: upper-center;
margin-top: 1px;
display: none; /* 기본: 아이콘만 표시. 텍스트 필요 시 display: flex; */
}
6.5 UTKToolBarToggleButton.uxml
경로: Assets/Resources/UIToolkit/ToolBar/UTKToolBarToggleButton.uxml
<ui:UXML xmlns:ui="UnityEngine.UIElements">
<ui:VisualElement name="button-root" class="utk-toolbar-btn utk-toolbar-toggle">
<ui:VisualElement name="icon" class="utk-toolbar-btn__icon" />
<ui:Label name="label" class="utk-toolbar-btn__label" />
</ui:VisualElement>
</ui:UXML>
6.6 UTKToolBarToggleButtonUss.uss
경로: Assets/Resources/UIToolkit/ToolBar/UTKToolBarToggleButtonUss.uss
.utk-toolbar-toggle--selected {
background-color: var(--color-primary);
border-radius: var(--radius-s);
}
.utk-toolbar-toggle--selected:hover {
background-color: var(--color-primary-hover);
}
.utk-toolbar-toggle--selected .utk-toolbar-btn__icon {
color: var(--color-text-on-primary);
}
.utk-toolbar-toggle--selected .utk-toolbar-btn__icon--material {
color: var(--color-text-on-primary);
}
6.7 UTKToolBarExpandableButton.uxml
경로: Assets/Resources/UIToolkit/ToolBar/UTKToolBarExpandableButton.uxml
<ui:UXML xmlns:ui="UnityEngine.UIElements">
<ui:VisualElement name="button-root" class="utk-toolbar-btn utk-toolbar-expandable">
<ui:VisualElement name="icon" class="utk-toolbar-btn__icon" />
<ui:Label name="label" class="utk-toolbar-btn__label" />
<ui:VisualElement name="arrow" class="utk-toolbar-expandable__arrow" />
</ui:VisualElement>
</ui:UXML>
6.8 UTKToolBarExpandableButtonUss.uss
경로: Assets/Resources/UIToolkit/ToolBar/UTKToolBarExpandableButtonUss.uss
.utk-toolbar-expandable {
position: relative;
}
.utk-toolbar-expandable__arrow {
width: 6px;
height: 6px;
position: absolute;
bottom: 2px;
right: 2px;
}
/* 가로 배치: 아래 화살표 ▼ */
.utk-toolbar--horizontal .utk-toolbar-expandable__arrow {
border-left-width: 3px;
border-right-width: 3px;
border-top-width: 4px;
border-left-color: transparent;
border-right-color: transparent;
border-top-color: var(--color-text-secondary);
border-bottom-width: 0;
}
/* 세로 배치: 오른쪽 화살표 ▶ */
.utk-toolbar--vertical .utk-toolbar-expandable__arrow {
border-top-width: 3px;
border-bottom-width: 3px;
border-left-width: 4px;
border-top-color: transparent;
border-bottom-color: transparent;
border-left-color: var(--color-text-secondary);
border-right-width: 0;
}
6.9 UTKToolBarSeparator.uxml
경로: Assets/Resources/UIToolkit/ToolBar/UTKToolBarSeparator.uxml
<ui:UXML xmlns:ui="UnityEngine.UIElements">
<ui:VisualElement name="separator-root" class="utk-toolbar-separator" />
</ui:UXML>
6.10 UTKToolBarSeparatorUss.uss
경로: Assets/Resources/UIToolkit/ToolBar/UTKToolBarSeparatorUss.uss
/* 가로 배치: 세로 구분선 */
.utk-toolbar--horizontal .utk-toolbar-separator {
width: var(--border-width);
height: 20px;
margin: 0 var(--space-s);
background-color: var(--color-border);
}
/* 세로 배치: 가로 구분선 */
.utk-toolbar--vertical .utk-toolbar-separator {
height: var(--border-width);
width: 20px;
margin: var(--space-s) 0;
background-color: var(--color-border);
}
6.11 UTKToolBarSubMenu.uxml
경로: Assets/Resources/UIToolkit/ToolBar/UTKToolBarSubMenu.uxml
<ui:UXML xmlns:ui="UnityEngine.UIElements">
<ui:VisualElement name="submenu-root" class="utk-toolbar-submenu">
<ui:VisualElement name="submenu-container" class="utk-toolbar-submenu__container" />
</ui:VisualElement>
</ui:UXML>
6.12 UTKToolBarSubMenuUss.uss
경로: Assets/Resources/UIToolkit/ToolBar/UTKToolBarSubMenuUss.uss
.utk-toolbar-submenu {
position: absolute;
background-color: var(--color-bg-primary);
border-width: var(--border-width);
border-color: var(--color-border);
border-radius: var(--radius-m);
padding: var(--space-xs);
min-width: 120px;
overflow: visible;
}
.utk-toolbar-submenu__container {
flex-direction: column;
}
/* 서브 메뉴 내 버튼은 가로로 펼침 */
.utk-toolbar-submenu .utk-toolbar-btn {
flex-direction: row;
min-width: 100px;
min-height: 28px;
justify-content: flex-start;
padding: var(--space-xs) var(--space-m);
margin: 1px 0;
}
.utk-toolbar-submenu .utk-toolbar-btn__icon {
margin-right: var(--space-s);
}
.utk-toolbar-submenu .utk-toolbar-btn__label {
display: flex;
font-size: var(--font-size-body2);
color: var(--color-text-primary);
-unity-text-align: middle-left;
}
🔑 핵심 기능: 가로/세로 배치
배치 전환 메커니즘
/// <summary>배치 방향을 적용합니다.</summary>
private void ApplyOrientation()
{
// 1. 기존 클래스 제거
RemoveFromClassList("utk-toolbar--horizontal");
RemoveFromClassList("utk-toolbar--vertical");
// 2. 새 클래스 추가
if (_orientation == UTKToolBarOrientation.Horizontal)
{
AddToClassList("utk-toolbar--horizontal");
}
else
{
AddToClassList("utk-toolbar--vertical");
}
// 3. 구분선 방향 업데이트
foreach (var element in _itemElements)
{
if (element is UTKToolBarSeparator separator)
{
separator.UpdateOrientation(_orientation);
}
}
// 4. 확장 버튼 서브 메뉴 닫기 (위치 재계산 필요)
CloseAllSubMenus();
}
USS 활용 (가로/세로 자동 전환)
구분선, 확장 버튼 화살표 등은 부모 클래스(.utk-toolbar--horizontal/.utk-toolbar--vertical)에 의한 자손 선택자로 자동 전환됩니다:
/* 부모가 가로면 → 세로 구분선 */
.utk-toolbar--horizontal .utk-toolbar-separator { width: 1px; height: 20px; }
/* 부모가 세로면 → 가로 구분선 */
.utk-toolbar--vertical .utk-toolbar-separator { height: 1px; width: 20px; }
레이아웃 예시
가로 배치 (Horizontal):
┌─────────────────────────────────────────────────┐
│ [🏠] [💾] [📋] │ [🔲Toggle] │ ◉R1 ◉R2 ◉R3 │ [▼Exp] │
└─────────────────────────────────────────────────┘
세로 배치 (Vertical):
┌──────┐
│ 🏠 │
│ 💾 │
│ 📋 │
│──────│ ← 구분선 (가로)
│🔲Tog │
│──────│
│ ◉ R1 │
│ ◉ R2 │
│ ◉ R3 │
│──────│
│ ▶Exp │
└──────┘
🔄 마이그레이션 순서
Phase 1: 기본 구조 구축
- 폴더 생성:
Assets/Scripts/UVC/UIToolkit/ToolBar/,Data/,Items/ - 폴더 생성:
Assets/Resources/UIToolkit/ToolBar/ - 열거형 및 인터페이스 구현:
UTKToolBarOrientationUTKToolBarActionTypeUTKToolBarActionEventArgsIUTKToolBarItem
- UXML/USS 파일 생성 (12개)
Phase 2: 데이터 레이어 구현
UTKToolBarButtonData.cs(추상 베이스) 구현- IDisposable, 이벤트, Command 실행
UTKToolBarStandardButtonData.cs구현UTKToolBarToggleButtonData.cs구현- IsSelected, SetSelected(), OnToggleStateChanged
UTKToolBarRadioButtonData.cs구현- GroupName, RadioGroup 연동
UTKToolBarRadioButtonGroup.cs구현- 상호 배타 선택 로직
UTKToolBarExpandableButtonData.cs구현- SubButtons, SelectSubButton()
UTKToolBarSeparatorData.cs구현UTKToolBarModel.cs구현 ⭐- 팩토리 메서드, 라디오 그룹 자동 등록, IDisposable
Phase 3: View 아이템 구현
UTKToolBarButtonBase.cs(추상 VisualElement) 구현- 아이콘(Material/Image), 텍스트, 테마, 데이터 바인딩
UTKToolBarStandardButton.cs구현UTKToolBarToggleButton.cs구현- 토글 시각 효과 (선택/비선택 스타일)
UTKToolBarRadioButton.cs구현UTKToolBarExpandableButton.cs구현- 서브 메뉴 지연 생성, 위치 계산, 외부 클릭 닫기
UTKToolBarSeparator.cs구현- 가로/세로 방향 전환
Phase 4: 메인 View + Controller
UTKToolBar.cs구현 ⭐⭐- Orientation 전환, BuildToolBar(), CloseAllSubMenus()
- 외부 클릭 감지 (panel.visualTree PointerDownEvent)
UTKToolBarController.cs구현- Toolbar/Toolbox 호환 API
Phase 5: 테마 및 스타일
- UTKThemeManager 연동
- StyleGuide 스타일 적용 (Buttons, Sidebar 참조)
- 라이트/다크 테마 지원
Phase 6: 테스트, 최적화, 문서화 ⭐
- 샘플 씬 업데이트 (
UTKToolBarSample.cs)- 가로/세로 전환 버튼 포함
- 4가지 버튼 타입 데모
- 메모리 누수 점검 (필수):
- Unity Profiler로 메모리 확인
- 씬 전환 시 메모리 증가 없는지 확인
- IDisposable 정상 호출 확인
- 이벤트 구독 해제 확인
- 성능 최적화 (필수):
- Unity Profiler로 CPU/GC 확인
- 버튼 50개 생성 테스트
- 쿼리 캐싱 적용 확인
- 완전한 문서화 (필수):
- 모든 public/protected 멤버 XML 주석
- README.md 작성
⚠️ 주의사항
필수 준수 사항 (위반 시 재작업)
#nullable enable파일 선두에 필수- Unity 6 방식:
[UxmlElement],partial class,[UxmlAttribute]사용 - 이벤트 등록:
RegisterCallback<ClickEvent>()사용 (RegisterValueChangedCallback금지) - UXML/USS 네이밍:
- UXML:
UTKToolBar.uxml - USS:
UTKToolBarUss.uss(접미사Uss필수)
- UXML:
- 케밥 케이스:
[UxmlAttribute("orientation")]소문자 + 하이픈 - IDisposable 구현 ⭐:
- 모든 데이터/View 클래스에 필수
_disposed플래그로 중복 호출 방지RegisterCallback↔UnregisterCallback대칭DetachFromPanelEvent에서 정리- 하위 객체도 재귀적으로 Dispose
- 아이콘 우선순위: Material Icons → Image Icons 순서
메모리 관리 체크리스트 ⭐
- 모든
RegisterCallback()대칭적으로UnregisterCallback() UTKThemeManager.OnThemeChanged구독 해제Dictionary<>전체 Clear() 호출- 확장 버튼 서브 메뉴 재귀적 Dispose
- 데이터 바인딩 해제 (
UnbindData()) - Unity Profiler로 메모리 누수 확인
성능 최적화 체크리스트 ⭐
Q<T>()결과 필드 캐싱 (생성 시 1회만)- 서브 메뉴 지연 생성 (Lazy Loading)
- UXML/USS 리소스 캐싱 (반복 로드 방지)
- LINQ 사용 금지 (foreach 사용)
- 불필요한
MarkDirtyRepaint()제거 - DisplayStyle 토글 (Add/Remove 대신)
- Unity Profiler로 CPU/GC 확인
문서화 체크리스트 ⭐
- 모든 public 멤버:
<summary>태그 - 모든 protected 멤버:
<summary>태그 - 모든 매개변수:
<param>태그 - 모든 반환값:
<returns>태그 - 예외 발생 가능:
<exception>태그 - 복잡한 로직:
<remarks>태그 또는 인라인 주석 - 사용 예제:
<example>태그 (Controller) - README.md 작성
코드 스타일
- ✅ 한국어 주석 (XML 문서 포함)
- ✅ BEM 네이밍 (CSS 클래스)
- ✅ 파일 경로 참조:
file_path:line_number형식 - ✅
namespace UVC.UIToolkit.ToolBar
📝 테스트 체크리스트
기능 테스트
- 일반 버튼 클릭 시 Command 실행
- 토글 버튼 On/Off 전환
- 라디오 버튼 그룹 내 상호 배타적 선택
- 확장 버튼 서브 메뉴 열기/닫기
- 확장 버튼 서브 아이템 선택 시 메인 아이콘 업데이트
- 구분선 표시 (가로/세로)
- 활성화/비활성화 상태 변경
- 외부 클릭 시 서브 메뉴 닫기
SetRadioButtonSelection()API 동작SetToggleButtonState()API 동작GetToggleButtonState()API 동작ClearRadioButtonSelection()API 동작- OnAction 이벤트 정상 발생
가로/세로 전환 테스트
- Horizontal → Vertical 전환 시 레이아웃 정상
- Vertical → Horizontal 전환 시 레이아웃 정상
- 전환 시 구분선 방향 자동 변경
- 전환 시 확장 버튼 화살표 방향 변경 (▼ ↔ ▶)
- 전환 시 서브 메뉴 위치 재계산 (아래 ↔ 오른쪽)
- UXML
orientation속성으로 초기 방향 설정 - 런타임
SetOrientation()호출 시 즉시 전환
성능 테스트
- 버튼 50개 생성 시 렉 없음
- 메모리 누수 없음 (Profiler 확인)
- 서브 메뉴 지연 생성 동작 확인
호환성 테스트
- Toolbar/Toolbox public API 호환성
- OnAction 이벤트 데이터 호환성 (Text, Value, ActionType)
- 기존 프로젝트와 동시 사용 가능 (uGUI/UIToolkit)
📚 참고 자료
CLAUDE.md 관련 섹션
StyleGuide 이미지
StyleGuide/style_guide_Buttons.png(버튼, Toggle, Icon 스타일)StyleGuide/style_guide_Sidebar.png(세로 배치 참고)StyleGuide/style_guide_Menu.png(서브 메뉴 스타일 참고)
기존 구현 참조
Assets/Scripts/UVC/UI/ToolBar/(전체 uGUI 구현)Assets/Scripts/UVC/UIToolkit/Menu/(UIToolkit 마이그레이션 패턴 참고)
UTKTopMenu 마이그레이션 작업지시서
작업지시서_TopMenu_UIToolkit_마이그레이션.md(동일 패턴 참조)
✅ 완료 조건 (모두 충족 필수)
기능 완료
- 모든 파일 생성 완료 (Data, Items, View, Controller)
- 4가지 버튼 타입 정상 동작 (Standard, Toggle, Radio, Expandable)
- 구분선 정상 표시
- 가로/세로 배치 전환 정상 동작
- Toolbar/Toolbox public API 호환
- 샘플 씬에서 정상 동작 확인
메모리 관리 완료 ⭐
- 모든 클래스 IDisposable 구현
- Unity Profiler로 메모리 누수 0 확인
- 이벤트 구독/해제 대칭 확인
- 데이터 바인딩/해제 대칭 확인
성능 완료 ⭐
- 서브 메뉴 지연 생성 적용
- UXML/USS 리소스 캐싱 적용
- 쿼리 캐싱 적용 확인
- Unity Profiler로 CPU/GC 확인
문서화 완료 ⭐
- 모든 public/protected 멤버 XML 주석 완료
- README.md 작성 완료
- 사용 예제 코드 작성 완료
스타일 완료
- StyleGuide 스타일 적용
- BEM 네이밍 준수
- CLAUDE.md 가이드 100% 준수
작성일: 2026-02-19 작성자: Claude Code Assistant 버전: 1.0