1.
먼저 DB 연결과 쿼리 실행 헬퍼 함수를 정의한다.
import * as sql from "mssql";
// Bun은 기본적으로 process.env를 지원합니다
const config: sql.config = {
user: process.env.USER || "your_username",
password: process.env.PASSWORD || "your_password",
server: process.env.SERVER || "localhost",
database: process.env.DATABASE || "your_database",
options: {
encrypt: false, // Azure SQL Database의 경우
trustServerCertificate: false, // 로컬 개발환경의 경우
},
pool: {
max: 20,
// 0이면 매번 새 연결을 생성해야하는 cold start 문제가 발생할 수 있다.
min: 2,
idleTimeoutMillis: 60000,
},
};
let pool: sql.ConnectionPool | null = null;
// 데이터베이스 연결 함수입니다
export async function connectDB(): Promise<sql.ConnectionPool> {
try {
if (!pool) {
pool = new sql.ConnectionPool(config);
await pool.connect();
console.log("SQL Server 연결 성공입니다");
}
return pool;
} catch (error) {
console.error("DB 연결 오류입니다:", error);
throw error;
}
}
다음으로 쿼리 실행 헬퍼 함수를 정의한다.
// 쿼리 실행 헬퍼 함수입니다
export async function executeQuery(
query: string,
params: any = {}
): Promise<sql.IResult<any>> {
try {
const dbPool = await connectDB();
const request = dbPool.request();
// 파라미터 바인딩
Object.keys(params).forEach((key) => {
request.input(key, params[key]);
});
const result = await request.query(query);
return result;
} catch (error) {
console.error("쿼리 실행 오류입니다:", error);
throw error;
}
}
2. GET
// 과정 목록 조회 API
courseRouter.get("/", async (c) => {
try {
const QUERY = `
SELECT
c.course_id,
c.title,
c.year,
c.quarter,
c.is_active,
c.created_at,
COALESCE(
(SELECT COUNT(*) FROM e_videos v WHERE v.course_id = c.course_id AND v.is_public = 1),
0
) as video_count,
COALESCE(
(SELECT SUM(v.duration) FROM e_videos v WHERE v.course_id = c.course_id AND v.is_public = 1),
0
) as total_duration
FROM e_courses c
WHERE c.is_active = 1
ORDER BY c.year DESC, c.quarter DESC, c.created_at DESC
`;
const result = await executeQuery(QUERY);
const response: ApiResponse = {
success: true,
data: result.recordset,
message: "과정 목록을 성공적으로 조회했습니다",
};
return c.json(response);
} catch (error: any) {
console.error("과정 목록 조회 오류입니다:", error);
const response: ApiResponse = {
success: false,
message: "과정 목록 조회 중 오류가 발생했습니다",
error: error.message,
};
return c.json(response, 500);
}
});
3.
POST 유즈케이스
// 공지사항 생성 API
announcementRouter.post("/", async (c) => {
try {
const body = await c.req.json();
const { title, content, is_active = true } = body;
// 입력 검증
if (!title || !title.trim()) {
const response: ApiResponse = {
success: false,
message: "제목을 입력해주세요",
};
return c.json(response, 400);
}
if (!content || !content.trim()) {
const response: ApiResponse = {
success: false,
message: "내용을 입력해주세요",
};
return c.json(response, 400);
}
const QUERY = `
INSERT INTO e_announcements (title, content, is_active, created_at)
OUTPUT INSERTED.announcement_id, INSERTED.title, INSERTED.content, INSERTED.is_active, INSERTED.created_at
VALUES (@title, @content, @is_active, GETDATE())
`;
const result = await executeQuery(QUERY, {
title: title.trim(),
content: content.trim(),
is_active,
});
const response: ApiResponse = {
success: true,
data: result.recordset[0],
message: "공지사항을 성공적으로 등록했습니다",
};
return c.json(response, 201);
} catch (error: any) {
console.error("공지사항 생성 오류입니다:", error);
const response: ApiResponse = {
success: false,
message: "공지사항 생성 중 오류가 발생했습니다",
error: error.message,
};
return c.json(response, 500);
}
});