Frame์ ๋ฐ์ดํฐ๋ฒ ์ด์ค Entity์ ์ฐ๊ฒฐ๋์ง ์์ ๋
๋ฆฝ์ ์ธ API ์๋ํฌ์ธํธ๋ฅผ ๋ง๋ค ๋ ์ฌ์ฉํฉ๋๋ค.
Frame ๊ฐ์
Entity ๋ถํ์
DB ํ
์ด๋ธ ์์ด ์๋์์ ๋ก์ด API ๊ตฌํ
๊ฐ๋จํ ๊ตฌ์กฐ
BaseFrameClass ์์@api ๋ฐ์ฝ๋ ์ดํฐ ์ฌ์ฉ
๋ค์ํ ์ฉ๋
ํฌ์ค์ฒดํฌ, ์ ํธ๋ฆฌํฐ์ธ๋ถ API ํ๋ก์
๋
๋ฆฝ์ ๋ก์ง
๋น์ฆ๋์ค ๋ก์ง๋ง ๊ตฌํEntity ์ ์ฝ ์์
Model vs Frame
Model (BaseModelClass)
// Entity ๊ธฐ๋ฐ API
class UserModelClass extends BaseModelClass<
UserSubsetKey,
UserSubsetMapping,
typeof userSubsetQueries,
typeof userLoaderQueries
> {
constructor() {
super("User", userSubsetQueries, userLoaderQueries); // โ Entity์ ์ฐ๊ฒฐ
}
@api({ httpMethod: "GET" })
async list(): Promise<User[]> {
// users ํ
์ด๋ธ ์กฐํ
const rdb = this.getPuri("r");
return rdb.table("users").select("*");
}
@api({ httpMethod: "POST" })
async create(params: UserSaveParams): Promise<{ userId: number }> {
// users ํ
์ด๋ธ์ ์ฝ์
const wdb = this.getPuri("w");
wdb.ubRegister("users", params);
const [userId] = await wdb.ubUpsert("users");
return { userId };
}
}
Model์ ํน์ง:
- Entity(DB ํ
์ด๋ธ)์ 1:1 ๋งคํ
- CRUD ์์
์ค์ฌ
- UpsertBuilder, Subset ํ์ฉ
- ํ์
์์ ์ฑ ๋ณด์ฅ
Frame (BaseFrameClass)
// Entity ์๋ ๋
๋ฆฝ์ API
class HealthFrame extends BaseFrameClass {
frameName = "Health"; // โ Entity์ ๋ฌด๊ด
@api({ httpMethod: "GET" })
async check(): Promise<{
status: "ok" | "error";
timestamp: Date;
uptime: number;
}> {
// DB ์ ๊ทผ ์์ด ์๋ฒ ์ํ๋ง ๋ฐํ
return {
status: "ok",
timestamp: new Date(),
uptime: process.uptime(),
};
}
@api({ httpMethod: "GET" })
async ping(): Promise<{ pong: string }> {
return { pong: "pong" };
}
}
Frame์ ํน์ง:
- Entity์ ๋ฌด๊ดํ ๋
๋ฆฝ์ ๋ก์ง
- ์ ํธ๋ฆฌํฐ, ํฌ์ค์ฒดํฌ, ํ๋ก์ ๋ฑ
- ์์ ๋ก์ด API ๊ตฌ์กฐ
- ๊ฐ๋จํ ๊ตฌํ
๋น๊ต ํ
| ํน์ง | Model | Frame |
|---|
| Entity ์ฐ๊ฒฐ | โ
ํ์ | โ ๋ถํ์ |
| DB ์ ๊ทผ | CRUD ์ค์ฌ | ์ ํ์ |
| ์ฌ์ฉ ์ฌ๋ก | ๋ฐ์ดํฐ ๊ด๋ฆฌ | ์ ํธ๋ฆฌํฐ, ํ๋ก์ |
| ๋ณต์ก๋ | ๋์ | ๋ฎ์ |
| UpsertBuilder | โ
์ฌ์ฉ | โ ๋ถํ์ |
| Subset | โ
์ฌ์ฉ | โ ๋ถํ์ |
Frame ์ฌ์ฉ ์๊ธฐ
โ
Frame์ ์ฌ์ฉํด์ผ ํ๋ ๊ฒฝ์ฐ
-
ํฌ์ค์ฒดํฌ API
// GET /api/health/check
// Entity ์์ด ์๋ฒ ์ํ๋ง ํ์ธ
-
์ ํธ๋ฆฌํฐ API
// POST /api/utils/hash
// ๋ฌธ์์ด ํด์ ๊ณ์ฐ ๋ฑ ๋จ์ ๊ธฐ๋ฅ
-
์ธ๋ถ API ํ๋ก์
// GET /api/weather/current
// ์ธ๋ถ ๋ ์จ API๋ฅผ ๋ด๋ถ API๋ก ๋ํ
-
์ง๊ณ ํต๊ณ
// GET /api/stats/dashboard
// ์ฌ๋ฌ ํ
์ด๋ธ์ ํต๊ณ๋ฅผ ์ง๊ณ
-
์ธ์ฆ/๊ถํ ์ฒดํฌ
// POST /api/auth/verify-token
// JWT ํ ํฐ ๊ฒ์ฆ๋ง ์ํ
โ Model์ ์ฌ์ฉํด์ผ ํ๋ ๊ฒฝ์ฐ
-
Entity CRUD ์์
// User, Product, Order ๋ฑ์ ์์ฑ/์กฐํ/์์ /์ญ์
-
๊ด๊ณ ๋ฐ์ดํฐ ์ฒ๋ฆฌ
// User โ Profile, Order โ OrderItems
-
๋ณต์กํ ๋น์ฆ๋์ค ๋ก์ง
// ์ฃผ๋ฌธ ์ฒ๋ฆฌ, ๊ฒฐ์ ๋ฑ ์ฌ๋ฌ Entity ์กฐ์
๊ตฌ์กฐ ์ฐจ์ด
Model ๊ตฌ์กฐ
api/src/application/
user/
โโโ user.entity.json # โ Entity ์ ์
โโโ user.model.ts # โ Model ํด๋์ค
โโโ user.types.ts # โ ํ์
์ ์
โโโ subsets/
โโโ user-list.subset.json
Frame ๊ตฌ์กฐ
api/src/frames/
health/
โโโ health.frame.ts # โ Frame ํด๋์ค๋ง
utils/
โโโ utils.frame.ts
Frame์ Entity ์ ์ ์์ด .frame.ts ํ์ผ๋ง ์์ผ๋ฉด ๋ฉ๋๋ค.
๊ฐ๋จํ ์์
ํฌ์ค์ฒดํฌ Frame
class HealthFrame extends BaseFrameClass {
frameName = "Health";
@api({ httpMethod: "GET" })
async check(): Promise<{
status: "ok" | "error";
timestamp: Date;
database: "connected" | "disconnected";
}> {
// DB ์ฐ๊ฒฐ ์ฒดํฌ
let dbStatus: "connected" | "disconnected" = "disconnected";
try {
const rdb = this.getPuri("r");
await rdb.raw("SELECT 1");
dbStatus = "connected";
} catch (error) {
console.error("Database connection failed:", error);
}
return {
status: dbStatus === "connected" ? "ok" : "error",
timestamp: new Date(),
database: dbStatus,
};
}
@api({ httpMethod: "GET" })
async ping(): Promise<{ message: string }> {
return { message: "pong" };
}
}
// ์๋ํฌ์ธํธ:
// GET /api/health/check
// GET /api/health/ping
์ ํธ๋ฆฌํฐ Frame
import crypto from "crypto";
class UtilsFrame extends BaseFrameClass {
frameName = "Utils";
@api({ httpMethod: "POST" })
async hash(params: {
text: string;
algorithm: "md5" | "sha256" | "sha512";
}): Promise<{ hash: string }> {
const hash = crypto
.createHash(params.algorithm)
.update(params.text)
.digest("hex");
return { hash };
}
@api({ httpMethod: "POST" })
async uuid(): Promise<{ uuid: string }> {
return { uuid: crypto.randomUUID() };
}
}
// ์๋ํฌ์ธํธ:
// POST /api/utils/hash
// POST /api/utils/uuid
์ฅ๋จ์
๋น ๋ฅธ ๊ตฌํ
Entity ์ ์ ๋ถํ์๊ฐ๋จํ ๊ตฌ์กฐ
์ ์ฐ์ฑ
์ ์ฝ ์๋ ๋ก์ง์์ ๋ก์ด API ์ค๊ณ
๋
๋ฆฝ์ฑ
DB ์คํค๋ง ๋
๋ฆฝ์ ์ธ๋ถ API ์ฐ๋ ์ฉ์ด
ํ
์คํธ ์ฉ์ด
๋จ์ํ ๋ก์งMock ๋ถํ์
ํ์
์์ ์ฑ ๊ฐ์
Entity ํ์
๋ฏธ์ฌ์ฉ์๋ ํ์
๊ด๋ฆฌ
์๋ ์์ฑ ๋ฏธ์ง์
Subset, Types ์๋ ์์ฑ ์์์๋ ์์ฑ ํ์
์ค์ ํ
1. Frame์ ๊ฐ๋จํ๊ฒ ์ ์ง
// โ
์ข์: ๋จ์ํ ๋ก์ง
class HealthFrame extends BaseFrameClass {
@api({ httpMethod: "GET" })
async check(): Promise<{ status: string }> {
return { status: "ok" };
}
}
// โ ๋์จ: ๋ณต์กํ ๋น์ฆ๋์ค ๋ก์ง
class OrderFrame extends BaseFrameClass {
@api({ httpMethod: "POST" })
async process(params: ComplexOrderParams): Promise<OrderResult> {
// 100์ค์ ๋ณต์กํ ๋ก์ง...
// ์ด๋ฐ ๊ฒฝ์ฐ๋ Model๋ก ๋ง๋ค์ด์ผ ํจ
}
}
2. ์ ์ ํ ๋ค์ด๋ฐ
// โ
์ข์: ๋ช
ํํ ์ด๋ฆ
class HealthFrame extends BaseFrameClass {
frameName = "Health";
}
class UtilsFrame extends BaseFrameClass {
frameName = "Utils";
}
// โ ๋์จ: ๋ชจํธํ ์ด๋ฆ
class MiscFrame extends BaseFrameClass {
frameName = "Misc";
}
3. ๊ด๋ จ ๊ธฐ๋ฅ ๊ทธ๋ฃนํ
// โ
์ข์: ๊ด๋ จ๋ ๊ธฐ๋ฅ์ ํ๋์ Frame์
class CryptoUtilsFrame extends BaseFrameClass {
frameName = "CryptoUtils";
@api({ httpMethod: "POST" })
async hash(params: HashParams): Promise<{ hash: string }> { /* ... */ }
@api({ httpMethod: "POST" })
async encrypt(params: EncryptParams): Promise<{ encrypted: string }> { /* ... */ }
@api({ httpMethod: "POST" })
async decrypt(params: DecryptParams): Promise<{ decrypted: string }> { /* ... */ }
}
์ธ์ Model๋ก ์ ํํ ๊น?
Frame์ผ๋ก ์์ํ์ง๋ง ๋ค์ ์ํฉ์ด ๋๋ฉด Model๋ก ์ ํ ๊ณ ๋ ค:
-
DB ๋ฐ์ดํฐ๋ฅผ ์์ฃผ ์กฐํ/์์
- ์ฒ์์ ๋จ์ ์กฐํ์์ง๋ง CRUD๊ฐ ํ์ํด์ง
-
ํ์
์์ ์ฑ์ด ์ค์ํด์ง
- ๋ณต์กํ ๋ฐ์ดํฐ ๊ตฌ์กฐ๊ฐ ์๊น
- Entity ํ์
์๋ ์์ฑ์ด ํ์
-
๊ด๊ณ ๋ฐ์ดํฐ ์ฒ๋ฆฌ ํ์
- ์ฌ๋ฌ ํ
์ด๋ธ ๊ฐ์ ๊ด๊ณ ์ฒ๋ฆฌ
- JOIN, ์ธ๋ํค ๋ฑ
-
๋น์ฆ๋์ค ๋ก์ง์ด ๋ณต์กํด์ง
- ๋จ์ ์ ํธ๋ฆฌํฐ๋ฅผ ๋์ด์ฌ
- ํธ๋์ญ์
์ฒ๋ฆฌ ํ์
๋ค์ ๋จ๊ณ