From dd40b5faccd27147af7140726a9d919c47962d30 Mon Sep 17 00:00:00 2001 From: Nuung Date: Mon, 24 Feb 2025 23:27:16 +0900 Subject: [PATCH 1/2] =?UTF-8?q?feature:=20noti=20app=20=EA=B8=B0=EB=B3=B8?= =?UTF-8?q?=20=EC=BB=A8=ED=8A=B8=EB=A1=A4=EB=9F=AC,=20=EC=84=9C=EB=B9=84?= =?UTF-8?q?=EC=8A=A4,=20=EB=A0=88=ED=8F=AC=EC=A7=80=ED=86=A0=EB=A6=AC,=20?= =?UTF-8?q?=ED=83=80=EC=9E=85=20=EC=B6=94=EA=B0=80,=20=EA=B7=B8=EC=97=90?= =?UTF-8?q?=20=EB=94=B0=EB=A5=B8=20=EA=B8=B0=EB=B3=B8=20get=20all=20?= =?UTF-8?q?=EA=B3=B5=EC=A7=80=EC=82=AC=ED=95=AD=20API=20=EA=B8=B0=EB=B3=B8?= =?UTF-8?q?=20=EC=84=B8=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controllers/noti.controller.ts | 28 ++++++++++++++++++++ src/repositories/noti.repository.ts | 28 ++++++++++++++++++++ src/services/noti.service.ts | 10 +++++++ src/types/dto/responses/notiResponse.type.ts | 8 ++++++ src/types/models/NotiPost.type.ts | 9 +++++++ 5 files changed, 83 insertions(+) create mode 100644 src/controllers/noti.controller.ts create mode 100644 src/repositories/noti.repository.ts create mode 100644 src/services/noti.service.ts create mode 100644 src/types/dto/responses/notiResponse.type.ts create mode 100644 src/types/models/NotiPost.type.ts diff --git a/src/controllers/noti.controller.ts b/src/controllers/noti.controller.ts new file mode 100644 index 0000000..29ddb9c --- /dev/null +++ b/src/controllers/noti.controller.ts @@ -0,0 +1,28 @@ +import { NextFunction, Request, RequestHandler, Response } from 'express'; +import logger from '@/configs/logger.config'; +import { NotiService } from "@/services/noti.service"; +import { NotiPostsResponseDto } from "@/types/dto/responses/notiResponse.type"; + +export class NotiController { + constructor(private notiService: NotiService) { } + + getAllNotiPosts: RequestHandler = async ( + req: Request, + res: Response, + next: NextFunction, + ) => { + try { + const result = await this.notiService.getAllNotiPosts(); + const response = new NotiPostsResponseDto( + true, + '전체 noti post 조회에 성공하였습니다.', + { posts: result }, + null, + ); + res.status(200).json(response); + } catch (error) { + logger.error('전체 조회 실패:', error); + next(error); + } + }; +} \ No newline at end of file diff --git a/src/repositories/noti.repository.ts b/src/repositories/noti.repository.ts new file mode 100644 index 0000000..7f8ecdb --- /dev/null +++ b/src/repositories/noti.repository.ts @@ -0,0 +1,28 @@ +import { Pool } from 'pg'; +import logger from '@/configs/logger.config'; +import { DBError } from '@/exception'; + +export class NotiRepository { + constructor(private pool: Pool) { } + + async getAllNotiPosts() { + try { + const query = ` + SELECT + n.id, + n.title, + n.content, + n.created_at, + FROM "noti_notipost" n + WHERE n.is_active = true + ORDER BY n.id DESC; + `; + + const result = await this.pool.query(query); + return result.rows; + } catch (error) { + logger.error('Noti Repo getAllNotiPosts Error : ', error); + throw new DBError('알림 조회 중 문제가 발생했습니다.'); + } + } +} \ No newline at end of file diff --git a/src/services/noti.service.ts b/src/services/noti.service.ts new file mode 100644 index 0000000..059cfff --- /dev/null +++ b/src/services/noti.service.ts @@ -0,0 +1,10 @@ +import { NotiRepository } from "@/repositories/noti.repository"; +import { NotiPost } from "@/types/models/NotiPost.type"; + +export class NotiService { + constructor(private notiRepo: NotiRepository) {} + + async getAllNotiPosts(): Promise { + return await this.notiRepo.getAllNotiPosts(); + } +} \ No newline at end of file diff --git a/src/types/dto/responses/notiResponse.type.ts b/src/types/dto/responses/notiResponse.type.ts new file mode 100644 index 0000000..0791742 --- /dev/null +++ b/src/types/dto/responses/notiResponse.type.ts @@ -0,0 +1,8 @@ +import { BaseResponseDto } from '@/types/dto/responses/baseResponse.type'; +import { NotiPost } from '@/types/models/NotiPost.type'; + +interface NotiPostsResponseData { + posts: NotiPost[]; +} + +export class NotiPostsResponseDto extends BaseResponseDto { } \ No newline at end of file diff --git a/src/types/models/NotiPost.type.ts b/src/types/models/NotiPost.type.ts new file mode 100644 index 0000000..ed3479e --- /dev/null +++ b/src/types/models/NotiPost.type.ts @@ -0,0 +1,9 @@ +export interface NotiPost { + id: number; + title: string; + content: string; + created_at: Date; + // is_active: boolean; + // updated_at: Date; + // author?: User | null; +} From 956fe2574f245d58eb9e9383d376ec356f328bf6 Mon Sep 17 00:00:00 2001 From: Nuung Date: Fri, 28 Feb 2025 01:13:39 +0900 Subject: [PATCH 2/2] =?UTF-8?q?feature:=20noti=20app=20=EC=97=90=EC=84=9C?= =?UTF-8?q?=20=EA=B3=B5=EC=A7=80=20=EA=B8=80=20=EA=B4=80=EB=A0=A8=20API,?= =?UTF-8?q?=20=EB=9D=BC=EC=9A=B0=ED=8C=85=20=EB=B0=8F=20=EC=8A=A4=EC=9B=A8?= =?UTF-8?q?=EA=B1=B0=20=EC=84=B8=ED=8C=85=20=EC=99=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/configs/db.config.ts | 14 +++++-- src/repositories/noti.repository.ts | 9 ++-- src/routes/index.ts | 2 + src/routes/noti.router.ts | 44 ++++++++++++++++++++ src/types/dto/responses/notiResponse.type.ts | 25 +++++++++++ 5 files changed, 86 insertions(+), 8 deletions(-) create mode 100644 src/routes/noti.router.ts diff --git a/src/configs/db.config.ts b/src/configs/db.config.ts index fb0c831..d0797d4 100644 --- a/src/configs/db.config.ts +++ b/src/configs/db.config.ts @@ -6,16 +6,22 @@ const { Pool } = pg; dotenv.config(); -const pool = new Pool({ +// local 세팅 및 접근시 SSL 은 기본 X, production 에서만 추가 +const poolConfig: pg.PoolConfig = { database: process.env.DATABASE_NAME, user: process.env.POSTGRES_USER, host: process.env.POSTGRES_HOST, password: process.env.POSTGRES_PASSWORD, port: Number(process.env.POSTGRES_PORT), - ssl: { +}; + +if (process.env.NODE_ENV === 'production') { + poolConfig.ssl = { rejectUnauthorized: false, - }, -}); + }; +} + +const pool = new Pool(poolConfig); (async () => { const client = await pool.connect(); diff --git a/src/repositories/noti.repository.ts b/src/repositories/noti.repository.ts index 7f8ecdb..1bcb501 100644 --- a/src/repositories/noti.repository.ts +++ b/src/repositories/noti.repository.ts @@ -5,17 +5,18 @@ import { DBError } from '@/exception'; export class NotiRepository { constructor(private pool: Pool) { } - async getAllNotiPosts() { + async getAllNotiPosts(limit: number = 5) { try { const query = ` SELECT n.id, n.title, n.content, - n.created_at, - FROM "noti_notipost" n + n.created_at + FROM noti_notipost n WHERE n.is_active = true - ORDER BY n.id DESC; + ORDER BY n.id DESC + LIMIT ${limit}; `; const result = await this.pool.query(query); diff --git a/src/routes/index.ts b/src/routes/index.ts index 356e122..053be54 100644 --- a/src/routes/index.ts +++ b/src/routes/index.ts @@ -2,6 +2,7 @@ import express, { Router } from 'express'; import UserRouter from './user.router'; import TrackingRouter from './tracking.router'; import PostRouter from './post.router'; +import NotiRouter from './noti.router'; const router: Router = express.Router(); @@ -12,4 +13,5 @@ router.use('/ping', (req, res) => { router.use('/', UserRouter); router.use('/', TrackingRouter); router.use('/', PostRouter); +router.use('/', NotiRouter); export default router; diff --git a/src/routes/noti.router.ts b/src/routes/noti.router.ts new file mode 100644 index 0000000..81ca796 --- /dev/null +++ b/src/routes/noti.router.ts @@ -0,0 +1,44 @@ +import express, { Router } from 'express'; +import pool from '@/configs/db.config'; + +import { authMiddleware } from '@/middlewares/auth.middleware'; +import { NotiRepository } from '@/repositories/noti.repository'; +import { NotiService } from '@/services/noti.service'; +import { NotiController } from '@/controllers/noti.controller'; + +/** + * @swagger + * tags: + * name: Notifications + * description: 알림 관련 API + */ +const router: Router = express.Router(); + +const notiRepository = new NotiRepository(pool); +const notiService = new NotiService(notiRepository); +const notiController = new NotiController(notiService); + +/** + * @swagger + * /notis: + * get: + * summary: 공지 게시글 목록 전체 조회 + * description: 사용자의 알림 게시글 목록을 조회합니다 + * tags: [Notifications] + * security: + * - bearerAuth: [] + * responses: + * 200: + * description: 알림 게시글 목록 조회 성공 + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/NotiPostsResponseDto' + * 401: + * description: 인증되지 않은 사용자 + * 500: + * description: 서버 에러 + */ +router.get('/notis', authMiddleware.login, notiController.getAllNotiPosts); + +export default router; diff --git a/src/types/dto/responses/notiResponse.type.ts b/src/types/dto/responses/notiResponse.type.ts index 0791742..3167b11 100644 --- a/src/types/dto/responses/notiResponse.type.ts +++ b/src/types/dto/responses/notiResponse.type.ts @@ -1,8 +1,33 @@ import { BaseResponseDto } from '@/types/dto/responses/baseResponse.type'; import { NotiPost } from '@/types/models/NotiPost.type'; +/** + * @swagger + * components: + * schemas: + * NotiPostsResponseData: + * type: object + * properties: + * posts: + * type: array + * description: 알림 게시글 목록 + * items: + * $ref: '#/components/schemas/NotiPost' + */ interface NotiPostsResponseData { posts: NotiPost[]; } +/** + * @swagger + * components: + * schemas: + * NotiPostsResponseDto: + * allOf: + * - $ref: '#/components/schemas/BaseResponseDto' + * - type: object + * properties: + * data: + * $ref: '#/components/schemas/NotiPostsResponseData' + */ export class NotiPostsResponseDto extends BaseResponseDto { } \ No newline at end of file