Rust 배우기 – 01. ollama copy
rust로 ollama와 같은 오픈소스 모델을 로드 실행하는 윈도우 애플리케이션을 개발할 것입니다.
LLM 모델 추론의 핵심인 llama-cpp-rs 같은 외부 라이브러리를 사용하기 위해 Cargo.toml 파일에 의존성을 추가하는 법을 익히시면 됩니다.
Cargo.toml은 프로젝트의 설정 파일로, 사용할 외부 라이브러리(Dependencies)와 프로그램의 이름 및 버전을 관리하는 명세서입니다. 라이브러리를 추가할 때는 [dependencies] 항목 아래에 라이브러리명 = "버전" 형식으로 작성하면 Cargo가 자동으로 파일을 내려받아 프로젝트에 연결합니다. 파일 내용을 수정한 후 저장하면 Cargo가 알아서 의존성 변화를 감지하고 빌드 시점에 필요한 모듈을 처리하므로 매우 편리합니다.
Cargo.toml 파일의 [dependencies] 섹션 아래에 llama-cpp-rs = "0.1"을 추가하여 외부 라이브러리를 프로젝트에 포함하세요. 그 후 프로젝트 루트 폴더에서 cargo build를 실행하면 해당 라이브러리가 자동으로 다운로드되고 컴파일됩니다. 이제 src/main.rs 파일에서 use llama_cpp_rs::... 구문을 통해 라이브러리 기능을 불러와 모델 로딩과 추론 코드를 작성할 수 있습니다.
에러 발생)
에러 메시지를 보면 llama-cpp-rs라는 이름의 패키지를 찾지 못하고 있습니다. Rust에서는 하이픈(-) 대신 밑줄(_)을 사용하는 경우가 많으니 Cargo.toml 파일의 [dependencies] 섹션을 llama_cpp_rs = "0.1"로 수정해 보시기 바랍니다. 만약 그래도 찾을 수 없다는 에러가 발생한다면, crates.io에서 해당 라이브러리의 정확한 이름과 최신 버전을 확인하여 다시 기입하십시오.
프로젝트 폴더 내에 생성된 Cargo.lock 파일을 열어보면 해당 라이브러리를 다운로드 받았는지 확인할 수 있습니다. 이 파일 안에 [[package]] 항목으로 name = "llama_cpp_rs"가 포함되어 있다면 프로젝트에 성공적으로 의존성이 등록된 것입니다. 터미널에서 cargo tree 명령어를 입력해도 현재 프로젝트가 사용하는 모든 라이브러리와 그 관계를 트리 구조로 즉시 확인할 수 있습니다.
에러 발생)
libclang은 C/C++ 코드를 분석하고 이해할 수 있게 해주는 도구 모음(LLVM 프로젝트의 일부)입니다. Rust에서 C++로 작성된 llama.cpp와 같은 라이브러리를 가져다 쓰려면, Rust가 C++ 헤더 파일을 읽고 구조를 파악해야 하는데 이때 libclang이 그 ‘번역기’ 역할을 수행합니다. 즉, 서로 다른 언어인 Rust와 C++를 연결(Binding)하는 빌드 과정에서 반드시 필요한 필수 컴포넌트입니다.
LLVM 공식 사이트에서 Windows용 LLVM-x.x.x-win64.exe 설치 파일을 받아 설치하세요. 설치 후 시스템 환경 변수 LIBCLANG_PATH를 생성하고, 해당 경로의 bin 폴더(예: C:\Program Files\LLVM\bin)를 값으로 입력한 뒤 터미널을 재시작하십시오. 이후 cargo run을 다시 실행하면 정상적으로 빌드될 것입니다. 설치할 때 패스 설정에 대한 부분을 체크할 수도 있습니다.
에러 발생)
이렇게 저렇게 해도, 빌드 에러가 계속 나면
- 환경 초기화: 모든 터미널을 종료하고, 윈도우 시작 메뉴에서 “Developer Command Prompt for VS 2022″를 찾아 실행합니다.
- 명령어 실행: 해당 창에서
hello_rust폴더로 이동합니다.cargo clean을 먼저 입력합니다. - 빌드 재시도: 다시
cargo build를 실행하여 컴파일을 진행하십시오.
시간 좀 걸림) 이 단계는 llama.cpp 엔진을 C++로 직접 컴파일하는 과정이라, CPU 성능에 따라 상당한 시간이 소요됩니다. 만약 15분 이상 아무런 변화가 없다면, Ctrl+C로 중단한 뒤 cargo build --release로 최적화 설정을 변경하여 다시 빌드해 보세요.
Rust의 main.rs 구조를 초보자 눈높이에서 핵심만 설명해 드립니다.
1. fn main() { ... } (프로그램의 시작점)
- 의미: Rust 프로그램은 반드시
main이라는 이름의 함수에서 시작합니다. - 설명:
fn은 함수(function)를 선언하는 키워드이며,{}중괄호 안의 코드가 프로그램이 실행될 때 순서대로 수행됩니다.
2. use (라이브러리 불러오기)
- 의미:
use키워드는 외부 라이브러리(crate)나 기능을 코드 안으로 가져옵니다. - 설명: 마치 요리할 때 필요한 재료(도구)를 미리 식탁 위에 올려두는 것과 같습니다. 예를 들어
use llama_cpp_2::model::Model;은 “llama-cpp-2 라이브러리 안에 있는 Model이라는 도구를 쓰겠다”는 선언입니다.
3. let (변수 선언)
- 의미: 데이터를 저장할 공간을 만듭니다.
- 설명:
let model_path = "파일경로";처럼 사용하며,model_path라는 이름의 상자에 “파일경로”라는 값을 담아두는 것입니다.
4. Result와 expect() (에러 처리)
- 의미: 프로그램이 죽지 않도록 안전장치를 겁니다.
- 설명: Rust는 데이터 로딩처럼 위험한 작업(실패할 수 있는 작업)을 수행할 때
Result라는 결과를 반환합니다..expect("실패 시 메시지")는 “만약 파일이 없어서 로드에 실패하면, 이 메시지를 띄우고 프로그램을 강제로 멈춰라”라는 뜻입니다.
5. println!() (화면 출력)
- 의미: 콘솔 창에 글자를 보여줍니다.
- 설명: 뒤에
!가 붙어있으면 일반 함수가 아니라 ‘매크로’라는 특수한 기능입니다. 화면에 결과를 보고 싶을 때 사용합니다.
팁: Rust는 엄격한 언어라 코드 중간에 세미콜론(;)을 빼먹으면 컴파일 에러가 납니다. 문장이 끝날 때마다 꼭 붙여주세요!
