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/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..1bcb501 --- /dev/null +++ b/src/repositories/noti.repository.ts @@ -0,0 +1,29 @@ +import { Pool } from 'pg'; +import logger from '@/configs/logger.config'; +import { DBError } from '@/exception'; + +export class NotiRepository { + constructor(private pool: Pool) { } + + async getAllNotiPosts(limit: number = 5) { + 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 + LIMIT ${limit}; + `; + + 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/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/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..3167b11 --- /dev/null +++ b/src/types/dto/responses/notiResponse.type.ts @@ -0,0 +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 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; +}