hyuntae's blog
원본: 한국어English日本語

AI에 코딩을 맡기기 시작한 뒤로 ③ — AI 서버가 죽으면 나도 바보가 된다

2026-06-30AI|조회수: 17
목차

AI 적응기 (4부작)

  1. 여러 도구를 써보고, 결국 직접 만들기까지
  2. AI와 '일하는' 시스템 — 엔지니어링과 검증 오너십
  3. AI 서버가 죽으면 나도 바보가 된다 — 로컬 LLM과 AI 의존 ← 지금 읽는 글
  4. AI를 쫓아가며, AI와 함께 — 배움의 방식까지 바뀌다

2편 끝에서 말했듯, 'AI가 나를 대체할까'라는 두려움이 가라앉은 자리에 다른 두려움이 자랐다. AI가 멈추면 나도 같이 멈추는 것 아닌가 하는, 의존에 대한 불안이다. 이 편은 그 불안을 줄여보려 로컬 LLM까지 굴려본 실험의 기록이다. 결론부터 말하면, 로컬 LLM은 그 의존을 0으로 만들어주는 도구가 아니었다. 다만 그 과정에서, 그동안 그저 갖다 쓰기만 하던 AI를 처음으로 내 손으로 직접 띄우고 만지고 튜닝해봤다. 블랙박스를 열어본 셈이다.

로컬 LLM을 만든 진짜 이유는 '토큰'이었다

오해를 먼저 풀어두자. 내가 로컬 LLM에 손댄 첫 동기는 거창한 '의존 탈출'이 아니었다. 순전히 토큰을 아끼기 위해서였다. 2편에서 본 생산성 폭증의 이면엔, 한 주에 수억 토큰을 태우며 5시간·주간 한도를 거덜내는 현실이 있었다. 마침 Gemma 4가 나오며 "로컬 모델 성능이 굉장하다"는 이야기가 돌던 무렵이라, 자연스레 "가벼운 일은 로컬로 돌리면 토큰을 아끼지 않을까" 하는 계산이 섰다.

정작 'AI 의존'이라는 묵직한 고민은, 시작할 때가 아니라 실험을 한참 굴려보다가 마주쳤다. 토큰을 아끼자고 벌인 일이, 어느새 *'AI에 이렇게까지 기대도 괜찮은가'*라는 질문 앞에 나를 데려다 놓은 것이다.

집 윈도우 PC: 12GB GPU의 벽

먼저 집 윈도우 PC에 로컬 LLM을 올렸다. 12GB 메모리의 GPU에 Ollama를 깔고 Gemma 계열 모델을 띄웠다. 부팅하면 로그인 없이 자동으로 떠 있도록 백그라운드 서비스로 등록해두고, 옵시디언 위키와 연동해 이런저런 질문을 던져봤다.

결론부터 말하면, 12GB로는 컨텍스트 용량이 너무 작아 실전에는 거의 도움이 되지 않았다. 기껏해야 위키를 참조해 내 질문에 답해주는 '비서' 수준에 머물렀다. 코딩 에이전트의 대용은 어림없었다.

왜 12GB가 발목을 잡았는지는 짚어둘 가치가 있다. GPU 메모리는 모델 가중치를 올리는 데 한 번 쓰이고, 그다음 KV 캐시(어텐션이 참조하는, 토큰마다 쌓이는 키·밸류 텐서)에 또 쓰인다. 이 KV 캐시는 컨텍스트 길이에 비례해 선형으로 불어난다. 최신 모델들이 GQA(Grouped-Query Attention) 같은 기법으로 KV 캐시 크기를 줄였다 해도, 코딩 에이전트가 요구하는 32k·64k 토큰대의 긴 컨텍스트에선 KV 캐시만으로 수 GB가 날아간다. 가중치를 올리고 남은 자투리 메모리로 그걸 감당해야 하니, 실제로 쓸 수 있는 컨텍스트 창이 확 쪼그라든다. 코딩 에이전트는 도구 정의와 파일 맥락만으로도 수만 토큰을 우습게 먹는데, 그 창이 작으면 애초에 게임이 안 된다.

무대를 회사로: 잔여 맥북 4대를 묶다

집에서 단일 머신의 벽을 확인할 무렵, 회사에 마침 놀고 있는 맥북 4대가 있었다. "남는 장비로 로컬 LLM을 꾸려 토큰을 아껴보자"는 이야기가 자연스레 나왔고, 내가 집에서 굴려본 경험이 조금 있으니 그걸 살리자는 흐름이었다. 그래서 나를 필두로, 맥북 4대와 2편에서 말한 Karpathy식 옵시디언, 그리고 syncthing을 엮어 사내용 로컬 에이전트 클러스터를 꾸리기 시작했다.

그런데 장비가 한 대에서 네 대로 늘자, 집에서는 보이지 않던 종류의 벽들이 줄줄이 나왔다. 아래는 그 4대를 셋업하며 부딪힌 이슈들이다.

실측이 더 아팠다 — 첫 응답 60초, 그리고 택일의 문제

기대는 "클라우드 없이도 비슷하게 돌아간다"였다. 현실은 숫자로 반박해왔다.

첫 응답이 느렸다. 풀 툴 컨텍스트(약 22k 토큰)를 prefill하는 첫 메시지에 약 60초가 걸렸다. 클라우드의 즉답에 길든 손에는 너무 긴 시간이었다. prefill은 모델이 첫 토큰을 뱉기 전에 입력 전체를 한 번 훑어 KV 캐시를 채우는 단계인데, 입력이 길수록, 그리고 하드웨어가 약할수록 이 구간이 길어진다.

여기서 가장 뼈아픈 트레이드오프를 만났다. 장비가 네 대니 자연스레 "부하를 여러 대로 나눠 돌리자"는 생각이 들었는데, 정작 "빠른 후속 응답"과 "여러 대로 분산"은 동시에 가질 수 없었다.

  • Ollama에는 prefix KV 캐시 기능이 있다. 같은 prefix(예: 매번 똑같은 시스템 프롬프트 + 도구 정의)를 재사용하면, 그 부분의 prefill을 다시 계산하지 않고 캐시에서 가져온다. 실제로 같은 prefix 재사용 시 prefill이 약 3.2초에서 0.03초로 떨어졌다. 100배다.
  • 그런데 부하를 네 대에 분산하려고 매 호출을 새 세션으로 던지면(stateless 디스패처), 그 prefix 캐시가 매번 죽는다. 즉 빠르게 하려면 캐시를 살려야 하고, 캐시를 살리려면 한곳에 붙어 있어야 한다. 분산과 캐시가 정면으로 충돌한 것이다.

한동안은 분산을 접는 쪽으로 기울었다. 4대로 나누는 걸 포기하고 한 대가 모델을 문 채 직접 받게 하면, 적어도 캐시는 살았으니까. 하지만 그건 나머지 장비를 놀리는 반쪽짜리였다.

진짜 해결은 엔진을 갈아치우면서 왔다. Ollama는 유휴 상태가 되면 모델을 메모리에서 내려버렸고, keep_alive=-1로 붙잡아 둬도 도구가 잔뜩 붙은 요청에선 매번 ~60초가 다시 났다. 그래서 Ollama를 버리고 llama.cppllama-server로 갈아탔다. 프로세스가 떠 있는 한 모델을 내리지 않으니, 언로드로 캐시가 통째로 날아가는 문제부터 사라졌다.

분산도 같은 작업에서 다시 짰다. 매 호출을 stateless로 워커에 흩뿌리는 대신, 게이트웨이 한 곳에서 페르소나·검색·맥락을 다 붙인 '완성된 프롬프트'를 만들어 워커로 넘기게 했다. 워커가 제각각 프롬프트를 조립하면 줄바꿈 하나, 공백 한 칸 차이로도 llama.cpp의 prefix 캐시 매칭(가장 긴 공통 prefix를 찾는 방식)이 깨지기 일쑤다. 게이트웨이가 단일 진실 공급원으로서 프롬프트를 결정론적으로 완성해 넘기니, 입력 포맷이 통일돼 여러 대로 분산하면서도 캐시를 높은 확률로 살릴 수 있었다. 장비들을 워커로 묶고 그중 한 대가 게이트웨이를 겸하게 하니, '분산이냐 캐시냐'의 딜레마가 비로소 풀렸다.

토큰 좀 아끼자고 시작한 일인데, 어느새 추론 엔진을 바꿔 끼우고 게이트웨이를 세우고 캐시 동작을 들여다보고 있었다.

품질의 천장과 운영의 잔손질

성능 자체에도 천장이 있었다. 처음엔 더 가벼운 Gemma 12B면 충분할 줄 알았다. 그런데 12B가 26B보다 오히려 느렸다. 우리가 쓴 26B가 MoE(Mixture of Experts) 모델이라 그렇다. 26B는 파라미터 총량은 26B여도, 토큰 하나를 처리할 때 실제로 켜지는 전문가는 약 4B뿐이다(모델명 26b-a4ba4b가 '활성 4B'를 뜻한다). 반면 12B는 dense라 매 토큰에 12B 전부가 연산에 들어간다. 게다가 토큰 생성은 **메모리 대역폭이 병목인 작업(memory-bandwidth bound)**이라, 매 토큰마다 활성 가중치를 메모리에서 읽어오는 양이 속도를 좌우한다. 26B-MoE는 토큰당 4B만 읽으면 되고 12B-dense는 매번 12B를 통째로 긁어와야 하니, 활성 4B인 26B가 더 빨랐다. '작은 모델이 빠르다'는 직관은 dense 모델끼리일 때만 맞다. (양자화는 26B 전체를 맥북 메모리에 올리려고 쓴 수단일 뿐, 속도 역전의 주역은 MoE 구조였다.) 결국 26B로 갔고, 품질도 그쪽이 나았다. 그래도 복잡한 추론이나 대규모 리팩터링은 여전히 클라우드 쪽이 안정적이었다. "로컬로 다 옮긴다"가 아니라 "가벼운 일만 로컬로"라는 원래 계산이, 실측 앞에서 더 좁아진 셈이다.

게다가 맥북 네 대를 똑같이 맞춰 굴리는 건 "그냥 켜두면 되는" 일이 아니었다. 운영의 잔손질이 끊이지 않았다.

  • 모델 quant·버전 통일. 같은 모델이라도 양자화 방식과 버전이 장비마다 제각각이면 동작과 품질이 미묘하게 달라진다. 네 대의 출력을 같게 맞추려면 이것부터 통일해야 했다.
  • 컨텍스트 길이 불일치로 인한 인스턴스 reload(스래싱). 요청마다 컨텍스트 설정이 다르면 모델을 다시 올리느라 시간을 까먹는다.
  • 백그라운드 서비스가 셸 환경변수를 안 읽는 문제 같은, 사소하지만 사람 잡는 함정들.

사내 연동, 그런데 보안에서 멈췄다

4대 클러스터에 인터넷 검색을 붙이고, 위키를 채우고, 사내 협업 도구들과 본격적으로 연동하려던 시점이었다. 그런데 거기서 보안 문제에 막혔다. 사내 협업 도구 곳곳에 자격증명이 평문으로 노출돼 있던 이슈가 적지 않았다. 로컬 LLM이 그 도구들을 RAG나 에이전트 맥락으로 그대로 빨아들이면, 누군가의 질문에 답하다 평문 자격증명이 그대로 흘러나오거나, 에이전트가 그 토큰으로 권한 밖 동작을 할 위험이 있었다. 결국 프로젝트는 일단 보류됐다. 토큰을 아끼려는 실험이, 역설적으로 우리 조직의 보안 위생을 들춰낸 셈이다.

여기까지가 내 로컬 LLM 이야기다.

그리고, AI 의존과 정면으로 마주쳤다

실험을 하다 보니 처음의 질문이 모양을 갖춰 돌아왔다. 간혹 클라우드 AI가 먹통이 되면, 나는 그냥 손을 놓게 되더라. AI가 없다고 코딩을 못 하는 건 아니다. 다만 효율이 극악으로 떨어진다. 내가 한 줄씩 직접 치고 있을 시간에, 차라리 AI가 복구되길 기다리며 프롬프트를 다듬는 편이 낫다고 느낄 정도로.

그러다 문득 무서워졌다. 어느새 이렇게까지 의존하게 됐다.

이 도구가 사라지면, 나는 어떻게 되는 거지?

대비를 안 한 건 아니다. 한 모델이 죽으면 다른 모델로 갈아탄다는 fallback 전략은 1편에서 말한 "한 모델에 다 맡기지 않는다"는 원칙의 연장이다. 한쪽이 죽는 동안 다른 쪽을 쓰면 된다. 그런데 — 만약 둘 다 죽는다면? 그땐 또 다른 모델을 써야 하는데, 그러려면 Claude, Codex(GPT), Antigravity(Gemini) 세 제품을 다 사둬야 한다. 평소엔 주력 하나만 쓰면서도, 만일을 대비해 나머지 도구들의 구독료나 API 안전망 비용까지 매달 떠안아야 한다는 뜻이다. 쓰지도 않는 안전망에 꼬박꼬박 돈을 붓는 셈이다. 그리고 정말로, 세 모델이 한꺼번에 다 죽는다면? 그 순간 사람이 할 수 있는 게 뭐가 있을까. 지금으로선 답이 보이지 않는다.

그래서 나는 *'AI 서버가 죽으면 나도 바보가 된다'*는 느낌을 자주 받았다. 그리고 이 말에, 팀원들도 깊이 공감했다. 혼자만의 과민함이 아니라, 우리 모두가 같은 절벽 위에 서 있었다.

그래서 로컬 LLM은 답이었나 — 부분적으로만

정직하게 말하면, 부분적으로만 그랬다. 로컬 LLM은 AI 의존을 0으로 만들어주는 도구가 아니었다. 비용 절감, 프라이버시, "끊겨도 돌아간다"는 안심은 실재했지만, 첫 응답 60초와 품질 천장은 분명한 벽이었다.

다만 이 실험에서 더 값진 수확은 따로 있었다. AI를 블랙박스가 아니라, 내가 직접 운영하고 튜닝하는 대상으로 끌어내렸다는 것이다. KV 캐시, prefill, 양자화, 컨텍스트 길이 같은 걸 손으로 만지는 동안, 역설적으로 AI에 대한 의존이 아니라 이해가 늘었다. 그리고 깨달았다 — AI 의존을 줄이는 건 더 강한 모델이 아니라, 내가 만든 시스템 쪽이라는 걸. 2편의 검증 절차와 외부 기억, 그리고 이 편에서 모델 밑단까지 직접 만져본 경험. 서버가 죽어도 내 검증 기준과 기록과 원리 이해는 죽지 않는다. 거기까지 만들어두는 것이, 지금 내가 찾은 'AI 의존을 줄이는 법'이다.

마지막 편에서는, 이 모든 변화가 결국 일을 넘어 배우는 방식까지 바꿔놓은 이야기를 한다. 그리고 솔직한 고백 하나 — 지금 당신이 읽는 이 글을, 실은 누가 썼는지에 대해서도.


이어지는 글: ④ AI를 쫓아가며, AI와 함께 — 배움의 방식까지 바뀌다

이 글이 도움이 되었다면

댓글