Skip to content

Commit 8b766f3

Browse files
committed
Merge remote-tracking branch 'origin/dev' into refactor/146
2 parents e508157 + f4a59a6 commit 8b766f3

File tree

7 files changed

+864
-1
lines changed

7 files changed

+864
-1
lines changed

src/main/java/com/back/domain/board/controller/CommentController.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,37 @@ public ResponseEntity<RsData<CommentResponse>> createComment(
3333
response
3434
));
3535
}
36+
37+
// 댓글 수정
38+
@PutMapping("/{commentId}")
39+
public ResponseEntity<RsData<CommentResponse>> updateComment(
40+
@PathVariable Long postId,
41+
@PathVariable Long commentId,
42+
@RequestBody @Valid CommentRequest request,
43+
@AuthenticationPrincipal CustomUserDetails user
44+
) {
45+
CommentResponse response = commentService.updateComment(postId, commentId, request, user.getUserId());
46+
return ResponseEntity
47+
.status(HttpStatus.OK)
48+
.body(RsData.success(
49+
"댓글이 수정되었습니다.",
50+
response
51+
));
52+
}
53+
54+
// 댓글 삭제
55+
@DeleteMapping("/{commentId}")
56+
public ResponseEntity<RsData<Void>> deleteComment(
57+
@PathVariable Long postId,
58+
@PathVariable Long commentId,
59+
@AuthenticationPrincipal CustomUserDetails user
60+
) {
61+
commentService.deleteComment(postId, commentId, user.getUserId());
62+
return ResponseEntity
63+
.status(HttpStatus.OK)
64+
.body(RsData.success(
65+
"댓글이 삭제되었습니다.",
66+
null
67+
));
68+
}
3669
}

src/main/java/com/back/domain/board/controller/CommentControllerDocs.java

Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,4 +141,260 @@ ResponseEntity<RsData<CommentResponse>> createComment(
141141
@RequestBody CommentRequest request,
142142
@AuthenticationPrincipal CustomUserDetails user
143143
);
144+
145+
@Operation(
146+
summary = "댓글 수정",
147+
description = "로그인한 사용자가 자신이 작성한 댓글을 수정합니다."
148+
)
149+
@ApiResponses({
150+
@ApiResponse(
151+
responseCode = "200",
152+
description = "댓글 수정 성공",
153+
content = @Content(
154+
mediaType = "application/json",
155+
examples = @ExampleObject(value = """
156+
{
157+
"success": true,
158+
"code": "SUCCESS_200",
159+
"message": "댓글이 수정되었습니다.",
160+
"data": {
161+
"commentId": 25,
162+
"postId": 101,
163+
"author": {
164+
"id": 5,
165+
"nickname": "홍길동"
166+
},
167+
"content": "수정된 댓글 내용입니다.",
168+
"createdAt": "2025-09-22T11:30:00",
169+
"updatedAt": "2025-09-22T13:00:00"
170+
}
171+
}
172+
""")
173+
)
174+
),
175+
@ApiResponse(
176+
responseCode = "400",
177+
description = "잘못된 요청 (필드 누락 등)",
178+
content = @Content(
179+
mediaType = "application/json",
180+
examples = @ExampleObject(value = """
181+
{
182+
"success": false,
183+
"code": "COMMON_400",
184+
"message": "잘못된 요청입니다.",
185+
"data": null
186+
}
187+
""")
188+
)
189+
),
190+
@ApiResponse(
191+
responseCode = "401",
192+
description = "인증 실패 (토큰 없음/잘못됨/만료)",
193+
content = @Content(
194+
mediaType = "application/json",
195+
examples = {
196+
@ExampleObject(name = "토큰 없음", value = """
197+
{
198+
"success": false,
199+
"code": "AUTH_001",
200+
"message": "인증이 필요합니다.",
201+
"data": null
202+
}
203+
"""),
204+
@ExampleObject(name = "잘못된 토큰", value = """
205+
{
206+
"success": false,
207+
"code": "AUTH_002",
208+
"message": "유효하지 않은 액세스 토큰입니다.",
209+
"data": null
210+
}
211+
"""),
212+
@ExampleObject(name = "만료된 토큰", value = """
213+
{
214+
"success": false,
215+
"code": "AUTH_004",
216+
"message": "만료된 액세스 토큰입니다.",
217+
"data": null
218+
}
219+
""")
220+
}
221+
)
222+
),
223+
@ApiResponse(
224+
responseCode = "403",
225+
description = "권한 없음 (작성자 아님)",
226+
content = @Content(
227+
mediaType = "application/json",
228+
examples = @ExampleObject(value = """
229+
{
230+
"success": false,
231+
"code": "COMMENT_002",
232+
"message": "댓글 작성자만 수정/삭제할 수 있습니다.",
233+
"data": null
234+
}
235+
""")
236+
)
237+
),
238+
@ApiResponse(
239+
responseCode = "404",
240+
description = "존재하지 않는 게시글 또는 댓글",
241+
content = @Content(
242+
mediaType = "application/json",
243+
examples = {
244+
@ExampleObject(name = "존재하지 않는 게시글", value = """
245+
{
246+
"success": false,
247+
"code": "POST_001",
248+
"message": "존재하지 않는 게시글입니다.",
249+
"data": null
250+
}
251+
"""),
252+
@ExampleObject(name = "존재하지 않는 댓글", value = """
253+
{
254+
"success": false,
255+
"code": "COMMENT_001",
256+
"message": "존재하지 않는 댓글입니다.",
257+
"data": null
258+
}
259+
""")
260+
}
261+
)
262+
),
263+
@ApiResponse(
264+
responseCode = "500",
265+
description = "서버 내부 오류",
266+
content = @Content(
267+
mediaType = "application/json",
268+
examples = @ExampleObject(value = """
269+
{
270+
"success": false,
271+
"code": "COMMON_500",
272+
"message": "서버 오류가 발생했습니다.",
273+
"data": null
274+
}
275+
""")
276+
)
277+
)
278+
})
279+
ResponseEntity<RsData<CommentResponse>> updateComment(
280+
@PathVariable Long postId,
281+
@PathVariable Long commentId,
282+
@RequestBody CommentRequest request,
283+
@AuthenticationPrincipal CustomUserDetails user
284+
);
285+
286+
@Operation(
287+
summary = "댓글 삭제",
288+
description = "로그인한 사용자가 자신이 작성한 댓글을 삭제합니다."
289+
)
290+
@ApiResponses({
291+
@ApiResponse(
292+
responseCode = "200",
293+
description = "댓글 삭제 성공",
294+
content = @Content(
295+
mediaType = "application/json",
296+
examples = @ExampleObject(value = """
297+
{
298+
"success": true,
299+
"code": "SUCCESS_200",
300+
"message": "댓글이 삭제되었습니다.",
301+
"data": null
302+
}
303+
""")
304+
)
305+
),
306+
@ApiResponse(
307+
responseCode = "401",
308+
description = "인증 실패 (토큰 없음/잘못됨/만료)",
309+
content = @Content(
310+
mediaType = "application/json",
311+
examples = {
312+
@ExampleObject(name = "토큰 없음", value = """
313+
{
314+
"success": false,
315+
"code": "AUTH_001",
316+
"message": "인증이 필요합니다.",
317+
"data": null
318+
}
319+
"""),
320+
@ExampleObject(name = "잘못된 토큰", value = """
321+
{
322+
"success": false,
323+
"code": "AUTH_002",
324+
"message": "유효하지 않은 액세스 토큰입니다.",
325+
"data": null
326+
}
327+
"""),
328+
@ExampleObject(name = "만료된 토큰", value = """
329+
{
330+
"success": false,
331+
"code": "AUTH_004",
332+
"message": "만료된 액세스 토큰입니다.",
333+
"data": null
334+
}
335+
""")
336+
}
337+
)
338+
),
339+
@ApiResponse(
340+
responseCode = "403",
341+
description = "권한 없음 (작성자 아님)",
342+
content = @Content(
343+
mediaType = "application/json",
344+
examples = @ExampleObject(value = """
345+
{
346+
"success": false,
347+
"code": "COMMENT_002",
348+
"message": "댓글 작성자만 수정/삭제할 수 있습니다.",
349+
"data": null
350+
}
351+
""")
352+
)
353+
),
354+
@ApiResponse(
355+
responseCode = "404",
356+
description = "존재하지 않는 게시글 또는 댓글",
357+
content = @Content(
358+
mediaType = "application/json",
359+
examples = {
360+
@ExampleObject(name = "존재하지 않는 게시글", value = """
361+
{
362+
"success": false,
363+
"code": "POST_001",
364+
"message": "존재하지 않는 게시글입니다.",
365+
"data": null
366+
}
367+
"""),
368+
@ExampleObject(name = "존재하지 않는 댓글", value = """
369+
{
370+
"success": false,
371+
"code": "COMMENT_001",
372+
"message": "존재하지 않는 댓글입니다.",
373+
"data": null
374+
}
375+
""")
376+
}
377+
)
378+
),
379+
@ApiResponse(
380+
responseCode = "500",
381+
description = "서버 내부 오류",
382+
content = @Content(
383+
mediaType = "application/json",
384+
examples = @ExampleObject(value = """
385+
{
386+
"success": false,
387+
"code": "COMMON_500",
388+
"message": "서버 오류가 발생했습니다.",
389+
"data": null
390+
}
391+
""")
392+
)
393+
)
394+
})
395+
ResponseEntity<RsData<Void>> deleteComment(
396+
@PathVariable Long postId,
397+
@PathVariable Long commentId,
398+
@AuthenticationPrincipal CustomUserDetails user
399+
);
144400
}

src/main/java/com/back/domain/board/entity/Comment.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,10 @@ public Comment(Post post, User user, String content) {
4141
this.user = user;
4242
this.content = content;
4343
}
44+
45+
// -------------------- 비즈니스 메서드 --------------------
46+
// 댓글 업데이트
47+
public void update(String content) {
48+
this.content = content;
49+
}
4450
}

src/main/java/com/back/domain/board/service/CommentService.java

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,57 @@ public CommentResponse createComment(Long postId, CommentRequest request, Long u
4545
commentRepository.save(comment);
4646
return CommentResponse.from(comment);
4747
}
48+
49+
/**
50+
* 댓글 수정 서비스
51+
* 1. Post 조회
52+
* 2. Comment 조회
53+
* 3. 작성자 검증
54+
* 4. Comment 업데이트 (내용)
55+
* 5. CommentResponse 반환
56+
*/
57+
public CommentResponse updateComment(Long postId, Long commentId, CommentRequest request, Long userId) {
58+
// Post 조회
59+
Post post = postRepository.findById(postId)
60+
.orElseThrow(() -> new CustomException(ErrorCode.POST_NOT_FOUND));
61+
62+
// Comment 조회
63+
Comment comment = commentRepository.findById(commentId)
64+
.orElseThrow(() -> new CustomException(ErrorCode.COMMENT_NOT_FOUND));
65+
66+
// 작성자 검증
67+
if (!comment.getUser().getId().equals(userId)) {
68+
throw new CustomException(ErrorCode.COMMENT_NO_PERMISSION);
69+
}
70+
71+
// Comment 업데이트
72+
comment.update(request.content());
73+
74+
// 응답 반환
75+
return CommentResponse.from(comment);
76+
}
77+
78+
/**
79+
* 댓글 삭제 서비스
80+
* 1. Post 조회
81+
* 2. Comment 조회
82+
* 3. 작성자 검증
83+
* 4. Comment 삭제
84+
*/
85+
public void deleteComment(Long postId, Long commentId, Long userId) {
86+
// Post 조회
87+
Post post = postRepository.findById(postId)
88+
.orElseThrow(() -> new CustomException(ErrorCode.POST_NOT_FOUND));
89+
90+
// Comment 조회
91+
Comment comment = commentRepository.findById(commentId)
92+
.orElseThrow(() -> new CustomException(ErrorCode.COMMENT_NOT_FOUND));
93+
94+
// 작성자 검증
95+
if (!comment.getUser().getId().equals(userId)) {
96+
throw new CustomException(ErrorCode.COMMENT_NO_PERMISSION);
97+
}
98+
99+
commentRepository.delete(comment);
100+
}
48101
}

src/main/java/com/back/global/exception/ErrorCode.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ public enum ErrorCode {
8686
POST_NOT_FOUND(HttpStatus.NOT_FOUND, "POST_001", "존재하지 않는 게시글입니다."),
8787
POST_NO_PERMISSION(HttpStatus.FORBIDDEN, "POST_002", "게시글 작성자만 수정/삭제할 수 있습니다."),
8888
CATEGORY_NOT_FOUND(HttpStatus.NOT_FOUND, "POST_003", "존재하지 않는 카테고리입니다."),
89+
COMMENT_NOT_FOUND(HttpStatus.NOT_FOUND, "COMMENT_001", "존재하지 않는 댓글입니다."),
90+
COMMENT_NO_PERMISSION(HttpStatus.FORBIDDEN, "COMMENT_002", "댓글 작성자만 수정/삭제할 수 있습니다."),
8991

9092
// ======================== 공통 에러 ========================
9193
BAD_REQUEST(HttpStatus.BAD_REQUEST, "COMMON_400", "잘못된 요청입니다."),

0 commit comments

Comments
 (0)