인터넷 없이도 AI 챗봇을 만들 수 있을까?

Ollama와 Spring AI가 인터넷 연결 없이 로컬 LLM을 호출하는 구조를 살펴보고, 동기·스트리밍 응답과 하드웨어·품질·보안 한계를 설명합니다.

Spring AI로 챗봇을 넘어 일하는 AI 만들기 2/9

Java·Spring 개발자가 LLM, RAG, Tool Calling, MCP를 공부하며 이해한 것들

이전 글 — Spring AI는 AI 모델을 만드는 도구일까?

기차에서 노트북으로 챗봇을 만들다가 인터넷이 끊겼다고 생각해 보겠습니다. 외부 회사의 AI 서버에 질문을 보내는 일반적인 챗봇은 더 이상 답하지 못합니다. 질문이 주방까지 갈 길이 사라졌기 때문입니다.

하지만 답은 “조건부로 가능하다”입니다. Ollama 실행 프로그램과 모델 파일을 미리 내려받았다면, 대화할 때는 인터넷 없이 내 컴퓨터에서 LLM을 실행하고 Spring AI로 호출할 수 있습니다.

처음 설치 파일과 모델을 받을 때는 보통 인터넷이 필요합니다. 웹 검색, 외부 Tool, Cloud 모델을 사용해도 네트워크가 다시 필요하며, 로컬이라고 자동으로 빠르고 무료이며 안전한 것도 아닙니다.

이 글은 Spring AI 학습 흐름에 맞춰 2026년 3월 기준으로 배치했습니다. 당시 안정 버전 흐름은 1.1.2였습니다. 이후 달라진 API와 설정은 2026년 8월 Spring AI 2.0.0과 Ollama 공식 문서를 기준으로 다시 확인했습니다.

모델과 모델을 실행하는 프로그램은 다르다

1편에서는 AI 모델을 이미 훈련된 요리사에 비유했습니다. 외부 AI API는 멀리 있는 주방에 주문을 보내는 방식입니다. 로컬 LLM은 작은 주방을 식당 건물 안에 직접 두는 방식입니다.

LLM(Large Language Model, 대규모 언어 모델)은 학습된 가중치와 구조를 가진 모델 자체입니다. Local LLM(Local Large Language Model, 로컬 대규모 언어 모델)은 그 모델을 외부 회사의 API가 아니라 사용자의 PC나 자체 서버에서 실행한다는 뜻입니다.

Ollama는 LLM 자체가 아닙니다. 모델을 보관하고 메모리에 올려 CPU나 GPU로 추론하며, 로컬 HTTP API를 여는 실행 환경입니다. 식당 비유에서는 로컬 주방 관리자에 가깝습니다.

Spring AI도 모델 가중치를 직접 계산하지 않습니다. Spring Boot 애플리케이션이 OllamaChatModel이나 ChatClient를 통해 Ollama의 HTTP API를 호출하도록 연결합니다. Spring Boot는 주문 창구이고, 실제 요리는 Ollama가 실행하는 모델이 맡습니다.

처음 준비할 때와 대화할 때를 나눠 본다

오프라인 대화 전에는 Ollama와 모델 파일을 로컬 디스크에 준비해야 합니다. 모델은 수 GB에서 수십 GB 이상이 될 수도 있어 디스크 공간도 필요합니다.

준비가 끝나면 질문과 응답은 내 컴퓨터 안에서 오갈 수 있습니다. 반대로 Ollama Cloud 모델이나 외부 검색 API를 선택하면 같은 Ollama 인터페이스를 쓰더라도 네트워크 경계는 달라집니다.

모델을 처음 받을 때는 인터넷이 필요할 수 있습니다. 모델이 준비된 뒤의 로컬 대화는 컴퓨터 안에서 처리됩니다. Cloud 모델이나 외부 도구를 선택하면 다시 네트워크 통신이 필요합니다.

한 질문은 다음 길을 지나갑니다.

  1. 사용자가 브라우저에서 질문합니다.
  2. Spring Boot Controller가 질문을 받습니다.
  3. ChatClient가 Prompt(모델에 보낼 질문과 지시)를 만듭니다.
  4. Spring AI의 Ollama 연동 구현체가 로컬 Ollama API에 요청합니다.
  5. Ollama가 디스크의 모델 파일을 메모리로 불러옵니다.
  6. CPU 또는 GPU가 다음 Token(토큰)을 계산합니다.
  7. 생성된 Token이 Ollama와 Spring Boot를 거쳐 사용자에게 돌아옵니다.

Ollama의 현재 기본 로컬 주소는 http://localhost:11434입니다. 원래 API의 기본 경로는 http://localhost:11434/api이고, Spring AI는 base URL을 기준으로 필요한 요청을 만듭니다.

GPU가 없어도 일부 모델은 CPU로 실행할 수 있지만 일반적으로 훨씬 느릴 수 있습니다. 실제 가능성과 속도는 모델 크기, 양자화, RAM, VRAM, Context Length(문맥 길이)에 따라 달라집니다.

2026년 8월 기준 최소 연결

이 글은 완전한 설치 강좌가 아니라 연결 구조를 이해하기 위한 최소 예제입니다. 아래 명령과 코드는 직접 실행 결과를 보여주는 것이 아니며, Spring AI 2.0.0 기준입니다.

Ollama와 모델 준비

Ollama를 설치한 뒤 자신의 하드웨어와 작업에 맞는 모델을 고릅니다. 특정 모델을 최신이나 최고라고 가정하지 않고 자리표시자를 사용하겠습니다.

ollama pull <model-name>
ollama run <model-name>

오프라인 환경으로 옮길 때는 설치 파일뿐 아니라 승인된 모델 파일과 필요한 패키지도 준비해야 합니다.

Spring AI 의존성과 설정

Spring AI 2.0.0의 Ollama starter 이름은 spring-ai-starter-model-ollama입니다. 프로젝트에서는 Spring AI BOM으로 버전을 맞추고, Spring Boot 4.0.x 또는 4.1.x 호환성을 확인해야 합니다.

<dependency>
  <groupId>org.springframework.ai</groupId>
  <artifactId>spring-ai-starter-model-ollama</artifactId>
</dependency>
spring:
  ai:
    ollama:
      base-url: http://localhost:11434
      chat:
        model: <model-name>

2.0.0에서 spring.ai.ollama.chat.enabled는 제거된 설정입니다. 여러 ChatModel 제공자를 함께 쓸 때는 spring.ai.model.chat 설정으로 활성 제공자를 구분합니다. Temperature 같은 생성 옵션은 필요할 때만 spring.ai.ollama.chat 아래에 명시합니다.

Spring AI 1.1.x는 Spring Boot 3.4.x·3.5.x를, 현재 2.0.x는 4.0.x·4.1.x를 지원합니다. 옛 의존성과 현재 설정을 섞으면 안 됩니다.

동기와 스트리밍 Controller

아래 예제는 ChatClient.Builder를 주입받습니다. 인증, 입력 검증, timeout, 예외 처리, 관측성은 흐름을 보여주기 위해 생략했습니다.

@RestController
class LocalChatController {
    private final ChatClient chatClient;

    LocalChatController(ChatClient.Builder builder) {
        this.chatClient = builder.build();
    }

    @GetMapping("/ai")
    String ask(@RequestParam String question) {
        return chatClient.prompt()
                .user(question)
                .call()
                .content();
    }

    @GetMapping(value = "/ai/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    Flux<String> stream(@RequestParam String question) {
        return chatClient.prompt()
                .user(question)
                .stream()
                .content();
    }
}

call()은 답 전체가 완성된 뒤 문자열을 돌려줍니다. stream()은 생성된 조각을 Flux<String>으로 전달합니다. MediaType.TEXT_EVENT_STREAM_VALUE는 SSE(Server-Sent Events, 서버 전송 이벤트) 형식으로 서버가 브라우저에 내용을 차례대로 보내겠다는 뜻입니다.

브라우저가 닫히면 WebFlux 구독 취소가 위쪽으로 전달될 수 있습니다. 실제 모델 계산까지 중단되는지는 실행 환경에서 확인하고, 취소와 timeout을 기록해야 합니다.

스트리밍은 답을 더 빨리 만드는 기술이 아니다

동기 응답은 요리가 모두 완성될 때까지 기다렸다가 한 번에 받는 방식입니다. 스트리밍은 완성되는 일부를 순서대로 먼저 받는 방식입니다.

스트리밍은 첫 글자를 일찍 보여주지만 총 계산량을 자동으로 줄이지 않습니다. 전체 완료 시간도 반드시 짧아지지 않으며, 기다리는 체감을 바꾸는 방법에 가깝습니다.

성능을 확인할 때는 TTFT(Time To First Token, 첫 토큰 도착 시간), TPS(Tokens Per Second, 초당 생성 토큰 수), 전체 응답 완료 시간, 모델 로딩 시간을 따로 봐야 합니다. 이 글에서는 측정하지 않은 수치를 만들지 않습니다.

모델은 왜 메모리를 많이 사용할까?

모델이 답을 만들려면 모델 가중치, 입력 Context, 생성 중간 상태, 실행 버퍼가 메모리에 있어야 합니다. 가중치만 놓고 본 아주 거친 하한은 다음처럼 생각할 수 있습니다.

필요한 메모리의 대략적인 하한 ≈ 파라미터 수 × 파라미터당 비트 수 ÷ 8

이 계산은 가중치만 센 값입니다. 실제 실행에는 Context와 중간 계산을 위한 메모리가 더 필요합니다. Context Length가 커질수록 한 번에 참고할 내용과 함께 필요한 메모리도 늘 수 있습니다.

Quantization(양자화)은 모델 숫자의 정밀도를 낮춰 저장 공간과 메모리 사용량을 줄이는 방법입니다. 더 작은 장치에서 모델을 실행할 가능성을 열지만 품질이 떨어질 수 있습니다. 모델과 작업에 따라 차이가 크고, 속도도 항상 빨라지는 것은 아닙니다.

로컬 실행의 비용과 실패 지점

로컬 모델은 요청당 외부 API 요금이 없을 수 있지만 전체 비용은 0원이 아닙니다. 디스크, 메모리, 전기, 장비와 운영 비용을 직접 부담합니다.

작은 모델은 복잡한 추론이나 한국어 품질이 부족할 수 있고, PC가 꺼지면 서비스도 멈춥니다. 동시 요청이 Ollama의 대기열 한도를 넘으면 현재 공식 동작 기준 503 과부하 응답이 발생할 수 있습니다.

Ollama는 응답 뒤에도 모델을 메모리에 유지할 수 있어 Cold Start(초기 기동)와 이후 지연이 다릅니다. 2026년 8월 공식 FAQ의 기본 유지 시간은 5분이며 설정으로 바꿀 수 있습니다.

실패는 여러 경계에서 생깁니다.

  • Ollama 프로세스가 꺼져 있거나 모델 이름이 틀릴 수 있습니다.
  • 모델 파일이 없거나 디스크·RAM·VRAM이 부족할 수 있습니다.
  • 모델 로딩이 늦거나 요청이 timeout을 넘을 수 있습니다.
  • 동시 요청이 몰리거나 사용자가 스트리밍 중 연결을 끊을 수 있습니다.
  • Spring Boot는 살아 있지만 Ollama만 응답하지 않을 수 있습니다.
  • 모델은 정상 실행돼도 그럴듯한 오답을 만들 수 있습니다.

애플리케이션과 Ollama의 준비 상태를 나눠 확인해야 합니다. timeout, 동시 요청 제한, fallback, 모델 사전 로딩, 취소 전파, 메트릭과 로그를 요구사항에 맞게 설계합니다.

로컬이면 개인정보가 자동으로 안전할까?

Ollama 공식 FAQ는 로컬 실행 중에는 Ollama 측이 사용자의 Prompt와 데이터를 보지 않는다고 설명합니다. 데이터가 외부 모델 API로 나가지 않도록 설계할 수 있다는 것은 분명한 장점입니다.

하지만 애플리케이션 로그에 Prompt가 남거나 운영체제 계정·악성 프로그램이 파일에 접근할 수 있습니다. 백업·모니터링 프로그램이 데이터를 외부로 보낼 가능성도 있습니다.

Ollama는 기본적으로 127.0.0.1:11434에 바인딩됩니다. 인증과 접근 통제 없이 이 포트를 외부 네트워크에 공개해서는 안 됩니다. Cloud 모델이나 외부 Tool을 쓰면 네트워크 통신과 해당 서비스의 데이터 정책을 다시 확인해야 합니다.

금융 내부망이라면 모델·패키지 반입 절차, 모델 파일 검증, 접근 통제, Prompt 로그 마스킹, 감사로그, 패치와 버전 관리도 필요합니다. 이는 가능한 설계 조건을 정리한 것이며, 실제 금융 내부망에서 Spring AI와 Ollama를 운영했다는 뜻은 아닙니다.

로컬·클라우드·하이브리드 중 무엇을 고를까?

방식 장점 비용과 한계
로컬 모델 오프라인 가능, 데이터 통제, 모델 실행 제어 하드웨어, 품질, 운영, 동시성 한계
외부 모델 API 높은 품질의 모델 선택, 탄력적 확장, 관리 부담 감소 호출 비용, 네트워크 의존, 데이터 정책
하이브리드 민감하거나 단순한 작업은 로컬, 복잡한 작업은 외부 모델로 분리 가능 라우팅 정책과 운영 복잡성 증가

하이브리드도 무조건 정답은 아닙니다. 데이터 민감도, 필요한 품질, 지연시간, 동시 사용자 수, 하드웨어, 비용, 인터넷 연결 가능 여부, 운영 인력을 함께 보고 선택해야 합니다.

한 단계 더 깊게: 로컬 추론을 읽는 단어

  • Model Weights(모델 가중치): 모델이 학습 과정에서 얻은 숫자들의 집합입니다.
  • Inference(추론): 학습된 모델로 입력에 대한 답을 계산하는 과정입니다.
  • Token(토큰): 모델이 입력과 출력을 처리하는 기본 단위입니다.
  • Context Length(문맥 길이): 한 번에 참고할 수 있는 Token 범위입니다.
  • Cold Start(초기 기동): 모델을 처음 메모리에 올려 요청을 처리할 준비를 하는 시간입니다.
  • Quantization(양자화): 숫자 정밀도를 낮춰 모델 크기와 메모리 사용량을 줄이는 방법입니다.
  • TTFT(Time To First Token): 요청 뒤 첫 Token이 도착할 때까지의 시간입니다.
  • TPS(Tokens Per Second): 준비가 끝난 뒤 1초에 생성하는 Token 수입니다.

인터넷 없이 가능하지만 준비와 대가가 필요하다

  1. 모델이 미리 설치돼 있다면 인터넷 없이도 Ollama와 Spring AI로 로컬 챗봇을 실행할 수 있습니다.
  2. Spring Boot는 Ollama API를 호출하고, 실제 모델 추론은 Ollama가 CPU·GPU를 이용해 수행합니다.
  3. 로컬 실행은 개인정보 통제 장점이 있지만 하드웨어·품질·동시성·운영 비용을 직접 감당해야 합니다.

같은 컴퓨터와 같은 모델을 사용했는데도 왜 답이 매번 달라질까요? 다음 편에서는 Prompt, Token, Temperature가 답변을 어떻게 바꾸는지 살펴봅니다.

다음 글 — AI는 같은 질문에도 왜 다른 답을 할까?

공식 문서