Skip to content

Commit bf4afa2

Browse files
authored
[25.06.27 / TASK-210] Feature - 리더보드 username 대응 개발 (#37)
* feature: 리더보드 username 대응 개발 리더보드 응답값에 username 필드 추가 유저 리더보드 집계 조건을 email is not null 에서 username is not null로 수정 * modify: 타입 반영 놓친 부분 수정 * modify: 테스트 타임아웃 연장 (임시)
1 parent 2009215 commit bf4afa2

File tree

6 files changed

+50
-20
lines changed

6 files changed

+50
-20
lines changed

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

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { PostLeaderboardSortType, UserLeaderboardSortType } from '@/types';
1212

1313
dotenv.config();
1414

15-
jest.setTimeout(20000); // 각 케이스당 20초 타임아웃 설정
15+
jest.setTimeout(60000); // 각 케이스당 60초 타임아웃 설정
1616

1717
/**
1818
* LeaderboardRepository 통합 테스트
@@ -44,7 +44,7 @@ describe('LeaderboardRepository 통합 테스트', () => {
4444
idleTimeoutMillis: 30000, // 연결 유휴 시간 (30초)
4545
connectionTimeoutMillis: 5000, // 연결 시간 초과 (5초)
4646
allowExitOnIdle: false, // 유휴 상태에서 종료 허용
47-
statement_timeout: 30000,
47+
statement_timeout: 60000, // 쿼리 타임아웃 증가 (60초)
4848
};
4949

5050
// localhost 가 아니면 ssl 필수
@@ -105,6 +105,7 @@ describe('LeaderboardRepository 통합 테스트', () => {
105105
result.forEach((leaderboardUser) => {
106106
expect(leaderboardUser).toHaveProperty('id');
107107
expect(leaderboardUser).toHaveProperty('email');
108+
expect(leaderboardUser).toHaveProperty('username');
108109
expect(leaderboardUser).toHaveProperty('total_views');
109110
expect(leaderboardUser).toHaveProperty('total_likes');
110111
expect(leaderboardUser).toHaveProperty('total_posts');
@@ -214,13 +215,13 @@ describe('LeaderboardRepository 통합 테스트', () => {
214215
}
215216
});
216217

217-
it('email이 null인 사용자는 제외되어야 한다', async () => {
218+
it('username이 null인 사용자는 제외되어야 한다', async () => {
218219
const result = await repo.getUserLeaderboard(DEFAULT_PARAMS.USER_SORT, DEFAULT_PARAMS.DATE_RANGE, 30);
219220

220-
if (!isEnoughData(result, 1, '사용자 리더보드 email null 제외')) return;
221+
if (!isEnoughData(result, 1, '사용자 리더보드 username null 제외')) return;
221222

222223
result.forEach((user) => {
223-
expect(user.email).not.toBeNull();
224+
expect(user.username).not.toBeNull();
224225
});
225226
});
226227
});
@@ -241,6 +242,7 @@ describe('LeaderboardRepository 통합 테스트', () => {
241242
expect(leaderboardPost).toHaveProperty('id');
242243
expect(leaderboardPost).toHaveProperty('title');
243244
expect(leaderboardPost).toHaveProperty('slug');
245+
expect(leaderboardPost).toHaveProperty('username');
244246
expect(leaderboardPost).toHaveProperty('total_views');
245247
expect(leaderboardPost).toHaveProperty('total_likes');
246248
expect(leaderboardPost).toHaveProperty('view_diff');

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import { mockPool, createMockQueryResult } from '@/utils/fixtures';
66

77
jest.mock('pg');
88

9-
109
describe('LeaderboardRepository', () => {
1110
let repo: LeaderboardRepository;
1211

@@ -20,6 +19,7 @@ describe('LeaderboardRepository', () => {
2019
{
2120
id: '1',
2221
22+
username: 'test',
2323
total_views: 100,
2424
total_likes: 50,
2525
total_posts: 1,
@@ -30,6 +30,7 @@ describe('LeaderboardRepository', () => {
3030
{
3131
id: '2',
3232
33+
username: 'test2',
3334
total_views: 200,
3435
total_likes: 100,
3536
total_posts: 2,
@@ -79,7 +80,7 @@ describe('LeaderboardRepository', () => {
7980

8081
expect(mockPool.query).toHaveBeenCalledWith(
8182
expect.stringContaining('WHERE date >='), // pastDateKST를 사용하는 부분 확인
82-
[expect.any(Number)] // limit
83+
[expect.any(Number)], // limit
8384
);
8485
});
8586

@@ -96,6 +97,7 @@ describe('LeaderboardRepository', () => {
9697
id: '2',
9798
title: 'test2',
9899
slug: 'test2',
100+
username: 'test2',
99101
total_views: 200,
100102
total_likes: 100,
101103
view_diff: 20,
@@ -106,6 +108,7 @@ describe('LeaderboardRepository', () => {
106108
id: '1',
107109
title: 'test',
108110
slug: 'test',
111+
username: 'test',
109112
total_views: 100,
110113
total_likes: 50,
111114
view_diff: 10,
@@ -154,7 +157,7 @@ describe('LeaderboardRepository', () => {
154157

155158
expect(mockPool.query).toHaveBeenCalledWith(
156159
expect.stringContaining('WHERE date >='), // pastDateKST를 사용하는 부분 확인
157-
[expect.any(Number)] // limit
160+
[expect.any(Number)], // limit
158161
);
159162
});
160163

src/repositories/leaderboard.repository.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export class LeaderboardRepository {
1717
SELECT
1818
u.id AS id,
1919
u.email AS email,
20+
u.username AS username,
2021
COALESCE(SUM(ts.today_view), 0) AS total_views,
2122
COALESCE(SUM(ts.today_like), 0) AS total_likes,
2223
COUNT(DISTINCT CASE WHEN p.is_active = true THEN p.id END) AS total_posts,
@@ -27,8 +28,8 @@ export class LeaderboardRepository {
2728
LEFT JOIN posts_post p ON p.user_id = u.id
2829
LEFT JOIN today_stats ts ON ts.post_id = p.id
2930
LEFT JOIN start_stats ss ON ss.post_id = p.id
30-
WHERE u.email IS NOT NULL
31-
GROUP BY u.id, u.email
31+
WHERE u.username IS NOT NULL
32+
GROUP BY u.id, u.email, u.username
3233
ORDER BY ${this.SORT_COL_MAPPING[sort]} DESC, u.id
3334
LIMIT $1;
3435
`;
@@ -52,11 +53,13 @@ export class LeaderboardRepository {
5253
p.title,
5354
p.slug,
5455
p.released_at,
56+
u.username AS username,
5557
COALESCE(ts.today_view, 0) AS total_views,
5658
COALESCE(ts.today_like, 0) AS total_likes,
5759
COALESCE(ts.today_view, 0) - COALESCE(ss.start_view, COALESCE(ts.today_view, 0)) AS view_diff,
5860
COALESCE(ts.today_like, 0) - COALESCE(ss.start_like, COALESCE(ts.today_like, 0)) AS like_diff
5961
FROM posts_post p
62+
LEFT JOIN users_user u ON u.id = p.user_id
6063
LEFT JOIN today_stats ts ON ts.post_id = p.id
6164
LEFT JOIN start_stats ss ON ss.post_id = p.id
6265
WHERE p.is_active = true

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ describe('LeaderboardService', () => {
3030
{
3131
id: '1',
3232
33+
username: 'test',
3334
total_views: '100',
3435
total_likes: '50',
3536
total_posts: '1',
@@ -40,6 +41,7 @@ describe('LeaderboardService', () => {
4041
{
4142
id: '2',
4243
44+
username: 'test2',
4345
total_views: '200',
4446
total_likes: '100',
4547
total_posts: '2',
@@ -54,6 +56,7 @@ describe('LeaderboardService', () => {
5456
{
5557
id: '1',
5658
59+
username: 'test',
5760
totalViews: 100,
5861
totalLikes: 50,
5962
totalPosts: 1,
@@ -64,6 +67,7 @@ describe('LeaderboardService', () => {
6467
{
6568
id: '2',
6669
70+
username: 'test2',
6771
totalViews: 200,
6872
totalLikes: 100,
6973
totalPosts: 2,
@@ -121,6 +125,7 @@ describe('LeaderboardService', () => {
121125
id: '1',
122126
title: 'test',
123127
slug: 'test-slug',
128+
username: 'test',
124129
total_views: '100',
125130
total_likes: '50',
126131
view_diff: '20',
@@ -131,6 +136,7 @@ describe('LeaderboardService', () => {
131136
id: '2',
132137
title: 'test2',
133138
slug: 'test2-slug',
139+
username: 'test2',
134140
total_views: '200',
135141
total_likes: '100',
136142
view_diff: '10',
@@ -145,6 +151,7 @@ describe('LeaderboardService', () => {
145151
id: '1',
146152
title: 'test',
147153
slug: 'test-slug',
154+
username: 'test',
148155
totalViews: 100,
149156
totalLikes: 50,
150157
viewDiff: 20,
@@ -155,6 +162,7 @@ describe('LeaderboardService', () => {
155162
id: '2',
156163
title: 'test2',
157164
slug: 'test2-slug',
165+
username: 'test2',
158166
totalViews: 200,
159167
totalLikes: 100,
160168
viewDiff: 10,

src/services/leaderboard.service.ts

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ export class LeaderboardService {
4141
private mapRawUserResult(rawResult: RawUserResult[]): UserLeaderboardData {
4242
const users = rawResult.map((user) => ({
4343
id: user.id,
44-
email: user.email,
44+
email: user.email || null,
45+
username: user.username,
4546
totalViews: Number(user.total_views),
4647
totalLikes: Number(user.total_likes),
4748
totalPosts: Number(user.total_posts),
@@ -58,6 +59,7 @@ export class LeaderboardService {
5859
id: post.id,
5960
title: post.title,
6061
slug: post.slug,
62+
username: post.username || null,
6163
totalViews: Number(post.total_views),
6264
totalLikes: Number(post.total_likes),
6365
viewDiff: Number(post.view_diff),
@@ -69,24 +71,26 @@ export class LeaderboardService {
6971
}
7072
}
7173

72-
interface RawPostResult {
74+
interface RawUserResult {
7375
id: string;
74-
title: string;
75-
slug: string;
76+
email: string | null;
77+
username: string;
7678
total_views: string;
7779
total_likes: string;
80+
total_posts: string;
7881
view_diff: string;
7982
like_diff: string;
80-
released_at: string;
83+
post_diff: string;
8184
}
8285

83-
interface RawUserResult {
86+
interface RawPostResult {
8487
id: string;
85-
email: string;
88+
title: string;
89+
slug: string;
90+
username: string | null;
8691
total_views: string;
8792
total_likes: string;
88-
total_posts: string;
8993
view_diff: string;
9094
like_diff: string;
91-
post_diff: string;
95+
released_at: string;
9296
}

src/types/dto/responses/leaderboardResponse.type.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ import { BaseResponseDto } from '@/types/dto/responses/baseResponse.type';
1313
* email:
1414
* type: string
1515
* description: 사용자 이메일
16+
* nullable: true
17+
* username:
18+
* type: string
19+
* description: 사용자 이름
1620
* totalViews:
1721
* type: integer
1822
* description: 누적 조회수
@@ -34,7 +38,8 @@ import { BaseResponseDto } from '@/types/dto/responses/baseResponse.type';
3438
*/
3539
interface LeaderboardUser {
3640
id: string;
37-
email: string;
41+
email: string | null;
42+
username: string;
3843
totalViews: number;
3944
totalLikes: number;
4045
totalPosts: number;
@@ -89,6 +94,10 @@ export class UserLeaderboardResponseDto extends BaseResponseDto<UserLeaderboardD
8994
* slug:
9095
* type: string
9196
* description: 게시물 url slug 값
97+
* username:
98+
* type: string
99+
* nullable: true
100+
* description: 게시물 작성자 이름
92101
* totalViews:
93102
* type: integer
94103
* description: 누적 조회수
@@ -110,6 +119,7 @@ interface LeaderboardPost {
110119
id: string;
111120
title: string;
112121
slug: string;
122+
username: string | null;
113123
totalViews: number;
114124
totalLikes: number;
115125
viewDiff: number;

0 commit comments

Comments
 (0)