업무에 필요한 Agent 직접 만들어보기 feat. Rust

2026-06-1812 분 소요0
시리
시리니

사이트에 들어올 때마다 늘 고쳐야 할 부분들만 신경 쓰다 보니, 정작 글을 짧게 남겨보고자 했던 초심을 잃고 계속 구경만 하다가 작업하러 가는 것 같아 그냥 편하게 글을 남겨봅니다.

 

요즘은 어딜 가나 에이전트가 대세가 되었고, 제가 다니고 있는 직장에서도 어김없이 대세가 되었습니다. 최근에는 사외 AI 서비스들도 전면적으로 개방 되어서 클로드 코드 같은 걸 업무에 활용하는 분들도 많아지고 있네요. (새삼스럽지만 AWS의 BedRock 서비스는 정말 대단합니다)

 

저의 경우에는 사외 AI 서비스 도입이 되기 전부터 여러 목적으로 자체 에이전트를 개발하며 사용 중입니다. 운이 좋게도 작년 초에 H100 GPU들을 여러 장 받아서 RAG와 MCP 기술을 활용한 AI 서비스 런칭을 했었는데요, 다행히 해외 동료들 중심으로 많이 활용되고 있습니다. 이후 후속 프로젝트 방향을 고민 하다가 나름의 목적에 충실한 에이전트를 Rust 언어로 개발하고 있습니다. 여기서는 짧게 Rust 언어의 첫 인상 내지는 언박싱(?) 같은 느낌 위주로 전해 드리려고 합니다. (언제나 그렇듯 두서 없이 끝날 거 같긴 합니다 😅)

 

에이전트를 만드는 이유?

일단 저의 경우 작년 초부터 RAG라는 기술을 활용하는 걸 시작으로 임베딩이나 LLM 모델 서빙 등 그동안 전혀 몰랐던 분야를 좌충우돌 하면서 하나씩 배우고 업무에 활용해 온 상황이었습니다.

 

그러면서 작년 말에 리뷰를 좀 해보니까, 잘 모르는 분야였지만 직접 서비스를 개발하고 모델들을 활용하면서 비로소 AI 서비스라는 것들의 실체를 조금은 배웠다는 생각이 들었습니다. 현업에 적용할 때 우선해서 고려해야 할 점들은 무엇인지 단순히 책이나 블로그 혹은 유튜브로 겉핥기만 하는 게 아니라 실전에서 몸으로 체득한 진짜를 얻은 느낌이라고 할까요?

 

그래서 올해 목표를 잡을 때, 자연스럽게 다음 단계로 내가 쓰고 싶은 에이전트를 직접 개발해보자. 만들어보면서 마찬가지로 머리가 아닌 고생을 통해서 몸으로 익혀보자는 생각에 이르게 되었습니다. 저는 머리가 좋지 않기 때문에, 말만 들어서는 제대로 이해하기 어려웠고 무작정 글을 봐도 한 번 직접 해보는 것만 못하다는 생각이 확고했거든요. 결국 지금 이 글을 남기는 시점에서 다시 한 번 저의 머리가 아닌 몸으로 겪은 고생들이 자산으로 남았다는 확신을 얻었습니다.

 

Rust를 선택한 이유?

에이전트라는 건 아주 쉽게 제 방식대로 설명하면, 결국 Loop를 하나 만드는데, 그 Loop 속에서 LLM에게 사용자의 요청과 함께 쓸 수 있는 도구들의 명세를 JSON으로 전달하고, LLM이 판단해서 필요한 도구들을 호출해 달라는 요청서를 JSON 형태로 전달해주면 그걸 받아서 실제 도구들을 호출하고, 결과를 정리해서 다음 번 Loop에 LLM에게 전달(피드백)하는 과정을 유한 번 반복하는 것입니다.

 

이런 Loop를 기반으로 LLM이 좀 더 잘 동작하도록 여러 안전 장치부터 가드레일까지 붙여둔 것들(하네스라고도 합니다)이 최근 핫한 Codex나 클로드 코드 같은 도구들입니다. 이런 유명한 도구들은 개발팀도 빵빵하고 더욱 중요한 토큰 사용량도 충분하니 터미널 한 10개 정도 띄워두고 역할 별로 채찍질만 주면 계속해서 개선이 되겠죠. 하지만 저는 약간 좁은 업무 목적(여러 대의 업무용 장비를 연결해서 테스트를 실행하고 대용량 로그를 수집/분석하는 기능)에 추가로 일반적인 기능 몇 가지를 지원하는 에이전트가 필요 했습니다. 때문에 가장 기본이 되는 언어 선택부터 약간 고심을 했습니다.

 

가장 우선순위가 높았던 언어는 Go 언어였습니다. TSBOARD부터 NUBO 프로젝트에 이르기까지 백엔드 개발을 Bun 런타임 기반의 TypeScript 코드에서 Go 언어로 변경한 이후 농담이 아니라 단 한 번도 후회하지 않았습니다. 물론 Go 언어의 단순함이 주는 강점에 매료되기도 했지만, 컴파일 언어가 주는 성능이 확실히 달랐고, 고루틴으로 대표 되는 동시성 작업에 있어서는 아직도 다른 언어들 대비해서 여전히 강점이 있다고 생각했습니다.

 

더구나 별도 GUI 도구 개발이 아닌 Windows Terminal 같은 터미널 도구에서 실행되는 TUI로 구상했었는데, 마침 Go 언어 기반으로 나온 버블티(https://github.com/charmbracelet/bubbletea) 와 같은 프레임워크가 상당히 매력적으로 보여서 거의 바로 Go 언어로 결정할 뻔 했었습니다.

 

그렇지만 결정적으로 업무 목적으로 활용할 때 대용량 로그 파일을 빠르게 분석하거나 추후 업무 용도의 확장 작업에서 제법 큰 크기의 RAW 파일들을 여러 장 다뤄야 하는 문제로 고심 끝에 Go 언어는 포기하게 되었습니다. 메모리 관리를 아무래도 GC 기반으로 하게 되면 이런 대용량 파일 핸들링에서는 조금 문제가 될 수 있을 것 같다는 생각이 들었거든요.

 

범용 에이전트가 아니라 다소 업무에 특화된, 비유를 들자면 Codex나 클로드 코드가 다목적 고성능 세단이라면 저는 약간 다마스 같은 느낌의 에이전트가 필요했습니다. 쓸 수 있는 자원이 넉넉하진 않지만 날라야 하는 짐들은 많은 느낌? 그래서 사무용 기기에서도 제 성능을 충실히 내면서 메모리 사용도 줄이고, 또 가능하면 안정적인 동작을 유지할 수 있어야 했습니다. 이것 저것 생각해보니, C++23 표준 기반으로만 만들면 목적 달성이 가능해지지 않을까 싶었는데 제가 C++에 대한 애증이 많아서 포기하고 결국 생전 처음 Rust 언어를 선택하게 되었습니다. Rust 언어가 (제가 생소하다는 것만 빼면) 요구 사항에 거의 완전히 맞았거든요.

 

Rust 개발, 언박싱 해 본 느낌?

Rust 언어를 아무것도 모른 채 AI에게 목표만 쥐어주고 개발을 시키기 보다는, 이 언어에 대해서 나도 좀 이해하고 몸으로 고생하면서 체득해보고 싶었습니다. 다만 업무에 활용하겠다는 분명한 목표와 더불어 일정이 있었기 때문에, 마냥 공부하긴 어려워서 점심 시간이나 휴게 시간, 주말 등을 이용해서 회사에서 굴러다니던 러스트 프로그래밍 공식 가이드 2판을 읽고 따라하기 시작했습니다.

 

약 2개월 가량 사전 스터디를 진행하고, 드디어 본 프로젝트를 시작하면서 책에서 글자로만 만났던 수많은 개념들이 다시 한 번 생소한 느낌으로 다가왔습니다. Go 언어를 배웠을 때처럼 어렵겠지만 금방 적응 하겠지? 생각했던 스스로의 뚝배기를 과감하게 내려쳐야 했을 정도로 쉽지 않더군요. 클로드를 개인 튜터로 두고 추천 받은 ratatui, tokio, anyhow, git2, reqwest, serde 등의 크레이트들을 활용하면서 TUI의 윤곽을 어설프게 나마 만들 수 있었습니다. (ratatui와 tokio는 정말 훌륭합니다! anyhow는 에러 처리가 너무 편해요. Go도 이건 좀 배웠으면 좋겠습니다. if err ≠ nil { … } 이거 좀 그만…)

 

vscode 기준으로 rust-analyzer가 사실 대부분의 빌림 오류나 라이프 타임 문제 등을 짚어주기 때문에 문제는 쉽게 발견할 수 있는데요, 이게 예제 수준의 코드에서는 쉬워 보이는 문제가 여러 모듈 내지는 여러 스레드 혹은 코루틴 사이를 오가기 시작하면서 조금씩 난해해지기 시작했습니다. 가령 한 번 쓰고 버릴 변수들이면 그냥 대입(=Rust 에서는 move가 기본) 처리하지만, 버릴 게 아니라면 .clone()으로 복사해서 넘겨주거나, Arc를 이용해서 여러 군데에서 접근해도 문제 없도록 처리를 해야 합니다. 그리고 Rust 언어는 기본적으로 컴파일 시점에 기본적으로 객체의 크기가 계산할 수 있어야 하는데(Sized), LLM이 쓸 도구들의 경우 여러 형태가 있을 수 있어서 컴파일 시점에 크기가 서로 다르기 때문에 Box로 한 번 감싸줘야 한다 같은 전혀 생각해보지도 않았던 문제들이 많았습니다.

 

그리고 아주 초보적인 헷갈림도 공존하는데요, Option과 Result 타입의 변수에서 값을 꺼낼 때 if let Some(value)를 써야 하는데 if let Ok(value)를 쓴다거나 하는 문제도 간간히 있었습니다. unwrap_or()unwrap_or_else()를 언제 어떤 걸 써야 할지도 헷갈리구요. (보통은 간단한 값 즉시 반환은 unwrap_or 이지만 defualt 값을 복잡한 연산으로 반환해야 할 때(혹은 메모리 할당이 필요할 때)는 지연 초기화가 가능한 unwrap_or_else를 쓴다! 알고는 있지만 늘 헷갈립니다. 😥)

 

전반적으로 Rust 언어로 TUI를 개발하는 첫인상 내지는 언박싱 느낌이 그다지 마법같이 느껴지거나 뭔가 그냥 즐겁다! 보다는, 배워야 할 게 너무 많아서 규칙들을 내가 지금 제대로 따라가고 있는지 매번 조심스럽게 확인해 나가는 약간은 긴장되고 지루한 느낌에 더 가까운 것 같네요.

 

하지만, 좋았던 점

언박싱(?)의 느낌이 프로젝트 초기의 느낌이라면, 지금은 그래도 좀 어느 정도 혼자서 안면이 텄다고 나름 좋아진 부분들도 있습니다. 대표적으로는 컴파일 = 실행됨 이게 가장 큰 것 같습니다. Go의 경우에도 드물긴 하지만 뭔가 잘못 처리하거나 하면 panic 을 만나기도 하고, C++은 뭐 두 말하면 입 아픈데 Rust 언어는 정말 컴파일만 되면 실행이 보장되고 대게의 경우 런타임에서 뻗는 문제가 드물어서 정말 마음에 들었습니다.

 

물론 대부분의 코드들을 작성할 때 클로드와 상의를 많이 하면서 작업하니까 100% 제가 모든 코드들을 백지에서 작성할 일은 없지만, 그럼에도 몸으로 체득하고자 일부러 코드를 내 맘대로 수정도 해보고, 컴파일이 안되면 왜 안되는지 찾아도 보면서 더 Rust와 친해지려고 노력하고 있습니다. 다른 언어를 쓸 때는 생각도 해본 적이 없었던 개념들을 배우는 즐거움도 조금은 생겼네요.

 

그리고 cargo 라는 도구가 주는 편리함, 필요한 외부 라이브러리들을 쉽게 받고 관리할 수 있는 장점은 정말 C++에서는 특히 따라오기 어려운 장점이 아닐까 싶습니다. 이 장점 하나로도 Rust 언어는 충분히 더 중요하게 취급 받을만한 언어라고 생각합니다. 에이전트 개발이라고 했지만 사실 대부분의 기능들은 이미 잘 만들어진 라이브러리들을 가져와서 잘 활용하는 것이 전부라고 해도 과언이 아닙니다. 이 때 중요한 게 의존하는 라이브러리들의 퀄리티나 버전 충돌 문제 등을 잘 잡아주는 것인데, cargo는 현대적인 패키지 매니저이자 빌드 시스템으로 모범이라 할만 합니다. C++ 생각하니 빌드 시스템 관점에서도 정말 차이가 크네요.

 

마치며: 만들어보면 더 잘 이해하게 된다

직접 해보지 않으면 제대로 알기 어려운 것들이 세상에는 많습니다. 자전거 타기만 하더라도 수십번 넘어지고 또 넘어진 후에야 비로소 2개의 바퀴만으로 온전히 바로 설 수 있는 방법을 이해하게 되지요. 컴퓨터를 직접 조립해보면 우리가 만나는 화면 그 너머에서 각자의 몫을 열심히 담당하는 부품들의 진면목을 알 수 있게 됩니다. 저는 프로그램도 마찬가지라고 생각하며, AI 시대에는 특히 더 이런 직접 해보기가 필요하다고 생각합니다.

 

LLM이라는 녀석이 결국 확률적으로 답변을 하는 머신이고, 결정론적인 작업에는 맞지 않기 때문에 실제로 계산 같은 작업들은 도구로 만들어서 붙여주는 것이 낫다는 걸 RAG부터 MCP 그리고 에이전트를 만들어보면서 직접 깨닫게 되었습니다. 저의 경우 이 에이전트가 좀 더 제대로 빠르게 처리해줬으면 하는 다소 특화된 업무가 있었기에 특히 더 의미 있는 작업이었던 것 같네요.

 

어설프더라도, 남들이 만든 것보다 부족해 보이더라도 일단 만들어보면 더 잘 이해할 수 있습니다. AI 시대에 가장 큰 장점은 AI가 나의 튜터로서 내 수준에 맞춰 설명도 해주고, 바보 같은 질문에서 성심껏 열심히 답변을 해준다는 점입니다. 만들고 배우는 게 이보다 쉬워진 시대가 없습니다. 때문에 저처럼 다른 분들도 직접 자신만의 에이전트를 만들어 보시면 어떨까 하는 생각이 들었습니다. Rust 언어를 써보지 않으셨다면 이참에 한 번 배워서 써보시는 건 어떨까요?

rust
agent
tui
ratatui
tokio
rag
mcp