테마 전환

Supabase Edge Functions 실전: Deno 런타임과 글로벌 엣지 배포

Easton editorial illustration: Deno edge-function capsule facing a Cloudflare Worker capsule across runtime, cold-start, and deployment gauges

모니터링 패널의 빨간 선을 보고 있었습니다. API 응답 시간이 2.3초까지 치솟았습니다.

사용자는 일본에 있고, 서버는 오하이오에 있었습니다. 왕복 네트워크만 해도 120ms가 걸리고, 여기에 데이터베이스 쿼리와 비즈니스 로직 처리까지 더해지니 느릴 수밖에 없었습니다.

“엣지 함수 한번 써볼까요?” 동료가 Slack으로 메시지를 던졌습니다.

처음에는 꽤 회의적이었습니다. 엣지 함수? 결국 코드를 다른 곳에서 실행하는 것 아닌가? 얼마나 차이가 나겠어? 그런데 측정해 보고 멈칫했습니다. 콜드 스타트 120ms, 요청 응답 47ms. 같은 로직을 오하이오에서 도쿄 엣지 노드로 옮겼을 뿐인데 거의 5배 빨라졌습니다.

Supabase Edge Functions는 Deno 런타임 기반으로 글로벌 엣지 노드에서 코드를 실행합니다. 이 글에서는 왜 이렇게 빠른지, Deno와 Node.js가 정확히 무엇이 다른지, Edge Function을 처음부터 어떻게 만들 수 있는지, 그리고 Cloudflare Workers와 비교해 어떻게 선택하면 좋을지 정리합니다.

1. 핵심 개념: Edge Functions란 무엇이고 왜 빠른가

간단히 말하면 Edge Functions는 코드를 “중앙 서버”에서 “엣지 노드”로 옮기는 방식입니다.

예전에는 Lambda 함수를 배포하면 특정 고정 리전의 서버에서 실행되었습니다. 예를 들어 미국 동부나 유럽 리전입니다. 도쿄에서 사용자가 요청을 보내면 데이터는 먼저 바다를 건너 미국 동부까지 갔다가, 처리가 끝난 뒤 다시 돌아와야 합니다. 물리적 거리가 지연 시간의 하한을 결정합니다. 빛의 속도는 무한하지 않으니까요.

Edge Functions는 관점을 바꿉니다. 코드는 ESZip이라는 압축된 형식으로 패키징되고, 전 세계 수십 개 엣지 노드에 자동으로 분산됩니다. 도쿄에서 사용자가 요청하면 코드는 도쿄의 엣지 노드에서 실행되고, 응답 시간에서 장거리 전송 구간이 바로 사라집니다.

ESZip이란 무엇인가요?

ESZip은 Deno 팀이 만든 패키징 형식입니다. 전통적인 JavaScript bundle과 달리 ESZip은 코드뿐 아니라 전체 모듈 의존성 그래프까지 함께 패키징합니다. 장점은 분명합니다. 시작할 때 네트워크로 의존성을 다시 가져올 필요가 없고, 모든 것이 하나의 파일 안에 있습니다. 콜드 스타트 과정이 “패키지 다운로드 + 파싱 + 실행”에서 “압축 해제 + 실행”으로 줄어듭니다.

Isolate 실행 모델

Supabase Edge Functions는 전통적인 컨테이너나 가상 머신이 아니라 V8 Isolate 위에서 실행됩니다. Isolate가 무엇일까요? 아주 가벼운 Worker 컨테이너라고 생각하면 됩니다. 하나의 프로세스에서 수십 개의 Isolate를 실행할 수 있고, 각 Isolate가 하나의 요청을 처리합니다. 컨테이너보다 가볍고 시작도 빠릅니다.

Supabase 공식 문서에 따르면 Isolate의 CPU 시간 상한은 400초입니다(소프트 제한 + 하드 제한). 길어 보이지만, 엣지 함수는 무거운 작업을 하기 위한 도구가 아닙니다. 가벼운 로직 처리, JWT 검증, 요청 전달 같은 일을 맡는 쪽에 가깝습니다. 무거운 계산은 중앙 서버로 보내는 편이 좋습니다.

가장 단순한 Edge Function 예제를 보겠습니다:

// 가장 단순한 Edge Function 예제
import "jsr:@supabase/functions-js/edge-runtime.d.ts"

Deno.serve(async (req) => {
  const { name } = await req.json()
  const data = { message: `Hello ${name}!` }
  return new Response(JSON.stringify(data), {
    headers: { "Content-Type": "application/json" }
  })
})

이 코드는 엣지 노드에서 실행됩니다. 사용자 요청이 들어오면 Deno.serve가 받아 처리하고 바로 반환합니다. 전체 과정이 사용자와 가장 가까운 노드에서 완료됩니다.

2. Deno 런타임: Node.js의 “보안 진화판”

솔직히 처음 Deno를 들었을 때 이런 의문이 들었습니다. Node.js가 이미 이렇게 성숙했는데 왜 새로운 런타임이 필요하지?

답은 의외로 단순합니다. Node.js는 너무 일찍 설계되었고, 많은 문제가 나중에 드러났기 때문입니다.

보안성의 차이

Node.js의 기본 전략은 “모든 것을 신뢰한다”에 가깝습니다. 코드가 파일 시스템을 읽고 싶나요? 됩니다. 네트워크에 접속하고 싶나요? 자유롭습니다. 시스템 명령을 실행하고 싶나요? 가능합니다. 이는 악성 의존성이 무엇이든 할 수 있다는 뜻입니다.

Deno는 반대로 설계되었습니다. 기본적으로 코드는 “샌드박스” 안에 갇혀 있습니다. 파일을 읽을 수 없고, 네트워크에 접속할 수 없고, 시스템 명령을 실행할 수도 없습니다. 권한이 필요하다면 명시적으로 선언해야 합니다. 예를 들면:

# 네트워크 권한 부여
deno run --allow-net server.ts

# 파일 읽기/쓰기 권한 부여
deno run --allow-read --allow-write file_ops.ts

# 모든 권한 부여(주의해서 사용)
deno run -A everything.ts

이런 설계는 엣지 환경에서 특히 중요합니다. 함수가 전 세계 수십 개 노드에서 실행되기 때문에, 한 번 공격당하면 영향 범위가 큽니다.

콜드 스타트의 차이

이건 실제 측정 데이터입니다. 같은 코드를 Deno Deploy와 AWS Lambda에서 각각 실행해 봤습니다:

런타임콜드 스타트 시간
Deno Deploy~120ms
AWS Lambda (Node.js)300-500ms

3배 차이입니다. 원인은 복잡하지만 핵심은 Deno에 Node.js의 역사적 부담이 없다는 점입니다. CommonJS 로딩 메커니즘, require 해석, node_modules 탐색 경로 같은 비용이 없습니다. ESZip 패키징과 네이티브 TypeScript 지원 덕분에 시작 비용이 크게 줄어듭니다.

모듈 시스템의 변화

Node.js는 CommonJS를 사용하고 require()로 모듈을 로드합니다. 그다음 npm을 설치하고, package.json을 만들고, node_modules 디렉터리를 둬야 합니다. 프로젝트가 커지면 이 디렉터리만 수백 MB가 되기도 합니다.

Deno는 ESM 표준을 직접 사용합니다. npm도, package.json도, node_modules도 없습니다. URL을 바로 import합니다:

// URL에서 직접 import
import { serve } from "https://deno.land/[email protected]/http/server.ts"

// 또는 JSR(Deno의 패키지 저장소) 사용
import { cors } from "jsr:@hono/hono/cors"

처음 실행할 때 Deno는 모듈을 내려받아 로컬에 캐시합니다. 이후 시작할 때는 캐시를 바로 읽기 때문에 다시 네트워크로 패키지를 가져올 필요가 없습니다.

TypeScript의 제로 설정

Node.js에서 TypeScript를 실행하려면 ts-node를 설치하거나 webpack/vite/esbuild를 설정해야 하고, tsconfig.json도 작성해야 합니다. Deno는 이런 과정이 필요 없습니다. TypeScript를 네이티브로 지원하므로 .ts 파일을 바로 실행할 수 있습니다:

deno run hello.ts

컴파일과 타입 검사도 런타임에서 처리됩니다. 개발 경험이 훨씬 깔끔합니다.

공급업체 종속 문제

이것도 당시 제가 꽤 걱정했던 부분입니다. Supabase Edge Functions를 쓰면 Supabase에 묶이는 것 아닐까요?

답은 꼭 그렇지는 않다는 것입니다. Deno 자체는 오픈소스 런타임이고, Supabase의 Edge Runtime도 오픈소스입니다(GitHub에 소스가 있습니다). 이론적으로는 자신의 서버에서 Edge Runtime을 실행할 수 있습니다. 물론 글로벌 엣지 네트워크 운영 비용은 직접 감당해야 합니다. 그래도 기술적으로는 “빠져나갈 수 없다”는 문제가 없습니다.

3. 실전 연습: 10분 만에 첫 Edge Function 만들기

이론만 볼 필요는 없습니다. 직접 해봐야 쓸 만한지 알 수 있습니다.

공식 문서를 따라 설치부터 배포까지 진행해 봤고, 전체 과정은 10분도 걸리지 않았습니다. 전체 흐름은 다음과 같습니다:

1단계: Supabase CLI 설치

npm install -g supabase

설치 후 버전을 확인할 수 있습니다:

supabase --version
# 출력 예: 1.200.0

2단계: 프로젝트 초기화

이미 Supabase 프로젝트가 있다면 프로젝트 디렉터리에서 초기화합니다:

supabase init

이 명령은 supabase 디렉터리를 만들고, 그 안에 config.toml 설정 파일을 생성합니다.

3단계: Edge Function 생성

supabase functions new hello-world

실행하면 supabase/functions/hello-world/ 아래에 index.ts 파일이 생성됩니다. 내용은 대략 다음과 같습니다:

import "jsr:@supabase/functions-js/edge-runtime.d.ts"

Deno.serve(async (req) => {
  const data = {
    message: "Hello from Edge Function!"
  }

  return new Response(JSON.stringify(data), {
    headers: { "Content-Type": "application/json" }
  })
})

원하는 로직으로 바꿀 수 있습니다. 예를 들어 name 파라미터를 받아 인사말을 반환하도록 만들 수 있습니다:

import "jsr:@supabase/functions-js/edge-runtime.d.ts"

Deno.serve(async (req) => {
  // POST 요청만 허용
  if (req.method !== "POST") {
    return new Response("Method not allowed", { status: 405 })
  }

  try {
    const body = await req.json()
    const name = body.name || "Stranger"

    return new Response(JSON.stringify({
      message: `Hey ${name}, welcome to the edge!`,
      timestamp: new Date().toISOString()
    }), {
      headers: { "Content-Type": "application/json" }
    })
  } catch (err) {
    return new Response(JSON.stringify({ error: "Invalid JSON" }), {
      status: 400,
      headers: { "Content-Type": "application/json" }
    })
  }
})

4단계: 로컬 테스트

Supabase CLI는 Edge Function을 로컬에서 실행할 수 있어 디버깅하기 편합니다:

supabase functions serve --no-verify-jwt

이 명령은 로컬 서비스를 시작하며 기본 포트는 54321입니다. curl로 테스트할 수 있습니다:

curl -X POST http://localhost:54321/functions/v1/hello-world \
  -H "Content-Type: application/json" \
  -d '{"name":"Easton"}'

# 반환:
# {"message":"Hey Easton, welcome to the edge!","timestamp":"2026-05-03T14:30:00.000Z"}

--no-verify-jwt 파라미터는 JWT를 검증하지 않겠다는 뜻이라 로컬 테스트에 편합니다. 프로덕션 환경에서는 검증을 켜는 것을 권장합니다.

5단계: 글로벌 배포

로컬 테스트에 문제가 없다면 Supabase 엣지 네트워크에 배포합니다:

# 먼저 로그인(아직 로그인하지 않았다면)
supabase login

# 프로젝트 연결
supabase link --project-ref <your-project-id>

# 함수 배포
supabase functions deploy hello-world

배포가 끝나면 Supabase가 함수를 글로벌 엣지 노드로 분산합니다. Dashboard에서 deployment 로그와 함수 URL을 확인할 수 있습니다.

6단계: 호출 테스트

함수 배포 후 URL 형식은 다음과 같습니다:

https://<project-id>.supabase.co/functions/v1/hello-world

curl로 호출합니다:

curl -X POST https://<project-id>.supabase.co/functions/v1/hello-world \
  -H "Authorization: Bearer <anon-key>" \
  -H "Content-Type: application/json" \
  -d '{"name":"World"}'

Authorization header에 주의하세요. Supabase Edge Functions는 기본적으로 JWT를 검증하므로 프로젝트의 anon key 또는 사용자 JWT token을 함께 보내야 합니다.


그렇다면 Edge Function으로 무엇을 할 수 있을까요? 데모를 넘어 실제 활용 장면도 꽤 많습니다.

Webhook 처리

예를 들어 Stripe 결제가 성공한 뒤 webhook을 보내면 Edge Function으로 이를 받아 데이터베이스에 기록할 수 있습니다:

// supabase/functions/stripe-webhook/index.ts
import "jsr:@supabase/functions-js/edge-runtime.d.ts"
import { createClient } from "jsr:@supabase/supabase-js@2"

Deno.serve(async (req) => {
  // Stripe 서명 검증(여기서는 단순화했으며 실제로는 hmac 검증 필요)
  const event = await req.json()

  const supabase = createClient(
    Deno.env.get("SUPABASE_URL")!,
    Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!
  )

  if (event.type === "payment_intent.succeeded") {
    const payment = event.data.object

    await supabase.from("payments").insert({
      id: payment.id,
      amount: payment.amount,
      customer_id: payment.customer,
      created_at: new Date().toISOString()
    })
  }

  return new Response(JSON.stringify({ received: true }), {
    headers: { "Content-Type": "application/json" }
  })
})

API 프록시

일부 서드파티 API는 API Key가 필요합니다. 프런트엔드에 노출하고 싶지 않다면 Edge Function을 프록시로 쓰면 됩니다:

// supabase/functions/openai-proxy/index.ts
Deno.serve(async (req) => {
  const body = await req.json()

  const response = await fetch("https://api.openai.com/v1/chat/completions", {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${Deno.env.get("OPENAI_API_KEY")}`,
      "Content-Type": "application/json"
    },
    body: JSON.stringify(body)
  })

  return new Response(response.body, {
    headers: { "Content-Type": "application/json" }
  })
})

프런트엔드는 당신의 Edge Function만 호출하고, API Key는 모두 엣지 노드 안에 숨길 수 있습니다.

4. 선택 기준: Supabase vs Cloudflare Workers

엣지 컴퓨팅 이야기를 하면 많은 사람이 묻습니다. Cloudflare Workers와 Supabase Edge Functions 중 무엇이 더 좋을까요?

솔직히 두 서비스는 완전한 직접 경쟁자가 아닙니다. 시나리오가 다르고, 선택도 자연히 달라집니다.

성능 비교

콜드 스타트 시간만 보면 Cloudflare Workers가 더 빠릅니다. 공식 데이터는 ~30ms이고, Supabase Edge Functions는 ~120ms입니다.

차이는 어디서 날까요? Cloudflare의 Workers runtime은 자체 개발한 런타임이고, 엣지 시나리오에 맞춰 최적화되어 있습니다. Supabase는 Deno를 사용합니다. Deno도 빠르지만 TypeScript 컴파일 계층이 하나 더 있습니다.

실제 체감은 어떨까요? 대부분의 시나리오에서는 120ms와 30ms의 차이를 사용자가 느끼기 어렵습니다. 웹 요청 자체의 네트워크 변동 폭이 이보다 큰 경우가 많습니다. 하지만 고빈도 거래나 실시간 경매처럼 밀리초를 다투는 업무라면 Cloudflare Workers가 더 적합합니다.

통합성 비교

이 부분은 Supabase의 강점입니다. Supabase Edge Functions를 쓰면 같은 프로젝트의 Postgres 데이터베이스에 바로 연결하고, Auth 서비스를 호출하고, Storage에 접근할 수 있습니다. 환경 변수도 자동으로 주입되므로 추가 설정이 적습니다.

Cloudflare Workers는 데이터베이스 연결을 직접 해결해야 합니다. Cloudflare D1(Cloudflare의 SQLite edge database)을 쓰거나 외부 데이터베이스에 연결할 수 있지만, 설정에는 조금 더 신경이 필요합니다.

공급업체 종속

이 점이 꽤 흥미롭습니다. Cloudflare Workers의 runtime은 비공개 소스입니다. 작성한 코드는 Cloudflare 위에서만 실행할 수 있고, 플랫폼을 바꾸려면 코드를 수정해야 합니다.

Supabase Edge Functions는 오픈소스 Deno를 사용합니다. 이론적으로는 같은 코드를 Deno Deploy로 옮기거나 Edge Runtime을 자체 호스팅할 수 있습니다. 물론 Supabase의 엣지 네트워크 자체를 옮길 수는 없지만, 적어도 런타임 계층에서는 완전히 묶이지 않습니다.

생태계 비교

Cloudflare Workers 생태계는 더 성숙합니다. 2017년에 출시되었고, 이미 많은 도구, 프레임워크, 커뮤니티 경험이 쌓여 있습니다. Hono와 Remix 같은 프레임워크도 Workers runtime을 지원합니다.

Supabase Edge Functions는 2022년에야 출시되어 생태계가 아직 성장 중입니다. 하지만 표준 Deno를 사용하기 때문에 많은 Deno 라이브러리를 그대로 사용할 수 있습니다.

선택 제안

구체적인 상황에 따라 대략 이런 판단 매트릭스를 사용할 수 있습니다:

상황추천
이미 Supabase(Auth, Database, Storage)를 사용 중Supabase Edge Functions
데이터베이스가 필요 없는 순수 엣지 컴퓨팅Cloudflare Workers
공급업체 종속이 걱정됨Supabase Edge Functions(Deno 오픈소스)
극단적인 콜드 스타트(<50ms)를 추구Cloudflare Workers
엣지에서 Postgres에 직접 연결해야 함Supabase Edge Functions

저는 현재 두 가지를 섞어 쓰고 있습니다. Cloudflare Workers는 프런트엔드 리버스 프록시와 캐시를 담당하고, Supabase Edge Functions는 Auth 관련 로직과 데이터베이스 작업을 처리합니다. 두 플랫폼의 장점을 각각 가져가는 방식입니다.

5. 겪어본 함정과 모범 사례

실전에서 제가 밟았던 함정을 공유하겠습니다. 당신은 피해 가면 좋겠습니다.

첫 번째 함정: Node.js 패키지를 사용했다

처음에는 HTTP 요청을 보내려고 axios를 쓰고 싶어서 바로 import axios from "axios"를 작성했습니다. 그런데 배포하자마자 모듈을 찾을 수 없다는 오류가 났습니다.

Edge Functions는 Node.js가 아니라 Deno를 사용합니다. npm 패키지를 직접 쓸 수 없습니다. Deno 호환 패키지로 바꾸거나 Deno 네이티브 fetch API를 사용해야 합니다. 사실 fetch만으로도 충분한 경우가 많습니다:

// axios를 쓰지 마세요
// import axios from "axios"  // 이 줄은 오류가 납니다

// fetch 사용
const response = await fetch("https://api.example.com/data", {
  method: "GET",
  headers: { "Authorization": "Bearer xxx" }
})
const data = await response.json()

axios와 비슷한 라이브러리가 꼭 필요하다면 Deno 생태계의 alternatives, 예를 들어 ky를 사용할 수 있습니다.

두 번째 함정: 함수가 너무 오래 실행되어 끊겼다

한 번은 배치 처리 함수를 작성해 수천 건의 레코드를 순회하며 계산하도록 했습니다. 로컬 테스트는 괜찮았지만, 배포 후 30초쯤 지나자 끊겼습니다.

이유는 단순합니다. Edge Functions에는 CPU 시간 제한이 있습니다. 공식 문서에 따르면 Isolate 상한은 400초입니다(소프트 제한과 하드 제한 포함). 제한을 넘으면 프로세스가 강제로 종료됩니다.

해결책은 Edge Function 안에서 무거운 일을 하지 않는 것입니다. 엣지 노드는 검증, 전달, 간단한 계산처럼 가벼운 로직 처리에 적합합니다. 복잡한 배치 작업은 중앙 서버나 전용 Worker 서비스로 보내세요.

세 번째 함정: 데이터베이스 연결 방식이 잘못됐다

엣지 환경에서 Postgres에 연결할 때 기존 방식의 장시간 연결을 그대로 쓰면 문제가 생깁니다. 엣지 노드 수가 많고, 각 노드가 긴 커넥션을 하나씩 잡으면 데이터베이스 커넥션 풀이 금방 터집니다.

권장 방식은 Supabase가 제공하는 커넥션 풀(Pooler)을 사용하거나, 요청마다 짧은 연결을 만들고 즉시 해제하는 것입니다. Supabase 공식 환경 변수 SUPABASE_DB_URL은 Pooler를 가리킵니다:

import { Pool } from "https://deno.land/x/[email protected]/mod.ts"

const pool = new Pool(Deno.env.get("SUPABASE_DB_URL")!, 10)

Deno.serve(async (req) => {
  const client = await pool.connect()
  try {
    const result = await client.queryArray("SELECT * FROM users LIMIT 10")
    return new Response(JSON.stringify(result.rows))
  } finally {
    client.release()  // 반드시 해제하세요!
  }
})

네 번째 함정: JWT 검증을 설정하지 않았다

로컬 테스트에서는 디버깅이 편해서 --no-verify-jwt를 사용했습니다. 그런데 배포 후 검증을 켜는 걸 잊었습니다. 결과적으로 누구나 제 함수를 호출할 수 있었습니다. 꽤 큰 보안 위험입니다.

올바른 방법은 config.toml에 다음과 같이 설정하는 것입니다:

[functions.hello-world]
verify_jwt = true

또는 배포할 때 지정할 수 있습니다:

supabase functions deploy hello-world --verify-jwt

몇 가지 실용적인 팁

  1. 네이티브 Deno.serve 대신 Hono 프레임워크 사용

Hono는 엣지 환경을 위해 설계된 초경량 Web 프레임워크입니다. 라우팅과 미들웨어를 지원하지만 크기는 13KB에 불과합니다. Express의 무거움과 비교하면 Hono는 Edge Functions에 매우 잘 맞습니다:

import { Hono } from "jsr:@hono/hono"
import { cors } from "jsr:@hono/hono/cors"

const app = new Hono()

app.use("*", cors())

app.get("/health", (c) => c.json({ status: "ok" }))

app.post("/echo", async (c) => {
  const body = await c.req.json()
  return c.json({ echo: body })
})

Deno.serve(app.fetch)
  1. 환경 변수 관리

Supabase Edge Functions에는 두 가지 runtime이 있습니다. Main Runtime(Supabase 플랫폼 제어)과 User Runtime(당신의 코드)입니다. 환경 변수 권한도 다릅니다:

  • SUPABASE_URL, SUPABASE_ANON_KEY, SUPABASE_SERVICE_ROLE_KEY는 자동으로 주입되며 코드에서 바로 사용할 수 있습니다.
  • 사용자 정의 변수는 Dashboard 또는 CLI에서 설정합니다: supabase secrets set MY_VAR=value
  1. Import Map으로 반복 해석 줄이기

함수가 여러 외부 모듈에 의존한다면 Import Map으로 미리 정의해 두면 요청마다 URL을 반복해서 해석하는 비용을 줄일 수 있습니다:

// deno.json 또는 import_map.json
{
  "imports": {
    "hono": "jsr:@hono/hono",
    "supabase-js": "jsr:@supabase/supabase-js@2"
  }
}

그다음 코드에서는 짧은 이름을 바로 사용할 수 있습니다:

import { Hono } from "hono"
import { createClient } from "supabase-js"

정리

길게 이야기했지만 핵심은 한 문장입니다. Supabase Edge Functions는 코드를 사용자와 가장 가까운 노드로 밀어 넣고, 안전한 Deno 런타임에서 실행하며, 120ms 수준의 콜드 스타트와 글로벌 분산을 자동으로 제공합니다.

프로젝트가 이미 Supabase를 사용하고 있다면, Auth든 Database든 Storage든 Edge Functions는 자연스러운 확장입니다. 데이터베이스 연결을 따로 고생할 필요도 없고, Auth 검증을 별도로 구성할 필요도 적으며, 모든 서비스가 바로 맞물립니다. 공급업체 종속이 걱정되나요? Deno는 오픈소스이고 Edge Runtime은 자체 호스팅할 수 있습니다. 적어도 “빠져나갈 선택지”는 있습니다.

다음 단계는 간단합니다. Supabase Dashboard를 열고 Quickstart를 따라 한 번 실행해 보세요. 10분 안에 첫 Edge Function이 글로벌 엣지 노드에서 실행될 것입니다.

참고로 이 시리즈의 다른 글도 함께 볼 수 있습니다:

Cloudflare Workers에 더 관심이 있다면 이 글도 볼 수 있습니다: Cloudflare Workers 실전 가이드. 두 플랫폼은 각자의 장점이 있으니, 당신에게 가장 잘 맞는 쪽을 선택하면 됩니다.

첫 Supabase Edge Function 배포하기

처음부터 Edge Function을 만들고 테스트한 뒤 글로벌 엣지 네트워크에 배포하는 과정

⏱️ Estimated time: 10 min

  1. 1

    Step 1: Supabase CLI 설치

    Supabase CLI 도구를 전역으로 설치합니다:

    ```bash
    npm install -g supabase
    ```

    설치 후 `supabase --version`을 실행해 설치가 성공했는지 확인합니다.
  2. 2

    Step 2: 프로젝트 초기화

    프로젝트 디렉터리에서 초기화 명령을 실행합니다:

    ```bash
    supabase init
    ```

    이 명령은 `supabase` 디렉터리와 `config.toml` 설정 파일을 생성합니다.
  3. 3

    Step 3: Edge Function 생성

    CLI로 새 Edge Function을 생성합니다:

    ```bash
    supabase functions new hello-world
    ```

    생성된 함수는 `supabase/functions/hello-world/index.ts`에 있으며, 기본적으로 JSON 응답을 반환합니다.
  4. 4

    Step 4: 로컬 테스트

    로컬 개발 서버를 시작합니다:

    ```bash
    supabase functions serve --no-verify-jwt
    ```

    기본 포트는 54321입니다. curl 또는 Postman으로 테스트합니다:

    ```bash
    curl -X POST http://localhost:54321/functions/v1/hello-world \
    -H "Content-Type: application/json" \
    -d '{"name":"Easton"}'
    ```
  5. 5

    Step 5: 글로벌 엣지 네트워크에 배포

    로그인하고 프로젝트를 연결한 뒤 배포합니다:

    ```bash
    supabase login
    supabase link --project-ref <your-project-id>
    supabase functions deploy hello-world
    ```

    배포가 끝나면 Dashboard에서 함수 URL과 로그를 확인할 수 있습니다.
  6. 6

    Step 6: 호출 테스트

    함수 URL과 인증 token으로 호출합니다:

    ```bash
    curl -X POST https://<project-id>.supabase.co/functions/v1/hello-world \
    -H "Authorization: Bearer <anon-key>" \
    -H "Content-Type: application/json" \
    -d '{"name":"World"}'
    ```

    프로덕션 환경에서는 JWT 검증을 켜는 것이 좋습니다: `supabase functions deploy hello-world --verify-jwt`

FAQ

Supabase Edge Functions와 AWS Lambda는 무엇이 다른가요?
주요 차이는 콜드 스타트 시간과 실행 위치입니다. Edge Functions의 콜드 스타트는 약 120ms이고, AWS Lambda Node.js 런타임은 300-500ms 수준입니다. Edge Functions 코드는 사용자와 더 가까운 글로벌 엣지 노드에서 실행되고, Lambda는 고정 리전에서 실행됩니다. Edge Functions에는 CPU 시간 제한(400초)이 있고, Lambda는 실행 시간이 더 깁니다(최대 15분). 다만 Lambda는 엣지 시나리오에는 덜 적합합니다.
Edge Functions에서 npm 패키지를 쓸 수 있나요?
직접 사용할 수는 없습니다. Edge Functions는 Deno 런타임 기반이라 Node.js의 npm 생태계를 지원하지 않습니다. 대신 1) deno.land 또는 jsr.io에서 가져오는 Deno 호환 패키지를 사용하고, 2) axios 같은 HTTP 라이브러리는 네이티브 fetch API로 대체하며, 3) axios 대신 ky처럼 Deno 생태계의 대안을 찾아야 합니다.
Edge Functions에는 실행 시간 제한이 있나요?
있습니다. V8 Isolate의 CPU 시간 상한은 400초입니다(소프트 제한 + 하드 제한). 제한을 넘으면 프로세스가 강제로 종료됩니다. Edge Functions는 JWT 검증, 요청 전달, 간단한 계산 같은 가벼운 로직을 위해 설계되었고, 무거운 계산 작업에는 적합하지 않습니다. 복잡한 배치 처리는 중앙 서버나 전용 Worker 서비스에 맡기는 편이 좋습니다.
Edge Function에서 Postgres 데이터베이스에 연결하려면 어떻게 해야 하나요?
커넥션 풀이 폭증하지 않도록 Supabase가 제공하는 Pooler 사용을 권장합니다:

• 환경 변수 `SUPABASE_DB_URL` 사용(자동으로 Pooler를 가리킴)
• `postgres` 패키지로 커넥션 풀 생성
• 요청마다 커넥션을 사용한 뒤 해제(`client.release()`)

기존의 장시간 연결 방식은 피하세요. 엣지 노드 수가 많으면 커넥션 풀이 금방 가득 찹니다.
Supabase Edge Functions와 Cloudflare Workers 중 무엇을 선택해야 하나요?
시나리오에 따라 다릅니다:

• 이미 Supabase 풀스택을 사용 중 → Edge Functions(깊은 통합, 빠른 개발)
• 백엔드 없이 순수 엣지 컴퓨팅만 필요 → Cloudflare Workers(콜드 스타트 ~30ms로 더 빠름)
• 공급업체 종속이 걱정됨 → Edge Functions(Deno 오픈소스, 자체 호스팅 가능)
• 엣지에서 Postgres에 직접 연결해야 함 → Edge Functions(매끄러운 통합)

실제로는 혼합 사용도 가능합니다. Cloudflare Workers는 프런트엔드 프록시와 캐시를 맡기고, Edge Functions는 Auth와 데이터베이스 로직을 처리하게 할 수 있습니다.
Edge Function을 보호하기 위한 JWT 검증은 어떻게 설정하나요?
두 가지 방식으로 설정할 수 있습니다:

• 설정 파일 방식: `config.toml`에 `[functions.hello-world] verify_jwt = true` 추가
• 명령줄 방식: 배포할 때 `--verify-jwt` 파라미터 추가

로컬 테스트에서는 `--no-verify-jwt`로 검증을 건너뛸 수 있지만, 프로덕션 환경에서는 반드시 켜야 합니다. JWT 검증을 설정하지 않으면 누구나 함수를 호출할 수 있습니다.

4분 읽기 · 게시일: 2026년 5월 3일 · 수정일: 2026년 10월 21일

댓글

GitHub로 로그인하여 댓글을 남기세요

Easton BlogEaston Blog