Skip to content

Commit cab238c

Browse files
committed
feature: post 의 상세 통계에서 start, end 에 대한 벨리데이션, 그리고 KST 기준 연산과 이를 위한 모든 부분 대응 개발
1 parent b9f58c5 commit cab238c

File tree

7 files changed

+45
-62
lines changed

7 files changed

+45
-62
lines changed

src/repositories/__test__/leaderboard.repo.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ describe('LeaderboardRepository', () => {
9494

9595
expect(mockPool.query).toHaveBeenCalledWith(
9696
expect.stringContaining('WHERE date >='), // pastDateKST를 사용하는 부분 확인
97-
expect.arrayContaining([expect.anything()]),
97+
[expect.any(Number)] // limit
9898
);
9999
});
100100

@@ -169,7 +169,7 @@ describe('LeaderboardRepository', () => {
169169

170170
expect(mockPool.query).toHaveBeenCalledWith(
171171
expect.stringContaining('WHERE date >='), // pastDateKST를 사용하는 부분 확인
172-
expect.arrayContaining([expect.anything()]),
172+
[expect.any(Number)] // limit
173173
);
174174
});
175175

src/repositories/post.repository.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,8 @@ export class PostRepository {
271271
}
272272

273273
async findPostByPostId(postId: number, start?: string, end?: string) {
274+
console.log(start, end);
275+
274276
try {
275277
// 기본 쿼리 부분
276278
const baseQuery = `

src/routes/post.router.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { PostRepository } from '@/repositories/post.repository';
66
import { PostService } from '@/services/post.service';
77
import { PostController } from '@/controllers/post.controller';
88
import { validateRequestDto } from '@/middlewares/validation.middleware';
9-
import { GetAllPostsQueryDto } from '@/types/dto/requests/getAllPostsQuery.type';
9+
import { GetAllPostsQueryDto, GetPostQueryDto } from '@/types';
1010

1111
const router: Router = express.Router();
1212
dotenv.config();
@@ -133,6 +133,11 @@ router.get('/post/velog/:postId', authMiddleware.verify, postController.getPostB
133133
* '500':
134134
* description: 서버 오류 / 데이터 베이스 조회 오류
135135
*/
136-
router.get('/post/:postId', authMiddleware.verify, postController.getPostByPostId);
136+
router.get(
137+
'/post/:postId',
138+
authMiddleware.verify,
139+
validateRequestDto(GetPostQueryDto, 'query'),
140+
postController.getPostByPostId
141+
);
137142

138143
export default router;

src/services/__test__/post.service.test.ts

Lines changed: 6 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -317,65 +317,39 @@ describe('PostService', () => {
317317
},
318318
];
319319

320-
it('게시물 ID로 상세 통계 조회', async () => {
321-
// Arrange
322-
postRepo.findPostByPostId.mockResolvedValue(mockPostStats);
323-
324-
// Act
325-
const result = await postService.getPostByPostId(1);
326-
327-
// Assert
328-
expect(result).toEqual(expectedTransformedStats);
329-
expect(postRepo.findPostByPostId).toHaveBeenCalledWith(1, undefined, undefined);
330-
});
331-
332320
it('시작일과 종료일을 지정하여 상세 통계 조회', async () => {
333-
// Arrange
334321
const start = '2025-03-01';
335322
const end = '2025-03-10';
336323

337324
postRepo.findPostByPostId.mockResolvedValue(mockPostStats);
338325

339-
// Act
340326
const result = await postService.getPostByPostId(1, start, end);
341-
342-
// Assert
343327
expect(result).toEqual(expectedTransformedStats);
344-
expect(postRepo.findPostByPostId).toHaveBeenCalledWith(1, start, end);
328+
expect(postRepo.findPostByPostId).toHaveBeenCalledWith(1, `${start} 00:00:00+09`, `${end} 00:00:00+09`);
345329
});
346330

347331
it('빈 통계 목록 처리', async () => {
348-
// Arrange
349332
postRepo.findPostByPostId.mockResolvedValue([]);
350333

351-
// Act
352334
const result = await postService.getPostByPostId(1);
353-
354-
// Assert
355335
expect(result).toEqual([]);
356336
});
357337

358338
it('쿼리 오류 발생 시 예외를 그대로 전파', async () => {
359-
// Arrange
360339
const errorMessage = '게시물 조회 중 문제가 발생했습니다.';
361340
postRepo.findPostByPostId.mockRejectedValue(new DBError(errorMessage));
362341

363-
// Act & Assert
364342
await expect(postService.getPostByPostId(1)).rejects.toThrow(errorMessage);
365343
});
366344

367345
it('숫자가 아닌 ID를 전달해도 처리되어야 함', async () => {
368-
// Arrange
369346
postRepo.findPostByPostId.mockResolvedValue(mockPostStats);
370-
371-
// Act
372347
const result = await postService.getPostByPostId('abc' as unknown as number);
373-
374-
// Assert
375348
expect(result).toEqual(expectedTransformedStats);
376349
// Repository에 ID가 'abc'로 전달됨 (내부적으로 변환하지 않음)
377-
expect(postRepo.findPostByPostId).toHaveBeenCalledWith('abc', undefined, undefined);
350+
expect(postRepo.findPostByPostId).toHaveBeenCalledWith('abc', "undefined 00:00:00+09", "undefined 00:00:00+09");
378351
});
352+
379353
});
380354

381355
describe('getPostByPostUUID', () => {
@@ -416,39 +390,30 @@ describe('PostService', () => {
416390
});
417391

418392
it('게시물 UUID로 상세 통계 조회 (기본 7일 범위)', async () => {
419-
// Arrange
420393
postRepo.findPostByPostUUID.mockResolvedValue(mockPostStats);
421394

422-
// Act
423395
const result = await postService.getPostByPostUUID('uuid-1234');
424396

425-
// Assert
426397
expect(result).toEqual(expectedTransformedStats);
427398
// 7일 범위 설정 확인 (현재 날짜 2025-03-15 기준)
428399
expect(postRepo.findPostByPostUUID).toHaveBeenCalledWith(
429400
'uuid-1234',
430-
'2025-03-09', // 6일 전
431-
'2025-03-15' // 오늘
432-
);
401+
'2025-03-15', // 현재 날짜 (테스트에서 고정된 날짜)
402+
'2025-03-15' // 현재 날짜 (테스트에서 고정된 날짜)
403+
);
433404
});
434405

435406
it('빈 통계 목록 처리', async () => {
436-
// Arrange
437407
postRepo.findPostByPostUUID.mockResolvedValue([]);
438-
439-
// Act
440408
const result = await postService.getPostByPostUUID('uuid-1234');
441409

442-
// Assert
443410
expect(result).toEqual([]);
444411
});
445412

446413
it('쿼리 오류 발생 시 예외를 그대로 전파', async () => {
447-
// Arrange
448414
const errorMessage = 'UUID로 게시물 조회 중 문제가 발생했습니다.';
449415
postRepo.findPostByPostUUID.mockRejectedValue(new DBError(errorMessage));
450416

451-
// Act & Assert
452417
await expect(postService.getPostByPostUUID('uuid-1234')).rejects.toThrow(errorMessage);
453418
});
454419

src/services/post.service.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,12 @@ export class PostService {
6161
}
6262

6363
async getPostByPostId(postId: number, start?: string, end?: string) {
64+
// start, end 가 yyyy-mm-dd 만 넘어옴, 이를 kst 형태로 바꿔줘야 함
65+
const kstStart = `${start} 00:00:00+09`;
66+
const kstEnd = `${end} 00:00:00+09`;
67+
6468
try {
65-
const posts = await this.postRepo.findPostByPostId(postId, start, end);
69+
const posts = await this.postRepo.findPostByPostId(postId, kstStart, kstEnd);
6670

6771
const transformedPosts = this.transformPosts(posts);
6872

@@ -73,15 +77,18 @@ export class PostService {
7377
}
7478
}
7579

80+
// !! 해당 함수 사용하지 않는 것으로 보임, 추후 정리 필요
7681
async getPostByPostUUID(postUUUID: string) {
7782
try {
7883
const seoulNow = new Date(new Date().getTime() + 9 * 60 * 60 * 1000);
7984
const sevenDaysAgo = new Date(seoulNow);
8085

86+
const start = sevenDaysAgo.toISOString().split('T')[0];
8187
const end = seoulNow.toISOString().split('T')[0];
8288
sevenDaysAgo.setDate(seoulNow.getDate() - 6);
83-
const start = sevenDaysAgo.toISOString().split('T')[0];
8489

90+
// start, end 가 무조건 yyyy-mm-dd 로 넘어옴
91+
console.log(start, end);
8592
const posts = await this.postRepo.findPostByPostUUID(postUUUID, start, end);
8693

8794
const transformedPosts = this.transformPosts(posts);

src/types/dto/requests/getPostQuery.type.ts

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
import { Type } from 'class-transformer';
2-
import { IsDate, IsOptional } from 'class-validator';
1+
import { IsString, Validate } from 'class-validator';
32

43
export interface PostParam extends Record<string, string> {
54
postId: string;
@@ -29,18 +28,21 @@ export interface GetPostQuery {
2928
* nullable: true
3029
*/
3130
export class GetPostQueryDto {
32-
@IsOptional()
33-
@IsDate()
34-
@Type(() => Date)
31+
@IsString()
32+
@Validate((value: string) => {
33+
const date = new Date(value);
34+
return !isNaN(date.getTime());
35+
}, {
36+
message: '유효한 날짜 형식이 아닙니다. (예: YYYY-MM-DD)'
37+
})
3538
start?: string;
3639

37-
@IsOptional()
38-
@IsDate()
39-
@Type(() => Date)
40+
@IsString()
41+
@Validate((value: string) => {
42+
const date = new Date(value);
43+
return !isNaN(date.getTime());
44+
}, {
45+
message: '유효한 날짜 형식이 아닙니다. (예: YYYY-MM-DD)'
46+
})
4047
end?: string;
41-
42-
constructor(start: string, end: string) {
43-
this.start = start;
44-
this.end = end;
45-
}
46-
}
48+
}

src/utils/__test__/date.util.test.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,10 @@ describe('Date Utilities', () => {
5858

5959
it('getKSTDateStringWithOffset(0)은 getCurrentKSTDateString과 동일한 값을 반환해야 한다', () => {
6060
// 시간을 고정하여 두 함수 호출 사이에 실제 시간이 변경되지 않도록 함
61-
const fixed = Date.now();
61+
const fixedDate = new Date();
62+
const fixed = fixedDate.getTime();
6263
Date.now = jest.fn(() => fixed);
64+
jest.spyOn(global, 'Date').mockImplementation(() => fixedDate);
6365

6466
const current = getCurrentKSTDateString();
6567
const offsetZero = getKSTDateStringWithOffset(0);

0 commit comments

Comments
 (0)