Vercel Serverless Functions
Short book about the Vercel Functions
1. Concept π©βπ»
Serverless Functions λ μ κ·Έλ¦Όκ³Ό κ°μ΄ Middleware λ₯Ό ν΅ν΄ μ€νλλ€. Proxy μλ²κ° Middleware κΈ°λ₯μ ν΄μ ν΅μ μ μ€κ°νλ μν μ νλλ° μ¬μ©λλ―μ΄ Edge Middleware μμ ν΅μ μ μ€κ°νλ€.
νμ§λ§ λμ λΆλͺ ν μ°¨μ΄μ μ΄ μ‘΄μ¬νλ€. Proxy λ App Layer μμ μλνλ€λ κ²λ§ μ μΈνλ©΄ VPN κ³Ό λ§μ°¬κ°μ§λ‘ ν΄λΌμ΄μΈνΈμ μλ² μ¬μ΄μ Middleware λ‘ μμΉν΄ μ§μ μλ°©ν₯μΌλ‘ ν΅μ μ νλ€. νμ§λ§ Serverless Functions λ₯Ό μ¬μ©νκΈ° μν Edge Middleware λ ν΄λΌμ°λ νκ²½μμ μ€νλλ μμ μ½λμ‘°κ°μΌλ‘ νΉμ μ΄λ²€νΈκ° λ°μν λ νμν 리μμ€λ§ μ¬μ©ν΄ μλνλ€.
Serverless Functions λ₯Ό μ μ¬μ©νλκ°μ λν΄μλ ν΄λΌμ°λ μΈ‘ μ€λͺ μ μλ²λ₯Ό ꡬμΆνκ³ κ΄λ¦¬, νμ₯νλ κ²κ³Ό κ°μ κ²μ΄ νμνμ§ μμ κ°λ°μλ μ½λλ₯Ό μμ±νκ³ λ°°ν¬νλ λ° μ§μ€ν μ μκ² ν΄μ€λ€κ³ νλ€.
λν λ 립μ μΌλ‘ μ€νλκΈ° λλ¬Έμ ν¨μμ μ€λ₯κ° μ± μ 체μ μν₯μ μ£Όμ§ μμΌλ©°, μ¬μ©ν λ§νΌμ λΉμ©λ§ μ§λΆνκΈ° λλ¬Έμ μ§μ μλ²λ₯Ό ꡬμΆν΄ νμ μ΄μνλ κ²λ³΄λ€ μ λ ΄νλ€κ³ νλ€.
νμ§λ§ μ΄κ²μ μ΄λκΉμ§λ ν΄λΌμ°λ μλΉμ€λ₯Ό μ¬μ©ν΄ νλ‘ νΈμλλ₯Ό μλ²λ₯Ό μ΄μνλ κ²μ κΈ°μ€μΌλ‘ ν μ€λͺ μ΄λ€. Serverless Functions κ° νμν κΆκ·Ήμ μΈ μ΄μ λ API Key μ κ°μ 보μ μ 보λ₯Ό λ ΈμΆνμ§ μκΈ° μν¨μ΄ λμ± ν¬λ€.
λ°±μλ μ€μ¬μ μΉ νκ²½μμλ λ°±μλκ° API μμ²μ λ¬Όλ‘ μ΄κ³ , νλ©΄μ λ§λλ λ λλ§ μμ κΉμ§ λͺ¨λ λ°±μλ μλ²μμ νλ€. μ΄ κ²½μ° ν΄λΌμ΄μΈνΈλ μμ μ μΈμ¦ μ λ³΄λ§ λ³΄κ΄νλ€ μλ²μ μμ²μ 보λ΄λ©΄ μλ²κ° κ²μ¦ ν λͺ¨λ μμ²κ±΄μ μ²λ¦¬νκΈ° λλ¬Έμ API Key κ° λ ΈμΆλ νμκ° μμλ€. λ¬Έμ λ νλ‘ νΈμλκ° λ¨μ Document κ° μλ μ±μΌλ‘ μλνκΈ° μμνλ©΄μλΆν°λ€. νλ‘ νΈμλκ° μ€μ¬μ΄ λλ κ²½μ°, ν΄λΌμ΄μΈνΈμ λΈλΌμ°μ μμμ νλμ μ±μ΄ λμκ°κ³ μλ κ²μ΄λ λ§μ°¬κ°μ§μ΄κΈ° λλ¬Έμ λͺ¨λ λΉμ¦λμ€ λ‘μ§μ΄ ν΄λΌμ΄μΈνΈμ μ μ‘λλ€. μ΄ λ§μ ν΄λΌμ΄μΈνΈκ° μ΄λ€ μμ²μ νκΈ° μν΄ API Key λ₯Ό κ°μ§κ³ μμ΄μΌνλ€λ λ§μ΄λ€.
λ§μ½ μ΄κ²μ λ ΈμΆνμ§ μκΈ° μν΄μλ μΈλ² μλ²λ₯Ό ν¬ν¨ν΄ ν΄λΌμ΄μΈνΈκ° μμ²νλ λͺ¨λ μλ²κ° λ΄ μΈμ¦ μ 보λ₯Ό κ°μ§κ³ μμ΄μΌ νλ€λ μλ―Έκ° λλ€. λ¬Όλ‘ , ν νμ¬κ° μ 곡νλ μ¬λ¬ μλΉμ€λΌλ©΄ Token μ μ¬μ©νλ©΄ κ°λ₯μ νλ€. μ΄λ₯Ό μν΄μλ λ°λμ API λ°±μλ μλ²μ νλ‘ νΈμλ μλ²μ λΆλ¦¬κ°λ° μ΄μμ΄ νμνλ©°, μΈλΆ μλ²μ μμ²μ ν΄λΌμ΄μΈνΈκ° μ§μ ν μ μκ³ λ°±μλκ° κ°λ°μ ν΄μ€ λ°±μλ μλ²λ₯Ό Middleware λ‘ μ¬μ©ν΄ ν΅μ ν΄μΌνλ€λ κ²μ΄λ€.
ν΄λΌμ΄μΈνΈ μ±λ₯μ΄ μ’μμ Έ λ°±μλ μλ²μ λΆλ΄μ λΆμ°μν€κ³ μ νλ‘ νΈμλ κ°λ°μ΄ μ겨λ¬λλ° κ²°κ΅ κ³Όκ±°μ λ°μ΄ λ¬Άμ΄λ κΌ΄μ΄ λλ κ²μ΄λ€. νλ‘ νΈμλ κ°λ°μμ Serverless λ νλ‘ νΈμλ κ°λ°μκ° λ°±μλμ μ’ μλμ§ μκ³ API μμ² μ²λ¦¬λ₯Ό ν μ μλλ‘ ν΄λΌμ°λκ° μ 곡νλ κ°νΈν λ°±μλ ν΄λΌμ°λ μλΉμ€λ₯Ό μ¬μ©ν΄ API Key λ₯Ό κ°μΆλ κ²μ΄ κ°μ₯ ν΅μ¬μ΄ λλ€.
μ¦, λ΄κ° μ§μ λ°±μλ API λ₯Ό ꡬμΆν΄ Middleware λ‘ μ¬μ©νλ λμ , ν΄λΌμ°λκ° μ 곡νλ Middleware λ₯Ό 리μμ€ λΉμ©λ§ λ΄κ³ μ λ ΄νκ² μ¬μ©νκ² ν΄μ€κ², λμ λ΄κ° μ μν κ·μΉλλ‘ λκ° μ¬μ©ν ν¨μλ§ λ§λ€μ΄κ° Serverless Functions μ ν΅μ¬μ΄λ€.
2. Quick Start π©βπ»
1. Install Vercel CLI
npm i -D vercel@latest
# or
yarn add vercel@latest
μμΈν μ€μ νκ²½μ Vercel Functions Quickstart νμ΄μ§λ₯Ό μ°Έκ³ νλ€. Vercel μ μ§μ TypeScript λ₯Ό μ§μνκΈ° λλ¬Έμ
d.ts
λ₯Ό μ€μΉν νμκ° μκ³ λ‘컬μμ μ€νμ μν΄ Vercel CLI λ§ μ€μΉνλ©΄ λλ€.
- package.json
{
"scripts": {
"vercel": "vercel dev"
}
}
κ·Έλ¦¬κ³ root κ²½λ‘μ vercel.json
νμΌμ μμ±νλ€.
{
"devCommand": "npm run dev",
"buildCommand": "npm run build"
}
yarn
μ μ¬μ©ν κ²½μ° μ λͺ
λ Ήμ΄λ npm
μ΄ μλ yarn
μ μ¬μ©νλ€.
2. Create API Functions
root κ²½λ‘μ api
λΌλ λλ ν 리λ₯Ό μμ±νκ³ , ν¨μλ₯Ό μμ±ν΄μΌνλλ° ν¨μλ MSW
μ μ μ¬νκ² μμ±νλ©΄ λλ€.
- /api/user.ts
import type { VercelRequest, VercelResponse } from "@vercel/node";
const ALLOWED_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE"];
export default function handler(request: VercelRequest, response: VercelResponse) {
const method = ALLOWED_METHODS.find((method) => method === request.method);
if (method === undefined) return;
user[method](request, response);
}
const user: Record<string, Function> = {
GET: getUser,
POST: postUser,
PUT: putUser,
PATCH: patchUser,
DELETE: deleteUser,
};
function getUser(request: VercelRequest, response: VercelResponse): VercelResponse {
return response.status(200).json({
name: "Hogwarts",
age: 32,
favorite: ["Movie", "Music", "Book", "Beer"],
});
}
function postUser(request: VercelRequest, response: VercelResponse): VercelResponse {
return response.status(200).json({});
}
function putUser(request: VercelRequest, response: VercelResponse): VercelResponse {
return response.status(200).json({});
}
function patchUser(request: VercelRequest, response: VercelResponse): VercelResponse {
return response.status(200).json({});
}
function deleteUser(request: VercelRequest, response: VercelResponse): VercelResponse {
return response.status(200).json({});
}
npm run vercel
λͺ
λ Ήμ μ
λ ₯νλ©΄ μμ μμ±ν Vercel λͺ
λ Ήμ΄κ° μ€νλλ€. Vercel μ λ‘κ·ΈμΈ ν νλ‘μ νΈ μ€μ μ΄ λμ¬ λ μ€λͺ
μ μ½κ³
μ μ ν Y/N μ μ€νν΄μ£Όλ©΄ λλ€. λλΆλΆμ κ²½μ° μ νν΄μΌ ν κ²μΌλ‘ νλ¨λλ κ²μ΄ λλ¬Έμλ‘ νκΈ°(Y/n μ΄λ©΄ Y λ₯Ό μ νν κ²μΌλ‘ μμ,
y/N μ΄λ©΄ N μ μ νν κ²μΌλ‘ μμνκ³ ν°λ―Έλ λ©μμ§κ° μΆλ ₯)λλ μ°Έκ³ νλ©΄ λλ€.
Vercel μ μ¬μ©ν΄ μλ²κ° μ€νλ λ©΄ μ κ²½λ‘μ API μμ²μ 보λ΄λ³΄μ. νΉμλΌλ Hash Router λ₯Ό μ¬μ©νκ³ μλ€λ©΄ #
λ μ§μμΌ νλ€. λΌμ°ν
JavaScript λ‘ μμ±ν κΈ°λ₯μ μ¬μ©νλ κ²μ΄ μλκ³ μμμ μμ±ν ν¨μλ λ λ€λ₯Έ Middleware Server λ‘ μλνλ κ²μ΄λΌ 보면 λκΈ° λλ¬Έμ΄λ€.
μ¦, express λ₯Ό μ¬μ©ν Node μλ²λ₯Ό λμ°κ±°λ MSW λ₯Ό μ¬μ©ν΄ HTTP API μμ²μ μλ΅νλ μλ²λ₯Ό μ¬μ©νλ κ²κ³Ό κ°λ€κ³ 보면 λλ€. μ΄κ²μ΄ κ°λ₯ν
μ΄μ κ° Vercel μ μ€μΉνλ©΄ dependencies λ‘ https-proxy-agent
, make-dir
, node-fetch
μ κ°μ κ²λ€μ΄ ν¨κ» μ€μΉλκΈ° λλ¬Έμ΄λ€.
μ΄μ npm run vercel
λ‘ μλ²λ₯Ό λμ΄ λ€μ μλ μ£Όμλ‘ GET μμ²μ 보λ΄λ³΄μ.
http://localhost:3000/api/user
Postman μ μ¬μ©ν΄λ μ’κ³ , ν°λ―Έλλ‘ Curl, Wget λ±μ μ¬μ©ν΄λ μ’λ€. μλλ©΄ νλ‘ νΈμλ μλ²μμ λ°λ‘ fetch μμ²μ λ λ €λ³΄λ κ²λ μ’λ€. μ°λ¦¬λ κ²°κ³Όλ‘ Status Code 200 κ³Ό λ€μ JSON λ°μ΄ν°λ₯Ό μ»μ μ μλ€.
{
"name": "Hogwarts",
"age": 32,
"favorite": [ "Movie", "Music", "Book", "Beer" ]
}
λ€μ λ§ν΄, νλ‘ νΈμλ μ½λμμ /api/user
κ²½λ‘λ‘ μμ²μ 보λ΄λ©΄ Vercel μ΄ λμ΄ Middleware Server κ° HTTP API μμ²μ μ²λ¦¬νλ
κ²μ΄λ€.
;(async () => {
const response = await fetch("/api/user");
const data = await response.json();
console.table(data);
})();
νλ‘ νΈμλ μλ²λ₯Ό λμ΄λ€λ κ²μ νλ‘ νΈμλλ₯Ό μλΉμ€νκΈ° μν HTML, CSS, JavaScript λ₯Ό νΈμ€ν νλ μλ²λ₯Ό λμ°λ κ²μ μλ―Ένλ€. μμ κ°μ API μμ²μ νλ €λ©΄ API μμ² μ²λ¦¬κ° κ°λ₯ν λ°±μλ μλ²κ° νμνλ€.
import express from "express";
import router from "./router/index";
import * as ejs from "ejs";
const app = express();
const port = 3000;
app.listen(port, () => {
console.log(`Playground app listening at http://localhost:${port}`)
})
app.use(express.static("public"));
app.use(express.static("router"));
app.use("/scripts", express.static("node_modules"));
app.use("/js", express.static("dist"));
app.disable("etag");
app.set("views", "/views");
app.set("view engine", "ejs");
app.engine("ejs", ejs.renderFile);
app.use(router);
import express, {response} from "express";
import * as path from "path";
const router = express.Router();
const __dirname = path.resolve();
export default router;
router.get("/", (req, res) => {
res.sendFile(path.join(__dirname, "public", "main.html"))
})
router.get("/product/vegetable", (req, res) => {
res.render(path.join(__dirname, "views", "product/vegetable.ejs"))
})
router.get("/join", (req, res) => {
res.render(path.join(__dirname, "views", "user/join.ejs"))
})
router.get("/promotion", (req, res) => {
const { type } = req
const promotion = // Request data to database...
res.json(promotion)
})
μ΄λ°μμΌλ‘ λ°±μλ μλ²λ₯Ό μ§μ λμμΌνλλ° Vercel μ μ€μΉνλ©΄ MSW
λ₯Ό μ¬μ©νλ― λ¨μνκ² κ²½λ‘λ₯Ό μμ±νκ³ νμν ν¨μλ§ λ§λ€λ©΄
λλ¨Έμ§ μλ² κ΅¬μΆμ μ¬μ©μκ° μ§μ νμ§ μμλ λκΈ° λλ¬Έμ Serverless Functions λΌ νλ
κ²μ΄λ€. μλ²κ° μμ΄μκ° μλκ³ μλ²λ₯Ό μ§μ λ§λ€μ§ μκΈ° λλ¬Έμ Serverless λΌλ κ²μ μ μν΄μΌνλ€.
3. Dynamic Routes
Next.js μ λ§μ°¬κ°μ§λ‘ λμ λΌμ°ν μ΄ κ°λ₯νλ€.
λλ
μ κ°μ κ΅¬μ‘°λ‘ λλ ν 리μ νμΌμ μμ±νλ©΄
/api/user
μ /api/user/uuid005435
λ₯Ό ꡬλΆν μ μλ€. uuid005435
λ₯Ό URL Parameters λ‘ μ¬μ©νλ μμ€ν
μ΄ λλ κ²μ΄λ€.
μ΄λ μ λ¬λ URL νλΌλ―Έν°λ request.query
λ‘λΆν° κΊΌλΌ μ μλλ°, [id]
κ° νλΌλ―Έν° μ΄λ¦μ΄ λμ΄ {id: value}
ννλ‘ λ΄κ²¨μλ€.
- /api/user/[id].ts
import type { VercelRequest, VercelResponse } from "@vercel/node";
const ALLOWED_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE"];
export default function handler(request: VercelRequest, response: VercelResponse) {
const method = ALLOWED_METHODS.find((method) => method === request.method);
if (method === undefined) return;
user[method](request, response);
}
const user: Record<string, Function> = {
GET: getUser,
POST: postUser,
PUT: putUser,
PATCH: patchUser,
DELETE: deleteUser,
};
function getUser(request: VercelRequest, response: VercelResponse) {
const { id } = request.query;
return response.status(200).json({
message: `${id} μ¬μ©μ μ 보μ λν μμ²`,
});
}
// ...
λ°λΌμ API μμ²μ λ€μκ³Ό κ°μ΄ URL Parameters λ₯Ό ꡬλΆν μ μκ² λλ€.
- GET
/api/user/
μμ²μ λν μλ΅
{
"name": "Hogwarts",
"age": 32,
"favorite": [ "Movie", "Music", "Book", "Beer" ]
}
- GET
/api/user/uuid005435
μμ²μ λν μλ΅
{
"message": "uuid005435 μ¬μ©μ μ 보μ λν μμ²"
}
λ¬Όλ‘ , URL Parameters λ₯Ό μΆμΆν λλ
request.query
μμ μΆμΆνμ§λ§, Body μ μ€λ € λ³΄λΈ μ 보λrequest.body
λ‘λΆν° μΆμΆνλ€.
Reference
- λ°μμ , βνλ‘ νΈμλ μΉ κ°λ°μ λͺ¨λ κ² μ΄κ²©μ°¨ ν¨ν€μ§ Online.β fastcampus.co.kr. last modified unknown, Fast Campus.
- βConfiguring Projects with vercel.json.β Vercel. Feb. 21, 2023, accessed May. 04, 2024, Vercel - Project Configuration.