From 8354b54c28a5a4ac6ff3b7080ae3a324b5c23cba Mon Sep 17 00:00:00 2001 From: kaswhy Date: Wed, 10 Dec 2025 23:42:51 +0900 Subject: [PATCH 01/18] =?UTF-8?q?refactor(*):=20ResponseCode=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80,=20SuccessCode,=20ErrorCode=20=EC=9D=B8=ED=84=B0?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=8A=A4=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/sopt/global/exception/ErrorCode.java | 41 +------------------ .../org/sopt/global/response/SuccessCode.java | 28 ------------- .../global/response/code/ResponseCode.java | 7 ++++ .../global/response/code/SuccessCode.java | 5 +++ 4 files changed, 14 insertions(+), 67 deletions(-) delete mode 100644 src/main/java/org/sopt/global/response/SuccessCode.java create mode 100644 src/main/java/org/sopt/global/response/code/ResponseCode.java create mode 100644 src/main/java/org/sopt/global/response/code/SuccessCode.java diff --git a/src/main/java/org/sopt/global/exception/ErrorCode.java b/src/main/java/org/sopt/global/exception/ErrorCode.java index 2793133..d6550dc 100644 --- a/src/main/java/org/sopt/global/exception/ErrorCode.java +++ b/src/main/java/org/sopt/global/exception/ErrorCode.java @@ -1,44 +1,7 @@ package org.sopt.global.exception; -import lombok.Getter; +import org.sopt.global.response.code.ResponseCode; -// TODO 도메인 별 분리 => 다음 주에 할게요 -@Getter -public enum ErrorCode { +public interface ErrorCode extends ResponseCode { - // =================== COMMON =================== - // 400 BAD REQUEST - INVALID_NUMBER_FORMAT(400, "COM_001", "숫자만 입력해주세요."), - INVALID_EMAIL_FORMAT(400, "COM_002", "잘못된 이메일 형식입니다."), - INVALID_DATE_FORMAT(400, "COM_005", "잘못된 날짜 형식입니다."), - INVALID_NULL_DATA(400, "COM_006", "빈 값은 허용되지 않습니다."), - INVALID_MAPPING_PARAMETER(400, "COM_007", "매핑할 수 없는 값입니다."), - - // 404 NOT FOUND - RESOURCE_NOT_FOUND(404, "COM_003", "존재하지 않는 리소스입니다."), - - // 500 INTERNAL SERVER ERROR - INTERNAL_SERVER_ERROR(500, "COM_004", "서버 내부 오류가 발생했습니다."), - - // =================== COMMON =================== - // 400 BAD REQUEST - AGE_MUST_UPPER_THAN_20(400, "MEM_003", "20세 미만은 가입할 수 없습니다."), - - // 404 NOT FOUND - MEMBER_NOT_FOUND(404, "MEM_001", "존재하지 않는 회원입니다."), - ARTICLE_NOT_FOUND(404, "ART_001", "존재하지 않는 게시물입니다."), - - // 409 CONFLICT - MEMBER_BY_EMAIL_ALREADY_EXISTS(409, "MEM_002", "해당 이메일로 가입된 회원이 이미 존재합니다."), - ARTICLE_BY_NAME_ALREADY_EXISTS(409, "ART_002", "해당 제목으로 작성된 게시글이 이미 존재합니다."); - - private final int status; - private final String code; - private final String message; - - ErrorCode(int status, String code, String message) { - this.status = status; - this.code = code; - this.message = message; - } } diff --git a/src/main/java/org/sopt/global/response/SuccessCode.java b/src/main/java/org/sopt/global/response/SuccessCode.java deleted file mode 100644 index 60091c5..0000000 --- a/src/main/java/org/sopt/global/response/SuccessCode.java +++ /dev/null @@ -1,28 +0,0 @@ -package org.sopt.global.response; - -import lombok.Getter; - -// TODO 도메인 별 분리 => 다음 주에 할게요 -@Getter -public enum SuccessCode { - - // 200 OK - MEMBER_RETRIEVED_SUCCESS(200, "특정 회원 조회가 성공적으로 완료되었습니다."), - MEMBER_LIST_RETRIEVED_SUCCESS(200, "회원 전체 조회가 성공적으로 완료되었습니다."), - MEMBER_DELETED_SUCCESS(200, "회원 삭제가 성공적으로 완료되었습니다."), - ARTICLE_RETRIEVED_SUCCESS(200, "특정 게시글 조회를 성공적으로 완료했습니다."), - ARTICLE_LIST_RETRIEVED_SUCCESS(200, "게시글 전체 조회가 성공적으로 완료되었습니다."), - - // 201 CREATED - MEMBER_CREATED_SUCCESS(201, "회원가입이 성공적으로 완료되었습니다."), - ARTICLE_CREATED_SUCCESS(201, "게시물이 성공적으로 생성되었습니다."); - - - private final int status; - private final String message; - - SuccessCode(int status, String message) { - this.status = status; - this.message = message; - } -} diff --git a/src/main/java/org/sopt/global/response/code/ResponseCode.java b/src/main/java/org/sopt/global/response/code/ResponseCode.java new file mode 100644 index 0000000..f6a015a --- /dev/null +++ b/src/main/java/org/sopt/global/response/code/ResponseCode.java @@ -0,0 +1,7 @@ +package org.sopt.global.response.code; + +public interface ResponseCode { + int getStatus(); + String getCode(); + String getMessage(); +} diff --git a/src/main/java/org/sopt/global/response/code/SuccessCode.java b/src/main/java/org/sopt/global/response/code/SuccessCode.java new file mode 100644 index 0000000..acffb35 --- /dev/null +++ b/src/main/java/org/sopt/global/response/code/SuccessCode.java @@ -0,0 +1,5 @@ +package org.sopt.global.response.code; + +public interface SuccessCode extends ResponseCode { + +} From feedab9ef243fd8b0ba6d4d4f9c8494da6fba099 Mon Sep 17 00:00:00 2001 From: kaswhy Date: Wed, 10 Dec 2025 23:43:09 +0900 Subject: [PATCH 02/18] =?UTF-8?q?refactor(global):=20ErrorMeta=20=EC=9C=84?= =?UTF-8?q?=EC=B9=98=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/org/sopt/global/response/{ => dto}/ErrorMeta.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/main/java/org/sopt/global/response/{ => dto}/ErrorMeta.java (64%) diff --git a/src/main/java/org/sopt/global/response/ErrorMeta.java b/src/main/java/org/sopt/global/response/dto/ErrorMeta.java similarity index 64% rename from src/main/java/org/sopt/global/response/ErrorMeta.java rename to src/main/java/org/sopt/global/response/dto/ErrorMeta.java index 82f54e1..665289c 100644 --- a/src/main/java/org/sopt/global/response/ErrorMeta.java +++ b/src/main/java/org/sopt/global/response/dto/ErrorMeta.java @@ -1,4 +1,4 @@ -package org.sopt.global.response; +package org.sopt.global.response.dto; public record ErrorMeta( String path, From 8be6ea82d46d57f22efd942dda22b6cec7099d13 Mon Sep 17 00:00:00 2001 From: kaswhy Date: Wed, 10 Dec 2025 23:43:20 +0900 Subject: [PATCH 03/18] =?UTF-8?q?refactor(global):=20ApiResponseBody=20?= =?UTF-8?q?=EC=9C=84=EC=B9=98=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/sopt/global/response/{ => dto}/ApiResponseBody.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) rename src/main/java/org/sopt/global/response/{ => dto}/ApiResponseBody.java (95%) diff --git a/src/main/java/org/sopt/global/response/ApiResponseBody.java b/src/main/java/org/sopt/global/response/dto/ApiResponseBody.java similarity index 95% rename from src/main/java/org/sopt/global/response/ApiResponseBody.java rename to src/main/java/org/sopt/global/response/dto/ApiResponseBody.java index 646f2ec..43bb752 100644 --- a/src/main/java/org/sopt/global/response/ApiResponseBody.java +++ b/src/main/java/org/sopt/global/response/dto/ApiResponseBody.java @@ -1,7 +1,8 @@ -package org.sopt.global.response; +package org.sopt.global.response.dto; import com.fasterxml.jackson.annotation.JsonInclude; import org.sopt.global.exception.ErrorCode; +import org.sopt.global.response.code.SuccessCode; @JsonInclude(JsonInclude.Include.NON_NULL) public record ApiResponseBody( From 377f201f90c14a58bd3a12356e1ee2e7217334c1 Mon Sep 17 00:00:00 2001 From: kaswhy Date: Wed, 10 Dec 2025 23:57:24 +0900 Subject: [PATCH 04/18] =?UTF-8?q?refactor(*):=20SuccessCode,=20ErrorCode?= =?UTF-8?q?=20=EB=8F=84=EB=A9=94=EC=9D=B8=EB=B3=84=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sopt/article/api/ArticleController.java | 8 +- .../article/api/code/ArticleSuccessCode.java | 23 ++++++ .../service/ArticleFindOneService.java | 2 +- .../service/ArticleWriteService.java | 4 +- .../domain/exception/ArticleErrorCode.java | 21 ++++++ .../domain/exception/CommentException.java | 11 +++ .../sopt/comment/domain/model/Comment.java | 75 +++++++++++++++++++ .../global/exception/GlobalErrorCode.java | 28 +++++++ .../exception/GlobalExceptionHandler.java | 12 +-- .../org/sopt/member/api/MemberController.java | 10 +-- .../member/api/code/MemberSuccessCode.java | 23 ++++++ .../api/dto/request/MemberCreateRequest.java | 8 +- .../service/MemberDeleteService.java | 3 +- .../service/MemberFindOneService.java | 2 +- .../service/MemberJoinService.java | 5 +- .../domain/exception/MemberErrorCode.java | 25 +++++++ .../org/sopt/member/domain/model/Member.java | 2 +- 17 files changed, 234 insertions(+), 28 deletions(-) create mode 100644 src/main/java/org/sopt/article/api/code/ArticleSuccessCode.java create mode 100644 src/main/java/org/sopt/article/domain/exception/ArticleErrorCode.java create mode 100644 src/main/java/org/sopt/comment/domain/exception/CommentException.java create mode 100644 src/main/java/org/sopt/comment/domain/model/Comment.java create mode 100644 src/main/java/org/sopt/global/exception/GlobalErrorCode.java create mode 100644 src/main/java/org/sopt/member/api/code/MemberSuccessCode.java create mode 100644 src/main/java/org/sopt/member/domain/exception/MemberErrorCode.java diff --git a/src/main/java/org/sopt/article/api/ArticleController.java b/src/main/java/org/sopt/article/api/ArticleController.java index 761463f..aa09c20 100644 --- a/src/main/java/org/sopt/article/api/ArticleController.java +++ b/src/main/java/org/sopt/article/api/ArticleController.java @@ -1,8 +1,8 @@ package org.sopt.article.api; -import static org.sopt.global.response.SuccessCode.ARTICLE_CREATED_SUCCESS; -import static org.sopt.global.response.SuccessCode.ARTICLE_LIST_RETRIEVED_SUCCESS; -import static org.sopt.global.response.SuccessCode.ARTICLE_RETRIEVED_SUCCESS; +import static org.sopt.article.api.code.ArticleSuccessCode.ARTICLE_CREATED_SUCCESS; +import static org.sopt.article.api.code.ArticleSuccessCode.ARTICLE_LIST_RETRIEVED_SUCCESS; +import static org.sopt.article.api.code.ArticleSuccessCode.ARTICLE_RETRIEVED_SUCCESS; import lombok.RequiredArgsConstructor; import org.sopt.article.api.dto.request.ArticleCreateRequest; @@ -18,7 +18,7 @@ import org.sopt.article.application.port.in.ArticleFindAllUsecase; import org.sopt.article.application.port.in.ArticleFindOneUsecase; import org.sopt.article.application.port.in.ArticleWriteUsecase; -import org.sopt.global.response.ApiResponseBody; +import org.sopt.global.response.dto.ApiResponseBody; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; diff --git a/src/main/java/org/sopt/article/api/code/ArticleSuccessCode.java b/src/main/java/org/sopt/article/api/code/ArticleSuccessCode.java new file mode 100644 index 0000000..5fcf8a1 --- /dev/null +++ b/src/main/java/org/sopt/article/api/code/ArticleSuccessCode.java @@ -0,0 +1,23 @@ +package org.sopt.article.api.code; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.sopt.global.response.code.SuccessCode; + +@Getter +@RequiredArgsConstructor +public enum ArticleSuccessCode implements SuccessCode { + + // 200 OK + ARTICLE_RETRIEVED_SUCCESS(200, "ART_200_001", "특정 게시글 조회를 성공적으로 완료했습니다."), + ARTICLE_LIST_RETRIEVED_SUCCESS(200, "ART_200_002", "게시글 전체 조회가 성공적으로 완료되었습니다."), + + // 201 CREATED + ARTICLE_CREATED_SUCCESS(201, "ART_201_001", "게시물이 성공적으로 생성되었습니다."), + + ; + + private final int status; + private final String code; + private final String message; +} diff --git a/src/main/java/org/sopt/article/application/service/ArticleFindOneService.java b/src/main/java/org/sopt/article/application/service/ArticleFindOneService.java index 67ee484..8595e95 100644 --- a/src/main/java/org/sopt/article/application/service/ArticleFindOneService.java +++ b/src/main/java/org/sopt/article/application/service/ArticleFindOneService.java @@ -1,6 +1,6 @@ package org.sopt.article.application.service; -import static org.sopt.global.exception.ErrorCode.ARTICLE_NOT_FOUND; +import static org.sopt.article.domain.exception.ArticleErrorCode.ARTICLE_NOT_FOUND; import lombok.RequiredArgsConstructor; import org.sopt.article.application.dto.result.ArticleFindOneResult; diff --git a/src/main/java/org/sopt/article/application/service/ArticleWriteService.java b/src/main/java/org/sopt/article/application/service/ArticleWriteService.java index 963206b..f283b99 100644 --- a/src/main/java/org/sopt/article/application/service/ArticleWriteService.java +++ b/src/main/java/org/sopt/article/application/service/ArticleWriteService.java @@ -1,7 +1,7 @@ package org.sopt.article.application.service; -import static org.sopt.global.exception.ErrorCode.ARTICLE_BY_NAME_ALREADY_EXISTS; -import static org.sopt.global.exception.ErrorCode.MEMBER_NOT_FOUND; +import static org.sopt.article.domain.exception.ArticleErrorCode.ARTICLE_BY_NAME_ALREADY_EXISTS; +import static org.sopt.member.domain.exception.MemberErrorCode.MEMBER_NOT_FOUND; import lombok.RequiredArgsConstructor; import org.sopt.article.application.dto.command.ArticleWriteCommand; diff --git a/src/main/java/org/sopt/article/domain/exception/ArticleErrorCode.java b/src/main/java/org/sopt/article/domain/exception/ArticleErrorCode.java new file mode 100644 index 0000000..507d693 --- /dev/null +++ b/src/main/java/org/sopt/article/domain/exception/ArticleErrorCode.java @@ -0,0 +1,21 @@ +package org.sopt.article.domain.exception; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.sopt.global.exception.ErrorCode; + +@Getter +@RequiredArgsConstructor +public enum ArticleErrorCode implements ErrorCode { + + // 404 NOT FOUND + ARTICLE_NOT_FOUND(404, "ART_404_001", "존재하지 않는 게시물입니다."), + + // 409 CONFLICT + ARTICLE_BY_NAME_ALREADY_EXISTS(409, "ART_409_001", "해당 제목으로 작성된 게시글이 이미 존재합니다."); + + private final int status; + private final String code; + private final String message; + +} diff --git a/src/main/java/org/sopt/comment/domain/exception/CommentException.java b/src/main/java/org/sopt/comment/domain/exception/CommentException.java new file mode 100644 index 0000000..598c67a --- /dev/null +++ b/src/main/java/org/sopt/comment/domain/exception/CommentException.java @@ -0,0 +1,11 @@ +package org.sopt.comment.domain.exception; + +import org.sopt.global.exception.BusinessException; +import org.sopt.global.exception.ErrorCode; + +public class CommentException extends BusinessException { + + public CommentException(ErrorCode errorCode) { + super(errorCode); + } +} diff --git a/src/main/java/org/sopt/comment/domain/model/Comment.java b/src/main/java/org/sopt/comment/domain/model/Comment.java new file mode 100644 index 0000000..670f881 --- /dev/null +++ b/src/main/java/org/sopt/comment/domain/model/Comment.java @@ -0,0 +1,75 @@ +package org.sopt.comment.domain.model; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.RequiredArgsConstructor; +import org.sopt.article.domain.model.Article; +import org.sopt.global.model.BaseEntity; +import org.sopt.member.domain.model.Member; + +@Getter +@RequiredArgsConstructor +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Entity +public class Comment extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "article_id") + private Article article; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id") + private Member author; + + @Column(nullable = false, length = 300) + private String content; + + @Builder(access = AccessLevel.PRIVATE) + private Comment(Article article, Member author, String content) { + validateComment(article, author, content); + this.article = article; + this.author = author; + this.content = content; + } + + public static Comment create(Article article, Member author, String content) { + return Comment.builder() + .article(article) + .author(author) + .content(content) + .build(); + } + + private void validateComment(Article article, Member author, String content) { + validateArticle(article); + validateAuthor(author); + validateContent(content); + } + + private void validateArticle(Article article) { + + } + + private void validateAuthor(Member author) { + + } + + private void validateContent(String content) { + + } + +} diff --git a/src/main/java/org/sopt/global/exception/GlobalErrorCode.java b/src/main/java/org/sopt/global/exception/GlobalErrorCode.java new file mode 100644 index 0000000..099504f --- /dev/null +++ b/src/main/java/org/sopt/global/exception/GlobalErrorCode.java @@ -0,0 +1,28 @@ +package org.sopt.global.exception; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public enum GlobalErrorCode implements ErrorCode { + + // 400 BAD REQUEST + INVALID_NUMBER_FORMAT(400, "COM_400_001", "숫자만 입력해주세요."), + INVALID_EMAIL_FORMAT(400, "COM_400_002", "잘못된 이메일 형식입니다."), + INVALID_DATE_FORMAT(400, "COM_400_003", "잘못된 날짜 형식입니다."), + INVALID_NULL_DATA(400, "COM_400_004", "빈 값은 허용되지 않습니다."), + INVALID_MAPPING_PARAMETER(400, "COM_400_005", "매핑할 수 없는 값입니다."), + + // 404 NOT FOUND + RESOURCE_NOT_FOUND(404, "COM_404_001", "존재하지 않는 리소스입니다."), + + // 500 INTERNAL SERVER ERROR + INTERNAL_SERVER_ERROR(500, "COM_500_001", "서버 내부 오류가 발생했습니다."), + + ; + + private final int status; + private final String code; + private final String message; +} diff --git a/src/main/java/org/sopt/global/exception/GlobalExceptionHandler.java b/src/main/java/org/sopt/global/exception/GlobalExceptionHandler.java index 46acf86..9f0a778 100644 --- a/src/main/java/org/sopt/global/exception/GlobalExceptionHandler.java +++ b/src/main/java/org/sopt/global/exception/GlobalExceptionHandler.java @@ -1,8 +1,11 @@ package org.sopt.global.exception; +import static org.sopt.global.exception.GlobalErrorCode.INTERNAL_SERVER_ERROR; +import static org.sopt.global.exception.GlobalErrorCode.INVALID_MAPPING_PARAMETER; + import jakarta.servlet.http.HttpServletRequest; -import org.sopt.global.response.ApiResponseBody; -import org.sopt.global.response.ErrorMeta; +import org.sopt.global.response.dto.ApiResponseBody; +import org.sopt.global.response.dto.ErrorMeta; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.http.converter.HttpMessageNotReadableException; @@ -39,7 +42,7 @@ public ResponseEntity> handleHttpMessageNotRead return ResponseEntity .status(HttpStatus.BAD_REQUEST) - .body(ApiResponseBody.onFailure(ErrorCode.INVALID_MAPPING_PARAMETER, null)); + .body(ApiResponseBody.onFailure(INVALID_MAPPING_PARAMETER, null)); } @ExceptionHandler(BusinessException.class) @@ -71,13 +74,12 @@ public ResponseEntity> handle( System.err.println(e.getClass().getName() + ": " + e.getMessage()); System.err.println("==============================="); - ErrorCode internalServerError = ErrorCode.INTERNAL_SERVER_ERROR; ErrorMeta meta = new ErrorMeta( request.getRequestURI(), System.currentTimeMillis() ); return ResponseEntity.status(500) - .body(ApiResponseBody.onFailure(internalServerError, e.getMessage(), meta)); + .body(ApiResponseBody.onFailure(INTERNAL_SERVER_ERROR, e.getMessage(), meta)); } } diff --git a/src/main/java/org/sopt/member/api/MemberController.java b/src/main/java/org/sopt/member/api/MemberController.java index 7656d9e..a975bb1 100644 --- a/src/main/java/org/sopt/member/api/MemberController.java +++ b/src/main/java/org/sopt/member/api/MemberController.java @@ -1,11 +1,11 @@ package org.sopt.member.api; -import static org.sopt.global.response.SuccessCode.MEMBER_CREATED_SUCCESS; -import static org.sopt.global.response.SuccessCode.MEMBER_DELETED_SUCCESS; -import static org.sopt.global.response.SuccessCode.MEMBER_LIST_RETRIEVED_SUCCESS; -import static org.sopt.global.response.SuccessCode.MEMBER_RETRIEVED_SUCCESS; +import static org.sopt.member.api.code.MemberSuccessCode.MEMBER_CREATED_SUCCESS; +import static org.sopt.member.api.code.MemberSuccessCode.MEMBER_DELETED_SUCCESS; +import static org.sopt.member.api.code.MemberSuccessCode.MEMBER_LIST_RETRIEVED_SUCCESS; +import static org.sopt.member.api.code.MemberSuccessCode.MEMBER_RETRIEVED_SUCCESS; -import org.sopt.global.response.ApiResponseBody; +import org.sopt.global.response.dto.ApiResponseBody; import org.sopt.member.api.dto.request.MemberCreateRequest; import org.sopt.member.api.dto.response.MemberCreateResponse; import org.sopt.member.api.dto.response.MemberFindOneResponse; diff --git a/src/main/java/org/sopt/member/api/code/MemberSuccessCode.java b/src/main/java/org/sopt/member/api/code/MemberSuccessCode.java new file mode 100644 index 0000000..35426cd --- /dev/null +++ b/src/main/java/org/sopt/member/api/code/MemberSuccessCode.java @@ -0,0 +1,23 @@ +package org.sopt.member.api.code; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.sopt.global.response.code.SuccessCode; + +@Getter +@RequiredArgsConstructor +public enum MemberSuccessCode implements SuccessCode { + + // 200 OK + MEMBER_RETRIEVED_SUCCESS(200, "MEM_200_001", "특정 회원 조회가 성공적으로 완료되었습니다."), + MEMBER_LIST_RETRIEVED_SUCCESS(200, "MEM_200_002","회원 전체 조회가 성공적으로 완료되었습니다."), + MEMBER_DELETED_SUCCESS(200, "MEM_200_003","회원 삭제가 성공적으로 완료되었습니다."), + + // 201 CREATED + MEMBER_CREATED_SUCCESS(201, "MEM_201_001", "회원가입이 성공적으로 완료되었습니다."), + ; + + private final int status; + private final String code; + private final String message; +} diff --git a/src/main/java/org/sopt/member/api/dto/request/MemberCreateRequest.java b/src/main/java/org/sopt/member/api/dto/request/MemberCreateRequest.java index bb77031..8fb408b 100644 --- a/src/main/java/org/sopt/member/api/dto/request/MemberCreateRequest.java +++ b/src/main/java/org/sopt/member/api/dto/request/MemberCreateRequest.java @@ -1,9 +1,9 @@ package org.sopt.member.api.dto.request; -import static org.sopt.global.exception.ErrorCode.INVALID_DATE_FORMAT; -import static org.sopt.global.exception.ErrorCode.INVALID_EMAIL_FORMAT; -import static org.sopt.global.exception.ErrorCode.INVALID_MAPPING_PARAMETER; -import static org.sopt.global.exception.ErrorCode.INVALID_NULL_DATA; +import static org.sopt.global.exception.GlobalErrorCode.INVALID_DATE_FORMAT; +import static org.sopt.global.exception.GlobalErrorCode.INVALID_EMAIL_FORMAT; +import static org.sopt.global.exception.GlobalErrorCode.INVALID_MAPPING_PARAMETER; +import static org.sopt.global.exception.GlobalErrorCode.INVALID_NULL_DATA; import java.util.regex.Pattern; import org.sopt.global.exception.BusinessException; diff --git a/src/main/java/org/sopt/member/application/service/MemberDeleteService.java b/src/main/java/org/sopt/member/application/service/MemberDeleteService.java index a202997..e0d9e33 100644 --- a/src/main/java/org/sopt/member/application/service/MemberDeleteService.java +++ b/src/main/java/org/sopt/member/application/service/MemberDeleteService.java @@ -1,12 +1,11 @@ package org.sopt.member.application.service; -import static org.sopt.global.exception.ErrorCode.MEMBER_NOT_FOUND; +import static org.sopt.member.domain.exception.MemberErrorCode.MEMBER_NOT_FOUND; import java.util.NoSuchElementException; import org.sopt.member.application.port.in.MemberDeleteUseCase; import org.sopt.member.domain.exception.MemberException; import org.sopt.member.domain.port.out.MemberRepositoryPort; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Service; @Service diff --git a/src/main/java/org/sopt/member/application/service/MemberFindOneService.java b/src/main/java/org/sopt/member/application/service/MemberFindOneService.java index b8c54d7..18e23d9 100644 --- a/src/main/java/org/sopt/member/application/service/MemberFindOneService.java +++ b/src/main/java/org/sopt/member/application/service/MemberFindOneService.java @@ -1,6 +1,6 @@ package org.sopt.member.application.service; -import static org.sopt.global.exception.ErrorCode.MEMBER_NOT_FOUND; +import static org.sopt.member.domain.exception.MemberErrorCode.MEMBER_NOT_FOUND; import lombok.RequiredArgsConstructor; import org.sopt.member.application.dto.result.MemberFindOneResult; diff --git a/src/main/java/org/sopt/member/application/service/MemberJoinService.java b/src/main/java/org/sopt/member/application/service/MemberJoinService.java index b664ce6..e67e324 100644 --- a/src/main/java/org/sopt/member/application/service/MemberJoinService.java +++ b/src/main/java/org/sopt/member/application/service/MemberJoinService.java @@ -1,7 +1,7 @@ package org.sopt.member.application.service; -import static org.sopt.global.exception.ErrorCode.INVALID_MAPPING_PARAMETER; -import static org.sopt.global.exception.ErrorCode.MEMBER_BY_EMAIL_ALREADY_EXISTS; +import static org.sopt.global.exception.GlobalErrorCode.INVALID_MAPPING_PARAMETER; +import static org.sopt.member.domain.exception.MemberErrorCode.MEMBER_BY_EMAIL_ALREADY_EXISTS; import org.sopt.member.application.dto.command.MemberJoinCommand; import org.sopt.member.application.dto.result.MemberJoinResult; @@ -9,7 +9,6 @@ import org.sopt.member.domain.exception.MemberException; import org.sopt.member.domain.model.Member; import org.sopt.member.domain.port.out.MemberRepositoryPort; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Service; @Service diff --git a/src/main/java/org/sopt/member/domain/exception/MemberErrorCode.java b/src/main/java/org/sopt/member/domain/exception/MemberErrorCode.java new file mode 100644 index 0000000..b235dda --- /dev/null +++ b/src/main/java/org/sopt/member/domain/exception/MemberErrorCode.java @@ -0,0 +1,25 @@ +package org.sopt.member.domain.exception; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.sopt.global.exception.ErrorCode; + +@Getter +@RequiredArgsConstructor +public enum MemberErrorCode implements ErrorCode { + + // 400 BAD REQUEST + AGE_MUST_UPPER_THAN_20(400, "MEM_400_001", "20세 미만은 가입할 수 없습니다."), + + // 404 NOT FOUND + MEMBER_NOT_FOUND(404, "MEM_404_001", "존재하지 않는 회원입니다."), + + // 409 CONFLICT + MEMBER_BY_EMAIL_ALREADY_EXISTS(409, "MEM_409_001", "해당 이메일로 가입된 회원이 이미 존재합니다."), + + ; + + private final int status; + private final String code; + private final String message; +} diff --git a/src/main/java/org/sopt/member/domain/model/Member.java b/src/main/java/org/sopt/member/domain/model/Member.java index 516f529..6adcb5f 100644 --- a/src/main/java/org/sopt/member/domain/model/Member.java +++ b/src/main/java/org/sopt/member/domain/model/Member.java @@ -1,6 +1,6 @@ package org.sopt.member.domain.model; -import static org.sopt.global.exception.ErrorCode.AGE_MUST_UPPER_THAN_20; +import static org.sopt.member.domain.exception.MemberErrorCode.AGE_MUST_UPPER_THAN_20; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; From b8e4c2518111ff71761fabc8e2925d4e022d7613 Mon Sep 17 00:00:00 2001 From: kaswhy Date: Thu, 11 Dec 2025 00:20:44 +0900 Subject: [PATCH 05/18] =?UTF-8?q?feat(comment):=20comment=20Entity=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/exception/CommentErrorCode.java | 22 +++++++++++++ .../sopt/comment/domain/model/Comment.java | 33 ++++++++++++++++--- 2 files changed, 50 insertions(+), 5 deletions(-) create mode 100644 src/main/java/org/sopt/comment/domain/exception/CommentErrorCode.java diff --git a/src/main/java/org/sopt/comment/domain/exception/CommentErrorCode.java b/src/main/java/org/sopt/comment/domain/exception/CommentErrorCode.java new file mode 100644 index 0000000..9e1b8b3 --- /dev/null +++ b/src/main/java/org/sopt/comment/domain/exception/CommentErrorCode.java @@ -0,0 +1,22 @@ +package org.sopt.comment.domain.exception; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.sopt.global.exception.ErrorCode; + +@Getter +@RequiredArgsConstructor +public enum CommentErrorCode implements ErrorCode { + + // 400 BAD REQUEST + ARTICLE_REQUIRED(400, "COMMENT_400_001", "관련된 아티클이 필요합니다."), + MEMBER_REQUIRED(400, "COMMENT_400_002", "댓글 작성자가 필요합니다."), + CONTENT_REQUIRED(400, "COMMENT_400_003", "댓글 본문이 필요합니다."), + CONTENT_TOO_LONG(400, "COMMENT_400_004", "댓글 본문 길이는 300자 이하여야 합니다."), + + ; + + private final int status; + private final String code; + private final String message; +} diff --git a/src/main/java/org/sopt/comment/domain/model/Comment.java b/src/main/java/org/sopt/comment/domain/model/Comment.java index 670f881..11f40df 100644 --- a/src/main/java/org/sopt/comment/domain/model/Comment.java +++ b/src/main/java/org/sopt/comment/domain/model/Comment.java @@ -1,5 +1,10 @@ package org.sopt.comment.domain.model; +import static org.sopt.comment.domain.exception.CommentErrorCode.ARTICLE_REQUIRED; +import static org.sopt.comment.domain.exception.CommentErrorCode.CONTENT_REQUIRED; +import static org.sopt.comment.domain.exception.CommentErrorCode.CONTENT_TOO_LONG; +import static org.sopt.comment.domain.exception.CommentErrorCode.MEMBER_REQUIRED; + import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.FetchType; @@ -12,17 +17,18 @@ import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; -import lombok.RequiredArgsConstructor; import org.sopt.article.domain.model.Article; +import org.sopt.comment.domain.exception.CommentException; import org.sopt.global.model.BaseEntity; import org.sopt.member.domain.model.Member; @Getter -@RequiredArgsConstructor @NoArgsConstructor(access = AccessLevel.PROTECTED) @Entity public class Comment extends BaseEntity { + private static final int MAX_CONTENT_LENGTH = 300; + @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @@ -35,7 +41,7 @@ public class Comment extends BaseEntity { @JoinColumn(name = "member_id") private Member author; - @Column(nullable = false, length = 300) + @Column(nullable = false, length = MAX_CONTENT_LENGTH) private String content; @Builder(access = AccessLevel.PRIVATE) @@ -61,15 +67,32 @@ private void validateComment(Article article, Member author, String content) { } private void validateArticle(Article article) { - + if (article == null) { + throw new CommentException(ARTICLE_REQUIRED); + } } private void validateAuthor(Member author) { - + if (author == null) { + throw new CommentException(MEMBER_REQUIRED); + } } private void validateContent(String content) { + validateContentNotEmpty(content); + validateContentLength(content); + } + + private void validateContentNotEmpty(String content) { + if (content == null || content.isBlank()) { + throw new CommentException(CONTENT_REQUIRED); + } + } + private void validateContentLength(String content) { + if (content.length() > MAX_CONTENT_LENGTH) { + throw new CommentException(CONTENT_TOO_LONG); + } } } From be5be04fe38a0c2b0f20deae46da381f59c9a591 Mon Sep 17 00:00:00 2001 From: kaswhy Date: Thu, 11 Dec 2025 15:23:49 +0900 Subject: [PATCH 06/18] =?UTF-8?q?feat(comment):=20comment=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=EC=BB=A8=ED=8A=B8=EB=A1=A4=EB=9F=AC,=20=EB=A6=AC?= =?UTF-8?q?=ED=8F=AC=EC=A7=80=ED=86=A0=EB=A6=AC=20=EC=8A=A4=EC=BC=88?= =?UTF-8?q?=EB=A0=88=ED=86=A4=20=EC=BD=94=EB=93=9C=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/sopt/comment/api/CommentController.java | 17 +++++++++++++++++ .../domain/port/out/CommentRepository.java | 5 +++++ .../jpa/CommentJpaRepository.java | 8 ++++++++ .../jpa/CommentJpaRepositoryImpl.java | 12 ++++++++++++ 4 files changed, 42 insertions(+) create mode 100644 src/main/java/org/sopt/comment/api/CommentController.java create mode 100644 src/main/java/org/sopt/comment/domain/port/out/CommentRepository.java create mode 100644 src/main/java/org/sopt/comment/infrastructure/jpa/CommentJpaRepository.java create mode 100644 src/main/java/org/sopt/comment/infrastructure/jpa/CommentJpaRepositoryImpl.java diff --git a/src/main/java/org/sopt/comment/api/CommentController.java b/src/main/java/org/sopt/comment/api/CommentController.java new file mode 100644 index 0000000..30de65c --- /dev/null +++ b/src/main/java/org/sopt/comment/api/CommentController.java @@ -0,0 +1,17 @@ +package org.sopt.comment.api; + +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.RestController; + +@RequiredArgsConstructor +@RestController +public class CommentController { + + // 작성 + + // 수정 + + // 삭제 + + // 조회 +} diff --git a/src/main/java/org/sopt/comment/domain/port/out/CommentRepository.java b/src/main/java/org/sopt/comment/domain/port/out/CommentRepository.java new file mode 100644 index 0000000..1462b77 --- /dev/null +++ b/src/main/java/org/sopt/comment/domain/port/out/CommentRepository.java @@ -0,0 +1,5 @@ +package org.sopt.comment.domain.port.out; + +public interface CommentRepository { + +} diff --git a/src/main/java/org/sopt/comment/infrastructure/jpa/CommentJpaRepository.java b/src/main/java/org/sopt/comment/infrastructure/jpa/CommentJpaRepository.java new file mode 100644 index 0000000..e9d36e4 --- /dev/null +++ b/src/main/java/org/sopt/comment/infrastructure/jpa/CommentJpaRepository.java @@ -0,0 +1,8 @@ +package org.sopt.comment.infrastructure.jpa; + +import org.sopt.comment.domain.model.Comment; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface CommentJpaRepository extends JpaRepository { + +} diff --git a/src/main/java/org/sopt/comment/infrastructure/jpa/CommentJpaRepositoryImpl.java b/src/main/java/org/sopt/comment/infrastructure/jpa/CommentJpaRepositoryImpl.java new file mode 100644 index 0000000..26971b9 --- /dev/null +++ b/src/main/java/org/sopt/comment/infrastructure/jpa/CommentJpaRepositoryImpl.java @@ -0,0 +1,12 @@ +package org.sopt.comment.infrastructure.jpa; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +@RequiredArgsConstructor +@Repository +public class CommentJpaRepositoryImpl { + + private final CommentJpaRepository jpa; + +} From 53b8b1b205ac1fec1b7d59b7906e0d5c967a7ce4 Mon Sep 17 00:00:00 2001 From: kaswhy Date: Thu, 11 Dec 2025 17:18:35 +0900 Subject: [PATCH 07/18] =?UTF-8?q?refactor(member):=20member=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=EB=A6=AC=ED=8F=AC=EC=A7=80=ED=86=A0=EB=A6=AC=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/MemberDeleteService.java | 8 ++-- .../service/MemberFindAllService.java | 9 ++-- .../service/MemberFindOneService.java | 4 +- .../service/MemberJoinService.java | 8 ++-- .../domain/port/out/MemberRepository.java | 14 +++++++ .../jpa/MemberJpaRepository.java} | 4 +- .../jpa/MemberJpaRepositoryImpl.java | 42 +++++++++++++++++++ 7 files changed, 72 insertions(+), 17 deletions(-) create mode 100644 src/main/java/org/sopt/member/domain/port/out/MemberRepository.java rename src/main/java/org/sopt/member/{domain/port/out/MemberRepositoryPort.java => infrastructure/jpa/MemberJpaRepository.java} (56%) create mode 100644 src/main/java/org/sopt/member/infrastructure/jpa/MemberJpaRepositoryImpl.java diff --git a/src/main/java/org/sopt/member/application/service/MemberDeleteService.java b/src/main/java/org/sopt/member/application/service/MemberDeleteService.java index e0d9e33..2165177 100644 --- a/src/main/java/org/sopt/member/application/service/MemberDeleteService.java +++ b/src/main/java/org/sopt/member/application/service/MemberDeleteService.java @@ -5,18 +5,18 @@ import java.util.NoSuchElementException; import org.sopt.member.application.port.in.MemberDeleteUseCase; import org.sopt.member.domain.exception.MemberException; -import org.sopt.member.domain.port.out.MemberRepositoryPort; +import org.sopt.member.domain.port.out.MemberRepository; import org.springframework.stereotype.Service; @Service public class MemberDeleteService implements MemberDeleteUseCase { - private final MemberRepositoryPort memberRepository; + private final MemberRepository memberRepository; public MemberDeleteService( - MemberRepositoryPort memberRepositoryPort + MemberRepository memberRepository ) { - this.memberRepository = memberRepositoryPort; + this.memberRepository = memberRepository; } @Override diff --git a/src/main/java/org/sopt/member/application/service/MemberFindAllService.java b/src/main/java/org/sopt/member/application/service/MemberFindAllService.java index b06726e..3a9015d 100644 --- a/src/main/java/org/sopt/member/application/service/MemberFindAllService.java +++ b/src/main/java/org/sopt/member/application/service/MemberFindAllService.java @@ -4,19 +4,18 @@ import org.sopt.member.application.dto.result.MemberListResult; import org.sopt.member.application.port.in.MemberFindAllUseCase; import org.sopt.member.domain.model.Member; -import org.sopt.member.domain.port.out.MemberRepositoryPort; -import org.springframework.beans.factory.annotation.Qualifier; +import org.sopt.member.domain.port.out.MemberRepository; import org.springframework.stereotype.Service; @Service public class MemberFindAllService implements MemberFindAllUseCase { - private final MemberRepositoryPort memberRepository; + private final MemberRepository memberRepository; public MemberFindAllService( - MemberRepositoryPort memberRepositoryPort + MemberRepository memberRepository ) { - this.memberRepository = memberRepositoryPort; + this.memberRepository = memberRepository; } @Override diff --git a/src/main/java/org/sopt/member/application/service/MemberFindOneService.java b/src/main/java/org/sopt/member/application/service/MemberFindOneService.java index 18e23d9..b5b7a62 100644 --- a/src/main/java/org/sopt/member/application/service/MemberFindOneService.java +++ b/src/main/java/org/sopt/member/application/service/MemberFindOneService.java @@ -7,14 +7,14 @@ import org.sopt.member.application.port.in.MemberFindOneUseCase; import org.sopt.member.domain.exception.MemberException; import org.sopt.member.domain.model.Member; -import org.sopt.member.domain.port.out.MemberRepositoryPort; +import org.sopt.member.domain.port.out.MemberRepository; import org.springframework.stereotype.Service; @RequiredArgsConstructor @Service public class MemberFindOneService implements MemberFindOneUseCase { - private final MemberRepositoryPort memberRepository; + private final MemberRepository memberRepository; @Override public MemberFindOneResult findOne(Long id) { diff --git a/src/main/java/org/sopt/member/application/service/MemberJoinService.java b/src/main/java/org/sopt/member/application/service/MemberJoinService.java index e67e324..17fb44b 100644 --- a/src/main/java/org/sopt/member/application/service/MemberJoinService.java +++ b/src/main/java/org/sopt/member/application/service/MemberJoinService.java @@ -8,18 +8,18 @@ import org.sopt.member.application.port.in.MemberJoinUseCase; import org.sopt.member.domain.exception.MemberException; import org.sopt.member.domain.model.Member; -import org.sopt.member.domain.port.out.MemberRepositoryPort; +import org.sopt.member.domain.port.out.MemberRepository; import org.springframework.stereotype.Service; @Service public class MemberJoinService implements MemberJoinUseCase { - private final MemberRepositoryPort memberRepository; + private final MemberRepository memberRepository; public MemberJoinService( - MemberRepositoryPort memberRepositoryPort + MemberRepository memberRepository ) { - this.memberRepository = memberRepositoryPort; + this.memberRepository = memberRepository; } @Override diff --git a/src/main/java/org/sopt/member/domain/port/out/MemberRepository.java b/src/main/java/org/sopt/member/domain/port/out/MemberRepository.java new file mode 100644 index 0000000..27b5eae --- /dev/null +++ b/src/main/java/org/sopt/member/domain/port/out/MemberRepository.java @@ -0,0 +1,14 @@ +package org.sopt.member.domain.port.out; + +import java.util.List; +import java.util.Optional; +import org.sopt.member.domain.model.Member; + +public interface MemberRepository { + + Member save(Member member); + boolean existsByEmail(String email); + List findAll(); + void deleteById(Long id); + Optional findById(Long id); +} diff --git a/src/main/java/org/sopt/member/domain/port/out/MemberRepositoryPort.java b/src/main/java/org/sopt/member/infrastructure/jpa/MemberJpaRepository.java similarity index 56% rename from src/main/java/org/sopt/member/domain/port/out/MemberRepositoryPort.java rename to src/main/java/org/sopt/member/infrastructure/jpa/MemberJpaRepository.java index 8deb0e4..f4db59c 100644 --- a/src/main/java/org/sopt/member/domain/port/out/MemberRepositoryPort.java +++ b/src/main/java/org/sopt/member/infrastructure/jpa/MemberJpaRepository.java @@ -1,9 +1,9 @@ -package org.sopt.member.domain.port.out; +package org.sopt.member.infrastructure.jpa; import org.sopt.member.domain.model.Member; import org.springframework.data.jpa.repository.JpaRepository; -public interface MemberRepositoryPort extends JpaRepository { +public interface MemberJpaRepository extends JpaRepository { boolean existsByEmail(String email); diff --git a/src/main/java/org/sopt/member/infrastructure/jpa/MemberJpaRepositoryImpl.java b/src/main/java/org/sopt/member/infrastructure/jpa/MemberJpaRepositoryImpl.java new file mode 100644 index 0000000..a2703cb --- /dev/null +++ b/src/main/java/org/sopt/member/infrastructure/jpa/MemberJpaRepositoryImpl.java @@ -0,0 +1,42 @@ +package org.sopt.member.infrastructure.jpa; + +import java.util.List; +import java.util.Optional; +import lombok.RequiredArgsConstructor; +import org.sopt.comment.infrastructure.jpa.CommentJpaRepository; +import org.sopt.member.domain.model.Member; +import org.sopt.member.domain.port.out.MemberRepository; +import org.springframework.stereotype.Repository; + +@RequiredArgsConstructor +@Repository +public class MemberJpaRepositoryImpl implements MemberRepository { + + private final MemberJpaRepository jpa; + + @Override + public Member save(Member member) { + return jpa.save(member); + } + + @Override + public boolean existsByEmail(String email) { + return jpa.existsByEmail(email); + } + + @Override + public List findAll() { + return jpa.findAll(); + } + + @Override + public void deleteById(Long id) { + jpa.deleteById(id); + } + + @Override + public Optional findById(Long id) { + return jpa.findById(id); + } + +} From ec7e3a465570fea407a5d72f62c6bd9acde27532 Mon Sep 17 00:00:00 2001 From: kaswhy Date: Thu, 11 Dec 2025 17:19:22 +0900 Subject: [PATCH 08/18] =?UTF-8?q?refactor(global):=20ApiResponseBody=20?= =?UTF-8?q?=EB=82=B4=EB=B6=80=20=EC=BB=AC=EB=9F=BC=20=EC=88=9C=EC=84=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/response/dto/ApiResponseBody.java | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/sopt/global/response/dto/ApiResponseBody.java b/src/main/java/org/sopt/global/response/dto/ApiResponseBody.java index 43bb752..7112692 100644 --- a/src/main/java/org/sopt/global/response/dto/ApiResponseBody.java +++ b/src/main/java/org/sopt/global/response/dto/ApiResponseBody.java @@ -8,9 +8,9 @@ public record ApiResponseBody( boolean success, int status, + String code, String message, T data, - String code, M meta ) { @@ -18,9 +18,9 @@ public static ApiResponseBody ok(SuccessCode successCode) { return new ApiResponseBody<>( true, successCode.getStatus(), + successCode.getCode(), successCode.getMessage(), null, - null, null ); } @@ -29,8 +29,8 @@ public static ApiResponseBody ok(SuccessCode successCode, T data) { return new ApiResponseBody<>( true, successCode.getStatus(), + successCode.getCode(), successCode.getMessage(), - data, null, null ); @@ -40,20 +40,21 @@ public static ApiResponseBody created(SuccessCode successCode, T da return new ApiResponseBody<>( true, successCode.getStatus(), + successCode.getCode(), successCode.getMessage(), - data, null, null ); } - public static ApiResponseBody onFailure(ErrorCode errorCode, ErrorMeta errorMeta) { + public static ApiResponseBody onFailure(ErrorCode errorCode, + ErrorMeta errorMeta) { return new ApiResponseBody<>( false, errorCode.getStatus(), errorCode.getMessage(), - null, errorCode.getCode(), + null, errorMeta ); } @@ -66,9 +67,9 @@ public static ApiResponseBody onFailure( return new ApiResponseBody<>( false, errorCode.getStatus(), - message, - null, errorCode.getCode(), + errorCode.getMessage(), + null, errorMeta ); } From 5cc99d008ae5d8b99b05322c7b892e6ea6530a61 Mon Sep 17 00:00:00 2001 From: kaswhy Date: Thu, 11 Dec 2025 17:29:10 +0900 Subject: [PATCH 09/18] =?UTF-8?q?refactor(article):=20article=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=EB=A6=AC=ED=8F=AC=EC=A7=80=ED=86=A0=EB=A6=AC=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/ArticleWriteService.java | 6 ++-- .../domain/port/out/ArticleRepository.java | 8 +++-- .../jpa/ArticleJpaRepository.java | 10 ++++++ .../jpa/ArticleJpaRepositoryImpl.java | 36 +++++++++++++++++++ 4 files changed, 55 insertions(+), 5 deletions(-) create mode 100644 src/main/java/org/sopt/article/infrastructure/jpa/ArticleJpaRepository.java create mode 100644 src/main/java/org/sopt/article/infrastructure/jpa/ArticleJpaRepositoryImpl.java diff --git a/src/main/java/org/sopt/article/application/service/ArticleWriteService.java b/src/main/java/org/sopt/article/application/service/ArticleWriteService.java index f283b99..7a7df77 100644 --- a/src/main/java/org/sopt/article/application/service/ArticleWriteService.java +++ b/src/main/java/org/sopt/article/application/service/ArticleWriteService.java @@ -12,14 +12,14 @@ import org.sopt.article.domain.port.out.ArticleRepository; import org.sopt.member.domain.exception.MemberException; import org.sopt.member.domain.model.Member; -import org.sopt.member.domain.port.out.MemberRepositoryPort; +import org.sopt.member.domain.port.out.MemberRepository; import org.springframework.stereotype.Service; @RequiredArgsConstructor @Service public class ArticleWriteService implements ArticleWriteUsecase { - private final MemberRepositoryPort memberRepositoryPort; + private final MemberRepository memberRepository; private final ArticleRepository articleRepository; public ArticleCreateResult writeArticle(ArticleWriteCommand articleWriteCommand) { @@ -27,7 +27,7 @@ public ArticleCreateResult writeArticle(ArticleWriteCommand articleWriteCommand) throw new ArticleException(ARTICLE_BY_NAME_ALREADY_EXISTS); } - Member member = memberRepositoryPort.findById(articleWriteCommand.memberId()) + Member member = memberRepository.findById(articleWriteCommand.memberId()) .orElseThrow(() -> new MemberException(MEMBER_NOT_FOUND)); Article article = Article.create( member, diff --git a/src/main/java/org/sopt/article/domain/port/out/ArticleRepository.java b/src/main/java/org/sopt/article/domain/port/out/ArticleRepository.java index 5bc3701..bf843c1 100644 --- a/src/main/java/org/sopt/article/domain/port/out/ArticleRepository.java +++ b/src/main/java/org/sopt/article/domain/port/out/ArticleRepository.java @@ -1,11 +1,15 @@ package org.sopt.article.domain.port.out; +import java.util.List; +import java.util.Optional; import org.sopt.article.domain.model.Article; -import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @Repository -public interface ArticleRepository extends JpaRepository { +public interface ArticleRepository { + Article save(Article article); boolean existsByTitle(String title); + Optional
findById(Long id); + List
findAll(); } diff --git a/src/main/java/org/sopt/article/infrastructure/jpa/ArticleJpaRepository.java b/src/main/java/org/sopt/article/infrastructure/jpa/ArticleJpaRepository.java new file mode 100644 index 0000000..326dd56 --- /dev/null +++ b/src/main/java/org/sopt/article/infrastructure/jpa/ArticleJpaRepository.java @@ -0,0 +1,10 @@ +package org.sopt.article.infrastructure.jpa; + +import org.sopt.article.domain.model.Article; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface ArticleJpaRepository extends JpaRepository { + + boolean existsByTitle(String title); + +} diff --git a/src/main/java/org/sopt/article/infrastructure/jpa/ArticleJpaRepositoryImpl.java b/src/main/java/org/sopt/article/infrastructure/jpa/ArticleJpaRepositoryImpl.java new file mode 100644 index 0000000..41e5a3d --- /dev/null +++ b/src/main/java/org/sopt/article/infrastructure/jpa/ArticleJpaRepositoryImpl.java @@ -0,0 +1,36 @@ +package org.sopt.article.infrastructure.jpa; + +import java.util.List; +import java.util.Optional; +import lombok.RequiredArgsConstructor; +import org.sopt.article.domain.model.Article; +import org.sopt.article.domain.port.out.ArticleRepository; +import org.springframework.stereotype.Repository; + +@RequiredArgsConstructor +@Repository +public class ArticleJpaRepositoryImpl implements ArticleRepository { + + private final ArticleJpaRepository jpa; + + @Override + public Article save(Article article) { + return jpa.save(article); + } + + @Override + public boolean existsByTitle(String title){ + return jpa.existsByTitle(title); + } + + @Override + public Optional
findById(Long id) { + return jpa.findById(id); + } + + @Override + public List
findAll() { + return jpa.findAll(); + } + +} From 496170ed280cd38fc04fcbd8a4f02af2e794c100 Mon Sep 17 00:00:00 2001 From: kaswhy Date: Thu, 11 Dec 2025 17:29:57 +0900 Subject: [PATCH 10/18] =?UTF-8?q?refactor(member):=20=ED=95=84=EC=9A=94?= =?UTF-8?q?=EC=97=86=EB=8A=94=20=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sopt/member/infrastructure/jpa/MemberJpaRepositoryImpl.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/org/sopt/member/infrastructure/jpa/MemberJpaRepositoryImpl.java b/src/main/java/org/sopt/member/infrastructure/jpa/MemberJpaRepositoryImpl.java index a2703cb..9ef7200 100644 --- a/src/main/java/org/sopt/member/infrastructure/jpa/MemberJpaRepositoryImpl.java +++ b/src/main/java/org/sopt/member/infrastructure/jpa/MemberJpaRepositoryImpl.java @@ -3,7 +3,6 @@ import java.util.List; import java.util.Optional; import lombok.RequiredArgsConstructor; -import org.sopt.comment.infrastructure.jpa.CommentJpaRepository; import org.sopt.member.domain.model.Member; import org.sopt.member.domain.port.out.MemberRepository; import org.springframework.stereotype.Repository; From 87bd4062af859532aeb85e03ed1f31d5a16633ed Mon Sep 17 00:00:00 2001 From: kaswhy Date: Thu, 11 Dec 2025 18:28:03 +0900 Subject: [PATCH 11/18] =?UTF-8?q?feat(comment):=20=EB=8C=93=EA=B8=80=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1=20api=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sopt/article/domain/model/Article.java | 2 +- .../domain/port/out/ArticleRepository.java | 2 - ...mpl.java => ArticleRepositoryAdapter.java} | 2 +- .../sopt/comment/api/CommentController.java | 35 ++++++++++++++++- .../comment/api/code/CommentSuccessCode.java | 22 +++++++++++ .../api/dto/request/CommentCreateRequest.java | 12 ++++++ .../dto/response/CommentCreateResponse.java | 7 ++++ .../api/mapper/CommentRequestMapper.java | 19 +++++++++ .../api/mapper/CommentResponseMapper.java | 15 +++++++ .../dto/command/CommentCreateCommand.java | 9 +++++ .../dto/result/CommentCreateResult.java | 12 ++++++ .../port/in/CommentCreateUsecase.java | 9 +++++ .../service/CommentCreateService.java | 39 +++++++++++++++++++ .../domain/port/out/CommentRepository.java | 3 ++ .../jpa/CommentJpaRepositoryImpl.java | 12 ------ .../jpa/CommentRepositoryAdapter.java | 19 +++++++++ .../org/sopt/member/domain/model/Member.java | 7 ---- ...Impl.java => MemberRepositoryAdapter.java} | 2 +- 18 files changed, 203 insertions(+), 25 deletions(-) rename src/main/java/org/sopt/article/infrastructure/jpa/{ArticleJpaRepositoryImpl.java => ArticleRepositoryAdapter.java} (92%) create mode 100644 src/main/java/org/sopt/comment/api/code/CommentSuccessCode.java create mode 100644 src/main/java/org/sopt/comment/api/dto/request/CommentCreateRequest.java create mode 100644 src/main/java/org/sopt/comment/api/dto/response/CommentCreateResponse.java create mode 100644 src/main/java/org/sopt/comment/api/mapper/CommentRequestMapper.java create mode 100644 src/main/java/org/sopt/comment/api/mapper/CommentResponseMapper.java create mode 100644 src/main/java/org/sopt/comment/application/dto/command/CommentCreateCommand.java create mode 100644 src/main/java/org/sopt/comment/application/dto/result/CommentCreateResult.java create mode 100644 src/main/java/org/sopt/comment/application/port/in/CommentCreateUsecase.java create mode 100644 src/main/java/org/sopt/comment/application/service/CommentCreateService.java delete mode 100644 src/main/java/org/sopt/comment/infrastructure/jpa/CommentJpaRepositoryImpl.java create mode 100644 src/main/java/org/sopt/comment/infrastructure/jpa/CommentRepositoryAdapter.java rename src/main/java/org/sopt/member/infrastructure/jpa/{MemberJpaRepositoryImpl.java => MemberRepositoryAdapter.java} (93%) diff --git a/src/main/java/org/sopt/article/domain/model/Article.java b/src/main/java/org/sopt/article/domain/model/Article.java index db4f424..76068ba 100644 --- a/src/main/java/org/sopt/article/domain/model/Article.java +++ b/src/main/java/org/sopt/article/domain/model/Article.java @@ -29,7 +29,7 @@ public class Article extends BaseEntity { private Long id; @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "memberId") + @JoinColumn(name = "member_id") private Member member; @Enumerated(EnumType.STRING) diff --git a/src/main/java/org/sopt/article/domain/port/out/ArticleRepository.java b/src/main/java/org/sopt/article/domain/port/out/ArticleRepository.java index bf843c1..865a317 100644 --- a/src/main/java/org/sopt/article/domain/port/out/ArticleRepository.java +++ b/src/main/java/org/sopt/article/domain/port/out/ArticleRepository.java @@ -3,9 +3,7 @@ import java.util.List; import java.util.Optional; import org.sopt.article.domain.model.Article; -import org.springframework.stereotype.Repository; -@Repository public interface ArticleRepository { Article save(Article article); diff --git a/src/main/java/org/sopt/article/infrastructure/jpa/ArticleJpaRepositoryImpl.java b/src/main/java/org/sopt/article/infrastructure/jpa/ArticleRepositoryAdapter.java similarity index 92% rename from src/main/java/org/sopt/article/infrastructure/jpa/ArticleJpaRepositoryImpl.java rename to src/main/java/org/sopt/article/infrastructure/jpa/ArticleRepositoryAdapter.java index 41e5a3d..df0ed2e 100644 --- a/src/main/java/org/sopt/article/infrastructure/jpa/ArticleJpaRepositoryImpl.java +++ b/src/main/java/org/sopt/article/infrastructure/jpa/ArticleRepositoryAdapter.java @@ -9,7 +9,7 @@ @RequiredArgsConstructor @Repository -public class ArticleJpaRepositoryImpl implements ArticleRepository { +public class ArticleRepositoryAdapter implements ArticleRepository { private final ArticleJpaRepository jpa; diff --git a/src/main/java/org/sopt/comment/api/CommentController.java b/src/main/java/org/sopt/comment/api/CommentController.java index 30de65c..cdfb93d 100644 --- a/src/main/java/org/sopt/comment/api/CommentController.java +++ b/src/main/java/org/sopt/comment/api/CommentController.java @@ -1,13 +1,46 @@ package org.sopt.comment.api; +import static org.sopt.comment.api.code.CommentSuccessCode.COMMENT_CREATED_SUCCESS; + +import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; +import org.sopt.comment.api.dto.request.CommentCreateRequest; +import org.sopt.comment.api.dto.response.CommentCreateResponse; +import org.sopt.comment.api.mapper.CommentRequestMapper; +import org.sopt.comment.api.mapper.CommentResponseMapper; +import org.sopt.comment.application.dto.command.CommentCreateCommand; +import org.sopt.comment.application.dto.result.CommentCreateResult; +import org.sopt.comment.application.port.in.CommentCreateUsecase; +import org.sopt.global.response.dto.ApiResponseBody; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +@RequestMapping("/api/v1") @RequiredArgsConstructor @RestController public class CommentController { - // 작성 + private final CommentCreateUsecase commentCreateUsecase; + + @PostMapping("/articles/{articleId}/comments") + public ApiResponseBody createComment( + @RequestHeader Long userId, + @PathVariable Long articleId, + @Valid @RequestBody CommentCreateRequest commentCreateRequest + ) { + CommentCreateCommand commentCreateCommand = CommentRequestMapper + .toCommentCreateCommand(userId, articleId, commentCreateRequest); + CommentCreateResult commentCreateResult = commentCreateUsecase + .writeComment(commentCreateCommand); + CommentCreateResponse commentCreateResponse = CommentResponseMapper + .toCreateResponse(commentCreateResult); + + return ApiResponseBody.created(COMMENT_CREATED_SUCCESS, commentCreateResponse); + } // 수정 diff --git a/src/main/java/org/sopt/comment/api/code/CommentSuccessCode.java b/src/main/java/org/sopt/comment/api/code/CommentSuccessCode.java new file mode 100644 index 0000000..92bae49 --- /dev/null +++ b/src/main/java/org/sopt/comment/api/code/CommentSuccessCode.java @@ -0,0 +1,22 @@ +package org.sopt.comment.api.code; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.sopt.global.response.code.SuccessCode; + +@Getter +@RequiredArgsConstructor +public enum CommentSuccessCode implements SuccessCode { + + // 200 OK + + // 201 CREATED + COMMENT_CREATED_SUCCESS(201, "COMMENT_201_001", "성공적으로 댓글을 작성했습니다."), + + ; + + private final int status; + private final String code; + private final String message; + +} diff --git a/src/main/java/org/sopt/comment/api/dto/request/CommentCreateRequest.java b/src/main/java/org/sopt/comment/api/dto/request/CommentCreateRequest.java new file mode 100644 index 0000000..b06637f --- /dev/null +++ b/src/main/java/org/sopt/comment/api/dto/request/CommentCreateRequest.java @@ -0,0 +1,12 @@ +package org.sopt.comment.api.dto.request; + +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.NotBlank; + +public record CommentCreateRequest( + @NotBlank + @Max(300) + String content +) { + +} diff --git a/src/main/java/org/sopt/comment/api/dto/response/CommentCreateResponse.java b/src/main/java/org/sopt/comment/api/dto/response/CommentCreateResponse.java new file mode 100644 index 0000000..0b7d454 --- /dev/null +++ b/src/main/java/org/sopt/comment/api/dto/response/CommentCreateResponse.java @@ -0,0 +1,7 @@ +package org.sopt.comment.api.dto.response; + +public record CommentCreateResponse( + Long commentId +){ + +} diff --git a/src/main/java/org/sopt/comment/api/mapper/CommentRequestMapper.java b/src/main/java/org/sopt/comment/api/mapper/CommentRequestMapper.java new file mode 100644 index 0000000..6ffcfa7 --- /dev/null +++ b/src/main/java/org/sopt/comment/api/mapper/CommentRequestMapper.java @@ -0,0 +1,19 @@ +package org.sopt.comment.api.mapper; + +import org.sopt.comment.api.dto.request.CommentCreateRequest; +import org.sopt.comment.application.dto.command.CommentCreateCommand; + +public class CommentRequestMapper { + + public static CommentCreateCommand toCommentCreateCommand( + Long userId, + Long articleId, + CommentCreateRequest commentCreateRequest + ) { + return new CommentCreateCommand( + userId, + articleId, + commentCreateRequest.content() + ); + } +} diff --git a/src/main/java/org/sopt/comment/api/mapper/CommentResponseMapper.java b/src/main/java/org/sopt/comment/api/mapper/CommentResponseMapper.java new file mode 100644 index 0000000..cd60105 --- /dev/null +++ b/src/main/java/org/sopt/comment/api/mapper/CommentResponseMapper.java @@ -0,0 +1,15 @@ +package org.sopt.comment.api.mapper; + +import org.sopt.comment.api.dto.response.CommentCreateResponse; +import org.sopt.comment.application.dto.result.CommentCreateResult; + +public class CommentResponseMapper { + + public static CommentCreateResponse toCreateResponse( + CommentCreateResult commentCreateResult + ) { + return new CommentCreateResponse( + commentCreateResult.commentId() + ); + } +} diff --git a/src/main/java/org/sopt/comment/application/dto/command/CommentCreateCommand.java b/src/main/java/org/sopt/comment/application/dto/command/CommentCreateCommand.java new file mode 100644 index 0000000..cf88883 --- /dev/null +++ b/src/main/java/org/sopt/comment/application/dto/command/CommentCreateCommand.java @@ -0,0 +1,9 @@ +package org.sopt.comment.application.dto.command; + +public record CommentCreateCommand( + Long userId, + Long articleId, + String content +) { + +} diff --git a/src/main/java/org/sopt/comment/application/dto/result/CommentCreateResult.java b/src/main/java/org/sopt/comment/application/dto/result/CommentCreateResult.java new file mode 100644 index 0000000..9e67ebe --- /dev/null +++ b/src/main/java/org/sopt/comment/application/dto/result/CommentCreateResult.java @@ -0,0 +1,12 @@ +package org.sopt.comment.application.dto.result; + +import org.sopt.comment.domain.model.Comment; + +public record CommentCreateResult( + Long commentId +) { + + public static CommentCreateResult create(Comment comment) { + return new CommentCreateResult(comment.getId()); + } +} diff --git a/src/main/java/org/sopt/comment/application/port/in/CommentCreateUsecase.java b/src/main/java/org/sopt/comment/application/port/in/CommentCreateUsecase.java new file mode 100644 index 0000000..c23a22b --- /dev/null +++ b/src/main/java/org/sopt/comment/application/port/in/CommentCreateUsecase.java @@ -0,0 +1,9 @@ +package org.sopt.comment.application.port.in; + +import org.sopt.comment.application.dto.command.CommentCreateCommand; +import org.sopt.comment.application.dto.result.CommentCreateResult; + +public interface CommentCreateUsecase { + + CommentCreateResult writeComment(CommentCreateCommand commentCreateCommand); +} diff --git a/src/main/java/org/sopt/comment/application/service/CommentCreateService.java b/src/main/java/org/sopt/comment/application/service/CommentCreateService.java new file mode 100644 index 0000000..1591288 --- /dev/null +++ b/src/main/java/org/sopt/comment/application/service/CommentCreateService.java @@ -0,0 +1,39 @@ +package org.sopt.comment.application.service; + +import static org.sopt.article.domain.exception.ArticleErrorCode.ARTICLE_NOT_FOUND; +import static org.sopt.member.domain.exception.MemberErrorCode.MEMBER_NOT_FOUND; + +import lombok.RequiredArgsConstructor; +import org.sopt.article.domain.exception.ArticleException; +import org.sopt.article.domain.model.Article; +import org.sopt.article.domain.port.out.ArticleRepository; +import org.sopt.comment.application.dto.command.CommentCreateCommand; +import org.sopt.comment.application.dto.result.CommentCreateResult; +import org.sopt.comment.application.port.in.CommentCreateUsecase; +import org.sopt.comment.domain.model.Comment; +import org.sopt.comment.domain.port.out.CommentRepository; +import org.sopt.member.domain.exception.MemberException; +import org.sopt.member.domain.model.Member; +import org.sopt.member.domain.port.out.MemberRepository; +import org.springframework.stereotype.Service; + +@RequiredArgsConstructor +@Service +public class CommentCreateService implements CommentCreateUsecase { + + private final MemberRepository memberRepository; + private final ArticleRepository articleRepository; + private final CommentRepository commentRepository; + + @Override + public CommentCreateResult writeComment(CommentCreateCommand commentCreateCommand) { + Member member = memberRepository.findById(commentCreateCommand.userId()) + .orElseThrow(() -> new MemberException(MEMBER_NOT_FOUND)); + Article article = articleRepository.findById(commentCreateCommand.articleId()) + .orElseThrow(() -> new ArticleException(ARTICLE_NOT_FOUND)); + Comment comment = Comment.create(article, member, commentCreateCommand.content()); + commentRepository.save(comment); + + return CommentCreateResult.create(comment); + } +} diff --git a/src/main/java/org/sopt/comment/domain/port/out/CommentRepository.java b/src/main/java/org/sopt/comment/domain/port/out/CommentRepository.java index 1462b77..663666e 100644 --- a/src/main/java/org/sopt/comment/domain/port/out/CommentRepository.java +++ b/src/main/java/org/sopt/comment/domain/port/out/CommentRepository.java @@ -1,5 +1,8 @@ package org.sopt.comment.domain.port.out; +import org.sopt.comment.domain.model.Comment; + public interface CommentRepository { + Comment save(Comment comment); } diff --git a/src/main/java/org/sopt/comment/infrastructure/jpa/CommentJpaRepositoryImpl.java b/src/main/java/org/sopt/comment/infrastructure/jpa/CommentJpaRepositoryImpl.java deleted file mode 100644 index 26971b9..0000000 --- a/src/main/java/org/sopt/comment/infrastructure/jpa/CommentJpaRepositoryImpl.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.sopt.comment.infrastructure.jpa; - -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Repository; - -@RequiredArgsConstructor -@Repository -public class CommentJpaRepositoryImpl { - - private final CommentJpaRepository jpa; - -} diff --git a/src/main/java/org/sopt/comment/infrastructure/jpa/CommentRepositoryAdapter.java b/src/main/java/org/sopt/comment/infrastructure/jpa/CommentRepositoryAdapter.java new file mode 100644 index 0000000..8771bda --- /dev/null +++ b/src/main/java/org/sopt/comment/infrastructure/jpa/CommentRepositoryAdapter.java @@ -0,0 +1,19 @@ +package org.sopt.comment.infrastructure.jpa; + +import lombok.RequiredArgsConstructor; +import org.sopt.comment.domain.model.Comment; +import org.sopt.comment.domain.port.out.CommentRepository; +import org.springframework.stereotype.Repository; + +@RequiredArgsConstructor +@Repository +public class CommentRepositoryAdapter implements CommentRepository { + + private final CommentJpaRepository jpa; + + @Override + public Comment save(Comment comment) { + return jpa.save(comment); + } + +} diff --git a/src/main/java/org/sopt/member/domain/model/Member.java b/src/main/java/org/sopt/member/domain/model/Member.java index 6adcb5f..07680ae 100644 --- a/src/main/java/org/sopt/member/domain/model/Member.java +++ b/src/main/java/org/sopt/member/domain/model/Member.java @@ -6,16 +6,12 @@ import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; -import jakarta.persistence.OneToMany; import java.time.LocalDate; -import java.util.ArrayList; -import java.util.List; import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; -import org.sopt.article.domain.model.Article; import org.sopt.global.model.BaseEntity; import org.sopt.member.domain.exception.MemberException; @@ -38,9 +34,6 @@ public class Member extends BaseEntity { private Gender gender; - @OneToMany - private List
articleList = new ArrayList<>(); - public static Member of( String name, LocalDate birthday, diff --git a/src/main/java/org/sopt/member/infrastructure/jpa/MemberJpaRepositoryImpl.java b/src/main/java/org/sopt/member/infrastructure/jpa/MemberRepositoryAdapter.java similarity index 93% rename from src/main/java/org/sopt/member/infrastructure/jpa/MemberJpaRepositoryImpl.java rename to src/main/java/org/sopt/member/infrastructure/jpa/MemberRepositoryAdapter.java index 9ef7200..0989f3b 100644 --- a/src/main/java/org/sopt/member/infrastructure/jpa/MemberJpaRepositoryImpl.java +++ b/src/main/java/org/sopt/member/infrastructure/jpa/MemberRepositoryAdapter.java @@ -9,7 +9,7 @@ @RequiredArgsConstructor @Repository -public class MemberJpaRepositoryImpl implements MemberRepository { +public class MemberRepositoryAdapter implements MemberRepository { private final MemberJpaRepository jpa; From 58df82a868ff4909530f7cd3922581f5511871f7 Mon Sep 17 00:00:00 2001 From: kaswhy Date: Wed, 24 Dec 2025 20:22:00 +0900 Subject: [PATCH 12/18] =?UTF-8?q?feat(comment):=20=EB=8C=93=EA=B8=80=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20api=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/dto/request/ArticleUpdateRequest.java | 7 ++++ .../sopt/comment/api/CommentController.java | 36 ++++++++++++++++--- .../comment/api/code/CommentSuccessCode.java | 1 + .../api/dto/request/CommentUpdateRequest.java | 12 +++++++ .../dto/response/CommentUpdateResponse.java | 7 ++++ .../api/mapper/CommentRequestMapper.java | 15 ++++++++ .../api/mapper/CommentResponseMapper.java | 12 +++++-- .../dto/command/CommentUpdateCommand.java | 9 +++++ .../dto/result/CommentUpdateResult.java | 12 +++++++ .../port/in/CommentUpdateUsecase.java | 9 +++++ .../service/CommentUpdateService.java | 29 +++++++++++++++ .../domain/exception/CommentErrorCode.java | 6 ++++ .../sopt/comment/domain/model/Comment.java | 13 +++++++ .../domain/port/out/CommentRepository.java | 4 ++- .../jpa/CommentRepositoryAdapter.java | 10 ++++-- 15 files changed, 172 insertions(+), 10 deletions(-) create mode 100644 src/main/java/org/sopt/article/api/dto/request/ArticleUpdateRequest.java create mode 100644 src/main/java/org/sopt/comment/api/dto/request/CommentUpdateRequest.java create mode 100644 src/main/java/org/sopt/comment/api/dto/response/CommentUpdateResponse.java create mode 100644 src/main/java/org/sopt/comment/application/dto/command/CommentUpdateCommand.java create mode 100644 src/main/java/org/sopt/comment/application/dto/result/CommentUpdateResult.java create mode 100644 src/main/java/org/sopt/comment/application/port/in/CommentUpdateUsecase.java create mode 100644 src/main/java/org/sopt/comment/application/service/CommentUpdateService.java diff --git a/src/main/java/org/sopt/article/api/dto/request/ArticleUpdateRequest.java b/src/main/java/org/sopt/article/api/dto/request/ArticleUpdateRequest.java new file mode 100644 index 0000000..8763dfb --- /dev/null +++ b/src/main/java/org/sopt/article/api/dto/request/ArticleUpdateRequest.java @@ -0,0 +1,7 @@ +package org.sopt.article.api.dto.request; + +public record ArticleUpdateRequest( + String content +) { + +} diff --git a/src/main/java/org/sopt/comment/api/CommentController.java b/src/main/java/org/sopt/comment/api/CommentController.java index cdfb93d..dc58548 100644 --- a/src/main/java/org/sopt/comment/api/CommentController.java +++ b/src/main/java/org/sopt/comment/api/CommentController.java @@ -1,17 +1,27 @@ package org.sopt.comment.api; import static org.sopt.comment.api.code.CommentSuccessCode.COMMENT_CREATED_SUCCESS; +import static org.sopt.comment.api.code.CommentSuccessCode.COMMENT_UPDATED_SUCCESS; +import static org.springframework.http.HttpStatus.CREATED; +import static org.springframework.http.HttpStatus.OK; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.sopt.comment.api.dto.request.CommentCreateRequest; +import org.sopt.comment.api.dto.request.CommentUpdateRequest; import org.sopt.comment.api.dto.response.CommentCreateResponse; +import org.sopt.comment.api.dto.response.CommentUpdateResponse; import org.sopt.comment.api.mapper.CommentRequestMapper; import org.sopt.comment.api.mapper.CommentResponseMapper; import org.sopt.comment.application.dto.command.CommentCreateCommand; +import org.sopt.comment.application.dto.command.CommentUpdateCommand; import org.sopt.comment.application.dto.result.CommentCreateResult; +import org.sopt.comment.application.dto.result.CommentUpdateResult; import org.sopt.comment.application.port.in.CommentCreateUsecase; +import org.sopt.comment.application.port.in.CommentUpdateUsecase; import org.sopt.global.response.dto.ApiResponseBody; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -25,9 +35,10 @@ public class CommentController { private final CommentCreateUsecase commentCreateUsecase; + private final CommentUpdateUsecase commentUpdateUsecase; @PostMapping("/articles/{articleId}/comments") - public ApiResponseBody createComment( + public ResponseEntity> createComment( @RequestHeader Long userId, @PathVariable Long articleId, @Valid @RequestBody CommentCreateRequest commentCreateRequest @@ -39,12 +50,29 @@ public ApiResponseBody createComment( CommentCreateResponse commentCreateResponse = CommentResponseMapper .toCreateResponse(commentCreateResult); - return ApiResponseBody.created(COMMENT_CREATED_SUCCESS, commentCreateResponse); + return ResponseEntity.status(CREATED) + .body(ApiResponseBody.created(COMMENT_CREATED_SUCCESS, commentCreateResponse)); } - // 수정 + // 조회 + + @PatchMapping("/comments/{commentId}") + public ResponseEntity> updateComment ( + @RequestHeader Long userId, + @PathVariable Long commentId, + @Valid @RequestBody CommentUpdateRequest commentUpdateRequest + ) { + CommentUpdateCommand commentUpdateCommand = CommentRequestMapper + .toCommentUpdateCommand(userId, commentId, commentUpdateRequest); + CommentUpdateResult commentUpdateResult = commentUpdateUsecase + .updateComment(commentUpdateCommand); + CommentUpdateResponse commentUpdateResponse = CommentResponseMapper + .toUpdateResponse(commentUpdateResult); + + return ResponseEntity.status(OK) + .body(ApiResponseBody.ok(COMMENT_UPDATED_SUCCESS, commentUpdateResponse)); + } // 삭제 - // 조회 } diff --git a/src/main/java/org/sopt/comment/api/code/CommentSuccessCode.java b/src/main/java/org/sopt/comment/api/code/CommentSuccessCode.java index 92bae49..1147719 100644 --- a/src/main/java/org/sopt/comment/api/code/CommentSuccessCode.java +++ b/src/main/java/org/sopt/comment/api/code/CommentSuccessCode.java @@ -9,6 +9,7 @@ public enum CommentSuccessCode implements SuccessCode { // 200 OK + COMMENT_UPDATED_SUCCESS(200, "COMMENT_200_001", "성공적으로 댓글을 수정했습니다."), // 201 CREATED COMMENT_CREATED_SUCCESS(201, "COMMENT_201_001", "성공적으로 댓글을 작성했습니다."), diff --git a/src/main/java/org/sopt/comment/api/dto/request/CommentUpdateRequest.java b/src/main/java/org/sopt/comment/api/dto/request/CommentUpdateRequest.java new file mode 100644 index 0000000..f8eb18d --- /dev/null +++ b/src/main/java/org/sopt/comment/api/dto/request/CommentUpdateRequest.java @@ -0,0 +1,12 @@ +package org.sopt.comment.api.dto.request; + +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.NotBlank; + +public record CommentUpdateRequest( + @NotBlank + @Max(300) + String content +) { + +} diff --git a/src/main/java/org/sopt/comment/api/dto/response/CommentUpdateResponse.java b/src/main/java/org/sopt/comment/api/dto/response/CommentUpdateResponse.java new file mode 100644 index 0000000..502fe39 --- /dev/null +++ b/src/main/java/org/sopt/comment/api/dto/response/CommentUpdateResponse.java @@ -0,0 +1,7 @@ +package org.sopt.comment.api.dto.response; + +public record CommentUpdateResponse( + Long commentId +) { + +} diff --git a/src/main/java/org/sopt/comment/api/mapper/CommentRequestMapper.java b/src/main/java/org/sopt/comment/api/mapper/CommentRequestMapper.java index 6ffcfa7..33dcca2 100644 --- a/src/main/java/org/sopt/comment/api/mapper/CommentRequestMapper.java +++ b/src/main/java/org/sopt/comment/api/mapper/CommentRequestMapper.java @@ -1,7 +1,9 @@ package org.sopt.comment.api.mapper; import org.sopt.comment.api.dto.request.CommentCreateRequest; +import org.sopt.comment.api.dto.request.CommentUpdateRequest; import org.sopt.comment.application.dto.command.CommentCreateCommand; +import org.sopt.comment.application.dto.command.CommentUpdateCommand; public class CommentRequestMapper { @@ -16,4 +18,17 @@ public static CommentCreateCommand toCommentCreateCommand( commentCreateRequest.content() ); } + + public static CommentUpdateCommand toCommentUpdateCommand( + Long userId, + Long articleId, + CommentUpdateRequest commentUpdateRequest + ) { + return new CommentUpdateCommand( + userId, + articleId, + commentUpdateRequest.content() + ); + } + } diff --git a/src/main/java/org/sopt/comment/api/mapper/CommentResponseMapper.java b/src/main/java/org/sopt/comment/api/mapper/CommentResponseMapper.java index cd60105..2b375c1 100644 --- a/src/main/java/org/sopt/comment/api/mapper/CommentResponseMapper.java +++ b/src/main/java/org/sopt/comment/api/mapper/CommentResponseMapper.java @@ -1,15 +1,21 @@ package org.sopt.comment.api.mapper; import org.sopt.comment.api.dto.response.CommentCreateResponse; +import org.sopt.comment.api.dto.response.CommentUpdateResponse; import org.sopt.comment.application.dto.result.CommentCreateResult; +import org.sopt.comment.application.dto.result.CommentUpdateResult; public class CommentResponseMapper { public static CommentCreateResponse toCreateResponse( CommentCreateResult commentCreateResult ) { - return new CommentCreateResponse( - commentCreateResult.commentId() - ); + return new CommentCreateResponse(commentCreateResult.commentId()); + } + + public static CommentUpdateResponse toUpdateResponse( + CommentUpdateResult commentUpdateResult + ) { + return new CommentUpdateResponse(commentUpdateResult.commentId()); } } diff --git a/src/main/java/org/sopt/comment/application/dto/command/CommentUpdateCommand.java b/src/main/java/org/sopt/comment/application/dto/command/CommentUpdateCommand.java new file mode 100644 index 0000000..7481f3e --- /dev/null +++ b/src/main/java/org/sopt/comment/application/dto/command/CommentUpdateCommand.java @@ -0,0 +1,9 @@ +package org.sopt.comment.application.dto.command; + +public record CommentUpdateCommand( + Long userId, + Long commentId, + String content +) { + +} diff --git a/src/main/java/org/sopt/comment/application/dto/result/CommentUpdateResult.java b/src/main/java/org/sopt/comment/application/dto/result/CommentUpdateResult.java new file mode 100644 index 0000000..a53e4f3 --- /dev/null +++ b/src/main/java/org/sopt/comment/application/dto/result/CommentUpdateResult.java @@ -0,0 +1,12 @@ +package org.sopt.comment.application.dto.result; + +import org.sopt.comment.domain.model.Comment; + +public record CommentUpdateResult( + Long commentId +) { + + public static CommentUpdateResult create(Comment comment) { + return new CommentUpdateResult(comment.getId()); + } +} diff --git a/src/main/java/org/sopt/comment/application/port/in/CommentUpdateUsecase.java b/src/main/java/org/sopt/comment/application/port/in/CommentUpdateUsecase.java new file mode 100644 index 0000000..7d49d8b --- /dev/null +++ b/src/main/java/org/sopt/comment/application/port/in/CommentUpdateUsecase.java @@ -0,0 +1,9 @@ +package org.sopt.comment.application.port.in; + +import org.sopt.comment.application.dto.command.CommentUpdateCommand; +import org.sopt.comment.application.dto.result.CommentUpdateResult; + +public interface CommentUpdateUsecase { + + CommentUpdateResult updateComment(CommentUpdateCommand commentUpdateCommand); +} diff --git a/src/main/java/org/sopt/comment/application/service/CommentUpdateService.java b/src/main/java/org/sopt/comment/application/service/CommentUpdateService.java new file mode 100644 index 0000000..fb85b00 --- /dev/null +++ b/src/main/java/org/sopt/comment/application/service/CommentUpdateService.java @@ -0,0 +1,29 @@ +package org.sopt.comment.application.service; + +import static org.sopt.comment.domain.exception.CommentErrorCode.COMMENT_NOT_FOUND; + +import lombok.RequiredArgsConstructor; +import org.sopt.comment.application.dto.command.CommentUpdateCommand; +import org.sopt.comment.application.dto.result.CommentUpdateResult; +import org.sopt.comment.application.port.in.CommentUpdateUsecase; +import org.sopt.comment.domain.exception.CommentException; +import org.sopt.comment.domain.model.Comment; +import org.sopt.comment.domain.port.out.CommentRepository; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +@RequiredArgsConstructor +@Service +public class CommentUpdateService implements CommentUpdateUsecase { + + private final CommentRepository commentRepository; + + public CommentUpdateResult updateComment(CommentUpdateCommand commentUpdateCommand) { + Comment comment = commentRepository.findById(commentUpdateCommand.commentId()) + .orElseThrow(() -> new CommentException(COMMENT_NOT_FOUND)); + comment.update(commentUpdateCommand.userId(), commentUpdateCommand.content()); + + return CommentUpdateResult.create(comment); + } +} diff --git a/src/main/java/org/sopt/comment/domain/exception/CommentErrorCode.java b/src/main/java/org/sopt/comment/domain/exception/CommentErrorCode.java index 9e1b8b3..567f1b9 100644 --- a/src/main/java/org/sopt/comment/domain/exception/CommentErrorCode.java +++ b/src/main/java/org/sopt/comment/domain/exception/CommentErrorCode.java @@ -14,6 +14,12 @@ public enum CommentErrorCode implements ErrorCode { CONTENT_REQUIRED(400, "COMMENT_400_003", "댓글 본문이 필요합니다."), CONTENT_TOO_LONG(400, "COMMENT_400_004", "댓글 본문 길이는 300자 이하여야 합니다."), + // 403 FORBIDDEN + COMMENT_EDIT_FORBIDDEN(403, "COMMENT_403_001", "댓글 수정 권한이 없습니다."), + + // 404 NOT FOUND + COMMENT_NOT_FOUND(404, "COMMENT_404_001", "해당 댓글이 존재하지 않습니다."), + ; private final int status; diff --git a/src/main/java/org/sopt/comment/domain/model/Comment.java b/src/main/java/org/sopt/comment/domain/model/Comment.java index 11f40df..d11dd70 100644 --- a/src/main/java/org/sopt/comment/domain/model/Comment.java +++ b/src/main/java/org/sopt/comment/domain/model/Comment.java @@ -1,6 +1,7 @@ package org.sopt.comment.domain.model; import static org.sopt.comment.domain.exception.CommentErrorCode.ARTICLE_REQUIRED; +import static org.sopt.comment.domain.exception.CommentErrorCode.COMMENT_EDIT_FORBIDDEN; import static org.sopt.comment.domain.exception.CommentErrorCode.CONTENT_REQUIRED; import static org.sopt.comment.domain.exception.CommentErrorCode.CONTENT_TOO_LONG; import static org.sopt.comment.domain.exception.CommentErrorCode.MEMBER_REQUIRED; @@ -60,6 +61,18 @@ public static Comment create(Article article, Member author, String content) { .build(); } + public void update(Long userId, String content) { + validateCanEdit(userId); + validateContent(content); + this.content = content; + } + + private void validateCanEdit(Long userId) { + if(!this.author.getId().equals(userId)) { + throw new CommentException(COMMENT_EDIT_FORBIDDEN); + } + } + private void validateComment(Article article, Member author, String content) { validateArticle(article); validateAuthor(author); diff --git a/src/main/java/org/sopt/comment/domain/port/out/CommentRepository.java b/src/main/java/org/sopt/comment/domain/port/out/CommentRepository.java index 663666e..cf47c6b 100644 --- a/src/main/java/org/sopt/comment/domain/port/out/CommentRepository.java +++ b/src/main/java/org/sopt/comment/domain/port/out/CommentRepository.java @@ -1,8 +1,10 @@ package org.sopt.comment.domain.port.out; +import java.util.Optional; import org.sopt.comment.domain.model.Comment; public interface CommentRepository { - Comment save(Comment comment); + void save(Comment comment); + Optional findById(Long id); } diff --git a/src/main/java/org/sopt/comment/infrastructure/jpa/CommentRepositoryAdapter.java b/src/main/java/org/sopt/comment/infrastructure/jpa/CommentRepositoryAdapter.java index 8771bda..2f1d669 100644 --- a/src/main/java/org/sopt/comment/infrastructure/jpa/CommentRepositoryAdapter.java +++ b/src/main/java/org/sopt/comment/infrastructure/jpa/CommentRepositoryAdapter.java @@ -1,5 +1,6 @@ package org.sopt.comment.infrastructure.jpa; +import java.util.Optional; import lombok.RequiredArgsConstructor; import org.sopt.comment.domain.model.Comment; import org.sopt.comment.domain.port.out.CommentRepository; @@ -12,8 +13,13 @@ public class CommentRepositoryAdapter implements CommentRepository { private final CommentJpaRepository jpa; @Override - public Comment save(Comment comment) { - return jpa.save(comment); + public void save(Comment comment) { + jpa.save(comment); + } + + @Override + public Optional findById(Long id) { + return jpa.findById(id); } } From e2d17892f768f88f778e2394e0ca52a12e1aa9b7 Mon Sep 17 00:00:00 2001 From: kaswhy Date: Wed, 24 Dec 2025 20:31:43 +0900 Subject: [PATCH 13/18] =?UTF-8?q?refactor(comment):=20=EB=8C=93=EA=B8=80?= =?UTF-8?q?=20=EC=83=9D=EC=84=B1=20=EC=9C=A0=EC=8A=A4=EC=BC=80=EC=9D=B4?= =?UTF-8?q?=EC=8A=A4=20=ED=8A=B8=EB=9E=9C=EC=9E=AD=EC=85=98=20=EA=B2=BD?= =?UTF-8?q?=EA=B3=84=20=EB=AA=85=EC=8B=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sopt/comment/application/service/CommentCreateService.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/org/sopt/comment/application/service/CommentCreateService.java b/src/main/java/org/sopt/comment/application/service/CommentCreateService.java index 1591288..0b7ef99 100644 --- a/src/main/java/org/sopt/comment/application/service/CommentCreateService.java +++ b/src/main/java/org/sopt/comment/application/service/CommentCreateService.java @@ -16,7 +16,9 @@ import org.sopt.member.domain.model.Member; import org.sopt.member.domain.port.out.MemberRepository; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +@Transactional @RequiredArgsConstructor @Service public class CommentCreateService implements CommentCreateUsecase { From f6af01c78bc8bafae777d0da15c36fc0434b33d0 Mon Sep 17 00:00:00 2001 From: kaswhy Date: Wed, 24 Dec 2025 20:35:10 +0900 Subject: [PATCH 14/18] =?UTF-8?q?refactor(comment):=20=EB=8C=93=EA=B8=80?= =?UTF-8?q?=20=EA=B4=80=EB=A0=A8=20=EC=84=9C=EB=B9=84=EC=8A=A4=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=20=EB=84=A4=EC=9D=B4=EB=B0=8D=20=EB=8F=99?= =?UTF-8?q?=EC=82=AC=20=EB=AA=85=EC=82=AC=20=EC=88=9C=EC=84=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/sopt/comment/api/CommentController.java | 16 ++++++++++++---- ...ateUsecase.java => CreateCommentUsecase.java} | 2 +- ...ateUsecase.java => UpdateCommentUsecase.java} | 2 +- ...ateService.java => CreateCommentService.java} | 4 ++-- ...ateService.java => UpdateCommentService.java} | 4 ++-- 5 files changed, 18 insertions(+), 10 deletions(-) rename src/main/java/org/sopt/comment/application/port/in/{CommentCreateUsecase.java => CreateCommentUsecase.java} (87%) rename src/main/java/org/sopt/comment/application/port/in/{CommentUpdateUsecase.java => UpdateCommentUsecase.java} (87%) rename src/main/java/org/sopt/comment/application/service/{CommentCreateService.java => CreateCommentService.java} (93%) rename src/main/java/org/sopt/comment/application/service/{CommentUpdateService.java => UpdateCommentService.java} (89%) diff --git a/src/main/java/org/sopt/comment/api/CommentController.java b/src/main/java/org/sopt/comment/api/CommentController.java index dc58548..2a42cd7 100644 --- a/src/main/java/org/sopt/comment/api/CommentController.java +++ b/src/main/java/org/sopt/comment/api/CommentController.java @@ -17,10 +17,11 @@ import org.sopt.comment.application.dto.command.CommentUpdateCommand; import org.sopt.comment.application.dto.result.CommentCreateResult; import org.sopt.comment.application.dto.result.CommentUpdateResult; -import org.sopt.comment.application.port.in.CommentCreateUsecase; -import org.sopt.comment.application.port.in.CommentUpdateUsecase; +import org.sopt.comment.application.port.in.CreateCommentUsecase; +import org.sopt.comment.application.port.in.UpdateCommentUsecase; import org.sopt.global.response.dto.ApiResponseBody; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; @@ -34,8 +35,8 @@ @RestController public class CommentController { - private final CommentCreateUsecase commentCreateUsecase; - private final CommentUpdateUsecase commentUpdateUsecase; + private final CreateCommentUsecase commentCreateUsecase; + private final UpdateCommentUsecase commentUpdateUsecase; @PostMapping("/articles/{articleId}/comments") public ResponseEntity> createComment( @@ -55,6 +56,13 @@ public ResponseEntity> createCommen } // 조회 + @GetMapping("/articles/{articleId}") + public ResponseEntity> getCommentList ( + @RequestHeader Long userId, + @PathVariable Long articleId + ) { + return null; + } @PatchMapping("/comments/{commentId}") public ResponseEntity> updateComment ( diff --git a/src/main/java/org/sopt/comment/application/port/in/CommentCreateUsecase.java b/src/main/java/org/sopt/comment/application/port/in/CreateCommentUsecase.java similarity index 87% rename from src/main/java/org/sopt/comment/application/port/in/CommentCreateUsecase.java rename to src/main/java/org/sopt/comment/application/port/in/CreateCommentUsecase.java index c23a22b..72c57a0 100644 --- a/src/main/java/org/sopt/comment/application/port/in/CommentCreateUsecase.java +++ b/src/main/java/org/sopt/comment/application/port/in/CreateCommentUsecase.java @@ -3,7 +3,7 @@ import org.sopt.comment.application.dto.command.CommentCreateCommand; import org.sopt.comment.application.dto.result.CommentCreateResult; -public interface CommentCreateUsecase { +public interface CreateCommentUsecase { CommentCreateResult writeComment(CommentCreateCommand commentCreateCommand); } diff --git a/src/main/java/org/sopt/comment/application/port/in/CommentUpdateUsecase.java b/src/main/java/org/sopt/comment/application/port/in/UpdateCommentUsecase.java similarity index 87% rename from src/main/java/org/sopt/comment/application/port/in/CommentUpdateUsecase.java rename to src/main/java/org/sopt/comment/application/port/in/UpdateCommentUsecase.java index 7d49d8b..6198c01 100644 --- a/src/main/java/org/sopt/comment/application/port/in/CommentUpdateUsecase.java +++ b/src/main/java/org/sopt/comment/application/port/in/UpdateCommentUsecase.java @@ -3,7 +3,7 @@ import org.sopt.comment.application.dto.command.CommentUpdateCommand; import org.sopt.comment.application.dto.result.CommentUpdateResult; -public interface CommentUpdateUsecase { +public interface UpdateCommentUsecase { CommentUpdateResult updateComment(CommentUpdateCommand commentUpdateCommand); } diff --git a/src/main/java/org/sopt/comment/application/service/CommentCreateService.java b/src/main/java/org/sopt/comment/application/service/CreateCommentService.java similarity index 93% rename from src/main/java/org/sopt/comment/application/service/CommentCreateService.java rename to src/main/java/org/sopt/comment/application/service/CreateCommentService.java index 0b7ef99..c09684f 100644 --- a/src/main/java/org/sopt/comment/application/service/CommentCreateService.java +++ b/src/main/java/org/sopt/comment/application/service/CreateCommentService.java @@ -9,7 +9,7 @@ import org.sopt.article.domain.port.out.ArticleRepository; import org.sopt.comment.application.dto.command.CommentCreateCommand; import org.sopt.comment.application.dto.result.CommentCreateResult; -import org.sopt.comment.application.port.in.CommentCreateUsecase; +import org.sopt.comment.application.port.in.CreateCommentUsecase; import org.sopt.comment.domain.model.Comment; import org.sopt.comment.domain.port.out.CommentRepository; import org.sopt.member.domain.exception.MemberException; @@ -21,7 +21,7 @@ @Transactional @RequiredArgsConstructor @Service -public class CommentCreateService implements CommentCreateUsecase { +public class CreateCommentService implements CreateCommentUsecase { private final MemberRepository memberRepository; private final ArticleRepository articleRepository; diff --git a/src/main/java/org/sopt/comment/application/service/CommentUpdateService.java b/src/main/java/org/sopt/comment/application/service/UpdateCommentService.java similarity index 89% rename from src/main/java/org/sopt/comment/application/service/CommentUpdateService.java rename to src/main/java/org/sopt/comment/application/service/UpdateCommentService.java index fb85b00..a12fe7b 100644 --- a/src/main/java/org/sopt/comment/application/service/CommentUpdateService.java +++ b/src/main/java/org/sopt/comment/application/service/UpdateCommentService.java @@ -5,7 +5,7 @@ import lombok.RequiredArgsConstructor; import org.sopt.comment.application.dto.command.CommentUpdateCommand; import org.sopt.comment.application.dto.result.CommentUpdateResult; -import org.sopt.comment.application.port.in.CommentUpdateUsecase; +import org.sopt.comment.application.port.in.UpdateCommentUsecase; import org.sopt.comment.domain.exception.CommentException; import org.sopt.comment.domain.model.Comment; import org.sopt.comment.domain.port.out.CommentRepository; @@ -15,7 +15,7 @@ @Transactional @RequiredArgsConstructor @Service -public class CommentUpdateService implements CommentUpdateUsecase { +public class UpdateCommentService implements UpdateCommentUsecase { private final CommentRepository commentRepository; From fc883052249b510880b6e8f5c05ded97b69266f2 Mon Sep 17 00:00:00 2001 From: kaswhy Date: Wed, 24 Dec 2025 20:37:06 +0900 Subject: [PATCH 15/18] =?UTF-8?q?refactor(comment):=20=EB=8C=93=EA=B8=80?= =?UTF-8?q?=20=EA=B4=80=EB=A0=A8=20=EC=84=9C=EB=B9=84=EC=8A=A4=EB=8B=A8=20?= =?UTF-8?q?DTO=20=EB=84=A4=EC=9D=B4=EB=B0=8D=20=EB=8F=99=EC=82=AC=20?= =?UTF-8?q?=EB=AA=85=EC=82=AC=20=EC=88=9C=EC=84=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../comment/api/mapper/CommentRequestMapper.java | 12 ++++++------ .../comment/api/mapper/CommentResponseMapper.java | 8 ++++---- ...reateCommand.java => CreateCommentCommand.java} | 2 +- ...pdateCommand.java => UpdateCommentCommand.java} | 2 +- .../dto/result/CommentCreateResult.java | 12 ------------ .../dto/result/CommentUpdateResult.java | 12 ------------ .../dto/result/CreateCommentResult.java | 12 ++++++++++++ .../dto/result/UpdateCommentResult.java | 12 ++++++++++++ .../application/port/in/CreateCommentUsecase.java | 6 +++--- .../application/port/in/UpdateCommentUsecase.java | 6 +++--- .../application/service/CreateCommentService.java | 14 +++++++------- .../application/service/UpdateCommentService.java | 12 ++++++------ 12 files changed, 55 insertions(+), 55 deletions(-) rename src/main/java/org/sopt/comment/application/dto/command/{CommentCreateCommand.java => CreateCommentCommand.java} (76%) rename src/main/java/org/sopt/comment/application/dto/command/{CommentUpdateCommand.java => UpdateCommentCommand.java} (76%) delete mode 100644 src/main/java/org/sopt/comment/application/dto/result/CommentCreateResult.java delete mode 100644 src/main/java/org/sopt/comment/application/dto/result/CommentUpdateResult.java create mode 100644 src/main/java/org/sopt/comment/application/dto/result/CreateCommentResult.java create mode 100644 src/main/java/org/sopt/comment/application/dto/result/UpdateCommentResult.java diff --git a/src/main/java/org/sopt/comment/api/mapper/CommentRequestMapper.java b/src/main/java/org/sopt/comment/api/mapper/CommentRequestMapper.java index 33dcca2..5be58ed 100644 --- a/src/main/java/org/sopt/comment/api/mapper/CommentRequestMapper.java +++ b/src/main/java/org/sopt/comment/api/mapper/CommentRequestMapper.java @@ -2,29 +2,29 @@ import org.sopt.comment.api.dto.request.CommentCreateRequest; import org.sopt.comment.api.dto.request.CommentUpdateRequest; -import org.sopt.comment.application.dto.command.CommentCreateCommand; -import org.sopt.comment.application.dto.command.CommentUpdateCommand; +import org.sopt.comment.application.dto.command.CreateCommentCommand; +import org.sopt.comment.application.dto.command.UpdateCommentCommand; public class CommentRequestMapper { - public static CommentCreateCommand toCommentCreateCommand( + public static CreateCommentCommand toCommentCreateCommand( Long userId, Long articleId, CommentCreateRequest commentCreateRequest ) { - return new CommentCreateCommand( + return new CreateCommentCommand( userId, articleId, commentCreateRequest.content() ); } - public static CommentUpdateCommand toCommentUpdateCommand( + public static UpdateCommentCommand toCommentUpdateCommand( Long userId, Long articleId, CommentUpdateRequest commentUpdateRequest ) { - return new CommentUpdateCommand( + return new UpdateCommentCommand( userId, articleId, commentUpdateRequest.content() diff --git a/src/main/java/org/sopt/comment/api/mapper/CommentResponseMapper.java b/src/main/java/org/sopt/comment/api/mapper/CommentResponseMapper.java index 2b375c1..11eb56b 100644 --- a/src/main/java/org/sopt/comment/api/mapper/CommentResponseMapper.java +++ b/src/main/java/org/sopt/comment/api/mapper/CommentResponseMapper.java @@ -2,19 +2,19 @@ import org.sopt.comment.api.dto.response.CommentCreateResponse; import org.sopt.comment.api.dto.response.CommentUpdateResponse; -import org.sopt.comment.application.dto.result.CommentCreateResult; -import org.sopt.comment.application.dto.result.CommentUpdateResult; +import org.sopt.comment.application.dto.result.CreateCommentResult; +import org.sopt.comment.application.dto.result.UpdateCommentResult; public class CommentResponseMapper { public static CommentCreateResponse toCreateResponse( - CommentCreateResult commentCreateResult + CreateCommentResult commentCreateResult ) { return new CommentCreateResponse(commentCreateResult.commentId()); } public static CommentUpdateResponse toUpdateResponse( - CommentUpdateResult commentUpdateResult + UpdateCommentResult commentUpdateResult ) { return new CommentUpdateResponse(commentUpdateResult.commentId()); } diff --git a/src/main/java/org/sopt/comment/application/dto/command/CommentCreateCommand.java b/src/main/java/org/sopt/comment/application/dto/command/CreateCommentCommand.java similarity index 76% rename from src/main/java/org/sopt/comment/application/dto/command/CommentCreateCommand.java rename to src/main/java/org/sopt/comment/application/dto/command/CreateCommentCommand.java index cf88883..d336355 100644 --- a/src/main/java/org/sopt/comment/application/dto/command/CommentCreateCommand.java +++ b/src/main/java/org/sopt/comment/application/dto/command/CreateCommentCommand.java @@ -1,6 +1,6 @@ package org.sopt.comment.application.dto.command; -public record CommentCreateCommand( +public record CreateCommentCommand( Long userId, Long articleId, String content diff --git a/src/main/java/org/sopt/comment/application/dto/command/CommentUpdateCommand.java b/src/main/java/org/sopt/comment/application/dto/command/UpdateCommentCommand.java similarity index 76% rename from src/main/java/org/sopt/comment/application/dto/command/CommentUpdateCommand.java rename to src/main/java/org/sopt/comment/application/dto/command/UpdateCommentCommand.java index 7481f3e..c360633 100644 --- a/src/main/java/org/sopt/comment/application/dto/command/CommentUpdateCommand.java +++ b/src/main/java/org/sopt/comment/application/dto/command/UpdateCommentCommand.java @@ -1,6 +1,6 @@ package org.sopt.comment.application.dto.command; -public record CommentUpdateCommand( +public record UpdateCommentCommand( Long userId, Long commentId, String content diff --git a/src/main/java/org/sopt/comment/application/dto/result/CommentCreateResult.java b/src/main/java/org/sopt/comment/application/dto/result/CommentCreateResult.java deleted file mode 100644 index 9e67ebe..0000000 --- a/src/main/java/org/sopt/comment/application/dto/result/CommentCreateResult.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.sopt.comment.application.dto.result; - -import org.sopt.comment.domain.model.Comment; - -public record CommentCreateResult( - Long commentId -) { - - public static CommentCreateResult create(Comment comment) { - return new CommentCreateResult(comment.getId()); - } -} diff --git a/src/main/java/org/sopt/comment/application/dto/result/CommentUpdateResult.java b/src/main/java/org/sopt/comment/application/dto/result/CommentUpdateResult.java deleted file mode 100644 index a53e4f3..0000000 --- a/src/main/java/org/sopt/comment/application/dto/result/CommentUpdateResult.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.sopt.comment.application.dto.result; - -import org.sopt.comment.domain.model.Comment; - -public record CommentUpdateResult( - Long commentId -) { - - public static CommentUpdateResult create(Comment comment) { - return new CommentUpdateResult(comment.getId()); - } -} diff --git a/src/main/java/org/sopt/comment/application/dto/result/CreateCommentResult.java b/src/main/java/org/sopt/comment/application/dto/result/CreateCommentResult.java new file mode 100644 index 0000000..20456d3 --- /dev/null +++ b/src/main/java/org/sopt/comment/application/dto/result/CreateCommentResult.java @@ -0,0 +1,12 @@ +package org.sopt.comment.application.dto.result; + +import org.sopt.comment.domain.model.Comment; + +public record CreateCommentResult( + Long commentId +) { + + public static CreateCommentResult create(Comment comment) { + return new CreateCommentResult(comment.getId()); + } +} diff --git a/src/main/java/org/sopt/comment/application/dto/result/UpdateCommentResult.java b/src/main/java/org/sopt/comment/application/dto/result/UpdateCommentResult.java new file mode 100644 index 0000000..50192fe --- /dev/null +++ b/src/main/java/org/sopt/comment/application/dto/result/UpdateCommentResult.java @@ -0,0 +1,12 @@ +package org.sopt.comment.application.dto.result; + +import org.sopt.comment.domain.model.Comment; + +public record UpdateCommentResult( + Long commentId +) { + + public static UpdateCommentResult create(Comment comment) { + return new UpdateCommentResult(comment.getId()); + } +} diff --git a/src/main/java/org/sopt/comment/application/port/in/CreateCommentUsecase.java b/src/main/java/org/sopt/comment/application/port/in/CreateCommentUsecase.java index 72c57a0..3e27cbf 100644 --- a/src/main/java/org/sopt/comment/application/port/in/CreateCommentUsecase.java +++ b/src/main/java/org/sopt/comment/application/port/in/CreateCommentUsecase.java @@ -1,9 +1,9 @@ package org.sopt.comment.application.port.in; -import org.sopt.comment.application.dto.command.CommentCreateCommand; -import org.sopt.comment.application.dto.result.CommentCreateResult; +import org.sopt.comment.application.dto.command.CreateCommentCommand; +import org.sopt.comment.application.dto.result.CreateCommentResult; public interface CreateCommentUsecase { - CommentCreateResult writeComment(CommentCreateCommand commentCreateCommand); + CreateCommentResult writeComment(CreateCommentCommand createCommentCommand); } diff --git a/src/main/java/org/sopt/comment/application/port/in/UpdateCommentUsecase.java b/src/main/java/org/sopt/comment/application/port/in/UpdateCommentUsecase.java index 6198c01..2513cf4 100644 --- a/src/main/java/org/sopt/comment/application/port/in/UpdateCommentUsecase.java +++ b/src/main/java/org/sopt/comment/application/port/in/UpdateCommentUsecase.java @@ -1,9 +1,9 @@ package org.sopt.comment.application.port.in; -import org.sopt.comment.application.dto.command.CommentUpdateCommand; -import org.sopt.comment.application.dto.result.CommentUpdateResult; +import org.sopt.comment.application.dto.command.UpdateCommentCommand; +import org.sopt.comment.application.dto.result.UpdateCommentResult; public interface UpdateCommentUsecase { - CommentUpdateResult updateComment(CommentUpdateCommand commentUpdateCommand); + UpdateCommentResult updateComment(UpdateCommentCommand updateCommentCommand); } diff --git a/src/main/java/org/sopt/comment/application/service/CreateCommentService.java b/src/main/java/org/sopt/comment/application/service/CreateCommentService.java index c09684f..6829ce9 100644 --- a/src/main/java/org/sopt/comment/application/service/CreateCommentService.java +++ b/src/main/java/org/sopt/comment/application/service/CreateCommentService.java @@ -7,8 +7,8 @@ import org.sopt.article.domain.exception.ArticleException; import org.sopt.article.domain.model.Article; import org.sopt.article.domain.port.out.ArticleRepository; -import org.sopt.comment.application.dto.command.CommentCreateCommand; -import org.sopt.comment.application.dto.result.CommentCreateResult; +import org.sopt.comment.application.dto.command.CreateCommentCommand; +import org.sopt.comment.application.dto.result.CreateCommentResult; import org.sopt.comment.application.port.in.CreateCommentUsecase; import org.sopt.comment.domain.model.Comment; import org.sopt.comment.domain.port.out.CommentRepository; @@ -28,14 +28,14 @@ public class CreateCommentService implements CreateCommentUsecase { private final CommentRepository commentRepository; @Override - public CommentCreateResult writeComment(CommentCreateCommand commentCreateCommand) { - Member member = memberRepository.findById(commentCreateCommand.userId()) + public CreateCommentResult writeComment(CreateCommentCommand createCommentCommand) { + Member member = memberRepository.findById(createCommentCommand.userId()) .orElseThrow(() -> new MemberException(MEMBER_NOT_FOUND)); - Article article = articleRepository.findById(commentCreateCommand.articleId()) + Article article = articleRepository.findById(createCommentCommand.articleId()) .orElseThrow(() -> new ArticleException(ARTICLE_NOT_FOUND)); - Comment comment = Comment.create(article, member, commentCreateCommand.content()); + Comment comment = Comment.create(article, member, createCommentCommand.content()); commentRepository.save(comment); - return CommentCreateResult.create(comment); + return CreateCommentResult.create(comment); } } diff --git a/src/main/java/org/sopt/comment/application/service/UpdateCommentService.java b/src/main/java/org/sopt/comment/application/service/UpdateCommentService.java index a12fe7b..68ce388 100644 --- a/src/main/java/org/sopt/comment/application/service/UpdateCommentService.java +++ b/src/main/java/org/sopt/comment/application/service/UpdateCommentService.java @@ -3,8 +3,8 @@ import static org.sopt.comment.domain.exception.CommentErrorCode.COMMENT_NOT_FOUND; import lombok.RequiredArgsConstructor; -import org.sopt.comment.application.dto.command.CommentUpdateCommand; -import org.sopt.comment.application.dto.result.CommentUpdateResult; +import org.sopt.comment.application.dto.command.UpdateCommentCommand; +import org.sopt.comment.application.dto.result.UpdateCommentResult; import org.sopt.comment.application.port.in.UpdateCommentUsecase; import org.sopt.comment.domain.exception.CommentException; import org.sopt.comment.domain.model.Comment; @@ -19,11 +19,11 @@ public class UpdateCommentService implements UpdateCommentUsecase { private final CommentRepository commentRepository; - public CommentUpdateResult updateComment(CommentUpdateCommand commentUpdateCommand) { - Comment comment = commentRepository.findById(commentUpdateCommand.commentId()) + public UpdateCommentResult updateComment(UpdateCommentCommand updateCommentCommand) { + Comment comment = commentRepository.findById(updateCommentCommand.commentId()) .orElseThrow(() -> new CommentException(COMMENT_NOT_FOUND)); - comment.update(commentUpdateCommand.userId(), commentUpdateCommand.content()); + comment.update(updateCommentCommand.userId(), updateCommentCommand.content()); - return CommentUpdateResult.create(comment); + return UpdateCommentResult.create(comment); } } From e794fb5259d7c0cca90e3e46a0f8cf5dd44eeacb Mon Sep 17 00:00:00 2001 From: kaswhy Date: Wed, 24 Dec 2025 20:39:20 +0900 Subject: [PATCH 16/18] =?UTF-8?q?refactor(comment):=20=EB=8C=93=EA=B8=80?= =?UTF-8?q?=20=EA=B4=80=EB=A0=A8=20=EC=BB=A8=ED=8A=B8=EB=A1=A4=EB=9F=AC?= =?UTF-8?q?=EB=8B=A8=20DTO=20=EB=84=A4=EC=9D=B4=EB=B0=8D=20=EB=8F=99?= =?UTF-8?q?=EC=82=AC=20=EB=AA=85=EC=82=AC=20=EC=88=9C=EC=84=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...tCreateRequest.java => CreateCommentRequest.java} | 2 +- ...tUpdateRequest.java => UpdateCommentRequest.java} | 2 +- ...reateResponse.java => CreateCommentResponse.java} | 2 +- ...pdateResponse.java => UpdateCommentResponse.java} | 2 +- .../comment/api/mapper/CommentRequestMapper.java | 12 ++++++------ .../comment/api/mapper/CommentResponseMapper.java | 12 ++++++------ 6 files changed, 16 insertions(+), 16 deletions(-) rename src/main/java/org/sopt/comment/api/dto/request/{CommentCreateRequest.java => CreateCommentRequest.java} (84%) rename src/main/java/org/sopt/comment/api/dto/request/{CommentUpdateRequest.java => UpdateCommentRequest.java} (84%) rename src/main/java/org/sopt/comment/api/dto/response/{CommentCreateResponse.java => CreateCommentResponse.java} (65%) rename src/main/java/org/sopt/comment/api/dto/response/{CommentUpdateResponse.java => UpdateCommentResponse.java} (65%) diff --git a/src/main/java/org/sopt/comment/api/dto/request/CommentCreateRequest.java b/src/main/java/org/sopt/comment/api/dto/request/CreateCommentRequest.java similarity index 84% rename from src/main/java/org/sopt/comment/api/dto/request/CommentCreateRequest.java rename to src/main/java/org/sopt/comment/api/dto/request/CreateCommentRequest.java index b06637f..711800c 100644 --- a/src/main/java/org/sopt/comment/api/dto/request/CommentCreateRequest.java +++ b/src/main/java/org/sopt/comment/api/dto/request/CreateCommentRequest.java @@ -3,7 +3,7 @@ import jakarta.validation.constraints.Max; import jakarta.validation.constraints.NotBlank; -public record CommentCreateRequest( +public record CreateCommentRequest( @NotBlank @Max(300) String content diff --git a/src/main/java/org/sopt/comment/api/dto/request/CommentUpdateRequest.java b/src/main/java/org/sopt/comment/api/dto/request/UpdateCommentRequest.java similarity index 84% rename from src/main/java/org/sopt/comment/api/dto/request/CommentUpdateRequest.java rename to src/main/java/org/sopt/comment/api/dto/request/UpdateCommentRequest.java index f8eb18d..a199797 100644 --- a/src/main/java/org/sopt/comment/api/dto/request/CommentUpdateRequest.java +++ b/src/main/java/org/sopt/comment/api/dto/request/UpdateCommentRequest.java @@ -3,7 +3,7 @@ import jakarta.validation.constraints.Max; import jakarta.validation.constraints.NotBlank; -public record CommentUpdateRequest( +public record UpdateCommentRequest( @NotBlank @Max(300) String content diff --git a/src/main/java/org/sopt/comment/api/dto/response/CommentCreateResponse.java b/src/main/java/org/sopt/comment/api/dto/response/CreateCommentResponse.java similarity index 65% rename from src/main/java/org/sopt/comment/api/dto/response/CommentCreateResponse.java rename to src/main/java/org/sopt/comment/api/dto/response/CreateCommentResponse.java index 0b7d454..31eb563 100644 --- a/src/main/java/org/sopt/comment/api/dto/response/CommentCreateResponse.java +++ b/src/main/java/org/sopt/comment/api/dto/response/CreateCommentResponse.java @@ -1,6 +1,6 @@ package org.sopt.comment.api.dto.response; -public record CommentCreateResponse( +public record CreateCommentResponse( Long commentId ){ diff --git a/src/main/java/org/sopt/comment/api/dto/response/CommentUpdateResponse.java b/src/main/java/org/sopt/comment/api/dto/response/UpdateCommentResponse.java similarity index 65% rename from src/main/java/org/sopt/comment/api/dto/response/CommentUpdateResponse.java rename to src/main/java/org/sopt/comment/api/dto/response/UpdateCommentResponse.java index 502fe39..6f4e21b 100644 --- a/src/main/java/org/sopt/comment/api/dto/response/CommentUpdateResponse.java +++ b/src/main/java/org/sopt/comment/api/dto/response/UpdateCommentResponse.java @@ -1,6 +1,6 @@ package org.sopt.comment.api.dto.response; -public record CommentUpdateResponse( +public record UpdateCommentResponse( Long commentId ) { diff --git a/src/main/java/org/sopt/comment/api/mapper/CommentRequestMapper.java b/src/main/java/org/sopt/comment/api/mapper/CommentRequestMapper.java index 5be58ed..b2fc51e 100644 --- a/src/main/java/org/sopt/comment/api/mapper/CommentRequestMapper.java +++ b/src/main/java/org/sopt/comment/api/mapper/CommentRequestMapper.java @@ -1,7 +1,7 @@ package org.sopt.comment.api.mapper; -import org.sopt.comment.api.dto.request.CommentCreateRequest; -import org.sopt.comment.api.dto.request.CommentUpdateRequest; +import org.sopt.comment.api.dto.request.CreateCommentRequest; +import org.sopt.comment.api.dto.request.UpdateCommentRequest; import org.sopt.comment.application.dto.command.CreateCommentCommand; import org.sopt.comment.application.dto.command.UpdateCommentCommand; @@ -10,24 +10,24 @@ public class CommentRequestMapper { public static CreateCommentCommand toCommentCreateCommand( Long userId, Long articleId, - CommentCreateRequest commentCreateRequest + CreateCommentRequest createCommentRequest ) { return new CreateCommentCommand( userId, articleId, - commentCreateRequest.content() + createCommentRequest.content() ); } public static UpdateCommentCommand toCommentUpdateCommand( Long userId, Long articleId, - CommentUpdateRequest commentUpdateRequest + UpdateCommentRequest updateCommentRequest ) { return new UpdateCommentCommand( userId, articleId, - commentUpdateRequest.content() + updateCommentRequest.content() ); } diff --git a/src/main/java/org/sopt/comment/api/mapper/CommentResponseMapper.java b/src/main/java/org/sopt/comment/api/mapper/CommentResponseMapper.java index 11eb56b..0ad99f6 100644 --- a/src/main/java/org/sopt/comment/api/mapper/CommentResponseMapper.java +++ b/src/main/java/org/sopt/comment/api/mapper/CommentResponseMapper.java @@ -1,21 +1,21 @@ package org.sopt.comment.api.mapper; -import org.sopt.comment.api.dto.response.CommentCreateResponse; -import org.sopt.comment.api.dto.response.CommentUpdateResponse; +import org.sopt.comment.api.dto.response.CreateCommentResponse; +import org.sopt.comment.api.dto.response.UpdateCommentResponse; import org.sopt.comment.application.dto.result.CreateCommentResult; import org.sopt.comment.application.dto.result.UpdateCommentResult; public class CommentResponseMapper { - public static CommentCreateResponse toCreateResponse( + public static CreateCommentResponse toCreateResponse( CreateCommentResult commentCreateResult ) { - return new CommentCreateResponse(commentCreateResult.commentId()); + return new CreateCommentResponse(commentCreateResult.commentId()); } - public static CommentUpdateResponse toUpdateResponse( + public static UpdateCommentResponse toUpdateResponse( UpdateCommentResult commentUpdateResult ) { - return new CommentUpdateResponse(commentUpdateResult.commentId()); + return new UpdateCommentResponse(commentUpdateResult.commentId()); } } From cf12624d3747a668b488be7d4f3faad62f2af51f Mon Sep 17 00:00:00 2001 From: kaswhy Date: Wed, 24 Dec 2025 22:07:16 +0900 Subject: [PATCH 17/18] =?UTF-8?q?feat(comment):=20=EB=8C=93=EA=B8=80=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20api=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sopt/comment/api/CommentController.java | 84 ++++++++++++------- .../comment/api/code/CommentSuccessCode.java | 1 + .../response/GetCommentAuthorResponse.java | 17 ++++ .../response/GetCommentContentResponse.java | 20 +++++ .../dto/response/GetCommentListResponse.java | 9 ++ .../dto/response/GetCommentMetaResponse.java | 24 ++++++ .../api/mapper/CommentResponseMapper.java | 13 +++ .../dto/result/GetCommentAuthorResult.java | 16 ++++ .../dto/result/GetCommentContentResult.java | 19 +++++ .../dto/result/GetCommentListResult.java | 20 +++++ .../dto/result/GetCommentMetaResult.java | 25 ++++++ .../port/in/GetCommentListUsecase.java | 8 ++ .../service/GetCommentListService.java | 41 +++++++++ .../sopt/comment/domain/model/Comment.java | 2 +- .../domain/port/out/CommentRepository.java | 4 + .../jpa/CommentJpaRepository.java | 4 + .../jpa/CommentRepositoryAdapter.java | 8 ++ .../global/response/dto/ApiResponseBody.java | 23 +++-- 18 files changed, 300 insertions(+), 38 deletions(-) create mode 100644 src/main/java/org/sopt/comment/api/dto/response/GetCommentAuthorResponse.java create mode 100644 src/main/java/org/sopt/comment/api/dto/response/GetCommentContentResponse.java create mode 100644 src/main/java/org/sopt/comment/api/dto/response/GetCommentListResponse.java create mode 100644 src/main/java/org/sopt/comment/api/dto/response/GetCommentMetaResponse.java create mode 100644 src/main/java/org/sopt/comment/application/dto/result/GetCommentAuthorResult.java create mode 100644 src/main/java/org/sopt/comment/application/dto/result/GetCommentContentResult.java create mode 100644 src/main/java/org/sopt/comment/application/dto/result/GetCommentListResult.java create mode 100644 src/main/java/org/sopt/comment/application/dto/result/GetCommentMetaResult.java create mode 100644 src/main/java/org/sopt/comment/application/port/in/GetCommentListUsecase.java create mode 100644 src/main/java/org/sopt/comment/application/service/GetCommentListService.java diff --git a/src/main/java/org/sopt/comment/api/CommentController.java b/src/main/java/org/sopt/comment/api/CommentController.java index 2a42cd7..14a4227 100644 --- a/src/main/java/org/sopt/comment/api/CommentController.java +++ b/src/main/java/org/sopt/comment/api/CommentController.java @@ -1,23 +1,28 @@ package org.sopt.comment.api; import static org.sopt.comment.api.code.CommentSuccessCode.COMMENT_CREATED_SUCCESS; +import static org.sopt.comment.api.code.CommentSuccessCode.COMMENT_LIST_RETRIEVED_SUCCESS; import static org.sopt.comment.api.code.CommentSuccessCode.COMMENT_UPDATED_SUCCESS; import static org.springframework.http.HttpStatus.CREATED; import static org.springframework.http.HttpStatus.OK; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; -import org.sopt.comment.api.dto.request.CommentCreateRequest; -import org.sopt.comment.api.dto.request.CommentUpdateRequest; -import org.sopt.comment.api.dto.response.CommentCreateResponse; -import org.sopt.comment.api.dto.response.CommentUpdateResponse; +import org.sopt.comment.api.dto.request.CreateCommentRequest; +import org.sopt.comment.api.dto.request.UpdateCommentRequest; +import org.sopt.comment.api.dto.response.CreateCommentResponse; +import org.sopt.comment.api.dto.response.GetCommentListResponse; +import org.sopt.comment.api.dto.response.GetCommentMetaResponse; +import org.sopt.comment.api.dto.response.UpdateCommentResponse; import org.sopt.comment.api.mapper.CommentRequestMapper; import org.sopt.comment.api.mapper.CommentResponseMapper; -import org.sopt.comment.application.dto.command.CommentCreateCommand; -import org.sopt.comment.application.dto.command.CommentUpdateCommand; -import org.sopt.comment.application.dto.result.CommentCreateResult; -import org.sopt.comment.application.dto.result.CommentUpdateResult; +import org.sopt.comment.application.dto.command.CreateCommentCommand; +import org.sopt.comment.application.dto.command.UpdateCommentCommand; +import org.sopt.comment.application.dto.result.CreateCommentResult; +import org.sopt.comment.application.dto.result.GetCommentListResult; +import org.sopt.comment.application.dto.result.UpdateCommentResult; import org.sopt.comment.application.port.in.CreateCommentUsecase; +import org.sopt.comment.application.port.in.GetCommentListUsecase; import org.sopt.comment.application.port.in.UpdateCommentUsecase; import org.sopt.global.response.dto.ApiResponseBody; import org.springframework.http.ResponseEntity; @@ -28,6 +33,7 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RequestMapping("/api/v1") @@ -35,50 +41,64 @@ @RestController public class CommentController { - private final CreateCommentUsecase commentCreateUsecase; - private final UpdateCommentUsecase commentUpdateUsecase; + private final CreateCommentUsecase createCommentUsecase; + private final UpdateCommentUsecase updateCommentUsecase; + private final GetCommentListUsecase getCommentListUsecase; @PostMapping("/articles/{articleId}/comments") - public ResponseEntity> createComment( + public ResponseEntity> createComment( @RequestHeader Long userId, @PathVariable Long articleId, - @Valid @RequestBody CommentCreateRequest commentCreateRequest + @Valid @RequestBody CreateCommentRequest createCommentRequest ) { - CommentCreateCommand commentCreateCommand = CommentRequestMapper - .toCommentCreateCommand(userId, articleId, commentCreateRequest); - CommentCreateResult commentCreateResult = commentCreateUsecase - .writeComment(commentCreateCommand); - CommentCreateResponse commentCreateResponse = CommentResponseMapper + CreateCommentCommand createCommentCommand = CommentRequestMapper + .toCommentCreateCommand(userId, articleId, createCommentRequest); + CreateCommentResult commentCreateResult = createCommentUsecase + .writeComment(createCommentCommand); + CreateCommentResponse createCommentResponse = CommentResponseMapper .toCreateResponse(commentCreateResult); return ResponseEntity.status(CREATED) - .body(ApiResponseBody.created(COMMENT_CREATED_SUCCESS, commentCreateResponse)); + .body(ApiResponseBody.created(COMMENT_CREATED_SUCCESS, createCommentResponse)); } - // 조회 - @GetMapping("/articles/{articleId}") - public ResponseEntity> getCommentList ( - @RequestHeader Long userId, - @PathVariable Long articleId + @GetMapping("/articles/{articleId}/comments") + public ResponseEntity> getCommentList( + @PathVariable Long articleId, + @RequestParam(defaultValue = "0") int page, + @RequestParam(defaultValue = "10") int size ) { - return null; + GetCommentListResult getCommentListResult = getCommentListUsecase + .findByArticleId(articleId, page, size); + GetCommentListResponse getCommentListResponse = CommentResponseMapper.toGetListResponse( + getCommentListResult + ); + GetCommentMetaResponse getCommentMetaResponse = GetCommentMetaResponse + .create(getCommentListResult.getCommentMetaResult()); + + return ResponseEntity.status(OK) + .body(ApiResponseBody.ok( + COMMENT_LIST_RETRIEVED_SUCCESS, + getCommentListResponse, + getCommentMetaResponse + )); } @PatchMapping("/comments/{commentId}") - public ResponseEntity> updateComment ( + public ResponseEntity> updateComment( @RequestHeader Long userId, @PathVariable Long commentId, - @Valid @RequestBody CommentUpdateRequest commentUpdateRequest + @Valid @RequestBody UpdateCommentRequest updateCommentRequest ) { - CommentUpdateCommand commentUpdateCommand = CommentRequestMapper - .toCommentUpdateCommand(userId, commentId, commentUpdateRequest); - CommentUpdateResult commentUpdateResult = commentUpdateUsecase - .updateComment(commentUpdateCommand); - CommentUpdateResponse commentUpdateResponse = CommentResponseMapper + UpdateCommentCommand updateCommentCommand = CommentRequestMapper + .toCommentUpdateCommand(userId, commentId, updateCommentRequest); + UpdateCommentResult commentUpdateResult = updateCommentUsecase + .updateComment(updateCommentCommand); + UpdateCommentResponse updateCommentResponse = CommentResponseMapper .toUpdateResponse(commentUpdateResult); return ResponseEntity.status(OK) - .body(ApiResponseBody.ok(COMMENT_UPDATED_SUCCESS, commentUpdateResponse)); + .body(ApiResponseBody.ok(COMMENT_UPDATED_SUCCESS, updateCommentResponse)); } // 삭제 diff --git a/src/main/java/org/sopt/comment/api/code/CommentSuccessCode.java b/src/main/java/org/sopt/comment/api/code/CommentSuccessCode.java index 1147719..0dfa032 100644 --- a/src/main/java/org/sopt/comment/api/code/CommentSuccessCode.java +++ b/src/main/java/org/sopt/comment/api/code/CommentSuccessCode.java @@ -10,6 +10,7 @@ public enum CommentSuccessCode implements SuccessCode { // 200 OK COMMENT_UPDATED_SUCCESS(200, "COMMENT_200_001", "성공적으로 댓글을 수정했습니다."), + COMMENT_LIST_RETRIEVED_SUCCESS(200, "COMMENT_200_002", "성공적으로 해당 게시물의 댓글 목록을 조회했습니다."), // 201 CREATED COMMENT_CREATED_SUCCESS(201, "COMMENT_201_001", "성공적으로 댓글을 작성했습니다."), diff --git a/src/main/java/org/sopt/comment/api/dto/response/GetCommentAuthorResponse.java b/src/main/java/org/sopt/comment/api/dto/response/GetCommentAuthorResponse.java new file mode 100644 index 0000000..e9c8118 --- /dev/null +++ b/src/main/java/org/sopt/comment/api/dto/response/GetCommentAuthorResponse.java @@ -0,0 +1,17 @@ +package org.sopt.comment.api.dto.response; + +import org.sopt.comment.application.dto.result.GetCommentAuthorResult; + +public record GetCommentAuthorResponse( + Long id, + String name +) { + + public static GetCommentAuthorResponse create(GetCommentAuthorResult getCommentAuthorResult) { + return new GetCommentAuthorResponse( + getCommentAuthorResult.id(), + getCommentAuthorResult.name() + ); + } + +} diff --git a/src/main/java/org/sopt/comment/api/dto/response/GetCommentContentResponse.java b/src/main/java/org/sopt/comment/api/dto/response/GetCommentContentResponse.java new file mode 100644 index 0000000..f21a915 --- /dev/null +++ b/src/main/java/org/sopt/comment/api/dto/response/GetCommentContentResponse.java @@ -0,0 +1,20 @@ +package org.sopt.comment.api.dto.response; + +import org.sopt.comment.application.dto.result.GetCommentContentResult; + +public record GetCommentContentResponse( + Long id, + GetCommentAuthorResponse author, + String content +) { + + public static GetCommentContentResponse create( + GetCommentContentResult getCommentContentResult + ) { + return new GetCommentContentResponse( + getCommentContentResult.id(), + GetCommentAuthorResponse.create(getCommentContentResult.author()), + getCommentContentResult.content() + ); + } +} diff --git a/src/main/java/org/sopt/comment/api/dto/response/GetCommentListResponse.java b/src/main/java/org/sopt/comment/api/dto/response/GetCommentListResponse.java new file mode 100644 index 0000000..43409fb --- /dev/null +++ b/src/main/java/org/sopt/comment/api/dto/response/GetCommentListResponse.java @@ -0,0 +1,9 @@ +package org.sopt.comment.api.dto.response; + +import java.util.List; + +public record GetCommentListResponse( + List comments +){ + +} diff --git a/src/main/java/org/sopt/comment/api/dto/response/GetCommentMetaResponse.java b/src/main/java/org/sopt/comment/api/dto/response/GetCommentMetaResponse.java new file mode 100644 index 0000000..430b9b7 --- /dev/null +++ b/src/main/java/org/sopt/comment/api/dto/response/GetCommentMetaResponse.java @@ -0,0 +1,24 @@ +package org.sopt.comment.api.dto.response; + +import org.sopt.comment.application.dto.result.GetCommentMetaResult; + +public record GetCommentMetaResponse( + int totalPages, + long totalElements, + int size, + int number, + boolean isFirst, + boolean isLast +) { + + public static GetCommentMetaResponse create(GetCommentMetaResult getCommentMetaResult) { + return new GetCommentMetaResponse( + getCommentMetaResult.totalPages(), + getCommentMetaResult.totalElements(), + getCommentMetaResult.size(), + getCommentMetaResult.number(), + getCommentMetaResult.isFirst(), + getCommentMetaResult.isLast() + ); + } +} diff --git a/src/main/java/org/sopt/comment/api/mapper/CommentResponseMapper.java b/src/main/java/org/sopt/comment/api/mapper/CommentResponseMapper.java index 0ad99f6..4bf1d38 100644 --- a/src/main/java/org/sopt/comment/api/mapper/CommentResponseMapper.java +++ b/src/main/java/org/sopt/comment/api/mapper/CommentResponseMapper.java @@ -1,8 +1,11 @@ package org.sopt.comment.api.mapper; import org.sopt.comment.api.dto.response.CreateCommentResponse; +import org.sopt.comment.api.dto.response.GetCommentContentResponse; +import org.sopt.comment.api.dto.response.GetCommentListResponse; import org.sopt.comment.api.dto.response.UpdateCommentResponse; import org.sopt.comment.application.dto.result.CreateCommentResult; +import org.sopt.comment.application.dto.result.GetCommentListResult; import org.sopt.comment.application.dto.result.UpdateCommentResult; public class CommentResponseMapper { @@ -18,4 +21,14 @@ public static UpdateCommentResponse toUpdateResponse( ) { return new UpdateCommentResponse(commentUpdateResult.commentId()); } + + public static GetCommentListResponse toGetListResponse( + GetCommentListResult getCommentListResult + ) { + return new GetCommentListResponse( + getCommentListResult.getCommentContentResult().stream() + .map(GetCommentContentResponse::create) + .toList() + ); + } } diff --git a/src/main/java/org/sopt/comment/application/dto/result/GetCommentAuthorResult.java b/src/main/java/org/sopt/comment/application/dto/result/GetCommentAuthorResult.java new file mode 100644 index 0000000..6febdfe --- /dev/null +++ b/src/main/java/org/sopt/comment/application/dto/result/GetCommentAuthorResult.java @@ -0,0 +1,16 @@ +package org.sopt.comment.application.dto.result; + +import org.sopt.member.domain.model.Member; + +public record GetCommentAuthorResult( + Long id, + String name +) { + + public static GetCommentAuthorResult create(Member member) { + return new GetCommentAuthorResult( + member.getId(), + member.getName() + ); + } +} diff --git a/src/main/java/org/sopt/comment/application/dto/result/GetCommentContentResult.java b/src/main/java/org/sopt/comment/application/dto/result/GetCommentContentResult.java new file mode 100644 index 0000000..c56dabf --- /dev/null +++ b/src/main/java/org/sopt/comment/application/dto/result/GetCommentContentResult.java @@ -0,0 +1,19 @@ +package org.sopt.comment.application.dto.result; + +import org.sopt.comment.domain.model.Comment; + +public record GetCommentContentResult( + Long id, + GetCommentAuthorResult author, + String content +) { + + public static GetCommentContentResult create(Comment comment) { + return new GetCommentContentResult( + comment.getId(), + GetCommentAuthorResult.create(comment.getAuthor()), + comment.getContent() + ); + } + +} diff --git a/src/main/java/org/sopt/comment/application/dto/result/GetCommentListResult.java b/src/main/java/org/sopt/comment/application/dto/result/GetCommentListResult.java new file mode 100644 index 0000000..9cd6b15 --- /dev/null +++ b/src/main/java/org/sopt/comment/application/dto/result/GetCommentListResult.java @@ -0,0 +1,20 @@ +package org.sopt.comment.application.dto.result; + +import java.util.List; +import org.sopt.comment.domain.model.Comment; +import org.springframework.data.domain.Page; + +public record GetCommentListResult( + List getCommentContentResult, + GetCommentMetaResult getCommentMetaResult +) { + + public static GetCommentListResult create(Page commentPage) { + return new GetCommentListResult( + commentPage.getContent().stream() + .map(GetCommentContentResult::create) + .toList(), + GetCommentMetaResult.create(commentPage) + ); + } +} diff --git a/src/main/java/org/sopt/comment/application/dto/result/GetCommentMetaResult.java b/src/main/java/org/sopt/comment/application/dto/result/GetCommentMetaResult.java new file mode 100644 index 0000000..7f3d17d --- /dev/null +++ b/src/main/java/org/sopt/comment/application/dto/result/GetCommentMetaResult.java @@ -0,0 +1,25 @@ +package org.sopt.comment.application.dto.result; + +import org.sopt.comment.domain.model.Comment; +import org.springframework.data.domain.Page; + +public record GetCommentMetaResult( + int totalPages, + long totalElements, + int size, + int number, + boolean isFirst, + boolean isLast +) { + + public static GetCommentMetaResult create(Page commentPage) { + return new GetCommentMetaResult( + commentPage.getTotalPages(), + commentPage.getTotalElements(), + commentPage.getSize(), + commentPage.getNumber(), + commentPage.isFirst(), + commentPage.isLast() + ); + } +} diff --git a/src/main/java/org/sopt/comment/application/port/in/GetCommentListUsecase.java b/src/main/java/org/sopt/comment/application/port/in/GetCommentListUsecase.java new file mode 100644 index 0000000..ba5bd83 --- /dev/null +++ b/src/main/java/org/sopt/comment/application/port/in/GetCommentListUsecase.java @@ -0,0 +1,8 @@ +package org.sopt.comment.application.port.in; + +import org.sopt.comment.application.dto.result.GetCommentListResult; + +public interface GetCommentListUsecase { + + GetCommentListResult findByArticleId(Long articleId, int page, int size); +} diff --git a/src/main/java/org/sopt/comment/application/service/GetCommentListService.java b/src/main/java/org/sopt/comment/application/service/GetCommentListService.java new file mode 100644 index 0000000..0537d51 --- /dev/null +++ b/src/main/java/org/sopt/comment/application/service/GetCommentListService.java @@ -0,0 +1,41 @@ +package org.sopt.comment.application.service; + +import static org.sopt.article.domain.exception.ArticleErrorCode.ARTICLE_NOT_FOUND; + +import lombok.RequiredArgsConstructor; +import org.sopt.article.domain.exception.ArticleException; +import org.sopt.article.domain.model.Article; +import org.sopt.article.domain.port.out.ArticleRepository; +import org.sopt.comment.application.dto.result.GetCommentListResult; +import org.sopt.comment.application.port.in.GetCommentListUsecase; +import org.sopt.comment.domain.model.Comment; +import org.sopt.comment.domain.port.out.CommentRepository; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Transactional(readOnly = true) +@RequiredArgsConstructor +@Service +public class GetCommentListService implements GetCommentListUsecase { + + private final ArticleRepository articleRepository; + private final CommentRepository commentRepository; + + @Override + public GetCommentListResult findByArticleId(Long articleId, int page, int size) { + Article article = getExistingArticle(articleId); + Pageable pageable = PageRequest.of(page, size); + Page commentPage = commentRepository.findByArticle(article, pageable); + + return GetCommentListResult.create(commentPage); + } + + private Article getExistingArticle(Long articleId) { + return articleRepository.findById(articleId) + .orElseThrow(() -> new ArticleException(ARTICLE_NOT_FOUND)); + } + +} diff --git a/src/main/java/org/sopt/comment/domain/model/Comment.java b/src/main/java/org/sopt/comment/domain/model/Comment.java index d11dd70..40e6f33 100644 --- a/src/main/java/org/sopt/comment/domain/model/Comment.java +++ b/src/main/java/org/sopt/comment/domain/model/Comment.java @@ -31,7 +31,7 @@ public class Comment extends BaseEntity { private static final int MAX_CONTENT_LENGTH = 300; @Id - @GeneratedValue(strategy = GenerationType.AUTO) + @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @ManyToOne(fetch = FetchType.LAZY) diff --git a/src/main/java/org/sopt/comment/domain/port/out/CommentRepository.java b/src/main/java/org/sopt/comment/domain/port/out/CommentRepository.java index cf47c6b..05cac3c 100644 --- a/src/main/java/org/sopt/comment/domain/port/out/CommentRepository.java +++ b/src/main/java/org/sopt/comment/domain/port/out/CommentRepository.java @@ -1,10 +1,14 @@ package org.sopt.comment.domain.port.out; import java.util.Optional; +import org.sopt.article.domain.model.Article; import org.sopt.comment.domain.model.Comment; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; public interface CommentRepository { void save(Comment comment); Optional findById(Long id); + Page findByArticle(Article article, Pageable pageable); } diff --git a/src/main/java/org/sopt/comment/infrastructure/jpa/CommentJpaRepository.java b/src/main/java/org/sopt/comment/infrastructure/jpa/CommentJpaRepository.java index e9d36e4..be0c9d3 100644 --- a/src/main/java/org/sopt/comment/infrastructure/jpa/CommentJpaRepository.java +++ b/src/main/java/org/sopt/comment/infrastructure/jpa/CommentJpaRepository.java @@ -1,8 +1,12 @@ package org.sopt.comment.infrastructure.jpa; +import org.sopt.article.domain.model.Article; import org.sopt.comment.domain.model.Comment; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; public interface CommentJpaRepository extends JpaRepository { + Page findByArticle(Article article, Pageable pageable); } diff --git a/src/main/java/org/sopt/comment/infrastructure/jpa/CommentRepositoryAdapter.java b/src/main/java/org/sopt/comment/infrastructure/jpa/CommentRepositoryAdapter.java index 2f1d669..f1f0181 100644 --- a/src/main/java/org/sopt/comment/infrastructure/jpa/CommentRepositoryAdapter.java +++ b/src/main/java/org/sopt/comment/infrastructure/jpa/CommentRepositoryAdapter.java @@ -2,8 +2,11 @@ import java.util.Optional; import lombok.RequiredArgsConstructor; +import org.sopt.article.domain.model.Article; import org.sopt.comment.domain.model.Comment; import org.sopt.comment.domain.port.out.CommentRepository; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Repository; @RequiredArgsConstructor @@ -22,4 +25,9 @@ public Optional findById(Long id) { return jpa.findById(id); } + @Override + public Page findByArticle(Article article, Pageable pageable) { + return jpa.findByArticle(article, pageable); + } + } diff --git a/src/main/java/org/sopt/global/response/dto/ApiResponseBody.java b/src/main/java/org/sopt/global/response/dto/ApiResponseBody.java index 7112692..fdee3bf 100644 --- a/src/main/java/org/sopt/global/response/dto/ApiResponseBody.java +++ b/src/main/java/org/sopt/global/response/dto/ApiResponseBody.java @@ -31,24 +31,37 @@ public static ApiResponseBody ok(SuccessCode successCode, T data) { successCode.getStatus(), successCode.getCode(), successCode.getMessage(), - null, + data, null ); } + public static ApiResponseBody ok(SuccessCode successCode, T data, M meta) { + return new ApiResponseBody<>( + true, + successCode.getStatus(), + successCode.getCode(), + successCode.getMessage(), + data, + meta + ); + } + public static ApiResponseBody created(SuccessCode successCode, T data) { return new ApiResponseBody<>( true, successCode.getStatus(), successCode.getCode(), successCode.getMessage(), - null, + data, null ); } - public static ApiResponseBody onFailure(ErrorCode errorCode, - ErrorMeta errorMeta) { + public static ApiResponseBody onFailure( + ErrorCode errorCode, + ErrorMeta errorMeta + ) { return new ApiResponseBody<>( false, errorCode.getStatus(), @@ -68,7 +81,7 @@ public static ApiResponseBody onFailure( false, errorCode.getStatus(), errorCode.getCode(), - errorCode.getMessage(), + message, null, errorMeta ); From 2d8124b29226fb3232e6066dc24cf58a8b597134 Mon Sep 17 00:00:00 2001 From: kaswhy Date: Thu, 25 Dec 2025 22:08:53 +0900 Subject: [PATCH 18/18] =?UTF-8?q?feat(comment):=20=EB=8C=93=EA=B8=80=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C=20api=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sopt/comment/api/CommentController.java | 15 ++++++- .../comment/api/code/CommentSuccessCode.java | 1 + .../port/in/DeleteCommentUsecase.java | 6 +++ .../service/DeleteCommentService.java | 39 +++++++++++++++++++ .../domain/exception/CommentErrorCode.java | 1 + .../sopt/comment/domain/model/Comment.java | 6 ++- .../domain/port/out/CommentRepository.java | 4 ++ .../jpa/CommentRepositoryAdapter.java | 5 +++ 8 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 src/main/java/org/sopt/comment/application/port/in/DeleteCommentUsecase.java create mode 100644 src/main/java/org/sopt/comment/application/service/DeleteCommentService.java diff --git a/src/main/java/org/sopt/comment/api/CommentController.java b/src/main/java/org/sopt/comment/api/CommentController.java index 14a4227..eb8f520 100644 --- a/src/main/java/org/sopt/comment/api/CommentController.java +++ b/src/main/java/org/sopt/comment/api/CommentController.java @@ -1,6 +1,7 @@ package org.sopt.comment.api; import static org.sopt.comment.api.code.CommentSuccessCode.COMMENT_CREATED_SUCCESS; +import static org.sopt.comment.api.code.CommentSuccessCode.COMMENT_DELETE_SUCCESS; import static org.sopt.comment.api.code.CommentSuccessCode.COMMENT_LIST_RETRIEVED_SUCCESS; import static org.sopt.comment.api.code.CommentSuccessCode.COMMENT_UPDATED_SUCCESS; import static org.springframework.http.HttpStatus.CREATED; @@ -22,10 +23,12 @@ import org.sopt.comment.application.dto.result.GetCommentListResult; import org.sopt.comment.application.dto.result.UpdateCommentResult; import org.sopt.comment.application.port.in.CreateCommentUsecase; +import org.sopt.comment.application.port.in.DeleteCommentUsecase; import org.sopt.comment.application.port.in.GetCommentListUsecase; import org.sopt.comment.application.port.in.UpdateCommentUsecase; import org.sopt.global.response.dto.ApiResponseBody; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -44,6 +47,7 @@ public class CommentController { private final CreateCommentUsecase createCommentUsecase; private final UpdateCommentUsecase updateCommentUsecase; private final GetCommentListUsecase getCommentListUsecase; + private final DeleteCommentUsecase deleteCommentUsecase; @PostMapping("/articles/{articleId}/comments") public ResponseEntity> createComment( @@ -101,6 +105,15 @@ public ResponseEntity> updateCommen .body(ApiResponseBody.ok(COMMENT_UPDATED_SUCCESS, updateCommentResponse)); } - // 삭제 + @DeleteMapping("/comments/{commentId}") + public ResponseEntity> deleteComment( + @RequestHeader Long userId, + @PathVariable Long commentId + ) { + deleteCommentUsecase.deleteComment(userId, commentId); + + return ResponseEntity.status(OK) + .body(ApiResponseBody.ok(COMMENT_DELETE_SUCCESS)); + } } diff --git a/src/main/java/org/sopt/comment/api/code/CommentSuccessCode.java b/src/main/java/org/sopt/comment/api/code/CommentSuccessCode.java index 0dfa032..fbfe927 100644 --- a/src/main/java/org/sopt/comment/api/code/CommentSuccessCode.java +++ b/src/main/java/org/sopt/comment/api/code/CommentSuccessCode.java @@ -11,6 +11,7 @@ public enum CommentSuccessCode implements SuccessCode { // 200 OK COMMENT_UPDATED_SUCCESS(200, "COMMENT_200_001", "성공적으로 댓글을 수정했습니다."), COMMENT_LIST_RETRIEVED_SUCCESS(200, "COMMENT_200_002", "성공적으로 해당 게시물의 댓글 목록을 조회했습니다."), + COMMENT_DELETE_SUCCESS(200, "COMMENT_200_003", "성공적으로 댓글을 삭제했습니다."), // 201 CREATED COMMENT_CREATED_SUCCESS(201, "COMMENT_201_001", "성공적으로 댓글을 작성했습니다."), diff --git a/src/main/java/org/sopt/comment/application/port/in/DeleteCommentUsecase.java b/src/main/java/org/sopt/comment/application/port/in/DeleteCommentUsecase.java new file mode 100644 index 0000000..489dd43 --- /dev/null +++ b/src/main/java/org/sopt/comment/application/port/in/DeleteCommentUsecase.java @@ -0,0 +1,6 @@ +package org.sopt.comment.application.port.in; + +public interface DeleteCommentUsecase { + + void deleteComment(Long userId, Long commentId); +} diff --git a/src/main/java/org/sopt/comment/application/service/DeleteCommentService.java b/src/main/java/org/sopt/comment/application/service/DeleteCommentService.java new file mode 100644 index 0000000..4eb4592 --- /dev/null +++ b/src/main/java/org/sopt/comment/application/service/DeleteCommentService.java @@ -0,0 +1,39 @@ +package org.sopt.comment.application.service; + +import static org.sopt.comment.domain.exception.CommentErrorCode.COMMENT_DELETE_FORBIDDEN; +import static org.sopt.comment.domain.exception.CommentErrorCode.COMMENT_NOT_FOUND; + +import lombok.RequiredArgsConstructor; +import org.sopt.comment.application.port.in.DeleteCommentUsecase; +import org.sopt.comment.domain.exception.CommentException; +import org.sopt.comment.domain.model.Comment; +import org.sopt.comment.domain.port.out.CommentRepository; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +@RequiredArgsConstructor +@Service +public class DeleteCommentService implements DeleteCommentUsecase { + + private final CommentRepository commentRepository; + + @Override + public void deleteComment(Long userId, Long commentId) { + Comment comment = getExistingComment(commentId); + validateCanDelete(userId, comment); + commentRepository.deleteById(commentId); + } + + private Comment getExistingComment(Long commentId) { + return commentRepository.findById(commentId) + .orElseThrow(() -> new CommentException(COMMENT_NOT_FOUND)); + } + + private void validateCanDelete(Long userId, Comment comment) { + if(!comment.isOwner(userId)) { + throw new CommentException(COMMENT_DELETE_FORBIDDEN); + } + } + +} diff --git a/src/main/java/org/sopt/comment/domain/exception/CommentErrorCode.java b/src/main/java/org/sopt/comment/domain/exception/CommentErrorCode.java index 567f1b9..6ba0f7f 100644 --- a/src/main/java/org/sopt/comment/domain/exception/CommentErrorCode.java +++ b/src/main/java/org/sopt/comment/domain/exception/CommentErrorCode.java @@ -16,6 +16,7 @@ public enum CommentErrorCode implements ErrorCode { // 403 FORBIDDEN COMMENT_EDIT_FORBIDDEN(403, "COMMENT_403_001", "댓글 수정 권한이 없습니다."), + COMMENT_DELETE_FORBIDDEN(403, "COMMENT_403_002", "댓글 삭제 권한이 없습니다."), // 404 NOT FOUND COMMENT_NOT_FOUND(404, "COMMENT_404_001", "해당 댓글이 존재하지 않습니다."), diff --git a/src/main/java/org/sopt/comment/domain/model/Comment.java b/src/main/java/org/sopt/comment/domain/model/Comment.java index 40e6f33..2f99a5a 100644 --- a/src/main/java/org/sopt/comment/domain/model/Comment.java +++ b/src/main/java/org/sopt/comment/domain/model/Comment.java @@ -67,8 +67,12 @@ public void update(Long userId, String content) { this.content = content; } + public boolean isOwner(Long userId) { + return this.author.getId().equals(userId); + } + private void validateCanEdit(Long userId) { - if(!this.author.getId().equals(userId)) { + if (!this.author.getId().equals(userId)) { throw new CommentException(COMMENT_EDIT_FORBIDDEN); } } diff --git a/src/main/java/org/sopt/comment/domain/port/out/CommentRepository.java b/src/main/java/org/sopt/comment/domain/port/out/CommentRepository.java index 05cac3c..53c18d1 100644 --- a/src/main/java/org/sopt/comment/domain/port/out/CommentRepository.java +++ b/src/main/java/org/sopt/comment/domain/port/out/CommentRepository.java @@ -9,6 +9,10 @@ public interface CommentRepository { void save(Comment comment); + Optional findById(Long id); + Page findByArticle(Article article, Pageable pageable); + + void deleteById(Long commentId); } diff --git a/src/main/java/org/sopt/comment/infrastructure/jpa/CommentRepositoryAdapter.java b/src/main/java/org/sopt/comment/infrastructure/jpa/CommentRepositoryAdapter.java index f1f0181..14051c8 100644 --- a/src/main/java/org/sopt/comment/infrastructure/jpa/CommentRepositoryAdapter.java +++ b/src/main/java/org/sopt/comment/infrastructure/jpa/CommentRepositoryAdapter.java @@ -30,4 +30,9 @@ public Page findByArticle(Article article, Pageable pageable) { return jpa.findByArticle(article, pageable); } + @Override + public void deleteById(Long id) { + jpa.deleteById(id); + } + }