diff --git a/backend-node/src/controllers/sampleController.ts b/backend-node/src/controllers/sampleController.ts new file mode 100644 index 00000000..d4f277ca --- /dev/null +++ b/backend-node/src/controllers/sampleController.ts @@ -0,0 +1,56 @@ +import { Request, Response } from "express"; +import { SampleService } from "../services/sampleService"; + +const sampleService = SampleService.getInstance(); + +/** + * 목록 조회 + * GET /api/sample/list?page=1&limit=20 + */ +export const getList = async (req: Request, res: Response): Promise => { + const page = Math.max(1, parseInt(req.query.page as string) || 1); + const limit = Math.min(100, Math.max(1, parseInt(req.query.limit as string) || 20)); + const result = await sampleService.getList(page, limit); + res.status(200).json({ + success: true, + data: result.items, + total: result.total, + page, + limit, + }); +}; + +/** + * 등록 + * POST /api/sample + */ +export const create = async (req: Request, res: Response): Promise => { + const { name, description } = req.body; + if (!name || !description) { + res.status(400).json({ success: false, message: "name, description은 필수입니다." }); + return; + } + const item = await sampleService.create(name, description); + res.status(201).json({ success: true, data: item }); +}; + +/** + * 수정 + * PUT /api/sample/:id + */ +export const update = async (req: Request, res: Response): Promise => { + const { id } = req.params; + const { name, description } = req.body; + const item = await sampleService.update(id, name, description); + res.status(200).json({ success: true, data: item }); +}; + +/** + * 삭제 (soft delete) + * DELETE /api/sample/:id + */ +export const remove = async (req: Request, res: Response): Promise => { + const { id } = req.params; + await sampleService.softDelete(id); + res.status(200).json({ success: true, message: "삭제되었습니다." }); +}; diff --git a/backend-node/src/routes/sampleRoutes.ts b/backend-node/src/routes/sampleRoutes.ts new file mode 100644 index 00000000..22f013fd --- /dev/null +++ b/backend-node/src/routes/sampleRoutes.ts @@ -0,0 +1,14 @@ +import { Router } from "express"; +import { authenticateToken } from "../middleware/authMiddleware"; +import * as sampleController from "../controllers/sampleController"; + +const router = Router(); + +router.use(authenticateToken); + +router.get("/list", sampleController.getList); +router.post("/", sampleController.create); +router.put("/:id", sampleController.update); +router.delete("/:id", sampleController.remove); + +export default router; diff --git a/backend-node/src/services/sampleService.ts b/backend-node/src/services/sampleService.ts new file mode 100644 index 00000000..204e7b8e --- /dev/null +++ b/backend-node/src/services/sampleService.ts @@ -0,0 +1,86 @@ +import { v4 as uuidv4 } from "uuid"; +import { logger } from "../utils/logger"; + +export interface SampleItem { + id: string; + name: string; + description: string; + is_deleted: boolean; + created_at: string; + updated_at: string; +} + +// 인메모리 저장소 (실제 DB 테이블 없이도 동작) +const store = new Map(); + +export class SampleService { + private static instance: SampleService; + + private constructor() {} + + public static getInstance(): SampleService { + if (!SampleService.instance) { + SampleService.instance = new SampleService(); + } + return SampleService.instance; + } + + /** + * 목록 조회 (페이징, soft delete 제외) + */ + public async getList(page: number, limit: number): Promise<{ items: SampleItem[]; total: number }> { + const active = Array.from(store.values()).filter((item) => !item.is_deleted); + const total = active.length; + const items = active.slice((page - 1) * limit, page * limit); + logger.info(`📋 sample 목록 조회: total=${total}, page=${page}, limit=${limit}`); + return { items, total }; + } + + /** + * 등록 + */ + public async create(name: string, description: string): Promise { + const now = new Date().toISOString(); + const item: SampleItem = { + id: uuidv4(), + name, + description, + is_deleted: false, + created_at: now, + updated_at: now, + }; + store.set(item.id, item); + logger.info(`✅ sample 등록: ${item.id}`); + return item; + } + + /** + * 수정 + */ + public async update(id: string, name?: string, description?: string): Promise { + const item = store.get(id); + if (!item || item.is_deleted) { + throw new Error(`항목을 찾을 수 없습니다: ${id}`); + } + if (name !== undefined) item.name = name; + if (description !== undefined) item.description = description; + item.updated_at = new Date().toISOString(); + store.set(id, item); + logger.info(`✅ sample 수정: ${id}`); + return item; + } + + /** + * Soft delete + */ + public async softDelete(id: string): Promise { + const item = store.get(id); + if (!item || item.is_deleted) { + throw new Error(`항목을 찾을 수 없습니다: ${id}`); + } + item.is_deleted = true; + item.updated_at = new Date().toISOString(); + store.set(id, item); + logger.info(`✅ sample 삭제(soft): ${id}`); + } +}