From 675f1028a764e0e80545cfc2fd468c215dc20d5a Mon Sep 17 00:00:00 2001 From: HA0N1 Date: Wed, 1 Jan 2025 01:16:14 +0900 Subject: [PATCH 1/9] refactor: post response dto --- src/configs/db.config.ts | 2 +- src/configs/logger.config.ts | 19 ++++--- src/controllers/post.controller.ts | 53 ++++++++++--------- src/controllers/tracking.controller.ts | 4 +- src/middlewares/validation.middleware.ts | 4 +- src/repositories/post.repository.ts | 52 ++++++++++++------ src/routes/post.router.ts | 10 +++- src/routes/tracking.router.ts | 6 +-- src/routes/user.router.ts | 4 +- src/services/post.service.ts | 13 +++++ src/services/tracking.service.ts | 4 +- src/types/dto/eventRequest.dto.ts | 6 +-- .../dto/requests/getAllPostsQuery.type.ts | 31 +++++++++++ src/types/dto/requests/getPostQuery.type.ts | 24 +++++++++ src/types/dto/responses/baseResponse.type.ts | 17 ++++++ .../{ => dto}/responses/loginResponse.type.ts | 0 src/types/dto/responses/postResponse.type.ts | 48 +++++++++++++++++ .../responses/trackingReponse.type.ts | 0 src/types/index.ts | 16 +++--- src/types/requests/getAllPostsQuery.type.ts | 5 -- src/types/responses/baseResponse.type.ts | 6 --- src/types/responses/postResponse.type.ts | 19 ------- 22 files changed, 244 insertions(+), 99 deletions(-) create mode 100644 src/types/dto/requests/getAllPostsQuery.type.ts create mode 100644 src/types/dto/requests/getPostQuery.type.ts create mode 100644 src/types/dto/responses/baseResponse.type.ts rename src/types/{ => dto}/responses/loginResponse.type.ts (100%) create mode 100644 src/types/dto/responses/postResponse.type.ts rename src/types/{ => dto}/responses/trackingReponse.type.ts (100%) delete mode 100644 src/types/requests/getAllPostsQuery.type.ts delete mode 100644 src/types/responses/baseResponse.type.ts delete mode 100644 src/types/responses/postResponse.type.ts diff --git a/src/configs/db.config.ts b/src/configs/db.config.ts index fb0c831..bb1d2aa 100644 --- a/src/configs/db.config.ts +++ b/src/configs/db.config.ts @@ -11,7 +11,7 @@ const pool = new Pool({ user: process.env.POSTGRES_USER, host: process.env.POSTGRES_HOST, password: process.env.POSTGRES_PASSWORD, - port: Number(process.env.POSTGRES_PORT), + port: parseInt(process.env.POSTGRES_PORT), ssl: { rejectUnauthorized: false, }, diff --git a/src/configs/logger.config.ts b/src/configs/logger.config.ts index cdf7755..9276e09 100644 --- a/src/configs/logger.config.ts +++ b/src/configs/logger.config.ts @@ -12,20 +12,26 @@ if (!fs.existsSync(errorLogDir)) { fs.mkdirSync(errorLogDir); } +const jsonFormat = winston.format.printf((info) => { + return JSON.stringify({ + timestamp: info.timestamp, + level: info.level.toUpperCase(), + logger: 'default', + message: info.message, + }); +}); + const logger = winston.createLogger({ format: winston.format.combine( winston.format.timestamp({ - format: 'YYYY-MM-DD HH:mm:ss', - }), - winston.format.printf((info) => { - return `${info.timestamp} ${info.level}: ${info.message}`; + format: 'YYYY-MM-DD HH:mm:ss.SSSZ', }), + jsonFormat, ), transports: [ new winston.transports.Console({ level: process.env.NODE_ENV === 'production' ? 'info' : 'debug', }), - new winstonDaily({ level: 'debug', datePattern: 'YYYY-MM-DD', @@ -38,10 +44,11 @@ const logger = winston.createLogger({ level: 'error', datePattern: 'YYYY-MM-DD', zippedArchive: true, - filename: `%DATE%error.log`, + filename: `%DATE%-error.log`, dirname: errorLogDir, maxFiles: '7d', }), ], }); + export default logger; diff --git a/src/controllers/post.controller.ts b/src/controllers/post.controller.ts index 551773b..5f1c80d 100644 --- a/src/controllers/post.controller.ts +++ b/src/controllers/post.controller.ts @@ -1,48 +1,31 @@ import { NextFunction, Request, RequestHandler, Response } from 'express'; import logger from '../configs/logger.config'; import { PostService } from '../services/post.service'; -import { GetAllPostsQuery, PostResponse } from '../types'; +import { GetAllPostsQuery, PostsResponseDto, PostResponseDto, GetPostQuery, PostParam } from '../types'; export class PostController { constructor(private postService: PostService) {} - private validateQueryParams(query: GetAllPostsQuery): { - cursor: string | undefined; - sort: string; - isAsc: boolean; - } { - return { - cursor: query.cursor, - sort: query.sort || '', - isAsc: query.asc === 'true', - }; - } - getAllPost: RequestHandler = async ( req: Request, - res: Response, + res: Response, next: NextFunction, ) => { try { const { id } = req.user; - const { cursor, sort, isAsc } = this.validateQueryParams(req.query); + const { cursor, sort, asc } = req.query; - const result = await this.postService.getAllposts(id, cursor, sort, isAsc); + const result = await this.postService.getAllposts(id, cursor, sort, asc); - res.status(200).json({ - success: true, - message: 'post 전체 조회에 성공하였습니다.', - data: { - nextCursor: result.nextCursor, - posts: result.posts, - }, - error: null, - }); + const response = new PostsResponseDto(true, 'post 조회에 성공하였습니다.', result.nextCursor, result.posts, null); + + res.status(200).json(response); } catch (error) { logger.error('전체 조회 실패:', error); next(error); } }; + getAllPostStatistics: RequestHandler = async (req: Request, res: Response, next: NextFunction) => { try { const { id } = req.user; @@ -61,4 +44,24 @@ export class PostController { next(error); } }; + + getPost: RequestHandler = async ( + req: Request, + res: Response, + next: NextFunction, + ) => { + try { + const postId = Number(req.params.postId); + const { start, end } = req.query; + + const post = await this.postService.getPost(postId, start, end); + + const response = new PostResponseDto(true, 'post 조회에 성공하였습니다.', post, null); + + res.status(200).json(response); + } catch (error) { + logger.error('단건 조회 실패 : ', error); + next(error); + } + }; } diff --git a/src/controllers/tracking.controller.ts b/src/controllers/tracking.controller.ts index 3d96182..44a94ab 100644 --- a/src/controllers/tracking.controller.ts +++ b/src/controllers/tracking.controller.ts @@ -8,10 +8,10 @@ export class TrackingController { event = (async (req: Request, res: Response, next: NextFunction) => { try { - const { type } = req.body; + const { eventType } = req.body; const { id } = req.user; - await this.trackingService.tracking(type, id); + await this.trackingService.tracking(eventType, id); return res.status(200).json({ success: true, message: '이벤트 데이터 저장완료', data: {}, error: null }); } catch (error) { logger.error('user tracking 실패 : ', error); diff --git a/src/middlewares/validation.middleware.ts b/src/middlewares/validation.middleware.ts index 7285008..2434c1c 100644 --- a/src/middlewares/validation.middleware.ts +++ b/src/middlewares/validation.middleware.ts @@ -3,10 +3,10 @@ import { plainToInstance } from 'class-transformer'; import { validate } from 'class-validator'; import logger from '../configs/logger.config'; -type RequestKey = 'body' | 'user'; +type RequestKey = 'body' | 'user' | 'query'; // eslint-disable-next-line @typescript-eslint/no-explicit-any -export const validateDto = (dtoClass: new (...args: any) => T, key: RequestKey) => { +export const validateRequestDto = (dtoClass: new (...args: any) => T, key: RequestKey) => { return (async (req: Request, res: Response, next: NextFunction) => { try { const value = plainToInstance(dtoClass, req[key]); diff --git a/src/repositories/post.repository.ts b/src/repositories/post.repository.ts index ba1899c..4050670 100644 --- a/src/repositories/post.repository.ts +++ b/src/repositories/post.repository.ts @@ -5,21 +5,15 @@ import { DBError } from '../exception'; export class PostRepository { constructor(private pool: Pool) {} - async findPostsByUserId( - userId: number, - cursor?: string, - sort?: string, - isAsc?: boolean, - limit: number = 15 - ) { + async findPostsByUserId(userId: number, cursor?: string, sort?: string, isAsc?: boolean, limit: number = 15) { try { // 1) 정렬 컬럼 매핑 let sortCol = 'p.released_at'; switch (sort) { - case 'daily_view_count': + case 'dailyViewCount': sortCol = 'pds.daily_view_count'; break; - case 'daily_like_count': + case 'dailyLikeCount': sortCol = 'pds.daily_like_count'; break; default: @@ -72,12 +66,12 @@ export class PostRepository { daily_like_count, date FROM posts_postdailystatistics - WHERE date::date = NOW()::date + WHERE (date AT TIME ZONE 'Asia/Seoul' AT TIME ZONE 'UTC')::date = (NOW() AT TIME ZONE 'UTC')::date ) pds ON p.id = pds.post_id LEFT JOIN ( SELECT post_id, daily_view_count, daily_like_count, date FROM posts_postdailystatistics - WHERE date::date = (NOW() - INTERVAL '1 day')::date + WHERE (date AT TIME ZONE 'Asia/Seoul' AT TIME ZONE 'UTC')::date = (NOW() AT TIME ZONE 'UTC' - INTERVAL '1 day')::date ) yesterday_stats ON p.id = yesterday_stats.post_id WHERE p.user_id = $1 AND (pds.post_id IS NOT NULL OR yesterday_stats.post_id IS NOT NULL) @@ -102,9 +96,9 @@ export class PostRepository { // nextCursor = `${정렬 컬럼 값},${p.id}` // 예: 만약 sortCol이 p.title인 경우, lastPost.title + ',' + lastPost.id let sortValueForCursor = ''; - if (sort === 'daily_view_count') { + if (sort === 'dailyViewCount') { sortValueForCursor = lastPost.daily_view_count; - } else if (sort === 'daily_like_count') { + } else if (sort === 'dailyLikeCount') { sortValueForCursor = lastPost.daily_like_count; } else { sortValueForCursor = new Date(lastPost.post_released_at).toISOString(); @@ -133,7 +127,6 @@ export class PostRepository { } async getYesterdayAndTodayViewLikeStats(userId: number) { - // pds.updated_at 은 FE 화면을 위해 억지로 9h 시간 더한 값임 주의 try { const query = ` @@ -147,12 +140,12 @@ export class PostRepository { LEFT JOIN ( SELECT post_id, daily_view_count, daily_like_count, updated_at FROM posts_postdailystatistics - WHERE date::date = NOW()::date + WHERE (date AT TIME ZONE 'Asia/Seoul' AT TIME ZONE 'UTC')::date = (NOW() AT TIME ZONE 'UTC')::date ) pds ON p.id = pds.post_id LEFT JOIN ( SELECT post_id, daily_view_count, daily_like_count FROM posts_postdailystatistics - WHERE date::date = (NOW() - INTERVAL '1 day')::date + WHERE (date AT TIME ZONE 'Asia/Seoul' AT TIME ZONE 'UTC')::date = (NOW() AT TIME ZONE 'UTC' - INTERVAL '1 day')::date ) yesterday_stats ON p.id = yesterday_stats.post_id WHERE p.user_id = $1 `; @@ -165,4 +158,31 @@ export class PostRepository { throw new DBError('전체 post 통계 조회 중 문제가 발생했습니다.'); } } + + async findPostByPostId(postId: number, start?: string, end?: string) { + try { + let query = ` + SELECT + (pds.date AT TIME ZONE 'Asia/Seoul') AT TIME ZONE 'UTC' AS date, + pds.daily_view_count, + pds.daily_like_count + FROM posts_postdailystatistics pds + WHERE pds.post_id = $1 + `; + + const values: (number | string)[] = [postId]; + + if (start && end) { + query += ` AND (pds.date AT TIME ZONE 'Asia/Seoul' AT TIME ZONE 'UTC')::date >= ($2 AT TIME ZONE 'Asia/Seoul' AT TIME ZONE 'UTC')::date + AND (pds.date AT TIME ZONE 'Asia/Seoul' AT TIME ZONE 'UTC')::date <= ($3 AT TIME ZONE 'Asia/Seoul' AT TIME ZONE 'UTC')::date`; + values.push(start, end); + } + + const result = await this.pool.query(query, values); + return result.rows; + } catch (error) { + logger.error('Post Repo findPostByPostId error : ', error); + throw new DBError('단건 post 조회 중 문제가 발생했습니다.'); + } + } } diff --git a/src/routes/post.router.ts b/src/routes/post.router.ts index b56167a..9ddc887 100644 --- a/src/routes/post.router.ts +++ b/src/routes/post.router.ts @@ -5,6 +5,8 @@ import { authMiddleware } from '../middlewares/auth.middleware'; import { PostRepository } from '../repositories/post.repository'; import { PostService } from '../services/post.service'; import { PostController } from '../controllers/post.controller'; +import { validateRequestDto } from 'src/middlewares/validation.middleware'; +import { GetAllPostsQueryDto } from 'src/types/dto/requests/getAllPostsQuery.type'; const router: Router = express.Router(); dotenv.config(); @@ -13,7 +15,13 @@ const postRepository = new PostRepository(pool); const postService = new PostService(postRepository); const postController = new PostController(postService); -router.get('/posts', authMiddleware.verify, postController.getAllPost); +router.get( + '/posts', + authMiddleware.verify, + validateRequestDto(GetAllPostsQueryDto, 'query'), + postController.getAllPost, +); router.get('/posts-stats', authMiddleware.verify, postController.getAllPostStatistics); +router.get('/post/:postId', authMiddleware.verify, postController.getPost); export default router; diff --git a/src/routes/tracking.router.ts b/src/routes/tracking.router.ts index 313c59c..540bf16 100644 --- a/src/routes/tracking.router.ts +++ b/src/routes/tracking.router.ts @@ -4,7 +4,7 @@ import dotenv from 'dotenv'; import { TrackingRepository } from '../repositories/tracking.repository'; import { TrackingService } from '../services/tracking.service'; import { TrackingController } from '../controllers/tracking.controller'; -import { validateDto } from '../middlewares/validation.middleware'; +import { validateRequestDto } from '../middlewares/validation.middleware'; import { EventRequestDto } from '../types'; import { authMiddleware } from '../middlewares/auth.middleware'; import { StayTimeRequestDto } from '../types'; @@ -16,7 +16,7 @@ const trackingRepository = new TrackingRepository(pool); const trackingService = new TrackingService(trackingRepository); const trackingController = new TrackingController(trackingService); -router.post('/event', authMiddleware.verify, validateDto(EventRequestDto, 'body'), trackingController.event); -router.post('/stay', authMiddleware.verify, validateDto(StayTimeRequestDto, 'body'), trackingController.stay); +router.post('/event', authMiddleware.verify, validateRequestDto(EventRequestDto, 'body'), trackingController.event); +router.post('/stay', authMiddleware.verify, validateRequestDto(StayTimeRequestDto, 'body'), trackingController.stay); export default router; diff --git a/src/routes/user.router.ts b/src/routes/user.router.ts index aecac14..f1d515b 100644 --- a/src/routes/user.router.ts +++ b/src/routes/user.router.ts @@ -4,7 +4,7 @@ import { UserRepository } from '../repositories/user.repository'; import { UserService } from '../services/user.service'; import pool from '../configs/db.config'; import { authMiddleware } from '../middlewares/auth.middleware'; -import { validateDto } from '../middlewares/validation.middleware'; +import { validateRequestDto } from '../middlewares/validation.middleware'; import dotenv from 'dotenv'; import { VelogUserLoginDto } from '../types'; @@ -15,7 +15,7 @@ const userRepository = new UserRepository(pool); const userService = new UserService(userRepository); const userController = new UserController(userService); -router.post('/login', authMiddleware.login, validateDto(VelogUserLoginDto, 'user'), userController.login); +router.post('/login', authMiddleware.login, validateRequestDto(VelogUserLoginDto, 'user'), userController.login); router.post('/logout', authMiddleware.login, userController.logout); router.get('/me', authMiddleware.login, userController.fetchCurrentUser); export default router; diff --git a/src/services/post.service.ts b/src/services/post.service.ts index fa64c4a..6ef0367 100644 --- a/src/services/post.service.ts +++ b/src/services/post.service.ts @@ -47,7 +47,20 @@ export class PostService { throw error; } } + async getTotalPostCounts(id: number) { return await this.postRepo.getTotalPostCounts(id); } + + async getPost(postId: number, start?: string, end?: string) { + const posts = await this.postRepo.findPostByPostId(postId, start, end); + + const transformedPosts = posts.map((post) => ({ + date: post.date, + dailyViewCount: parseInt(post.daily_view_count), + dailyLikeCount: parseInt(post.daily_like_count), + })); + + return transformedPosts; + } } diff --git a/src/services/tracking.service.ts b/src/services/tracking.service.ts index 51ed348..350acd5 100644 --- a/src/services/tracking.service.ts +++ b/src/services/tracking.service.ts @@ -6,8 +6,8 @@ import { BadRequestError } from '../exception'; export class TrackingService { constructor(private trackingRepo: TrackingRepository) {} - async tracking(type: EventRequestDto, id: number) { - return await this.trackingRepo.createEvent(type, id); + async tracking(eventType: EventRequestDto, id: number) { + return await this.trackingRepo.createEvent(eventType, id); } async stay(data: StayTimeRequestDto, userId: number) { try { diff --git a/src/types/dto/eventRequest.dto.ts b/src/types/dto/eventRequest.dto.ts index 5c675de..b210709 100644 --- a/src/types/dto/eventRequest.dto.ts +++ b/src/types/dto/eventRequest.dto.ts @@ -4,9 +4,9 @@ import { UserEventType } from '../userEvent.type'; export class EventRequestDto { @IsEnum(UserEventType) @IsNotEmpty() - type: UserEventType; + eventType: UserEventType; - constructor(type: UserEventType) { - this.type = type; + constructor(eventType: UserEventType) { + this.eventType = eventType; } } diff --git a/src/types/dto/requests/getAllPostsQuery.type.ts b/src/types/dto/requests/getAllPostsQuery.type.ts new file mode 100644 index 0000000..ee6bec4 --- /dev/null +++ b/src/types/dto/requests/getAllPostsQuery.type.ts @@ -0,0 +1,31 @@ +import { Transform } from 'class-transformer'; +import { IsBoolean, IsOptional, IsString } from 'class-validator'; + +export type PostSortType = '' | 'dailyViewCount' | 'dailyLikeCount'; + +export interface GetAllPostsQuery { + cursor?: string; + sort?: PostSortType; + asc?: boolean; +} + +export class GetAllPostsQueryDto { + @IsOptional() + @IsString() + cursor?: string; + + @IsOptional() + @IsString() + sort?: PostSortType; + + @IsOptional() + @IsBoolean() + @Transform(({ value }) => value === 'true') + asc?: boolean; + + constructor(cursor: string | undefined, sort: PostSortType, asc: boolean = false) { + this.cursor = cursor; + this.sort = sort || ''; + this.asc = asc; + } +} diff --git a/src/types/dto/requests/getPostQuery.type.ts b/src/types/dto/requests/getPostQuery.type.ts new file mode 100644 index 0000000..3250282 --- /dev/null +++ b/src/types/dto/requests/getPostQuery.type.ts @@ -0,0 +1,24 @@ +import { IsDate, IsOptional } from 'class-validator'; + +export interface PostParam { + postId?: string; +} + +export interface GetPostQuery { + start?: string; + end?: string; +} + +export class GetPostQueryDto { + @IsOptional() + @IsDate() + start?: string; + @IsOptional() + @IsDate() + end?: string; + + constructor(start: string, end: string) { + this.start = start; + this.end = end; + } +} diff --git a/src/types/dto/responses/baseResponse.type.ts b/src/types/dto/responses/baseResponse.type.ts new file mode 100644 index 0000000..ee6a780 --- /dev/null +++ b/src/types/dto/responses/baseResponse.type.ts @@ -0,0 +1,17 @@ +export class BaseResponseDto { + success: boolean; + message: string; + data?: T; + error?: string | null; + + constructor(success: boolean, message: string, data?: T, error?: string | null) { + this.success = success; + this.message = message; + this.data = data; + this.error = error; + } + + static success(message: string, data?: T): BaseResponseDto { + return new BaseResponseDto(true, message, data, null); + } +} diff --git a/src/types/responses/loginResponse.type.ts b/src/types/dto/responses/loginResponse.type.ts similarity index 100% rename from src/types/responses/loginResponse.type.ts rename to src/types/dto/responses/loginResponse.type.ts diff --git a/src/types/dto/responses/postResponse.type.ts b/src/types/dto/responses/postResponse.type.ts new file mode 100644 index 0000000..fa89722 --- /dev/null +++ b/src/types/dto/responses/postResponse.type.ts @@ -0,0 +1,48 @@ +import { BaseResponseDto } from './baseResponse.type'; + +// ------ 전체 조회 ------ +interface GetAllPostType { + id: number; + title: string; + views: number; + likes: number; + yesterdayViews: number; + yesterdayLikes: number; + createdAt: string; + releasedAt: string; +} + +interface PostsResponseData { + nextCursor: string | null; + posts: GetAllPostType[]; +} + +export class PostsResponseDto extends BaseResponseDto { + constructor( + success: boolean, + message: string, + nextCursor: string | null, + posts: GetAllPostType[], + error: string | null, + ) { + const data = { nextCursor, posts }; + super(success, message, data, error); + } +} +// ------ 단건 조회 ------ +interface GetPostType { + date: string; + dailyViewCount: number; + dailyLikeCount: number; +} + +interface PostResponseData { + post: GetPostType[]; +} + +export class PostResponseDto extends BaseResponseDto { + constructor(success: boolean, message: string, post: GetPostType[], error: string | null) { + const data = { post }; + super(success, message, data, error); + } +} diff --git a/src/types/responses/trackingReponse.type.ts b/src/types/dto/responses/trackingReponse.type.ts similarity index 100% rename from src/types/responses/trackingReponse.type.ts rename to src/types/dto/responses/trackingReponse.type.ts diff --git a/src/types/index.ts b/src/types/index.ts index 3132ca0..fd3e444 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -3,13 +3,17 @@ export type { Post } from './models/Post.type'; export type { PostDailyStatistics } from './models/PostDailyStatistics.type'; export type { PostStatistics } from './models/PostStatistics.type'; export type { UserEventTracking } from './models/UserEventTracking.type'; +export type { UserEventType } from './userEvent.type'; +export type { VelogUserLoginResponse } from './velog.type'; +export type { LoginResponse } from './dto/responses/loginResponse.type'; +export type { TrackingResponse } from './dto/responses/trackingReponse.type'; +export type { GetAllPostsQuery } from './dto/requests/getAllPostsQuery.type'; +export type { GetPostQuery, PostParam } from './dto/requests/getPostQuery.type'; + export { EventRequestDto } from './dto/eventRequest.dto'; export { UserWithTokenDto } from './dto/userWithToken.dto'; export { VelogUserLoginDto } from './dto/velogUser.dto'; export { StayTimeRequestDto } from './dto/stayTimeRequest.dto'; -export type { UserEventType } from './userEvent.type'; -export type { VelogUserLoginResponse } from './velog.type'; -export type { PostResponse } from './responses/postResponse.type'; -export type { LoginResponse } from './responses/loginResponse.type'; -export type { TrackingResponse } from './responses/trackingReponse.type'; -export type { GetAllPostsQuery } from './requests/getAllPostsQuery.type'; +export { PostsResponseDto, PostResponseDto } from './dto/responses/postResponse.type'; +export { GetAllPostsQueryDto } from './dto/requests/getAllPostsQuery.type'; +export { GetPostQueryDto } from './dto/requests/getPostQuery.type'; diff --git a/src/types/requests/getAllPostsQuery.type.ts b/src/types/requests/getAllPostsQuery.type.ts deleted file mode 100644 index e1409fd..0000000 --- a/src/types/requests/getAllPostsQuery.type.ts +++ /dev/null @@ -1,5 +0,0 @@ -export interface GetAllPostsQuery { - cursor?: string; - sort?: '' | 'daily_view_count' | 'daily_like_count'; - asc?: string; -} diff --git a/src/types/responses/baseResponse.type.ts b/src/types/responses/baseResponse.type.ts deleted file mode 100644 index 941e1a3..0000000 --- a/src/types/responses/baseResponse.type.ts +++ /dev/null @@ -1,6 +0,0 @@ -export interface BaseResponse { - success: boolean; - message: string; - data: T | null; - error: string | null; -} diff --git a/src/types/responses/postResponse.type.ts b/src/types/responses/postResponse.type.ts deleted file mode 100644 index 1dcfb25..0000000 --- a/src/types/responses/postResponse.type.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { BaseResponse } from './baseResponse.type'; - -interface PostType { - id: number; - title: string; - views: number; - likes: number; - yesterdayViews: number; - yesterdayLikes: number; - createdAt: string; - releasedAt: string; -} - -interface PostResponseData { - nextCursor: string | null; - posts: PostType[]; -} - -export type PostResponse = BaseResponse; From 22f89cc697a3b6feac3e0d76299b3654585c5510 Mon Sep 17 00:00:00 2001 From: HA0N1 Date: Wed, 1 Jan 2025 01:58:31 +0900 Subject: [PATCH 2/9] refactor: post response dto v2 --- src/controllers/post.controller.ts | 40 ++++++++++++++----- .../dto/{ => requests}/eventRequest.dto.ts | 2 +- .../dto/{ => requests}/stayTimeRequest.dto.ts | 0 src/types/dto/responses/loginResponse.type.ts | 4 +- src/types/dto/responses/postResponse.type.ts | 36 +++++++++++++++++ .../dto/responses/trackingReponse.type.ts | 4 +- src/types/index.ts | 5 ++- 7 files changed, 74 insertions(+), 17 deletions(-) rename src/types/dto/{ => requests}/eventRequest.dto.ts (81%) rename src/types/dto/{ => requests}/stayTimeRequest.dto.ts (100%) diff --git a/src/controllers/post.controller.ts b/src/controllers/post.controller.ts index 5f1c80d..54cdc93 100644 --- a/src/controllers/post.controller.ts +++ b/src/controllers/post.controller.ts @@ -1,7 +1,14 @@ import { NextFunction, Request, RequestHandler, Response } from 'express'; import logger from '../configs/logger.config'; import { PostService } from '../services/post.service'; -import { GetAllPostsQuery, PostsResponseDto, PostResponseDto, GetPostQuery, PostParam } from '../types'; +import { + GetAllPostsQuery, + PostsResponseDto, + PostResponseDto, + GetPostQuery, + PostParam, + PostStatisticsResponseDto, +} from '../types'; export class PostController { constructor(private postService: PostService) {} @@ -17,7 +24,13 @@ export class PostController { const result = await this.postService.getAllposts(id, cursor, sort, asc); - const response = new PostsResponseDto(true, 'post 조회에 성공하였습니다.', result.nextCursor, result.posts, null); + const response = new PostsResponseDto( + true, + '전체 post 조회에 성공하였습니다.', + result.nextCursor, + result.posts, + null, + ); res.status(200).json(response); } catch (error) { @@ -26,19 +39,26 @@ export class PostController { } }; - getAllPostStatistics: RequestHandler = async (req: Request, res: Response, next: NextFunction) => { + getAllPostStatistics: RequestHandler = async ( + req: Request, + res: Response, + next: NextFunction, + ) => { try { const { id } = req.user; const result = await this.postService.getAllPostStatistics(id); const totalPostCount = await this.postService.getTotalPostCounts(id); - res.status(200).json({ - success: true, - message: 'post 전체 통계 조회에 성공하였습니다.', - data: { totalPostCount, stats: result }, - error: null, - }); + const response = new PostStatisticsResponseDto( + true, + '전체 post 통계 조회에 성공하였습니다.', + totalPostCount, + result, + null, + ); + + res.status(200).json(response); } catch (error) { logger.error('전체 통계 조회 실패:', error); next(error); @@ -56,7 +76,7 @@ export class PostController { const post = await this.postService.getPost(postId, start, end); - const response = new PostResponseDto(true, 'post 조회에 성공하였습니다.', post, null); + const response = new PostResponseDto(true, '단건 post 조회에 성공하였습니다.', post, null); res.status(200).json(response); } catch (error) { diff --git a/src/types/dto/eventRequest.dto.ts b/src/types/dto/requests/eventRequest.dto.ts similarity index 81% rename from src/types/dto/eventRequest.dto.ts rename to src/types/dto/requests/eventRequest.dto.ts index b210709..689031c 100644 --- a/src/types/dto/eventRequest.dto.ts +++ b/src/types/dto/requests/eventRequest.dto.ts @@ -1,5 +1,5 @@ import { IsEnum, IsNotEmpty } from 'class-validator'; -import { UserEventType } from '../userEvent.type'; +import { UserEventType } from '../../userEvent.type'; export class EventRequestDto { @IsEnum(UserEventType) diff --git a/src/types/dto/stayTimeRequest.dto.ts b/src/types/dto/requests/stayTimeRequest.dto.ts similarity index 100% rename from src/types/dto/stayTimeRequest.dto.ts rename to src/types/dto/requests/stayTimeRequest.dto.ts diff --git a/src/types/dto/responses/loginResponse.type.ts b/src/types/dto/responses/loginResponse.type.ts index 7374882..aeea59a 100644 --- a/src/types/dto/responses/loginResponse.type.ts +++ b/src/types/dto/responses/loginResponse.type.ts @@ -1,4 +1,4 @@ -import { BaseResponse } from './baseResponse.type'; +import { BaseResponseDto } from './baseResponse.type'; interface ProfileType { thumbnail: string; @@ -10,4 +10,4 @@ interface LoginResponseData { profile: ProfileType; } -export type LoginResponse = BaseResponse; +export type LoginResponse = BaseResponseDto; diff --git a/src/types/dto/responses/postResponse.type.ts b/src/types/dto/responses/postResponse.type.ts index fa89722..9021abe 100644 --- a/src/types/dto/responses/postResponse.type.ts +++ b/src/types/dto/responses/postResponse.type.ts @@ -46,3 +46,39 @@ export class PostResponseDto extends BaseResponseDto { super(success, message, data, error); } } +/** + * "data": { + "totalPostCount": 90, + "stats": { + "totalViews": 1470, + "totalLikes": 11, + "yesterdayViews": 1469, + "yesterdayLikes": 11, + "lastUpdatedDate": "2024-12-31T00:15:23.704Z" + } + */ +// ------ 전체 통계 ------ +interface PostStatisticsType { + totalViews: number; + totalLikes: number; + yesterdayViews: number; + yesterdayLikes: number; + lastUpdatedDate: string; +} + +interface PostStatisticsData { + totalPostCount: number; + stats: PostStatisticsType; +} +export class PostStatisticsResponseDto extends BaseResponseDto { + constructor( + success: boolean, + message: string, + totalPostCount: number, + stats: PostStatisticsType, + error: string | null, + ) { + const data = { totalPostCount, stats }; + super(success, message, data, error); + } +} diff --git a/src/types/dto/responses/trackingReponse.type.ts b/src/types/dto/responses/trackingReponse.type.ts index c3e0c8c..6f5b9b2 100644 --- a/src/types/dto/responses/trackingReponse.type.ts +++ b/src/types/dto/responses/trackingReponse.type.ts @@ -1,4 +1,4 @@ -import { BaseResponse } from './baseResponse.type'; +import { BaseResponseDto } from './baseResponse.type'; type TrackingResponseData = Record; -export type TrackingResponse = BaseResponse; +export type TrackingResponse = BaseResponseDto; diff --git a/src/types/index.ts b/src/types/index.ts index fd3e444..7a81d0a 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -10,10 +10,11 @@ export type { TrackingResponse } from './dto/responses/trackingReponse.type'; export type { GetAllPostsQuery } from './dto/requests/getAllPostsQuery.type'; export type { GetPostQuery, PostParam } from './dto/requests/getPostQuery.type'; -export { EventRequestDto } from './dto/eventRequest.dto'; +export { PostStatisticsResponseDto } from './dto/responses/postResponse.type'; +export { EventRequestDto } from './dto/requests/eventRequest.dto'; export { UserWithTokenDto } from './dto/userWithToken.dto'; export { VelogUserLoginDto } from './dto/velogUser.dto'; -export { StayTimeRequestDto } from './dto/stayTimeRequest.dto'; +export { StayTimeRequestDto } from './dto/requests/stayTimeRequest.dto'; export { PostsResponseDto, PostResponseDto } from './dto/responses/postResponse.type'; export { GetAllPostsQueryDto } from './dto/requests/getAllPostsQuery.type'; export { GetPostQueryDto } from './dto/requests/getPostQuery.type'; From 238812c107a370c6490213a3b9c0d7c1ca70a199 Mon Sep 17 00:00:00 2001 From: HA0N1 Date: Wed, 1 Jan 2025 02:43:11 +0900 Subject: [PATCH 3/9] =?UTF-8?q?refactor:=20=EB=AA=A8=EB=93=A0=20api=20resp?= =?UTF-8?q?onse=20dto=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/configs/db.config.ts | 2 +- src/controllers/tracking.controller.ts | 20 ++++++---- src/controllers/user.controller.ts | 38 ++++++++++--------- src/types/dto/responses/baseResponse.type.ts | 4 -- src/types/dto/responses/emptyReponse.type.ts | 9 +++++ src/types/dto/responses/loginResponse.type.ts | 14 ++++++- src/types/dto/responses/postResponse.type.ts | 14 ++----- .../dto/responses/trackingReponse.type.ts | 4 -- src/types/index.ts | 4 +- 9 files changed, 62 insertions(+), 47 deletions(-) create mode 100644 src/types/dto/responses/emptyReponse.type.ts delete mode 100644 src/types/dto/responses/trackingReponse.type.ts diff --git a/src/configs/db.config.ts b/src/configs/db.config.ts index bb1d2aa..3a5f4fd 100644 --- a/src/configs/db.config.ts +++ b/src/configs/db.config.ts @@ -11,7 +11,7 @@ const pool = new Pool({ user: process.env.POSTGRES_USER, host: process.env.POSTGRES_HOST, password: process.env.POSTGRES_PASSWORD, - port: parseInt(process.env.POSTGRES_PORT), + port: Number(process.env.DATA_PORT), ssl: { rejectUnauthorized: false, }, diff --git a/src/controllers/tracking.controller.ts b/src/controllers/tracking.controller.ts index 44a94ab..843bf27 100644 --- a/src/controllers/tracking.controller.ts +++ b/src/controllers/tracking.controller.ts @@ -1,34 +1,40 @@ import { NextFunction, Request, RequestHandler, Response } from 'express'; import logger from '../configs/logger.config'; import { TrackingService } from '../services/tracking.service'; -import { TrackingResponse } from '../types'; +import { EmptyResponseDto } from '../types'; export class TrackingController { constructor(private trackingService: TrackingService) {} - event = (async (req: Request, res: Response, next: NextFunction) => { + event: RequestHandler = async (req: Request, res: Response, next: NextFunction) => { try { const { eventType } = req.body; const { id } = req.user; await this.trackingService.tracking(eventType, id); - return res.status(200).json({ success: true, message: '이벤트 데이터 저장완료', data: {}, error: null }); + + const response = new EmptyResponseDto(true, '이벤트 데이터 저장완료', null); + + res.status(200).json(response); } catch (error) { logger.error('user tracking 실패 : ', error); next(error); } - }) as RequestHandler; + }; - stay = (async (req: Request, res: Response, next: NextFunction) => { + stay: RequestHandler = async (req: Request, res: Response, next: NextFunction) => { try { const { loadDate, unloadDate } = req.body; const { id } = req.user; await this.trackingService.stay({ loadDate, unloadDate }, id); - return res.status(200).json({ success: true, message: '체류시간 데이터 저장 완료', data: {}, error: null }); + + const response = new EmptyResponseDto(true, '체류시간 데이터 저장 완료', null); + + res.status(200).json(response); } catch (error) { logger.error('user stay time 저장 실패 : ', error); next(error); } - }) as RequestHandler; + }; } diff --git a/src/controllers/user.controller.ts b/src/controllers/user.controller.ts index 0c25844..99391e8 100644 --- a/src/controllers/user.controller.ts +++ b/src/controllers/user.controller.ts @@ -1,6 +1,6 @@ import { NextFunction, Request, Response, RequestHandler, CookieOptions } from 'express'; import logger from '../configs/logger.config'; -import { LoginResponse, UserWithTokenDto } from '../types'; +import { EmptyResponseDto, LoginResponseDto, UserWithTokenDto } from '../types'; import { UserService } from '../services/user.service'; export class UserController { constructor(private userService: UserService) {} @@ -21,7 +21,7 @@ export class UserController { return baseOptions; } - login: RequestHandler = async (req: Request, res: Response, next: NextFunction): Promise => { + login: RequestHandler = async (req: Request, res: Response, next: NextFunction): Promise => { try { const { id, email, profile, username } = req.user; const { accessToken, refreshToken } = req.tokens; @@ -35,32 +35,36 @@ export class UserController { res.cookie('access_token', accessToken, this.cookieOption()); res.cookie('refresh_token', refreshToken, this.cookieOption()); - res.status(200).json({ - success: true, - message: '로그인에 성공하였습니다.', - data: { id: isExistUser.id, username, profile }, - error: null, - }); + const response = new LoginResponseDto(true, '로그인에 성공하였습니다.', isExistUser.id, username, profile, null); + + res.status(200).json(response); } catch (error) { logger.error('로그인 실패 : ', error); next(error); } }; - logout: RequestHandler = async (req: Request, res: Response) => { + logout: RequestHandler = async (req: Request, res: Response) => { res.clearCookie('access_token'); res.clearCookie('refresh_token'); - res.status(200).json({ success: true, message: '로그아웃에 성공하였습니다.', data: {}, error: null }); + const response = new EmptyResponseDto(true, '로그아웃에 성공하였습니다.', null); + + res.status(200).json(response); }; - fetchCurrentUser: RequestHandler = (req: Request, res: Response) => { + fetchCurrentUser: RequestHandler = (req: Request, res: Response) => { const { user } = req; - res.status(200).json({ - success: true, - message: '프로필 조회에 성공하였습니다.', - data: { user }, - error: null, - }); + + const response = new LoginResponseDto( + true, + '유저 정보 조회에 성공하였습니다.', + user.id, + user.username, + user.profile, + null, + ); + + res.status(200).json(response); }; } diff --git a/src/types/dto/responses/baseResponse.type.ts b/src/types/dto/responses/baseResponse.type.ts index ee6a780..29e0a2b 100644 --- a/src/types/dto/responses/baseResponse.type.ts +++ b/src/types/dto/responses/baseResponse.type.ts @@ -10,8 +10,4 @@ export class BaseResponseDto { this.data = data; this.error = error; } - - static success(message: string, data?: T): BaseResponseDto { - return new BaseResponseDto(true, message, data, null); - } } diff --git a/src/types/dto/responses/emptyReponse.type.ts b/src/types/dto/responses/emptyReponse.type.ts new file mode 100644 index 0000000..16cf08f --- /dev/null +++ b/src/types/dto/responses/emptyReponse.type.ts @@ -0,0 +1,9 @@ +import { BaseResponseDto } from './baseResponse.type'; + +// eslint-disable-next-line @typescript-eslint/no-empty-object-type +interface EmptyResponseData {} +export class EmptyResponseDto extends BaseResponseDto { + constructor(success: boolean, message: string, error: string | null = null) { + super(success, message, {}, error); + } +} diff --git a/src/types/dto/responses/loginResponse.type.ts b/src/types/dto/responses/loginResponse.type.ts index aeea59a..8bf9f49 100644 --- a/src/types/dto/responses/loginResponse.type.ts +++ b/src/types/dto/responses/loginResponse.type.ts @@ -10,4 +10,16 @@ interface LoginResponseData { profile: ProfileType; } -export type LoginResponse = BaseResponseDto; +export class LoginResponseDto extends BaseResponseDto { + constructor( + success: boolean, + message: string, + id: number, + username: string, + profile: ProfileType, + error: string | null, + ) { + const data = { id, username, profile }; + super(success, message, data, error); + } +} diff --git a/src/types/dto/responses/postResponse.type.ts b/src/types/dto/responses/postResponse.type.ts index 9021abe..819d2dc 100644 --- a/src/types/dto/responses/postResponse.type.ts +++ b/src/types/dto/responses/postResponse.type.ts @@ -29,6 +29,7 @@ export class PostsResponseDto extends BaseResponseDto { super(success, message, data, error); } } + // ------ 단건 조회 ------ interface GetPostType { date: string; @@ -46,17 +47,7 @@ export class PostResponseDto extends BaseResponseDto { super(success, message, data, error); } } -/** - * "data": { - "totalPostCount": 90, - "stats": { - "totalViews": 1470, - "totalLikes": 11, - "yesterdayViews": 1469, - "yesterdayLikes": 11, - "lastUpdatedDate": "2024-12-31T00:15:23.704Z" - } - */ + // ------ 전체 통계 ------ interface PostStatisticsType { totalViews: number; @@ -70,6 +61,7 @@ interface PostStatisticsData { totalPostCount: number; stats: PostStatisticsType; } + export class PostStatisticsResponseDto extends BaseResponseDto { constructor( success: boolean, diff --git a/src/types/dto/responses/trackingReponse.type.ts b/src/types/dto/responses/trackingReponse.type.ts deleted file mode 100644 index 6f5b9b2..0000000 --- a/src/types/dto/responses/trackingReponse.type.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { BaseResponseDto } from './baseResponse.type'; - -type TrackingResponseData = Record; -export type TrackingResponse = BaseResponseDto; diff --git a/src/types/index.ts b/src/types/index.ts index 7a81d0a..bcf2b05 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -5,11 +5,11 @@ export type { PostStatistics } from './models/PostStatistics.type'; export type { UserEventTracking } from './models/UserEventTracking.type'; export type { UserEventType } from './userEvent.type'; export type { VelogUserLoginResponse } from './velog.type'; -export type { LoginResponse } from './dto/responses/loginResponse.type'; -export type { TrackingResponse } from './dto/responses/trackingReponse.type'; export type { GetAllPostsQuery } from './dto/requests/getAllPostsQuery.type'; export type { GetPostQuery, PostParam } from './dto/requests/getPostQuery.type'; +export { LoginResponseDto } from './dto/responses/loginResponse.type'; +export { EmptyResponseDto } from './dto/responses/emptyReponse.type'; export { PostStatisticsResponseDto } from './dto/responses/postResponse.type'; export { EventRequestDto } from './dto/requests/eventRequest.dto'; export { UserWithTokenDto } from './dto/userWithToken.dto'; From 790a52ab154ebe55558d2cc8e8e27302b6d54f0d Mon Sep 17 00:00:00 2001 From: HA0N1 Date: Wed, 1 Jan 2025 14:45:29 +0900 Subject: [PATCH 4/9] =?UTF-8?q?refactor:=20=ED=95=A8=EC=88=98=20=EC=A4=84?= =?UTF-8?q?=20=EB=B0=94=EA=BF=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/repositories/post.repository.ts | 2 +- src/repositories/tracking.repository.ts | 1 + src/repositories/user.repository.ts | 2 ++ src/services/post.service.ts | 19 ++++++++++++------- src/services/tracking.service.ts | 3 +++ 5 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/repositories/post.repository.ts b/src/repositories/post.repository.ts index 4050670..5e257ee 100644 --- a/src/repositories/post.repository.ts +++ b/src/repositories/post.repository.ts @@ -127,7 +127,7 @@ export class PostRepository { } async getYesterdayAndTodayViewLikeStats(userId: number) { - // pds.updated_at 은 FE 화면을 위해 억지로 9h 시간 더한 값임 주의 + // ! pds.updated_at 은 FE 화면을 위해 억지로 9h 시간 더한 값임 주의 try { const query = ` SELECT diff --git a/src/repositories/tracking.repository.ts b/src/repositories/tracking.repository.ts index 459b083..01cd054 100644 --- a/src/repositories/tracking.repository.ts +++ b/src/repositories/tracking.repository.ts @@ -22,6 +22,7 @@ export class TrackingRepository { throw new DBError('User Tracking 정보 저장 중 문제가 발생하였습니다.'); } } + async createStayTime(loadDate: Date, unloadDate: Date, userId: number) { try { await this.pool.query( diff --git a/src/repositories/user.repository.ts b/src/repositories/user.repository.ts index d715083..543fde9 100644 --- a/src/repositories/user.repository.ts +++ b/src/repositories/user.repository.ts @@ -16,6 +16,7 @@ export class UserRepository { throw new DBError('유저 조회 중 문제가 발생했습니다.'); } } + async updateTokens(uuid: string, encryptedAccessToken: string, encryptedRefreshToken: string): Promise { try { const query = ` @@ -68,6 +69,7 @@ export class UserRepository { if (!result.rows[0]) { throw new DBError('유저 생성에 실패했습니다.'); } + return result.rows[0]; } catch (error) { logger.error('User Repo createUser Error : ', error); diff --git a/src/services/post.service.ts b/src/services/post.service.ts index 6ef0367..aa2bc98 100644 --- a/src/services/post.service.ts +++ b/src/services/post.service.ts @@ -53,14 +53,19 @@ export class PostService { } async getPost(postId: number, start?: string, end?: string) { - const posts = await this.postRepo.findPostByPostId(postId, start, end); + try { + const posts = await this.postRepo.findPostByPostId(postId, start, end); - const transformedPosts = posts.map((post) => ({ - date: post.date, - dailyViewCount: parseInt(post.daily_view_count), - dailyLikeCount: parseInt(post.daily_like_count), - })); + const transformedPosts = posts.map((post) => ({ + date: post.date, + dailyViewCount: parseInt(post.daily_view_count), + dailyLikeCount: parseInt(post.daily_like_count), + })); - return transformedPosts; + return transformedPosts; + } catch (error) { + logger.error('PostService getTotalPostCounts error : ', error); + throw error; + } } } diff --git a/src/services/tracking.service.ts b/src/services/tracking.service.ts index 350acd5..623fc7f 100644 --- a/src/services/tracking.service.ts +++ b/src/services/tracking.service.ts @@ -9,14 +9,17 @@ export class TrackingService { async tracking(eventType: EventRequestDto, id: number) { return await this.trackingRepo.createEvent(eventType, id); } + async stay(data: StayTimeRequestDto, userId: number) { try { const { loadDate, unloadDate } = data; if (new Date(loadDate) > new Date(unloadDate)) { throw new BadRequestError('시간 정보가 올바르지 않습니다.'); } + const stayTime = new Date(unloadDate).getTime() - new Date(loadDate).getTime(); await this.trackingRepo.createStayTime(loadDate, unloadDate, userId); + return stayTime; } catch (error) { logger.error('Tracking Service stay error : ', error); From 66ecfe1289dfd110d747aff60184b4807b98c66c Mon Sep 17 00:00:00 2001 From: HA0N1 Date: Wed, 1 Jan 2025 15:03:57 +0900 Subject: [PATCH 5/9] =?UTF-8?q?hotfix:=20db=20=EC=97=B0=EA=B2=B0=20port?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/configs/db.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/configs/db.config.ts b/src/configs/db.config.ts index 3a5f4fd..fb0c831 100644 --- a/src/configs/db.config.ts +++ b/src/configs/db.config.ts @@ -11,7 +11,7 @@ const pool = new Pool({ user: process.env.POSTGRES_USER, host: process.env.POSTGRES_HOST, password: process.env.POSTGRES_PASSWORD, - port: Number(process.env.DATA_PORT), + port: Number(process.env.POSTGRES_PORT), ssl: { rejectUnauthorized: false, }, From e5968c01dcccd956ea0f7fb19d3aeda55d6d8dfe Mon Sep 17 00:00:00 2001 From: HA0N1 Date: Fri, 3 Jan 2025 01:05:24 +0900 Subject: [PATCH 6/9] deploy branch squash merge --- .dockerignore | 5 + Dockerfile | 40 ++++++ docker-compose.yml | 16 +++ ecosystem.config.js | 18 +++ package.json | 11 +- pnpm-lock.yaml | 129 ++++++++++-------- src/services/user.service.ts | 2 +- ...entRequest.dto.ts => eventRequest.type.ts} | 9 +- ...WithToken.dto.ts => userWithToken.type.ts} | 0 src/types/dto/velogUser.dto.ts | 17 +-- src/types/dto/velogUser.type.ts | 32 +++++ src/types/index.ts | 14 +- src/types/requests/getPostQuery.type.ts | 4 - tsconfig.json | 16 ++- 14 files changed, 222 insertions(+), 91 deletions(-) create mode 100644 .dockerignore create mode 100644 Dockerfile create mode 100644 docker-compose.yml create mode 100644 ecosystem.config.js rename src/types/dto/requests/{eventRequest.dto.ts => eventRequest.type.ts} (64%) rename src/types/dto/{userWithToken.dto.ts => userWithToken.type.ts} (100%) create mode 100644 src/types/dto/velogUser.type.ts delete mode 100644 src/types/requests/getPostQuery.type.ts diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..3cec456 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,5 @@ + +node_modules +.git +.env +dist diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..e76fcbf --- /dev/null +++ b/Dockerfile @@ -0,0 +1,40 @@ +# ========================================== # +# Build stage +# ========================================== # +FROM node:23-alpine AS builder + +WORKDIR /usr/src/app + +# pnpm 설치 +RUN npm install -g pnpm + +# 빌드에 필요한 파일만 복사 +COPY package.json pnpm-lock.yaml ./ + +# 의존성 설치 +RUN pnpm install --frozen-lockfile + +# 소스 코드 복사 및 빌드 +COPY . . +RUN pnpm run build + +# ========================================== # +# Production stage +# ========================================== # +FROM node:23-alpine + +WORKDIR /usr/src/app + +RUN npm install -g pnpm pm2 + +# 프로덕션 의존성만 설치 +COPY package.json pnpm-lock.yaml ./ +RUN pnpm install --prod --frozen-lockfile + +# 빌드 결과물과 PM2 설정 파일 복사 +COPY --from=builder /usr/src/app/dist ./dist +COPY ecosystem.config.js . + +EXPOSE 3000 + +CMD ["pm2-runtime", "start", "ecosystem.config.js"] diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..912c1f6 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,16 @@ +# docker-compose.yml +version: '3.9' + +services: + app: + build: + context: . + dockerfile: Dockerfile + container_name: velog-dashboard-v2-api + ports: + - "8080:8080" + env_file: + - .env + environment: + NODE_ENV: production + restart: unless-stopped diff --git a/ecosystem.config.js b/ecosystem.config.js new file mode 100644 index 0000000..ab89e48 --- /dev/null +++ b/ecosystem.config.js @@ -0,0 +1,18 @@ +module.exports = { + apps: [ + { + name: 'velog-dashboard-v2-api', + script: 'dist/index.js', // 빌드된 메인 파일 경로 + instances: 1, + autorestart: true, + watch: false, + max_memory_restart: '1G', + env: { + NODE_ENV: 'production', + }, + error_file: 'logs/err.log', + out_file: 'logs/out.log', + restart_delay: 4000, + }, + ], +}; diff --git a/package.json b/package.json index 16ffee7..dc395ff 100644 --- a/package.json +++ b/package.json @@ -3,16 +3,16 @@ "version": "1.0.0", "description": "", "main": "index.js", - "type": "module", "scripts": { "dev": "tsx watch src/index.ts", - "build": "tsc --build", + "build": "tsc", "lint": "eslint src/**/*.ts", "lint:fix": "eslint src/ --fix", "format": "prettier --write src/**/*.ts", "test": "jest", "test:watch": "jest --watch", - "test:coverage": "jest --coverage" + "test:coverage": "jest --coverage", + "start": "node dist/index.js" }, "lint-staged": { "*.{ts,tsx}": [ @@ -55,7 +55,8 @@ "prettier": "^3.3.3", "ts-jest": "^29.2.5", "tsx": "^4.19.2", - "typescript": "^5.6.3", - "typescript-eslint": "^8.15.0" + "typescript": "^5.7.2", + "typescript-eslint": "^8.15.0", + "typescript-transform-paths": "^3.5.3" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f9e1f18..be68ee3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -77,13 +77,13 @@ importers: version: 9.1.0(eslint@9.15.0) eslint-plugin-jest: specifier: ^28.9.0 - version: 28.9.0(@typescript-eslint/eslint-plugin@8.15.0(@typescript-eslint/parser@8.15.0(eslint@9.15.0)(typescript@5.6.3))(eslint@9.15.0)(typescript@5.6.3))(eslint@9.15.0)(jest@29.7.0(@types/node@22.9.0)(ts-node@10.9.2(@types/node@22.9.0)(typescript@5.6.3)))(typescript@5.6.3) + version: 28.9.0(@typescript-eslint/eslint-plugin@8.15.0(@typescript-eslint/parser@8.15.0(eslint@9.15.0)(typescript@5.7.2))(eslint@9.15.0)(typescript@5.7.2))(eslint@9.15.0)(jest@29.7.0(@types/node@22.9.0)(ts-node@10.9.2(@types/node@22.9.0)(typescript@5.7.2)))(typescript@5.7.2) eslint-plugin-prettier: specifier: ^5.2.1 version: 5.2.1(eslint-config-prettier@9.1.0(eslint@9.15.0))(eslint@9.15.0)(prettier@3.3.3) jest: specifier: ^29.7.0 - version: 29.7.0(@types/node@22.9.0)(ts-node@10.9.2(@types/node@22.9.0)(typescript@5.6.3)) + version: 29.7.0(@types/node@22.9.0)(ts-node@10.9.2(@types/node@22.9.0)(typescript@5.7.2)) lint-staged: specifier: ^15.2.10 version: 15.2.10 @@ -92,16 +92,19 @@ importers: version: 3.3.3 ts-jest: specifier: ^29.2.5 - version: 29.2.5(@babel/core@7.26.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.0))(jest@29.7.0(@types/node@22.9.0)(ts-node@10.9.2(@types/node@22.9.0)(typescript@5.6.3)))(typescript@5.6.3) + version: 29.2.5(@babel/core@7.26.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.0))(jest@29.7.0(@types/node@22.9.0)(ts-node@10.9.2(@types/node@22.9.0)(typescript@5.7.2)))(typescript@5.7.2) tsx: specifier: ^4.19.2 version: 4.19.2 typescript: - specifier: ^5.6.3 - version: 5.6.3 + specifier: ^5.7.2 + version: 5.7.2 typescript-eslint: specifier: ^8.15.0 - version: 8.15.0(eslint@9.15.0)(typescript@5.6.3) + version: 8.15.0(eslint@9.15.0)(typescript@5.7.2) + typescript-transform-paths: + specifier: ^3.5.3 + version: 3.5.3(typescript@5.7.2) packages: @@ -2453,8 +2456,13 @@ packages: typescript: optional: true - typescript@5.6.3: - resolution: {integrity: sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==} + typescript-transform-paths@3.5.3: + resolution: {integrity: sha512-5y2l2iPKNHKOj08/1i+02+ljBVUhWcXQLXomiOXCmNpiTuSxIkj0dM1LUE7OOAt53+/6KidY+sFTCP781J64Eg==} + peerDependencies: + typescript: '>=3.6.5' + + typescript@5.7.2: + resolution: {integrity: sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==} engines: {node: '>=14.17'} hasBin: true @@ -2919,7 +2927,7 @@ snapshots: jest-util: 29.7.0 slash: 3.0.0 - '@jest/core@29.7.0(ts-node@10.9.2(@types/node@22.9.0)(typescript@5.6.3))': + '@jest/core@29.7.0(ts-node@10.9.2(@types/node@22.9.0)(typescript@5.7.2))': dependencies: '@jest/console': 29.7.0 '@jest/reporters': 29.7.0 @@ -2933,7 +2941,7 @@ snapshots: exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@22.9.0)(ts-node@10.9.2(@types/node@22.9.0)(typescript@5.6.3)) + jest-config: 29.7.0(@types/node@22.9.0)(ts-node@10.9.2(@types/node@22.9.0)(typescript@5.7.2)) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -3252,34 +3260,34 @@ snapshots: dependencies: '@types/yargs-parser': 21.0.3 - '@typescript-eslint/eslint-plugin@8.15.0(@typescript-eslint/parser@8.15.0(eslint@9.15.0)(typescript@5.6.3))(eslint@9.15.0)(typescript@5.6.3)': + '@typescript-eslint/eslint-plugin@8.15.0(@typescript-eslint/parser@8.15.0(eslint@9.15.0)(typescript@5.7.2))(eslint@9.15.0)(typescript@5.7.2)': dependencies: '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.15.0(eslint@9.15.0)(typescript@5.6.3) + '@typescript-eslint/parser': 8.15.0(eslint@9.15.0)(typescript@5.7.2) '@typescript-eslint/scope-manager': 8.15.0 - '@typescript-eslint/type-utils': 8.15.0(eslint@9.15.0)(typescript@5.6.3) - '@typescript-eslint/utils': 8.15.0(eslint@9.15.0)(typescript@5.6.3) + '@typescript-eslint/type-utils': 8.15.0(eslint@9.15.0)(typescript@5.7.2) + '@typescript-eslint/utils': 8.15.0(eslint@9.15.0)(typescript@5.7.2) '@typescript-eslint/visitor-keys': 8.15.0 eslint: 9.15.0 graphemer: 1.4.0 ignore: 5.3.2 natural-compare: 1.4.0 - ts-api-utils: 1.4.0(typescript@5.6.3) + ts-api-utils: 1.4.0(typescript@5.7.2) optionalDependencies: - typescript: 5.6.3 + typescript: 5.7.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.15.0(eslint@9.15.0)(typescript@5.6.3)': + '@typescript-eslint/parser@8.15.0(eslint@9.15.0)(typescript@5.7.2)': dependencies: '@typescript-eslint/scope-manager': 8.15.0 '@typescript-eslint/types': 8.15.0 - '@typescript-eslint/typescript-estree': 8.15.0(typescript@5.6.3) + '@typescript-eslint/typescript-estree': 8.15.0(typescript@5.7.2) '@typescript-eslint/visitor-keys': 8.15.0 debug: 4.3.7 eslint: 9.15.0 optionalDependencies: - typescript: 5.6.3 + typescript: 5.7.2 transitivePeerDependencies: - supports-color @@ -3288,21 +3296,21 @@ snapshots: '@typescript-eslint/types': 8.15.0 '@typescript-eslint/visitor-keys': 8.15.0 - '@typescript-eslint/type-utils@8.15.0(eslint@9.15.0)(typescript@5.6.3)': + '@typescript-eslint/type-utils@8.15.0(eslint@9.15.0)(typescript@5.7.2)': dependencies: - '@typescript-eslint/typescript-estree': 8.15.0(typescript@5.6.3) - '@typescript-eslint/utils': 8.15.0(eslint@9.15.0)(typescript@5.6.3) + '@typescript-eslint/typescript-estree': 8.15.0(typescript@5.7.2) + '@typescript-eslint/utils': 8.15.0(eslint@9.15.0)(typescript@5.7.2) debug: 4.3.7 eslint: 9.15.0 - ts-api-utils: 1.4.0(typescript@5.6.3) + ts-api-utils: 1.4.0(typescript@5.7.2) optionalDependencies: - typescript: 5.6.3 + typescript: 5.7.2 transitivePeerDependencies: - supports-color '@typescript-eslint/types@8.15.0': {} - '@typescript-eslint/typescript-estree@8.15.0(typescript@5.6.3)': + '@typescript-eslint/typescript-estree@8.15.0(typescript@5.7.2)': dependencies: '@typescript-eslint/types': 8.15.0 '@typescript-eslint/visitor-keys': 8.15.0 @@ -3311,21 +3319,21 @@ snapshots: is-glob: 4.0.3 minimatch: 9.0.5 semver: 7.6.3 - ts-api-utils: 1.4.0(typescript@5.6.3) + ts-api-utils: 1.4.0(typescript@5.7.2) optionalDependencies: - typescript: 5.6.3 + typescript: 5.7.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.15.0(eslint@9.15.0)(typescript@5.6.3)': + '@typescript-eslint/utils@8.15.0(eslint@9.15.0)(typescript@5.7.2)': dependencies: '@eslint-community/eslint-utils': 4.4.1(eslint@9.15.0) '@typescript-eslint/scope-manager': 8.15.0 '@typescript-eslint/types': 8.15.0 - '@typescript-eslint/typescript-estree': 8.15.0(typescript@5.6.3) + '@typescript-eslint/typescript-estree': 8.15.0(typescript@5.7.2) eslint: 9.15.0 optionalDependencies: - typescript: 5.6.3 + typescript: 5.7.2 transitivePeerDependencies: - supports-color @@ -3628,13 +3636,13 @@ snapshots: object-assign: 4.1.1 vary: 1.1.2 - create-jest@29.7.0(@types/node@22.9.0)(ts-node@10.9.2(@types/node@22.9.0)(typescript@5.6.3)): + create-jest@29.7.0(@types/node@22.9.0)(ts-node@10.9.2(@types/node@22.9.0)(typescript@5.7.2)): dependencies: '@jest/types': 29.6.3 chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@22.9.0)(ts-node@10.9.2(@types/node@22.9.0)(typescript@5.6.3)) + jest-config: 29.7.0(@types/node@22.9.0)(ts-node@10.9.2(@types/node@22.9.0)(typescript@5.7.2)) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -3758,13 +3766,13 @@ snapshots: dependencies: eslint: 9.15.0 - eslint-plugin-jest@28.9.0(@typescript-eslint/eslint-plugin@8.15.0(@typescript-eslint/parser@8.15.0(eslint@9.15.0)(typescript@5.6.3))(eslint@9.15.0)(typescript@5.6.3))(eslint@9.15.0)(jest@29.7.0(@types/node@22.9.0)(ts-node@10.9.2(@types/node@22.9.0)(typescript@5.6.3)))(typescript@5.6.3): + eslint-plugin-jest@28.9.0(@typescript-eslint/eslint-plugin@8.15.0(@typescript-eslint/parser@8.15.0(eslint@9.15.0)(typescript@5.7.2))(eslint@9.15.0)(typescript@5.7.2))(eslint@9.15.0)(jest@29.7.0(@types/node@22.9.0)(ts-node@10.9.2(@types/node@22.9.0)(typescript@5.7.2)))(typescript@5.7.2): dependencies: - '@typescript-eslint/utils': 8.15.0(eslint@9.15.0)(typescript@5.6.3) + '@typescript-eslint/utils': 8.15.0(eslint@9.15.0)(typescript@5.7.2) eslint: 9.15.0 optionalDependencies: - '@typescript-eslint/eslint-plugin': 8.15.0(@typescript-eslint/parser@8.15.0(eslint@9.15.0)(typescript@5.6.3))(eslint@9.15.0)(typescript@5.6.3) - jest: 29.7.0(@types/node@22.9.0)(ts-node@10.9.2(@types/node@22.9.0)(typescript@5.6.3)) + '@typescript-eslint/eslint-plugin': 8.15.0(@typescript-eslint/parser@8.15.0(eslint@9.15.0)(typescript@5.7.2))(eslint@9.15.0)(typescript@5.7.2) + jest: 29.7.0(@types/node@22.9.0)(ts-node@10.9.2(@types/node@22.9.0)(typescript@5.7.2)) transitivePeerDependencies: - supports-color - typescript @@ -4232,16 +4240,16 @@ snapshots: - babel-plugin-macros - supports-color - jest-cli@29.7.0(@types/node@22.9.0)(ts-node@10.9.2(@types/node@22.9.0)(typescript@5.6.3)): + jest-cli@29.7.0(@types/node@22.9.0)(ts-node@10.9.2(@types/node@22.9.0)(typescript@5.7.2)): dependencies: - '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@22.9.0)(typescript@5.6.3)) + '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@22.9.0)(typescript@5.7.2)) '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@22.9.0)(ts-node@10.9.2(@types/node@22.9.0)(typescript@5.6.3)) + create-jest: 29.7.0(@types/node@22.9.0)(ts-node@10.9.2(@types/node@22.9.0)(typescript@5.7.2)) exit: 0.1.2 import-local: 3.2.0 - jest-config: 29.7.0(@types/node@22.9.0)(ts-node@10.9.2(@types/node@22.9.0)(typescript@5.6.3)) + jest-config: 29.7.0(@types/node@22.9.0)(ts-node@10.9.2(@types/node@22.9.0)(typescript@5.7.2)) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.2 @@ -4251,7 +4259,7 @@ snapshots: - supports-color - ts-node - jest-config@29.7.0(@types/node@22.9.0)(ts-node@10.9.2(@types/node@22.9.0)(typescript@5.6.3)): + jest-config@29.7.0(@types/node@22.9.0)(ts-node@10.9.2(@types/node@22.9.0)(typescript@5.7.2)): dependencies: '@babel/core': 7.26.0 '@jest/test-sequencer': 29.7.0 @@ -4277,7 +4285,7 @@ snapshots: strip-json-comments: 3.1.1 optionalDependencies: '@types/node': 22.9.0 - ts-node: 10.9.2(@types/node@22.9.0)(typescript@5.6.3) + ts-node: 10.9.2(@types/node@22.9.0)(typescript@5.7.2) transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -4497,12 +4505,12 @@ snapshots: merge-stream: 2.0.0 supports-color: 8.1.1 - jest@29.7.0(@types/node@22.9.0)(ts-node@10.9.2(@types/node@22.9.0)(typescript@5.6.3)): + jest@29.7.0(@types/node@22.9.0)(ts-node@10.9.2(@types/node@22.9.0)(typescript@5.7.2)): dependencies: - '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@22.9.0)(typescript@5.6.3)) + '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@22.9.0)(typescript@5.7.2)) '@jest/types': 29.6.3 import-local: 3.2.0 - jest-cli: 29.7.0(@types/node@22.9.0)(ts-node@10.9.2(@types/node@22.9.0)(typescript@5.6.3)) + jest-cli: 29.7.0(@types/node@22.9.0)(ts-node@10.9.2(@types/node@22.9.0)(typescript@5.7.2)) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -5112,22 +5120,22 @@ snapshots: triple-beam@1.4.1: {} - ts-api-utils@1.4.0(typescript@5.6.3): + ts-api-utils@1.4.0(typescript@5.7.2): dependencies: - typescript: 5.6.3 + typescript: 5.7.2 - ts-jest@29.2.5(@babel/core@7.26.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.0))(jest@29.7.0(@types/node@22.9.0)(ts-node@10.9.2(@types/node@22.9.0)(typescript@5.6.3)))(typescript@5.6.3): + ts-jest@29.2.5(@babel/core@7.26.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.0))(jest@29.7.0(@types/node@22.9.0)(ts-node@10.9.2(@types/node@22.9.0)(typescript@5.7.2)))(typescript@5.7.2): dependencies: bs-logger: 0.2.6 ejs: 3.1.10 fast-json-stable-stringify: 2.1.0 - jest: 29.7.0(@types/node@22.9.0)(ts-node@10.9.2(@types/node@22.9.0)(typescript@5.6.3)) + jest: 29.7.0(@types/node@22.9.0)(ts-node@10.9.2(@types/node@22.9.0)(typescript@5.7.2)) jest-util: 29.7.0 json5: 2.2.3 lodash.memoize: 4.1.2 make-error: 1.3.6 semver: 7.6.3 - typescript: 5.6.3 + typescript: 5.7.2 yargs-parser: 21.1.1 optionalDependencies: '@babel/core': 7.26.0 @@ -5135,7 +5143,7 @@ snapshots: '@jest/types': 29.6.3 babel-jest: 29.7.0(@babel/core@7.26.0) - ts-node@10.9.2(@types/node@22.9.0)(typescript@5.6.3): + ts-node@10.9.2(@types/node@22.9.0)(typescript@5.7.2): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.11 @@ -5149,7 +5157,7 @@ snapshots: create-require: 1.1.1 diff: 4.0.2 make-error: 1.3.6 - typescript: 5.6.3 + typescript: 5.7.2 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 optional: true @@ -5176,18 +5184,23 @@ snapshots: media-typer: 0.3.0 mime-types: 2.1.35 - typescript-eslint@8.15.0(eslint@9.15.0)(typescript@5.6.3): + typescript-eslint@8.15.0(eslint@9.15.0)(typescript@5.7.2): dependencies: - '@typescript-eslint/eslint-plugin': 8.15.0(@typescript-eslint/parser@8.15.0(eslint@9.15.0)(typescript@5.6.3))(eslint@9.15.0)(typescript@5.6.3) - '@typescript-eslint/parser': 8.15.0(eslint@9.15.0)(typescript@5.6.3) - '@typescript-eslint/utils': 8.15.0(eslint@9.15.0)(typescript@5.6.3) + '@typescript-eslint/eslint-plugin': 8.15.0(@typescript-eslint/parser@8.15.0(eslint@9.15.0)(typescript@5.7.2))(eslint@9.15.0)(typescript@5.7.2) + '@typescript-eslint/parser': 8.15.0(eslint@9.15.0)(typescript@5.7.2) + '@typescript-eslint/utils': 8.15.0(eslint@9.15.0)(typescript@5.7.2) eslint: 9.15.0 optionalDependencies: - typescript: 5.6.3 + typescript: 5.7.2 transitivePeerDependencies: - supports-color - typescript@5.6.3: {} + typescript-transform-paths@3.5.3(typescript@5.7.2): + dependencies: + minimatch: 9.0.5 + typescript: 5.7.2 + + typescript@5.7.2: {} undici-types@6.19.8: {} diff --git a/src/services/user.service.ts b/src/services/user.service.ts index 33e570f..1876b9a 100644 --- a/src/services/user.service.ts +++ b/src/services/user.service.ts @@ -4,7 +4,7 @@ import { getKeyByGroup } from '../utils/key.util'; import AESEncryption from '../modules/token_encryption/aes_encryption'; import { UserRepository } from '../repositories/user.repository'; import { UserWithTokenDto, User } from '../types'; -import { generateRandomGroupId } from 'src/utils/generateGroupId.util'; +import { generateRandomGroupId } from '../utils/generateGroupId.util'; export class UserService { constructor(private userRepo: UserRepository) {} diff --git a/src/types/dto/requests/eventRequest.dto.ts b/src/types/dto/requests/eventRequest.type.ts similarity index 64% rename from src/types/dto/requests/eventRequest.dto.ts rename to src/types/dto/requests/eventRequest.type.ts index 689031c..cb255c5 100644 --- a/src/types/dto/requests/eventRequest.dto.ts +++ b/src/types/dto/requests/eventRequest.type.ts @@ -1,5 +1,12 @@ import { IsEnum, IsNotEmpty } from 'class-validator'; -import { UserEventType } from '../../userEvent.type'; + +export enum UserEventType { + LOGIN = '01', + POST_CLICK = '02', + POST_GRAPH_CLICK = '03', + EXIT = '04', + NOTHING = '99', +} export class EventRequestDto { @IsEnum(UserEventType) diff --git a/src/types/dto/userWithToken.dto.ts b/src/types/dto/userWithToken.type.ts similarity index 100% rename from src/types/dto/userWithToken.dto.ts rename to src/types/dto/userWithToken.type.ts diff --git a/src/types/dto/velogUser.dto.ts b/src/types/dto/velogUser.dto.ts index 11859df..4e54e49 100644 --- a/src/types/dto/velogUser.dto.ts +++ b/src/types/dto/velogUser.dto.ts @@ -1,6 +1,15 @@ import { IsEmail, IsString, IsUUID, ValidateNested } from 'class-validator'; import { Type } from 'class-transformer'; +class ProfileDTO { + @IsString() + thumbnail: string; + + constructor(thumbnail: string) { + this.thumbnail = thumbnail; + } +} + export class VelogUserLoginDto { @IsUUID() id: string; @@ -22,11 +31,3 @@ export class VelogUserLoginDto { this.profile = profile; } } -class ProfileDTO { - @IsString() - thumbnail: string; - - constructor(thumbnail: string) { - this.thumbnail = thumbnail; - } -} diff --git a/src/types/dto/velogUser.type.ts b/src/types/dto/velogUser.type.ts new file mode 100644 index 0000000..11859df --- /dev/null +++ b/src/types/dto/velogUser.type.ts @@ -0,0 +1,32 @@ +import { IsEmail, IsString, IsUUID, ValidateNested } from 'class-validator'; +import { Type } from 'class-transformer'; + +export class VelogUserLoginDto { + @IsUUID() + id: string; + + @IsString() + username: string; + + @IsEmail() + email: string; + + @ValidateNested() + @Type(() => ProfileDTO) + profile: ProfileDTO; + + constructor(id: string, username: string, email: string, profile: ProfileDTO) { + this.id = id; + this.username = username; + this.email = email; + this.profile = profile; + } +} +class ProfileDTO { + @IsString() + thumbnail: string; + + constructor(thumbnail: string) { + this.thumbnail = thumbnail; + } +} diff --git a/src/types/index.ts b/src/types/index.ts index bcf2b05..934dd0f 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -3,18 +3,16 @@ export type { Post } from './models/Post.type'; export type { PostDailyStatistics } from './models/PostDailyStatistics.type'; export type { PostStatistics } from './models/PostStatistics.type'; export type { UserEventTracking } from './models/UserEventTracking.type'; -export type { UserEventType } from './userEvent.type'; export type { VelogUserLoginResponse } from './velog.type'; export type { GetAllPostsQuery } from './dto/requests/getAllPostsQuery.type'; export type { GetPostQuery, PostParam } from './dto/requests/getPostQuery.type'; -export { LoginResponseDto } from './dto/responses/loginResponse.type'; -export { EmptyResponseDto } from './dto/responses/emptyReponse.type'; -export { PostStatisticsResponseDto } from './dto/responses/postResponse.type'; -export { EventRequestDto } from './dto/requests/eventRequest.dto'; -export { UserWithTokenDto } from './dto/userWithToken.dto'; -export { VelogUserLoginDto } from './dto/velogUser.dto'; export { StayTimeRequestDto } from './dto/requests/stayTimeRequest.dto'; -export { PostsResponseDto, PostResponseDto } from './dto/responses/postResponse.type'; export { GetAllPostsQueryDto } from './dto/requests/getAllPostsQuery.type'; export { GetPostQueryDto } from './dto/requests/getPostQuery.type'; +export { LoginResponseDto } from './dto/responses/loginResponse.type'; +export { EmptyResponseDto } from './dto/responses/emptyReponse.type'; +export { PostsResponseDto, PostResponseDto, PostStatisticsResponseDto } from './dto/responses/postResponse.type'; +export { EventRequestDto } from './dto/requests/eventRequest.type'; +export { UserWithTokenDto } from './dto/userWithToken.type'; +export { VelogUserLoginDto } from './dto/velogUser.type'; diff --git a/src/types/requests/getPostQuery.type.ts b/src/types/requests/getPostQuery.type.ts deleted file mode 100644 index 464f9fc..0000000 --- a/src/types/requests/getPostQuery.type.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface GetPostQuery { - start: string; - end: string; -} diff --git a/tsconfig.json b/tsconfig.json index 4d372b7..0360352 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,9 +1,13 @@ { "compilerOptions": { - "target": "es6", - "module": "ESNext", - "moduleResolution": "node", - "baseUrl": "./", + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "lib": ["ES2022"], + "baseUrl": "./src", + "paths": { + "@/*": ["*"] + }, "outDir": "./dist", "rootDir": "./src", "declaration": true, @@ -15,10 +19,10 @@ "skipLibCheck": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "moduleDetection": "force", "resolveJsonModule": true, "allowJs": true, - "typeRoots": ["./node_modules/@types", "./src/types/*"] + "allowSyntheticDefaultImports": true, + "plugins": [] }, "include": ["src/**/*"], "exclude": ["node_modules", "dist"] From a5df130d217bec475adb2b1992ebf061c7a3db0f Mon Sep 17 00:00:00 2001 From: HA0N1 Date: Fri, 3 Jan 2025 01:28:50 +0900 Subject: [PATCH 7/9] =?UTF-8?q?refactor:=20response=20dto=20=EB=A6=AC?= =?UTF-8?q?=ED=8E=99=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jest.config.ts | 3 ++ src/controllers/post.controller.ts | 16 ++++----- src/controllers/tracking.controller.ts | 10 +++--- src/controllers/user.controller.ts | 19 +++++----- src/middlewares/auth.middleware.ts | 9 +++-- src/middlewares/errorHandling.middleware.ts | 4 +-- src/middlewares/validation.middleware.ts | 2 +- .../__test__/test.aes.encryption.test.ts | 6 ++-- src/repositories/post.repository.ts | 4 +-- src/repositories/tracking.repository.ts | 6 ++-- src/repositories/user.repository.ts | 6 ++-- src/routes/post.router.ts | 14 ++++---- src/routes/tracking.router.ts | 16 ++++----- src/routes/user.router.ts | 14 ++++---- src/services/post.service.ts | 4 +-- src/services/tracking.service.ts | 8 ++--- src/services/user.service.ts | 14 ++++---- src/types/dto/responses/emptyReponse.type.ts | 8 ++--- src/types/dto/responses/loginResponse.type.ts | 16 ++------- src/types/dto/responses/postResponse.type.ts | 35 +++---------------- src/types/express.d.ts | 2 +- src/types/index.ts | 34 +++++++++--------- src/types/models/UserEventTracking.type.ts | 2 +- tsconfig.json | 3 +- 24 files changed, 106 insertions(+), 149 deletions(-) diff --git a/jest.config.ts b/jest.config.ts index 5799ac5..86cb583 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -4,6 +4,9 @@ const config: Config = { preset: 'ts-jest', testEnvironment: 'node', testMatch: ['**/src/**/*.test.ts'], + moduleNameMapper: { + '^@/(.*)$': '/src/$1', + }, moduleFileExtensions: ['ts', 'js'], transform: { '^.+\\.ts$': 'ts-jest', diff --git a/src/controllers/post.controller.ts b/src/controllers/post.controller.ts index 54cdc93..af099fa 100644 --- a/src/controllers/post.controller.ts +++ b/src/controllers/post.controller.ts @@ -1,6 +1,6 @@ import { NextFunction, Request, RequestHandler, Response } from 'express'; -import logger from '../configs/logger.config'; -import { PostService } from '../services/post.service'; +import logger from '@/configs/logger.config'; +import { PostService } from '@/services/post.service'; import { GetAllPostsQuery, PostsResponseDto, @@ -8,7 +8,7 @@ import { GetPostQuery, PostParam, PostStatisticsResponseDto, -} from '../types'; +} from '@/types'; export class PostController { constructor(private postService: PostService) {} @@ -27,8 +27,7 @@ export class PostController { const response = new PostsResponseDto( true, '전체 post 조회에 성공하였습니다.', - result.nextCursor, - result.posts, + { nextCursor: result.nextCursor, posts: result.posts }, null, ); @@ -47,14 +46,13 @@ export class PostController { try { const { id } = req.user; - const result = await this.postService.getAllPostStatistics(id); + const stats = await this.postService.getAllPostStatistics(id); const totalPostCount = await this.postService.getTotalPostCounts(id); const response = new PostStatisticsResponseDto( true, '전체 post 통계 조회에 성공하였습니다.', - totalPostCount, - result, + { totalPostCount, stats }, null, ); @@ -76,7 +74,7 @@ export class PostController { const post = await this.postService.getPost(postId, start, end); - const response = new PostResponseDto(true, '단건 post 조회에 성공하였습니다.', post, null); + const response = new PostResponseDto(true, '단건 post 조회에 성공하였습니다.', { post }, null); res.status(200).json(response); } catch (error) { diff --git a/src/controllers/tracking.controller.ts b/src/controllers/tracking.controller.ts index 843bf27..a9009c2 100644 --- a/src/controllers/tracking.controller.ts +++ b/src/controllers/tracking.controller.ts @@ -1,7 +1,7 @@ import { NextFunction, Request, RequestHandler, Response } from 'express'; -import logger from '../configs/logger.config'; -import { TrackingService } from '../services/tracking.service'; -import { EmptyResponseDto } from '../types'; +import logger from '@/configs/logger.config'; +import { TrackingService } from '@/services/tracking.service'; +import { EmptyResponseDto } from '@/types'; export class TrackingController { constructor(private trackingService: TrackingService) {} @@ -13,7 +13,7 @@ export class TrackingController { await this.trackingService.tracking(eventType, id); - const response = new EmptyResponseDto(true, '이벤트 데이터 저장완료', null); + const response = new EmptyResponseDto(true, '이벤트 데이터 저장완료', {}, null); res.status(200).json(response); } catch (error) { @@ -29,7 +29,7 @@ export class TrackingController { await this.trackingService.stay({ loadDate, unloadDate }, id); - const response = new EmptyResponseDto(true, '체류시간 데이터 저장 완료', null); + const response = new EmptyResponseDto(true, '체류시간 데이터 저장 완료', {}, null); res.status(200).json(response); } catch (error) { diff --git a/src/controllers/user.controller.ts b/src/controllers/user.controller.ts index 99391e8..36ae3e9 100644 --- a/src/controllers/user.controller.ts +++ b/src/controllers/user.controller.ts @@ -1,7 +1,7 @@ import { NextFunction, Request, Response, RequestHandler, CookieOptions } from 'express'; -import logger from '../configs/logger.config'; -import { EmptyResponseDto, LoginResponseDto, UserWithTokenDto } from '../types'; -import { UserService } from '../services/user.service'; +import logger from '@/configs/logger.config'; +import { EmptyResponseDto, LoginResponseDto, UserWithTokenDto } from '@/types'; +import { UserService } from '@/services/user.service'; export class UserController { constructor(private userService: UserService) {} @@ -35,7 +35,12 @@ export class UserController { res.cookie('access_token', accessToken, this.cookieOption()); res.cookie('refresh_token', refreshToken, this.cookieOption()); - const response = new LoginResponseDto(true, '로그인에 성공하였습니다.', isExistUser.id, username, profile, null); + const response = new LoginResponseDto( + true, + '로그인에 성공하였습니다.', + { id: isExistUser.id, username, profile }, + null, + ); res.status(200).json(response); } catch (error) { @@ -48,7 +53,7 @@ export class UserController { res.clearCookie('access_token'); res.clearCookie('refresh_token'); - const response = new EmptyResponseDto(true, '로그아웃에 성공하였습니다.', null); + const response = new EmptyResponseDto(true, '로그아웃에 성공하였습니다.', {}, null); res.status(200).json(response); }; @@ -59,9 +64,7 @@ export class UserController { const response = new LoginResponseDto( true, '유저 정보 조회에 성공하였습니다.', - user.id, - user.username, - user.profile, + { id: user.id, username: user.username, profile: user.profile }, null, ); diff --git a/src/middlewares/auth.middleware.ts b/src/middlewares/auth.middleware.ts index 0a537f7..fdc63aa 100644 --- a/src/middlewares/auth.middleware.ts +++ b/src/middlewares/auth.middleware.ts @@ -1,10 +1,10 @@ import { NextFunction, Request, Response } from 'express'; import axios from 'axios'; import { isUUID } from 'class-validator'; -import logger from '../configs/logger.config'; -import pool from '../configs/db.config'; -import { DBError, InvalidTokenError } from '../exception'; -import { VELOG_API_URL, VELOG_QUERIES } from '../constants/velog.constans'; +import logger from '@/configs/logger.config'; +import pool from '@/configs/db.config'; +import { DBError, InvalidTokenError } from '@/exception'; +import { VELOG_API_URL, VELOG_QUERIES } from '@/constants/velog.constans'; /** * 요청에서 토큰을 추출하는 함수 @@ -58,7 +58,6 @@ const fetchVelogApi = async (query: string, accessToken: string, refreshToken: s } }; - /** * JWT 토큰에서 페이로드를 추출하고 디코딩하는 함수 * @param token - 디코딩할 JWT 토큰 문자열 diff --git a/src/middlewares/errorHandling.middleware.ts b/src/middlewares/errorHandling.middleware.ts index 2380093..67513ee 100644 --- a/src/middlewares/errorHandling.middleware.ts +++ b/src/middlewares/errorHandling.middleware.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ import { Request, Response, NextFunction, ErrorRequestHandler } from 'express'; -import { CustomError } from '../exception'; -import logger from '../configs/logger.config'; +import { CustomError } from '@/exception'; +import logger from '@/configs/logger.config'; export const errorHandlingMiddleware = ((err: CustomError, req: Request, res: Response, next: NextFunction) => { if (err instanceof CustomError) { diff --git a/src/middlewares/validation.middleware.ts b/src/middlewares/validation.middleware.ts index 2434c1c..e635011 100644 --- a/src/middlewares/validation.middleware.ts +++ b/src/middlewares/validation.middleware.ts @@ -1,7 +1,7 @@ import { NextFunction, Request, Response, RequestHandler } from 'express'; import { plainToInstance } from 'class-transformer'; import { validate } from 'class-validator'; -import logger from '../configs/logger.config'; +import logger from '@/configs/logger.config'; type RequestKey = 'body' | 'user' | 'query'; diff --git a/src/modules/__test__/test.aes.encryption.test.ts b/src/modules/__test__/test.aes.encryption.test.ts index 4624ca7..9c0be8f 100644 --- a/src/modules/__test__/test.aes.encryption.test.ts +++ b/src/modules/__test__/test.aes.encryption.test.ts @@ -1,5 +1,5 @@ import crypto from 'crypto'; -import AESEncryption from '../token_encryption/aes_encryption'; +import AESEncryption from '@/modules/token_encryption/aes_encryption'; describe('AESEncryption 클래스 테스트', () => { const validKey = crypto.randomBytes(32).toString('hex').slice(0, 32); // 32바이트 키 생성 @@ -14,9 +14,7 @@ describe('AESEncryption 클래스 테스트', () => { }); test('잘못된 키 길이를 사용하면 오류가 발생해야 한다', () => { - expect(() => new AESEncryption(invalidKey)).toThrow( - '키는 256비트(32바이트)여야 합니다.' - ); + expect(() => new AESEncryption(invalidKey)).toThrow('키는 256비트(32바이트)여야 합니다.'); }); test('암호화 결과는 base64 형식이어야 한다', () => { diff --git a/src/repositories/post.repository.ts b/src/repositories/post.repository.ts index 5e257ee..784be5e 100644 --- a/src/repositories/post.repository.ts +++ b/src/repositories/post.repository.ts @@ -1,6 +1,6 @@ import { Pool } from 'pg'; -import logger from '../configs/logger.config'; -import { DBError } from '../exception'; +import logger from '@/configs/logger.config'; +import { DBError } from '@/exception'; export class PostRepository { constructor(private pool: Pool) {} diff --git a/src/repositories/tracking.repository.ts b/src/repositories/tracking.repository.ts index 01cd054..ee47853 100644 --- a/src/repositories/tracking.repository.ts +++ b/src/repositories/tracking.repository.ts @@ -1,7 +1,7 @@ import { Pool } from 'pg'; -import logger from '../configs/logger.config'; -import { DBError } from '../exception'; -import { EventRequestDto } from '../types'; +import logger from '@/configs/logger.config'; +import { DBError } from '@/exception'; +import { EventRequestDto } from '@/types'; export class TrackingRepository { constructor(private readonly pool: Pool) {} diff --git a/src/repositories/user.repository.ts b/src/repositories/user.repository.ts index 543fde9..ed344d7 100644 --- a/src/repositories/user.repository.ts +++ b/src/repositories/user.repository.ts @@ -1,7 +1,7 @@ import { Pool } from 'pg'; -import logger from '../configs/logger.config'; -import { User } from '../types'; -import { DBError } from '../exception'; +import logger from '@/configs/logger.config'; +import { User } from '@/types'; +import { DBError } from '@/exception'; export class UserRepository { constructor(private readonly pool: Pool) {} diff --git a/src/routes/post.router.ts b/src/routes/post.router.ts index 9ddc887..8a6ed31 100644 --- a/src/routes/post.router.ts +++ b/src/routes/post.router.ts @@ -1,12 +1,12 @@ import express, { Router } from 'express'; -import pool from '../configs/db.config'; import dotenv from 'dotenv'; -import { authMiddleware } from '../middlewares/auth.middleware'; -import { PostRepository } from '../repositories/post.repository'; -import { PostService } from '../services/post.service'; -import { PostController } from '../controllers/post.controller'; -import { validateRequestDto } from 'src/middlewares/validation.middleware'; -import { GetAllPostsQueryDto } from 'src/types/dto/requests/getAllPostsQuery.type'; +import pool from '@/configs/db.config'; +import { authMiddleware } from '@/middlewares/auth.middleware'; +import { PostRepository } from '@/repositories/post.repository'; +import { PostService } from '@/services/post.service'; +import { PostController } from '@/controllers/post.controller'; +import { validateRequestDto } from '@/middlewares/validation.middleware'; +import { GetAllPostsQueryDto } from '@/types/dto/requests/getAllPostsQuery.type'; const router: Router = express.Router(); dotenv.config(); diff --git a/src/routes/tracking.router.ts b/src/routes/tracking.router.ts index 540bf16..2a08a97 100644 --- a/src/routes/tracking.router.ts +++ b/src/routes/tracking.router.ts @@ -1,13 +1,13 @@ import express, { Router } from 'express'; -import pool from '../configs/db.config'; +import pool from '@/configs/db.config'; import dotenv from 'dotenv'; -import { TrackingRepository } from '../repositories/tracking.repository'; -import { TrackingService } from '../services/tracking.service'; -import { TrackingController } from '../controllers/tracking.controller'; -import { validateRequestDto } from '../middlewares/validation.middleware'; -import { EventRequestDto } from '../types'; -import { authMiddleware } from '../middlewares/auth.middleware'; -import { StayTimeRequestDto } from '../types'; +import { TrackingRepository } from '@/repositories/tracking.repository'; +import { TrackingService } from '@/services/tracking.service'; +import { TrackingController } from '@/controllers/tracking.controller'; +import { validateRequestDto } from '@/middlewares/validation.middleware'; +import { EventRequestDto } from '@/types'; +import { authMiddleware } from '@/middlewares/auth.middleware'; +import { StayTimeRequestDto } from '@/types'; const router: Router = express.Router(); dotenv.config(); diff --git a/src/routes/user.router.ts b/src/routes/user.router.ts index f1d515b..4206790 100644 --- a/src/routes/user.router.ts +++ b/src/routes/user.router.ts @@ -1,12 +1,12 @@ import express, { Router } from 'express'; -import { UserController } from '../controllers/user.controller'; -import { UserRepository } from '../repositories/user.repository'; -import { UserService } from '../services/user.service'; -import pool from '../configs/db.config'; -import { authMiddleware } from '../middlewares/auth.middleware'; -import { validateRequestDto } from '../middlewares/validation.middleware'; +import { UserController } from '@/controllers/user.controller'; +import { UserRepository } from '@/repositories/user.repository'; +import { UserService } from '@/services/user.service'; +import pool from '@/configs/db.config'; +import { authMiddleware } from '@/middlewares/auth.middleware'; +import { validateRequestDto } from '@/middlewares/validation.middleware'; import dotenv from 'dotenv'; -import { VelogUserLoginDto } from '../types'; +import { VelogUserLoginDto } from '@/types'; const router: Router = express.Router(); dotenv.config(); diff --git a/src/services/post.service.ts b/src/services/post.service.ts index aa2bc98..0297281 100644 --- a/src/services/post.service.ts +++ b/src/services/post.service.ts @@ -1,5 +1,5 @@ -import logger from '../configs/logger.config'; -import { PostRepository } from '../repositories/post.repository'; +import logger from '@/configs/logger.config'; +import { PostRepository } from '@/repositories/post.repository'; export class PostService { constructor(private postRepo: PostRepository) {} diff --git a/src/services/tracking.service.ts b/src/services/tracking.service.ts index 623fc7f..9dfe354 100644 --- a/src/services/tracking.service.ts +++ b/src/services/tracking.service.ts @@ -1,7 +1,7 @@ -import { EventRequestDto, StayTimeRequestDto } from '../types'; -import { TrackingRepository } from '../repositories/tracking.repository'; -import logger from '../configs/logger.config'; -import { BadRequestError } from '../exception'; +import { EventRequestDto, StayTimeRequestDto } from '@/types'; +import { TrackingRepository } from '@/repositories/tracking.repository'; +import logger from '@/configs/logger.config'; +import { BadRequestError } from '@/exception'; export class TrackingService { constructor(private trackingRepo: TrackingRepository) {} diff --git a/src/services/user.service.ts b/src/services/user.service.ts index 1876b9a..d7e3113 100644 --- a/src/services/user.service.ts +++ b/src/services/user.service.ts @@ -1,10 +1,10 @@ -import logger from '../configs/logger.config'; -import { TokenError } from '../exception/'; -import { getKeyByGroup } from '../utils/key.util'; -import AESEncryption from '../modules/token_encryption/aes_encryption'; -import { UserRepository } from '../repositories/user.repository'; -import { UserWithTokenDto, User } from '../types'; -import { generateRandomGroupId } from '../utils/generateGroupId.util'; +import logger from '@/configs/logger.config'; +import { TokenError } from '@/exception/'; +import { getKeyByGroup } from '@/utils/key.util'; +import AESEncryption from '@/modules/token_encryption/aes_encryption'; +import { UserRepository } from '@/repositories/user.repository'; +import { UserWithTokenDto, User } from '@/types'; +import { generateRandomGroupId } from '@/utils/generateGroupId.util'; export class UserService { constructor(private userRepo: UserRepository) {} diff --git a/src/types/dto/responses/emptyReponse.type.ts b/src/types/dto/responses/emptyReponse.type.ts index 16cf08f..41b7e28 100644 --- a/src/types/dto/responses/emptyReponse.type.ts +++ b/src/types/dto/responses/emptyReponse.type.ts @@ -1,9 +1,5 @@ -import { BaseResponseDto } from './baseResponse.type'; +import { BaseResponseDto } from '@/types/dto/responses/baseResponse.type'; // eslint-disable-next-line @typescript-eslint/no-empty-object-type interface EmptyResponseData {} -export class EmptyResponseDto extends BaseResponseDto { - constructor(success: boolean, message: string, error: string | null = null) { - super(success, message, {}, error); - } -} +export class EmptyResponseDto extends BaseResponseDto {} diff --git a/src/types/dto/responses/loginResponse.type.ts b/src/types/dto/responses/loginResponse.type.ts index 8bf9f49..f842020 100644 --- a/src/types/dto/responses/loginResponse.type.ts +++ b/src/types/dto/responses/loginResponse.type.ts @@ -1,4 +1,4 @@ -import { BaseResponseDto } from './baseResponse.type'; +import { BaseResponseDto } from '@/types/dto/responses/baseResponse.type'; interface ProfileType { thumbnail: string; @@ -10,16 +10,4 @@ interface LoginResponseData { profile: ProfileType; } -export class LoginResponseDto extends BaseResponseDto { - constructor( - success: boolean, - message: string, - id: number, - username: string, - profile: ProfileType, - error: string | null, - ) { - const data = { id, username, profile }; - super(success, message, data, error); - } -} +export class LoginResponseDto extends BaseResponseDto {} diff --git a/src/types/dto/responses/postResponse.type.ts b/src/types/dto/responses/postResponse.type.ts index 819d2dc..94d0414 100644 --- a/src/types/dto/responses/postResponse.type.ts +++ b/src/types/dto/responses/postResponse.type.ts @@ -1,4 +1,4 @@ -import { BaseResponseDto } from './baseResponse.type'; +import { BaseResponseDto } from '@/types/dto/responses/baseResponse.type'; // ------ 전체 조회 ------ interface GetAllPostType { @@ -17,18 +17,7 @@ interface PostsResponseData { posts: GetAllPostType[]; } -export class PostsResponseDto extends BaseResponseDto { - constructor( - success: boolean, - message: string, - nextCursor: string | null, - posts: GetAllPostType[], - error: string | null, - ) { - const data = { nextCursor, posts }; - super(success, message, data, error); - } -} +export class PostsResponseDto extends BaseResponseDto {} // ------ 단건 조회 ------ interface GetPostType { @@ -41,12 +30,7 @@ interface PostResponseData { post: GetPostType[]; } -export class PostResponseDto extends BaseResponseDto { - constructor(success: boolean, message: string, post: GetPostType[], error: string | null) { - const data = { post }; - super(success, message, data, error); - } -} +export class PostResponseDto extends BaseResponseDto {} // ------ 전체 통계 ------ interface PostStatisticsType { @@ -62,15 +46,4 @@ interface PostStatisticsData { stats: PostStatisticsType; } -export class PostStatisticsResponseDto extends BaseResponseDto { - constructor( - success: boolean, - message: string, - totalPostCount: number, - stats: PostStatisticsType, - error: string | null, - ) { - const data = { totalPostCount, stats }; - super(success, message, data, error); - } -} +export class PostStatisticsResponseDto extends BaseResponseDto {} diff --git a/src/types/express.d.ts b/src/types/express.d.ts index 8c3231a..03a95c3 100644 --- a/src/types/express.d.ts +++ b/src/types/express.d.ts @@ -1,4 +1,4 @@ -import { VelogUserLoginResponse } from '../velog.type'; +import { VelogUserLoginResponse } from '@/velog.type'; declare global { namespace Express { diff --git a/src/types/index.ts b/src/types/index.ts index 934dd0f..318c3a8 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,18 +1,18 @@ -export type { User } from './models/User.type'; -export type { Post } from './models/Post.type'; -export type { PostDailyStatistics } from './models/PostDailyStatistics.type'; -export type { PostStatistics } from './models/PostStatistics.type'; -export type { UserEventTracking } from './models/UserEventTracking.type'; -export type { VelogUserLoginResponse } from './velog.type'; -export type { GetAllPostsQuery } from './dto/requests/getAllPostsQuery.type'; -export type { GetPostQuery, PostParam } from './dto/requests/getPostQuery.type'; +export type { User } from '@/types/models/User.type'; +export type { Post } from '@/types/models/Post.type'; +export type { PostDailyStatistics } from '@/types/models/PostDailyStatistics.type'; +export type { PostStatistics } from '@/types/models/PostStatistics.type'; +export type { UserEventTracking } from '@/types/models/UserEventTracking.type'; +export type { VelogUserLoginResponse } from '@/types/velog.type'; +export type { GetAllPostsQuery } from '@/types/dto/requests/getAllPostsQuery.type'; +export type { GetPostQuery, PostParam } from '@/types/dto/requests/getPostQuery.type'; -export { StayTimeRequestDto } from './dto/requests/stayTimeRequest.dto'; -export { GetAllPostsQueryDto } from './dto/requests/getAllPostsQuery.type'; -export { GetPostQueryDto } from './dto/requests/getPostQuery.type'; -export { LoginResponseDto } from './dto/responses/loginResponse.type'; -export { EmptyResponseDto } from './dto/responses/emptyReponse.type'; -export { PostsResponseDto, PostResponseDto, PostStatisticsResponseDto } from './dto/responses/postResponse.type'; -export { EventRequestDto } from './dto/requests/eventRequest.type'; -export { UserWithTokenDto } from './dto/userWithToken.type'; -export { VelogUserLoginDto } from './dto/velogUser.type'; +export { StayTimeRequestDto } from '@/types/dto/requests/stayTimeRequest.dto'; +export { GetAllPostsQueryDto } from '@/types/dto/requests/getAllPostsQuery.type'; +export { GetPostQueryDto } from '@/types/dto/requests/getPostQuery.type'; +export { LoginResponseDto } from '@/types/dto/responses/loginResponse.type'; +export { EmptyResponseDto } from '@/types/dto/responses/emptyReponse.type'; +export { PostsResponseDto, PostResponseDto, PostStatisticsResponseDto } from '@/types/dto/responses/postResponse.type'; +export { EventRequestDto } from '@/types/dto/requests/eventRequest.type'; +export { UserWithTokenDto } from '@/types/dto/userWithToken.type'; +export { VelogUserLoginDto } from '@/types/dto/velogUser.type'; diff --git a/src/types/models/UserEventTracking.type.ts b/src/types/models/UserEventTracking.type.ts index 0e799c0..921d7bf 100644 --- a/src/types/models/UserEventTracking.type.ts +++ b/src/types/models/UserEventTracking.type.ts @@ -1,4 +1,4 @@ -import { UserEventType } from '../userEvent.type'; +import { UserEventType } from '@/userEvent.type'; export interface UserEventTracking { id: number; diff --git a/tsconfig.json b/tsconfig.json index 0360352..ae01cdd 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -9,7 +9,6 @@ "@/*": ["*"] }, "outDir": "./dist", - "rootDir": "./src", "declaration": true, "sourceMap": true, "removeComments": true, @@ -24,6 +23,6 @@ "allowSyntheticDefaultImports": true, "plugins": [] }, - "include": ["src/**/*"], + "include": ["src/**/*", "jest.config.ts"], "exclude": ["node_modules", "dist"] } From abeb79bf06d0e2c3fbb455cdf4411c63871bd236 Mon Sep 17 00:00:00 2001 From: HA0N1 Date: Fri, 3 Jan 2025 01:33:17 +0900 Subject: [PATCH 8/9] =?UTF-8?q?hotfix:=20import=20path=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/types/models/UserEventTracking.type.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/models/UserEventTracking.type.ts b/src/types/models/UserEventTracking.type.ts index 921d7bf..a58e450 100644 --- a/src/types/models/UserEventTracking.type.ts +++ b/src/types/models/UserEventTracking.type.ts @@ -1,4 +1,4 @@ -import { UserEventType } from '@/userEvent.type'; +import { UserEventType } from '@/types/userEvent.type'; export interface UserEventTracking { id: number; From 39fa94d36d35f6e76cbead0781d5df979b67e816 Mon Sep 17 00:00:00 2001 From: HA0N1 Date: Fri, 3 Jan 2025 20:02:37 +0900 Subject: [PATCH 9/9] =?UTF-8?q?modify:=20EmptyResponseDto=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ecosystem.config.js | 2 +- src/types/dto/requests/getPostQuery.type.ts | 4 ++++ src/types/dto/responses/emptyReponse.type.ts | 3 +-- tsconfig.json | 3 ++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/ecosystem.config.js b/ecosystem.config.js index ab89e48..e20fe20 100644 --- a/ecosystem.config.js +++ b/ecosystem.config.js @@ -2,7 +2,7 @@ module.exports = { apps: [ { name: 'velog-dashboard-v2-api', - script: 'dist/index.js', // 빌드된 메인 파일 경로 + script: 'dist/src/index.js', // 빌드된 메인 파일 경로 instances: 1, autorestart: true, watch: false, diff --git a/src/types/dto/requests/getPostQuery.type.ts b/src/types/dto/requests/getPostQuery.type.ts index 3250282..c4dffe4 100644 --- a/src/types/dto/requests/getPostQuery.type.ts +++ b/src/types/dto/requests/getPostQuery.type.ts @@ -1,3 +1,4 @@ +import { Type } from 'class-transformer'; import { IsDate, IsOptional } from 'class-validator'; export interface PostParam { @@ -12,9 +13,12 @@ export interface GetPostQuery { export class GetPostQueryDto { @IsOptional() @IsDate() + @Type(() => Date) start?: string; + @IsOptional() @IsDate() + @Type(() => Date) end?: string; constructor(start: string, end: string) { diff --git a/src/types/dto/responses/emptyReponse.type.ts b/src/types/dto/responses/emptyReponse.type.ts index 41b7e28..e708962 100644 --- a/src/types/dto/responses/emptyReponse.type.ts +++ b/src/types/dto/responses/emptyReponse.type.ts @@ -1,5 +1,4 @@ import { BaseResponseDto } from '@/types/dto/responses/baseResponse.type'; -// eslint-disable-next-line @typescript-eslint/no-empty-object-type -interface EmptyResponseData {} +type EmptyResponseData = Record; export class EmptyResponseDto extends BaseResponseDto {} diff --git a/tsconfig.json b/tsconfig.json index ae01cdd..eff9de6 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,7 +8,8 @@ "paths": { "@/*": ["*"] }, - "outDir": "./dist", + "outDir": "dist", + "rootDir": ".", "declaration": true, "sourceMap": true, "removeComments": true,