본문 바로가기
카테고리 없음

실제 유즈케이스 hono

by 잘먹는 개발자 에단 2025. 6. 24.

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);
  }
});