2026-04-15

Node.js 에서 DeepSeek V4 사용하기: TypeScript / 스트리밍 / tool use

DeepSeek V4 는 OpenAI 호환이라 Node.js 에서는 openai 패키지를 그대로 사용할 수 있습니다. 이 튜토리얼은 TypeScript 로 설치 → 첫 호출 → 스트리밍 → tool use → Next.js route handler 까지 프로덕션에서 바로 쓸 수 있는 구성을 정리합니다.

1. 설치와 환경 설정

openai 공식 npm 패키지가 baseURL 교체로 DeepSeek 에 연결됩니다. API 키는 .env 에 두고 Git 커밋 금지, 절대 브라우저로 노출 금지.

npm install openai zod
echo "DEEPSEEK_API_KEY=sk-..." >> .env.local

2. TypeScript 로 첫 호출

클라이언트 인스턴스는 한 번만 만들고 재사용하세요. baseURL 을 DeepSeek 엔드포인트로, model 은 deepseek-chat.

import OpenAI from "openai";

const client = new OpenAI({
  apiKey: process.env.DEEPSEEK_API_KEY,
  baseURL: "https://api.deepseek.com/v1",
});

export async function ask(question: string): Promise<string> {
  const res = await client.chat.completions.create({
    model: "deepseek-chat",
    messages: [
      { role: "system", content: "You are a concise technical assistant." },
      { role: "user", content: question },
    ],
    temperature: 0.2,
  });
  return res.choices[0].message.content ?? "";
}

3. Next.js route handler 스트리밍

브라우저에 tokens 를 실시간으로 보여주려면 route handler 안에서 SDK 의 async iterable 을 ReadableStream 으로 바꿔 그대로 Response 에 넣는 방식이 가장 간단합니다.

// app/api/chat/route.ts
import OpenAI from "openai";

export const runtime = "nodejs";

const client = new OpenAI({
  apiKey: process.env.DEEPSEEK_API_KEY,
  baseURL: "https://api.deepseek.com/v1",
});

export async function POST(req: Request) {
  const { messages } = await req.json();

  const stream = await client.chat.completions.create({
    model: "deepseek-chat",
    messages,
    stream: true,
  });

  const encoder = new TextEncoder();
  const body = new ReadableStream({
    async start(controller) {
      for await (const chunk of stream) {
        const delta = chunk.choices[0]?.delta?.content ?? "";
        if (delta) controller.enqueue(encoder.encode(delta));
      }
      controller.close();
    },
  });

  return new Response(body, {
    headers: { "Content-Type": "text/plain; charset=utf-8" },
  });
}

4. Zod 로 function calling 인자 검증

V4 의 tool use 는 실제 에이전트에 올릴 만큼 안정적이지만, 모델이 가끔 인자에 오류를 낼 수 있으니 Zod 로 검증 후 실행하세요.

import { z } from "zod";

const GetWeatherArgs = z.object({ city: z.string().min(1) });

const tools = [{
  type: "function" as const,
  function: {
    name: "get_weather",
    description: "Get the current weather for a city",
    parameters: {
      type: "object",
      properties: { city: { type: "string" } },
      required: ["city"],
    },
  },
}];

const res = await client.chat.completions.create({
  model: "deepseek-chat",
  messages: [{ role: "user", content: "Weather in Tokyo?" }],
  tools,
});

const call = res.choices[0].message.tool_calls?.[0];
if (call?.function.name === "get_weather") {
  const args = GetWeatherArgs.parse(JSON.parse(call.function.arguments));
  // … execute tool and feed result back as a { role: "tool" } message …
}

5. Node.js 비용 관리

입력 / 출력 tokens 가 별도 과금이고 출력이 약 2 배. system prompt 와 히스토리가 모두 입력 tokens 에 포함되므로, 제어 없이 두면 비용이 빠르게 증가합니다.

실전 체크리스트: maxTokens 상한 설정, 오래된 히스토리 요약, RAG 청크 캐싱, 대량 트래픽이라면 /pricing 할인 키.

FAQ

브라우저에서 직접 호출할 수 있나요?

안 됩니다. API 키 노출 위험이 있으므로 반드시 Node.js 서버·서버리스·Next.js route handler 를 거쳐 호출하세요.

Bun / Deno / Cloudflare Workers 에서 동작?

작동합니다. openai SDK 는 ESM 빌드를 제공하고 fetch 만 있으면 됩니다. 장기 스트리밍은 Node.js 가 안정적입니다.

LangChain / Vercel AI SDK 와 조합 가능?

OpenAI 호환 baseURL 을 지원하는 라이브러리는 그대로 DeepSeek 에 붙입니다.

저렴한 API 키는?

/pricing 에 공식 스펙 할인 키가 있습니다.

Node.js / Next.js 제품이라면 DeepSeek V4 이전은 baseURL 과 model 두 줄만 바꾸면 끝. 기존 프롬프트·툴·재시도 로직을 그대로 유지하면서 tokens 비용을 한 자릿수 배로 절감할 수 있습니다.