메뉴 닫기

WinAPI 트리뷰·리스트뷰 활용법, SysTreeView32와 SysListView32로 고급 UI 구성하기

WinAPI 트리뷰·리스트뷰 활용법, SysTreeView32와 SysListView32로 고급 UI 구성하기

🖥️ 윈도우 개발에서 구조적 데이터 표현을 완벽하게 구현하는 방법

프로그램에서 복잡한 데이터를 직관적으로 보여주는 것은 사용자 경험을 좌우하는 중요한 요소입니다.
특히 윈도우 환경에서는 트리뷰(TreeView)리스트뷰(ListView) 컨트롤을 적절히 활용하면 구조적 데이터를 한눈에 이해할 수 있게 표현할 수 있습니다.
SysTreeView32와 SysListView32 클래스는 WinAPI 기반으로 동작하며, 파일 탐색기나 관리 도구처럼 계층 구조와 목록을 효율적으로 구성할 수 있게 해줍니다.
이 글에서는 이 두 컨트롤의 기본 개념부터 실무 적용 팁까지 단계별로 살펴보겠습니다.

이번 글을 통해 개발자는 물론, 윈도우 UI를 다루는 초보자도 고급 UI 요소를 구현할 수 있도록 이해를 돕는 예제와 함께 설명합니다.
특히 데이터 탐색, 파일 관리, 시스템 모니터링과 같이 구조화된 정보가 필요한 프로그램에서 트리뷰와 리스트뷰가 어떻게 협력하는지, 그리고 어떤 API 호출과 메시지를 통해 원하는 기능을 구현할 수 있는지를 다뤄봅니다.



🌳 트리뷰(TreeView) 기본 구조와 원리

트리뷰(TreeView)는 계층형 데이터를 시각적으로 표현하는 윈도우 UI 컨트롤입니다.
윈도우 탐색기에서 폴더 구조를 표시하는 영역이 대표적인 예시죠.
WinAPI에서 트리뷰를 구현할 때는 SysTreeView32 클래스를 사용하며, 각 항목은 부모-자식 관계를 기반으로 트리 구조를 형성합니다.

트리뷰의 핵심은 HTREEITEM 핸들을 이용한 항목 제어입니다.
항목 추가 시에는 TVINSERTSTRUCT 구조체를 채워서 TreeView_InsertItem 함수를 호출합니다.
각 항목은 고유 핸들을 가지므로, 이를 통해 자식 항목을 추가하거나 삭제, 확장, 축소 같은 작업이 가능합니다.

🔍 데이터 구조와 노드 관계

트리뷰의 각 항목은 노드(Node)라고 부르며, 루트 노드에서 시작해 여러 개의 하위 노드를 가질 수 있습니다.
이러한 계층 관계는 부모-자식 구조를 유지하며, 항목의 깊이에 따라 들여쓰기가 적용됩니다.
WinAPI에서는 이러한 구조를 내부적으로 TREEITEM 데이터로 관리해 빠른 탐색과 변경이 가능합니다.

  • 🌱루트(Root) 노드 생성
  • 🌿부모-자식 관계 설정
  • 🍂노드 확장·축소 제어

⚡ 이벤트와 메시지 처리

트리뷰 컨트롤은 다양한 WM_NOTIFY 메시지를 통해 이벤트를 전달합니다.
예를 들어 항목이 선택될 때는 TVN_SELCHANGED가 발생하며, 확장·축소 시에는 TVN_ITEMEXPANDED 이벤트가 전달됩니다.
이를 통해 UI의 다른 요소와 연동하거나, 항목 클릭 시 리스트뷰에 관련 데이터를 표시하는 등의 동작을 구현할 수 있습니다.

CODE BLOCK
// 예시: 트리뷰 루트 노드 생성
TVINSERTSTRUCT tvis = {0};
tvis.hParent = TVI_ROOT;
tvis.hInsertAfter = TVI_LAST;
tvis.item.mask = TVIF_TEXT;
tvis.item.pszText = TEXT("루트 노드");
HTREEITEM hRoot = TreeView_InsertItem(hTree, &tvis);

📄 리스트뷰(ListView) 활용과 데이터 표시

리스트뷰(ListView)는 정형화된 다량의 데이터를 표 형태로 보여주는 컨트롤로, 열(Column)과 행(Item), 서브아이템(Subitem)을 통해 정보를 구조적으로 표현합니다.
WinAPI에서는 SysListView32 클래스를 사용합니다.
보기 모드는 아이콘, 작은 아이콘, 리스트, 세부정보(Report) 등으로 전환할 수 있으며, 일반적으로 데이터 테이블을 구성할 때는 세부정보 모드를 선택합니다.

세부정보 모드에서는 LVCOLUMN으로 열을 정의하고, LVITEM으로 행과 서브아이템을 채웁니다.
또한 가상 리스트뷰(LVS_OWNERDATA) 모드를 활용하면 수십만 건의 데이터도 메모리 사용을 최소화하면서 고속으로 표시할 수 있습니다.
이 모드에서는 컨트롤이 필요한 시점에만 데이터를 요청하므로, 성능이 중요한 로그 뷰어나 파일 인덱서 UI에 적합합니다.

🧱 컬럼과 아이템 구성

먼저 열을 정의해 헤더를 구성한 뒤, 각 행의 주요 텍스트와 서브아이템을 채워 넣습니다.
열의 폭은 ListView_SetColumnWidth로 조절하고, 정렬은 LVM_SORTITEMS 또는 사용자 정의 비교 함수로 구현합니다.
체크박스, 그리드라인, 전체 행 선택 등은 LVS_EX_* 확장 스타일로 활성화할 수 있습니다.

CODE BLOCK
// 예시: 리스트뷰 컬럼/아이템 추가
LVCOLUMN lvc = {0};
lvc.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM;
lvc.pszText = TEXT("이름");
lvc.cx = 160;
ListView_InsertColumn(hList, 0, &lvc);

LVITEM lvi = {0};
lvi.mask = LVIF_TEXT;
lvi.pszText = TEXT("sample.txt");
lvi.iItem = 0;
lvi.iSubItem = 0;
ListView_InsertItem(hList, &lvi);

// 서브아이템
ListView_SetItemText(hList, 0, 1, TEXT("12 KB"));
ListView_SetItemText(hList, 0, 2, TEXT("2025-08-13"));

⚙️ 확장 스타일과 사용자 경험

리스트뷰의 사용성을 높이려면 ListView_SetExtendedListViewStyle을 통해 LVS_EX_FULLROWSELECT, LVS_EX_GRIDLINES, LVS_EX_CHECKBOXES 등을 적절히 조합합니다.
또한 이미지 리스트를 연결하여 파일 타입 아이콘을 표시하면 가독성이 향상됩니다.
컬럼 클릭으로 정렬 방향을 토글하거나, 더블클릭으로 상세 창을 여는 등 인터랙션을 추가해 생산성을 높일 수 있습니다.

🚀 고성능을 위한 가상 모드

대용량 데이터를 다룰 때는 LVS_OWNERDATA를 설정하여 가상 모드로 전환합니다.
이 경우 컨트롤은 행의 개수만 알고, 개별 셀의 텍스트는 LVN_GETDISPINFO를 통해 필요할 때 요청합니다.
필터링과 페이징을 함께 적용하면 스크롤이 긴 목록에서도 부드럽게 동작합니다.

💬 SysListView32는 정렬, 그룹화, 가상화 등 고급 기능을 제공하며, 적절한 확장 스타일 조합으로 사용자 경험을 크게 향상시킬 수 있습니다.



⚙️ SysTreeView32와 SysListView32 클래스 이해

윈도우 환경에서 트리뷰와 리스트뷰 컨트롤은 각각 SysTreeView32, SysListView32라는 클래스 이름을 기반으로 동작합니다.
이 클래스 이름은 WinAPI의 CreateWindowEx 함수 호출 시 컨트롤을 생성하는 데 사용되며, 각 컨트롤의 고유 기능과 메시지를 지원합니다.
또한 커스텀 드로잉, 가상 모드, 사용자 정의 이벤트 처리 등 고급 기능을 구현할 수 있는 기반이 됩니다.

두 클래스는 모두 공용 컨트롤 DLL(comctl32.dll)에 의해 제공되며, 사용 전 InitCommonControlsEx 함수를 호출해 초기화가 필요합니다.
트리뷰는 계층 구조, 리스트뷰는 표 형태의 데이터 표현에 특화되어 있지만, 내부적으로는 공통된 메시지 구조와 스타일 플래그를 공유합니다.
따라서 하나의 애플리케이션에서 이 둘을 연계하면 파일 탐색기처럼 강력한 데이터 브라우징 인터페이스를 만들 수 있습니다.

🛠️ 컨트롤 생성과 초기화

컨트롤 생성 시 CreateWindowEx에 클래스 이름을 SysTreeView32 또는 SysListView32로 지정합니다.
스타일 플래그로는 트리뷰의 경우 TVS_HASBUTTONS, TVS_HASLINES, 리스트뷰의 경우 LVS_REPORT, LVS_SHOWSELALWAYS 등을 주로 사용합니다.

CODE BLOCK
// 예시: SysTreeView32 생성
HWND hTree = CreateWindowEx(
    0, WC_TREEVIEW, NULL,
    WS_VISIBLE | WS_CHILD | TVS_HASBUTTONS | TVS_LINESATROOT | TVS_HASLINES,
    0, 0, 200, 400,
    hWndParent, (HMENU)IDC_TREEVIEW, hInstance, NULL
);

// 예시: SysListView32 생성
HWND hList = CreateWindowEx(
    WS_EX_CLIENTEDGE, WC_LISTVIEW, NULL,
    WS_VISIBLE | WS_CHILD | LVS_REPORT | LVS_SHOWSELALWAYS,
    200, 0, 400, 400,
    hWndParent, (HMENU)IDC_LISTVIEW, hInstance, NULL
);

📡 메시지와 이벤트 처리

이 두 컨트롤은 다양한 WM_NOTIFY 기반 메시지를 통해 사용자 상호작용을 알려줍니다.
트리뷰에서는 TVN_SELCHANGED, 리스트뷰에서는 LVN_ITEMCHANGED와 같은 이벤트가 대표적입니다.
이 이벤트를 활용하면 트리뷰에서 특정 항목을 클릭했을 때 해당 내용을 리스트뷰에 표시하는 연동 기능을 구현할 수 있습니다.

💡 TIP: SysTreeView32와 SysListView32를 함께 사용하면 복잡한 데이터 네비게이션 UI를 구현할 수 있으며, Windows 탐색기와 유사한 사용자 경험을 제공할 수 있습니다.

🔌 트리뷰와 리스트뷰 연동 기법

트리뷰(TreeView)와 리스트뷰(ListView)를 함께 사용하면 파일 탐색기처럼 계층 구조와 상세 목록을 동시에 보여주는 강력한 UI를 구현할 수 있습니다.
트리뷰에서 상위 항목을 선택하면 해당 항목에 대응하는 데이터가 리스트뷰에 즉시 로드되도록 이벤트를 처리하는 방식이 일반적입니다.
이러한 연동은 WM_NOTIFY 메시지를 통해 구현하며, 두 컨트롤 간의 데이터 매핑 로직이 핵심입니다.

특히 HTREEITEM과 리스트뷰의 LVITEM 인덱스를 서로 연결하는 데이터 구조를 마련하면, 항목 선택 시 리스트뷰를 빠르게 갱신할 수 있습니다.
이를 위해 해시맵, 배열, 또는 사용자 정의 구조체를 활용하는 방법이 널리 사용됩니다.

🔄 이벤트 기반 데이터 로딩

트리뷰에서 TVN_SELCHANGED 이벤트가 발생하면 선택된 항목의 고유 키를 가져와 리스트뷰에 관련 데이터를 로드합니다.
이 과정에서 리스트뷰의 기존 항목은 ListView_DeleteAllItems로 초기화한 뒤, 새 데이터를 ListView_InsertItemListView_SetItemText를 사용해 채웁니다.

CODE BLOCK
// 트리뷰에서 선택 변경 시 리스트뷰 갱신 예제
case WM_NOTIFY:
{
    LPNMHDR pnmh = (LPNMHDR)lParam;
    if (pnmh->idFrom == IDC_TREEVIEW && pnmh->code == TVN_SELCHANGED) {
        LPNMTREEVIEW pnmtv = (LPNMTREEVIEW)lParam;
        HTREEITEM hItem = pnmtv->itemNew.hItem;

        // 1. 선택된 트리 항목의 키 값 가져오기
        MyDataKey key = GetItemKey(hItem);

        // 2. 리스트뷰 초기화
        ListView_DeleteAllItems(hList);

        // 3. 해당 키에 맞는 데이터 로드
        LoadListViewData(hList, key);
    }
}
break;

📂 동적 로딩과 가상 모드 활용

대용량 데이터의 경우 모든 내용을 한 번에 로드하는 것은 비효율적입니다.
트리뷰 선택 시 해당 데이터의 일부만 리스트뷰에 로드하고, 사용자가 스크롤을 내릴 때 필요한 나머지 데이터를 로드하는 ‘지연 로딩(lazy loading)’ 기법이 유용합니다.
리스트뷰의 LVS_OWNERDATA 모드와 함께 사용하면 성능 저하 없이 대량 데이터를 처리할 수 있습니다.

💡 TIP: 두 컨트롤 간 연동 시 데이터 구조와 이벤트 흐름을 명확히 설계하면 유지보수성과 성능이 크게 향상됩니다.

⚠️ 주의사항

트리뷰와 리스트뷰의 연동 시 이벤트 루프에서 서로를 갱신하는 로직이 무한 반복되지 않도록 주의해야 합니다.
항목 선택 변경 이벤트가 다시 항목 선택 변경을 유발하지 않도록 플래그를 두고 제어하는 것이 좋습니다.



💡 고급 UI 구성 팁과 성능 최적화

트리뷰와 리스트뷰를 이용한 UI 구성에서 고급 기능과 성능을 모두 만족시키려면 세부적인 튜닝이 필요합니다.
아이콘, 색상, 폰트 스타일 등을 사용자 정의하여 정보 전달력을 높이고, 데이터 로딩과 렌더링 속도를 최적화하는 것이 핵심입니다.
특히 Custom Draw 기능을 활용하면 각 항목의 배경색, 텍스트 색상, 폰트 굵기 등을 상황에 맞게 동적으로 변경할 수 있습니다.

또한 대량 데이터 처리 시 불필요한 화면 갱신을 최소화해야 합니다.
리스트뷰에서는 WM_SETREDRAW 메시지를 이용해 데이터 추가 과정에서 화면 그리기를 잠시 중지하고, 모든 처리가 끝난 뒤 다시 활성화하면 눈에 띄는 속도 향상을 경험할 수 있습니다.

🎨 UI 커스터마이징

트리뷰와 리스트뷰는 기본적으로 윈도우 테마에 맞춰 렌더링되지만, NM_CUSTOMDRAW 이벤트를 처리하면 원하는 스타일을 직접 지정할 수 있습니다.
예를 들어 중요 데이터는 붉은색 글씨로, 비활성 데이터는 회색으로 표시하는 등 시각적 구분을 줄 수 있습니다.
또한 행 배경색을 번갈아 적용(Alternate Row Coloring)하면 가독성이 향상됩니다.

CODE BLOCK
// 리스트뷰 항목에 교차 배경색 적용 예제
case NM_CUSTOMDRAW:
{
    LPNMLVCUSTOMDRAW lplvcd = (LPNMLVCUSTOMDRAW)lParam;
    if (lplvcd->nmcd.dwDrawStage == CDDS_ITEMPREPAINT) {
        if (lplvcd->nmcd.dwItemSpec % 2 == 0) {
            lplvcd->clrTextBk = RGB(245, 245, 245); // 밝은 회색
        } else {
            lplvcd->clrTextBk = RGB(255, 255, 255); // 흰색
        }
        return CDRF_NEWFONT;
    }
}
break;

⚡ 성능 최적화 기법

  • 🚀대량 데이터 추가 시 WM_SETREDRAW로 화면 갱신 일시 중지
  • 🗂️리스트뷰 가상 모드(LVS_OWNERDATA) 활용
  • 🔍필요한 시점에만 데이터 로딩(지연 로딩)

📌 유지보수성과 확장성

코드 구조를 MVC 또는 MVP 패턴에 맞춰 설계하면 UI 변경이나 기능 확장이 쉬워집니다.
데이터와 UI 로직을 분리하고, 컨트롤 이벤트를 중앙에서 관리하면 복잡한 UI에서도 버그를 최소화할 수 있습니다.
또한 향후 다른 플랫폼이나 프레임워크로 이식할 때도 재사용성이 높아집니다.

💎 핵심 포인트:
트리뷰와 리스트뷰의 성능과 디자인을 동시에 끌어올리려면, Custom Draw·지연 로딩·화면 갱신 제어를 적극 활용하는 것이 좋습니다.

자주 묻는 질문 (FAQ)

트리뷰(TreeView)와 리스트뷰(ListView)의 차이는 무엇인가요?
트리뷰는 계층 구조 데이터를 시각적으로 표현하는 데 적합하고, 리스트뷰는 정렬된 표 형태로 데이터를 표시하는 데 적합합니다. 두 컨트롤을 함께 사용하면 탐색과 상세 보기 모두 지원 가능합니다.
SysTreeView32와 SysListView32는 어떤 역할을 하나요?
이들은 WinAPI에서 트리뷰와 리스트뷰를 구현하는 클래스 이름으로, CreateWindowEx 호출 시 지정해 컨트롤을 생성합니다. comctl32.dll이 내부 기능을 제공합니다.
트리뷰에 항목을 추가하는 기본 방법은 무엇인가요?
TVINSERTSTRUCT 구조체에 부모 항목, 텍스트, 이미지 등을 지정한 뒤 TreeView_InsertItem 함수를 호출해 항목을 추가합니다.
리스트뷰에서 열과 행을 구성하는 방법은?
LVCOLUMN 구조체로 열을 정의하고, LVITEM 구조체로 행과 서브아이템을 채웁니다. 확장 스타일로 전체 행 선택, 그리드라인, 체크박스 등을 활성화할 수 있습니다.
두 컨트롤을 연동하려면 어떤 이벤트를 사용해야 하나요?
트리뷰의 TVN_SELCHANGED와 리스트뷰의 LVN_ITEMCHANGED 같은 WM_NOTIFY 이벤트를 사용해 선택 변경 시 데이터 갱신 로직을 구현합니다.
Custom Draw를 사용하는 이유는 무엇인가요?
기본 스타일을 넘어 각 항목의 색상, 폰트, 배경을 상황에 맞게 변경해 가독성을 높이고 UI를 브랜드 스타일에 맞게 조정할 수 있습니다.
가상 모드(LVS_OWNERDATA)는 언제 사용하나요?
수십만 건 이상의 대량 데이터를 표시할 때 메모리 사용을 줄이고 성능을 유지하기 위해 가상 모드를 사용합니다. 필요한 시점에만 데이터를 요청합니다.
트리뷰와 리스트뷰 연동 시 주의할 점은?
서로 갱신 이벤트가 무한 루프를 일으키지 않도록 조건문이나 플래그를 사용해 제어해야 합니다.

🖥️ WinAPI 트리뷰·리스트뷰 활용 핵심 정리

트리뷰(TreeView)와 리스트뷰(ListView)는 윈도우 환경에서 구조적 데이터와 세부 정보를 효과적으로 표현하는 핵심 UI 컨트롤입니다.
트리뷰는 계층 구조를, 리스트뷰는 표 형식의 데이터를 시각적으로 보여주며, SysTreeView32SysListView32 클래스를 기반으로 동작합니다.
두 컨트롤을 함께 사용하면 파일 탐색기처럼 직관적이고 강력한 데이터 브라우징 인터페이스를 만들 수 있습니다.

효율적인 사용을 위해서는 각 컨트롤의 이벤트 구조를 이해하고, 데이터 매핑 로직을 명확히 설계하는 것이 중요합니다.
또한 Custom Draw로 UI를 커스터마이징하고, 지연 로딩·가상 모드·화면 갱신 제어 등을 활용하면 대규모 데이터에서도 성능과 가독성을 모두 확보할 수 있습니다.
이러한 기법을 적절히 조합하면 유지보수성과 확장성이 뛰어난 윈도우 애플리케이션 UI를 구현할 수 있습니다.


🏷️ 관련 태그 : WinAPI, 트리뷰, 리스트뷰, SysTreeView32, SysListView32, Windows프로그래밍, UI구성, CustomDraw, 가상모드, 성능최적화