Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,6 @@ out/

### 보안정보
application-secret.yml
*.pem

src/main/generated
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public class AuthService {

public void signup(SignupRequestDto dto, MultipartFile image){
if (userRepository.existsByEmail(dto.getEmail())) {
throw new BusinessException("중복된 이메일입니다.");
throw new BusinessException(AuthErrorStatus.DUPLICATE_EMAIL);
}
userRepository.findByEmailAndUserStatus(dto.getEmail(), UserStatus.DELETED)
.ifPresent(deletedUser -> {
Expand All @@ -50,7 +50,7 @@ public void signup(SignupRequestDto dto, MultipartFile image){
}
});
if (userRepository.existsByNickname(dto.getNickname())){
throw new BusinessException("중복된 닉네임입니다.");
throw new BusinessException(AuthErrorStatus.DUPLICATE_NICKNAME);
}
// TODO: bio, 닉네임에 금칙어 검사
String encodedPassword = passwordEncoder.encode(dto.getPassword());
Expand Down Expand Up @@ -117,7 +117,7 @@ public void logout(User user){

public void signOut(User user, com.daramg.server.user.dto.PasswordRequestDto request){
if (!passwordEncoder.matches(request.getPassword(), user.getPassword())) {
throw new BusinessException("비밀번호가 일치하지 않습니다.");
throw new BusinessException(AuthErrorStatus.INVALID_PASSWORD);
}
redisTemplate.delete(user.getEmail());
user.withdraw();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import com.daramg.server.auth.util.MimeMessageGenerator;
import com.daramg.server.auth.util.VerificationCodeGenerator;
import com.daramg.server.common.exception.BusinessException;
import com.daramg.server.user.exception.UserErrorStatus;
import com.daramg.server.user.repository.UserRepository;
import jakarta.mail.MessagingException;
import jakarta.mail.internet.MimeMessage;
Expand All @@ -35,7 +36,7 @@ public void sendVerificationEmail(EmailVerificationRequestDto request) {
case SIGNUP -> sendForSignup(request);
case PASSWORD_RESET -> sendForPasswordReset(request);
case EMAIL_CHANGE -> sendForEmailChange(request);
default -> throw new BusinessException("지원하지 않는 이메일 발송 목적입니다.");
default -> throw new BusinessException(AuthErrorStatus.UNSUPPORTED_EMAIL_PURPOSE);
}
}

Expand All @@ -53,7 +54,7 @@ private void sendForPasswordReset(EmailVerificationRequestDto request) {
private void sendForEmailChange(EmailVerificationRequestDto request) {
if (userRepository.existsByEmail(request.getEmail())
&& !request.getEmail().equals(request.getOriginalEmail())) {
throw new BusinessException("이미 가입되어 있는 이메일입니다.");
throw new BusinessException(UserErrorStatus.DUPLICATE_EMAIL);
}
sendVerificationCode(request);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ public enum AuthErrorStatus implements BaseErrorCode {

USER_NOT_FOUND_EXCEPTION(HttpStatus.NOT_FOUND, ErrorCategory.AUTH.generate(404_1), "존재하지 않는 사용자입니다."),

DUPLICATE_EMAIL(HttpStatus.CONFLICT, ErrorCategory.AUTH.generate(409_1), "중복된 이메일입니다."),
DUPLICATE_NICKNAME(HttpStatus.CONFLICT, ErrorCategory.AUTH.generate(409_2), "중복된 닉네임입니다."),
INVALID_PASSWORD(HttpStatus.BAD_REQUEST, ErrorCategory.AUTH.generate(400_8), "비밀번호가 일치하지 않습니다."),
UNSUPPORTED_EMAIL_PURPOSE(HttpStatus.BAD_REQUEST, ErrorCategory.AUTH.generate(400_9), "지원하지 않는 이메일 발송 목적입니다."),

SEND_VERIFICATION_EMAIL_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, ErrorCategory.AUTH.generate(500), "이메일 전송에 실패했습니다."),
REDIS_CONNECTION_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, ErrorCategory.AUTH.generate(500_1), "Redis 연결에 실패했습니다. 서버 관리자에게 문의 바랍니다.");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.daramg.server.comment.repository.CommentLikeRepository;
import com.daramg.server.comment.repository.CommentRepository;
import com.daramg.server.common.application.EntityUtils;
import com.daramg.server.comment.exception.CommentErrorStatus;
import com.daramg.server.common.exception.BusinessException;
import com.daramg.server.post.domain.Post;
import com.daramg.server.notification.domain.NotificationType;
Expand All @@ -31,7 +32,7 @@ public class CommentService {
public void createComment(Long postId, CommentCreateDto request, User user){
Post post = entityUtils.getEntity(postId, Post.class);
if (post.isBlocked()){
throw new BusinessException("블락된 포스트에는 댓글을 남길 수 없습니다.");
throw new BusinessException(CommentErrorStatus.BLOCKED_POST);
}

Comment comment = Comment.of(
Expand All @@ -53,11 +54,11 @@ public void createComment(Long postId, CommentCreateDto request, User user){
public void createReply(Long commentId, CommentReplyCreateDto request, User user){
Comment parentComment = entityUtils.getEntity(commentId, Comment.class);
if (parentComment.isDeleted() || parentComment.isBlocked()){
throw new BusinessException("삭제되었거나 블락된 댓글에는 대댓글을 남길 수 없습니다.");
throw new BusinessException(CommentErrorStatus.BLOCKED_OR_DELETED_COMMENT);
}
Post post = parentComment.getPost();
if (post.isBlocked()){
throw new BusinessException("블락된 포스트에는 댓글을 남길 수 없습니다.");
throw new BusinessException(CommentErrorStatus.BLOCKED_POST);
}

Comment reply = Comment.of(
Expand All @@ -79,7 +80,7 @@ public void createReply(Long commentId, CommentReplyCreateDto request, User user
public CommentLikeResponseDto toggleCommentLike(Long commentId, User user){
Comment comment = entityUtils.getEntity(commentId, Comment.class);
if (comment.isDeleted() || comment.isBlocked()){
throw new BusinessException("삭제되었거나 블락된 댓글에는 좋아요를 누를 수 없습니다.");
throw new BusinessException(CommentErrorStatus.BLOCKED_OR_DELETED_COMMENT);
}

boolean alreadyLiked = commentLikeRepository
Expand All @@ -105,10 +106,10 @@ public void deleteComment(Long commentId, User user){
Comment comment = entityUtils.getEntity(commentId, Comment.class);

if (comment.isDeleted()){
throw new BusinessException("이미 삭제 처리된 댓글입니다.");
throw new BusinessException(CommentErrorStatus.ALREADY_DELETED);
}
if (comment.getUser() == null || !comment.getUser().getId().equals(user.getId())){
throw new BusinessException("댓글을 작성한 유저만 댓글을 삭제할 수 있습니다.");
throw new BusinessException(CommentErrorStatus.NOT_COMMENT_AUTHOR);
}

comment.softDelete();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.daramg.server.comment.exception;

import com.daramg.server.common.exception.BaseErrorCode;
import com.daramg.server.common.exception.ErrorCategory;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.http.HttpStatus;

@Getter
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public enum CommentErrorStatus implements BaseErrorCode {

BLOCKED_POST(HttpStatus.FORBIDDEN, ErrorCategory.COMMENT.generate(403_1), "블락된 포스트에는 댓글을 남길 수 없습니다."),
BLOCKED_OR_DELETED_COMMENT(HttpStatus.FORBIDDEN, ErrorCategory.COMMENT.generate(403_2), "삭제되었거나 블락된 댓글입니다."),
ALREADY_DELETED(HttpStatus.BAD_REQUEST, ErrorCategory.COMMENT.generate(400_1), "이미 삭제 처리된 댓글입니다."),
NOT_COMMENT_AUTHOR(HttpStatus.FORBIDDEN, ErrorCategory.COMMENT.generate(403_3), "댓글 작성자만 삭제할 수 있습니다.");

private final HttpStatus httpStatus;
private final String code;
private final String message;
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public enum CommonErrorStatus implements BaseErrorCode {
GATEWAY_TIMEOUT(HttpStatus.GATEWAY_TIMEOUT, ErrorCategory.COMMON.generate(504), "타임아웃 에러, 서버 관리자에게 문의 바랍니다."),

BAD_REQUEST(HttpStatus.BAD_REQUEST, ErrorCategory.COMMON.generate(400), "유효하지 않은 요청입니다."),
INVALID_CURSOR(HttpStatus.BAD_REQUEST, ErrorCategory.COMMON.generate(400_1), "유효하지 않은 커서 포맷입니다."),
UNAUTHORIZED(HttpStatus.UNAUTHORIZED, ErrorCategory.COMMON.generate(401), "인증이 필요합니다."),
FORBIDDEN(HttpStatus.FORBIDDEN, ErrorCategory.COMMON.generate(403), "금지된 요청입니다."),
NOT_FOUND(HttpStatus.NOT_FOUND , ErrorCategory.COMMON.generate(404), "찾을 수 없는 리소스입니다.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ public enum ErrorCategory {
AUTH("AUTH_"),
USER("USER_"),
POST("POST_"),
IMAGE("IMAGE_");
IMAGE("IMAGE_"),
COMMENT("COMMENT_"),
NOTIFICATION("NOTIFICATION_");

private final String prefix;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
public ResponseEntity<ErrorResponse> handleGeneralException(BusinessException e) {
BaseErrorCode errorCode = e.getErrorCode();

log.warn("GeneralException: {}", errorCode.getMessage());
log.warn("GeneralException: {} - {}", errorCode.getMessage(), e.getMessage());

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

로그 메시지 형식을 개선하면 더 유용할 것 같습니다. 현재 형식은 BusinessException(BaseErrorCode) 생성자를 사용할 때 메시지가 중복으로 로깅될 수 있습니다. (GeneralException: <msg> - <msg>)

e.getMessage()BusinessException(String message, BaseErrorCode errorCode) 생성자에서 errorCode.getMessage()를 포함하도록 구성되어 있으므로, 다음과 같이 변경하면 중복을 피하고 에러 코드를 명시적으로 포함하여 디버깅에 더 도움이 될 것입니다.

log.warn("GeneralException: {} (code: {})", e.getMessage(), errorCode.getCode());

이렇게 하면 다음과 같이 로깅됩니다.

  • new BusinessException(errorCode) 사용 시: GeneralException: <메시지> (code: <에러코드>)
  • new BusinessException(detail, errorCode) 사용 시: GeneralException: <메시지><상세정보> (code: <에러코드>)
Suggested change
log.warn("GeneralException: {} - {}", errorCode.getMessage(), e.getMessage());
log.warn("GeneralException: {} (code: {})", e.getMessage(), errorCode.getCode());

return ResponseEntity
.status(errorCode.getHttpStatus())
.body(ErrorResponse.of(errorCode));
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/com/daramg/server/common/util/PagingUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.daramg.server.common.dto.PageRequestDto;
import com.daramg.server.common.dto.PageResponseDto;
import com.daramg.server.common.exception.BusinessException;
import com.daramg.server.common.exception.CommonErrorStatus;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.core.types.dsl.DateTimePath;
import com.querydsl.core.types.dsl.NumberPath;
Expand Down Expand Up @@ -116,7 +117,7 @@ private Cursor decodeCursor(String cursorString) {
return new Cursor(LocalDateTime.parse(parts[0]), Long.parseLong(parts[1]));
} catch (IllegalArgumentException | ArrayIndexOutOfBoundsException |
DateTimeParseException e) {
throw new BusinessException("유효하지 않은 커서 포맷입니다.");
throw new BusinessException(CommonErrorStatus.INVALID_CURSOR);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@

import com.daramg.server.common.exception.BusinessException;
import com.daramg.server.notice.domain.Notice;
import com.daramg.server.notification.exception.NotificationErrorStatus;
import com.daramg.server.user.domain.User;

public class NoticeUserValidator {

public static void check(Notice notice, User user) {
if (!notice.getUser().getId().equals(user.getId())) {
throw new BusinessException("공지사항의 작성자가 일치하지 않습니다.");
throw new BusinessException(NotificationErrorStatus.NOT_NOTICE_AUTHOR);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.daramg.server.common.application.EntityUtils;
import com.daramg.server.common.exception.BusinessException;
import com.daramg.server.notification.exception.NotificationErrorStatus;
import com.daramg.server.notification.domain.Notification;
import com.daramg.server.notification.repository.NotificationRepository;
import com.daramg.server.user.domain.User;
Expand Down Expand Up @@ -35,7 +36,7 @@ public void delete(Long notificationId, User user) {

private void validateReceiver(Notification notification, User user) {
if (!notification.getReceiver().getId().equals(user.getId())) {
throw new BusinessException("본인의 알림만 처리할 수 있습니다.");
throw new BusinessException(NotificationErrorStatus.NOT_OWN_NOTIFICATION);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.daramg.server.notification.exception;

import com.daramg.server.common.exception.BaseErrorCode;
import com.daramg.server.common.exception.ErrorCategory;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.http.HttpStatus;

@Getter
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public enum NotificationErrorStatus implements BaseErrorCode {

NOT_OWN_NOTIFICATION(HttpStatus.FORBIDDEN, ErrorCategory.NOTIFICATION.generate(403_1), "본인의 알림만 처리할 수 있습니다."),
NOT_NOTICE_AUTHOR(HttpStatus.FORBIDDEN, ErrorCategory.NOTIFICATION.generate(403_2), "공지사항 작성자만 수정/삭제할 수 있습니다.");

private final HttpStatus httpStatus;
private final String code;
private final String message;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.daramg.server.common.application.EntityUtils;
import com.daramg.server.common.exception.BusinessException;
import com.daramg.server.post.exception.PostErrorStatus;
import com.daramg.server.common.exception.NotFoundException;
import com.daramg.server.composer.domain.Composer;
import com.daramg.server.composer.repository.ComposerRepository;
Expand Down Expand Up @@ -61,7 +62,7 @@ public void createCuration(PostCreateDto.CreateCuration dto, User user) {
if (dto.getPrimaryComposerId() != null) {
primaryComposer = entityUtils.getEntity(dto.getPrimaryComposerId(), Composer.class);
} else if (dto.getPostStatus() == PostStatus.PUBLISHED) {
throw new BusinessException("발행 시 주요 작곡가는 필수입니다.");
throw new BusinessException(PostErrorStatus.PRIMARY_COMPOSER_REQUIRED);
}
List<Composer> additionalComposers = new ArrayList<>();

Expand Down Expand Up @@ -95,7 +96,7 @@ public void createStory(PostCreateDto.CreateStory dto, User user) {
if (dto.getPrimaryComposerId() != null) {
primaryComposer = entityUtils.getEntity(dto.getPrimaryComposerId(), Composer.class);
} else if (dto.getPostStatus() == PostStatus.PUBLISHED) {
throw new BusinessException("발행 시 주요 작곡가는 필수입니다.");
throw new BusinessException(PostErrorStatus.PRIMARY_COMPOSER_REQUIRED);
}
PostCreateVo.Story vo = new PostCreateVo.Story(
user,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.daramg.server.post.exception;

import com.daramg.server.common.exception.BaseErrorCode;
import com.daramg.server.common.exception.ErrorCategory;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.http.HttpStatus;

@Getter
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public enum PostErrorStatus implements BaseErrorCode {

PRIMARY_COMPOSER_REQUIRED(HttpStatus.BAD_REQUEST, ErrorCategory.POST.generate(400_1), "발행 시 주요 작곡가는 필수입니다."),
NOT_POST_AUTHOR(HttpStatus.FORBIDDEN, ErrorCategory.POST.generate(403_1), "포스트 작성자만 수정/삭제할 수 있습니다.");

private final HttpStatus httpStatus;
private final String code;
private final String message;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@

import com.daramg.server.common.exception.BusinessException;
import com.daramg.server.post.domain.Post;
import com.daramg.server.post.exception.PostErrorStatus;
import com.daramg.server.user.domain.User;

public class PostUserValidator {

public static void check(Post post, User user) {
if (!post.getUser().getId().equals(user.getId())) {
throw new BusinessException("포스트와 작성자가 일치하지 않습니다.");
throw new BusinessException(PostErrorStatus.NOT_POST_AUTHOR);
}
}
}
Loading
Loading