Skip to content

Commit ee13189

Browse files
committed
게시글 리스트 조회 기능 수정
1 parent 9d04319 commit ee13189

7 files changed

+278
-355
lines changed

src/post/dtos/total-postsResponse.dto.ts src/post/dtos/all-posts.response.ts

+16-16
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,32 @@ class PostImageDto {
1515
}
1616

1717
class UserDto {
18+
@ApiProperty({
19+
example: '19',
20+
description: '작성자의 user ID 입니다.',
21+
})
22+
userId: number;
23+
1824
@ApiProperty({
1925
example: 'nickname',
20-
description: '사용자의 닉네임입니다.',
26+
description: '작성자의 닉네임입니다.',
2127
})
2228
nickname: string;
2329

2430
@ApiProperty({
2531
example: 'http://profilepictureurl.example',
26-
description: '사용자의 프로필 사진 URL입니다.',
32+
description: '작성자의 프로필 사진 URL입니다.',
2733
})
2834
profilePictureUrl: string;
2935
}
3036

31-
class PostDto {
37+
export class PostDto {
38+
@ApiProperty({
39+
example: '3',
40+
description: '게시글의 번호입니다.',
41+
})
42+
postId: number;
43+
3244
@ApiProperty({
3345
example: '게시글 내용 content',
3446
description: '게시글의 내용입니다.',
@@ -58,24 +70,12 @@ class PostDto {
5870
description: '게시글 작성자의 정보입니다.',
5971
})
6072
user: UserDto;
61-
62-
@ApiProperty({
63-
example: true,
64-
description: '현재 사용자가 해당 게시글의 작성자인지 여부입니다.',
65-
})
66-
isPostWriter: boolean;
6773
}
6874

69-
export class GetPostsResponse {
75+
export class GetAllPostsResponse {
7076
@ApiProperty({
7177
type: [PostDto],
7278
description: '조회된 게시글 목록입니다.',
7379
})
7480
post: PostDto[];
75-
76-
@ApiProperty({
77-
example: false,
78-
description: '매칭 여부입니다.',
79-
})
80-
isMatching: boolean = false;
8181
}

src/post/dtos/page-options.dto.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ export class PageOptionsDto {
1010
@Type(() => Number)
1111
@IsInt()
1212
@IsOptional()
13-
@Min(1) // 페이지는 1 이상
14-
page?: number;
13+
@Min(1)
14+
page?: number = 1;
1515

1616
@ApiProperty({
1717
example: '10',
@@ -20,7 +20,7 @@ export class PageOptionsDto {
2020
@Type(() => Number)
2121
@IsInt()
2222
@IsOptional()
23-
take?: number;
23+
take?: number = 10;
2424

2525
// 기본값 설정
2626
constructor() {

src/post/dtos/page.dto.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
import { IsArray } from 'class-validator';
22
import { PageMetaDto } from './page-meta.dto';
33
import { ApiProperty } from '@nestjs/swagger';
4-
import { GetPostsResponse } from './total-postsResponse.dto';
4+
import { GetAllPostsResponse } from './all-posts.response';
55
import {
66
GetMyPostsResponse,
77
GetOtherPostsResponse,
8-
} from './user-postsResponse.dto';
8+
} from './user-posts.response';
99

1010
export class PageDto<T> {
1111
@ApiProperty({
1212
description: '실제 데이터',
13-
type: [GetPostsResponse, GetMyPostsResponse, GetOtherPostsResponse],
13+
type: [GetAllPostsResponse, GetMyPostsResponse, GetOtherPostsResponse],
1414
})
1515
@IsArray()
1616
readonly data: T[]; // 실제 데이터

src/post/dtos/user-postsResponse.dto.ts src/post/dtos/user-posts.response.ts

+14-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,18 @@
11
import { ApiProperty, OmitType } from '@nestjs/swagger';
22

33
class PostDto {
4+
@ApiProperty({
5+
example: '19',
6+
description: '조회한 작성자의 user ID 입니다.',
7+
})
8+
userId: number;
9+
10+
@ApiProperty({
11+
example: '3',
12+
description: '게시글의 번호입니다.',
13+
})
14+
postId: number;
15+
416
@ApiProperty({
517
example: true,
618
description: '대표 게시글 여부를 나타냅니다.',
@@ -48,7 +60,7 @@ export class GetMyPostsResponse {
4860
type: [PostDto],
4961
description: '조회된 사용자의 게시글 목록입니다.',
5062
})
51-
posts: PostDto[];
63+
post: PostDto[];
5264

5365
@ApiProperty({
5466
example: 20,
@@ -79,7 +91,7 @@ export class GetOtherPostsResponse {
7991
type: [OtherUserPostDto],
8092
description: '다른 사용자의 게시글 목록입니다.',
8193
})
82-
posts: OtherUserPostDto[];
94+
post: OtherUserPostDto[];
8395

8496
@ApiProperty({
8597
example: 30,

src/post/post.controller.ts

+101-16
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,14 @@ import {
1111
UseGuards,
1212
} from '@nestjs/common';
1313
import { PostService } from './post.service';
14-
import { GetPostsResponse } from './dtos/total-postsResponse.dto';
14+
import { GetAllPostsResponse } from './dtos/all-posts.response';
1515
import {
1616
GetMyPostsResponse,
1717
GetOtherPostsResponse,
18-
} from './dtos/user-postsResponse.dto';
18+
} from './dtos/user-posts.response';
1919
import {
2020
CreatePostsSwagger,
21+
DeletePostSwagger,
2122
GetPostsSwagger,
2223
GetPostSwagger,
2324
PatchIsRepresentativeSwagger,
@@ -32,20 +33,25 @@ import { GetPostResponse } from './dtos/get-post.dto';
3233
import { PatchPostDto } from './dtos/patch-Post.dto';
3334
import { PageOptionsDto } from './dtos/page-options.dto';
3435
import { PageDto } from './dtos/page.dto';
36+
import dayjs from 'dayjs';
37+
import { PageMetaDto } from './dtos/page-meta.dto';
38+
import { DataNotFoundException } from 'src/common/exception/service.exception';
39+
import { Post as PostEntity } from 'src/common/entities/post.entity';
3540

3641
@Controller('post')
37-
@ApiBearerAuth()
42+
@ApiBearerAuth('Authorization')
3843
@UseGuards(AuthGuard)
3944
@ApiTags('[서비스] 게시글')
4045
export class PostController {
4146
constructor(private readonly postService: PostService) {}
4247

43-
@Get('/')
48+
@Get()
4449
@GetPostsSwagger('게시글 리스트 조회 API')
4550
@ApiQuery({
4651
name: 'userId',
4752
required: false,
48-
description: 'User ID',
53+
description:
54+
'User ID가 제공되면 사용자 게시글 조회, 제공되지 않으면 전체 게시글이 조회됩니다.',
4955
type: Number,
5056
})
5157
@ApiQuery({
@@ -65,25 +71,104 @@ export class PostController {
6571
@Query('userId') userId?: number,
6672
): Promise<
6773
BaseResponse<
68-
PageDto<GetPostsResponse | GetMyPostsResponse | GetOtherPostsResponse>
74+
PageDto<GetAllPostsResponse | GetMyPostsResponse | GetOtherPostsResponse>
6975
>
7076
> {
71-
const currentUserId = req.user.id;
72-
73-
const options = pageOptionsDto
77+
const pageOptions = pageOptionsDto
7478
? {
7579
...new PageOptionsDto(),
76-
...pageOptionsDto, // Dto에 전달된 기본값
80+
...pageOptionsDto,
7781
}
7882
: new PageOptionsDto();
7983

80-
const postsResponse = await this.postService.getPosts(
81-
options,
82-
userId,
83-
currentUserId,
84+
const { posts, total } = userId
85+
? await this.postService.getUserPosts(pageOptions, userId)
86+
: await this.postService.getAllPosts(pageOptions, req.user.id);
87+
88+
const pageMetaDto = new PageMetaDto({
89+
pageOptionsDto: pageOptions,
90+
total,
91+
});
92+
93+
if (pageMetaDto.last_page < pageMetaDto.page) {
94+
throw DataNotFoundException('해당 페이지는 존재하지 않습니다');
95+
}
96+
const postsResponse = userId
97+
? this.createUserPostsResponse(posts, req.user.id, userId)
98+
: this.createAllPostsResponse(posts, req.user.id);
99+
100+
return new BaseResponse(
101+
true,
102+
'게시글 리스트 조회 성공',
103+
new PageDto([postsResponse], pageMetaDto),
84104
);
105+
}
85106

86-
return new BaseResponse(true, '게시글 리스트 조회 성공', postsResponse);
107+
private createUserPostsResponse(
108+
posts: PostEntity[],
109+
currentUserId: number,
110+
userId: number,
111+
): GetMyPostsResponse | GetOtherPostsResponse {
112+
if (userId == currentUserId) {
113+
return {
114+
post: posts.map((post) => ({
115+
userId: post.user.id,
116+
postId: post.id,
117+
createdAt: dayjs(post.createdAt).format('YYYY-MM-DDTHH:mm:ssZ'),
118+
imageUrl: post.postImages.find(
119+
(image) => image.orderNum == 1 && image.status === 'activated',
120+
)?.url,
121+
isRepresentative: post.isRepresentative,
122+
likeCount: post.postLikes.length,
123+
commentCount: post.postComments.length,
124+
isPostLike: this.postService.checkIsPostLiked(post, currentUserId),
125+
isPostComment: this.postService.checkIsPostCommented(
126+
post,
127+
currentUserId,
128+
),
129+
})),
130+
totalComments: this.postService.calculateTotalComments(posts),
131+
totalPosts: posts.length,
132+
totalLikes: this.postService.calculateTotalLikes(posts),
133+
};
134+
} else {
135+
return {
136+
post: posts.map((post) => ({
137+
userId: post.user.id,
138+
postId: post.id,
139+
createdAt: dayjs(post.createdAt).format('YYYY-MM-DDTHH:mm:ssZ'),
140+
imageUrl: post.postImages.find(
141+
(image) => image.orderNum == 1 && image.status === 'activated',
142+
)?.url,
143+
isRepresentative: post.isRepresentative,
144+
likeCount: post.postLikes.length,
145+
isPostLike: this.postService.checkIsPostLiked(post, currentUserId),
146+
})),
147+
totalPosts: posts.length,
148+
totalLikes: this.postService.calculateTotalLikes(posts),
149+
};
150+
}
151+
}
152+
private createAllPostsResponse(posts: PostEntity[], currentUserId: number) {
153+
return {
154+
post: posts.map((post) => ({
155+
postId: post.id,
156+
content: post.content,
157+
createdAt: dayjs(post.createdAt).format('YYYY-MM-DDTHH:mm:ssZ'),
158+
postImages: post.postImages
159+
.filter((image) => image.status === 'activated')
160+
.map((image) => ({
161+
url: image.url,
162+
orderNum: image.orderNum,
163+
})),
164+
isPostLike: this.postService.checkIsPostLiked(post, currentUserId),
165+
user: {
166+
userId: post.user.id,
167+
nickname: post.user.nickname,
168+
profilePictureUrl: post.user.profilePictureUrl,
169+
},
170+
})),
171+
};
87172
}
88173

89174
@Get(':postId')
@@ -165,7 +250,7 @@ export class PostController {
165250
}
166251

167252
@Delete(':postId')
168-
@PatchPostSwagger('게시글 삭제 API')
253+
@DeletePostSwagger('게시글 삭제 API')
169254
async deletePost(
170255
@Param('postId') postId: number,
171256
@Req() req: Request,

0 commit comments

Comments
 (0)