본문으로 건너뛰기
5분 AI.
GITHUB TRENDING C# WEEKLY #11 딥다이브 · 2026-10-23 분석

Llama-Windows 딥다이브
— 로컬 AI를 Windows의 일상 기능으로 만드는 C# 설계

데스크탑 앱

빠른 판단

무엇
llama.cpp 실행 엔진과 모델을 관리하고 트레이·전역 단축키로 대화를 여는 Windows WinUI 앱
이럴 때
Windows에서 로컬 모델을 설치·실행하고 작업 중인 앱 위에서 단축키로 채팅을 시작할 때
입력
llama.cpp 기반 모델과 대화 요청
결과
로컬 엔진의 채팅 응답·스트림
모델을 고르고, 내려받고, 메모리에 올리고, Alt+Space로 대화창을 부르는 Windows 트레이 앱입니다. 이 글에서는 추론 수학보다 추론 엔진을 실제 데스크탑 제품으로 감싸는 방법을 배웁니다. WinUI 3·WebView2·REST·SSE·프로세스 관리·메모리 예측·MSIX 배포를 하나의 흐름으로 연결합니다. 저장소: ggml-org/Llama-Windows · ⭐270 · 포크 28 · C# · MIT (2026-10-23 GitHub 기준) · 소스는 커밋 0c44e05 기준.
목차
  1. 프로젝트 한줄 요약
  2. 왜 주목받는가
  3. 기술 스택 전체 지도
  4. 아키텍처 심화 분석
  5. 디렉토리 구조 해부
  6. 학습 포인트 (기술별)
  7. 하드웨어 / 시스템 요구사항
  8. 직접 해볼 수 있는 실습 과제
  9. 관련 기술 심화 학습 로드맵
  10. 핵심 키워드 사전
  11. 참고 링크

1프로젝트 한줄 요약

작은 트레이 아이콘 뒤에 있는, 로컬 모델 운영 도구

Llama-Windows는 llama.cpp 기반 모델을 Windows에서 관리하고 대화에 연결하는 앱입니다. README는 이를 Windows 11용 트레이 앱이자 Llama for Mac의 WinUI 3 포트라고 소개합니다. 이 저장소에서 배울 핵심은 외부 추론 엔진을 안정적으로 실행하고, 그 상태를 사용자에게 정확히 보여주는 C# 설계입니다.

C# 앱은 별도 실행 파일인 llama.exe를 찾고, 필요하면 설치 경로를 거쳐 llama serve를 시작합니다. 이후 HTTP API로 모델 목록·다운로드·로드 상태를 다룹니다. 채팅 화면은 C#으로 전부 다시 만들지 않고, 서버가 제공하는 WebUI를 WebView2에 띄웁니다.

먼저 구분할 용어
모델 파일 · 추론 엔진 · 관리 앱
모델 파일은 학습된 숫자를 담은 재료이고, 추론 엔진은 그 숫자를 읽어 답을 계산하는 실행기입니다. 관리 앱은 어떤 모델을 쓸지 고르고 실행 상태를 다루는 창구입니다. 이 프로젝트는 세 번째 역할을 담당하며, 두 번째 역할은 외부 llama.cpp 실행 파일에 맡깁니다.
이 글을 관통하는 비유

주방 엔진과 주문·재고·호출벨을 연결하는 작은 식당

모델 파일은 식재료, llama.cpp는 요리하는 주방, C# 앱은 주문을 받아 주방을 운영하는 매니저에 가깝습니다. 재료가 창고에 있어도 아직 조리대에 올라간 것은 아닙니다. 그래서 다운로드 완료와 로드 완료는 서로 다른 상태입니다.

매니저가 요리가 끝났다고 먼저 말하면 손님이 빈 접시를 받습니다. 이 앱이 서버의 실제 상태를 계속 확인하는 이유도 같습니다. 버튼 클릭은 요청이고, 완료 여부는 주방의 응답으로 판단합니다.

1-1. 사용자의 행동을 코드의 책임으로 바꿔 보기

사용자 행동앱이 처리하는 일읽을 파일
트레이 아이콘 클릭모델 목록 플라이아웃 표시TrayIconManager.cs, Views/MainWindow.xaml.cs
모델 검색·선택추천 카탈로그와 Hugging Face Hub 정보 조회LlamaApp.HuggingFace/Catalog.cs, LlamaApp.HuggingFace/HubClient.cs
다운로드·실행REST 요청, 진행률 감시, 화면 상태 갱신LlamaApp.LlamaCpp/LlamaManager.cs
Alt+Space오버레이를 표시하고 현재 모델의 WebUI로 연결GlobalHotkey.cs, Views/OverlayWindow.xaml.cs
GGUF 파일 열기로컬 모델을 등록하고 서버에서 로드App.xaml.cs, LlamaApp.LlamaCpp/LocalModelImport.cs

첫 번째 소스 독해 목표는 “클릭 → 요청 → 상태 확인 → 화면 갱신”의 연결을 설명하는 것입니다. 모델의 신경망 구조를 몰라도 이 흐름은 읽을 수 있습니다. 비동기 프로그래밍과 운영체제 연동을 공부하기에 좋은 출발점입니다.

2왜 주목받는가

트렌딩 순위와, 코드에서 확인할 수 있는 제품의 매력을 구분하기

2026-10-23 GitHub 기준 선정 근거는 GitHub Trending C# Weekly #11입니다. 스타 270개와 포크 28개는 그날의 관심도 지표입니다. 다만 순위와 누적 스타만으로 유입 경로나 인기를 만든 단일 원인을 증명할 수는 없습니다. 아래는 소스 구조에서 읽을 수 있는 주목할 만한 이유에 대한 해석입니다.

2-1. 로컬 추론을 운영체제 안으로 가져온다

모델 실행 명령을 아는 것과 매일 편하게 쓰는 것은 다릅니다. 트레이·전역 단축키·파일 연결·Windows 공유·알림을 붙이면 AI가 별도 개발 도구에서 일상 기능으로 바뀝니다. Package.appxmanifest에는 .gguf 파일 연결, 공유 대상, llama 프로토콜 등록이 실제로 있습니다.

2-2. 엔진을 감싸는 계층이 얇다

저장소 소유자는 ggml-org이며 추론 실행은 llama.cpp에 맡깁니다. 앱은 모델 관리와 Windows 통합에 집중합니다. 엔진을 재구현하지 않고 프로세스와 HTTP 경계로 연결하면, 제품 코드를 운영체제 경험에 집중할 수 있습니다.

이 구조가 자동으로 더 빠른 추론을 뜻하지는 않습니다. 실제 속도는 모델·양자화·컨텍스트 길이·하드웨어·외부 런타임의 빌드에 좌우됩니다. 여기서 확인할 장점은 구현 책임의 분리와 기존 WebUI의 재사용입니다.

2-3. 용도가 겹치는 선택지와 비교하면

선택지사용자가 다루는 중심Llama-Windows의 학습상 차별점
llama.cpp 직접 사용실행 명령·서버·모델 파일을 직접 연결같은 엔진 위에 트레이, 상태 표시, 파일 열기와 설정 관리를 얹는 과정을 볼 수 있음
서버 WebUI 직접 열기브라우저에서 채팅 화면 사용WebView2로 같은 서버 화면을 재사용하면서 Alt+Space 호출과 창 수명을 관리
Llama for MacmacOS의 메뉴바 앱 경험README 기준 포트 관계. WinUI·Win32·MSIX로 Windows에 맞게 옮긴 구현을 학습
Llama-WindowsWindows 안에서 모델 선택·다운로드·실행현재 메모리 예산, 로드 상태, 다운로드 오류를 사용자 행동으로 연결하는 코드가 한곳에 있음

비교의 기준은 기능 수나 추론 성능 순위가 아닙니다. 어떤 책임을 사용자가 직접 맡고, 어떤 책임을 앱이 대신 맡는가입니다. 다른 로컬 AI 제품보다 빠르거나 모델 호환성이 넓다는 벤치마크는 이 비교에서 주장하지 않습니다.

2-4. 작은 창에도 테스트할 것은 많다

C# 소스 35,429줄 중 테스트 프로젝트가 12,818줄입니다. LlamaApp.Tests/ModelItemStateMachineTests.cs는 다운로드·로드·실패 때 어떤 버튼이 보여야 하는지 검사하고, LlamaApp.LlamaCpp.Tests/WebOriginPolicyTests.cs는 오버레이에 허용할 주소를 검사합니다. 이는 “AI 앱”에도 네트워크 실패, UI 상태 전이, 배포 경계 같은 전통적인 소프트웨어 문제가 핵심이라는 좋은 사례입니다.

읽는 관점
트렌딩을 학습 가치로 바꾸는 질문

“어떤 모델이 들어 있나?”에서 한 걸음 더 가서 “외부 프로세스가 늦거나 죽어도 화면이 거짓말하지 않게 하려면 어떻게 해야 하나?”를 물어보세요. 이 질문은 모델 런처뿐 아니라 다운로드 관리자, 개발 도구, 미디어 변환기에도 그대로 적용됩니다.

3기술 스택 전체 지도

웹 서비스의 앞·뒤가 아니라, 네이티브 앱·외부 서버·배포 도구의 조합

3-1. 프론트엔드: WinUI 3와 서버 WebUI의 역할 분담

LlamaApp.csproj의 UseWinUI=true가 네이티브 화면의 기반입니다. XAML로 모델 목록과 설정·상세 화면을 만들고 C#으로 상호작용을 처리합니다. 오버레이 안의 채팅은 Microsoft.Web.WebView2가 표시합니다. React·Vue·Electron을 앱의 프론트엔드라고 부를 근거는 이 저장소에 없습니다.

의존성고정 버전이 프로젝트에서의 역할
Microsoft.WindowsAppSDK.WinUI2.3.9WinUI 화면·컨트롤
Microsoft.WindowsAppSDK.Runtime2.5.1Windows App Runtime 배포 구성
Microsoft.WindowsAppSDK.Foundation2.3.12Windows App SDK 기반 구성 요소
Microsoft.Web.WebView21.0.4258.31서버 WebUI를 네이티브 창에 표시
H.NotifyIcon2.4.1트레이 아이콘 연동
Microsoft.Windows.SDK.BuildTools10.0.28000.2705Windows SDK 빌드 도구

위 버전은 LlamaApp.csproj의 직접 참조이며 루트 packages.lock.json의 직접 의존성 해석 결과와도 일치합니다. WinUI·Runtime·Foundation의 버전이 서로 다르다는 점을 보세요. 하나의 “Windows App SDK 버전”으로 뭉뚱그리면 실제 빌드 구성을 놓칩니다.

3-2. 백엔드: C# 라이브러리와 외부 llama 서버

계층기술·설정주요 책임
실행 관리LlamaApp.LlamaCpp / net10.0프로세스 실행, HTTP, SSE, GGUF 헤더, 메모리 판단
모델 정보LlamaApp.HuggingFace / net10.0원격 카탈로그·Hub 검색·모델 메타데이터
공통 정책LlamaApp.Common / net10.0모델 인터페이스, 주소 정책, 오류 분류, 비밀값 보호
실제 추론별도 llama.exe의 serve모델 다운로드·로드·추론 API와 WebUI 제공
직렬화System.Text.Json과 생성된 JSON 컨텍스트설정·카탈로그·서버 응답 변환
로컬 보관JSON 설정, 캐시 폴더, 모델 프리셋 INIDB 서버 없이 파일 기반 상태 저장

외부 llama.cpp 버전은 앱 의존성 파일에 단일 버전으로 고정되어 있지 않습니다. Resolve()·ReadVersionAsync()가 찾은 실행 파일의 버전을 읽습니다. 따라서 앱 릴리스 v0.13.0과 엔진 빌드 번호를 같은 것으로 이해하면 안 됩니다.

용어
REST와 SSE
REST 요청은 “이 모델을 로드해 주세요”처럼 한 번 묻고 응답을 받는 통신입니다. SSE는 연결을 열어 두고 서버가 진행 상황을 계속 보내는 통신입니다. 이 앱은 요청을 REST로 보내고, 진행률은 SSE로 듣고, 최종 상태는 주기적 조회로 다시 맞춥니다.

3-3. 인프라·빌드: .NET, MSIX, GitHub Actions

파일실제 값해석
global.json10.0.0, latestMajor, allowPrerelease=false.NET 10을 출발점으로 안정 SDK의 롤포워드를 허용. 정확히 한 SDK만 강제하는 설정은 아님
LlamaApp.csprojnet10.0-windows10.0.19041.0.NET 10과 Windows API 대상 프레임워크
LlamaApp.Common/LlamaApp.Common.csprojProtectedData 10.0.12Windows DPAPI로 저장된 토큰·API 키 보호
두 테스트 csprojMicrosoft.NET.Test.Sdk 18.10.1, xunit 2.9.3, xunit.runner.visualstudio 4.0.1관리 로직과 UI 상태를 테스트하는 도구
Directory.Build.propsRestorePackagesWithLockFile=true프로젝트마다 잠금 파일을 남겨 의존성 변화를 검토
.github/workflows/_build.ymlRestoreLockedMode=trueCI에서 잠금 파일과 다른 의존성 해석을 거부

.github/workflows/ci.yml과 .github/workflows/release.yml은 재사용 워크플로 .github/workflows/_build.yml을 호출합니다. 테스트 뒤에 MSIX 패키징이 오도록 needs: test를 둡니다. LlamaCpp 테스트는 x64·ARM64에서, 앱 테스트는 x64에서 실행하도록 구성되어 있습니다. 같은 소스를 빌드하더라도 테스트·일반 배포·실험용 배포의 설정은 서로 다를 수 있습니다.

# .github/workflows/_build.yml: 정식 MSIX를 만드는 핵심 설정 발췌
-p:GenerateAppxPackageOnBuild=true
-p:AppxPackageSigningEnabled=false
-p:PublishTrimmed=false
-p:PublishReadyToRun=false
-p:PublishSingleFile=false

# .github/workflows/publish.yml: 수동 ZIP 배포 경로의 다른 선택
-p:PublishTrimmed=true
-p:PublishReadyToRun=false
-p:TrimmerSingleWarn=false
README와 현재 설정의 차이
문서의 빌드 설명을 모든 배포 경로에 적용하면 안 됩니다

README는 공식 빌드에서 트리밍을 끈다고 설명하지만, 현재 .github/workflows/publish.yml의 수동 ZIP 경로는 트리밍을 켭니다. 정식 MSIX 경로인 .github/workflows/_build.yml은 끕니다. 또한 README의 ARM64 테스트 설명은 앱 참조를 x64로 고정한다고 적지만, 현재 테스트 프로젝트는 Platform=$(Platform)으로 같은 플랫폼을 전달합니다. 실제 CI에서 앱 테스트를 x64로 제한하는 사실과는 구분해야 합니다.

4아키텍처 심화 분석

네 개의 설계 패턴으로 요청·상태·화면·정책을 연결하기

4-1. 전체 구조: 채팅 데이터와 관리 요청은 경로가 다르다

[Windows 사용자] | 트레이 / Alt+Space / 파일 열기 / 공유 v +---------------- C# 앱 프로세스 ------------------+ | App.xaml.cs : 시작·설정·단일 인스턴스·활성화 | | | | MainWindow + ModelItem : 모델 목록과 상태 | | | 관리 요청 ^ ModelsChanged | | v | | | LlamaManager : 실행·REST·SSE·주기적 상태 조회 | | | | OverlayWindow + WebView2 : 서버 WebUI를 표시 | +--------|-----------------------------|------------+ | 관리 HTTP / SSE | 채팅 HTTP v v +---------------- 외부 llama serve -----------------+ | 기본 접속: 127.0.0.1:9931 | | /health, /models, /models/load, /models/sse | | WebUI, /v1/chat/completions | | | 모델별 추론 작업 | +-------|------------------------------------------+ v [GGUF 모델 / HF 캐시 / .llama\models / 등록 폴더] [앱의 모델 정보 조회] ----> llama.app 카탈로그 ----> Hugging Face Hub API [모델 내려받기] ----> 외부 llama 런타임이 처리

중요한 갈림길은 WebView2입니다. 채팅 WebUI는 서버와 직접 통신하므로 C#의 모델 관리 코드가 모든 채팅 요청을 관찰하는 것은 아닙니다. BuildServeArguments()가 유휴 모델 해제를 앱 타이머가 아닌 서버의 --sleep-idle-seconds에 맡기는 이유도 여기에 있습니다.

4-2. 패턴 ① 프로세스 경계를 감싸는 파사드

LlamaApp.LlamaCpp/LlamaManager.cs의 LlamaManager는 실행 파일 탐색·설치·기동·상태 조회를 화면에서 사용하기 쉬운 메서드로 감쌉니다. 이런 단일 진입점을 파사드라고 부릅니다. App.OnLaunched()는 설정을 읽은 뒤 Initialize()로 공유 인스턴스를 먼저 만듭니다.

  1. EnsureLlamaOrDownloadAsync(): 먼저 /health로 기존 서버를 확인합니다.
  2. 서버가 없으면 Resolve()·FindOnPath()로 실행 파일을 찾습니다.
  3. 파일도 없으면 InstallAsync() 경로를 거친 뒤 다시 실행 파일을 찾습니다.
  4. StartServerAsync(): 직전에 다시 서버를 확인해 중복 실행 경쟁을 줄입니다.

StartServerAsync()는 ProcessStartInfo.ArgumentList에 인수를 하나씩 넣습니다. 하나의 큰 명령 문자열을 조립해 셸에 넘기는 방식보다 공백이 있는 경로를 다루기 쉽습니다. UseShellExecute=false이며 표준 출력과 오류를 읽어 기동 실패 원인도 보관합니다.

// StartServerAsync()의 핵심 흐름을 줄인 예시
var psi = new ProcessStartInfo
{
    FileName = BinaryPath,
    UseShellExecute = false,
    CreateNoWindow = true,
    RedirectStandardOutput = true,
    RedirectStandardError = true,
};

foreach (var arg in BuildServeArguments(/* 설정 인수 생략 */))
    psi.ArgumentList.Add(arg);

// 비밀값을 명령행 인수 대신 자식 프로세스 환경에 전달
psi.EnvironmentVariables["LLAMA_API_KEY"] = ApiKey;

프로세스 소유권도 구분합니다. 앱이 시작한 서버의 PID는 %LOCALAPPDATA%\Llama\.llama.pid에 기록하고 ReadLiveManagedPid()·StopServer()에서 관리합니다. 외부에서 실행한 서버를 발견해 사용하는 것과, 앱이 종료시킬 권한이 있는 서버는 별개의 개념입니다.

비유

이미 영업 중인 주방에 주문을 연결했다고 해서 그 주방의 전원을 끌 권한까지 얻는 것은 아닙니다. 연결 대상과 소유 대상을 구분하는 습관은 다른 프로그램의 프로세스를 관리할 때 특히 중요합니다.

4-3. 패턴 ② 상태 전이와 관찰자

ServerState는 Stopped·Starting·Running·Failed입니다. DeriveServerStatus()는 현재 상태와 API 도달 여부를 다음 상태로 바꾸는 함수입니다. 프로세스 생성 성공만으로 Running이라고 판단하지 않는 것이 핵심입니다. 다만 ProbeHealthAsync()는 HTTP 상태 코드와 무관하게 응답이 오면 도달한 것으로 판단하므로, Running이 모델 로드 완료까지 뜻하지는 않습니다.

현재 상태API에 도달다음 상태
Starting예Running
Starting아니요Starting — 기동 함수의 대기 제한이 별도로 작동
Running아니요Failed
Failed예Running — 서버가 돌아오면 회복
Stopped어느 값이든Stopped — 명시적 시작 전까지 유지

SupervisorLoopAsync()가 상태를 관찰하고 ModelsChanged 이벤트를 발행합니다. Views/MainWindow.xaml.cs의 LlamaManager_ModelsChanged()는 DispatcherQueue.TryEnqueue()로 UI 스레드에 전달한 뒤 ReconcileAsync()를 호출합니다. 목록 전체를 매번 지우고 만드는 대신 기존 행의 속성을 바꿔 화면의 깜빡임과 클릭 상태 손실을 줄입니다.

Views/ModelItem.cs는 INotifyPropertyChanged를 구현합니다. IsLoaded가 바뀌면 OpenGlyphVisible 같은 파생 속성도 변경됐다고 알립니다. 상태를 저장하는 쪽과 그 변화를 그리는 쪽을 이벤트로 연결하는 관찰자 방식을 여기서 볼 수 있습니다.

4-4. 패턴 ③ SSE와 폴링을 결합한 점진적 대응

LoadModelAsync()는 진행률이 필요하면 SSE를 먼저 열고 POST /models/load를 보냅니다. 반대로 하면 POST 직후 발생한 첫 이벤트를 놓칠 수 있습니다. SSE 연결이 불가능한 서버에서는 요청 수락 여부를 반환하고 폴링이 상태를 따라가도록 둡니다.

요청과 관찰의 순서 — 개념 도식
1. GET /models/sse 연결
2. POST /models/load 요청
3. status_change의 loading 진행률 수신
4. loaded 또는 unloaded로 결과 확인
5. 관찰 시간 초과 때 주기적 /models 조회로 재확인
   관찰 중 I/O·HTTP·객체 해제 예외는 실패 반환
   예외 없이 스트림이 끝나면 요청 수락 상태 유지

로드 관찰 제한은 LoadWatchTimeout=5분입니다. SSE가 unloaded를 보고하면 실패로 반환하지만, 단순 관찰 시간 초과는 이미 수락된 요청을 취소한 것으로 취급하지 않습니다. 요청이 수락됐다는 사실과 실제 작업이 끝났다는 사실을 구분해야 비동기 화면을 정확하게 만들 수 있습니다.

서버가 Running일 때 폴링은 상태 변화 직후 15초 동안 500ms, 안정 상태에서는 2초를 대기한 뒤 반복합니다. HTTP 조회 시간은 이 대기 시간에 더해집니다. SnapshotsEqual()로 관찰 가능한 필드가 같은지 비교해 불필요한 이벤트 발행을 줄이고, 목록 조회가 성공하면 변화가 없어도 마지막 알림으로부터 60초가 지난 시점에 알림을 보냅니다. /models 조회가 실패하면 /health를 한 번 더 확인해 일시적인 목록 오류를 서버 사망으로 오인하지 않도록 합니다.

용어
점진적 대응과 상태 재조정
가장 풍부한 기능인 실시간 진행률을 사용할 수 없어도, 더 단순한 조회 방식으로 기본 기능을 유지하는 접근입니다. SSE는 빠른 상황 전달에, 폴링은 현재의 사실을 다시 맞추는 데 쓰입니다. 둘을 함께 쓰는 것은 중복이 아니라 서로 다른 실패 양상을 보완하는 선택입니다.

4-5. 패턴 ④ 순수 정책 함수와 주입 가능한 외부 작업

HardwareFitHeuristic.Classify()와 WebOriginPolicy.IsServerOrigin()은 입력으로 결론을 계산하는 작은 정책 함수입니다. 창을 띄우거나 모델을 읽지 않고도 규칙을 검토할 수 있습니다. 반면 실제 파일·장치 조회는 다른 코드에 둡니다.

Views/ModelItemDetailsViewModel.cs의 생성자는 모델 상세 로드·메모리 예산 조회·런타임 기반 적합성 조회를 교체 가능한 함수로 받습니다. 기본값은 실제 작업을 사용하고, 테스트에서는 준비된 결과를 주입할 수 있습니다. 거대한 의존성 주입 프레임워크 없이도 느리고 불확실한 외부 작업을 분리하는 방법입니다.

4-6. 같은 경계에서 읽을 보안 설계

OverlayWindow.ConfigureWebViewThenNavigateAsync()는 문서와 프레임 탐색을 GuardNavigation()으로 검사합니다. WebOriginPolicy는 http·접속 호스트·포트가 모두 맞는지 확인합니다. 새 창 요청은 별도로 받아 일반 HTTP(S) 주소만 시스템 브라우저로 넘깁니다.

기본은 루프백 접속입니다. 다른 IPv4 주소로 열 때는 ServerAuth.RequiresApiKey()에 따라 API 키를 준비하고, C# HTTP 클라이언트와 WebView2의 허용된 서버 요청에 Bearer 헤더를 붙입니다. Settings는 저장된 비밀값을 SecretProtector의 DPAPI로 보호합니다. 이것은 전송 구간을 HTTPS로 바꾸거나 같은 사용자 권한의 모든 프로세스를 차단한다는 뜻은 아닙니다.

5디렉토리 구조 해부

프로젝트 이름보다 실제 참조 관계와 호출 지점을 먼저 보기
Llama-Windows/ ├── App.xaml / App.xaml.cs 앱 시작·활성화·공유 처리 ├── LlamaApp.csproj WinUI 앱 프로젝트 ├── Settings.cs / SettingsWindow.* 설정 보관·설정 화면 ├── TrayIconManager.cs 트레이 동작 ├── GlobalHotkey.cs 전역 단축키 ├── Views/ │ ├── MainWindow.xaml[.cs] 모델 목록·동기화·검색 │ ├── ModelItem.cs 모델 행의 알림 가능한 상태 │ ├── ModelItemDetailsViewModel.cs 상세 화면 동작·컨텍스트 선택 │ ├── OverlayWindow.xaml[.cs] WebView2 채팅 오버레이 │ └── *Presentation.cs 표시 문구·아이콘 등의 변환 ├── LlamaApp.Common/ 공통 인터페이스·정책·오류 ├── LlamaApp.HuggingFace/ 카탈로그·Hub API ├── LlamaApp.LlamaCpp/ 엔진 실행·GGUF·메모리·폴더 감시 ├── LlamaApp.Catalog/ 별도 카탈로그 추상화 흔적 ├── LlamaApp.Tests/ 앱·표시·설정·검색 로직 테스트 ├── LlamaApp.LlamaCpp.Tests/ 실행 관리·파일·정책·메모리 테스트 ├── Assets/ 아이콘·로고 ├── Properties/PublishProfiles/ 플랫폼별 게시 프로필 ├── .github/workflows/ CI·패키징·서명·배포 ├── packaging/winget/ winget 메타데이터 ├── docs/store-publishing.md Microsoft Store 배포 설명 ├── Package.appxmanifest 패키지·권한·파일·프로토콜 등록 ├── Directory.Build.props 공통 복원 설정 ├── global.json SDK 선택 └── LlamaApp.sln / LlamaApp.slnx 솔루션 구성

5-1. 세 보조 라이브러리가 맡는 일

폴더대표 파일·함수읽을 때의 질문
LlamaApp.CommonIModel, ArgumentTokenizer, ServeArgumentPolicy여러 계층이 공유할 규칙은 무엇인가?
LlamaApp.HuggingFaceCatalog.Flatten(), HubClient, Repository원격 데이터의 모양을 화면용 모델로 어떻게 바꾸는가?
LlamaApp.LlamaCppLlamaManager, GgufMetadata, LocalModelImport프로세스·파일·네트워크의 실패를 어떻게 감싸는가?
ViewsModelItem, ReconcileAsync(), *Presentation서버 상태를 어떤 문구와 행동으로 바꾸는가?

LlamaApp.Catalog/도 있지만, 현재 루트 앱의 ProjectReference는 Common·HuggingFace·LlamaCpp 세 라이브러리를 가리킵니다. LlamaApp.slnx에도 별도 Catalog 프로젝트는 포함되어 있지 않습니다. 실행 화면이 사용하는 Catalog.FetchFamiliesAsync()는 LlamaApp.HuggingFace의 Catalog입니다.

소스 독해 팁
폴더가 있다고 실행 경로에 포함되는 것은 아닙니다

동일한 이름의 클래스가 있으면 네임스페이스, 프로젝트 참조, 실제 호출 지점을 함께 보세요. 초안이나 미사용 추상화를 현재 아키텍처의 중심으로 오해하는 일을 줄일 수 있습니다.

5-2. 코드 규모: 구현과 테스트를 따로 세어 보기

분류파일 수물리적 줄 수
C# 구현10022,611
C# 테스트 프로젝트8912,818
C# 전체18935,429
XAML73,196
YAML 워크플로·패키지 메타7808
JSON 설정·잠금 파일8668

커밋 0c44e05 기준이며 .git은 제외했습니다. 빈 줄과 주석을 포함하고 마지막에 개행이 없는 줄도 한 줄로 센 값입니다. 테스트 코드는 C# 전체의 약 36.2%지만, 이 비율이 곧 테스트 커버리지는 아닙니다. C/C++ 추론 엔진이나 별도 JavaScript·TypeScript 프론트엔드 소스는 이 저장소에 포함되어 있지 않습니다.

LlamaApp.LlamaCpp/LlamaManager.cs만 3,605줄입니다. 처음부터 끝까지 순서대로 읽기보다 EnsureLlamaOrDownloadAsync() → BuildServeArguments() → LoadModelAsync() → SupervisorLoopAsync() 순서로 한 기능의 연결을 추적하는 편이 이해하기 쉽습니다.

6학습 포인트 (기술별)

기능을 복제하기보다, 다른 프로젝트로 옮길 수 있는 규칙을 배우기

6-1. C#: 화면 이름과 기계용 식별자를 분리하기

LlamaApp.Common/IModel.cs에는 Name과 ServerModelId가 있습니다. Hugging Face 저장소 이름과 양자화 선택은 함께 식별되어야 하므로 Repository.ServerModelId는 다음처럼 만듭니다.

// LlamaApp.HuggingFace/Repository.cs
public string ServerModelId => string.IsNullOrEmpty(Quant)
    ? Name
    : $"{Name}:{Quant}";

// 같은 저장소라도 양자화 선택이 다르면 다른 실행 대상
// ggml-org/gemma-3-270m-it-qat-GGUF:Q4_0

ModelItem은 IModel.Name을 명시적으로 구현하여 화면 이름과 서버에 전달할 저장소 이름을 구분합니다. 실습 아이디어: 화면에 표시할 이름·캐시 탐색용 이름·서버용 ID를 세 열로 정리하고, 양자화 접미사를 빼면 어떤 다른 파일이 선택될 수 있는지 설명해 보세요.

6-2. XAML: 값이 바뀌면 화면에 알려 주기

Views/ModelItemDetailsView.xaml은 같은 ModelItem을 상세 화면에서도 참조합니다. 모델 상태를 복사해 따로 보관하면 목록과 상세 화면이 어긋날 수 있습니다. 공유 모델과 변경 알림을 통해 두 화면을 함께 갱신하는 방식을 읽어 보세요.

<!-- 실제 바인딩의 핵심 속성만 남긴 학습용 예시 -->
<Image Source="{x:Bind ViewModel.Model.Logo, Mode=OneWay}" />

<TextBlock Text="{x:Bind ViewModel.DisplayName}" />

Mode=OneWay와 기본 모드를 무작정 같은 것으로 보지 마세요. 나중에 바뀌는 속성과 처음 정해지는 속성의 차이를 살펴보는 단서입니다. 실습 아이디어: ModelItem.IsLoaded의 setter에서 알리는 파생 속성을 찾아, 알림 하나를 빼면 어떤 버튼이 이전 모습으로 남을지 종이에 표시해 보세요.

6-3. 비동기: await와 UI 스레드는 다른 문제다

await는 작업을 기다리는 문법이고, UI 객체를 어느 스레드에서 바꿀지는 별도 규칙입니다. 관리자의 이벤트는 백그라운드에서 올 수 있으므로 LlamaManager_ModelsChanged()에서 DispatcherQueue를 확인합니다.

실습 아이디어: 모델 로드 흐름을 “UI 스레드”, “HTTP 대기”, “SSE 읽기”, “UI 스레드 복귀”로 나눠 보세요. 취소에는 CancellationToken, 관찰 제한에는 별도 타임아웃이 있다는 점도 표시합니다. 취소와 서버의 실패, 단순한 관찰 시간 초과가 같은 결과인지 비교하면 좋습니다.

6-4. 데이터 변환: 트리와 목록은 목적이 다르다

Catalog.FetchFamiliesAsync()는 모델 계열 → 크기 → 빌드의 계층을 읽습니다. Catalog.Flatten()은 이를 빌드당 하나의 Repository로 펼칩니다. 탐색 화면에는 계층이 편하고, 다운로드 대상 식별에는 평평한 목록이 편하기 때문입니다.

모델 계열 ├─ 1B 크기 │ ├─ Q4 빌드 ──> Repository 1 │ └─ Q8 빌드 ──> Repository 2 └─ 3B 크기 └─ Q4 빌드 ──> Repository 3

실습 아이디어: 계열 2개, 계열당 크기 2개, 크기당 빌드 3개인 작은 데이터를 그려 보세요. 펼친 결과가 12개가 되는 이유와, 계열 수를 다운로드 가능한 모델 수로 세면 안 되는 이유를 설명해 봅니다.

6-5. 바이너리: 거대한 모델을 전부 읽지 않고 정보 얻기

GgufMetadata.ReadContextInfoAsync()는 파일 스트림에서 헤더를 읽습니다. ParseCore()는 매직 값, GGUF 버전 2·3, 메타데이터 개수 등을 확인하고 context_length·block_count·embedding_length 등을 추출합니다. 메타데이터 개수가 100,000을 넘으면 손상 가능성이 있다고 보고 거부합니다.

실습 아이디어: LlamaApp.LlamaCpp.Tests/GgufMetadataTests.cs에서 작은 메모리 스트림으로 헤더를 만드는 예를 읽고, “파일 끝이 잘린 경우”, “버전이 다른 경우”, “필요한 키 순서가 다른 경우”의 기대 결과를 적어 보세요. 수 GB짜리 모델 없이도 파서를 이해할 수 있습니다.

6-6. 제품 규칙: 메모리 추정은 불확실성도 표현해야 한다

ModelMemoryEstimator.Estimate()는 파일 크기를 알면 가중치 크기로 사용하고, 모르면 매개변수 수와 양자화 비트 수로 추정합니다. 여기에 KV 캐시 추정과 가중치의 10%, 최소 256MiB의 추가 메모리를 더합니다. HardwareFitHeuristic는 결과를 Fit·Might·NoWay로 나눕니다.

정보가 부족할 때 불가능하다고 단정하지 않고, 모른다는 상태를 화면에 표현하는 것도 설계입니다. Classify()는 필요량이나 예산이 0이면 Might를 반환합니다. 실습 아이디어: 정확한 파일 크기가 있는 경우와 매개변수 표기만 있는 경우에, 같은 색상이라도 신뢰도가 다를 수 있음을 설명하는 도움말을 써 보세요.

6-7. 빌드: 소스 생성과 트리밍을 연결해서 읽기

LlamaApp.HuggingFace/HubJsonContext.cs·LlamaApp.LlamaCpp/LlamaJsonContext.cs·SettingsJsonContext.cs는 JSON 변환에 필요한 형식 정보를 선언합니다. .github/workflows/publish.yml의 트리밍 경고 검사와 함께 읽으면 실행 중 형식을 추측하는 코드와 빌드 시 필요한 형식을 알려 주는 코드의 차이를 배울 수 있습니다.

실습 아이디어: 카탈로그 응답 DTO 하나가 JSON 컨텍스트에 등록되는 지점을 찾으세요. 이어 ZIP 배포의 경고 필터가 ABI.System.·ABI.Windows.·WinRT.·IL2104가 포함된 줄을 제외하고, 나머지 warning IL숫자 경고가 있으면 실패하는 조건을 적어 봅니다. IL2104는 어셈블리 이름과 관계없이 제외되므로 모든 앱 코드 경고를 잡는다고 단정할 수 없습니다. CI 설정도 프로그램의 동작 조건을 기록한 소스라는 점을 익힐 수 있습니다.

7하드웨어 / 시스템 요구사항

앱 설치 조건, 개발 환경, 모델 실행 메모리는 따로 계산하기

7-1. 운영체제와 개발 환경

구분확인할 조건근거·주의점
설치 패키지 OSWindows.Desktop 10.0.26100.0 이상Package.appxmanifest의 현재 MinVersion
정식 패키지 아키텍처x64·ARM64.github/workflows/_build.yml과 .github/workflows/release.yml의 행렬
x86 설정프로젝트·게시 프로필에 존재현재 정식 CI의 x64·ARM64 패키징 대상과는 다름
소스 개발Windows와 .NET 10 SDKREADME·global.json·CI의 10.0.x
MSIX 생성Visual Studio MSBuild와 Windows SDK 도구.github/workflows/_build.yml은 msbuild로 패키징
채팅 화면WebView2 실행 환경NuGet 참조 버전과 PC의 브라우저 실행 환경은 같은 개념이 아님

LlamaApp.csproj의 대상 프레임워크는 windows10.0.19041.0이고 TargetPlatformMinVersion은 10.0.17763.0입니다. 그러나 현재 설치 패키지의 최소값은 26100입니다. 매니페스트의 오래된 설명 주석보다 실제 속성값을 우선해 읽어야 합니다. 프레임워크 이름에 Windows 10이 들어간다고 패키지의 Windows 10 설치 지원을 뜻하지 않습니다.

WindowsAppSDKSelfContained=true는 Windows App SDK 구성 요소를 앱에 함께 싣는 설정입니다. .NET 런타임의 자체 포함 여부는 또 다른 설정이며 수동 ZIP 워크플로는 기본 입력값이 true입니다. 한 런타임을 포함했다고 모든 외부 실행 환경까지 포함되는 것으로 확대해석하면 안 됩니다.

7-2. RAM·VRAM: 모델 파일 크기에 대화 공간을 더한다

저장소는 모든 모델에 통용되는 “최소 RAM 8GB” 같은 단일 조건을 제시하지 않습니다. 같은 모델도 양자화와 컨텍스트 길이에 따라 필요 메모리가 달라집니다. CPU만 있는 경우도 판단 로직에 포함되어 있지만, 실행 가능 판정은 응답 속도를 보장하지 않습니다.

용어
가중치와 KV 캐시
가중치는 모델이 이미 배운 숫자입니다. KV 캐시는 대화를 계산하면서 앞선 토큰의 일부 계산 결과를 보관하는 공간입니다. 양자화로 모델 파일을 줄여도 대화가 길어지면 별도 캐시가 커질 수 있습니다.

ContextMemoryEstimate.EstimateKvCacheBytes()의 기본 f16 추정은 다음과 같습니다. 필요한 KV 헤드 정보가 있으면 그것을 쓰고, 없으면 더 넓은 임베딩 길이로 대체합니다.

# LlamaApp.LlamaCpp/ContextMemoryEstimate.cs의 계산을 식으로 옮기면
KV 바이트 = 토큰 수 × 레이어 수 × KV 폭 × 2(K와 V) × 2(f16 바이트)

# 학습용 가상 모델: 레이어 32, KV 헤드 8, 헤드 길이 128
KV 폭 = 8 × 128 = 1,024
4,096 토큰: 4,096 × 32 × 1,024 × 2 × 2 = 512 MiB
8,192 토큰: 1,024 MiB
가중치 4 GiB + 4,096 토큰의 KV = 4.5 GiB

이 예시는 실측 벤치마크가 아닙니다. ContextMemoryEstimate.EstimateTotalBytes()의 합계는 가중치와 KV 캐시이며 계산용 임시 버퍼 전체를 포함하지 않습니다. 설정에서 K·V 캐시 형식을 바꿔도 이 단순 함수 자체는 f16을 가정합니다. 아키텍처별 특성과 GPU 분배까지 정확히 반영하는 식으로 보아서는 안 됩니다.

상세 화면의 RefineContextFitsAsync()는 가능하면 QueryFitParamsAsync()를 통해 런타임의 fit-params 결과로 적합성 판정을 보완합니다. 파일 헤더 추정, 카탈로그 추정, 런타임 질의가 서로 다른 정보 수준이라는 점이 중요합니다.

7-3. 색상은 메모리 예산에 대한 휴리스틱이다

MemoryFit.CpuRamBudgetFraction=0.75이므로 메모리 예산은 대략 가용 VRAM 합계 + 가용 RAM의 75%입니다. 전체 장착 RAM이 아니라 현재 가용량이 들어갑니다. HardwareFitHeuristic의 구간은 다음과 같습니다.

판정규칙읽는 방법
Fit필요량 ≤ 예산의 90%현재 추정으로 여유가 있음
Might예산의 90% < 필요량 ≤ 예산의 125%, 또는 정보 부족경계에 있거나 판단 근거가 부족함
NoWay필요량 > 예산의 125%현재 추정에서 낙관적인 구간도 넘음

모델을 고를 때는 다운로드 크기, 실행 메모리, 컨텍스트 길이를 각각 확인해야 합니다. 예를 들어 가용 RAM 8GiB와 가용 VRAM 4GiB라면 계산상 예산은 10GiB입니다. 이때 초록 구간의 상한은 9GiB입니다. 이는 규칙 설명용 산술 예시이며 특정 장치에서의 실행 보증이 아닙니다.

7-4. 디스크 경로도 요구사항의 일부다

내용기본 경로이유
Hub 모델 캐시%USERPROFILE%\.cache\huggingface\hub다른 HF 도구와 모델 공유
로컬 등록 모델%USERPROFILE%\.llama\models패키지 밖의 서버도 같은 파일을 볼 수 있게 구성
설정·로그%LOCALAPPDATA%\Llama사용자별 앱 데이터

LocalModelImport는 하드링크가 가능하면 활용하고, 조건이 맞지 않으면 복사하는 경로를 갖습니다. 따라서 로컬 모델을 등록할 때도 언제나 추가 디스크 사용량이 0이라고 생각하면 안 됩니다. MSIX의 파일 시스템 가상화 때문에 서버가 읽는 파일은 .llama 아래에 두는 설계도 함께 살펴보세요.

8직접 해볼 수 있는 실습 과제

모델을 내려받지 않아도 할 수 있는 다섯 가지 소스 독해 과제

처음에는 실행 결과보다 입력·기대 상태·근거 함수를 적는 연습을 권합니다. 아래 과제는 파일 읽기와 계산만으로 시작할 수 있습니다. 코드로 옮기고 싶다면 원본과 분리된 연습 프로젝트에서 작은 순수 함수부터 재현해 보세요.

과제 1 · 입문

모델 하나의 세 가지 이름 추적하기 30~45분

목표: 화면의 이름과 API 식별자가 왜 다른지 설명합니다. LlamaApp.Common/IModel.cs → LlamaApp.HuggingFace/Repository.cs → Views/ModelItem.cs 순서로 읽습니다.

할 일: 저장소 ID 하나에 Q4_0과 Q8_0을 붙인 두 행을 만들고, 화면 이름·IModel.Name·ServerModelId를 표로 정리하세요. Quant가 빈 문자열인 경우도 한 행 추가합니다.

완료 기준: 양자화 접미사를 누락하면 왜 사용자가 고른 변형과 서버가 해석한 변형이 달라질 수 있는지, IModel.ServerModelId의 설명과 연결해 말할 수 있습니다.

과제 2 · 중급

서버와 모델 상태를 시간순으로 그리기 60~90분

목표: 서버 실행 상태와 모델 로드 상태를 분리합니다. DeriveServerStatus()·LoadModelAsync()·LlamaApp.Tests/ModelItemStateMachineTests.cs를 읽습니다.

할 일: ① 서버 시작 중 ② API 도달 ③ 로드 요청 수락 ④ SSE의 로드 진행 ⑤ 모델 로드 완료 ⑥ 서버 연결 끊김을 여섯 칸에 그립니다. SSE를 열 수 없거나 5분 관찰 시간이 지난 경우에는 별도 화살표를 추가합니다.

완료 기준: HTTP 성공 응답만 보고 IsLoaded=true로 바꾸면 안 되는 이유와, 서버가 Failed에서 다시 Running이 되는 조건을 설명할 수 있습니다.

과제 3 · 중급

컨텍스트 길이와 색상 판정 계산하기 45~60분

목표: 메모리 숫자가 어떻게 화면의 선택지로 바뀌는지 이해합니다. LlamaApp.LlamaCpp/ContextMemoryEstimate.cs와 LlamaApp.LlamaCpp/HardwareFitHeuristic.cs를 읽습니다.

할 일: 7절의 가상 모델로 4,096·8,192·16,384 토큰의 KV 크기를 계산합니다. 별도로 예산 10GiB에 대해 필요량 9GiB, 12.5GiB, 12.6GiB, 알 수 없음의 판정을 써 보세요.

완료 기준: 순서대로 Fit, Might, NoWay, Might가 됩니다. 이 결과가 특정 GPU에서의 실측값이 아닌 이유도 한 문장으로 덧붙입니다.

과제 4 · 고급

WebView와 사용자 인수의 허용 조건 표 만들기 90분

목표: “내부 페이지”나 “사용자 설정”도 명시적인 정책이 필요함을 이해합니다. LlamaApp.Common/WebOriginPolicy.cs·LlamaApp.Common/ServeArgumentPolicy.cs와 각각의 테스트를 읽습니다.

할 일: 호스트가 같은 경우, 포트만 다른 경우, HTTPS로 바뀐 경우를 나누어 내부 탐색과 외부 브라우저 열기의 기대 결과를 적습니다. 사용자 인수 --port 9000과 --port=9000이 모두 예약 옵션으로 잡히는지도 추적합니다.

완료 기준: URI 문자열의 단순 접두사 비교 대신 스킴·호스트·포트를 따로 비교하는 이유를 설명합니다. 비밀 인수의 --옵션=값 형태까지 로그에서 가려야 하는 이유도 적습니다.

과제 5 · 고급

같은 소스의 두 배포 경로와 업데이트 조건 비교하기 90~120분

목표: 워크플로와 정책 함수를 함께 읽습니다. .github/workflows/_build.yml·.github/workflows/publish.yml·LlamaApp.LlamaCpp/RuntimeUpdateScheduler.cs·LlamaApp.Common/InstallScriptIntegrity.cs를 봅니다.

할 일: MSIX와 ZIP의 트리밍·서명·실행 트리거를 표로 비교합니다. 이어 PinnedSha256=null일 때 최초 설치와 무인 업데이트가 각각 어떤 정책을 적용하는지 정리합니다.

완료 기준: “해시를 계산한다”와 “신뢰하는 고정 해시와 비교한다”의 차이, 그리고 현재 무인 업데이트의 설치가 거부되는 이유를 코드의 인수값으로 설명할 수 있습니다.

9관련 기술 심화 학습 로드맵

주 3~5시간, 기능 한 줄기를 끝까지 따라가는 다섯 주

모든 파일을 읽는 것이 목표는 아닙니다. 매주 소스의 규칙 하나를 자신의 말과 작은 예제로 다시 표현하는 것을 결과물로 삼으세요. C#이 처음이라면 1주 차에 속성·인터페이스·이벤트를 먼저 익히면 좋습니다.

주차배울 것·읽을 코드남길 결과물
1주 차C#의 속성·인터페이스·record·변경 알림. IModel, Repository, ModelItem식별자 매핑표와 행 상태별 버튼 표
2주 차async/await·취소·타임아웃·HTTP·SSE. LoadModelAsync(), ParseSseStreamAsync()성공·거부·연결 실패·관찰 시간 초과의 시퀀스 도식
3주 차GGUF 헤더·양자화·컨텍스트·KV 캐시. GgufMetadata, ContextMemoryEstimate, HardwareFitHeuristic토큰 길이별 메모리 계산표와 추정의 한계 목록
4주 차WinUI·XAML·UI 스레드·Win32·WebView2. OverlayWindow, GlobalHotkey, WebOriginPolicy호출·표시·포커스·숨김의 창 수명 도식과 주소 허용표
5주 차NuGet 잠금·테스트 분리·트리밍·MSIX·서명. Directory.Build.props, 워크플로 4개커밋에서 설치 패키지까지의 배포 도식과 업데이트 정책표
학습 순서의 이유

식당을 이해할 때 재료 이름부터 외우고, 주문 전달을 따라가고, 주방의 용량을 계산한 뒤, 출입문과 영업 절차를 살펴보는 순서입니다. 화면·서버·파일·배포를 한꺼번에 읽을 때보다 각 선택의 이유가 잘 보입니다.

이후에는 EnrolledFolderWatcher와 LocalModelImport로 파일 시스템 연동을 확장해서 읽거나, HubClient와 실패 분류 코드로 네트워크 검색 UX를 깊게 읽을 수 있습니다. 다음 학습 대상은 새로운 기술 이름보다, 지금 설명할 수 없는 실패 상황을 기준으로 고르는 편이 좋습니다.

10핵심 키워드 사전

이 저장소에서 그 용어가 맡는 역할까지 함께 기억하기
용어쉬운 설명
로컬 추론내 기기에서 모델을 계산해 답을 만드는 것. 모델을 구하는 인터넷 통신과는 별개입니다.
llama.cpp이 앱이 실제 모델 계산을 맡기는 외부 추론 런타임입니다.
llama serve앱이 실행하는 서버 명령. 모델 관리 API와 WebUI의 접점입니다.
GGUF모델의 숫자와 설명 정보를 담는 파일 형식. 앱은 헤더에서 컨텍스트 정보를 읽습니다.
양자화모델 숫자를 더 적은 비트로 표현하는 방법. 파일 크기·메모리·품질의 관계를 살펴야 합니다.
컨텍스트 길이모델이 한 번의 처리 문맥으로 다루는 토큰 범위. 길어질수록 KV 캐시 요구량도 커질 수 있습니다.
KV 캐시앞서 처리한 토큰의 계산 결과 일부를 보관해 반복 계산을 줄이는 메모리입니다.
GQA여러 질의 헤드가 K·V 헤드를 공유하는 구조. 이 앱의 KV 폭 계산에 헤드 수가 쓰입니다.
VRAMGPU가 사용하는 메모리. 앱은 가용량을 RAM과 함께 적합성 판단에 사용합니다.
오프로딩모델 계산의 일부를 CPU에서 GPU 쪽으로 맡기는 것. 전체가 VRAM에 들어가야만 하는 것은 아닙니다.
WinUI 3C#과 XAML로 Windows 네이티브 화면을 만드는 기반입니다.
XAML컨트롤과 배치를 선언하는 마크업. 이 앱의 모델 목록·설정·상세 화면을 구성합니다.
WebView2네이티브 창 안에 웹 화면을 표시하는 구성 요소. 여기서는 서버의 채팅 화면을 보여줍니다.
ViewModel화면이 보여줄 상태와 행동을 담는 객체. ModelItemDetailsViewModel이 대표 예입니다.
변경 알림속성이 바뀌었다고 바인딩된 화면에 알려 주는 것. INotifyPropertyChanged로 구현합니다.
DispatcherQueueUI 스레드가 처리하도록 작업을 보내는 창구. 백그라운드의 모델 이벤트를 화면으로 옮깁니다.
REST APIHTTP 요청으로 모델 조회·로드 같은 작업을 부탁하는 인터페이스입니다.
SSEHTTP 연결을 유지하며 서버가 이벤트를 보내는 방식. 다운로드·로드 진행률을 전달합니다.
폴링일정 간격으로 서버에 현재 상태를 다시 묻는 것. 이 앱은 상태 변화에 따라 간격을 조절합니다.
루프백내 컴퓨터 자신을 가리키는 주소. 기본 접속 주소는 127.0.0.1입니다.
오리진주소의 스킴·호스트·포트 조합. WebView 내부에 표시할 문서를 제한할 때 비교합니다.
Bearer 토큰HTTP Authorization 헤더로 보내는 접근용 비밀값입니다.
DPAPIWindows 사용자 범위에 묶어 저장 비밀값을 보호하는 기능. 현재 사용자로 실행되는 모든 프로그램을 격리하는 기능은 아닙니다.
PID실행 중인 프로세스의 번호. 앱이 시작한 서버를 재식별하는 데 사용합니다.
MSIXWindows 설치 패키지 형식. 앱 식별자·버전·파일 연결 같은 정보를 함께 선언합니다.
자체 포함 배포필요한 런타임을 앱과 함께 싣는 방식. 어떤 런타임을 포함하는 설정인지 구분해야 합니다.
트리밍사용하지 않는 코드를 배포 결과에서 줄이는 처리. 동적으로 쓰는 코드가 잘못 제거되지 않도록 주의가 필요합니다.
잠금 파일의존성 해석 결과를 기록한 파일. CI가 검토된 버전 조합을 재현하는 기준입니다.
하드링크같은 파일 내용을 다른 이름으로도 가리키는 파일 시스템 기능. 모델을 복제하지 않고 등록할 때 활용됩니다.
휴리스틱완전한 실측 대신 제한된 정보로 내리는 실용적 판단 규칙. 이 앱의 메모리 색상도 이에 해당합니다.

11참고 링크

소스는 커밋에 고정하고, 기술의 기본 개념은 공식 문서로 이어가기

11-1. 공식 저장소와 핵심 소스

11-2. 관련 기술 공식 문서

11-3. 현재 상태 (2026-10-23 GitHub 기준)

항목상태
저장소ggml-org/Llama-Windows · 설명: Windows Llama App Companion (Windows용 Llama 앱 보조 도구)
트렌딩 선정GitHub Trending C# Weekly #11 · 분석일 2026-10-23
스타 / 포크270 / 28 · 2026-10-23 GitHub 기준
생성일2026-06-29
최신 릴리스v0.13.0 · 2026-10-20
최근 push2026-10-22
분석 기준 커밋0c44e05 · 2026-10-22 · misc: use right windows store DisplayName
앱 선언 버전LlamaApp.csproj·Package.appxmanifest: 0.13.0.0
주 언어 / 코드 규모C# 189파일 약 35,429줄, XAML 7파일 약 3,196줄 · 테스트 포함
테스트 소스 규모C# 89파일 약 12,818줄 · 실행 성공률이나 커버리지를 뜻하지 않음
라이선스MIT · Copyright (c) 2026 Hugging Face Inc. · 개별 모델의 라이선스는 별도
쓰기 전에 알아둘 한계
로컬 앱이라는 말만으로 실행·연결·업데이트 조건을 생략할 수는 없습니다

로컬 처리의 범위: README는 텔레메트리 없음과 로컬 추론을 주장합니다. 기본 루프백 구조는 이에 맞지만 카탈로그·모델·릴리스 조회에는 외부 통신이 필요합니다. 설정은 비루프백 IPv4 바인딩도 허용합니다. “어떤 설정에서도 모든 통신이 기기 안에만 있다”는 의미로 읽으면 안 됩니다.

설치와 업데이트: InstallScriptIntegrity.PinnedSha256은 현재 null입니다. 최초 설치 경로의 InstallAsync()는 기본적으로 미고정 스크립트를 허용하지만, RuntimeUpdateScheduler는 allowUnpinnedInstall:false를 넘깁니다. 따라서 현 커밋에서 무인 업데이트는 설치 단계에서 거부됩니다. 7일 간격의 스케줄러가 있다는 사실만으로 자동 갱신이 완료된다고 단정할 수 없습니다.

호환성과 수치: 설치 최소 OS는 현재 매니페스트 값이 기준입니다. 메모리 색상과 예시는 추정이며 실제 속도·최대 컨텍스트·모든 GGUF 호환성을 보증하지 않습니다. 앱의 MIT 라이선스와 다운로드할 모델의 이용 조건도 따로 확인해야 합니다.

소스 기준: 동작 설명은 커밋 0c44e05의 코드와 설정을 기준으로 합니다. 테스트 코드의 존재와 규모만으로 특정 PC에서의 실행 성공이나 성능을 보장할 수는 없습니다.