Files
EnglewoodLAB/작업지시서_ToolBar_UIToolkit_마이그레이션.md

55 KiB

ToolBar UIToolkit 마이그레이션 작업지시서

📋 개요

uGUI 기반의 ToolBar 시스템(Assets/Scripts/UVC/UI/ToolBar/)을 UI Toolkit 기반으로 마이그레이션하는 작업입니다. 기존 ToolbarModel의 데이터 구조와 Toolbar/Toolbox의 public API를 동일하게 유지하면서, 가로/세로 배치 전환 기능을 추가하고, 관심사 분리 원칙을 준수합니다.


🎯 작업 목표

  1. 독립적인 데이터 구조: UIToolkit 전용 UTKToolBarModel, UTKToolBarItemData 별도 구현
  2. Controller 호환성: Toolbar/Toolbox의 public API 동일하게 구현
  3. UIToolkit 전환: View를 UIToolkit 기반으로 재구현
  4. 가로/세로 배치: Orientation 속성으로 Horizontal/Vertical 전환 지원
  5. 4가지 버튼 타입: Standard, Toggle, Radio, Expandable 모두 지원
  6. 관심사 분리: Model, View, Controller 명확히 분리
  7. 메모리 관리: IDisposable 구현, 이벤트 정리, 메모리 누수 방지
  8. 성능 최적화: 쿼리 캐싱, GC 최소화, 불필요한 리빌드 방지
  9. 완전한 문서화: 모든 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);
    }
}

구현 지침:

  1. ExecuteClick()에서 IUndoableCommand 지원 (Execute 후 Undo 스택 관리)
  2. 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);
    }
}

구현 지침:

  1. _radioGroupsDictionary<string, UTKToolBarRadioButtonGroup>으로 관리
  2. AddRadioButton() 호출 시 자동으로 그룹 생성/등록
  3. 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();
    }
}

구현 지침:

  1. 아이콘 우선순위: UseMaterialIcon == true이면 UTKMaterialIcons 폰트 사용, 아니면 Resources.Load<Sprite>() 사용
  2. 테마: UTKThemeManager 패턴 (AttachToPanelEvent/DetachFromPanelEvent)
  3. 데이터 바인딩: 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: 기본 구조 구축

  1. 폴더 생성: Assets/Scripts/UVC/UIToolkit/ToolBar/, Data/, Items/
  2. 폴더 생성: Assets/Resources/UIToolkit/ToolBar/
  3. 열거형 및 인터페이스 구현:
    • UTKToolBarOrientation
    • UTKToolBarActionType
    • UTKToolBarActionEventArgs
    • IUTKToolBarItem
  4. UXML/USS 파일 생성 (12개)

Phase 2: 데이터 레이어 구현

  1. UTKToolBarButtonData.cs (추상 베이스) 구현
    • IDisposable, 이벤트, Command 실행
  2. UTKToolBarStandardButtonData.cs 구현
  3. UTKToolBarToggleButtonData.cs 구현
    • IsSelected, SetSelected(), OnToggleStateChanged
  4. UTKToolBarRadioButtonData.cs 구현
    • GroupName, RadioGroup 연동
  5. UTKToolBarRadioButtonGroup.cs 구현
    • 상호 배타 선택 로직
  6. UTKToolBarExpandableButtonData.cs 구현
    • SubButtons, SelectSubButton()
  7. UTKToolBarSeparatorData.cs 구현
  8. UTKToolBarModel.cs 구현
    • 팩토리 메서드, 라디오 그룹 자동 등록, IDisposable

Phase 3: View 아이템 구현

  1. UTKToolBarButtonBase.cs (추상 VisualElement) 구현
    • 아이콘(Material/Image), 텍스트, 테마, 데이터 바인딩
  2. UTKToolBarStandardButton.cs 구현
  3. UTKToolBarToggleButton.cs 구현
    • 토글 시각 효과 (선택/비선택 스타일)
  4. UTKToolBarRadioButton.cs 구현
  5. UTKToolBarExpandableButton.cs 구현
    • 서브 메뉴 지연 생성, 위치 계산, 외부 클릭 닫기
  6. UTKToolBarSeparator.cs 구현
    • 가로/세로 방향 전환

Phase 4: 메인 View + Controller

  1. UTKToolBar.cs 구현
    • Orientation 전환, BuildToolBar(), CloseAllSubMenus()
    • 외부 클릭 감지 (panel.visualTree PointerDownEvent)
  2. UTKToolBarController.cs 구현
    • Toolbar/Toolbox 호환 API

Phase 5: 테마 및 스타일

  1. UTKThemeManager 연동
  2. StyleGuide 스타일 적용 (Buttons, Sidebar 참조)
  3. 라이트/다크 테마 지원

Phase 6: 테스트, 최적화, 문서화

  1. 샘플 씬 업데이트 (UTKToolBarSample.cs)
    • 가로/세로 전환 버튼 포함
    • 4가지 버튼 타입 데모
  2. 메모리 누수 점검 (필수):
    • Unity Profiler로 메모리 확인
    • 씬 전환 시 메모리 증가 없는지 확인
    • IDisposable 정상 호출 확인
    • 이벤트 구독 해제 확인
  3. 성능 최적화 (필수):
    • Unity Profiler로 CPU/GC 확인
    • 버튼 50개 생성 테스트
    • 쿼리 캐싱 적용 확인
  4. 완전한 문서화 (필수):
    • 모든 public/protected 멤버 XML 주석
    • README.md 작성

⚠️ 주의사항

필수 준수 사항 (위반 시 재작업)

  1. #nullable enable 파일 선두에 필수
  2. Unity 6 방식: [UxmlElement], partial class, [UxmlAttribute] 사용
  3. 이벤트 등록: RegisterCallback<ClickEvent>() 사용 (RegisterValueChangedCallback 금지)
  4. UXML/USS 네이밍:
    • UXML: UTKToolBar.uxml
    • USS: UTKToolBarUss.uss (접미사 Uss 필수)
  5. 케밥 케이스: [UxmlAttribute("orientation")] 소문자 + 하이픈
  6. IDisposable 구현 :
    • 모든 데이터/View 클래스에 필수
    • _disposed 플래그로 중복 호출 방지
    • RegisterCallbackUnregisterCallback 대칭
    • DetachFromPanelEvent에서 정리
    • 하위 객체도 재귀적으로 Dispose
  7. 아이콘 우선순위: 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