Skip to content

Commit 3477d3a

Browse files
authored
[25.01.01/ TASK-85] refactor dto and type (#10)
* refactor: post response dto * refactor: post response dto v2 * refactor: 모든 api response dto 수정 * refactor: 함수 줄 바꿈 * hotfix: db 연결 port * deploy branch squash merge * refactor: response dto 리펙토링 * hotfix: import path 수정 * modify: EmptyResponseDto 수정
1 parent c3d7bd0 commit 3477d3a

38 files changed

+377
-223
lines changed

.dockerignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
node_modules
33
.git
44
.env
5-
dist
5+
dist

jest.config.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ const config: Config = {
44
preset: 'ts-jest',
55
testEnvironment: 'node',
66
testMatch: ['**/src/**/*.test.ts'],
7+
moduleNameMapper: {
8+
'^@/(.*)$': '<rootDir>/src/$1',
9+
},
710
moduleFileExtensions: ['ts', 'js'],
811
transform: {
912
'^.+\\.ts$': 'ts-jest',

src/controllers/post.controller.ts

Lines changed: 47 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,88 +1,82 @@
11
import { NextFunction, Request, RequestHandler, Response } from 'express';
2-
import logger from '../configs/logger.config';
3-
import { PostService } from '../services/post.service';
4-
import { GetAllPostsQuery, PostResponse } from '../types';
5-
import { GetPostQuery } from '../types/requests/getPostQuery.type';
2+
import logger from '@/configs/logger.config';
3+
import { PostService } from '@/services/post.service';
4+
import {
5+
GetAllPostsQuery,
6+
PostsResponseDto,
7+
PostResponseDto,
8+
GetPostQuery,
9+
PostParam,
10+
PostStatisticsResponseDto,
11+
} from '@/types';
612

713
export class PostController {
814
constructor(private postService: PostService) {}
915

10-
private validateQueryParams(query: GetAllPostsQuery): {
11-
cursor: string | undefined;
12-
sort: string;
13-
isAsc: boolean;
14-
} {
15-
return {
16-
cursor: query.cursor,
17-
sort: query.sort || '',
18-
isAsc: query.asc === 'true',
19-
};
20-
}
21-
private validateQueryParams2(query: Partial<GetPostQuery>): {
22-
start: string;
23-
end: string;
24-
} {
25-
return {
26-
start: query.start || '',
27-
end: query.end || '',
28-
};
29-
}
3016
getAllPost: RequestHandler = async (
3117
req: Request<object, object, object, GetAllPostsQuery>,
32-
res: Response<PostResponse>,
18+
res: Response<PostsResponseDto>,
3319
next: NextFunction,
3420
) => {
3521
try {
3622
const { id } = req.user;
37-
const { cursor, sort, isAsc } = this.validateQueryParams(req.query);
23+
const { cursor, sort, asc } = req.query;
3824

39-
const result = await this.postService.getAllposts(id, cursor, sort, isAsc);
25+
const result = await this.postService.getAllposts(id, cursor, sort, asc);
4026

41-
res.status(200).json({
42-
success: true,
43-
message: 'post 전체 조회에 성공하였습니다.',
44-
data: {
45-
nextCursor: result.nextCursor,
46-
posts: result.posts,
47-
},
48-
error: null,
49-
});
27+
const response = new PostsResponseDto(
28+
true,
29+
'전체 post 조회에 성공하였습니다.',
30+
{ nextCursor: result.nextCursor, posts: result.posts },
31+
null,
32+
);
33+
34+
res.status(200).json(response);
5035
} catch (error) {
5136
logger.error('전체 조회 실패:', error);
5237
next(error);
5338
}
5439
};
5540

56-
getAllPostStatistics: RequestHandler = async (req: Request, res: Response, next: NextFunction) => {
41+
getAllPostStatistics: RequestHandler = async (
42+
req: Request,
43+
res: Response<PostStatisticsResponseDto>,
44+
next: NextFunction,
45+
) => {
5746
try {
5847
const { id } = req.user;
5948

60-
const result = await this.postService.getAllPostStatistics(id);
49+
const stats = await this.postService.getAllPostStatistics(id);
6150
const totalPostCount = await this.postService.getTotalPostCounts(id);
6251

63-
res.status(200).json({
64-
success: true,
65-
message: 'post 전체 통계 조회에 성공하였습니다.',
66-
data: { totalPostCount, stats: result },
67-
error: null,
68-
});
52+
const response = new PostStatisticsResponseDto(
53+
true,
54+
'전체 post 통계 조회에 성공하였습니다.',
55+
{ totalPostCount, stats },
56+
null,
57+
);
58+
59+
res.status(200).json(response);
6960
} catch (error) {
7061
logger.error('전체 통계 조회 실패:', error);
7162
next(error);
7263
}
7364
};
7465

75-
getPost: RequestHandler = async (req: Request, res: Response, next: NextFunction) => {
66+
getPost: RequestHandler = async (
67+
req: Request<PostParam, object, object, GetPostQuery>,
68+
res: Response<PostResponseDto>,
69+
next: NextFunction,
70+
) => {
7671
try {
77-
const postId = parseInt(req.params.postId);
78-
const { start, end } = this.validateQueryParams2(req.query);
72+
const postId = Number(req.params.postId);
73+
const { start, end } = req.query;
74+
7975
const post = await this.postService.getPost(postId, start, end);
80-
res.status(200).json({
81-
success: true,
82-
message: 'post 단건 조회에 성공하였습니다',
83-
data: { post },
84-
error: null,
85-
});
76+
77+
const response = new PostResponseDto(true, '단건 post 조회에 성공하였습니다.', { post }, null);
78+
79+
res.status(200).json(response);
8680
} catch (error) {
8781
logger.error('단건 조회 실패 : ', error);
8882
next(error);
Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,40 @@
11
import { NextFunction, Request, RequestHandler, Response } from 'express';
2-
import logger from '../configs/logger.config';
3-
import { TrackingService } from '../services/tracking.service';
4-
import { TrackingResponse } from '../types';
2+
import logger from '@/configs/logger.config';
3+
import { TrackingService } from '@/services/tracking.service';
4+
import { EmptyResponseDto } from '@/types';
55

66
export class TrackingController {
77
constructor(private trackingService: TrackingService) {}
88

9-
event = (async (req: Request, res: Response<TrackingResponse>, next: NextFunction) => {
9+
event: RequestHandler = async (req: Request, res: Response<EmptyResponseDto>, next: NextFunction) => {
1010
try {
1111
const { eventType } = req.body;
1212
const { id } = req.user;
1313

1414
await this.trackingService.tracking(eventType, id);
15-
return res.status(200).json({ success: true, message: '이벤트 데이터 저장완료', data: {}, error: null });
15+
16+
const response = new EmptyResponseDto(true, '이벤트 데이터 저장완료', {}, null);
17+
18+
res.status(200).json(response);
1619
} catch (error) {
1720
logger.error('user tracking 실패 : ', error);
1821
next(error);
1922
}
20-
}) as RequestHandler;
23+
};
2124

22-
stay = (async (req: Request, res: Response<TrackingResponse>, next: NextFunction) => {
25+
stay: RequestHandler = async (req: Request, res: Response<EmptyResponseDto>, next: NextFunction) => {
2326
try {
2427
const { loadDate, unloadDate } = req.body;
2528
const { id } = req.user;
2629

2730
await this.trackingService.stay({ loadDate, unloadDate }, id);
28-
return res.status(200).json({ success: true, message: '체류시간 데이터 저장 완료', data: {}, error: null });
31+
32+
const response = new EmptyResponseDto(true, '체류시간 데이터 저장 완료', {}, null);
33+
34+
res.status(200).json(response);
2935
} catch (error) {
3036
logger.error('user stay time 저장 실패 : ', error);
3137
next(error);
3238
}
33-
}) as RequestHandler;
39+
};
3440
}

src/controllers/user.controller.ts

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { NextFunction, Request, Response, RequestHandler, CookieOptions } from 'express';
2-
import logger from '../configs/logger.config';
3-
import { LoginResponse, UserWithTokenDto } from '../types';
4-
import { UserService } from '../services/user.service';
2+
import logger from '@/configs/logger.config';
3+
import { EmptyResponseDto, LoginResponseDto, UserWithTokenDto } from '@/types';
4+
import { UserService } from '@/services/user.service';
55
export class UserController {
66
constructor(private userService: UserService) {}
77

@@ -21,7 +21,7 @@ export class UserController {
2121
return baseOptions;
2222
}
2323

24-
login: RequestHandler = async (req: Request, res: Response<LoginResponse>, next: NextFunction): Promise<void> => {
24+
login: RequestHandler = async (req: Request, res: Response<LoginResponseDto>, next: NextFunction): Promise<void> => {
2525
try {
2626
const { id, email, profile, username } = req.user;
2727
const { accessToken, refreshToken } = req.tokens;
@@ -35,32 +35,39 @@ export class UserController {
3535
res.cookie('access_token', accessToken, this.cookieOption());
3636
res.cookie('refresh_token', refreshToken, this.cookieOption());
3737

38-
res.status(200).json({
39-
success: true,
40-
message: '로그인에 성공하였습니다.',
41-
data: { id: isExistUser.id, username, profile },
42-
error: null,
43-
});
38+
const response = new LoginResponseDto(
39+
true,
40+
'로그인에 성공하였습니다.',
41+
{ id: isExistUser.id, username, profile },
42+
null,
43+
);
44+
45+
res.status(200).json(response);
4446
} catch (error) {
4547
logger.error('로그인 실패 : ', error);
4648
next(error);
4749
}
4850
};
4951

50-
logout: RequestHandler = async (req: Request, res: Response) => {
52+
logout: RequestHandler = async (req: Request, res: Response<EmptyResponseDto>) => {
5153
res.clearCookie('access_token');
5254
res.clearCookie('refresh_token');
5355

54-
res.status(200).json({ success: true, message: '로그아웃에 성공하였습니다.', data: {}, error: null });
56+
const response = new EmptyResponseDto(true, '로그아웃에 성공하였습니다.', {}, null);
57+
58+
res.status(200).json(response);
5559
};
5660

57-
fetchCurrentUser: RequestHandler = (req: Request, res: Response) => {
61+
fetchCurrentUser: RequestHandler = (req: Request, res: Response<LoginResponseDto>) => {
5862
const { user } = req;
59-
res.status(200).json({
60-
success: true,
61-
message: '프로필 조회에 성공하였습니다.',
62-
data: { user },
63-
error: null,
64-
});
63+
64+
const response = new LoginResponseDto(
65+
true,
66+
'유저 정보 조회에 성공하였습니다.',
67+
{ id: user.id, username: user.username, profile: user.profile },
68+
null,
69+
);
70+
71+
res.status(200).json(response);
6572
};
6673
}

src/middlewares/auth.middleware.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import { NextFunction, Request, Response } from 'express';
22
import axios from 'axios';
33
import { isUUID } from 'class-validator';
4-
import logger from '../configs/logger.config';
5-
import pool from '../configs/db.config';
6-
import { DBError, InvalidTokenError } from '../exception';
7-
import { VELOG_API_URL, VELOG_QUERIES } from '../constants/velog.constans';
4+
import logger from '@/configs/logger.config';
5+
import pool from '@/configs/db.config';
6+
import { DBError, InvalidTokenError } from '@/exception';
7+
import { VELOG_API_URL, VELOG_QUERIES } from '@/constants/velog.constans';
88

99
/**
1010
* 요청에서 토큰을 추출하는 함수
@@ -58,7 +58,6 @@ const fetchVelogApi = async (query: string, accessToken: string, refreshToken: s
5858
}
5959
};
6060

61-
6261
/**
6362
* JWT 토큰에서 페이로드를 추출하고 디코딩하는 함수
6463
* @param token - 디코딩할 JWT 토큰 문자열

src/middlewares/errorHandling.middleware.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* eslint-disable @typescript-eslint/no-unused-vars */
22
import { Request, Response, NextFunction, ErrorRequestHandler } from 'express';
3-
import { CustomError } from '../exception';
4-
import logger from '../configs/logger.config';
3+
import { CustomError } from '@/exception';
4+
import logger from '@/configs/logger.config';
55

66
export const errorHandlingMiddleware = ((err: CustomError, req: Request, res: Response, next: NextFunction) => {
77
if (err instanceof CustomError) {

src/middlewares/validation.middleware.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import { NextFunction, Request, Response, RequestHandler } from 'express';
22
import { plainToInstance } from 'class-transformer';
33
import { validate } from 'class-validator';
4-
import logger from '../configs/logger.config';
4+
import logger from '@/configs/logger.config';
55

6-
type RequestKey = 'body' | 'user';
6+
type RequestKey = 'body' | 'user' | 'query';
77

88
// eslint-disable-next-line @typescript-eslint/no-explicit-any
9-
export const validateDto = <T extends object>(dtoClass: new (...args: any) => T, key: RequestKey) => {
9+
export const validateRequestDto = <T extends object>(dtoClass: new (...args: any) => T, key: RequestKey) => {
1010
return (async (req: Request, res: Response, next: NextFunction) => {
1111
try {
1212
const value = plainToInstance(dtoClass, req[key]);

src/modules/__test__/test.aes.encryption.test.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import crypto from 'crypto';
2-
import AESEncryption from '../token_encryption/aes_encryption';
2+
import AESEncryption from '@/modules/token_encryption/aes_encryption';
33

44
describe('AESEncryption 클래스 테스트', () => {
55
const validKey = crypto.randomBytes(32).toString('hex').slice(0, 32); // 32바이트 키 생성
@@ -14,9 +14,7 @@ describe('AESEncryption 클래스 테스트', () => {
1414
});
1515

1616
test('잘못된 키 길이를 사용하면 오류가 발생해야 한다', () => {
17-
expect(() => new AESEncryption(invalidKey)).toThrow(
18-
'키는 256비트(32바이트)여야 합니다.'
19-
);
17+
expect(() => new AESEncryption(invalidKey)).toThrow('키는 256비트(32바이트)여야 합니다.');
2018
});
2119

2220
test('암호화 결과는 base64 형식이어야 한다', () => {

src/repositories/post.repository.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Pool } from 'pg';
2-
import logger from '../configs/logger.config';
3-
import { DBError } from '../exception';
2+
import logger from '@/configs/logger.config';
3+
import { DBError } from '@/exception';
44

55
export class PostRepository {
66
constructor(private pool: Pool) {}
@@ -10,10 +10,10 @@ export class PostRepository {
1010
// 1) 정렬 컬럼 매핑
1111
let sortCol = 'p.released_at';
1212
switch (sort) {
13-
case 'daily_view_count':
13+
case 'dailyViewCount':
1414
sortCol = 'pds.daily_view_count';
1515
break;
16-
case 'daily_like_count':
16+
case 'dailyLikeCount':
1717
sortCol = 'pds.daily_like_count';
1818
break;
1919
default:
@@ -96,9 +96,9 @@ export class PostRepository {
9696
// nextCursor = `${정렬 컬럼 값},${p.id}`
9797
// 예: 만약 sortCol이 p.title인 경우, lastPost.title + ',' + lastPost.id
9898
let sortValueForCursor = '';
99-
if (sort === 'daily_view_count') {
99+
if (sort === 'dailyViewCount') {
100100
sortValueForCursor = lastPost.daily_view_count;
101-
} else if (sort === 'daily_like_count') {
101+
} else if (sort === 'dailyLikeCount') {
102102
sortValueForCursor = lastPost.daily_like_count;
103103
} else {
104104
sortValueForCursor = new Date(lastPost.post_released_at).toISOString();
@@ -127,7 +127,7 @@ export class PostRepository {
127127
}
128128

129129
async getYesterdayAndTodayViewLikeStats(userId: number) {
130-
// pds.updated_at 은 FE 화면을 위해 억지로 9h 시간 더한 값임 주의
130+
// ! pds.updated_at 은 FE 화면을 위해 억지로 9h 시간 더한 값임 주의
131131
try {
132132
const query = `
133133
SELECT

0 commit comments

Comments
 (0)