Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 16 additions & 2 deletions src/main/java/org/sopt/common/ErrorResponse.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,25 @@
package org.sopt.common;

import org.sopt.exception.ErrorStatus;

public record ErrorResponse(
String status,
String code,
String message
) {
public static ErrorResponse of(String code, String message) {
return new ErrorResponse("FAILURE", code, message);
public static ErrorResponse of(ErrorStatus errorStatus) {
return new ErrorResponse(
"FAILURE",
errorStatus.getCode(),
errorStatus.getMessage()
);
}

public static ErrorResponse of(ErrorStatus errorStatus, String message) {
return new ErrorResponse(
"FAILURE",
errorStatus.getCode(),
message
);
}
}
20 changes: 13 additions & 7 deletions src/main/java/org/sopt/common/GlobalExceptionHandler.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package org.sopt.common;

import org.sopt.dto.response.ApiCode;
import org.sopt.exception.ErrorStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
Expand All @@ -10,22 +10,28 @@ public class GlobalExceptionHandler {

@ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<ErrorResponse> handleIllegalArgument(IllegalArgumentException e) {
ErrorStatus errorStatus = ErrorStatus.NOT_FOUND_MEMBER_EXCEPTION;

return ResponseEntity
.status(ApiCode.FAILURE_MEMBER_NOT_FOUND.status())
.body(ErrorResponse.of(ApiCode.FAILURE_MEMBER_NOT_FOUND.code(), e.getMessage()));
.status(errorStatus.getHttpStatus())
.body(ErrorResponse.of(errorStatus, e.getMessage()));
}

@ExceptionHandler(IllegalStateException.class)
public ResponseEntity<ErrorResponse> handleIllegalState(IllegalStateException e) {
ErrorStatus errorStatus = ErrorStatus.VALIDATION_REQUEST_MISSING_EXCEPTION;

return ResponseEntity
.badRequest()
.body(ErrorResponse.of(ApiCode.FAILURE_INVALID_REQUEST.code(), e.getMessage()));
.status(errorStatus.getHttpStatus())
.body(ErrorResponse.of(errorStatus, e.getMessage()));
}

@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleException(Exception e) {
ErrorStatus errorStatus = ErrorStatus.INTERNAL_SERVER_ERROR;

return ResponseEntity
.internalServerError()
.body(ErrorResponse.of("f5000", e.getMessage()));
.status(errorStatus.getHttpStatus())
.body(ErrorResponse.of(errorStatus, e.getMessage()));
}
}
18 changes: 12 additions & 6 deletions src/main/java/org/sopt/common/SuccessResponse.java
Original file line number Diff line number Diff line change
@@ -1,23 +1,29 @@
package org.sopt.common;

import org.sopt.dto.response.ApiCode;
import org.sopt.common.code.BaseCode;
import org.sopt.controller.common.SuccessStatus;

public record SuccessResponse<T>(
String status,
String code,
String message,
T data
) {
public static <T> SuccessResponse<T> of(ApiCode apiCode, T data) {
public static <T> SuccessResponse<T> of(SuccessStatus successStatus, T data) {
return new SuccessResponse<>(
"SUCCESS",
apiCode.code(),
apiCode.message(),
successStatus.getCode(),
successStatus.getMessage(),
data
);
}

public static <T> SuccessResponse<T> of(String message, T data) {
return new SuccessResponse<>("SUCCESS", "s2000", message, data);
public static <T> SuccessResponse<T> of(BaseCode code, T data) {
return new SuccessResponse<>(
"SUCCESS",
code.getCode(),
code.getMessage(),
data
);
}
}
9 changes: 9 additions & 0 deletions src/main/java/org/sopt/common/code/BaseCode.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.sopt.common.code;

import org.springframework.http.HttpStatus;

public interface BaseCode {
HttpStatus getHttpStatus();
String getCode();
String getMessage();
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.sopt.common;
package org.sopt.common.validator;

import org.sopt.domain.ArticleTag;

Expand Down
20 changes: 20 additions & 0 deletions src/main/java/org/sopt/common/validator/CommentValidator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.sopt.common.validator;

import org.sopt.domain.Comment;
import org.springframework.stereotype.Component;

@Component
public class CommentValidator {

public void validateLength(String content) {
if (content == null || content.length() > 300) {
throw new IllegalArgumentException("댓글은 300자 이내여야 합니다.");
}
}

public void validateWriter(Comment comment, Long requestMemberId) {
if (!comment.getMember().getId().equals(requestMemberId)) {
throw new IllegalArgumentException("작성자만 댓글을 수정/삭제할 수 있습니다.");
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.sopt.common;
package org.sopt.common.validator;

import java.time.LocalDate;
import java.time.Period;
Expand Down
12 changes: 7 additions & 5 deletions src/main/java/org/sopt/controller/ArticleController.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package org.sopt.controller;

import org.sopt.common.SuccessResponse;
import org.sopt.controller.common.SuccessStatus;
import org.sopt.dto.request.ArticleCreateRequest;
import org.sopt.dto.response.ArticleCreateResponse;
import org.sopt.dto.response.ArticleListResponse;
import org.sopt.dto.response.ArticleResponse;
import org.sopt.dto.response.ApiCode;
import org.sopt.service.ArticleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
Expand All @@ -28,8 +28,8 @@ public ResponseEntity<SuccessResponse<ArticleCreateResponse>> createArticle(
) {
ArticleCreateResponse response = articleService.createArticle(request);
return ResponseEntity
.status(ApiCode.SUCCESS_CREATE_ARTICLE.status())
.body(SuccessResponse.of(ApiCode.SUCCESS_CREATE_ARTICLE, response));
.status(SuccessStatus.SUCCESS_CREATE_ARTICLE.getHttpStatus())
.body(SuccessResponse.of(SuccessStatus.SUCCESS_CREATE_ARTICLE, response));
}

@GetMapping("/{id}")
Expand All @@ -38,13 +38,15 @@ public ResponseEntity<SuccessResponse<ArticleResponse>> getArticle(
) {
ArticleResponse response = articleService.getArticle(id);
return ResponseEntity
.ok(SuccessResponse.of(ApiCode.SUCCESS_FIND_ARTICLE, response));
.status(SuccessStatus.SUCCESS_FIND_ARTICLE.getHttpStatus())
.body(SuccessResponse.of(SuccessStatus.SUCCESS_FIND_ARTICLE, response));
}

@GetMapping
public ResponseEntity<SuccessResponse<ArticleListResponse>> getAllArticles() {
ArticleListResponse response = articleService.getAllArticles();
return ResponseEntity
.ok(SuccessResponse.of(ApiCode.SUCCESS_FIND_ARTICLE, response));
.status(SuccessStatus.SUCCESS_FIND_ALL_ARTICLES.getHttpStatus())
.body(SuccessResponse.of(SuccessStatus.SUCCESS_FIND_ALL_ARTICLES, response));
}
}
66 changes: 66 additions & 0 deletions src/main/java/org/sopt/controller/CommentController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package org.sopt.controller;

import org.sopt.common.SuccessResponse;
import org.sopt.controller.common.SuccessStatus;
import org.sopt.dto.request.CommentRequest;
import org.sopt.dto.response.CommentResponse;
import org.sopt.service.CommentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/api/v1")
public class CommentController {

private final CommentService commentService;

@Autowired
public CommentController(CommentService commentService) {
this.commentService = commentService;
}

@PostMapping("/articles/{articleId}/comments")
public ResponseEntity<SuccessResponse<Long>> createComment(
@PathVariable Long articleId,
@RequestBody CommentRequest request
) {
Long commentId = commentService.createComment(articleId, request);
return ResponseEntity
.status(SuccessStatus.SUCCESS_CREATE_COMMENT.getHttpStatus())
.body(SuccessResponse.of(SuccessStatus.SUCCESS_CREATE_COMMENT, commentId));
}

@PatchMapping("/comments/{commentId}")
public ResponseEntity<SuccessResponse<Long>> updateComment(
@PathVariable Long commentId,
@RequestBody CommentRequest request
) {
commentService.updateComment(commentId, request);
return ResponseEntity
.status(SuccessStatus.SUCCESS_UPDATE_COMMENT.getHttpStatus())
.body(SuccessResponse.of(SuccessStatus.SUCCESS_UPDATE_COMMENT, commentId));
}

@DeleteMapping("/comments/{commentId}")
public ResponseEntity<SuccessResponse<Long>> deleteComment(
@PathVariable Long commentId
) {
commentService.deleteComment(commentId);
return ResponseEntity
.status(SuccessStatus.SUCCESS_DELETE_COMMENT.getHttpStatus())
.body(SuccessResponse.of(SuccessStatus.SUCCESS_DELETE_COMMENT, commentId));
}

@GetMapping("/articles/{articleId}/comments")
public ResponseEntity<SuccessResponse<List<CommentResponse>>> getComments(
@PathVariable Long articleId
) {
List<CommentResponse> response = commentService.getComments(articleId);
return ResponseEntity
.status(SuccessStatus.SUCCESS_FIND_COMMENT.getHttpStatus())
.body(SuccessResponse.of(SuccessStatus.SUCCESS_FIND_COMMENT, response));
}
}
19 changes: 9 additions & 10 deletions src/main/java/org/sopt/controller/MemberController.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package org.sopt.controller;

import org.sopt.common.SuccessResponse;
import org.sopt.controller.common.SuccessStatus;
import org.sopt.dto.request.MemberCreateRequest;
import org.sopt.dto.response.ApiCode;
import org.sopt.dto.response.MemberCreateResponse;
import org.sopt.dto.response.MemberListResponse;
import org.sopt.dto.response.MemberResponse;
Expand All @@ -22,42 +22,41 @@ public MemberController(MemberService memberService) {
this.memberService = memberService;
}


@PostMapping
public ResponseEntity<SuccessResponse<MemberCreateResponse>> createMember(
@RequestBody MemberCreateRequest request
) {
MemberCreateResponse response = memberService.join(request);
return ResponseEntity
.status(ApiCode.SUCCESS_CREATE_MEMBER.status())
.body(SuccessResponse.of(ApiCode.SUCCESS_CREATE_MEMBER, response));
.status(SuccessStatus.SUCCESS_CREATE_MEMBER.getHttpStatus())
.body(SuccessResponse.of(SuccessStatus.SUCCESS_CREATE_MEMBER, response));
}


@DeleteMapping("/{id}")
public ResponseEntity<SuccessResponse<Void>> deleteMember(
@PathVariable Long id
) {
memberService.deleteMember(id);
return ResponseEntity
.ok(SuccessResponse.of(ApiCode.SUCCESS_DELETE_MEMBER, null));
.status(SuccessStatus.SUCCESS_DELETE_MEMBER.getHttpStatus())
.body(SuccessResponse.of(SuccessStatus.SUCCESS_DELETE_MEMBER, null));
}


@GetMapping("/{id}")
public ResponseEntity<SuccessResponse<MemberResponse>> getMember(
@PathVariable Long id
) {
MemberResponse response = memberService.findOne(id);
return ResponseEntity
.ok(SuccessResponse.of(ApiCode.SUCCESS_FIND_MEMBER, response));
.status(SuccessStatus.SUCCESS_FIND_MEMBER.getHttpStatus())
.body(SuccessResponse.of(SuccessStatus.SUCCESS_FIND_MEMBER, response));
}


@GetMapping
public ResponseEntity<SuccessResponse<MemberListResponse>> getAllMembers() {
MemberListResponse response = memberService.findAllMembers();
return ResponseEntity
.ok(SuccessResponse.of(ApiCode.SUCCESS_FIND_MEMBER, response));
.status(SuccessStatus.SUCCESS_FIND_MEMBER.getHttpStatus())
.body(SuccessResponse.of(SuccessStatus.SUCCESS_FIND_MEMBER, response));
}
}
30 changes: 30 additions & 0 deletions src/main/java/org/sopt/controller/common/SuccessStatus.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package org.sopt.controller.common;

import lombok.AccessLevel;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.sopt.common.code.BaseCode;
import org.springframework.http.HttpStatus;

@Getter
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
public enum SuccessStatus implements BaseCode {
// 200 OK
SUCCESS_FIND_MEMBER(HttpStatus.OK, "s2002", "회원 조회를 성공하였습니다."),
SUCCESS_FIND_ARTICLE(HttpStatus.OK, "s3002", "게시글 조회를 성공하였습니다."),
SUCCESS_FIND_ALL_ARTICLES(HttpStatus.OK, "s3003", "전체 게시글 조회를 성공하였습니다."),
SUCCESS_UPDATE_COMMENT(HttpStatus.OK, "s2000", "댓글 수정 성공"),
SUCCESS_DELETE_COMMENT(HttpStatus.OK, "s2000", "댓글 삭제 성공"),
SUCCESS_FIND_COMMENT(HttpStatus.OK, "s2000", "댓글 조회 성공"),
SUCCESS_DELETE_MEMBER(HttpStatus.OK, "s2001", "회원이 성공적으로 삭제되었습니다."),

// 201 CREATED
SUCCESS_CREATE_MEMBER(HttpStatus.CREATED, "s2010", "회원이 성공적으로 생성되었습니다."),
SUCCESS_CREATE_ARTICLE(HttpStatus.CREATED, "s3010", "게시글이 성공적으로 생성되었습니다."),
SUCCESS_CREATE_COMMENT(HttpStatus.CREATED, "s2010", "댓글 생성 성공"),
;

private final HttpStatus httpStatus;
private final String code;
private final String message;
}
5 changes: 5 additions & 0 deletions src/main/java/org/sopt/domain/Article.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

@Entity
@EntityListeners(AuditingEntityListener.class)
Expand Down Expand Up @@ -36,6 +38,9 @@ public class Article {
@CreatedDate
private LocalDateTime createdAt;

@OneToMany(mappedBy = "article", cascade = CascadeType.REMOVE)
private List<Comment> comments = new ArrayList<>();

public Article(Member member, String title, String content, ArticleTag tag) {
this.member = member;
this.title = title;
Expand Down
Loading