Skip to main content

Python·JavaScript 연동: 내 코드에서 Ollama 사용하기

터미널에서 모델과 대화할 때만 해도 Ollama는 꽤 똑똑한 로컬 도구처럼 보입니다. 그런데 Python 스크립트, Node 서버, 사내 자동화 배치, 웹 서비스에 붙는 순간 분위기가 달라집니다. 이때부터 Ollama는 단순한 채팅 상대가 아니라 코드 안에서 호출하는 AI 엔진이 됩니다.

공식적으로는 Python 라이브러리와 JavaScript 라이브러리가 모두 제공되며, 두 라이브러리 모두 Ollama의 REST API 구조를 거의 그대로 따라갑니다. 그래서 한쪽을 익히면 다른 쪽으로 넘어가도 낯설지 않습니다.12

Python·JavaScript 연동: 내 코드에서 Ollama 사용하기

코드에 붙이기 전에 확인할 것

가장 먼저 확인할 것은 세 가지입니다.

  • Ollama가 실행 중인지

  • 사용할 모델이 이미 내려받아졌는지

  • 내 프로젝트가 localhost:11434에 접근할 수 있는지

터미널에서 ollama ls를 쳤을 때 모델 목록이 보이면 준비의 절반은 끝난 셈입니다. 나머지 절반은 "내 코드가 그 모델에게 말을 걸 수 있게 만드는 일"입니다.

예를 들어 작은 실험 스크립트라면 공식 라이브러리가 가장 편합니다. 반대로 기존 프레임워크가 HTTP 요청 중심으로 구성돼 있다면 requests나 fetch로 직접 호출하는 편이 더 자연스러울 수 있습니다. 중요한 것은 "무엇이 더 고급인가"가 아니라 내 프로젝트의 흐름에 무엇이 더 잘 맞는가입니다.

공식 라이브러리와 HTTP 직접 호출, 무엇이 다를까

둘 다 결국 같은 Ollama 서버를 향해 갑니다. 차이는 손잡이 모양입니다.

방식장점잘 맞는 상황
공식 라이브러리코드가 짧고 읽기 쉽다새 프로젝트, 빠른 프로토타입, 내부 도구
HTTP 직접 호출요청과 응답을 세밀하게 제어하기 쉽다기존 백엔드, 프록시 환경, 커스텀 네트워크 처리
둘의 혼합핵심 로직은 라이브러리, 외부 연동은 HTTP로 분리 가능서비스가 점점 커지는 경우

처음에는 라이브러리로 시작하고, 서비스가 커지면서 특정 구간만 HTTP로 내려오는 경우가 많습니다. 처음엔 편한 손잡이를 잡고, 나중에 필요한 곳만 더 세밀하게 다루면 됩니다.

Python에서 가장 빠르게 시작하기

Python 공식 라이브러리는 pip install ollama로 설치할 수 있고, chat(), generate(), Client, AsyncClient 같은 진입점을 제공합니다.1

pip install ollama

가장 단순한 예제는 다음과 같습니다.

from ollama import chat

response = chat(
    model='gemma3',
    messages=[
        {'role': 'user', 'content': '파이썬 데코레이터를 쉬운 말로 설명해줘.'}
    ]
)

print(response.message.content)

이 코드는 읽는 순간 무슨 뜻인지 바로 이해됩니다. 모델 이름을 고르고, 메시지를 보내고, 답을 꺼냅니다. 채팅형 인터페이스가 필요한 앱이라면 보통 chat()부터 시작하면 됩니다.

한 번성 요청이라면 generate()도 잘 맞습니다.

import ollama

result = ollama.generate(
    model='gemma3',
    prompt='회의록을 세 문장으로 요약해줘.'
)

print(result['response'])

감각적으로 보면 이렇습니다. chat()은 대화를 만들 때, generate()는 작업 지시를 던질 때 잘 맞습니다. 상담봇은 chat(), 문서 요약기는 generate() 쪽이 더 자연스럽습니다.

Python에서 대화 상태를 유지하는 법

많은 초보자가 여기서 한 번 헷갈립니다. "이전 질문을 모델이 기억하지 못하네요?" 하고 말입니다. 사실 Ollama가 까먹은 것이 아니라, 애플리케이션이 이전 대화를 다시 보내지 않았기 때문입니다. 대화 맥락은 messages 배열을 직접 관리하면서 유지합니다.

from ollama import chat

messages = [
    {'role': 'system', 'content': '너는 초보 개발자를 돕는 친절한 튜터야.'}
]

while True:
    user_input = input('You: ').strip()
    if not user_input:
        break

    messages.append({'role': 'user', 'content': user_input})
    response = chat(model='gemma3', messages=messages)

    answer = response.message.content
    print('Assistant:', answer)

    messages.append({'role': 'assistant', 'content': answer})

핵심은 단순합니다. 사용자의 질문을 넣고, 모델의 답도 다시 넣습니다. 그러면 다음 요청에서 그 대화가 문맥으로 살아납니다.

Python에서 스트리밍을 붙이면 체감이 달라진다

긴 답변을 한 번에 기다리는 것보다, 조금씩 흘러나오게 만드는 편이 사용자 경험이 훨씬 좋습니다. 공식 라이브러리에서는 stream=True만 붙이면 됩니다.1

from ollama import chat

stream = chat(
    model='gemma3',
    messages=[
        {'role': 'user', 'content': '재귀를 비유를 섞어서 설명해줘.'}
    ],
    stream=True,
)

full_text = ''
for chunk in stream:
    piece = chunk['message']['content']
    print(piece, end='', flush=True)
    full_text += piece

print()

여기서 좋은 습관이 하나 있습니다. 화면에 출력만 하지 말고 full_text처럼 문자열을 함께 누적해 두는 것입니다. 그래야 나중에 대화 기록 저장, 로그 분석, DB 적재, 후처리를 자연스럽게 이어 갈 수 있습니다.

Python에서 HTTP로 직접 호출하기

공식 라이브러리가 편하긴 하지만, 항상 정답은 아닙니다. 예를 들어 이미 requests 기반의 백엔드 유틸리티가 많거나, 프록시와 헤더를 세밀하게 통제해야 하거나, 디버깅을 위해 순수 HTTP 흐름을 눈으로 보고 싶다면 직접 호출하는 편이 낫습니다.

import requests

OLLAMA_URL = 'http://localhost:11434/api/chat'

def ask_ollama(messages, model='gemma3'):
    response = requests.post(
        OLLAMA_URL,
        json={
            'model': model,
            'messages': messages,
            'stream': False,
        },
        timeout=60,
    )
    response.raise_for_status()
    data = response.json()
    return data['message']['content']

reply = ask_ollama([
    {'role': 'system', 'content': '너는 문장을 간결하게 다듬는 편집자야.'},
    {'role': 'user', 'content': '이 문장을 더 자연스럽게 고쳐줘: 우리는 빠르게 성장하고 있는 팀입니다.'}
])

print(reply)

스트리밍도 가능합니다. 응답이 줄 단위 JSON으로 오기 때문에 iter_lines()로 읽으면 됩니다.

import json
import requests

response = requests.post(
    'http://localhost:11434/api/chat',
    json={
        'model': 'gemma3',
        'messages': [
            {'role': 'user', 'content': '파이썬 가상환경이 왜 필요한지 설명해줘.'}
        ]
    },
    stream=True,
    timeout=60,
)

for line in response.iter_lines():
    if line:
        chunk = json.loads(line)
        print(chunk.get('message', {}).get('content', ''), end='', flush=True)

이 방식의 장점은 언어와 프레임워크에 덜 묶인다는 점입니다. 나중에 같은 로직을 다른 언어로 옮겨도 사고방식이 거의 그대로 유지됩니다.

Python 비동기 연동은 웹 서버와 궁합이 좋다

FastAPI, Starlette, aiohttp처럼 비동기 기반 서버를 쓰고 있다면 AsyncClient가 잘 맞습니다. 공식 라이브러리는 동기 클라이언트와 거의 같은 형태로 비동기 버전도 제공합니다.1

import asyncio
from ollama import AsyncClient

async def main():
    client = AsyncClient()

    response = await client.chat(
        model='gemma3',
        messages=[
            {'role': 'user', 'content': '비동기 프로그래밍을 한 문단으로 설명해줘.'}
        ],
    )
    print(response.message.content)

asyncio.run(main())

스트리밍일 때는 async for를 씁니다.

import asyncio
from ollama import AsyncClient

async def main():
    client = AsyncClient()
    stream = await client.chat(
        model='gemma3',
        messages=[{'role': 'user', 'content': '짧은 자기소개 예시를 써줘.'}],
        stream=True,
    )

    async for part in stream:
        print(part['message']['content'], end='', flush=True)

asyncio.run(main())

비동기 연동의 장점은 더 멋져 보이는 데 있지 않습니다. 웹 서버가 다른 요청을 막지 않으면서 Ollama 응답을 기다릴 수 있다는 데 있습니다.

Python 프로젝트에서 자주 쓰는 운영 코드

공식 라이브러리에는 대화만 있는 것이 아닙니다. list(), show(), pull(), delete(), ps() 같은 모델 관리 함수도 함께 들어 있습니다.1

import ollama

models = ollama.list()
for model in models['models']:
    print(model['name'])

이 기능은 앱 시작 시 환경 점검용으로 꽤 유용합니다. 예를 들어 서버 부팅 때 필요한 모델이 없으면 경고를 띄우거나, 관리 스크립트에서 특정 모델이 이미 존재하는지 검사할 수 있습니다.

오류 처리도 챙겨 두는 편이 좋습니다.

import ollama

model = 'does-not-yet-exist'

try:
    ollama.chat(model=model, messages=[{'role': 'user', 'content': 'hello'}])
except ollama.ResponseError as e:
    print('오류:', e.error)
    if e.status_code == 404:
        print('모델이 없습니다. 먼저 pull이 필요합니다.')

실무에서는 이런 한 줄짜리 방어 코드가 장애를 꽤 많이 줄여 줍니다.

JavaScript·TypeScript에서는 스트리밍 감각이 특히 좋다

JavaScript 공식 라이브러리는 npm i ollama로 설치할 수 있습니다.2

npm i ollama

기본 채팅 예제는 아주 간단합니다.

import ollama from 'ollama'

const response = await ollama.chat({
  model: 'gemma3',
  messages: [
    { role: 'user', content: '자바스크립트의 클로저를 짧게 설명해줘.' },
  ],
})

console.log(response.message.content)

한 번성 생성도 비슷하게 갑니다.

import ollama from 'ollama'

const result = await ollama.generate({
  model: 'gemma3',
  prompt: '회의 초대 메일 초안을 작성해줘.',
})

console.log(result.response)

여기서도 원칙은 같습니다. 대화는 chat, 작업 지시는 generate입니다.

JavaScript 스트리밍은 채팅 UI의 핵심이다

JavaScript 쪽 스트리밍은 AsyncGenerator로 반환되며, for await...of로 읽습니다.2

import ollama from 'ollama'

const response = await ollama.chat({
  model: 'gemma3',
  messages: [
    { role: 'user', content: '로컬 LLM의 장점을 세 문단으로 설명해줘.' },
  ],
  stream: true,
})

let fullText = ''
for await (const part of response) {
  process.stdout.write(part.message.content)
  fullText += part.message.content
}

이 패턴은 터미널 도구뿐 아니라 웹 채팅에도 거의 그대로 들어갑니다. 화면에는 청크를 뿌리고, 내부에서는 전체 문자열을 누적한 뒤 대화 기록에 저장하면 됩니다.

공식 JavaScript 라이브러리는 abort()도 제공합니다. 길게 말하는 모델을 중간에 끊고 싶을 때 유용합니다.2

JavaScript에서 커스텀 호스트를 쓰는 법

Ollama가 같은 컴퓨터가 아니라 다른 서버나 컨테이너에서 돌고 있을 수도 있습니다. 이럴 때는 커스텀 클라이언트를 만들면 됩니다.2

import { Ollama } from 'ollama'

const client = new Ollama({
  host: 'http://127.0.0.1:11434',
})

const response = await client.chat({
  model: 'gemma3',
  messages: [
    { role: 'user', content: '원격 Ollama 서버 연결 테스트 문장을 써줘.' },
  ],
})

console.log(response.message.content)

브라우저에서 직접 쓰고 싶다면 ollama/browser 진입점을 사용할 수도 있습니다.2 다만 실제 서비스에서는 브라우저가 Ollama 서버를 바로 두드리기보다, 보통은 내 백엔드가 중간에서 요청을 받아 전달하는 구조가 더 안전합니다.

fetch()로 직접 호출하면 프레임워크 경계가 사라진다

공식 라이브러리가 아닌 fetch()를 쓰면 Node.js, 브라우저, 서버리스 함수 사이에서 사고방식을 통일하기 쉽습니다.

const response = await fetch('http://localhost:11434/api/chat', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    model: 'gemma3',
    messages: [
      { role: 'user', content: '제품 소개 문장을 한 줄로 써줘.' },
    ],
    stream: false,
  }),
})

if (!response.ok) {
  throw new Error(`HTTP ${response.status}`)
}

const data = await response.json()
console.log(data.message.content)

스트리밍은 조금 더 손이 가지만, 한 번 이해해 두면 프론트엔드와 백엔드 사이에서 스트림을 중계하는 작업이 쉬워집니다.

서비스에 붙일 때는 백엔드가 스트림을 중계해 주면 편하다

실제 업무에서는 브라우저가 Ollama를 직접 호출하기보다, Express나 Fastify 같은 서버가 먼저 요청을 받고 Ollama 스트림을 받아 다시 클라이언트로 흘려주는 방식이 자주 쓰입니다. 예를 들어 SSE 형태로 중계할 수 있습니다.

import express from 'express'
import ollama from 'ollama'

const app = express()
app.use(express.json())

app.post('/chat', async (req, res) => {
  const { messages, model = 'gemma3' } = req.body

  res.setHeader('Content-Type', 'text/event-stream')
  res.setHeader('Cache-Control', 'no-cache')
  res.setHeader('Connection', 'keep-alive')

  try {
    const stream = await ollama.chat({ model, messages, stream: true })

    for await (const chunk of stream) {
      res.write(`data: ${JSON.stringify({ content: chunk.message.content })}nn`)
    }

    res.write('data: [DONE]nn')
  } catch (err) {
    res.write(`data: ${JSON.stringify({ error: err.message })}nn`)
  } finally {
    res.end()
  }
})

app.listen(3000)

이 패턴이 좋은 이유는 세 가지입니다.

  • 프런트엔드는 단순히 내 서버만 호출하면 됩니다.

  • 모델 이름, 시스템 프롬프트, 접근 제어를 백엔드에서 통제할 수 있습니다.

  • 나중에 로깅, 캐싱, 권한 검사, 사용량 집계를 붙이기 쉽습니다.

자주 걸리는 함정

처음 연동할 때는 비슷한 곳에서 자꾸 발이 걸립니다.

  • 모델 이름 오타: 항상 ollama ls로 실제 이름을 확인하는 습관이 좋습니다.

  • 응답 필드 혼동: generate()는 response, chat()은 message.content를 봅니다.

  • 대화 기억 착각: 이전 대화를 자동으로 기억한다고 기대하면 안 됩니다. messages를 내가 관리해야 합니다.

  • 스트리밍 결과 저장 누락: 화면에는 잘 나오는데 저장 데이터는 비는 경우가 있습니다. 출력과 누적을 함께 해야 합니다.

  • Ollama 미실행 상태: 코드만 멀쩡하고 서버가 꺼져 있으면 아무 일도 일어나지 않습니다.

마무리

Ollama를 코드에서 호출하기 시작하면 관점이 바뀝니다. 이제 모델은 더 이상 터미널 안의 대화 상대가 아닙니다. 함수처럼 호출하고, 스트림처럼 흘리고, 서버 뒤에서 감싸고, 서비스 로직 안으로 넣을 수 있는 실행 엔진이 됩니다.

Python에서는 빠르게 프로토타입을 만들고 자동화를 붙이기 좋고, JavaScript에서는 웹 서버와 스트리밍 UI까지 한 언어로 이어 붙이기 좋습니다. 그리고 공식 라이브러리를 쓰든 HTTP 요청을 직접 쓰든, 결국 중요한 것은 같습니다. 내 제품의 흐름 안에 모델 호출을 얼마나 자연스럽게 녹여 넣느냐입니다.

참고

1GitHub - ollama/ollama-python: Ollama Python library

2GitHub - ollama/ollama-js: Ollama JavaScript library

책 목록

Ollama 사용법: 내 PC에서 시작하는 로컬 LLM 실전 가이드

전체 목차 보기

이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.

틸노트는 사용자가 AI를 활용해 노트를 쉽게 작성하거나 편집할 수 있는 서비스입니다.

AI 스퀘어 단톡방에 함께해요

AI 활용과 틸노트 사용 팁을 나누는 오픈채팅방입니다. 입장 비밀번호는 tilnote입니다.

참여하기