Skip to content

Commit dce7076

Browse files
authored
Merge pull request #8 from oodd-team/OD-41
게시글 상세 조회 기능
2 parents fe613bf + dabeb01 commit dce7076

6 files changed

+217
-16
lines changed

oodd.erd.json

+2-10
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,7 @@
1717
"bracketType": 1,
1818
"relationshipDataTypeSync": true,
1919
"relationshipOptimization": false,
20-
"columnOrder": [
21-
16,
22-
8,
23-
1,
24-
2,
25-
4,
26-
32,
27-
64
28-
],
20+
"columnOrder": [16, 8, 1, 2, 4, 32, 64],
2921
"maxWidthComment": -1,
3022
"ignoreSaveSettings": 0
3123
},
@@ -4679,4 +4671,4 @@
46794671
}
46804672
]
46814673
}
4682-
}
4674+
}

src/post/dtos/get-post.dto.ts

+125
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
import { ApiProperty } from '@nestjs/swagger';
2+
3+
class PostImageDto {
4+
@ApiProperty({
5+
example: 'http://example.com/image.jpg',
6+
description: '게시물 이미지 URL',
7+
})
8+
url: string;
9+
10+
@ApiProperty({
11+
example: 1,
12+
description: '이미지의 순서 번호',
13+
})
14+
orderNum: number;
15+
}
16+
17+
class PostClothingDto {
18+
@ApiProperty({
19+
example: 'http://example.com/clothing.jpg',
20+
description: '옷 이미지 URL입니다.',
21+
})
22+
imageUrl: string;
23+
24+
@ApiProperty({
25+
example: '브랜드 이름',
26+
description: '옷 브랜드 이름입니다.',
27+
})
28+
brandName: string;
29+
30+
@ApiProperty({ example: '모델 이름', description: '옷 상품명입니다.' })
31+
modelName: string;
32+
33+
@ApiProperty({ example: '모델 넘버', description: '옷 모델 넘버입니다.' })
34+
modelNumber: string;
35+
36+
@ApiProperty({
37+
example: 'http://example.com/product',
38+
description: '옷 상품 링크입니다.',
39+
})
40+
url: string;
41+
}
42+
43+
class UserDto {
44+
@ApiProperty({
45+
example: '1',
46+
description: '사용자 id',
47+
})
48+
userId: number;
49+
50+
@ApiProperty({
51+
example: 'nickname',
52+
description: '사용자의 닉네임',
53+
})
54+
nickname: string;
55+
56+
@ApiProperty({
57+
example: 'http://example.com/image.jpg',
58+
description: '사용자의 프로필 사진 URL',
59+
})
60+
profilePictureUrl: string;
61+
}
62+
63+
class PostDetailDto {
64+
@ApiProperty({
65+
example: '게시물 내용',
66+
description: '게시물의 내용',
67+
})
68+
content: string;
69+
70+
@ApiProperty({
71+
example: '2024-10-11T09:00:00.000Z',
72+
description: '게시물이 생성된 날짜 및 시간',
73+
})
74+
createdAt: Date;
75+
76+
@ApiProperty({
77+
type: [PostImageDto],
78+
description: '게시물에 포함된 이미지 목록',
79+
})
80+
postImages: PostImageDto[];
81+
82+
@ApiProperty({
83+
type: [PostClothingDto],
84+
description: '게시물에 포함된 옷 정보 목록',
85+
})
86+
postClothings: PostClothingDto[];
87+
88+
@ApiProperty({
89+
type: UserDto,
90+
description: '게시물 작성자 정보',
91+
})
92+
user: UserDto;
93+
94+
@ApiProperty({
95+
example: 10,
96+
description: '게시글에 달린 댓글 수입니다.',
97+
})
98+
commentCount: number;
99+
100+
@ApiProperty({
101+
example: 5,
102+
description: '게시글의 좋아요 수입니다.',
103+
})
104+
likeCount: number;
105+
106+
@ApiProperty({
107+
example: false,
108+
description: '현재 사용자가 게시물에 좋아요를 눌렀는지 여부',
109+
})
110+
isPostLike: boolean;
111+
112+
@ApiProperty({
113+
example: false,
114+
description: '현재 사용자가 게시물 작성자인지 여부',
115+
})
116+
isPostWriter: boolean;
117+
}
118+
119+
export class GetPostResponse {
120+
@ApiProperty({
121+
type: PostDetailDto,
122+
description: '게시물 상세 정보',
123+
})
124+
post: PostDetailDto;
125+
}

src/post/post.controller.ts

+44-3
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import { CreatePostDto } from './dtos/create-post.dto';
2727
import { BaseResponse } from 'src/common/response/dto';
2828
import { AuthGuard } from 'src/auth/guards/jwt.auth.guard';
2929
import { Request } from 'express';
30+
import { GetPostResponse } from './dtos/get-post.dto';
3031
import { PatchPostDto } from './dtos/patch-Post.dto';
3132

3233
@Controller('post')
@@ -54,10 +55,50 @@ export class PostController {
5455
return new BaseResponse(true, 'SUCCESS', postsResponse);
5556
}
5657

57-
@Get()
58+
@Get(':postId')
5859
@GetPostSwagger('게시글 상세 조회 API')
59-
getPost() {
60-
// return this.userService.getHello();
60+
async getPost(
61+
@Param('postId') postId: number,
62+
@Req() req: Request,
63+
): Promise<BaseResponse<GetPostResponse>> {
64+
const currentUserId = req.user.userId;
65+
66+
await this.postService.validatePost(postId);
67+
68+
const post = await this.postService.getPost(postId);
69+
70+
const postResponse: GetPostResponse = {
71+
post: {
72+
content: post.content,
73+
createdAt: post.createdAt,
74+
postImages: post.postImages
75+
.filter((image) => image.status === 'activated')
76+
.map((image) => ({
77+
url: image.url,
78+
orderNum: image.orderNum,
79+
})),
80+
postClothings: post.postClothings
81+
.filter((postClothing) => postClothing.status === 'activated')
82+
.map((postClothing) => ({
83+
imageUrl: postClothing.clothing.imageUrl,
84+
brandName: postClothing.clothing.brandName,
85+
modelName: postClothing.clothing.modelName,
86+
modelNumber: postClothing.clothing.modelNumber,
87+
url: postClothing.clothing.url,
88+
})),
89+
likeCount: post.postLikes.length,
90+
commentCount: post.postComments.length,
91+
isPostLike: this.postService.checkIsPostLiked(post, currentUserId),
92+
user: {
93+
userId: post.user.id,
94+
nickname: post.user.nickname,
95+
profilePictureUrl: post.user.profilePictureUrl,
96+
},
97+
isPostWriter: post.user.id === currentUserId,
98+
},
99+
};
100+
101+
return new BaseResponse(true, '게시글 조회 성공', postResponse);
61102
}
62103

63104
@Post()

src/post/post.module.ts

-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import { DayjsModule } from 'src/common/dayjs/dayjs.module';
2020
PostStyletagModule,
2121
UserBlockModule,
2222
PostClothingModule,
23-
PostImageModule,
2423
DayjsModule,
2524
],
2625
controllers: [PostController],

src/post/post.service.ts

+17
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,23 @@ export class PostService {
255255
}
256256
}
257257

258+
// 게시글 상세 조회
259+
async getPost(postId: number): Promise<Post> {
260+
const post = await this.postRepository.findOne({
261+
where: { id: postId, status: 'activated' },
262+
relations: [
263+
'postImages',
264+
'user',
265+
'postLikes',
266+
'postComments',
267+
'postClothings',
268+
'postClothings.clothing',
269+
],
270+
});
271+
272+
return post;
273+
}
274+
258275
// 게시글 검증 메서드
259276
async validatePost(postId: number, userId?: number): Promise<void> {
260277
const post = await this.postRepository.findOne({

src/post/post.swagger.ts

+29-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {
2+
ApiAcceptedResponse,
23
ApiBadRequestResponse,
34
ApiCreatedResponse,
45
ApiForbiddenResponse,
@@ -18,6 +19,7 @@ import {
1819
GetOtherPostsResponse,
1920
} from './dtos/user-postsResponse.dto';
2021
import { applyDecorators } from '@nestjs/common';
22+
import { GetPostResponse } from './dtos/get-post.dto';
2123

2224
// 게시글 리스트 조회하기 API Swagger
2325
export function GetPostsSwagger(text: string) {
@@ -65,8 +67,33 @@ export function GetPostsSwagger(text: string) {
6567
}
6668

6769
// 게시글 상세 조회하기 API Swagger
68-
export function GetPostSwagger(apiSummary: string) {
69-
return ApiOperation({ summary: apiSummary });
70+
export function GetPostSwagger(text: string) {
71+
return BaseSwaggerDecorator(
72+
{ summary: text },
73+
[],
74+
[
75+
ApiAcceptedResponse({
76+
description: '게시글 조회 성공',
77+
type: GetPostResponse,
78+
}),
79+
ApiBadRequestResponse({
80+
description: '잘못된 요청입니다.',
81+
type: BaseResponse,
82+
}),
83+
ApiUnauthorizedResponse({
84+
description: '인증되지 않은 사용자입니다.',
85+
type: BaseResponse,
86+
}),
87+
ApiNotFoundResponse({
88+
description: '존재하지 않는 게시글입니다.',
89+
type: BaseResponse,
90+
}),
91+
ApiInternalServerErrorResponse({
92+
description: '서버 에러입니다.',
93+
type: BaseResponse,
94+
}),
95+
],
96+
);
7097
}
7198

7299
// 게시글 생성하기 API Swagger

0 commit comments

Comments
 (0)