2026년 09월 27일 Stories worth reading. Perspectives worth sharing.
C#에서 로컬 LLM 돌리기: LlamaSharp 설치와 함정
자동화와 인프라

C#에서 로컬 LLM 돌리기: LlamaSharp 설치와 함정

Caston 6월 29, 2026 1 min read

C#으로 만든 프로그램에 AI를 넣으려면 보통 파이썬 서버를 따로 띄우거나 클라우드 API를 부른다. 둘 다 번거롭다. 파이썬은 환경이 하나 더 늘고, 클라우드는 데이터가 밖으로 나간다.

LlamaSharp는 그 사이를 메운다. .NET 프로젝트에 패키지 하나를 넣으면 모델 파일을 직접 읽어 내 프로그램 안에서 돌린다. 다만 처음 붙일 때 걸리는 곳이 정해져 있어서, 그 지점들을 중심으로 정리한다.

어떤 도구인가

LlamaSharp는 llama.cpp라는 C++ 라이브러리를 C#에서 부를 수 있게 감싼 것이다. llama.cpp는 AI 모델을 개인 컴퓨터에서 돌리기 위해 만들어진 프로그램으로, 이 분야에서 사실상 표준으로 쓰인다.

즉 실제 계산은 검증된 C++ 코드가 하고, C# 쪽은 그것을 부르는 창구만 맡는다. 성능이 llama.cpp에 가깝다고 공식 저장소가 밝히는 이유가 여기 있다.

모델은 GGUF라는 형식의 파일 하나로 받는다. 허깅페이스에서 모델 이름 뒤에 gguf를 붙여 검색하면 대부분 이미 변환된 파일이 올라와 있다. 이 파일 하나만 있으면 인터넷 없이도 돈다.

설치에서 걸리는 지점

패키지는 두 개를 넣어야 한다. 본체인 LlamaSharp와, 실제 계산을 맡을 백엔드 패키지다. 이 둘을 헷갈려 본체만 넣으면 실행할 때 라이브러리를 못 찾는다는 오류가 난다.

백엔드는 환경에 맞춰 고른다. 그래픽카드 없이 쓰면 CPU용, 엔비디아 카드면 CUDA 버전에 맞는 것, 맥이면 Metal용이다. 여기서 가장 흔한 실수가 내 컴퓨터에 깔린 CUDA 버전과 다른 백엔드를 넣는 것이다. 이러면 오류 없이 그냥 CPU로 돌아간다. 느리다고 느껴질 뿐 어디가 잘못됐는지 알기 어렵다.

공식 문서도 이 경우를 따로 다룬다. 그래픽카드를 쓰는지 확인하려면 모델을 불러올 때 GPU에 올릴 층 수를 지정하는 값이 0보다 큰지 보라고 안내한다. 이 값이 0이면 카드가 있어도 쓰지 않는다.

두 번째 함정은 버전이다. llama.cpp는 변경이 잦고 호환이 자주 깨진다. LlamaSharp 저장소는 버전마다 어느 llama.cpp 커밋과 맞는지 표로 관리한다. 직접 컴파일해서 쓸 생각이라면 그 표에 적힌 커밋을 정확히 써야 한다.

모델 파일도 마찬가지다. 너무 최신 모델을 오래된 LlamaSharp로 열면 형식이 안 맞아 실패한다. 모델이 나온 날짜와 라이브러리 버전 날짜를 맞춰보는 게 빠른 확인 방법이다.

첫 코드

패키지를 넣었으면 아래 순서로 동작한다. 모델 파일 경로를 주고, 설정을 만들고, 대화 상대를 만든 뒤 질문을 던진다.

using LLama;
using LLama.Common;

var parameters = new ModelParams("C:/models/내려받은모델.gguf")
{
    ContextSize = 8192,   // 한 번에 기억할 분량
    GpuLayerCount = 35    // 그래픽카드에 올릴 층 수, 0이면 CPU만 사용
};

using var weights = LLamaWeights.LoadFromFile(parameters);
using var context = weights.CreateContext(parameters);
var executor = new InteractiveExecutor(context);

await foreach (var text in executor.InferAsync("안녕, 자기소개 해줘"))
    Console.Write(text);

여기서 값 두 개가 결과를 좌우한다. ContextSize는 모델이 한 번에 기억할 수 있는 분량이다. 이 값이 작으면 앞에서 한 말을 잊는다. 긴 문서를 넣거나 대화를 오래 이어갈 생각이라면 넉넉히 잡아야 한다.

나는 다른 도구에서 이 값 때문에 한참 헤맸다. 모델이 자꾸 앞 내용을 잊길래 성능 문제라고 생각했는데, 기본값이 4096으로 잡혀 있던 게 원인이었다. 자세한 과정은 로컬 LLM을 올리려다 겪은 기록에 적어뒀다.

GpuLayerCount는 모델의 몇 개 층을 그래픽카드에 올릴지 정한다. 카드 메모리가 부족하면 이 값을 줄여 나머지를 시스템 메모리로 넘긴다. 전부 올리고 싶으면 큰 숫자를 넣으면 되고, 모델 층 수보다 크면 알아서 전부 올린다.

카드 메모리보다 큰 모델

흔한 오해가 하나 있다. 그래픽카드 메모리가 12GB면 12GB보다 작은 모델만 돌릴 수 있다는 말이다. 실제로는 그렇지 않다.

층을 나눠 올리는 방식 덕분에 카드에 다 안 들어가는 모델도 돌아간다. 들어가는 만큼만 카드에 올리고 나머지는 시스템 메모리에서 처리한다. 나는 12GB 카드로 23GB짜리 모델을 초당 32개 단어 속도로 돌려봤다. 과정은 따로 정리해뒀다.

다만 카드 밖으로 넘어간 층이 많을수록 느려진다. 모델을 고를 때는 파일 크기를 카드 메모리와 비교해보고, 넘친다면 양자화된 버전을 찾는 편이 낫다. 양자화는 모델의 숫자 정밀도를 낮춰 파일을 줄이는 방법이다. 같은 모델이라도 파일 크기가 절반 이하인 버전이 함께 올라와 있는 경우가 많다.

언제 이 방식을 쓰나

클라우드 API가 더 나은 경우가 많다. 성능이 앞서고, 관리할 것도 없다. 그런데도 로컬을 고르는 이유는 대개 셋 중 하나다.

데이터가 밖으로 나가면 안 되는 경우가 첫째다. 사내 문서나 고객 정보를 다루는 프로그램이라면 이 조건이 성능보다 먼저다. 둘째는 인터넷이 없는 환경이다. 공장이나 현장 장비에 들어가는 프로그램이 여기 해당한다. 셋째는 호출량이 아주 많은 경우다. 한 번 내려받으면 호출 요금이 없으니 일정 규모를 넘으면 유리해진다.

반대로 가끔 쓰는 기능에 붙이려고 로컬을 택하면 손해다. 그래픽카드 값과 관리 부담이 API 요금보다 크다. 어느 쪽이 싼지는 모델별 API 가격에 예상 호출량을 곱해보면 바로 나온다.

C# 프로젝트에 AI를 넣을 자리가 있다면 패키지 두 개와 모델 파일 하나로 시작할 수 있다. 처음 한 번만 백엔드와 버전을 맞춰 놓으면 그 뒤로는 평범한 .NET 라이브러리와 다르지 않다.

함께 읽기

Leave a Comment