첫째: Neon 개요, 시작하기, Neon 개발 모범 사례 등은 상위 neon 스킬을 사용하세요.
neon 스킬이 설치되지 않은 경우, https://neon.com/docs/ai/skills/neon/SKILL.md 에서 가져오거나 다음 명령으로 설치하세요:
npx skills add neondatabase/agent-skills --skill neon
Neon Functions
이 기능은 퍼블릭 베타이며, 현재 us-east-2 및 eu-central-1 리전에서 사용할 수 있습니다.
Neon Functions는 Neon 브랜치에 배포되는 장기 실행 Node.js HTTP 핸들러입니다. 각 함수는 공개 HTTPS URL을 가지며, 데이터베이스와 동일한 리전에서 실행되고, 브랜치에 Postgres가 있으면 DATABASE_URL이 자동으로 주입됩니다. 이미 사용 중인 동일한 Neon CLI, neon.ts, API로 배포하고 관리할 수 있습니다.
이 스킬을 사용하여 사용자가 데이터베이스 옆에서 함수를 정의, 로컬 실행, 배포 및 관리하도록 도와주세요. 배포된 함수의 호출 URL, 작동하는 로컬 neon dev 루프, 또는 공식 Neon 문서의 정확한 답변을 제공하세요.
사용 시기
Neon Functions는 요청/응답 핸들러로, 살아있는 상태를 유지하고 데이터에 가까이 있는 것이 이점인 워크로드에 적합합니다:
- 람다 스타일 제한을 초과하는 장기 실행 요청/응답 흐름. 요청당 여러 LLM 호출과 도구 호출을 하는 에이전트나 이미지/비디오 생성은 일반적인 서버리스 함수의 ~10-60초 실행 제한과 짧은 스트리밍 창을 자주 초과합니다. Neon Functions는 장기 실행 방식으로, 핸들러는 15분 이내에 응답을 시작하기만 하면 되고, 열린 스트림은 데이터가 계속 흐르는 한 유지됩니다. 이는 실제 에이전트 워크로드에 충분한 여유를 제공합니다.
- Redis를 추가하지 않고 상태 저장 스트리밍. 함수가 요청 전반에 걸쳐 살아 있기 때문에 SSE 엔드포인트나 WebSocket 서버를 호스팅하고 연결을 인프로세스로 유지할 수 있습니다. 스트림 일관성을 유지하기 위해 외부 상태 저장소(Redis 등)가 필요하지 않습니다. 모듈 범위 상태(
pg풀, 인메모리 카운터)는 동일한 격리 환경에서 요청 간에 유지됩니다. - Postgres 옆에 있어야 하는 컴퓨팅. 함수는 브랜치 데이터베이스와 동일한 리전에서 실행되므로, 모든 쿼리에 대해 크로스 리전 왕복이 없습니다.
DATABASE_URL이 자동으로 주입됩니다. - 데이터와 함께 브랜칭되는 백엔드. 각 브랜치는 고유한 URL에서 자체 함수 버전을 실행하며, 자체 격리된 데이터베이스(및 스토리지, 게이트웨이) 상태를 가집니다. 프리뷰 배포, CI, 개발 환경은 각각 독립된 백엔드를 가지며, 하위 브랜치에 배포해도 상위 브랜치에 영향을 주지 않습니다.
- 웹훅, 봇, 사후 응답 작업. 여러 DB 쓰기로 분산되는 웹훅 핸들러, Discord/WebSocket 봇,
waitUntil을 통한 파이어 앤 포겟 방식의 후속 작업(분석, 감사 로그) 모두 적합합니다. - 반복 HTTP 작업. Function Trigger가 크론(
type: "schedule")으로 함수에 POST 요청을 보냅니다. 동일한fetch핸들러, 동일한 15분 첫 바이트 시간 제한이 적용됩니다. Function Triggers를 참조하세요.
워크로드가 순수 정적 사이트이거나 현재 지원 리전(us-east-2, eu-central-1) 외부에서 실행해야 하는 경우, 이 도구는 아직 적합하지 않습니다(시간 제한 및 런타임 제한 및 가용성 참조).
기능
- 장기 실행 및 서버리스 — WebSocket 서버(WebSocket 서버 참조), SSE 엔드포인트(Server-Sent Events (SSE) 참조), 긴 에이전트 HTTP 스트림, API에 적합합니다. 유휴 상태에서는 제로 스케일링됩니다.
- 웹 표준 핸들러 — 함수는
Response를 반환하는fetch(request)메서드를 가진 기본 내보내기(Workers/WinterTC 호환)입니다. Hono 앱은 정확히 이 형태를 내보내므로,export default app가 바로 작동합니다. Node.js 24에서 실행되므로 모든 Node API를 사용할 수 있습니다. - 데이터베이스에 가까움 — 브랜치 리전에서 실행되며, 브랜치에 Postgres가 있으면
DATABASE_URL이 자동으로 주입됩니다. - 브랜칭 가능 — 각 브랜치는 자체 격리된 상태에 대해 고유한 URL에서 자체 함수 버전을 실행합니다.
- 동일한 CLI/API —
neon,neon.ts또는 Neon API를 통해 배포 및 관리합니다. - Function Triggers — Neon이 크론에 따라 함수에 POST 요청을 보냅니다. Function Triggers를 참조하세요.
가용성
무엇을 설정하기 전에 이 전제 조건을 확인하세요: Neon Functions는 퍼블릭 베타이며 현재 us-east-2 및 eu-central-1에서 사용할 수 있습니다. 사용자의 Neon 프로젝트가 이 리전 중 하나에 있는지 확인하세요. 퍼블릭 베타 기간 동안 함수 사용에는 요금이 부과되지 않습니다.
아키텍처: Functions의 위치
Neon(Functions 포함)은 백엔드 프리미티브이며, 풀스택 앱 호스팅이 아닙니다. 앱을 Vercel(또는 Netlify, 기타 프론트엔드/앱 호스트)에 호스팅하고, Functions는 데이터 옆에 위치하는 장기 실행 상태 저장 백엔드 조각입니다. 두 가지 방식으로 해당 플랫폼과 구성됩니다:
- 풀스택 앱에 Function 추가. Vercel(또는 Netlify)의 Next.js / TanStack Start 앱이 UI, 인증(예: Neon Auth)을 소유하고 Lakebase Postgres 및 Object Storage와 직접 통신합니다. 워크로드가 호스트의 짧은 서버리스 제한을 초과할 때(WebSocket 또는 SSE 서버, 시간 초과될 장기 실행 에이전트) 해당 부분만 Neon Function으로 이동합니다.(클라이언트-직접 패턴은 Functions를 에이전트 백엔드로 사용 참조)
- 전체 백엔드 제어판을 Functions에서 실행. 특히 프론트엔드가 클라이언트 전용인 경우(TanStack Router, React Router의 클라이언트 모드 등 Vercel 또는 Netlify에 호스팅된 SPA) 클라이언트가 Functions를 직접 호출합니다. REST API 및 요청/응답 에이전트를 구축하고, MCP 서버를 호스팅하며, Postgres 및 Object Storage와 가까이 있어야 하는 모든 상태 저장 작업을 실행합니다.
어느 쪽이든, Function을 독립형 REST API처럼 보호하세요: 핸들러 상단에서 JWT 또는 API 키를 검증합니다(Functions를 에이전트 백엔드로 사용의 WARNING 참조). Function은 단순히 백엔드이므로, 호스트와 Neon 간에 조각을 이동할 수 있습니다. 더 많은 런타임이 필요할 때 에이전트나 상태 저장 WebSocket 서버를 Function으로 옮기고, 필요하면 다시 되돌릴 수 있습니다.
설정
Functions는 neon.ts에서 선언됩니다(브랜치 우선 워크플로 및 neon.ts 기본 사항은 neon 스킬 참조). @neon/config를 추가하고 preview.functions 아래에 slug를 키로 하여 함수를 선언하세요:
// neon.ts
import { defineConfig } from "@neon/config/v1";
export default defineConfig({
preview: {
functions: {
todos: {
// slug: ^[a-z0-9]{1,20}$ — 소문자/숫자, 하이픈 없음
name: "todo api", // 표시 레이블만
source: "src/index.ts", // 항목 파일, neon.ts 기준 상대 경로
},
},
},
});
slug는 함수의 영구 식별자이며(호출 URL 및 CLI 명령에 표시됨) 첫 배포 후에는 변경할 수 없습니다. 사람이 읽을 수 있는 레이블에는 name을 사용하세요.
최소 함수 예제 — 주입된 DATABASE_URL을 통해 브랜치의 Postgres를 쿼리하는 Hono 앱:
// src/index.ts
import { Hono } from "hono";
import { drizzle } from "drizzle-orm/node-postgres";
import { Pool } from "pg";
import { parseEnv } from "@neon/env";
import { attachDatabasePool } from "@neon/functions";
import config from "../neon";
import { todos } from "./db/schema";
const env = parseEnv(config);
const pool = new Pool({ connectionString: env.postgres.databaseUrl, max: 5 });
attachDatabasePool(pool);
const db = drizzle(pool);
const app = new Hono();
app.get("/", (c) => c.text("Neon + Hono + Drizzle"));
app.post("/todos", async (c) => {
const { text } = await c.req.json<{ text: string }>();
const [row] = await db.insert(todos).values({ text }).returning();
return c.json(row, 201);
});
app.get("/todos", async (c) => c.json(await db.select().from(todos)));
export default app;
pg 풀은 모듈 범위에서 생성하고(동일한 격리 환경의 요청 간에 재사용됨) max는 작게 유지하세요(예: 5). 각 격리 환경은 자체 풀을 유지합니다. attachDatabasePool(pool)을 호출하여 유휴 연결 해제가 uncaughtException이 되지 않도록 하세요 — Postgres 연결 참조.
parseEnv(config)는 설정이 암시하는 모든 변수가 필요합니다. 풀링된 URL을 통해서만 Postgres와 통신하는 함수는 해당 키로 범위를 좁힐 수 있습니다 — parseEnv는 요청한 것만 검증하고 반환합니다(키는 neon.ts에서 자동 완성됨):
const { postgres } = parseEnv(config, ["DATABASE_URL"]); // 비풀링 URL, 인증 등은 제외
const pool = new Pool({ connectionString: postgres.databaseUrl, max: 5 });
attachDatabasePool(pool);
로컬 개발 및 배포
neon dev # neon.ts의 모든 함수를 핫 리로드와 함께 제공; DATABASE_URL 및 기타 변수 주입
neon deploy --env <file> # neon.ts에서 전체 배포 선호; --env는 Function 환경 변수를 읽을 파일
.env 또는 .env.local을 preview.functions.*.env의 모든 키로 최신 상태로 유지하세요. neon env pull은 Neon 관리 변수만 작성합니다. Function 비밀을 해당 파일에 추가한 다음 --env로 전달하세요. neon deploy --env <file>은 매번 해당 파일을 process.env에 로드한 다음 값을 업로드합니다. 누락된 값은 undefined이며 defineConfig가 오류를 발생시킵니다. neon.ts에서 키를 생략하면 해당 키를 쓰지 않으려는 것입니다. 누락된 process.env 값을 빈 문자열로 강제 변환하지 마세요(그러면 ""가 업로드되고 라이브 키가 삭제됩니다). 파일에서 빈 할당(KEY=)도 ""입니다. TypeScript에서 어설션이 필요하면 process.env.X!를 사용하세요.
neon.ts를 적용하지 않고 단일 함수를 배포하려면: neon functions deploy <slug> --src src/index.ts(--src는 항목 파일 또는 index.ts, index.mjs, index.js가 포함된 디렉토리). 해당 명령의 --env는 KEY=VALUE(반복 가능)이며 파일 경로가 아닙니다. 대상 환경 업데이트에 사용하세요. neon functions get <slug>로 공개 URL을 검색하세요(invocation_url 필드, 형식: https://<branch_id>-<slug>.compute.<cell>.us-east-2.aws.neon.tech). neon functions list|get|delete로 관리하세요.
neon checkout이 새 브랜치를 _생성_하고 neon.ts가 있으면 정책이 자동으로 적용됩니다. 이 생성-적용은 --env를 로드하지 않습니다. Function env가 process.env를 읽으면 체크아웃 후 neon deploy --env <file>을 실행하세요(체크아웃이 이미 브랜치를 생성한 경우 --update-existing 추가). 기존 브랜치를 체크아웃하는 경우 재배포되지 않습니다. 명시적으로 neon deploy --env <file>을 실행하세요.
Neon Infrastructure as Code (neon.ts)
설정의 preview.functions 블록은 neon.ts의 일부입니다. 이는 Neon의 Infrastructure as Code 파일로, 하나의 TypeScript 파일이 각 함수(해당 source, 표시 name, env)를 다른 브랜치 서비스와 함께 선언하며 버전 관리됩니다(전체 참조는 neon 스킬 참조). 브랜치용 Terraform처럼 취급하세요:
neon config status # 브랜치의 라이브 구성 출력 (배포된 함수)
neon config plan # dry-run diff, apply가 변경할 내용 표시
neon config apply --env <file> # 선언된 함수를 번들 + 배포 (neon deploy는 별칭, Function env가 process.env를 읽을 때 --env 전달)
Functions는 브랜치 범위입니다: 각 브랜치는 고유한 URL에서 자체 배포를 실행합니다. neon.ts가 있으면 neon checkout이 브랜치를 _생성_할 때 정책을 적용합니다. 이 생성-적용은 --env를 로드하지 않습니다. Function env가 process.env를 읽으면 체크아웃 후 neon deploy --env <file>을 실행하세요. 기존 브랜치를 체크아웃하는 경우 재배포되지 않습니다. neon deploy --env <file>을 실행하여 변경 사항을 적용하세요.
브랜치별 배포 조정(예: runtime)은 branch 클로저에 slug별로 키가 지정되어 있어, 어떤 함수가 존재하는지 변경하지 않고 브랜치별로 다르게 설정할 수 있습니다:
export default defineConfig({
preview: {
functions: { todos: { name: "todo api", source: "src/index.ts" } },
},
branch: (branch) => ({
preview: { functions: { todos: { runtime: "nodejs24" } } },
}),
});
환경 변수
Neon은 런타임에 브랜치 범위의 연결 문자열과 서비스 URL을 주입합니다. 이를 선언하거나 배포 시 전달할 필요가 없습니다:
| 변수 | 참고 |
|---|---|
NEON_BRANCH | 브랜치 이름(예: main, preview/foo). 기본 브랜치를 포함한 모든 브랜치에 주입됩니다. |
DATABASE_URL | 풀링된 연결 문자열. 대부분의 쿼리에 사용합니다. 브랜치에 Postgres가 있는 경우에만 존재합니다. |
DATABASE_URL_UNPOOLED | 직접 연결. 마이그레이션, LISTEN/NOTIFY, 다중 왕복 트랜잭션에 사용합니다. |
NEON_AUTH_BASE_URL | 브랜치에서 Neon Auth가 활성화된 경우 존재합니다. |
NEON_DATA_API_URL | 브랜치에서 Data API가 활성화된 경우 존재합니다. |
Object storage(AWS_*) 및 AI Gateway(NEON_AI_GATEWAY_*) 변수도 해당 서비스가 선언된 경우 주입됩니다 — neon-object-storage 및 neon-ai-gateway 스킬 참조.
neon env pull / neon-env run / neon dev는 로컬 개발 환경에도 NEON_BRANCH(및 연결 문자열)를 내보내므로 로컬 실행이 배포된 런타임을 미러링합니다.
자신의 비밀은 배포별로 관리됩니다. 권장 경로: neon.ts에 선언하고 neon deploy --env <file>을 실행하세요. <file>은 env pull이 이미 작성하는 gitignored 파일입니다(.env 파일이 있으면 .env, 없으면 .env.local). Env pull은 Neon 관리 변수만 작성합니다. Function 비밀을 해당 파일에 추가하세요. 선언된 모든 Function env 키가 있어야 합니다. neon.ts에서 키를 생략하면 해당 키를 쓰지 않으려는 것입니다. undefined는 키를 쓰려고 했지만 값이 누락되었음을 의미합니다(defineConfig가 오류를 발생시킴). 누락된 process.env 값을 빈 문자열로 강제 변환하지 마세요: 그러면 ""가 업로드되고 라이브 키가 삭제됩니다. 파일에서 빈 할당(KEY=)도 ""입니다. TypeScript에서 어설션이 필요하면 process.env.X!를 사용하세요.
런타임 및 Node.js 버전
각 함수는 기본적으로 Node.js 24에서 실행됩니다. 런타임은 브랜치별 구성에서 재정의할 수 있습니다(Neon Infrastructure as Code (neon.ts)의 runtime 키 참조). 유일한 지원 값은 nodejs24입니다. 함수가 사용자 정의 HTTP 서버를 실행하지 않고 기본 fetch 핸들러를 사용하는 한, Node.js 18과의 차이 없이 작동해야 합니다. 사용자가 레거시 Node.js 패턴(예: http.createServer)을 사용하는 경우 Node.js 24 호환성에 대한 경고를 제공하세요.
Postgres 연결
pg 풀은 모듈 범위에서 생성하고 함수가 시작될 때 연결 풀을 초기화해야 합니다(설정 참조). max는 작게 유지하세요 (예: 5) — 각 격리 환경은 자체 풀을 유지합니다. 항상 attachDatabasePool(pool)을 호출하여 유휴 연결 해제가 uncaughtException이 되지 않도록 하세요. 이는 pg 풀에서 연결이 자동으로 해제될 때(예: 장기 유휴 후) 프로세스가 충돌하지 않도록 방지합니다.
import { attachDatabasePool } from "@neon/functions";
// ... 풀 생성 ...
attachDatabasePool(pool);
시간 제한 및 런타임 제한
| 제한 | 값 | 참고 |
|---|---|---|
| 첫 바이트까지 시간 | 15분 | 핸들러는 수신 요청 후 15분 이내에 응답을 시작해야 합니다. |
| 요청 시간 제한 | 없음 (데이터가 흐르면) | 응답이 시작되면 연결은 열린 상태를 유지해야 합니다. 데이터가 흐르는 한 연결이 유지됩니다. |
| 응답 본문 크기 | 10 MB | 단일 응답 본문. 스트리밍 응답은 이 제한이 없습니다. |
| 요청 본문 크기 | 10 MB | 단일 요청 본문. |
| 헤더 크기 | 16 KB | 개별 헤더. |
| CPU/메모리 | 공유됨 | 구체적인 제한은 문서화되지 않았습니다. |
| 동시 연결 | 격리 환경당 1 | 각 격리 환경은 한 번에 하나의 요청만 처리합니다. (단, WebSocket 연결은 예외일 수 있습니다.) |
| 격리 환경 크기 | 1 vCPU, 512 MB RAM | |
| 최대 격리 환경 수 | 브랜치당 10 | |
| 지원 리전 | 2 (us-east-2, eu-central-1) |
가용성
현재 us-east-2 및 eu-central-1 리전에서만 사용할 수 있습니다. 기능은 퍼블릭 베타이며 요금이 부과되지 않습니다. 프로덕션 사용에는 적합하지 않을 수 있습니다. 더 많은 리전 가용성은 정식 출시 시 제공됩니다.
WebSocket 서버
함수는 WebSocket 서버를 호스팅할 수 있습니다. 인기 있는 라이브러리 ws와 호환됩니다. 주의: 최대 격리 환경 수와 동시 연결 제한이 적용되며, WebSocket의 긴 수명으로 인해 다른 요청 처리를 위해 새 격리 환경을 스핀업해야 할 수 있습니다.
import { Hono } from "hono";
import { upgradeWebSocket } from "hono/ws";
const app = new Hono();
app.get(
"/ws",
upgradeWebSocket((c) => ({
onMessage(event, ws) {
console.log("수신 메시지:", event.data);
ws.send("에코: " + event.data);
},
onClose: () => console.log("연결 종료됨"),
}))
);
export default app;
Server-Sent Events (SSE)
함수는 SSE 엔드포인트를 기본적으로 지원합니다. Response를 스트리밍 본문과 함께 반환하면 됩니다.
import { Hono } from "hono";
const app = new Hono();
app.get("/events", (c) => {
const stream = new ReadableStream({
start(controller) {
controller.enqueue("data: hello\n\n");
// 이벤트를 스트리밍하기 위해 타이머/인터벌 사용
const interval = setInterval(() => {
controller.enqueue(`data: ${new Date().toISOString()}\n\n`);
}, 1000);
// 연결이 닫힐 때 정리
c.event?.waitUntil(
new Promise((resolve) => {
// 클린업 로직
clearInterval(interval);
resolve();
})
);
},
});
return new Response(stream, {
headers: { "Content-Type": "text/event-stream" },
});
});
export default app;
이 코드는 기본 SSE 엔드포인트를 설정합니다. 실제 프로덕션 사용을 위해서는 적절한 연결 관리를 추가하세요.
Functions를 에이전트 백엔드로 사용 (Next.js 및 유사 프레임워크)
Next.js 앱 라우터를 Vercel에 배포하고 WebSocket을 통해 실시간 통신이 필요한 AI 에이전트를 호스팅하는 시나리오를 생각해보세요. Vercel 서버리스 함수는 WebSocket 연결에 적합하지 않지만, Neon Functions는 완벽하게 처리할 수 있습니다.
경고: Functions를 에이전트 백엔드로 사용할 때는 항상 인증을 추가하세요. Function은 공개 HTTPS URL을 가지므로, 프로덕션에서는 JWT, API 키, 또는 다른 인증 메커니즘으로 보호해야 합니다.
import { Hono } from "hono";
import { jwt } from "hono/jwt";
const app = new Hono();
// JWT 미들웨어로 함수 보호 (인증서 URL 예시)
app.use(
"/*",
jwt({
secret: process.env.JWT_SECRET!,
})
);
app.get("/agent", async (c) => {
// AI 에이전트 로직
const user = c.get("jwtPayload");
return c.json({ message: `Hello ${user.sub}` });
});
export default app;
Function Triggers
Function Trigger는 크론 일정에 따라 함수에 POST 요청을 보냅니다. type: "schedule"로 선언됩니다. 동일한 fetch 핸들러를 사용하며 동일한 15분 첫 바이트 시간 제한이 적용됩니다.
// neon.ts
export default defineConfig({
preview: {
functions: {
myFunction: {
name: "My Scheduled Function",
source: "src/index.ts",
triggers: {
schedule: "*/5 * * * *", // 5분마다
},
},
},
},
});
제한 사항
- 현재
us-east-2및eu-central-1리전만 지원합니다. - 각 격리 환경은 한 번에 하나의 요청만 처리합니다(WebSocket 예외 가능).
- 각 브랜치당 최대 10개의 격리 환경이 있습니다.
- 함수는 Node.js 24에서만 실행됩니다.
- 함수는 Neon 브랜치 내에서만 실행됩니다. 독립형 호스팅이 아닙니다.
- 베타 기능이므로 프로덕션 사용에는 적합하지 않을 수 있습니다.